【问题标题】:Shortests version to choose posedge/negedge sensitivity from module parameter?从模块参数中选择 posedge/negedge 灵敏度的最短版本?
【发布时间】:2018-04-24 09:45:33
【问题描述】:

我想构建一个 Verilog 模块,以便用户可以通过模块参数选择某些输入时钟信号的灵敏度。例如,我编写了以下计数器,它可以对参数clockEdge 选择的 posedge 或 negedge 进行计数。

module Counter (clk, reset, value);
parameter clockEdge = 1; // react to rising edge by default

input clk;
input reset;
output reg [7:0] value;

generate
    if(clockEdge == 1) begin
        always @(posedge clk or posedge reset) begin
            if (reset) begin
                value <= 0;
            end else begin
                value <= value + 1;
            end
        end
    end else begin
        always @(negedge clk or posedge reset) begin
            if (reset) begin
                value <= 0;
            end else begin
                value <= value + 1;
            end
        end
    end
endgenerate

endmodule

这个原则是有效的,但是我不喜欢这两种变体的实现基本上都是复制和粘贴。什么是没有重复的较短版本?

【问题讨论】:

    标签: verilog


    【解决方案1】:

    最简单的方法是用异或门反转时钟。然后在 always 部分使用该时钟:

    wire user_clock;
    assign user_clock = clk ^ falling_edge;
    
    always @(posedge user_clock) // or posedge/negedge reset/_n etc.
       test_signal <= ~ test_signal;
    

    如果 falling_edge 为 0,则 user_clock 和 clk 具有相同的极性,并且数据的时钟几乎与 clk 的上升沿相同。
    如果falling_edge 为 1,则 user_clock 和 clk 具有相反的极性,并且数据的时钟几乎与 clk 的下降沿相同。

    更改falling_edge 的极性时要小心,因为它会产生短时钟脉冲!最安全的是使用门控时钟:停止时钟,切换极性,然后重新启动。

    在这两种情况下,user_clock 都会稍微滞后系统时钟。数量取决于出口门的延迟。 这是一个模拟+

    +test_signal 在初始语句中设置为零。

    【讨论】:

    • 正如我的问题所说,时钟选择由模块参数控制。所以我要求一个解决方案,它在编译时控制这种行为,而不是作为电路的一部分。因此,额外的门是不可接受的,尽管它可能会被综合优化掉。
    • 您所要做的就是将falling_edge 设为一个参数。门将被优化掉。如果您在设计中使用 posedge 和 negedge,您将永远不知道是否添加了门延迟。例如您的库可能只有 posedge FF,在这种情况下,您最终会在时钟线或正负时钟树中使用反相器。在所有情况下,综合工具都会意识到这一点并调整您的时序。如果您的设计无法复制反相器的时钟偏差,那么您就有一个大问题。
    【解决方案2】:

    我认为这个小例子中的剪切粘贴是可以的,尽管 verilog 有一些工具可以更容易地处理更复杂的情况,即函数和宏。您也可以使用它们,即

     function newValue(input reset, input reg[7:0] oldValue);
        if (reset) begin
           newValue = 0;
        end else begin
           newValue = value + 1;
        end
     endfunction
    
     generate
        if(clockEdge == 1) begin
           always @(posedge clk or posedge reset) begin
              value <= newValue(reset, value);
           end
        end else begin
           always @(negedge clk or posedge reset) begin
              value <= newValue(reset, value);
           end
        end
     endgenerate
    

    另一种可能性是使用带或不带参数的宏。方法方面的宏通常比函数更糟糕,尽管这是一个极端的例子,但在我看来,它使代码的可读性降低,并且可能在调试工具方面出现问题。

    `define NEW_VALUE(clk_edge) \
       always @(clk_edge clk or posedge reset) begin\
          if (reset) begin\
             value <= 0;\
          end else begin\
              value <= value + 1;\
           end\
       end
    
         generate
            if(clockEdge == 1) begin
               `NEW_VALUE(posedge)
            end else begin
               `NEW_VALUE(negedge)
            end
         endgenerate
    

    【讨论】:

      【解决方案3】:

      实现遵循 clk 的 posedge 或 negedge 的自定义时钟 reg 可能是一种方法。这似乎在我的机器上运行良好:)

      reg myclk;
      always@(clk) begin
          if(clk) begin
              myclk = clockEdge? 1 : 0; #1 myclk = 0;
          end else begin
              myclk = clockEdge? 0 : 1; #1 myclk = 0;
          end
      end     
      
      always@(posedge myclk or posedge reset) begin
          if(reset)
              cnt <= 0;
          else
              cnt <= cnt+1;
      end
      

      【讨论】:

      • 您的回答暗示使用门控时钟。这意味着时钟路径会增加额外的传播延迟,这在时序方面是有问题的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多