You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何保证UVM组件中TLM FIFO读写进程的执行顺序?

问题描述

需要实现一个包含两个TLM输入FIFO和一个输出分析端口(AP)的UVM组件:

  • 一个FIFO接收用于构建内部状态的写数据包
  • 另一个FIFO接收查询内部状态的读请求数据包
  • 输出AP负责广播读请求对应的状态

以下是缓存建模的简化代码(省略new/build等方法):

class cache_model extends uvm_component;
  `uvm_component_utils(cache_model)

  // 两个TLM分析FIFO输入
  uvm_tlm_analysis_fifo#(cache_write_pkt_t) write_af;
  uvm_tlm_analysis_fifo#(tag_t) read_query_req_af;
  
  // 查询响应输出端口
  uvm_analysis_port#(data_t) read_query_rsp_ap;

  // 内部建模状态
  data_t cache[tag_t];

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    fork
      forever process_writes();
      forever process_queries();
    join_none
  endtask

  protected task process_writes();
    cache_write_pkt_t pkt;
    write_af.get(pkt);
    // 将数据包转换为tag和data并写入缓存
  endtask

  protected task process_queries();
    tag_t tag;
    read_query_req_af.get(tag);
    read_query_rsp_ap.write(cache[tag]);
  endtask
endclass

当前遇到的问题:同一仿真时间步内同时存在写操作和读操作时,需要保证先执行写操作,再执行读操作(让读操作获取最新写入的数据),但FIFO的数据包推入顺序无法保证这一点。之前尝试用事件同步的方案无效,因为process_queries判断write_af是否为空时,写数据包可能还未在同一时间步的后续阶段被推入FIFO。

解决方案

方案一:统一调度进程(推荐)

放弃两个独立的forever进程,改用单个循环进程优先处理写操作,再处理读操作,从根源上避免进程竞争:

class cache_model extends uvm_component;
  `uvm_component_utils(cache_model)

  uvm_tlm_analysis_fifo#(cache_write_pkt_t) write_af;
  uvm_tlm_analysis_fifo#(tag_t) read_query_req_af;
  uvm_analysis_port#(data_t) read_query_rsp_ap;

  data_t cache[tag_t];

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    fork
      forever process_transactions();
    join_none
  endtask

  protected task process_transactions();
    cache_write_pkt_t write_pkt;
    tag_t read_tag;

    // 等待任一FIFO有数据
    wait(write_af.num_items() > 0 || read_query_req_af.num_items() > 0);

    // 优先处理所有待处理的写操作
    while (write_af.try_get(write_pkt)) begin
      // 转换数据包并更新缓存
      cache[write_pkt.tag] = write_pkt.data;
    end

    // 处理读请求
    if (read_query_req_af.try_get(read_tag)) begin
      read_query_rsp_ap.write(cache[read_tag]);
    end
  endtask
endclass

核心逻辑:每次循环先清空所有待处理的写请求,再处理读请求。即使同一时间步内写和读数据包同时被推入FIFO,写操作会被优先处理,确保读请求拿到最新缓存状态。

方案二:事件同步+时间步确认

如果必须保留两个独立进程,可以通过uvm_event结合时间戳判断,确保读操作等待同一时间步内的所有写操作完成:

class cache_model extends uvm_component;
  `uvm_component_utils(cache_model)

  uvm_tlm_analysis_fifo#(cache_write_pkt_t) write_af;
  uvm_tlm_analysis_fifo#(tag_t) read_query_req_af;
  uvm_analysis_port#(data_t) read_query_rsp_ap;

  data_t cache[tag_t];
  uvm_event write_done_ev;

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    write_done_ev = new("write_done_ev");
    fork
      forever process_writes();
      forever process_queries();
    join_none
  endtask

  protected task process_writes();
    cache_write_pkt_t pkt;
    write_af.get(pkt);
    // 更新缓存
    cache[pkt.tag] = pkt.data;
    write_done_ev.trigger();
  endtask

  protected task process_queries();
    tag_t tag;
    time current_time;

    read_query_req_af.get(tag);
    current_time = $time;

    // 等待同一时间步内的所有写操作完成
    while (write_done_ev.get_trigger_time() == current_time) begin
      write_done_ev.reset();
      wait(write_done_ev.triggered());
    end

    read_query_rsp_ap.write(cache[tag]);
    // 重置事件,为下一个时间步做准备
    if (write_done_ev.get_trigger_time() == current_time) begin
      write_done_ev.reset();
    end
  endtask
endclass

核心逻辑:利用uvm_event的get_trigger_time()方法判断事件是否在当前时间步触发,确保读操作等待同一时间步内所有写操作完成后再执行。

关键方法论总结
  • 优先统一调度:当多个进程存在依赖时,用单个调度进程按优先级处理不同事务,从根源避免进程竞争,代码更易维护。
  • 不依赖FIFO写入顺序:TLM分析FIFO的写入顺序由外部组件决定,无法保证,组件内部不能依赖该顺序来保证处理优先级。
  • 慎用#0延迟:#0会强制跳转到当前时间步末尾,但可能引入不可预测的调度行为,仅作为最后的备选方案。

内容的提问来源于stack exchange,提问作者Sean McLoughlin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 06:35:37