AHB GPIO项目理解

发布时间 2023-07-12 15:11:21作者: 傅红雪a

 

框架与协议理解1--基本框架

 

 

框架与协议理解2--portin和portout的访问

masked bit:只对当前位操作,其他位不变
GPIO的portin和portout不是一个配置寄存器,它的写和读有不同含义

 

 

 

【以协议中portout为例】

动作为:写入和检测

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1.写入
比如给pout_wr的第0位写入1,表示第0位的线拉高,因为logic四值逻辑的默认值为x,其他位都为x,即16'hxxxx-xxxx-xxxx-xxx1.
然后进入到set_portout_bits中,set_portout_bits中先读寄存器地址这个操作是因为软件处理都是先读后处理的。读到的数据pout和写入的数据bits进行masked的预处理。

若写入的bits的某位为x值,用当前读到的pout的那位,反之若写入bits的某位不是x值,用当前bits的那位。(这可以保证保证上一次其他位不变,这一次操作仅对拉高的线操作

注意:真正写入的值是与写入的值进过masked预处理后的值。然后将想写入的值与总线上监测到的值进行比较即可

想写入的值:pout_wr(bits)
真正写入的值:pout
总线上监测到的值:pout_mo


注意:这里的测试都是对一组寄存器操作(16 pins)
注意:代码要精练。一般测试用例如果超过200行,要注意是否写了垃圾代码。

 

框架与协议理解3--mask access机制 屏蔽访问


一次写操作只把你关心的一个bit或者多个bit拉高
若先读后写(之前的测试都是用这种),有可能在读之后写之前寄存器给别人写了,此时这个情况不可靠。
因此使用mask access可以达到`屏蔽指定位`的目的,仅对当前位进行操作.

屏蔽访问功能允许在单次传输中读取或写入单个位或多个位。 这避免了基于软件的非线程安全的读取-修改-写入操作。 通过屏蔽访问操作,16 位 I/O 被分为两半,即低字节和高字节。 位掩码地址空间定义为两个数组,每个数组包含 256 个字。


框架与协议理解4--分析两种采样收集覆盖率策略(data from bus or reg model)

cov、subscriber的write函数中用bus上的数据来进行采样

数据从transaction data里面来,而不是reg里面,因为此时reg model还没更新。bus上的数据通过更新数据就是寄存器模型的数据,在bus上拿到了数据,但不代表这时候寄存器模型就已经更新了,这需要时间的。因此用bus上的数据来进行采样

cov、subscriber的do_listen_events任务中用寄存器模型上的数据来进行采样

框架与协议理解5--具体的采样收集覆盖率策略(uvm_reg_map)

map 主要用于寻址,将 uvm_reg 变量与地址关联起来。map 完成后,操作 uvm_reg 时,就不用考虑寻址了。
reg_block 中一般至少有 1 个 map。
get_reg_by_offset:在偏移处映射寄存器 通过rgm对应的地址找到该寄存器
本gpio工程,在父类subscriber的write函数中,当AHB接收到transaction,接收到的是address信息, 将这个address信息翻译得到目前在访问哪个register,根据register的不一样去判断对哪些信息去做采样。然后触发事件gpio_reg_acc_fd_e
在子类中cov的write函数中进行采样

 

//这个是所见即所得
//write函数父类trigger 子类sample ,父类和子类都用cur_reg访问
//这种情况是monitor连接到子类
  function void write(lvc_ahb_transaction tr);
    super.write(tr); //注意这个继承要给参数,因为父类和子类的write函数都有作用的
    if(tr.xact_type == lvc_ahb_pkg::WRITE)begin
      case (cur_reg.get_name())
      //注意:get_name得到的是字符串类型
      //严格来讲,只采用这16bit tr.data[0][15:0]
        "DATA":      cg_reg_data_write.sample(tr.data[0]);  
        "DATAOUT":   cg_reg_dataout_write.sample(tr.data[0]);
        "INTENSET":  cg_reg_intenset_write.sample(tr.data[0]);
        "INTENCLR":  cg_reg_intenclr_write.sample(tr.data[0]);
      endcase
    end
    else if(tr.xact_type == lvc_ahb_pkg::READ)begin
        case (cur_reg.get_name())
        "DATA":      cg_reg_data_read.sample(tr.data[0]);  
        "DATAOUT":   cg_reg_dataout_read.sample(tr.data[0]);
        "INTENSET":  cg_reg_intenset_read.sample(tr.data[0]);
        "INTENCLR":  cg_reg_intenclr_read.sample(tr.data[0]);
      endcase
    end
  endfunction 
//这个目标是,以后从更新的寄存器模型里面读,do_listen_events会起到作用
  task do_listen_events();
    uvm_object tmp;
    uvm_reg r;
    super.do_listen_events();
    fork
      forever begin
        wait(cfg.enable_cov);//cover model 可以不做采样
        gpio_reg_acc_df_e.wait_trigger_data(tmp);//
        void'($cast(r,tmp));
        #1ps;//wait time for reg model updated
        case(r.get_name())
        
        endcase
      end
    join_none
  endtask

 

框架与协议理解6--寄存器覆盖率收集的思路(可能要添加其他测试用例)

GPIO的portin和portout不是一个配置寄存器,它的写和读有不同含义,见协议
portin有自己的write read,portout有自己的write read
目前的三个测试用例只覆盖了portin 的read,portout 的write read,后续需要自己修改或添加测试用例来提高覆盖率


MASKLOWBYTE MASKLOWBYTE 32位256长度的数组 的覆盖率收集
思路:首先要关心256个的address是否都要访问,因为就是mask,比如你可以先cover 地址0x0400 和 地址0x07FC 这种全部翻转的情况
关心的address是,这8bit的每一位有没有翻转过,这意味着mask被设置过。以及此时对应的data有没有设置成0,有没有设置成1.

 

框架与协议理解7--中断生成测试用例

大致思路:
1.设置中断类型、极性
2.触发中断
3.使能中断
4.监测中断(通过接口检查中断、通过寄存器检查中断)
5.清除中断
6.清除中断类型、极性

具体代码:

  1 `ifndef RKV_GPIO_INTERRUPT_SET_CLEAR_VIRT_SEQ_SV
  2 `define RKV_GPIO_INTERRUPT_SET_CLEAR_VIRT_SEQ_SV
  3 
  4 
  5 class rkv_gpio_interrupt_set_clear_virt_seq extends rkv_gpio_base_virtual_sequence;
  6   `uvm_object_utils(rkv_gpio_interrupt_set_clear_virt_seq)
  7 
  8   function new (string name = "rkv_gpio_interrupt_set_clear_virt_seq");
  9     super.new(name);
 10   endfunction
 11 
 12   virtual task body();
 13     bit [31:0] addr,data;
 14     bit [3:0]  int_num;
 15     bit [15:0] set_bits;
 16     bit bit_pattern[$] = {0,1};
 17     RKV_INT_POL_T intpol;
 18     RKV_INT_TYPE_T inttype;
 19     super.body();
 20 
 21     `uvm_info("body", "Entered...", UVM_LOW)
 22     for (int i=0; i<16; i++) begin
 23         foreach (bit_pattern[j]) begin
 24             foreach (bit_pattern[k]) begin
 25                 set_bits = bit_pattern[j] << i;   //移位运算符,bit_pattern向左移动i位。即用于设置每一个GPIO pin的中断拉起,对每一路执行中断type pol的两两组合
 26                 set_inttypeset(set_bits,i);
 27                 inttype = RKV_INT_TYPE_T'(bit_pattern[j]); //数值转化成RKV_INT_TYPE_T的值的形式
 28             
 29                 set_bits = bit_pattern[k] << i;
 30                 set_intpolset(set_bits,i);
 31                 intpol = RKV_INT_POL_T'(bit_pattern[k]);
 32                 int_set_and_check(i,inttype,intpol); //中断设置和检查
 33             end
 34         end
 35     end
 36     `uvm_info("body", "Exiting...", UVM_LOW)
 37   endtask
 38 //中断type和中断pol 两两组合有四种情况,然后每一位都要执行4中情况,即16*4=64个
 39 //电平类型 低、电平类型 高、边沿类型 低、边沿类型 高
 40 //每一个pin的GPIO(共十六位 十六个pin),经过bit_pattern 0 1,设置对应的中断type,并记录下inttype中,内嵌循环,经过bit_pattern 0 1,设置对应的中断pol,并记录下intpol中
 41 //然后调用int_set_and_check,使能中断设置,后延时#100ns,清除中断各个设置
 42 
 43 
 44 //正确的逻辑是应该先触发一个中断,然后在使能中断,不同模式设置给不同的信号驱动,使其触发中断,如要触发低电平中断,那么给信号先高电平,后低电平即可触发中断
 45 
 46 
 47 
 48 
 49 //0.设置中断类型、极性 1.触发中断 2.使能中断 3.监测中断(通过接口检查中断、通过寄存器检查中断) 4.清除中断 5.清除中断类型、极性
 50 
 51     task int_set_and_check(int id,RKV_INT_TYPE_T inttype ,RKV_INT_POL_T intpol);
 52         bit[15:0] set_bits;
 53     //    set_bits = 1'b1 << id;      //1向左移i位,当id=0时表示 在pin0中设置拉起 -----------对应1
 54 //check interrupt bit is inactive  在检查之前 确保是中断未使能
 55         check_int_via_intf(id,0); 
 56         check_int_via_reg(id,0);
 57         case ({inttype.intpol})
 58             {ACTIVE_LEVEL,ACTIVE_LOW}:begin    
 59                 vif.portin[id] <= 1'b1;
 60                 wait_cycles(10,CLK_FCLK);
 61                 vif.portin[id] <= 1'b0;
 62             end
 63             {ACTIVE_LEVEL,ACTIVE_HIGH}:begin
 64                 vif.portin[id] <= 1'b0;
 65                 wait_cycles(10,CLK_FCLK);
 66                 vif.portin[id] <= 1'b1;
 67             end
 68             {ACTIVE_EDGE,ACTIVE_LOW}:begin
 69                 vif.portin[id] <= 1'b1;
 70                 wait_cycles(10,CLK_FCLK);
 71                 vif.portin[id] <= 1'b0;
 72             end
 73             {ACTIVE_EDGE,ACTIVE_HIGH}:begin
 74                 vif.portin[id] <= 1'b0;
 75                 wait_cycles(10,CLK_FCLK);
 76                 vif.portin[id] <= 1'b1;
 77             end            
 78         endcase
 79 
 80 //根据协议需要三个FCLK周期,其中两个用于输入信号同步的周期和一个用于记录中断状态。
 81 //因此,等待三个或者多个都是可以的,至于为什么4个周期,只是因为lvc_driver的采样行为是在上升沿,若用三个周期延迟会产生delta cycle
 82 //因此如果lvc_driver是下降沿也可以三个周期满足,改lvc vip的driver的采样时序是一种策略,直接延迟4个周期的策略显然更加简便。
 83 
 84         set_bits = 1'b1 << id;      //1向左移i位,若id=0表示 在pin0中设置拉起 
 85 //check interrupt bit is active after PORTIN changes with INTTYPE
 86 // & INTPOL
 87         set_intenset(set_bits,id);  //
 88         wait_cycles(4,CLK_FCLK);
 89         check_int_via_intf(id,1);//检查中断是否生效
 90         check_int_via_reg(id,1);//花费一拍的时间去读一个值
 91 
 92 //check interrupt bit is inactive after INTENCLR & INTCLEAR is set
 93         set_intenclr(set_bits);//清除中断使能
 94         set_intclear(set_bits);//清除中断请求
 95         wait_cycles(4,CLK_FCLK);
 96         check_int_via_intf(id,0);
 97         check_int_via_reg(id,0);
 98 
 99         set_inttypeclr(set_bits);//清除中断类型
100         set_intpolclr(set_bits);//清除中断极性
101         
102         vif.portin[id] <= 1'b0;//我的理解加上的,测完一个pin,重新置为0,这样波形图会比较清楚
103     endtask
104 
105 //通过接口检查中断
106     task check_int_via_intf(int id, bit val);
107         compare_data(vif.gpioint[id],val);
108     endtask
109 //通过寄存器检查中断
110     task check_int_via_reg(int id, bit val);
111         uvm_status_e status;
112         bit[15:0] gpioint;
113         rgm.INTSTATUS.read(status,gpioint);  //在这个地址读取状态,映射到一个寄存器
114         compare_data(gpioint[id],val);
115     endtask
116 
117 endclass
118 
119 
120 `endif 
View Code

 

框架与协议理解8--掩码访问测试用例

 

大致思路:mask access机制 1.address的方式 2.rgm的方式

1.正常先读后写 2.portout mask access(rgm addr) 3.portin mask access(rgm addr)

 

具体代码:

 1 `ifndef RKV_GPIO_MASKED_ACCESS_VIRT_SEQ_SV
 2 `define RKV_GPIO_MASKED_ACCESS_VIRT_SEQ_SV
 3 
 4 
 5 class rkv_gpio_masked_access_virt_seq extends rkv_gpio_base_virtual_sequence;
 6     `uvm_object_utils(rkv_gpio_masked_access_virt_seq)
 7 
 8     function new (string name = "rkv_gpio_masked_access_virt_seq");
 9     super.new(name);
10     endfunction
11 
12     virtual task body();
13         super.body();
14         `uvm_info("body", "Entered...", UVM_LOW)
15         repeat(10)begin
16             set_and_check_masked_access(0); //via address
17             set_and_check_masked_access(1); //via RGM
18         end
19         `uvm_info("body", "Exiting...", UVM_LOW)
20     endtask
21 
22 //1.正常先读后写 2.portout mask access(rgm addr) 3.portin mask access(rgm addr)
23 
24 //注意:写入或驱动后需要等一些时钟后才能读取或检测到数据
25     task set_and_check_masked_access(bit via_rgm = 0);
26         logic[15:0] portout_reg_write;//前一次写进portout的值
27         logic[15:0] portout_port_mon;
28         logic[15:0] portout_expected;//mask 处理后的值
29         logic[15:0] maskbyte_reg_write,maskbyte_reg_read;//mask写进的值,mask读出的值
30         logic[15:0] portin_port_drv;
31         bit [15:0] mask;
32         bit [15:0] addr_masklowbyte,addr_maskhighbyte;
33         uvm_status_e status;
34         std::randomize(portout_reg_write,mask,maskbyte_reg_write,portin_port_drv);
35 
36     //check PORTOUT  常用的先读后写
37       set_portout_bits(portout_reg_write); // 写入--检测
38       wait_cycles(2);   //one-cycle from io-bridge to portout
39       portout_port_mon = vif.portout;
40       compare_data(portout_reg_write,portout_port_mon);
41       
42     //check PORTOUT mask access机制
43         //calculate addr
44         addr_masklowbyte = RKV_ROUTER_REG_ADDR_MASKLOWBYTE + (mask[7:0] << 2 );
45         addr_maskhighbyte = RKV_ROUTER_REG_ADDR_MASKHIGHBYTE + (mask[15:8] << 2);
46         
47         //set MASKLOWBYTE and MASKHIGHBYTE
48         if (!via_rgm) begin
49             write_reg_with_addr(addr_masklowbyte, maskbyte_reg_write & 16'h00FF);
50             write_reg_with_addr(addr_maskhighbyte, maskbyte_reg_write & 16'hFF00);
51 
52         end
53             //因为在协议中,masklowbyte地址只有低八位有效,maskhighbyte地址只有高八位有效,所以只写8位置1,另外8位置0
54             
55             //注意:这是对整个寄存器255位的一个mask access
56             //MASKHIGHBYTE、MASKLOWBYTE是一个共32位x 256的数组   而7:0或者15:8刚好也是255位的一个向量,必须加上
57         else begin
58             rgm.MASKLOWBYTE[mask[7:0]].write(status,maskbyte_reg_write & 16'h00FF);
59             rgm.MASKHIGHBYTE[mask[15:8]].write(status,maskbyte_reg_write & 16'hFF00);
60         end
61         //check PORTOUT w/wo MASKED-BYTE
62         //写完之后,等两拍使输入信号同步
63             //mask的某一位如果是1,使用portout mask那位的值 ;如果是0,使用portout write那位的值
64             //符合协议内容
65         wait_cycles(2);
66         foreach (portout_expected[i]) begin
67             portout_expected[i] = mask[i] ? maskbyte_reg_write[i] : portout_reg_write[i]; 
68 
69         end
70         compare_data(vif.portout,portout_expected);
71 
72 //用monitor检测vif上的信号检查portout,用寄存器的read来数据来检查portin
73 //read那个被mask后的值 然后去比较
74         //TODO:: check if design is consistent with the check intention below      
75         //check read addr value from MASKED-BYTE
76         vif.portin = portin_port_drv;
77         wait_cycles(4,CLK_FCLK);
78         if(!via_rgm)begin
79             read_reg_with_addr(addr_masklowbyte,maskbyte_reg_read);
80             compare_data(maskbyte_reg_read,portin_port_drv & mask & 16'h00FF);
81             
82             read_reg_with_addr(addr_maskhighbyte,maskbyte_reg_read);
83             compare_data(maskbyte_reg_read,portin_port_drv & mask & 16'hFF00);            
84         end
85         else begin
86             rgm.MASKLOWBYTE[mask[7:0]].read(status,maskbyte_reg_read);
87             compare_data(maskbyte_reg_read,portin_port_drv & mask & 16'h00FF);
88             rgm.MASKHIGHBYTE[mask[15:8]].write(status,maskbyte_reg_read);
89             compare_data(maskbyte_reg_read,portin_port_drv & mask & 16'hFF00);
90         end
91     endtask
92 
93 endclass
94 
95 
96 `endif 
View Code