UVM环境介绍
HEAD commitID: 1f968ef
// DVT LINTER waivers are fine because this is not a UVM component.
//@DVT_LINTER_WAIVER_START "MT20210811_0" disable SVTB.29.1.3.1, SVTB.29.1.7
module dp_ram
#(parameter ADDR_WIDTH = 8,
parameter INSTR_RDATA_WIDTH = 128)
(input logic clk_i,
input logic en_a_i,
input logic [ADDR_WIDTH-1:0] addr_a_i,
input logic [31:0] wdata_a_i,
output logic [INSTR_RDATA_WIDTH-1:0] rdata_a_o,
input logic we_a_i,
input logic [3:0] be_a_i,
input logic en_b_i,
input logic [ADDR_WIDTH-1:0] addr_b_i,
input logic [31:0] wdata_b_i,
output logic [31:0] rdata_b_o,
input logic we_b_i,
input logic [3:0] be_b_i);
localparam bytes = 2**ADDR_WIDTH;
logic [7:0] mem[bytes];
logic [ADDR_WIDTH-1:0] addr_a_int;
logic [ADDR_WIDTH-1:0] addr_b_int;
always_comb addr_a_int = {
addr_a_i[ADDR_WIDTH-1:2], 2'b0};
always_comb addr_b_int = {
addr_b_i[ADDR_WIDTH-1:2], 2'b0};
always @(posedge clk_i) begin
for (int i = 0; i < INSTR_RDATA_WIDTH/8; i++) begin
rdata_a_o[(i*8)+: 8] <= mem[addr_a_int + i];
end
/* addr_b_i is the actual memory address referenced */
if (en_b_i) begin
/* handle writes */
if (we_b_i) begin
if (be_b_i[0]) mem[addr_b_int ] <= wdata_b_i[ 0+:8];
if (be_b_i[1]) mem[addr_b_int + 1] <= wdata_b_i[ 8+:8];
if (be_b_i[2]) mem[addr_b_int + 2] <= wdata_b_i[16+:8];
if (be_b_i[3]) mem[addr_b_int + 3] <= wdata_b_i[24+:8];
end
/* handle reads */
else begin
if ($test$plusargs("verbose"))
$display("read addr=0x%08x: data=0x%08x", addr_b_int,
{
mem[addr_b_int + 3], mem[addr_b_int + 2],
mem[addr_b_int + 1], mem[addr_b_int + 0]});
rdata_b_o[ 7: 0] <= mem[addr_b_int ];
rdata_b_o[15: 8] <= mem[addr_b_int + 1];
rdata_b_o[23:16] <= mem[addr_b_int + 2];
rdata_b_o[31:24] <= mem[addr_b_int + 3];
end
end
end
export "DPI-C" function read_byte;
export "DPI-C" task write_byte;
function int read_byte(input logic [ADDR_WIDTH-1:0] byte_addr);
read_byte = mem[byte_addr];
endfunction
task write_byte(input logic [ADDR_WIDTH-1:0] byte_addr, logic [7:0] val, output logic [7:0] other);
mem[byte_addr] = val;
other = mem[byte_addr];
endtask
endmodule // dp_ram
//@DVT_LINTER_WAIVER_END "MT20210811_0"
dp_ram.sv
是一个 SystemVerilog 文件,用于定义一个双端口随机存取存储器(Dual-Port RAM, DP-RAM)模块。
// Copyright 2015 ETH Zurich and University of Bologna.
// Copyright 2017 Embecosm Limited
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this 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.
此部分声明了文件的版权归属以及使用该文件需遵循的 Solderpad Hardware License 0.51 许可协议。
// DVT LINTER waivers are fine because this is not a UVM component.
//@DVT_LINTER_WAIVER_START "MT20210811_0" disable SVTB.29.1.3.1, SVTB.29.1.7
这里禁用了 DVT 检查器的特定警告,原因是该模块并非 UVM 组件。
module dp_ram
#(parameter ADDR_WIDTH = 8,
parameter INSTR_RDATA_WIDTH = 128)
(input logic clk_i,
input logic en_a_i,
input logic [ADDR_WIDTH-1:0] addr_a_i,
input logic [31:0] wdata_a_i,
output logic [INSTR_RDATA_WIDTH-1:0] rdata_a_o,
input logic we_a_i,
input logic [3:0] be_a_i,
input logic en_b_i,
input logic [ADDR_WIDTH-1:0] addr_b_i,
input logic [31:0] wdata_b_i,
output logic [31:0] rdata_b_o,
input logic we_b_i,
input logic [3:0] be_b_i);
ADDR_WIDTH
:地址总线的宽度,默认值为 8。INSTR_RDATA_WIDTH
:端口 A 的读数据总线宽度,默认值为 128。clk_i
:时钟信号,作为整个模块的同步时钟。en_a_i
:端口 A 的使能信号,高电平有效。addr_a_i
:端口 A 的地址输入,宽度为 ADDR_WIDTH
。wdata_a_i
:端口 A 的写数据输入,宽度为 32 位。rdata_a_o
:端口 A 的读数据输出,宽度为 INSTR_RDATA_WIDTH
。we_a_i
:端口 A 的写使能信号,高电平有效。be_a_i
:端口 A 的字节使能信号,宽度为 4 位,用于控制每个字节的写入。en_b_i
:端口 B 的使能信号,高电平有效。addr_b_i
:端口 B 的地址输入,宽度为 ADDR_WIDTH
。wdata_b_i
:端口 B 的写数据输入,宽度为 32 位。rdata_b_o
:端口 B 的读数据输出,宽度为 32 位。we_b_i
:端口 B 的写使能信号,高电平有效。be_b_i
:端口 B 的字节使能信号,宽度为 4 位,用于控制每个字节的写入。 localparam bytes = 2**ADDR_WIDTH;
logic [7:0] mem[bytes];
logic [ADDR_WIDTH-1:0] addr_a_int;
logic [ADDR_WIDTH-1:0] addr_b_int;
bytes
:本地参数,计算出存储器的字节数,通过 2**ADDR_WIDTH
得到。mem
:一个数组,用于模拟 RAM 的存储单元,每个元素为 8 位,数组大小为 bytes
。addr_a_int
和 addr_b_int
:内部地址变量,用于存储端口 A 和端口 B 的内部地址。 always_comb addr_a_int = {addr_a_i[ADDR_WIDTH-1:2], 2'b0};
always_comb addr_b_int = {addr_b_i[ADDR_WIDTH-1:2], 2'b0};
这两个 always_comb
块是组合逻辑,将输入地址 addr_a_i
和 addr_b_i
的低 2 位清零,以实现按字对齐的地址访问。
always @(posedge clk_i) begin
for (int i = 0; i < INSTR_RDATA_WIDTH/8; i++) begin
rdata_a_o[(i*8)+: 8] <= mem[addr_a_int + i];
end
/* addr_b_i is the actual memory address referenced */
if (en_b_i) begin
/* handle writes */
if (we_b_i) begin
if (be_b_i[0]) mem[addr_b_int ] <= wdata_b_i[ 0+:8];
if (be_b_i[1]) mem[addr_b_int + 1] <= wdata_b_i[ 8+:8];
if (be_b_i[2]) mem[addr_b_int + 2] <= wdata_b_i[16+:8];
if (be_b_i[3]) mem[addr_b_int + 3] <= wdata_b_i[24+:8];
end
/* handle reads */
else begin
if ($test$plusargs("verbose"))
$display("read addr=0x%08x: data=0x%08x", addr_b_int,
{mem[addr_b_int + 3], mem[addr_b_int + 2],
mem[addr_b_int + 1], mem[addr_b_int + 0]});
rdata_b_o[ 7: 0] <= mem[addr_b_int ];
rdata_b_o[15: 8] <= mem[addr_b_int + 1];
rdata_b_o[23:16] <= mem[addr_b_int + 2];
rdata_b_o[31:24] <= mem[addr_b_int + 3];
end
end
end
for
循环将存储器中从 addr_a_int
开始的连续 INSTR_RDATA_WIDTH/8
个字节的数据依次赋值给 rdata_a_o
。en_b_i
为高电平时,端口 B 有效。
we_b_i
为高电平时,根据 be_b_i
的值,将 wdata_b_i
的相应字节写入存储器。we_b_i
为低电平时,进行读操作。如果命令行中指定了 verbose
参数,则打印读操作的地址和数据信息。然后将存储器中从 addr_b_int
开始的 4 个字节的数据依次赋值给 rdata_b_o
。 export "DPI-C" function read_byte;
export "DPI-C" task write_byte;
这两行代码将 read_byte
函数和 write_byte
任务导出为 DPI-C 接口,以便在 C 代码中调用。
function int read_byte(input logic [ADDR_WIDTH-1:0] byte_addr);
read_byte = mem[byte_addr];
endfunction
task write_byte(input logic [ADDR_WIDTH-1:0] byte_addr, logic [7:0] val, output logic [7:0] other);
mem[byte_addr] = val;
other = mem[byte_addr];
endtask
read_byte
函数:接受一个字节地址作为输入,返回该地址处的存储器内容。write_byte
任务:接受一个字节地址和一个 8 位值作为输入,将该值写入指定地址的存储器,并将写入后的值输出到 other
变量。endmodule // dp_ram
//@DVT_LINTER_WAIVER_END "MT20210811_0"
结束模块定义,并恢复之前禁用的 DVT 检查器警告。
该文件定义了一个双端口 RAM 模块,具有两个独立的端口(端口 A 和端口 B),可以同时进行读写操作。端口 A 主要用于读取数据,端口 B 可以进行读写操作,并且支持字节使能。此外,该模块还提供了 DPI-C 接口,方便与 C 代码进行交互。
module mm_ram
`ifndef VERILATOR
import uvm_pkg::*;
`include "uvm_macros.svh"
`endif
#(
parameter RAM_ADDR_WIDTH = 16,
INSTR_RDATA_WIDTH = 128, // width of read_data on instruction bus
DATA_RDATA_WIDTH = 32, // width of read_data on data bus
DBG_ADDR_WIDTH = 14, // POT ammount of memory allocated for debugger
// physically located at end of memory
IRQ_WIDTH = 32 // IRQ vector width
)
(
input logic clk_i,
input logic rst_ni,
input logic [31:0] dm_halt_addr_i,
input logic instr_req_i,
input logic [31:0] instr_addr_i,
output logic [INSTR_RDATA_WIDTH-1:0] instr_rdata_o,
output logic instr_rvalid_o,
output logic instr_gnt_o,
input logic data_req_i,
input logic [31:0] data_addr_i,
input logic data_we_i,
input logic [3:0] data_be_i,
input logic [31:0] data_wdata_i,
output logic [31:0] data_rdata_o,
output logic data_rvalid_o,
output logic data_gnt_o,
input logic [4:0] irq_id_i,
input logic irq_ack_i,
output logic [IRQ_WIDTH-1:0] irq_o,
input logic [31:0] pc_core_id_i,
output logic debug_req_o,
output logic tests_passed_o,
output logic tests_failed_o,
output logic exit_valid_o,
output logic [31:0] exit_value_o);
localparam int RND_STALL_REGS = 16;
localparam int RND_STALL_INSTR_EN = 0;
localparam int RND_STALL_INSTR_MODE = 2;
localparam int RND_STALL_INSTR_MAX = 4;
localparam int RND_STALL_INSTR_GNT = 6;
localparam int RND_STALL_INSTR_VALID = 8;
localparam int RND_STALL_DATA_EN = 1;
localparam int RND_STALL_DATA_MODE = 3;
localparam int RND_STALL_DATA_MAX = 5;
localparam int RND_STALL_DATA_GNT = 7;
localparam int RND_STALL_DATA_VALID = 9;
localparam int RND_IRQ_ID = 31;
localparam int MMADDR_PRINT = 32'h1000_0000;
localparam int MMADDR_TESTSTATUS = 32'h2000_0000;
localparam int MMADDR_EXIT = 32'h2000_0004;
localparam int MMADDR_SIGBEGIN = 32'h2000_0008;
localparam int MMADDR_SIGEND = 32'h2000_000C;
localparam int MMADDR_SIGDUMP = 32'h2000_0010;
localparam int MMADDR_TIMERREG = 32'h1500_0000;
localparam int MMADDR_TIMERVAL = 32'h1500_0004;
localparam int MMADDR_DBG = 32'h1500_0008;
localparam int MMADDR_RNDSTALL = 16'h1600;
localparam int MMADDR_RNDNUM = 32'h1500_1000;
localparam int MMADDR_TICKS = 32'h1500_1004;
// UVM info tags
localparam string MM_RAM_TAG = "MM_RAM";
localparam string RNDSTALL_TAG = "RNDSTALL";
// mux for read and writes
enum logic [2:0]{
RAM, MM, RND_STALL, ERR, RND_NUM, TICKS} select_rdata_d, select_rdata_q;
enum logic {
T_RAM, T_PER} transaction;
int i;
logic [31:0] data_addr_aligned;
// signals for handshake
logic data_rvalid_q;
logic instr_rvalid_q;
logic [INSTR_RDATA_WIDTH