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内存接口从模式序列项类定义,主要功能包括:
- 定义从设备响应数据结构
- 封装读取数据和错误信号
- 支持原子操作响应
- 提供响应延迟控制
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
- 代码分析:
- 注册所有关键字段
- 支持自动化操作
- 配置默认显示格式
4.2 默认约束
constraint defaults_cons {
/*soft*/ err == 0;
}
- 代码分析:
- 设置默认无错误
- 使用soft约束可重载
- 确保基本功能正常
5. 总结
该从模式序列项类具有以下特点:
- 完整的响应字段定义
- 灵活的随机化控制
- 标准的UVM实现
- 可扩展的设计架构
作为验证环境的核心组件,它为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内存接口从模式序列实现类,主要功能包括:
- 实现虚拟外设功能
- 处理主设备请求并生成响应
- 支持地址映射和错误注入
- 提供内存读写操作
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
- 代码分析:
- 启动虚拟外设序列
- 持续监听主设备请求
- 并行处理每个事务
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
- 代码分析:
- 支持字节粒度读写
- 处理字对齐地址
- 根据字节使能控制操作
5. 总结
该从模式序列实现类具有以下特点:
- 完善的虚拟外设支持
- 灵活的内存访问控制
- 标准化的错误处理
- 可扩展的设计架构
作为验证环境的核心组件,它为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. 简要介绍
该文件实现了一个存储型从设备序列,主要功能包括:
- 模拟内存存储行为
- 处理读写请求
- 支持地址范围检查
- 提供默认值返回机制
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
- 代码分析:
- 检查地址是否在有效范围内
- 设置错误标志
- 跳过错误请求处理
4.2 读操作处理
if (mem.exists(addr)) begin
_req.rdata = mem[addr];
end
else begin
_req.rdata = 0;
end
- 代码分析:
- 检查地址是否存在存储数据
- 返回存储值或默认0
- 保持响应一致性
5. 总结
该存储型从设备序列具有以下特点:
- 灵活的内存模拟机制
- 严格的地址范围检查
- 可靠的默认值处理
- 标准化的响应生成
作为验证环境的重要组件,它为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内存接口虚拟外设基础序列类,主要功能包括:
- 定义虚拟外设的标准接口
- 提供地址映射功能
- 支持多寄存器虚拟外设
- 作为所有虚拟外设的基类
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
- 代码分析:
- 持续等待新事务
- 取出队列头部事务
- 调用抽象处理任务
4.2 虚拟外设索引计算
function int unsigned get_vp_index(uvma_obi_memory_mon_trn_c mon_trn);
index = (mon_trn.address - start_address) >> 2;
endfunction
- 代码分析:
- 计算相对于基地址的偏移
- 右移2位转换为字索引
- 包含严格的地址范围检查
5. 总结
该虚拟外设基础序列类具有以下特点:
- 标准化的虚拟外设框架
- 严格的地址范围检查
- 灵活的多寄存器支持
- 清晰的抽象接口定义
作为验证环境的高级组件,它为各类虚拟外设提供了统一的开发框架,确保能够方便地实现各种硬件模拟功能。
//
// 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. 简要介绍
该文件实现了一个虚拟周期计数器外设序列,主要功能包括:
- 提供周期计数功能
- 支持读写操作
- 实现打印功能
- 作为虚拟外设的具体实现
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
- 代码分析:
- 无限循环计数
- 每个时钟周期递增
- 支持安全停止机制
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
- 代码分析:
- 写操作更新计数值
- 读操作返回当前计数值
- 包含线程安全控制
5. 总结
该虚拟周期计数器序列具有以下特点:
- 精确的周期计数功能
- 安全的线程控制机制
- 标准化的虚拟外设接口
- 灵活的读写操作支持
作为验证环境的高级组件,它为性能分析和调试提供了重要的周期计数功能,确保能够准确测量各类操作的执行周期。