如何在 Verilog 中重用多个 always 块

How to reuse multiple always blocks in Verilog

下面是始终块代码。

我需要使用相同的代码 11 次,功能相同,但变量不同。那么如何重用代码呢?

  always @(posedge tconClk or negedge tconRst_n)
  begin
    if(~tconRst_n)
    begin
      pulse_cnt <= 0;
      pulse_start = 0;
      start_written = 0;
      pulse_width <= 'h271;
    end
    else if(~pulse_rst)
    begin
      pulse_cnt <= 0;
      pulse_start = 0;
    end
    else
    begin
      if(start_signal)
      begin
//        start_written = 0;
        pulse_width <= (pulse_start) ? pulse_width : START_PW;
        pulse_start = 1;
      end
      pulse_cnt <= (pulse_start) ? (pulse_cnt + 1) : pulse_cnt;
    end
  end

命名模式 -

备注 -

  1. 定义宏将不起作用,因为此代码包含许多 verilog 标记。

  2. 我无法制作代码模块,因为 always 块中使用的信号也用于代码的其他部分。因此,如果我制作模块,那么我将无法确保与模块的正确连接或电线连接。 (就像模块输出端口必须是电线,但在代码的其他部分已将相同的信号用作 reg)

always 放在 generate 语句中。你应该在 SO 上找到很多例子:例如 here。您仍然需要修改您的代码,这可能并不比修改它以进行模块实例化更容易。

您的代码目前已损坏,因为 pulse_rst 在时钟边沿之前进行了测试,并且不在敏感列表中。你应该把它放在列表中,或者重新编码块。请注意,Verilog 中存在两个异步控件和一个时钟的问题;查找 "verilog flops with async set and reset",或用不同的问题再次提问。

我觉得你还是可以用宏的。把那些不常用的变量改成宏,比如把pulse_cnt改成`pulse_cnt。将它们放入文件中并将其用作包含文件。

例如你的代码,

always @(...) 
begin
  `pulse_cnt <= 0;
  // ...
end

将此模板放入一个名为 my_always.v

的文件中

然后在您的另一个 file/module 中重用代码,如下所示:

`define pulse_cnt pulse_cnt0
`include "my_always.v"
`undef pulse_cnt

`define pulse_cnt pulse_cnt1
`include "my_always.v"
`undef pulse_cnt

//... and so on

您可以使用 emacs 脚本 verilog-mode,将 RTL 封装在模块中并利用 AUTO_TEMPLATE。类似下面的东西(未测试)然后在(或一批)emacs 中执行 verilog-batch-auto

/* PulseModule AUTO_TEMPLATE "\([0-9]+\)$" (
     .pulse_cnt(pulse_cnt@),
     .pulse_width(pulse_width@),
     .pulse_start(pulse_start[@]),
     .start_written(start_written[@]),
     .pulse_rst(pulse_rst[@]),
     .start_signal(start_signal[@]),
     .tconClk(tconClk),
     .tconRst_n(tconRst_n)
    );
*/
PulseModule pm_0 (/*AUTOINST*/ .start_pulse_width(START_PW) );
PulseModule pm_1 (/*AUTOINST*/ .start_pulse_width(OTHER_START_PW) );
...
PulseModule pm_10 (/*AUTOINST*/ .start_pulse_width(SOME_OTHER_START_PW) );

还有各种嵌入式代码(如Perl的EP3, Ruby's eRuby/ruby_it, Python's prepro等)可以生成需要的代码

SystemVerilog 增强了宏的功能。对您来说,`` 功能将使您的任务更轻松。参见 IEEE Std 1800-2012 § 22.5.1 `define.

多行宏很难调试。尽管可以将您的 RTL 放在一个宏中,但我强烈建议将其放在一个模块中并让宏实例化宏。以下内容(未测试):

`define PULSEMACRO(id,val) \
   PulseModule pm_``id( \
     .pulse_cnt(pulse_cnt``id), .pulse_width(pulse_width``id), \
     .pulse_start(pulse_start[id]), .start_written(start_written[id]), \
     .pulse_rst(pulse_rst[id]), .start_signal(start_signal[id]), \
     .start_pulse_width(val) \
     .tconClk(tconClk), .tconRst_n(tconRst_n) )

实例化如下。请注意,生成 for 循环将不起作用。在生成块之前评估宏。

`PULSEMACRO(0,START_PW);
`PULSEMACRO(1,OTHER_START_PW);
...
`PULSEMACRO(10,SOME_OTHER_START_PW);

SystemVerilog 还可以通过模块端口传递多维数组。因此,您可以将 reg [PULSE_CNT_WIDTH-1:0] pulse_cnt0,...,pulse_cnt10 重命名为 logic [PULSE_CNT_WIDTH-1:0] pulse_cnt [11]。通过此转换,您可以使用生成循环。


或者,您可以将 pulse_cnt 折叠成一个大总线 reg [PULSE_CNT_WIDTH*11-1:0] pulse_cnt,然后使用位分片进行索引 pulse_cnt[PULSE_CNT_WIDTH*index +: PULSE_CNT_WIDTH]。位分片也与 Verilog 兼容。参见 What is `+:` and `-:`? and Indexing vectors and arrays with +: