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

【UVM COOKBOOK】Sequences||激励

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

使用UVM Sequences生成激励

uvm_sequence_base 类通过添加body方法扩展了 uvm_sequence_item 类。sequence用于通过执行其body来产生激励。sequence item被设计为一个瞬态动态对象,这意味着它可以在被取消引用后被创建、使用和垃圾回收。

在 UVM 中使用sequence可以实现非常灵活的激励。sequence用于控制sequence item的生成和流入driver,但它们也可以在同一驱动程序或不同驱动程序上创建和执行其他sequence。sequence还可以将 sequence_items 的生成与子sequence的执行混合在一起。由于sequence是object,灵活使用oop可以生成多变地激励。

通过sequence产生激励的过程中,sequence的层次主要分为三个:

  1. 主控线程 - 这可能是 UVM 测试组件中的run task或高级sequence,例如vritual sequence或default sequence。该线程的目的是启动下一级sequence。
  2. 单独的sequence - 这些可能是独立的sequence,它们只是将sequence item发送到driver,或者它们可以依次创建和执行子sequence。
  3. sequence_item - 这包含使driver所需要的pin级事务的信息。sequence item包含 rand 字段,这些字段在sequence中的进行了受约束的随机化。

sequence flow的几种方案

下面实际中sequence构造flow的基本方案

线性flow

sequence将顺序启动

并行flow

在并行flow中,用fork-join 并行执行sequence。这意味着两个或多个sequence可能在任何时间点与driver交互。SystemVerilog join_any 和 join_none 允许sequence在执行的时候和他后面的sequence存在时间上的交叠。

编码指南-让sequence完成执行

sequence在结束之前必须把所有item发送完毕,否则会导致sequencer和driver的死锁。对sequence使用 fork join_any/join_nones 注意一些细节。

不要对fork join_any使用disable fork

在sequence中使用 fork <multiple_sequences> join_any 后跟一个disable fork 将导致未完成的sequence线程被终止,会导致sequencer死锁。

代码语言:javascript
复制
// 
// DO NOT USE THIS PATTERN - Supplied as an example of what NOT to do 
// 
// Parent sequence body method running child sub_sequences within a fork join_any 
task body(); 
// 
// Code creating and randomizing the child sequences 
// 
 fork 
  seq_A.start(m_sequencer); 
  seq_B.start(m_sequencer); 
  seq_C.start(m_sequencer); 
 join_any 
// First past the post completes the fork and disables it 
 disable fork; 
// Assuming seq_A completes first - seq_B and seq_C will be terminated in an indeterminate 
// way, locking up the sequencer 
不要使用fork join_none退出sequence

在 body 方法中使用 fork,join_none 来让sequence和而后续的sequence并行,会导致导致sequence start 方法在 body 方法内容开始与sequencer通信之前终止。通过在更高级别的控制线程中使用 fork join_none 可以避免这种情况。

代码语言:javascript
复制
// 
// DO NOT USE THIS PATTERN - Supplied as an example of what NOT to do 
// 
// Inside sequence_As body method 
// 
task body(); 
  // Initialise etc 
 fork 
// The body code including other sequences and sequence_items 
// ... 
 join_none 
endtask body; 
// 
// The body task exits immediately, in the controlling thread the idea is that sequence_A executes 
// in parallel with other sequences: 
// 
task run_phase( uvm_phase phase ); 
// .... 
 sequence_A_h.start(m_sequencer); 
  another_seq_h.start(m_sequencer);  // This sequence executes in parallel with sequence_A_h 
// ... 
// The way to achieve the desired functionality is to remove the fork join_none from sequence_A 
// and to fork join the two sequences in the control thread: 
// 
task run; 
// .... 
 fork 
  sequence_A_h.start(m_sequencer); 
  another_seq_h.start(m_sequencer); 
 join 
// .... 
不要在fork join内部的sequence中使用无限循环

如果一个sequence在其body方法中包含一个无限循环,并且这个sequence在父sequence的 fork join中启动并且父sequence终止,那么子sequence的body方法将继续循环。如果父sequence被disable终止,则子sequence可能会在握手过程中被捕获,sequencer也有可能被死锁。

分层执行flow

分层flow从创建并执行一个或多个子sequence的顶级sequence开始,这些子sequence又创建并执行进一步的子sequence。这种方法类似于使用自顶向下组织的分层软件,以便将高级命令转换为一系列低级事务,直到它达到可以执行总线级命令的原子级。后续章节中会详细展开。

将sequence看作对象

sequence看作对象,意味着会在产生激励的过程中使用对象的特性。

随机化字段

与 sequence_item 一样,sequence可以包含可以标记为 rand 字段的数据字段。这意味着可以通过在开始之前随机化其变量来使sequence表现出不同的行为。sequence的内部约束和外部约束共同作用就能够产生我们所期望了随机字段。

通常,sequence通过内部约束随机化字段。例如,一个将数据从一个内存块移动到另一个内存块的sequence将包含一个随机的从地址开始、一个开始到地址和一个传输大小。传输大小可以限制在系统限制内 - 例如 1K 字节。当sequence被随机化时,起始位置将被限制在相关内存区域的范围内。

代码语言:javascript
复制
// 
// This sequence shows how data members can be set to rand values 
// to allow the sequence to either be randomized or set to a directed 
// set of values in the controlling thread 
// 
// The sequence reads one block of memory (src_addr) into a buffer and then 
// writes the buffer into another block of memory (dst_addr). The size 
// of the buffer is determined by the transfer size 
// 
class mem_trans_seq extends bus_seq_base; 
 `uvm_object_utils(mem_trans_seq) 
// Randomised variables 
 rand logic[31:0] src_addr; 
 rand logic[31:0] dst_addr; 
 rand int transfer_size; 
// Internal buffer 
 logic[31:0] buffer[]; 
// Legal limit on the page size is 1023 transfers 
// 
// No point in doing a transfer of 0 transfers 
// 
 constraint page_size { 
  transfer_size inside {[1:1024]}; 
 } 
// Addresses need to be aligned to 32 bit transfers 
 constraint address_alignment {
   src_addr[1:0] == 0; 
  dst_addr[1:0] == 0; 
 } 
 function new(string name = "mem_trans_seq"); 
  super.new(name); 
 endfunction 
 task body; 
  bus_seq_item req = bus_seq_item::type_id::create("req"); 
  logic[31:0] dst_start_addr = dst_addr; 
    buffer = new[transfer_size]; 
  `uvm_info("run:", $sformatf("Transfer block of %0d words from %0h-%0h to %0h-%0h", transfer_size, src_addr, src_addr+((transfer_size-1)*4), dst_addr, dst_addr+((transfer_size-1)*4)), UVM_LOW) 
// Fill the buffer 
  for(int i = 0; i < transfer_size-1; i++) begin 
   start_item(req); 
   if(!req.randomize() with {addr == src_addr; read_not_write == 1; delay < 3;}) begin 
    `uvm_error("body", "randomization failed for req") 
   end 
   finish_item(req); 
     buffer[i] = req.read_data; 
   src_addr = src_addr + 4; // Increment to the next location 
  end 
// Empty the buffer 
  for(int i = 0; i < transfer_size-1; i++) begin 
   start_item(req); 
   if(!req.randomize() with {addr == dst_addr; read_not_write == 0; write_data == buffer[i];delay < 3;}) begin 
    `uvm_error("body", "randomization failed for req") 
   end 
   finish_item(req); 
   dst_addr = dst_addr + 4; // Increment to the next location 
  end 
  dst_addr = dst_start_addr; 
// Check the buffer transfer 
  for(int i = 0; i < transfer_size-1; i++) begin 
   start_item(req); 
   if(!req.randomize() with {addr == dst_addr; read_not_write == 1; write_data == buffer[i];delay < 3;}) begin 
    `uvm_error("body", "randomization failed for req") 
    end 
   finish_item(req); 
   if(buffer[i] != req.read_data) begin 
    `uvm_error("run:", $sformatf("Error in transfer @%0h : Expected %0h, Actual %0h", dst_addr, buffer[i], req.read_data)) 
   end 
   dst_addr = dst_addr + 4; // Increment to the next location 
  end 
  `uvm_info("run:", $sformatf("Finished transfer end addresses SRC: %0h DST:%0h", src_addr, dst_addr), UVM_LOW) 
 endtask: body 
endclass: mem_trans_seq 
// 
// This test shows how to randomize the memory_trans_seq 
// to set it up for a block transfer 
// 
class seq_rand_test extends bus_test_base; 
 `uvm_component_utils(seq_rand_test) 
 function new(string name = "seq_rand_test", uvm_component parent = null); 
  super.new(name); 
 endfunction 
 task run_phase( uvm_phase phase ); phase.raise_objection( this , "start mem_trans_seq" ); 
  mem_trans_seq seq = mem_trans_seq::type_id::create("seq"); 
// Using randomization and constraints to set the initial values 
// 
// This could also be done directly 
// 
  assert(seq.randomize() with {src_addr == 32'h0100_0800; dst_addr inside {[32'h0101_0000:32'h0103_0000]}; transfer_size == 128;}); 
  seq.start(m_agent.m_sequencer); 
  phase.drop_objection( this , "finished mem_trans_seq" ); 
 endtask: run 
endclass: seq_rand_test 

systemverilog的类可以使用randomize函数进行一次随机化,也可以通过循环,实现多次随机化。

sequence对象持久化

当创建一个sequence然后使用 sequence.start() 执行时,将执行sequence的body方法。当 body 方法完成时,sequence对象仍然存在于内存中。这意味着sequence及其对象层次结构中包含的任何信息仍然可以访问。可以利用此功能将一系列sequence链接在一起,使用来自一个sequence的信息为另一个sequence的执行提供种子。

以前面的内存传输sequence为例,可以在不随机化的情况下重新执行相同的sequence以进行一系列相同大小的顺序传输,然后重新随机化该sequence以从不同的起始地址进行不同大小的传输。

再举一个例子,从外围设备读取或读取块的sequence。然后下一个sequence可以使用前一个sequence数据字段的内容来指导它做什么。

代码语言:javascript
复制
// 
// This class shows how to reuse the values persistent within a sequence 
// It runs the mem_trans_seq once with randomized values and then repeats it 
// several times without further randomization until the memory limit is 
// reached. This shows how the end address values are reused on each repeat. 
// 
class rpt_mem_trans_seq extends bus_seq_base; 
 `uvm_object_utils(rpt_mem_trans_seq) 
 function new(string name = "rpt_mem_trans_seq"); 
  super.new(name); 
 endfunction 
 task body(); 
  mem_trans_seq trans_seq = 
  mem_trans_seq::type_id::create("trans_seq"); 
// First transfer: 
  assert(trans_seq.randomize() with {src_addr inside {[32'h0100_0000:32'h0100_FFFF]}; dst_addr inside {[32'h0103_0000:(32'h0104_0000 - (transfer_size*4))]}; transfer_size < 512; solve transfer_size before dst_addr;}); 
  trans_seq.start(m_sequencer); 
// Continue with next block whilst we can complete within range 
// Each block transfer continues from where the last one left off 
   while ((trans_seq.dst_addr + (trans_seq.transfer_size*4)) < 32'h0104_0000) begin 
   trans_seq.start(m_sequencer); 
  end 
 endtask: body 
endclass: rpt_mem_trans_seq 

利用sequence的多态

如果创建了一个sequence库,所有这些sequence都源自相同的对象类型,那么就可以创建这些sequence并将它们放入一个数组中,然后以随机顺序执行它们。通过随机生成数组的索引,或使用 <array>.shuffle() 方法改组数组的顺序,可以使该顺序随机化。

代码语言:javascript
复制
// 
// This sequence executes some sub-sequences in a random order 
// 
class rand_order_seq extends bus_seq_base; 
 `uvm_object_utils(rand_order_seq) 
 function new(string name = "rand_order_seq"); 
  super.new(name); 
 endfunction 
// 
// The sub-sequences are created and put into an array of 
// the common base type. 
// 
// Then the array order is shuffled before each sequence is 
// randomized and then executed 
// 
 task body; 
  bus_seq_base seq_array[4]; 
  seq_array[0] = n_m_rw  interleaved_seq::type_id::create("seq_0"); 
  seq_array[1] = rwr_seq::type_id::create("seq_1"); 
  seq_array[2] = n_m_rw_seq::type_id::create("seq_2"); 
  seq_array[3] = fill_memory_seq::type_id::create("seq_3"); 
// Shuffle the array contents into a random order: 
  seq_array.shuffle(); 
// Execute all the array items in turn 
  foreach(seq_array[i]) begin 
   if(!seq_array[i].randomize()) begin 
   `uvm_error("body", "randomization failed for req") 
   end 
   seq_array[i].start(m_sequencer); 
  end 
  endtask: body 
endclass: rand_order_seq 

sequence也可以使用 UVM 工厂用派生类型的sequence覆盖,有关更多信息,请参阅关于覆盖sequence的的章节。这种方法允许生成flow改变其特性而不必改变原始sequence代码。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-13,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用UVM Sequences生成激励
    • sequence flow的几种方案
      • 将sequence看作对象
        • sequence对象持久化
          • 利用sequence的多态
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档