当前位置: 首页 > news >正文

前端学习 10-2 :验证中的SV

一些学习备注

目录

push_back​

randomize()

constraint

repeat(3) @(posedge vif.clk);

my_monitor.sv

UVM 端口

UVM_FIFO

objection 机制

$value$plusargs


push_back

push_back是 SystemVerilog ​​队列(queue)​​ 的方法,用于在队列末尾添加元素。

在 UVM 中,它主要用于:动态管理数据集合(如事务队列、配置列表),构建灵活的数据结构(如序列项列表、覆盖率事件队列)

​​(1) 队列声明与操作​​

// 声明队列
int q[$];                    // 整数队列
uvm_transaction tr_q[$];    
// UVM事务队列
my_data_t data_queue[$];     // 自定义数据类型队列

// 添加元素到队列末尾
q.push_back(10);            // 队列变为 {10},把10 添加到q的队尾
q.push_back(20);            // 队列变为 {10, 20}

​​(2) UVM 中的典型应用​

// 在sequence中收集生成的transaction, // 监测接口并捕获事务
class my_sequence extends uvm_sequence;uvm_transaction tr_queue[$];task body();for (int i = 0; i < 10; i++) beginmy_transaction tr;tr = my_transaction::type_id::create("tr");//工厂创建对象tr.randomize();tr_queue.push_back(tr);  // tr值添加到队列tr_queue里endendtask
endclass

data_q是动态队列​​(bit [7:0] data_q[$]),其大小会随着 push_back自动增长

task my_monitor::collect_one_pkt(my_transaction tr);
    bit[7:0] data_q[$];
    int psize;
    while(1) begin  
        @(posedge vif.clk);         //// 等待时钟上升沿
        if(vif.valid) break;         // 如果 valid=1,退出循环
    end
    
     `uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);
  
    while(vif.valid) begin           //只要vif.valid信号为高电平(1),就在每个时钟上升沿将                                                 vif.data存入队列 data_q。
        data_q.push_back(vif.data);         // 将当前数据存入队列
        @(posedge vif.clk);                 // 等待下一个时钟上升沿
   end
   
   //pop dmac
   for(int i = 0; i < 6; i++) begin
        tr.dmac = {tr.mac[39:0],data_q.pop_front()};
    end
   //pop smac
   for(int i = 0; i < 6; i++) begin
      tr.smac = {tr.smac[39:0], data_q.pop_front()};
   end    
   //pop ether_type
   for(int i = 0; i < 2; i++) begin
      tr.ether_type = {tr.ether_type[7:0], data_q.pop_front()};
   end

//pop dmac,循环6次​​(对应MAC地址的6字节长度)。每次从队列 data_q头部弹出一个字节(data_q.pop_front()),并将其拼接到 tr.dmac的低8位

randomize()

​​SystemVerilog 的内置方法​​,由语言标准定义(IEEE 1800),并非 UVM 专属。所有声明了 rand或 randc变量的类都会自动继承此方法。

当类中声明 rand变量时,SystemVerilog 编译器会自动为类添加 randomize()方法。

class my_transaction extends uvm_sequence_item;
    rand bit [31:0] addr;  // 随机变量   
    rand bit [63:0] data;
    bit [7:0]       mode;  // 非随机变量

    constraint valid_addr {
        addr[1:0] == 0;    // 地址对齐约束
    }
endclass

my_transaction tr = new();
if (!tr.randomize()) 
    `uvm_error("RAND", "Randomization failed")

constraint

SystemVerilog 中用于​​限制随机变量取值范围​​的构造块,属于​​约束随机验证(CRV)​​的核心机制。

​​核心作用​​:在随机化(randomize())时,自动生成符合设计规则的合法值。

语法​

​作用​

​示例​

inside

限定值范围

addr inside {[0:255], 1024}

dist

权重分布

data dist {0:=20, [1:3]:=80}

==/!=/>/<

关系运算

addr > 32'h0000_FFFF

class my_transaction;
    rand bit [31:0] addr;  // 随机变量
    rand bit [63:0] data;

    // 约束定义
    constraint valid_range {
        addr inside {[0x1000:0x1FFF]};  // 地址范围限制
        data dist {0 :/ 50, [1:100] :/ 50};  // 数据分布权重
    }
endclass

repeat(3) @(posedge vif.clk);

​​在驱动数据包之前插入3个时钟周期的延迟,repeat(3)​:重复执行后续语句3次,@(posedge vif.clk)​:每次等待 vif.clk的上升沿。组合效果​​:仿真器会暂停当前线程,直到观察到 ​​3个时钟上升沿​​,然后继续执行后续代码。

延迟的目的​​

​​(1) 协议时序对齐​​

​​场景​​:在以太网、AXI 等协议中,发送数据前可能需要空闲周期(IDLE)。

​​作用​​:确保DUT(被测设计)准备好接收数据,避免因信号未稳定导致的冲突。

​​示例​​:

        某些DUT要求数据包之间至少间隔2个时钟周期。

        模拟真实硬件中的信号建立时间(Setup Time)。
 

​(2) 调试与观察​​

​​波形调试​​:在数据包发送前留出空白周期,便于在波形工具(如Verdi)中区分不同数据包。

​​日志同步​​:uvm_info打印日志后,延迟3个周期再驱动数据,避免日志与波形不同步。
 

​​(3) 避免竞争条件​​

​​风险​​:若直接驱动数据,可能与DUT的复位或其他控制信号冲突。

​解决​​:延迟3个周期确保DUT处于稳定状态。

my_monitor.sv

`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
//收集DUT 端口数据(interface),并转换成 transaction 交给后续组件
class my_monitor extends uvm_monitor;virtual my_if vif;`uvm_component_utils(my_monitor)function new(string name="my_monitor",uvm_component parent=null);super.new(name,parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);if(!uvm_config_db#(virtual my_if)::get(this,"","vif",vif))`uvm_fatal("my_monitor","virtual interface must be set for vif!!!")endfunctionextern task main_phase(uvm_phase); // 因为注册在工厂里,所以可以直接引用extern task collect_one_pkt(my_transaction tr);endclss//在循环里新建对象,每次捕获到有效数据时生成一个​​独立的事务对象​​。类似于一张张照片
task my_monitor::main_phase(uvm_phase phase);my_transaction tr;while(1) begin //monitor需要一直活动,所以 while(1)tr = new("tr"); // 创建事务对象collect_one_pkt(tr); //收集事务end
endtasktask my_monitor::collect_one_pkt(my_transaction tr);bit[7:0] data_q[$];int psize;while(1) begin  @(posedge vif.clk); //// 等待时钟上升沿if(vif.valid) break; // 如果 valid=1,退出循环end`uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);//只要vif.valid信号为高电平(1),就在每个时钟上升沿将 vif.data存入队列 data_q。while(vif.valid) begindata_q.push_back(vif.data); // 将当前数据存入队列@(posedge vif.clk); // 等待下一个时钟上升沿end//pop dmac, 以太网目的地址//循环6次​​,每次从队列 头部弹出一个字节,并将其拼接到 tr.dmac的低8位,bit[7:0] data_q[$];所以每次弹出8位for(int i = 0; i < 6; i++) begintr.dmac = {tr.mac[39:0],data_q.pop_front()};end//pop smac 以太网源地址for(int i = 0; i < 6; i++) begintr.smac = {tr.smac[39:0], data_q.pop_front()};end	//pop ether_type 以太网类型for(int i = 0; i < 2; i++) begintr.ether_type = {tr.ether_type[7:0], data_q.pop_front()};endpsize = data_q.size()-4;tr.pload = new[psize];//pop payload  携带数据大小for(int i = 0; i < psize; i++) begintr.pload[i] = data_q.pop_front();end//pop crc  之前所有数据的校验值for(int i = 0; i < 4; i++) begintr.crc = {tr.crc[23:0], data_q.pop_front()};end`uvm_info("my_monitor", "end collect one pkt, print it:", UVM_LOW);tr.my_print();
endtask`endif

my_driver.sv

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SVclass my_driver extends uvm_driver;virtual my_if vif;`uvm_component_utils(my_driver)function new(string name="my_driver", uvm_component parent=null);super.new(name,parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);if(!uvm_config_db#(virtual my_if)::get(this,"","vif",vif))`uvm_fatal("my_driver","virtual interface must be set for vif!!")endfunctionextern task main_phase(uvm_phase phase);extern task drive_one_pkt(my_transaction tr);endclasstask my_driver::main_phase(uvm_phase phase);my_transaction tr;phase.raise_objection(this);vif.data <= 8'b0;vif.valid <= 1'b0;while(!vif.rst_n)@(posedge vif.clk);for(int i = 0; i < 2; i++) begintr=new("tr"); //创建事务assert(tr.randomize() with {pload.size == 200;});drive_one_pkt(tr);endrepeat(5) @(posedge vif.clk);phase.drop_objection(this);endtasktask my_driver::drive_one_pkt(my_transaction tr);bit [47:0] tmp_data; bit [7:0] data_q[$];  //8位动态队列tmp_data = tr.dmac;	//每次放8 位=1个字节,总共6次for(int i=0; i<6;i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >>8);endtmp_data = tr.smac;	for(int i=0; i<6;i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >>8);end		tmp_data = tr.ether_type;	for(int i=0; i<2;i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >>8);end		//   rand byte      pload[];for(int i=0; i<tr.pload.size;i++) begindata_q.push_back(tr.pload[i]);end		tmp_data = tr.crc;	for(int i=0; i<4;i++) begindata_q.push_back(tmp_data[7:0]);tmp_data = (tmp_data >>8);end					`uvm_info("my_driver","begin to drive one pkt",UVM_LOW);repeat(3) @(posedge vif.clk); //数据包之间间隔3个时钟周期while(data_q.size()>0) begin@(posedge vif.clk);vif.valid <= 1'b1;vif.data <= data_q.pop_front();end@(posedge vif.clk);vif.valid <= 1'b0;`uvm_info("my_driver","end drive one pkt",UVM_LOW);endtask`endif

UVM 端口

   uvm_blocking_get_port #(my_transaction)  port;
   uvm_analysis_port #(my_transaction)  ap;

uvm_blocking_get_port、 uvm_analysis_port是两种不同类型的通信端口(port),它们分别用于不同的数据传输场景。它们都是由UVM库提供的标准类,定义在UVM的基础类库中。

uvm_blocking_get_port #(my_transaction) port;​​

  • ​​所属类​​:uvm_blocking_get_port是UVM标准库的一部分,定义在 uvm_tlm命名空间中。

  • ​​头文件​​:通常包含在 uvm_tlm.svh或 uvm_pkg.sv中(UVM会自动导入)。

  • ​​作用​​:用于​​阻塞式​​的​​点对点​​数据传输(TLM,Transaction Level Modeling)。

​​用途​

  • 用于​​从上游组件(如 sequencer或 driver)获取事务(transaction)​​。

  • get()是一个​​阻塞方法​​,如果端口没有数据,它会一直等待,直到数据到达。

  • 适用于​​生产者-消费者模型​​,其中 my_model是消费者,等待上游组件发送数据。

uvm_analysis_port #(my_transaction) ap;​​

​​来源​​

  • ​​所属类​​:uvm_analysis_port也是UVM标准库的一部分,定义在 uvm_tlm命名空间中。

  • ​​头文件​​:同样包含在 uvm_tlm.svh或 uvm_pkg.sv中。

  • ​​作用​​:用于​​非阻塞式​​的​​广播​​数据传输(一对多通信)。

​​用途​​

  • 用于​​向下游组件(如 scoreboardcoverage collector或 monitor)广播事务​​。

  • write()是一个​​非阻塞方法​​,它会立即将数据发送给所有连接的组件。

  • 适用于​​观察者模式​​,多个组件可以订阅同一个数据流

在UVM中,TLM(Transaction Level Modeling)通信主要分为:

  • ​​阻塞式通信​​(Blocking):uvm_blocking_get_port,uvm_blocking_put_port,uvm_blocking_transport_port
  • 非阻塞式通信​​(Non-blocking):uvm_nonblocking_get_portuvm_nonblocking_put_port
  • ​​分析端口​​(Analysis):uvm_analysis_port(广播),uvm_analysis_export(接收端)

UVM_FIFO

uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
 i_agt.ap.connect(agt_mdl_fifo.analysis_export);
   mdl.port.connect(agt_mdl_fifo.blocking_get_export);

分析:

uvm_tlm_analysis_fifo是一个 ​​UVM TLM FIFO​​,标准库中自定义的类它​​继承自 uvm_tlm_fifo​​,主要解决 analysis_port和 blocking_get_port的兼容性问题。主要特点是​​内部维护一个队列​​,用于缓存事务(my_transaction)。

​​提供两种接口​​:

  • analysis_export:用于接收来自 analysis_port的数据(write()方法)。

  • blocking_get_export:用于提供数据给 blocking_get_portget()方法)。

uvm_tlm_analysis_fifo的核心功能​

功能

说明

write()方法​

通过 analysis_export接收非阻塞广播(来自 analysis_port)。

get()方法​

通过 blocking_get_export提供阻塞式拉取(供 blocking_get_port使用)。

​内部队列​

缓存事务,解决生产者和消费者的速率不匹配问题。

​线程安全​

UVM 自动处理多线程竞争(如 monitor 和 model 运行在不同线程)。

objection 机制

​​objection机制​​ 是用于控制 UVM 仿真阶段(phase)执行流程的核心机制,其主要作用是 ​​防止仿真阶段提前终止​​,确保测试逻辑完整执行。
UVM 的 ​​动态运行阶段​​(即task phase,比如 reset_phasemain_phaseshutdown_phase)都需要 objection控制。最常用的是 sequence、scoreboard。
而 function 类的phase,不需要。

例外:静态阶段​​是自动执行的,无需 objection控制。
function void build_phase(uvm_phase phase);
    super.build_phase(phase);  // 无需 objection
    agent = my_agent::type_id::create("agent", this);
endfunction

$value$plusargs

SystemVerilog 内置函数,用于从命令行解析参数。

从仿真命令行中读取参数 i2c_dma_sel的值,并将其赋给变量 i2c_dma_sel

if(!$value$plusargs("i2c_dma_sel=%d", i2c_dma_sel))  
    i2c_dma_sel = 0;  // 默认值

仿真命令行 示例: 
simv +i2c_dma_sel=2 +UVM_TESTNAME=i2c_dma_test

http://www.dtcms.com/a/364331.html

相关文章:

  • 时序数据库选型指南:为何Apache IoTDB成为工业物联网首选
  • 东土智建 | 让塔吊更聪明的“四大绝技”工地安全效率双升级
  • 亚马逊流量突围:如何用智能化关键词运营找到更多高转化机会?
  • InnoDB存储引擎-事务
  • 企业网络安全建设三阶段实战指南
  • 海康摄像头开发---JSON数据与图片分离
  • VPS云服务器高可用架构部署方案-企业级实施指南
  • “互联网 +”时代商业生态变革:以开源 AI 智能名片链动 2+1 模式 S2B2C 商城小程序为例
  • 深度学习入门:从神经网络基础到 BP 算法全解析
  • 2004-2023年各省生活垃圾无害化处理率数据(无缺失)
  • (11)用于无GPS导航的制图师SLAM(二)
  • 开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信
  • 远程的 develop 比你本地的 develop 更新,Git 拒绝直接覆盖
  • Python爬虫实战: 纵横中文网小说爬取方案与代码实现
  • NLP插曲番外 · 猫猫狐狐问答夜话
  • AI+教育:用BERT构建个性化错题推荐系统
  • Chapter1—设计模式基础
  • More Effective C++ 条款25:将构造函数和非成员函数虚拟化
  • 外网获取瀚高.NET驱动dll方法和使用案例
  • WSL + VSCode + Git + Node.js 开发环境配置文档
  • uni-app与Vue3,实现3D圆柱形旋转画廊效果
  • 人工智能学习:什么是RNN模型
  • VMware Workstation 磁盘空间不足扩容
  • 二、Scala流程控制:分支与循环
  • C题目训练【三连击】
  • 【正则表达式】 正则表达式有哪些语法?
  • Spring中stereotype注解
  • Shell-AWK详解
  • EasyMeeting-注册登录
  • FART 自动化脱壳框架优化实战:Bug 修复与代码改进记录