如何在 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
命名模式 -
tconClk
,tconRst_n
常见,
pulse_cnt0
、pulse_cnt1
最多 10
、
pulse_width0
、pulse_width1
最多 10
、
pulse_start[0:10]
(数组),
start_written[0:10]
(数组),
pulse_rst[0:10]
(数组),
start_signal[0:10]
(数组),
START_PW
(无模式,每 11 个始终块的不同名称)
备注 -
定义宏将不起作用,因为此代码包含许多 verilog 标记。
我无法制作代码模块,因为 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 +:
下面是始终块代码。
我需要使用相同的代码 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
命名模式 -
tconClk
,tconRst_n
常见,pulse_cnt0
、pulse_cnt1
最多10
、pulse_width0
、pulse_width1
最多10
、pulse_start[0:10]
(数组),start_written[0:10]
(数组),pulse_rst[0:10]
(数组),start_signal[0:10]
(数组),START_PW
(无模式,每 11 个始终块的不同名称)
备注 -
定义宏将不起作用,因为此代码包含许多 verilog 标记。
我无法制作代码模块,因为 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 +: