mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-20 12:17:19 -04:00
Add AXI adapter2, and connect l15 adapter.
This commit is contained in:
parent
e382dc866f
commit
feb187fa96
8 changed files with 854 additions and 493 deletions
|
@ -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
|
||||
|
|
|
@ -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
327
src/axi_adapter2.sv
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue