• MCDF实验4:魔龙的狂舞(从verilog到SV的入门lab4)


    前言:验证结构与实验3是相同的,但需要验证的对象是完整的mcdf。对比之前新添加了reg寄存器模块(选择数据),formatter模块(数据打包)。

    种一棵树最好的时间是十年前,其次是现在。不是吗?

    1. 实验3结构包含moinitor、checker、generator、initiator、test,这已经是一个完整的仿真结构,实验4可以说是实验3结构的复制粘贴。
    2. 实验4将设计变得更复杂,添加了reg寄存器模块,formatter模块。验证过程完全相同,需要像实验3的验证过程一样对这两个模块也做仿真验证。
      设计中reg的功能是可以选择从哪个fifo接收数据,并且可以判断fifo余量(之前是margin),可以控制打包的长度。formatter做数据打包。

    在实验4,理解整个验证结构组成,理解各个package。类比实验3来学习实验4。
    关于实验代码、验证相关的资料,你找的到的找不到的都可以从公众号"袁小丑"免费获取!

    结构框图

    • chnl_agent与之前相比是完全相同的。
    • reg模块通过reg_interface与负责测试reg的 reg_agent连接。
    • formatter通过Formater_interface与负责测试它的fomatter_agent完成连接。
    • mcdf_env顶层环境包括chnl_agent、reg_agent、fomatter_agent和checker。
    • 左边橘黄色是test的部分,对mcdf_env、generator例化,发送数据。

    在这里插入图片描述
    每个package负责验证一个模块。

    1. reg_pkg

    • reg这个模块用来发送寄存器的配置。
    • 包括trans激励的类型、driver发送激励、generator产生激励、monitor检测激励、agent将mon和driver包在一起。激励也就是发送的数据。
    
    `include "param_def.v"//定义宏,比如定义SLV0_RW_ADDR的具体值,修改值只需要修改宏。
    
    package reg_pkg;
    
    • 1
    • 2
    • 3
    • 4

    1.1 reg_trans

      class reg_trans;
        rand bit[7:0] addr;//哪个寄存器
        rand bit[1:0] cmd;//读写
        rand bit[31:0] data;//数据内容
        bit rsp;//判断读写是否正常
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.11 寄存器约束

        constraint cstr {
          soft cmd inside {`WRITE, `READ, `IDLE};//读还是写引用定义
          soft addr inside {`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_R_ADDR, `SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};//3个读地址,3个写地址
          addr[7:4]==0 && cmd==`WRITE -> soft data[31:6]==0;
          soft addr[7:5]==0;
          addr[4]==1 -> soft cmd == `READ;//表示只读寄存器
        };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    mcdf说明文档中寄存器的地址有00、04、08、10、14和18。这6中,用二进制表示为0000_0000、0000_0100、0000_1000、0001_0000、0001_0100、0001_1000。

    • 当高4位为0,即addr[7:4]为0,为读写寄存器,如果是写操作,让保留位即不能更改的位置,置0。
    • 第4位为1表示只读寄存器,此时的cmd为read。
    • 数据地址的高三位为0。

    读写寄存器

    • bit(0):通道使能信号。1为打开,0位关闭。复位值为1。 bit(2:1):优先级。0为最高,3为最低。复位值为3。

    • bit(5:3):数据包长度,解码对应表为,
      0对应长度4,1对应长度8,2对应长度16,3对应长度32,其它数值(4-7)均暂时对应长度32。复位值为0。

    • bit(31:6):保留位,无法写入。复位值为0。

    只读寄存器

    bit(7:0):上行数据从端FIFO的可写余量,同FIFO的数据余量保持同步变化。复位值为FIFO的深度数。
    bit(31:8):保留位,复位值为0。

    1.12 clone、sprint、

        function reg_trans clone();
          reg_trans c = new();
          c.addr = this.addr;
          c.cmd = this.cmd;
          c.data = this.data;
          c.rsp = this.rsp;
          return c;
        endfunction
    
       function string sprint();
          string s;
          s = {s, $sformatf("=======================================\n")};
          s = {s, $sformatf("reg_trans object content is as below: \n")};
          s = {s, $sformatf("addr = %2x: \n", this.addr)};
          s = {s, $sformatf("cmd = %2b: \n", this.cmd)};
          s = {s, $sformatf("data = %8x: \n", this.data)};
          s = {s, $sformatf("rsp = %0d: \n", this.rsp)};
          s = {s, $sformatf("=======================================\n")};
          return s;
        endfunction
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.2 reg_driver

      class reg_driver;
        local string name;
        local virtual reg_intf intf;
        mailbox #(reg_trans) req_mb;
        mailbox #(reg_trans) rsp_mb;
    
        function new(string name = "reg_driver");
          this.name = name;
        endfunction
      
        function void set_interface(virtual reg_intf intf);
          if(intf == null)
            $error("interface handle is NULL, please check if target interface has been intantiated");
          else
            this.intf = intf;
        endfunction
    
        task run();
          fork
            this.do_drive();
            this.do_reset();
          join
        endtask
    
        task do_reset(); //复位
          forever begin
            @(negedge intf.rstn);
            intf.cmd_addr <= 0;
            intf.cmd <= `IDLE;
            intf.cmd_data_m2s <= 0;
          end
        endtask
    
        task do_drive();
          reg_trans req, rsp;
          @(posedge intf.rstn);
          forever begin
            this.req_mb.get(req);
            this.reg_write(req);
            rsp = req.clone();
            rsp.rsp = 1;
            this.rsp_mb.put(rsp);
          end
        endtask
      
        task reg_write(reg_trans t);//给寄存器
          @(posedge intf.clk iff intf.rstn);
          case(t.cmd)
            `WRITE: begin //如果是写操作
                      intf.drv_ck.cmd_addr <= t.addr; //地址
                      intf.drv_ck.cmd <= t.cmd; 
                      intf.drv_ck.cmd_data_m2s <= t.data; //数据
                    end
            `READ:  begin 
                      intf.drv_ck.cmd_addr <= t.addr; //总线
                      intf.drv_ck.cmd <= t.cmd; 
                      repeat(2) @(negedge intf.clk);//等两个时钟的下降沿,第一个下降沿还在
                      t.data = intf.cmd_data_s2m; //采样了intf的数据
                    end
            `IDLE:  begin 
                      this.reg_idle(); //等一拍
                    end
            default: $error("command %b is illegal", t.cmd);
          endcase
          $display("%0t reg driver [%s] sent addr %2x, cmd %2b, data %8x", $time, name, t.addr, t.cmd, t.data);
        endtask
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    当case为 READ时,等待了两个时钟的下降沿。
    第一个下降沿还在当前周期,第二个下降沿就在下一个周期了。此时数据已经驱动到接口cmd_data_s2m处了,这时去采样接口处的数据就一定是要读的数据。
    在这里插入图片描述

    • 在控制寄存器接口上,需要在每一个时钟解析cmd。当cmd为写(WR)指令时,需要把数据cmd_data_in写入到cmd_addr对应的寄存器中;当cmd为读指令(RD)时,即需要从cmd_addr对应的寄存器中读取数据,并在下一个周期,将数据驱动至cmd_data_out接口。
        task reg_idle();
          @(posedge intf.clk);
          intf.drv_ck.cmd_addr <= 0;
          intf.drv_ck.cmd <= `IDLE;
          intf.drv_ck.cmd_data_m2s <= 0;
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.3 reg_generator

      class reg_generator;
        rand bit[7:0] addr = -1;
        rand bit[1:0] cmd = -1;
        rand bit[31:0] data = -1;
    
        mailbox #(reg_trans) req_mb;
        mailbox #(reg_trans) rsp_mb;
    
        reg_trans reg_req[$];
    
        constraint cstr{
          soft addr == -1;
          soft cmd == -1;
          soft data == -1;
        }
    
        function new();
          this.req_mb = new();
          this.rsp_mb = new();
        endfunction
    
        task start();
          send_trans();
        endtask
    
        // generate transaction and put into local mailbox
        task send_trans();
          reg_trans req, rsp;//做例化
          req = new();
          assert(req.randomize with {local::addr >= 0 -> addr == local::addr;
                                     local::cmd >= 0 -> cmd == local::cmd;
                                     local::data >= 0 -> data == local::data;
                                   })
            else $fatal("[RNDFAIL] register packet randomization failure!");
          $display(req.sprint());
          this.req_mb.put(req);//放入数据
          this.rsp_mb.get(rsp);
          $display(rsp.sprint());
          if(req.cmd == `READ) 
            this.data = rsp.data;
          assert(rsp.rsp)
            else $error("[RSPERR] %0t error response received!", $time);
        endtask
    
        function string sprint();
          string s;
          s = {s, $sformatf("=======================================\n")};
          s = {s, $sformatf("reg_generator object content is as below: \n")};
          s = {s, $sformatf("addr = %2x: \n", this.addr)};
          s = {s, $sformatf("cmd = %2b: \n", this.cmd)};
          s = {s, $sformatf("data = %8x: \n", this.data)};
          s = {s, $sformatf("=======================================\n")};
          return s;
        endfunction
    
        function void post_randomize();
          string s;
          s = {"AFTER RANDOMIZATION \n", this.sprint()};
          $display(s);
        endfunction
      endclass
    
      class reg_monitor;
        local string name;
        local virtual reg_intf intf;
        mailbox #(reg_trans) mon_mb;
        function new(string name="reg_monitor");
          this.name = name;
        endfunction
        function void set_interface(virtual reg_intf intf);
          if(intf == null)
            $error("interface handle is NULL, please check if target interface has been intantiated");
          else
            this.intf = intf;
        endfunction
        task run();
          this.mon_trans();
        endtask
    
        task mon_trans();
          reg_trans m;
          forever begin
            @(posedge intf.clk iff (intf.rstn && intf.mon_ck.cmd != `IDLE));//时钟的上升沿,并且数据合理
            m = new();//生成一个对象,把数据地址放入
            m.addr = intf.mon_ck.cmd_addr;
            m.cmd = intf.mon_ck.cmd;
            if(intf.mon_ck.cmd == `WRITE) begin//写指令,把总线上的数据放入
              m.data = intf.mon_ck.cmd_data_m2s;
            end
            else if(intf.mon_ck.cmd == `READ) begin//读指令,等下一个周期,再放入数据
              @(posedge intf.clk);
              m.data = intf.mon_ck.cmd_data_s2m;
            end
            mon_mb.put(m);//把数据交给checker
            $display("%0t %s monitored addr %2x, cmd %2b, data %8x", $time, this.name, m.addr, m.cmd, m.data);
          end
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    1.4 reg_agent

    
      class reg_agent;//包含了monitor,driver,让driver,monitor工作起来
        local string name;
        reg_driver driver;
        reg_monitor monitor;
        local virtual reg_intf vif;
        function new(string name = "reg_agent");
          this.name = name;
          this.driver = new({name, ".driver"});
          this.monitor = new({name, ".monitor"});
        endfunction
    
        function void set_interface(virtual reg_intf vif);
          this.vif = vif;
          driver.set_interface(vif);
          monitor.set_interface(vif);
        endfunction
        task run();
          fork
            driver.run();
            monitor.run();
          join
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    激励的类型、发送激励、产生激励、检测激励、agent

    2. fmt_apk

    chnl和reg的driver都是主动的发送数据给dut,而fmt的driver是被动的接受发送出来的数据。所以fmt像一个fifo,储存发送出来的数据。
    需要设计fifo的大小宽度,来储存发送出来的数据。

    package fmt_pkg;
      import rpt_pkg::*;
    //传出来的数据可能有不同的带宽、需要的fifo大小也不同,把可能需要的都枚举出来。
      typedef enum {SHORT_FIFO, MED_FIFO, LONG_FIFO, ULTRA_FIFO} fmt_fifo_t;
      typedef enum {LOW_WIDTH, MED_WIDTH, HIGH_WIDTH, ULTRA_WIDTH} fmt_bandwidth_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.1 fmt_trans

      class fmt_trans;
        rand fmt_fifo_t fifo;
        rand fmt_bandwidth_t bandwidth;
        bit [9:0] length;
        bit [31:0] data[];
        bit [1:0] ch_id;
        bit rsp;
        constraint cstr{
          soft fifo == MED_FIFO;//fifo
          soft bandwidth == MED_WIDTH;//带宽
        };
        function fmt_trans clone();
          fmt_trans c = new();
          c.fifo = this.fifo;
          c.bandwidth = this.bandwidth;
          c.length = this.length;
          c.data = this.data;
          c.ch_id = this.ch_id;
          c.rsp = this.rsp;
          return c;
        endfunction
    
        function string sprint();
          string s;
          s = {s, $sformatf("=======================================\n")};
          s = {s, $sformatf("fmt_trans object content is as below: \n")};
          s = {s, $sformatf("fifo = %s: \n", this.fifo)};
          s = {s, $sformatf("bandwidth = %s: \n", this.bandwidth)};
          s = {s, $sformatf("length = %s: \n", this.length)};
          foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
          s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
          s = {s, $sformatf("rsp = %0d: \n", this.rsp)};
          s = {s, $sformatf("=======================================\n")};
          return s;
        endfunction
    
        function bit compare(fmt_trans t);//当前的与传进来的数作比较
          string s;
          compare = 1;
          s = "\n=======================================\n";
          s = {s, $sformatf("COMPARING fmt_trans object at time %0d \n", $time)};
          if(this.length != t.length) begin
            compare = 0;
            s = {s, $sformatf("sobj length %0d != tobj length %0d \n", this.length, t.length)};
          end
          if(this.ch_id != t.ch_id) begin
            compare = 0;
            s = {s, $sformatf("sobj ch_id %0d != tobj ch_id %0d\n", this.ch_id, t.ch_id)};
          end
          foreach(this.data[i]) begin
            if(this.data[i] != t.data[i]) begin
              compare = 0;
              s = {s, $sformatf("sobj data[%0d] %8x != tobj data[%0d] %8x\n", i, this.data[i], i, t.data[i])};
            end
          end
          if(compare == 1) s = {s, "COMPARED SUCCESS!\n"};
          else  s = {s, "COMPARED FAILURE!\n"};
          s = {s, "=======================================\n"};
          rpt_pkg::rpt_msg("[CMPOBJ]", s, rpt_pkg::INFO, rpt_pkg::MEDIUM);
        endfunction
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    2.2 fmt_driver

      class fmt_driver;
        local string name;
        local virtual fmt_intf intf;
        mailbox #(fmt_trans) req_mb;
        mailbox #(fmt_trans) rsp_mb;
    
        local mailbox #(bit[31:0]) fifo;
        local int fifo_bound;//长度
        local int data_consum_peroid;//消耗周期,侧面反应带宽,带宽越大,消耗周期越短
    
      
        function new(string name = "fmt_driver");
          this.name = name;
          this.fifo = new();//fifo初始化
          this.fifo_bound = 4096;
          this.data_consum_peroid = 1;
        endfunction
      
        function void set_interface(virtual fmt_intf intf);
          if(intf == null)
            $error("interface handle is NULL, please check if target interface has been intantiated");
          else
            this.intf = intf;
        endfunction
    
        task run();
          fork
            this.do_receive();
            this.do_consume();
            this.do_config();
            this.do_reset();
          join
        endtask
    //配置fifo长或者短,消耗数据快还是慢
        task do_config();//配置driver,配置完成后重新例化fifo
          fmt_trans req, rsp;
          forever begin
            this.req_mb.get(req);
            case(req.fifo)//配置为不同长度
              SHORT_FIFO: this.fifo_bound = 64;
              MED_FIFO: this.fifo_bound = 256;
              LONG_FIFO: this.fifo_bound = 512;
              ULTRA_FIFO: this.fifo_bound = 2048;
            endcase
            this.fifo = new(this.fifo_bound);//重新例化,并且规定长度
            case(req.bandwidth)
              LOW_WIDTH: this.data_consum_peroid = 8;
              MED_WIDTH: this.data_consum_peroid = 4;
              HIGH_WIDTH: this.data_consum_peroid = 2;
              ULTRA_WIDTH: this.data_consum_peroid = 1;
            endcase
            rsp = req.clone();
            rsp.rsp = 1;
            this.rsp_mb.put(rsp);
          end
        endtask
    
        task do_reset(); 
          forever begin
            @(negedge intf.rstn) 
            intf.fmt_grant <= 0;
          end
        endtask
    
        task do_receive();//模拟从formater接受数据
          forever begin
            @(posedge intf.fmt_req);//fmt的req拉高
            forever begin
              @(posedge intf.clk);//等待一拍
              if((this.fifo_bound-this.fifo.num()) >= intf.fmt_length)//fifo最大容量-已经存放了多少>=即将存放的数据长度
                break;
            end
            intf.drv_ck.fmt_grant <= 1;//可以存放数据,让grant为1
            @(posedge intf.fmt_start);
            fork
              begin
                @(posedge intf.clk);
                intf.drv_ck.fmt_grant <= 0;//过一拍置0
              end
            join_none
            repeat(intf.fmt_length) begin//把传进来的数据在每一拍做采样,重复采样length次。
              @(negedge intf.clk);
              this.fifo.put(intf.fmt_data);
            end
          end
        endtask
    
        task do_consume();//不断的消耗数据,每过一拍拿一组数据
          bit[31:0] data;
          forever begin
            void'(this.fifo.try_get(data));
            repeat($urandom_range(1, this.data_consum_peroid)) @(posedge intf.clk);
          end
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    2.3 fmt_generator

      class fmt_generator;
        rand fmt_fifo_t fifo = MED_FIFO;
        rand fmt_bandwidth_t bandwidth = MED_WIDTH;
    
        mailbox #(fmt_trans) req_mb;
        mailbox #(fmt_trans) rsp_mb;
    
        constraint cstr{
          soft fifo == MED_FIFO;
          soft bandwidth == MED_WIDTH;
        }
    
        function new();
          this.req_mb = new();
          this.rsp_mb = new();
        endfunction
    
        task start();
          send_trans();
        endtask
    
        // generate transaction and put into local mailbox
        task send_trans();
          fmt_trans req, rsp;
          req = new();
          assert(req.randomize with {local::fifo != MED_FIFO -> fifo == local::fifo; 
                                     local::bandwidth != MED_WIDTH -> bandwidth == local::bandwidth;
                                   })
            else $fatal("[RNDFAIL] formatter packet randomization failure!");
          $display(req.sprint());
          this.req_mb.put(req);
          this.rsp_mb.get(rsp);
          $display(rsp.sprint());
          assert(rsp.rsp)
            else $error("[RSPERR] %0t error response received!", $time);
        endtask
    
        function string sprint();
          string s;
          s = {s, $sformatf("=======================================\n")};
          s = {s, $sformatf("fmt_generator object content is as below: \n")};
          s = {s, $sformatf("fifo = %s: \n", this.fifo)};
          s = {s, $sformatf("bandwidth = %s: \n", this.bandwidth)};
          s = {s, $sformatf("=======================================\n")};
          return s;
        endfunction
    
        function void post_randomize();
          string s;
          s = {"AFTER RANDOMIZATION \n", this.sprint()};
          $display(s);
        endfunction
    
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    2.4 fmt_monitor

      class fmt_monitor;
        local string name;
        local virtual fmt_intf intf;
        mailbox #(fmt_trans) mon_mb;
        function new(string name="fmt_monitor");
          this.name = name;
        endfunction
        function void set_interface(virtual fmt_intf intf);
          if(intf == null)
            $error("interface handle is NULL, please check if target interface has been intantiated");
          else
            this.intf = intf;
        endfunction
    
        task run();
          this.mon_trans();
        endtask
    
        task mon_trans();
          fmt_trans m;
          string s;
          forever begin
            @(posedge intf.mon_ck.fmt_start);//假定协议是正常的
            m = new();
            m.length = intf.mon_ck.fmt_length;
            m.ch_id = intf.mon_ck.fmt_chid;
            m.data = new[m.length];
            foreach(m.data[i]) begin
              @(posedge intf.clk);
              m.data[i] = intf.mon_ck.fmt_data;//每一次数据来的时候,都存放在动态数组
            end
            mon_mb.put(m);//放好数据后,放入信箱
            s = $sformatf("=======================================\n");
            s = {s, $sformatf("%0t %s monitored a packet: \n", $time, this.name)};
            s = {s, $sformatf("length = %0d: \n", m.length)};
            s = {s, $sformatf("chid = %0d: \n", m.ch_id)};
            foreach(m.data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, m.data[i])};
            s = {s, $sformatf("=======================================\n")};
            $display(s);
          end
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    2.5 fmt_agent

      class fmt_agent;
        local string name;
        fmt_driver driver;
        fmt_monitor monitor;
        local virtual fmt_intf vif;
        function new(string name = "fmt_agent");
          this.name = name;
          this.driver = new({name, ".driver"});
          this.monitor = new({name, ".monitor"});
        endfunction
    
        function void set_interface(virtual fmt_intf vif);
          this.vif = vif;
          driver.set_interface(vif);
          monitor.set_interface(vif);
        endfunction
        task run();
          fork
            driver.run();
            monitor.run();
          join
        endtask
      endclass
    
    endpackage
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3. checker

    • 检测到的数据传到checker,3个chnl、fmt、reg的数据都会存放在checke已经例化好的信箱里。而fmt和3个chnl的数据格式是完全不同的。fmt对数据进行了打包,把数据整合成了新格式。
    • 所以比较输入输出先要把chnl、reg的数据需要先整形转化,模拟mcdf的行为。模拟打包后再进行数据的比较。
    • refmod利用句柄把数据拿出来,模拟硬件对数据打包。从chnl拿数据,按照长度的不同把数据放入三个输出端。

    在这里插入图片描述
    其中:

    • doreset对寄存器复位,清空信箱中的数据。
    • doregupdate更新checker中寄存器的配置。
    • dopackage对三个chnl做打包。

    包含了checker、env和test。

    `include "param_def.v"
    
    package mcdf_pkg;
    
      import chnl_pkg::*;
      import reg_pkg::*;
      import arb_pkg::*;
      import fmt_pkg::*;
      import rpt_pkg::*;
    
      typedef struct packed {
        bit[2:0] len; //长度
        bit[1:0] prio;//
        bit en;//fifo使能
        bit[7:0] avail;//表示fifo可选余量 
      } mcdf_reg_t;
     
      typedef enum {RW_LEN, RW_PRIO, RW_EN, RD_AVAIL} mcdf_field_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.1 mcdf_refmod

    模拟mcdf的功能接受寄存器的读写行为,对检测来的数据进行打包。

      class mcdf_refmod;//模拟mcdf的功能接受寄存器的读写行为,对数据进行打包
        local virtual mcdf_intf intf;
        local string name;
        mcdf_reg_t regs[3];
        mailbox #(reg_trans) reg_mb;
        mailbox #(mon_data_t) in_mbs[3];
        mailbox #(fmt_trans) out_mbs[3];
    
        function new(string name="mcdf_refmod");
          this.name = name;
          foreach(this.out_mbs[i]) this.out_mbs[i] = new();
        endfunction
    
        task run();
          fork
            do_reset();
            this.do_reg_update();//模拟寄存器
            do_packet(0);
            do_packet(1);
            do_packet(2);
          join
        endtask
    
        task do_reg_update();//硬件上对寄存器的读写更新到仿真中
          reg_trans t;//引用regtrans里的数据
          forever begin
            this.reg_mb.get(t);
            if(t.addr[7:4] == 0 && t.cmd == `WRITE) begin
              this.regs[t.addr[3:2]].en = t.data[0];
              this.regs[t.addr[3:2]].prio = t.data[2:1];
              this.regs[t.addr[3:2]].len = t.data[5:3];
            end
            else if(t.addr[7:4] == 1 && t.cmd == `READ) begin
              this.regs[t.addr[3:2]].avail = t.data[7:0];
            end
          end
        endtask
    
        task do_packet(int id);//把chnl的数据打包
          fmt_trans ot; 
          mon_data_t it;
          forever begin
            this.in_mbs[id].peek(it);
            ot = new();
            ot.length = 4 << (this.get_field_value(id, RW_LEN) & 'b11);//拿到打包的长度
            ot.data = new[ot.length];//开辟空间
            ot.ch_id = id;
            foreach(ot.data[m]) begin//拿完数据,把ot放到对应的信箱。
              this.in_mbs[id].get(it);
              ot.data[m] = it.data;
            end
            this.out_mbs[id].put(ot);
          end
        endtask
    
        function int get_field_value(int id, mcdf_field_t f);
          case(f)
            RW_LEN: return regs[id].len;
            RW_PRIO: return regs[id].prio;
            RW_EN: return regs[id].en;
            RD_AVAIL: return regs[id].avail;
          endcase
        endfunction 
    
        task do_reset();//寄存器复位
          forever begin
            @(negedge intf.rstn); 
            foreach(regs[i]) begin
              regs[i].len = 'h0;
              regs[i].prio = 'h3;
              regs[i].en = 'h1;
              regs[i].avail = 'h20;
            end
          end
        endtask
    
        function void set_interface(virtual mcdf_intf intf);
          if(intf == null)
            $error("interface handle is NULL, please check if target interface has been intantiated");
          else
            this.intf = intf;
        endfunction
        
      endclass
    ## 3.2 mcdf_checker
    ```java
      class mcdf_checker;
        local string name;
        local int err_count;
        local int total_count;
        local int chnl_count[3];
        local virtual mcdf_intf intf;
        local mcdf_refmod refmod;
        mailbox #(mon_data_t) chnl_mbs[3];
        mailbox #(fmt_trans) fmt_mb;
        mailbox #(reg_trans) reg_mb;
        mailbox #(fmt_trans) exp_mbs[3];
    
        function new(string name="mcdf_checker");
          this.name = name;
          foreach(this.chnl_mbs[i]) this.chnl_mbs[i] = new();
          this.fmt_mb = new();
          this.reg_mb = new();
          this.refmod = new();
          foreach(this.refmod.in_mbs[i]) begin
            this.refmod.in_mbs[i] = this.chnl_mbs[i];
            this.exp_mbs[i] = this.refmod.out_mbs[i];
          end
          this.refmod.reg_mb = this.reg_mb;
          this.err_count = 0;
          this.total_count = 0;
          foreach(this.chnl_count[i]) this.chnl_count[i] = 0;
        endfunction
    
        function void set_interface(virtual mcdf_intf intf);
          if(intf == null)
            $error("interface handle is NULL, please check if target interface has been intantiated");
          else
            this.intf = intf;
            this.refmod.set_interface(intf);
        endfunction
    
        task run();
          fork
            this.do_compare();
            this.refmod.run();
          join
        endtask
    
        task do_compare();
          fmt_trans expt, mont;
          bit cmp;
          forever begin
            this.fmt_mb.get(mont);
            this.exp_mbs[mont.ch_id].get(expt);
            cmp = mont.compare(expt);   
            this.total_count++;
            this.chnl_count[mont.ch_id]++;
            if(cmp == 0) begin
              this.err_count++;
              rpt_pkg::rpt_msg("[CMPFAIL]", 
                $sformatf("%0t %0dth times comparing but failed! MCDF monitored output packet is different with reference model output", $time, this.total_count),
                rpt_pkg::ERROR,
                rpt_pkg::TOP,
                rpt_pkg::LOG);
            end
            else begin
              rpt_pkg::rpt_msg("[CMPSUCD]",
                $sformatf("%0t %0dth times comparing and succeeded! MCDF monitored output packet is the same with reference model output", $time, this.total_count),
                rpt_pkg::INFO,
                rpt_pkg::HIGH);
            end
          end
        endtask
    
        function void do_report();
          string s;
          s = "\n---------------------------------------------------------------\n";
          s = {s, "CHECKER SUMMARY \n"}; 
          s = {s, $sformatf("total comparison count: %0d \n", this.total_count)}; 
          foreach(this.chnl_count[i]) s = {s, $sformatf(" channel[%0d] comparison count: %0d \n", i, this.chnl_count[i])};
          s = {s, $sformatf("total error count: %0d \n", this.err_count)}; 
          foreach(this.chnl_mbs[i]) begin
            if(this.chnl_mbs[i].num() != 0)
              s = {s, $sformatf("WARNING:: chnl_mbs[%0d] is not empty! size = %0d \n", i, this.chnl_mbs[i].num())}; 
          end
          if(this.fmt_mb.num() != 0)
              s = {s, $sformatf("WARNING:: fmt_mb is not empty! size = %0d \n", this.fmt_mb.num())}; 
          s = {s, "---------------------------------------------------------------\n"};
          rpt_pkg::rpt_msg($sformatf("[%s]",this.name), s, rpt_pkg::INFO, rpt_pkg::TOP);
        endfunction
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172

    3.2 mcdf_env

      class mcdf_env;
        chnl_agent chnl_agts[3];
        reg_agent reg_agt;
        fmt_agent fmt_agt;
        mcdf_checker chker;
        protected string name;
    
        function new(string name = "mcdf_env");//做例化和连接
          this.name = name;
          this.chker = new();
          foreach(chnl_agts[i]) begin
            this.chnl_agts[i] = new($sformatf("chnl_agts[%0d]",i));
            this.chnl_agts[i].monitor.mon_mb = this.chker.chnl_mbs[i];
          end
          this.reg_agt = new("reg_agt");
          this.reg_agt.monitor.mon_mb = this.chker.reg_mb;
          this.fmt_agt = new("fmt_agt");
          this.fmt_agt.monitor.mon_mb = this.chker.fmt_mb;
          $display("%s instantiated and connected objects", this.name);
        endfunction
    
        virtual task run();
          $display($sformatf("*****************%s started********************", this.name));
          this.do_config();
          fork
            this.chnl_agts[0].run();
            this.chnl_agts[1].run();
            this.chnl_agts[2].run();
            this.reg_agt.run();
            this.fmt_agt.run();
            this.chker.run();
          join
        endtask
    
        virtual function void do_config();
        endfunction
    
        virtual function void do_report();
          this.chker.do_report();
        endfunction
    
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3.3 test

      class mcdf_base_test;
        chnl_generator chnl_gens[3];
        reg_generator reg_gen;
        fmt_generator fmt_gen;
        mcdf_env env;
        protected string name;
    
        function new(string name = "mcdf_base_test");
          this.name = name;
          this.env = new("env");
    
          foreach(this.chnl_gens[i]) begin
            this.chnl_gens[i] = new();
            this.env.chnl_agts[i].driver.req_mb = this.chnl_gens[i].req_mb;
            this.env.chnl_agts[i].driver.rsp_mb = this.chnl_gens[i].rsp_mb;
          end
    
          this.reg_gen = new();
          this.env.reg_agt.driver.req_mb = this.reg_gen.req_mb;
          this.env.reg_agt.driver.rsp_mb = this.reg_gen.rsp_mb;
    
          this.fmt_gen = new();
          this.env.fmt_agt.driver.req_mb = this.fmt_gen.req_mb;
          this.env.fmt_agt.driver.rsp_mb = this.fmt_gen.rsp_mb;
    
          rpt_pkg::logname = {this.name, "_check.log"};
          rpt_pkg::clean_log();
          $display("%s instantiated and connected objects", this.name);
        endfunction
    
        virtual task run();
          fork
            env.run();
          join_none
          rpt_pkg::rpt_msg("[TEST]",
            $sformatf("=====================%s AT TIME %0t STARTED=====================", this.name, $time),
            rpt_pkg::INFO,
            rpt_pkg::HIGH);
          this.do_reg();
          this.do_formatter();//先配置fmt的fifo
          this.do_data();
          rpt_pkg::rpt_msg("[TEST]",
            $sformatf("=====================%s AT TIME %0t FINISHED=====================", this.name, $time),
            rpt_pkg::INFO,
            rpt_pkg::HIGH);
          this.do_report();
          $finish();
        endtask
    
        // do register configuration
        virtual task do_reg();
        endtask
    
        // do external formatter down stream slave configuration
        virtual task do_formatter();
        endtask
    
        // do data transition from 3 channel slaves
        virtual task do_data();
        endtask
    
        // do simulation summary
        virtual function void do_report();
          this.env.do_report();
          rpt_pkg::do_report();
        endfunction
    
        virtual function void set_interface(virtual chnl_intf ch0_vif 
                                            ,virtual chnl_intf ch1_vif 
                                            ,virtual chnl_intf ch2_vif 
                                            ,virtual reg_intf reg_vif
                                            ,virtual fmt_intf fmt_vif
                                            ,virtual mcdf_intf mcdf_vif
                                          );
          this.env.chnl_agts[0].set_interface(ch0_vif);
          this.env.chnl_agts[1].set_interface(ch1_vif);
          this.env.chnl_agts[2].set_interface(ch2_vif);
          this.env.reg_agt.set_interface(reg_vif);
          this.env.fmt_agt.set_interface(fmt_vif);
          this.env.chker.set_interface(mcdf_vif);
        endfunction
    
        virtual function bit diff_value(int val1, int val2, string id = "value_compare");
          if(val1 != val2) begin
            rpt_pkg::rpt_msg("[CMPERR]", 
              $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2), 
              rpt_pkg::ERROR, 
              rpt_pkg::TOP);
            return 0;
          end
          else begin
            rpt_pkg::rpt_msg("[CMPSUC]", 
              $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2),
              rpt_pkg::INFO,
              rpt_pkg::HIGH);
            return 1;
          end
        endfunction
    
        virtual task idle_reg();
          void'(reg_gen.randomize() with {cmd == `IDLE; addr == 0; data == 0;});
          reg_gen.start();
        endtask
    
        virtual task write_reg(bit[7:0] addr, bit[31:0] data);
          void'(reg_gen.randomize() with {cmd == `WRITE; addr == local::addr; data == local::data;});
          reg_gen.start();
        endtask
    
        virtual task read_reg(bit[7:0] addr, output bit[31:0] data);
          void'(reg_gen.randomize() with {cmd == `READ; addr == local::addr;});
          reg_gen.start();
          data = reg_gen.data;
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    3.5 子类test

    3.51 完整性测试

      class mcdf_data_consistence_basic_test extends mcdf_base_test;
        function new(string name = "mcdf_data_consistence_basic_test");
          super.new(name);
        endfunction
    
        task do_reg();
          bit[31:0] wr_val, rd_val;
          //寄存器规定了第三位为1、2、3时对应8、16、32。
          // slv0 with len=8,  prio=0, en=1
          wr_val = (1<<3)+(0<<1)+1;
          this.write_reg(`SLV0_RW_ADDR, wr_val);	//	把值写进去
          this.read_reg(`SLV0_RW_ADDR, rd_val);//再把写进去的值读回来
          void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));//作比较
    
          // slv1 with len=16, prio=1, en=1
          wr_val = (2<<3)+(1<<1)+1;
          this.write_reg(`SLV1_RW_ADDR, wr_val);
          this.read_reg(`SLV1_RW_ADDR, rd_val);
          void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));
    
          // slv2 with len=32, prio=2, en=1
          wr_val = (3<<3)+(2<<1)+1;
          this.write_reg(`SLV2_RW_ADDR, wr_val);
          this.read_reg(`SLV2_RW_ADDR, rd_val);
          void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));
    
          // send IDLE command
          this.idle_reg();
        endtask
    
        task do_formatter();	//定义了fmt的fifo为LONG_FIFO, HIGH_WIDTH
          void'(fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
          fmt_gen.start();
        endtask
    
        task do_data();//发送数据
          void'(chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });
          void'(chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;});
          void'(chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;});
          fork
            chnl_gens[0].start();
            chnl_gens[1].start();
            chnl_gens[2].start();
          join
          #10us; // wait until all data haven been transfered through MCDF
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    从仿真波形图可以看出,只是一开始的时候3个通道同时发送,但是过了某个时间之后,你会发现已经按通道0、1、2的顺序依次发送了。但是在class env fork join中规定的并行发送。这是为何呢?

     fork
       this.chnl_agts[0].run();
       this.chnl_agts[1].run();
       this.chnl_agts[2].run();
    
     join
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 三个chnl_agts同时向slave-fifo发送数据,而发送到通道0、1、2的data_size不一样,分别为8、16、32,这些数据暂时存在了FIFO0、FIFO1、FIFO2里。
    • 由于在mcdf_data_consistence_basic_test
      里定义了优先级排序是0>1>2,所以当三个通道同时申请仲裁时,先通过0的,再通过1的,最后才是2。所以结果不是并行发送。
    • 如果你在mcdf_data_consistence_basic_test 把优先级排序改成2>1>0,那么情况就反了。

    3.52其他测试

    测试主要从do_reg、do_fmt和do_data来测试不同配置的寄存器、不同的数据。
    在这里插入图片描述

    在这里插入图片描述

    3.52 寄存器读写测试

    把写进寄存器的配置读回来,然后比较一下看对不对。

    class mcdf_reg_stability_test extends mcdf_base_test;
        function new(string name = "mcdf_data_consistence_basic_test");
          super.new(name);
        endfunction
    
        task do_reg();
          bit[7:0] chnl_rw_addrs[] = '{`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
          bit[7:0] chnl_ro_addrs[] = '{`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
          int pwidth = `PAC_LEN_WIDTH + `PRIO_WIDTH + 1;
          bit[31:0] check_pattern[] = '{((1<<pwidth)-1), 0, ((1<<pwidth)-1)};
          bit[31:0] wr_val, rd_val;
    
          // RW register access and bits toggle
          foreach(chnl_rw_addrs[i]) begin
            foreach(check_pattern[i]) begin
              wr_val = check_pattern[i];
              this.write_reg(chnl_rw_addrs[i], wr_val);
              this.read_reg(chnl_rw_addrs[i], rd_val);
              void'(this.diff_value(wr_val, rd_val));
            end
          end
    
          // RO register read access
          foreach(chnl_ro_addrs[i]) begin
              this.read_reg(chnl_ro_addrs[i], rd_val);
          end
    
          // send IDLE command
          this.idle_reg();
        endtask
      endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    check_pattern[]中的数就是要写进寄存器里的数,此处的do_reg先把数写进reg,再读回来看是否一致。

    3.53 寄存器稳定性测试

    寄存器的相关说明:
    地址0x00 通道1控制寄存器 32bits 读写寄存器
    bit(0):通道使能信号。1为打开,0位关闭。复位值为1。 bit(2:1):优先级。0为最高,3为最低。复位值为3。
    bit(5:3):数据包长度,解码对应表为,
    0对应长度4,1对应长度8,2对应长度16,3对应长度32,其它数值(4-7)均暂时对应长度32。复位值为0。
    bit(31:6):保留位,无法写入。复位值为0。

    class mcdf_reg_illegal_access_test extends mcdf_base_test;
      function new(string name = "mcdf_reg_illegal_access_test");
        super.new(name);
      endfunction
    
      task do_reg();
        bit[7:0] chnl_rw_addrs[] = '{`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
        bit[7:0] chnl_ro_addrs[] = '{`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
        int pwidth = `PAC_LEN_WIDTH + `PRIO_WIDTH + 1;  //=6
        bit[31:0] check_pattern[] = '{32'h0000_FFC0, 32'hFFFF_0000};
        bit[31:0] wr_val, rd_val;
    
        // RW register write reserved field and check
        foreach(chnl_rw_addrs[i]) begin
          foreach(check_pattern[j]) begin
            wr_val = check_pattern[j];
            this.write_reg(chnl_rw_addrs[i], wr_val);
            this.read_reg(chnl_rw_addrs[i], rd_val);
            void'(this.diff_value(wr_val & ((1<<pwidth)-1), rd_val));//将期望值和读出来的值对比
          end                                                       
        end
    
        // RO register write reserved field and check (no care readable field
        // value)
        foreach(chnl_ro_addrs[i]) begin
            wr_val = 32'hFFFF_FF00;
            this.write_reg(chnl_ro_addrs[i], wr_val);
            this.read_reg(chnl_ro_addrs[i], rd_val);
            void'(this.diff_value(0 , rd_val & 32'hFFFFFF00));
        end
    
        // send IDLE command
        this.idle_reg();
      endtask
    endclass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 寄存器说明中指出了只能读取低六位的数据。1与任何数想‘与’都是那个数本身,只需要将低6位和1相‘与’即可。最后和读回来的数作比较。
    • 代码最后测试了ro寄存器是不是真的只读。
    pwidth = `PAC_LEN_WIDTH + `PRIO_WIDTH + 1 = 6
    • 1
    (1<<pwidth)-1='b1000000-1='b0111111。
    
    • 1

    wr_val & ((1< diff_value(wr_val & ((1<

    3.54 数据通道开关检查

    数据通道关闭时,mcdf_checker不会收到输入/出端的检测数据,因此也没有数据比较的信息。使能信号en默认初始值位1,当把en改位0后数据通道关闭,测试数据停留在数据通道端口,无法写入。同时ready为低,表示不接收数据。

    3.55 优先级测试

    将不同数据通道配置为相同或者不同的优先级,在数据通道使能的情况下进行测试 如果优先级相同,那么arbiter应该采取轮询机制从各个通道接收数据;如果优先级不同,那么arbiter应该先接收高优先级通道的数据,同时,最终所有的数据都应该从MCDF发送出来。

    task do_arbiter_priority_check();
          int id;
          forever begin
            @(posedge this.arb_vif.clk iff (this.arb_vif.rstn && this.arb_vif.mon_ck.f2a_id_req===1));
            id = this.get_slave_id_with_prio();           //id的优先级最高
            if(id >= 0) begin
              @(posedge this.arb_vif.clk);
              if(!(refmod.get_field_value(0,RW_PRIO) == refmod.get_field_value(1,RW_PRIO)
              && refmod.get_field_value(1,RW_PRIO) == refmod.get_field_value(2,RW_PRIO)))
                if(this.arb_vif.mon_ck.a2s_acks[id] !== 1)    //id的优先级最高,那么对应的a2s_acks应该为1,否则报错
                  rpt_pkg::rpt_msg("[CHKERR]",
                    $sformatf("ERROR! %0t arbiter received f2a_id_req===1 and channel[%0d] raising request with high priority, but is not granted by arbiter", $time, id),
                    rpt_pkg::ERROR,                               
                    rpt_pkg::TOP);
            end
          end
        endtask
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    通过get_field_value函数获取每个chnl的优先级,如果三个优先级都不一样,那么就不会执行轮询,按照原来优先级的高低依次执行。
    如果三个优先级都一样,那么就采用轮询机制,不执行接下来信息打印部分,否则就一定会报错。因为id的优先级最高,那么对应的a2s_acks应该为1。而轮询机制并不是按照优先级来控制a2s_acks信号,所以会出现错误。

    if(!(refmod.get_field_value(0,RW_PRIO) == refmod.get_field_value(1,RW_PRIO)
       && refmod.get_field_value(1,RW_PRIO) == refmod.get_field_value(2,RW_PRIO)))
    
    • 1
    • 2

    关于实验代码、验证相关的资料,你找的到的找不到的都可以从公众号"袁小丑"免费获取!

  • 相关阅读:
    Java框架 MyBaits核心配置文件详解
    SQL的CASE WHEN函数、CAST函数、CONVERT() 函数、COALESCE()函数、DATEDIFF()函数
    生成requirements.txt
    MySQL分批插入/更新数据
    MySQL---表的增查改删(CRUD进阶)
    【无标题】
    Python 使用executemany批量向mysql插入数据
    「PAT乙级真题解析」Basic Level 1078 字符串压缩与解压 (问题分析+完整步骤+伪代码描述+提交通过代码)
    商品大类C
    数据发布!智驾域控前装搭载同比增长90.99%,哪些企业在领跑市场
  • 原文地址:https://blog.csdn.net/jackack/article/details/127487631