我对 HDL 语言非常陌生。我有一个关于如何对移位寄存器进行编程的问题。 (我知道我转向另一个方向)。为什么这本书用
wire[N-1:0] r_next
?我的实现有什么缺点?
谢谢
我的第一次尝试如下
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
:
else begin
r_reg[0]=data;
r_reg = r_reg<<1;
end
如果没有这个,只有
r_reg[0]=data
将出现在else
语句的if
子句中。这可行,但由于顺序逻辑描述中的阻塞语句而被认为是不好的风格......
其次,对于顺序块建模,请使用非阻塞分配(
<=
),否则您的计算可能会“失败”(谷歌非阻塞与阻塞以获取更多信息)。您的示例可能非常有效(您在模拟器中尝试过吗?),但是如果事情变得更加复杂并且添加了更多变量,事情可能会崩溃。
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
重置),则应如此命名,例如 resetb
或 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。
如果您使用不错的 linting 和综合工具,您的代码可能会收到一堆警告。 这会提醒您进行一些更改。