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

前端学习 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.xdnf.cn/news/19807.html

相关文章:

  • Qt使用Maintenance添加、卸载组件(未完)
  • Java 技术支撑 AI 系统落地:从模型部署到安全合规的企业级解决方案(四)
  • 嵌入式学习 51单片机(2)
  • 【C++】string类完全解析与实战指南
  • centos 压缩命令
  • (二)文件管理-基础命令-mkdir命令的使用
  • Linux应用(1)——文件IO
  • 部署jenkins并基于ansible部署Discuz应用
  • 嵌入式|RTOS教学——FreeRTOS基础3:消息队列
  • Unity之Spine动画资源导入
  • 小游戏公司接单难?这几点原因与破局思路值得看看
  • 聚焦诊断管理(DM)的传输层设计、诊断服务器实现、事件与通信管理、生命周期与报告五大核心模块
  • RTSP流端口占用详解:TCP模式与UDP模式的对比
  • 面向深层语义分析的公理化NLP模型:理论可行性、关键技术与应用挑战
  • 大语言模型领域最新进展
  • 如何将JPG图片批量转为PDF?其实可用的方法有很多种
  • TC-2024《Fuzzy Clustering guided by Spectral Rotation and Scaling》
  • shell-awk命令详解(理论+实战)
  • 通过IDEA写一个服务端和一个客户端之间的交互
  • 解决通过南瑞加密网关传输文件和推送视频的失败的问题
  • PyTorch 面试题及详细答案120题(116-120)-- 综合应用与实践
  • 专项智能练习(音频基础)
  • 水泵运行组态监控系统御控物联网解决方案
  • 基于SpringBoot的旅游管理系统
  • 03 - HTML常用标签
  • Nano Banana 的 100 种用法 - AI 图像生成完整提示词宝典
  • 超低延迟RTSP播放器的技术挑战与跨平台实现之道
  • 【GitOps】Argo CD部署应用程序
  • 嵌入式|RTOS教学——FreeRTOS基础2:任务调度
  • 【mac】如何在 macOS 终端中高效查找文件:五种实用方法