core-v-verif系列之cv32e40p UVM环境介绍<16>

UVM环境介绍
HEAD commitID: 1f968ef

1. tb/core/dp_ram.sv


// 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)模块。

1. 版权和许可声明

// 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 许可协议。

2. 禁用 DVT 检查器警告

// 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 组件。

3. 模块定义

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:时钟信号,作为整个模块的同步时钟。
    • 端口 A
      • 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 位,用于控制每个字节的写入。
    • 端口 B
      • 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 位,用于控制每个字节的写入。

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_intaddr_b_int:内部地址变量,用于存储端口 A 和端口 B 的内部地址。

5. 组合逻辑部分

    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_iaddr_b_i 的低 2 位清零,以实现按字对齐的地址访问。

6. 时序逻辑部分

    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
  • 端口 A 读操作:在时钟上升沿,通过 for 循环将存储器中从 addr_a_int 开始的连续 INSTR_RDATA_WIDTH/8 个字节的数据依次赋值给 rdata_a_o
  • 端口 B 操作
    • en_b_i 为高电平时,端口 B 有效。
      • 写操作:当 we_b_i 为高电平时,根据 be_b_i 的值,将 wdata_b_i 的相应字节写入存储器。
      • 读操作:当 we_b_i 为低电平时,进行读操作。如果命令行中指定了 verbose 参数,则打印读操作的地址和数据信息。然后将存储器中从 addr_b_int 开始的 4 个字节的数据依次赋值给 rdata_b_o

7. DPI-C 导出

    export "DPI-C" function read_byte;
    export "DPI-C" task write_byte;

这两行代码将 read_byte 函数和 write_byte 任务导出为 DPI-C 接口,以便在 C 代码中调用。

8. 函数和任务定义

    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 变量。

9. 结束模块定义和恢复 DVT 检查器警告

endmodule // dp_ram

//@DVT_LINTER_WAIVER_END "MT20210811_0"

结束模块定义,并恢复之前禁用的 DVT 检查器警告。

总结

该文件定义了一个双端口 RAM 模块,具有两个独立的端口(端口 A 和端口 B),可以同时进行读写操作。端口 A 主要用于读取数据,端口 B 可以进行读写操作,并且支持字节使能。此外,该模块还提供了 DPI-C 接口,方便与 C 代码进行交互。

2. tb/core/mm_ram.sv


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

你可能感兴趣的:(core-v-verif)