为什么这段代码只能部分工作?

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