UVM组件故事版 · driver:那个把"指令"翻译成"电信号"的人

UVM组件故事版 · driver:那个把"指令"翻译成"电信号"的人


想象一下打仗。

将军坐在指挥部里,写了一份作战命令:"明天凌晨三点,从A点向B点发起进攻,炮火掩护从C方向过来。"

这份命令写得很清楚,但问题来了------士兵在战壕里,拿到的是一张写满字的纸。他们听不懂"炮火掩护从C方向"是什么意思,他们只知道:什么时候该扣扳机,往哪个方向开枪,火力往哪打。

中间需要一个人,把将军的命令翻译成士兵能执行的具体动作。

这个翻译官,就是driver。


driver到底在干什么

在UVM的世界里,这个场景是这样的:

arduino 复制代码
sequence(将军)→ 发送一个transaction:"我想要你发一个读命令,读地址0x1000"
    ↓
sequencer(传令兵)→ 把transaction传递给driver
    ↓
driver(翻译官)→ 把transaction转换成具体的时序信号,送到DUT的接口上
    ↓
DUT(士兵)→ 收到电信号,执行实际操作

driver做的事情本质上是两件:

1. 接收上层传来的数据对象(transaction)

2. 把这个对象"翻译"成DUT能看懂的时序信号

比如DUT是一个UART模块,它的接口信号长这样:

复制代码
uart_tx    → 发送数据的信号
baud_rate  → 波特率信号

driver从sequence那里拿到一条"发数据0x55"的消息,driver要做的事情是:

markdown 复制代码
1. 把0x55转成二进制:01010101
2. 按照UART协议加上起始位、校验位、停止位
3. 在对应的时钟沿上,把这些bit一个个送到uart_tx信号上

这中间涉及到很精确的时序控制------早了不行,晚了不行,数据宽度不对不行。driver就是那个必须分毫不差的人。


为什么会有人把driver写错

见过两种典型错误:

第一种:把所有事情都塞给driver

有人觉得driver是唯一能接触DUT的人,所以恨不得把所有的判断逻辑都写在driver里:

kotlin 复制代码
driver里写:if (data == 0x55) { 发送A } else if (data == 0xAA) { 发送B }

这是新手常干的事情。driver不是用来判断"做什么"的地方,那是sequence和test的工作。driver的职责是:我收到了一个任务,把它正确地执行下去。

第二种:不懂得和sequencer配合

driver和sequencer之间有一个握手机制:

arduino 复制代码
driver发送req给sequencer:"我准备好了,给我下一个任务"
sequencer把下一个transaction传给driver
driver执行,然后再次请求

这个握手叫seq_item_port,是UVM里最常见的组件间通信方式。很多人写driver的时候跳过了这个理解,导致driver只跑一次就卡住了,或者跑起来之后和sequence的节奏完全对不上。


driver的正确打开方式

一个正确的driver,通常长这样:

scss 复制代码
class uart_driver extends uvm_driver #(uart_transaction);
  virtual uart_if vif;  // 硬件接口的虚接口
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (!uvm_config_db #(virtual uart_if)::get(this, "", "vif", vif))
      `uvm_fatal("NOVIF", "virtual interface must be set");
  endfunction
  
  // driver的核心:不断从sequencer拿任务,执行它
  task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);  // 从sequencer拿任务(阻塞等待)
      drive_transaction(req);             // 执行任务
      seq_item_port.item_done();          // 告诉sequencer任务完成了
    end
  endtask
  
  // 把transaction翻译成时序信号
  virtual protected task drive_transaction(uart_transaction tr);
    vif.rst_n <= 0;  // 假设要reset一下
    #10;
    vif.rst_n <= 1;
    
    // 发送起始位
    vif.uart_tx <= 1'b0;
    #(BIT_PERIOD);
    
    // 发送8位数据
    for (int i = 0; i < 8; i++) begin
      vif.uart_tx <= tr.data[i];
      #(BIT_PERIOD);
    end
    
    // 停止位
    vif.uart_tx <= 1'b1;
    #(BIT_PERIOD);
  endtask
endclass

核心循环就三步:

scss 复制代码
get_next_item() → drive_transaction() → item_done()

理解了这三步,driver就不难了。


打个比方收尾

sequence是将军,写作战计划。sequencer是传令官,把计划传递给翻译官。driver是翻译官,把将军的命令翻译成士兵能执行的具体动作------几时几分,从哪个方向,开什么枪。

翻译官不需要知道为什么要打这一仗,那是将军的事。翻译官只需要把命令准确无误地执行到位。

driver在UVM验证环境里的角色,就是这样:精确执行,不多不少。


下篇预告:monitor------那个躲在角落里,把一切看见的都记下来的人。




UVM军事系列 · 第一篇:driver------那个把命令翻译成战场语言的人


特种作战行动开始前,指挥部传来一份任务书:

"凌晨0300,A组从东侧突入,B组掩护,C组接应。炮火支援在C组进入位置后30秒启动。"

这份任务书写得清清楚楚,但问题是------前线的士兵拿到的是无线电里嘈杂的指令,他们听不懂"炮火支援在C组进入位置后30秒启动"这种协调语言,他们只知道:什么时候冲,往哪开枪,枪口抬多高。

中间需要一个人,把指挥部的命令翻译成前线士兵能执行的具体动作。

这个人叫通讯兵 ,也叫传译官

在UVM的世界里,这个角色,叫driver


driver到底在干什么

特种部队的通讯兵,是指挥部和前线之间唯一的翻译通道。

他的工作看起来很简单:收到命令,执行命令。但细看下去,每一步都藏着细节:

复制代码
mission_order(任务书)→ 作战参谋排序 → 通讯兵接收任务 → 翻译成战场信号 → 士兵执行
    指挥部                      调度官                通讯兵              物理动作

driver做的事情本质上只有两件:

第一,接收上层传来的指令对象(mission order)。

第二,把这个指令翻译成DUT能看懂的时序信号。

比如,DUT是一个通信电台模块,它的物理接口信号长这样:

css 复制代码
tx_data[7:0]    → 8位数据线
tx_valid        → 数据有效信号(高电平表示data有效)
tx_ready        → 电台准备好了(握手信号)
clk             → 时钟信号

driver从sequencer那里拿到一条"发送数据0xA5"的消息,driver要做的事情是:

arduino 复制代码
第一步:在tx_valid上拉高一个时钟周期,同时把0xA5放到tx_data上
第二步:等待tx_ready握手信号(告诉士兵"我收到命令了")
第三步:清空tx_valid,一个命令执行完毕

这中间涉及精确的时序控制------早了,电台还没准备好,数据丢了;晚了,士兵的火力窗口错过了。driver就是那个必须分毫不差的人。


为什么会有人把通讯兵派错岗位

见过两种典型的错误:

第一种:让通讯兵去决定打不打。

有人觉得通讯兵是唯一能接触前线的人,所以把"战术判断"也塞给他:

arduino 复制代码
driver里写:if (urgent_mission) { 立即执行 } else { 排队等 }

这是新手常干的事。通讯兵不是战术决策者,那是作战参谋的活。通讯兵的职责是:我收到命令了,准确地翻译和执行。 翻译官不需要知道为什么要打这一仗,那是将军的事。

第二种:通讯兵不听参谋的调度,擅自行动。

driver和sequencer之间有一个握手机制:

arduino 复制代码
通讯兵(driver):"我执行完了,下一个任务是什么?"
作战参谋(sequencer):"收到,给你新的任务。"
通讯兵执行新任务,再问:"执行完了,下一个呢?"
......循环往复

这个握手在UVM里叫seq_item_port,是UVM中最常见的通讯方式。多数人写driver的时候跳过了这个理解,导致通讯兵只跑一轮就停在那里,或者自顾自地一直发,完全不听参谋的节奏。


driver的正确打开方式

一个正确的通讯兵(driver),是这样的:

scala 复制代码
class comm_driver extends uvm_driver #(mission_item);
  virtual radio_if vif;  // 电台物理接口
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // 从配置池里拿到电台接口
    if (!uvm_config_db #(virtual radio_if)::get(this, "", "vif", vif))
      `uvm_fatal("NORADIO", "电台接口未配置,通讯兵没有电台怎么打仗?")
  endfunction
  
  // 通讯兵的日常:等命令 → 执行 → 报告完成 → 等下一个
  task run_phase(uvm_phase phase);
    forever begin
      // 从作战参谋那里拿到任务(阻塞等待,没有任务就待命)
      seq_item_port.get_next_item(req);  
      transmit_mission(req);              // 执行任务
      seq_item_port.item_done();           // 报告参谋:任务完成
    end
  endtask
  
  // 具体的翻译工作:把任务对象变成电台信号
  virtual protected task transmit_mission(mission_item tr);
    // 第一步:把数据放到数据线上,同时拉高有效信号
    @(posedge vif.clk);
    vif.tx_valid <= 1'b1;
    vif.tx_data  <= tr.payload;
    
    // 第二步:等待电台握手确认
    do begin
      @(posedge vif.clk);
    end while (vif.tx_ready == 1'b0);  // 电台没ready就一直等
    
    // 第三步:任务完成,清除有效信号
    @(posedge vif.clk);
    vif.tx_valid <= 1'b0;
  endtask
endclass

核心循环就三步:

scss 复制代码
get_next_item()  →  transmit_mission()  →  item_done()
  从参谋拿任务       执行翻译              报告完成

理解这三步,通讯兵就不难当了。


打个比方收尾

sequence是将军,写作战命令。sequencer是作战参谋,把命令排序整理。driver是通讯兵,把参谋整理好的命令翻译成前线士兵能执行的具体动作------几点几分,从哪个方向,打什么目标,打几发。

通讯兵不需要知道为什么要打这一仗,那是将军的事。通讯兵只需要把命令准确无误地传达下去。

迟一秒不行,早一秒也不行,信号错了更不行。

这就是driver在UVM验证环境里的角色:精确执行,不多不少。


下篇预告:monitor------那个躲在暗处,把战场上发生的一切都记录下来的人。侦察兵不上前线,但战场上没有人比他更清楚发生了什么。

相关推荐
至为芯7 小时前
IP5356H_G3至为芯支持双C快充的22.5W新国标移动电源方案芯片
集成电路·芯片·电子元器件
婷婷_1728 小时前
【PCIe 验证每日学习・Day26】PCIe 错误处理与异常恢复机制
网络·学习·程序人生·芯片·原子操作·pcie 验证
嵌入式小企鹅11 小时前
RISC-V爆发、AI编程变天、半导体涨价潮
物联网·学习·ai编程·开发工具·risc-v·芯片·工具链
婷婷_1721 天前
DWC Ethernet QoS VLAN高级功能深度解析
网络·学习·程序人生·ethernet·芯片·vlan·gmac
ZenasLDR2 天前
Type-C接口LDR多协议取电芯片
接口·芯片·usb
嵌入式小企鹅2 天前
阿里编程模型赶超、半导体涨价蔓延、RISC-V新品密集上线
人工智能·学习·ai·程序员·risc-v·芯片
婷婷_1722 天前
DWC Ethernet QoS VLAN功能实现详解
网络·学习·程序人生·ethernet·芯片·vlan·gmac
婷婷_1723 天前
深入理解VLAN:从原理到实践(基于DesignWare Ethernet QoS)
网络·学习·程序人生·ethernet·芯片·gmac
婷婷_1724 天前
【PCIe验证每日学习·Day25】PCIe 电源管理机制(L0s/L1/L2/L3)全解析
网络·学习·程序人生·芯片·电源管理·pcie 验证·低功耗状态