Add AXI adapter2, and connect l15 adapter.

This commit is contained in:
Michael Schaffner 2018-10-19 20:59:54 +02:00
parent e382dc866f
commit feb187fa96
No known key found for this signature in database
GPG key ID: 7AA09AE049819C2C
8 changed files with 854 additions and 493 deletions

View file

@ -227,17 +227,31 @@ package ariane_pkg;
// Cache config
// ---------------
// I$
localparam int unsigned ICACHE_INDEX_WIDTH = 12; // in bit
localparam int unsigned ICACHE_TAG_WIDTH = 44; // in bit
localparam int unsigned ICACHE_LINE_WIDTH = 128; // in bit
localparam int unsigned ICACHE_SET_ASSOC = 4;
// D$
localparam int unsigned DCACHE_INDEX_WIDTH = 12;
localparam int unsigned DCACHE_TAG_WIDTH = 44;
localparam int unsigned DCACHE_LINE_WIDTH = 128;
localparam int unsigned DCACHE_SET_ASSOC = 8;
// align to openpiton for the time being (this should be more configurable in the future)
`ifdef SERPENT_PULP
// I$
localparam int unsigned ICACHE_INDEX_WIDTH = 12; // in bit
localparam int unsigned ICACHE_TAG_WIDTH = 44; // in bit
localparam int unsigned ICACHE_LINE_WIDTH = 256; // in bit
localparam int unsigned ICACHE_SET_ASSOC = 4;
// D$
localparam int unsigned DCACHE_INDEX_WIDTH = 12;
localparam int unsigned DCACHE_TAG_WIDTH = 44;
localparam int unsigned DCACHE_LINE_WIDTH = 128;
localparam int unsigned DCACHE_SET_ASSOC = 4;
`else
// align to openpiton for the time being (this should be more configurable in the future)
// I$
localparam int unsigned ICACHE_INDEX_WIDTH = 12; // in bit
localparam int unsigned ICACHE_TAG_WIDTH = 44; // in bit
localparam int unsigned ICACHE_LINE_WIDTH = 128; // in bit
localparam int unsigned ICACHE_SET_ASSOC = 4;
// D$
localparam int unsigned DCACHE_INDEX_WIDTH = 12;
localparam int unsigned DCACHE_TAG_WIDTH = 44;
localparam int unsigned DCACHE_LINE_WIDTH = 128;
localparam int unsigned DCACHE_SET_ASSOC = 8;
`endif
// ---------------
// EX Stage

View file

@ -18,10 +18,9 @@
package serpent_cache_pkg;
localparam L15_SET_ASSOC = 4;
// these parames need to coincide with the current L1.5 parameterization
// do not change
localparam L15_SET_ASSOC = 4;
localparam L15_TID_WIDTH = 2;
localparam L15_TLB_CSM_WIDTH = 33;
@ -31,9 +30,7 @@ package serpent_cache_pkg;
// FIFO depths of L15 adapter
localparam ADAPTER_REQ_FIFO_DEPTH = 2;
// since packets have to be consumed immediately,
// we need not have a deeper FIFO
localparam ADAPTER_RTRN_FIFO_DEPTH = 1;
localparam ADAPTER_RTRN_FIFO_DEPTH = 2;
// Calculated parameter
@ -137,39 +134,41 @@ package serpent_cache_pkg;
// taken from iop.h in openpiton
// to l1.5 (only marked subset is used)
typedef enum logic [4:0] {LOAD_RQ = 5'b00000, // load request
IMISS_RQ = 5'b10000, // instruction fill request
STORE_RQ = 5'b00001, // store request
ATOMIC_RQ = 5'b00110, // atomic op
//CAS1_RQ = 5'b00010, // compare and swap1 packet (OpenSparc atomics)
//CAS2_RQ = 5'b00011, // compare and swap2 packet (OpenSparc atomics)
//SWAP_RQ = 5'b00110, // swap packet (OpenSparc atomics)
STRLOAD_RQ = 5'b00100, // unused
STRST_RQ = 5'b00101, // unused
STQ_RQ = 5'b00111, // unused
INT_RQ = 5'b01001, // interrupt request
FWD_RQ = 5'b01101, // unused
FWD_RPY = 5'b01110, // unused
RSVD_RQ = 5'b11111 // unused
typedef enum logic [4:0] {
L15_LOAD_RQ = 5'b00000, // load request
L15_IMISS_RQ = 5'b10000, // instruction fill request
L15_STORE_RQ = 5'b00001, // store request
L15_ATOMIC_RQ = 5'b00110, // atomic op
//L15_CAS1_RQ = 5'b00010, // compare and swap1 packet (OpenSparc atomics)
//L15_CAS2_RQ = 5'b00011, // compare and swap2 packet (OpenSparc atomics)
//L15_SWAP_RQ = 5'b00110, // swap packet (OpenSparc atomics)
L15_STRLOAD_RQ = 5'b00100, // unused
L15_STRST_RQ = 5'b00101, // unused
L15_STQ_RQ = 5'b00111, // unused
L15_INT_RQ = 5'b01001, // interrupt request
L15_FWD_RQ = 5'b01101, // unused
L15_FWD_RPY = 5'b01110, // unused
L15_RSVD_RQ = 5'b11111 // unused
} l15_reqtypes_t;
// from l1.5 (only marked subset is used)
typedef enum logic [3:0] {LOAD_RET = 4'b0000, // load packet
// INV_RET = 4'b0011, // invalidate packet, not unique...
ST_ACK = 4'b0100, // store ack packet
//AT_ACK = 4'b0011, // unused, not unique...
INT_RET = 4'b0111, // interrupt packet
TEST_RET = 4'b0101, // unused
FP_RET = 4'b1000, // unused
IFILL_RET = 4'b0001, // instruction fill packet
EVICT_REQ = 4'b0011, // eviction request
ERR_RET = 4'b1100, // unused
STRLOAD_RET = 4'b0010, // unused
STRST_ACK = 4'b0110, // unused
FWD_RQ_RET = 4'b1010, // unused
FWD_RPY_RET = 4'b1011, // unused
RSVD_RET = 4'b1111, // unused
CPX_RESTYPE_ATOMIC_RES = 4'b1110 // custom type for atomic responses
typedef enum logic [3:0] {
L15_LOAD_RET = 4'b0000, // load packet
// L15_INV_RET = 4'b0011, // invalidate packet, not unique...
L15_ST_ACK = 4'b0100, // store ack packet
//L15_AT_ACK = 4'b0011, // unused, not unique...
L15_INT_RET = 4'b0111, // interrupt packet
L15_TEST_RET = 4'b0101, // unused
L15_FP_RET = 4'b1000, // unused
L15_IFILL_RET = 4'b0001, // instruction fill packet
L15_EVICT_REQ = 4'b0011, // eviction request
L15_ERR_RET = 4'b1100, // unused
L15_STRLOAD_RET = 4'b0010, // unused
L15_STRST_ACK = 4'b0110, // unused
L15_FWD_RQ_RET = 4'b1010, // unused
L15_FWD_RPY_RET = 4'b1011, // unused
L15_RSVD_RET = 4'b1111, // unused
L15_CPX_RESTYPE_ATOMIC_RES = 4'b1110 // custom type for atomic responses
} l15_rtrntypes_t;

327
src/axi_adapter2.sv Normal file
View file

@ -0,0 +1,327 @@
/* Copyright 2018 ETH Zurich and University of Bologna.
* Copyright and related rights are licensed under the Solderpad Hardware
* License, Version 0.51 (the License); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
* or agreed to in writing, software, hardware and materials distributed under
* this License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* File: axi_adapter.sv
* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
* Date: 1.8.2018
*
* Description: Manages communication with the AXI Bus. Note that if you intend
* to use read bursts with BLEN>0, you have to either use the same ID for all reads
* to ensure ordering of the transactions, or you have to make sure that only one read
* is in flight. otherwise, the read response deserialization mechanism may not work
* correctly due to axi beat interleaving.
*/
import std_cache_pkg::*;
module axi_adapter2 #(
parameter int unsigned DATA_WORDS = 4, // data width in dwords, this is also the maximum burst length, must be >=2
parameter int unsigned AXI_ID_WIDTH = 10,
parameter int unsigned CRITICAL_WORD_FIRST = 0 // this must be supported by the AXI subsystem, note that the data will be shifted by the word offset when this is enabled
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// read channel
// request
input logic rd_req_i,
output logic rd_gnt_o,
input logic [63:0] rd_addr_i,
input logic [$clog2(DATA_WORDS)-1:0] rd_blen_i, // axi convention: LEN-1
input logic [1:0] rd_size_i,
input logic [AXI_ID_WIDTH-1:0] rd_id_i, // use same ID for reads, or make sure you only have one outstanding read tx
// read response
input logic rd_rdy_i,
output logic rd_valid_o,
output logic [DATA_WORDS-1:0][63:0] rd_data_o,
output logic [AXI_ID_WIDTH-1:0] rd_id_o,
// can be used to determine critical word
output logic [63:0] rd_word_o,
output logic rd_word_valid_o,
output logic rd_word_cnt_o,
// write channel
input logic wr_req_i,
output logic wr_gnt_o,
input logic [63:0] wr_addr_i,
input logic [DATA_WORDS-1:0][63:0] wr_data_i,
input logic [DATA_WORDS-1:0][7:0] wr_be_i,
input logic [$clog2(DATA_WORDS)-1:0] wr_blen_i, // axi convention: LEN-1
input logic [1:0] wr_size_i,
input logic [AXI_ID_WIDTH-1:0] wr_id_i,
// write response
input logic wr_rdy_i,
output logic wr_valid_o,
output logic [AXI_ID_WIDTH-1:0] wr_id_o,
// AXI port
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
);
localparam ADDR_INDEX = ($clog2(DATA_WORDS) > 0) ? $clog2(DATA_WORDS) : 1;
///////////////////////////////////////////////////////
// write channel
///////////////////////////////////////////////////////
enum logic [3:0] {
IDLE, WAIT_AW_READY, WAIT_LAST_W_READY, WAIT_LAST_W_READY_AW_READY, WAIT_AW_READY_BURST
} wr_state_q, wr_state_d;
// AXI tx counter
logic [ADDR_INDEX-1:0] wr_cnt_d, wr_cnt_q;
logic wr_single_req, wr_cnt_done, wr_cnt_clr, wr_cnt_en;
assign wr_single_req = (wr_blen_i == 0);
// address
assign axi_req_o.aw.burst = (wr_single_req) ? 2'b00 : 2'b01; // fixed size for single request and incremental transfer for everything else
assign axi_req_o.aw.addr = wr_addr_i;
assign axi_req_o.aw.size = wr_size_i;
assign axi_req_o.aw.len = wr_blen_i;
assign axi_req_o.aw.id = wr_id_i;
assign axi_req_o.aw.prot = 3'b0;
assign axi_req_o.aw.region = 4'b0;
assign axi_req_o.aw.lock = 1'b0;
assign axi_req_o.aw.cache = 4'b0;
assign axi_req_o.aw.qos = 4'b0;
assign axi_req_o.aw.atop = '0; // currently not used
// data
assign axi_req_o.w.data = wr_data_i[wr_cnt_q];
assign axi_req_o.w.strb = wr_be_i[wr_cnt_q];
assign axi_req_o.w.last = wr_cnt_done;
// response
assign axi_req_o.b_ready = wr_rdy_i;
assign wr_valid_o = axi_resp_i.b_valid;
assign wr_id_o = axi_resp_i.b.id;
// tx counter
assign wr_cnt_done = (wr_cnt_q == wr_blen_i);
assign wr_cnt_d = (wr_cnt_clr) ? '0 :
(wr_cnt_en) ? wr_cnt_q+1 :
wr_cnt_q;
always_comb begin : p_axi_write_fsm
// default
wr_state_d = wr_state_q;
axi_req_o.aw_valid = 1'b0;
axi_req_o.w_valid = 1'b0;
wr_gnt_o = 1'b0;
wr_cnt_en = 1'b0;
wr_cnt_clr = 1'b0;
case (wr_state_q)
///////////////////////////////////
IDLE: begin
// we have an incoming request
if (wr_req_i) begin
// is this a read or write?
axi_req_o.aw_valid = 1'b1;
axi_req_o.w_valid = 1'b1;
// its a single write
if (wr_single_req) begin
wr_cnt_clr = 1'b1;
// single req can be granted here
wr_gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
2'b01: wr_state_d = WAIT_AW_READY;
2'b10: wr_state_d = WAIT_LAST_W_READY;
default: wr_state_d = IDLE;
endcase
// its a request for the whole cache line
end else begin
wr_cnt_en = axi_resp_i.w_ready;
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
2'b11: wr_state_d = WAIT_LAST_W_READY;
2'b01: wr_state_d = WAIT_LAST_W_READY_AW_READY;
2'b10: wr_state_d = WAIT_LAST_W_READY;
default:;
endcase
end
end
end
///////////////////////////////////
// ~> from single write
WAIT_AW_READY: begin
axi_req_o.aw_valid = 1'b1;
if (axi_resp_i.aw_ready) begin
wr_state_d = IDLE;
wr_gnt_o = 1'b1;
end
end
///////////////////////////////////
// ~> we need to wait for an aw_ready and there is at least one outstanding write
WAIT_LAST_W_READY_AW_READY: begin
axi_req_o.w_valid = 1'b1;
axi_req_o.aw_valid = 1'b1;
// we got an aw_ready
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
// we got an aw ready
2'b01: begin
// are there any outstanding transactions?
if (wr_cnt_done) begin
wr_state_d = WAIT_AW_READY_BURST;
wr_cnt_clr = 1'b1;
end else begin
// yes, so reduce the count and stay here
wr_cnt_en = 1'b1;
end
end
2'b10: wr_state_d = WAIT_LAST_W_READY;
2'b11: begin
// we are finished
if (wr_cnt_done) begin
wr_state_d = IDLE;
wr_gnt_o = 1'b1;
wr_cnt_clr = 1'b1;
// there are outstanding transactions
end else begin
wr_state_d = WAIT_LAST_W_READY;
wr_cnt_en = 1'b1;
end
end
default:;
endcase
end
///////////////////////////////////
// ~> all data has already been sent, we are only waiting for the aw_ready
WAIT_AW_READY_BURST: begin
axi_req_o.aw_valid = 1'b1;
if (axi_resp_i.aw_ready) begin
wr_state_d = IDLE;
wr_gnt_o = 1'b1;
end
end
///////////////////////////////////
// ~> from write, there is an outstanding write
WAIT_LAST_W_READY: begin
axi_req_o.w_valid = 1'b1;
// this is the last write
if (wr_cnt_done) begin
if (axi_resp_i.w_ready) begin
wr_state_d = IDLE;
wr_cnt_clr = 1'b1;
wr_gnt_o = 1'b1;
end
end else if (axi_resp_i.w_ready) begin
wr_cnt_en = 1'b1;
end
end
///////////////////////////////////
default: begin
wr_state_d = IDLE;
end
endcase
end
///////////////////////////////////////////////////////
// read channel
///////////////////////////////////////////////////////
// AXI tx counter
logic [ADDR_INDEX-1:0] rd_cnt_d, rd_cnt_q;
logic rd_single_req, rd_cnt_clr, rd_cnt_en;
logic [DATA_WORDS-1:0][63:0] rd_data_d, rd_data_q;
logic rd_valid_d, rd_valid_q;
logic [AXI_ID_WIDTH-1:0] rd_id_d, rd_id_q;
assign rd_single_req = (rd_blen_i == 0);
// address
// in case of a single request or wrapping transfer we can simply begin at the address, if we want to request a cache-line
// with an incremental transfer we need to output the corresponding base address of the cache line
assign axi_req_o.ar.burst = (rd_single_req) ? 2'b00 :
(CRITICAL_WORD_FIRST) ? 2'b10 :
2'b01; // wrapping transfer in case of a critical word first strategy
assign axi_req_o.ar.addr = rd_addr_i;
assign axi_req_o.ar.size = rd_size_i;
assign axi_req_o.ar.len = rd_blen_i;
assign axi_req_o.ar.id = rd_id_i;
assign axi_req_o.ar.prot = 3'b0;
assign axi_req_o.ar.region = 4'b0;
assign axi_req_o.ar.lock = 1'b0;
assign axi_req_o.ar.cache = 4'b0;
assign axi_req_o.ar.qos = 4'b0;
// make the read request
assign axi_req_o.ar_valid = rd_req_i;
assign rd_gnt_o = rd_req_i & axi_resp_i.ar_ready;
// return path
// we are always ready
assign axi_req_o.r_ready = rd_rdy_i;
assign rd_cnt_en = axi_resp_i.r_valid;
assign rd_cnt_clr = axi_resp_i.r.last;
assign rd_valid_d = axi_resp_i.r_valid & axi_resp_i.r.last;
assign rd_valid_o = rd_valid_q;
assign rd_id_d = axi_resp_i.r.id;
assign rd_id_o = rd_id_q;
assign rd_data_o = rd_data_q;
// used to determine critical word
assign rd_word_o = axi_resp_i.r.data;
assign rd_word_valid_o = axi_resp_i.r_valid;
assign rd_word_cnt_o = rd_cnt_q;
// tx counter
assign rd_cnt_d = (rd_cnt_clr) ? '0 :
(rd_cnt_en) ? rd_cnt_q+1 :
rd_cnt_q;
generate
for(genvar k=0; k<DATA_WORDS; k++) begin : g_rd_data
assign rd_data_d[k] = (rd_cnt_q==k && rd_cnt_en) ? axi_resp_i.r.data : rd_data_q[k];
end
endgenerate
// ----------------
// Registers
// ----------------
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
// start in flushing state and initialize the memory
wr_state_q <= IDLE;
wr_cnt_q <= '0;
rd_cnt_q <= '0;
rd_data_q <= '0;
rd_valid_q <= '0;
rd_id_q <= '0;
end else begin
wr_state_q <= wr_state_d;
wr_cnt_q <= wr_cnt_d;
rd_cnt_q <= rd_cnt_d;
rd_data_q <= rd_data_d;
rd_valid_q <= rd_valid_d;
rd_id_q <= rd_id_d;
end
end
// ----------------
// Assertions
// ----------------
//pragma translate_off
`ifndef VERILATOR
initial begin
assert (DATA_WORDS >= 1) else
$fatal(1,"[axi adapter] DATA_WORDS must be >= 1");
end
`endif
//pragma translate_on
endmodule // axi_adapter2

View file

@ -27,271 +27,309 @@ 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,
input logic rst_ni,
// I$
input logic icache_en_i, // enable icache (or bypass e.g: in debug mode)
input logic icache_flush_i, // flush the icache, flush and kill have to be asserted together
output logic icache_miss_o, // to performance counter
// address translation requests
input icache_areq_i_t icache_areq_i, // to/from frontend
output icache_areq_o_t icache_areq_o,
// data requests
input icache_dreq_i_t icache_dreq_i, // to/from frontend
output icache_dreq_o_t icache_dreq_o,
// D$
// Cache management
input logic dcache_enable_i, // from CSR
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,
// Request ports
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
parameter int unsigned AXI_ID_WIDTH = 10,
`endif
parameter logic [63:0] CACHE_START_ADDR = 64'h4000_0000
) (
input logic clk_i,
input logic rst_ni,
// I$
input logic icache_en_i, // enable icache (or bypass e.g: in debug mode)
input logic icache_flush_i, // flush the icache, flush and kill have to be asserted together
output logic icache_miss_o, // to performance counter
// address translation requests
input icache_areq_i_t icache_areq_i, // to/from frontend
output icache_areq_o_t icache_areq_o,
// data requests
input icache_dreq_i_t icache_dreq_i, // to/from frontend
output icache_dreq_o_t icache_dreq_o,
// D$
// Cache management
input logic dcache_enable_i, // from CSR
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,
// Request ports
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
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
// memory side
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
`else
// L15 (memory side)
output l15_req_t l15_req_o,
input l15_rtrn_t l15_rtrn_i
// L15 (memory side)
output l15_req_t l15_req_o,
input l15_rtrn_t l15_rtrn_i
`endif
// TODO: interrupt interface
// TODO: interrupt interface
);
logic icache_adapter_data_req, adapter_icache_data_ack, adapter_icache_rtrn_vld;
serpent_cache_pkg::icache_req_t icache_adapter;
serpent_cache_pkg::icache_rtrn_t adapter_icache;
logic icache_adapter_data_req, adapter_icache_data_ack, adapter_icache_rtrn_vld;
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;
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 ),
// used for local plumbing in this case
l15_req_t l15_req;
l15_rtrn_t l15_rtrn;
`endif
.NC_ADDR_BEGIN ( CACHE_START_ADDR )
) i_serpent_icache (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( icache_flush_i ),
.en_i ( icache_en_i ),
.miss_o ( icache_miss_o ),
.areq_i ( icache_areq_i ),
.areq_o ( icache_areq_o ),
.dreq_i ( icache_dreq_i ),
.dreq_o ( icache_dreq_o ),
.mem_rtrn_vld_i ( adapter_icache_rtrn_vld ),
.mem_rtrn_i ( adapter_icache ),
.mem_data_req_o ( icache_adapter_data_req ),
.mem_data_ack_i ( adapter_icache_data_ack ),
.mem_data_o ( icache_adapter )
);
// 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 #(
serpent_icache #(
`ifdef AXI64_CACHE_PORTS
.NC_ADDR_GE_LT ( 0 ), // std config is for openpiton, where the upper memory region is NC
.AXI64BIT_COMPLIANT ( 1'b1 ),
.NC_ADDR_GE_LT ( 0 ),
`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 )
);
.NC_ADDR_BEGIN ( CACHE_START_ADDR )
) i_serpent_icache (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( icache_flush_i ),
.en_i ( icache_en_i ),
.miss_o ( icache_miss_o ),
.areq_i ( icache_areq_i ),
.areq_o ( icache_areq_o ),
.dreq_i ( icache_dreq_i ),
.dreq_o ( icache_dreq_o ),
.mem_rtrn_vld_i ( adapter_icache_rtrn_vld ),
.mem_rtrn_i ( adapter_icache ),
.mem_data_req_o ( icache_adapter_data_req ),
.mem_data_ack_i ( adapter_icache_data_ack ),
.mem_data_o ( icache_adapter )
);
// arbiter/adapter
serpent_l15_adapter #(
) i_adapter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.icache_data_req_i ( icache_adapter_data_req ),
.icache_data_ack_o ( adapter_icache_data_ack ),
.icache_data_i ( icache_adapter ),
.icache_rtrn_vld_o ( adapter_icache_rtrn_vld ),
.icache_rtrn_o ( adapter_icache ),
.dcache_data_req_i ( dcache_adapter_data_req ),
.dcache_data_ack_o ( adapter_dcache_data_ack ),
.dcache_data_i ( dcache_adapter ),
.dcache_rtrn_vld_o ( adapter_dcache_rtrn_vld ),
.dcache_rtrn_o ( adapter_dcache ),
.l15_req_o ( l15_req_o ),
.l15_rtrn_i ( l15_port_i )
);
// 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 )
);
// arbiter/adapter
serpent_l15_adapter #(
`ifdef AXI64_CACHE_PORTS
.SWAP_ENDIANESS(0)
`endif
) i_adapter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.icache_data_req_i ( icache_adapter_data_req ),
.icache_data_ack_o ( adapter_icache_data_ack ),
.icache_data_i ( icache_adapter ),
.icache_rtrn_vld_o ( adapter_icache_rtrn_vld ),
.icache_rtrn_o ( adapter_icache ),
.dcache_data_req_i ( dcache_adapter_data_req ),
.dcache_data_ack_o ( adapter_dcache_data_ack ),
.dcache_data_i ( dcache_adapter ),
.dcache_rtrn_vld_o ( adapter_dcache_rtrn_vld ),
.dcache_rtrn_o ( adapter_dcache ),
`ifdef AXI64_CACHE_PORTS
.l15_req_o ( l15_req ),
.l15_rtrn_i ( l15_rtrn )
`else
.l15_req_o ( l15_req_o ),
.l15_rtrn_i ( l15_rtrn_i )
`endif
);
// different memory plumbing
// note that this is a workaround that is mainly used to verify L15 adapter
// and serpent cache system with the standard AXI-based testharness
`ifdef AXI64_CACHE_PORTS
// cannot handle invalidations ATM
assign adapter_icache.rtype = ICACHE_IFILL_ACK;
assign adapter_icache.inv = '0;
assign adapter_icache.nc = icache_adapter.nc;
assign adapter_icache.tid = '0;
assign adapter_icache.f4b = icache_adapter.nc;
// this is static at the moment due to the openpiton config.
// TODO: make this parameterizable in the future
localparam AXI_NUM_WORDS = 4;
std_cache_pkg::req_t icache_axi_req_type;
assign icache_axi_req_type = ( icache_adapter.nc ) ? std_cache_pkg::SINGLE_REQ : std_cache_pkg::CACHE_LINE_REQ;
logic axi_rd_req, axi_rd_gnt;
logic [63:0] axi_rd_addr, axi_wr_addr;
logic [$clog2(AXI_NUM_WORDS)-1:0] axi_rd_blen, axi_wr_blen;
logic [1:0] axi_rd_size, axi_wr_size;
logic [AXI_ID_WIDTH-1:0] axi_rd_id_in, axi_wr_id_in, axi_rd_id_out, axi_wr_id_out;
logic axi_rd_valid;
logic [AXI_NUM_WORDS-1:0][63:0] axi_rd_data, axi_wr_data;
logic [63:0] axi_rd_word;
logic [AXI_NUM_WORDS-1:0][7:0] axi_wr_be;
logic axi_rd_word_valid, axi_rd_word_cnt, axi_wr_req, axi_wr_gnt;
logic axi_wr_valid, axi_rd_rdy, axi_wr_rdy;
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 ),
.type_i ( icache_axi_req_type ),
.gnt_o ( adapter_icache_data_ack ),
.gnt_id_o ( ),
.addr_i ( icache_adapter.paddr ),
.we_i ( '0 ),
.wdata_i ( '0 ),
.be_i ( '0 ),
.size_i ( 2'b11 ),// always request in multiples of 64bit
.id_i ( '0 ),
.valid_o ( adapter_icache_rtrn_vld ),
.rdata_o ( adapter_icache.data ),
.id_o ( ),
.critical_word_o ( ),
.critical_word_valid_o( ),
.axi_req_o ( axi_data_o ),
.axi_resp_i ( axi_data_i )
);
logic ifill;
logic [serpent_cache_pkg::L15_TID_WIDTH+2-1:0] id_tmp;
logic rd_pending_d, rd_pending_q;
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;
// request side
assign ifill = (l15_req.l15_rqtype==serpent_cache_pkg::L15_IMISS_RQ);
// 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;
assign axi_rd_req = l15_req.l15_val && (l15_req.l15_rqtype==serpent_cache_pkg::L15_LOAD_RQ | ifill) && !rd_pending_q;
assign axi_wr_req = l15_req.l15_val && (l15_req.l15_rqtype==serpent_cache_pkg::L15_STORE_RQ);
// 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;
assign axi_rd_addr = l15_req.l15_address;
assign axi_wr_addr = axi_rd_addr;
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_req_o ( ),
.axi_resp_i ( '0 )
);
// the axi interconnect does not correctly handle the ordering of read responses with same IDs that
// go to different slaves. workaround: only allow for one outstanding TX
assign rd_pending_d = (axi_rd_valid) ? '0 : axi_rd_gnt;
assign axi_rd_id_in = {l15_req.l15_threadid, ifill, l15_req.l15_nc};
assign axi_wr_id_in = axi_rd_id_in;
assign axi_rd_size = (ifill) ? 2'b11 : l15_req.l15_size[1:0];// always request 64bit (fix this... at some point)
assign axi_wr_size = l15_req.l15_size[1:0];
assign axi_rd_blen = (l15_req.l15_size[2]) ? ((ifill) ? ariane_pkg::ICACHE_LINE_WIDTH/64-1 :
ariane_pkg::DCACHE_LINE_WIDTH/64-1) : '0;
assign axi_wr_blen = '0;// single word writes
assign axi_wr_data = l15_req.l15_data;
assign axi_wr_be = (axi_wr_req) ? serpent_cache_pkg::toByteEnable8(axi_wr_addr[2:0], axi_wr_size) : '0;
// return path
always_comb begin : p_axi_rtrn
// default
l15_rtrn = '0;
// from request path
l15_rtrn.l15_ack = axi_rd_gnt | axi_wr_gnt;
l15_rtrn.l15_header_ack = axi_rd_gnt | axi_wr_gnt;
// we are always ready to consume packets unconditionally,
// but in case of returning reads, we have to stall the write response
axi_rd_rdy = 1'b1;
axi_wr_rdy = ~axi_rd_valid;// this vld signal comes directly from a register
// unconditionally consume packets
l15_rtrn.l15_val = axi_rd_valid | axi_wr_valid;
// encode packet type
id_tmp = (axi_rd_valid) ? axi_rd_id_out : axi_wr_id_out;
l15_rtrn.l15_returntype = (axi_rd_valid && id_tmp[1]) ? L15_IFILL_RET :
(axi_rd_valid) ? L15_LOAD_RET :
L15_ST_ACK;
// decode id and set flags accordingly
l15_rtrn.l15_noncacheable = id_tmp[0];
l15_rtrn.l15_threadid = id_tmp>>2;
// 4B non-cacheable ifill
l15_rtrn.l15_f4b = id_tmp[0] & id_tmp[1] & axi_rd_valid;
l15_rtrn.l15_data_0 = axi_rd_data[0];
l15_rtrn.l15_data_1 = axi_rd_data[1];
l15_rtrn.l15_data_2 = axi_rd_data[2];
l15_rtrn.l15_data_3 = axi_rd_data[3];
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if(~rst_ni) begin
rd_pending_q <= '0;
end else begin
rd_pending_q <= rd_pending_d;
end
end
axi_adapter2 #(
.DATA_WORDS ( AXI_NUM_WORDS ),
.AXI_ID_WIDTH ( AXI_ID_WIDTH )
) i_axi_adapter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.rd_req_i ( axi_rd_req ),
.rd_gnt_o ( axi_rd_gnt ),
.rd_addr_i ( axi_rd_addr ),
.rd_blen_i ( axi_rd_blen ),
.rd_size_i ( axi_rd_size ),
.rd_id_i ( axi_rd_id_in ),
.rd_rdy_i ( axi_rd_rdy ),
.rd_valid_o ( axi_rd_valid ),
.rd_data_o ( axi_rd_data ),
.rd_id_o ( axi_rd_id_out ),
.rd_word_o ( ),
.rd_word_valid_o ( ),
.rd_word_cnt_o ( ),
.wr_req_i ( axi_wr_req ),
.wr_gnt_o ( axi_wr_gnt ),
.wr_addr_i ( axi_wr_addr ),
.wr_data_i ( axi_wr_data ),
.wr_be_i ( axi_wr_be ),
.wr_blen_i ( axi_wr_blen ),
.wr_size_i ( axi_wr_size ),
.wr_id_i ( axi_wr_id_in ),
.wr_rdy_i ( axi_wr_rdy ),
.wr_valid_o ( axi_wr_valid ),
.wr_id_o ( axi_wr_id_out ),
.axi_req_o ( axi_req_o ),
.axi_resp_i ( axi_resp_i )
);
`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");
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
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_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);
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
for(genvar j=0; j<2; j++) begin : g_assertion
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
`endif
//pragma translate_on

View file

@ -188,6 +188,7 @@ assign miss_req_o = (|dirty) && (tx_cnt_q < DCACHE_MAX_TX);
// 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]);
// replicate transfers shorter than a dword
assign miss_wdata_o = repData64(wbuffer_q[dirty_ptr].data,
bdirty_off,
miss_size_o[1:0]);

View file

@ -20,7 +20,7 @@
//
// 1) refills always have the size of one cache line, except for accesses to the I/O region, which is mapped
// to the top half of the physical address space (bit 39 = 1). the data width of the interface has the width
// of one cache line, and hence the ifills can be transferred in a single cycle. note that the ifills must be
// of one cache line, and hence the ifills can be transferred in a single cycle. note that the ifills must be
// consumed unconditionally.
//
// 2) instruction fetches are always assumed to be aligned to 32bit (lower 2 bits are ignored)
@ -31,7 +31,7 @@
import ariane_pkg::*;
import serpent_cache_pkg::*;
module serpent_icache #(
module serpent_icache #(
parameter bit AXI64BIT_COMPLIANT = 1'b0, // set this to 1 when using in conjunction with 64bit AXI bus adapter
parameter NC_ADDR_BEGIN = 40'h8000000000, // start address of noncacheable I/O region
parameter bit NC_ADDR_GE_LT = 1'b1 // determines how the physical address is compared with NC_ADDR_BEGIN
@ -57,18 +57,18 @@ module serpent_icache #(
input logic mem_data_ack_i,
output icache_req_t mem_data_o
);
// signals
logic cache_en_d, cache_en_q; // cache is enabled
logic [63:0] vaddr_d, vaddr_q;
logic cache_en_d, cache_en_q; // cache is enabled
logic [63:0] vaddr_d, vaddr_q;
logic paddr_is_nc; // asserted if physical address is non-cacheable
logic [ICACHE_SET_ASSOC-1:0] cl_hit; // hit from tag compare
logic cache_rden; // triggers cache lookup
logic cache_wren; // triggers write to cacheline
logic cmp_en_d, cmp_en_q; // enable tag comparison in next cycle. used to cut long path due to NC signal.
logic cmp_en_d, cmp_en_q; // enable tag comparison in next cycle. used to cut long path due to NC signal.
logic flush_d, flush_q; // used to register and signal pending flushes
// replacement strategy
// replacement strategy
logic update_lfsr; // shift the LFSR
logic [$clog2(ICACHE_SET_ASSOC)-1:0] inv_way; // first non-valid encountered
logic [$clog2(ICACHE_SET_ASSOC)-1:0] rnd_way; // random index for replacement
@ -76,8 +76,8 @@ module serpent_icache #(
logic [ICACHE_SET_ASSOC-1:0] repl_way_oh_d, repl_way_oh_q; // way to replace (onehot)
logic all_ways_valid; // we need to switch repl strategy since all are valid
// invalidations / flushing
logic inv_en; // incoming invalidations
// invalidations / flushing
logic inv_en; // incoming invalidations
logic flush_en, flush_done; // used to flush cache entries
logic [ICACHE_CL_IDX_WIDTH-1:0] flush_cnt_d, flush_cnt_q; // used to flush cache entries
@ -94,32 +94,32 @@ module serpent_icache #(
logic vld_we; // valid bits write enable
logic [ICACHE_SET_ASSOC-1:0] vld_wdata; // valid bits to write
logic [ICACHE_SET_ASSOC-1:0] vld_rdata; // valid bits coming from valid regs
logic [ICACHE_CL_IDX_WIDTH-1:0] vld_addr; // valid bit
logic [ICACHE_CL_IDX_WIDTH-1:0] vld_addr; // valid bit
// cpmtroller FSM
typedef enum logic[2:0] {FLUSH, IDLE, READ, MISS, TLB_MISS, KILL_ATRANS, KILL_MISS} state_t;
state_t state_d, state_q;
///////////////////////////////////////////////////////
// address -> cl_index mapping, interface plumbing
// address -> cl_index mapping, interface plumbing
///////////////////////////////////////////////////////
// extract tag from physical address, check if NC
assign cl_tag_d = (areq_i.fetch_valid) ? areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH] : cl_tag_q;
assign cl_tag_d = (areq_i.fetch_valid) ? areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH] : cl_tag_q;
// noncacheable if request goes to I/O space, or if cache is disabled
generate
generate
if (NC_ADDR_GE_LT) begin : g_nc_addr_high
assign paddr_is_nc = (cl_tag_d >= (NC_ADDR_BEGIN>>ICACHE_INDEX_WIDTH)) | ~cache_en_q;
end
if (~NC_ADDR_GE_LT) begin : g_nc_addr_low
assign paddr_is_nc = (cl_tag_d < (NC_ADDR_BEGIN>>ICACHE_INDEX_WIDTH)) | ~cache_en_q;
end
endgenerate
endgenerate
// pass exception through
assign dreq_o.ex = areq_i.fetch_exception;
// latch this in case we have to stall later on
// make sure this is 32bit aligned
assign vaddr_d = (dreq_o.ready & dreq_i.req) ? dreq_i.vaddr : vaddr_q;
@ -127,7 +127,7 @@ module serpent_icache #(
// split virtual address into index and offset to address cache arrays
assign cl_index = vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH];
generate
if(AXI64BIT_COMPLIANT)begin
// if we generate a noncacheable access, the word will be at offset 0 or 4 in the cl coming from memory
@ -148,11 +148,11 @@ module serpent_icache #(
assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:2], 2'b0} : // align to 32bit
{cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH], {ICACHE_OFFSET_WIDTH{1'b0}}}; // align to cl
end
endgenerate
endgenerate
// currently we can only have one outstanding tx here
assign mem_data_o.tid = '0;
assign mem_data_o.nc = paddr_is_nc;
// way that is being replaced
assign mem_data_o.way = repl_way;
@ -172,8 +172,8 @@ module serpent_icache #(
cache_wren = 1'b0;
inv_en = 1'b0;
flush_d = flush_q | flush_i; // register incoming flush
// interfaces
// interfaces
dreq_o.ready = 1'b0;
areq_o.fetch_req = 1'b0;
dreq_o.valid = 1'b0;
@ -182,15 +182,15 @@ module serpent_icache #(
miss_o = 1'b0;
// handle invalidations unconditionally
// note: invald are mutually exclusive with
// note: invald are mutually exclusive with
// ifills, since both arrive over the same IF
// however, we need to make sure below that we
// do not trigger a cache readout at the same time...
// however, we need to make sure below that we
// do not trigger a cache readout at the same time...
if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_INV_REQ) begin
inv_en = 1'b1;
end
end
unique case (state_q)
unique case (state_q)
//////////////////////////////////
// this clears all valid bits
FLUSH: begin
@ -200,7 +200,7 @@ module serpent_icache #(
flush_d = 1'b0;
// if the cache was not enabled set this
cache_en_d = en_i;
end
end
end
//////////////////////////////////
// wait for an incoming request
@ -212,8 +212,8 @@ module serpent_icache #(
if (flush_d | (en_i & ~cache_en_q)) begin
state_d = FLUSH;
// wait for incoming requests
end else begin
// mem requests are for sure invals here
end else begin
// mem requests are for sure invals here
if (~mem_rtrn_vld_i) begin
dreq_o.ready = 1'b1;
// we have a new request
@ -221,11 +221,11 @@ module serpent_icache #(
cache_rden = 1'b1;
state_d = READ;
end
end
if (dreq_i.kill_s1) begin
end
if (dreq_i.kill_s1) begin
state_d = IDLE;
end
end
end
end
end
//////////////////////////////////
// check whether we have a hit
@ -240,7 +240,7 @@ module serpent_icache #(
cmp_en_d = cache_en_q;
// readout speculatively
cache_rden = cache_en_q;
if (areq_i.fetch_valid) begin
// check if we have to flush
if (flush_d) begin
@ -258,16 +258,16 @@ module serpent_icache #(
if (dreq_i.req) begin
state_d = READ;
end
end
// if a request is being killed at this stage,
// we have to bail out and wait for the address translation to complete
if (dreq_i.kill_s1) begin
end
// if a request is being killed at this stage,
// we have to bail out and wait for the address translation to complete
if (dreq_i.kill_s1) begin
state_d = IDLE;
end
// we have a miss / NC transaction
end else if (dreq_i.kill_s2) begin
// we have a miss / NC transaction
end else if (dreq_i.kill_s2) begin
state_d = IDLE;
end else begin
end else begin
cmp_en_d = 1'b0;
// only count this as a miss if the cache is enabled, and
// the address is cacheable
@ -276,8 +276,8 @@ module serpent_icache #(
if (mem_data_ack_i) begin
miss_o = (~paddr_is_nc);
state_d = MISS;
end
end
end
end
// bail out if this request is being killed (and we missed on the TLB)
end else if (dreq_i.kill_s2 | flush_d) begin
state_d = KILL_ATRANS;
@ -306,18 +306,18 @@ module serpent_icache #(
// but if we got an invalidation, we have to wait another cycle
end else if (~mem_rtrn_vld_i) begin
state_d = READ;
end
end
// bail out if this request is being killed
end else if (dreq_i.kill_s2 | flush_d) begin
state_d = KILL_ATRANS;
end
end
end
//////////////////////////////////
// wait until the memory transaction
// returns. do not write to memory
// if the nc bit is set.
MISS: begin
// note: this is mutually exclusive with ICACHE_INV_REQ,
// note: this is mutually exclusive with ICACHE_INV_REQ,
// so we do not have to check for invals here
if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_IFILL_ACK) begin
state_d = IDLE;
@ -326,30 +326,30 @@ module serpent_icache #(
dreq_o.valid = 1'b1;
// only write to cache if this address is cacheable
cache_wren = ~paddr_is_nc;
end
end
// bail out if this request is being killed
end else if (dreq_i.kill_s2 | flush_d) begin
state_d = KILL_MISS;
end
end
end
//////////////////////////////////
// killed address translation,
// wait until paddr is valid, and go
// wait until paddr is valid, and go
// back to idle
KILL_ATRANS: begin
areq_o.fetch_req = '1;
if (areq_i.fetch_valid) begin
state_d = IDLE;
end
end
end
//////////////////////////////////
// killed miss,
// wait until memory responds and
// wait until memory responds and
// go back to idle
KILL_MISS: begin
if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_IFILL_ACK) begin
state_d = IDLE;
end
end
end
default: begin
// we should never get here
@ -363,32 +363,32 @@ module serpent_icache #(
///////////////////////////////////////////////////////
// note: it cannot happen that we get an invalidation + a cl replacement
// in the same cycle as these requests arrive via the same interface
// flushes take precedence over invalidations (it is ok if we ignore
// in the same cycle as these requests arrive via the same interface
// flushes take precedence over invalidations (it is ok if we ignore
// the inval since the cache is cleared anyway)
assign flush_cnt_d = (flush_done) ? '0 :
(flush_en) ? flush_cnt_q + 1 :
flush_cnt_q;
assign flush_done = (flush_cnt_q==(ICACHE_NUM_WORDS-1));
flush_cnt_q;
assign flush_done = (flush_cnt_q==(ICACHE_NUM_WORDS-1));
// invalidation/clearing address
// flushing takes precedence over invals
assign vld_addr = (flush_en) ? flush_cnt_q :
(inv_en) ? mem_rtrn_i.inv.idx[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH] :
cl_index;
assign vld_req = (flush_en | cache_rden) ? '1 :
(mem_rtrn_i.inv.all & inv_en) ? '1 :
cl_index;
assign vld_req = (flush_en | cache_rden) ? '1 :
(mem_rtrn_i.inv.all & inv_en) ? '1 :
(mem_rtrn_i.inv.vld & inv_en) ? icache_way_bin2oh(mem_rtrn_i.inv.way) :
repl_way_oh_q;
assign vld_wdata = (cache_wren) ? '1 : '0;
assign vld_we = (cache_wren | inv_en | flush_en);
// assign vld_req = (vld_we | cache_rden);
assign vld_we = (cache_wren | inv_en | flush_en);
// assign vld_req = (vld_we | cache_rden);
// chose random replacement if all are valid
assign update_lfsr = cache_wren & all_ways_valid;
@ -396,11 +396,11 @@ module serpent_icache #(
assign repl_way_oh_d = (cmp_en_q) ? icache_way_bin2oh(repl_way) : repl_way_oh_q;
// enable signals for memory arrays
assign cl_req = (cache_rden) ? '1 :
assign cl_req = (cache_rden) ? '1 :
(cache_wren) ? repl_way_oh_q :
'0;
assign cl_we = cache_wren;
// find invalid cache line
lzc #(
@ -419,7 +419,7 @@ module serpent_icache #(
.rst_ni ( rst_ni ),
.en_i ( update_lfsr ),
.refill_way_oh ( ),
.refill_way_bin ( rnd_way )
.refill_way_bin ( rnd_way )
);
@ -429,7 +429,7 @@ module serpent_icache #(
logic [$clog2(ICACHE_SET_ASSOC)-1:0] hit_idx;
generate
generate
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];
assign cl_sel[i] = cl_rdata[i][{cl_offset_q,3'b0} +: FETCH_WIDTH];
@ -444,9 +444,9 @@ module serpent_icache #(
.empty_o ( )
);
assign dreq_o.data = ( cmp_en_q ) ? cl_sel[hit_idx] :
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
///////////////////////////////////////////////////////
@ -467,7 +467,7 @@ module serpent_icache #(
.req_i ( vld_req[i] ),
.we_i ( vld_we ),
.addr_i ( vld_addr ),
// we can always use the saved tag here since it takes a
// we can always use the saved tag here since it takes a
// couple of cycle until we write to the cache upon a miss
.wdata_i ( {vld_wdata[i], cl_tag_q} ),
.be_i ( '1 ),
@ -475,7 +475,7 @@ module serpent_icache #(
);
assign cl_tag_rdata[i] = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH-1:0];
assign vld_rdata[i] = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH];
assign vld_rdata[i] = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH];
// Data RAM
sram #(
@ -525,37 +525,37 @@ module serpent_icache #(
//pragma translate_off
`ifndef VERILATOR
noncacheable0: assert property (
@(posedge clk_i) disable iff (~rst_ni) paddr_is_nc |-> mem_rtrn_vld_i && (mem_rtrn_i.rtype == ICACHE_IFILL_ACK) |-> mem_rtrn_i.nc)
else $fatal("[l1 icache] NC paddr implies nc ifill");
@(posedge clk_i) disable iff (~rst_ni) paddr_is_nc |-> mem_rtrn_vld_i |-> state_q != KILL_MISS |-> mem_rtrn_i.rtype == ICACHE_IFILL_ACK |-> mem_rtrn_i.nc)
else $fatal(1,"[l1 icache] NC paddr implies nc ifill");
noncacheable1: assert property (
@(posedge clk_i) disable iff (~rst_ni) mem_rtrn_vld_i |-> mem_rtrn_i.f4b |-> mem_rtrn_i.nc)
@(posedge clk_i) disable iff (~rst_ni) mem_rtrn_vld_i |-> state_q != KILL_MISS |-> mem_rtrn_i.f4b |-> mem_rtrn_i.nc)
else $fatal(1,"[l1 icache] 4b ifill implies NC");
noncacheable2: assert property (
@(posedge clk_i) disable iff (~rst_ni) mem_rtrn_vld_i |-> mem_rtrn_i.nc |-> mem_rtrn_i.f4b)
else $fatal(1,"[l1 icache] NC implies 4b ifill");
@(posedge clk_i) disable iff (~rst_ni) mem_rtrn_vld_i |-> state_q != KILL_MISS |-> mem_rtrn_i.nc |-> mem_rtrn_i.f4b)
else $fatal(1,"[l1 icache] NC implies 4b ifill");
repl_inval0: assert property (
@(posedge clk_i) disable iff (~rst_ni) cache_wren |-> ~(mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld))
@(posedge clk_i) disable iff (~rst_ni) cache_wren |-> ~(mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld))
else $fatal(1,"[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");
repl_inval1: assert property (
@(posedge clk_i) disable iff (~rst_ni) (mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld) |-> ~cache_wren)
@(posedge clk_i) disable iff (~rst_ni) (mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld) |-> ~cache_wren)
else $fatal(1,"[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");
invalid_state: assert property (
@(posedge clk_i) disable iff (~rst_ni) (state_q inside {FLUSH, IDLE, READ, MISS, TLB_MISS, KILL_ATRANS, KILL_MISS}))
@(posedge clk_i) disable iff (~rst_ni) (state_q inside {FLUSH, IDLE, READ, MISS, TLB_MISS, KILL_ATRANS, KILL_MISS}))
else $fatal(1,"[l1 icache] fsm reached an invalid state");
hot1: assert property (
@(posedge clk_i) disable iff (~rst_ni) (~inv_en) |=> cmp_en_q |-> $onehot0(cl_hit))
@(posedge clk_i) disable iff (~rst_ni) (~inv_en) |=> cmp_en_q |-> $onehot0(cl_hit))
else $fatal(1,"[l1 icache] cl_hit signal must be hot1");
initial begin
// assert wrong parameterizations
assert (ICACHE_INDEX_WIDTH<=12)
else $fatal(1,"[l1 icache] cache index width can be maximum 12bit since VM uses 4kB pages");
assert (ICACHE_INDEX_WIDTH<=12)
else $fatal(1,"[l1 icache] cache index width can be maximum 12bit since VM uses 4kB pages");
end
`endif
//pragma translate_on

View file

@ -56,7 +56,7 @@ import ariane_pkg::*;
import serpent_cache_pkg::*;
module serpent_l15_adapter #(
parameter SWAP_ENDIANESS = 1
)(
input logic clk_i,
input logic rst_ni,
@ -87,14 +87,13 @@ module serpent_l15_adapter #(
// request path
icache_req_t icache_data;
logic icache_data_full, icache_data_empty, icache_data_data, icache_data_push;
logic icache_data_full, icache_data_empty;
dcache_req_t dcache_data;
logic dcache_data_full, dcache_data_empty, dcache_data_data, dcache_data_push;
logic dcache_data_full, dcache_data_empty;
logic [1:0] arb_req;
logic [1:0] arb_ack;
logic [1:0] arb_idx;
logic [1:0] arb_req, arb_ack;
logic arb_idx;
logic header_ack_d, header_ack_q;
@ -119,94 +118,73 @@ l15_rtrn_t rtrn_fifo_data;
// logic [63:0] l15_req_o.l15_data_next_entry; // unused in Ariane (only used for CAS atomic requests)
// logic [L15_TLB_CSM_WIDTH-1:0] l15_req_o.l15_csm_data;
assign icache_data_ack_o = icache_data_req_i & ~icache_data_full;
assign dcache_data_ack_o = dcache_data_req_i & ~dcache_data_full;
// data mux
assign l15_req_o.l15_nc = (arb_idx) ? dcache_data.nc : icache_data.nc;
assign l15_req_o.l15_size = (arb_idx) ? dcache_data.size :
(icache_data.nc) ? 3'b10 : 3'b111;// NC ifills are always 4byte
assign l15_req_o.l15_threadid = (arb_idx) ? dcache_data.tid : icache_data.tid;
assign l15_req_o.l15_prefetch = '0; // unused in openpiton
assign l15_req_o.l15_invalidate_cacheline = '0; // unused by Ariane as L1 has no ECC at the moment
assign l15_req_o.l15_blockstore = '0; // unused in openpiton
assign l15_req_o.l15_blockinitstore = '0; // unused in openpiton
assign l15_req_o.l15_l1rplway = (arb_idx) ? dcache_data.way : icache_data.way;
assign l15_req_o.l15_address = (arb_idx) ? dcache_data.paddr : icache_data.paddr;
assign l15_req_o.l15_data_next_entry = '0; // unused in Ariane (only used for CAS atomic requests)
assign l15_req_o.l15_csm_data = '0; // unused in Ariane (only used for coherence domain restriction features)
assign l15_req_o.l15_amo_op = dcache_data.amo_op;
// openpiton is big endian
generate
if (SWAP_ENDIANESS) assign l15_req_o.l15_data = swendian64(dcache_data.data);
else assign l15_req_o.l15_data = dcache_data.data;
endgenerate
// arbiter
rrarbiter #(
.NUM_REQ(2),
.LOCK_IN(1)
) i_rrarbiter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( '0 ),
.en_i ( l15_rtrn_i.l15_ack ),
.req_i ( arb_req ),
.ack_o ( arb_ack ),
.vld_o ( ),
.idx_o ( arb_idx )
);
// need to deassert valid signal when header is acked
// can move on when packed is acked (need to clear header ack)
assign arb_req = {~dcache_data_empty, ~icache_data_empty};
assign l15_req_o.l15_val = (|arb_req) & ~header_ack_q;
assign header_ack_d = (l15_rtrn_i.l15_ack) ? 1'b0 : (header_ack_q | l15_rtrn_i.l15_header_ack);
assign arb_req = {~dcache_data_empty,
~icache_data_empty};
assign dcache_data_pop = arb_ack[1];
assign icache_data_pop = arb_ack[0];
assign icache_data_ack_o = icache_data_req_i & ~ icache_data_full;
assign dcache_data_ack_o = dcache_data_req_i & ~ dcache_data_full;
// data mux
assign l15_req_o.l15_nc = (arb_idx) ? dcache_data.nc : icache_data.nc;
assign l15_req_o.l15_size = (arb_idx) ? dcache_data.size : 3'b111;// always request full cache line for icache
assign l15_req_o.l15_threadid = (arb_idx) ? dcache_data.tid : icache_data.tid;
assign l15_req_o.l15_invalidate_cacheline = 1'b0; // unused by Ariane as L1 has no ECC at the moment
assign l15_req_o.l15_l1rplway = (arb_idx) ? dcache_data.way : icache_data.way;
assign l15_req_o.l15_address = (arb_idx) ? dcache_data.paddr : icache_data.paddr;
assign l15_req_o.l15_data_next_entry = 1'b0; // unused in Ariane (only used for CAS atomic requests)
assign l15_req_o.l15_csm_data = 1'b0; // unused in Ariane (only used for coherence domain restriction features)
assign l15_req_o.l15_amo_op = dcache_data.amo_op;
// swap endianess and replicate datawords if necessary
always_comb begin : p_datarepl
unique case(dcache_data.size)
3'b000: begin // 1byte
l15_req_o.l15_data = swendian64({dcache_data.data[0],
dcache_data.data[0],
dcache_data.data[0],
dcache_data.data[0],
dcache_data.data[0],
dcache_data.data[0],
dcache_data.data[0],
dcache_data.data[0]});
end
3'b001: begin // 2byte
l15_req_o.l15_data = swendian64({dcache_data.data[1:0],
dcache_data.data[1:0],
dcache_data.data[1:0],
dcache_data.data[1:0]});
end
3'b010: begin // 4byte
l15_req_o.l15_data = swendian64({dcache_data.data[3:0],
dcache_data.data[3:0]});
end
default: begin // 8 byte
l15_req_o.l15_data = swendian64(dcache_data.data);
end
endcase // dcache_data.size
end
// arbiter
// ifills always have priority
always_comb begin : p_arb
arb_idx = '0;
arb_ack = '0;
if(arb_req[0] & l15_rtrn_i.l15_ack) begin
arb_ack[0] = 1'b1;
arb_idx = 0;
end else if (arb_req[1] & l15_rtrn_i.l15_ack) begin
arb_ack[1] = 1'b1;
arb_idx = 1;
end
end // p_arb
// encode packet type
always_comb begin : p_req
l15_req_o.l15_rqtype = LOAD_RQ;
l15_req_o.l15_rqtype = L15_LOAD_RQ;
unique case (arb_idx)
0: begin// icache
l15_req_o.l15_rqtype = IMISS_RQ;
l15_req_o.l15_rqtype = L15_IMISS_RQ;
end
1: begin
unique case (dcache_data.rtype)
DCACHE_STORE_REQ: begin
l15_req_o.l15_rqtype = STORE_RQ;
l15_req_o.l15_rqtype = L15_STORE_RQ;
end
DCACHE_LOAD_REQ: begin
l15_req_o.l15_rqtype = LOAD_RQ;
l15_req_o.l15_rqtype = L15_LOAD_RQ;
end
DCACHE_ATOMIC_REQ: begin
l15_req_o.l15_rqtype = ATOMIC_RQ;
l15_req_o.l15_rqtype = L15_ATOMIC_RQ;
end
// DCACHE_INT_REQ: begin
// //TODO
// //TODO interrupt requests
// end
default: begin
;
@ -219,10 +197,9 @@ always_comb begin : p_req
endcase
end // p_req
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if(~rst_ni) begin
header_ack_q <= 0;
header_ack_q <= '0;
end else begin
header_ack_q <= header_ack_d;
end
@ -242,9 +219,9 @@ fifo_v2 #(
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i ( icache_data_i ),
.push_i ( icache_data_push ),
.push_i ( icache_data_ack_o ),
.data_o ( icache_data ),
.pop_i ( icache_data_pop )
.pop_i ( arb_ack[0] )
);
fifo_v2 #(
@ -260,9 +237,9 @@ fifo_v2 #(
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i ( dcache_data_i ),
.push_i ( dcache_data_push ),
.push_i ( dcache_data_ack_o ),
.data_o ( dcache_data ),
.pop_i ( dcache_data_pop )
.pop_i ( arb_ack[1] )
);
///////////////////////////////////////////////////////
@ -297,57 +274,67 @@ always_comb begin : p_rtrn_logic
dcache_rtrn_vld_o = 1'b0;
if(~rtrn_fifo_empty) begin
unique case (rtrn_fifo_data.l15_returntype)
LOAD_RET: begin
dcache_rtrn_o.rtype = DCACHE_LOAD_ACK;
dcache_rtrn_vld_o = 1'b1;
L15_LOAD_RET: begin
dcache_rtrn_o.rtype = DCACHE_LOAD_ACK;
dcache_rtrn_vld_o = 1'b1;
end
ST_ACK: begin
dcache_rtrn_o.rtype = DCACHE_STORE_ACK;
dcache_rtrn_vld_o = 1'b1;
L15_ST_ACK: begin
dcache_rtrn_o.rtype = DCACHE_STORE_ACK;
dcache_rtrn_vld_o = 1'b1;
end
// INT_RET: begin
// TODO: implement this
// dcache_rtrn_o.reqType = DCACHE_INT_ACK;
// end
IFILL_RET: begin
L15_IFILL_RET: begin
icache_rtrn_o.rtype = ICACHE_IFILL_ACK;
icache_rtrn_vld_o = 1'b1;
end
EVICT_REQ: begin
L15_EVICT_REQ: begin
icache_rtrn_o.rtype = ICACHE_INV_REQ;
dcache_rtrn_o.rtype = DCACHE_INV_REQ;
icache_rtrn_vld_o = 1'b1;
icache_rtrn_vld_o = icache_rtrn_o.inv.vld | icache_rtrn_o.inv.all;
dcache_rtrn_vld_o = dcache_rtrn_o.inv.vld | dcache_rtrn_o.inv.all;
end
L15_CPX_RESTYPE_ATOMIC_RES: begin
dcache_rtrn_o.rtype = DCACHE_ATOMIC_ACK;
dcache_rtrn_vld_o = 1'b1;
end
CPX_RESTYPE_ATOMIC_RES: begin
dcache_rtrn_o.rtype = DCACHE_ATOMIC_ACK;
end
default: begin
;
end
// L15_INT_RET: begin
// TODO: implement this
// dcache_rtrn_o.reqType = DCACHE_INT_ACK;
// end
// default: begin
// ;
// end
endcase // rtrn_fifo_data.l15_returntype
end
end
// openpiton is big endian
generate
if (SWAP_ENDIANESS) begin
assign dcache_rtrn_o.data = { swendian64(rtrn_fifo_data.l15_data_1),
swendian64(rtrn_fifo_data.l15_data_0) };
// icache fifo signal mapping
// swap endianess here since openpiton is big endian
assign icache_rtrn_o.data = { swendian64(rtrn_fifo_data.l15_data_3),
swendian64(rtrn_fifo_data.l15_data_2),
swendian64(rtrn_fifo_data.l15_data_1),
swendian64(rtrn_fifo_data.l15_data_0) };
assign icache_rtrn_o.tid = rtrn_fifo_data.l15_threadid;
assign icache_rtrn_o.nc = rtrn_fifo_data.l15_noncacheable;
assign icache_rtrn_o.f4b = rtrn_fifo_data.l15_f4b;
assign icache_rtrn_o.data = { swendian64(rtrn_fifo_data.l15_data_3),
swendian64(rtrn_fifo_data.l15_data_2),
swendian64(rtrn_fifo_data.l15_data_1),
swendian64(rtrn_fifo_data.l15_data_0) };
end else begin
assign dcache_rtrn_o.data = { rtrn_fifo_data.l15_data_1,
rtrn_fifo_data.l15_data_0 };
// dcache fifo signal mapping
assign dcache_rtrn_o.data = { swendian64(rtrn_fifo_data.l15_data_1),
swendian64(rtrn_fifo_data.l15_data_0) };
assign icache_rtrn_o.data = { rtrn_fifo_data.l15_data_3,
rtrn_fifo_data.l15_data_2,
rtrn_fifo_data.l15_data_1,
rtrn_fifo_data.l15_data_0 };
end
endgenerate
// fifo signals
assign icache_rtrn_o.tid = rtrn_fifo_data.l15_threadid;
assign icache_rtrn_o.nc = rtrn_fifo_data.l15_noncacheable;
assign icache_rtrn_o.f4b = rtrn_fifo_data.l15_f4b;
assign dcache_rtrn_o.tid = rtrn_fifo_data.l15_threadid;
assign dcache_rtrn_o.nc = rtrn_fifo_data.l15_noncacheable;
// invalidation signal mapping
assign icache_rtrn_o.inv.idx = {rtrn_fifo_data.l15_inval_address_15_4, 4'b0000};
assign icache_rtrn_o.inv.way = rtrn_fifo_data.l15_inval_way;
@ -372,7 +359,7 @@ fifo_v2 #(
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i ( l15_rtrn_i ),
.push_i ( l15_req_o.l15_req_ack ),
.push_i ( l15_req_o.l15_req_ack ),
.data_o ( rtrn_fifo_data ),
.pop_i ( rtrn_fifo_pop )
);
@ -383,51 +370,46 @@ fifo_v2 #(
///////////////////////////////////////////////////////
//pragma translate_off
`ifndef verilator
iospace: assert property (
@(posedge clk_i) disable iff (~rst_ni) l15_req_o.l15_val |-> l15_req_o.l15_address >= {40'h8000000000} |-> l15_req_o.l15_nc)
else $fatal("[l15_adapter] accesses to I/O space must have noncacheable bit set!");
`ifndef VERILATOR
invalidations: assert property (
@(posedge clk_i) disable iff (~rst_ni) l15_rtrn_i.l15_val |-> l15_rtrn_i.l15_returntype == EVICT_REQ |-> (l15_rtrn_i.l15_inval_icache_inval |
l15_rtrn_i.l15_inval_dcache_inval |
l15_rtrn_i.l15_inval_icache_all_way |
l15_rtrn_i.l15_inval_dcache_all_way))
else $fatal("[l15_adapter] got invalidation package with zero invalidation flags");
@(posedge clk_i) disable iff (~rst_ni) l15_rtrn_i.l15_val |-> l15_rtrn_i.l15_returntype == L15_EVICT_REQ |-> (l15_rtrn_i.l15_inval_icache_inval |
l15_rtrn_i.l15_inval_dcache_inval |
l15_rtrn_i.l15_inval_icache_all_way |
l15_rtrn_i.l15_inval_dcache_all_way))
else $fatal(1,"[l15_adapter] got invalidation package with zero invalidation flags");
blockstore_o: assert property (
@(posedge clk_i) disable iff (~rst_ni) l15_req_o.l15_val|-> !l15_req_o.l15_blockstore)
else $fatal("[l15_adapter] blockstores are not supported");
else $fatal(1,"[l15_adapter] blockstores are not supported");
blockstore_i: assert property (
@(posedge clk_i) disable iff (~rst_ni) l15_rtrn_i.l15_val|-> !l15_rtrn_i.l15_blockinitstore)
else $fatal("[l15_adapter] blockstores are not supported");
instr_fill_size: assert property (
@(posedge clk_i) disable iff (~rst_ni) (!l15_rtrn_i.l15_f4b))
else $fatal("[l15_adapter] 4b instruction fills not supported");
else $fatal(1,"[l15_adapter] blockstores are not supported");
unsuported_rtrn_types: assert property (
@(posedge clk_i) disable iff (~rst_ni) (l15_rtrn_i.l15_val |-> l15_rtrn_i.l15_returntype inside {LOAD_RET, ST_ACK, IFILL_RET, EVICT_REQ}))
else $fatal("[l15_adapter] unsupported rtrn type");
@(posedge clk_i) disable iff (~rst_ni) (l15_rtrn_i.l15_val |-> l15_rtrn_i.l15_returntype inside {L15_LOAD_RET, L15_ST_ACK, L15_IFILL_RET, L15_EVICT_REQ, L15_CPX_RESTYPE_ATOMIC_RES}))
else $fatal(1,"[l15_adapter] unsupported rtrn type");
amo_type: assert property (
@(posedge clk_i) disable iff (~rst_ni) (l15_rtrn_i.l15_val |-> l15_rtrn_i.l15_returntype inside {L15_CPX_RESTYPE_ATOMIC_RES} |-> l15_rtrn_i.l15_atomic ))
else $fatal(1,"[l15_adapter] l15_atomic must be asserted when the return type is an ATOMIC_RES");
initial begin
// assert wrong parameterizations
assert (L15_SET_ASSOC == ICACHE_SET_ASSOC)
else $fatal("[l15_adapter] number of icache ways not aligned with L15");
else $fatal(1,"[l15_adapter] number of icache ways not aligned with L15");
// assert wrong parameterizations
assert (L15_SET_ASSOC == DCACHE_SET_ASSOC)
else $fatal("[l15_adapter] number of dcache ways not aligned with L15");
else $fatal(1,"[l15_adapter] number of dcache ways not aligned with L15");
// invalidation address returned by L1.5 is 16 bit
assert (16 >= $max(ICACHE_INDEX_WIDTH, DCACHE_INDEX_WIDTH))
else $fatal("[l15_adapter] maximum number of index bits supported by L1.5 is 16");
assert (16 >= DCACHE_INDEX_WIDTH && 16 >= ICACHE_INDEX_WIDTH)
else $fatal(1,"[l15_adapter] maximum number of index bits supported by L1.5 is 16");
// assert mismatch of cache line width
assert (ICACHE_LINE_WIDTH==256)
else $fatal("[l15_adapter] ichache lines are currently restricted to 256 bits");
else $fatal(1,"[l15_adapter] ichache lines are currently restricted to 256 bits");
assert (DCACHE_LINE_WIDTH==128)
else $fatal("[l15_adapter] dchache lines are currently restricted to 128 bits");
else $fatal(1,"[l15_adapter] dchache lines are currently restricted to 128 bits");
end
`endif
//pragma translate_on

@ -1 +1 @@
Subproject commit 1bea86b04d0c341c555e2ff83a8e247c7d1a3260
Subproject commit 38e48d33627cd35d4e6e30fbf186e70218df8dc0