我是 fpga 的新手。 我想制作每次 SCK 看到上升沿时迭代的计数器。 我对代码遇到的问题是它似乎计数两次。 每次出现上升沿过渡时,两个 LED 点亮 - 而不是只有一个 LED。 知道这可能来自哪里吗?
module spi_slave(pcEn, LED, clk, SCK);
input clk, SCK;
output reg pcEn;
output reg [7:0] LED = 8'h00;
reg r1 = 0;
reg r2 = 0;
reg r3 = 0;
reg [3:0] cnt = 4'b0000;
always @(posedge clk)
begin
r1 <= SCK;
r2 <= r1;
pcEn <= r1 && !r3;
if (pcEn == 1) begin
cnt = cnt + 4'b0001;
if (cnt == 4'b0001) begin
LED[0] = 1'b1;
end
else if (cnt == 4'b0010) begin
LED[1] = 1'b1;
end
else if (cnt == 4'b0011) begin
LED[2] = 1'b1;
end
else if (cnt == 4'b0100) begin
LED[3] = 1'b1;
end
else if (cnt == 4'b0101) begin
LED[4] = 1'b1;
end
else if (cnt == 4'b0110) begin
LED[5] = 1'b1;
end
else if (cnt == 4'b0111) begin
LED[6] = 1'b1;
end
else if (cnt == 4'b1000) begin
LED[7] = 1'b1;
end
else
LED = 8'h00;
end
else
#100;
r3 <= r2;
end
endmodule
计数器正在计数两次,因为您正在比较r1 & !r3
。
R1->R2->R3 .在 R1 等于 1 之后设置 R3 需要 2 个时钟。这意味着r1&!r3
条件将在 2 个时钟内保持有效。pcEn将生成2个时钟,因此计数器将计数两次。
r1 && !r2
或者如果你想要延迟r2 && !r3
应该工作正常。
您应该能够在要调试的波形中看到此行为。在仿真中使用$dumpvars;
查看波形。
还有一些更改来改进代码。
- 使用重置。
- 始终使用非阻塞分配。 不需要
#100延迟。
module spi_slave(pcEn, LED, clk, SCK,rst_n); input clk, SCK,rst_n; output reg pcEn; output reg [7:0] LED ; reg r1 ; reg r2 ; reg r3 ; reg [3:0] cnt ; always @(posedge clk or negedge rst_n) begin if ( rst_n == 0 ) begin r1 <=0 ; r2 <= 0 ; r3 <= 0 ; cnt <= 0 ; LED <=0 ; pcEn <=0 ; end else begin r1 <= SCK; r2 <= r1; r3 <= r2; pcEn <= r2 && !r3; if (pcEn == 1) begin cnt <= cnt + 4'b0001; if (cnt == 4'b0001) begin LED[0] <= 1'b1; end else if (cnt == 4'b0010) begin LED[1] <= 1'b1; end else if (cnt == 4'b0011) begin LED[2] <= 1'b1; end else if (cnt == 4'b0100) begin LED[3] <= 1'b1; end else if (cnt == 4'b0101) begin LED[4] <= 1'b1; end else if (cnt == 4'b0110) begin LED[5] <= 1'b1; end else if (cnt == 4'b0111) begin LED[6] <= 1'b1; end else if (cnt == 4'b1000) begin LED[7] <= 1'b1; end else LED <= 8'h00; end end end endmodule
首先#
延迟是不可合成的,它们只是用于模拟的延迟。
通常被认为是将块和非阻塞逻辑分离到不同的始终块中的最佳实践。 always @*
用于组合(阻塞分配),always @(posedge clk)
用于顺序(非阻塞分配)。仅供参考:Verilog支持大小写语句,这使得编码值比较比嵌套else-if更容易。
我想你可能想使用r2 && !r3
而不是r1 && !r3
正如拉胡尔还指出的那样
always @* begin
if (pcEn == 1'b0) begin
next_cnt = cnt;
next_LED = LED;
else begin
next_cnt = cnt + 4'b0001;
next_LED = 8'h00; // Rest all to 0s
if(cnt >= 8'h8) next_cnt = 4'b0000; // optional : assuming you want to roll back before waiting another 8 SCK toggles
case(cnt)
4'b0000 : next_LED[0] = 1'b1;
4'b0001 : next_LED[1] = 1'b1;
// ...
4'b0111 : next_LED[7] = 1'b1;
endcase
end
end
always @(posedge clk) begin
r1 <= SCK;
r2 <= r1;
r3 <= r2;
pcEn <= r2 && !r3;
cnt <= next_cnt;
LED <= next_LED;
end