mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-20 12:17:19 -04:00
Integrate serpent cache into Ariane
This commit is contained in:
parent
fa3192ac78
commit
ffec3c85ee
21 changed files with 663 additions and 394 deletions
10
Makefile
10
Makefile
|
@ -32,7 +32,7 @@ torture-logs := -log
|
|||
ariane_pkg := include/riscv_pkg.sv \
|
||||
src/debug/dm_pkg.sv \
|
||||
src/axi/src/axi_pkg.sv \
|
||||
include/ariane_pkg.sv \
|
||||
include/ariane_pkg.sv \
|
||||
include/std_cache_pkg.sv \
|
||||
include/serpent_cache_pkg.sv \
|
||||
include/axi_intf.sv
|
||||
|
@ -91,7 +91,7 @@ riscv-benchmarks-list := ci/riscv-benchmarks.list
|
|||
riscv-asm-tests := $(shell xargs printf '\n%s' < $(riscv-asm-tests-list) | cut -b 1-)
|
||||
riscv-benchmarks := $(shell xargs printf '\n%s' < $(riscv-benchmarks-list) | cut -b 1-)
|
||||
# preset which runs a single test
|
||||
riscv-test ?= rv64ui-p-add
|
||||
riscv-test ?= $(riscv-benchmarks-dir)/towers.riscv
|
||||
|
||||
# Search here for include files (e.g.: non-standalone components)
|
||||
incdir :=
|
||||
|
@ -143,15 +143,15 @@ sim: build
|
|||
vsim${questa_version} +permissive -64 -lib ${library} +max-cycles=$(max_cycles) +UVM_TESTNAME=${test_case} \
|
||||
+BASEDIR=$(riscv-test-dir) $(uvm-flags) "+UVM_VERBOSITY=LOW" -coverage -classdebug +jtag_rbb_enable=0 \
|
||||
$(QUESTASIM_FLAGS) \
|
||||
-gblso $(RISCV)/lib/libfesvr.so -sv_lib $(dpi-library)/ariane_dpi -do " log -r /*; run -all; exit" \
|
||||
${top_level}_optimized +permissive-off ++$(riscv-test-dir)/$(riscv-test) ++$(target-options)
|
||||
-gblso $(RISCV)/lib/libfesvr.so -sv_lib $(dpi-library)/ariane_dpi -do " log -r /*; run -all;" \
|
||||
${top_level}_optimized +permissive-off ++$(riscv-test) ++$(target-options)
|
||||
|
||||
simc: build
|
||||
vsim${questa_version} +permissive -64 -c -lib ${library} +max-cycles=$(max_cycles) +UVM_TESTNAME=${test_case} \
|
||||
+BASEDIR=$(riscv-test-dir) $(uvm-flags) "+UVM_VERBOSITY=LOW" -coverage -classdebug +jtag_rbb_enable=0 \
|
||||
$(QUESTASIM_FLAGS) \
|
||||
-gblso $(RISCV)/lib/libfesvr.so -sv_lib $(dpi-library)/ariane_dpi -do " run -all; exit" \
|
||||
${top_level}_optimized +permissive-off ++$(riscv-test-dir)/$(riscv-test) ++$(target-options)
|
||||
${top_level}_optimized +permissive-off ++$(riscv-test) ++$(target-options)
|
||||
|
||||
$(riscv-asm-tests): build
|
||||
vsim${questa_version} +permissive -64 -c -lib ${library} +max-cycles=$(max_cycles) +UVM_TESTNAME=${test_case} \
|
||||
|
|
|
@ -261,6 +261,76 @@ package serpent_cache_pkg;
|
|||
return cnt;
|
||||
endfunction : popcnt64
|
||||
|
||||
function automatic logic [7:0] toByteEnable8(
|
||||
input logic [2:0] offset,
|
||||
input logic [1:0] size
|
||||
);
|
||||
logic [7:0] be;
|
||||
be = '0;
|
||||
unique case(size)
|
||||
2'b00: be[offset] = '1; // byte
|
||||
2'b01: be[offset +:2 ] = '1; // hword
|
||||
2'b10: be[offset +:4 ] = '1; // word
|
||||
default: be = '1; // dword
|
||||
endcase // size
|
||||
return be;
|
||||
endfunction : toByteEnable8
|
||||
|
||||
// openpiton requires the data to be replicated in case of smaller sizes than dwords
|
||||
function automatic logic [63:0] repData64(
|
||||
input logic [63:0] data,
|
||||
input logic [2:0] offset,
|
||||
input logic [1:0] size
|
||||
);
|
||||
logic [63:0] out;
|
||||
unique case(size)
|
||||
2'b00: for(int k=0; k<8; k++) out[k*8 +: 8] = data[offset*8 +: 8]; // byte
|
||||
2'b01: for(int k=0; k<4; k++) out[k*16 +: 16] = data[offset*8 +: 16]; // hword
|
||||
2'b10: for(int k=0; k<2; k++) out[k*32 +: 32] = data[offset*8 +: 32]; // word
|
||||
default: out = data; // dword
|
||||
endcase // size
|
||||
return out;
|
||||
endfunction : repData64
|
||||
|
||||
// note: this is openpiton specific. cannot transmit unaligned words.
|
||||
// hence we default to individual bytes in that case, and they have to be transmitted
|
||||
// one after the other
|
||||
function automatic logic [1:0] toSize64(
|
||||
input logic [7:0] be
|
||||
);
|
||||
logic [1:0] size;
|
||||
unique case(be)
|
||||
8'b1111_1111: size = 2'b11; // dword
|
||||
8'b0000_1111, 8'b1111_0000: size = 2'b10; // word
|
||||
8'b1100_0000, 8'b0011_0000, 8'b0000_1100, 8'b0000_0011: size = 2'b01; // hword
|
||||
default: size = 2'b00; // individual bytes
|
||||
endcase // be
|
||||
return size;
|
||||
endfunction : toSize64
|
||||
|
||||
// align the physical address to the specified size:
|
||||
// 000: bytes
|
||||
// 001: hword
|
||||
// 010: word
|
||||
// 011: dword
|
||||
// 111: DCACHE line
|
||||
function automatic logic [63:0] paddrSizeAlign(
|
||||
input logic [63:0] paddr,
|
||||
input logic [2:0] size
|
||||
);
|
||||
logic [63:0] out;
|
||||
out = paddr;
|
||||
unique case (size)
|
||||
3'b001: out[0:0] = '0;
|
||||
3'b010: out[1:0] = '0;
|
||||
3'b011: out[2:0] = '0;
|
||||
3'b111: out[DCACHE_OFFSET_WIDTH-1:0] = '0;
|
||||
default: ;
|
||||
endcase
|
||||
return out;
|
||||
endfunction : paddrSizeAlign
|
||||
|
||||
|
||||
|
||||
|
||||
endpackage : serpent_cache_pkg
|
||||
|
|
|
@ -45,19 +45,19 @@ module ariane #(
|
|||
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
// memory side
|
||||
AXI_BUS.Master instr_if, // I$ refill port
|
||||
AXI_BUS.Master data_if, // D$ refill port
|
||||
AXI_BUS.Master bypass_if // bypass axi port (disabled D$ or uncacheable access)
|
||||
`else
|
||||
// L15 (memory side)
|
||||
output logic l15_val_o,
|
||||
input logic l15_ack_i,
|
||||
input logic l15_header_ack_i,
|
||||
output l15_req_t l15_data_o,
|
||||
|
||||
input logic l15_val_i,
|
||||
output logic l15_req_ack_o,
|
||||
input l15_rtrn_t l15_rtrn_i
|
||||
AXI_BUS.Master instr_if, // I$ refill port
|
||||
AXI_BUS.Master data_if, // D$ refill port
|
||||
AXI_BUS.Master bypass_if // bypass axi port (disabled D$ or uncacheable access)
|
||||
`else
|
||||
// L15 (memory side)
|
||||
output logic l15_val_o,
|
||||
input logic l15_ack_i,
|
||||
input logic l15_header_ack_i,
|
||||
output l15_req_t l15_data_o,
|
||||
|
||||
input logic l15_val_i,
|
||||
output logic l15_req_ack_o,
|
||||
input l15_rtrn_t l15_rtrn_i
|
||||
`endif
|
||||
|
||||
);
|
||||
|
@ -145,9 +145,10 @@ module ariane #(
|
|||
// CSR Commit
|
||||
logic csr_commit_commit_ex;
|
||||
// LSU Commit
|
||||
logic lsu_commit_commit_ex;
|
||||
logic lsu_commit_ready_ex_commit;
|
||||
logic no_st_pending_ex_commit;
|
||||
logic lsu_commit_req_commit_ex;
|
||||
logic lsu_commit_ack_ex_commit;
|
||||
logic no_st_pending_ex;
|
||||
logic no_st_pending_commit;
|
||||
logic amo_valid_commit;
|
||||
// --------------
|
||||
// ID <-> COMMIT
|
||||
|
@ -230,6 +231,7 @@ module ariane #(
|
|||
// ----------------
|
||||
dcache_req_i_t [2:0] dcache_req_ports_ex_cache;
|
||||
dcache_req_o_t [2:0] dcache_req_ports_cache_ex;
|
||||
logic dcache_commit_wbuffer_empty;
|
||||
|
||||
// --------------
|
||||
// Frontend
|
||||
|
@ -371,10 +373,10 @@ module ariane #(
|
|||
.lsu_result_o ( lsu_result_ex_id ),
|
||||
.lsu_trans_id_o ( lsu_trans_id_ex_id ),
|
||||
.lsu_valid_o ( lsu_valid_ex_id ),
|
||||
.lsu_commit_i ( lsu_commit_commit_ex ), // from commit
|
||||
.lsu_commit_ready_o ( lsu_commit_ready_ex_commit ), // to commit
|
||||
.lsu_commit_req_i ( lsu_commit_req_commit_ex ), // from commit
|
||||
.lsu_commit_ack_o ( lsu_commit_ack_ex_commit ), // to commit
|
||||
.lsu_exception_o ( lsu_exception_ex_id ),
|
||||
.no_st_pending_o ( no_st_pending_ex_commit ),
|
||||
.no_st_pending_o ( no_st_pending_ex ),
|
||||
.amo_valid_commit_i ( amo_valid_commit ),
|
||||
.amo_req_o ( amo_req ),
|
||||
.amo_resp_i ( amo_resp ),
|
||||
|
@ -416,6 +418,11 @@ module ariane #(
|
|||
// ---------
|
||||
// Commit
|
||||
// ---------
|
||||
|
||||
// we have to make sure that the whole write buffer path is empty before
|
||||
// used e.g. for fence instructions.
|
||||
assign no_st_pending_commit = no_st_pending_ex & dcache_commit_wbuffer_empty;
|
||||
|
||||
commit_stage commit_stage_i (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
|
@ -427,12 +434,12 @@ module ariane #(
|
|||
.single_step_i ( single_step_csr_commit ),
|
||||
.commit_instr_i ( commit_instr_id_commit ),
|
||||
.commit_ack_o ( commit_ack ),
|
||||
.no_st_pending_i ( no_st_pending_ex_commit ),
|
||||
.no_st_pending_i ( no_st_pending_commit ),
|
||||
.waddr_o ( waddr_commit_id ),
|
||||
.wdata_o ( wdata_commit_id ),
|
||||
.we_o ( we_commit_id ),
|
||||
.commit_lsu_o ( lsu_commit_commit_ex ),
|
||||
.commit_lsu_ready_i ( lsu_commit_ready_ex_commit ),
|
||||
.commit_lsu_req_o ( lsu_commit_req_commit_ex ),
|
||||
.commit_lsu_ack_i ( lsu_commit_ack_ex_commit ),
|
||||
.amo_valid_commit_o ( amo_valid_commit ),
|
||||
.amo_resp_i ( amo_resp ),
|
||||
.commit_csr_o ( csr_commit_commit_ex ),
|
||||
|
@ -551,8 +558,13 @@ module ariane #(
|
|||
// -------------------
|
||||
// Cache Subsystem
|
||||
// -------------------
|
||||
|
||||
`ifdef SERPENT_PULP
|
||||
// this is a cache subsystem that is compatible with OpenPiton
|
||||
serpent_cache_subsystem #(
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
.AXI_ID_WIDTH ( AXI_ID_WIDTH ),
|
||||
`endif
|
||||
.CACHE_START_ADDR ( CACHE_START_ADDR )
|
||||
) i_cache_subsystem (
|
||||
// to D$
|
||||
|
@ -577,7 +589,8 @@ module ariane #(
|
|||
.dcache_miss_o ( dcache_miss_cache_perf ),
|
||||
.dcache_req_ports_i ( dcache_req_ports_ex_cache ),
|
||||
.dcache_req_ports_o ( dcache_req_ports_cache_ex ),
|
||||
|
||||
// write buffer status
|
||||
.wbuffer_empty_o ( dcache_commit_wbuffer_empty ),
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
// memory side
|
||||
.icache_data_if ( instr_if ),
|
||||
|
@ -594,7 +607,9 @@ module ariane #(
|
|||
`endif
|
||||
);
|
||||
`else
|
||||
|
||||
std_cache_subsystem #(
|
||||
.AXI_ID_WIDTH ( AXI_ID_WIDTH ),
|
||||
.CACHE_START_ADDR ( CACHE_START_ADDR )
|
||||
) i_cache_subsystem (
|
||||
// to D$
|
||||
|
@ -616,6 +631,8 @@ module ariane #(
|
|||
.amo_req_i ( amo_req ),
|
||||
.amo_resp_o ( amo_resp ),
|
||||
.dcache_miss_o ( dcache_miss_cache_perf ),
|
||||
// this is statically set to 1 as the std_cache does not have a wbuffer
|
||||
.wbuffer_empty_o ( dcache_commit_wbuffer_empty ),
|
||||
// from PTW, Load Unit and Store Unit
|
||||
.dcache_req_ports_i ( dcache_req_ports_ex_cache ),
|
||||
.dcache_req_ports_o ( dcache_req_ports_cache_ex ),
|
||||
|
|
|
@ -27,6 +27,9 @@ import ariane_pkg::*;
|
|||
import serpent_cache_pkg::*;
|
||||
|
||||
module serpent_cache_subsystem #(
|
||||
// `ifdef AXI64_CACHE_PORTS
|
||||
parameter int unsigned AXI_ID_WIDTH = 10,
|
||||
// `endif
|
||||
parameter logic [63:0] CACHE_START_ADDR = 64'h4000_0000
|
||||
)(
|
||||
input logic clk_i,
|
||||
|
@ -50,6 +53,7 @@ module serpent_cache_subsystem #(
|
|||
input logic dcache_flush_i, // high until acknowledged
|
||||
output logic dcache_flush_ack_o, // send a single cycle acknowledge signal when the cache is flushed
|
||||
output logic dcache_miss_o, // we missed on a ld/st
|
||||
|
||||
// AMO interface
|
||||
input amo_req_t dcache_amo_req_i,
|
||||
output amo_resp_t dcache_amo_resp_o,
|
||||
|
@ -58,6 +62,9 @@ module serpent_cache_subsystem #(
|
|||
input dcache_req_i_t [2:0] dcache_req_ports_i, // to/from LSU
|
||||
output dcache_req_o_t [2:0] dcache_req_ports_o, // to/from LSU
|
||||
|
||||
// writebuffer status
|
||||
output logic wbuffer_empty_o,
|
||||
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
// memory side
|
||||
AXI_BUS.Master icache_data_if, // I$ refill port
|
||||
|
@ -81,12 +88,17 @@ module serpent_cache_subsystem #(
|
|||
serpent_cache_pkg::icache_req_t icache_adapter;
|
||||
serpent_cache_pkg::icache_rtrn_t adapter_icache;
|
||||
|
||||
|
||||
logic dcache_adapter_data_req, adapter_dcache_data_ack, adapter_dcache_rtrn_vld;
|
||||
serpent_cache_pkg::dcache_req_t dcache_adapter;
|
||||
serpent_cache_pkg::dcache_rtrn_t adapter_dcache;
|
||||
|
||||
serpent_icache #(
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
.AXI64BIT_COMPLIANT ( 1'b1 ),
|
||||
.NC_ADDR_GE_LT ( 0 ),
|
||||
`endif
|
||||
.NC_ADDR_BEGIN ( CACHE_START_ADDR ),
|
||||
.NC_ADDR_GE_LT ( 0 ) // todo: make compliant with openpiton
|
||||
.NC_ADDR_BEGIN ( CACHE_START_ADDR )
|
||||
) i_serpent_icache (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
|
@ -104,63 +116,64 @@ module serpent_cache_subsystem #(
|
|||
.mem_data_o ( icache_adapter )
|
||||
);
|
||||
|
||||
// decreasing priority
|
||||
// Port 0: PTW
|
||||
// Port 1: Load Unit
|
||||
// Port 2: Store Unit
|
||||
std_nbdcache #(
|
||||
.CACHE_START_ADDR ( CACHE_START_ADDR )
|
||||
) i_nbdcache (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.enable_i ( dcache_enable_i ),
|
||||
.flush_i ( dcache_flush_i ),
|
||||
.flush_ack_o ( dcache_flush_ack_o ),
|
||||
.miss_o ( dcache_miss_o ),
|
||||
.data_if ( dcache_data_if ),
|
||||
.bypass_if ( dcache_bypass_if ),
|
||||
.amo_req_i ( dcache_amo_req_i ),
|
||||
.amo_resp_o ( dcache_amo_resp_o ),
|
||||
.req_ports_i ( dcache_req_ports_i ),
|
||||
.req_ports_o ( dcache_req_ports_o )
|
||||
);
|
||||
|
||||
|
||||
// logic dcache_adapter_data_req, adapter_dcache_data_ack, adapter_dcache_rtrn_vld;
|
||||
// dcache_req_t dcache_adapter;
|
||||
// dcache_rtrn_t adapter_dcache;
|
||||
|
||||
// // decreasing priority
|
||||
// // Port 0: PTW
|
||||
// // Port 1: Load Unit
|
||||
// // Port 2: Store Unit
|
||||
// serpent_dcache #(
|
||||
// std_nbdcache #(
|
||||
// .AXI_ID_WIDTH ( AXI_ID_WIDTH ),
|
||||
// .CACHE_START_ADDR ( CACHE_START_ADDR )
|
||||
// ) i_serpent_dcache (
|
||||
// .clk_i ( clk_i ),
|
||||
// .rst_ni ( rst_ni ),
|
||||
// .enable_i ( dcache_enable_i ),
|
||||
// .flush_i ( dcache_flush_i ),
|
||||
// .flush_ack_o ( dcache_flush_ack_o ),
|
||||
// .miss_o ( dcache_miss_o ),
|
||||
// .amo_commit_i ( dcache_amo_commit_i ),
|
||||
// .amo_valid_o ( dcache_amo_valid_o ),
|
||||
// .amo_result_o ( dcache_amo_result_o ),
|
||||
// .amo_flush_i ( dcache_amo_flush_i ),
|
||||
// .req_ports_i ( dcache_req_ports_i ),
|
||||
// .req_ports_o ( dcache_req_ports_o ),
|
||||
// .mem_rtrn_vld_i ( adapter_dcache_rtrn_vld ),
|
||||
// .mem_rtrn_i ( adapter_dcache ),
|
||||
// .mem_data_req_o ( dcache_adapter_data_req ),
|
||||
// .mem_data_ack_i ( adapter_dcache_data_ack ),
|
||||
// .mem_data_o ( dcache_adapter )
|
||||
// ) i_nbdcache (
|
||||
// .clk_i ( clk_i ),
|
||||
// .rst_ni ( rst_ni ),
|
||||
// .enable_i ( dcache_enable_i ),
|
||||
// .flush_i ( dcache_flush_i ),
|
||||
// .flush_ack_o ( dcache_flush_ack_o ),
|
||||
// .miss_o ( dcache_miss_o ),
|
||||
// .data_if ( dcache_data_if ),
|
||||
// .bypass_if ( dcache_bypass_if ),
|
||||
// .amo_req_i ( dcache_amo_req_i ),
|
||||
// .amo_resp_o ( dcache_amo_resp_o ),
|
||||
// .req_ports_i ( dcache_req_ports_i ),
|
||||
// .req_ports_o ( dcache_req_ports_o )
|
||||
// );
|
||||
|
||||
// assign wbuffer_empty_o = 1'b1;
|
||||
|
||||
|
||||
// Note:
|
||||
// Ports 0/1 for PTW and LD unit are read only.
|
||||
// they have equal prio and are RR arbited
|
||||
// Port 2 is write only and goes into the merging write buffer
|
||||
serpent_dcache #(
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
.NC_ADDR_GE_LT ( 0 ), // std config is for openpiton, where the upper memory region is NC
|
||||
`endif
|
||||
.NC_ADDR_BEGIN ( CACHE_START_ADDR )
|
||||
) i_serpent_dcache (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.enable_i ( dcache_enable_i ),
|
||||
.flush_i ( dcache_flush_i ),
|
||||
.flush_ack_o ( dcache_flush_ack_o ),
|
||||
.miss_o ( dcache_miss_o ),
|
||||
.wbuffer_empty_o ( wbuffer_empty_o ),
|
||||
.amo_req_i ( dcache_amo_req_i ),
|
||||
.amo_resp_o ( dcache_amo_resp_o ),
|
||||
.req_ports_i ( dcache_req_ports_i ),
|
||||
.req_ports_o ( dcache_req_ports_o ),
|
||||
.mem_rtrn_vld_i ( adapter_dcache_rtrn_vld ),
|
||||
.mem_rtrn_i ( adapter_dcache ),
|
||||
.mem_data_req_o ( dcache_adapter_data_req ),
|
||||
.mem_data_ack_i ( adapter_dcache_data_ack ),
|
||||
.mem_data_o ( dcache_adapter )
|
||||
);
|
||||
|
||||
|
||||
// different memory plumbing
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
|
||||
// cannot handle invalidations atm
|
||||
// cannot handle invalidations ATM
|
||||
assign adapter_icache.rtype = ICACHE_IFILL_ACK;
|
||||
assign adapter_icache.inv = '0;
|
||||
assign adapter_icache.nc = icache_adapter.nc;
|
||||
|
@ -171,8 +184,9 @@ module serpent_cache_subsystem #(
|
|||
assign icache_axi_req_type = ( icache_adapter.nc ) ? std_cache_pkg::SINGLE_REQ : std_cache_pkg::CACHE_LINE_REQ;
|
||||
|
||||
axi_adapter #(
|
||||
.DATA_WIDTH ( ICACHE_LINE_WIDTH )
|
||||
) i_axi_adapter (
|
||||
.DATA_WIDTH ( ICACHE_LINE_WIDTH ),
|
||||
.AXI_ID_WIDTH ( AXI_ID_WIDTH )
|
||||
) i_icache_axi_adapter (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.req_i ( icache_adapter_data_req ),
|
||||
|
@ -193,6 +207,89 @@ module serpent_cache_subsystem #(
|
|||
.axi ( icache_data_if )
|
||||
);
|
||||
|
||||
std_cache_pkg::req_t dcache_axi_req_type;
|
||||
logic [DCACHE_LINE_WIDTH-1:0] dcache_axi_wdata;
|
||||
logic dcache_axi_we;
|
||||
logic [DCACHE_LINE_WIDTH/8-1:0] dcache_axi_be;
|
||||
logic [1:0] dcache_axi_size;
|
||||
logic [63:0] dcache_axi_paddr;
|
||||
// AXI IDs are 10 wide here
|
||||
logic [AXI_ID_WIDTH-1:0] dcache_axi_id, axi_dcache_id;
|
||||
|
||||
// encode NC, RD, and TX ID into AXI ID field
|
||||
// dcache is aware of the fact that transactions with different IDs can overtake each other in the
|
||||
// interconnect, and issues the transactions accordingly. so this is safe.
|
||||
assign dcache_axi_req_type = ( dcache_adapter.size[2] ) ? std_cache_pkg::CACHE_LINE_REQ : std_cache_pkg::SINGLE_REQ;
|
||||
assign dcache_axi_size = ( dcache_adapter.size[2] ) ? 2'b11 : dcache_adapter.size;
|
||||
assign dcache_axi_we = ( dcache_adapter.rtype == serpent_cache_pkg::DCACHE_STORE_REQ );
|
||||
assign dcache_axi_id = {dcache_adapter.tid, dcache_adapter.nc, dcache_axi_we};
|
||||
assign dcache_axi_wdata = dcache_adapter.data;
|
||||
assign dcache_axi_be = ( dcache_axi_we ) ? serpent_cache_pkg::toByteEnable8(dcache_adapter.paddr[2:0], dcache_adapter.size) : '0;
|
||||
assign dcache_axi_paddr = dcache_adapter.paddr;
|
||||
|
||||
// cannot handle invalidations and atomics ATM
|
||||
assign adapter_dcache.inv = '0;
|
||||
assign adapter_dcache.rtype = (axi_dcache_id[0]) ? serpent_cache_pkg::DCACHE_STORE_ACK : serpent_cache_pkg::DCACHE_LOAD_ACK;
|
||||
assign adapter_dcache.nc = axi_dcache_id[1];
|
||||
assign adapter_dcache.tid = axi_dcache_id>>2;
|
||||
|
||||
axi_adapter #(
|
||||
.DATA_WIDTH ( DCACHE_LINE_WIDTH ),
|
||||
.AXI_ID_WIDTH ( AXI_ID_WIDTH )
|
||||
) i_dcache_axi_adapter (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.req_i ( dcache_adapter_data_req ),
|
||||
.type_i ( dcache_axi_req_type ),
|
||||
.gnt_o ( adapter_dcache_data_ack ),
|
||||
.gnt_id_o ( ),
|
||||
.addr_i ( dcache_axi_paddr ),
|
||||
.we_i ( dcache_axi_we ),
|
||||
.wdata_i ( dcache_axi_wdata ),
|
||||
.be_i ( dcache_axi_be ),
|
||||
.size_i ( dcache_axi_size ),// always request in multiples of 64bit
|
||||
.id_i ( dcache_axi_id ),
|
||||
.valid_o ( adapter_dcache_rtrn_vld ),
|
||||
.rdata_o ( adapter_dcache.data ),
|
||||
.id_o ( axi_dcache_id ),
|
||||
.critical_word_o ( ),
|
||||
.critical_word_valid_o( ),
|
||||
.axi ( dcache_data_if )
|
||||
);
|
||||
|
||||
// tie to zero here...
|
||||
assign dcache_bypass_if.aw_valid = '0;
|
||||
assign dcache_bypass_if.aw_addr = '0;
|
||||
assign dcache_bypass_if.aw_prot = '0;
|
||||
assign dcache_bypass_if.aw_region = '0;
|
||||
assign dcache_bypass_if.aw_len = '0;
|
||||
assign dcache_bypass_if.aw_size = '0;
|
||||
assign dcache_bypass_if.aw_burst = '0;
|
||||
assign dcache_bypass_if.aw_lock = '0;
|
||||
assign dcache_bypass_if.aw_cache = '0;
|
||||
assign dcache_bypass_if.aw_qos = '0;
|
||||
assign dcache_bypass_if.aw_id = '0;
|
||||
assign dcache_bypass_if.aw_user = '0;
|
||||
assign dcache_bypass_if.ar_valid = '0;
|
||||
assign dcache_bypass_if.ar_addr = '0;
|
||||
assign dcache_bypass_if.ar_prot = '0;
|
||||
assign dcache_bypass_if.ar_region = '0;
|
||||
assign dcache_bypass_if.ar_len = '0;
|
||||
assign dcache_bypass_if.ar_size = '0;
|
||||
assign dcache_bypass_if.ar_burst = '0;
|
||||
assign dcache_bypass_if.ar_lock = '0;
|
||||
assign dcache_bypass_if.ar_cache = '0;
|
||||
assign dcache_bypass_if.ar_qos = '0;
|
||||
assign dcache_bypass_if.ar_id = '0;
|
||||
assign dcache_bypass_if.ar_user = '0;
|
||||
assign dcache_bypass_if.w_valid = '0;
|
||||
assign dcache_bypass_if.w_data = '0;
|
||||
assign dcache_bypass_if.w_strb = '0;
|
||||
assign dcache_bypass_if.w_user = '0;
|
||||
assign dcache_bypass_if.w_last = '0;
|
||||
assign dcache_bypass_if.b_ready = '0;
|
||||
assign dcache_bypass_if.r_ready = '0;
|
||||
|
||||
`else
|
||||
|
||||
serpent_l15_adapter #(
|
||||
|
@ -220,4 +317,49 @@ module serpent_cache_subsystem #(
|
|||
|
||||
`endif
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// assertions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//pragma translate_off
|
||||
`ifndef VERILATOR
|
||||
|
||||
`ifdef AXI64_CACHE_PORTS
|
||||
a_write_size: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) dcache_adapter_data_req |-> adapter_dcache_data_ack |-> dcache_axi_we |-> dcache_axi_req_type==std_cache_pkg::SINGLE_REQ)
|
||||
else $fatal(1,"[l1 cache] full cacheline stores not supported at the moment");
|
||||
|
||||
a_paddr_align: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) dcache_adapter_data_req |-> adapter_dcache_data_ack |-> dcache_axi_req_type==std_cache_pkg::CACHE_LINE_REQ |-> dcache_axi_paddr[2:0] == 3'b000)
|
||||
else $fatal(1,"[l1 cache] CL address must be aligned");
|
||||
`endif
|
||||
|
||||
a_invalid_instruction_fetch: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) icache_dreq_o.valid |-> (|icache_dreq_o.data) !== 1'hX)
|
||||
else $warning(1,"[l1 dcache] reading invalid instructions: vaddr=%08X, data=%08X",
|
||||
icache_dreq_o.vaddr, icache_dreq_o.data);
|
||||
|
||||
a_invalid_write_data: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_i[2].data_req |-> |dcache_req_ports_i[2].data_be |-> (|dcache_req_ports_i[2].data_wdata) !== 1'hX)
|
||||
else $warning(1,"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X",
|
||||
{dcache_req_ports_i[2].address_tag, dcache_req_ports_i[2].address_index}, dcache_req_ports_i[2].data_be, dcache_req_ports_i[2].data_wdata);
|
||||
|
||||
generate
|
||||
for(genvar j=0; j<2; j++) begin
|
||||
a_invalid_read_data: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_o[j].data_rvalid |-> (|dcache_req_ports_o[j].data_rdata) !== 1'hX)
|
||||
else $warning(1,"[l1 dcache] reading invalid data on port %01d: data=%016X",
|
||||
j, dcache_req_ports_o[j].data_rdata);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
initial begin
|
||||
assert (AXI_ID_WIDTH >= $clog2(serpent_cache_pkg::DCACHE_MAX_TX)+2) else
|
||||
$fatal(1,$psprintf("[l1 cache] AXI ID must be at least %01d bit wide", $clog2(serpent_cache_pkg::DCACHE_MAX_TX)+2));
|
||||
end
|
||||
`endif
|
||||
//pragma translate_on
|
||||
|
||||
|
||||
endmodule // serpent_cache_subsystem
|
||||
|
|
|
@ -31,7 +31,7 @@ module serpent_dcache #(
|
|||
|
||||
// AMO interface
|
||||
input amo_req_t amo_req_i,
|
||||
output amo_resp_t amo_ack_o,
|
||||
output amo_resp_t amo_resp_o,
|
||||
|
||||
// Request ports
|
||||
input dcache_req_i_t [2:0] req_ports_i,
|
||||
|
@ -48,7 +48,7 @@ module serpent_dcache #(
|
|||
localparam NUM_PORTS = 3;
|
||||
|
||||
// miss unit <-> read controllers
|
||||
logic cache_en, flush_en;
|
||||
logic cache_en;
|
||||
|
||||
// miss unit <-> memory
|
||||
logic wr_cl_vld;
|
||||
|
@ -116,10 +116,9 @@ module serpent_dcache #(
|
|||
.miss_o ( miss_o ),
|
||||
.wbuffer_empty_i ( wbuffer_empty_o ),
|
||||
.cache_en_o ( cache_en ),
|
||||
.flush_en_o ( flush_en ),
|
||||
// amo interface
|
||||
.amo_req_i ( amo_req_i ),
|
||||
.amo_ack_o ( amo_ack_o ),
|
||||
.amo_resp_o ( amo_resp_o ),
|
||||
// miss handling interface
|
||||
.miss_req_i ( miss_req ),
|
||||
.miss_ack_o ( miss_ack ),
|
||||
|
@ -170,7 +169,6 @@ module serpent_dcache #(
|
|||
i_serpent_dcache_ctrl (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.flush_i ( flush_en ),
|
||||
.cache_en_i ( cache_en ),
|
||||
// reqs from core
|
||||
.req_port_i ( req_ports_i [k] ),
|
||||
|
@ -217,7 +215,9 @@ module serpent_dcache #(
|
|||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.empty_o ( wbuffer_empty_o ),
|
||||
// TODO: fix this
|
||||
.cache_en_i ( cache_en ),
|
||||
// .cache_en_i ( '0 ),
|
||||
// request ports from core (store unit)
|
||||
.req_port_i ( req_ports_i [2] ),
|
||||
.req_port_o ( req_ports_o [2] ),
|
||||
|
|
|
@ -22,7 +22,6 @@ module serpent_dcache_ctrl #(
|
|||
)(
|
||||
input logic clk_i, // Clock
|
||||
input logic rst_ni, // Asynchronous reset active low
|
||||
input logic flush_i,
|
||||
input logic cache_en_i,
|
||||
// core request ports
|
||||
input dcache_req_i_t req_port_i,
|
||||
|
@ -120,7 +119,7 @@ module serpent_dcache_ctrl #(
|
|||
// wait for an incoming request
|
||||
IDLE: begin
|
||||
|
||||
if ((~flush_i) && req_port_i.data_req) begin
|
||||
if (req_port_i.data_req) begin
|
||||
rd_req_o = 1'b1;
|
||||
|
||||
if (rd_ack_i) begin
|
||||
|
@ -139,8 +138,8 @@ module serpent_dcache_ctrl #(
|
|||
// speculatively request cache line
|
||||
rd_req_o = 1'b1;
|
||||
|
||||
// flush or kill -> go back to IDLE
|
||||
if(flush_i || req_port_i.kill_req) begin
|
||||
// kill -> go back to IDLE
|
||||
if(req_port_i.kill_req) begin
|
||||
state_d = IDLE;
|
||||
end else if(req_port_i.tag_valid | state_q==REPLAY_READ) begin
|
||||
save_tag = (state_q!=REPLAY_READ);
|
||||
|
@ -166,7 +165,7 @@ module serpent_dcache_ctrl #(
|
|||
MISS_REQ: begin
|
||||
miss_req_o = 1'b1;
|
||||
|
||||
if (flush_i || req_port_i.kill_req) begin
|
||||
if(req_port_i.kill_req) begin
|
||||
if(miss_ack_i) begin
|
||||
state_d = KILL_MISS;
|
||||
end else begin
|
||||
|
@ -182,7 +181,7 @@ module serpent_dcache_ctrl #(
|
|||
// wait until the memory transaction
|
||||
// returns.
|
||||
MISS_WAIT: begin
|
||||
if(flush_i || req_port_i.kill_req) begin
|
||||
if(req_port_i.kill_req) begin
|
||||
if(miss_rtrn_vld_i) begin
|
||||
state_d = IDLE;
|
||||
end else begin
|
||||
|
@ -197,7 +196,7 @@ module serpent_dcache_ctrl #(
|
|||
// replay read request
|
||||
REPLAY_REQ: begin
|
||||
rd_req_o = 1'b1;
|
||||
if (flush_i || req_port_i.kill_req) begin
|
||||
if (req_port_i.kill_req) begin
|
||||
state_d = IDLE;
|
||||
end else if(rd_ack_i) begin
|
||||
state_d = REPLAY_READ;
|
||||
|
|
|
@ -163,7 +163,7 @@ module serpent_dcache_mem #(
|
|||
bank_collision[k] = rd_off_i[k][DCACHE_OFFSET_WIDTH-1:3] == wr_off_i[DCACHE_OFFSET_WIDTH-1:3];
|
||||
end
|
||||
|
||||
if(wr_cl_vld_i) begin
|
||||
if(wr_cl_vld_i & |wr_cl_we_i) begin
|
||||
bank_req = '1;
|
||||
bank_we = '1;
|
||||
bank_idx = '{default:wr_cl_idx_i};
|
||||
|
@ -189,8 +189,6 @@ module serpent_dcache_mem #(
|
|||
// tag comparison, hit generatio, readoud muxes
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
logic [DCACHE_WBUF_DEPTH-1:0][7:0] wbuffer_bvalid;
|
||||
logic [DCACHE_WBUF_DEPTH-1:0][63:0] wbuffer_data;
|
||||
logic [DCACHE_OFFSET_WIDTH-1:0] wr_cl_off;
|
||||
logic [$clog2(DCACHE_WBUF_DEPTH)-1:0] wbuffer_hit_idx;
|
||||
logic [$clog2(DCACHE_SET_ASSOC)-1:0] rd_hit_idx;
|
||||
|
|
|
@ -24,16 +24,15 @@ module serpent_dcache_missunit #(
|
|||
input logic rst_ni, // Asynchronous reset active low
|
||||
// cache management, signals from/to core
|
||||
input logic enable_i, // from CSR
|
||||
input logic flush_i, // high until acknowledged
|
||||
input logic flush_i, // flush request, this waits for pending tx (write, read) to finish and will clear the cache
|
||||
output logic flush_ack_o, // send a single cycle acknowledge signal when the cache is flushed
|
||||
output logic miss_o, // we missed on a ld/st
|
||||
// local cache management signals
|
||||
input logic wbuffer_empty_i,
|
||||
output logic cache_en_o, // local cache enable signal
|
||||
output logic flush_en_o, // local flush enable signal
|
||||
// AMO interface
|
||||
input amo_req_t amo_req_i,
|
||||
output amo_resp_t amo_ack_o,
|
||||
output amo_resp_t amo_resp_o,
|
||||
// miss handling interface (ld, ptw, wbuffer)
|
||||
input logic [NUM_PORTS-1:0] miss_req_i,
|
||||
output logic [NUM_PORTS-1:0] miss_ack_o,
|
||||
|
@ -71,7 +70,7 @@ module serpent_dcache_missunit #(
|
|||
);
|
||||
|
||||
// controller FSM
|
||||
typedef enum logic[2:0] {IDLE, DRAIN, AMO, AMO_WAIT, FLUSH} state_t;
|
||||
typedef enum logic[2:0] {IDLE, DRAIN, AMO, FLUSH, STORE_WAIT, LOAD_WAIT, AMO_WAIT} state_t;
|
||||
state_t state_d, state_q;
|
||||
|
||||
// MSHR for reads
|
||||
|
@ -90,15 +89,17 @@ module serpent_dcache_missunit #(
|
|||
logic mshr_vld_d, mshr_vld_q, mshr_vld_q1;
|
||||
logic mshr_allocate;
|
||||
logic update_lfsr, all_ways_valid;
|
||||
|
||||
|
||||
logic enable_d, enable_q;
|
||||
logic flush_ack_d, flush_ack_q;
|
||||
logic flush_en, flush_done;
|
||||
logic amo_sel, mask_reads, miss_is_write;
|
||||
logic [63:0] amo_data;
|
||||
logic mask_reads, lock_reqs;
|
||||
logic amo_sel, miss_is_write;
|
||||
logic [63:0] amo_data, tmp_paddr;
|
||||
|
||||
logic [$clog2(NUM_PORTS)-1:0] miss_port_idx;
|
||||
logic [DCACHE_CL_IDX_WIDTH-1:0] cnt_d, cnt_q;
|
||||
logic [NUM_PORTS-1:0] miss_req;
|
||||
logic [NUM_PORTS-1:0] miss_req_masked_d, miss_req_masked_q;
|
||||
|
||||
logic inv_vld, inv_vld_all, cl_write_en;
|
||||
logic load_ack, store_ack, amo_ack;
|
||||
|
@ -115,18 +116,24 @@ module serpent_dcache_missunit #(
|
|||
assign cnt_d = (flush_en) ? cnt_q + 1 : '0;
|
||||
assign flush_done = (cnt_q == DCACHE_NUM_WORDS-1);
|
||||
|
||||
assign miss_req = (mask_reads) ? miss_we_i & miss_req_i : miss_req_i;
|
||||
assign miss_is_write = miss_we_i[miss_port_idx];
|
||||
assign miss_req_masked_d = ( lock_reqs ) ? miss_req_masked_q :
|
||||
( mask_reads ) ? miss_we_i & miss_req_i : miss_req_i;
|
||||
assign miss_is_write = miss_we_i[miss_port_idx];
|
||||
|
||||
// determine which port to serve (lower indices have higher prio)
|
||||
// read port arbiter
|
||||
lzc #(
|
||||
.WIDTH ( NUM_PORTS )
|
||||
) i_port (
|
||||
.in_i ( miss_req ),
|
||||
.cnt_o ( miss_port_idx ),
|
||||
.empty_o ( )
|
||||
) i_lzc_reqs (
|
||||
.in_i ( miss_req_masked_d ),
|
||||
.cnt_o ( miss_port_idx ),
|
||||
.empty_o ( )
|
||||
);
|
||||
|
||||
always_comb begin : p_ack
|
||||
miss_ack_o = '0;
|
||||
miss_ack_o[miss_port_idx] = mem_data_ack_i & mem_data_req_o;
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// MSHR and way replacement logic (only for read ops)
|
||||
///////////////////////////////////////////////////////
|
||||
|
@ -134,16 +141,16 @@ module serpent_dcache_missunit #(
|
|||
// find invalid cache line
|
||||
lzc #(
|
||||
.WIDTH ( DCACHE_SET_ASSOC )
|
||||
) i_lzc (
|
||||
) i_lzc_inv (
|
||||
.in_i ( ~miss_vld_bits_i[miss_port_idx] ),
|
||||
.cnt_o ( inv_way ),
|
||||
.empty_o ( all_ways_valid )
|
||||
.cnt_o ( inv_way ),
|
||||
.empty_o ( all_ways_valid )
|
||||
);
|
||||
|
||||
// generate random cacheline index
|
||||
lfsr_8bit #(
|
||||
.WIDTH ( DCACHE_SET_ASSOC )
|
||||
) i_lfsr (
|
||||
) i_lfsr_inv (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.en_i ( update_lfsr ),
|
||||
|
@ -179,6 +186,7 @@ module serpent_dcache_missunit #(
|
|||
// read/write collision, stalls the corresponding request
|
||||
// write collides with MSHR
|
||||
assign mshr_rdwr_collision = (mshr_q.paddr[63:DCACHE_OFFSET_WIDTH] == miss_paddr_i[NUM_PORTS-1][63:DCACHE_OFFSET_WIDTH]) && mshr_vld_q;
|
||||
|
||||
// read collides with inflight TX
|
||||
always_comb begin : p_tx_coll
|
||||
tx_rdwr_collision = 1'b0;
|
||||
|
@ -197,8 +205,8 @@ module serpent_dcache_missunit #(
|
|||
amo_req_i.operand_b;
|
||||
|
||||
// always sign extend 32bit values
|
||||
assign amo_ack_o.result = (amo_req_i.size==2'b10) ? $signed(mem_rtrn_i.data[amo_req_i.operand_a[2]*32 +: 32]) :
|
||||
mem_rtrn_i.data[63:0];
|
||||
assign amo_resp_o.result = (amo_req_i.size==2'b10) ? $signed(mem_rtrn_i.data[amo_req_i.operand_a[2]*32 +: 32]) :
|
||||
mem_rtrn_i.data[63:0];
|
||||
|
||||
// outgoing memory requests
|
||||
assign mem_data_o.tid = (amo_sel) ? '0 : miss_wr_id_i[miss_port_idx];
|
||||
|
@ -208,17 +216,8 @@ module serpent_dcache_missunit #(
|
|||
assign mem_data_o.size = (amo_sel) ? amo_req_i.size : miss_size_i [miss_port_idx];
|
||||
assign mem_data_o.amo_op = (amo_sel) ? amo_req_i.amo_op : AMO_NONE;
|
||||
|
||||
// align address depending on transfer size
|
||||
always_comb begin : p_align
|
||||
mem_data_o.paddr = (amo_sel) ? amo_req_i.operand_a : miss_paddr_i[miss_port_idx];
|
||||
unique case (mem_data_o.size)
|
||||
3'b001: mem_data_o.paddr[0:0] = '0;
|
||||
3'b010: mem_data_o.paddr[1:0] = '0;
|
||||
3'b011: mem_data_o.paddr[2:0] = '0;
|
||||
3'b111: mem_data_o.paddr[DCACHE_OFFSET_WIDTH-1:0] = '0;
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
assign tmp_paddr = (amo_sel) ? amo_req_i.operand_a : miss_paddr_i[miss_port_idx];
|
||||
assign mem_data_o.paddr = serpent_cache_pkg::paddrSizeAlign(tmp_paddr, mem_data_o.size);
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// responses from memory
|
||||
|
@ -299,21 +298,21 @@ module serpent_dcache_missunit #(
|
|||
always_comb begin : p_fsm
|
||||
// default assignment
|
||||
state_d = state_q;
|
||||
|
||||
flush_ack_o = 1'b0;
|
||||
mem_data_o.rtype = DCACHE_LOAD_REQ;
|
||||
mem_data_req_o = 1'b0;
|
||||
amo_resp_o.ack = 1'b0;
|
||||
miss_replay_o = '0;
|
||||
|
||||
// disabling cache is possible anytime, enabling goes via flush
|
||||
enable_d = enable_q & enable_i;
|
||||
flush_ack_d = flush_ack_q;
|
||||
flush_ack_o = 1'b0;
|
||||
flush_en_o = 1'b0;
|
||||
flush_en = 1'b0;
|
||||
|
||||
mem_data_o.rtype = DCACHE_LOAD_REQ;
|
||||
mem_data_req_o = 1'b0;
|
||||
amo_sel = 1'b0;
|
||||
amo_ack_o.ack = 1'b0;
|
||||
update_lfsr = 1'b0;
|
||||
mshr_allocate = 1'b0;
|
||||
miss_ack_o = '0;
|
||||
miss_replay_o = '0;
|
||||
lock_reqs = 1'b0;
|
||||
mask_reads = mshr_vld_q;
|
||||
|
||||
// interfaces
|
||||
|
@ -322,27 +321,29 @@ module serpent_dcache_missunit #(
|
|||
// wait for misses / amo ops
|
||||
IDLE: begin
|
||||
if(flush_i | (enable_i & ~enable_q)) begin
|
||||
if(wbuffer_empty_i) begin
|
||||
if(wbuffer_empty_i && ~mshr_vld_q) begin
|
||||
flush_ack_d = flush_i;
|
||||
state_d = FLUSH;
|
||||
end else begin
|
||||
state_d = DRAIN;
|
||||
end
|
||||
end else if(amo_req_i.req) begin
|
||||
if(wbuffer_empty_i) begin
|
||||
if(wbuffer_empty_i && ~mshr_vld_q) begin
|
||||
state_d = AMO;
|
||||
end else begin
|
||||
state_d = DRAIN;
|
||||
end
|
||||
// we've got a miss
|
||||
end else if(|miss_req) begin
|
||||
// we've got a miss to handle
|
||||
end else if(|miss_req_masked_d) begin
|
||||
// this is a write miss, just pass through (but check whether write collides with MSHR)
|
||||
if(miss_is_write) begin
|
||||
// stall in case this write collides with the MSHR address
|
||||
if(~mshr_rdwr_collision) begin
|
||||
mem_data_req_o = 1'b1;
|
||||
mem_data_o.rtype = DCACHE_STORE_REQ;
|
||||
miss_ack_o[miss_port_idx] = mem_data_ack_i;
|
||||
if(~mem_data_ack_i) begin
|
||||
state_d = STORE_WAIT;
|
||||
end
|
||||
end
|
||||
// this is a read miss, can only allocate 1 MSHR
|
||||
// in case of a load_ack we can accept a new miss, since the MSHR is being cleared
|
||||
|
@ -355,23 +356,46 @@ module serpent_dcache_missunit #(
|
|||
end else if(~tx_rdwr_collision) begin
|
||||
mem_data_req_o = 1'b1;
|
||||
mem_data_o.rtype = DCACHE_LOAD_REQ;
|
||||
miss_ack_o[miss_port_idx] = mem_data_ack_i;
|
||||
mshr_allocate = mem_data_ack_i;
|
||||
update_lfsr = all_ways_valid & mem_data_ack_i;// need to evict a random way
|
||||
mshr_allocate = mem_data_ack_i;
|
||||
if(~mem_data_ack_i) begin
|
||||
state_d = LOAD_WAIT;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
//////////////////////////////////
|
||||
// wait until this request is acked
|
||||
STORE_WAIT: begin
|
||||
lock_reqs = 1'b1;
|
||||
mem_data_req_o = 1'b1;
|
||||
mem_data_o.rtype = DCACHE_STORE_REQ;
|
||||
if(mem_data_ack_i) begin
|
||||
state_d = IDLE;
|
||||
end
|
||||
end
|
||||
//////////////////////////////////
|
||||
// wait until this request is acked
|
||||
LOAD_WAIT: begin
|
||||
lock_reqs = 1'b1;
|
||||
mem_data_req_o = 1'b1;
|
||||
mem_data_o.rtype = DCACHE_LOAD_REQ;
|
||||
if(mem_data_ack_i) begin
|
||||
update_lfsr = all_ways_valid;// need to evict a random way
|
||||
mshr_allocate = 1'b1;;
|
||||
state_d = IDLE;
|
||||
end
|
||||
end
|
||||
//////////////////////////////////
|
||||
// only handle stores, do not accept new read requests
|
||||
// wait until MSHR is cleared and wbuffer is empty
|
||||
DRAIN: begin
|
||||
mask_reads = 1'b1;
|
||||
// these are writes, check whether they collide with MSHR
|
||||
if(|miss_req && ~mshr_rdwr_collision) begin
|
||||
if(|miss_req_masked_d && ~mshr_rdwr_collision) begin
|
||||
mem_data_req_o = 1'b1;
|
||||
mem_data_o.rtype = DCACHE_STORE_REQ;
|
||||
miss_ack_o[miss_port_idx] = mem_data_ack_i;
|
||||
end
|
||||
|
||||
if(wbuffer_empty_i && ~mshr_vld_q) begin
|
||||
|
@ -383,9 +407,6 @@ module serpent_dcache_missunit #(
|
|||
FLUSH: begin
|
||||
// internal flush signal
|
||||
flush_en = 1'b1;
|
||||
// only flush port controllers if this is a real flush
|
||||
// (i.e., not only a "clear" upon enabling the cache)
|
||||
flush_en_o = flush_ack_q;
|
||||
if(flush_done) begin
|
||||
state_d = IDLE;
|
||||
flush_ack_o = flush_ack_q;
|
||||
|
@ -408,8 +429,8 @@ module serpent_dcache_missunit #(
|
|||
AMO_WAIT: begin
|
||||
amo_sel = 1'b1;
|
||||
if(amo_ack) begin
|
||||
amo_ack_o.ack = 1'b1;
|
||||
state_d = IDLE;
|
||||
amo_resp_o.ack = 1'b1;
|
||||
state_d = IDLE;
|
||||
end
|
||||
end
|
||||
//////////////////////////////////
|
||||
|
@ -434,6 +455,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
|
|||
mshr_vld_q1 <= '0;
|
||||
mshr_q <= '0;
|
||||
mshr_rdrd_collision_q <= '0;
|
||||
miss_req_masked_q <= '0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
cnt_q <= cnt_d;
|
||||
|
@ -443,6 +465,7 @@ always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
|
|||
mshr_vld_q1 <= mshr_vld_q;
|
||||
mshr_q <= mshr_d;
|
||||
mshr_rdrd_collision_q <= mshr_rdrd_collision_d;
|
||||
miss_req_masked_q <= miss_req_masked_d;
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -44,11 +44,9 @@
|
|||
// word from the write buffer. if the word is not allocated to the cache, it is just evicted from the write buffer.
|
||||
// if the word cache state is VOID, the pipeline is stalled until it is clear whether that word is in the cache or not.
|
||||
//
|
||||
// 4) we handle NC writes using the writebuffer circuitry. the only difference is that the NC write
|
||||
// is not acked and popped from the core fifo until the write response returns from memory. this
|
||||
// ensures that the data cannot be overwritten by subsequent writes. note that, although the NC word is written
|
||||
// into the write buffer and hence visible to the forwarding logic, this particular word will
|
||||
// never be forwarded. the reason is that only addresses allocated in the data cache can be forwarded.
|
||||
// 4) we handle NC writes using the writebuffer circuitry. upon an NC request, the writebuffer will first be drained.
|
||||
// then, only the NC word is written into the write buffer and no further write requests are acknowledged until that
|
||||
// word has been evicted from the write buffer.
|
||||
|
||||
import ariane_pkg::*;
|
||||
import serpent_cache_pkg::*;
|
||||
|
@ -167,11 +165,12 @@ endgenerate
|
|||
// 10: word
|
||||
// 11: dword
|
||||
// non-contiguous writes need to be serialized!
|
||||
// e.g. merged dwords with BE like this: 8'b01001100
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// get byte offset
|
||||
lzc #(
|
||||
.WIDTH ( DCACHE_WBUF_DEPTH )
|
||||
.WIDTH ( 8 )
|
||||
) i_vld_bdirty (
|
||||
.in_i ( bdirty[dirty_ptr] ),
|
||||
.cnt_o ( bdirty_off ),
|
||||
|
@ -183,58 +182,24 @@ assign miss_paddr_o = {wbuffer_q[dirty_ptr].wtag, bdirty_off};
|
|||
assign miss_wr_id_o = tx_id_q;
|
||||
assign miss_req_o = (|dirty) && (tx_cnt_q < DCACHE_MAX_TX);
|
||||
|
||||
always_comb begin : p_be_to_size
|
||||
unique case(bdirty[dirty_ptr])
|
||||
8'b1111_1111: begin // dword
|
||||
miss_size_o = 3'b011;
|
||||
end
|
||||
8'b0000_1111, 8'b1111_0000: begin // word
|
||||
miss_size_o = 3'b010;
|
||||
end
|
||||
8'b1100_0000, 8'b0011_0000, 8'b0000_1100, 8'b0000_0011: begin // hword
|
||||
miss_size_o = 3'b001;
|
||||
end
|
||||
default: begin // individual bytes
|
||||
miss_size_o = 3'b000;
|
||||
end
|
||||
endcase // dbe_idx
|
||||
end
|
||||
// get size of aligned words, and the corresponding byte enables
|
||||
// note: openpiton can only handle aligned offsets + size, and hence
|
||||
// we have to split unaligned data into multiple transfers (see toSize64)
|
||||
// e.g. if we have the following valid bytes: 0011_1001 -> TX0: 0000_0001, TX1: 0000_1000, TX2: 0011_0000
|
||||
assign miss_size_o = toSize64(bdirty[dirty_ptr]);
|
||||
|
||||
// openpiton requires the data to be replicated in case of smaller sizes than dwords
|
||||
always_comb begin : p_offset_mux
|
||||
miss_wdata_o = '0;
|
||||
tx_be = '0;
|
||||
unique case(miss_size_o)
|
||||
3'b001: begin // hword
|
||||
for(int k=0; k<4; k++) begin
|
||||
miss_wdata_o[k*16 +: 16] = wbuffer_q[dirty_ptr].data[bdirty_off*8 +: 16];
|
||||
end
|
||||
tx_be[bdirty_off +:2 ] = '1;
|
||||
end
|
||||
3'b010: begin // word
|
||||
for(int k=0; k<2; k++) begin
|
||||
miss_wdata_o[k*32 +: 32] = wbuffer_q[dirty_ptr].data[bdirty_off*8 +: 32];
|
||||
end
|
||||
tx_be[bdirty_off +:4 ] = '1;
|
||||
end
|
||||
3'b011: begin // dword
|
||||
miss_wdata_o = wbuffer_q[dirty_ptr].data;
|
||||
tx_be = '1;
|
||||
end
|
||||
default: begin // byte
|
||||
for(int k=0; k<8; k++) begin
|
||||
miss_wdata_o[k*8 +: 8] = wbuffer_q[dirty_ptr].data[bdirty_off*8 +: 8];
|
||||
end
|
||||
tx_be[bdirty_off] = '1;
|
||||
end
|
||||
endcase // miss_size_o
|
||||
end
|
||||
assign miss_wdata_o = repData64(wbuffer_q[dirty_ptr].data,
|
||||
bdirty_off,
|
||||
miss_size_o[1:0]);
|
||||
|
||||
assign tx_be = toByteEnable8(bdirty_off,
|
||||
miss_size_o[1:0]);
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// TX status registers and ID counters
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// TODO: todo: make this fall through!
|
||||
// TODO: todo: make this fall through if timing permits it
|
||||
fifo_v2 #(
|
||||
.FALL_THROUGH ( 1'b0 ),
|
||||
.DATA_WIDTH ( $clog2(DCACHE_MAX_TX) ),
|
||||
|
@ -345,8 +310,8 @@ generate
|
|||
// checks if an invalidation/cache refill hits a particular word
|
||||
// note: an invalidation can hit multiple words!
|
||||
// need to respect previous cycle, too, since we add a cycle of latency to the rd_hit_oh_i signal...
|
||||
assign inval_hit[k] = (wr_cl_vld_d & valid[k] & (wbuffer_q[k].wtag[DCACHE_INDEX_WIDTH-1:DCACHE_OFFSET_WIDTH-3] == wr_cl_idx_d)) |
|
||||
(wr_cl_vld_q & valid[k] & (wbuffer_q[k].wtag[DCACHE_INDEX_WIDTH-1:DCACHE_OFFSET_WIDTH-3] == wr_cl_idx_q));
|
||||
assign inval_hit[k] = (wr_cl_vld_d & valid[k] & (wbuffer_q[k].wtag[DCACHE_INDEX_WIDTH-1:0]<<3 == wr_cl_idx_d<<DCACHE_OFFSET_WIDTH)) |
|
||||
(wr_cl_vld_q & valid[k] & (wbuffer_q[k].wtag[DCACHE_INDEX_WIDTH-1:0]<<3 == wr_cl_idx_q<<DCACHE_OFFSET_WIDTH));
|
||||
|
||||
// these word have to be looked up in the cache
|
||||
assign tocheck[k] = (~wbuffer_q[k].checked) & valid[k];
|
||||
|
@ -355,8 +320,10 @@ endgenerate
|
|||
|
||||
assign wr_ptr = (|wbuffer_hit_oh) ? hit_ptr : next_ptr;
|
||||
assign empty_o = ~(|valid);
|
||||
// assign rdy = empty_o;
|
||||
//(|wbuffer_hit_oh) | (~full);
|
||||
assign rdy = (|wbuffer_hit_oh) | (~full);
|
||||
|
||||
//assign rdy = (~full) && ~(|wbuffer_hit_oh);
|
||||
|
||||
// next free entry in the buffer
|
||||
lzc #(
|
||||
|
@ -367,7 +334,7 @@ lzc #(
|
|||
.empty_o ( full )
|
||||
);
|
||||
|
||||
// next free entry in the buffer
|
||||
// get index of hit
|
||||
lzc #(
|
||||
.WIDTH ( DCACHE_WBUF_DEPTH )
|
||||
) i_hit_lzc (
|
||||
|
@ -378,7 +345,8 @@ lzc #(
|
|||
|
||||
// next dirty word to serve
|
||||
rrarbiter #(
|
||||
.NUM_REQ ( DCACHE_WBUF_DEPTH )
|
||||
.NUM_REQ ( DCACHE_WBUF_DEPTH ),
|
||||
.LOCK_IN ( 1 )// lock the decision, once request is asserted
|
||||
) i_dirty_rr (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
|
@ -404,7 +372,6 @@ rrarbiter #(
|
|||
.idx_o ( check_ptr_d )
|
||||
);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// update logic
|
||||
///////////////////////////////////////////////////////
|
||||
|
@ -470,35 +437,28 @@ always_comb begin : p_buffer
|
|||
end
|
||||
end
|
||||
|
||||
// if we are serving an NC write, we block until it is served
|
||||
if(nc_pending_q) begin
|
||||
if(empty_o) begin
|
||||
nc_pending_d = 1'b0;
|
||||
end
|
||||
end else begin
|
||||
// write new word into the buffer
|
||||
if(req_port_i.data_req & rdy) begin
|
||||
// in case we have an NC address, need to drain the buffer first
|
||||
if(empty_o | ~addr_is_nc) begin
|
||||
wbuffer_wren = 1'b1;
|
||||
// write new word into the buffer
|
||||
if(req_port_i.data_req & rdy) begin
|
||||
// in case we have an NC address, need to drain the buffer first
|
||||
// in case we are serving an NC address, we block until it is written to memory
|
||||
if(empty_o | ~(addr_is_nc | nc_pending_q)) begin
|
||||
wbuffer_wren = 1'b1;
|
||||
|
||||
// leave in the core fifo if it is NC
|
||||
req_port_o.data_gnt = 1'b1;
|
||||
nc_pending_d = addr_is_nc;
|
||||
req_port_o.data_gnt = 1'b1;
|
||||
nc_pending_d = addr_is_nc;
|
||||
|
||||
wbuffer_d[wr_ptr].checked = 1'b0;
|
||||
wbuffer_d[wr_ptr].wtag = {req_port_i.address_tag, req_port_i.address_index[DCACHE_INDEX_WIDTH-1:3]};
|
||||
|
||||
// mark bytes as dirty
|
||||
for(int k=0; k<8; k++) begin
|
||||
if(req_port_i.data_be[k]) begin
|
||||
wbuffer_d[wr_ptr].valid[k] = 1'b1;
|
||||
wbuffer_d[wr_ptr].dirty[k] = 1'b1;
|
||||
wbuffer_d[wr_ptr].data[k*8 +: 8] = req_port_i.data_wdata[k*8 +: 8];
|
||||
end
|
||||
wbuffer_d[wr_ptr].checked = 1'b0;
|
||||
wbuffer_d[wr_ptr].wtag = {req_port_i.address_tag, req_port_i.address_index[DCACHE_INDEX_WIDTH-1:3]};
|
||||
|
||||
// mark bytes as dirty
|
||||
for(int k=0; k<8; k++) begin
|
||||
if(req_port_i.data_be[k]) begin
|
||||
wbuffer_d[wr_ptr].valid[k] = 1'b1;
|
||||
wbuffer_d[wr_ptr].dirty[k] = 1'b1;
|
||||
wbuffer_d[wr_ptr].data[k*8 +: 8] = req_port_i.data_wdata[k*8 +: 8];
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -580,19 +540,14 @@ end
|
|||
for(genvar j=0; j<8; j++) begin
|
||||
byteStates: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) {wbuffer_q[k].valid[j], wbuffer_q[k].dirty[j], wbuffer_q[k].txblock[j]} inside {3'b000, 3'b110, 3'b101, 3'b111} )
|
||||
else $fatal(1,$psprintf("[l1 dcache wbuffer] byte %02d of wbuffer entry %02d has invalid state: valid=%01b, dirty=%01b, txblock=%01b",
|
||||
else $fatal(1,"[l1 dcache wbuffer] byte %02d of wbuffer entry %02d has invalid state: valid=%01b, dirty=%01b, txblock=%01b",
|
||||
j,k,
|
||||
wbuffer_q[k].valid[j],
|
||||
wbuffer_q[k].dirty[j],
|
||||
wbuffer_q[k].txblock[j]));
|
||||
wbuffer_q[k].txblock[j]);
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
// initial begin
|
||||
// // assert wrong parameterizations
|
||||
// assert (DCACHE_INDEX_WIDTH<=12)
|
||||
// else $fatal(1,"[l1 dcache ctrl] cache index width can be maximum 12bit since VM uses 4kB pages");
|
||||
// end
|
||||
`endif
|
||||
//pragma translate_on
|
||||
|
||||
|
|
|
@ -426,33 +426,27 @@ module serpent_icache #(
|
|||
///////////////////////////////////////////////////////
|
||||
// tag comparison, hit generation
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// tag comparison of way 0
|
||||
assign cl_hit[0] = (cl_tag_d == cl_tag_rdata[0]) & vld_rdata[0];
|
||||
|
||||
// use way 0 to bypass read data in case we missed on the cache or in case the req is NC
|
||||
assign cl_sel[0] = (cmp_en_q & cl_hit[0]) ? cl_rdata[0][{cl_offset_q,3'b0} +: FETCH_WIDTH]:
|
||||
(cmp_en_q) ? '0 :
|
||||
mem_rtrn_i.data[{cl_offset_q,3'b0} +: FETCH_WIDTH];
|
||||
|
||||
logic [$clog2(ICACHE_SET_ASSOC)-1:0] hit_idx;
|
||||
|
||||
generate
|
||||
for (genvar i=1;i<ICACHE_SET_ASSOC;i++) begin : g_tag_cmpsel
|
||||
// tag comparison of ways >0
|
||||
for (genvar i=0;i<ICACHE_SET_ASSOC;i++) begin : g_tag_cmpsel
|
||||
assign cl_hit[i] = (cl_tag_rdata[i] == cl_tag_d) & vld_rdata[i];
|
||||
|
||||
// byte offset mux of ways >0
|
||||
assign cl_sel[i] = (cmp_en_q & cl_hit[i]) ? cl_rdata[i][{cl_offset_q,3'b0} +: FETCH_WIDTH] : '0;
|
||||
assign cl_sel[i] = cl_rdata[i][{cl_offset_q,3'b0} +: FETCH_WIDTH];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// OR reduction of selected cachelines
|
||||
always_comb begin : p_reduction
|
||||
dreq_o.data = cl_sel[0];
|
||||
for(int i=1; i<ICACHE_SET_ASSOC;i++)
|
||||
dreq_o.data |= cl_sel[i];
|
||||
end
|
||||
|
||||
|
||||
lzc #(
|
||||
.WIDTH ( ICACHE_SET_ASSOC )
|
||||
) i_lzc_hit (
|
||||
.in_i ( cl_hit ),
|
||||
.cnt_o ( hit_idx ),
|
||||
.empty_o ( )
|
||||
);
|
||||
|
||||
assign dreq_o.data = ( cmp_en_q ) ? cl_sel[hit_idx] :
|
||||
mem_rtrn_i.data[{cl_offset_q,3'b0} +: FETCH_WIDTH];
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// memory arrays and regs
|
||||
///////////////////////////////////////////////////////
|
||||
|
|
|
@ -42,8 +42,8 @@ module commit_stage #(
|
|||
input logic [63:0] csr_rdata_i, // data to read from CSR
|
||||
input exception_t csr_exception_i, // exception or interrupt occurred in CSR stage (the same as commit)
|
||||
// commit signals to ex
|
||||
output logic commit_lsu_o, // commit the pending store
|
||||
input logic commit_lsu_ready_i, // commit buffer of LSU is ready
|
||||
output logic commit_lsu_req_o, // request commit of pending store
|
||||
input logic commit_lsu_ack_i, // asserted when the LSU can commit the store requested
|
||||
output logic amo_valid_commit_o, // valid AMO in commit stage
|
||||
input logic no_st_pending_i, // there is no store pending
|
||||
output logic commit_csr_o, // commit the pending CSR instruction
|
||||
|
@ -74,7 +74,7 @@ module commit_stage #(
|
|||
we_o[0] = 1'b0;
|
||||
we_o[1] = 1'b0;
|
||||
|
||||
commit_lsu_o = 1'b0;
|
||||
commit_lsu_req_o= 1'b0;
|
||||
commit_csr_o = 1'b0;
|
||||
// amos will commit on port 0
|
||||
wdata_o[0] = (amo_resp_i.ack) ? amo_resp_i.result : commit_instr_i[0].result;
|
||||
|
@ -108,10 +108,9 @@ module commit_stage #(
|
|||
// by the subsequent flush triggered by an exception
|
||||
if (commit_instr_i[0].fu == STORE && !instr_0_is_amo) begin
|
||||
// check if the LSU is ready to accept another commit entry (e.g.: a non-speculative store)
|
||||
if (commit_lsu_ready_i)
|
||||
commit_lsu_o = 1'b1;
|
||||
else // if the LSU buffer is not ready - do not commit, wait
|
||||
commit_ack_o[0] = 1'b0;
|
||||
commit_lsu_req_o = 1'b1;
|
||||
// if the LSU buffer is not ready - do not commit, wait
|
||||
commit_ack_o[0] = commit_lsu_ack_i;
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 21a060d2c2c75173312b82cc72db96a2c62e66c5
|
||||
Subproject commit ae62ffdcaa54f1b8d10881e4b5163d7b233336d2
|
|
@ -54,8 +54,8 @@ module ex_stage #(
|
|||
output logic lsu_valid_o, // Output is valid
|
||||
output logic [63:0] lsu_result_o,
|
||||
output logic [TRANS_ID_BITS-1:0] lsu_trans_id_o,
|
||||
input logic lsu_commit_i,
|
||||
output logic lsu_commit_ready_o, // commit queue is ready to accept another commit request
|
||||
input logic lsu_commit_req_i,
|
||||
output logic lsu_commit_ack_o, // commit queue is ready to accept another commit request
|
||||
output exception_t lsu_exception_o,
|
||||
output logic no_st_pending_o,
|
||||
input logic amo_valid_commit_i,
|
||||
|
@ -131,8 +131,8 @@ module ex_stage #(
|
|||
// Load-Store Unit
|
||||
// ----------------
|
||||
lsu lsu_i (
|
||||
.commit_i ( lsu_commit_i ),
|
||||
.commit_ready_o ( lsu_commit_ready_o ),
|
||||
.commit_req_i ( lsu_commit_req_i ),
|
||||
.commit_ack_o ( lsu_commit_ack_o ),
|
||||
.dcache_req_ports_i,
|
||||
.dcache_req_ports_o,
|
||||
.amo_req_o,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3e925e169bd02ebf26e3d4ab65cd1832319cf580
|
||||
Subproject commit 45aa2b5bae9a25efb3bc8f5e0b1d5d0cb018f41a
|
|
@ -34,8 +34,8 @@ module lsu #(
|
|||
output logic [TRANS_ID_BITS-1:0] lsu_trans_id_o, // ID of scoreboard entry at which to write back
|
||||
output logic [63:0] lsu_result_o,
|
||||
output logic lsu_valid_o, // transaction id for which the output is the requested one
|
||||
input logic commit_i, // commit the pending store
|
||||
output logic commit_ready_o, // commit queue is ready to accept another commit request
|
||||
input logic commit_req_i, // request to commit the pending store
|
||||
output logic commit_ack_o, // acknowledged it the queue is ready to accept the commit request
|
||||
|
||||
input logic enable_translation_i, // enable virtual memory translation
|
||||
input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores
|
||||
|
@ -149,8 +149,8 @@ module lsu #(
|
|||
.valid_i ( st_valid_i ),
|
||||
.lsu_ctrl_i ( lsu_ctrl ),
|
||||
.pop_st_o ( pop_st ),
|
||||
.commit_i,
|
||||
.commit_ready_o,
|
||||
.commit_req_i,
|
||||
.commit_ack_o,
|
||||
.amo_valid_commit_i,
|
||||
|
||||
.valid_o ( st_valid ),
|
||||
|
|
|
@ -25,8 +25,8 @@ module store_buffer (
|
|||
input logic [11:0] page_offset_i, // check for the page offset (the last 12 bit if the current load matches them)
|
||||
output logic page_offset_matches_o, // the above input page offset matches -> let the store buffer drain
|
||||
|
||||
input logic commit_i, // commit the instruction which was placed there most recently
|
||||
output logic commit_ready_o, // commit queue is ready to accept another commit request
|
||||
input logic commit_req_i, // request to commit the instruction which was placed there most recently
|
||||
output logic commit_ack_o, // acknowledge if the queue is ready to commit the request
|
||||
output logic ready_o, // the store queue is ready to accept a new request
|
||||
// it is only ready if it can unconditionally commit the instruction, e.g.:
|
||||
// the commit buffer needs to be empty
|
||||
|
@ -47,10 +47,11 @@ module store_buffer (
|
|||
// allocate more space for the commit buffer to be on the save side
|
||||
localparam int unsigned DEPTH_COMMIT = 4;
|
||||
|
||||
|
||||
// the store queue has two parts:
|
||||
// 1. Speculative queue
|
||||
// 2. Commit queue which is non-speculative, e.g.: the store will definitely happen.
|
||||
// note that the serpent cache subsystem contains a merging write buffer,
|
||||
// and if enabled (i.e. the macro SERPENT_PULP is defined), we do not need the commit queue
|
||||
|
||||
struct packed {
|
||||
logic [63:0] address;
|
||||
|
@ -80,7 +81,7 @@ module store_buffer (
|
|||
speculative_status_cnt = speculative_status_cnt_q;
|
||||
|
||||
// we are ready if the speculative and the commit queue have a space left
|
||||
ready_o = (speculative_status_cnt_q < (DEPTH_SPEC - 1)) || commit_i;
|
||||
ready_o = (speculative_status_cnt_q < (DEPTH_SPEC - 1)) || commit_ack_o;
|
||||
// default assignments
|
||||
speculative_status_cnt_n = speculative_status_cnt_q;
|
||||
speculative_read_pointer_n = speculative_read_pointer_q;
|
||||
|
@ -101,7 +102,7 @@ module store_buffer (
|
|||
|
||||
// evict the current entry out of this queue, the commit queue will thankfully take it and commit it
|
||||
// to the memory hierarchy
|
||||
if (commit_i) begin
|
||||
if (commit_ack_o) begin
|
||||
// invalidate
|
||||
speculative_queue_n[speculative_read_pointer_q].valid = 1'b0;
|
||||
// advance the read pointer
|
||||
|
@ -126,24 +127,38 @@ module store_buffer (
|
|||
// ----------------------------------------
|
||||
// Commit Queue - Memory Interface
|
||||
// ----------------------------------------
|
||||
|
||||
// we will never kill a request in the store buffer since we already know that the translation is valid
|
||||
// e.g.: a kill request will only be necessary if we are not sure if the requested memory address will result in a TLB fault
|
||||
assign req_port_o.kill_req = 1'b0;
|
||||
assign req_port_o.data_we = 1'b1; // we will always write in the store queue
|
||||
assign req_port_o.tag_valid = 1'b0;
|
||||
|
||||
`ifdef SERPENT_PULP
|
||||
// there is a separate signal coming in from the dcache write buffer that is connected directly to the commit stage
|
||||
assign no_st_pending_o = 1'b1;
|
||||
// in this case, we directly output data from the speculative queue
|
||||
assign req_port_o.data_req = commit_req_i;
|
||||
assign commit_ack_o = req_port_i.data_gnt;
|
||||
assign req_port_o.address_index = speculative_queue_q[speculative_read_pointer_q].address[11:0];
|
||||
assign req_port_o.address_tag = speculative_queue_q[speculative_read_pointer_q].address[55:12];
|
||||
assign req_port_o.data_wdata = speculative_queue_q[speculative_read_pointer_q].data;
|
||||
assign req_port_o.data_be = speculative_queue_q[speculative_read_pointer_q].be;
|
||||
assign req_port_o.data_size = speculative_queue_q[speculative_read_pointer_q].data_size;
|
||||
`else
|
||||
// those signals can directly be output to the memory
|
||||
assign req_port_o.address_index = commit_queue_q[commit_read_pointer_q].address[11:0];
|
||||
// if we got a new request we already saved the tag from the previous cycle
|
||||
assign req_port_o.address_tag = commit_queue_q[commit_read_pointer_q].address[55:12];
|
||||
assign req_port_o.tag_valid = 1'b0;
|
||||
assign req_port_o.data_wdata = commit_queue_q[commit_read_pointer_q].data;
|
||||
assign req_port_o.data_be = commit_queue_q[commit_read_pointer_q].be;
|
||||
assign req_port_o.data_size = commit_queue_q[commit_read_pointer_q].data_size;
|
||||
// we will never kill a request in the store buffer since we already know that the translation is valid
|
||||
// e.g.: a kill request will only be necessary if we are not sure if the requested memory address will result in a TLB fault
|
||||
assign req_port_o.kill_req = 1'b0;
|
||||
assign req_port_o.data_we = 1'b1; // we will always write in the store queue
|
||||
|
||||
|
||||
always_comb begin : store_if
|
||||
automatic logic [DEPTH_COMMIT:0] commit_status_cnt;
|
||||
commit_status_cnt = commit_status_cnt_q;
|
||||
|
||||
commit_ready_o = (commit_status_cnt_q < DEPTH_COMMIT);
|
||||
commit_ack_o = (commit_status_cnt_q < DEPTH_COMMIT) && commit_req_i;
|
||||
// no store is pending if we don't have any element in the commit queue e.g.: it is empty
|
||||
no_st_pending_o = (commit_status_cnt_q == 0);
|
||||
// default assignments
|
||||
|
@ -170,7 +185,7 @@ module store_buffer (
|
|||
// happened if we got a grant
|
||||
|
||||
// shift the store request from the speculative buffer to the non-speculative
|
||||
if (commit_i) begin
|
||||
if (commit_ack_o) begin
|
||||
commit_queue_n[commit_write_pointer_q] = speculative_queue_q[speculative_read_pointer_q];
|
||||
commit_write_pointer_n = commit_write_pointer_n + 1'b1;
|
||||
commit_status_cnt++;
|
||||
|
@ -178,7 +193,7 @@ module store_buffer (
|
|||
|
||||
commit_status_cnt_n = commit_status_cnt;
|
||||
end
|
||||
|
||||
`endif
|
||||
// ------------------
|
||||
// Address Checker
|
||||
// ------------------
|
||||
|
@ -196,6 +211,8 @@ module store_buffer (
|
|||
// page offsets are virtually and physically the same
|
||||
always_comb begin : address_checker
|
||||
page_offset_matches_o = 1'b0;
|
||||
|
||||
`ifndef SERPENT_PULP
|
||||
// check if the LSBs are identical and the entry is valid
|
||||
for (int unsigned i = 0; i < DEPTH_COMMIT; i++) begin
|
||||
// Check if the page offset matches and whether the entry is valid, for the commit queue
|
||||
|
@ -204,6 +221,8 @@ module store_buffer (
|
|||
break;
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
for (int unsigned i = 0; i < DEPTH_SPEC; i++) begin
|
||||
// do the same for the speculative queue
|
||||
if ((page_offset_i[11:3] == speculative_queue_q[i].address[11:3]) && speculative_queue_q[i].valid) begin
|
||||
|
@ -219,35 +238,47 @@ module store_buffer (
|
|||
|
||||
|
||||
// registers
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : proc_
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_spec
|
||||
if (~rst_ni) begin
|
||||
// initialize the queues
|
||||
speculative_queue_q <= '{default: 0};
|
||||
commit_queue_q <= '{default: 0};
|
||||
commit_read_pointer_q <= '0;
|
||||
commit_write_pointer_q <= '0;
|
||||
commit_status_cnt_q <= '0;
|
||||
speculative_read_pointer_q <= '0;
|
||||
speculative_write_pointer_q <= '0;
|
||||
speculative_status_cnt_q <= '0;
|
||||
end else begin
|
||||
speculative_queue_q <= speculative_queue_n;
|
||||
commit_queue_q <= commit_queue_n;
|
||||
commit_read_pointer_q <= commit_read_pointer_n;
|
||||
commit_write_pointer_q <= commit_write_pointer_n;
|
||||
commit_status_cnt_q <= commit_status_cnt_n;
|
||||
speculative_read_pointer_q <= speculative_read_pointer_n;
|
||||
speculative_write_pointer_q <= speculative_write_pointer_n;
|
||||
speculative_status_cnt_q <= speculative_status_cnt_n;
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
`ifndef SERPENT_PULP
|
||||
// registers
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_commit
|
||||
if (~rst_ni) begin
|
||||
commit_queue_q <= '{default: 0};
|
||||
commit_read_pointer_q <= '0;
|
||||
commit_write_pointer_q <= '0;
|
||||
commit_status_cnt_q <= '0;
|
||||
end else begin
|
||||
commit_queue_q <= commit_queue_n;
|
||||
commit_read_pointer_q <= commit_read_pointer_n;
|
||||
commit_write_pointer_q <= commit_write_pointer_n;
|
||||
commit_status_cnt_q <= commit_status_cnt_n;
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// assertions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
//pragma translate_off
|
||||
`ifndef verilator
|
||||
// assert that commit is never set when we are flushing this would be counter intuitive
|
||||
// as flush and commit is decided in the same stage
|
||||
commit_and_flush: assert property (
|
||||
@(posedge clk_i) rst_ni && flush_i |-> !commit_i)
|
||||
@(posedge clk_i) rst_ni && flush_i |-> !commit_req_i)
|
||||
else $error ("[Commit Queue] You are trying to commit and flush in the same cycle");
|
||||
|
||||
speculative_buffer_overflow: assert property (
|
||||
|
@ -255,13 +286,17 @@ module store_buffer (
|
|||
else $error ("[Speculative Queue] You are trying to push new data although the buffer is not ready");
|
||||
|
||||
speculative_buffer_underflow: assert property (
|
||||
@(posedge clk_i) rst_ni && (speculative_status_cnt_q == 0) |-> !commit_i)
|
||||
@(posedge clk_i) rst_ni && (speculative_status_cnt_q == 0) |-> !commit_req_i)
|
||||
else $error ("[Speculative Queue] You are committing although there are no stores to commit");
|
||||
|
||||
commit_buffer_overflow: assert property (
|
||||
@(posedge clk_i) rst_ni && (commit_status_cnt_q == DEPTH_SPEC) |-> !commit_i)
|
||||
else $error("[Commit Queue] You are trying to commit a store although the buffer is full");
|
||||
|
||||
`ifndef SERPENT_PULP
|
||||
commit_buffer_overflow: assert property (
|
||||
@(posedge clk_i) rst_ni && (commit_status_cnt_q == DEPTH_SPEC) |-> !commit_ack_o)
|
||||
else $error("[Commit Queue] You are trying to commit a store although the buffer is full");
|
||||
`endif
|
||||
`endif
|
||||
//pragma translate_on
|
||||
endmodule
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ module store_unit (
|
|||
input logic valid_i,
|
||||
input lsu_ctrl_t lsu_ctrl_i,
|
||||
output logic pop_st_o,
|
||||
input logic commit_i,
|
||||
output logic commit_ready_o,
|
||||
input logic commit_req_i,
|
||||
output logic commit_ack_o,
|
||||
input logic amo_valid_commit_i,
|
||||
// store unit output port
|
||||
output logic valid_o,
|
||||
|
@ -219,8 +219,8 @@ module store_unit (
|
|||
.no_st_pending_o,
|
||||
.page_offset_i,
|
||||
.page_offset_matches_o,
|
||||
.commit_i,
|
||||
.commit_ready_o,
|
||||
.commit_req_i,
|
||||
.commit_ack_o,
|
||||
.ready_o ( store_buffer_ready ),
|
||||
.valid_i ( store_buffer_valid ),
|
||||
// the flush signal can be critical and we need this valid
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
module sram #(
|
||||
parameter DATA_WIDTH = 64,
|
||||
parameter NUM_WORDS = 1024,
|
||||
parameter OUT_REGS = 0 // enables output registers in FPGA macro (read lat = 2)
|
||||
parameter OUT_REGS = 0, // enables output registers in FPGA macro (read lat = 2)
|
||||
parameter SIM_INIT = 2 // initialize simulation model with random data upon reset
|
||||
)(
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
@ -55,9 +56,10 @@ generate
|
|||
for (k = 0; k<(DATA_WIDTH+63)/64; k++) begin
|
||||
// unused byte-enable segments (8bits) are culled by the tool
|
||||
SyncSpRamBeNx64 #(
|
||||
.ADDR_WIDTH($clog2(NUM_WORDS)),
|
||||
.DATA_DEPTH(NUM_WORDS),
|
||||
.OUT_REGS (0)
|
||||
.ADDR_WIDTH ( $clog2(NUM_WORDS) ),
|
||||
.DATA_DEPTH ( NUM_WORDS ),
|
||||
.OUT_REGS ( 0 ),
|
||||
.SIM_INIT ( SIM_INIT )
|
||||
) i_ram (
|
||||
.Clk_CI ( clk_i ),
|
||||
.Rst_RBI ( rst_ni ),
|
||||
|
|
|
@ -50,6 +50,7 @@ module tb;
|
|||
parameter TLB_HIT_RATE = 95;
|
||||
|
||||
// parameters for random read sequences (in %)
|
||||
parameter FLUSH_RATE = 10;
|
||||
parameter KILL_RATE = 5;
|
||||
|
||||
parameter VERBOSE = 0;
|
||||
|
@ -64,7 +65,7 @@ module tb;
|
|||
logic miss_o;
|
||||
logic wbuffer_empty_o;
|
||||
amo_req_t amo_req_i;
|
||||
amo_resp_t amo_ack_o;
|
||||
amo_resp_t amo_resp_o;
|
||||
dcache_req_i_t [2:0] req_ports_i;
|
||||
dcache_req_o_t [2:0] req_ports_o;
|
||||
logic mem_rtrn_vld_i;
|
||||
|
@ -113,6 +114,8 @@ module tb;
|
|||
resp_fifo_t fifo_data_in[1:0];
|
||||
resp_fifo_t fifo_data[1:0];
|
||||
logic [1:0] fifo_push, fifo_pop, fifo_flush;
|
||||
logic [2:0] flush;
|
||||
logic flush_rand_en;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// helper tasks
|
||||
|
@ -130,9 +133,9 @@ module tb;
|
|||
endtask : runSeq
|
||||
|
||||
task automatic flushCache();
|
||||
flush_i = 1'b1;
|
||||
flush[2] = 1'b1;
|
||||
`APPL_WAIT_SIG(clk_i, flush_ack_o);
|
||||
flush_i = 0'b0;
|
||||
flush[2] = 0'b0;
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
endtask : flushCache
|
||||
|
||||
|
@ -212,7 +215,7 @@ module tb;
|
|||
.miss_o ( miss_o ),
|
||||
.wbuffer_empty_o ( wbuffer_empty_o ),
|
||||
.amo_req_i ( amo_req_i ),
|
||||
.amo_ack_o ( amo_ack_o ),
|
||||
.amo_resp_o ( amo_resp_o ),
|
||||
.req_ports_i ( req_ports_i ),
|
||||
.req_ports_o ( req_ports_o ),
|
||||
.mem_rtrn_vld_i ( mem_rtrn_vld_i ),
|
||||
|
@ -242,7 +245,7 @@ module tb;
|
|||
|
||||
assign exp_rdata[k] = mem_array[fifo_data[k].paddr>>3];
|
||||
assign fifo_push[k] = req_ports_i[k].data_req & req_ports_o[k].data_gnt;
|
||||
assign fifo_flush[k] = req_ports_i[k].kill_req | flush_i;
|
||||
assign fifo_flush[k] = req_ports_i[k].kill_req;
|
||||
assign fifo_pop[k] = req_ports_o[k].data_rvalid;
|
||||
|
||||
fifo_v2 #(
|
||||
|
@ -265,7 +268,8 @@ module tb;
|
|||
endgenerate
|
||||
|
||||
tb_readport #(
|
||||
.PORT_NAME ( "RD0" ),
|
||||
.PORT_NAME ( "RD0" ),
|
||||
.FLUSH_RATE ( FLUSH_RATE ),
|
||||
.KILL_RATE ( KILL_RATE ),
|
||||
.TLB_HIT_RATE ( TLB_HIT_RATE ),
|
||||
.MEM_WORDS ( MEM_WORDS ),
|
||||
|
@ -273,27 +277,31 @@ module tb;
|
|||
.RND_SEED ( 5555555 ),
|
||||
.VERBOSE ( VERBOSE )
|
||||
) i_tb_readport0 (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.test_name_i ( test_name ),
|
||||
.req_rate_i ( req_rate[0] ),
|
||||
.seq_type_i ( seq_type[0] ),
|
||||
.tlb_rand_en_i ( tlb_rand_en ),
|
||||
.seq_run_i ( seq_run ),
|
||||
.seq_num_resp_i ( seq_num_resp ),
|
||||
.seq_last_i ( seq_last ),
|
||||
.seq_done_o ( seq_done[0] ),
|
||||
.exp_paddr_o ( exp_paddr[0] ),
|
||||
.exp_size_i ( fifo_data[0].size ),
|
||||
.exp_paddr_i ( fifo_data[0].paddr ),
|
||||
.exp_rdata_i ( exp_rdata[0] ),
|
||||
.act_paddr_i ( act_paddr[0] ),
|
||||
.dut_req_port_o ( req_ports_i[0] ),
|
||||
.dut_req_port_i ( req_ports_o[0] )
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.test_name_i ( test_name ),
|
||||
.req_rate_i ( req_rate[0] ),
|
||||
.seq_type_i ( seq_type[0] ),
|
||||
.tlb_rand_en_i ( tlb_rand_en ),
|
||||
.flush_rand_en_i ( flush_rand_en ),
|
||||
.seq_run_i ( seq_run ),
|
||||
.seq_num_resp_i ( seq_num_resp ),
|
||||
.seq_last_i ( seq_last ),
|
||||
.seq_done_o ( seq_done[0] ),
|
||||
.exp_paddr_o ( exp_paddr[0] ),
|
||||
.exp_size_i ( fifo_data[0].size ),
|
||||
.exp_paddr_i ( fifo_data[0].paddr ),
|
||||
.exp_rdata_i ( exp_rdata[0] ),
|
||||
.act_paddr_i ( act_paddr[0] ),
|
||||
.flush_o ( flush[0] ),
|
||||
.flush_ack_i ( flush_ack_o ),
|
||||
.dut_req_port_o ( req_ports_i[0] ),
|
||||
.dut_req_port_i ( req_ports_o[0] )
|
||||
);
|
||||
|
||||
tb_readport #(
|
||||
.PORT_NAME ( "RD1" ),
|
||||
.FLUSH_RATE ( FLUSH_RATE ),
|
||||
.KILL_RATE ( KILL_RATE ),
|
||||
.TLB_HIT_RATE ( TLB_HIT_RATE ),
|
||||
.MEM_WORDS ( MEM_WORDS ),
|
||||
|
@ -301,23 +309,26 @@ module tb;
|
|||
.RND_SEED ( 3333333 ),
|
||||
.VERBOSE ( VERBOSE )
|
||||
) i_tb_readport1 (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.test_name_i ( test_name ),
|
||||
.req_rate_i ( req_rate[1] ),
|
||||
.seq_type_i ( seq_type[1] ),
|
||||
.tlb_rand_en_i ( tlb_rand_en ),
|
||||
.seq_run_i ( seq_run ),
|
||||
.seq_num_resp_i ( seq_num_resp ),
|
||||
.seq_last_i ( seq_last ),
|
||||
.exp_paddr_o ( exp_paddr[1] ),
|
||||
.exp_size_i ( fifo_data[1].size ),
|
||||
.exp_paddr_i ( fifo_data[1].paddr ),
|
||||
.exp_rdata_i ( exp_rdata[1] ),
|
||||
.act_paddr_i ( act_paddr[1] ),
|
||||
.seq_done_o ( seq_done[1] ),
|
||||
.dut_req_port_o ( req_ports_i[1] ),
|
||||
.dut_req_port_i ( req_ports_o[1] )
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.test_name_i ( test_name ),
|
||||
.req_rate_i ( req_rate[1] ),
|
||||
.seq_type_i ( seq_type[1] ),
|
||||
.tlb_rand_en_i ( tlb_rand_en ),
|
||||
.flush_rand_en_i ( flush_rand_en ),
|
||||
.seq_run_i ( seq_run ),
|
||||
.seq_num_resp_i ( seq_num_resp ),
|
||||
.seq_last_i ( seq_last ),
|
||||
.exp_paddr_o ( exp_paddr[1] ),
|
||||
.exp_size_i ( fifo_data[1].size ),
|
||||
.exp_paddr_i ( fifo_data[1].paddr ),
|
||||
.exp_rdata_i ( exp_rdata[1] ),
|
||||
.act_paddr_i ( act_paddr[1] ),
|
||||
.seq_done_o ( seq_done[1] ),
|
||||
.flush_o ( flush[1] ),
|
||||
.flush_ack_i ( flush_ack_o ),
|
||||
.dut_req_port_o ( req_ports_i[1] ),
|
||||
.dut_req_port_i ( req_ports_o[1] )
|
||||
);
|
||||
|
||||
tb_writeport #(
|
||||
|
@ -356,7 +367,9 @@ module tb;
|
|||
assign amo_req_i.size = '0;
|
||||
assign amo_req_i.operand_a = '0;
|
||||
assign amo_req_i.operand_b = '0;
|
||||
// amo_ack_o
|
||||
// amo_resp_o
|
||||
|
||||
assign flush_i = |flush;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// simulation coordinator process
|
||||
|
@ -383,8 +396,9 @@ module tb;
|
|||
tlb_rand_en = 0;
|
||||
inv_rand_en = 0;
|
||||
amo_rand_en = 0;
|
||||
flush_rand_en = 0;
|
||||
// cache ctrl
|
||||
flush_i = 0;
|
||||
flush[2] = 0;
|
||||
// flush_ack_o
|
||||
// wbuffer_empty_o
|
||||
enable_i = 0;
|
||||
|
@ -582,6 +596,19 @@ module tb;
|
|||
inv_rand_en = 0;
|
||||
seq_type = '{WRAP_SEQ, IDLE_SEQ, WRAP_SEQ};
|
||||
req_rate = '{100,0,20};
|
||||
runSeq(5000,5000);
|
||||
flushCache();
|
||||
memCheck();
|
||||
///////////////////////////////////////////////
|
||||
test_name = "TEST 16 -- random write/read-- enabled cache + tlb, mem contentions + invalidations + random flushes";
|
||||
// config
|
||||
enable_i = 1;
|
||||
tlb_rand_en = 1;
|
||||
mem_rand_en = 1;
|
||||
inv_rand_en = 1;
|
||||
flush_rand_en = 1;
|
||||
seq_type = '{RANDOM_SEQ, RANDOM_SEQ, RANDOM_SEQ};
|
||||
req_rate = '{default:25};
|
||||
runSeq(5000,5000,1);// last sequence flag, terminates agents
|
||||
flushCache();
|
||||
memCheck();
|
||||
|
|
|
@ -225,26 +225,14 @@ module tb_mem #(
|
|||
DCACHE_LOAD_REQ: begin
|
||||
infifo_data.tid = outfifo_data.tid;
|
||||
infifo_data.nc = outfifo_data.nc;
|
||||
// openpiton replicates the data if size < dword
|
||||
infifo_data.data = 'x;
|
||||
unique case(outfifo_data.size)
|
||||
3'b000: begin
|
||||
for(int k=0;k<64;k+=8) infifo_data.data[k+:8] = mem_array_q[outfifo_data.paddr>>3][outfifo_data.paddr[2:0]*8 +: 8];
|
||||
end
|
||||
3'b001: begin
|
||||
for(int k=0;k<64;k+=16) infifo_data.data[k+:16] = mem_array_q[outfifo_data.paddr>>3][outfifo_data.paddr[2:1]*16+:16];
|
||||
end
|
||||
3'b010: begin
|
||||
for(int k=0;k<64;k+=32) infifo_data.data[k+:32] = mem_array_q[outfifo_data.paddr>>3][outfifo_data.paddr[2] *32+:32];
|
||||
end
|
||||
3'b011: infifo_data.data[0+:64] = mem_array_q[outfifo_data.paddr>>3];
|
||||
3'b111: begin// full cacheline
|
||||
for (int k=0; k<DCACHE_LINE_WIDTH/64; k++) begin
|
||||
infifo_data.data[k*64 +:64] = mem_array_q[(outfifo_data.paddr>>3) + k];
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
$fatal(1,"unsupported transfer size for read");
|
||||
end
|
||||
3'b000: for(int k=0;k<64;k+=8) infifo_data.data[outfifo_data.paddr[2:0]*8 +: 8] = mem_array_q[outfifo_data.paddr>>3][outfifo_data.paddr[2:0]*8 +: 8];
|
||||
3'b001: for(int k=0;k<64;k+=16) infifo_data.data[outfifo_data.paddr[2:1]*16+:16] = mem_array_q[outfifo_data.paddr>>3][outfifo_data.paddr[2:1]*16+:16];
|
||||
3'b010: for(int k=0;k<64;k+=32) infifo_data.data[outfifo_data.paddr[2] *32+:32] = mem_array_q[outfifo_data.paddr>>3][outfifo_data.paddr[2] *32+:32];
|
||||
3'b011: infifo_data.data[0+:64] = mem_array_q[outfifo_data.paddr>>3];
|
||||
3'b111: for(int k=0; k<DCACHE_LINE_WIDTH/64; k++) infifo_data.data[k*64 +:64] = mem_array_q[(outfifo_data.paddr>>3) + k];
|
||||
default: $fatal(1,"unsupported transfer size for read");
|
||||
endcase // infifo_data.size
|
||||
end
|
||||
DCACHE_STORE_REQ: begin
|
||||
|
|
|
@ -27,6 +27,7 @@ import tb_pkg::*;
|
|||
|
||||
program tb_readport #(
|
||||
parameter string PORT_NAME = "read port 0",
|
||||
parameter FLUSH_RATE = 1,
|
||||
parameter KILL_RATE = 5,
|
||||
parameter TLB_HIT_RATE = 95,
|
||||
parameter MEM_WORDS = 1024*1024,// in 64bit words
|
||||
|
@ -42,6 +43,7 @@ program tb_readport #(
|
|||
input logic [6:0] req_rate_i, //a rate between 0 and 100 percent
|
||||
input seq_t seq_type_i,
|
||||
input logic tlb_rand_en_i,
|
||||
input logic flush_rand_en_i,
|
||||
input logic seq_run_i,
|
||||
input logic [31:0] seq_num_resp_i,
|
||||
input logic seq_last_i,
|
||||
|
@ -55,6 +57,8 @@ program tb_readport #(
|
|||
input logic [63:0] act_paddr_i,
|
||||
|
||||
// interface to DUT
|
||||
output logic flush_o,
|
||||
input logic flush_ack_i,
|
||||
output dcache_req_i_t dut_req_port_o,
|
||||
input dcache_req_o_t dut_req_port_i
|
||||
);
|
||||
|
@ -136,6 +140,14 @@ program tb_readport #(
|
|||
// Helper tasks
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
task automatic flushCache();
|
||||
flush_o = 1'b1;
|
||||
`APPL_WAIT_SIG(clk_i, flush_ack_i);
|
||||
flush_o = 0'b0;
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
endtask : flushCache
|
||||
|
||||
|
||||
task automatic genRandReq();
|
||||
automatic logic [63:0] val;
|
||||
automatic logic [1:0] size;
|
||||
|
@ -158,27 +170,32 @@ program tb_readport #(
|
|||
dut_req_port_o.kill_req = 1'b0;
|
||||
end else begin
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address
|
||||
void'(randomize(val) with {val >= 0; val < (MEM_WORDS<<3);});
|
||||
void'(randomize(size));
|
||||
|
||||
dut_req_port_o.data_size = size;
|
||||
paddr = val;
|
||||
if(val < FLUSH_RATE && flush_rand_en_i) begin
|
||||
flushCache();
|
||||
end else begin
|
||||
void'(randomize(val) with {val > 0; val <= 100;});
|
||||
if(val < req_rate_i) begin
|
||||
dut_req_port_o.data_req = 1'b1;
|
||||
// generate random address
|
||||
void'(randomize(val) with {val >= 0; val < (MEM_WORDS<<3);});
|
||||
void'(randomize(size));
|
||||
|
||||
dut_req_port_o.data_size = size;
|
||||
paddr = val;
|
||||
|
||||
// align to size
|
||||
unique case(size)
|
||||
2'b01: paddr[0] = 1'b0;
|
||||
2'b10: paddr[1:0] = 2'b00;
|
||||
2'b11: paddr[2:0] = 3'b000;
|
||||
default: ;
|
||||
endcase
|
||||
// align to size
|
||||
unique case(size)
|
||||
2'b01: paddr[0] = 1'b0;
|
||||
2'b10: paddr[1:0] = 2'b00;
|
||||
2'b11: paddr[2:0] = 3'b000;
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
`APPL_WAIT_COMB_SIG(clk_i, dut_req_port_i.data_gnt)
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
`APPL_WAIT_CYC(clk_i,1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dut_req_port_o.data_req = '0;
|
||||
|
@ -230,6 +247,7 @@ program tb_readport #(
|
|||
dut_req_port_o.kill_req = '0;
|
||||
endtask : genWrapSeq
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sequence application
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -243,10 +261,12 @@ program tb_readport #(
|
|||
dut_req_port_o.data_size = '0;
|
||||
dut_req_port_o.kill_req = '0;
|
||||
seq_end_ack = '0;
|
||||
flush_o = '0;
|
||||
|
||||
// print some info
|
||||
$display("%s> current configuration:", PORT_NAME);
|
||||
$display("%s> KILL_RATE %d", PORT_NAME, KILL_RATE);
|
||||
$display("%s> FLUSH_RATE %d", PORT_NAME, FLUSH_RATE);
|
||||
$display("%s> TLB_HIT_RATE %d", PORT_NAME, TLB_HIT_RATE);
|
||||
$display("%s> RND_SEED %d", PORT_NAME, RND_SEED);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue