为什么这段代码只能部分工作?
Why does this code work only partially?
此代码应该在按下一个按钮时递增计数器(输出到 LED),并在按下另一个按钮时递减计数器。它在递减时工作正常,但在递增时它会将 LED 更改为随机配置。
module LED_COUNTER( CLK_50M, LED, Positive, Negative );
input wire CLK_50M;
input wire Positive;
input wire Negative;
output reg[7:0] LED;
always@( posedge Positive or posedge Negative )
begin
if(Positive == 1)
LED <= LED + 1;
else
LED <= LED - 1;
end
endmodule
我正在使用这个开发板:http://www.ebay.com/itm/111621868286。引脚分配为:
连接:
交换按钮的引脚分配后,行为保持不变。
您没有去抖动电路或逻辑。机械开关会在物理上反弹很多,因此您的 50MHz 时钟会在输入信号上看到很多很多转换,从而导致不稳定的行为。
我忘了说您甚至没有在同步设计中使用 50MHz 时钟。相反,您正在异步寻找转换。
你在某处需要一个低通滤波器。要么在输入信号上使用模拟组件实现,要么作为硬件计数器实现。
首先,将设计更新为同步设计,其中状态仅在 CLK_50M
的上升沿发生变化,例如
always@( posedge CLK_50M)
begin
...
end
然后为两个开关输入添加de-bounce logic逻辑;参见 contact bounce。这可以通过您自己编写的一个小模块来完成;这是一个很好的练习。
然后可以使用触点去抖动逻辑的输出进行变化检测,每次按下触点时进行一次循环指示,然后可以使用该指示更新计数器。
正如其他人已经指出的那样,您应该使用 CLK_50M
计时并且您应该对输入进行去抖动(一些 FPGA 会自动为您完成,请查看您的手册)。
您看到部分功能的原因是合成器解释 RTL 的方式。如果灵敏度列表是边沿触发的并且该信号在 always 块的主体中被引用,那么合成器将认为它是一个异步电平敏感信号。这用于异步复位和设置(有时称为清除和预设)。 ASIC 设计通常在大多数设计中使用具有异步复位功能的触发器。 FPGA 往往具有有限数量的异步置位或复位触发器,因此请查看您的手册并谨慎使用。
使用您的代码,Negative
是时钟,Positive
被视为高电平有效异步输入。
将代码修改为如下所示的等效功能行为(在仿真中),然后 Positive
将成为时钟,Negative
将成为高电平有效异步输入。
always@( posedge Positive or posedge Negative )
begin
if(Negative == 1)
LED <= LED - 1;
else
LED <= LED + 1;
end
网上有一些学习 Verilog 的资源(使用你最喜欢的搜索引擎),我在 my profile 上发布了一些资源,但更多的是针对 SystemVerilog。
以下是一些伪代码,可为您指明项目的正确方向:
always @(posedge CLK_50M)
begin
past_Positive <= Positive;
// ...
case({/* ... , */ past_Positive,Positive})
4'b0001 : LED <= LED + 1;
4'b0100 : LED <= LED - 1;
// ...
endcase
end
此代码应该在按下一个按钮时递增计数器(输出到 LED),并在按下另一个按钮时递减计数器。它在递减时工作正常,但在递增时它会将 LED 更改为随机配置。
module LED_COUNTER( CLK_50M, LED, Positive, Negative );
input wire CLK_50M;
input wire Positive;
input wire Negative;
output reg[7:0] LED;
always@( posedge Positive or posedge Negative )
begin
if(Positive == 1)
LED <= LED + 1;
else
LED <= LED - 1;
end
endmodule
我正在使用这个开发板:http://www.ebay.com/itm/111621868286。引脚分配为:
连接:
交换按钮的引脚分配后,行为保持不变。
您没有去抖动电路或逻辑。机械开关会在物理上反弹很多,因此您的 50MHz 时钟会在输入信号上看到很多很多转换,从而导致不稳定的行为。
我忘了说您甚至没有在同步设计中使用 50MHz 时钟。相反,您正在异步寻找转换。
你在某处需要一个低通滤波器。要么在输入信号上使用模拟组件实现,要么作为硬件计数器实现。
首先,将设计更新为同步设计,其中状态仅在 CLK_50M
的上升沿发生变化,例如
always@( posedge CLK_50M)
begin
...
end
然后为两个开关输入添加de-bounce logic逻辑;参见 contact bounce。这可以通过您自己编写的一个小模块来完成;这是一个很好的练习。
然后可以使用触点去抖动逻辑的输出进行变化检测,每次按下触点时进行一次循环指示,然后可以使用该指示更新计数器。
正如其他人已经指出的那样,您应该使用 CLK_50M
计时并且您应该对输入进行去抖动(一些 FPGA 会自动为您完成,请查看您的手册)。
您看到部分功能的原因是合成器解释 RTL 的方式。如果灵敏度列表是边沿触发的并且该信号在 always 块的主体中被引用,那么合成器将认为它是一个异步电平敏感信号。这用于异步复位和设置(有时称为清除和预设)。 ASIC 设计通常在大多数设计中使用具有异步复位功能的触发器。 FPGA 往往具有有限数量的异步置位或复位触发器,因此请查看您的手册并谨慎使用。
使用您的代码,Negative
是时钟,Positive
被视为高电平有效异步输入。
将代码修改为如下所示的等效功能行为(在仿真中),然后 Positive
将成为时钟,Negative
将成为高电平有效异步输入。
always@( posedge Positive or posedge Negative )
begin
if(Negative == 1)
LED <= LED - 1;
else
LED <= LED + 1;
end
网上有一些学习 Verilog 的资源(使用你最喜欢的搜索引擎),我在 my profile 上发布了一些资源,但更多的是针对 SystemVerilog。
以下是一些伪代码,可为您指明项目的正确方向:
always @(posedge CLK_50M)
begin
past_Positive <= Positive;
// ...
case({/* ... , */ past_Positive,Positive})
4'b0001 : LED <= LED + 1;
4'b0100 : LED <= LED - 1;
// ...
endcase
end