我知道Verilog只有2个月,3个月的时间。我对这一件事感到非常沮丧,如果它发生在其他一些非HDLanguage中,我会称之为竞争条件。以下是最简化形式的文件,描述了案例:
circuit.v
module circuit(f, x, clk, rst);
output f;
input x, clk, rst;
wire d, q;
d_flip_flop flipper (q, d, clk, rst);
assign f = q;
assign d = x;
endmodule
d_flip_flop.v
module d_flip_flop(q, d, clk, rst);
output reg q;
input d, clk, rst;
always @(posedge clk, posedge rst)
begin
if (rst)
q <= 1'b0;
else
q <= d;
end
endmodule
我用这个tester.v
module tester;
reg x, clk, rst;
wire f;
circuit uut (f, x, clk, rst);
initial clk <= 0;
always #10 clk <= ~clk;
initial
begin
x <= 0;
rst <= 0;
rst <= #2 1;
rst <= #4 0;
#10 x <= 1;
end
endmodule
这是我得到的波形:
我从波形中看到的是,Verilog已决定首先处理clk
中的更改,因此执行q <= d;
;然后才处理x
中的更改,从而执行assign d = x;
。
我所说的可能在硬件中甚至没有意义,但无论如何。
现在:我周围有人,所谓的讲座助理,更有经验,可能更有知识,声称正确编写的 Verilog顺序模块始终认为变化发生了,如时钟击中posedge。然而,即使在这个非常简单的例子中,我也认为它是(否则也许不一定)。
所以,问题是,我应该怎么做才能将其变成正确编写的 Verilog顺序模块?我怎么能让我的坏代码像其他人一样好?或者就是没有这样的事情,Verilog在这种情况下自豪地是不确定的?
答案 0 :(得分:0)
我在这种情况下经常看到的行为是:首先时钟从低到高,然后产生一个posedge事件。发生这种情况时,非阻塞分配中的右侧表达式将以其当前值读取。
我不能更具体地说明这一点,但我的假设是,如果右侧表达式在时钟生成posedge事件的同时发生变化,则读取的值不确定。真实世界的触发器需要一定的时间才能使输入信号在被认为有效(建立时间)之前稳定下来。如果不满足该建立时间,则触发器存储的值将是输入信号的先前值。因此,模拟器试图模仿这种行为。
正确设计的顺序系统应该仅在尚未发生有效时钟转换时才允许更改输入信号。例如:SPI协议规定数据信号必须在相反的时钟转换中更改为用于接受当前数据值的时钟转换。
数据(MOSI和MISO信号)在SCLK的下降沿发生变化。在SCLK的上升沿读取或写入数据。请注意,此时数据在两条数据线中都是稳定的,因此满足了建立和保持时间约束,并且当前值有效地存储在SPI使用的移位寄存器中。
这种描述的行为应该是一致的并且是完全确定的,因为它是许多已知硬件结构的基础,例如移位寄存器和计数器:
module shiftreg2bits (
input wire clk,
input wire i,
output wire [1:0] o
);
reg ff1 = 1'b0;
reg ff2 = 1'b0;
always @(posedge clk) begin
ff1 <= i;
ff2 <= ff1;
end
assign o = {ff1,ff2};
endmodule
开头时间#0
,o = 00
。
假设第一个i = 1
事件发生时clk
:
ff1
获取i
的 CURRENT 值,即1
ff2
获取ff1
的 CURRENT 值,即0
。 ff2
不会1
,因为ff1
存储刚刚读取的值1
的机制需要一些时间(模拟中的一个时间单位没有给出延迟,和一个特定的非零时间,取决于硬件的实际触发器,即所谓的“propagation delay”),因此当{{1}时ff1
的输出仍为0
事件发生。clk