AHB GPIO项目理解
框架与协议理解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
框架与协议理解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