core-v-verif系列之lib<59>

UVM环境介绍
HEAD commitID: 1f968ef

lib/uvm_agents/uvma_obi_memory/src/seq

// 
// Copyright 2021 OpenHW Group
// Copyright 2021 Datum Technology Corporation
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
// 
// Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may
// not use this file except in compliance with the License, or, at your option,
// the Apache License version 2.0. You may obtain a copy of the License at
// 
//     https://solderpad.org/licenses/SHL-2.1/
// 
// Unless required by applicable law or agreed to in writing, any work
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// 


`ifndef __UVMA_OBI_MEMORY_SLV_SEQ_ITEM_SV__
`define __UVMA_OBI_MEMORY_SLV_SEQ_ITEM_SV__


/**
 * Object created by Open Bus Interface agent sequences extending
 * uvma_obi_memory_slv_seq_base_c.
 */
class uvma_obi_memory_slv_seq_item_c extends uvma_obi_memory_base_seq_item_c;
   
   // Data
   rand uvma_obi_memory_data_b_t   rdata   ; ///< Read data.
   rand uvma_obi_memory_ruser_b_t  ruser   ; ///< Response phase User signals. Only valid for read transactions. Undefined for write transactions.
   rand uvma_obi_memory_id_b_t     rid     ; ///< Response Phase transaction identifier.
   rand uvma_obi_memory_err_b_t    err     ; ///< Error.
   rand uvma_obi_memory_exokay_b_t exokay  ; ///< Atomic acceess response
   
   // Metadata
   rand int unsigned          rvalid_latency; ///< Number of clock cycles to wait before driving rvalid for a slave response
   uvma_obi_memory_mon_trn_c  orig_trn      ; ///< Monitored transaction to which this seq_item is responding
   
   
   `uvm_object_utils_begin(uvma_obi_memory_slv_seq_item_c)
      `uvm_field_int(rdata  , UVM_DEFAULT)
      `uvm_field_int(ruser  , UVM_DEFAULT)
      `uvm_field_int(rid    , UVM_DEFAULT)
      `uvm_field_int(err    , UVM_DEFAULT)
      `uvm_field_int(exokay , UVM_DEFAULT)
      
      `uvm_field_int(rvalid_latency, UVM_DEFAULT + UVM_DEC + UVM_NOCOMPARE)      
      
      `uvm_field_object(orig_trn, UVM_DEFAULT + UVM_NOCOMPARE)
   `uvm_object_utils_end
   
   
   constraint defaults_cons {
      /*soft*/ err == 0;      
   }
   
   /**
    * Default constructor.
    */
   extern function new(string name="uvma_obi_memory_slv_seq_item");
   
endclass : uvma_obi_memory_slv_seq_item_c


function uvma_obi_memory_slv_seq_item_c::new(string name="uvma_obi_memory_slv_seq_item");
   
   super.new(name);
   
endfunction : new


`endif // __UVMA_OBI_MEMORY_SLV_SEQ_ITEM_SV__


uvma_obi_memory_slv_seq_item.sv

1. 简要介绍

该文件是OBI内存接口从模式序列项类定义,主要功能包括:

  1. 定义从设备响应数据结构
  2. 封装读取数据和错误信号
  3. 支持原子操作响应
  4. 提供响应延迟控制

2. 接口介绍

2.1 类定义
class uvma_obi_memory_slv_seq_item_c extends uvma_obi_memory_base_seq_item_c;
  • 代码介绍:定义从模式序列项类
  • 继承关系:继承自OBI基础序列项类
  • 特点:扩展从设备特有字段

3. 参数介绍

3.1 响应数据字段
rand uvma_obi_memory_data_b_t rdata;
rand uvma_obi_memory_err_b_t err;
  • 参数说明
    • rdata:随机化读取数据
    • err:随机化错误信号
3.2 原子操作字段
rand uvma_obi_memory_exokay_b_t exokay;
  • 参数说明:原子操作响应标志
  • 取值:0或1

4. 模块实现介绍

4.1 UVM自动化注册
`uvm_object_utils_begin(uvma_obi_memory_slv_seq_item_c)
   `uvm_field_int(rdata, UVM_DEFAULT)
   `uvm_field_int(err, UVM_DEFAULT)
`uvm_object_utils_end
  • 代码分析
    1. 注册所有关键字段
    2. 支持自动化操作
    3. 配置默认显示格式
4.2 默认约束
constraint defaults_cons {
   /*soft*/ err == 0;
}
  • 代码分析
    1. 设置默认无错误
    2. 使用soft约束可重载
    3. 确保基本功能正常

5. 总结

该从模式序列项类具有以下特点:

  1. 完整的响应字段定义
  2. 灵活的随机化控制
  3. 标准的UVM实现
  4. 可扩展的设计架构

作为验证环境的核心组件,它为OBI从模式测试提供了统一的响应模型,确保从设备能够准确响应各类主设备请求。

//
// Copyright 2021 OpenHW Group
// Copyright 2021 Datum Technology Corporation
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may
// not use this file except in compliance with the License, or, at your option,
// the Apache License version 2.0. You may obtain a copy of the License at
//
//     https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//


`ifndef __UVMA_OBI_MEMORY_SLV_SEQ_SV__
`define __UVMA_OBI_MEMORY_SLV_SEQ_SV__


/**
 * Virtual sequence implementing the cv32e40x virtual peripherals.
 * TODO Move most of the functionality to a cv32e env base class.
 */
class uvma_obi_memory_slv_seq_c extends uvma_obi_memory_slv_base_seq_c;

   // Queue of virtual peripheral sequences to spawn when this sequence is spawned
   uvma_obi_memory_vp_base_seq_c vp_seq_q[$];

   // Lookup table to trigger sequences when bus address is detected
   uvma_obi_memory_vp_base_seq_c vp_seq_table[bit[31:0]];

   `uvm_object_utils_begin(uvma_obi_memory_slv_seq_c)
   `uvm_object_utils_end

   /**
    * Default constructor.
    */
   extern function new(string name="uvma_obi_memory_slv_seq");

   /**
    * Register sequences with a range of addresses on this OBI
    * Waiving Verissimo SVTB.32.2.0: Pass strings by reference unless otherwise needed
    */
   extern virtual function uvma_obi_memory_vp_base_seq_c register_vp_vseq(string name,                  //@DVT_LINTER_WAIVER "MT20211228_9" disable SVTB.32.2.0
                                                                          bit[31:0] start_address,
                                                                          uvm_object_wrapper seq_type);

   /**
    * Main sequence body
    */
   extern virtual task body();

   /**
    * Spawn virtual peripheral sequences when this sequence starts
    */
   extern virtual task spawn_vp_sequences();

   /**
    * TODO Describe uvma_obi_memory_slv_seq_c::do_response()
    */
   extern virtual task do_response(ref uvma_obi_memory_mon_trn_c mon_req);

   /**
    * TODO Describe uvma_obi_memory_slv_seq_c::do_mem_operation()
    */
   extern virtual task do_mem_operation(ref uvma_obi_memory_mon_trn_c mon_req);

endclass : uvma_obi_memory_slv_seq_c


function uvma_obi_memory_slv_seq_c::new(string name="uvma_obi_memory_slv_seq");

   super.new(name);
   if (!this.randomize()) begin
      `uvm_fatal("OBIMEMSLVSEQ", "Randomize failed");
   end

endfunction : new

task uvma_obi_memory_slv_seq_c::body();

   uvma_obi_memory_mon_trn_c  mon_trn;

   // Start the virtual peripheral sequences
   spawn_vp_sequences();

   fork
      begin
         forever begin
            // Wait for the monitor to send us the mstr's "req" with an access request
            p_sequencer.mon_trn_fifo.get(mon_trn);
            `uvm_info("SLV_SEQ", $sformatf("Got mon_trn:\n%s", mon_trn.sprint()), UVM_DEBUG)
            do_response(mon_trn);
         end
      end

   join_none

endtask : body


task uvma_obi_memory_slv_seq_c::do_response(ref uvma_obi_memory_mon_trn_c mon_req);

   bit  err_req;
   bit  err_siz;

   `uvm_info("SLV_SEQ", $sformatf("mon_req.address before data_addr_dec remap: x%h", mon_req.address), UVM_HIGH)

   // Check the virtual peripheral address hash table to see if transaction should be sent to a virtual peripheral
   if (vp_seq_table.exists(mon_req.address)) begin
      vp_seq_table[mon_req.address].mon_trn_q.push_back(mon_req);
      return;
   end

   // If we fell through, then handle the transaction locally
   `uvm_info("SLV_SEQ", $sformatf("VP not handled: x%h", mon_req.address), UVM_HIGH)
   err_req  = mon_req.err;
   if (err_req) `uvm_info("SLV_SEQ", $sformatf("ERROR1: mon_req.err=%0b", mon_req.err), UVM_HIGH)
   err_siz = 0;
   if (err_siz) `uvm_info("SLV_SEQ", $sformatf("ERROR2: mon_req.address=%0h", mon_req.address), UVM_HIGH)

   if (!(err_req | err_siz)) begin
      do_mem_operation(mon_req);
   end
   else begin
      uvma_obi_memory_slv_seq_item_c  slv_rsp;

      `uvm_create(slv_rsp)
      slv_rsp.orig_trn = mon_req;

      `uvm_info("SLV_SEQ", $sformatf("Error!\n%s", mon_req.sprint()), UVM_LOW)
      if (mon_req.access_type == UVMA_OBI_MEMORY_ACCESS_READ) begin
         // TODO: need to figured out what a proper error response is
         slv_rsp.rdata = 32'hdead_beef;
      end
      add_r_fields(mon_req, slv_rsp);
      slv_rsp.set_sequencer(p_sequencer);
      `uvm_send(slv_rsp)
   end

endtask : do_response

task uvma_obi_memory_slv_seq_c::do_mem_operation(ref uvma_obi_memory_mon_trn_c mon_req);

   bit [31:0] word_aligned_addr;

   uvma_obi_memory_slv_seq_item_c  slv_rsp;
   `uvm_create(slv_rsp)
   slv_rsp.orig_trn = mon_req;
   slv_rsp.access_type = mon_req.access_type;

   word_aligned_addr = { mon_req.address[31:2], 2'h0 };

   `uvm_info("SLV_SEQ", $sformatf("Performing operation:\n%s", mon_req.sprint()), UVM_HIGH)
   if (mon_req.access_type == UVMA_OBI_MEMORY_ACCESS_WRITE) begin
      if (mon_req.be[3]) cntxt.mem.write(word_aligned_addr+3, mon_req.data[31:24]);
      if (mon_req.be[2]) cntxt.mem.write(word_aligned_addr+2, mon_req.data[23:16]);
      if (mon_req.be[1]) cntxt.mem.write(word_aligned_addr+1, mon_req.data[15:08]);
      if (mon_req.be[0]) cntxt.mem.write(word_aligned_addr+0, mon_req.data[07:00]);
   end
   else begin
      if (mon_req.be[3]) slv_rsp.rdata[31:24] = cntxt.mem.read(word_aligned_addr+3);
      if (mon_req.be[2]) slv_rsp.rdata[23:16] = cntxt.mem.read(word_aligned_addr+2);
      if (mon_req.be[1]) slv_rsp.rdata[15:08] = cntxt.mem.read(word_aligned_addr+1);
      if (mon_req.be[0]) slv_rsp.rdata[07:00] = cntxt.mem.read(word_aligned_addr+0);
   end

   add_r_fields(mon_req, slv_rsp);
   slv_rsp.set_sequencer(p_sequencer);
   `uvm_send(slv_rsp)

endtask : do_mem_operation

function uvma_obi_memory_vp_base_seq_c uvma_obi_memory_slv_seq_c::register_vp_vseq(string name,
                                                                                   bit[31:0] start_address,
                                                                                   uvm_object_wrapper seq_type);

   uvma_obi_memory_vp_base_seq_c vp_seq;

   // Create an instance of the sequence type passed in,
   // Ensure that the sequence type is derived from uvma_obi_memory_vp_base_seq_c
   if (!$cast(vp_seq, seq_type.create_object(name))) begin
      `uvm_fatal("OBIVPVSEQ", $sformatf("Could not cast seq_type of type name: %s to a uvma_obi_memory_vp_base_seq_c type", seq_type.get_type_name()))
   end

   // Sanity check num_words
   if (vp_seq.get_num_words() == 0) begin
      `uvm_fatal("OBIVPVSEQ", $sformatf("num_words for type %s cannot be zero", seq_type.get_type_name()))
   end

   // Configure fields in the virtual peripheral sequence
   vp_seq.start_address = start_address;

   // Use hash to efficiently look up word-aligned addresses to this handle
   for (int unsigned word = 0; word < vp_seq.get_num_words(); word++) begin
      bit[31:0] addr = (start_address & ~32'h3) + (4 * word);

      // If an address is being claimed twice, then issue a fatal error
      if (vp_seq_table.exists(addr)) begin
         `uvm_fatal("OBIVPVSEQ", $sformatf("address: 0x%08x, tried to register vp_vseq [%s], but vp_vseq [%s] already registered",
                                           addr, vp_seq.get_name(), vp_seq_table[addr].get_name()));
      end
      `uvm_info("OBIVPSEQ", $sformatf("Virtual register: %s, addr: 0x%08x", vp_seq.get_full_name(), addr), UVM_LOW);

      vp_seq_table[addr] = vp_seq;
   end
   vp_seq_q.push_back(vp_seq);

   return vp_seq;

endfunction : register_vp_vseq

task uvma_obi_memory_slv_seq_c::spawn_vp_sequences();

   if (p_sequencer == null) begin
      `uvm_fatal("SLV_SEQ", "Cannot find p_sequencer handle to spawn virtual sequences on");
   end

   // Iterate through the queue and spawn all unique register virtual peripheral sequence objects on this sequencer
   foreach (vp_seq_q[i]) begin
      automatic int ii = i;

      fork
         vp_seq_q[ii].start(p_sequencer, this);
      join_none
   end

endtask : spawn_vp_sequences

`endif // __UVMA_OBI_MEMORY_SLV_SEQ__

uvma_obi_memory_slv_seq.sv

1. 简要介绍

该文件是OBI内存接口从模式序列实现类,主要功能包括:

  1. 实现虚拟外设功能
  2. 处理主设备请求并生成响应
  3. 支持地址映射和错误注入
  4. 提供内存读写操作

2. 接口介绍

2.1 类定义
class uvma_obi_memory_slv_seq_c extends uvma_obi_memory_slv_base_seq_c;
  • 代码介绍:定义从模式序列类
  • 继承关系:继承自从模式基础序列类
  • 特点:扩展虚拟外设支持
2.2 虚拟外设管理
uvma_obi_memory_vp_base_seq_c vp_seq_q[$];
uvma_obi_memory_vp_base_seq_c vp_seq_table[bit[31:0]];
  • 代码介绍:管理虚拟外设序列
  • 功能:通过地址映射触发特定序列

3. 参数介绍

3.1 构造函数
function new(string name="uvma_obi_memory_slv_seq");
   super.new(name);
   if (!this.randomize()) begin
      `uvm_fatal("OBIMEMSLVSEQ", "Randomize failed");
   end
endfunction
  • 参数说明:初始化序列对象
  • 特点:强制随机化检查

4. 模块实现介绍

4.1 主体任务
task body();
   spawn_vp_sequences();
   fork
      forever begin
         p_sequencer.mon_trn_fifo.get(mon_trn);
         do_response(mon_trn);
      end
   join_none
endtask
  • 代码分析
    1. 启动虚拟外设序列
    2. 持续监听主设备请求
    3. 并行处理每个事务
4.2 内存操作
task do_mem_operation(ref uvma_obi_memory_mon_trn_c mon_req);
   if (mon_req.access_type == UVMA_OBI_MEMORY_ACCESS_WRITE) begin
      if (mon_req.be[3]) cntxt.mem.write(word_aligned_addr+3, mon_req.data[31:24]);
   end
   else begin
      if (mon_req.be[3]) slv_rsp.rdata[31:24] = cntxt.mem.read(word_aligned_addr+3);
   end
endtask
  • 代码分析
    1. 支持字节粒度读写
    2. 处理字对齐地址
    3. 根据字节使能控制操作

5. 总结

该从模式序列实现类具有以下特点:

  1. 完善的虚拟外设支持
  2. 灵活的内存访问控制
  3. 标准化的错误处理
  4. 可扩展的设计架构

作为验证环境的核心组件,它为OBI从设备提供了完整的响应机制,确保能够正确处理各类主设备请求。

// 
// Copyright 2021 OpenHW Group
// Copyright 2021 Datum Technology Corporation
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
// 
// Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may
// not use this file except in compliance with the License, or, at your option,
// the Apache License version 2.0. You may obtain a copy of the License at
// 
//     https://solderpad.org/licenses/SHL-2.1/
// 
// Unless required by applicable law or agreed to in writing, any work
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// 


`ifndef __UVMA_OBI_MEMORY_STORAGE_SLV_SEQ_SV__
`define __UVMA_OBI_MEMORY_STORAGE_SLV_SEQ_SV__


/**
 * 'slv' sequence that reads back '0' as data, unless the address has been
 * written to.
 */
class uvma_obi_memory_storage_slv_seq_c extends uvma_obi_memory_slv_base_seq_c;
   
   // Fields
   rand longint unsigned  min_address;
   rand longint unsigned  max_address;
   uvma_obi_memory_data_b_t      mem[int unsigned];
   
   
   `uvm_object_utils_begin(uvma_obi_memory_storage_slv_seq_c)
      `uvm_field_int(min_address, UVM_DEFAULT)
      `uvm_field_int(max_address, UVM_DEFAULT)
      `uvm_field_aa_int_int_unsigned(mem, UVM_DEFAULT)
   `uvm_object_utils_end
   
   
   /**
    * Describe defaults_cons
    */
   constraint defaults_cons {
      /*soft*/min_address ==     0;
      /*soft*/max_address == 2**32;
   }
   
   
   /**
    * Default constructor.
    */
   extern function new(string name="uvma_obi_memory_storage_slv_seq");
   
   /**
    * TODO Describe uvma_obi_memory_storage_slv_seq_c::do_response()
    */
   extern virtual task do_response(ref uvma_obi_memory_mon_trn_c mon_req);
   
endclass : uvma_obi_memory_storage_slv_seq_c


function uvma_obi_memory_storage_slv_seq_c::new(string name="uvma_obi_memory_storage_slv_seq");
   
   super.new(name);
   
endfunction : new


task uvma_obi_memory_storage_slv_seq_c::do_response(ref uvma_obi_memory_mon_trn_c mon_req);
   
   uvma_obi_memory_addr_b_t        addr  = 0;
   bit                             error = 0;
   uvma_obi_memory_slv_seq_item_c  _req;
   
   // Ignore malformed requests
   if (mon_req.__has_error) begin
      return;
   end
   
   if (!(mon_req.address inside {[min_address:max_address]})) begin
      error = 1;
   end
   
   for (int unsigned ii=0; ii<cfg.addr_width; ii++) begin
      addr[ii] = mon_req.address[ii];
   end
   case (mon_req.access_type)
      UVMA_OBI_MEMORY_ACCESS_READ: begin
         if (mem.exists(addr)) begin
            // The following code is currently incompatible with xsim (2020.2)
            // Temporary replacement below
            //`uvm_do_with(_req, {
            //   _req.access_type == UVMA_OBI_ACCESS_READ;
            //   _req.err         == error;
            //   foreach (_req.rdata[ii]) {
            //      if (ii < cfg.data_width) {
            //         _req.rdata[ii] == mem[addr][ii];
            //      }
            //   }
            //})
            `uvm_create(_req)
            //if (_req.randomize()) begin
               _req.access_type    = UVMA_OBI_MEMORY_ACCESS_READ;
               _req.err            = error;
               //_req.gnt_latency    = 1;
               //_req.hold_duration  = 1'               
               foreach (_req.rdata[ii]) begin
                  if (ii < cfg.data_width) begin
                     _req.rdata[ii] = mem[addr][ii];
                  end
               end
               `uvm_send(_req)
            //end
            //else begin
            //   `uvm_fatal("OBI_SLV_SEQ", $sformatf("Failed to randomize _req:\n%s", _req.sprint()))
            //end
         end
         else begin
            // The following code is currently incompatible with xsim (2020.2)
            // Temporary replacement below
            //`uvm_do_with(_req, {
            //   _req.access_type == UVMA_OBI_ACCESS_READ;
            //   _req.err         == error;
            //   foreach (_req.rdata[ii]) {
            //      _req.rdata[ii] == 1'b0;
            //   }
            //})
            `uvm_create(_req)
            //if (_req.randomize()) begin
               _req.access_type    = UVMA_OBI_MEMORY_ACCESS_READ;
               _req.err            = error;
               _req.rdata          = 0;
               //_req.gnt_latency    = 1;               
               //_req.hold_duration  = 1;               
               `uvm_send(_req)
            //end
            //else begin
            //   `uvm_fatal("OBI_SLV_SEQ", $sformatf("Failed to randomize _req:\n%s", _req.sprint()))
            //end
         end
      end
      
      UVMA_OBI_MEMORY_ACCESS_WRITE: begin
         if (!error) begin
            foreach (mon_req.data[ii]) begin
               if (ii < cfg.data_width) begin
                  mem[addr][ii] = mon_req.data[ii];
               end
            end
         end
         // The following code is currently incompatible with xsim (2020.3)
         // Temporary replacement below
         //`uvm_do_with(_req, {
         //   _req.access_type    == UVMA_OBI_ACCESS_WRITE;
         //   _req.err            == error;
         //   _req.gnt_latency    == 1;
         //   _req.access_latency == 1;
         //   _req.hold_duration  == 1;
         //   _req.tail_length    == 1;
         //})
         `uvm_create(_req)
         _req.access_type    = UVMA_OBI_MEMORY_ACCESS_WRITE;
         _req.err            = error;
         //_req.gnt_latency    = 1;         
         //_req.hold_duration  = 1;         
         `uvm_send(_req)
      end
      
      default: `uvm_fatal("OBI_MEMORY_SLV_SEQ", $sformatf("Invalid access_type (%0d):\n%s", mon_req.access_type, mon_req.sprint()))
   endcase
   
endtask : do_response


`endif // __UVMA_OBI_MEMORY_STORAGE_SLV_SEQ_SV__

uvma_obi_memory_storage_slv_seq.sv

1. 简要介绍

该文件实现了一个存储型从设备序列,主要功能包括:

  1. 模拟内存存储行为
  2. 处理读写请求
  3. 支持地址范围检查
  4. 提供默认值返回机制

2. 接口介绍

2.1 类定义
class uvma_obi_memory_storage_slv_seq_c extends uvma_obi_memory_slv_base_seq_c;
  • 代码介绍:定义存储型从设备序列
  • 继承关系:继承自从设备基础序列类
  • 特点:添加内存存储功能

3. 参数介绍

3.1 地址范围参数
rand longint unsigned min_address;
rand longint unsigned max_address;
  • 参数说明:定义有效地址范围
  • 默认值:0到2^32-1
  • 约束:可通过soft约束重载
3.2 内存存储
uvma_obi_memory_data_b_t mem[int unsigned];
  • 参数说明:使用关联数组模拟内存
  • 键类型:32位无符号地址
  • 值类型:OBI数据总线宽度

4. 模块实现介绍

4.1 响应处理
task do_response(ref uvma_obi_memory_mon_trn_c mon_req);
   if (!(mon_req.address inside {[min_address:max_address]})) begin
      error = 1;
   end
  • 代码分析
    1. 检查地址是否在有效范围内
    2. 设置错误标志
    3. 跳过错误请求处理
4.2 读操作处理
if (mem.exists(addr)) begin
   _req.rdata = mem[addr];
end
else begin
   _req.rdata = 0;
end
  • 代码分析
    1. 检查地址是否存在存储数据
    2. 返回存储值或默认0
    3. 保持响应一致性

5. 总结

该存储型从设备序列具有以下特点:

  1. 灵活的内存模拟机制
  2. 严格的地址范围检查
  3. 可靠的默认值处理
  4. 标准化的响应生成

作为验证环境的重要组件,它为OBI从设备提供了基础的存储功能,确保能够正确模拟各类内存访问行为。

// 
// Copyright 2021 OpenHW Group
// Copyright 2021 Silicon Labs
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
// 
// Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may
// not use this file except in compliance with the License, or, at your option,
// the Apache License version 2.0. You may obtain a copy of the License at
// 
//     https://solderpad.org/licenses/SHL-2.1/
// 
// Unless required by applicable law or agreed to in writing, any work
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// 

`ifndef __UVMA_OBI_MEMORY_VP_BASE_SEQ_SV__
`define __UVMA_OBI_MEMORY_VP_BASE_SEQ_SV__


/**
 * Virtual sequence implementing the cv32e40x virtual peripherals.
 * TODO Move most of the functionality to a cv32e env base class.
 */
virtual class uvma_obi_memory_vp_base_seq_c extends uvma_obi_memory_slv_base_seq_c;

   uvma_obi_memory_mon_trn_c       mon_trn_q[$]; // Used to add transactions to execute (monitored requests)

   // Base address of this virtual peripheral, used to generated offset index for multi-register
   // virtual perhipeerals
   // Should be filled in during registration
   bit [31:0] start_address; 

   `uvm_field_utils_begin(uvma_obi_memory_vp_base_seq_c)
   `uvm_field_utils_end
      
   /**
    * Default constructor.
    */
   extern function new(string name="uvma_obi_memory_vp_base_seq_c");
   
   /**
    * Simple loop that is triggered externally when the main slave sequence detects an address range
    * claimed by this virtual peripheral
    */
   extern virtual task body();

   /**
    * Utility to get an index for virtual peripheral with multiple registers
    */
   extern virtual function int unsigned get_vp_index(uvma_obi_memory_mon_trn_c mon_trn);

   /**
    * Derived classes must implement the operation of the virtual peripheral
    */
   pure virtual task vp_body(uvma_obi_memory_mon_trn_c mon_trn);

   /**
    * Derived classes must implement accessor to return number of virtual peripheral registers
    */
   pure virtual function int unsigned get_num_words();

endclass : uvma_obi_memory_vp_base_seq_c


function uvma_obi_memory_vp_base_seq_c::new(string name="uvma_obi_memory_vp_base_seq_c");
   
   super.new(name);
   
endfunction : new


task uvma_obi_memory_vp_base_seq_c::body();

   forever begin
      wait (mon_trn_q.size());

      vp_body(mon_trn_q.pop_front());   
   end

endtask : body


function int unsigned uvma_obi_memory_vp_base_seq_c::get_vp_index(uvma_obi_memory_mon_trn_c mon_trn);

   int unsigned index;

   // Fatal error if the address in the incoming transaction is less than the configured base address
   if (mon_trn.address < start_address) begin
      `uvm_fatal("FATAL", $sformatf("%s: get_vp_index(), mon_trn.address 0x%08x is less than start address 0x%08x", 
                                    this.get_name(), 
                                    mon_trn.address,
                                    start_address));
   end                                   

   index = (mon_trn.address - start_address) >> 2;

   // Fatal if the index is greater than expected
   if (index >= get_num_words()) begin
      `uvm_fatal("FATAL", $sformatf("%s: get_vp_index(), mon_trn.address 0x%08x base address 0x%08x, should only have %0s vp registers", 
                                    this.get_name(), 
                                    mon_trn.address,
                                    start_address,
                                    get_num_words()));
   end

   return index;

endfunction : get_vp_index


`endif // __UVMA_OBI_MEMORY_VP_BASE_SEQ_SV__

uvma_obi_memory_vp_base_seq.sv

1. 简要介绍

该文件是OBI内存接口虚拟外设基础序列类,主要功能包括:

  1. 定义虚拟外设的标准接口
  2. 提供地址映射功能
  3. 支持多寄存器虚拟外设
  4. 作为所有虚拟外设的基类

2. 接口介绍

2.1 类定义
virtual class uvma_obi_memory_vp_base_seq_c extends uvma_obi_memory_slv_base_seq_c;
  • 代码介绍:定义抽象虚拟外设基类
  • 继承关系:继承自从设备基础序列类
  • 特点:使用virtual关键字声明为抽象类
2.2 事务队列
uvma_obi_memory_mon_trn_c mon_trn_q[$];
  • 代码介绍:存储监控到的事务请求
  • 数据结构:SystemVerilog队列
  • 用途:暂存待处理的主设备请求

3. 参数介绍

3.1 基地址参数
bit [31:0] start_address;
  • 参数说明:虚拟外设的基地址
  • 特点:在注册时设置
  • 用途:计算寄存器偏移量

4. 模块实现介绍

4.1 主体任务
task body();
   forever begin
      wait (mon_trn_q.size());
      vp_body(mon_trn_q.pop_front());
   end
endtask
  • 代码分析
    1. 持续等待新事务
    2. 取出队列头部事务
    3. 调用抽象处理任务
4.2 虚拟外设索引计算
function int unsigned get_vp_index(uvma_obi_memory_mon_trn_c mon_trn);
   index = (mon_trn.address - start_address) >> 2;
endfunction
  • 代码分析
    1. 计算相对于基地址的偏移
    2. 右移2位转换为字索引
    3. 包含严格的地址范围检查

5. 总结

该虚拟外设基础序列类具有以下特点:

  1. 标准化的虚拟外设框架
  2. 严格的地址范围检查
  3. 灵活的多寄存器支持
  4. 清晰的抽象接口定义

作为验证环境的高级组件,它为各类虚拟外设提供了统一的开发框架,确保能够方便地实现各种硬件模拟功能。

// 
// Copyright 2021 OpenHW Group
// Copyright 2021 Silicon Labs
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
// 
// Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may
// not use this file except in compliance with the License, or, at your option,
// the Apache License version 2.0. You may obtain a copy of the License at
// 
//     https://solderpad.org/licenses/SHL-2.1/
// 
// Unless required by applicable law or agreed to in writing, any work
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// 

`ifndef __UVMA_OBI_MEMORY_VP_CYCLE_COUNTER_SEQ_SV__
`define __UVMA_OBI_MEMORY_VP_CYCLE_COUNTER_SEQ_SV__


/**
 * Virtual sequence implementing the cv32e40x virtual peripherals.
 * TODO Move most of the functionality to a cv32e env base class.
 */
class uvma_obi_memory_vp_cycle_counter_seq_c extends uvma_obi_memory_vp_base_seq_c;

   longint unsigned cycle_counter;

   protected bit _stop_count_cycles;

   `uvm_object_utils_begin(uvma_obi_memory_vp_cycle_counter_seq_c)
      `uvm_field_int(cycle_counter, UVM_DEFAULT)      
   `uvm_object_utils_end
      
   /**
    * Default constructor.
    */
   extern function new(string name="uvma_obi_memory_vp_cycle_counter_seq_c");

   /**
    * Body will start cycle counting thread before starting parent
    */
   extern virtual task body();

   /**
    * Implement sequence that will return a random number
    */
   extern virtual task vp_body(uvma_obi_memory_mon_trn_c mon_trn);

   /**
    * Implement accessor to return number of register
    */
   extern virtual function int unsigned get_num_words();

   /**
    * Implements the virtual register to read or write the counter
    */
   extern virtual task rw_counter(uvma_obi_memory_mon_trn_c mon_trn, uvma_obi_memory_slv_seq_item_c slv_rsp);

   /**
    * Implements the virtual register to read or write the counter
    */
   extern virtual task print_counter(uvma_obi_memory_mon_trn_c mon_trn);

   /**
    * Implements the counting thread, should always be fork-join_none'd
    */
   extern virtual task count_cycles();


endclass : uvma_obi_memory_vp_cycle_counter_seq_c

function uvma_obi_memory_vp_cycle_counter_seq_c::new(string name="uvma_obi_memory_vp_cycle_counter_seq_c");
   
   super.new(name);
   
endfunction : new


task uvma_obi_memory_vp_cycle_counter_seq_c::body();

   fork
      count_cycles();
   join_none

   super.body();

endtask : body

function int unsigned uvma_obi_memory_vp_cycle_counter_seq_c::get_num_words();

   return 2;

endfunction : get_num_words

task uvma_obi_memory_vp_cycle_counter_seq_c::vp_body(uvma_obi_memory_mon_trn_c mon_trn);
   
   uvma_obi_memory_slv_seq_item_c  slv_rsp;
   
   `uvm_create(slv_rsp)
   
   slv_rsp.orig_trn = mon_trn;
   slv_rsp.err = 1'b0;

   case (get_vp_index(mon_trn))
      0: rw_counter(mon_trn, slv_rsp);
      1: print_counter(mon_trn);
   endcase

   add_r_fields(mon_trn, slv_rsp);
   slv_rsp.set_sequencer(p_sequencer);
   `uvm_send(slv_rsp)

endtask : vp_body

task uvma_obi_memory_vp_cycle_counter_seq_c::count_cycles();

   fork begin
      fork
         begin
            wait  (_stop_count_cycles == 1);
         end
         begin
            while(1) begin
               @(cntxt.vif.mon_cb);
               cycle_counter++;
            end
         end
      join_any

      // kill counting thread
      disable fork;

      // Handshake that the thread is stopped
      _stop_count_cycles = 0;
   end
   join

endtask : count_cycles

task uvma_obi_memory_vp_cycle_counter_seq_c::rw_counter(uvma_obi_memory_mon_trn_c mon_trn, uvma_obi_memory_slv_seq_item_c slv_rsp);

   if (mon_trn.access_type == UVMA_OBI_MEMORY_ACCESS_WRITE) begin
      // First stop the thread, reset counter to write data, then restart
      
      _stop_count_cycles = 1;
      wait (_stop_count_cycles == 0);

      cycle_counter = mon_trn.data;
      fork         
         count_cycles();
      join_none
   end
   else if (mon_trn.access_type == UVMA_OBI_MEMORY_ACCESS_READ) begin      
      slv_rsp.rdata = cycle_counter;
   end

endtask : rw_counter

task uvma_obi_memory_vp_cycle_counter_seq_c::print_counter(uvma_obi_memory_mon_trn_c mon_trn);

   if (mon_trn.access_type == UVMA_OBI_MEMORY_ACCESS_WRITE) begin
      `uvm_info("CYCLE", $sformatf("Cycle count is %0d", cycle_counter), UVM_LOW);
   end

endtask : print_counter

`endif // __UVMA_OBI_MEMORY_VP_CYCLE_COUNTER_SEQ_SV__


uvma_obi_memory_vp_cycle_counter_seq.sv

1. 简要介绍

该文件实现了一个虚拟周期计数器外设序列,主要功能包括:

  1. 提供周期计数功能
  2. 支持读写操作
  3. 实现打印功能
  4. 作为虚拟外设的具体实现

2. 接口介绍

2.1 类定义
class uvma_obi_memory_vp_cycle_counter_seq_c extends uvma_obi_memory_vp_base_seq_c;
  • 代码介绍:定义周期计数器虚拟外设类
  • 继承关系:继承自虚拟外设基类
  • 特点:实现具体计数功能

3. 参数介绍

3.1 计数器参数
longint unsigned cycle_counter;
  • 参数说明:64位无符号周期计数器
  • 用途:记录时钟周期数
3.2 控制标志
protected bit _stop_count_cycles;
  • 参数说明:计数线程控制标志
  • 取值:0/1
  • 用途:安全停止计数线程

4. 模块实现介绍

4.1 计数线程
task count_cycles();
   while(1) begin
      @(cntxt.vif.mon_cb);
      cycle_counter++;
   end
endtask
  • 代码分析
    1. 无限循环计数
    2. 每个时钟周期递增
    3. 支持安全停止机制
4.2 读写操作
task rw_counter(uvma_obi_memory_mon_trn_c mon_trn, uvma_obi_memory_slv_seq_item_c slv_rsp);
   if (mon_trn.access_type == UVMA_OBI_MEMORY_ACCESS_WRITE) begin
      cycle_counter = mon_trn.data;
   end
endtask
  • 代码分析
    1. 写操作更新计数值
    2. 读操作返回当前计数值
    3. 包含线程安全控制

5. 总结

该虚拟周期计数器序列具有以下特点:

  1. 精确的周期计数功能
  2. 安全的线程控制机制
  3. 标准化的虚拟外设接口
  4. 灵活的读写操作支持

作为验证环境的高级组件,它为性能分析和调试提供了重要的周期计数功能,确保能够准确测量各类操作的执行周期。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值