Integrate serpent cache into Ariane

This commit is contained in:
Michael Schaffner 2018-10-10 16:54:53 +02:00
parent fa3192ac78
commit ffec3c85ee
No known key found for this signature in database
GPG key ID: 7AA09AE049819C2C
21 changed files with 663 additions and 394 deletions

View file

@ -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} \

View file

@ -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

View file

@ -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 ),

View file

@ -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

View file

@ -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] ),

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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
///////////////////////////////////////////////////////

View file

@ -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

View file

@ -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

View file

@ -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 ),

View file

@ -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

View file

@ -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

View file

@ -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 ),

View file

@ -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();

View file

@ -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

View file

@ -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);