· 

VHDLでSPIスレーブ(2)

VHDLでSPIスレーブを実装します。SPIで受信して、値を保持するってだけ。ghdlではちゃんと動いていたんだけど、QuartusのModelSimで試したらちゃんとデータを受信できていなかったので、修正。やっぱり、コアクロックを入力するようにして、クロック同期で動作するようにする。

 

my_spi_slave.vhd

library ieee;
use ieee.std_logic_1164.all;
--library altera;
--use altera.altera_syn_attributes.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);
        i_clk : in std_logic
    );
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";
    signal cs_wire : std_logic:='1';
    signal sck_wire : std_logic:='0';
    signal mosi_wire : std_logic:='0';
    signal miso_wire : std_logic:='0';

    signal cs_pres : std_logic:='1';
    signal cs_prev : std_logic:='1';
    signal cs_rising : std_logic:='0';
    signal cs_falling : std_logic:='0';
     
    signal sck_pres : std_logic:='0';
    signal sck_prev : std_logic:='0';
    signal sck_rising : std_logic:='0';
    signal sck_falling : std_logic:='0';
begin
    a0<=a0_buf;
    a1<=a1_buf;
    b0<=b0_buf;
    b1<=b1_buf;

    sck_wire<=sck;
    miso<=miso_wire;
    cs_wire<=cs;
    mosi_wire<=mosi;

    cs_pres<=cs_wire;
    cs_rising<='1' when (cs_prev='0' and cs_pres='1') else '0';
    cs_falling<='1' when (cs_prev='1' and cs_pres='0') else '0';

    sck_pres<=sck_wire;
    sck_rising<='1' when (sck_prev='0' and sck_pres='1') else '0';
    sck_falling<='1' when (sck_prev='1' and sck_pres='0') else '0';
    
    process(i_clk)begin
        if i_clk'event and i_clk='1' then
            cs_prev<=cs_wire;
            sck_prev<=sck_wire;
        end if;
    end process;
    process(i_clk)begin
        if i_clk'event and i_clk='1' then
            if cs_falling='1' then
                mosi_pos<=0;
                miso_pos<=0;
                tx_buf<=X"0000000000000000";
                rx_buf<=X"0000000000000000";
                cmd<=X"00";
                miso_wire<=tx_buf(63);
            end if;
            if cs_rising='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_rising='1' and cs_wire='0' then
                if mosi_pos=7 then
                    cmd<=(rx_buf(6 downto 0) & mosi_wire);
                end if;
                rx_buf<=(rx_buf(62 downto 0) & mosi_wire);
                tx_buf<=(tx_buf(62 downto 0) & '0');
                mosi_pos<=mosi_pos+1;
            end if;
            if sck_falling='1' and cs_wire='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_wire<=a0_buf(31);
                        when "00000001"=> miso_wire<=a1_buf(31);
                        when "00000010"=> miso_wire<=b0_buf(31);
                        when "00000011"=> miso_wire<=b1_buf(31);
                        when others=> null;
                    end case;
                else
                    miso_wire<=tx_buf(63);
                end if;
                miso_pos<=miso_pos+1;
            end if;            
        end if;
    end process;
end;

これで、とりあえず、Analysis&Synthesisをすると、ErrorもWarningもでない。で、Tools-->Run Simulation Tool-->RTL SimulationでModelSimを起動。、、、ていうかこのあたりの操作は、SPIハードをFPGAで(2)SPIハードをFPGAで(3)参照で。

 

そして、テストベンチ。

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);
        i_clk : in std_logic
    ); end component;
    signal clksys : std_logic:='0';
    constant STEPSYS : TIME := 6.25 ns;

    signal cs_tb : std_logic:='1';
    signal sck_tb : std_logic:='0';
    signal mosi_tb : std_logic:='0';
    signal miso_tb : std_logic:='0';
    signal a0_tb : std_logic_vector(31 downto 0):=X"3F800000";
    signal a1_tb : std_logic_vector(31 downto 0):=X"BF7814CD";
    signal b0_tb : std_logic_vector(31 downto 0):=X"3C7D6652";
    signal b1_tb : std_logic_vector(31 downto 0):=X"3C7D6652";
    signal spitxbuf : std_logic_vector(39 downto 0):=X"0000000000";
    signal spirxbuf : std_logic_vector(39 downto 0):=X"0000000000";
    signal a0 : std_logic_vector(31 downto 0);
    signal a1 : std_logic_vector(31 downto 0);
    signal b0 : std_logic_vector(31 downto 0);
    signal b1 : std_logic_vector(31 downto 0);

begin
    my_spi_slave_inst : component my_spi_slave port map(
        cs=>cs_tb,
        sck=>sck_tb,
        mosi=>mosi_tb,
        miso=>miso_tb,
        a0=>a0,
        a1=>a1,
        b0=>b0,
        b1=>b1,
        i_clk=>clksys
    );

    process begin
        cs_tb<='1';

        spitxbuf<=X"80"&a0_tb;
        wait for 100 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';

        spitxbuf<=X"81"&a1_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';
        
        spitxbuf<=X"82"&b0_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';

        spitxbuf<=X"83"&b1_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';
        wait for 100 us;
        
        assert false
        report "Finish"
        severity failure;
    end process;

    clksys <= not(clksys) after STEPSYS/2;

end;

doファイル。

my_spi_slave_run_msim_rtl_vhdl.do

transcript on
if {[file exists rtl_work]} {
        vdel -lib rtl_work -all
}
vlib rtl_work
vmap work rtl_work

vcom -93 -work work {C:/Users/hoge/Documents/Quartus/work_Prime18.1/my_spi_slave_101/my_spi_slave.vhd}

vcom -93 -work work {C:/Users/hoge/Documents/Quartus/work_Prime18.1/my_spi_slave_101/simulation/modelsim/tb.vhd}
vsim -L altera_mf_ver -c work.my_spi_slave_tb
add wave -hex sim:/my_spi_slave_tb/clksys
add wave -position end  sim:/my_spi_slave_tb/cs_tb
add wave -position end  sim:/my_spi_slave_tb/sck_tb
add wave -position end  sim:/my_spi_slave_tb/mosi_tb
add wave -position end  sim:/my_spi_slave_tb/miso_tb
add wave -radix hexadecimal -position end  sim:/my_spi_slave_tb/a0
add wave -radix hexadecimal -position end  sim:/my_spi_slave_tb/a1
add wave -radix hexadecimal -position end  sim:/my_spi_slave_tb/b0
add wave -radix hexadecimal -position end  sim:/my_spi_slave_tb/b1
add wave -radix hexadecimal -position end  sim:/my_spi_slave_tb/my_spi_slave_inst/cmd
log -r *
run -all

で、Transcryptウィンドウで、

do my_spi_slave_run_msim_rtl_vhdl.do

ってすると、

ってことで、うまくいったっぽい。

デバイスメーカーのツールはいろいろやれることが多くてむしろ邪魔くさいってことも多いけど、慣れればこれはこれで使いやすいんだろうなー、、、

ところで、QuartusPrimeの最新版(現時点で22.1.1)でNios開発するならWSLのUbuntu 18.04 LTSが要件ってやつ、何とかならんのかね、、、WSLには複数のディストリビューションは入れれんから、とんでもなく狭い範囲に拘束されるわけで。なので今でもうちはQuartusPrime18.1.0。