VHDL 仿真失败,结果意外
VHDL simulation failed with unexpected result
我在 5 年前学习了 VHDL,之后就再也没有用过,因为我在不同的领域工作。现在我在一个项目中工作,该项目需要一些 VHDL 方面的工作。我必须实施 SPI 才能对 ADF4158 设备进行编程。我打开了一本语法书并尝试编程。我需要开发以下模块,我根据自己的理解和测试平台进行了编码,但它无法像我需要的那样在模拟中工作。下面是我要开发的模块和整体框图。
下面是我为实现上述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)
一旦掌握了基础知识,您可能会进一步考虑将单个写入或读取操作封装到子程序中。
我在 5 年前学习了 VHDL,之后就再也没有用过,因为我在不同的领域工作。现在我在一个项目中工作,该项目需要一些 VHDL 方面的工作。我必须实施 SPI 才能对 ADF4158 设备进行编程。我打开了一本语法书并尝试编程。我需要开发以下模块,我根据自己的理解和测试平台进行了编码,但它无法像我需要的那样在模拟中工作。下面是我要开发的模块和整体框图。
下面是我为实现上述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)
一旦掌握了基础知识,您可能会进一步考虑将单个写入或读取操作封装到子程序中。