VHDL 仿真失败,结果意外

VHDL simulation failed with unexpected result

我在 5 年前学习了 VHDL,之后就再也没有用过,因为我在不同的领域工作。现在我在一个项目中工作,该项目需要一些 VHDL 方面的工作。我必须实施 SPI 才能对 ADF4158 设备进行编程。我打开了一本语法书并尝试编程。我需要开发以下模块,我根据自己的理解和测试平台进行了编码,但它无法像我需要的那样在模拟中工作。下面是我要开发的模块和整体框图。

流程框图和vhdl模块(可点击):

下面是SPI时序图如下(可点击):

下面是我为实现上述SPI通信而写的VHDL代码:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.all;

entity sender is
  Generic( DATA_WIDTH  : INTEGER := 32); --32 bit registers to pogram (total 8 registers to program)
  Port ( sys_clk : in  STD_LOGIC;   --clock from microblaze (32 MHz)
         enable : in  STD_LOGIC;  --from microblaze to trigger start
         reset : in  STD_LOGIC;   --reset spi clk generation
         start_data_read : out STD_LOGIC;   --to microblaze as flag so that microblaze starts sending register_select codes (pgogram data for target register) and the data to pogram - to buffer
         start_data_write : out STD_LOGIC;  --to microblaze as flag so that microblaze starts sending register_select codes (pgogram data for target register) and the data to pogram - to mosi port
         data_in : in  STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --program data (32bit data in 8 phases depending on register_select)
         process_done : out  STD_LOGIC; --flag bit to microblaze when complete process is complete
         register_select : in  STD_LOGIC_VECTOR(2 downto 0); --select code to identify which register to grogram, theorder can be manupulated from microblaze
         sclk : buffer  STD_LOGIC; --spi clock for ADC (3.2 MHz)
         ss : out  STD_LOGIC := '1'; --select line for ADF (slave)
         mosi : out  STD_LOGIC); --program data for ADF
end sender;

architecture Behavioral of sender is

   type machine is(store, sending); --states of state-machine
   signal state : machine := store;
   signal clk_divide: STD_LOGIC_VECTOR(5 downto 0); --clock cycle ratio between system clock from microblaze and spi clock for ADF
   signal tx_buffer_0   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 0)
   signal tx_buffer_1   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 1)
   signal tx_buffer_2   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 2)
   signal tx_buffer_3   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 3)
   signal tx_buffer_4   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 4)
   signal tx_buffer_5   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 5)
   signal tx_buffer_6   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 6)
   signal tx_buffer_7   : STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0); --IO Buffer to hold program data to mosi (register 7)
begin

   ClK_GEN: process(sys_clk)
   begin --spi sclk 20:1 cycles of sys_clk(system runs at 32 MHz and SPI CLK required is 1.6 MHz)
   if rising_edge(sys_clk) then
      if reset = '1' then
         clk_divide <= (others => '0');
         mosi <= 'Z'; --send high impedence
      else
         if clk_divide < "001010" then --10
            sclk <= '0';
            clk_divide <= clk_divide + 1;
         else
            if clk_divide < "010100" then --20
               sclk <= '1';
            else
               clk_divide <= (others => '0');
            end if;
        end if;
      end if;
   end if;
end process ClK_GEN;

SEND: process(sclk,enable,register_select)
begin
   if rising_edge(sclk) then
      if enable = '1' then
         case state is
            when store =>
               start_data_read <= '1'; --ask microblaze to send register_selectcodes and data
               case register_select is
                  when "000" =>
                      tx_buffer_7 <= data_in; --copy data to buffer
                  when "001" =>
                      tx_buffer_6 <= data_in;
                  when "010" =>
                      tx_buffer_5 <= data_in;
                  when "011" =>
                      tx_buffer_4 <= data_in;
                  when "100" =>
                      tx_buffer_3 <= data_in;
                  when "101" =>
                      tx_buffer_2 <= data_in;
                  when "110" =>
                      tx_buffer_1 <= data_in;
                  when "111" =>
                      tx_buffer_0 <= data_in;
                  when others =>
                      tx_buffer_1 <= (OTHERS => '0');
               end case;
               state <= sending; --change state to next
            when sending =>
               start_data_write <= '1'; --ask microblaze to send register_select codes to pgrogram a register
               case register_select is
                  when "000" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_7 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "001" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_6 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "010" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     ss <= '1';
                  when "011" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_4 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "100" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_3 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "101" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_2 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "110" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_1 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when "111" =>
                     ss <= '0';
                     mosi <= tx_buffer_7(DATA_WIDTH-1);
                     tx_buffer_0 <= tx_buffer_7(DATA_WIDTH-2 downto 0) & '0';
                     ss <= '1';
                  when others =>
                    mosi <= '0';
               end case;
            end case;
         end if;
      end if;
   end process SEND;

end Behavioral;

我也写了一个test bench代码来发送上面的模块,代码如下:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL; 

ENTITY test_sender IS
END test_sender;

ARCHITECTURE behavior OF test_sender IS 
 -- Component Declaration for the Unit Under Test (UUT)

COMPONENT sender
PORT(
    sys_clk : IN  std_logic;
    enable : IN  std_logic;
    reset : IN  std_logic;
    start_data_read : OUT  std_logic;
    start_data_write : OUT  std_logic;
    data_in : IN  std_logic_vector(31 downto 0);
    process_done : OUT  std_logic;
    register_select : IN  std_logic_vector(2 downto 0);
    sclk : buffer  std_logic;
    ss : OUT  std_logic;
    mosi : OUT  std_logic
);
END COMPONENT;
--Inputs
signal sys_clk : std_logic := '0';
signal enable : std_logic := '0';
signal reset : std_logic := '0';
signal data_in : std_logic_vector(31 downto 0) := (others => '0');
signal register_select : std_logic_vector(2 downto 0) := (others => '0');

--Outputs
signal start_data_read : std_logic;
signal start_data_write : std_logic;
signal process_done : std_logic;
signal sclk : std_logic;
signal ss : std_logic;
signal mosi : std_logic;

-- Clock period definitions
constant clk_sys_period : time := 31.25 ns;

BEGIN

-- Instantiate the Unit Under Test (UUT)
uut: sender PORT MAP (
   sys_clk => sys_clk,
   enable => enable,
   reset => reset,
   start_data_read => start_data_read,
   start_data_write => start_data_write,
   data_in => data_in,
   process_done => process_done,
   register_select => register_select,
   sclk => sclk,
   ss => ss,
   mosi => mosi
);

-- Clock process definitions
clk_sys_process :process
begin
    sys_clk <= '0';
    wait for clk_sys_period/2;
    sys_clk <= '1';
    wait for clk_sys_period/2;
end process;

-- Stimulus process
stim_proc: process
begin       
-- hold reset state for 100 ns.
    wait for 1ns;
    enable <= '1';
    if start_data_read = '1' then
        wait for 1ns;
        register_select <= "000";
        wait for 1 ns;
        data_in <= "01001111001101010101010111100001";
        wait for 5 ns;
        register_select <= "001";
        wait for 1 ns;
        data_in <= "11001111001101010111011111100101";
        wait for 5 ns;
        register_select <= "010";
        wait for 1 ns;
        data_in <= "00000011001101010101011101100101";
        wait for 5 ns;
        register_select <= "011";
        wait for 1 ns;
        data_in <= "00011111001101010101010011100101";
        wait for 5 ns;
        register_select <= "100";
        wait for 1 ns;
        data_in <= "10001111001101010101011111100001";
        wait for 5 ns;
        register_select <= "101";
        wait for 1 ns;
        data_in <= "11001111001101010101011110000101";
        wait for 5 ns;
        register_select <= "110";
        wait for 1 ns;
        data_in <= "00101000001101010101011111100101";
        wait for 5 ns;
        register_select <= "111";
        wait for 1 ns;
        data_in <= "11111111001101010101011110100101";
        wait for 5 ns;
    end if;

    if start_data_write = '1' then
        wait for 1ns;
        register_select <= "000";
        wait for 5 ns;
        register_select <= "001";
        wait for 5 ns;
        register_select <= "010";
        wait for 5 ns;
        register_select <= "011";
        wait for 5 ns;
        register_select <= "100";
        wait for 5 ns;
        register_select <= "101";
        wait for 5 ns;
        register_select <= "110";
        wait for 5 ns;
        register_select <= "111";
        wait for 5 ns;
    end if;

-- insert stimulus here 
    wait;
  end process;
END;

模拟运行不正常,所以代码是错误的,但是我找不到错误在哪里,如果有人能帮助我,让我明白我的错误就太好了。

你的意思是在这个模拟中做一个data_read和data_write吗?在 RTL 中,我们可以进行如下检查,因为我们查看了很多次(可能在时钟的边缘):

if start_data_read = '1' then

但是您的测试台进程​​没有循环。因此,不是像我们在 RTL 代码中那样执行 "IF",而是需要做一些事情来使测试停止,直到该条件存在。所以你的测试如下:

stim_proc: process
begin       
-- hold reset state for 100 ns.
    wait for 1ns;
    enable <= '1';
    if start_data_read /= '1' then
        wait until start_data_read = '1' ;
    end if ;
    -- Do a read sequence
    . . . 
    if start_data_write /= '1' then
        wait until start_data_write = '1' ;
    end if ;
    -- Do a write sequence

在您的读写序列中,您有 "wait for 5 ns"。您的设备真的可以这么快地接受操作吗?这比一个时钟周期更快。这些操作是否以任何方式与时钟保持一致?您可以通过执行以下任一操作来等待时钟周期:

wait until rising_edge(Clk) ; 
wait until Clk = '1' ; -- assumes Clk is only '0' or '1' (safe for clocks)

一旦掌握了基础知识,您可能会进一步考虑将单个写入或读取操作封装到子程序中。