VHDLでSPIスレーブを実装します。SPIで受信して、値を保持するってだけだけど、そういうのが必要なんだよねー。IC作っている人たちってすごいよねー。まぁ逆に当たり前すぎるのかもしれん。それを真っ白な状態から自力で作る、、、意味あるのかわからん。が、自分テイストで(自分の欲しいものを最小規模で)やることにごく僅かばかりの意味がある。
今回作りたいものは、SPIで受信したデータをポートに出力するってやつで、SPIの1byte目でどのポートに割り当てるかを判断する。
基本的にはSCKの立上がりや立下りでバッファを左にシフトしていくってだけの制御。
Makefile
VHDLC=/usr/bin/ghdl SRC=my_spi_slave.vhd TBSRC=my_spi_slave_tb.vhd VCD=my_spi_slave.vcd SIMRTL=my_spi_slave_tb all : @make comp @make tb @make tb_e @make vcd comp : $(VHDLC) -a --ieee=synopsys -fexplicit $(SRC) tb : $(VHDLC) -a --ieee=synopsys -fexplicit $(TBSRC) tb_e : $(VHDLC) -e --ieee=synopsys -fexplicit $(SIMRTL) vcd : $(VHDLC) -r --ieee=synopsys -fexplicit $(SIMRTL) --vcd=$(VCD)
my_spi_slave.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity my_spi_slave is port ( cs : in std_logic; sck : in std_logic; mosi : in std_logic; miso : out std_logic; a0 : out std_logic_vector(31 downto 0); a1 : out std_logic_vector(31 downto 0); b0 : out std_logic_vector(31 downto 0); b1 : out std_logic_vector(31 downto 0) ); end my_spi_slave; architecture ppl_type of my_spi_slave is signal miso_pos : integer range 0 to 63:=0; signal mosi_pos : integer range 0 to 63:=0; signal tx_buf : std_logic_vector(63 downto 0):=X"0000000000000000"; signal rx_buf : std_logic_vector(63 downto 0):=X"0000000000000000"; signal cmd : std_logic_vector(7 downto 0):=X"00"; signal a0_buf : std_logic_vector(31 downto 0):=X"00000000"; signal a1_buf : std_logic_vector(31 downto 0):=X"00000000"; signal b0_buf : std_logic_vector(31 downto 0):=X"00000000"; signal b1_buf : std_logic_vector(31 downto 0):=X"00000000"; begin a0<=a0_buf; a1<=a1_buf; b0<=b0_buf; b1<=b1_buf; process (sck,cs) begin if cs'event and cs='0' then mosi_pos<=0; miso_pos<=0; tx_buf<=X"0000000000000000"; rx_buf<=X"0000000000000000"; cmd<=X"00"; miso<=tx_buf(63); end if; if cs'event and cs='1' then case cmd is when "10000000"=> a0_buf<=rx_buf(31 downto 0); when "10000001"=> a1_buf<=rx_buf(31 downto 0); when "10000010"=> b0_buf<=rx_buf(31 downto 0); when "10000011"=> b1_buf<=rx_buf(31 downto 0); when others=> null; end case; end if; if sck'event and sck='1' then if cs='0' then if mosi_pos=7 then cmd<=(rx_buf(6 downto 0) & mosi); end if; rx_buf<=(rx_buf(62 downto 0) & mosi); tx_buf<=(tx_buf(62 downto 0) & '0'); mosi_pos<=mosi_pos+1; end if; end if; if sck'event and sck='0' then if cs='0' then if mosi_pos=8 then case cmd is when "00000000"=> tx_buf(63 downto 32)<=a0_buf; when "00000001"=> tx_buf(63 downto 32)<=a1_buf; when "00000010"=> tx_buf(63 downto 32)<=b0_buf; when "00000011"=> tx_buf(63 downto 32)<=b1_buf; when others=> null; end case; case cmd is when "00000000"=> miso<=a0_buf(31); when "00000001"=> miso<=a1_buf(31); when "00000010"=> miso<=b0_buf(31); when "00000011"=> miso<=b1_buf(31); when others=> null; end case; else miso<=tx_buf(63); end if; miso_pos<=miso_pos+1; end if; end if; end process; end;
my_spi_slave_tb.vhd
LIBRARY ieee; USE ieee.std_logic_1164.ALL; USE ieee.std_logic_unsigned.ALL; entity my_spi_slave_tb is end my_spi_slave_tb; architecture sim of my_spi_slave_tb is component my_spi_slave is port ( cs : in std_logic; sck : in std_logic; mosi : in std_logic; miso : out std_logic; a0 : out std_logic_vector(31 downto 0); a1 : out std_logic_vector(31 downto 0); b0 : out std_logic_vector(31 downto 0); b1 : out std_logic_vector(31 downto 0) ); end component; signal tb_sck : std_logic:='0'; signal tb_mosi : std_logic:='0'; signal tb_miso : std_logic; signal tb_cs : std_logic:='1'; signal tb_a0 : std_logic_vector(31 downto 0); signal tb_a1 : std_logic_vector(31 downto 0); signal tb_b0 : std_logic_vector(31 downto 0); signal tb_b1 : std_logic_vector(31 downto 0); constant step_sck : time:=500 ns; signal tb_tx_buf : std_logic_vector(63 downto 0); signal tb_rx_buf : std_logic_vector(63 downto 0); signal tb_tx_pos : integer range 0 to 63:=0; signal tb_rx_pos : integer range 0 to 63:=0; BEGIN my_spi_slave_inst : component my_spi_slave port map( cs=>tb_cs, sck=>tb_sck, mosi=>tb_mosi, miso=>tb_miso, a0=>tb_a0, a1=>tb_a1, b0=>tb_b0, b1=>tb_b1 ); process begin -- 1st stage tb_tx_buf<=X"821234567A000000"; tb_rx_buf<=X"0000000000000000"; tb_tx_pos<=0; tb_rx_pos<=0; wait for 10 us; tb_cs<='0'; tb_sck<='0'; tb_mosi<=tb_tx_buf(63); tb_tx_pos<=tb_tx_pos+1; for i in 0 to 39 loop wait for step_sck; tb_sck<='1'; tb_rx_buf(63-tb_rx_pos)<=tb_miso; tb_rx_pos<=tb_rx_pos+1; wait for step_sck; tb_sck<='0'; tb_mosi<=tb_tx_buf(63-tb_tx_pos); tb_tx_pos<=tb_tx_pos+1; end loop; wait for step_sck; tb_cs<='1'; tb_tx_buf<=X"0200000000000000"; tb_rx_buf<=X"0000000000000000"; tb_tx_pos<=0; tb_rx_pos<=0; wait for 10 us; tb_cs<='0'; tb_sck<='0'; tb_mosi<=tb_tx_buf(63); tb_tx_pos<=tb_tx_pos+1; for i in 0 to 39 loop wait for step_sck; tb_sck<='1'; tb_rx_buf(63-tb_rx_pos)<=tb_miso; tb_rx_pos<=tb_rx_pos+1; wait for step_sck; tb_sck<='0'; tb_mosi<=tb_tx_buf(63-tb_tx_pos); tb_tx_pos<=tb_tx_pos+1; end loop; wait for step_sck; tb_cs<='1'; wait for 10 us; tb_cs<='0'; wait for step_sck; tb_cs<='1'; -- 2nd stage tb_tx_buf<=X"80FF55FF12000000"; tb_rx_buf<=X"0000000000000000"; tb_tx_pos<=0; tb_rx_pos<=0; wait for 10 us; tb_cs<='0'; tb_sck<='0'; tb_mosi<=tb_tx_buf(63); tb_tx_pos<=tb_tx_pos+1; for i in 0 to 39 loop wait for step_sck; tb_sck<='1'; tb_rx_buf(63-tb_rx_pos)<=tb_miso; tb_rx_pos<=tb_rx_pos+1; wait for step_sck; tb_sck<='0'; tb_mosi<=tb_tx_buf(63-tb_tx_pos); tb_tx_pos<=tb_tx_pos+1; end loop; wait for step_sck; tb_cs<='1'; tb_tx_buf<=X"0000000000000000"; tb_rx_buf<=X"0000000000000000"; tb_tx_pos<=0; tb_rx_pos<=0; wait for 10 us; tb_cs<='0'; tb_sck<='0'; tb_mosi<=tb_tx_buf(63); tb_tx_pos<=tb_tx_pos+1; for i in 0 to 39 loop wait for step_sck; tb_sck<='1'; tb_rx_buf(63-tb_rx_pos)<=tb_miso; tb_rx_pos<=tb_rx_pos+1; wait for step_sck; tb_sck<='0'; tb_mosi<=tb_tx_buf(63-tb_tx_pos); tb_tx_pos<=tb_tx_pos+1; end loop; wait for step_sck; tb_cs<='1'; wait for 10 us; tb_cs<='0'; wait for step_sck; tb_cs<='1'; assert false report "Finish" severity failure; end process; end;
またもやいきなり答えを書いてしまった。
VHDLは代入タイミングが記述順ではないので、なかなか混乱します。gtkwaveの結果を見ながらさんざん調整した結果が上記のコードです。一発で書き上げたわけではないです。VHDL(Verilogも同じだと思うけど)いつまでたっても慣れない。ある変数に代入して、その変数で判断するって流れを作るのは大変に困難。variableとか使う手もあるらしい。、、、使ってないけど。システムクロックを受け付けて、クロックごとに状態を確認しながら動くのが順序を記述するのに向いているが、それじゃ芸がないので、そういうことせずにやりきったー。素人がスクラッチから書いた割にはすっきりできていると思う。
実は今回の課題、最初はChatGPTにやらそうと思ってた。比較的メジャーな課題なので。
何度やってもやり切ってくれない。
ChatGPTでノーコードプログラムってのが話題になったりしてたけど、本当にできるんかいな?
コメントをお書きください