通过字符串访问generate模块内部的变量
动机
今天一个朋友问了这样一个问题
失败原因
首先介绍一下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
,也可以快速实现
同样的,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
,否则字符串会与真实路径不匹配
可以看到访问成功
下面给出完整代码
// 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
一应俱全
如果有更好的办法,欢迎留言
#Java#