我在工作中继承了一些传统的VHDL,一直在试图弄清楚问题。首先让我说我对Verilog更有经验但是已经花了一段时间用VHDL来解决这个问题。
因此,我们的产品自2011年以来一直运行良好。但是,我们需要更改FPGA,因此我们正在移植代码。该代码的高级视图是FPGA在板载闪存中存储CPU的引导加载程序。上电时,CPU读取FPGA以提取引导加载程序。在我们的旧设计上它运作良好。在新设计中,它在寒冷时不起作用,在温暖的温度下也不是100%可靠。
我注意到很多代码完成了我认为奇怪的事情,其中触发器用信号而不是时钟来计时。一个例子:
process( N_RESET, DATA_READ, START_ADDRESS, END_ADDRESS )
begin
if( N_RESET = '0' ) then
ADDR_TMP <= START_ADDRESS;
elsif( DATA_READ'event and (DATA_READ='0') ) then
if( ADDR_TMP <= END_ADDRESS ) then
ADDR_TMP <= ADDR_TMP + "10";
incremented_tmp <= incremented_tmp xor '1';
end if;
end if;
end process;
因此,生成的RTL最终成为由DATA_READ计时的触发器。数据读取是来自CPU的读取使能信号。 CPU正在尝试读取该寄存器。
这是一种有效的做事方式吗?我真的不喜欢自己使用read enable作为时钟的想法。我在Verilog中写了一个使用时钟来检测DATA_READ的上升沿:
always@(posedge CLK, negedge RESETn )
begin
if(RESETn == 1'b0) begin
end else begin
last_usr_read_n <= usr_read;
// Next state logic, only transition on de-assertion of usr_read
if (last_usr_read_n == 1'd0 && usr_read == 1'd1) begin
// check next state logic
envm_internal_address <= envm_internal_address + 18'h2; // incrmeent address counter
incremented<= incremented^ 1'b1;
end
endmodule
我注意到在执行期间VHDL停止更改incremented_tmp,而我的verilog代码继续按预期执行。
我们遇到的主要问题是设计在寒冷时不起作用。我不知道为什么。设计中有一堆闩锁,我已尽力去除。剩下的就是这样的代码(并且有很多代码)。我的问题是:这是一个为触发器计时的有效方法吗?它会引起我们所看到的意外问题吗?
谢谢, 尼克
答案 0 :(得分:2)
VHDL代码在DATA_READ信号上创建一个生成的时钟,与Verilog代码的等效代码不同。虽然这肯定是有效的&#34;从某种意义上说,它是可综合的,如果不是适当的约束,这可能会产生时间问题和边缘行为,就像你在寒冷和炎热时看到的那样。首选解决方案是使用同步边沿检测电路替换生成的时钟,就像您在Verilog代码中所做的那样。
不幸的是,如果您需要清理大量代码,我不知道自动执行此操作的好方法。如果代码遵循这些构造的相同通用结构,则可以尝试搜索和替换。我建议开发一个可靠的回归测试套件(如果您还没有),以验证您的更改是否有任何不受欢迎的副作用。
或者,您可以尝试在布局布线工具中识别和约束所有生成的时钟,并注意在时钟域交叉处保持时间。特别是如果在同一时钟边沿上启动END_ADDRESS和DATA_READ,则在需要通过添加延迟来解决的信号之间会出现竞争条件。但是,如果您计划将来重复使用此代码,那么最好不要在RTL中修复此代码。
答案 1 :(得分:0)
代码混乱 - 转储它,重新开始。关于它的一切都是一面红旗。具体问题:
DATA_READ
的CPU数据表 - 如果它不能保证单调,则它不能用作时钟。它可能只是保证了相对于其他信号的设置和保持,因此它可以在其他时间执行任何操作,可能为您的代码提供时钟。DATA_READ
分配给时钟网incremented_tmp
在时钟分支中分配,但不在异步重置分支中分配。这是一个问题 - 它必须在N_RESET
为低并且有时钟优势时保持,因此您已经获得了锁存行为,并且问题与N_RESET
相关的时间有关。 DATA_READ
。