• 通过字符串访问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];
    
    

    如果在代码并不多的情况下,利用插件,例如sublimeinsert 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
    

    有几个注意事项

    1. 在描述路径时,要传入绝对路径,不能使用相对路径
    2. 在描述路径时,使用%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一应俱全

    如果有更好的办法,欢迎留言

  • 相关阅读:
    求解答可用性测试记
    Teambition可用性测试记
    海丁网可用性测试记
    go语言的切片
    go语言的数组
    go语言的函数
    go语言的接口
    go语言的结构体
    go语言的flag
    创建二叉树和三种遍历
  • 原文地址:https://www.cnblogs.com/icparadigm/p/14013413.html
Copyright © 2020-2023  润新知