前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【UVM COOKBOOK】Sequences||Sequencer与Driver-Sequence API

【UVM COOKBOOK】Sequences||Sequencer与Driver-Sequence API

作者头像
空白的贝塔
发布2021-11-17 10:19:40
1.2K0
发布2021-11-17 10:19:40
举报
文章被收录于专栏:摸鱼范式摸鱼范式摸鱼范式

Sequencer

sequence和它们的目标driver之间的req和rsp item的传输是通过在sequencer中实现的双向 TLM 通信机制来实现的。uvm_driver 类包含一个 uvm_seq_item_pull_port,它和sequencer中的 uvm_seq_item_pull_export。port和export类是sequence_items 类型参数化的。一旦建立了port和export连接,driver代码就可以使用export中实现的 API 从sequence中获取请求 sequence_items 并将rsp返回给它们。

上图是连接关系,下面是对应的代码示例

// Driver parameterized with the same sequence_item for request & response 
// response defaults to request 
class adpcm_driver extends uvm_driver #(adpcm_seq_item); 
 .... 
endclass: adpcm_driver 
// Agent containing a driver and a sequencer - uninteresting bits left out 
class adpcm_agent extends uvm_agent; 
 adpcm_driver m_driver; 
 adpcm_agent_config m_cfg; 
// uvm_sequencer parameterized with the adpcm_seq_item for request & response 
 uvm_sequencer #(adpcm_seq_item) m_sequencer; 
// Sequencer-Driver connection: 
 function void connect_phase(uvm_phase phase); 
    if(m_cfg.active == UVM_ACTIVE) begin  // The agent is actively driving stimulus 
      m_driver.seq_item_port.connect(m_sequencer.seq_item_export);  // TLM connection 
   m_driver.vif = cfg.vif;  // Virtual interface assignment
  end 
 endfunction: connect_phase 

driver和sequencer之间的连接是一对一的,不会出现多对一也不会出现一对多。除了标准端口,driver里还有一个analysis_port,可以连接sequencer中的一个analysis_export,实现driver和sequencer之间的单向响应通信路径。不过这是一个历史遗留组件,不常用,要使用这个口的话,下面是示例代码。

// Same agent as in the previous bidirectional example: 
class adpcm_agent extends uvm_agent; 
 adpcm_driver m_driver; 
 uvm_sequencer #(adpcm_seq_item) m_sequencer; 
 adpcm_agent_config m_cfg; 
// Connect method: 
 function void connect_phase(uvm_phase phase ); 
  if(m_cfg.active == UVM_ACTIVE) begin 
      m_driver.seq_item_port.connect(m_sequencer.seq_item_export);  //Always need this 
      m_driver.rsp_port.connect(m_sequencer.rsp_export);  // Response analysis port connection 
   m_driver.vif = cfg.vif; 
  end 
  //... 
 endfunction: connect_phase 
endclass: adpcm_agent 

如果要在返回rsp的时候通知其他组件,可以用这个口,否则不要用。

Driver-Sequence API

uvm_driver 是 uvm_component 类的扩展,它添加了一个 uvm_seq_item_pull_port,来和sequence通信。uvm_driver 是一个参数化类,它被参数化为req sequence_item 的类型和rsp sequence_item 的类型。进一步地,这些参数用于参数化 uvm_seq_item_pull_port。rsp sequence_item 可以独立参数化的事实意味着driver可以从req类型返回不同的item类型。实际上,大多数driver对req和rsp使用相同的item,因此在源代码中,rsp item类型默认为req item类型。

uvm_driver 类的行为模型是它使用握手通信机制从sequencer req FIFO 中获取sequence_items,并将rsp sequence_items 返回到sequencer rsp FIFO(也可以不返回)。uvm_driver 中 seq_item_pull_port 的句柄是 seq_item_port类型。driver代码用来与sequencer交互的 API 被 seq_item_port 引用,但实际上是在sequencer seq_item_export 中实现的(这是标准的 TLM )。

“说人话就是握手,driver收了可以不发rsp,不过这样sequence就就没有相关信息了。然后由于本质上还是通过tlm进行的,所有通过端口调用的函数都是在目标端口实现的,也就是说是在sequencer里实现的。 ”

UVM Driver API

相关API包括:

get_next_item

这是一个阻塞方法,直到sequence用item_done方法把item发过来。如果在item_done调用之前,进行另一次get_next_item调用会导致握手死锁。

try_next_item

这是 get_next_item() 方法的非阻塞变体。sequencer的item fifo里没东西就会返回一个空句柄,如果有的话,行为就和get_next_item一样

item_done

是一个非阻塞方法,应该在get_next_item() 或成功的 try_next_item() 调用之后调用。如果不传参数或者传一个空句柄,也可以完成握手,然后sequencer的fifo也不会有item进去,如果传的不是空句柄的话,就会送进对应的fifo。

peek

如果在sequencer的对应 FIFO 中没有可用的 REQ sequence_item,peek() 方法将阻塞一直等待,然后返回一个指向 REQ 对象的指针,执行握手的前半部分。在 get() 或 item_done() 调用之前调用peek的话,返回的都是同一个对象。

“很好理解,peek本质就是不会影响fifo的内容,get和item_done一个取走item,一个送入item,自然会产生影响。 ”

get

和上面的peek对应,会影响fifo

put

这个是一个非阻塞的方法,可以在任何时候调用,和握手机制没有关系。

注意:get_next_item()、get() 和 peek() 方法会启动sequencer的仲裁,会从获得权限的sequence那里返回一个 sequence_item。

推荐的Driver和sequencer的api调用模型

对于driver和sequencer之间的item交互,推荐两种方式

get_next_item() 后跟 item_done()

通过get_next_item,拿到item以后,进行处理,最后再使用item_done完成握手。在 item_done() 调用中不应传递任何参数。

这种握手流程是最推荐的,因为他很明确的分离了driver和sequencer之间的任务。

// 
// Driver run method 
// 
task run_phase( uvm_phase phase ); 
 bus_seq_item req_item; 
 forever begin 
    seq_item_port.get_next_item(req_item);  // Blocking call returning the next transaction 
  //BFM handles all pin wiggling and population of req_item with response data 
  m_bfm.drive(req_item); 
    seq_item_port.item_done();  // Signal to the sequence that the driver has finished with the item 
 end 
endtask: run 

相应的sequence代码里是 start_item() 后跟一个 finish_item()。由于driver和sequence中的sequence item指向的是同一个对象,因此可以通过item句柄在sequence内使用从driver返回的任何数据。换句话说,当一个 sequence_item 的句柄作为参数传递给 finish_item() 方法时,driver的 get_next_item() 方法拿到的 sequence_item 所指向的对象是同一个。当driver对 sequence_item 进行任何更改时,sequence也能看到item中的所有修改。driver对 item_done() 的调用会解除对sequence中的 finish_item() 调用的阻塞,然后sequence可以访问 sequence_item 中的字段,包括driver对item修改的那些字段。

// 
// Sequence body method: 
// 
task body(); 
 bus_seq_item req_item; 
 bus_seq_item req_item_c; 
 req_item = bus_seq_item::type_id::create("req_item"); 
 repeat(10) begin 
    $cast(req_item_c, req_item.clone);  // Good practice to clone the req_item item 
  start_item(req_item_c); 
  req_item_c.randomize(); 
    finish_item(req_item_c);  // Driver has returned REQ with the response fields updated 
  `uvm_info("body", req_item_c.convert2string()) 
 end 
endtask: body 
get(req) 之后 put(rsp)

这种流程下,driver通过 get(req) 获取下一个 sequence_item 并在driver消耗时间处理 sequence_item 之前一次性发送回sequence的握手。driver使用 put(rsp) 方法来告诉sequence, sequence_item 已被传送出去。driver使用响应的建议在下一个章节中会提及。

使用这种flow,则sequence在 finish_item() 后面调用get_response() ,会阻塞直到驱动程序调用 put(rsp)。这种使用flow的缺点是在driver实现起来更复杂,并且sequence一定要记得处理rsp。

// 
// run method within the driver 
// 
task run_phase( uvm_phase phase ); 
 REQ req_item;  //REQ is parameterized type for requests
 RSP rsp_item;  //RSP is parameterized type for responses
 bit [15:0] rdata; 
 bit error; 
 forever begin 
    seq_item_port.get(req_item);  // finish_item in sequence is unblocked 
  //BFM handles all pin wiggling and returns response data as separate arguments 
   m_bfm.drive(req_item, rdata, error); 
    $cast(rsp_item, req_item.clone());  // Create a response transaction by cloning req_item 
    rsp_item.set_id_info(req_item);  // Set the rsp_item sequence id to match req_item 
    if(req_item.read_or_write == READ) begin  // Copy the bus data to the response fields 
   rsp_item.read_data = rdata; 
  end 
  rsp_item.resp = error; 
    seq_item_port.put(rsp_item);  // Handshake back to the sequence via its get_response() call 
 end 
endtask 
// 
// Corresponding code within the sequence body method 
// 
task body(); 
 REQ req_item;  //REQ is parameterized type for requests
 RSP rsp_item;  //RSP is parameterized type for responses
 repeat(10) begin 
  req_item = bus_seq_item::type_id::create("req_item"); 
  start_item(req_item); 
  req_item.randomize(); 
    finish_item(req_item);  // This passes to the driver get() call and is returned immediately 
  get_response(rsp_item);  // Block until a response is received
  `uvm_info("body", rsp_item.convert2string(), UVM_LOW); 
 end 
endtask: body 
将respond item路由回到parent sequence中

当有多个sequence通过sequencer与driver通信时,模型的复杂度就会提高。sequencer负责将哪个 sequence_item 从哪个sequence路由到driver中。而当driver创建响应 sequence_item 之后,需要将其路由回正确的sequence。UVM 处理这个问题的方式是每个 sequence_item 都有一对 id 字段,一个用于parent sequence,一个用于标识 sequence_item,sequencer 使用这些字段将响应路由回parent sequence。作为 start_item() 方法的结果,请求 sequence_item 具有由sequencer设置的这些字段,因此,新的响应 sequence_item 需要获取请求 ID 信息的副本,以便可以将其路由回原始sequence。为此,在 uvm_sequence_item 基类中提供了 set_id_info() 方法。

“意思就是返回rsp的时候,把id要复制一下,以便sequencer对于item的正确路由 ”

Driver Response Items 的编码指南

set_id_info

uvm_sequence_item 有一个 id 字段,该字段由sequencer在sequence调用 start_item() 的时候设置。此 id 帮助sequencer跟踪每个 sequence_item 关联的sequence,并且此信息用于将响应item路由回正确的sequence。尽管在大多数情况下只有一个sequence与driver主动通信,但这个机制是一直运行的。sequence_item set_id_info 方法用于把请求item的id复制给响应id。返回响应item的时候,必须设置id。

如果通过克隆产生或者创建新的响应item,就必须要调用set_id_info给item设置对应的id。

“和前面一段的意思是一样的”

使用指针克隆保证安全

当一个响应item从driver发送回一个sequence时,它的指针将存储在sequencer的响应 FIFO 中。如果在发送下一个响应item指针之前没有使用响应item,除非新的响应item指针是针对新对象的,否则两个指针都将引用同一个对象。此问题的一个常见结果是连续读取 FIFO 会产生具有相同内容的对象。

防止这种情况发生的方法,推荐克隆响应item,以便创建一个新对象并将指向该新对象的指针传递给sequencer响应 FIFO 或具有不同的请求和响应类型。

// Somewhere in a driver - request and response items 
bus_seq_item req_item; 
bus_seq_item rsp_item; 
task run_phase( uvm_phase phase ); 
 forever begin 
  seq_item_port.get(req_item); 
    assert($cast(rsp_item, req_item.clone());  // This does not copy the id info 
  rsp_item.set_id_info(req_item);  // This sets the rsp_item id to thereq_item id 
// 
// Do the pin level transaction, populate the response fields 
// 
// Return the response: 
  seq_item_port.put(rsp_item); 
// 
  end 
endtask: run 
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 摸鱼范式 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Sequencer
    • Driver-Sequence API
      • UVM Driver API
      • 推荐的Driver和sequencer的api调用模型
      • Driver Response Items 的编码指南
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档