移位寄存器Verilog
我对HDL语言很陌生。我有一个关于如何编程移位寄存器的问题。 (我知道我转向另一个方向)。为什么这本书使用wire[N-1:0] r_next
?我的实施有什么缺点? 感谢移位寄存器Verilog
我第一次尝试是如下
module lesson04#(parameter N=8)(
input wire clk, reset,
input wire data,
output wire out
);
reg [N-1: 0] r_reg;
always @(posedge clk or negedge reset)
begin
if(!reset)
r_reg =0;
else
r_reg[0]=data;
r_reg = r_reg<<1;
end
assign out =r_reg[N-1];
endmodule
但书中给出:
module lesson04#(parameter N=8)(
input wire clk, reset,
input wire data,
output wire out
);
reg [N-1: 0] r_reg;
wire[N-1:0] r_next;
always @(posedge clk or negedge reset)
begin
if(!reset)
r_reg =0;
else
r_reg <= r_next;
end
assign r_next= {data, r_reg[N-1:1]};
assign out =r_reg[N-1];
endmodule
首先,不要忘记你的begin
- 周围的代码段end
S:
else begin
r_reg[0]=data;
r_reg = r_reg<<1;
end
没有这个,onl y r_reg[0]=data
将在if
声明的else
条款中。这可以工作,但由于顺序逻辑描述中的阻塞语句而被认为是不好的样式...其次,对于顺序块的建模,使用非阻塞赋值(<=
)或者您的计算可能会“跌倒”(谷歌非阻塞与阻止更多信息)。你的例子可能工作得很好(你是否在模拟器中尝试过)?但是如果事情变得更加复杂,并且添加更多变量,事情可能会破坏。
always @(posedge clk or negedge reset)
begin
if(!reset)
r_reg <= 0;
else begin // This is horrible! Don't write code like this!
r_reg[0] = data; // blocking
r_reg <= r_reg<<1; // non-blocking
end
end
由于上述原因,它有时被建议组合逻辑被从时序逻辑分开,这样就可以写非阻塞赋值给在连续块的寄存器,并且在组合块阻塞和从来不用担心调度。
要以这种方式进行编码,您需要计算下一个输出应该使用当前状态,因此在答案中使用r_next
总线。如果所有触发器都以这种方式与周围的组合逻辑分离,我认为它也倾向于帮助综合工具。另外,如果您的重置为低电平有效(例如LOW
重置),则应将其命名为或reset_n
。
你的实现产生了与本书完全不同的输出。您应该通过构建一个简单的测试台来驱动您的输入并运行仿真来证明这一点。您将看到,本书的输出将输入数据移动一个时钟周期,而您的输出通过八个时钟周期移动输入数据。
顺便说一句,你缩进了你的always
块,我被引导认为它不是你想要的。这是你的块是如何真正的行为:
always @(posedge clk or negedge reset)
begin
if(!reset) begin
r_reg =0;
end else begin
r_reg[0]=data;
end
r_reg = r_reg<<1;
end
我总是明确地使用begin/end
关键字if/else
语句来避免这种混乱。
它的模拟方式r_reg
始终为0,因为您将第一个作业(r_reg[0]=data;
)与第二个作业(r_reg = r_reg<<1;
)打成一体。另一个区别是,本书将data
分配给移位寄存器的MSB,但将其分配给LSB。
如果你使用体面的短线和综合工具,你可能会得到一堆警告代码。这会提醒您进行一些更改。
'if/else's中的'begin/end'很快成为反射! – Marty 2010-08-19 13:28:26
我同意。实际上,我甚至在编辑器中为此创建了一个宏。 – toolic 2010-08-19 13:30:20
那么这本书的答案是'错误',不是吗?如果只想延迟一个时钟周期,为什么会有8个触发器? ......除非是成绩单中的拼写错误(毕竟有copypasta证据).. – Marty 2010-08-19 18:09:42