動機
今天一個朋友問了這樣一個問題

我有一個朋友
失敗原因
首先介紹一下
generate
的用法,
generate
用于減少verilog的重複語句,批量進行操作。
雖然0202年了,綜合工具對于
for
的支援已經很好了,但是使用
generate
進行for循環,不僅可以實作普通的變量指派,還可以批量生成
assign
或者
always
語句,它的作用實際上和宏定義是一樣的,
直接将代碼展開
舉個例子,我有兩個數組
reg [7:0] a [7:0]
和
reg [7:0] b [7:0]
,我需要把他們對應元素相乘,那麼可以這麼做
genvar i;
reg [ 7:0] a [7:0];
reg [ 7:0] b [7:0];
wire [15:0] c [7:0];
generate
for(i;i<8;i++) begin
assign c[i] = a[i]*b[i];
end
endgenerate
複制
這種寫法是完全等價于下面這種寫法的
genvar i;
reg [ 7:0] a [7:0];
reg [ 7:0] b [7:0];
wire [15:0] c [7:0];
assign c[0] = a[0]*b[0];
assign c[1] = a[1]*b[1];
assign c[2] = a[2]*b[2];
assign c[3] = a[3]*b[3];
assign c[4] = a[4]*b[4];
assign c[5] = a[5]*b[5];
assign c[6] = a[6]*b[6];
assign c[7] = a[7]*b[7];
複制
如果在代碼并不多的情況下,利用插件,例如
sublime
的
insert num
,也可以快速實作
sublime
同樣的,
generate
也可以批量進行例化,例如
module adder(
input clk,rst_n,
input [2:0] a,b,
output [3:0] c
);
logic [3:0] c_f,c_ff;
always_ff @(posedge clk or negedge rst_n) begin : proc_adder
if(~rst_n) begin
c_f <= '0;
c_ff <= '0;
end else begin
c_f <= a+b;
c_ff <= c_f;
end
end
assign c=c_ff;
endmodule
module test (
input clk, // Clock
input rst_n, // Asynchronous reset active low
input [2:0] a [3:0],
input [2:0] b [3:0],
output [3:0] c [3:0]
);
genvar i;
generate
for (i = 0; i < 4; i++) begin
adder i_adder (.clk(clk), .rst_n(rst_n), .a(a[i]), .b(b[i]), .c(c[i]));
end
endgenerate
endmodule
複制
如果在仿真器中檢視子產品名,子產品會被自動進行編号
通過路徑
i_test.genblk1[3].i_adder.c_f
就能通路到對應的變量
// Module: tb
//
module tb();
logic clk,rst_n;
logic [2:0] a [3:0];
logic [2:0] b [3:0];
logic [3:0] c [3:0];
test i_test (.clk(clk), .rst_n(rst_n), .a(a), .b(b), .c(c));
initial begin
clk <= '0;
forever begin
#5 clk <= ~clk;
end
end
initial begin
rst_n <= '0;
repeat(5) @(posedge clk);
rst_n <= '1;
end
initial begin
a <= '{4{'0}};
b <= '{4{'0}};
@(posedge clk iff rst_n);
for (int i = 0; i<4 ; i++ ) begin
a[i] <= i;
b[i] <= i;
end
repeat(5) @(posedge clk);
$display("c_f[3]:%h",i_test.genblk1[3].i_adder.c_f);
@(posedge clk);
$stop();
end
endmodule: tb
複制
可以看到通路成功
如果通過文章開頭說的方式,就會出現錯誤
for (int i = 0; i<4 ; i++ ) begin
$display("c_f[%0d]:%h",i_test.genblk1[i].i_adder.c_f);
end
複制
其實主要原因是,這個
genblk1
根本就不是一個數組,也就無法通過這種索引的方法通路到對應變量
解決辦法
目前我能想到的方法就是通過
uvm
提供的函數
uvm_hdl_read
實作,他在底層通過
dpi
從外部通路變量,是以可以通過字元串通路到對應的變量。
uvm_hdl_read
的原型是
import "DPI-C" context function int uvm_hdl_read(
string path,
output uvm_hdl_data_t value
)
複制
傳回的
uvm_hdl_data_t
在uvm中的定義是
parameter int UVM_HDL_MAX_WIDTH = `UVM_HDL_MAX_WIDTH;
typedef logic [UVM_HDL_MAX_WIDTH-1:0] uvm_hdl_data_t;
複制
是以,我們可以通過下面的代碼通路
genblk1
中的變量
for (int i = 0; i<4 ; i++ ) begin
uvm_hdl_read($sformatf("tb.i_test.genblk1[%0d].i_adder.c_f",i),temp)
$display("c_f[%0d]:%2h",i,temp);
end
複制
有幾個注意事項
- 在描述路徑時,要傳入絕對路徑,不能使用相對路徑
- 在描述路徑時,使用
,否則字元串會與真實路徑不比對%0d
可以看到通路成功
uvm讀取
下面給出完整代碼
// Module: tb
//
module tb();
import uvm_pkg::*;
`include "uvm_macros.svh"
logic clk,rst_n;
logic [2:0] a [3:0];
logic [2:0] b [3:0];
logic [3:0] c [3:0];
test i_test (.clk(clk), .rst_n(rst_n), .a(a), .b(b), .c(c));
initial begin
clk <= '0;
forever begin
#5 clk <= ~clk;
end
end
initial begin
rst_n <= '0;
repeat(5) @(posedge clk);
rst_n <= '1;
end
initial begin
uvm_hdl_data_t temp;
a <= '{4{'0}};
b <= '{4{'0}};
@(posedge clk iff rst_n);
for (int i = 0; i<4 ; i++ ) begin
a[i] <= i;
b[i] <= i;
end
repeat(5) @(posedge clk);
$display("c_f[3]:%h",i_test.genblk1[3].i_adder.c_f);
for (int i = 0; i<4 ; i++ ) begin
uvm_hdl_read($sformatf("tb.i_test.genblk1[%0d].i_adder.c_f",i),temp)
$display("c_f[%0d]:%2h",i,temp);
end
@(posedge clk);
$stop();
end
endmodule: tb
複制
當然,uvm不僅提供了讀取,還提供了全家桶服務,
force
deposit
一應俱全
如果有更好的辦法,歡迎留言
END