mirror of
https://github.com/vortexgpgpu/vortex.git
synced 2025-04-21 04:17:53 -04:00
372 lines
No EOL
15 KiB
Systemverilog
372 lines
No EOL
15 KiB
Systemverilog
`include "VX_define.vh"
|
|
|
|
module VX_lsu_unit #(
|
|
parameter CORE_ID = 0
|
|
) (
|
|
`SCOPE_IO_VX_lsu_unit
|
|
|
|
input wire clk,
|
|
input wire reset,
|
|
|
|
// Dcache interface
|
|
VX_dcache_req_if.master dcache_req_if,
|
|
VX_dcache_rsp_if.slave dcache_rsp_if,
|
|
|
|
// inputs
|
|
VX_lsu_req_if.slave lsu_req_if,
|
|
|
|
// outputs
|
|
VX_commit_if.master ld_commit_if,
|
|
VX_commit_if.master st_commit_if
|
|
);
|
|
localparam MEM_ASHIFT = `CLOG2(`MEM_BLOCK_SIZE);
|
|
localparam MEM_ADDRW = 32 - MEM_ASHIFT;
|
|
localparam REQ_ASHIFT = `CLOG2(`DCACHE_WORD_SIZE);
|
|
|
|
`STATIC_ASSERT(0 == (`IO_BASE_ADDR % MEM_ASHIFT), ("invalid parameter"))
|
|
`STATIC_ASSERT(0 == (`SMEM_BASE_ADDR % MEM_ASHIFT), ("invalid parameter"))
|
|
`STATIC_ASSERT(`SMEM_SIZE == `MEM_BLOCK_SIZE * (`SMEM_SIZE / `MEM_BLOCK_SIZE), ("invalid parameter"))
|
|
|
|
wire req_valid;
|
|
wire [`UUID_BITS-1:0] req_uuid;
|
|
wire [`NUM_THREADS-1:0] req_tmask;
|
|
wire [`NUM_THREADS-1:0][31:0] req_addr;
|
|
wire [`INST_LSU_BITS-1:0] req_type;
|
|
wire [`NUM_THREADS-1:0][31:0] req_data;
|
|
wire [`NR_BITS-1:0] req_rd;
|
|
wire req_wb;
|
|
wire [`NW_BITS-1:0] req_wid;
|
|
wire [31:0] req_pc;
|
|
wire req_is_dup;
|
|
wire req_is_prefetch;
|
|
|
|
wire mbuf_empty;
|
|
|
|
wire [`NUM_THREADS-1:0][`CACHE_ADDR_TYPE_BITS-1:0] lsu_addr_type, req_addr_type;
|
|
|
|
// full address calculation
|
|
wire [`NUM_THREADS-1:0][31:0] full_addr;
|
|
for (genvar i = 0; i < `NUM_THREADS; i++) begin
|
|
assign full_addr[i] = lsu_req_if.base_addr[i] + lsu_req_if.offset;
|
|
end
|
|
|
|
// detect duplicate addresses
|
|
wire [`NUM_THREADS-2:0] addr_matches;
|
|
for (genvar i = 0; i < (`NUM_THREADS-1); i++) begin
|
|
assign addr_matches[i] = (lsu_req_if.base_addr[i+1] == lsu_req_if.base_addr[0]) || ~lsu_req_if.tmask[i+1];
|
|
end
|
|
|
|
wire lsu_is_dup = lsu_req_if.tmask[0] && (& addr_matches);
|
|
|
|
for (genvar i = 0; i < `NUM_THREADS; i++) begin
|
|
// is non-cacheable address
|
|
wire is_addr_nc = (full_addr[i][MEM_ASHIFT +: MEM_ADDRW] >= MEM_ADDRW'(`IO_BASE_ADDR >> MEM_ASHIFT));
|
|
if (`SM_ENABLE) begin
|
|
// is shared memory address
|
|
wire is_addr_sm = (full_addr[i][MEM_ASHIFT +: MEM_ADDRW] >= MEM_ADDRW'((`SMEM_BASE_ADDR - `SMEM_SIZE) >> MEM_ASHIFT))
|
|
& (full_addr[i][MEM_ASHIFT +: MEM_ADDRW] < MEM_ADDRW'(`SMEM_BASE_ADDR >> MEM_ASHIFT));
|
|
assign lsu_addr_type[i] = {is_addr_nc, is_addr_sm};
|
|
end else begin
|
|
assign lsu_addr_type[i] = is_addr_nc;
|
|
end
|
|
end
|
|
|
|
// fence stalls the pipeline until all pending requests are sent
|
|
wire fence_wait = lsu_req_if.is_fence && (req_valid || !mbuf_empty);
|
|
|
|
wire ready_in;
|
|
wire stall_in = ~ready_in && req_valid;
|
|
|
|
wire lsu_valid = lsu_req_if.valid && ~fence_wait;
|
|
|
|
wire lsu_wb = lsu_req_if.wb | lsu_req_if.is_prefetch;
|
|
|
|
VX_pipe_register #(
|
|
.DATAW (1 + 1 + 1 + `UUID_BITS + `NW_BITS + `NUM_THREADS + 32 + (`NUM_THREADS * 32) + (`NUM_THREADS * `CACHE_ADDR_TYPE_BITS) + `INST_LSU_BITS + `NR_BITS + 1 + (`NUM_THREADS * 32)),
|
|
.RESETW (1)
|
|
) req_pipe_reg (
|
|
.clk (clk),
|
|
.reset (reset),
|
|
.enable (!stall_in),
|
|
.data_in ({lsu_valid, lsu_is_dup, lsu_req_if.is_prefetch, lsu_req_if.uuid, lsu_req_if.wid, lsu_req_if.tmask, lsu_req_if.PC, full_addr, lsu_addr_type, lsu_req_if.op_type, lsu_req_if.rd, lsu_wb, lsu_req_if.store_data}),
|
|
.data_out ({req_valid, req_is_dup, req_is_prefetch, req_uuid, req_wid, req_tmask, req_pc, req_addr, req_addr_type, req_type, req_rd, req_wb, req_data})
|
|
);
|
|
|
|
// Can accept new request?
|
|
assign lsu_req_if.ready = ~stall_in && ~fence_wait;
|
|
|
|
wire [`UUID_BITS-1:0] rsp_uuid;
|
|
wire [`NW_BITS-1:0] rsp_wid;
|
|
wire [31:0] rsp_pc;
|
|
wire [`NR_BITS-1:0] rsp_rd;
|
|
wire rsp_wb;
|
|
wire [`INST_LSU_BITS-1:0] rsp_type;
|
|
wire rsp_is_dup;
|
|
wire rsp_is_prefetch;
|
|
|
|
reg [`LSUQ_SIZE-1:0][`NUM_THREADS-1:0] rsp_rem_mask;
|
|
wire [`NUM_THREADS-1:0] rsp_rem_mask_n;
|
|
wire [`NUM_THREADS-1:0] rsp_tmask;
|
|
|
|
reg [`NUM_THREADS-1:0] req_sent_mask;
|
|
reg is_req_start;
|
|
|
|
wire [`LSUQ_ADDR_BITS-1:0] mbuf_waddr, mbuf_raddr;
|
|
wire mbuf_full;
|
|
|
|
`UNUSED_VAR (rsp_type)
|
|
`UNUSED_VAR (rsp_is_prefetch)
|
|
|
|
wire [`NUM_THREADS-1:0][REQ_ASHIFT-1:0] req_offset, rsp_offset;
|
|
for (genvar i = 0; i < `NUM_THREADS; i++) begin
|
|
assign req_offset[i] = req_addr[i][1:0];
|
|
end
|
|
|
|
wire [`NUM_THREADS-1:0] dcache_req_fire = dcache_req_if.valid & dcache_req_if.ready;
|
|
|
|
wire dcache_rsp_fire = dcache_rsp_if.valid && dcache_rsp_if.ready;
|
|
|
|
wire [`NUM_THREADS-1:0] req_tmask_dup = req_tmask & {{(`NUM_THREADS-1){~req_is_dup}}, 1'b1};
|
|
|
|
wire mbuf_push = ~mbuf_full
|
|
&& (| ({`NUM_THREADS{req_valid}} & req_tmask_dup & dcache_req_if.ready))
|
|
&& is_req_start // first submission only
|
|
&& req_wb; // loads only
|
|
|
|
wire mbuf_pop = dcache_rsp_fire && (0 == rsp_rem_mask_n);
|
|
|
|
assign mbuf_raddr = dcache_rsp_if.tag[`CACHE_ADDR_TYPE_BITS +: `LSUQ_ADDR_BITS];
|
|
`UNUSED_VAR (dcache_rsp_if.tag)
|
|
|
|
// do not writeback from software prefetch
|
|
wire req_wb2 = req_wb && ~req_is_prefetch;
|
|
|
|
VX_index_buffer #(
|
|
.DATAW (`UUID_BITS + `NW_BITS + 32 + `NUM_THREADS + `NR_BITS + 1 + `INST_LSU_BITS + (`NUM_THREADS * REQ_ASHIFT) + 1 + 1),
|
|
.SIZE (`LSUQ_SIZE)
|
|
) req_metadata (
|
|
.clk (clk),
|
|
.reset (reset),
|
|
.write_addr (mbuf_waddr),
|
|
.acquire_slot (mbuf_push),
|
|
.read_addr (mbuf_raddr),
|
|
.write_data ({req_uuid, req_wid, req_pc, req_tmask, req_rd, req_wb2, req_type, req_offset, req_is_dup, req_is_prefetch}),
|
|
.read_data ({rsp_uuid, rsp_wid, rsp_pc, rsp_tmask, rsp_rd, rsp_wb, rsp_type, rsp_offset, rsp_is_dup, rsp_is_prefetch}),
|
|
.release_addr (mbuf_raddr),
|
|
.release_slot (mbuf_pop),
|
|
.full (mbuf_full),
|
|
.empty (mbuf_empty)
|
|
);
|
|
|
|
wire dcache_req_ready = &(dcache_req_if.ready | req_sent_mask | ~req_tmask_dup);
|
|
|
|
wire [`NUM_THREADS-1:0] req_sent_mask_n = req_sent_mask | dcache_req_fire;
|
|
|
|
always @(posedge clk) begin
|
|
if (reset) begin
|
|
req_sent_mask <= 0;
|
|
is_req_start <= 1;
|
|
end else begin
|
|
if (dcache_req_ready) begin
|
|
req_sent_mask <= 0;
|
|
is_req_start <= 1;
|
|
end else begin
|
|
req_sent_mask <= req_sent_mask_n;
|
|
is_req_start <= (0 == req_sent_mask_n);
|
|
end
|
|
end
|
|
end
|
|
|
|
// need to hold the acquired tag index until the full request is submitted
|
|
reg [`LSUQ_ADDR_BITS-1:0] req_tag_hold;
|
|
wire [`LSUQ_ADDR_BITS-1:0] req_tag = is_req_start ? mbuf_waddr : req_tag_hold;
|
|
always @(posedge clk) begin
|
|
if (mbuf_push) begin
|
|
req_tag_hold <= mbuf_waddr;
|
|
end
|
|
end
|
|
|
|
assign rsp_rem_mask_n = rsp_rem_mask[mbuf_raddr] & ~dcache_rsp_if.tmask;
|
|
|
|
always @(posedge clk) begin
|
|
if (mbuf_push) begin
|
|
rsp_rem_mask[mbuf_waddr] <= req_tmask_dup;
|
|
end
|
|
if (dcache_rsp_fire) begin
|
|
rsp_rem_mask[mbuf_raddr] <= rsp_rem_mask_n;
|
|
end
|
|
end
|
|
|
|
// ensure all dependencies for the requests are resolved
|
|
wire req_dep_ready = (req_wb && ~(mbuf_full && is_req_start))
|
|
|| (~req_wb && st_commit_if.ready);
|
|
|
|
// DCache Request
|
|
|
|
for (genvar i = 0; i < `NUM_THREADS; i++) begin
|
|
|
|
reg [3:0] mem_req_byteen;
|
|
reg [31:0] mem_req_data;
|
|
|
|
always @(*) begin
|
|
mem_req_byteen = {4{req_wb}};
|
|
case (`INST_LSU_WSIZE(req_type))
|
|
0: mem_req_byteen[req_offset[i]] = 1;
|
|
1: begin
|
|
mem_req_byteen[req_offset[i]] = 1;
|
|
mem_req_byteen[{req_offset[i][1], 1'b1}] = 1;
|
|
end
|
|
default : mem_req_byteen = {4{1'b1}};
|
|
endcase
|
|
end
|
|
|
|
always @(*) begin
|
|
mem_req_data = req_data[i];
|
|
case (req_offset[i])
|
|
1: mem_req_data[31:8] = req_data[i][23:0];
|
|
2: mem_req_data[31:16] = req_data[i][15:0];
|
|
3: mem_req_data[31:24] = req_data[i][7:0];
|
|
default:;
|
|
endcase
|
|
end
|
|
|
|
assign dcache_req_if.valid[i] = req_valid && req_dep_ready && req_tmask_dup[i] && !req_sent_mask[i];
|
|
assign dcache_req_if.rw[i] = ~req_wb;
|
|
assign dcache_req_if.addr[i] = req_addr[i][31:2];
|
|
assign dcache_req_if.byteen[i] = mem_req_byteen;
|
|
assign dcache_req_if.data[i] = mem_req_data;
|
|
assign dcache_req_if.tag[i] = {req_uuid, `LSU_TAG_ID_BITS'(req_tag), req_addr_type[i]};
|
|
end
|
|
|
|
assign ready_in = req_dep_ready && dcache_req_ready;
|
|
|
|
// send store commit
|
|
|
|
wire is_store_rsp = req_valid && ~req_wb && dcache_req_ready;
|
|
|
|
assign st_commit_if.valid = is_store_rsp;
|
|
assign st_commit_if.uuid = req_uuid;
|
|
assign st_commit_if.wid = req_wid;
|
|
assign st_commit_if.tmask = req_tmask;
|
|
assign st_commit_if.PC = req_pc;
|
|
assign st_commit_if.rd = 0;
|
|
assign st_commit_if.wb = 0;
|
|
assign st_commit_if.eop = 1'b1;
|
|
assign st_commit_if.data = 0;
|
|
|
|
// load response formatting
|
|
|
|
reg [`NUM_THREADS-1:0][31:0] rsp_data;
|
|
wire [`NUM_THREADS-1:0] rsp_tmask_qual;
|
|
|
|
for (genvar i = 0; i < `NUM_THREADS; i++) begin
|
|
wire [31:0] rsp_data32 = (i == 0 || rsp_is_dup) ? dcache_rsp_if.data[0] : dcache_rsp_if.data[i];
|
|
wire [15:0] rsp_data16 = rsp_offset[i][1] ? rsp_data32[31:16] : rsp_data32[15:0];
|
|
wire [7:0] rsp_data8 = rsp_offset[i][0] ? rsp_data16[15:8] : rsp_data16[7:0];
|
|
|
|
always @(*) begin
|
|
case (`INST_LSU_FMT(rsp_type))
|
|
`INST_FMT_B: rsp_data[i] = 32'(signed'(rsp_data8));
|
|
`INST_FMT_H: rsp_data[i] = 32'(signed'(rsp_data16));
|
|
`INST_FMT_BU: rsp_data[i] = 32'(unsigned'(rsp_data8));
|
|
`INST_FMT_HU: rsp_data[i] = 32'(unsigned'(rsp_data16));
|
|
default: rsp_data[i] = rsp_data32;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
assign rsp_tmask_qual = rsp_is_dup ? rsp_tmask : dcache_rsp_if.tmask;
|
|
|
|
// send load commit
|
|
|
|
wire load_rsp_stall = ~ld_commit_if.ready && ld_commit_if.valid;
|
|
|
|
VX_pipe_register #(
|
|
.DATAW (1 + `UUID_BITS + `NW_BITS + `NUM_THREADS + 32 + `NR_BITS + 1 + (`NUM_THREADS * 32) + 1),
|
|
.RESETW (1)
|
|
) rsp_pipe_reg (
|
|
.clk (clk),
|
|
.reset (reset),
|
|
.enable (!load_rsp_stall),
|
|
.data_in ({dcache_rsp_if.valid, rsp_uuid, rsp_wid, rsp_tmask_qual, rsp_pc, rsp_rd, rsp_wb, rsp_data, mbuf_pop}),
|
|
.data_out ({ld_commit_if.valid, ld_commit_if.uuid, ld_commit_if.wid, ld_commit_if.tmask, ld_commit_if.PC, ld_commit_if.rd, ld_commit_if.wb, ld_commit_if.data, ld_commit_if.eop})
|
|
);
|
|
|
|
// Can accept new cache response?
|
|
assign dcache_rsp_if.ready = ~load_rsp_stall;
|
|
|
|
// scope registration
|
|
`SCOPE_ASSIGN (dcache_req_fire, dcache_req_fire);
|
|
`SCOPE_ASSIGN (dcache_req_uuid, req_uuid);
|
|
`SCOPE_ASSIGN (dcache_req_addr, req_addr);
|
|
`SCOPE_ASSIGN (dcache_req_rw, ~req_wb);
|
|
`SCOPE_ASSIGN (dcache_req_byteen,dcache_req_if.byteen);
|
|
`SCOPE_ASSIGN (dcache_req_data, dcache_req_if.data);
|
|
`SCOPE_ASSIGN (dcache_req_tag, req_tag);
|
|
`SCOPE_ASSIGN (dcache_rsp_fire, dcache_rsp_if.tmask & {`NUM_THREADS{dcache_rsp_fire}});
|
|
`SCOPE_ASSIGN (dcache_rsp_uuid, rsp_uuid);
|
|
`SCOPE_ASSIGN (dcache_rsp_data, dcache_rsp_if.data);
|
|
`SCOPE_ASSIGN (dcache_rsp_tag, mbuf_raddr);
|
|
|
|
`ifndef SYNTHESIS
|
|
reg [`LSUQ_SIZE-1:0][(`NW_BITS + 32 + `NR_BITS + `UUID_BITS + 64 + 1)-1:0] pending_reqs;
|
|
wire [63:0] delay_timeout = 10000 * (1 ** (`L2_ENABLE + `L3_ENABLE));
|
|
|
|
always @(posedge clk) begin
|
|
if (reset) begin
|
|
pending_reqs <= '0;
|
|
end begin
|
|
if (mbuf_push) begin
|
|
pending_reqs[mbuf_waddr] <= {req_wid, req_pc, req_rd, req_uuid, $time, 1'b1};
|
|
end
|
|
if (mbuf_pop) begin
|
|
pending_reqs[mbuf_raddr] <= '0;
|
|
end
|
|
end
|
|
|
|
for (integer i = 0; i < `LSUQ_SIZE; ++i) begin
|
|
if (pending_reqs[i][0]) begin
|
|
`ASSERT(($time - pending_reqs[i][1 +: 64]) < delay_timeout,
|
|
("%t: *** D$%0d response timeout: remaining=%b, wid=%0d, PC=%0h, rd=%0d (#%0d)",
|
|
$time, CORE_ID, rsp_rem_mask[i], pending_reqs[i][1+64+`UUID_BITS+`NR_BITS+32 +: `NW_BITS],
|
|
pending_reqs[i][1+64+`UUID_BITS+`NR_BITS +: 32],
|
|
pending_reqs[i][1+64+`UUID_BITS +: `NR_BITS],
|
|
pending_reqs[i][1+64 +: `UUID_BITS]));
|
|
end
|
|
end
|
|
end
|
|
`endif
|
|
|
|
`ifdef DBG_TRACE_CORE_DCACHE
|
|
wire dcache_req_fire_any = (| dcache_req_fire);
|
|
always @(posedge clk) begin
|
|
if (lsu_req_if.valid && fence_wait) begin
|
|
dpi_trace("%d: *** D$%0d fence wait\n", $time, CORE_ID);
|
|
end
|
|
if (dcache_req_fire_any) begin
|
|
if (dcache_req_if.rw[0]) begin
|
|
dpi_trace("%d: D$%0d Wr Req: wid=%0d, PC=%0h, tmask=%b, addr=", $time, CORE_ID, req_wid, req_pc, dcache_req_fire);
|
|
`TRACE_ARRAY1D(req_addr, `NUM_THREADS);
|
|
dpi_trace(", tag=%0h, byteen=%0h, type=", req_tag, dcache_req_if.byteen);
|
|
`TRACE_ARRAY1D(req_addr_type, `NUM_THREADS);
|
|
dpi_trace(", data=");
|
|
`TRACE_ARRAY1D(dcache_req_if.data, `NUM_THREADS);
|
|
dpi_trace(", (#%0d)\n", req_uuid);
|
|
end else begin
|
|
dpi_trace("%d: D$%0d Rd Req: prefetch=%b, wid=%0d, PC=%0h, tmask=%b, addr=", $time, CORE_ID, req_is_prefetch, req_wid, req_pc, dcache_req_fire);
|
|
`TRACE_ARRAY1D(req_addr, `NUM_THREADS);
|
|
dpi_trace(", tag=%0h, byteen=%0h, type=", req_tag, dcache_req_if.byteen);
|
|
`TRACE_ARRAY1D(req_addr_type, `NUM_THREADS);
|
|
dpi_trace(", rd=%0d, is_dup=%b (#%0d)\n", req_rd, req_is_dup, req_uuid);
|
|
end
|
|
end
|
|
if (dcache_rsp_fire) begin
|
|
dpi_trace("%d: D$%0d Rsp: prefetch=%b, wid=%0d, PC=%0h, tmask=%b, tag=%0h, rd=%0d, data=",
|
|
$time, CORE_ID, rsp_is_prefetch, rsp_wid, rsp_pc, dcache_rsp_if.tmask, mbuf_raddr, rsp_rd);
|
|
`TRACE_ARRAY1D(dcache_rsp_if.data, `NUM_THREADS);
|
|
dpi_trace(", is_dup=%b (#%0d)\n", rsp_is_dup, rsp_uuid);
|
|
end
|
|
end
|
|
`endif
|
|
|
|
endmodule |