VHDL模拟失败,结果意外
5年前我学习了VHDL,此后我就再也没有使用过,因为我在不同的领域工作。现在我在一个项目中工作,这个项目需要一些VHDL语言的工作。我必须实现SPI来编程ADF4158设备。我打开一本语法书,试着编写程序。我需要开发下面的模块,我按照我的理解编写代码,还有一个测试台,但它在模拟中无法像我需要的那样工作。下面是我想开发的模块和总体框图 过程和vhdl模块框图(可点击): 以下是SPI时序图,如下所示(可单击): 下面是我为实现上述SPI通信而编写的VHDL代码:VHDL模拟失败,结果意外,vhdl,fpga,hdl,spartan,xilinx-ise,Vhdl,Fpga,Hdl,Spartan,Xilinx Ise,5年前我学习了VHDL,此后我就再也没有使用过,因为我在不同的领域工作。现在我在一个项目中工作,这个项目需要一些VHDL语言的工作。我必须实现SPI来编程ADF4158设备。我打开一本语法书,试着编写程序。我需要开发下面的模块,我按照我的理解编写代码,还有一个测试台,但它在模拟中无法像我需要的那样工作。下面是我想开发的模块和总体框图 过程和vhdl模块框图(可点击): 以下是SPI时序图,如下所示(可单击): 下面是我为实现上述SPI通信而编写的VHDL代码: library IEEE; u
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;
IEEE库;
使用IEEE.STD_LOGIC_1164.ALL;
使用IEEE.STD_LOGIC_ARITH.ALL;
使用IEEE.STD_LOGIC_UNSIGNED.ALL;
使用IEEE.NUMERIC_STD.all;
实体发送方是
通用(数据宽度:整数=32)--32位寄存器到pogram(总共8个寄存器到程序)
端口(系统时钟:在标准逻辑中——来自微LAZE的时钟(32 MHz)
启用:在STD_逻辑中;--从微脉冲到触发启动
复位:在标准逻辑中;--复位spi时钟生成
start_data_read:out STD_LOGIC;--以microblaze为标志,以便microblaze开始发送寄存器选择代码(目标寄存器的PGGRAM数据)和数据到pogram-到缓冲区
start_data_write:out STD_LOGIC;--以microblaze为标志,以便microblaze开始发送寄存器选择代码(目标寄存器的pgogram数据)和pogram的数据-至mosi端口
数据输入:标准逻辑向量(数据宽度-1到0);--程序数据(8相32位数据,取决于寄存器选择)
过程完成:out STD_LOGIC;--完成过程时微博客的标志位
寄存器选择:在标准逻辑向量(2到0)中——选择代码以标识要生成的寄存器,可以从microblaze中生成该寄存器
sclk:缓冲器标准逻辑——用于ADC的spi时钟(3.2MHz)
ss:out标准逻辑:='1';--为ADF(从)选择线路
mosi:输出标准(U逻辑)--ADF的程序数据
端发送器;
发送方的行为体系结构是
机器类型为(存储、发送)--状态机状态
信号状态:机器:=存储;
信号时钟除法:标准逻辑向量(5到0)--来自microblaze的系统时钟与ADF的spi时钟之间的时钟周期比
信号发送缓冲区0:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器0)
信号发送缓冲区1:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器1)
信号发送缓冲区2:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器2)
信号发送缓冲区3:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器3)
信号发送缓冲区4:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器4)
信号发送缓冲区5:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器5)
信号发送缓冲区6:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器6)
信号发送缓冲区7:标准逻辑向量(数据宽度-1向下至0)--将程序数据保存到mosi的IO缓冲区(寄存器7)
开始
时钟发电机:过程(系统时钟)
开始--spi sclk 20:1系统时钟周期(系统以32 MHz运行,所需spi时钟为1.6 MHz)
如果上升沿(系统时钟),则
如果重置='1',则
clk_除以“0”);
mosi你的意思是在这个模拟中进行数据读取和数据写入吗?在RTL中,我们可以进行如下检查,因为我们查看了很多次(可能是在时钟边缘):
但是,您的测试台过程不会循环。因此,与我们在RTL代码中所做的“如果”操作不同,您需要做一些事情使您的测试停止,直到该条件存在为止。因此,您的测试将如下所示:
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 until rising_edge(Clk) ;
wait until Clk = '1' ; -- assumes Clk is only '0' or '1' (safe for clocks)
一旦你的基础工作了,你可能会考虑把一个写或读操作封装到一个子程序中。
你可以缩进你的代码吗?谢谢PaBeles的建议。代码在4个空格编辑后不可读,对此表示抱歉。它现在被编辑了!您的代码有几个问题,包括死状态“发送”。下面是一个有问题的报告:将TX_Buffer设置为一个数组,您将清除许多case语句。您还将在两个单独的进程中驱动MOSI:将其重置操作移动到必须驱动它的进程中。这些虽然不能解决问题,但可以帮助清理代码。我试图让测试变得更好,但结果仍然不可理解。所以我想一步一步地解决这个问题:在一个单独的过程中生成SCLK类时钟计数器可以吗?或者我应该使用IP核心时钟向导来提供SPI时钟。谢谢Jim,非常感谢您的建议。我将按照你的建议重写测试台。时间真的很快,我会计算出数学上的意义。您认为,实际的VHDL代码是正确的吗?我使用左移位发送数据的方式?我没有检查您的RTL代码。也存在一些问题。请记住,VHDL仅用于捕获。你还得做详细的设计。我希望,一旦MicroBlaze写入寄存器文件,硬件就可以自动将值发送到SPI。您可能想做一个详细的框图,然后编写代码。仅使用数字标准,不使用标准逻辑。
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 until rising_edge(Clk) ;
wait until Clk = '1' ; -- assumes Clk is only '0' or '1' (safe for clocks)