· 

VHDLでSPIスレーブ

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にやらそうと思ってた。比較的メジャーな課題なので。

ダウンロード
20230504_SPI Slave Controller VHDL.pdf
PDFファイル 224.3 KB

何度やってもやり切ってくれない。

ChatGPTでノーコードプログラムってのが話題になったりしてたけど、本当にできるんかいな?