Bootstrap

RISC_V GPU skybox 系列 core 模块之VX_lsu_unit.sv & VX_lsu_slice.sv(4)

VX_lsu_slice.sv

内存调度器

  wire                                    lsu_mem_req_valid;
    wire                                    lsu_mem_req_rw;
    wire [NUM_LANES-1:0]                    lsu_mem_req_mask;
    wire [NUM_LANES-1:0][LSU_WORD_SIZE-1:0] lsu_mem_req_byteen;
    wire [NUM_LANES-1:0][LSU_ADDR_WIDTH-1:0] lsu_mem_req_addr;
    wire [NUM_LANES-1:0][`ADDR_TYPE_WIDTH-1:0] lsu_mem_req_atype;
    wire [NUM_LANES-1:0][(LSU_WORD_SIZE*8)-1:0] lsu_mem_req_data;
    wire [LSU_TAG_WIDTH-1:0]                lsu_mem_req_tag;
    wire                                    lsu_mem_req_ready;

    wire                                    lsu_mem_rsp_valid;
    wire [NUM_LANES-1:0]                    lsu_mem_rsp_mask;
    wire [NUM_LANES-1:0][(LSU_WORD_SIZE*8)-1:0] lsu_mem_rsp_data;
    wire [LSU_TAG_WIDTH-1:0]                lsu_mem_rsp_tag;
    wire                                    lsu_mem_rsp_ready;

    `RESET_RELAY (mem_scheduler_reset, reset);

    VX_mem_scheduler #(
        .INSTANCE_ID ($sformatf("%s-scheduler", INSTANCE_ID)),
        .CORE_REQS   (NUM_LANES),
        .MEM_CHANNELS(NUM_LANES),
        .WORD_SIZE   (LSU_WORD_SIZE),
        .LINE_SIZE   (LSU_WORD_SIZE),
        .ADDR_WIDTH  (LSU_ADDR_WIDTH),
        .ATYPE_WIDTH (`ADDR_TYPE_WIDTH),
        .TAG_WIDTH   (TAG_WIDTH),
        .CORE_QUEUE_SIZE (`LSUQ_IN_SIZE),
        .MEM_QUEUE_SIZE (`LSUQ_OUT_SIZE),
        .UUID_WIDTH  (`UUID_WIDTH),
        .RSP_PARTIAL (1),
        .MEM_OUT_BUF (0),
        .CORE_OUT_BUF(0)
    ) mem_scheduler (
        .clk            (clk),
        .reset          (mem_scheduler_reset),

        // Input request
        .core_req_valid (mem_req_valid),
        .core_req_rw    (mem_req_rw),
        .core_req_mask  (mem_req_mask),
        .core_req_byteen(mem_req_byteen),
        .core_req_addr  (mem_req_addr),
        .core_req_atype (mem_req_atype),
        .core_req_data  (mem_req_data),
        .core_req_tag   (mem_req_tag),
        .core_req_ready (mem_req_ready),
        `UNUSED_PIN (core_req_empty),
        `UNUSED_PIN (core_write_notify),

        // Output response
        .core_rsp_valid (mem_rsp_valid),
        .core_rsp_mask  (mem_rsp_mask),
        .core_rsp_data  (mem_rsp_data),
        .core_rsp_tag   (mem_rsp_tag),
        .core_rsp_sop   (mem_rsp_sop),
        .core_rsp_eop   (mem_rsp_eop),
        .core_rsp_ready (mem_rsp_ready),

        // Memory request
        .mem_req_valid  (lsu_mem_req_valid),
        .mem_req_rw     (lsu_mem_req_rw),
        .mem_req_mask   (lsu_mem_req_mask),
        .mem_req_byteen (lsu_mem_req_byteen),
        .mem_req_addr   (lsu_mem_req_addr),
        .mem_req_atype  (lsu_mem_req_atype),
        .mem_req_data   (lsu_mem_req_data),
        .mem_req_tag    (lsu_mem_req_tag),
        .mem_req_ready  (lsu_mem_req_ready),

        // Memory response
        .mem_rsp_valid  (lsu_mem_rsp_valid),
        .mem_rsp_mask   (lsu_mem_rsp_mask),
        .mem_rsp_data   (lsu_mem_rsp_data),
        .mem_rsp_tag    (lsu_mem_rsp_tag),
        .mem_rsp_ready  (lsu_mem_rsp_ready)
    );

    assign lsu_mem_if.req_valid = lsu_mem_req_valid;
    assign lsu_mem_if.req_data.mask = lsu_mem_req_mask;
    assign lsu_mem_if.req_data.rw = lsu_mem_req_rw;
    assign lsu_mem_if.req_data.byteen = lsu_mem_req_byteen;
    assign lsu_mem_if.req_data.addr = lsu_mem_req_addr;
    assign lsu_mem_if.req_data.atype = lsu_mem_req_atype;
    assign lsu_mem_if.req_data.data = lsu_mem_req_data;
    assign lsu_mem_if.req_data.tag = lsu_mem_req_tag;
    assign lsu_mem_req_ready = lsu_mem_if.req_ready;

    assign lsu_mem_rsp_valid = lsu_mem_if.rsp_valid;
    assign lsu_mem_rsp_mask = lsu_mem_if.rsp_data.mask;
    assign lsu_mem_rsp_data = lsu_mem_if.rsp_data.data;
    assign lsu_mem_rsp_tag = lsu_mem_if.rsp_data.tag;
    assign lsu_mem_if.rsp_ready = lsu_mem_rsp_ready;

    wire [`UUID_WIDTH-1:0] rsp_uuid;
    wire [`NW_WIDTH-1:0] rsp_wid;
    wire [`PC_BITS-1:0] rsp_pc;
    wire rsp_wb;
    wire [`NR_BITS-1:0] rsp_rd;
    wire [`INST_LSU_BITS-1:0] rsp_op_type;
    wire [NUM_LANES-1:0][REQ_ASHIFT-1:0] rsp_align;
    wire [PID_WIDTH-1:0] rsp_pid;
    `UNUSED_VAR (rsp_op_type)

这段代码实现了一个LSU(Load/Store Unit)模块的内存请求和响应处理逻辑。它包括信号定义、内存调度器实例化、信号赋值以及内存响应标签的解包。

主要功能

  1. 信号定义:定义了与内存请求和响应相关的信号,包括请求有效性、读写标志、掩码、字节使能、地址、数据和标签等。
  2. 内存调度器实例化:实例化了VX_mem_scheduler模块,用于协调内存请求和响应的调度。
  3. 信号赋值:将内部信号赋值给接口信号,以便与外部模块进行通信。
  4. 内存响应标签解包:将内存响应标签解包为各个字段,以便进一步处理。

接口描述

  • 输入接口

    • core_req_valid:内存请求有效信号。
    • core_req_rw:读写标志。
    • core_req_mask:请求掩码。
    • core_req_byteen:字节使能信号。
    • core_req_addr:请求地址。
    • core_req_atype:地址类型。
    • core_req_data:请求数据。
    • core_req_tag:请求标签。
    • core_req_ready:内存调度器是否准备好接收请求。
  • 输出接口

    • core_rsp_valid:内存响应有效信号。
    • core_rsp_mask:响应掩码。
    • core_rsp_data:响应数据。
    • core_rsp_tag:响应标签。
    • core_rsp_sop:响应开始信号。
    • core_rsp_eop:响应结束信号。
    • core_rsp_ready:内存调度器是否准备好发送响应。
输入数据流
  • 内存请求信号
    • mem_req_valid:指示内存请求是否有效。
    • mem_req_rw:内存请求的读/写标志。
    • mem_req_mask:内存请求的掩码。
    • mem_req_byteen:内存请求的字节使能信号。
    • mem_req_addr:内存请求的地址。
    • mem_req_atype:内存请求的地址类型。
    • mem_req_data:内存请求的数据。
    • mem_req_tag:内存请求的标签。
    • mem_req_ready:指示内存系统是否准备好接收请求。
输出数据流
  • 内存响应信号
    • mem_rsp_valid:指示内存响应是否有效。
    • mem_rsp_mask:内存响应的掩码。
    • mem_rsp_data:内存响应的数据。
    • mem_rsp_tag:内存响应的标签。
    • mem_rsp_ready:指示核心是否准备好接收响应。

解包内存响应标签

    // unpack memory response tag
    assign {
        rsp_uuid,
        rsp_wid,
        rsp_pc,
        rsp_wb,
        rsp_rd,
        rsp_op_type,
        rsp_align,
        rsp_pid,
        pkt_raddr,
        rsp_is_fence
    } = mem_rsp_tag;

 
  • 解包(unpack):将 mem_rsp_tag 信号解包成多个单独的信号。这些信号包含了内存响应的各种信息,例如唯一标识符(UUID)、工作线程ID(wid)、程序计数器(PC)、写回标志(wb)、目的寄存器(rd)、操作类型(op_type)、对齐信息(align)、进程ID(pid)、包读地址(pkt_raddr)和栅栏标志(fence)。

加载响应数据的格式化

// load response formatting
reg [NUM_LANES-1:0][`XLEN-1:0] rsp_data;

`ifdef XLEN_64
`ifdef EXT_F_ENABLE
// apply nan-boxing to flw outputs
wire rsp_is_float = rsp_rd[5];
`else
wire rsp_is_float = 0;
`endif
`endif
  • rsp_data:用于存储每个通道的加载响应数据。
  • rsp_is_float:在64位架构且启用浮点扩展(EXT_F_ENABLE)的情况下,判断目标寄存器是否为浮点寄存器。

数据位宽处理

根据架构位宽(32位或64位),对数据进行处理和格式化。

for (genvar i = 0; i < NUM_LANES; i++) begin
`ifdef XLEN_64
    wire [63:0] rsp_data64 = mem_rsp_data[i];
    wire [31:0] rsp_data32 = (rsp_align[i][2] ? mem_rsp_data[i][63:32] : mem_rsp_data[i][31:0]);
`else
    wire [31:0] rsp_data32 = mem_rsp_data[i];
`endif
    wire [15:0] rsp_data16 = rsp_align[i][1] ? rsp_data32[31:16] : rsp_data32[15:0];
    wire [7:0]  rsp_data8  = rsp_align[i][0] ? rsp_data16[15:8] : rsp_data16[7:0];
  • rsp_data64:在64位架构下,从 mem_rsp_data 中提取64位数据。
  • rsp_data32:从 mem_rsp_data 中提取32位数据,根据 rsp_align 决定高位或低位。
  • rsp_data16:从 rsp_data32 中提取16位数据,根据 rsp_align 决定高位或低位。
  • rsp_data8:从 rsp_data16 中提取8位数据,根据 rsp_align 决定高位或低位。
根据操作类型格式化数据
    always @(*) begin
        case (`INST_LSU_FMT(rsp_op_type))
        `INST_FMT_B:  rsp_data[i] = `XLEN'(signed'(rsp_data8));
        `INST_FMT_H:  rsp_data[i] = `XLEN'(signed'(rsp_data16));
        `INST_FMT_BU: rsp_data[i] = `XLEN'(unsigned'(rsp_data8));
        `INST_FMT_HU: rsp_data[i] = `XLEN'(unsigned'(rsp_data16));
    `ifdef XLEN_64
        `INST_FMT_W:  rsp_data[i] = rsp_is_float ? (`XLEN'(rsp_data32) | 64'hffffffff00000000) : `XLEN'(signed'(rsp_data32));
        `INST_FMT_WU: rsp_data[i] = `XLEN'(unsigned'(rsp_data32));
        `INST_FMT_D:  rsp_data[i] = `XLEN'(signed'(rsp_data64));
    `else
        `INST_FMT_W:  rsp_data[i] = `XLEN'(signed'(rsp_data32));
    `endif
        default: rsp_data[i] = 'x;
        endcase
    end
end
  • INST_LSU_FMT(rsp_op_type):根据操作类型,选择不同的数据格式化方式。
    • INST_FMT_B:将 rsp_data8 扩展为 XLEN 位的有符号数。
    • INST_FMT_H:将 rsp_data16 扩展为 XLEN 位的有符号数。
    • INST_FMT_BU:将 rsp_data8 扩展为 XLEN 位的无符号数。
    • INST_FMT_HU:将 rsp_data16 扩展为 XLEN 位的无符号数。
    • INST_FMT_W:在64位架构下,如果是浮点数,进行NaN-boxing,否则将 rsp_data32 扩展为 XLEN 位的有符号数。
    • INST_FMT_WU:将 rsp_data32 扩展为 XLEN 位的无符号数。
    • INST_FMT_D:在64位架构下,将 rsp_data64 扩展为 XLEN 位的有符号数。
    • default:默认情况下,将 rsp_data[i] 置为未定义值。
总结

这段代码实现了内存响应的解包和加载数据的格式化。通过解包内存响应标签,将内存响应的各个字段分离出来,并根据操作类型,对加载的数据进行格式化处理,确保数据在不同位宽和格式下能够正确处理和使用。

提交

  // commit

    VX_elastic_buffer #(
        .DATAW (`UUID_WIDTH + `NW_WIDTH + NUM_LANES + `PC_BITS + 1 + `NR_BITS + (NUM_LANES * `XLEN) + PID_WIDTH + 1 + 1),
        .SIZE  (2)
    ) rsp_buf (
        .clk       (clk),
        .reset     (reset),
        .valid_in  (mem_rsp_valid),
        .ready_in  (mem_rsp_ready),
        .data_in   ({rsp_uuid, rsp_wid, mem_rsp_mask, rsp_pc, rsp_wb, rsp_rd, rsp_data, rsp_pid, mem_rsp_sop_pkt, mem_rsp_eop_pkt}),
        .data_out  ({commit_rsp_if.data.uuid, commit_rsp_if.data.wid, commit_rsp_if.data.tmask, commit_rsp_if.data.PC, commit_rsp_if.data.wb, commit_rsp_if.data.rd, commit_rsp_if.data.data, commit_rsp_if.data.pid, commit_rsp_if.data.sop, commit_rsp_if.data.eop}),
        .valid_out (commit_rsp_if.valid),
        .ready_out (commit_rsp_if.ready)
    );

    VX_elastic_buffer #(
        .DATAW (`UUID_WIDTH + `NW_WIDTH + NUM_LANES + `PC_BITS + PID_WIDTH + 1 + 1),
        .SIZE  (2)
    ) no_rsp_buf (
        .clk       (clk),
        .reset     (reset),
        .valid_in  (no_rsp_buf_valid),
        .ready_in  (no_rsp_buf_ready),
        .data_in   ({execute_if.data.uuid, execute_if.data.wid, execute_if.data.tmask, execute_if.data.PC, execute_if.data.pid, execute_if.data.sop, execute_if.data.eop}),
        .data_out  ({commit_no_rsp_if.data.uuid, commit_no_rsp_if.data.wid, commit_no_rsp_if.data.tmask, commit_no_rsp_if.data.PC, commit_no_rsp_if.data.pid, commit_no_rsp_if.data.sop, commit_no_rsp_if.data.eop}),
        .valid_out (commit_no_rsp_if.valid),
        .ready_out (commit_no_rsp_if.ready)
    );
    assign commit_no_rsp_if.data.rd   = '0;
    assign commit_no_rsp_if.data.wb   = 1'b0;
    assign commit_no_rsp_if.data.data = commit_rsp_if.data.data; // arbiter MUX optimization

    VX_stream_arb #(
        .NUM_INPUTS (2),
        .DATAW      (RSP_ARB_DATAW),
        .ARBITER    ("P"), // prioritize commit_rsp_if
        .OUT_BUF    (3)
    ) rsp_arb (
        .clk       (clk),
        .reset     (reset),
        .valid_in  ({commit_no_rsp_if.valid, commit_rsp_if.valid}),
        .ready_in  ({commit_no_rsp_if.ready, commit_rsp_if.ready}),
        .data_in   ({commit_no_rsp_if.data, commit_rsp_if.data}),
        .data_out  (commit_if.data),
        .valid_out (commit_if.valid),
        .ready_out (commit_if.ready),
        `UNUSED_PIN (sel_out)
    );

这段代码实现了内存响应数据的提交逻辑,使用了弹性缓冲区(VX_elastic_buffer)和流仲裁器(VX_stream_arb)模块。

提交响应数据 (commit response data)
// commit

VX_elastic_buffer #(
    .DATAW (`UUID_WIDTH + `NW_WIDTH + NUM_LANES + `PC_BITS + 1 + `NR_BITS + (NUM_LANES * `XLEN) + PID_WIDTH + 1 + 1),
    .SIZE  (2)
) rsp_buf (
    .clk       (clk),
    .reset     (reset),
    .valid_in  (mem_rsp_valid),
    .ready_in  (mem_rsp_ready),
    .data_in   ({rsp_uuid, rsp_wid, mem_rsp_mask, rsp_pc, rsp_wb, rsp_rd, rsp_data, rsp_pid, mem_rsp_sop_pkt, mem_rsp_eop_pkt}),
    .data_out  ({commit_rsp_if.data.uuid, commit_rsp_if.data.wid, commit_rsp_if.data.tmask, commit_rsp_if.data.PC, commit_rsp_if.data.wb, commit_rsp_if.data.rd, commit_rsp_if.data.data, commit_rsp_if.data.pid, commit_rsp_if.data.sop, commit_rsp_if.data.eop}),
    .valid_out (commit_rsp_if.valid),
    .ready_out (commit_rsp_if.ready)
);
  • rsp_buf:这是一个弹性缓冲区,用于存储和传输内存响应数据。
    • DATAW:数据宽度,包括UUID、工作线程ID、掩码、程序计数器、写回标志、目标寄存器、数据、进程ID、SOP和EOP信号。
    • SIZE:缓冲区大小,这里设置为2。
    • clk:时钟信号。
    • reset:复位信号。
    • valid_in:输入数据有效信号。
    • ready_in:输入数据准备好信号。
    • data_in:输入数据,包括各个字段的组合。
    • data_out:输出数据,传递给 commit_rsp_if 接口。
    • valid_out:输出数据有效信号。
    • ready_out:输出数据准备好信号。
提交无响应数据 (commit no response data)
VX_elastic_buffer #(
    .DATAW (`UUID_WIDTH + `NW_WIDTH + NUM_LANES + `PC_BITS + PID_WIDTH + 1 + 1),
    .SIZE  (2)
) no_rsp_buf (
    .clk       (clk),
    .reset     (reset),
    .valid_in  (no_rsp_buf_valid),
    .ready_in  (no_rsp_buf_ready),
    .data_in   ({execute_if.data.uuid, execute_if.data.wid, execute_if.data.tmask, execute_if.data.PC, execute_if.data.pid, execute_if.data.sop, execute_if.data.eop}),
    .data_out  ({commit_no_rsp_if.data.uuid, commit_no_rsp_if.data.wid, commit_no_rsp_if.data.tmask, commit_no_rsp_if.data.PC, commit_no_rsp_if.data.pid, commit_no_rsp_if.data.sop, commit_no_rsp_if.data.eop}),
    .valid_out (commit_no_rsp_if.valid),
    .ready_out (commit_no_rsp_if.ready)
);
assign commit_no_rsp_if.data.rd   = '0;
assign commit_no_rsp_if.data.wb   = 1'b0;
assign commit_no_rsp_if.data.data = commit_rsp_if.data.data; // arbiter MUX optimization
  • no_rsp_buf:这是另一个弹性缓冲区,用于存储和传输无响应数据。

    • DATAW:数据宽度,包括UUID、工作线程ID、掩码、程序计数器、进程ID、SOP和EOP信号。
    • SIZE:缓冲区大小,这里设置为2。
    • clk:时钟信号。
    • reset:复位信号。
    • valid_in:输入数据有效信号。
    • ready_in:输入数据准备好信号。
    • data_in:输入数据,包括各个字段的组合。
    • data_out:输出数据,传递给 commit_no_rsp_if 接口。
    • valid_out:输出数据有效信号。
    • ready_out:输出数据准备好信号。
  • assign 语句:

    • commit_no_rsp_if.data.rd:将目标寄存器设为0。
    • commit_no_rsp_if.data.wb:将写回标志设为0。
    • commit_no_rsp_if.data.data:将数据设为 commit_rsp_if.data.data,用于仲裁器MUX优化。
响应数据仲裁 (response data arbitration)
VX_stream_arb #(
    .NUM_INPUTS (2),
    .DATAW      (RSP_ARB_DATAW),
    .ARBITER    ("P"), // prioritize commit_rsp_if
    .OUT_BUF    (3)
) rsp_arb (
    .clk       (clk),
    .reset     (reset),
    .valid_in  ({commit_no_rsp_if.valid, commit_rsp_if.valid}),
    .ready_in  ({commit_no_rsp_if.ready, commit_rsp_if.ready}),
    .data_in   ({commit_no_rsp_if.data, commit_rsp_if.data}),
    .data_out  (commit_if.data),
    .valid_out (commit_if.valid),
    .ready_out (commit_if.ready),
    `UNUSED_PIN (sel_out)
);
  • rsp_arb:这是一个流仲裁器,用于仲裁多个输入的数据流,并输出到 commit_if 接口。
    • NUM_INPUTS:输入数据流的数量,这里设置为2。
    • DATAW:数据宽度。
    • ARBITER:仲裁策略,这里设置为"P",优先处理 commit_rsp_if
    • OUT_BUF:输出缓冲区大小,这里设置为3。
    • clk:时钟信号。
    • reset:复位信号。
    • valid_in:输入数据有效信号,包含 commit_no_rsp_ifcommit_rsp_if 的有效信号。
    • ready_in:输入数据准备好信号,包含 commit_no_rsp_ifcommit_rsp_if 的准备好信号。
    • data_in:输入数据,包含 commit_no_rsp_ifcommit_rsp_if 的数据。
    • data_out:输出数据,传递给 commit_if 接口。
    • valid_out:输出数据有效信号。
    • ready_out:输出数据准备好信号。
总结

这段代码实现了内存响应数据的提交逻辑,包括响应数据和无响应数据的处理。使用弹性缓冲区(VX_elastic_buffer)存储和传输数据,并通过流仲裁器(VX_stream_arb)仲裁多个输入的数据流,最终输出到 commit_if 接口。这确保了内存响应数据的有效传输和处理。

Trace


`ifdef DBG_TRACE_MEM
    always @(posedge clk) begin
        if (execute_if.valid && fence_lock) begin
            `TRACE(1, ("%d: *** %s fence wait\n", $time, INSTANCE_ID));
        end
        if (mem_req_fire) begin
            if (mem_req_rw) begin
                `TRACE(1, ("%d: %s Wr Req: wid=%0d, PC=0x%0h, tmask=%b, addr=", $time, INSTANCE_ID, execute_if.data.wid, {execute_if.data.PC, 1'b0}, mem_req_mask));
                `TRACE_ARRAY1D(1, "0x%h", full_addr, NUM_LANES);
                `TRACE(1, (", atype="));
                `TRACE_ARRAY1D(1, "%b", mem_req_atype, NUM_LANES);
                `TRACE(1, (", byteen=0x%0h, data=", mem_req_byteen));
                `TRACE_ARRAY1D(1, "0x%0h", mem_req_data, NUM_LANES);
                `TRACE(1, (", tag=0x%0h (#%0d)\n", mem_req_tag, execute_if.data.uuid));
            end else begin
                `TRACE(1, ("%d: %s Rd Req: wid=%0d, PC=0x%0h, tmask=%b, addr=", $time, INSTANCE_ID, execute_if.data.wid, {execute_if.data.PC, 1'b0}, mem_req_mask));
                `TRACE_ARRAY1D(1, "0x%h", full_addr, NUM_LANES);
                `TRACE(1, (", atype="));
                `TRACE_ARRAY1D(1, "%b", mem_req_atype, NUM_LANES);
                `TRACE(1, (", byteen=0x%0h, rd=%0d, tag=0x%0h (#%0d)\n", mem_req_byteen, execute_if.data.rd, mem_req_tag, execute_if.data.uuid));
            end
        end
        if (mem_rsp_fire) begin
            `TRACE(1, ("%d: %s Rsp: wid=%0d, PC=0x%0h, tmask=%b, rd=%0d, sop=%b, eop=%b, data=",
                $time, INSTANCE_ID, rsp_wid, {rsp_pc, 1'b0}, mem_rsp_mask, rsp_rd, mem_rsp_sop, mem_rsp_eop));
            `TRACE_ARRAY1D(1, "0x%0h", mem_rsp_data, NUM_LANES);
            `TRACE(1, (", tag=0x%0h (#%0d)\n", mem_rsp_tag, rsp_uuid));
        end
    end
`endif

`ifdef DBG_SCOPE_LSU
    VX_scope_tap #(
        .SCOPE_ID (3),
        .TRIGGERW (3),
        .PROBEW   (1 + NUM_LANES*(`XLEN + LSU_WORD_SIZE + LSU_WORD_SIZE*8) + `UUID_WIDTH + NUM_LANES*LSU_WORD_SIZE*8 + `UUID_WIDTH)
    ) scope_tap (
        .clk    (clk),
        .reset  (scope_reset),
        .start  (1'b0),
        .stop   (1'b0),
        .triggers({reset, mem_req_fire, mem_rsp_fire}),
        .probes ({mem_req_rw, full_addr, mem_req_byteen, mem_req_data, execute_if.data.uuid, rsp_data, rsp_uuid}),
        .bus_in (scope_bus_in),
        .bus_out(scope_bus_out)
    );
`else
    `SCOPE_IO_UNUSED()
`endif

这段代码实现了内存请求和响应的调试跟踪和范围探针(scope tap)功能。

内存请求和响应的调试跟踪
`ifdef DBG_TRACE_MEM
    always @(posedge clk) begin
        if (execute_if.valid && fence_lock) begin
            `TRACE(1, ("%d: *** %s fence wait\n", $time, INSTANCE_ID));
        end
        if (mem_req_fire) begin
            if (mem_req_rw) begin
                `TRACE(1, ("%d: %s Wr Req: wid=%0d, PC=0x%0h, tmask=%b, addr=", $time, INSTANCE_ID, execute_if.data.wid, {execute_if.data.PC, 1'b0}, mem_req_mask));
                `TRACE_ARRAY1D(1, "0x%h", full_addr, NUM_LANES);
                `TRACE(1, (", atype="));
                `TRACE_ARRAY1D(1, "%b", mem_req_atype, NUM_LANES);
                `TRACE(1, (", byteen=0x%0h, data=", mem_req_byteen));
                `TRACE_ARRAY1D(1, "0x%0h", mem_req_data, NUM_LANES);
                `TRACE(1, (", tag=0x%0h (#%0d)\n", mem_req_tag, execute_if.data.uuid));
            end else begin
                `TRACE(1, ("%d: %s Rd Req: wid=%0d, PC=0x%0h, tmask=%b, addr=", $time, INSTANCE_ID, execute_if.data.wid, {execute_if.data.PC, 1'b0}, mem_req_mask));
                `TRACE_ARRAY1D(1, "0x%h", full_addr, NUM_LANES);
                `TRACE(1, (", atype="));
                `TRACE_ARRAY1D(1, "%b", mem_req_atype, NUM_LANES);
                `TRACE(1, (", byteen=0x%0h, rd=%0d, tag=0x%0h (#%0d)\n", mem_req_byteen, execute_if.data.rd, mem_req_tag, execute_if.data.uuid));
            end
        end
        if (mem_rsp_fire) begin
            `TRACE(1, ("%d: %s Rsp: wid=%0d, PC=0x%0h, tmask=%b, rd=%0d, sop=%b, eop=%b, data=",
                $time, INSTANCE_ID, rsp_wid, {rsp_pc, 1'b0}, mem_rsp_mask, rsp_rd, mem_rsp_sop, mem_rsp_eop));
            `TRACE_ARRAY1D(1, "0x%0h", mem_rsp_data, NUM_LANES);
            `TRACE(1, (", tag=0x%0h (#%0d)\n", mem_rsp_tag, rsp_uuid));
        end
    end
`endif
  • 条件编译指令 (ifdef DBG_TRACE_MEM):用于在编译时选择性包含调试跟踪代码。
  • always @(posedge clk):在每个时钟上升沿触发。
  • if (execute_if.valid && fence_lock):如果执行接口有效且栅栏锁定,则记录栅栏等待信息。
  • if (mem_req_fire):如果内存请求触发(有效且准备好),则记录请求信息。
    • if (mem_req_rw):如果是写请求,记录写请求的详细信息。
    • else:如果是读请求,记录读请求的详细信息。
  • if (mem_rsp_fire):如果内存响应触发(有效且准备好),则记录响应信息。
范围探针(scope tap)的实现
`ifdef DBG_SCOPE_LSU
    VX_scope_tap #(
        .SCOPE_ID (3),
        .TRIGGERW (3),
        .PROBEW   (1 + NUM_LANES*(`XLEN + LSU_WORD_SIZE + LSU_WORD_SIZE*8) + `UUID_WIDTH + NUM_LANES*LSU_WORD_SIZE*8 + `UUID_WIDTH)
    ) scope_tap (
        .clk    (clk),
        .reset  (scope_reset),
        .start  (1'b0),
        .stop   (1'b0),
        .triggers({reset, mem_req_fire, mem_rsp_fire}),
        .probes ({mem_req_rw, full_addr, mem_req_byteen, mem_req_data, execute_if.data.uuid, rsp_data, rsp_uuid}),
        .bus_in (scope_bus_in),
        .bus_out(scope_bus_out)
    );
`else
    `SCOPE_IO_UNUSED()
`endif
  • 条件编译指令 (ifdef DBG_SCOPE_LSU):用于在编译时选择性包含范围探针代码。
  • VX_scope_tap:实例化一个范围探针模块,用于调试和监控内部信号。
    • SCOPE_ID:范围探针的ID,这里设置为3。
    • TRIGGERW:触发信号的宽度,这里设置为3。
    • PROBEW:探针信号的宽度,计算得出。
    • clk:时钟信号。
    • reset:复位信号。
    • startstop:控制范围探针的启动和停止信号,这里都设置为0。
    • triggers:触发信号,包括复位信号、内存请求触发信号和内存响应触发信号。
    • probes:探针信号,包括内存请求的读写标志、完整地址、字节使能信号、请求数据、UUID、响应数据和响应UUID。
    • bus_inbus_out:范围探针的输入和输出总线信号。
总结

这段代码实现了内存请求和响应的调试跟踪和范围探针功能。通过条件编译指令,可以在编译时选择性包含这些调试功能,以便在运行时记录和监控内存请求和响应的详细信息。这对于调试和优化内存访问操作非常有用。