我在Verilog中实现了一个简单的序列化程序,但我不明白阻塞分配可能导致问题的细微差别。我特别难以理解this answer的一部分。 “但是,你不应该使用阻塞分配进行同步通信,因为这是不确定的。”
我正在构建一个块作为输入:
作为输出,我有:
每当数据有效变为高电平时,块开始从位时钟的下一个上升沿开始一次一位地输出5位值。当最后一位在线上时,该块信号“完成”,因此可以使用新的5位值。
省略一些重置逻辑,执行此操作的代码如下所示:
always @ (posedge clk) begin
if(shiftIndex == 0) begin
if(dataValid == 1) transmitting = 1; //Blocking assign
else transmitting = 0; //Blocking assign
end
//Need the blocking assign up above to get this part to run
//for the 1st bit
if(transmitting == 1) begin
shiftIndex <= shiftIndex + 1;
dataOut <= data5b[shiftIndex];
if(shiftIndex == 4) begin
complete <= 1;
shiftIndex <= 0;
end
else begin
complete <= 0;
end
end
end
现在,我可以使用所有非阻塞分配编写块,但我觉得它会损害可读性。这看起来像这样:
always @ (posedge clk) begin
if(shiftIndex == 0) begin
if(dataValid == 1) begin
transmitting <= 1; //Non-blocking now
shiftIndex <= shiftIndex + 1; //Duplicated code
dataOut <= data5b[shiftIndex]; //Duplicated code
complete <= 0; //Duplicated code
end
else transmitting <= 0;
end
//Now, this only runs for the 2nd, 3rd, 4th, and 5th bit.
else if(transmitting == 1) begin
shiftIndex <= shiftIndex + 1;
dataOut <= data5b[shiftIndex];
if(shiftIndex == 4) begin
complete <= 1;
shiftIndex <= 0;
end
else begin
complete <= 0;
end
end
end
两者似乎都在模拟中做我想做的事情,我赞成第一个,因为我更容易阅读,但因为我不明白为什么使用阻塞分配同步通信是不确定的,我担心我'编码了定时炸弹
问题:我在第一段代码中做错了什么,当我尝试合成时,它会爆炸吗?第二个代码是否更好,尽管有点难(对我来说无论如何)阅读?我应该做些第三件事吗?
答案 0 :(得分:4)
使用阻止(=
)赋值时,可以在下一行代码中使用该值。这意味着它是组合的而不是从触发器驱动的。
在仿真中看起来它是从触发器驱动的,因为该块仅在正时钟边缘上进行评估,实际上它不会破坏接口。
我是一个从不混合样式的派系,因为它可能是代码审查和重构中的问题。重构,如果一个模块需要输出一个新信号,并且看到它已经存在,它们只是改为输出。乍一看看起来像是一个触发器,因为它在always @(posedge clk
块中。
因此我建议不要混合样式,但是拉出组合部分并将其放在自己的块中。这仍然符合您的要求吗?如果不是那么你会遇到问题。
我没有看到数据有效是如何被控制的,但它可以改变输出传输,潜在的传输也可能因为来自组合解码而出现故障,而不是从触发器干净地驱动。接收接口可能是异步的,故障可能导致锁定等。
always @* begin
if(shiftIndex == 0) begin
if(dataValid == 1) transmitting = 1; //Blocking assign
else transmitting = 0; //Blocking assign
end
end
always @ (posedge clk) begin
if(transmitting == 1) begin
shiftIndex <= shiftIndex + 1;
dataOut <= data5b[shiftIndex];
if(shiftIndex == 4) begin
complete <= 1;
shiftIndex <= 0;
end
else begin
complete <= 0;
end
end
end
答案 1 :(得分:2)
就正确性而言,混合阻塞和非阻塞分配没有问题,但您需要清楚地了解哪个信号是顺序的,哪个信号是组合块(请注意该组合的输入)块来自其他顺序块或主要输入)。此外,您需要决定是否要在设计中使用任何锁存器。
如果您对变量使用阻塞分配并不意味着顺序,请确保始终分配给它,否则,它可能会被解释为顺序元素。< / p>
在第一段代码中,您transmitting
时无法分配到(shiftIndex != 0)
。这意味着在transmitting
时应使用(shiftIndex != 0)
的先前值,因此它将是一个顺序元素。但是你需要在当前时钟中使用它的值,因此你使用了阻塞赋值。
以下是您的代码的另一个版本,其中第一位使用firstBit_comb
并始终分配给。
always @ (posedge clk) begin
//Default value to avoid sequentials. Will be overwritten later if necessary
firstBit_comb = 0;
if(shiftIndex == 0) begin
if(dataValid == 1) begin
transmitting_seq <= 1;
firstBit_comb = 1;
end
else begin
transmitting_seq <= 0;
firstBit_comb = 1;
end
end
//Need the blocking assign up above to get this part to run
//for the 1st bit
if(firstBit_comb || transmitting_seq) begin
shiftIndex <= shiftIndex + 1;
dataOut <= data5b[shiftIndex];
if(shiftIndex == 4) begin
complete <= 1;
shiftIndex <= 0;
end
else begin
complete <= 0;
end
end
end
然而,如果将序列块和组合块分开,则更清楚。请注意,顺序元素的下一个状态通常是组合块的输出。
//combinational block
always_comb
begin
//The default value of next state is the previous state
transmitting_next = transmitting_seq;
//The default value of firstBit_comb=0. It would be overwritten if necessary
firstBit_comb = 0;
if(shiftIndex == 0 && dataValid == 1) begin
firstBit_comb = 1;
transmitting_next = 1;
end
else begin
firstBit_comb = 0;
transmitting_next = 0;
end
end
//Sequential block
always @ (posedge clk) begin
//update transmitting_seq with its next state
transmitting_seq <= transmitting_next;
if(firstBit_comb || transmitting_seq) begin
shiftIndex <= shiftIndex + 1;
dataOut <= data5b[shiftIndex];
if(shiftIndex == 4) begin
complete <= 1;
shiftIndex <= 0;
end
else begin
complete <= 0;
end
end
end