verible-verilog-format: apply it on core directory (#1540)

using verible-v0.0-3422-g520ca4b9/bin/verible-verilog-format
with default configuration

Note: two files are not correctly handled by verible
- core/include/std_cache_pkg.sv
- core/cache_subsystem/cva6_hpdcache_if_adapter.sv
This commit is contained in:
André Sintzoff 2023-10-18 16:36:00 +02:00 committed by GitHub
parent 3d47805dfc
commit 7cd183b710
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
94 changed files with 21423 additions and 19841 deletions

View file

@ -13,7 +13,10 @@
// Date: 20.11.2020
// Description: Functional unit that dispatches CVA6 instructions to accelerators.
module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
module acc_dispatcher
import ariane_pkg::*;
import riscv::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter type acc_req_t = acc_pkg::accelerator_req_t,
parameter type acc_resp_t = acc_pkg::accelerator_resp_t,
@ -62,7 +65,7 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
// Accelerator interface
output acc_req_t acc_req_o,
input acc_resp_t acc_resp_i
);
);
`include "common_cells/registers.svh"
@ -104,8 +107,7 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
STORE:
// 3. We're issuing a scalar store but there is an inflight accelerator load or store.
issue_stall_o = acc_cons_en_i & (~acc_no_st_pending | ~acc_no_ld_pending);
default:
issue_stall_o = 1'b0;
default: issue_stall_o = 1'b0;
endcase
end
@ -121,30 +123,30 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
logic acc_insn_queue_empty;
logic [idx_width(InstructionQueueDepth)-1:0] acc_insn_queue_usage;
logic acc_commit;
logic [TRANS_ID_BITS-1:0] acc_commit_trans_id;
logic [ TRANS_ID_BITS-1:0] acc_commit_trans_id;
assign acc_data = acc_valid_ex_o ? fu_data_i : '0;
fifo_v3 #(
.DEPTH (InstructionQueueDepth),
.FALL_THROUGH(1'b1 ),
.dtype (fu_data_t )
.FALL_THROUGH(1'b1),
.dtype (fu_data_t)
) i_acc_insn_queue (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i (flush_ex_i ),
.testmode_i(1'b0 ),
.data_i (fu_data_i ),
.push_i (acc_valid_q ),
.full_o (/* Unused */ ),
.data_o (acc_insn_queue_o ),
.pop_i (acc_insn_queue_pop ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (flush_ex_i),
.testmode_i(1'b0),
.data_i (fu_data_i),
.push_i (acc_valid_q),
.full_o ( /* Unused */),
.data_o (acc_insn_queue_o),
.pop_i (acc_insn_queue_pop),
.empty_o (acc_insn_queue_empty),
.usage_o (acc_insn_queue_usage)
);
// We are ready if the instruction queue is able to accept at least one more entry.
assign acc_ready = acc_insn_queue_usage < (InstructionQueueDepth-1);
assign acc_ready = acc_insn_queue_usage < (InstructionQueueDepth - 1);
/**********************************
* Non-speculative instructions *
@ -160,17 +162,15 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
logic [NR_SB_ENTRIES-1:0] insn_ready_d, insn_ready_q;
`FF(insn_ready_q, insn_ready_d, '0)
always_comb begin: p_non_speculative_ff
always_comb begin : p_non_speculative_ff
// Maintain state
insn_pending_d = insn_pending_q;
insn_ready_d = insn_ready_q;
// We received a new instruction
if (acc_valid_q)
insn_pending_d[acc_data.trans_id] = 1'b1;
if (acc_valid_q) insn_pending_d[acc_data.trans_id] = 1'b1;
// Flush all received instructions
if (flush_ex_i)
insn_pending_d = '0;
if (flush_ex_i) insn_pending_d = '0;
// An accelerator instruction is no longer speculative.
if (acc_commit && insn_pending_q[acc_commit_trans_id]) begin
@ -179,9 +179,8 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
end
// An accelerator instruction was issued.
if (acc_req_o.req_valid)
insn_ready_d[acc_req_o.trans_id] = 1'b0;
end: p_non_speculative_ff
if (acc_req_o.req_valid) insn_ready_d[acc_req_o.trans_id] = 1'b0;
end : p_non_speculative_ff
/*************************
* Accelerator request *
@ -195,14 +194,14 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
fall_through_register #(
.T(acc_pkg::accelerator_req_t)
) i_accelerator_req_register (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.clr_i (1'b0 ),
.testmode_i(1'b0 ),
.data_i (acc_req ),
.valid_i (acc_req_valid ),
.ready_o (acc_req_ready ),
.data_o (acc_req_int ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.clr_i (1'b0),
.testmode_i(1'b0),
.data_i (acc_req),
.valid_i (acc_req_valid),
.ready_o (acc_req_ready),
.data_o (acc_req_int),
.valid_o (acc_req_o.req_valid),
.ready_i (acc_resp_i.req_ready)
);
@ -216,7 +215,7 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
assign acc_req_o.acc_cons_en = acc_cons_en_i;
assign acc_req_o.inval_ready = inval_ready_i;
always_comb begin: accelerator_req_dispatcher
always_comb begin : accelerator_req_dispatcher
// Do not fetch from the instruction queue
acc_insn_queue_pop = 1'b0;
@ -232,12 +231,17 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
// frm rounding information is up to date during a valid request to the accelerator
// The scoreboard synchronizes it with previous fcsr writes, and future fcsr writes
// do not take place until the accelerator answers (Ariane commits in-order)
insn : acc_insn_queue_o.imm[31:0],
insn :
acc_insn_queue_o.imm[
31
:
0
],
rs1 : acc_insn_queue_o.operand_a,
rs2 : acc_insn_queue_o.operand_b,
frm : fpnew_pkg::roundmode_e'(fcsr_frm_i),
trans_id: acc_insn_queue_o.trans_id,
default : '0
default: '0
};
// Wait until the instruction is no longer speculative.
acc_req_valid = insn_ready_q[acc_insn_queue_o.trans_id] ||
@ -257,11 +261,7 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
assign acc_trans_id_o = acc_resp_i.trans_id;
assign acc_result_o = acc_resp_i.result;
assign acc_valid_o = acc_resp_i.resp_valid;
assign acc_exception_o = '{
cause: riscv::ILLEGAL_INSTR,
tval : '0,
valid: acc_resp_i.error
};
assign acc_exception_o = '{cause: riscv::ILLEGAL_INSTR, tval : '0, valid: acc_resp_i.error};
assign acc_fflags_valid_o = acc_resp_i.fflags_valid;
assign acc_fflags_o = acc_resp_i.fflags;
// Always ready to receive responses
@ -282,12 +282,10 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
// Instruction can be issued to the (in-order) back-end if
// it reached the top of the scoreboard and it hasn't been
// issued yet
always_comb begin: accelerator_commit
always_comb begin : accelerator_commit
acc_commit = 1'b0;
if (!commit_instr_i[0].valid && commit_instr_i[0].fu == ACCEL)
acc_commit = 1'b1;
if (commit_instr_i[0].valid &&
!commit_instr_i[1].valid && commit_instr_i[1].fu == ACCEL)
if (!commit_instr_i[0].valid && commit_instr_i[0].fu == ACCEL) acc_commit = 1'b1;
if (commit_instr_i[0].valid && !commit_instr_i[1].valid && commit_instr_i[1].fu == ACCEL)
acc_commit = 1'b1;
end
@ -331,36 +329,37 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
// Count speculative loads. These can still be flushed.
counter #(
.WIDTH (3),
.STICKY_OVERFLOW (0)
.STICKY_OVERFLOW(0)
) i_acc_spec_loads (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.clear_i (flush_ex_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.clear_i (flush_ex_i),
.en_i ((acc_valid_d && issue_instr_i.op == ACCEL_OP_LOAD) ^ acc_ld_disp),
.load_i (1'b0 ),
.down_i (acc_ld_disp ),
.d_i ('0 ),
.q_o (acc_spec_loads_pending ),
.overflow_o (acc_spec_loads_overflow )
.load_i (1'b0),
.down_i (acc_ld_disp),
.d_i ('0),
.q_o (acc_spec_loads_pending),
.overflow_o(acc_spec_loads_overflow)
);
// Count dispatched loads. These cannot be flushed anymore.
counter #(
.WIDTH (3),
.STICKY_OVERFLOW (0)
.STICKY_OVERFLOW(0)
) i_acc_disp_loads (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.clear_i (1'b0 ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.clear_i (1'b0),
.en_i (acc_ld_disp ^ acc_resp_i.load_complete),
.load_i (1'b0 ),
.load_i (1'b0),
.down_i (acc_resp_i.load_complete),
.d_i ('0 ),
.q_o (acc_disp_loads_pending ),
.overflow_o (acc_disp_loads_overflow )
.d_i ('0),
.q_o (acc_disp_loads_pending),
.overflow_o(acc_disp_loads_overflow)
);
acc_dispatcher_no_load_overflow: assert property (
acc_dispatcher_no_load_overflow :
assert property (
@(posedge clk_i) disable iff (~rst_ni) (acc_spec_loads_overflow == 1'b0) && (acc_disp_loads_overflow == 1'b0) )
else $error("[acc_dispatcher] Too many pending loads.");
@ -375,36 +374,37 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
// Count speculative stores. These can still be flushed.
counter #(
.WIDTH (3),
.STICKY_OVERFLOW (0)
.STICKY_OVERFLOW(0)
) i_acc_spec_stores (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.clear_i (flush_ex_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.clear_i (flush_ex_i),
.en_i ((acc_valid_d && issue_instr_i.op == ACCEL_OP_STORE) ^ acc_st_disp),
.load_i (1'b0 ),
.down_i (acc_st_disp ),
.d_i ('0 ),
.q_o (acc_spec_stores_pending ),
.overflow_o (acc_spec_stores_overflow)
.load_i (1'b0),
.down_i (acc_st_disp),
.d_i ('0),
.q_o (acc_spec_stores_pending),
.overflow_o(acc_spec_stores_overflow)
);
// Count dispatched stores. These cannot be flushed anymore.
counter #(
.WIDTH (3),
.STICKY_OVERFLOW (0)
.STICKY_OVERFLOW(0)
) i_acc_disp_stores (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.clear_i (1'b0 ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.clear_i (1'b0),
.en_i (acc_st_disp ^ acc_resp_i.store_complete),
.load_i (1'b0 ),
.load_i (1'b0),
.down_i (acc_resp_i.store_complete),
.d_i ('0 ),
.q_o (acc_disp_stores_pending ),
.overflow_o (acc_disp_stores_overflow )
.d_i ('0),
.q_o (acc_disp_stores_pending),
.overflow_o(acc_disp_stores_overflow)
);
acc_dispatcher_no_store_overflow: assert property (
acc_dispatcher_no_store_overflow :
assert property (
@(posedge clk_i) disable iff (~rst_ni) (acc_spec_stores_overflow == 1'b0) && (acc_disp_stores_overflow == 1'b0) )
else $error("[acc_dispatcher] Too many pending stores.");

View file

@ -18,7 +18,9 @@
// Description: Ariane ALU based on RI5CY's ALU
module alu import ariane_pkg::*; #(
module alu
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -29,26 +31,25 @@ module alu import ariane_pkg::*; #(
);
riscv::xlen_t operand_a_rev;
logic [31:0] operand_a_rev32;
logic [riscv::XLEN:0] operand_b_neg;
logic [ 31:0] operand_a_rev32;
logic [ riscv::XLEN:0] operand_b_neg;
logic [riscv::XLEN+1:0] adder_result_ext_o;
logic less; // handles both signed and unsigned forms
logic [31:0] rolw; // Rotate Left Word
logic [31:0] rorw; // Rotate Right Word
logic [ 31:0] rolw; // Rotate Left Word
logic [ 31:0] rorw; // Rotate Right Word
logic [31:0] orcbw, rev8w;
logic [$clog2(riscv::XLEN) :0] cpop; // Count Population
logic [$clog2(riscv::XLEN)-1 :0] lz_tz_count; // Count Leading Zeros
logic [4:0] lz_tz_wcount; // Count Leading Zeros Word
logic [ $clog2(riscv::XLEN) : 0] cpop; // Count Population
logic [$clog2(riscv::XLEN)-1 : 0] lz_tz_count; // Count Leading Zeros
logic [ 4:0] lz_tz_wcount; // Count Leading Zeros Word
logic lz_tz_empty, lz_tz_wempty;
// bit reverse operand_a for left shifts and bit counting
generate
genvar k;
for(k = 0; k < riscv::XLEN; k++)
for (k = 0; k < riscv::XLEN; k++)
assign operand_a_rev[k] = fu_data_i.operand_a[riscv::XLEN-1-k];
for (k = 0; k < 32; k++)
assign operand_a_rev32[k] = fu_data_i.operand_a[31-k];
for (k = 0; k < 32; k++) assign operand_a_rev32[k] = fu_data_i.operand_a[31-k];
endgenerate
// ------
@ -65,9 +66,7 @@ module alu import ariane_pkg::*; #(
unique case (fu_data_i.operation)
// ADDER OPS
EQ, NE,
SUB, SUBW,
ANDN, ORN, XNOR: adder_op_b_negate = 1'b1;
EQ, NE, SUB, SUBW, ANDN, ORN, XNOR: adder_op_b_negate = 1'b1;
default: ;
endcase
end
@ -77,16 +76,16 @@ module alu import ariane_pkg::*; #(
if (ariane_pkg::BITMANIP) begin
unique case (fu_data_i.operation)
SH1ADD : operand_a_bitmanip = fu_data_i.operand_a << 1;
SH2ADD : operand_a_bitmanip = fu_data_i.operand_a << 2;
SH3ADD : operand_a_bitmanip = fu_data_i.operand_a << 3;
SH1ADDUW : operand_a_bitmanip = fu_data_i.operand_a[31:0] << 1;
SH2ADDUW : operand_a_bitmanip = fu_data_i.operand_a[31:0] << 2;
SH3ADDUW : operand_a_bitmanip = fu_data_i.operand_a[31:0] << 3;
CTZ : operand_a_bitmanip = operand_a_rev;
CTZW : operand_a_bitmanip = operand_a_rev32;
ADDUW, CPOPW, CLZW : operand_a_bitmanip = fu_data_i.operand_a[31:0];
default : ;
SH1ADD: operand_a_bitmanip = fu_data_i.operand_a << 1;
SH2ADD: operand_a_bitmanip = fu_data_i.operand_a << 2;
SH3ADD: operand_a_bitmanip = fu_data_i.operand_a << 3;
SH1ADDUW: operand_a_bitmanip = fu_data_i.operand_a[31:0] << 1;
SH2ADDUW: operand_a_bitmanip = fu_data_i.operand_a[31:0] << 2;
SH3ADDUW: operand_a_bitmanip = fu_data_i.operand_a[31:0] << 3;
CTZ: operand_a_bitmanip = operand_a_rev;
CTZW: operand_a_bitmanip = operand_a_rev32;
ADDUW, CPOPW, CLZW: operand_a_bitmanip = fu_data_i.operand_a[31:0];
default: ;
endcase
end
end
@ -95,8 +94,8 @@ module alu import ariane_pkg::*; #(
assign adder_in_a = {operand_a_bitmanip, 1'b1};
// prepare operand b
assign operand_b_neg = {fu_data_i.operand_b, 1'b0} ^ {riscv::XLEN+1{adder_op_b_negate}};
assign adder_in_b = operand_b_neg ;
assign operand_b_neg = {fu_data_i.operand_b, 1'b0} ^ {riscv::XLEN + 1{adder_op_b_negate}};
assign adder_in_b = operand_b_neg;
// actual adder
assign adder_result_ext_o = $unsigned(adder_in_a) + $unsigned(adder_in_b);
@ -126,16 +125,16 @@ module alu import ariane_pkg::*; #(
riscv::xlen_t shift_amt; // amount of shift, to the right
riscv::xlen_t shift_op_a; // input of the shifter
logic [31:0] shift_op_a32; // input to the 32 bit shift operation
logic [ 31:0] shift_op_a32; // input to the 32 bit shift operation
riscv::xlen_t shift_result;
logic [31:0] shift_result32;
logic [ 31:0] shift_result32;
logic [riscv::XLEN:0] shift_right_result;
logic [32:0] shift_right_result32;
logic [ 32:0] shift_right_result32;
riscv::xlen_t shift_left_result;
logic [31:0] shift_left_result32;
logic [ 31:0] shift_left_result32;
assign shift_amt = fu_data_i.operand_b;
@ -151,8 +150,8 @@ module alu import ariane_pkg::*; #(
assign shift_op_a = shift_left ? operand_a_rev : fu_data_i.operand_a;
assign shift_op_a32 = shift_left ? operand_a_rev32 : fu_data_i.operand_a[31:0];
assign shift_op_a_64 = { shift_arithmetic & shift_op_a[riscv::XLEN-1], shift_op_a};
assign shift_op_a_32 = { shift_arithmetic & shift_op_a[31], shift_op_a32};
assign shift_op_a_64 = {shift_arithmetic & shift_op_a[riscv::XLEN-1], shift_op_a};
assign shift_op_a_32 = {shift_arithmetic & shift_op_a[31], shift_op_a32};
assign shift_right_result = $unsigned($signed(shift_op_a_64) >>> shift_amt[5:0]);
@ -160,11 +159,10 @@ module alu import ariane_pkg::*; #(
// bit reverse the shift_right_result for left shifts
genvar j;
generate
for(j = 0; j < riscv::XLEN; j++)
for (j = 0; j < riscv::XLEN; j++)
assign shift_left_result[j] = shift_right_result[riscv::XLEN-1-j];
for(j = 0; j < 32; j++)
assign shift_left_result32[j] = shift_right_result32[31-j];
for (j = 0; j < 32; j++) assign shift_left_result32[j] = shift_right_result32[31-j];
endgenerate
@ -186,7 +184,8 @@ module alu import ariane_pkg::*; #(
(fu_data_i.operation == MIN))
sgn = 1'b1;
less = ($signed({sgn & fu_data_i.operand_a[riscv::XLEN-1], fu_data_i.operand_a}) < $signed({sgn & fu_data_i.operand_b[riscv::XLEN-1], fu_data_i.operand_b}));
less = ($signed({sgn & fu_data_i.operand_a[riscv::XLEN-1], fu_data_i.operand_a}) <
$signed({sgn & fu_data_i.operand_b[riscv::XLEN-1], fu_data_i.operand_b}));
end
if (ariane_pkg::BITMANIP) begin : gen_bitmanip
@ -196,7 +195,7 @@ module alu import ariane_pkg::*; #(
.INPUT_WIDTH(riscv::XLEN)
) i_cpop_count (
.data_i (operand_a_bitmanip),
.popcount_o (cpop)
.popcount_o(cpop)
);
// Count Leading/Trailing Zeros
@ -205,18 +204,18 @@ module alu import ariane_pkg::*; #(
.WIDTH(riscv::XLEN),
.MODE (1)
) i_clz_64b (
.in_i (operand_a_bitmanip),
.cnt_o (lz_tz_count),
.empty_o (lz_tz_empty)
.in_i(operand_a_bitmanip),
.cnt_o(lz_tz_count),
.empty_o(lz_tz_empty)
);
//32b
lzc #(
.WIDTH(32),
.MODE (1)
) i_clz_32b (
.in_i (operand_a_bitmanip[31:0]),
.cnt_o (lz_tz_wcount),
.empty_o (lz_tz_wempty)
.in_i(operand_a_bitmanip[31:0]),
.cnt_o(lz_tz_wcount),
.empty_o(lz_tz_wempty)
);
end
@ -228,40 +227,47 @@ module alu import ariane_pkg::*; #(
unique case (fu_data_i.operation)
// Standard Operations
ANDL, ANDN: result_o = fu_data_i.operand_a & operand_b_neg[riscv::XLEN:1];
ORL, ORN : result_o = fu_data_i.operand_a | operand_b_neg[riscv::XLEN:1];
ORL, ORN: result_o = fu_data_i.operand_a | operand_b_neg[riscv::XLEN:1];
XORL, XNOR: result_o = fu_data_i.operand_a ^ operand_b_neg[riscv::XLEN:1];
// Adder Operations
ADD, SUB,
ADDUW,
SH1ADD, SH2ADD, SH3ADD,
SH1ADDUW, SH2ADDUW, SH3ADDUW: result_o = adder_result;
ADD, SUB, ADDUW, SH1ADD, SH2ADD, SH3ADD, SH1ADDUW, SH2ADDUW, SH3ADDUW:
result_o = adder_result;
// Add word: Ignore the upper bits and sign extend to 64 bit
ADDW, SUBW: result_o = {{riscv::XLEN-32{adder_result[31]}}, adder_result[31:0]};
ADDW, SUBW: result_o = {{riscv::XLEN - 32{adder_result[31]}}, adder_result[31:0]};
// Shift Operations
SLL,
SRL, SRA: result_o = (riscv::XLEN == 64) ? shift_result : shift_result32;
SLL, SRL, SRA: result_o = (riscv::XLEN == 64) ? shift_result : shift_result32;
// Shifts 32 bit
SLLW,
SRLW, SRAW: result_o = {{riscv::XLEN-32{shift_result32[31]}}, shift_result32[31:0]};
SLLW, SRLW, SRAW: result_o = {{riscv::XLEN - 32{shift_result32[31]}}, shift_result32[31:0]};
// Comparison Operations
SLTS, SLTU: result_o = {{riscv::XLEN-1{1'b0}}, less};
SLTS, SLTU: result_o = {{riscv::XLEN - 1{1'b0}}, less};
default: ; // default case to suppress unique warning
endcase
if (ariane_pkg::BITMANIP) begin
// Index for Bitwise Rotation
bit_indx = 1 << (fu_data_i.operand_b & (riscv::XLEN-1));
orcbw = {{8{|fu_data_i.operand_a[31:24]}}, {8{|fu_data_i.operand_a[23:16]}}, {8{|fu_data_i.operand_a[15:8]}}, {8{|fu_data_i.operand_a[7:0]}}};
rev8w = {{fu_data_i.operand_a[7:0]}, {fu_data_i.operand_a[15:8]}, {fu_data_i.operand_a[23:16]}, {fu_data_i.operand_a[31:24]}};
bit_indx = 1 << (fu_data_i.operand_b & (riscv::XLEN - 1));
orcbw = {
{8{|fu_data_i.operand_a[31:24]}},
{8{|fu_data_i.operand_a[23:16]}},
{8{|fu_data_i.operand_a[15:8]}},
{8{|fu_data_i.operand_a[7:0]}}
};
rev8w = {
{fu_data_i.operand_a[7:0]},
{fu_data_i.operand_a[15:8]},
{fu_data_i.operand_a[23:16]},
{fu_data_i.operand_a[31:24]}
};
// rolw, roriw, rorw
rolw = ({{riscv::XLEN-32{1'b0}},fu_data_i.operand_a[31:0]} << fu_data_i.operand_b[4:0]) | ({{riscv::XLEN-32{1'b0}},fu_data_i.operand_a[31:0]} >> (riscv::XLEN-32-fu_data_i.operand_b[4:0]));
rorw = ({{riscv::XLEN-32{1'b0}},fu_data_i.operand_a[31:0]} >> fu_data_i.operand_b[4:0]) | ({{riscv::XLEN-32{1'b0}},fu_data_i.operand_a[31:0]} << (riscv::XLEN-32-fu_data_i.operand_b[4:0]));
unique case (fu_data_i.operation)
// Left Shift 32 bit unsigned
SLLIUW: result_o = {{riscv::XLEN-32{1'b0}}, fu_data_i.operand_a[31:0]} << fu_data_i.operand_b[5:0];
SLLIUW:
result_o = {{riscv::XLEN-32{1'b0}}, fu_data_i.operand_a[31:0]} << fu_data_i.operand_b[5:0];
// Integer minimum/maximum
MAX: result_o = less ? fu_data_i.operand_b : fu_data_i.operand_a;
MAXU: result_o = less ? fu_data_i.operand_b : fu_data_i.operand_a;
@ -270,37 +276,45 @@ module alu import ariane_pkg::*; #(
// Single bit instructions operations
BCLR, BCLRI: result_o = fu_data_i.operand_a & ~bit_indx;
BEXT, BEXTI: result_o = {{riscv::XLEN-1{1'b0}}, |(fu_data_i.operand_a & bit_indx)};
BEXT, BEXTI: result_o = {{riscv::XLEN - 1{1'b0}}, |(fu_data_i.operand_a & bit_indx)};
BINV, BINVI: result_o = fu_data_i.operand_a ^ bit_indx;
BSET, BSETI: result_o = fu_data_i.operand_a | bit_indx;
// Count Leading/Trailing Zeros
CLZ, CTZ : result_o = (lz_tz_empty) ? ({{riscv::XLEN-$clog2(riscv::XLEN){1'b0}}, lz_tz_count} + 1) : {{riscv::XLEN-$clog2(riscv::XLEN){1'b0}}, lz_tz_count};
CLZW, CTZW: result_o = (lz_tz_wempty) ? 32 : {{riscv::XLEN-5{1'b0}}, lz_tz_wcount};
CLZ, CTZ:
result_o = (lz_tz_empty) ? ({{riscv::XLEN - $clog2(riscv::XLEN) {1'b0}}, lz_tz_count} + 1) :
{{riscv::XLEN - $clog2(riscv::XLEN) {1'b0}}, lz_tz_count};
CLZW, CTZW: result_o = (lz_tz_wempty) ? 32 : {{riscv::XLEN - 5{1'b0}}, lz_tz_wcount};
// Count population
CPOP, CPOPW: result_o = {{(riscv::XLEN-($clog2(riscv::XLEN)+1)){1'b0}}, cpop};
CPOP, CPOPW: result_o = {{(riscv::XLEN - ($clog2(riscv::XLEN) + 1)) {1'b0}}, cpop};
// Sign and Zero Extend
SEXTB: result_o = {{riscv::XLEN-8{fu_data_i.operand_a[7]}}, fu_data_i.operand_a[7:0]};
SEXTH: result_o = {{riscv::XLEN-16{fu_data_i.operand_a[15]}}, fu_data_i.operand_a[15:0]};
ZEXTH: result_o = {{riscv::XLEN-16{1'b0}}, fu_data_i.operand_a[15:0]};
SEXTB: result_o = {{riscv::XLEN - 8{fu_data_i.operand_a[7]}}, fu_data_i.operand_a[7:0]};
SEXTH: result_o = {{riscv::XLEN - 16{fu_data_i.operand_a[15]}}, fu_data_i.operand_a[15:0]};
ZEXTH: result_o = {{riscv::XLEN - 16{1'b0}}, fu_data_i.operand_a[15:0]};
// Bitwise Rotation
ROL: result_o = (riscv::XLEN == 64) ? ((fu_data_i.operand_a << fu_data_i.operand_b[5:0]) | (fu_data_i.operand_a >> (riscv::XLEN-fu_data_i.operand_b[5:0]))) : ((fu_data_i.operand_a << fu_data_i.operand_b[4:0]) | (fu_data_i.operand_a >> (riscv::XLEN-fu_data_i.operand_b[4:0])));
ROLW: result_o = {{riscv::XLEN-32{rolw[31]}}, rolw};
ROR, RORI: result_o = (riscv::XLEN == 64) ? ((fu_data_i.operand_a >> fu_data_i.operand_b[5:0]) | (fu_data_i.operand_a << (riscv::XLEN-fu_data_i.operand_b[5:0]))) : ((fu_data_i.operand_a >> fu_data_i.operand_b[4:0]) | (fu_data_i.operand_a << (riscv::XLEN-fu_data_i.operand_b[4:0])));
RORW, RORIW: result_o = {{riscv::XLEN-32{rorw[31]}}, rorw};
ORCB: result_o = (riscv::XLEN == 64) ? ({{8{|fu_data_i.operand_a[63:56]}}, {8{|fu_data_i.operand_a[55:48]}}, {8{|fu_data_i.operand_a[47:40]}}, {8{|fu_data_i.operand_a[39:32]}}, orcbw}) : orcbw;
REV8: result_o = (riscv::XLEN == 64) ? ({rev8w , {fu_data_i.operand_a[39:32]}, {fu_data_i.operand_a[47:40]}, {fu_data_i.operand_a[55:48]}, {fu_data_i.operand_a[63:56]}}) : rev8w;
ROL:
result_o = (riscv::XLEN == 64) ? ((fu_data_i.operand_a << fu_data_i.operand_b[5:0]) | (fu_data_i.operand_a >> (riscv::XLEN-fu_data_i.operand_b[5:0]))) : ((fu_data_i.operand_a << fu_data_i.operand_b[4:0]) | (fu_data_i.operand_a >> (riscv::XLEN-fu_data_i.operand_b[4:0])));
ROLW: result_o = {{riscv::XLEN - 32{rolw[31]}}, rolw};
ROR, RORI:
result_o = (riscv::XLEN == 64) ? ((fu_data_i.operand_a >> fu_data_i.operand_b[5:0]) | (fu_data_i.operand_a << (riscv::XLEN-fu_data_i.operand_b[5:0]))) : ((fu_data_i.operand_a >> fu_data_i.operand_b[4:0]) | (fu_data_i.operand_a << (riscv::XLEN-fu_data_i.operand_b[4:0])));
RORW, RORIW: result_o = {{riscv::XLEN - 32{rorw[31]}}, rorw};
ORCB:
result_o = (riscv::XLEN == 64) ? ({{8{|fu_data_i.operand_a[63:56]}}, {8{|fu_data_i.operand_a[55:48]}}, {8{|fu_data_i.operand_a[47:40]}}, {8{|fu_data_i.operand_a[39:32]}}, orcbw}) : orcbw;
REV8:
result_o = (riscv::XLEN == 64) ? ({rev8w , {fu_data_i.operand_a[39:32]}, {fu_data_i.operand_a[47:40]}, {fu_data_i.operand_a[55:48]}, {fu_data_i.operand_a[63:56]}}) : rev8w;
default: ; // default case to suppress unique warning
endcase
end
if (CVA6Cfg.ZiCondExtEn) begin
unique case (fu_data_i.operation)
CZERO_EQZ : result_o = (|fu_data_i.operand_b) ? fu_data_i.operand_a : '0; // move zero to rd if rs2 is equal to zero else rs1
CZERO_NEZ : result_o = (|fu_data_i.operand_b) ? '0 : fu_data_i.operand_a; // move zero to rd if rs2 is nonzero else rs1
CZERO_EQZ:
result_o = (|fu_data_i.operand_b) ? fu_data_i.operand_a : '0; // move zero to rd if rs2 is equal to zero else rs1
CZERO_NEZ:
result_o = (|fu_data_i.operand_b) ? '0 : fu_data_i.operand_a; // move zero to rd if rs2 is nonzero else rs1
default: ; // default case to suppress unique warning
endcase
end

View file

@ -42,7 +42,7 @@ module amo_buffer #(
logic [riscv::PLEN-1:0] paddr;
riscv::xlen_t data;
logic [1:0] size;
} amo_op_t ;
} amo_op_t;
amo_op_t amo_data_in, amo_data_out;
@ -50,8 +50,8 @@ module amo_buffer #(
assign amo_req_o.req = no_st_pending_i & amo_valid_commit_i & amo_valid;
assign amo_req_o.amo_op = amo_data_out.op;
assign amo_req_o.size = amo_data_out.size;
assign amo_req_o.operand_a = {{64-riscv::PLEN{1'b0}}, amo_data_out.paddr};
assign amo_req_o.operand_b = {{64-riscv::XLEN{1'b0}}, amo_data_out.data};
assign amo_req_o.operand_a = {{64 - riscv::PLEN{1'b0}}, amo_data_out.paddr};
assign amo_req_o.operand_b = {{64 - riscv::XLEN{1'b0}}, amo_data_out.data};
assign amo_data_in.op = amo_op_i;
assign amo_data_in.data = data_i;
@ -63,20 +63,20 @@ module amo_buffer #(
assign flush_amo_buffer = flush_i & !amo_valid_commit_i;
fifo_v3 #(
.DEPTH ( 1 ),
.dtype ( amo_op_t )
.DEPTH(1),
.dtype(amo_op_t)
) i_amo_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_amo_buffer ),
.testmode_i ( 1'b0 ),
.full_o ( amo_valid ),
.empty_o ( ready_o ),
.usage_o ( ), // left open
.data_i ( amo_data_in ),
.push_i ( valid_i ),
.data_o ( amo_data_out ),
.pop_i ( amo_resp_i.ack )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (flush_amo_buffer),
.testmode_i(1'b0),
.full_o (amo_valid),
.empty_o (ready_o),
.usage_o (), // left open
.data_i (amo_data_in),
.push_i (valid_i),
.data_o (amo_data_out),
.pop_i (amo_resp_i.ack)
);
endmodule

View file

@ -28,34 +28,33 @@ module ariane_regfile_lol #(
parameter int unsigned DATA_WIDTH = 32,
parameter int unsigned NR_READ_PORTS = 2,
parameter bit ZERO_REG_ZERO = 0
)(
) (
// clock and reset
input logic clk_i,
input logic rst_ni,
// disable clock gates for testing
input logic test_en_i,
// read port
input logic [NR_READ_PORTS-1:0][4:0] raddr_i,
output logic [NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
input logic [ NR_READ_PORTS-1:0][ 4:0] raddr_i,
output logic [ NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
// write port
input logic [CVA6Cfg.NrCommitPorts-1:0][4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][ 4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][DATA_WIDTH-1:0] wdata_i,
input logic [CVA6Cfg.NrCommitPorts-1:0] we_i
);
localparam ADDR_WIDTH = 5;
localparam NUM_WORDS = 2**ADDR_WIDTH;
localparam NUM_WORDS = 2 ** ADDR_WIDTH;
logic [NUM_WORDS-1:ZERO_REG_ZERO] mem_clocks;
logic [DATA_WIDTH-1:0] mem[NUM_WORDS];
logic [CVA6Cfg.NrCommitPorts-1:0][NUM_WORDS-1:1] waddr_onehot,waddr_onehot_q;
logic [ DATA_WIDTH-1:0] mem [NUM_WORDS];
logic [CVA6Cfg.NrCommitPorts-1:0][NUM_WORDS-1:1] waddr_onehot, waddr_onehot_q;
logic [CVA6Cfg.NrCommitPorts-1:0][DATA_WIDTH-1:0] wdata_q;
// decode addresses
for (genvar i = 0; i < NR_READ_PORTS; i++)
assign rdata_o[i] = mem[raddr_i[i][ADDR_WIDTH-1:0]];
for (genvar i = 0; i < NR_READ_PORTS; i++) assign rdata_o[i] = mem[raddr_i[i][ADDR_WIDTH-1:0]];
always_ff @(posedge clk_i, negedge rst_ni) begin : sample_waddr
if (~rst_ni) begin
@ -74,10 +73,8 @@ module ariane_regfile_lol #(
always_comb begin : decode_write_addess
for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
for (int unsigned j = 1; j < NUM_WORDS; j++) begin
if (we_i[i] && (waddr_i[i] == j))
waddr_onehot[i][j] = 1'b1;
else
waddr_onehot[i][j] = 1'b0;
if (we_i[i] && (waddr_i[i] == j)) waddr_onehot[i][j] = 1'b1;
else waddr_onehot[i][j] = 1'b0;
end
end
end
@ -87,14 +84,13 @@ module ariane_regfile_lol #(
logic [CVA6Cfg.NrCommitPorts-1:0] waddr_ored;
for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++)
assign waddr_ored[i] = waddr_onehot[i][x];
for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++) assign waddr_ored[i] = waddr_onehot[i][x];
cluster_clock_gating i_cg (
.clk_i ( clk_i ),
.en_i ( |waddr_ored ),
.test_en_i ( test_en_i ),
.clk_o ( mem_clocks[x] )
.clk_i (clk_i),
.en_i (|waddr_ored),
.test_en_i(test_en_i),
.clk_o (mem_clocks[x])
);
end
@ -107,13 +103,11 @@ module ariane_regfile_lol #(
// Integer registers
always_latch begin : latch_wdata
// Note: The assignment has to be done inside this process or Modelsim complains about it
if (ZERO_REG_ZERO)
mem[0] = '0;
if (ZERO_REG_ZERO) mem[0] = '0;
for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
for (int unsigned k = ZERO_REG_ZERO; k < NUM_WORDS; k++) begin
if (mem_clocks[k] && waddr_onehot_q[i][k])
mem[k] = wdata_q[i];
if (mem_clocks[k] && waddr_onehot_q[i][k]) mem[k] = wdata_q[i];
end
end
end

View file

@ -27,35 +27,33 @@ module ariane_regfile #(
parameter int unsigned DATA_WIDTH = 32,
parameter int unsigned NR_READ_PORTS = 2,
parameter bit ZERO_REG_ZERO = 0
)(
) (
// clock and reset
input logic clk_i,
input logic rst_ni,
// disable clock gates for testing
input logic test_en_i,
// read port
input logic [NR_READ_PORTS-1:0][4:0] raddr_i,
output logic [NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
input logic [ NR_READ_PORTS-1:0][ 4:0] raddr_i,
output logic [ NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
// write port
input logic [CVA6Cfg.NrCommitPorts-1:0][4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][ 4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][DATA_WIDTH-1:0] wdata_i,
input logic [CVA6Cfg.NrCommitPorts-1:0] we_i
);
localparam ADDR_WIDTH = 5;
localparam NUM_WORDS = 2**ADDR_WIDTH;
localparam NUM_WORDS = 2 ** ADDR_WIDTH;
logic [NUM_WORDS-1:0][DATA_WIDTH-1:0] mem;
logic [CVA6Cfg.NrCommitPorts-1:0][NUM_WORDS-1:0] we_dec;
logic [ NUM_WORDS-1:0][DATA_WIDTH-1:0] mem;
logic [CVA6Cfg.NrCommitPorts-1:0][ NUM_WORDS-1:0] we_dec;
always_comb begin : we_decoder
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) begin
for (int unsigned i = 0; i < NUM_WORDS; i++) begin
if (waddr_i[j] == i)
we_dec[j][i] = we_i[j];
else
we_dec[j][i] = 1'b0;
if (waddr_i[j] == i) we_dec[j][i] = we_i[j];
else we_dec[j][i] = 1'b0;
end
end
end

View file

@ -30,31 +30,31 @@ module ariane_regfile_fpga #(
parameter int unsigned DATA_WIDTH = 32,
parameter int unsigned NR_READ_PORTS = 2,
parameter bit ZERO_REG_ZERO = 0
)(
) (
// clock and reset
input logic clk_i,
input logic rst_ni,
// disable clock gates for testing
input logic test_en_i,
// read port
input logic [NR_READ_PORTS-1:0][4:0] raddr_i,
output logic [NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
input logic [ NR_READ_PORTS-1:0][ 4:0] raddr_i,
output logic [ NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o,
// write port
input logic [CVA6Cfg.NrCommitPorts-1:0][4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][ 4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][DATA_WIDTH-1:0] wdata_i,
input logic [CVA6Cfg.NrCommitPorts-1:0] we_i
);
localparam ADDR_WIDTH = 5;
localparam NUM_WORDS = 2**ADDR_WIDTH;
localparam NUM_WORDS = 2 ** ADDR_WIDTH;
localparam LOG_NR_WRITE_PORTS = CVA6Cfg.NrCommitPorts == 1 ? 1 : $clog2(CVA6Cfg.NrCommitPorts);
// Distributed RAM usually supports one write port per block - duplicate for each write port.
logic [NUM_WORDS-1:0][DATA_WIDTH-1:0] mem [CVA6Cfg.NrCommitPorts];
logic [ NUM_WORDS-1:0][ DATA_WIDTH-1:0] mem [CVA6Cfg.NrCommitPorts];
logic [CVA6Cfg.NrCommitPorts-1:0][NUM_WORDS-1:0] we_dec;
logic [NUM_WORDS-1:0][LOG_NR_WRITE_PORTS-1:0] mem_block_sel;
logic [NUM_WORDS-1:0][LOG_NR_WRITE_PORTS-1:0] mem_block_sel_q;
logic [CVA6Cfg.NrCommitPorts-1:0][ NUM_WORDS-1:0] we_dec;
logic [ NUM_WORDS-1:0][LOG_NR_WRITE_PORTS-1:0] mem_block_sel;
logic [ NUM_WORDS-1:0][LOG_NR_WRITE_PORTS-1:0] mem_block_sel_q;
// write adress decoder (for block selector)
always_comb begin
@ -75,8 +75,8 @@ module ariane_regfile_fpga #(
// index has priority.
always_comb begin
mem_block_sel = mem_block_sel_q;
for (int i = 0; i<NUM_WORDS; i++) begin
for (int j = 0; j<CVA6Cfg.NrCommitPorts; j++) begin
for (int i = 0; i < NUM_WORDS; i++) begin
for (int j = 0; j < CVA6Cfg.NrCommitPorts; j++) begin
if (we_dec[j][i] == 1'b1) begin
mem_block_sel[i] = LOG_NR_WRITE_PORTS'(j);
end
@ -94,14 +94,14 @@ module ariane_regfile_fpga #(
end
// distributed RAM blocks
logic [NR_READ_PORTS-1:0] [DATA_WIDTH-1:0] mem_read [CVA6Cfg.NrCommitPorts];
for (genvar j=0; j<CVA6Cfg.NrCommitPorts; j++) begin : regfile_ram_block
logic [NR_READ_PORTS-1:0][DATA_WIDTH-1:0] mem_read[CVA6Cfg.NrCommitPorts];
for (genvar j = 0; j < CVA6Cfg.NrCommitPorts; j++) begin : regfile_ram_block
always_ff @(posedge clk_i) begin
if (we_i[j] && ~waddr_i[j] != 0) begin
mem[j][waddr_i[j]] <= wdata_i[j];
end
end
for (genvar k=0; k<NR_READ_PORTS; k++) begin : block_read
for (genvar k = 0; k < NR_READ_PORTS; k++) begin : block_read
assign mem_read[j][k] = mem[j][raddr_i[k]];
end
end
@ -110,15 +110,13 @@ module ariane_regfile_fpga #(
logic [NR_READ_PORTS-1:0][LOG_NR_WRITE_PORTS-1:0] block_addr;
for (genvar k = 0; k < NR_READ_PORTS; k++) begin : regfile_read_port
assign block_addr[k] = mem_block_sel_q[raddr_i[k]];
assign rdata_o[k] =
(ZERO_REG_ZERO && raddr_i[k] == '0 ) ? '0 : mem_read[block_addr[k]][k];
assign rdata_o[k] = (ZERO_REG_ZERO && raddr_i[k] == '0) ? '0 : mem_read[block_addr[k]][k];
end
// random initialization of the memory to suppress assert warnings on Questa.
initial
begin
for(int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
for(int j = 0; j < NUM_WORDS; j++) begin
initial begin
for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
for (int j = 0; j < NUM_WORDS; j++) begin
mem[i][j] = $random();
end
end

View file

@ -67,13 +67,18 @@ module axi_shim #(
);
localparam AddrIndex = ($clog2(AxiNumWords) > 0) ? $clog2(AxiNumWords) : 1;
///////////////////////////////////////////////////////
// write channel
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// 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;
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 [AddrIndex-1:0] wr_cnt_d, wr_cnt_q;
@ -109,9 +114,7 @@ module axi_shim #(
// 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;
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
@ -138,7 +141,9 @@ module axi_shim #(
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})
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;
@ -147,11 +152,13 @@ module axi_shim #(
end else begin
wr_cnt_en = axi_resp_i.w_ready;
case ({axi_resp_i.aw_ready, 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:;
default: ;
endcase
end
end
@ -172,7 +179,9 @@ module axi_shim #(
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})
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
// we got an aw ready
2'b01: begin
// are there any outstanding transactions?
@ -197,7 +206,7 @@ module axi_shim #(
wr_cnt_en = 1'b1;
end
end
default:;
default: ;
endcase
end
///////////////////////////////////
@ -234,9 +243,9 @@ module axi_shim #(
end
///////////////////////////////////////////////////////
// read channel
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// read channel
///////////////////////////////////////////////////////
// address
// in case of a wrapping transfer we can simply begin at the address, if we want to request a cache-line
@ -285,17 +294,17 @@ module axi_shim #(
end
end
// ----------------
// Assertions
// ----------------
// ----------------
// Assertions
// ----------------
//pragma translate_off
initial begin
assert (AxiNumWords >= 1) else
$fatal(1, "[axi adapter] AxiNumWords must be >= 1");
assert (CVA6Cfg.AxiIdWidth >= 2) else
$fatal(1, "[axi adapter] AXI id width must be at least 2 bit wide");
end
//pragma translate_on
//pragma translate_off
initial begin
assert (AxiNumWords >= 1)
else $fatal(1, "[axi adapter] AxiNumWords must be >= 1");
assert (CVA6Cfg.AxiIdWidth >= 2)
else $fatal(1, "[axi adapter] AXI id width must be at least 2 bit wide");
end
//pragma translate_on
endmodule // axi_adapter2

View file

@ -67,7 +67,7 @@ module branch_unit #(
resolved_branch_o.target_address = (branch_comp_res_i) ? target_address : next_pc;
resolved_branch_o.is_taken = branch_comp_res_i;
// check the outcome of the branch speculation
if ( ariane_pkg::op_is_branch(fu_data_i.operation) ) begin
if (ariane_pkg::op_is_branch(fu_data_i.operation)) begin
// Set the `cf_type` of the output as `branch`, this will update the BHT.
resolved_branch_o.cf_type = ariane_pkg::Branch;
// If the ALU comparison does not agree with the BHT prediction set the resolution as mispredicted.
@ -78,7 +78,8 @@ module branch_unit #(
&& (branch_predict_i.cf == ariane_pkg::NoCF || target_address != branch_predict_i.predict_address)) begin
resolved_branch_o.is_mispredict = 1'b1;
// update BTB only if this wasn't a return
if (branch_predict_i.cf != ariane_pkg::Return) resolved_branch_o.cf_type = ariane_pkg::JumpR;
if (branch_predict_i.cf != ariane_pkg::Return)
resolved_branch_o.cf_type = ariane_pkg::JumpR;
end
// to resolve the branch in ID
resolve_branch_o = 1'b1;
@ -95,12 +96,9 @@ module branch_unit #(
((ariane_pkg::op_is_branch(fu_data_i.operation)) && branch_comp_res_i);
branch_exception_o.cause = riscv::INSTR_ADDR_MISALIGNED;
branch_exception_o.valid = 1'b0;
branch_exception_o.tval = {{riscv::XLEN-riscv::VLEN{pc_i[riscv::VLEN-1]}}, pc_i};
branch_exception_o.tval = {{riscv::XLEN - riscv::VLEN{pc_i[riscv::VLEN-1]}}, pc_i};
// Only throw instruction address misaligned exception if this is indeed a `taken` conditional branch or
// an unconditional jump
if (branch_valid_i &&
target_address[0] != 1'b0 &&
jump_taken)
branch_exception_o.valid = 1'b1;
if (branch_valid_i && target_address[0] != 1'b0 && jump_taken) branch_exception_o.valid = 1'b1;
end
endmodule

View file

@ -35,8 +35,8 @@ module amo_alu #(
unique case (amo_op_i)
// the default is to output operand_b
ariane_pkg::AMO_SC:;
ariane_pkg::AMO_SWAP:;
ariane_pkg::AMO_SC: ;
ariane_pkg::AMO_SWAP: ;
ariane_pkg::AMO_ADD: amo_result_o = adder_sum[63:0];
ariane_pkg::AMO_AND: amo_result_o = amo_operand_a_i & amo_operand_b_i;
ariane_pkg::AMO_OR: amo_result_o = amo_operand_a_i | amo_operand_b_i;

View file

@ -23,7 +23,7 @@ module axi_adapter #(
parameter int unsigned CACHELINE_BYTE_OFFSET = 8,
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
@ -48,17 +48,31 @@ module axi_adapter #(
output axi_req_t axi_req_o,
input axi_rsp_t axi_resp_i
);
localparam BURST_SIZE = (DATA_WIDTH/CVA6Cfg.AxiDataWidth)-1;
localparam ADDR_INDEX = ($clog2(DATA_WIDTH/CVA6Cfg.AxiDataWidth) > 0) ? $clog2(DATA_WIDTH/CVA6Cfg.AxiDataWidth) : 1;
localparam BURST_SIZE = (DATA_WIDTH / CVA6Cfg.AxiDataWidth) - 1;
localparam ADDR_INDEX = ($clog2(
DATA_WIDTH / CVA6Cfg.AxiDataWidth
) > 0) ? $clog2(
DATA_WIDTH / CVA6Cfg.AxiDataWidth
) : 1;
enum logic [3:0] {
IDLE, WAIT_B_VALID, WAIT_AW_READY, WAIT_LAST_W_READY, WAIT_LAST_W_READY_AW_READY, WAIT_AW_READY_BURST,
WAIT_R_VALID, WAIT_R_VALID_MULTIPLE, COMPLETE_READ, WAIT_AMO_R_VALID
} state_q, state_d;
IDLE,
WAIT_B_VALID,
WAIT_AW_READY,
WAIT_LAST_W_READY,
WAIT_LAST_W_READY_AW_READY,
WAIT_AW_READY_BURST,
WAIT_R_VALID,
WAIT_R_VALID_MULTIPLE,
COMPLETE_READ,
WAIT_AMO_R_VALID
}
state_q, state_d;
// counter for AXI transfers
logic [ADDR_INDEX-1:0] cnt_d, cnt_q;
logic [(DATA_WIDTH/CVA6Cfg.AxiDataWidth)-1:0][CVA6Cfg.AxiDataWidth-1:0] cache_line_d, cache_line_q;
logic [(DATA_WIDTH/CVA6Cfg.AxiDataWidth)-1:0][CVA6Cfg.AxiDataWidth-1:0]
cache_line_d, cache_line_q;
// save the address for a read, as we allow for non-cacheline aligned accesses
logic [(DATA_WIDTH/CVA6Cfg.AxiDataWidth)-1:0] addr_offset_d, addr_offset_q;
logic [CVA6Cfg.AxiIdWidth-1:0] id_d, id_q;
@ -149,7 +163,9 @@ module axi_adapter #(
axi_req_o.w.last = 1'b1;
// single req can be granted here
gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
2'b11: state_d = WAIT_B_VALID;
2'b01: state_d = WAIT_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
@ -171,16 +187,16 @@ module axi_adapter #(
axi_req_o.w.data = wdata_i[0];
axi_req_o.w.strb = be_i[0];
if (axi_resp_i.w_ready)
cnt_d = BURST_SIZE[ADDR_INDEX-1:0] - 1;
else
cnt_d = BURST_SIZE[ADDR_INDEX-1:0];
if (axi_resp_i.w_ready) cnt_d = BURST_SIZE[ADDR_INDEX-1:0] - 1;
else cnt_d = BURST_SIZE[ADDR_INDEX-1:0];
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
case ({
axi_resp_i.aw_ready, axi_resp_i.w_ready
})
2'b11: state_d = WAIT_LAST_W_READY;
2'b01: state_d = WAIT_LAST_W_READY_AW_READY;
2'b10: state_d = WAIT_LAST_W_READY;
default:;
default: ;
endcase
end
// read
@ -234,12 +250,13 @@ module axi_adapter #(
// we are here because we want to write a cache line
axi_req_o.aw.len = BURST_SIZE[7:0];
// we got an aw_ready
case ({axi_resp_i.aw_ready, axi_resp_i.w_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 (cnt_q == 0)
state_d = WAIT_AW_READY_BURST;
if (cnt_q == 0) state_d = WAIT_AW_READY_BURST;
else // yes, so reduce the count and stay here
cnt_d = cnt_q - 1;
end
@ -255,7 +272,7 @@ module axi_adapter #(
cnt_d = cnt_q - 1;
end
end
default:;
default: ;
endcase
end
@ -347,10 +364,8 @@ module axi_adapter #(
// ~> cacheline read, single read
WAIT_R_VALID_MULTIPLE, WAIT_R_VALID: begin
if (CRITICAL_WORD_FIRST)
index = addr_offset_q + (BURST_SIZE[ADDR_INDEX-1:0]-cnt_q);
else
index = BURST_SIZE[ADDR_INDEX-1:0]-cnt_q;
if (CRITICAL_WORD_FIRST) index = addr_offset_q + (BURST_SIZE[ADDR_INDEX-1:0] - cnt_q);
else index = BURST_SIZE[ADDR_INDEX-1:0] - cnt_q;
// reads are always wrapping here
axi_req_o.r_ready = 1'b1;
@ -380,8 +395,7 @@ module axi_adapter #(
if (state_q == WAIT_R_VALID_MULTIPLE) begin
cache_line_d[index] = axi_resp_i.r.data;
end else
cache_line_d[0] = axi_resp_i.r.data;
end else cache_line_d[0] = axi_resp_i.r.data;
// Decrease the counter
cnt_d = cnt_q - 1;
@ -425,17 +439,25 @@ module axi_adapter #(
function automatic axi_pkg::atop_t atop_from_amo(ariane_pkg::amo_t amo);
axi_pkg::atop_t result = 6'b000000;
unique case(amo)
unique case (amo)
ariane_pkg::AMO_NONE: result = {axi_pkg::ATOP_NONE, 4'b0000};
ariane_pkg::AMO_SWAP: result = {axi_pkg::ATOP_ATOMICSWAP};
ariane_pkg::AMO_ADD : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
ariane_pkg::AMO_AND : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
ariane_pkg::AMO_OR : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
ariane_pkg::AMO_XOR : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
ariane_pkg::AMO_MAX : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
ariane_pkg::AMO_MAXU: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
ariane_pkg::AMO_MIN : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
ariane_pkg::AMO_MINU: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
ariane_pkg::AMO_ADD:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
ariane_pkg::AMO_AND:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
ariane_pkg::AMO_OR:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
ariane_pkg::AMO_XOR:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
ariane_pkg::AMO_MAX:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
ariane_pkg::AMO_MAXU:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
ariane_pkg::AMO_MIN:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
ariane_pkg::AMO_MINU:
result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
ariane_pkg::AMO_CAS1: result = {axi_pkg::ATOP_NONE, 4'b0000}; // Unsupported
ariane_pkg::AMO_CAS2: result = {axi_pkg::ATOP_NONE, 4'b0000}; // Unsupported
default: result = 6'b000000;

View file

@ -18,7 +18,10 @@
// Description: Cache controller
module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
module cache_ctrl
import ariane_pkg::*;
import std_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -68,7 +71,8 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
WAIT_TAG_SAVED, // 8
WAIT_MSHR, // 9
WAIT_CRITICAL_WORD // 10
} state_d, state_q;
}
state_d, state_q;
typedef struct packed {
logic [DCACHE_INDEX_WIDTH-1:0] index;
@ -93,9 +97,7 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
always_comb begin : way_select
cl_i = '0;
for (int unsigned i = 0; i < DCACHE_SET_ASSOC; i++)
if (hit_way_i[i])
cl_i = data_i[i].data;
for (int unsigned i = 0; i < DCACHE_SET_ASSOC; i++) if (hit_way_i[i]) cl_i = data_i[i].data;
// cl_i = data_i[one_hot_to_bin(hit_way_i)].data;
end
@ -160,8 +162,7 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
state_d = WAIT_TAG;
mem_req_d.bypass = 1'b0;
// only for a read
if (!req_port_i.data_we)
req_port_o.data_gnt = 1'b1;
if (!req_port_i.data_we) req_port_o.data_gnt = 1'b1;
end
end
end
@ -205,7 +206,7 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
end
// this is timing critical
req_port_o.data_rdata = cl_i[cl_offset +: 64];
req_port_o.data_rdata = cl_i[cl_offset+:64];
// report data for a read
if (!mem_req_q.we) begin
@ -250,7 +251,9 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
// -------------------------
// Check for cache-ability
// -------------------------
if (!config_pkg::is_inside_cacheable_regions(CVA6Cfg, {{{64-riscv::PLEN}{1'b0}}, tag_o, {DCACHE_INDEX_WIDTH{1'b0}}})) begin
if (!config_pkg::is_inside_cacheable_regions(
CVA6Cfg, {{{64 - riscv::PLEN} {1'b0}}, tag_o, {DCACHE_INDEX_WIDTH{1'b0}}}
)) begin
mem_req_d.bypass = 1'b1;
state_d = WAIT_REFILL_GNT;
end
@ -302,8 +305,8 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
be_o.vldrty = hit_way_q;
// set the correct byte enable
be_o.data[cl_offset>>3 +: 8] = mem_req_q.be;
data_o.data[cl_offset +: 64] = mem_req_q.wdata;
be_o.data[cl_offset>>3+:8] = mem_req_q.be;
data_o.data[cl_offset+:64] = mem_req_q.wdata;
// ~> change the state
data_o.dirty = 1'b1;
data_o.valid = 1'b1;
@ -358,12 +361,10 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
if (bypass_gnt_i) begin
state_d = WAIT_REFILL_VALID;
// if this was a write we still need to give a grant to the store unit
if (mem_req_q.we)
req_port_o.data_gnt = 1'b1;
if (mem_req_q.we) req_port_o.data_gnt = 1'b1;
end
if (miss_gnt_i && !mem_req_q.we)
state_d = WAIT_CRITICAL_WORD;
if (miss_gnt_i && !mem_req_q.we) state_d = WAIT_CRITICAL_WORD;
else if (miss_gnt_i) begin
state_d = IDLE;
req_port_o.data_gnt = 1'b1;
@ -426,9 +427,7 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
if (req_port_i.kill_req) begin
req_port_o.data_rvalid = 1'b1;
if (!(state_q inside {
WAIT_REFILL_GNT,
WAIT_CRITICAL_WORD})) begin
if (!(state_q inside {WAIT_REFILL_GNT, WAIT_CRITICAL_WORD})) begin
state_d = IDLE;
end
end
@ -450,14 +449,22 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
end
//pragma translate_off
`ifndef VERILATOR
`ifndef VERILATOR
initial begin
assert (DCACHE_LINE_WIDTH == 128) else $error ("Cacheline width has to be 128 for the moment. But only small changes required in data select logic");
assert (DCACHE_LINE_WIDTH == 128)
else
$error(
"Cacheline width has to be 128 for the moment. But only small changes required in data select logic"
);
end
// if the full MSHR address matches so should also match the partial one
partial_full_mshr_match: assert property(@(posedge clk_i) disable iff (~rst_ni) mshr_addr_matches_i -> mshr_index_matches_i) else $fatal (1, "partial mshr index doesn't match");
partial_full_mshr_match :
assert property(@(posedge clk_i) disable iff (~rst_ni) mshr_addr_matches_i -> mshr_index_matches_i)
else $fatal(1, "partial mshr index doesn't match");
// there should never be a valid answer when the MSHR matches and we are not being served
no_valid_on_mshr_match: assert property(@(posedge clk_i) disable iff (~rst_ni) (mshr_addr_matches_i && !active_serving_i)-> !req_port_o.data_rvalid || req_port_i.kill_req) else $fatal (1, "rvalid_o should not be set on MSHR match");
`endif
no_valid_on_mshr_match :
assert property(@(posedge clk_i) disable iff (~rst_ni) (mshr_addr_matches_i && !active_serving_i)-> !req_port_o.data_rvalid || req_port_i.kill_req)
else $fatal(1, "rvalid_o should not be set on MSHR match");
`endif
//pragma translate_on
endmodule

View file

@ -10,7 +10,7 @@
// Date: February, 2023
// Description: Interface adapter for the CVA6 core
module cva6_hpdcache_if_adapter
import hpdcache_pkg::*;
import hpdcache_pkg::*;
// Parameters
// {{{
@ -48,7 +48,7 @@ import hpdcache_pkg::*;
input logic hpdcache_rsp_valid_i,
input hpdcache_pkg::hpdcache_rsp_t hpdcache_rsp_i
);
// }}}
// }}}
// Internal nets and registers
// {{{
@ -62,11 +62,14 @@ import hpdcache_pkg::*;
// LOAD request
// {{{
if (is_load_port == 1'b1) begin : load_port_gen
assign hpdcache_req_is_uncacheable =
!config_pkg::is_inside_cacheable_regions(CVA6Cfg,
{{64 - ariane_pkg::DCACHE_TAG_WIDTH{1'b0}}
assign hpdcache_req_is_uncacheable = !config_pkg::is_inside_cacheable_regions(
CVA6Cfg,
{
{64 - ariane_pkg::DCACHE_TAG_WIDTH{1'b0}}
, cva6_req_i.address_tag
, {ariane_pkg::DCACHE_INDEX_WIDTH{1'b0}}});
, {ariane_pkg::DCACHE_INDEX_WIDTH{1'b0}}
}
);
// Request forwarding
assign hpdcache_req_valid_o = cva6_req_i.data_req,
@ -92,8 +95,7 @@ import hpdcache_pkg::*;
cva6_req_o.data_rdata = hpdcache_rsp_i.rdata,
cva6_req_o.data_rid = hpdcache_rsp_i.tid,
cva6_req_o.data_gnt = hpdcache_req_ready_i;
end
// }}}
end // }}}
// STORE/AMO request
// {{{
@ -109,21 +111,18 @@ import hpdcache_pkg::*;
// AMO logic
// {{{
always_comb
begin : amo_op_comb
always_comb begin : amo_op_comb
amo_addr = cva6_amo_req_i.operand_a;
amo_addr_offset = amo_addr[0 +: HPDCACHE_REQ_OFFSET_WIDTH];
amo_tag = amo_addr[HPDCACHE_REQ_OFFSET_WIDTH +: HPDCACHE_TAG_WIDTH];
amo_addr_offset = amo_addr[0+:HPDCACHE_REQ_OFFSET_WIDTH];
amo_tag = amo_addr[HPDCACHE_REQ_OFFSET_WIDTH+:HPDCACHE_TAG_WIDTH];
amo_is_word = (cva6_amo_req_i.size == 2'b10);
amo_is_word_hi = cva6_amo_req_i.operand_a[2];
amo_data = amo_is_word ? {2{cva6_amo_req_i.operand_b[0 +: 32]}}
: cva6_amo_req_i.operand_b;
amo_data = amo_is_word ? {2{cva6_amo_req_i.operand_b[0+:32]}} : cva6_amo_req_i.operand_b;
amo_data_be = amo_is_word_hi ? 8'hf0 :
amo_is_word ? 8'h0f : 8'hff;
amo_data_be = amo_is_word_hi ? 8'hf0 : amo_is_word ? 8'h0f : 8'hff;
unique case(cva6_amo_req_i.amo_op)
unique case (cva6_amo_req_i.amo_op)
ariane_pkg::AMO_LR: amo_op = HPDCACHE_REQ_AMO_LR;
ariane_pkg::AMO_SC: amo_op = HPDCACHE_REQ_AMO_SC;
ariane_pkg::AMO_SWAP: amo_op = HPDCACHE_REQ_AMO_SWAP;
@ -145,25 +144,23 @@ import hpdcache_pkg::*;
// Request forwarding
// {{{
assign hpdcache_req_is_uncacheable =
!config_pkg::is_inside_cacheable_regions(CVA6Cfg,
{{64 - ariane_pkg::DCACHE_TAG_WIDTH{1'b0}}
, hpdcache_req_o.addr_tag, {ariane_pkg::DCACHE_INDEX_WIDTH{1'b0}}});
assign hpdcache_req_is_uncacheable = !config_pkg::is_inside_cacheable_regions(
CVA6Cfg,
{
{64 - ariane_pkg::DCACHE_TAG_WIDTH{1'b0}}
, hpdcache_req_o.addr_tag,
{ariane_pkg::DCACHE_INDEX_WIDTH{1'b0}}
}
);
assign forward_store = cva6_req_i.data_req,
forward_amo = cva6_amo_req_i.req;
assign forward_store = cva6_req_i.data_req, forward_amo = cva6_amo_req_i.req;
assign hpdcache_req_valid_o = forward_store | forward_amo,
hpdcache_req_o.addr_offset = forward_amo ? amo_addr_offset
: cva6_req_i.address_index,
hpdcache_req_o.wdata = forward_amo ? amo_data
: cva6_req_i.data_wdata,
hpdcache_req_o.op = forward_amo ? amo_op
: hpdcache_pkg::HPDCACHE_REQ_STORE,
hpdcache_req_o.be = forward_amo ? amo_data_be
: cva6_req_i.data_be,
hpdcache_req_o.size = forward_amo ? cva6_amo_req_i.size
: cva6_req_i.data_size,
hpdcache_req_o.addr_offset = forward_amo ? amo_addr_offset : cva6_req_i.address_index,
hpdcache_req_o.wdata = forward_amo ? amo_data : cva6_req_i.data_wdata,
hpdcache_req_o.op = forward_amo ? amo_op : hpdcache_pkg::HPDCACHE_REQ_STORE,
hpdcache_req_o.be = forward_amo ? amo_data_be : cva6_req_i.data_be,
hpdcache_req_o.size = forward_amo ? cva6_amo_req_i.size : cva6_req_i.data_size,
hpdcache_req_o.sid = hpdcache_req_sid_i,
hpdcache_req_o.tid = forward_amo ? '1 : '0,
hpdcache_req_o.need_rsp = forward_amo,
@ -195,9 +192,9 @@ import hpdcache_pkg::*;
// Assertions
// {{{
// pragma translate_off
forward_one_request_assert: assert property (@(posedge clk_i)
($onehot0({forward_store, forward_amo}))) else
$error("Only one request shall be forwarded");
forward_one_request_assert :
assert property (@(posedge clk_i) ($onehot0({forward_store, forward_amo})))
else $error("Only one request shall be forwarded");
// pragma translate_on
// }}}
endmodule

View file

@ -76,7 +76,7 @@ module cva6_hpdcache_subsystem
input logic [NrHwPrefetchers-1:0] hwpf_throttle_set_i,
input logic [NrHwPrefetchers-1:0][63:0] hwpf_throttle_i,
output logic [NrHwPrefetchers-1:0][63:0] hwpf_throttle_o,
output logic [63:0] hwpf_status_o,
output logic [ 63:0] hwpf_status_o,
// }}}
// AXI port to upstream memory/peripherals
@ -85,7 +85,7 @@ module cva6_hpdcache_subsystem
input noc_resp_t noc_resp_i
// }}}
);
// }}}
// }}}
`include "axi/typedef.svh"
@ -100,7 +100,7 @@ module cva6_hpdcache_subsystem
localparam int ICACHE_RDTXID = 1 << (ariane_pkg::MEM_TID_WIDTH - 1);
cva6_icache #(
.CVA6Cfg (CVA6Cfg),
.CVA6Cfg(CVA6Cfg),
.RdTxId (ICACHE_RDTXID)
) i_cva6_icache (
.clk_i (clk_i),
@ -112,10 +112,10 @@ module cva6_hpdcache_subsystem
.areq_o (icache_areq_o),
.dreq_i (icache_dreq_i),
.dreq_o (icache_dreq_o),
.mem_rtrn_vld_i (icache_miss_resp_valid),
.mem_rtrn_vld_i(icache_miss_resp_valid),
.mem_rtrn_i (icache_miss_resp),
.mem_data_req_o (icache_miss_valid),
.mem_data_ack_i (icache_miss_ready),
.mem_data_req_o(icache_miss_valid),
.mem_data_ack_i(icache_miss_ready),
.mem_data_o (icache_miss)
);
// }}}
@ -146,21 +146,21 @@ module cva6_hpdcache_subsystem
typedef logic [63:0] hwpf_stride_param_t;
logic dcache_req_valid [HPDCACHE_NREQUESTERS-1:0];
logic dcache_req_ready [HPDCACHE_NREQUESTERS-1:0];
logic dcache_req_valid[HPDCACHE_NREQUESTERS-1:0];
logic dcache_req_ready[HPDCACHE_NREQUESTERS-1:0];
hpdcache_pkg::hpdcache_req_t dcache_req [HPDCACHE_NREQUESTERS-1:0];
logic dcache_req_abort [HPDCACHE_NREQUESTERS-1:0];
logic dcache_req_abort[HPDCACHE_NREQUESTERS-1:0];
hpdcache_pkg::hpdcache_tag_t dcache_req_tag [HPDCACHE_NREQUESTERS-1:0];
hpdcache_pkg::hpdcache_pma_t dcache_req_pma [HPDCACHE_NREQUESTERS-1:0];
logic dcache_rsp_valid [HPDCACHE_NREQUESTERS-1:0];
logic dcache_rsp_valid[HPDCACHE_NREQUESTERS-1:0];
hpdcache_pkg::hpdcache_rsp_t dcache_rsp [HPDCACHE_NREQUESTERS-1:0];
logic dcache_read_miss, dcache_write_miss;
logic [2:0] snoop_valid;
logic [2:0] snoop_abort;
hpdcache_pkg::hpdcache_req_offset_t [2:0] snoop_addr_offset;
hpdcache_pkg::hpdcache_tag_t [2:0] snoop_addr_tag;
logic [2:0] snoop_phys_indexed;
logic [ 2:0] snoop_valid;
logic [ 2:0] snoop_abort;
hpdcache_pkg::hpdcache_req_offset_t [ 2:0] snoop_addr_offset;
hpdcache_pkg::hpdcache_tag_t [ 2:0] snoop_addr_tag;
logic [ 2:0] snoop_phys_indexed;
logic dcache_cmo_req_is_prefetch;
@ -210,89 +210,89 @@ module cva6_hpdcache_subsystem
generate
ariane_pkg::dcache_req_i_t dcache_req_ports[HPDCACHE_NREQUESTERS-1:0];
for (genvar r = 0; r < (NumPorts-1); r++) begin : cva6_hpdcache_load_if_adapter_gen
for (genvar r = 0; r < (NumPorts - 1); r++) begin : cva6_hpdcache_load_if_adapter_gen
assign dcache_req_ports[r] = dcache_req_ports_i[r];
cva6_hpdcache_if_adapter #(
.CVA6Cfg (CVA6Cfg),
.is_load_port (1'b1)
.is_load_port(1'b1)
) i_cva6_hpdcache_load_if_adapter (
.clk_i,
.rst_ni,
.hpdcache_req_sid_i (hpdcache_pkg::hpdcache_req_sid_t'(r)),
.hpdcache_req_sid_i(hpdcache_pkg::hpdcache_req_sid_t'(r)),
.cva6_req_i (dcache_req_ports[r]),
.cva6_req_o (dcache_req_ports_o[r]),
.cva6_amo_req_i ('0),
.cva6_amo_resp_o (/* unused */),
.cva6_amo_resp_o( /* unused */),
.hpdcache_req_valid_o (dcache_req_valid[r]),
.hpdcache_req_ready_i (dcache_req_ready[r]),
.hpdcache_req_valid_o(dcache_req_valid[r]),
.hpdcache_req_ready_i(dcache_req_ready[r]),
.hpdcache_req_o (dcache_req[r]),
.hpdcache_req_abort_o (dcache_req_abort[r]),
.hpdcache_req_abort_o(dcache_req_abort[r]),
.hpdcache_req_tag_o (dcache_req_tag[r]),
.hpdcache_req_pma_o (dcache_req_pma[r]),
.hpdcache_rsp_valid_i (dcache_rsp_valid[r]),
.hpdcache_rsp_valid_i(dcache_rsp_valid[r]),
.hpdcache_rsp_i (dcache_rsp[r])
);
end
cva6_hpdcache_if_adapter #(
.CVA6Cfg (CVA6Cfg),
.is_load_port (1'b0)
.is_load_port(1'b0)
) i_cva6_hpdcache_store_if_adapter (
.clk_i,
.rst_ni,
.hpdcache_req_sid_i (hpdcache_pkg::hpdcache_req_sid_t'(NumPorts-1)),
.hpdcache_req_sid_i(hpdcache_pkg::hpdcache_req_sid_t'(NumPorts - 1)),
.cva6_req_i (dcache_req_ports_i[NumPorts-1]),
.cva6_req_o (dcache_req_ports_o[NumPorts-1]),
.cva6_amo_req_i (dcache_amo_req_i),
.cva6_amo_resp_o (dcache_amo_resp_o),
.cva6_amo_resp_o(dcache_amo_resp_o),
.hpdcache_req_valid_o (dcache_req_valid[NumPorts-1]),
.hpdcache_req_ready_i (dcache_req_ready[NumPorts-1]),
.hpdcache_req_valid_o(dcache_req_valid[NumPorts-1]),
.hpdcache_req_ready_i(dcache_req_ready[NumPorts-1]),
.hpdcache_req_o (dcache_req[NumPorts-1]),
.hpdcache_req_abort_o (dcache_req_abort[NumPorts-1]),
.hpdcache_req_abort_o(dcache_req_abort[NumPorts-1]),
.hpdcache_req_tag_o (dcache_req_tag[NumPorts-1]),
.hpdcache_req_pma_o (dcache_req_pma[NumPorts-1]),
.hpdcache_rsp_valid_i (dcache_rsp_valid[NumPorts-1]),
.hpdcache_rsp_valid_i(dcache_rsp_valid[NumPorts-1]),
.hpdcache_rsp_i (dcache_rsp[NumPorts-1])
);
`ifdef HPDCACHE_ENABLE_CMO
cva6_hpdcache_cmo_if_adapter #(
.cmo_req_t (cmo_req_t),
.cmo_rsp_t (cmo_rsp_t)
.cmo_req_t(cmo_req_t),
.cmo_rsp_t(cmo_rsp_t)
) i_cva6_hpdcache_cmo_if_adapter (
.clk_i,
.rst_ni,
.dcache_req_sid_i (hpdcache_pkg::hpdcache_req_sid_t'(NumPorts)),
.dcache_req_sid_i(hpdcache_pkg::hpdcache_req_sid_t'(NumPorts)),
.cva6_cmo_req_i (dcache_cmo_req_i),
.cva6_cmo_resp_o (dcache_cmo_resp_o),
.cva6_cmo_resp_o(dcache_cmo_resp_o),
.dcache_req_valid_o (dcache_req_valid[NumPorts]),
.dcache_req_ready_i (dcache_req_ready[NumPorts]),
.dcache_req_valid_o(dcache_req_valid[NumPorts]),
.dcache_req_ready_i(dcache_req_ready[NumPorts]),
.dcache_req_o (dcache_req[NumPorts]),
.dcache_req_abort_o (dcache_req_abort[NumPorts]),
.dcache_req_abort_o(dcache_req_abort[NumPorts]),
.dcache_req_tag_o (dcache_req_tag[NumPorts]),
.dcache_req_pma_o (dcache_req_pma[NumPorts]),
.dcache_rsp_valid_i (dcache_rsp_valid[NumPorts]),
.dcache_rsp_valid_i(dcache_rsp_valid[NumPorts]),
.dcache_rsp_i (dcache_rsp[NumPorts])
);
`else
assign dcache_req_valid[NumPorts] = 1'b0,
dcache_req [NumPorts] = '0,
dcache_req[NumPorts] = '0,
dcache_req_abort[NumPorts] = 1'b0,
dcache_req_tag [NumPorts] = '0,
dcache_req_pma [NumPorts] = '0;
dcache_req_tag[NumPorts] = '0,
dcache_req_pma[NumPorts] = '0;
`endif
endgenerate
@ -312,8 +312,9 @@ module cva6_hpdcache_subsystem
`ifdef HPDCACHE_ENABLE_CMO
// Snoop CMO port (in case of read prefetch accesses)
assign dcache_cmo_req_is_prefetch =
hpdcache_pkg::is_cmo_prefetch(dcache_req[NumPorts].op, dcache_req[NumPorts].size);
assign dcache_cmo_req_is_prefetch = hpdcache_pkg::is_cmo_prefetch(
dcache_req[NumPorts].op, dcache_req[NumPorts].size
);
assign snoop_valid[2] = dcache_req_valid[NumPorts]
& dcache_req_ready[NumPorts]
& dcache_cmo_req_is_prefetch,
@ -337,8 +338,8 @@ module cva6_hpdcache_subsystem
endgenerate
hwpf_stride_wrapper #(
.NUM_HW_PREFETCH (NrHwPrefetchers),
.NUM_SNOOP_PORTS (3)
.NUM_HW_PREFETCH(NrHwPrefetchers),
.NUM_SNOOP_PORTS(3)
) i_hwpf_stride_wrapper (
.clk_i,
.rst_ni,
@ -349,7 +350,7 @@ module cva6_hpdcache_subsystem
.hwpf_stride_param_set_i (hwpf_param_set_i),
.hwpf_stride_param_i (hwpf_param_i),
.hwpf_stride_param_o (hwpf_param_o),
.hwpf_stride_throttle_set_i (hwpf_throttle_set_i),
.hwpf_stride_throttle_set_i(hwpf_throttle_set_i),
.hwpf_stride_throttle_i (hwpf_throttle_in),
.hwpf_stride_throttle_o (hwpf_throttle_out),
.hwpf_stride_status_o (hwpf_status_o),
@ -358,17 +359,17 @@ module cva6_hpdcache_subsystem
.snoop_abort_i (snoop_abort),
.snoop_addr_offset_i (snoop_addr_offset),
.snoop_addr_tag_i (snoop_addr_tag),
.snoop_phys_indexed_i (snoop_phys_indexed),
.snoop_phys_indexed_i(snoop_phys_indexed),
.hpdcache_req_sid_i (hpdcache_pkg::hpdcache_req_sid_t'(NumPorts+1)),
.hpdcache_req_sid_i(hpdcache_pkg::hpdcache_req_sid_t'(NumPorts + 1)),
.hpdcache_req_valid_o (dcache_req_valid[NumPorts+1]),
.hpdcache_req_ready_i (dcache_req_ready[NumPorts+1]),
.hpdcache_req_valid_o(dcache_req_valid[NumPorts+1]),
.hpdcache_req_ready_i(dcache_req_ready[NumPorts+1]),
.hpdcache_req_o (dcache_req[NumPorts+1]),
.hpdcache_req_abort_o (dcache_req_abort[NumPorts+1]),
.hpdcache_req_abort_o(dcache_req_abort[NumPorts+1]),
.hpdcache_req_tag_o (dcache_req_tag[NumPorts+1]),
.hpdcache_req_pma_o (dcache_req_pma[NumPorts+1]),
.hpdcache_rsp_valid_i (dcache_rsp_valid[NumPorts+1]),
.hpdcache_rsp_valid_i(dcache_rsp_valid[NumPorts+1]),
.hpdcache_rsp_i (dcache_rsp[NumPorts+1])
);
@ -378,93 +379,91 @@ module cva6_hpdcache_subsystem
.HPDcacheMemDataWidth (CVA6Cfg.AxiDataWidth),
.hpdcache_mem_req_t (hpdcache_mem_req_t),
.hpdcache_mem_req_w_t (hpdcache_mem_req_w_t),
.hpdcache_mem_resp_r_t (hpdcache_mem_resp_r_t),
.hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t)
) i_hpdcache(
.hpdcache_mem_resp_r_t(hpdcache_mem_resp_r_t),
.hpdcache_mem_resp_w_t(hpdcache_mem_resp_w_t)
) i_hpdcache (
.clk_i,
.rst_ni,
.wbuf_flush_i (dcache_flush_i),
.wbuf_flush_i(dcache_flush_i),
.core_req_valid_i (dcache_req_valid),
.core_req_ready_o (dcache_req_ready),
.core_req_valid_i(dcache_req_valid),
.core_req_ready_o(dcache_req_ready),
.core_req_i (dcache_req),
.core_req_abort_i (dcache_req_abort),
.core_req_abort_i(dcache_req_abort),
.core_req_tag_i (dcache_req_tag),
.core_req_pma_i (dcache_req_pma),
.core_rsp_valid_o (dcache_rsp_valid),
.core_rsp_valid_o(dcache_rsp_valid),
.core_rsp_o (dcache_rsp),
.mem_req_miss_read_ready_i (dcache_miss_ready),
.mem_req_miss_read_valid_o (dcache_miss_valid),
.mem_req_miss_read_ready_i(dcache_miss_ready),
.mem_req_miss_read_valid_o(dcache_miss_valid),
.mem_req_miss_read_o (dcache_miss),
.mem_resp_miss_read_ready_o (dcache_miss_resp_ready),
.mem_resp_miss_read_valid_i (dcache_miss_resp_valid),
.mem_resp_miss_read_ready_o(dcache_miss_resp_ready),
.mem_resp_miss_read_valid_i(dcache_miss_resp_valid),
.mem_resp_miss_read_i (dcache_miss_resp),
.mem_req_wbuf_write_ready_i (dcache_wbuf_ready),
.mem_req_wbuf_write_valid_o (dcache_wbuf_valid),
.mem_req_wbuf_write_ready_i(dcache_wbuf_ready),
.mem_req_wbuf_write_valid_o(dcache_wbuf_valid),
.mem_req_wbuf_write_o (dcache_wbuf),
.mem_req_wbuf_write_data_ready_i (dcache_wbuf_data_ready),
.mem_req_wbuf_write_data_valid_o (dcache_wbuf_data_valid),
.mem_req_wbuf_write_data_ready_i(dcache_wbuf_data_ready),
.mem_req_wbuf_write_data_valid_o(dcache_wbuf_data_valid),
.mem_req_wbuf_write_data_o (dcache_wbuf_data),
.mem_resp_wbuf_write_ready_o (dcache_wbuf_resp_ready),
.mem_resp_wbuf_write_valid_i (dcache_wbuf_resp_valid),
.mem_resp_wbuf_write_ready_o(dcache_wbuf_resp_ready),
.mem_resp_wbuf_write_valid_i(dcache_wbuf_resp_valid),
.mem_resp_wbuf_write_i (dcache_wbuf_resp),
.mem_req_uc_read_ready_i (dcache_uc_read_ready),
.mem_req_uc_read_valid_o (dcache_uc_read_valid),
.mem_req_uc_read_ready_i(dcache_uc_read_ready),
.mem_req_uc_read_valid_o(dcache_uc_read_valid),
.mem_req_uc_read_o (dcache_uc_read),
.mem_resp_uc_read_ready_o (dcache_uc_read_resp_ready),
.mem_resp_uc_read_valid_i (dcache_uc_read_resp_valid),
.mem_resp_uc_read_ready_o(dcache_uc_read_resp_ready),
.mem_resp_uc_read_valid_i(dcache_uc_read_resp_valid),
.mem_resp_uc_read_i (dcache_uc_read_resp),
.mem_req_uc_write_ready_i (dcache_uc_write_ready),
.mem_req_uc_write_valid_o (dcache_uc_write_valid),
.mem_req_uc_write_ready_i(dcache_uc_write_ready),
.mem_req_uc_write_valid_o(dcache_uc_write_valid),
.mem_req_uc_write_o (dcache_uc_write),
.mem_req_uc_write_data_ready_i (dcache_uc_write_data_ready),
.mem_req_uc_write_data_valid_o (dcache_uc_write_data_valid),
.mem_req_uc_write_data_ready_i(dcache_uc_write_data_ready),
.mem_req_uc_write_data_valid_o(dcache_uc_write_data_valid),
.mem_req_uc_write_data_o (dcache_uc_write_data),
.mem_resp_uc_write_ready_o (dcache_uc_write_resp_ready),
.mem_resp_uc_write_valid_i (dcache_uc_write_resp_valid),
.mem_resp_uc_write_ready_o(dcache_uc_write_resp_ready),
.mem_resp_uc_write_valid_i(dcache_uc_write_resp_valid),
.mem_resp_uc_write_i (dcache_uc_write_resp),
.evt_cache_write_miss_o (dcache_write_miss),
.evt_cache_write_miss_o(dcache_write_miss),
.evt_cache_read_miss_o (dcache_read_miss),
.evt_uncached_req_o (/* unused */),
.evt_cmo_req_o (/* unused */),
.evt_write_req_o (/* unused */),
.evt_read_req_o (/* unused */),
.evt_prefetch_req_o (/* unused */),
.evt_req_on_hold_o (/* unused */),
.evt_rtab_rollback_o (/* unused */),
.evt_stall_refill_o (/* unused */),
.evt_stall_o (/* unused */),
.evt_uncached_req_o ( /* unused */),
.evt_cmo_req_o ( /* unused */),
.evt_write_req_o ( /* unused */),
.evt_read_req_o ( /* unused */),
.evt_prefetch_req_o ( /* unused */),
.evt_req_on_hold_o ( /* unused */),
.evt_rtab_rollback_o ( /* unused */),
.evt_stall_refill_o ( /* unused */),
.evt_stall_o ( /* unused */),
.wbuf_empty_o (wbuffer_empty_o),
.wbuf_empty_o(wbuffer_empty_o),
.cfg_enable_i (dcache_enable_i),
.cfg_wbuf_threshold_i (4'd2),
.cfg_wbuf_reset_timecnt_on_write_i (1'b1),
.cfg_wbuf_sequential_waw_i (1'b0),
.cfg_wbuf_inhibit_write_coalescing_i (1'b0),
.cfg_wbuf_inhibit_write_coalescing_i(1'b0),
.cfg_prefetch_updt_plru_i (1'b1),
.cfg_error_on_cacheable_amo_i (1'b0),
.cfg_rtab_single_entry_i (1'b0)
);
assign dcache_miss_o = dcache_read_miss,
wbuffer_not_ni_o = wbuffer_empty_o;
assign dcache_miss_o = dcache_read_miss, wbuffer_not_ni_o = wbuffer_empty_o;
always_ff @(posedge clk_i or negedge rst_ni)
begin : dcache_flush_ff
always_ff @(posedge clk_i or negedge rst_ni) begin : dcache_flush_ff
if (!rst_ni) dcache_flush_ack_o <= 1'b0;
else dcache_flush_ack_o <= ~dcache_flush_ack_o & dcache_flush_i;
end
@ -489,15 +488,15 @@ module cva6_hpdcache_subsystem
.HPDcacheMemDataWidth (CVA6Cfg.AxiDataWidth),
.hpdcache_mem_req_t (hpdcache_mem_req_t),
.hpdcache_mem_req_w_t (hpdcache_mem_req_w_t),
.hpdcache_mem_resp_r_t (hpdcache_mem_resp_r_t),
.hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t),
.hpdcache_mem_resp_r_t(hpdcache_mem_resp_r_t),
.hpdcache_mem_resp_w_t(hpdcache_mem_resp_w_t),
.AxiAddrWidth (CVA6Cfg.AxiAddrWidth),
.AxiDataWidth (CVA6Cfg.AxiDataWidth),
.AxiIdWidth (CVA6Cfg.AxiIdWidth),
.AxiUserWidth (CVA6Cfg.AxiUserWidth),
.axi_ar_chan_t (axi_ar_chan_t),
.axi_aw_chan_t (axi_aw_chan_t),
.axi_ar_chan_t(axi_ar_chan_t),
.axi_aw_chan_t(axi_aw_chan_t),
.axi_w_chan_t (axi_w_chan_t),
.axi_req_t (noc_req_t),
.axi_rsp_t (noc_resp_t)
@ -505,82 +504,104 @@ module cva6_hpdcache_subsystem
.clk_i,
.rst_ni,
.icache_miss_valid_i (icache_miss_valid),
.icache_miss_ready_o (icache_miss_ready),
.icache_miss_valid_i(icache_miss_valid),
.icache_miss_ready_o(icache_miss_ready),
.icache_miss_i (icache_miss),
.icache_miss_id_i (hpdcache_mem_id_t'(ICACHE_RDTXID)),
.icache_miss_resp_valid_o (icache_miss_resp_valid),
.icache_miss_resp_valid_o(icache_miss_resp_valid),
.icache_miss_resp_o (icache_miss_resp),
.dcache_miss_ready_o (dcache_miss_ready),
.dcache_miss_valid_i (dcache_miss_valid),
.dcache_miss_ready_o(dcache_miss_ready),
.dcache_miss_valid_i(dcache_miss_valid),
.dcache_miss_i (dcache_miss),
.dcache_miss_resp_ready_i (dcache_miss_resp_ready),
.dcache_miss_resp_valid_o (dcache_miss_resp_valid),
.dcache_miss_resp_ready_i(dcache_miss_resp_ready),
.dcache_miss_resp_valid_o(dcache_miss_resp_valid),
.dcache_miss_resp_o (dcache_miss_resp),
.dcache_wbuf_ready_o (dcache_wbuf_ready),
.dcache_wbuf_valid_i (dcache_wbuf_valid),
.dcache_wbuf_ready_o(dcache_wbuf_ready),
.dcache_wbuf_valid_i(dcache_wbuf_valid),
.dcache_wbuf_i (dcache_wbuf),
.dcache_wbuf_data_ready_o (dcache_wbuf_data_ready),
.dcache_wbuf_data_valid_i (dcache_wbuf_data_valid),
.dcache_wbuf_data_ready_o(dcache_wbuf_data_ready),
.dcache_wbuf_data_valid_i(dcache_wbuf_data_valid),
.dcache_wbuf_data_i (dcache_wbuf_data),
.dcache_wbuf_resp_ready_i (dcache_wbuf_resp_ready),
.dcache_wbuf_resp_valid_o (dcache_wbuf_resp_valid),
.dcache_wbuf_resp_ready_i(dcache_wbuf_resp_ready),
.dcache_wbuf_resp_valid_o(dcache_wbuf_resp_valid),
.dcache_wbuf_resp_o (dcache_wbuf_resp),
.dcache_uc_read_ready_o (dcache_uc_read_ready),
.dcache_uc_read_valid_i (dcache_uc_read_valid),
.dcache_uc_read_ready_o(dcache_uc_read_ready),
.dcache_uc_read_valid_i(dcache_uc_read_valid),
.dcache_uc_read_i (dcache_uc_read),
.dcache_uc_read_id_i ('1),
.dcache_uc_read_resp_ready_i (dcache_uc_read_resp_ready),
.dcache_uc_read_resp_valid_o (dcache_uc_read_resp_valid),
.dcache_uc_read_resp_ready_i(dcache_uc_read_resp_ready),
.dcache_uc_read_resp_valid_o(dcache_uc_read_resp_valid),
.dcache_uc_read_resp_o (dcache_uc_read_resp),
.dcache_uc_write_ready_o (dcache_uc_write_ready),
.dcache_uc_write_valid_i (dcache_uc_write_valid),
.dcache_uc_write_ready_o(dcache_uc_write_ready),
.dcache_uc_write_valid_i(dcache_uc_write_valid),
.dcache_uc_write_i (dcache_uc_write),
.dcache_uc_write_id_i ('1),
.dcache_uc_write_data_ready_o (dcache_uc_write_data_ready),
.dcache_uc_write_data_valid_i (dcache_uc_write_data_valid),
.dcache_uc_write_data_ready_o(dcache_uc_write_data_ready),
.dcache_uc_write_data_valid_i(dcache_uc_write_data_valid),
.dcache_uc_write_data_i (dcache_uc_write_data),
.dcache_uc_write_resp_ready_i (dcache_uc_write_resp_ready),
.dcache_uc_write_resp_valid_o (dcache_uc_write_resp_valid),
.dcache_uc_write_resp_ready_i(dcache_uc_write_resp_ready),
.dcache_uc_write_resp_valid_o(dcache_uc_write_resp_valid),
.dcache_uc_write_resp_o (dcache_uc_write_resp),
.axi_req_o (noc_req_o),
.axi_resp_i (noc_resp_i)
.axi_resp_i(noc_resp_i)
);
// }}}
// Assertions
// {{{
// pragma translate_off
initial assert (hpdcache_pkg::HPDCACHE_REQ_SRC_ID_WIDTH >= $clog2(HPDCACHE_NREQUESTERS))
initial
assert (hpdcache_pkg::HPDCACHE_REQ_SRC_ID_WIDTH >= $clog2(HPDCACHE_NREQUESTERS))
else $fatal(1, "HPDCACHE_REQ_SRC_ID_WIDTH is not wide enough");
a_invalid_instruction_fetch: assert property (
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);
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 (
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);
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
);
for (genvar j=0; j<2; j++) begin : gen_assertion
a_invalid_read_data: assert property (
for (genvar j = 0; j < 2; j++) begin : gen_assertion
a_invalid_read_data :
assert property (
@(posedge clk_i) disable iff (!rst_ni) dcache_req_ports_o[j].data_rvalid && ~dcache_req_ports_i[j].kill_req |-> (|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);
else
$warning(
1,
"[l1 dcache] reading invalid data on port %01d: data=%016X",
j,
dcache_req_ports_o[j].data_rdata
);
end
// pragma translate_on
// }}}

View file

@ -108,7 +108,7 @@ module cva6_hpdcache_subsystem_axi_arbiter
input axi_rsp_t axi_resp_i
// }}}
);
// }}}
// }}}
// Internal type definitions
// {{{
@ -133,15 +133,17 @@ module cva6_hpdcache_subsystem_axi_arbiter
// Adapt the I$ interface to the HPDcache memory interface
// {{{
localparam int ICACHE_CL_WORDS = ariane_pkg::ICACHE_LINE_WIDTH/64;
localparam int ICACHE_CL_WORDS = ariane_pkg::ICACHE_LINE_WIDTH / 64;
localparam int ICACHE_CL_WORD_INDEX = $clog2(ICACHE_CL_WORDS);
localparam int ICACHE_CL_SIZE = $clog2(ariane_pkg::ICACHE_LINE_WIDTH/8);
localparam int ICACHE_CL_SIZE = $clog2(ariane_pkg::ICACHE_LINE_WIDTH / 8);
localparam int ICACHE_WORD_SIZE = 3;
localparam int ICACHE_MEM_REQ_CL_LEN =
(ariane_pkg::ICACHE_LINE_WIDTH + HPDcacheMemDataWidth - 1)/HPDcacheMemDataWidth;
localparam int ICACHE_MEM_REQ_CL_SIZE =
(HPDcacheMemDataWidth <= ariane_pkg::ICACHE_LINE_WIDTH) ?
$clog2(HPDcacheMemDataWidth/8) : ICACHE_CL_SIZE;
$clog2(
HPDcacheMemDataWidth / 8
) : ICACHE_CL_SIZE;
// I$ request
hpdcache_mem_req_t icache_miss_req_wdata;
@ -159,22 +161,21 @@ module cva6_hpdcache_subsystem_axi_arbiter
// - Cut a possible long timing path.
hpdcache_fifo_reg #(
.FIFO_DEPTH (1),
.fifo_data_t (hpdcache_mem_req_t)
) i_icache_miss_req_fifo(
.fifo_data_t(hpdcache_mem_req_t)
) i_icache_miss_req_fifo (
.clk_i,
.rst_ni,
.w_i (icache_miss_req_w),
.wok_o (icache_miss_req_wok),
.wdata_i (icache_miss_req_wdata),
.wdata_i(icache_miss_req_wdata),
.r_i (icache_miss_req_r),
.rok_o (icache_miss_req_rok),
.rdata_o (icache_miss_req_rdata)
.rdata_o(icache_miss_req_rdata)
);
assign icache_miss_req_w = icache_miss_valid_i,
icache_miss_ready_o = icache_miss_req_wok;
assign icache_miss_req_w = icache_miss_valid_i, icache_miss_ready_o = icache_miss_req_wok;
assign icache_miss_req_wdata.mem_req_addr = icache_miss_i.paddr,
icache_miss_req_wdata.mem_req_len = icache_miss_i.nc ? 0 : ICACHE_MEM_REQ_CL_LEN - 1,
@ -203,43 +204,41 @@ module cva6_hpdcache_subsystem_axi_arbiter
if (HPDcacheMemDataWidth < ariane_pkg::ICACHE_LINE_WIDTH) begin
hpdcache_fifo_reg #(
.FIFO_DEPTH (1),
.fifo_data_t (hpdcache_mem_id_t)
.fifo_data_t(hpdcache_mem_id_t)
) i_icache_refill_meta_fifo (
.clk_i,
.rst_ni,
.w_i (icache_miss_resp_meta_w),
.wok_o (icache_miss_resp_meta_wok),
.wdata_i (icache_miss_resp_wdata.mem_resp_r_id),
.wdata_i(icache_miss_resp_wdata.mem_resp_r_id),
.r_i (icache_miss_resp_meta_r),
.rok_o (icache_miss_resp_meta_rok),
.rdata_o (icache_miss_resp_meta_id)
.rdata_o(icache_miss_resp_meta_id)
);
hpdcache_data_upsize #(
.WR_WIDTH (HPDcacheMemDataWidth),
.RD_WIDTH (ariane_pkg::ICACHE_LINE_WIDTH),
.WR_WIDTH(HPDcacheMemDataWidth),
.RD_WIDTH(ariane_pkg::ICACHE_LINE_WIDTH),
.DEPTH (1)
) i_icache_hpdcache_data_upsize (
.clk_i,
.rst_ni,
.w_i (icache_miss_resp_data_w),
.wlast_i (icache_miss_resp_wdata.mem_resp_r_last),
.wlast_i(icache_miss_resp_wdata.mem_resp_r_last),
.wok_o (icache_miss_resp_data_wok),
.wdata_i (icache_miss_resp_wdata.mem_resp_r_data),
.wdata_i(icache_miss_resp_wdata.mem_resp_r_data),
.r_i (icache_miss_resp_data_r),
.rok_o (icache_miss_resp_data_rok),
.rdata_o (icache_miss_resp_data_rdata)
.rdata_o(icache_miss_resp_data_rdata)
);
assign icache_miss_resp_meta_r = 1'b1,
icache_miss_resp_data_r = 1'b1;
assign icache_miss_resp_meta_r = 1'b1, icache_miss_resp_data_r = 1'b1;
assign icache_miss_resp_meta_w = icache_miss_resp_w &
icache_miss_resp_wdata.mem_resp_r_last;
assign icache_miss_resp_meta_w = icache_miss_resp_w & icache_miss_resp_wdata.mem_resp_r_last;
assign icache_miss_resp_data_w = icache_miss_resp_w;
@ -256,14 +255,13 @@ module cva6_hpdcache_subsystem_axi_arbiter
assign icache_miss_resp_data_rdata = icache_miss_resp_wdata.mem_resp_r_data;
// In the case of uncacheable accesses, the Icache expects the data to be right-aligned
always_comb
begin : icache_miss_resp_data_comb
always_comb begin : icache_miss_resp_data_comb
if (!icache_miss_req_rdata.mem_req_cacheable) begin
automatic logic [ICACHE_CL_WORD_INDEX - 1: 0] icache_miss_word_index;
automatic logic [ICACHE_CL_WORD_INDEX - 1:0] icache_miss_word_index;
automatic logic [63:0] icache_miss_word;
icache_miss_word_index = icache_miss_req_rdata.mem_req_addr[3 +: ICACHE_CL_WORD_INDEX];
icache_miss_word = icache_miss_resp_data_rdata[icache_miss_word_index*64 +: 64];
icache_miss_rdata = {{ariane_pkg::ICACHE_LINE_WIDTH-64{1'b0}}, icache_miss_word};
icache_miss_word_index = icache_miss_req_rdata.mem_req_addr[3+:ICACHE_CL_WORD_INDEX];
icache_miss_word = icache_miss_resp_data_rdata[icache_miss_word_index*64+:64];
icache_miss_rdata = {{ariane_pkg::ICACHE_LINE_WIDTH - 64{1'b0}}, icache_miss_word};
end else begin
icache_miss_rdata = icache_miss_resp_data_rdata;
end
@ -307,17 +305,17 @@ module cva6_hpdcache_subsystem_axi_arbiter
hpdcache_mem_req_read_arbiter #(
.N (3),
.hpdcache_mem_req_t (hpdcache_mem_req_t)
.hpdcache_mem_req_t(hpdcache_mem_req_t)
) i_mem_req_read_arbiter (
.clk_i,
.rst_ni,
.mem_req_read_ready_o (mem_req_read_ready),
.mem_req_read_valid_i (mem_req_read_valid),
.mem_req_read_ready_o(mem_req_read_ready),
.mem_req_read_valid_i(mem_req_read_valid),
.mem_req_read_i (mem_req_read),
.mem_req_read_ready_i (mem_req_read_ready_arb),
.mem_req_read_valid_o (mem_req_read_valid_arb),
.mem_req_read_ready_i(mem_req_read_ready_arb),
.mem_req_read_valid_o(mem_req_read_valid_arb),
.mem_req_read_o (mem_req_read_arb)
);
// }}}
@ -328,14 +326,13 @@ module cva6_hpdcache_subsystem_axi_arbiter
logic mem_resp_read_valid;
hpdcache_mem_resp_r_t mem_resp_read;
logic mem_resp_read_ready_arb [2:0];
logic mem_resp_read_valid_arb [2:0];
logic mem_resp_read_ready_arb[2:0];
logic mem_resp_read_valid_arb[2:0];
hpdcache_mem_resp_r_t mem_resp_read_arb [2:0];
mem_resp_rt_t mem_resp_read_rt;
always_comb
begin
always_comb begin
for (int i = 0; i < MEM_RESP_RT_DEPTH; i++) begin
mem_resp_read_rt[i] = (i == int'( icache_miss_id_i)) ? 0 :
(i == int'(dcache_uc_read_id_i)) ? 2 : 1;
@ -345,21 +342,21 @@ module cva6_hpdcache_subsystem_axi_arbiter
hpdcache_mem_resp_demux #(
.N (3),
.resp_t (hpdcache_mem_resp_r_t),
.resp_id_t (hpdcache_mem_id_t)
.resp_id_t(hpdcache_mem_id_t)
) i_mem_resp_read_demux (
.clk_i,
.rst_ni,
.mem_resp_ready_o (mem_resp_read_ready),
.mem_resp_valid_i (mem_resp_read_valid),
.mem_resp_ready_o(mem_resp_read_ready),
.mem_resp_valid_i(mem_resp_read_valid),
.mem_resp_id_i (mem_resp_read.mem_resp_r_id),
.mem_resp_i (mem_resp_read),
.mem_resp_ready_i (mem_resp_read_ready_arb),
.mem_resp_valid_o (mem_resp_read_valid_arb),
.mem_resp_ready_i(mem_resp_read_ready_arb),
.mem_resp_valid_o(mem_resp_read_valid_arb),
.mem_resp_o (mem_resp_read_arb),
.mem_resp_rt_i (mem_resp_read_rt)
.mem_resp_rt_i(mem_resp_read_rt)
);
assign icache_miss_resp_w = mem_resp_read_valid_arb[0],
@ -412,25 +409,25 @@ module cva6_hpdcache_subsystem_axi_arbiter
hpdcache_mem_req_write_arbiter #(
.N (2),
.hpdcache_mem_req_t (hpdcache_mem_req_t),
.hpdcache_mem_req_w_t (hpdcache_mem_req_w_t)
.hpdcache_mem_req_w_t(hpdcache_mem_req_w_t)
) i_mem_req_write_arbiter (
.clk_i,
.rst_ni,
.mem_req_write_ready_o (mem_req_write_ready),
.mem_req_write_valid_i (mem_req_write_valid),
.mem_req_write_ready_o(mem_req_write_ready),
.mem_req_write_valid_i(mem_req_write_valid),
.mem_req_write_i (mem_req_write),
.mem_req_write_data_ready_o (mem_req_write_data_ready),
.mem_req_write_data_valid_i (mem_req_write_data_valid),
.mem_req_write_data_ready_o(mem_req_write_data_ready),
.mem_req_write_data_valid_i(mem_req_write_data_valid),
.mem_req_write_data_i (mem_req_write_data),
.mem_req_write_ready_i (mem_req_write_ready_arb),
.mem_req_write_valid_o (mem_req_write_valid_arb),
.mem_req_write_ready_i(mem_req_write_ready_arb),
.mem_req_write_valid_o(mem_req_write_valid_arb),
.mem_req_write_o (mem_req_write_arb),
.mem_req_write_data_ready_i (mem_req_write_data_ready_arb),
.mem_req_write_data_valid_o (mem_req_write_data_valid_arb),
.mem_req_write_data_ready_i(mem_req_write_data_ready_arb),
.mem_req_write_data_valid_o(mem_req_write_data_valid_arb),
.mem_req_write_data_o (mem_req_write_data_arb)
);
// }}}
@ -441,14 +438,13 @@ module cva6_hpdcache_subsystem_axi_arbiter
logic mem_resp_write_valid;
hpdcache_mem_resp_w_t mem_resp_write;
logic mem_resp_write_ready_arb [1:0];
logic mem_resp_write_valid_arb [1:0];
logic mem_resp_write_ready_arb[1:0];
logic mem_resp_write_valid_arb[1:0];
hpdcache_mem_resp_w_t mem_resp_write_arb [1:0];
mem_resp_rt_t mem_resp_write_rt;
always_comb
begin
always_comb begin
for (int i = 0; i < MEM_RESP_RT_DEPTH; i++) begin
mem_resp_write_rt[i] = (i == int'(dcache_uc_write_id_i)) ? 1 : 0;
end
@ -457,21 +453,21 @@ module cva6_hpdcache_subsystem_axi_arbiter
hpdcache_mem_resp_demux #(
.N (2),
.resp_t (hpdcache_mem_resp_w_t),
.resp_id_t (hpdcache_mem_id_t)
.resp_id_t(hpdcache_mem_id_t)
) i_hpdcache_mem_resp_write_demux (
.clk_i,
.rst_ni,
.mem_resp_ready_o (mem_resp_write_ready),
.mem_resp_valid_i (mem_resp_write_valid),
.mem_resp_ready_o(mem_resp_write_ready),
.mem_resp_valid_i(mem_resp_write_valid),
.mem_resp_id_i (mem_resp_write.mem_resp_w_id),
.mem_resp_i (mem_resp_write),
.mem_resp_ready_i (mem_resp_write_ready_arb),
.mem_resp_valid_o (mem_resp_write_valid_arb),
.mem_resp_ready_i(mem_resp_write_ready_arb),
.mem_resp_valid_o(mem_resp_write_valid_arb),
.mem_resp_o (mem_resp_write_arb),
.mem_resp_rt_i (mem_resp_write_rt)
.mem_resp_rt_i(mem_resp_write_rt)
);
assign dcache_wbuf_resp_valid_o = mem_resp_write_valid_arb[0],
@ -485,8 +481,7 @@ module cva6_hpdcache_subsystem_axi_arbiter
// I$ miss pending
// {{{
always_ff @(posedge clk_i or negedge rst_ni)
begin : icache_miss_pending_ff
always_ff @(posedge clk_i or negedge rst_ni) begin : icache_miss_pending_ff
if (!rst_ni) begin
icache_miss_pending_q <= 1'b0;
end else begin
@ -504,57 +499,57 @@ module cva6_hpdcache_subsystem_axi_arbiter
hpdcache_mem_to_axi_write #(
.hpdcache_mem_req_t (hpdcache_mem_req_t),
.hpdcache_mem_req_w_t (hpdcache_mem_req_w_t),
.hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t),
.hpdcache_mem_resp_w_t(hpdcache_mem_resp_w_t),
.aw_chan_t (axi_aw_chan_t),
.w_chan_t (axi_w_chan_t),
.b_chan_t (axi_b_chan_t)
) i_hpdcache_mem_to_axi_write (
.req_ready_o (mem_req_write_ready_arb),
.req_valid_i (mem_req_write_valid_arb),
.req_ready_o(mem_req_write_ready_arb),
.req_valid_i(mem_req_write_valid_arb),
.req_i (mem_req_write_arb),
.req_data_ready_o (mem_req_write_data_ready_arb),
.req_data_valid_i (mem_req_write_data_valid_arb),
.req_data_ready_o(mem_req_write_data_ready_arb),
.req_data_valid_i(mem_req_write_data_valid_arb),
.req_data_i (mem_req_write_data_arb),
.resp_ready_i (mem_resp_write_ready),
.resp_valid_o (mem_resp_write_valid),
.resp_ready_i(mem_resp_write_ready),
.resp_valid_o(mem_resp_write_valid),
.resp_o (mem_resp_write),
.axi_aw_valid_o (axi_req.aw_valid),
.axi_aw_valid_o(axi_req.aw_valid),
.axi_aw_o (axi_req.aw),
.axi_aw_ready_i (axi_resp.aw_ready),
.axi_aw_ready_i(axi_resp.aw_ready),
.axi_w_valid_o (axi_req.w_valid),
.axi_w_valid_o(axi_req.w_valid),
.axi_w_o (axi_req.w),
.axi_w_ready_i (axi_resp.w_ready),
.axi_w_ready_i(axi_resp.w_ready),
.axi_b_valid_i (axi_resp.b_valid),
.axi_b_valid_i(axi_resp.b_valid),
.axi_b_i (axi_resp.b),
.axi_b_ready_o (axi_req.b_ready)
.axi_b_ready_o(axi_req.b_ready)
);
hpdcache_mem_to_axi_read #(
.hpdcache_mem_req_t (hpdcache_mem_req_t),
.hpdcache_mem_resp_r_t (hpdcache_mem_resp_r_t),
.hpdcache_mem_resp_r_t(hpdcache_mem_resp_r_t),
.ar_chan_t (axi_ar_chan_t),
.r_chan_t (axi_r_chan_t)
) i_hpdcache_mem_to_axi_read (
.req_ready_o (mem_req_read_ready_arb),
.req_valid_i (mem_req_read_valid_arb),
.req_ready_o(mem_req_read_ready_arb),
.req_valid_i(mem_req_read_valid_arb),
.req_i (mem_req_read_arb),
.resp_ready_i (mem_resp_read_ready),
.resp_valid_o (mem_resp_read_valid),
.resp_ready_i(mem_resp_read_ready),
.resp_valid_o(mem_resp_read_valid),
.resp_o (mem_resp_read),
.axi_ar_valid_o (axi_req.ar_valid),
.axi_ar_valid_o(axi_req.ar_valid),
.axi_ar_o (axi_req.ar),
.axi_ar_ready_i (axi_resp.ar_ready),
.axi_ar_ready_i(axi_resp.ar_ready),
.axi_r_valid_i (axi_resp.r_valid),
.axi_r_valid_i(axi_resp.r_valid),
.axi_r_i (axi_resp.r),
.axi_r_ready_o (axi_req.r_ready)
.axi_r_ready_o(axi_req.r_ready)
);
assign axi_req_o = axi_req;
@ -564,16 +559,27 @@ module cva6_hpdcache_subsystem_axi_arbiter
// Assertions
// {{{
// pragma translate_off
initial assert (HPDcacheMemIdWidth <= AxiIdWidth) else
$fatal("HPDcacheMemIdWidth shall be less or equal to AxiIdWidth");
initial assert (HPDcacheMemIdWidth >= (hpdcache_pkg::HPDCACHE_MSHR_SET_WIDTH + hpdcache_pkg::HPDCACHE_MSHR_WAY_WIDTH + 1)) else
$fatal("HPDcacheMemIdWidth shall be wide enough to identify all pending HPDcache misses and Icache misses");
initial assert (HPDcacheMemIdWidth >= (hpdcache_pkg::HPDCACHE_WBUF_DIR_PTR_WIDTH + 1)) else
$fatal("HPDcacheMemIdWidth shall be wide enough to identify all pending HPDcache cacheable writes and uncacheable writes");
initial assert (HPDcacheMemDataWidth <= ariane_pkg::ICACHE_LINE_WIDTH) else
$fatal("HPDcacheMemDataWidth shall be less or equal to the width of a Icache line");
initial assert (HPDcacheMemDataWidth <= ariane_pkg::DCACHE_LINE_WIDTH) else
$fatal("HPDcacheMemDataWidth shall be less or equal to the width of a Dcache line");
initial
assert (HPDcacheMemIdWidth <= AxiIdWidth)
else $fatal("HPDcacheMemIdWidth shall be less or equal to AxiIdWidth");
initial
assert (HPDcacheMemIdWidth >= (hpdcache_pkg::HPDCACHE_MSHR_SET_WIDTH + hpdcache_pkg::HPDCACHE_MSHR_WAY_WIDTH + 1))
else
$fatal(
"HPDcacheMemIdWidth shall be wide enough to identify all pending HPDcache misses and Icache misses"
);
initial
assert (HPDcacheMemIdWidth >= (hpdcache_pkg::HPDCACHE_WBUF_DIR_PTR_WIDTH + 1))
else
$fatal(
"HPDcacheMemIdWidth shall be wide enough to identify all pending HPDcache cacheable writes and uncacheable writes"
);
initial
assert (HPDcacheMemDataWidth <= ariane_pkg::ICACHE_LINE_WIDTH)
else $fatal("HPDcacheMemDataWidth shall be less or equal to the width of a Icache line");
initial
assert (HPDcacheMemDataWidth <= ariane_pkg::DCACHE_LINE_WIDTH)
else $fatal("HPDcacheMemDataWidth shall be less or equal to the width of a Dcache line");
// pragma translate_on
// }}}

View file

@ -25,7 +25,10 @@
//
module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
module cva6_icache
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
/// ID to be used for read transactions
parameter logic [MEM_TID_WIDTH-1:0] RdTxId = 0
@ -54,9 +57,8 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
);
// functions
function automatic logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] icache_way_bin2oh (
input logic [L1I_WAY_WIDTH-1:0] in
);
function automatic logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] icache_way_bin2oh(
input logic [L1I_WAY_WIDTH-1:0] in);
logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] out;
out = '0;
out[in] = 1'b1;
@ -70,7 +72,9 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
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
@ -89,14 +93,14 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
// mem arrays
logic cl_we; // write enable to memory array
logic [ICACHE_SET_ASSOC-1:0] cl_req; // request to memory array
logic [ ICACHE_SET_ASSOC-1:0] cl_req; // request to memory array
logic [ICACHE_CL_IDX_WIDTH-1:0] cl_index; // this is a cache-line index, to memory array
logic [ICACHE_OFFSET_WIDTH-1:0] cl_offset_d, cl_offset_q; // offset in cache line
logic [ICACHE_TAG_WIDTH-1:0] cl_tag_d, cl_tag_q; // this is the cache tag
logic [ICACHE_TAG_WIDTH-1:0] cl_tag_rdata [ICACHE_SET_ASSOC-1:0]; // these are the tags coming from the tagmem
logic [ICACHE_LINE_WIDTH-1:0] cl_rdata [ICACHE_SET_ASSOC-1:0]; // these are the cachelines coming from the cache
logic [ICACHE_USER_LINE_WIDTH-1:0] cl_ruser[ICACHE_SET_ASSOC-1:0]; // these are the cachelines coming from the user cache
logic [ICACHE_SET_ASSOC-1:0][FETCH_WIDTH-1:0]cl_sel; // selected word from each cacheline
logic [ICACHE_SET_ASSOC-1:0][FETCH_WIDTH-1:0] cl_sel; // selected word from each cacheline
logic [ICACHE_SET_ASSOC-1:0][FETCH_USER_WIDTH-1:0] cl_user; // selected word from each cacheline
logic [ICACHE_SET_ASSOC-1:0] vld_req; // bit enable for valid regs
logic vld_we; // valid bits write enable
@ -105,18 +109,27 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
logic [ICACHE_CL_IDX_WIDTH-1:0] vld_addr; // valid bit
// cpmtroller FSM
typedef enum logic[2:0] {FLUSH, IDLE, READ, MISS, KILL_ATRANS, KILL_MISS} state_e;
typedef enum logic [2:0] {
FLUSH,
IDLE,
READ,
MISS,
KILL_ATRANS,
KILL_MISS
} state_e;
state_e 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;
// noncacheable if request goes to I/O space, or if cache is disabled
assign paddr_is_nc = (~cache_en_q) | (~config_pkg::is_inside_cacheable_regions(CVA6Cfg, {{{64-riscv::PLEN}{1'b0}}, cl_tag_d, {ICACHE_INDEX_WIDTH{1'b0}}}));
assign paddr_is_nc = (~cache_en_q) | (~config_pkg::is_inside_cacheable_regions(
CVA6Cfg, {{{64 - riscv::PLEN} {1'b0}}, cl_tag_d, {ICACHE_INDEX_WIDTH{1'b0}}}
));
// pass exception through
assign dreq_o.ex = areq_i.fetch_exception;
@ -124,7 +137,7 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
// 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;
assign areq_o.fetch_vaddr = {vaddr_q>>2, 2'b0};
assign areq_o.fetch_vaddr = {vaddr_q >> 2, 2'b0};
// split virtual address into index and offset to address cache arrays
assign cl_index = vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH];
@ -138,11 +151,10 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
// request word address instead of cl address in case of NC access
assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:3], 3'b0} : // align to 64bit
{cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH], {ICACHE_OFFSET_WIDTH{1'b0}}}; // align to cl
end else begin : gen_piton_offset
end else begin : gen_piton_offset
// icache fills are either cachelines or 4byte fills, depending on whether they go to the Piton I/O space or not.
// since the piton cache system replicates the data, we can always index the full CL
assign cl_offset_d = ( dreq_o.ready & dreq_i.req) ? {dreq_i.vaddr>>2, 2'b0} :
cl_offset_q;
assign cl_offset_d = (dreq_o.ready & dreq_i.req) ? {dreq_i.vaddr >> 2, 2'b0} : cl_offset_q;
// request word address instead of cl address in case of NC access
assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:2], 2'b0} : // align to 32bit
@ -160,9 +172,9 @@ end else begin : gen_piton_offset
// invalidations take two cycles
assign inv_d = inv_en;
///////////////////////////////////////////////////////
// main control logic
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// main control logic
///////////////////////////////////////////////////////
logic addr_ni;
assign addr_ni = config_pkg::is_inside_nonidempotent_regions(CVA6Cfg, areq_i.fetch_paddr);
always_comb begin : p_fsm
@ -243,13 +255,13 @@ end else begin : gen_piton_offset
// readout speculatively
cache_rden = cache_en_q;
if (areq_i.fetch_valid && (!dreq_i.spec || !addr_ni) ) begin
if (areq_i.fetch_valid && (!dreq_i.spec || !addr_ni)) begin
// check if we have to flush
if (flush_d) begin
state_d = IDLE;
// we have a hit or an exception output valid result
end else if (((|cl_hit && cache_en_q) || areq_i.fetch_exception.valid) && !inv_q) begin
dreq_o.valid = ~dreq_i.kill_s2;// just don't output in this case
dreq_o.valid = ~dreq_i.kill_s2; // just don't output in this case
state_d = IDLE;
// we can accept another request
@ -331,20 +343,18 @@ end else begin : gen_piton_offset
endcase // state_q
end
///////////////////////////////////////////////////////
// valid bit invalidation and replacement strategy
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// valid bit invalidation and replacement strategy
///////////////////////////////////////////////////////
// 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
// 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_cnt_d = (flush_done) ? '0 : (flush_en) ? flush_cnt_q + 1 : flush_cnt_q;
assign flush_done = (flush_cnt_q==(ICACHE_NUM_WORDS-1));
assign flush_done = (flush_cnt_q == (ICACHE_NUM_WORDS - 1));
// invalidation/clearing address
// flushing takes precedence over invals
@ -354,8 +364,9 @@ end else begin : gen_piton_offset
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;
(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;
@ -369,52 +380,50 @@ end else begin : gen_piton_offset
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 :
(cache_wren) ? repl_way_oh_q :
'0;
assign cl_req = (cache_rden) ? '1 : (cache_wren) ? repl_way_oh_q : '0;
assign cl_we = cache_wren;
// find invalid cache line
lzc #(
.WIDTH ( ICACHE_SET_ASSOC )
.WIDTH(ICACHE_SET_ASSOC)
) i_lzc (
.in_i ( ~vld_rdata ),
.cnt_o ( inv_way ),
.empty_o ( all_ways_valid )
.in_i (~vld_rdata),
.cnt_o (inv_way),
.empty_o(all_ways_valid)
);
// generate random cacheline index
lfsr #(
.LfsrWidth ( 8 ),
.OutWidth ( $clog2(ariane_pkg::ICACHE_SET_ASSOC))
.LfsrWidth(8),
.OutWidth ($clog2(ariane_pkg::ICACHE_SET_ASSOC))
) i_lfsr (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.en_i ( update_lfsr ),
.out_o ( rnd_way )
.clk_i (clk_i),
.rst_ni(rst_ni),
.en_i (update_lfsr),
.out_o (rnd_way)
);
///////////////////////////////////////////////////////
// tag comparison, hit generation
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// tag comparison, hit generation
///////////////////////////////////////////////////////
logic [$clog2(ICACHE_SET_ASSOC)-1:0] hit_idx;
for (genvar i=0;i<ICACHE_SET_ASSOC;i++) begin : gen_tag_cmpsel
for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_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];
assign cl_user[i] = cl_ruser[i][{cl_offset_q,3'b0} +: FETCH_USER_WIDTH];
assign cl_sel[i] = cl_rdata[i][{cl_offset_q, 3'b0}+:FETCH_WIDTH];
assign cl_user[i] = cl_ruser[i][{cl_offset_q, 3'b0}+:FETCH_USER_WIDTH];
end
lzc #(
.WIDTH ( ICACHE_SET_ASSOC )
.WIDTH(ICACHE_SET_ASSOC)
) i_lzc_hit (
.in_i ( cl_hit ),
.cnt_o ( hit_idx ),
.empty_o ( )
.in_i (cl_hit),
.cnt_o (hit_idx),
.empty_o()
);
always_comb begin
@ -422,37 +431,37 @@ end else begin : gen_piton_offset
dreq_o.data = cl_sel[hit_idx];
dreq_o.user = cl_user[hit_idx];
end else begin
dreq_o.data = mem_rtrn_i.data[{cl_offset_q,3'b0} +: FETCH_WIDTH];
dreq_o.user = mem_rtrn_i.user[{cl_offset_q,3'b0} +: FETCH_USER_WIDTH];
dreq_o.data = mem_rtrn_i.data[{cl_offset_q, 3'b0}+:FETCH_WIDTH];
dreq_o.user = mem_rtrn_i.user[{cl_offset_q, 3'b0}+:FETCH_USER_WIDTH];
end
end
///////////////////////////////////////////////////////
// memory arrays and regs
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// memory arrays and regs
///////////////////////////////////////////////////////
logic [ICACHE_TAG_WIDTH:0] cl_tag_valid_rdata [ICACHE_SET_ASSOC-1:0];
logic [ICACHE_TAG_WIDTH:0] cl_tag_valid_rdata[ICACHE_SET_ASSOC-1:0];
for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_sram
// Tag RAM
sram #(
// tag + valid bit
.DATA_WIDTH ( ICACHE_TAG_WIDTH+1 ),
.NUM_WORDS ( ICACHE_NUM_WORDS )
.DATA_WIDTH(ICACHE_TAG_WIDTH + 1),
.NUM_WORDS (ICACHE_NUM_WORDS)
) tag_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( vld_req[i] ),
.we_i ( vld_we ),
.addr_i ( vld_addr ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.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
// couple of cycle until we write to the cache upon a miss
.wuser_i ( '0 ),
.wdata_i ( {vld_wdata[i], cl_tag_q} ),
.be_i ( '1 ),
.ruser_o ( ),
.rdata_o ( cl_tag_valid_rdata[i] )
.wuser_i('0),
.wdata_i({vld_wdata[i], cl_tag_q}),
.be_i ('1),
.ruser_o(),
.rdata_o(cl_tag_valid_rdata[i])
);
assign cl_tag_rdata[i] = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH-1:0];
@ -460,27 +469,27 @@ end else begin : gen_piton_offset
// Data RAM
sram #(
.USER_WIDTH ( ICACHE_USER_LINE_WIDTH ),
.DATA_WIDTH ( ICACHE_LINE_WIDTH ),
.USER_EN ( ariane_pkg::FETCH_USER_EN ),
.NUM_WORDS ( ICACHE_NUM_WORDS )
.USER_WIDTH(ICACHE_USER_LINE_WIDTH),
.DATA_WIDTH(ICACHE_LINE_WIDTH),
.USER_EN (ariane_pkg::FETCH_USER_EN),
.NUM_WORDS (ICACHE_NUM_WORDS)
) data_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( cl_req[i] ),
.we_i ( cl_we ),
.addr_i ( cl_index ),
.wuser_i ( mem_rtrn_i.user ),
.wdata_i ( mem_rtrn_i.data ),
.be_i ( '1 ),
.ruser_o ( cl_ruser[i] ),
.rdata_o ( cl_rdata[i] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (cl_req[i]),
.we_i (cl_we),
.addr_i (cl_index),
.wuser_i(mem_rtrn_i.user),
.wdata_i(mem_rtrn_i.data),
.be_i ('1),
.ruser_o(cl_ruser[i]),
.rdata_o(cl_rdata[i])
);
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if(!rst_ni) begin
if (!rst_ni) begin
cl_tag_q <= '0;
flush_cnt_q <= '0;
vaddr_q <= '0;
@ -505,27 +514,33 @@ end else begin : gen_piton_offset
end
end
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
repl_inval0: assert property (
repl_inval0 :
assert property (
@(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");
else $fatal(1, "[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");
repl_inval1: assert property (
repl_inval1 :
assert property (
@(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");
else $fatal(1, "[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");
invalid_state: assert property (
invalid_state :
assert property (
@(posedge clk_i) disable iff (!rst_ni) (state_q inside {FLUSH, IDLE, READ, MISS, KILL_ATRANS, KILL_MISS}))
else $fatal(1,"[l1 icache] fsm reached an invalid state");
else $fatal(1, "[l1 icache] fsm reached an invalid state");
hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) (!inv_en) |-> cache_rden |=> cmp_en_q |-> $onehot0(cl_hit))
else $fatal(1,"[l1 icache] cl_hit signal must be hot1");
hot1 :
assert property (
@(posedge clk_i) disable iff (!rst_ni) (!inv_en) |-> cache_rden |=> cmp_en_q |-> $onehot0(
cl_hit
))
else $fatal(1, "[l1 icache] cl_hit signal must be hot1");
// this is only used for verification!
logic vld_mirror[wt_cache_pkg::ICACHE_NUM_WORDS-1:0][ariane_pkg::ICACHE_SET_ASSOC-1:0];
@ -533,12 +548,12 @@ end else begin : gen_piton_offset
logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] tag_write_duplicate_test;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_mirror
if(!rst_ni) begin
vld_mirror <= '{default:'0};
tag_mirror <= '{default:'0};
if (!rst_ni) begin
vld_mirror <= '{default: '0};
tag_mirror <= '{default: '0};
end else begin
for (int i = 0; i < ICACHE_SET_ASSOC; i++) begin
if(vld_req[i] & vld_we) begin
if (vld_req[i] & vld_we) begin
vld_mirror[vld_addr][i] <= vld_wdata[i];
tag_mirror[vld_addr][i] <= cl_tag_q;
end
@ -550,17 +565,18 @@ end else begin : gen_piton_offset
assign tag_write_duplicate_test[i] = (tag_mirror[vld_addr][i] == cl_tag_q) & vld_mirror[vld_addr][i] & (|vld_wdata);
end
tag_write_duplicate: assert property (
tag_write_duplicate :
assert property (
@(posedge clk_i) disable iff (!rst_ni) |vld_req |-> vld_we |-> !(|tag_write_duplicate_test))
else $fatal(1,"[l1 icache] cannot allocate a CL that is already present in the cache");
else $fatal(1, "[l1 icache] cannot allocate a CL that is already present in the cache");
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
//pragma translate_on
endmodule // cva6_icache

View file

@ -13,7 +13,10 @@
// Description: wrapper module to connect the L1I$ to a 64bit AXI bus.
//
module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
module cva6_icache_axi_wrapper
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic
@ -48,21 +51,22 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
logic axi_rd_req;
logic axi_rd_gnt;
logic [CVA6Cfg.AxiAddrWidth-1:0] axi_rd_addr;
logic [$clog2(AxiNumWords)-1:0] axi_rd_blen;
logic [2:0] axi_rd_size;
logic [CVA6Cfg.AxiIdWidth-1:0] axi_rd_id_in;
logic [ $clog2(AxiNumWords)-1:0] axi_rd_blen;
logic [ 2:0] axi_rd_size;
logic [ CVA6Cfg.AxiIdWidth-1:0] axi_rd_id_in;
logic axi_rd_rdy;
logic axi_rd_lock;
logic axi_rd_last;
logic axi_rd_valid;
logic [CVA6Cfg.AxiDataWidth-1:0] axi_rd_data;
logic [CVA6Cfg.AxiIdWidth-1:0] axi_rd_id_out;
logic [ CVA6Cfg.AxiIdWidth-1:0] axi_rd_id_out;
logic axi_rd_exokay;
logic req_valid_d, req_valid_q;
icache_req_t req_data_d, req_data_q;
logic first_d, first_q;
logic [ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:0][CVA6Cfg.AxiDataWidth-1:0] rd_shift_d, rd_shift_q;
logic [ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:0][CVA6Cfg.AxiDataWidth-1:0]
rd_shift_d, rd_shift_q;
// Keep read request asserted until we have an AXI grant. This is not guaranteed by icache (but
// required by AXI).
@ -76,8 +80,8 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
assign axi_rd_addr = CVA6Cfg.AxiAddrWidth'(req_data_d.paddr);
// Fetch a full cache line on a cache miss, or a single word on a bypassed access
assign axi_rd_blen = (req_data_d.nc) ? '0 : ariane_pkg::ICACHE_LINE_WIDTH/64-1;
assign axi_rd_size = $clog2(CVA6Cfg.AxiDataWidth/8); // Maximum
assign axi_rd_blen = (req_data_d.nc) ? '0 : ariane_pkg::ICACHE_LINE_WIDTH / 64 - 1;
assign axi_rd_size = $clog2(CVA6Cfg.AxiDataWidth / 8); // Maximum
assign axi_rd_id_in = req_data_d.tid;
assign axi_rd_rdy = 1'b1;
assign axi_rd_lock = 1'b0;
@ -97,67 +101,67 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
// -------
cva6_icache #(
// use ID 0 for icache reads
.CVA6Cfg ( CVA6Cfg ),
.RdTxId ( 0 )
.CVA6Cfg(CVA6Cfg),
.RdTxId (0)
) i_cva6_icache (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_i ),
.en_i ( en_i ),
.miss_o ( miss_o ),
.areq_i ( areq_i ),
.areq_o ( areq_o ),
.dreq_i ( dreq_i ),
.dreq_o ( dreq_o ),
.mem_rtrn_vld_i ( icache_mem_rtrn_vld ),
.mem_rtrn_i ( icache_mem_rtrn ),
.mem_data_req_o ( icache_mem_data_req ),
.mem_data_ack_i ( icache_mem_data_ack ),
.mem_data_o ( icache_mem_data )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (flush_i),
.en_i (en_i),
.miss_o (miss_o),
.areq_i (areq_i),
.areq_o (areq_o),
.dreq_i (dreq_i),
.dreq_o (dreq_o),
.mem_rtrn_vld_i(icache_mem_rtrn_vld),
.mem_rtrn_i (icache_mem_rtrn),
.mem_data_req_o(icache_mem_data_req),
.mem_data_ack_i(icache_mem_data_ack),
.mem_data_o (icache_mem_data)
);
// --------
// AXI shim
// --------
axi_shim #(
.CVA6Cfg ( CVA6Cfg ),
.AxiNumWords ( AxiNumWords ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.AxiNumWords(AxiNumWords),
.axi_req_t (axi_req_t),
.axi_rsp_t (axi_rsp_t)
) i_axi_shim (
.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_lock_i ( axi_rd_lock ),
.rd_last_o ( axi_rd_last ),
.rd_valid_o ( axi_rd_valid ),
.rd_data_o ( axi_rd_data ),
.rd_user_o ( ),
.rd_id_o ( axi_rd_id_out ),
.rd_exokay_o ( axi_rd_exokay ),
.wr_req_i ( '0 ),
.wr_gnt_o ( ),
.wr_addr_i ( '0 ),
.wr_data_i ( '0 ),
.wr_user_i ( '0 ),
.wr_be_i ( '0 ),
.wr_blen_i ( '0 ),
.wr_size_i ( '0 ),
.wr_id_i ( '0 ),
.wr_lock_i ( '0 ),
.wr_atop_i ( '0 ),
.wr_rdy_i ( '0 ),
.wr_valid_o ( ),
.wr_id_o ( ),
.wr_exokay_o ( ),
.axi_req_o ( axi_req_o ),
.axi_resp_i ( axi_resp_i )
.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_lock_i (axi_rd_lock),
.rd_last_o (axi_rd_last),
.rd_valid_o (axi_rd_valid),
.rd_data_o (axi_rd_data),
.rd_user_o (),
.rd_id_o (axi_rd_id_out),
.rd_exokay_o(axi_rd_exokay),
.wr_req_i ('0),
.wr_gnt_o (),
.wr_addr_i ('0),
.wr_data_i ('0),
.wr_user_i ('0),
.wr_be_i ('0),
.wr_blen_i ('0),
.wr_size_i ('0),
.wr_id_i ('0),
.wr_lock_i ('0),
.wr_atop_i ('0),
.wr_rdy_i ('0),
.wr_valid_o (),
.wr_id_o (),
.wr_exokay_o(),
.axi_req_o (axi_req_o),
.axi_resp_i (axi_resp_i)
);
// Buffer burst data in shift register

View file

@ -16,12 +16,15 @@
// MISS Handler
// --------------
module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
module miss_handler
import ariane_pkg::*;
import std_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NR_PORTS = 4,
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic
)(
) (
input logic clk_i,
input logic rst_ni,
input logic flush_i, // flush request
@ -82,7 +85,8 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
INIT, // B
AMO_REQ, // C
AMO_WAIT_RESP // D
} state_d, state_q;
}
state_d, state_q;
// Registers
mshr_t mshr_d, mshr_q;
@ -93,21 +97,21 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
logic serve_amo_d, serve_amo_q;
// Request from one FSM
logic [NR_PORTS-1:0] miss_req_valid;
logic [NR_PORTS-1:0] miss_req_bypass;
logic [NR_PORTS-1:0][63:0] miss_req_addr;
logic [NR_PORTS-1:0][63:0] miss_req_wdata;
logic [NR_PORTS-1:0] miss_req_we;
logic [NR_PORTS-1:0][7:0] miss_req_be;
logic [NR_PORTS-1:0][1:0] miss_req_size;
logic [ NR_PORTS-1:0] miss_req_valid;
logic [ NR_PORTS-1:0] miss_req_bypass;
logic [ NR_PORTS-1:0][63:0] miss_req_addr;
logic [ NR_PORTS-1:0][63:0] miss_req_wdata;
logic [ NR_PORTS-1:0] miss_req_we;
logic [ NR_PORTS-1:0][ 7:0] miss_req_be;
logic [ NR_PORTS-1:0][ 1:0] miss_req_size;
// Bypass AMO port
bypass_req_t amo_bypass_req;
bypass_rsp_t amo_bypass_rsp;
// Bypass ports <-> Arbiter
bypass_req_t [NR_BYPASS_PORTS-1:0] bypass_ports_req;
bypass_rsp_t [NR_BYPASS_PORTS-1:0] bypass_ports_rsp;
bypass_req_t [ NR_BYPASS_PORTS-1:0] bypass_ports_req;
bypass_rsp_t [ NR_BYPASS_PORTS-1:0] bypass_ports_rsp;
// Arbiter <-> Bypass AXI adapter
bypass_req_t bypass_adapter_req;
@ -115,24 +119,24 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
// Cache Line Refill <-> AXI
logic req_fsm_miss_valid;
logic [63:0] req_fsm_miss_addr;
logic [DCACHE_LINE_WIDTH-1:0] req_fsm_miss_wdata;
logic [ 63:0] req_fsm_miss_addr;
logic [ DCACHE_LINE_WIDTH-1:0] req_fsm_miss_wdata;
logic req_fsm_miss_we;
logic [(DCACHE_LINE_WIDTH/8)-1:0] req_fsm_miss_be;
logic [ (DCACHE_LINE_WIDTH/8)-1:0] req_fsm_miss_be;
ariane_pkg::ad_req_t req_fsm_miss_req;
logic [1:0] req_fsm_miss_size;
logic [ 1:0] req_fsm_miss_size;
logic gnt_miss_fsm;
logic valid_miss_fsm;
logic [(DCACHE_LINE_WIDTH/64)-1:0][63:0] data_miss_fsm;
logic [ (DCACHE_LINE_WIDTH/64)-1:0][63:0] data_miss_fsm;
// Cache Management <-> LFSR
logic lfsr_enable;
logic [DCACHE_SET_ASSOC-1:0] lfsr_oh;
logic [ DCACHE_SET_ASSOC-1:0] lfsr_oh;
logic [$clog2(DCACHE_SET_ASSOC-1)-1:0] lfsr_bin;
// AMOs
ariane_pkg::amo_t amo_op;
logic [63:0] amo_operand_b;
logic [ 63:0] amo_operand_b;
// ------------------------------
// Cache Management
@ -260,8 +264,7 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
evict_cl_d.data = data_i[lfsr_bin].data;
cnt_d = mshr_q.addr[DCACHE_INDEX_WIDTH-1:0];
// no - we can request a cache line now
end else
state_d = REQ_CACHELINE;
end else state_d = REQ_CACHELINE;
// we have at least one free way
end else begin
// get victim cache-line by looking for the first non-valid bit
@ -304,8 +307,7 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
// Yes, so safe the updated data now
for (int i = 0; i < 8; i++) begin
// check if we really want to write the corresponding byte
if (mshr_q.be[i])
data_o.data[(cl_offset + i*8) +: 8] = mshr_q.wdata[i];
if (mshr_q.be[i]) data_o.data[(cl_offset+i*8)+:8] = mshr_q.wdata[i];
end
// its immediately dirty if we write
data_o.dirty = 1'b1;
@ -324,7 +326,11 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
WB_CACHELINE_FLUSH, WB_CACHELINE_MISS: begin
req_fsm_miss_valid = 1'b1;
req_fsm_miss_addr = {evict_cl_q.tag, cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET], {{DCACHE_BYTE_OFFSET}{1'b0}}};
req_fsm_miss_addr = {
evict_cl_q.tag,
cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET],
{{DCACHE_BYTE_OFFSET} {1'b0}}
};
req_fsm_miss_be = '1;
req_fsm_miss_we = 1'b1;
req_fsm_miss_wdata = evict_cl_q.data;
@ -371,7 +377,7 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
be_o.vldrty = INVALIDATE_ON_FLUSH ? '1 : '0;
we_o = 1'b1;
// finished with flushing operation, go back to idle
if (cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] == DCACHE_NUM_WORDS-1) begin
if (cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] == DCACHE_NUM_WORDS - 1) begin
// only acknowledge if the flush wasn't triggered by an atomic
flush_ack_o = ~serve_amo_q;
state_d = IDLE;
@ -389,8 +395,7 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
be_o.vldrty = '1;
cnt_d = cnt_q + (1'b1 << DCACHE_BYTE_OFFSET);
// finished initialization
if (cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] == DCACHE_NUM_WORDS-1)
state_d = IDLE;
if (cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] == DCACHE_NUM_WORDS - 1) state_d = IDLE;
end
// ----------------------
// AMOs
@ -506,11 +511,11 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
end
//pragma translate_off
`ifndef VERILATOR
`ifndef VERILATOR
// assert that cache only hits on one way
assert property (
@(posedge clk_i) $onehot0(evict_way_q)) else $warning("Evict-way should be one-hot encoded");
`endif
assert property (@(posedge clk_i) $onehot0(evict_way_q))
else $warning("Evict-way should be one-hot encoded");
`endif
//pragma translate_on
// ----------------------
@ -567,31 +572,31 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
assign bypass_addr = bypass_adapter_req.addr;
axi_adapter #(
.CVA6Cfg ( CVA6Cfg ),
.DATA_WIDTH ( 64 ),
.CACHELINE_BYTE_OFFSET ( DCACHE_BYTE_OFFSET ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.DATA_WIDTH (64),
.CACHELINE_BYTE_OFFSET(DCACHE_BYTE_OFFSET),
.axi_req_t (axi_req_t),
.axi_rsp_t (axi_rsp_t)
) i_bypass_axi_adapter (
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (bypass_adapter_req.req),
.type_i (bypass_adapter_req.reqtype),
.amo_i (bypass_adapter_req.amo),
.id_i (({{CVA6Cfg.AxiIdWidth-4{1'b0}}, bypass_adapter_req.id})),
.addr_i (bypass_addr),
.wdata_i (bypass_adapter_req.wdata),
.we_i (bypass_adapter_req.we),
.be_i (bypass_adapter_req.be),
.size_i (bypass_adapter_req.size),
.gnt_o (bypass_adapter_rsp.gnt),
.valid_o (bypass_adapter_rsp.valid),
.rdata_o (bypass_adapter_rsp.rdata),
.id_o (), // not used, single outstanding request in arbiter
.critical_word_o (), // not used for single requests
.clk_i(clk_i),
.rst_ni(rst_ni),
.req_i(bypass_adapter_req.req),
.type_i(bypass_adapter_req.reqtype),
.amo_i(bypass_adapter_req.amo),
.id_i(({{CVA6Cfg.AxiIdWidth - 4{1'b0}}, bypass_adapter_req.id})),
.addr_i(bypass_addr),
.wdata_i(bypass_adapter_req.wdata),
.we_i(bypass_adapter_req.we),
.be_i(bypass_adapter_req.be),
.size_i(bypass_adapter_req.size),
.gnt_o(bypass_adapter_rsp.gnt),
.valid_o(bypass_adapter_rsp.valid),
.rdata_o(bypass_adapter_rsp.rdata),
.id_o(), // not used, single outstanding request in arbiter
.critical_word_o(), // not used for single requests
.critical_word_valid_o(), // not used for single requests
.axi_req_o (axi_bypass_o),
.axi_resp_i (axi_bypass_i)
.axi_req_o(axi_bypass_o),
.axi_resp_i(axi_bypass_i)
);
// ----------------------
@ -602,40 +607,42 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
assign miss_addr = req_fsm_miss_addr;
axi_adapter #(
.CVA6Cfg ( CVA6Cfg ),
.DATA_WIDTH ( DCACHE_LINE_WIDTH ),
.CACHELINE_BYTE_OFFSET ( DCACHE_BYTE_OFFSET ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.DATA_WIDTH (DCACHE_LINE_WIDTH),
.CACHELINE_BYTE_OFFSET(DCACHE_BYTE_OFFSET),
.axi_req_t (axi_req_t),
.axi_rsp_t (axi_rsp_t)
) i_miss_axi_adapter (
.clk_i,
.rst_ni,
.req_i ( req_fsm_miss_valid ),
.type_i ( req_fsm_miss_req ),
.amo_i ( AMO_NONE ),
.gnt_o ( gnt_miss_fsm ),
.addr_i ( miss_addr ),
.we_i ( req_fsm_miss_we ),
.wdata_i ( req_fsm_miss_wdata ),
.be_i ( req_fsm_miss_be ),
.size_i ( req_fsm_miss_size ),
.id_i ( {{CVA6Cfg.AxiIdWidth-4{1'b0}}, 4'b0111} ),
.valid_o ( valid_miss_fsm ),
.rdata_o ( data_miss_fsm ),
.id_o ( ),
.critical_word_o ( critical_word_o ),
.critical_word_valid_o (critical_word_valid_o),
.axi_req_o ( axi_data_o ),
.axi_resp_i ( axi_data_i )
.req_i (req_fsm_miss_valid),
.type_i (req_fsm_miss_req),
.amo_i (AMO_NONE),
.gnt_o (gnt_miss_fsm),
.addr_i (miss_addr),
.we_i (req_fsm_miss_we),
.wdata_i (req_fsm_miss_wdata),
.be_i (req_fsm_miss_be),
.size_i (req_fsm_miss_size),
.id_i ({{CVA6Cfg.AxiIdWidth - 4{1'b0}}, 4'b0111}),
.valid_o (valid_miss_fsm),
.rdata_o (data_miss_fsm),
.id_o (),
.critical_word_o (critical_word_o),
.critical_word_valid_o(critical_word_valid_o),
.axi_req_o (axi_data_o),
.axi_resp_i (axi_data_i)
);
// -----------------
// Replacement LFSR
// -----------------
lfsr_8bit #(.WIDTH (DCACHE_SET_ASSOC)) i_lfsr (
.en_i ( lfsr_enable ),
.refill_way_oh ( lfsr_oh ),
.refill_way_bin ( lfsr_bin ),
lfsr_8bit #(
.WIDTH(DCACHE_SET_ASSOC)
) i_lfsr (
.en_i (lfsr_enable),
.refill_way_oh (lfsr_oh),
.refill_way_bin(lfsr_bin),
.*
);
@ -648,13 +655,13 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
for (int unsigned i = 0; i < NR_PORTS; i++) begin
miss_req = miss_req_t'(miss_req_i[i]);
miss_req_valid [i] = miss_req.valid;
miss_req_bypass [i] = miss_req.bypass;
miss_req_addr [i] = miss_req.addr;
miss_req_wdata [i] = miss_req.wdata;
miss_req_we [i] = miss_req.we;
miss_req_be [i] = miss_req.be;
miss_req_size [i] = miss_req.size;
miss_req_valid[i] = miss_req.valid;
miss_req_bypass[i] = miss_req.bypass;
miss_req_addr[i] = miss_req.addr;
miss_req_wdata[i] = miss_req.wdata;
miss_req_we[i] = miss_req.we;
miss_req_be[i] = miss_req.be;
miss_req_size[i] = miss_req.size;
end
end
endmodule
@ -669,7 +676,7 @@ module axi_adapter_arbiter #(
parameter NR_PORTS = 4,
parameter type req_t = std_cache_pkg::bypass_req_t,
parameter type rsp_t = std_cache_pkg::bypass_rsp_t
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// Master ports
@ -680,7 +687,11 @@ module axi_adapter_arbiter #(
input rsp_t rsp_i
);
enum logic { IDLE, SERVING } state_d, state_q;
enum logic {
IDLE,
SERVING
}
state_d, state_q;
req_t req_d, req_q;
logic [NR_PORTS-1:0] sel_d, sel_q;
@ -720,7 +731,7 @@ module axi_adapter_arbiter #(
end
end
default : /* default */;
default: /* default */;
endcase
end
@ -740,17 +751,26 @@ module axi_adapter_arbiter #(
// ------------
//pragma translate_off
`ifndef VERILATOR
`ifndef VERILATOR
// make sure that we eventually get an rvalid after we received a grant
assert property (@(posedge clk_i) rsp_i.gnt |-> ##[1:$] rsp_i.valid )
else begin $error("There was a grant without a rvalid"); $stop(); end
assert property (@(posedge clk_i) rsp_i.gnt |-> ##[1:$] rsp_i.valid)
else begin
$error("There was a grant without a rvalid");
$stop();
end
// assert that there is no grant without a request
assert property (@(negedge clk_i) rsp_i.gnt |-> req_o.req)
else begin $error("There was a grant without a request."); $stop(); end
else begin
$error("There was a grant without a request.");
$stop();
end
// assert that the address does not contain X when request is sent
assert property ( @(posedge clk_i) (req_o.req) |-> (!$isunknown(req_o.addr)) )
else begin $error("address contains X when request is set"); $stop(); end
assert property (@(posedge clk_i) (req_o.req) |-> (!$isunknown(req_o.addr)))
else begin
$error("address contains X when request is set");
$stop();
end
`endif
`endif
//pragma translate_on
endmodule

View file

@ -15,7 +15,10 @@
// write-back data cache.
module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
module std_cache_subsystem
import ariane_pkg::*;
import std_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NumPorts = 4,
parameter type axi_ar_chan_t = logic,
@ -65,22 +68,22 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
axi_rsp_t axi_resp_data;
cva6_icache_axi_wrapper #(
.CVA6Cfg ( CVA6Cfg ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.axi_req_t(axi_req_t),
.axi_rsp_t(axi_rsp_t)
) i_cva6_icache_axi_wrapper (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.priv_lvl_i ( priv_lvl_i ),
.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 ),
.axi_req_o ( axi_req_icache ),
.axi_resp_i ( axi_resp_icache )
.clk_i (clk_i),
.rst_ni (rst_ni),
.priv_lvl_i(priv_lvl_i),
.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),
.axi_req_o (axi_req_icache),
.axi_resp_i(axi_resp_icache)
);
// decreasing priority
@ -89,23 +92,23 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
// Port 2: Accelerator
// Port 3: Store Unit
std_nbdcache #(
.CVA6Cfg ( CVA6Cfg ),
.NumPorts ( NumPorts ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.NumPorts (NumPorts),
.axi_req_t(axi_req_t),
.axi_rsp_t(axi_rsp_t)
) i_nbdcache (
.clk_i,
.rst_ni,
.enable_i ( dcache_enable_i ),
.flush_i ( dcache_flush_i ),
.flush_ack_o ( dcache_flush_ack_o ),
.miss_o ( dcache_miss_o ),
.axi_bypass_o ( axi_req_bypass ),
.axi_bypass_i ( axi_resp_bypass ),
.axi_data_o ( axi_req_data ),
.axi_data_i ( axi_resp_data ),
.req_ports_i ( dcache_req_ports_i ),
.req_ports_o ( dcache_req_ports_o ),
.enable_i (dcache_enable_i),
.flush_i (dcache_flush_i),
.flush_ack_o (dcache_flush_ack_o),
.miss_o (dcache_miss_o),
.axi_bypass_o(axi_req_bypass),
.axi_bypass_i(axi_resp_bypass),
.axi_data_o (axi_req_data),
.axi_data_i (axi_resp_data),
.req_ports_i (dcache_req_ports_i),
.req_ports_o (dcache_req_ports_o),
.amo_req_i,
.amo_resp_o
);
@ -120,32 +123,32 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
// AR Channel
stream_arbiter #(
.DATA_T ( axi_ar_chan_t ),
.N_INP ( 3 )
.DATA_T(axi_ar_chan_t),
.N_INP (3)
) i_stream_arbiter_ar (
.clk_i,
.rst_ni,
.inp_data_i ( {axi_req_icache.ar, axi_req_bypass.ar, axi_req_data.ar} ),
.inp_valid_i ( {axi_req_icache.ar_valid, axi_req_bypass.ar_valid, axi_req_data.ar_valid} ),
.inp_ready_o ( {axi_resp_icache.ar_ready, axi_resp_bypass.ar_ready, axi_resp_data.ar_ready} ),
.oup_data_o ( axi_req_o.ar ),
.oup_valid_o ( axi_req_o.ar_valid ),
.oup_ready_i ( axi_resp_i.ar_ready )
.inp_data_i ({axi_req_icache.ar, axi_req_bypass.ar, axi_req_data.ar}),
.inp_valid_i({axi_req_icache.ar_valid, axi_req_bypass.ar_valid, axi_req_data.ar_valid}),
.inp_ready_o({axi_resp_icache.ar_ready, axi_resp_bypass.ar_ready, axi_resp_data.ar_ready}),
.oup_data_o (axi_req_o.ar),
.oup_valid_o(axi_req_o.ar_valid),
.oup_ready_i(axi_resp_i.ar_ready)
);
// AW Channel
stream_arbiter #(
.DATA_T ( axi_aw_chan_t ),
.N_INP ( 3 )
.DATA_T(axi_aw_chan_t),
.N_INP (3)
) i_stream_arbiter_aw (
.clk_i,
.rst_ni,
.inp_data_i ( {axi_req_icache.aw, axi_req_bypass.aw, axi_req_data.aw} ),
.inp_valid_i ( {axi_req_icache.aw_valid, axi_req_bypass.aw_valid, axi_req_data.aw_valid} ),
.inp_ready_o ( {axi_resp_icache.aw_ready, axi_resp_bypass.aw_ready, axi_resp_data.aw_ready} ),
.oup_data_o ( axi_req_o.aw ),
.oup_valid_o ( axi_req_o.aw_valid ),
.oup_ready_i ( axi_resp_i.aw_ready )
.inp_data_i ({axi_req_icache.aw, axi_req_bypass.aw, axi_req_data.aw}),
.inp_valid_i({axi_req_icache.aw_valid, axi_req_bypass.aw_valid, axi_req_data.aw_valid}),
.inp_ready_o({axi_resp_icache.aw_ready, axi_resp_bypass.aw_ready, axi_resp_data.aw_ready}),
.oup_data_o (axi_req_o.aw),
.oup_valid_o(axi_req_o.aw_valid),
.oup_ready_i(axi_resp_i.aw_ready)
);
// WID has been removed in AXI 4 so we need to keep track which AW request has been accepted
@ -161,25 +164,25 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
// W Channel
fifo_v3 #(
.DATA_WIDTH ( 2 ),
.DATA_WIDTH (2),
// we can have a maximum of 4 oustanding transactions as each port is blocking
.DEPTH ( 4 ),
.FALL_THROUGH ( 1'b1 )
.DEPTH (4),
.FALL_THROUGH(1'b1)
) i_fifo_w_channel (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( w_fifo_full ),
.empty_o ( ), // leave open
.usage_o ( w_fifo_usage ),
.data_i ( w_select ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (w_fifo_full),
.empty_o (), // leave open
.usage_o (w_fifo_usage),
.data_i (w_select),
// a new transaction was requested and granted
.push_i ( axi_req_o.aw_valid & axi_resp_i.aw_ready ),
.push_i (axi_req_o.aw_valid & axi_resp_i.aw_ready),
// write ID to select the output MUX
.data_o ( w_select_fifo ),
.data_o (w_select_fifo),
// transaction has finished
.pop_i ( axi_req_o.w_valid & axi_resp_i.w_ready & axi_req_o.w.last )
.pop_i (axi_req_o.w_valid & axi_resp_i.w_ready & axi_req_o.w.last)
);
// In fall-through mode, the empty_o will be low when push_i is high (on zero usage).
@ -191,16 +194,16 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
assign w_select_arbiter = w_fifo_empty ? (axi_req_o.aw_valid ? w_select : 0) : w_select_fifo;
stream_mux #(
.DATA_T ( axi_w_chan_t ),
.N_INP ( 3 )
.DATA_T(axi_w_chan_t),
.N_INP (3)
) i_stream_mux_w (
.inp_data_i ( {axi_req_data.w, axi_req_bypass.w, axi_req_icache.w} ),
.inp_valid_i ( {axi_req_data.w_valid, axi_req_bypass.w_valid, axi_req_icache.w_valid} ),
.inp_ready_o ( {axi_resp_data.w_ready, axi_resp_bypass.w_ready, axi_resp_icache.w_ready} ),
.inp_sel_i ( w_select_arbiter ),
.oup_data_o ( axi_req_o.w ),
.oup_valid_o ( axi_req_o.w_valid ),
.oup_ready_i ( axi_resp_i.w_ready )
.inp_data_i ({axi_req_data.w, axi_req_bypass.w, axi_req_icache.w}),
.inp_valid_i({axi_req_data.w_valid, axi_req_bypass.w_valid, axi_req_icache.w_valid}),
.inp_ready_o({axi_resp_data.w_ready, axi_resp_bypass.w_ready, axi_resp_icache.w_ready}),
.inp_sel_i (w_select_arbiter),
.oup_data_o (axi_req_o.w),
.oup_valid_o(axi_req_o.w_valid),
.oup_ready_i(axi_resp_i.w_ready)
);
// Route responses based on ID
@ -225,13 +228,13 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
end
stream_demux #(
.N_OUP ( 3 )
.N_OUP(3)
) i_stream_demux_r (
.inp_valid_i ( axi_resp_i.r_valid ),
.inp_ready_o ( axi_req_o.r_ready ),
.oup_sel_i ( r_select ),
.oup_valid_o ( {axi_resp_icache.r_valid, axi_resp_bypass.r_valid, axi_resp_data.r_valid} ),
.oup_ready_i ( {axi_req_icache.r_ready, axi_req_bypass.r_ready, axi_req_data.r_ready} )
.inp_valid_i(axi_resp_i.r_valid),
.inp_ready_o(axi_req_o.r_ready),
.oup_sel_i (r_select),
.oup_valid_o({axi_resp_icache.r_valid, axi_resp_bypass.r_valid, axi_resp_data.r_valid}),
.oup_ready_i({axi_req_icache.r_ready, axi_req_bypass.r_ready, axi_req_data.r_ready})
);
// B Channel
@ -252,40 +255,61 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
end
stream_demux #(
.N_OUP ( 3 )
.N_OUP(3)
) i_stream_demux_b (
.inp_valid_i ( axi_resp_i.b_valid ),
.inp_ready_o ( axi_req_o.b_ready ),
.oup_sel_i ( b_select ),
.oup_valid_o ( {axi_resp_icache.b_valid, axi_resp_bypass.b_valid, axi_resp_data.b_valid} ),
.oup_ready_i ( {axi_req_icache.b_ready, axi_req_bypass.b_ready, axi_req_data.b_ready} )
.inp_valid_i(axi_resp_i.b_valid),
.inp_ready_o(axi_req_o.b_ready),
.oup_sel_i (b_select),
.oup_valid_o({axi_resp_icache.b_valid, axi_resp_bypass.b_valid, axi_resp_data.b_valid}),
.oup_ready_i({axi_req_icache.b_ready, axi_req_bypass.b_ready, axi_req_data.b_ready})
);
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
a_invalid_instruction_fetch: assert property (
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);
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 (
a_invalid_write_data :
assert property (
@(posedge clk_i) disable iff (~rst_ni) dcache_req_ports_i[NumPorts-1].data_req |-> |dcache_req_ports_i[NumPorts-1].data_be |-> (|dcache_req_ports_i[NumPorts-1].data_wdata) !== 1'hX)
else $warning(1,"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X",
{dcache_req_ports_i[NumPorts-1].address_tag, dcache_req_ports_i[NumPorts-1].address_index}, dcache_req_ports_i[NumPorts-1].data_be, dcache_req_ports_i[NumPorts-1].data_wdata);
else
$warning(
1,
"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X",
{
dcache_req_ports_i[NumPorts-1].address_tag, dcache_req_ports_i[NumPorts-1].address_index
},
dcache_req_ports_i[NumPorts-1].data_be,
dcache_req_ports_i[NumPorts-1].data_wdata
);
generate
for(genvar j=0; j<NumPorts-1; j++) begin
a_invalid_read_data: assert property (
for (genvar j = 0; j < NumPorts - 1; 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);
else
$warning(
1,
"[l1 dcache] reading invalid data on port %01d: data=%016X",
j,
dcache_req_ports_o[j].data_rdata
);
end
endgenerate
`endif
//pragma translate_on
//pragma translate_on
endmodule // std_cache_subsystem

View file

@ -13,12 +13,15 @@
// Description: Nonblocking private L1 dcache
module std_nbdcache import std_cache_pkg::*; import ariane_pkg::*; #(
module std_nbdcache
import std_cache_pkg::*;
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NumPorts = 4,
parameter type axi_req_t = logic,
parameter type axi_rsp_t = logic
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// Cache management
@ -39,7 +42,7 @@ module std_nbdcache import std_cache_pkg::*; import ariane_pkg::*; #(
input axi_rsp_t axi_bypass_i
);
import std_cache_pkg::*;
import std_cache_pkg::*;
// -------------------------------
// Controller <-> Arbiter
@ -49,41 +52,41 @@ import std_cache_pkg::*;
// 3. Load Unit
// 4. Accelerator
// 5. Store unit
logic [NumPorts:0][DCACHE_SET_ASSOC-1:0] req;
logic [NumPorts:0][DCACHE_INDEX_WIDTH-1:0]addr;
logic [NumPorts:0] gnt;
cache_line_t [DCACHE_SET_ASSOC-1:0] rdata;
logic [NumPorts:0][DCACHE_TAG_WIDTH-1:0] tag;
logic [ NumPorts:0][ DCACHE_SET_ASSOC-1:0] req;
logic [ NumPorts:0][DCACHE_INDEX_WIDTH-1:0] addr;
logic [ NumPorts:0] gnt;
cache_line_t [ DCACHE_SET_ASSOC-1:0] rdata;
logic [ NumPorts:0][ DCACHE_TAG_WIDTH-1:0] tag;
cache_line_t [NumPorts:0] wdata;
logic [NumPorts:0] we;
cl_be_t [NumPorts:0] be;
logic [DCACHE_SET_ASSOC-1:0] hit_way;
cache_line_t [ NumPorts:0] wdata;
logic [ NumPorts:0] we;
cl_be_t [ NumPorts:0] be;
logic [ DCACHE_SET_ASSOC-1:0] hit_way;
// -------------------------------
// Controller <-> Miss unit
// -------------------------------
logic [NumPorts-1:0] busy;
logic [NumPorts-1:0][55:0] mshr_addr;
logic [NumPorts-1:0] mshr_addr_matches;
logic [NumPorts-1:0] mshr_index_matches;
logic [63:0] critical_word;
logic [ NumPorts-1:0] busy;
logic [ NumPorts-1:0][ 55:0] mshr_addr;
logic [ NumPorts-1:0] mshr_addr_matches;
logic [ NumPorts-1:0] mshr_index_matches;
logic [ 63:0] critical_word;
logic critical_word_valid;
logic [NumPorts-1:0][$bits(miss_req_t)-1:0] miss_req;
logic [NumPorts-1:0] miss_gnt;
logic [NumPorts-1:0] active_serving;
logic [ NumPorts-1:0][ $bits(miss_req_t)-1:0] miss_req;
logic [ NumPorts-1:0] miss_gnt;
logic [ NumPorts-1:0] active_serving;
logic [NumPorts-1:0] bypass_gnt;
logic [NumPorts-1:0] bypass_valid;
logic [NumPorts-1:0][63:0] bypass_data;
logic [ NumPorts-1:0] bypass_gnt;
logic [ NumPorts-1:0] bypass_valid;
logic [ NumPorts-1:0][ 63:0] bypass_data;
// -------------------------------
// Arbiter <-> Datram,
// -------------------------------
logic [DCACHE_SET_ASSOC-1:0] req_ram;
logic [ DCACHE_SET_ASSOC-1:0] req_ram;
logic [DCACHE_INDEX_WIDTH-1:0] addr_ram;
logic we_ram;
cache_line_t wdata_ram;
cache_line_t [DCACHE_SET_ASSOC-1:0] rdata_ram;
cache_line_t [ DCACHE_SET_ASSOC-1:0] rdata_ram;
cl_be_t be_ram;
// ------------------
@ -92,36 +95,36 @@ import std_cache_pkg::*;
generate
for (genvar i = 0; i < NumPorts; i++) begin : master_ports
cache_ctrl #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_cache_ctrl (
.bypass_i ( ~enable_i ),
.busy_o ( busy [i] ),
.bypass_i (~enable_i),
.busy_o (busy[i]),
// from core
.req_port_i ( req_ports_i [i] ),
.req_port_o ( req_ports_o [i] ),
.req_port_i(req_ports_i[i]),
.req_port_o(req_ports_o[i]),
// to SRAM array
.req_o ( req [i+1] ),
.addr_o ( addr [i+1] ),
.gnt_i ( gnt [i+1] ),
.data_i ( rdata ),
.tag_o ( tag [i+1] ),
.data_o ( wdata [i+1] ),
.we_o ( we [i+1] ),
.be_o ( be [i+1] ),
.hit_way_i ( hit_way ),
.req_o (req[i+1]),
.addr_o (addr[i+1]),
.gnt_i (gnt[i+1]),
.data_i (rdata),
.tag_o (tag[i+1]),
.data_o (wdata[i+1]),
.we_o (we[i+1]),
.be_o (be[i+1]),
.hit_way_i (hit_way),
.miss_req_o ( miss_req [i] ),
.miss_gnt_i ( miss_gnt [i] ),
.active_serving_i ( active_serving [i] ),
.critical_word_i ( critical_word ),
.critical_word_valid_i ( critical_word_valid ),
.bypass_gnt_i ( bypass_gnt [i] ),
.bypass_valid_i ( bypass_valid [i] ),
.bypass_data_i ( bypass_data [i] ),
.miss_req_o (miss_req[i]),
.miss_gnt_i (miss_gnt[i]),
.active_serving_i (active_serving[i]),
.critical_word_i (critical_word),
.critical_word_valid_i(critical_word_valid),
.bypass_gnt_i (bypass_gnt[i]),
.bypass_valid_i (bypass_valid[i]),
.bypass_data_i (bypass_data[i]),
.mshr_addr_o ( mshr_addr [i] ),
.mshr_addr_matches_i ( mshr_addr_matches [i] ),
.mshr_index_matches_i ( mshr_index_matches[i] ),
.mshr_addr_o (mshr_addr[i]),
.mshr_addr_matches_i (mshr_addr_matches[i]),
.mshr_index_matches_i(mshr_index_matches[i]),
.*
);
end
@ -131,33 +134,33 @@ import std_cache_pkg::*;
// Miss Handling Unit
// ------------------
miss_handler #(
.CVA6Cfg ( CVA6Cfg ),
.NR_PORTS ( NumPorts ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.NR_PORTS (NumPorts),
.axi_req_t(axi_req_t),
.axi_rsp_t(axi_rsp_t)
) i_miss_handler (
.flush_i ( flush_i ),
.busy_i ( |busy ),
.flush_i (flush_i),
.busy_i (|busy),
// AMOs
.amo_req_i ( amo_req_i ),
.amo_resp_o ( amo_resp_o ),
.miss_req_i ( miss_req ),
.miss_gnt_o ( miss_gnt ),
.bypass_gnt_o ( bypass_gnt ),
.bypass_valid_o ( bypass_valid ),
.bypass_data_o ( bypass_data ),
.critical_word_o ( critical_word ),
.critical_word_valid_o ( critical_word_valid ),
.mshr_addr_i ( mshr_addr ),
.mshr_addr_matches_o ( mshr_addr_matches ),
.mshr_index_matches_o ( mshr_index_matches ),
.active_serving_o ( active_serving ),
.req_o ( req [0] ),
.addr_o ( addr [0] ),
.data_i ( rdata ),
.be_o ( be [0] ),
.data_o ( wdata [0] ),
.we_o ( we [0] ),
.amo_req_i (amo_req_i),
.amo_resp_o (amo_resp_o),
.miss_req_i (miss_req),
.miss_gnt_o (miss_gnt),
.bypass_gnt_o (bypass_gnt),
.bypass_valid_o (bypass_valid),
.bypass_data_o (bypass_data),
.critical_word_o (critical_word),
.critical_word_valid_o(critical_word_valid),
.mshr_addr_i (mshr_addr),
.mshr_addr_matches_o (mshr_addr_matches),
.mshr_index_matches_o (mshr_index_matches),
.active_serving_o (active_serving),
.req_o (req[0]),
.addr_o (addr[0]),
.data_i (rdata),
.be_o (be[0]),
.data_o (wdata[0]),
.we_o (we[0]),
.axi_bypass_o,
.axi_bypass_i,
.axi_data_o,
@ -172,34 +175,34 @@ import std_cache_pkg::*;
// --------------
for (genvar i = 0; i < DCACHE_SET_ASSOC; i++) begin : sram_block
sram #(
.DATA_WIDTH ( DCACHE_LINE_WIDTH ),
.NUM_WORDS ( DCACHE_NUM_WORDS )
.DATA_WIDTH(DCACHE_LINE_WIDTH),
.NUM_WORDS (DCACHE_NUM_WORDS)
) data_sram (
.req_i ( req_ram [i] ),
.rst_ni ( rst_ni ),
.we_i ( we_ram ),
.addr_i ( addr_ram[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] ),
.wuser_i ( '0 ),
.wdata_i ( wdata_ram.data ),
.be_i ( be_ram.data ),
.ruser_o ( ),
.rdata_o ( rdata_ram[i].data ),
.req_i (req_ram[i]),
.rst_ni (rst_ni),
.we_i (we_ram),
.addr_i (addr_ram[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET]),
.wuser_i('0),
.wdata_i(wdata_ram.data),
.be_i (be_ram.data),
.ruser_o(),
.rdata_o(rdata_ram[i].data),
.*
);
sram #(
.DATA_WIDTH ( DCACHE_TAG_WIDTH ),
.NUM_WORDS ( DCACHE_NUM_WORDS )
.DATA_WIDTH(DCACHE_TAG_WIDTH),
.NUM_WORDS (DCACHE_NUM_WORDS)
) tag_sram (
.req_i ( req_ram [i] ),
.rst_ni ( rst_ni ),
.we_i ( we_ram ),
.addr_i ( addr_ram[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] ),
.wuser_i ( '0 ),
.wdata_i ( wdata_ram.tag ),
.be_i ( be_ram.tag ),
.ruser_o ( ),
.rdata_o ( rdata_ram[i].tag ),
.req_i (req_ram[i]),
.rst_ni (rst_ni),
.we_i (we_ram),
.addr_i (addr_ram[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET]),
.wuser_i('0),
.wdata_i(wdata_ram.tag),
.be_i (be_ram.tag),
.ruser_o(),
.rdata_o(rdata_ram[i].tag),
.*
);
@ -222,54 +225,55 @@ import std_cache_pkg::*;
end
sram #(
.USER_WIDTH ( 1 ),
.DATA_WIDTH ( 4*DCACHE_DIRTY_WIDTH ),
.NUM_WORDS ( DCACHE_NUM_WORDS )
.USER_WIDTH(1),
.DATA_WIDTH(4 * DCACHE_DIRTY_WIDTH),
.NUM_WORDS (DCACHE_NUM_WORDS)
) valid_dirty_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( |req_ram ),
.we_i ( we_ram ),
.addr_i ( addr_ram[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET] ),
.wuser_i ( '0 ),
.wdata_i ( dirty_wdata ),
.be_i ( be_ram.vldrty ),
.ruser_o ( ),
.rdata_o ( dirty_rdata )
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (|req_ram),
.we_i (we_ram),
.addr_i (addr_ram[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET]),
.wuser_i('0),
.wdata_i(dirty_wdata),
.be_i (be_ram.vldrty),
.ruser_o(),
.rdata_o(dirty_rdata)
);
// ------------------------------------------------
// Tag Comparison and memory arbitration
// ------------------------------------------------
tag_cmp #(
.CVA6Cfg ( CVA6Cfg ),
.NR_PORTS ( NumPorts+1 ),
.ADDR_WIDTH ( DCACHE_INDEX_WIDTH ),
.DCACHE_SET_ASSOC ( DCACHE_SET_ASSOC )
.CVA6Cfg (CVA6Cfg),
.NR_PORTS (NumPorts + 1),
.ADDR_WIDTH (DCACHE_INDEX_WIDTH),
.DCACHE_SET_ASSOC(DCACHE_SET_ASSOC)
) i_tag_cmp (
.req_i ( req ),
.gnt_o ( gnt ),
.addr_i ( addr ),
.wdata_i ( wdata ),
.we_i ( we ),
.be_i ( be ),
.rdata_o ( rdata ),
.tag_i ( tag ),
.hit_way_o ( hit_way ),
.req_i (req),
.gnt_o (gnt),
.addr_i (addr),
.wdata_i (wdata),
.we_i (we),
.be_i (be),
.rdata_o (rdata),
.tag_i (tag),
.hit_way_o(hit_way),
.req_o ( req_ram ),
.addr_o ( addr_ram ),
.wdata_o ( wdata_ram ),
.we_o ( we_ram ),
.be_o ( be_ram ),
.rdata_i ( rdata_ram ),
.req_o (req_ram),
.addr_o (addr_ram),
.wdata_o(wdata_ram),
.we_o (we_ram),
.be_o (be_ram),
.rdata_i(rdata_ram),
.*
);
//pragma translate_off
//pragma translate_off
initial begin
assert (DCACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth inside {2, 4, 8, 16}) else $fatal(1, "Cache line size needs to be a power of two multiple of AxiDataWidth");
assert (DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth inside {2, 4, 8, 16})
else $fatal(1, "Cache line size needs to be a power of two multiple of AxiDataWidth");
end
//pragma translate_on
//pragma translate_on
endmodule

View file

@ -38,7 +38,7 @@ module tag_cmp #(
output logic [DCACHE_SET_ASSOC-1:0] req_o,
output logic [ADDR_WIDTH-1:0] addr_o,
output logic [ ADDR_WIDTH-1:0] addr_o,
output l_data_t wdata_o,
output logic we_o,
output l_be_t be_o,
@ -52,9 +52,7 @@ module tag_cmp #(
always_comb begin : tag_sel
sel_tag = '0;
for (int unsigned i = 0; i < NR_PORTS; i++)
if (id_q[i])
sel_tag = tag_i[i];
for (int unsigned i = 0; i < NR_PORTS; i++) if (id_q[i]) sel_tag = tag_i[i];
end
for (genvar j = 0; j < DCACHE_SET_ASSOC; j++) begin : tag_cmp
@ -81,19 +79,20 @@ module tag_cmp #(
we_o = we_i[i];
wdata_o = wdata_i[i];
if (req_i[i])
break;
if (req_i[i]) break;
end
`ifndef SYNTHESIS
`ifndef VERILATOR
`ifndef SYNTHESIS
`ifndef VERILATOR
// assert that cache only hits on one way
// this only needs to be checked one cycle after all ways have been requested
onehot: assert property (
@(posedge clk_i) disable iff (!rst_ni) &req_i |=> $onehot0(hit_way_o))
else begin $fatal(1,"Hit should be one-hot encoded"); end
`endif
`endif
onehot :
assert property (@(posedge clk_i) disable iff (!rst_ni) &req_i |=> $onehot0(hit_way_o))
else begin
$fatal(1, "Hit should be one-hot encoded");
end
`endif
`endif
end
always_ff @(posedge clk_i or negedge rst_ni) begin

View file

@ -14,7 +14,10 @@
//
module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_axi_adapter
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned ReqFifoDepth = 2,
parameter int unsigned MetaFifoDepth = wt_cache_pkg::DCACHE_MAX_TX,
@ -74,7 +77,8 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
logic [CVA6Cfg.AxiAddrWidth-1:0] axi_rd_addr, axi_wr_addr;
logic [$clog2(AxiNumWords)-1:0] axi_rd_blen, axi_wr_blen;
logic [2:0] axi_rd_size, axi_wr_size;
logic [CVA6Cfg.AxiIdWidth-1:0] axi_rd_id_in, axi_wr_id_in, axi_rd_id_out, axi_wr_id_out, wr_id_out;
logic [CVA6Cfg.AxiIdWidth-1:0]
axi_rd_id_in, axi_wr_id_in, axi_rd_id_out, axi_wr_id_out, wr_id_out;
logic [AxiNumWords-1:0][CVA6Cfg.AxiDataWidth-1:0] axi_wr_data;
logic [AxiNumWords-1:0][CVA6Cfg.AxiUserWidth-1:0] axi_wr_user;
logic [CVA6Cfg.AxiDataWidth-1:0] axi_rd_data;
@ -98,31 +102,29 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
assign dcache_data_ack_o = dcache_data_req_i & ~dcache_data_full;
// arbiter
assign arb_req = {~(dcache_data_empty |
dcache_wr_full |
dcache_rd_full),
~(icache_data_empty |
icache_rd_full)};
assign arb_req = {
~(dcache_data_empty | dcache_wr_full | dcache_rd_full), ~(icache_data_empty | icache_rd_full)
};
assign arb_gnt = axi_rd_gnt | axi_wr_gnt;
rr_arb_tree #(
.NumIn (2),
.DataWidth (1),
.AxiVldRdy (1'b1),
.DataWidth(1),
.AxiVldRdy(1'b1),
.LockIn (1'b1)
) i_rr_arb_tree (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i('0 ),
.rr_i ('0 ),
.req_i (arb_req ),
.gnt_o (arb_ack ),
.data_i ('0 ),
.gnt_i (arb_gnt ),
.req_o ( ),
.data_o ( ),
.idx_o (arb_idx )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i('0),
.rr_i ('0),
.req_i (arb_req),
.gnt_o (arb_ack),
.data_i ('0),
.gnt_i (arb_gnt),
.req_o (),
.data_o (),
.idx_o (arb_idx)
);
// request side
@ -149,9 +151,9 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
axi_rd_blen = '0;
if (dcache_data.paddr[2] == 1'b0) begin
axi_wr_user = {{64-CVA6Cfg.AxiUserWidth{1'b0}}, dcache_data.user};
axi_wr_user = {{64 - CVA6Cfg.AxiUserWidth{1'b0}}, dcache_data.user};
end else begin
axi_wr_user = {dcache_data.user, {64-CVA6Cfg.AxiUserWidth{1'b0}}};
axi_wr_user = {dcache_data.user, {64 - CVA6Cfg.AxiUserWidth{1'b0}}};
end
// arbiter mux
@ -159,16 +161,17 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
// Cast to AXI address width
axi_rd_addr = dcache_data.paddr;
// If dcache_data.size MSB is set, we want to read as much as possible
axi_rd_size = dcache_data.size[2] ? $clog2(CVA6Cfg.AxiDataWidth/8) : dcache_data.size;
axi_rd_size = dcache_data.size[2] ? $clog2(CVA6Cfg.AxiDataWidth / 8) : dcache_data.size;
if (dcache_data.size[2]) begin
axi_rd_blen = ariane_pkg::DCACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1;
axi_rd_blen = ariane_pkg::DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth - 1;
end
end else begin
// Cast to AXI address width
axi_rd_addr = icache_data.paddr;
axi_rd_size = $clog2(CVA6Cfg.AxiDataWidth/8); // always request max number of words in case of ifill
axi_rd_size =
$clog2(CVA6Cfg.AxiDataWidth / 8); // always request max number of words in case of ifill
if (!icache_data.nc) begin
axi_rd_blen = ariane_pkg::ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1;
axi_rd_blen = ariane_pkg::ICACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth - 1;
end
end
@ -193,11 +196,15 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
wt_cache_pkg::DCACHE_STORE_REQ: begin
axi_wr_req = 1'b1;
axi_wr_be = '0;
unique case(dcache_data.size[1:0])
2'b00: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]] = '1; // byte
2'b01: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] +:2 ] = '1; // hword
2'b10: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] +:4 ] = '1; // word
default: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] +:8 ] = '1; // dword
unique case (dcache_data.size[1:0])
2'b00:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]] = '1; // byte
2'b01:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]+:2] = '1; // hword
2'b10:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]+:4] = '1; // word
default:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]+:8] = '1; // dword
endcase
end
//////////////////////////////////////
@ -210,11 +217,15 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
invalidate = arb_gnt;
axi_wr_req = 1'b1;
axi_wr_be = '0;
unique case(dcache_data.size[1:0])
2'b00: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]] = '1; // byte
2'b01: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] +:2 ] = '1; // hword
2'b10: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] +:4 ] = '1; // word
default: axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] +:8 ] = '1; // dword
unique case (dcache_data.size[1:0])
2'b00:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]] = '1; // byte
2'b01:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]+:2] = '1; // hword
2'b10:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]+:4] = '1; // word
default:
axi_wr_be[0][dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0]+:8] = '1; // dword
endcase
amo_gen_r_d = 1'b1;
// need to use a separate ID here, so concat an additional bit
@ -234,23 +245,44 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
amo_gen_r_d = 1'b0;
// needed to properly encode success. store the result at offset within the returned
// AXI data word aligned with the requested word size.
amo_off_d = dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-1:0] & ~((1 << dcache_data.size[1:0]) - 1);
amo_off_d = dcache_data.paddr[$clog2(CVA6Cfg.AxiDataWidth/8)-
1:0] & ~((1 << dcache_data.size[1:0]) - 1);
end
// RISC-V atops have a load semantic
AMO_SWAP: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ATOMICSWAP};
AMO_ADD: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
AMO_SWAP:
axi_wr_atop = {
axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ATOMICSWAP
};
AMO_ADD:
axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
AMO_AND: begin
// in this case we need to invert the data to get a "CLR"
axi_wr_data = ~{(CVA6Cfg.AxiDataWidth/riscv::XLEN){dcache_data.data}};
axi_wr_user = ~{(CVA6Cfg.AxiDataWidth/riscv::XLEN){dcache_data.user}};
axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
axi_wr_data = ~{(CVA6Cfg.AxiDataWidth / riscv::XLEN) {dcache_data.data}};
axi_wr_user = ~{(CVA6Cfg.AxiDataWidth / riscv::XLEN) {dcache_data.user}};
axi_wr_atop = {
axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR
};
end
AMO_OR: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
AMO_XOR: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
AMO_MAX: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
AMO_MAXU: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
AMO_MIN: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
AMO_MINU: axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
AMO_OR:
axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
AMO_XOR:
axi_wr_atop = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
AMO_MAX:
axi_wr_atop = {
axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX
};
AMO_MAXU:
axi_wr_atop = {
axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX
};
AMO_MIN:
axi_wr_atop = {
axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN
};
AMO_MINU:
axi_wr_atop = {
axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN
};
default: ; // Do nothing
endcase
end
@ -262,103 +294,103 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
end
fifo_v3 #(
.dtype ( icache_req_t ),
.DEPTH ( ReqFifoDepth )
.dtype(icache_req_t),
.DEPTH(ReqFifoDepth)
) i_icache_data_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( icache_data_full ),
.empty_o ( icache_data_empty ),
.usage_o ( ),
.data_i ( icache_data_i ),
.push_i ( icache_data_ack_o ),
.data_o ( icache_data ),
.pop_i ( arb_ack[0] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (icache_data_full),
.empty_o (icache_data_empty),
.usage_o (),
.data_i (icache_data_i),
.push_i (icache_data_ack_o),
.data_o (icache_data),
.pop_i (arb_ack[0])
);
fifo_v3 #(
.dtype ( dcache_req_t ),
.DEPTH ( ReqFifoDepth )
.dtype(dcache_req_t),
.DEPTH(ReqFifoDepth)
) i_dcache_data_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( dcache_data_full ),
.empty_o ( dcache_data_empty ),
.usage_o ( ),
.data_i ( dcache_data_i ),
.push_i ( dcache_data_ack_o ),
.data_o ( dcache_data ),
.pop_i ( arb_ack[1] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (dcache_data_full),
.empty_o (dcache_data_empty),
.usage_o (),
.data_i (dcache_data_i),
.push_i (dcache_data_ack_o),
.data_o (dcache_data),
.pop_i (arb_ack[1])
);
///////////////////////////////////////////////////////
// meta info feedback fifos
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// meta info feedback fifos
///////////////////////////////////////////////////////
logic icache_rtrn_rd_en, dcache_rtrn_rd_en;
logic icache_rtrn_vld_d, icache_rtrn_vld_q, dcache_rtrn_vld_d, dcache_rtrn_vld_q;
fifo_v3 #(
.DATA_WIDTH ( wt_cache_pkg::CACHE_ID_WIDTH ),
.DEPTH ( MetaFifoDepth )
.DATA_WIDTH(wt_cache_pkg::CACHE_ID_WIDTH),
.DEPTH (MetaFifoDepth)
) i_rd_icache_id (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( icache_rd_full ),
.empty_o ( icache_rd_empty ),
.usage_o ( ),
.data_i ( icache_data.tid ),
.push_i ( arb_ack[0] & axi_rd_gnt ),
.data_o ( icache_rtrn_tid_d ),
.pop_i ( icache_rtrn_vld_d )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (icache_rd_full),
.empty_o (icache_rd_empty),
.usage_o (),
.data_i (icache_data.tid),
.push_i (arb_ack[0] & axi_rd_gnt),
.data_o (icache_rtrn_tid_d),
.pop_i (icache_rtrn_vld_d)
);
fifo_v3 #(
.DATA_WIDTH ( wt_cache_pkg::CACHE_ID_WIDTH ),
.DEPTH ( MetaFifoDepth )
.DATA_WIDTH(wt_cache_pkg::CACHE_ID_WIDTH),
.DEPTH (MetaFifoDepth)
) i_rd_dcache_id (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( dcache_rd_full ),
.empty_o ( dcache_rd_empty ),
.usage_o ( ),
.data_i ( dcache_data.tid ),
.push_i ( arb_ack[1] & axi_rd_gnt ),
.data_o ( dcache_rtrn_rd_tid ),
.pop_i ( dcache_rd_pop )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (dcache_rd_full),
.empty_o (dcache_rd_empty),
.usage_o (),
.data_i (dcache_data.tid),
.push_i (arb_ack[1] & axi_rd_gnt),
.data_o (dcache_rtrn_rd_tid),
.pop_i (dcache_rd_pop)
);
fifo_v3 #(
.DATA_WIDTH ( wt_cache_pkg::CACHE_ID_WIDTH ),
.DEPTH ( MetaFifoDepth )
.DATA_WIDTH(wt_cache_pkg::CACHE_ID_WIDTH),
.DEPTH (MetaFifoDepth)
) i_wr_dcache_id (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( dcache_wr_full ),
.empty_o ( dcache_wr_empty ),
.usage_o ( ),
.data_i ( dcache_data.tid ),
.push_i ( arb_ack[1] & axi_wr_gnt ),
.data_o ( dcache_rtrn_wr_tid ),
.pop_i ( dcache_wr_pop )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (dcache_wr_full),
.empty_o (dcache_wr_empty),
.usage_o (),
.data_i (dcache_data.tid),
.push_i (arb_ack[1] & axi_wr_gnt),
.data_o (dcache_rtrn_wr_tid),
.pop_i (dcache_wr_pop)
);
// select correct tid to return
assign dcache_rtrn_tid_d = (dcache_wr_pop) ? dcache_rtrn_wr_tid : dcache_rtrn_rd_tid;
///////////////////////////////////////////////////////
// return path
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// return path
///////////////////////////////////////////////////////
// buffer write responses
logic b_full, b_empty, b_push, b_pop;
@ -366,29 +398,33 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
assign b_push = axi_wr_valid & axi_wr_rdy;
fifo_v3 #(
.DATA_WIDTH ( CVA6Cfg.AxiIdWidth + 1 ),
.DEPTH ( MetaFifoDepth ),
.FALL_THROUGH ( 1'b1 )
.DATA_WIDTH (CVA6Cfg.AxiIdWidth + 1),
.DEPTH (MetaFifoDepth),
.FALL_THROUGH(1'b1)
) i_b_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( b_full ),
.empty_o ( b_empty ),
.usage_o ( ),
.data_i ( {axi_wr_exokay, axi_wr_id_out} ),
.push_i ( b_push ),
.data_o ( {wr_exokay, wr_id_out} ),
.pop_i ( b_pop )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (b_full),
.empty_o (b_empty),
.usage_o (),
.data_i ({axi_wr_exokay, axi_wr_id_out}),
.push_i (b_push),
.data_o ({wr_exokay, wr_id_out}),
.pop_i (b_pop)
);
// buffer read responses in shift regs
logic icache_first_d, icache_first_q, dcache_first_d, dcache_first_q;
logic [ICACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:0][CVA6Cfg.AxiUserWidth-1:0] icache_rd_shift_user_d, icache_rd_shift_user_q;
logic [DCACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:0][CVA6Cfg.AxiUserWidth-1:0] dcache_rd_shift_user_d, dcache_rd_shift_user_q;
logic [ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:0][CVA6Cfg.AxiDataWidth-1:0] icache_rd_shift_d, icache_rd_shift_q;
logic [DCACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:0][CVA6Cfg.AxiDataWidth-1:0] dcache_rd_shift_d, dcache_rd_shift_q;
logic [ICACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:0][CVA6Cfg.AxiUserWidth-1:0]
icache_rd_shift_user_d, icache_rd_shift_user_q;
logic [DCACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:0][CVA6Cfg.AxiUserWidth-1:0]
dcache_rd_shift_user_d, dcache_rd_shift_user_q;
logic [ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:0][CVA6Cfg.AxiDataWidth-1:0]
icache_rd_shift_d, icache_rd_shift_q;
logic [DCACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:0][CVA6Cfg.AxiDataWidth-1:0]
dcache_rd_shift_d, dcache_rd_shift_q;
wt_cache_pkg::dcache_in_t dcache_rtrn_type_d, dcache_rtrn_type_q;
wt_cache_pkg::dcache_inval_t dcache_rtrn_inv_d, dcache_rtrn_inv_q;
logic dcache_sc_rtrn, axi_rd_last;
@ -423,9 +459,13 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
if (ICACHE_LINE_WIDTH == CVA6Cfg.AxiDataWidth) begin
icache_rd_shift_d = axi_rd_data;
end else begin
icache_rd_shift_d = {axi_rd_data, icache_rd_shift_q[ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:1]};
icache_rd_shift_d = {
axi_rd_data, icache_rd_shift_q[ICACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:1]
};
end
icache_rd_shift_user_d = {axi_rd_user, icache_rd_shift_user_q[ICACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:1]};
icache_rd_shift_user_d = {
axi_rd_user, icache_rd_shift_user_q[ICACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:1]
};
// if this is a single word transaction, we need to make sure that word is placed at offset 0
if (icache_first_q) begin
icache_rd_shift_d[0] = axi_rd_data;
@ -438,9 +478,13 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
if (DCACHE_LINE_WIDTH == CVA6Cfg.AxiDataWidth) begin
dcache_rd_shift_d = axi_rd_data;
end else begin
dcache_rd_shift_d = {axi_rd_data, dcache_rd_shift_q[DCACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:1]};
dcache_rd_shift_d = {
axi_rd_data, dcache_rd_shift_q[DCACHE_LINE_WIDTH/CVA6Cfg.AxiDataWidth-1:1]
};
end
dcache_rd_shift_user_d = {axi_rd_user, dcache_rd_shift_user_q[DCACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:1]};
dcache_rd_shift_user_d = {
axi_rd_user, dcache_rd_shift_user_q[DCACHE_USER_LINE_WIDTH/CVA6Cfg.AxiUserWidth-1:1]
};
// if this is a single word transaction, we need to make sure that word is placed at offset 0
if (dcache_first_q) begin
dcache_rd_shift_d[0] = axi_rd_data;
@ -598,59 +642,59 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
end
///////////////////////////////////////////////////////
// axi protocol shim
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// axi protocol shim
///////////////////////////////////////////////////////
axi_shim #(
.CVA6Cfg ( CVA6Cfg ),
.AxiNumWords ( AxiNumWords ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
.CVA6Cfg (CVA6Cfg),
.AxiNumWords(AxiNumWords),
.axi_req_t (axi_req_t),
.axi_rsp_t (axi_rsp_t)
) i_axi_shim (
.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_lock_i ( axi_rd_lock ),
.rd_last_o ( axi_rd_last ),
.rd_valid_o ( axi_rd_valid ),
.rd_data_o ( axi_rd_data ),
.rd_user_o ( axi_rd_user ),
.rd_id_o ( axi_rd_id_out ),
.rd_exokay_o ( axi_rd_exokay ),
.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_user_i ( axi_wr_user ),
.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_lock_i ( axi_wr_lock ),
.wr_atop_i ( axi_wr_atop ),
.wr_rdy_i ( axi_wr_rdy ),
.wr_valid_o ( axi_wr_valid ),
.wr_id_o ( axi_wr_id_out ),
.wr_exokay_o ( axi_wr_exokay ),
.axi_req_o ( axi_req_o ),
.axi_resp_i ( axi_resp_i )
.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_lock_i (axi_rd_lock),
.rd_last_o (axi_rd_last),
.rd_valid_o (axi_rd_valid),
.rd_data_o (axi_rd_data),
.rd_user_o (axi_rd_user),
.rd_id_o (axi_rd_id_out),
.rd_exokay_o(axi_rd_exokay),
.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_user_i (axi_wr_user),
.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_lock_i (axi_wr_lock),
.wr_atop_i (axi_wr_atop),
.wr_rdy_i (axi_wr_rdy),
.wr_valid_o (axi_wr_valid),
.wr_id_o (axi_wr_id_out),
.wr_exokay_o(axi_wr_exokay),
.axi_req_o (axi_req_o),
.axi_resp_i (axi_resp_i)
);
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_l15_adapter

View file

@ -19,7 +19,10 @@
// L1.5 interface.
module wt_cache_subsystem import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_cache_subsystem
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NumPorts = 4,
parameter type noc_req_t = logic,
@ -75,23 +78,23 @@ module wt_cache_subsystem import ariane_pkg::*; import wt_cache_pkg::*; #(
cva6_icache #(
// use ID 0 for icache reads
.CVA6Cfg ( CVA6Cfg ),
.RdTxId ( 0 )
.CVA6Cfg(CVA6Cfg),
.RdTxId (0)
) i_cva6_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 )
.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)
);
@ -100,109 +103,131 @@ module wt_cache_subsystem import ariane_pkg::*; import wt_cache_pkg::*; #(
// they have equal prio and are RR arbited
// Port 2 is write only and goes into the merging write buffer
wt_dcache #(
.CVA6Cfg ( CVA6Cfg ),
.CVA6Cfg (CVA6Cfg),
// use ID 1 for dcache reads and amos. note that the writebuffer
// uses all IDs up to DCACHE_MAX_TX-1 for write transactions.
.RdAmoTxId ( 1 )
.RdAmoTxId(1)
) i_wt_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 ),
.wbuffer_not_ni_o ( wbuffer_not_ni_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 ),
.miss_vld_bits_o ( miss_vld_bits_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 )
.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),
.wbuffer_not_ni_o(wbuffer_not_ni_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),
.miss_vld_bits_o (miss_vld_bits_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)
);
///////////////////////////////////////////////////////
// memory plumbing, either use 64bit AXI port or native
// L15 cache interface (derived from OpenSPARC CCX).
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// memory plumbing, either use 64bit AXI port or native
// L15 cache interface (derived from OpenSPARC CCX).
///////////////////////////////////////////////////////
`ifdef PITON_ARIANE
wt_l15_adapter #(
.CVA6Cfg ( CVA6Cfg ),
.CVA6Cfg(CVA6Cfg),
) 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 ( noc_req_o ),
.l15_rtrn_i ( noc_resp_i )
.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 (noc_req_o),
.l15_rtrn_i (noc_resp_i)
);
`else
wt_axi_adapter #(
.CVA6Cfg ( CVA6Cfg ),
.axi_req_t ( noc_req_t ),
.axi_rsp_t ( noc_resp_t )
.CVA6Cfg (CVA6Cfg),
.axi_req_t(noc_req_t),
.axi_rsp_t(noc_resp_t)
) 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 ),
.axi_req_o ( noc_req_o ),
.axi_resp_i ( noc_resp_i ),
.inval_addr_i ( inval_addr_i ),
.inval_valid_i ( inval_valid_i ),
.inval_ready_o ( inval_ready_o )
.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),
.axi_req_o (noc_req_o),
.axi_resp_i (noc_resp_i),
.inval_addr_i (inval_addr_i),
.inval_valid_i (inval_valid_i),
.inval_ready_o (inval_ready_o)
);
`endif
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
a_invalid_instruction_fetch: assert property (
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);
else
$warning(
1,
"[l1 dcache] reading invalid instructions: vaddr=%08X, data=%08X",
icache_dreq_o.vaddr,
icache_dreq_o.data
);
for (genvar j=0; j<riscv::XLEN/8; j++) begin : gen_invalid_write_assertion
a_invalid_write_data: assert property (
for (genvar j = 0; j < riscv::XLEN / 8; j++) begin : gen_invalid_write_assertion
a_invalid_write_data :
assert property (
@(posedge clk_i) disable iff (!rst_ni) dcache_req_ports_i[NumPorts-1].data_req |-> dcache_req_ports_i[NumPorts-1].data_be[j] |-> (|dcache_req_ports_i[NumPorts-1].data_wdata[j*8+:8] !== 1'hX))
else $warning(1,"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X, databe=%016X",
{dcache_req_ports_i[NumPorts-1].address_tag, dcache_req_ports_i[NumPorts-1].address_index}, dcache_req_ports_i[NumPorts-1].data_be, dcache_req_ports_i[NumPorts-1].data_wdata, dcache_req_ports_i[NumPorts-1].data_be & dcache_req_ports_i[NumPorts-1].data_wdata);
else
$warning(
1,
"[l1 dcache] writing invalid data: paddr=%016X, be=%02X, data=%016X, databe=%016X",
{
dcache_req_ports_i[NumPorts-1].address_tag, dcache_req_ports_i[NumPorts-1].address_index
},
dcache_req_ports_i[NumPorts-1].data_be,
dcache_req_ports_i[NumPorts-1].data_wdata,
dcache_req_ports_i[NumPorts-1].data_be & dcache_req_ports_i[NumPorts-1].data_wdata
);
end
for (genvar j=0; j<NumPorts-1; j++) begin : gen_assertion
a_invalid_read_data: assert property (
for (genvar j = 0; j < NumPorts - 1; j++) begin : gen_assertion
a_invalid_read_data :
assert property (
@(posedge clk_i) disable iff (!rst_ni) dcache_req_ports_o[j].data_rvalid && ~dcache_req_ports_i[j].kill_req |-> (|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);
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
//pragma translate_on
endmodule // wt_cache_subsystem

View file

@ -13,7 +13,10 @@
// Description: Write-Through Data cache that is compatible with openpiton.
module wt_dcache import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_dcache
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NumPorts = 4, // number of miss ports
// ID to be used for read and AMO transactions.
@ -54,284 +57,285 @@ module wt_dcache import ariane_pkg::*; import wt_cache_pkg::*; #(
// miss unit <-> memory
logic wr_cl_vld;
logic wr_cl_nc;
logic [DCACHE_SET_ASSOC-1:0] wr_cl_we;
logic [DCACHE_TAG_WIDTH-1:0] wr_cl_tag;
logic [DCACHE_CL_IDX_WIDTH-1:0] wr_cl_idx;
logic [DCACHE_OFFSET_WIDTH-1:0] wr_cl_off;
logic [DCACHE_LINE_WIDTH-1:0] wr_cl_data;
logic [ DCACHE_SET_ASSOC-1:0] wr_cl_we;
logic [ DCACHE_TAG_WIDTH-1:0] wr_cl_tag;
logic [ DCACHE_CL_IDX_WIDTH-1:0] wr_cl_idx;
logic [ DCACHE_OFFSET_WIDTH-1:0] wr_cl_off;
logic [ DCACHE_LINE_WIDTH-1:0] wr_cl_data;
logic [DCACHE_USER_LINE_WIDTH-1:0] wr_cl_user;
logic [DCACHE_LINE_WIDTH/8-1:0] wr_cl_data_be;
logic [DCACHE_SET_ASSOC-1:0] wr_vld_bits;
logic [DCACHE_SET_ASSOC-1:0] wr_req;
logic [ DCACHE_LINE_WIDTH/8-1:0] wr_cl_data_be;
logic [ DCACHE_SET_ASSOC-1:0] wr_vld_bits;
logic [ DCACHE_SET_ASSOC-1:0] wr_req;
logic wr_ack;
logic [DCACHE_CL_IDX_WIDTH-1:0] wr_idx;
logic [DCACHE_OFFSET_WIDTH-1:0] wr_off;
logic [ DCACHE_CL_IDX_WIDTH-1:0] wr_idx;
logic [ DCACHE_OFFSET_WIDTH-1:0] wr_off;
riscv::xlen_t wr_data;
logic [(riscv::XLEN/8)-1:0] wr_data_be;
logic [DCACHE_USER_WIDTH-1:0] wr_user;
logic [ (riscv::XLEN/8)-1:0] wr_data_be;
logic [ DCACHE_USER_WIDTH-1:0] wr_user;
// miss unit <-> controllers/wbuffer
logic [NumPorts-1:0] miss_req;
logic [NumPorts-1:0] miss_ack;
logic [NumPorts-1:0] miss_nc;
logic [NumPorts-1:0] miss_we;
logic [NumPorts-1:0][riscv::XLEN-1:0] miss_wdata;
logic [NumPorts-1:0][DCACHE_USER_WIDTH-1:0] miss_wuser;
logic [NumPorts-1:0][riscv::PLEN-1:0] miss_paddr;
logic [NumPorts-1:0][2:0] miss_size;
logic [NumPorts-1:0][CACHE_ID_WIDTH-1:0] miss_id;
logic [NumPorts-1:0] miss_replay;
logic [NumPorts-1:0] miss_rtrn_vld;
logic [CACHE_ID_WIDTH-1:0] miss_rtrn_id;
logic [ NumPorts-1:0] miss_req;
logic [ NumPorts-1:0] miss_ack;
logic [ NumPorts-1:0] miss_nc;
logic [ NumPorts-1:0] miss_we;
logic [ NumPorts-1:0][ riscv::XLEN-1:0] miss_wdata;
logic [ NumPorts-1:0][ DCACHE_USER_WIDTH-1:0] miss_wuser;
logic [ NumPorts-1:0][ riscv::PLEN-1:0] miss_paddr;
logic [ NumPorts-1:0][ 2:0] miss_size;
logic [ NumPorts-1:0][ CACHE_ID_WIDTH-1:0] miss_id;
logic [ NumPorts-1:0] miss_replay;
logic [ NumPorts-1:0] miss_rtrn_vld;
logic [ CACHE_ID_WIDTH-1:0] miss_rtrn_id;
// memory <-> read controllers/miss unit
logic [NumPorts-1:0] rd_prio;
logic [NumPorts-1:0] rd_tag_only;
logic [NumPorts-1:0] rd_req;
logic [NumPorts-1:0] rd_ack;
logic [NumPorts-1:0][DCACHE_TAG_WIDTH-1:0] rd_tag;
logic [NumPorts-1:0][DCACHE_CL_IDX_WIDTH-1:0] rd_idx;
logic [NumPorts-1:0][DCACHE_OFFSET_WIDTH-1:0] rd_off;
logic [ NumPorts-1:0] rd_prio;
logic [ NumPorts-1:0] rd_tag_only;
logic [ NumPorts-1:0] rd_req;
logic [ NumPorts-1:0] rd_ack;
logic [ NumPorts-1:0][ DCACHE_TAG_WIDTH-1:0] rd_tag;
logic [ NumPorts-1:0][DCACHE_CL_IDX_WIDTH-1:0] rd_idx;
logic [ NumPorts-1:0][DCACHE_OFFSET_WIDTH-1:0] rd_off;
riscv::xlen_t rd_data;
logic [DCACHE_USER_WIDTH-1:0] rd_user;
logic [DCACHE_SET_ASSOC-1:0] rd_vld_bits;
logic [DCACHE_SET_ASSOC-1:0] rd_hit_oh;
logic [ DCACHE_USER_WIDTH-1:0] rd_user;
logic [ DCACHE_SET_ASSOC-1:0] rd_vld_bits;
logic [ DCACHE_SET_ASSOC-1:0] rd_hit_oh;
// miss unit <-> wbuffer
logic [DCACHE_MAX_TX-1:0][riscv::PLEN-1:0] tx_paddr;
logic [DCACHE_MAX_TX-1:0] tx_vld;
logic [ DCACHE_MAX_TX-1:0][ riscv::PLEN-1:0] tx_paddr;
logic [ DCACHE_MAX_TX-1:0] tx_vld;
// wbuffer <-> memory
wbuffer_t [DCACHE_WBUF_DEPTH-1:0] wbuffer_data;
wbuffer_t [ DCACHE_WBUF_DEPTH-1:0] wbuffer_data;
///////////////////////////////////////////////////////
// miss handling unit
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// miss handling unit
///////////////////////////////////////////////////////
wt_dcache_missunit #(
.CVA6Cfg ( CVA6Cfg ),
.AmoTxId ( RdAmoTxId ),
.NumPorts ( NumPorts )
.CVA6Cfg (CVA6Cfg),
.AmoTxId (RdAmoTxId),
.NumPorts(NumPorts)
) i_wt_dcache_missunit (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.enable_i ( enable_i ),
.flush_i ( flush_i ),
.flush_ack_o ( flush_ack_o ),
.miss_o ( miss_o ),
.wbuffer_empty_i ( wbuffer_empty_o ),
.cache_en_o ( cache_en ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.enable_i (enable_i),
.flush_i (flush_i),
.flush_ack_o (flush_ack_o),
.miss_o (miss_o),
.wbuffer_empty_i(wbuffer_empty_o),
.cache_en_o (cache_en),
// amo interface
.amo_req_i ( amo_req_i ),
.amo_resp_o ( amo_resp_o ),
.amo_req_i (amo_req_i),
.amo_resp_o (amo_resp_o),
// miss handling interface
.miss_req_i ( miss_req ),
.miss_ack_o ( miss_ack ),
.miss_nc_i ( miss_nc ),
.miss_we_i ( miss_we ),
.miss_wdata_i ( miss_wdata ),
.miss_wuser_i ( miss_wuser ),
.miss_paddr_i ( miss_paddr ),
.miss_vld_bits_i ( miss_vld_bits_o ),
.miss_size_i ( miss_size ),
.miss_id_i ( miss_id ),
.miss_replay_o ( miss_replay ),
.miss_rtrn_vld_o ( miss_rtrn_vld ),
.miss_rtrn_id_o ( miss_rtrn_id ),
.miss_req_i (miss_req),
.miss_ack_o (miss_ack),
.miss_nc_i (miss_nc),
.miss_we_i (miss_we),
.miss_wdata_i (miss_wdata),
.miss_wuser_i (miss_wuser),
.miss_paddr_i (miss_paddr),
.miss_vld_bits_i(miss_vld_bits_o),
.miss_size_i (miss_size),
.miss_id_i (miss_id),
.miss_replay_o (miss_replay),
.miss_rtrn_vld_o(miss_rtrn_vld),
.miss_rtrn_id_o (miss_rtrn_id),
// from writebuffer
.tx_paddr_i ( tx_paddr ),
.tx_vld_i ( tx_vld ),
.tx_paddr_i (tx_paddr),
.tx_vld_i (tx_vld),
// cache memory interface
.wr_cl_vld_o ( wr_cl_vld ),
.wr_cl_nc_o ( wr_cl_nc ),
.wr_cl_we_o ( wr_cl_we ),
.wr_cl_tag_o ( wr_cl_tag ),
.wr_cl_idx_o ( wr_cl_idx ),
.wr_cl_off_o ( wr_cl_off ),
.wr_cl_data_o ( wr_cl_data ),
.wr_cl_user_o ( wr_cl_user ),
.wr_cl_data_be_o ( wr_cl_data_be ),
.wr_vld_bits_o ( wr_vld_bits ),
.wr_cl_vld_o (wr_cl_vld),
.wr_cl_nc_o (wr_cl_nc),
.wr_cl_we_o (wr_cl_we),
.wr_cl_tag_o (wr_cl_tag),
.wr_cl_idx_o (wr_cl_idx),
.wr_cl_off_o (wr_cl_off),
.wr_cl_data_o (wr_cl_data),
.wr_cl_user_o (wr_cl_user),
.wr_cl_data_be_o(wr_cl_data_be),
.wr_vld_bits_o (wr_vld_bits),
// memory interface
.mem_rtrn_vld_i ( mem_rtrn_vld_i ),
.mem_rtrn_i ( mem_rtrn_i ),
.mem_data_req_o ( mem_data_req_o ),
.mem_data_ack_i ( mem_data_ack_i ),
.mem_data_o ( mem_data_o )
.mem_rtrn_vld_i (mem_rtrn_vld_i),
.mem_rtrn_i (mem_rtrn_i),
.mem_data_req_o (mem_data_req_o),
.mem_data_ack_i (mem_data_ack_i),
.mem_data_o (mem_data_o)
);
///////////////////////////////////////////////////////
// read controllers (LD unit and PTW/MMU)
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// read controllers (LD unit and PTW/MMU)
///////////////////////////////////////////////////////
// note: last read port is used by the write buffer
for(genvar k=0; k<NumPorts-1; k++) begin : gen_rd_ports
for (genvar k = 0; k < NumPorts - 1; k++) begin : gen_rd_ports
// set these to high prio ports
assign rd_prio[k] = 1'b1;
wt_dcache_ctrl #(
.CVA6Cfg ( CVA6Cfg ),
.RdTxId ( RdAmoTxId )
.CVA6Cfg(CVA6Cfg),
.RdTxId (RdAmoTxId)
) i_wt_dcache_ctrl (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.cache_en_i ( cache_en ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.cache_en_i (cache_en),
// reqs from core
.req_port_i ( req_ports_i [k] ),
.req_port_o ( req_ports_o [k] ),
.req_port_i (req_ports_i[k]),
.req_port_o (req_ports_o[k]),
// miss interface
.miss_req_o ( miss_req [k] ),
.miss_ack_i ( miss_ack [k] ),
.miss_we_o ( miss_we [k] ),
.miss_wdata_o ( miss_wdata [k] ),
.miss_wuser_o ( miss_wuser [k] ),
.miss_vld_bits_o ( miss_vld_bits_o[k]),
.miss_paddr_o ( miss_paddr [k] ),
.miss_nc_o ( miss_nc [k] ),
.miss_size_o ( miss_size [k] ),
.miss_id_o ( miss_id [k] ),
.miss_replay_i ( miss_replay [k] ),
.miss_rtrn_vld_i ( miss_rtrn_vld [k] ),
.miss_req_o (miss_req[k]),
.miss_ack_i (miss_ack[k]),
.miss_we_o (miss_we[k]),
.miss_wdata_o (miss_wdata[k]),
.miss_wuser_o (miss_wuser[k]),
.miss_vld_bits_o(miss_vld_bits_o[k]),
.miss_paddr_o (miss_paddr[k]),
.miss_nc_o (miss_nc[k]),
.miss_size_o (miss_size[k]),
.miss_id_o (miss_id[k]),
.miss_replay_i (miss_replay[k]),
.miss_rtrn_vld_i(miss_rtrn_vld[k]),
// used to detect readout mux collisions
.wr_cl_vld_i ( wr_cl_vld ),
.wr_cl_vld_i (wr_cl_vld),
// cache mem interface
.rd_tag_o ( rd_tag [k] ),
.rd_idx_o ( rd_idx [k] ),
.rd_off_o ( rd_off [k] ),
.rd_req_o ( rd_req [k] ),
.rd_tag_only_o ( rd_tag_only [k] ),
.rd_ack_i ( rd_ack [k] ),
.rd_data_i ( rd_data ),
.rd_user_i ( rd_user ),
.rd_vld_bits_i ( rd_vld_bits ),
.rd_hit_oh_i ( rd_hit_oh )
.rd_tag_o (rd_tag[k]),
.rd_idx_o (rd_idx[k]),
.rd_off_o (rd_off[k]),
.rd_req_o (rd_req[k]),
.rd_tag_only_o (rd_tag_only[k]),
.rd_ack_i (rd_ack[k]),
.rd_data_i (rd_data),
.rd_user_i (rd_user),
.rd_vld_bits_i (rd_vld_bits),
.rd_hit_oh_i (rd_hit_oh)
);
end
///////////////////////////////////////////////////////
// store unit controller
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// store unit controller
///////////////////////////////////////////////////////
// set read port to low priority
assign rd_prio[NumPorts-1] = 1'b0;
wt_dcache_wbuffer #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_wt_dcache_wbuffer (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.empty_o ( wbuffer_empty_o ),
.not_ni_o ( wbuffer_not_ni_o ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.empty_o (wbuffer_empty_o),
.not_ni_o (wbuffer_not_ni_o),
// TODO: fix this
.cache_en_i ( cache_en ),
.cache_en_i (cache_en),
// .cache_en_i ( '0 ),
// request ports from core (store unit)
.req_port_i ( req_ports_i [NumPorts-1] ),
.req_port_o ( req_ports_o [NumPorts-1] ),
.req_port_i (req_ports_i[NumPorts-1]),
.req_port_o (req_ports_o[NumPorts-1]),
// miss unit interface
.miss_req_o ( miss_req [NumPorts-1] ),
.miss_ack_i ( miss_ack [NumPorts-1] ),
.miss_we_o ( miss_we [NumPorts-1] ),
.miss_wdata_o ( miss_wdata [NumPorts-1] ),
.miss_wuser_o ( miss_wuser [NumPorts-1] ),
.miss_vld_bits_o ( miss_vld_bits_o[NumPorts-1] ),
.miss_paddr_o ( miss_paddr [NumPorts-1] ),
.miss_nc_o ( miss_nc [NumPorts-1] ),
.miss_size_o ( miss_size [NumPorts-1] ),
.miss_id_o ( miss_id [NumPorts-1] ),
.miss_rtrn_vld_i ( miss_rtrn_vld [NumPorts-1] ),
.miss_rtrn_id_i ( miss_rtrn_id ),
.miss_req_o (miss_req[NumPorts-1]),
.miss_ack_i (miss_ack[NumPorts-1]),
.miss_we_o (miss_we[NumPorts-1]),
.miss_wdata_o (miss_wdata[NumPorts-1]),
.miss_wuser_o (miss_wuser[NumPorts-1]),
.miss_vld_bits_o(miss_vld_bits_o[NumPorts-1]),
.miss_paddr_o (miss_paddr[NumPorts-1]),
.miss_nc_o (miss_nc[NumPorts-1]),
.miss_size_o (miss_size[NumPorts-1]),
.miss_id_o (miss_id[NumPorts-1]),
.miss_rtrn_vld_i(miss_rtrn_vld[NumPorts-1]),
.miss_rtrn_id_i (miss_rtrn_id),
// cache read interface
.rd_tag_o ( rd_tag [NumPorts-1] ),
.rd_idx_o ( rd_idx [NumPorts-1] ),
.rd_off_o ( rd_off [NumPorts-1] ),
.rd_req_o ( rd_req [NumPorts-1] ),
.rd_tag_only_o ( rd_tag_only [NumPorts-1] ),
.rd_ack_i ( rd_ack [NumPorts-1] ),
.rd_data_i ( rd_data ),
.rd_vld_bits_i ( rd_vld_bits ),
.rd_hit_oh_i ( rd_hit_oh ),
.rd_tag_o (rd_tag[NumPorts-1]),
.rd_idx_o (rd_idx[NumPorts-1]),
.rd_off_o (rd_off[NumPorts-1]),
.rd_req_o (rd_req[NumPorts-1]),
.rd_tag_only_o (rd_tag_only[NumPorts-1]),
.rd_ack_i (rd_ack[NumPorts-1]),
.rd_data_i (rd_data),
.rd_vld_bits_i (rd_vld_bits),
.rd_hit_oh_i (rd_hit_oh),
// incoming invalidations/cache refills
.wr_cl_vld_i ( wr_cl_vld ),
.wr_cl_idx_i ( wr_cl_idx ),
.wr_cl_vld_i (wr_cl_vld),
.wr_cl_idx_i (wr_cl_idx),
// single word write interface
.wr_req_o ( wr_req ),
.wr_ack_i ( wr_ack ),
.wr_idx_o ( wr_idx ),
.wr_off_o ( wr_off ),
.wr_data_o ( wr_data ),
.wr_user_o ( wr_user ),
.wr_data_be_o ( wr_data_be ),
.wr_req_o (wr_req),
.wr_ack_i (wr_ack),
.wr_idx_o (wr_idx),
.wr_off_o (wr_off),
.wr_data_o (wr_data),
.wr_user_o (wr_user),
.wr_data_be_o (wr_data_be),
// write buffer forwarding
.wbuffer_data_o ( wbuffer_data ),
.tx_paddr_o ( tx_paddr ),
.tx_vld_o ( tx_vld )
.wbuffer_data_o (wbuffer_data),
.tx_paddr_o (tx_paddr),
.tx_vld_o (tx_vld)
);
///////////////////////////////////////////////////////
// memory arrays, arbitration and tag comparison
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// memory arrays, arbitration and tag comparison
///////////////////////////////////////////////////////
wt_dcache_mem #(
.CVA6Cfg ( CVA6Cfg ),
.NumPorts ( NumPorts )
.CVA6Cfg (CVA6Cfg),
.NumPorts(NumPorts)
) i_wt_dcache_mem (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.clk_i (clk_i),
.rst_ni (rst_ni),
// read ports
.rd_prio_i ( rd_prio ),
.rd_tag_i ( rd_tag ),
.rd_idx_i ( rd_idx ),
.rd_off_i ( rd_off ),
.rd_req_i ( rd_req ),
.rd_tag_only_i ( rd_tag_only ),
.rd_ack_o ( rd_ack ),
.rd_vld_bits_o ( rd_vld_bits ),
.rd_hit_oh_o ( rd_hit_oh ),
.rd_data_o ( rd_data ),
.rd_user_o ( rd_user ),
.rd_prio_i (rd_prio),
.rd_tag_i (rd_tag),
.rd_idx_i (rd_idx),
.rd_off_i (rd_off),
.rd_req_i (rd_req),
.rd_tag_only_i (rd_tag_only),
.rd_ack_o (rd_ack),
.rd_vld_bits_o (rd_vld_bits),
.rd_hit_oh_o (rd_hit_oh),
.rd_data_o (rd_data),
.rd_user_o (rd_user),
// cacheline write port
.wr_cl_vld_i ( wr_cl_vld ),
.wr_cl_nc_i ( wr_cl_nc ),
.wr_cl_we_i ( wr_cl_we ),
.wr_cl_tag_i ( wr_cl_tag ),
.wr_cl_idx_i ( wr_cl_idx ),
.wr_cl_off_i ( wr_cl_off ),
.wr_cl_data_i ( wr_cl_data ),
.wr_cl_user_i ( wr_cl_user ),
.wr_cl_data_be_i ( wr_cl_data_be ),
.wr_vld_bits_i ( wr_vld_bits ),
.wr_cl_vld_i (wr_cl_vld),
.wr_cl_nc_i (wr_cl_nc),
.wr_cl_we_i (wr_cl_we),
.wr_cl_tag_i (wr_cl_tag),
.wr_cl_idx_i (wr_cl_idx),
.wr_cl_off_i (wr_cl_off),
.wr_cl_data_i (wr_cl_data),
.wr_cl_user_i (wr_cl_user),
.wr_cl_data_be_i(wr_cl_data_be),
.wr_vld_bits_i (wr_vld_bits),
// single word write port
.wr_req_i ( wr_req ),
.wr_ack_o ( wr_ack ),
.wr_idx_i ( wr_idx ),
.wr_off_i ( wr_off ),
.wr_data_i ( wr_data ),
.wr_user_i ( wr_user ),
.wr_data_be_i ( wr_data_be ),
.wr_req_i (wr_req),
.wr_ack_o (wr_ack),
.wr_idx_i (wr_idx),
.wr_off_i (wr_off),
.wr_data_i (wr_data),
.wr_user_i (wr_user),
.wr_data_be_i (wr_data_be),
// write buffer forwarding
.wbuffer_data_i ( wbuffer_data )
.wbuffer_data_i (wbuffer_data)
);
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
// check for concurrency issues
// check for concurrency issues
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
flush: assert property (
flush :
assert property (
@(posedge clk_i) disable iff (!rst_ni) flush_i |-> flush_ack_o |-> wbuffer_empty_o)
else $fatal(1,"[l1 dcache] flushed cache implies flushed wbuffer");
else $fatal(1, "[l1 dcache] flushed cache implies flushed wbuffer");
initial begin
// assert wrong parameterizations
assert (DCACHE_INDEX_WIDTH<=12)
else $fatal(1,"[l1 dcache] cache index width can be maximum 12bit since VM uses 4kB pages");
assert (DCACHE_INDEX_WIDTH <= 12)
else $fatal(1, "[l1 dcache] cache index width can be maximum 12bit since VM uses 4kB pages");
end
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_dcache

View file

@ -13,7 +13,10 @@
// Description: DCache controller for read port
module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_dcache_ctrl
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter logic [CACHE_ID_WIDTH-1:0] RdTxId = 1
) (
@ -52,7 +55,16 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
);
// controller FSM
typedef enum logic[2:0] {IDLE, READ, MISS_REQ, MISS_WAIT, KILL_MISS, KILL_MISS_ACK, REPLAY_REQ, REPLAY_READ} state_e;
typedef enum logic [2:0] {
IDLE,
READ,
MISS_REQ,
MISS_WAIT,
KILL_MISS,
KILL_MISS_ACK,
REPLAY_REQ,
REPLAY_READ
} state_e;
state_e state_d, state_q;
logic [DCACHE_TAG_WIDTH-1:0] address_tag_d, address_tag_q;
@ -63,9 +75,9 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
logic save_tag, rd_req_d, rd_req_q, rd_ack_d, rd_ack_q;
logic [1:0] data_size_d, data_size_q;
///////////////////////////////////////////////////////
// misc
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// misc
///////////////////////////////////////////////////////
// map address to tag/idx/offset and save
assign vld_data_d = (rd_req_q) ? rd_vld_bits_i : vld_data_q;
@ -88,7 +100,10 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
assign miss_size_o = (miss_nc_o) ? data_size_q : 3'b111;
// noncacheable if request goes to I/O space, or if cache is disabled
assign miss_nc_o = (~cache_en_i) | (~config_pkg::is_inside_cacheable_regions(CVA6Cfg, {{{64-DCACHE_TAG_WIDTH-DCACHE_INDEX_WIDTH}{1'b0}}, address_tag_q, {DCACHE_INDEX_WIDTH{1'b0}}}));
assign miss_nc_o = (~cache_en_i) | (~config_pkg::is_inside_cacheable_regions(
CVA6Cfg,
{{{64-DCACHE_TAG_WIDTH-DCACHE_INDEX_WIDTH}{1'b0}}, address_tag_q, {DCACHE_INDEX_WIDTH{1'b0}}}
));
assign miss_we_o = '0;
@ -99,9 +114,9 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
assign rd_ack_d = rd_ack_i;
assign rd_tag_only_o = '0;
///////////////////////////////////////////////////////
// main control logic
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// main control logic
///////////////////////////////////////////////////////
always_comb begin : p_fsm
// default assignment
@ -137,15 +152,15 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
rd_req_o = 1'b1;
// kill -> go back to IDLE
if(req_port_i.kill_req) begin
if (req_port_i.kill_req) begin
state_d = IDLE;
req_port_o.data_rvalid = 1'b1;
end else if(req_port_i.tag_valid | state_q==REPLAY_READ) begin
save_tag = (state_q!=REPLAY_READ);
if(wr_cl_vld_i || !rd_ack_q) begin
end else if (req_port_i.tag_valid | state_q == REPLAY_READ) begin
save_tag = (state_q != REPLAY_READ);
if (wr_cl_vld_i || !rd_ack_q) begin
state_d = REPLAY_REQ;
// we've got a hit
end else if((|rd_hit_oh_i) && cache_en_i) begin
end else if ((|rd_hit_oh_i) && cache_en_i) begin
state_d = IDLE;
req_port_o.data_rvalid = 1'b1;
// we can handle another request
@ -164,16 +179,16 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
MISS_REQ: begin
miss_req_o = 1'b1;
if(req_port_i.kill_req) begin
if (req_port_i.kill_req) begin
req_port_o.data_rvalid = 1'b1;
if(miss_ack_i) begin
if (miss_ack_i) begin
state_d = KILL_MISS;
end else begin
state_d = KILL_MISS_ACK;
end
end else if(miss_replay_i) begin
end else if (miss_replay_i) begin
state_d = REPLAY_REQ;
end else if(miss_ack_i) begin
end else if (miss_ack_i) begin
state_d = MISS_WAIT;
end
end
@ -181,14 +196,14 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
// wait until the memory transaction
// returns.
MISS_WAIT: begin
if(req_port_i.kill_req) begin
if (req_port_i.kill_req) begin
req_port_o.data_rvalid = 1'b1;
if(miss_rtrn_vld_i) begin
if (miss_rtrn_vld_i) begin
state_d = IDLE;
end else begin
state_d = KILL_MISS;
end
end else if(miss_rtrn_vld_i) begin
end else if (miss_rtrn_vld_i) begin
state_d = IDLE;
req_port_o.data_rvalid = 1'b1;
end
@ -200,7 +215,7 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
if (req_port_i.kill_req) begin
req_port_o.data_rvalid = 1'b1;
state_d = IDLE;
end else if(rd_ack_i) begin
end else if (rd_ack_i) begin
state_d = REPLAY_READ;
end
end
@ -209,9 +224,9 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
miss_req_o = 1'b1;
// in this case the miss handler did not issue
// a transaction and we can safely go to idle
if(miss_replay_i) begin
if (miss_replay_i) begin
state_d = IDLE;
end else if(miss_ack_i) begin
end else if (miss_ack_i) begin
state_d = KILL_MISS;
end
end
@ -231,12 +246,12 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
endcase // state_q
end
///////////////////////////////////////////////////////
// ff's
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ff's
///////////////////////////////////////////////////////
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if(!rst_ni) begin
if (!rst_ni) begin
state_q <= IDLE;
address_tag_q <= '0;
address_idx_q <= '0;
@ -259,23 +274,26 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
end
end
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) (!rd_ack_i) |=> cache_en_i |-> $onehot0(rd_hit_oh_i))
else $fatal(1,"[l1 dcache ctrl] rd_hit_oh_i signal must be hot1");
hot1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) (!rd_ack_i) |=> cache_en_i |-> $onehot0(
rd_hit_oh_i
))
else $fatal(1, "[l1 dcache ctrl] rd_hit_oh_i signal must be hot1");
initial begin
// assert wrong parameterizations
assert (DCACHE_INDEX_WIDTH<=12)
else $fatal(1,"[l1 dcache ctrl] cache index width can be maximum 12bit since VM uses 4kB pages");
assert (DCACHE_INDEX_WIDTH <= 12)
else
$fatal(1, "[l1 dcache ctrl] cache index width can be maximum 12bit since VM uses 4kB pages");
end
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_dcache_ctrl

View file

@ -26,7 +26,10 @@
// low prio ports (rd_prio_i[port_nr] = '1b0)
module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_dcache_mem
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NumPorts = 3
) (
@ -49,14 +52,14 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
// only available on port 0, uses address signals of port 0
input logic wr_cl_vld_i,
input logic wr_cl_nc_i, // noncacheable access
input logic [DCACHE_SET_ASSOC-1:0] wr_cl_we_i, // writes a full cacheline
input logic [DCACHE_TAG_WIDTH-1:0] wr_cl_tag_i,
input logic [DCACHE_CL_IDX_WIDTH-1:0] wr_cl_idx_i,
input logic [DCACHE_OFFSET_WIDTH-1:0] wr_cl_off_i,
input logic [DCACHE_LINE_WIDTH-1:0] wr_cl_data_i,
input logic [ DCACHE_SET_ASSOC-1:0] wr_cl_we_i, // writes a full cacheline
input logic [ DCACHE_TAG_WIDTH-1:0] wr_cl_tag_i,
input logic [ DCACHE_CL_IDX_WIDTH-1:0] wr_cl_idx_i,
input logic [ DCACHE_OFFSET_WIDTH-1:0] wr_cl_off_i,
input logic [ DCACHE_LINE_WIDTH-1:0] wr_cl_data_i,
input logic [DCACHE_USER_LINE_WIDTH-1:0] wr_cl_user_i,
input logic [DCACHE_LINE_WIDTH/8-1:0] wr_cl_data_be_i,
input logic [DCACHE_SET_ASSOC-1:0] wr_vld_bits_i,
input logic [ DCACHE_LINE_WIDTH/8-1:0] wr_cl_data_be_i,
input logic [ DCACHE_SET_ASSOC-1:0] wr_vld_bits_i,
// separate port for single word write, no tag access
input logic [DCACHE_SET_ASSOC-1:0] wr_req_i, // write a single word to offset off_i[:3]
@ -72,9 +75,8 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
);
// functions
function automatic logic [DCACHE_NUM_BANKS-1:0] dcache_cl_bin2oh (
input logic [DCACHE_NUM_BANKS_WIDTH-1:0] in
);
function automatic logic [DCACHE_NUM_BANKS-1:0] dcache_cl_bin2oh(
input logic [DCACHE_NUM_BANKS_WIDTH-1:0] in);
logic [DCACHE_NUM_BANKS-1:0] out;
out = '0;
out[in] = 1'b1;
@ -83,11 +85,15 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
// number of bits needed to address AXI data. If AxiDataWidth equals XLEN this parameter
// is not needed. Therefore, increment it by one to avoid reverse range select during elaboration.
localparam AXI_OFFSET_WIDTH = CVA6Cfg.AxiDataWidth == riscv::XLEN ? $clog2(CVA6Cfg.AxiDataWidth/8)+1 : $clog2(CVA6Cfg.AxiDataWidth/8);
localparam AXI_OFFSET_WIDTH = CVA6Cfg.AxiDataWidth == riscv::XLEN ? $clog2(
CVA6Cfg.AxiDataWidth / 8
) + 1 : $clog2(
CVA6Cfg.AxiDataWidth / 8
);
logic [DCACHE_NUM_BANKS-1:0] bank_req;
logic [DCACHE_NUM_BANKS-1:0] bank_we;
logic [DCACHE_NUM_BANKS-1:0][DCACHE_SET_ASSOC-1:0][(riscv::XLEN/8)-1:0] bank_be;
logic [DCACHE_NUM_BANKS-1:0][ DCACHE_SET_ASSOC-1:0][(riscv::XLEN/8)-1:0] bank_be;
logic [DCACHE_NUM_BANKS-1:0][DCACHE_CL_IDX_WIDTH-1:0] bank_idx;
logic [DCACHE_CL_IDX_WIDTH-1:0] bank_idx_d, bank_idx_q;
logic [DCACHE_OFFSET_WIDTH-1:0] bank_off_d, bank_off_q;
@ -109,7 +115,7 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
logic [$clog2(NumPorts)-1:0] vld_sel_d, vld_sel_q;
logic [DCACHE_WBUF_DEPTH-1:0] wbuffer_hit_oh;
logic [(riscv::XLEN/8)-1:0] wbuffer_be;
logic [ (riscv::XLEN/8)-1:0] wbuffer_be;
riscv::xlen_t wbuffer_rdata, rdata;
logic [DCACHE_USER_WIDTH-1:0] wbuffer_ruser, ruser;
logic [riscv::PLEN-1:0] wbuffer_cmp_addr;
@ -118,9 +124,9 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
logic rd_acked;
logic [NumPorts-1:0] bank_collision, rd_req_masked, rd_req_prio;
///////////////////////////////////////////////////////
// arbiter
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// arbiter
///////////////////////////////////////////////////////
// Priority is highest for lowest read port index
//
@ -130,8 +136,8 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
// [way0, w0] [way1, w0] .. [way0, w1] [way1, w1] ..
// byte enable mapping
for (genvar k=0;k<DCACHE_NUM_BANKS;k++) begin : gen_bank
for (genvar j=0;j<DCACHE_SET_ASSOC;j++) begin : gen_bank_way
for (genvar k = 0; k < DCACHE_NUM_BANKS; k++) begin : gen_bank
for (genvar j = 0; j < DCACHE_SET_ASSOC; j++) begin : gen_bank_way
assign bank_be[k][j] = (wr_cl_we_i[j] & wr_cl_vld_i) ? wr_cl_data_be_i[k*(riscv::XLEN/8) +: (riscv::XLEN/8)] :
(wr_req_i[j] & wr_ack_o) ? wr_data_be_i :
'0;
@ -158,19 +164,19 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
logic rd_req;
rr_arb_tree #(
.NumIn (NumPorts),
.DataWidth (1)
.DataWidth(1)
) i_rr_arb_tree (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i('0 ),
.rr_i ('0 ),
.req_i (rd_req_masked ),
.gnt_o (rd_ack_o ),
.data_i ('0 ),
.gnt_i (~wr_cl_vld_i ),
.req_o (rd_req ),
.data_o ( ),
.idx_o (vld_sel_d )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i('0),
.rr_i ('0),
.req_i (rd_req_masked),
.gnt_o (rd_ack_o),
.data_i ('0),
.gnt_i (~wr_cl_vld_i),
.req_o (rd_req),
.data_o (),
.idx_o (vld_sel_d)
);
assign rd_acked = rd_req & ~wr_cl_vld_i;
@ -180,26 +186,27 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
bank_req = '0;
wr_ack_o = '0;
bank_we = '0;
bank_idx = '{default:wr_idx_i};
bank_idx = '{default: wr_idx_i};
for(int k=0; k<NumPorts; k++) begin
for (int k = 0; k < NumPorts; k++) begin
bank_collision[k] = rd_off_i[k][DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES] == wr_off_i[DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES];
end
if(wr_cl_vld_i & |wr_cl_we_i) begin
if (wr_cl_vld_i & |wr_cl_we_i) begin
bank_req = '1;
bank_we = '1;
bank_idx = '{default:wr_cl_idx_i};
bank_idx = '{default: wr_cl_idx_i};
end else begin
if(rd_acked) begin
if(!rd_tag_only_i[vld_sel_d]) begin
bank_req = dcache_cl_bin2oh(rd_off_i[vld_sel_d][DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES]);
if (rd_acked) begin
if (!rd_tag_only_i[vld_sel_d]) begin
bank_req =
dcache_cl_bin2oh(rd_off_i[vld_sel_d][DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES]);
bank_idx[rd_off_i[vld_sel_d][DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES]] = rd_idx_i[vld_sel_d];
end
end
if(|wr_req_i) begin
if(rd_tag_only_i[vld_sel_d] || !(rd_ack_o[vld_sel_d] && bank_collision[vld_sel_d])) begin
if (|wr_req_i) begin
if (rd_tag_only_i[vld_sel_d] || !(rd_ack_o[vld_sel_d] && bank_collision[vld_sel_d])) begin
wr_ack_o = 1'b1;
bank_req |= dcache_cl_bin2oh(wr_off_i[DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES]);
bank_we = dcache_cl_bin2oh(wr_off_i[DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES]);
@ -208,14 +215,14 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
end
end
///////////////////////////////////////////////////////
// tag comparison, hit generatio, readoud muxes
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// tag comparison, hit generatio, readoud muxes
///////////////////////////////////////////////////////
logic [DCACHE_OFFSET_WIDTH-riscv::XLEN_ALIGN_BYTES-1:0] wr_cl_off;
logic [DCACHE_OFFSET_WIDTH-riscv::XLEN_ALIGN_BYTES-1:0] wr_cl_nc_off;
logic [$clog2(DCACHE_WBUF_DEPTH)-1:0] wbuffer_hit_idx;
logic [$clog2(DCACHE_SET_ASSOC)-1:0] rd_hit_idx;
logic [ $clog2(DCACHE_WBUF_DEPTH)-1:0] wbuffer_hit_idx;
logic [ $clog2(DCACHE_SET_ASSOC)-1:0] rd_hit_idx;
assign cmp_en_d = (|vld_req) & ~vld_we;
@ -223,7 +230,7 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
assign wbuffer_cmp_addr = (wr_cl_vld_i) ? {wr_cl_tag_i, wr_cl_idx_i, wr_cl_off_i} :
{rd_tag, bank_idx_q, bank_off_q};
// hit generation
for (genvar i=0;i<DCACHE_SET_ASSOC;i++) begin : gen_tag_cmpsel
for (genvar i = 0; i < DCACHE_SET_ASSOC; i++) begin : gen_tag_cmpsel
// tag comparison of ways >0
assign rd_hit_oh_o[i] = (rd_tag == tag_rdata[i]) & rd_vld_bits_o[i] & cmp_en_q;
// byte offset mux of ways >0
@ -231,24 +238,24 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
assign ruser_cl[i] = bank_ruser[bank_off_q[DCACHE_OFFSET_WIDTH-1:riscv::XLEN_ALIGN_BYTES]][i];
end
for(genvar k=0; k<DCACHE_WBUF_DEPTH; k++) begin : gen_wbuffer_hit
for (genvar k = 0; k < DCACHE_WBUF_DEPTH; k++) begin : gen_wbuffer_hit
assign wbuffer_hit_oh[k] = (|wbuffer_data_i[k].valid) & (wbuffer_data_i[k].wtag == (wbuffer_cmp_addr >> riscv::XLEN_ALIGN_BYTES));
end
lzc #(
.WIDTH ( DCACHE_WBUF_DEPTH )
.WIDTH(DCACHE_WBUF_DEPTH)
) i_lzc_wbuffer_hit (
.in_i ( wbuffer_hit_oh ),
.cnt_o ( wbuffer_hit_idx ),
.empty_o ( )
.in_i (wbuffer_hit_oh),
.cnt_o (wbuffer_hit_idx),
.empty_o()
);
lzc #(
.WIDTH ( DCACHE_SET_ASSOC )
.WIDTH(DCACHE_SET_ASSOC)
) i_lzc_rd_hit (
.in_i ( rd_hit_oh_o ),
.cnt_o ( rd_hit_idx ),
.empty_o ( )
.in_i (rd_hit_oh_o),
.cnt_o (rd_hit_idx),
.empty_o()
);
assign wbuffer_rdata = wbuffer_data_i[wbuffer_hit_idx].data;
@ -266,8 +273,8 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
always_comb begin
if (wr_cl_vld_i) begin
rdata = wr_cl_data_i[wr_cl_off*riscv::XLEN +: riscv::XLEN];
ruser = wr_cl_user_i[wr_cl_off*DCACHE_USER_WIDTH +: DCACHE_USER_WIDTH];
rdata = wr_cl_data_i[wr_cl_off*riscv::XLEN+:riscv::XLEN];
ruser = wr_cl_user_i[wr_cl_off*DCACHE_USER_WIDTH+:DCACHE_USER_WIDTH];
end else begin
rdata = rdata_cl[rd_hit_idx];
ruser = ruser_cl[rd_hit_idx];
@ -275,37 +282,37 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
end
// overlay bytes that hit in the write buffer
for(genvar k=0; k<(riscv::XLEN/8); k++) begin : gen_rd_data
assign rd_data_o[8*k +: 8] = (wbuffer_be[k]) ? wbuffer_rdata[8*k +: 8] : rdata[8*k +: 8];
for (genvar k = 0; k < (riscv::XLEN / 8); k++) begin : gen_rd_data
assign rd_data_o[8*k+:8] = (wbuffer_be[k]) ? wbuffer_rdata[8*k+:8] : rdata[8*k+:8];
end
for(genvar k=0; k<DCACHE_USER_WIDTH/8; k++) begin : gen_rd_user
assign rd_user_o[8*k +: 8] = (wbuffer_be[k]) ? wbuffer_ruser[8*k +: 8] : ruser[8*k +: 8];
for (genvar k = 0; k < DCACHE_USER_WIDTH / 8; k++) begin : gen_rd_user
assign rd_user_o[8*k+:8] = (wbuffer_be[k]) ? wbuffer_ruser[8*k+:8] : ruser[8*k+:8];
end
///////////////////////////////////////////////////////
// memory arrays and regs
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// memory arrays and regs
///////////////////////////////////////////////////////
logic [DCACHE_TAG_WIDTH:0] vld_tag_rdata [DCACHE_SET_ASSOC-1:0];
logic [DCACHE_TAG_WIDTH:0] vld_tag_rdata[DCACHE_SET_ASSOC-1:0];
for (genvar k = 0; k < DCACHE_NUM_BANKS; k++) begin : gen_data_banks
// Data RAM
sram #(
.USER_WIDTH ( ariane_pkg::DCACHE_SET_ASSOC * DATA_USER_WIDTH ),
.DATA_WIDTH ( ariane_pkg::DCACHE_SET_ASSOC * riscv::XLEN ),
.USER_EN ( ariane_pkg::DATA_USER_EN ),
.NUM_WORDS ( wt_cache_pkg::DCACHE_NUM_WORDS )
.USER_WIDTH(ariane_pkg::DCACHE_SET_ASSOC * DATA_USER_WIDTH),
.DATA_WIDTH(ariane_pkg::DCACHE_SET_ASSOC * riscv::XLEN),
.USER_EN (ariane_pkg::DATA_USER_EN),
.NUM_WORDS (wt_cache_pkg::DCACHE_NUM_WORDS)
) i_data_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( bank_req [k] ),
.we_i ( bank_we [k] ),
.addr_i ( bank_idx [k] ),
.wuser_i ( bank_wuser [k] ),
.wdata_i ( bank_wdata [k] ),
.be_i ( bank_be [k] ),
.ruser_o ( bank_ruser [k] ),
.rdata_o ( bank_rdata [k] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (bank_req[k]),
.we_i (bank_we[k]),
.addr_i (bank_idx[k]),
.wuser_i(bank_wuser[k]),
.wdata_i(bank_wdata[k]),
.be_i (bank_be[k]),
.ruser_o(bank_ruser[k]),
.rdata_o(bank_rdata[k])
);
end
@ -317,24 +324,24 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
// Tag RAM
sram #(
// tag + valid bit
.DATA_WIDTH ( ariane_pkg::DCACHE_TAG_WIDTH + 1 ),
.NUM_WORDS ( wt_cache_pkg::DCACHE_NUM_WORDS )
.DATA_WIDTH(ariane_pkg::DCACHE_TAG_WIDTH + 1),
.NUM_WORDS (wt_cache_pkg::DCACHE_NUM_WORDS)
) i_tag_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( vld_req[i] ),
.we_i ( vld_we ),
.addr_i ( vld_addr ),
.wuser_i ( '0 ),
.wdata_i ( {vld_wdata[i], wr_cl_tag_i} ),
.be_i ( '1 ),
.ruser_o ( ),
.rdata_o ( vld_tag_rdata[i] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (vld_req[i]),
.we_i (vld_we),
.addr_i (vld_addr),
.wuser_i('0),
.wdata_i({vld_wdata[i], wr_cl_tag_i}),
.be_i ('1),
.ruser_o(),
.rdata_o(vld_tag_rdata[i])
);
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if(!rst_ni) begin
if (!rst_ni) begin
bank_idx_q <= '0;
bank_off_q <= '0;
vld_sel_q <= '0;
@ -342,43 +349,50 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
end else begin
bank_idx_q <= bank_idx_d;
bank_off_q <= bank_off_d;
vld_sel_q <= vld_sel_d ;
vld_sel_q <= vld_sel_d;
cmp_en_q <= cmp_en_d;
end
end
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
initial begin
cach_line_width_axi: assert (DCACHE_LINE_WIDTH >= CVA6Cfg.AxiDataWidth)
cach_line_width_axi :
assert (DCACHE_LINE_WIDTH >= CVA6Cfg.AxiDataWidth)
else $fatal(1, "[l1 dcache] cache line size needs to be greater or equal AXI data width");
end
initial begin
axi_xlen: assert (CVA6Cfg.AxiDataWidth >= riscv::XLEN)
axi_xlen :
assert (CVA6Cfg.AxiDataWidth >= riscv::XLEN)
else $fatal(1, "[l1 dcache] AXI data width needs to be greater or equal XLEN");
end
initial begin
cach_line_width_xlen: assert (DCACHE_LINE_WIDTH > riscv::XLEN)
cach_line_width_xlen :
assert (DCACHE_LINE_WIDTH > riscv::XLEN)
else $fatal(1, "[l1 dcache] cache_line_size needs to be greater than XLEN");
end
hit_hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) &vld_req |-> !vld_we |=> $onehot0(rd_hit_oh_o))
else $fatal(1,"[l1 dcache] rd_hit_oh_o signal must be hot1");
hit_hot1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) &vld_req |-> !vld_we |=> $onehot0(
rd_hit_oh_o
))
else $fatal(1, "[l1 dcache] rd_hit_oh_o signal must be hot1");
word_write_hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) wr_ack_o |-> $onehot0(wr_req_i))
else $fatal(1,"[l1 dcache] wr_req_i signal must be hot1");
word_write_hot1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) wr_ack_o |-> $onehot0(wr_req_i))
else $fatal(1, "[l1 dcache] wr_req_i signal must be hot1");
wbuffer_hit_hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) &vld_req |-> !vld_we |=> $onehot0(wbuffer_hit_oh))
else $fatal(1,"[l1 dcache] wbuffer_hit_oh signal must be hot1");
wbuffer_hit_hot1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) &vld_req |-> !vld_we |=> $onehot0(
wbuffer_hit_oh
))
else $fatal(1, "[l1 dcache] wbuffer_hit_oh signal must be hot1");
// this is only used for verification!
logic vld_mirror[wt_cache_pkg::DCACHE_NUM_WORDS-1:0][ariane_pkg::DCACHE_SET_ASSOC-1:0];
@ -386,12 +400,12 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] tag_write_duplicate_test;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_mirror
if(!rst_ni) begin
vld_mirror <= '{default:'0};
tag_mirror <= '{default:'0};
if (!rst_ni) begin
vld_mirror <= '{default: '0};
tag_mirror <= '{default: '0};
end else begin
for (int i = 0; i < DCACHE_SET_ASSOC; i++) begin
if(vld_req[i] & vld_we) begin
if (vld_req[i] & vld_we) begin
vld_mirror[vld_addr][i] <= vld_wdata[i];
tag_mirror[vld_addr][i] <= wr_cl_tag_i;
end
@ -403,11 +417,12 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
assign tag_write_duplicate_test[i] = (tag_mirror[vld_addr][i] == wr_cl_tag_i) & vld_mirror[vld_addr][i] & (|vld_wdata);
end
tag_write_duplicate: assert property (
tag_write_duplicate :
assert property (
@(posedge clk_i) disable iff (!rst_ni) |vld_req |-> vld_we |-> !(|tag_write_duplicate_test))
else $fatal(1,"[l1 dcache] cannot allocate a CL that is already present in the cache");
else $fatal(1, "[l1 dcache] cannot allocate a CL that is already present in the cache");
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_dcache_mem

View file

@ -14,7 +14,10 @@
// is that the port with the highest index issues writes instead of reads.
module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_dcache_missunit
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter logic [CACHE_ID_WIDTH-1:0] AmoTxId = 1, // TX id to be used for AMOs
parameter int unsigned NumPorts = 4 // number of miss ports
@ -71,9 +74,8 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
);
// functions
function automatic logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] dcache_way_bin2oh (
input logic [L1D_WAY_WIDTH-1:0] in
);
function automatic logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] dcache_way_bin2oh(
input logic [L1D_WAY_WIDTH-1:0] in);
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] out;
out = '0;
out[in] = 1'b1;
@ -86,10 +88,8 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// 010: word
// 011: dword
// 111: DCACHE line
function automatic logic [riscv::PLEN-1:0] paddrSizeAlign(
input logic [riscv::PLEN-1:0] paddr,
input logic [2:0] size
);
function automatic logic [riscv::PLEN-1:0] paddrSizeAlign(input logic [riscv::PLEN-1:0] paddr,
input logic [2:0] size);
logic [riscv::PLEN-1:0] out;
out = paddr;
unique case (size)
@ -103,16 +103,24 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
endfunction : paddrSizeAlign
// controller FSM
typedef enum logic[2:0] {IDLE, DRAIN, AMO, FLUSH, STORE_WAIT, LOAD_WAIT, AMO_WAIT} state_e;
typedef enum logic [2:0] {
IDLE,
DRAIN,
AMO,
FLUSH,
STORE_WAIT,
LOAD_WAIT,
AMO_WAIT
} state_e;
state_e state_d, state_q;
// MSHR for reads
typedef struct packed {
logic [riscv::PLEN-1:0] paddr ;
logic [2:0] size ;
logic [riscv::PLEN-1:0] paddr;
logic [2:0] size;
logic [DCACHE_SET_ASSOC-1:0] vld_bits;
logic [CACHE_ID_WIDTH-1:0] id ;
logic nc ;
logic [CACHE_ID_WIDTH-1:0] id;
logic nc;
logic [$clog2(DCACHE_SET_ASSOC)-1:0] repl_way;
logic [$clog2(NumPorts)-1:0] miss_port_idx;
} mshr_t;
@ -144,13 +152,13 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
logic [NumPorts-1:0] mshr_rdrd_collision;
logic tx_rdwr_collision, mshr_rdwr_collision;
///////////////////////////////////////////////////////
// input arbitration and general control sigs
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// input arbitration and general control sigs
///////////////////////////////////////////////////////
assign cache_en_o = enable_q;
assign cnt_d = (flush_en) ? cnt_q + 1 : '0;
assign flush_done = (cnt_q == wt_cache_pkg::DCACHE_NUM_WORDS-1);
assign flush_done = (cnt_q == wt_cache_pkg::DCACHE_NUM_WORDS - 1);
assign miss_req_masked_d = (lock_reqs) ? miss_req_masked_q :
(mask_reads) ? miss_we_i & miss_req_i : miss_req_i;
@ -158,11 +166,11 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// read port arbiter
lzc #(
.WIDTH ( NumPorts )
.WIDTH(NumPorts)
) i_lzc_reqs (
.in_i ( miss_req_masked_d ),
.cnt_o ( miss_port_idx ),
.empty_o ( )
.in_i (miss_req_masked_d),
.cnt_o (miss_port_idx),
.empty_o()
);
always_comb begin : p_ack
@ -172,49 +180,47 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
end
end
///////////////////////////////////////////////////////
// MSHR and way replacement logic (only for read ops)
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// MSHR and way replacement logic (only for read ops)
///////////////////////////////////////////////////////
// find invalid cache line
lzc #(
.WIDTH ( ariane_pkg::DCACHE_SET_ASSOC )
.WIDTH(ariane_pkg::DCACHE_SET_ASSOC)
) i_lzc_inv (
.in_i ( ~miss_vld_bits_i[miss_port_idx] ),
.cnt_o ( inv_way ),
.empty_o ( all_ways_valid )
.in_i (~miss_vld_bits_i[miss_port_idx]),
.cnt_o (inv_way),
.empty_o(all_ways_valid)
);
// generate random cacheline index
lfsr #(
.LfsrWidth ( 8 ),
.OutWidth ( $clog2(ariane_pkg::DCACHE_SET_ASSOC))
.LfsrWidth(8),
.OutWidth ($clog2(ariane_pkg::DCACHE_SET_ASSOC))
) i_lfsr_inv (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.en_i ( update_lfsr ),
.out_o ( rnd_way )
.clk_i (clk_i),
.rst_ni(rst_ni),
.en_i (update_lfsr),
.out_o (rnd_way)
);
assign repl_way = (all_ways_valid) ? rnd_way : inv_way;
assign mshr_d.size = (mshr_allocate) ? miss_size_i [miss_port_idx] : mshr_q.size;
assign mshr_d.paddr = (mshr_allocate) ? miss_paddr_i [miss_port_idx] : mshr_q.paddr;
assign mshr_d.size = (mshr_allocate) ? miss_size_i[miss_port_idx] : mshr_q.size;
assign mshr_d.paddr = (mshr_allocate) ? miss_paddr_i[miss_port_idx] : mshr_q.paddr;
assign mshr_d.vld_bits = (mshr_allocate) ? miss_vld_bits_i[miss_port_idx] : mshr_q.vld_bits;
assign mshr_d.id = (mshr_allocate) ? miss_id_i [miss_port_idx] : mshr_q.id;
assign mshr_d.nc = (mshr_allocate) ? miss_nc_i [miss_port_idx] : mshr_q.nc;
assign mshr_d.id = (mshr_allocate) ? miss_id_i[miss_port_idx] : mshr_q.id;
assign mshr_d.nc = (mshr_allocate) ? miss_nc_i[miss_port_idx] : mshr_q.nc;
assign mshr_d.repl_way = (mshr_allocate) ? repl_way : mshr_q.repl_way;
assign mshr_d.miss_port_idx = (mshr_allocate) ? miss_port_idx : mshr_q.miss_port_idx;
// currently we only have one outstanding read TX, hence an incoming load clears the MSHR
assign mshr_vld_d = (mshr_allocate) ? 1'b1 :
(load_ack) ? 1'b0 :
mshr_vld_q;
assign mshr_vld_d = (mshr_allocate) ? 1'b1 : (load_ack) ? 1'b0 : mshr_vld_q;
assign miss_o = (mshr_allocate) ? ~miss_nc_i[miss_port_idx] : 1'b0;
for(genvar k=0; k<NumPorts; k++) begin : gen_rdrd_collision
for (genvar k = 0; k < NumPorts; k++) begin : gen_rdrd_collision
assign mshr_rdrd_collision[k] = (mshr_q.paddr[riscv::PLEN-1:DCACHE_OFFSET_WIDTH] == miss_paddr_i[k][riscv::PLEN-1:DCACHE_OFFSET_WIDTH]) && (mshr_vld_q | mshr_vld_q1);
assign mshr_rdrd_collision_d[k] = (!miss_req_i[k]) ? 1'b0 : mshr_rdrd_collision_q[k] | mshr_rdrd_collision[k];
end
@ -226,25 +232,25 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// read collides with inflight TX
always_comb begin : p_tx_coll
tx_rdwr_collision = 1'b0;
for(int k=0; k<DCACHE_MAX_TX; k++) begin
for (int k = 0; k < DCACHE_MAX_TX; k++) begin
tx_rdwr_collision |= (miss_paddr_i[miss_port_idx][riscv::PLEN-1:DCACHE_OFFSET_WIDTH] == tx_paddr_i[k][riscv::PLEN-1:DCACHE_OFFSET_WIDTH]) && tx_vld_i[k];
end
end
///////////////////////////////////////////////////////
// to memory
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// to memory
///////////////////////////////////////////////////////
// if size = 32bit word, select appropriate offset, replicate for openpiton...
always_comb begin
if (riscv::IS_XLEN64) begin
if (amo_req_i.size==2'b10) begin
amo_data = {amo_req_i.operand_b[0 +: 32], amo_req_i.operand_b[0 +: 32]};
if (amo_req_i.size == 2'b10) begin
amo_data = {amo_req_i.operand_b[0+:32], amo_req_i.operand_b[0+:32]};
end else begin
amo_data = amo_req_i.operand_b;
end
end else begin
amo_data = amo_req_i.operand_b[0 +: 32];
amo_data = amo_req_i.operand_b[0+:32];
end
if (ariane_pkg::DATA_USER_EN) begin
amo_user = amo_data;
@ -256,12 +262,14 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// note: openpiton returns a full cacheline!
if (CVA6Cfg.NOCType == config_pkg::NOC_TYPE_AXI4_ATOP) begin : gen_axi_rtrn_mux
if (CVA6Cfg.AxiDataWidth > 64) begin
assign amo_rtrn_mux = mem_rtrn_i.data[amo_req_i.operand_a[$clog2(CVA6Cfg.AxiDataWidth/8)-1:3]*64 +: 64];
assign amo_rtrn_mux = mem_rtrn_i.data[amo_req_i.operand_a[$clog2(
CVA6Cfg.AxiDataWidth/8
)-1:3]*64+:64];
end else begin
assign amo_rtrn_mux = mem_rtrn_i.data[0 +: 64];
assign amo_rtrn_mux = mem_rtrn_i.data[0+:64];
end
end else begin : gen_piton_rtrn_mux
assign amo_rtrn_mux = mem_rtrn_i.data[amo_req_i.operand_a[DCACHE_OFFSET_WIDTH-1:3]*64 +: 64];
assign amo_rtrn_mux = mem_rtrn_i.data[amo_req_i.operand_a[DCACHE_OFFSET_WIDTH-1:3]*64+:64];
end
// always sign extend 32bit values
@ -278,31 +286,31 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
assign mem_data_o.way = (amo_sel) ? '0 : repl_way;
assign mem_data_o.data = (amo_sel) ? amo_data : miss_wdata_i[miss_port_idx];
assign mem_data_o.user = (amo_sel) ? amo_user : miss_wuser_i[miss_port_idx];
assign mem_data_o.size = (amo_sel) ? amo_req_i.size : miss_size_i [miss_port_idx];
assign mem_data_o.size = (amo_sel) ? amo_req_i.size : miss_size_i[miss_port_idx];
assign mem_data_o.amo_op = (amo_sel) ? amo_req_i.amo_op : AMO_NONE;
assign tmp_paddr = (amo_sel) ? amo_req_i.operand_a[riscv::PLEN-1:0] : miss_paddr_i[miss_port_idx];
assign mem_data_o.paddr = paddrSizeAlign(tmp_paddr, mem_data_o.size);
///////////////////////////////////////////////////////
// back-off mechanism for LR/SC completion guarantee
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// back-off mechanism for LR/SC completion guarantee
///////////////////////////////////////////////////////
logic sc_fail, sc_pass, sc_backoff_over;
exp_backoff #(
.Seed(3),
.Seed (3),
.MaxExp(16)
) i_exp_backoff (
.clk_i,
.rst_ni,
.set_i ( sc_fail ),
.clr_i ( sc_pass ),
.is_zero_o ( sc_backoff_over )
.set_i (sc_fail),
.clr_i (sc_pass),
.is_zero_o(sc_backoff_over)
);
///////////////////////////////////////////////////////
// responses from memory
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// responses from memory
///////////////////////////////////////////////////////
// keep track of pending stores
logic store_sent;
@ -323,7 +331,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
inv_vld_all = 1'b0;
sc_fail = 1'b0;
sc_pass = 1'b0;
miss_rtrn_vld_o ='0;
miss_rtrn_vld_o = '0;
if (mem_rtrn_vld_i) begin
unique case (mem_rtrn_i.rtype)
DCACHE_LOAD_ACK: begin
@ -333,7 +341,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
end
end
DCACHE_STORE_ACK: begin
if (stores_inflight_q>0) begin
if (stores_inflight_q > 0) begin
store_ack = 1'b1;
miss_rtrn_vld_o[NumPorts-1] = 1'b1;
end
@ -344,7 +352,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// need to set SC backoff counter if
// this op failed
if (amo_req_i.amo_op == AMO_SC) begin
if (amo_resp_o.result>0) begin
if (amo_resp_o.result > 0) begin
sc_fail = 1'b1;
end else begin
sc_pass = 1'b1;
@ -359,7 +367,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// TODO:
// DCACHE_INT_REQ: begin
// end
default : begin
default: begin
end
endcase
end
@ -368,24 +376,23 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// to write buffer
assign miss_rtrn_id_o = mem_rtrn_i.tid;
///////////////////////////////////////////////////////
// writes to cache memory
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// writes to cache memory
///////////////////////////////////////////////////////
// cacheline write port
assign wr_cl_nc_o = mshr_q.nc;
assign wr_cl_vld_o = load_ack | (| wr_cl_we_o);
assign wr_cl_vld_o = load_ack | (|wr_cl_we_o);
assign wr_cl_we_o = (flush_en ) ? '1 :
(inv_vld_all) ? '1 :
(inv_vld ) ? dcache_way_bin2oh(mem_rtrn_i.inv.way) :
(cl_write_en) ? dcache_way_bin2oh(mshr_q.repl_way) :
'0;
assign wr_cl_we_o = (flush_en) ? '1 : (inv_vld_all) ? '1 : (inv_vld) ? dcache_way_bin2oh(
mem_rtrn_i.inv.way
) : (cl_write_en) ? dcache_way_bin2oh(
mshr_q.repl_way
) : '0;
assign wr_vld_bits_o = (flush_en ) ? '0 :
(inv_vld ) ? '0 :
(cl_write_en) ? dcache_way_bin2oh(mshr_q.repl_way) :
'0;
assign wr_vld_bits_o = (flush_en) ? '0 : (inv_vld) ? '0 : (cl_write_en) ? dcache_way_bin2oh(
mshr_q.repl_way
) : '0;
assign wr_cl_idx_o = (flush_en) ? cnt_q :
(inv_vld) ? mem_rtrn_i.inv.idx[DCACHE_INDEX_WIDTH-1:DCACHE_OFFSET_WIDTH] :
@ -400,9 +407,9 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
// only non-NC responses write to the cache
assign cl_write_en = load_ack & ~mshr_q.nc;
///////////////////////////////////////////////////////
// main control logic for generating tx
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// main control logic for generating tx
///////////////////////////////////////////////////////
always_comb begin : p_fsm
// default assignment
@ -465,7 +472,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
end else if (!tx_rdwr_collision) begin
mem_data_req_o = 1'b1;
mem_data_o.rtype = DCACHE_LOAD_REQ;
update_lfsr = all_ways_valid & mem_data_ack_i;// need to evict a random way
update_lfsr = all_ways_valid & mem_data_ack_i; // need to evict a random way
mshr_allocate = mem_data_ack_i;
if (!mem_data_ack_i) begin
state_d = LOAD_WAIT;
@ -491,7 +498,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
mem_data_req_o = 1'b1;
mem_data_o.rtype = DCACHE_LOAD_REQ;
if (mem_data_ack_i) begin
update_lfsr = all_ways_valid;// need to evict a random way
update_lfsr = all_ways_valid; // need to evict a random way
mshr_allocate = 1'b1;
state_d = IDLE;
end
@ -553,11 +560,11 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
endcase // state_q
end
///////////////////////////////////////////////////////
// ff's
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ff's
///////////////////////////////////////////////////////
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
state_q <= FLUSH;
cnt_q <= '0;
@ -583,33 +590,39 @@ always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
amo_req_q <= amo_req_d;
stores_inflight_q <= stores_inflight_d;
end
end
end
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
read_tid : assert property (
read_tid :
assert property (
@(posedge clk_i) disable iff (!rst_ni) mshr_vld_q |-> mem_rtrn_vld_i |-> load_ack |-> mem_rtrn_i.tid == mshr_q.id)
else $fatal(1,"[l1 dcache missunit] TID of load response doesn't match");
else $fatal(1, "[l1 dcache missunit] TID of load response doesn't match");
read_ports : assert property (
read_ports :
assert property (
@(posedge clk_i) disable iff (!rst_ni) |miss_req_i[NumPorts-2:0] |-> miss_we_i[NumPorts-2:0] == 0)
else $fatal(1,"[l1 dcache missunit] only last port can issue write requests");
else $fatal(1, "[l1 dcache missunit] only last port can issue write requests");
write_port : assert property (
write_port :
assert property (
@(posedge clk_i) disable iff (!rst_ni) miss_req_i[NumPorts-1] |-> miss_we_i[NumPorts-1])
else $fatal(1,"[l1 dcache missunit] last port can only issue write requests");
else $fatal(1, "[l1 dcache missunit] last port can only issue write requests");
initial begin
// assert wrong parameterizations
assert (NumPorts>=2)
else $fatal(1,"[l1 dcache missunit] at least two ports are required (one read port, one write port)");
assert (NumPorts >= 2)
else
$fatal(
1, "[l1 dcache missunit] at least two ports are required (one read port, one write port)"
);
end
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_dcache_missunit

View file

@ -49,7 +49,10 @@
// word has been evicted from the write buffer.
module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_dcache_wbuffer
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -111,7 +114,8 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
//logic [DCACHE_WBUF_DEPTH-1:0][7:0] bdirty;
logic [DCACHE_WBUF_DEPTH-1:0][(riscv::XLEN/8)-1:0] bdirty;
logic [$clog2(DCACHE_WBUF_DEPTH)-1:0] next_ptr, dirty_ptr, hit_ptr, wr_ptr, check_ptr_d, check_ptr_q, check_ptr_q1, rtrn_ptr;
logic [$clog2(DCACHE_WBUF_DEPTH)-1:0]
next_ptr, dirty_ptr, hit_ptr, wr_ptr, check_ptr_d, check_ptr_q, check_ptr_q1, rtrn_ptr;
logic [CACHE_ID_WIDTH-1:0] tx_id, rtrn_id;
logic [riscv::XLEN_ALIGN_BYTES-1:0] bdirty_off;
@ -129,49 +133,61 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
logic wr_cl_vld_q, wr_cl_vld_d;
logic [DCACHE_CL_IDX_WIDTH-1:0] wr_cl_idx_q, wr_cl_idx_d;
logic [riscv::PLEN-1:0] debug_paddr [DCACHE_WBUF_DEPTH-1:0];
logic [riscv::PLEN-1:0] debug_paddr[DCACHE_WBUF_DEPTH-1:0];
wbuffer_t wbuffer_check_mux, wbuffer_dirty_mux;
///////////////////////////////////////////////////////
// misc
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// misc
///////////////////////////////////////////////////////
logic [ariane_pkg::DCACHE_TAG_WIDTH-1:0] miss_tag;
logic is_nc_miss;
logic is_ni;
assign miss_tag = miss_paddr_o[ariane_pkg::DCACHE_INDEX_WIDTH+:ariane_pkg::DCACHE_TAG_WIDTH];
assign is_nc_miss = !config_pkg::is_inside_cacheable_regions(CVA6Cfg, {{64-DCACHE_TAG_WIDTH-DCACHE_INDEX_WIDTH{1'b0}}, miss_tag, {DCACHE_INDEX_WIDTH{1'b0}}});
assign is_nc_miss = !config_pkg::is_inside_cacheable_regions(
CVA6Cfg,
{
{64 - DCACHE_TAG_WIDTH - DCACHE_INDEX_WIDTH{1'b0}}, miss_tag, {DCACHE_INDEX_WIDTH{1'b0}}
}
);
assign miss_nc_o = !cache_en_i || is_nc_miss;
// Non-idempotent if request goes to NI region
assign is_ni = config_pkg::is_inside_nonidempotent_regions(CVA6Cfg, {{64-DCACHE_TAG_WIDTH-DCACHE_INDEX_WIDTH{1'b0}}, req_port_i.address_tag, {DCACHE_INDEX_WIDTH{1'b0}}});
assign is_ni = config_pkg::is_inside_nonidempotent_regions(
CVA6Cfg,
{
{64 - DCACHE_TAG_WIDTH - DCACHE_INDEX_WIDTH{1'b0}},
req_port_i.address_tag,
{DCACHE_INDEX_WIDTH{1'b0}}
}
);
assign miss_we_o = 1'b1;
assign miss_vld_bits_o = '0;
assign wbuffer_data_o = wbuffer_q;
for (genvar k=0; k<DCACHE_MAX_TX;k++) begin : gen_tx_vld
for (genvar k = 0; k < DCACHE_MAX_TX; k++) begin : gen_tx_vld
assign tx_vld_o[k] = tx_stat_q[k].vld;
assign tx_paddr_o[k] = wbuffer_q[tx_stat_q[k].ptr].wtag<<riscv::XLEN_ALIGN_BYTES;
assign tx_paddr_o[k] = wbuffer_q[tx_stat_q[k].ptr].wtag << riscv::XLEN_ALIGN_BYTES;
end
///////////////////////////////////////////////////////
// openpiton does not understand byte enable sigs
// need to convert to the four cases:
// 00: byte
// 01: halfword
// 10: word
// 11: dword
// non-contiguous writes need to be serialized!
// e.g. merged dwords with BE like this: 8'b01001100
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// openpiton does not understand byte enable sigs
// need to convert to the four cases:
// 00: byte
// 01: halfword
// 10: word
// 11: dword
// non-contiguous writes need to be serialized!
// e.g. merged dwords with BE like this: 8'b01001100
///////////////////////////////////////////////////////
// get byte offset
lzc #(
.WIDTH ( riscv::XLEN/8 )
.WIDTH(riscv::XLEN / 8)
) i_vld_bdirty (
.in_i ( bdirty[dirty_ptr] ),
.cnt_o ( bdirty_off ),
.empty_o ( )
.in_i (bdirty[dirty_ptr]),
.cnt_o (bdirty_off),
.empty_o()
);
// add the offset to the physical base address of this buffer entry
@ -186,43 +202,51 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
// we have to split unaligned data into multiple transfers (see toSize64)
// e.g. if we have the following valid bytes: 0011_1001 -> TX0: 0000_0001, TX1: 0000_1000, TX2: 0011_0000
assign miss_size_o = riscv::IS_XLEN64 ? toSize64(bdirty[dirty_ptr]):
toSize32(bdirty[dirty_ptr]);
assign miss_size_o = riscv::IS_XLEN64 ? toSize64(bdirty[dirty_ptr]) : toSize32(bdirty[dirty_ptr]);
// replicate transfers shorter than a dword
assign miss_wdata_o = riscv::IS_XLEN64 ? repData64(wbuffer_dirty_mux.data, bdirty_off, miss_size_o[1:0]):
repData32(wbuffer_dirty_mux.data, bdirty_off, miss_size_o[1:0]);
assign miss_wdata_o = riscv::IS_XLEN64 ? repData64(
wbuffer_dirty_mux.data, bdirty_off, miss_size_o[1:0]
) : repData32(
wbuffer_dirty_mux.data, bdirty_off, miss_size_o[1:0]
);
if (ariane_pkg::DATA_USER_EN) begin
assign miss_wuser_o = riscv::IS_XLEN64 ? repData64(wbuffer_dirty_mux.user, bdirty_off, miss_size_o[1:0]):
repData32(wbuffer_dirty_mux.user, bdirty_off, miss_size_o[1:0]);
assign miss_wuser_o = riscv::IS_XLEN64 ? repData64(
wbuffer_dirty_mux.user, bdirty_off, miss_size_o[1:0]
) : repData32(
wbuffer_dirty_mux.user, bdirty_off, miss_size_o[1:0]
);
end else begin
assign miss_wuser_o = '0;
end
assign tx_be = riscv::IS_XLEN64 ? to_byte_enable8(bdirty_off, miss_size_o[1:0]):
to_byte_enable4(bdirty_off, miss_size_o[1:0]);
assign tx_be = riscv::IS_XLEN64 ? to_byte_enable8(
bdirty_off, miss_size_o[1:0]
) : to_byte_enable4(
bdirty_off, miss_size_o[1:0]
);
///////////////////////////////////////////////////////
// TX status registers and ID counters
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// TX status registers and ID counters
///////////////////////////////////////////////////////
// TODO: todo: make this fall through if timing permits it
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DATA_WIDTH ( $clog2(DCACHE_MAX_TX) ),
.DEPTH ( DCACHE_MAX_TX )
.FALL_THROUGH(1'b0),
.DATA_WIDTH ($clog2(DCACHE_MAX_TX)),
.DEPTH (DCACHE_MAX_TX)
) i_rtrn_id_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( ),
.empty_o ( rtrn_empty ),
.usage_o ( ),
.data_i ( miss_rtrn_id_i ),
.push_i ( miss_rtrn_vld_i ),
.data_o ( rtrn_id ),
.pop_i ( evict )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (),
.empty_o (rtrn_empty),
.usage_o (),
.data_i (miss_rtrn_id_i),
.push_i (miss_rtrn_vld_i),
.data_o (rtrn_id),
.pop_i (evict)
);
always_comb begin : p_tx_stat
@ -260,33 +284,33 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
rr_arb_tree #(
.NumIn (DCACHE_MAX_TX),
.LockIn (1'b1),
.DataWidth (1)
.DataWidth(1)
) i_tx_id_rr (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i('0 ),
.rr_i ('0 ),
.req_i (~tx_vld_o ),
.gnt_o ( ),
.data_i ('0 ),
.gnt_i (dirty_rd_en ),
.req_o ( ),
.data_o ( ),
.idx_o (tx_id )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i('0),
.rr_i ('0),
.req_i (~tx_vld_o),
.gnt_o (),
.data_i ('0),
.gnt_i (dirty_rd_en),
.req_o (),
.data_o (),
.idx_o (tx_id)
);
///////////////////////////////////////////////////////
// cache readout & update
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// cache readout & update
///////////////////////////////////////////////////////
assign extract_tag = rd_paddr>>DCACHE_INDEX_WIDTH;
assign extract_tag = rd_paddr >> DCACHE_INDEX_WIDTH;
assign rd_tag_d = extract_tag[DCACHE_TAG_WIDTH-1:0];
// trigger TAG readout in cache
assign rd_tag_only_o = 1'b1;
assign rd_paddr = wbuffer_check_mux.wtag<<riscv::XLEN_ALIGN_BYTES;
assign rd_paddr = wbuffer_check_mux.wtag << riscv::XLEN_ALIGN_BYTES;
assign rd_req_o = |tocheck;
assign rd_tag_o = rd_tag_q;//delay by one cycle
assign rd_tag_o = rd_tag_q; //delay by one cycle
assign rd_idx_o = rd_paddr[DCACHE_INDEX_WIDTH-1:DCACHE_OFFSET_WIDTH];
assign rd_off_o = rd_paddr[DCACHE_OFFSET_WIDTH-1:0];
assign check_en_d = rd_req_o & rd_ack_i;
@ -296,23 +320,23 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
// if we wrote into a word while it was in-flight, we cannot write the dirty bytes to the cache
// when the TX returns
assign wr_data_be_o = tx_stat_q[rtrn_id].be & (~wbuffer_q[rtrn_ptr].dirty);
assign wr_paddr = wbuffer_q[rtrn_ptr].wtag<<riscv::XLEN_ALIGN_BYTES;
assign wr_paddr = wbuffer_q[rtrn_ptr].wtag << riscv::XLEN_ALIGN_BYTES;
assign wr_idx_o = wr_paddr[DCACHE_INDEX_WIDTH-1:DCACHE_OFFSET_WIDTH];
assign wr_off_o = wr_paddr[DCACHE_OFFSET_WIDTH-1:0];
assign wr_data_o = wbuffer_q[rtrn_ptr].data;
assign wr_user_o = wbuffer_q[rtrn_ptr].user;
///////////////////////////////////////////////////////
// readout of status bits, index calculation
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// readout of status bits, index calculation
///////////////////////////////////////////////////////
logic [DCACHE_WBUF_DEPTH-1:0][DCACHE_CL_IDX_WIDTH-1:0] wtag_comp;
assign wr_cl_vld_d = wr_cl_vld_i;
assign wr_cl_idx_d = wr_cl_idx_i;
for (genvar k=0; k<DCACHE_WBUF_DEPTH; k++) begin : gen_flags
for (genvar k = 0; k < DCACHE_WBUF_DEPTH; k++) begin : gen_flags
// only for debug, will be pruned
assign debug_paddr[k] = wbuffer_q[k].wtag << riscv::XLEN_ALIGN_BYTES;
@ -342,62 +366,62 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
// next free entry in the buffer
lzc #(
.WIDTH ( DCACHE_WBUF_DEPTH )
.WIDTH(DCACHE_WBUF_DEPTH)
) i_vld_lzc (
.in_i ( ~valid ),
.cnt_o ( next_ptr ),
.empty_o ( full )
.in_i (~valid),
.cnt_o (next_ptr),
.empty_o(full)
);
// get index of hit
lzc #(
.WIDTH ( DCACHE_WBUF_DEPTH )
.WIDTH(DCACHE_WBUF_DEPTH)
) i_hit_lzc (
.in_i ( wbuffer_hit_oh ),
.cnt_o ( hit_ptr ),
.empty_o ( )
.in_i (wbuffer_hit_oh),
.cnt_o (hit_ptr),
.empty_o()
);
// next dirty word to serve
rr_arb_tree #(
.NumIn ( DCACHE_WBUF_DEPTH ),
.LockIn ( 1'b1 ),
.DataType ( wbuffer_t )
.NumIn (DCACHE_WBUF_DEPTH),
.LockIn (1'b1),
.DataType(wbuffer_t)
) i_dirty_rr (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( '0 ),
.rr_i ( '0 ),
.req_i ( dirty ),
.gnt_o ( ),
.data_i ( wbuffer_q ),
.gnt_i ( dirty_rd_en ),
.req_o ( ),
.data_o ( wbuffer_dirty_mux ),
.idx_o ( dirty_ptr )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i('0),
.rr_i ('0),
.req_i (dirty),
.gnt_o (),
.data_i (wbuffer_q),
.gnt_i (dirty_rd_en),
.req_o (),
.data_o (wbuffer_dirty_mux),
.idx_o (dirty_ptr)
);
// next word to lookup in the cache
rr_arb_tree #(
.NumIn ( DCACHE_WBUF_DEPTH ),
.DataType ( wbuffer_t )
.NumIn (DCACHE_WBUF_DEPTH),
.DataType(wbuffer_t)
) i_clean_rr (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( '0 ),
.rr_i ( '0 ),
.req_i ( tocheck ),
.gnt_o ( ),
.data_i ( wbuffer_q ),
.gnt_i ( check_en_d ),
.req_o ( ),
.data_o ( wbuffer_check_mux ),
.idx_o ( check_ptr_d )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i('0),
.rr_i ('0),
.req_i (tocheck),
.gnt_o (),
.data_i (wbuffer_q),
.gnt_i (check_en_d),
.req_o (),
.data_o (wbuffer_check_mux),
.idx_o (check_ptr_d)
);
///////////////////////////////////////////////////////
// update logic
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// update logic
///////////////////////////////////////////////////////
assign req_port_o.data_rvalid = '0;
assign req_port_o.data_rdata = '0;
@ -406,7 +430,7 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
assign rd_hit_oh_d = rd_hit_oh_i;
logic ni_inside,ni_conflict;
logic ni_inside, ni_conflict;
assign ni_inside = |ni_pending_q;
assign ni_conflict = is_ni && ni_inside;
assign not_ni_o = !ni_inside;
@ -430,7 +454,7 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
// if an invalidation or cache line refill comes in and hits on the write buffer,
// we have to discard our knowledge of the corresponding cacheline state
for (int k=0; k<DCACHE_WBUF_DEPTH; k++) begin
for (int k = 0; k < DCACHE_WBUF_DEPTH; k++) begin
if (inval_hit[k]) begin
wbuffer_d[k].checked = 1'b0;
end
@ -439,7 +463,7 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
// once TX write response came back, we can clear the TX block. if it was not dirty, we
// can completely evict it - otherwise we have to leave it there for retransmission
if (evict) begin
for (int k=0; k<(riscv::XLEN/8); k++) begin
for (int k = 0; k < (riscv::XLEN / 8); k++) begin
if (tx_stat_q[rtrn_id].be[k]) begin
wbuffer_d[rtrn_ptr].txblock[k] = 1'b0;
if (!wbuffer_q[rtrn_ptr].dirty[k]) begin
@ -461,7 +485,7 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
// mark bytes sent out to the memory system
if (miss_req_o && miss_ack_i) begin
dirty_rd_en = 1'b1;
for (int k=0; k<(riscv::XLEN/8); k++) begin
for (int k = 0; k < (riscv::XLEN / 8); k++) begin
if (tx_be[k]) begin
wbuffer_d[dirty_ptr].dirty[k] = 1'b0;
wbuffer_d[dirty_ptr].txblock[k] = 1'b1;
@ -480,18 +504,21 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
ni_pending_d[wr_ptr] = is_ni;
wbuffer_d[wr_ptr].checked = 1'b0;
wbuffer_d[wr_ptr].wtag = {req_port_i.address_tag, req_port_i.address_index[DCACHE_INDEX_WIDTH-1:riscv::XLEN_ALIGN_BYTES]};
wbuffer_d[wr_ptr].wtag = {
req_port_i.address_tag,
req_port_i.address_index[DCACHE_INDEX_WIDTH-1:riscv::XLEN_ALIGN_BYTES]
};
// mark bytes as dirty
for (int k=0; k<(riscv::XLEN/8); k++) begin
for (int k = 0; k < (riscv::XLEN / 8); k++) begin
if (req_port_i.data_be[k]) begin
wbuffer_d[wr_ptr].valid[k] = 1'b1;
wbuffer_d[wr_ptr].dirty[k] = 1'b1;
wbuffer_d[wr_ptr].data[k*8 +: 8] = req_port_i.data_wdata[k*8 +: 8];
wbuffer_d[wr_ptr].data[k*8+:8] = req_port_i.data_wdata[k*8+:8];
if (ariane_pkg::DATA_USER_EN) begin
wbuffer_d[wr_ptr].user[k*8 +: 8] = req_port_i.data_wuser[k*8 +: 8];
wbuffer_d[wr_ptr].user[k*8+:8] = req_port_i.data_wuser[k*8+:8];
end else begin
wbuffer_d[wr_ptr].user[k*8 +: 8] = '0;
wbuffer_d[wr_ptr].user[k*8+:8] = '0;
end
end
end
@ -500,9 +527,9 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
end
///////////////////////////////////////////////////////
// ff's
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ff's
///////////////////////////////////////////////////////
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
@ -533,53 +560,63 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
end
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) req_port_i.data_req |-> $onehot0(wbuffer_hit_oh))
else $fatal(1,"[l1 dcache wbuffer] wbuffer_hit_oh signal must be hot1");
hot1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) req_port_i.data_req |-> $onehot0(
wbuffer_hit_oh
))
else $fatal(1, "[l1 dcache wbuffer] wbuffer_hit_oh signal must be hot1");
tx_status: assert property (
tx_status :
assert property (
@(posedge clk_i) disable iff (!rst_ni) evict && miss_ack_i && miss_req_o |-> (tx_id != rtrn_id))
else $fatal(1,"[l1 dcache wbuffer] cannot allocate and clear same tx slot id in the same cycle");
else $fatal(1, "[l1 dcache wbuffer] cannot allocate and clear same tx slot id in the same cycle");
tx_valid0: assert property (
@(posedge clk_i) disable iff (!rst_ni) evict |-> tx_stat_q[rtrn_id].vld)
else $fatal(1,"[l1 dcache wbuffer] evicting invalid transaction slot");
tx_valid0 :
assert property (@(posedge clk_i) disable iff (!rst_ni) evict |-> tx_stat_q[rtrn_id].vld)
else $fatal(1, "[l1 dcache wbuffer] evicting invalid transaction slot");
tx_valid1: assert property (
@(posedge clk_i) disable iff (!rst_ni) evict |-> |wbuffer_q[rtrn_ptr].valid)
else $fatal(1,"[l1 dcache wbuffer] wbuffer entry corresponding to this transaction is invalid");
tx_valid1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) evict |-> |wbuffer_q[rtrn_ptr].valid)
else $fatal(1, "[l1 dcache wbuffer] wbuffer entry corresponding to this transaction is invalid");
write_full: assert property (
write_full :
assert property (
@(posedge clk_i) disable iff (!rst_ni) req_port_i.data_req |-> req_port_o.data_gnt |-> ((!full) || (|wbuffer_hit_oh)))
else $fatal(1,"[l1 dcache wbuffer] cannot write if full or no hit");
else $fatal(1, "[l1 dcache wbuffer] cannot write if full or no hit");
unused0: assert property (
@(posedge clk_i) disable iff (!rst_ni) !req_port_i.tag_valid)
else $fatal(1,"[l1 dcache wbuffer] req_port_i.tag_valid should not be asserted");
unused0 :
assert property (@(posedge clk_i) disable iff (!rst_ni) !req_port_i.tag_valid)
else $fatal(1, "[l1 dcache wbuffer] req_port_i.tag_valid should not be asserted");
unused1: assert property (
@(posedge clk_i) disable iff (!rst_ni) !req_port_i.kill_req)
else $fatal(1,"[l1 dcache wbuffer] req_port_i.kill_req should not be asserted");
unused1 :
assert property (@(posedge clk_i) disable iff (!rst_ni) !req_port_i.kill_req)
else $fatal(1, "[l1 dcache wbuffer] req_port_i.kill_req should not be asserted");
for (genvar k=0; k<DCACHE_WBUF_DEPTH; k++) begin : gen_assert1
for (genvar j=0; j<(riscv::XLEN/8); j++) begin : gen_assert2
byteStates: assert property (
for (genvar k = 0; k < DCACHE_WBUF_DEPTH; k++) begin : gen_assert1
for (genvar j = 0; j < (riscv::XLEN / 8); j++) begin : gen_assert2
byteStates :
assert property (
@(posedge clk_i) disable iff (!rst_ni) {wbuffer_q[k].valid[j], wbuffer_q[k].dirty[j], wbuffer_q[k].txblock[j]} inside {3'b000, 3'b110, 3'b101, 3'b111} )
else $fatal(1,"[l1 dcache wbuffer] byte %02d of wbuffer entry %02d has invalid state: valid=%01b, dirty=%01b, txblock=%01b",
j,k,
else
$fatal(
1,
"[l1 dcache wbuffer] byte %02d of wbuffer entry %02d has invalid state: valid=%01b, dirty=%01b, txblock=%01b",
j,
k,
wbuffer_q[k].valid[j],
wbuffer_q[k].dirty[j],
wbuffer_q[k].txblock[j]);
wbuffer_q[k].txblock[j]
);
end
end
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_dcache_wbuffer

View file

@ -49,7 +49,10 @@
//
module wt_l15_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
module wt_l15_adapter
import ariane_pkg::*;
import wt_cache_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -76,24 +79,24 @@ module wt_l15_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
input l15_rtrn_t l15_rtrn_i
);
// request path
icache_req_t icache_data;
logic icache_data_full, icache_data_empty;
// request path
icache_req_t icache_data;
logic icache_data_full, icache_data_empty;
dcache_req_t dcache_data;
logic dcache_data_full, dcache_data_empty;
dcache_req_t dcache_data;
logic dcache_data_full, dcache_data_empty;
logic [1:0] arb_req, arb_ack;
logic arb_idx;
logic [1:0] arb_req, arb_ack;
logic arb_idx;
// return path
logic rtrn_fifo_empty, rtrn_fifo_full, rtrn_fifo_pop;
l15_rtrn_t rtrn_fifo_data;
// return path
logic rtrn_fifo_empty, rtrn_fifo_full, rtrn_fifo_pop;
l15_rtrn_t rtrn_fifo_data;
///////////////////////////////////////////////////////
// request path to L15
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// request path to L15
///////////////////////////////////////////////////////
// relevant l15 signals
// l15_req_t l15_req_o.l15_rqtype; // see below for encoding
@ -114,8 +117,7 @@ l15_rtrn_t rtrn_fifo_data;
// data mux
assign l15_req_o.l15_nc = (arb_idx) ? dcache_data.nc : icache_data.nc;
// icache fills are either cachelines or 4byte fills, depending on whether they go to the Piton I/O space or not.
assign l15_req_o.l15_size = (arb_idx) ? dcache_data.size :
(icache_data.nc) ? 3'b010 : 3'b111;
assign l15_req_o.l15_size = (arb_idx) ? dcache_data.size : (icache_data.nc) ? 3'b010 : 3'b111;
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
@ -123,8 +125,7 @@ l15_rtrn_t rtrn_fifo_data;
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_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)
@ -132,34 +133,36 @@ l15_rtrn_t rtrn_fifo_data;
// openpiton is big endian
if (CVA6Cfg.NOCType == config_pkg::NOC_TYPE_L15_BIG_ENDIAN) assign l15_req_o.l15_data = swendian64(dcache_data.data);
else if (CVA6Cfg.NOCType == config_pkg::NOC_TYPE_L15_LITTLE_ENDIAN) assign l15_req_o.l15_data = dcache_data.data;
else $fatal(1,"[wt_l15_adapter] Unsupported NOC type");
if (CVA6Cfg.NOCType == config_pkg::NOC_TYPE_L15_BIG_ENDIAN)
assign l15_req_o.l15_data = swendian64(dcache_data.data);
else if (CVA6Cfg.NOCType == config_pkg::NOC_TYPE_L15_LITTLE_ENDIAN)
assign l15_req_o.l15_data = dcache_data.data;
else $fatal(1, "[wt_l15_adapter] Unsupported NOC type");
// 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 )
.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)
);
assign arb_req = {~dcache_data_empty, ~icache_data_empty};
assign l15_req_o.l15_val = (|arb_req);// & ~header_ack_q;
assign l15_req_o.l15_val = (|arb_req); // & ~header_ack_q;
// encode packet type
always_comb begin : p_req
l15_req_o.l15_rqtype = L15_LOAD_RQ;
unique case (arb_idx)
0: begin// icache
0: begin // icache
l15_req_o.l15_rqtype = L15_IMISS_RQ;
end
1: begin
@ -188,44 +191,44 @@ l15_rtrn_t rtrn_fifo_data;
end // p_req
fifo_v2 #(
.dtype ( icache_req_t ),
.DEPTH ( ADAPTER_REQ_FIFO_DEPTH )
.dtype(icache_req_t),
.DEPTH(ADAPTER_REQ_FIFO_DEPTH)
) i_icache_data_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( icache_data_full ),
.empty_o ( icache_data_empty ),
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i ( icache_data_i ),
.push_i ( icache_data_ack_o ),
.data_o ( icache_data ),
.pop_i ( arb_ack[0] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i (1'b0),
.full_o (icache_data_full),
.empty_o (icache_data_empty),
.alm_full_o (),
.alm_empty_o(),
.data_i (icache_data_i),
.push_i (icache_data_ack_o),
.data_o (icache_data),
.pop_i (arb_ack[0])
);
fifo_v2 #(
.dtype ( dcache_req_t ),
.DEPTH ( ADAPTER_REQ_FIFO_DEPTH )
.dtype(dcache_req_t),
.DEPTH(ADAPTER_REQ_FIFO_DEPTH)
) i_dcache_data_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( dcache_data_full ),
.empty_o ( dcache_data_empty ),
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i ( dcache_data_i ),
.push_i ( dcache_data_ack_o ),
.data_o ( dcache_data ),
.pop_i ( arb_ack[1] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i (1'b0),
.full_o (dcache_data_full),
.empty_o (dcache_data_empty),
.alm_full_o (),
.alm_empty_o(),
.data_i (dcache_data_i),
.push_i (dcache_data_ack_o),
.data_o (dcache_data),
.pop_i (arb_ack[1])
);
///////////////////////////////////////////////////////
// return path from L15
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// return path from L15
///////////////////////////////////////////////////////
// relevant l15 signals
// l15_rtrn_i.l15_returntype; // see below for encoding
@ -253,7 +256,7 @@ l15_rtrn_t rtrn_fifo_data;
dcache_rtrn_o.rtype = DCACHE_LOAD_ACK;
icache_rtrn_vld_o = 1'b0;
dcache_rtrn_vld_o = 1'b0;
if(!rtrn_fifo_empty) begin
if (!rtrn_fifo_empty) begin
unique case (rtrn_fifo_data.l15_returntype)
L15_LOAD_RET: begin
dcache_rtrn_o.rtype = DCACHE_LOAD_ACK;
@ -290,21 +293,25 @@ l15_rtrn_t rtrn_fifo_data;
// openpiton is big endian
if (SwapEndianess) begin : gen_swap
assign dcache_rtrn_o.data = { swendian64(rtrn_fifo_data.l15_data_1),
swendian64(rtrn_fifo_data.l15_data_0) };
assign dcache_rtrn_o.data = {
swendian64(rtrn_fifo_data.l15_data_1), swendian64(rtrn_fifo_data.l15_data_0)
};
assign icache_rtrn_o.data = { swendian64(rtrn_fifo_data.l15_data_3),
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) };
swendian64(rtrn_fifo_data.l15_data_0)
};
end else begin : gen_no_swap
assign dcache_rtrn_o.data = { rtrn_fifo_data.l15_data_1,
rtrn_fifo_data.l15_data_0 };
assign dcache_rtrn_o.data = {rtrn_fifo_data.l15_data_1, rtrn_fifo_data.l15_data_0};
assign icache_rtrn_o.data = { rtrn_fifo_data.l15_data_3,
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 };
rtrn_fifo_data.l15_data_0
};
end
// fifo signals
@ -323,66 +330,81 @@ l15_rtrn_t rtrn_fifo_data;
assign dcache_rtrn_o.inv.all = rtrn_fifo_data.l15_inval_dcache_all_way;
fifo_v2 #(
.dtype ( l15_rtrn_t ),
.DEPTH ( ADAPTER_RTRN_FIFO_DEPTH )
.dtype(l15_rtrn_t),
.DEPTH(ADAPTER_RTRN_FIFO_DEPTH)
) i_rtrn_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( rtrn_fifo_full ),
.empty_o ( rtrn_fifo_empty ),
.alm_full_o ( ),
.alm_empty_o ( ),
.data_i ( l15_rtrn_i ),
.push_i ( l15_req_o.l15_req_ack ),
.data_o ( rtrn_fifo_data ),
.pop_i ( rtrn_fifo_pop )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i (1'b0),
.full_o (rtrn_fifo_full),
.empty_o (rtrn_fifo_empty),
.alm_full_o (),
.alm_empty_o(),
.data_i (l15_rtrn_i),
.push_i (l15_req_o.l15_req_ack),
.data_o (rtrn_fifo_data),
.pop_i (rtrn_fifo_pop)
);
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
invalidations: assert property (
invalidations :
assert property (
@(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");
else $fatal(1, "[l15_adapter] got invalidation package with zero invalidation flags");
blockstore_o: assert property (
blockstore_o :
assert property (
@(posedge clk_i) disable iff (!rst_ni) l15_req_o.l15_val |-> l15_req_o.l15_rqtype == L15_STORE_RQ |-> !(l15_req_o.l15_blockstore || l15_req_o.l15_blockinitstore))
else $fatal(1,"[l15_adapter] blockstores are not supported (out)");
else $fatal(1, "[l15_adapter] blockstores are not supported (out)");
blockstore_i: assert property (
blockstore_i :
assert property (
@(posedge clk_i) disable iff (!rst_ni) l15_rtrn_i.l15_val |-> l15_rtrn_i.l15_returntype inside {L15_ST_ACK, L15_ST_ACK} |-> !l15_rtrn_i.l15_blockinitstore)
else $fatal(1,"[l15_adapter] blockstores are not supported (in)");
else $fatal(1, "[l15_adapter] blockstores are not supported (in)");
unsuported_rtrn_types: assert property (
unsuported_rtrn_types :
assert property (
@(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 $warning("[l15_adapter] return type %X04 is not (yet) supported by l15 adapter.", l15_rtrn_i.l15_returntype);
else
$warning(
"[l15_adapter] return type %X04 is not (yet) supported by l15 adapter.",
l15_rtrn_i.l15_returntype
);
amo_type: assert property (
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");
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(1,"[l15_adapter] number of icache ways must be smaller or equal the number of L15 ways");
else
$fatal(
1, "[l15_adapter] number of icache ways must be smaller or equal the number of L15 ways"
);
// assert wrong parameterizations
assert (L15_SET_ASSOC >= DCACHE_SET_ASSOC)
else $fatal(1,"[l15_adapter] number of dcache ways must be smaller or equal the number of L15 ways");
else
$fatal(
1, "[l15_adapter] number of dcache ways must be smaller or equal the number of L15 ways"
);
// invalidation address returned by L1.5 is 16 bit
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");
else $fatal(1, "[l15_adapter] maximum number of index bits supported by L1.5 is 16");
end
`endif
//pragma translate_on
//pragma translate_on
endmodule // wt_l15_adapter

View file

@ -13,9 +13,11 @@
// Description: Commits to the architectural state resulting from the scoreboard.
module commit_stage import ariane_pkg::*; #(
module commit_stage
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
)(
) (
input logic clk_i,
input logic rst_ni,
input logic halt_i, // request to halt the core
@ -54,19 +56,19 @@ module commit_stage import ariane_pkg::*; #(
output logic sfence_vma_o // flush TLBs and pipeline
);
// ila_0 i_ila_commit (
// .clk(clk_i), // input wire clk
// .probe0(commit_instr_i[0].pc), // input wire [63:0] probe0
// .probe1(commit_instr_i[1].pc), // input wire [63:0] probe1
// .probe2(commit_instr_i[0].valid), // input wire [0:0] probe2
// .probe3(commit_instr_i[1].valid), // input wire [0:0] probe3
// .probe4(commit_ack_o[0]), // input wire [0:0] probe4
// .probe5(commit_ack_o[0]), // input wire [0:0] probe5
// .probe6(1'b0), // input wire [0:0] probe6
// .probe7(1'b0), // input wire [0:0] probe7
// .probe8(1'b0), // input wire [0:0] probe8
// .probe9(1'b0) // input wire [0:0] probe9
// );
// ila_0 i_ila_commit (
// .clk(clk_i), // input wire clk
// .probe0(commit_instr_i[0].pc), // input wire [63:0] probe0
// .probe1(commit_instr_i[1].pc), // input wire [63:0] probe1
// .probe2(commit_instr_i[0].valid), // input wire [0:0] probe2
// .probe3(commit_instr_i[1].valid), // input wire [0:0] probe3
// .probe4(commit_ack_o[0]), // input wire [0:0] probe4
// .probe5(commit_ack_o[0]), // input wire [0:0] probe5
// .probe6(1'b0), // input wire [0:0] probe6
// .probe7(1'b0), // input wire [0:0] probe7
// .probe8(1'b0), // input wire [0:0] probe8
// .probe9(1'b0) // input wire [0:0] probe9
// );
for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin : gen_waddr
assign waddr_o[i] = commit_instr_i[i].rd[4:0];
@ -77,7 +79,9 @@ module commit_stage import ariane_pkg::*; #(
always_comb begin : dirty_fp_state
dirty_fp_state_o = 1'b0;
for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
dirty_fp_state_o |= commit_ack_o[i] & (commit_instr_i[i].fu inside {FPU, FPU_VEC} || (CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(commit_instr_i[i].op)));
dirty_fp_state_o |= commit_ack_o[i] & (commit_instr_i[i].fu inside {FPU, FPU_VEC} || (CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(
commit_instr_i[i].op
)));
// Check if we issued a vector floating-point instruction to the accellerator
dirty_fp_state_o |= commit_instr_i[i].fu == ACCEL && commit_instr_i[i].vfp;
end
@ -136,10 +140,10 @@ module commit_stage import ariane_pkg::*; #(
// ---------
// FPU Flags
// ---------
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
if (commit_instr_i[0].fu inside {FPU, FPU_VEC}) begin
// write the CSR with potential exception flags from retiring floating point instruction
csr_wdata_o = {{riscv::XLEN-5{1'b0}}, commit_instr_i[0].ex.cause[4:0]};
csr_wdata_o = {{riscv::XLEN - 5{1'b0}}, commit_instr_i[0].ex.cause[4:0]};
csr_write_fflags_o = 1'b1;
commit_ack_o[0] = 1'b1;
end
@ -231,10 +235,8 @@ module commit_stage import ariane_pkg::*; #(
if (!exception_o.valid && !commit_instr_i[1].ex.valid
&& (commit_instr_i[1].fu inside {ALU, LOAD, CTRL_FLOW, MULT, FPU, FPU_VEC})) begin
if (CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(commit_instr_i[1].op))
we_fpr_o[1] = 1'b1;
else
we_gpr_o[1] = 1'b1;
if (CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(commit_instr_i[1].op)) we_fpr_o[1] = 1'b1;
else we_gpr_o[1] = 1'b1;
commit_ack_o[1] = 1'b1;
@ -242,9 +244,11 @@ module commit_stage import ariane_pkg::*; #(
// exception flags
if (CVA6Cfg.FpPresent && commit_instr_i[1].fu inside {FPU, FPU_VEC}) begin
if (csr_write_fflags_o)
csr_wdata_o = {{riscv::XLEN-5{1'b0}}, (commit_instr_i[0].ex.cause[4:0] | commit_instr_i[1].ex.cause[4:0])};
else
csr_wdata_o = {{riscv::XLEN-5{1'b0}}, commit_instr_i[1].ex.cause[4:0]};
csr_wdata_o = {
{riscv::XLEN - 5{1'b0}},
(commit_instr_i[0].ex.cause[4:0] | commit_instr_i[1].ex.cause[4:0])
};
else csr_wdata_o = {{riscv::XLEN - 5{1'b0}}, commit_instr_i[1].ex.cause[4:0]};
csr_write_fflags_o = 1'b1;
end

View file

@ -45,19 +45,54 @@ module compressed_decoder #(
unique case (instr_i[15:13])
riscv::OpcodeC0Addi4spn: begin
// c.addi4spn -> addi rd', x2, imm
instr_o = {2'b0, instr_i[10:7], instr_i[12:11], instr_i[5], instr_i[6], 2'b00, 5'h02, 3'b000, 2'b01, instr_i[4:2], riscv::OpcodeOpImm};
instr_o = {
2'b0,
instr_i[10:7],
instr_i[12:11],
instr_i[5],
instr_i[6],
2'b00,
5'h02,
3'b000,
2'b01,
instr_i[4:2],
riscv::OpcodeOpImm
};
if (instr_i[12:5] == 8'b0) illegal_instr_o = 1'b1;
end
riscv::OpcodeC0Fld: begin
// c.fld -> fld rd', imm(rs1')
// CLD: | funct3 | imm[5:3] | rs1' | imm[7:6] | rd' | C0 |
instr_o = {4'b0, instr_i[6:5], instr_i[12:10], 3'b000, 2'b01, instr_i[9:7], 3'b011, 2'b01, instr_i[4:2], riscv::OpcodeLoadFp};
instr_o = {
4'b0,
instr_i[6:5],
instr_i[12:10],
3'b000,
2'b01,
instr_i[9:7],
3'b011,
2'b01,
instr_i[4:2],
riscv::OpcodeLoadFp
};
end
riscv::OpcodeC0Lw: begin
// c.lw -> lw rd', imm(rs1')
instr_o = {5'b0, instr_i[5], instr_i[12:10], instr_i[6], 2'b00, 2'b01, instr_i[9:7], 3'b010, 2'b01, instr_i[4:2], riscv::OpcodeLoad};
instr_o = {
5'b0,
instr_i[5],
instr_i[12:10],
instr_i[6],
2'b00,
2'b01,
instr_i[9:7],
3'b010,
2'b01,
instr_i[4:2],
riscv::OpcodeLoad
};
end
riscv::OpcodeC0Ld: begin
@ -67,10 +102,33 @@ module compressed_decoder #(
// c.flw -> flw fprd', imm(rs1')
if (riscv::XLEN == 64) begin
// CLD: | funct3 | imm[5:3] | rs1' | imm[7:6] | rd' | C0 |
instr_o = {4'b0, instr_i[6:5], instr_i[12:10], 3'b000, 2'b01, instr_i[9:7], 3'b011, 2'b01, instr_i[4:2], riscv::OpcodeLoad};
instr_o = {
4'b0,
instr_i[6:5],
instr_i[12:10],
3'b000,
2'b01,
instr_i[9:7],
3'b011,
2'b01,
instr_i[4:2],
riscv::OpcodeLoad
};
end else begin
// CFLW: | funct3 (change to LW) | imm[5:3] | rs1' | imm[2|6] | rd' | C0 |
instr_o = {5'b0, instr_i[5], instr_i[12:10], instr_i[6], 2'b00, 2'b01, instr_i[9:7], 3'b010, 2'b01, instr_i[4:2], riscv::OpcodeLoadFp};
instr_o = {
5'b0,
instr_i[5],
instr_i[12:10],
instr_i[6],
2'b00,
2'b01,
instr_i[9:7],
3'b010,
2'b01,
instr_i[4:2],
riscv::OpcodeLoadFp
};
end
end
@ -79,27 +137,79 @@ module compressed_decoder #(
unique case (instr_i[12:10])
3'b000: begin
// c.lbu -> lbu rd', uimm(rs1')
instr_o = {10'b0, instr_i[5], instr_i[6], 2'b01, instr_i[9:7], 3'b100, 2'b01, instr_i[4:2], riscv::OpcodeLoad};
instr_o = {
10'b0,
instr_i[5],
instr_i[6],
2'b01,
instr_i[9:7],
3'b100,
2'b01,
instr_i[4:2],
riscv::OpcodeLoad
};
end
3'b001: begin
if (instr_i[6])begin
if (instr_i[6]) begin
// c.lh -> lh rd', uimm(rs1')
instr_o = {10'b0, instr_i[5], 1'b0, 2'b01, instr_i[9:7], 3'b001, 2'b01, instr_i[4:2], riscv::OpcodeLoad};
instr_o = {
10'b0,
instr_i[5],
1'b0,
2'b01,
instr_i[9:7],
3'b001,
2'b01,
instr_i[4:2],
riscv::OpcodeLoad
};
end else begin
// c.lhu -> lhu rd', uimm(rs1')
instr_o = {10'b0, instr_i[5], 1'b0, 2'b01, instr_i[9:7], 3'b101, 2'b01, instr_i[4:2], riscv::OpcodeLoad};
instr_o = {
10'b0,
instr_i[5],
1'b0,
2'b01,
instr_i[9:7],
3'b101,
2'b01,
instr_i[4:2],
riscv::OpcodeLoad
};
end
end
3'b010: begin
// c.sb -> sb rs2', uimm(rs1')
instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b000, 3'b0, instr_i[5], instr_i[6], riscv::OpcodeStore};
instr_o = {
7'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b000,
3'b0,
instr_i[5],
instr_i[6],
riscv::OpcodeStore
};
end
3'b011: begin
// c.sh -> sh rs2', uimm(rs1')
instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b001, 3'b0, instr_i[5], 1'b0, riscv::OpcodeStore};
instr_o = {
7'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b001,
3'b0,
instr_i[5],
1'b0,
riscv::OpcodeStore
};
end
default: begin
@ -115,12 +225,37 @@ module compressed_decoder #(
riscv::OpcodeC0Fsd: begin
// c.fsd -> fsd rs2', imm(rs1')
instr_o = {4'b0, instr_i[6:5], instr_i[12], 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b011, instr_i[11:10], 3'b000, riscv::OpcodeStoreFp};
instr_o = {
4'b0,
instr_i[6:5],
instr_i[12],
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b011,
instr_i[11:10],
3'b000,
riscv::OpcodeStoreFp
};
end
riscv::OpcodeC0Sw: begin
// c.sw -> sw rs2', imm(rs1')
instr_o = {5'b0, instr_i[5], instr_i[12], 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b010, instr_i[11:10], instr_i[6], 2'b00, riscv::OpcodeStore};
instr_o = {
5'b0,
instr_i[5],
instr_i[12],
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b010,
instr_i[11:10],
instr_i[6],
2'b00,
riscv::OpcodeStore
};
end
riscv::OpcodeC0Sd: begin
@ -129,9 +264,34 @@ module compressed_decoder #(
// RV32
// c.fsw -> fsw fprs2', imm(rs1')
if (riscv::XLEN == 64) begin
instr_o = {4'b0, instr_i[6:5], instr_i[12], 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b011, instr_i[11:10], 3'b000, riscv::OpcodeStore};
instr_o = {
4'b0,
instr_i[6:5],
instr_i[12],
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b011,
instr_i[11:10],
3'b000,
riscv::OpcodeStore
};
end else begin
instr_o = {5'b0, instr_i[5], instr_i[12], 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b010, instr_i[11:10], instr_i[6], 2'b00, riscv::OpcodeStoreFp};
instr_o = {
5'b0,
instr_i[5],
instr_i[12],
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b010,
instr_i[11:10],
instr_i[6],
2'b00,
riscv::OpcodeStoreFp
};
end
end
@ -147,7 +307,15 @@ module compressed_decoder #(
riscv::OpcodeC1Addi: begin
// c.addi -> addi rd, rd, nzimm
// c.nop -> addi 0, 0, 0
instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], instr_i[11:7], 3'b0, instr_i[11:7], riscv::OpcodeOpImm};
instr_o = {
{6{instr_i[12]}},
instr_i[12],
instr_i[6:2],
instr_i[11:7],
3'b0,
instr_i[11:7],
riscv::OpcodeOpImm
};
end
@ -155,13 +323,33 @@ module compressed_decoder #(
if (riscv::XLEN == 64) begin
// c.addiw -> addiw rd, rd, nzimm for RV64IC
if (instr_i[11:7] != 5'h0) begin // only valid if the destination is not r0
instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], instr_i[11:7], 3'b0, instr_i[11:7], riscv::OpcodeOpImm32};
instr_o = {
{6{instr_i[12]}},
instr_i[12],
instr_i[6:2],
instr_i[11:7],
3'b0,
instr_i[11:7],
riscv::OpcodeOpImm32
};
end else begin
illegal_instr_o = 1'b1;
end
end else begin
// c.jal -> jal x1, imm for RV32IC only
instr_o = {instr_i[12], instr_i[8], instr_i[10:9], instr_i[6], instr_i[7], instr_i[2], instr_i[11], instr_i[5:3], {9 {instr_i[12]}}, 5'b1, riscv::OpcodeJal};
instr_o = {
instr_i[12],
instr_i[8],
instr_i[10:9],
instr_i[6],
instr_i[7],
instr_i[2],
instr_i[11],
instr_i[5:3],
{9{instr_i[12]}},
5'b1,
riscv::OpcodeJal
};
@ -170,16 +358,35 @@ module compressed_decoder #(
riscv::OpcodeC1Li: begin
// c.li -> addi rd, x0, nzimm
instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], 5'b0, 3'b0, instr_i[11:7], riscv::OpcodeOpImm};
instr_o = {
{6{instr_i[12]}},
instr_i[12],
instr_i[6:2],
5'b0,
3'b0,
instr_i[11:7],
riscv::OpcodeOpImm
};
end
riscv::OpcodeC1LuiAddi16sp: begin
// c.lui -> lui rd, imm
instr_o = {{15 {instr_i[12]}}, instr_i[6:2], instr_i[11:7], riscv::OpcodeLui};
instr_o = {{15{instr_i[12]}}, instr_i[6:2], instr_i[11:7], riscv::OpcodeLui};
if (instr_i[11:7] == 5'h02) begin
// c.addi16sp -> addi x2, x2, nzimm
instr_o = {{3 {instr_i[12]}}, instr_i[4:3], instr_i[5], instr_i[2], instr_i[6], 4'b0, 5'h02, 3'b000, 5'h02, riscv::OpcodeOpImm};
instr_o = {
{3{instr_i[12]}},
instr_i[4:3],
instr_i[5],
instr_i[2],
instr_i[6],
4'b0,
5'h02,
3'b000,
5'h02,
riscv::OpcodeOpImm
};
end
if ({instr_i[12], instr_i[6:2]} == 6'b0) illegal_instr_o = 1'b1;
@ -187,53 +394,150 @@ module compressed_decoder #(
riscv::OpcodeC1MiscAlu: begin
unique case (instr_i[11:10])
2'b00,
2'b01: begin
2'b00, 2'b01: begin
// 00: c.srli -> srli rd, rd, shamt
// 01: c.srai -> srai rd, rd, shamt
instr_o = {1'b0, instr_i[10], 4'b0, instr_i[12], instr_i[6:2], 2'b01, instr_i[9:7], 3'b101, 2'b01, instr_i[9:7], riscv::OpcodeOpImm};
instr_o = {
1'b0,
instr_i[10],
4'b0,
instr_i[12],
instr_i[6:2],
2'b01,
instr_i[9:7],
3'b101,
2'b01,
instr_i[9:7],
riscv::OpcodeOpImm
};
end
2'b10: begin
// c.andi -> andi rd, rd, imm
instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], 2'b01, instr_i[9:7], 3'b111, 2'b01, instr_i[9:7], riscv::OpcodeOpImm};
instr_o = {
{6{instr_i[12]}},
instr_i[12],
instr_i[6:2],
2'b01,
instr_i[9:7],
3'b111,
2'b01,
instr_i[9:7],
riscv::OpcodeOpImm
};
end
2'b11: begin
unique case ({instr_i[12], instr_i[6:5]})
unique case ({
instr_i[12], instr_i[6:5]
})
3'b000: begin
// c.sub -> sub rd', rd', rs2'
instr_o = {2'b01, 5'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b000, 2'b01, instr_i[9:7], riscv::OpcodeOp};
instr_o = {
2'b01,
5'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b000,
2'b01,
instr_i[9:7],
riscv::OpcodeOp
};
end
3'b001: begin
// c.xor -> xor rd', rd', rs2'
instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b100, 2'b01, instr_i[9:7], riscv::OpcodeOp};
instr_o = {
7'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b100,
2'b01,
instr_i[9:7],
riscv::OpcodeOp
};
end
3'b010: begin
// c.or -> or rd', rd', rs2'
instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b110, 2'b01, instr_i[9:7], riscv::OpcodeOp};
instr_o = {
7'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b110,
2'b01,
instr_i[9:7],
riscv::OpcodeOp
};
end
3'b011: begin
// c.and -> and rd', rd', rs2'
instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b111, 2'b01, instr_i[9:7], riscv::OpcodeOp};
instr_o = {
7'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b111,
2'b01,
instr_i[9:7],
riscv::OpcodeOp
};
end
3'b100: begin
// c.subw -> subw rd', rd', rs2'
instr_o = {2'b01, 5'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b000, 2'b01, instr_i[9:7], riscv::OpcodeOp32};
instr_o = {
2'b01,
5'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b000,
2'b01,
instr_i[9:7],
riscv::OpcodeOp32
};
end
3'b101: begin
// c.addw -> addw rd', rd', rs2'
instr_o = {2'b00, 5'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b000, 2'b01, instr_i[9:7], riscv::OpcodeOp32};
instr_o = {
2'b00,
5'b0,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b000,
2'b01,
instr_i[9:7],
riscv::OpcodeOp32
};
end
3'b110: begin
if (CVA6Cfg.RVZCB) begin
// c.mul -> mul rd', rd', rs2'
instr_o = {6'b0, 1'b1, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b000, 2'b01, instr_i[9:7], riscv::OpcodeOp};
instr_o = {
6'b0,
1'b1,
2'b01,
instr_i[4:2],
2'b01,
instr_i[9:7],
3'b000,
2'b01,
instr_i[9:7],
riscv::OpcodeOp
};
end else begin
instr_o = instr_i;
illegal_instr_o = 1'b1;
@ -246,13 +550,31 @@ module compressed_decoder #(
unique case (instr_i[4:2])
3'b000: begin
// c.zext.b -> andi rd', rd', 0xff
instr_o = {4'b0, 8'hFF, 2'b01, instr_i[9:7], 3'b111, 2'b01, instr_i[9:7], riscv::OpcodeOpImm};
instr_o = {
4'b0,
8'hFF,
2'b01,
instr_i[9:7],
3'b111,
2'b01,
instr_i[9:7],
riscv::OpcodeOpImm
};
end
3'b001: begin
if (ariane_pkg::BITMANIP) begin
// c.sext.b -> sext.b rd', rd'
instr_o = {7'h30, 5'h4, 2'b01, instr_i[9:7], 3'b001, 2'b01, instr_i[9:7], riscv::OpcodeOpImm};
instr_o = {
7'h30,
5'h4,
2'b01,
instr_i[9:7],
3'b001,
2'b01,
instr_i[9:7],
riscv::OpcodeOpImm
};
end else illegal_instr_o = 1'b1;
end
@ -260,9 +582,27 @@ module compressed_decoder #(
if (ariane_pkg::BITMANIP) begin
// c.zext.h -> zext.h rd', rd'
if (riscv::XLEN == 64) begin
instr_o = {7'h4, 5'h0, 2'b01, instr_i[9:7], 3'b100, 2'b01, instr_i[9:7], riscv::OpcodeOp32};
instr_o = {
7'h4,
5'h0,
2'b01,
instr_i[9:7],
3'b100,
2'b01,
instr_i[9:7],
riscv::OpcodeOp32
};
end else begin
instr_o = {7'h4, 5'h0, 2'b01, instr_i[9:7], 3'b100, 2'b01, instr_i[9:7], riscv::OpcodeOp};
instr_o = {
7'h4,
5'h0,
2'b01,
instr_i[9:7],
3'b100,
2'b01,
instr_i[9:7],
riscv::OpcodeOp
};
end
end else illegal_instr_o = 1'b1;
end
@ -270,7 +610,16 @@ module compressed_decoder #(
3'b011: begin
if (ariane_pkg::BITMANIP) begin
// c.sext.h -> sext.h rd', rd'
instr_o = {7'h30, 5'h5, 2'b01, instr_i[9:7], 3'b001, 2'b01, instr_i[9:7], riscv::OpcodeOpImm};
instr_o = {
7'h30,
5'h5,
2'b01,
instr_i[9:7],
3'b001,
2'b01,
instr_i[9:7],
riscv::OpcodeOpImm
};
end else illegal_instr_o = 1'b1;
end
@ -278,7 +627,16 @@ module compressed_decoder #(
if (ariane_pkg::BITMANIP) begin
// c.zext.w -> add.uw
if (riscv::XLEN == 64) begin
instr_o = {7'h4, 5'h0, 2'b01, instr_i[9:7], 3'b000, 2'b01, instr_i[9:7], riscv::OpcodeOp32};
instr_o = {
7'h4,
5'h0,
2'b01,
instr_i[9:7],
3'b000,
2'b01,
instr_i[9:7],
riscv::OpcodeOp32
};
end else begin
illegal_instr_o = 1'b1;
end
@ -287,7 +645,15 @@ module compressed_decoder #(
3'b101: begin
// c.not -> xori rd', rd', -1
instr_o = {12'hFFF, 2'b01, instr_i[9:7], 3'b100, 2'b01, instr_i[9:7], riscv::OpcodeOpImm};
instr_o = {
12'hFFF,
2'b01,
instr_i[9:7],
3'b100,
2'b01,
instr_i[9:7],
riscv::OpcodeOpImm
};
end
default: begin
@ -304,13 +670,39 @@ module compressed_decoder #(
riscv::OpcodeC1J: begin
// 101: c.j -> jal x0, imm
instr_o = {instr_i[12], instr_i[8], instr_i[10:9], instr_i[6], instr_i[7], instr_i[2], instr_i[11], instr_i[5:3], {9 {instr_i[12]}}, 4'b0, ~instr_i[15], riscv::OpcodeJal};
instr_o = {
instr_i[12],
instr_i[8],
instr_i[10:9],
instr_i[6],
instr_i[7],
instr_i[2],
instr_i[11],
instr_i[5:3],
{9{instr_i[12]}},
4'b0,
~instr_i[15],
riscv::OpcodeJal
};
end
riscv::OpcodeC1Beqz, riscv::OpcodeC1Bnez: begin
// 0: c.beqz -> beq rs1', x0, imm
// 1: c.bnez -> bne rs1', x0, imm
instr_o = {{4 {instr_i[12]}}, instr_i[6:5], instr_i[2], 5'b0, 2'b01, instr_i[9:7], 2'b00, instr_i[13], instr_i[11:10], instr_i[4:3], instr_i[12], riscv::OpcodeBranch};
instr_o = {
{4{instr_i[12]}},
instr_i[6:5],
instr_i[2],
5'b0,
2'b01,
instr_i[9:7],
2'b00,
instr_i[13],
instr_i[11:10],
instr_i[4:3],
instr_i[12],
riscv::OpcodeBranch
};
end
endcase
end
@ -320,17 +712,45 @@ module compressed_decoder #(
unique case (instr_i[15:13])
riscv::OpcodeC2Slli: begin
// c.slli -> slli rd, rd, shamt
instr_o = {6'b0, instr_i[12], instr_i[6:2], instr_i[11:7], 3'b001, instr_i[11:7], riscv::OpcodeOpImm};
instr_o = {
6'b0,
instr_i[12],
instr_i[6:2],
instr_i[11:7],
3'b001,
instr_i[11:7],
riscv::OpcodeOpImm
};
end
riscv::OpcodeC2Fldsp: begin
// c.fldsp -> fld rd, imm(x2)
instr_o = {3'b0, instr_i[4:2], instr_i[12], instr_i[6:5], 3'b000, 5'h02, 3'b011, instr_i[11:7], riscv::OpcodeLoadFp};
instr_o = {
3'b0,
instr_i[4:2],
instr_i[12],
instr_i[6:5],
3'b000,
5'h02,
3'b011,
instr_i[11:7],
riscv::OpcodeLoadFp
};
end
riscv::OpcodeC2Lwsp: begin
// c.lwsp -> lw rd, imm(x2)
instr_o = {4'b0, instr_i[3:2], instr_i[12], instr_i[6:4], 2'b00, 5'h02, 3'b010, instr_i[11:7], riscv::OpcodeLoad};
instr_o = {
4'b0,
instr_i[3:2],
instr_i[12],
instr_i[6:4],
2'b00,
5'h02,
3'b010,
instr_i[11:7],
riscv::OpcodeLoad
};
if (instr_i[11:7] == 5'b0) illegal_instr_o = 1'b1;
end
@ -340,10 +760,30 @@ module compressed_decoder #(
// RV32
// c.flwsp -> flw fprd, imm(x2)
if (riscv::XLEN == 64) begin
instr_o = {3'b0, instr_i[4:2], instr_i[12], instr_i[6:5], 3'b000, 5'h02, 3'b011, instr_i[11:7], riscv::OpcodeLoad};
instr_o = {
3'b0,
instr_i[4:2],
instr_i[12],
instr_i[6:5],
3'b000,
5'h02,
3'b011,
instr_i[11:7],
riscv::OpcodeLoad
};
if (instr_i[11:7] == 5'b0) illegal_instr_o = 1'b1;
end else begin
instr_o = {4'b0, instr_i[3:2], instr_i[12], instr_i[6:4], 2'b00, 5'h02, 3'b010, instr_i[11:7], riscv::OpcodeLoadFp};
instr_o = {
4'b0,
instr_i[3:2],
instr_i[12],
instr_i[6:4],
2'b00,
5'h02,
3'b010,
instr_i[11:7],
riscv::OpcodeLoadFp
};
end
end
@ -374,12 +814,32 @@ module compressed_decoder #(
riscv::OpcodeC2Fsdsp: begin
// c.fsdsp -> fsd rs2, imm(x2)
instr_o = {3'b0, instr_i[9:7], instr_i[12], instr_i[6:2], 5'h02, 3'b011, instr_i[11:10], 3'b000, riscv::OpcodeStoreFp};
instr_o = {
3'b0,
instr_i[9:7],
instr_i[12],
instr_i[6:2],
5'h02,
3'b011,
instr_i[11:10],
3'b000,
riscv::OpcodeStoreFp
};
end
riscv::OpcodeC2Swsp: begin
// c.swsp -> sw rs2, imm(x2)
instr_o = {4'b0, instr_i[8:7], instr_i[12], instr_i[6:2], 5'h02, 3'b010, instr_i[11:9], 2'b00, riscv::OpcodeStore};
instr_o = {
4'b0,
instr_i[8:7],
instr_i[12],
instr_i[6:2],
5'h02,
3'b010,
instr_i[11:9],
2'b00,
riscv::OpcodeStore
};
end
riscv::OpcodeC2Sdsp: begin
@ -388,9 +848,29 @@ module compressed_decoder #(
// RV32
// c.fswsp -> fsw fprs2, imm(x2)
if (riscv::XLEN == 64) begin
instr_o = {3'b0, instr_i[9:7], instr_i[12], instr_i[6:2], 5'h02, 3'b011, instr_i[11:10], 3'b000, riscv::OpcodeStore};
instr_o = {
3'b0,
instr_i[9:7],
instr_i[12],
instr_i[6:2],
5'h02,
3'b011,
instr_i[11:10],
3'b000,
riscv::OpcodeStore
};
end else begin
instr_o = {4'b0, instr_i[8:7], instr_i[12], instr_i[6:2], 5'h02, 3'b010, instr_i[11:9], 2'b00, riscv::OpcodeStoreFp};
instr_o = {
4'b0,
instr_i[8:7],
instr_i[12],
instr_i[6:2],
5'h02,
3'b010,
instr_i[11:9],
2'b00,
riscv::OpcodeStoreFp
};
end
end

View file

@ -13,7 +13,9 @@
// Description: Flush controller
module controller import ariane_pkg::*; #(
module controller
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -83,8 +85,8 @@ module controller import ariane_pkg::*; #(
flush_unissued_instr_o = 1'b1;
flush_id_o = 1'b1;
flush_ex_o = 1'b1;
// this is not needed in the case since we
// have a write-through cache in this case
// this is not needed in the case since we
// have a write-through cache in this case
if (DCACHE_TYPE == int'(config_pkg::WB)) begin
flush_dcache = 1'b1;
fence_active_d = 1'b1;
@ -101,16 +103,16 @@ module controller import ariane_pkg::*; #(
flush_id_o = 1'b1;
flush_ex_o = 1'b1;
flush_icache_o = 1'b1;
// this is not needed in the case since we
// have a write-through cache in this case
// this is not needed in the case since we
// have a write-through cache in this case
if (DCACHE_TYPE == int'(config_pkg::WB)) begin
flush_dcache = 1'b1;
fence_active_d = 1'b1;
end
end
// this is not needed in the case since we
// have a write-through cache in this case
// this is not needed in the case since we
// have a write-through cache in this case
if (DCACHE_TYPE == int'(config_pkg::WB)) begin
// wait for the acknowledge here
if (flush_dcache_ack_i && fence_active_q) begin

View file

@ -14,7 +14,9 @@
// to the scoreboard.
module csr_buffer import ariane_pkg::*; #(
module csr_buffer
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -35,7 +37,8 @@ module csr_buffer import ariane_pkg::*; #(
struct packed {
logic [11:0] csr_address;
logic valid;
} csr_reg_n, csr_reg_q;
}
csr_reg_n, csr_reg_q;
// control logic, scoreboard signals
assign csr_result_o = fu_data_i.operand_a;
@ -47,8 +50,7 @@ module csr_buffer import ariane_pkg::*; #(
// by default we are ready
csr_ready_o = 1'b1;
// if we have a valid uncomiited csr req or are just getting one WITHOUT a commit in, we are not ready
if ((csr_reg_q.valid || csr_valid_i) && ~csr_commit_i)
csr_ready_o = 1'b0;
if ((csr_reg_q.valid || csr_valid_i) && ~csr_commit_i) csr_ready_o = 1'b0;
// if we got a valid from the scoreboard
// store the CSR address
if (csr_valid_i) begin
@ -60,12 +62,11 @@ module csr_buffer import ariane_pkg::*; #(
csr_reg_n.valid = 1'b0;
end
// clear the buffer if we flushed
if (flush_i)
csr_reg_n.valid = 1'b0;
if (flush_i) csr_reg_n.valid = 1'b0;
end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
if (~rst_ni) begin
csr_reg_q <= '{default: 0};
end else begin
csr_reg_q <= csr_reg_n;

View file

@ -13,7 +13,9 @@
// Description: CSR Register File as specified by RISC-V
module csr_regfile import ariane_pkg::*; #(
module csr_regfile
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int AsidWidth = 1,
parameter int unsigned MHPMCounterNum = 6
@ -35,11 +37,11 @@ module csr_regfile import ariane_pkg::*; #(
input fu_op csr_op_i, // Operation to perform on the CSR file
input logic [11:0] csr_addr_i, // Address of the register to read/write
input logic[riscv::XLEN-1:0] csr_wdata_i, // Write data in
output logic[riscv::XLEN-1:0] csr_rdata_o, // Read data out
input logic [riscv::XLEN-1:0] csr_wdata_i, // Write data in
output logic [riscv::XLEN-1:0] csr_rdata_o, // Read data out
input logic dirty_fp_state_i, // Mark the FP sate as dirty
input logic csr_write_fflags_i, // Write fflags register e.g.: we are retiring a floating point instruction
input logic dirty_v_state_i , // Mark the V state as dirty
input logic dirty_v_state_i, // Mark the V state as dirty
input logic [riscv::VLEN-1:0] pc_i, // PC of instruction accessing the CSR
output exception_t csr_exception_o, // attempts to access a CSR without appropriate privilege
// level or to write a read-only register also
@ -67,7 +69,7 @@ module csr_regfile import ariane_pkg::*; #(
output riscv::priv_lvl_t ld_st_priv_lvl_o, // Privilege level at which load and stores should happen
output logic sum_o,
output logic mxr_o,
output logic[riscv::PPNW-1:0] satp_ppn_o,
output logic [riscv::PPNW-1:0] satp_ppn_o,
output logic [AsidWidth-1:0] asid_o,
// external interrupts
input logic [1:0] irq_i, // external interrupt in
@ -87,8 +89,8 @@ module csr_regfile import ariane_pkg::*; #(
output logic acc_cons_en_o, // Accelerator memory consistent mode
// Performance Counter
output logic [11:0] perf_addr_o, // read/write address to performance counter module
output logic[riscv::XLEN-1:0] perf_data_o, // write data to performance counter module
input logic[riscv::XLEN-1:0] perf_data_i, // read data from performance counter module
output logic [riscv::XLEN-1:0] perf_data_o, // write data to performance counter module
input logic [riscv::XLEN-1:0] perf_data_i, // read data from performance counter module
output logic perf_we_o,
// PMPs
output riscv::pmpcfg_t [15:0] pmpcfg_o, // PMP configuration containing pmpcfg for max 16 PMPs
@ -117,7 +119,7 @@ module csr_regfile import ariane_pkg::*; #(
riscv::priv_lvl_t priv_lvl_d, priv_lvl_q;
// we are in debug
logic debug_mode_q, debug_mode_d;
logic mtvec_rst_load_q;// used to determine whether we came out of reset
logic mtvec_rst_load_q; // used to determine whether we came out of reset
riscv::xlen_t dpc_q, dpc_d;
riscv::xlen_t dscratch0_q, dscratch0_d;
@ -127,14 +129,14 @@ module csr_regfile import ariane_pkg::*; #(
riscv::xlen_t mideleg_q, mideleg_d;
riscv::xlen_t mip_q, mip_d;
riscv::xlen_t mie_q, mie_d;
riscv::xlen_t mcounteren_q,mcounteren_d;
riscv::xlen_t mcounteren_q, mcounteren_d;
riscv::xlen_t mscratch_q, mscratch_d;
riscv::xlen_t mepc_q, mepc_d;
riscv::xlen_t mcause_q, mcause_d;
riscv::xlen_t mtval_q, mtval_d;
riscv::xlen_t stvec_q, stvec_d;
riscv::xlen_t scounteren_q,scounteren_d;
riscv::xlen_t scounteren_q, scounteren_d;
riscv::xlen_t sscratch_q, sscratch_d;
riscv::xlen_t sepc_q, sepc_d;
riscv::xlen_t scause_q, scause_d;
@ -150,21 +152,21 @@ module csr_regfile import ariane_pkg::*; #(
riscv::pmpcfg_t [15:0] pmpcfg_q, pmpcfg_d;
logic [15:0][riscv::PLEN-3:0] pmpaddr_q, pmpaddr_d;
logic [MHPMCounterNum+3-1:0] mcountinhibit_d,mcountinhibit_q;
logic [MHPMCounterNum+3-1:0] mcountinhibit_d, mcountinhibit_q;
logic [3:0] index;
localparam riscv::xlen_t IsaCode = (riscv::XLEN'(CVA6Cfg.RVA) << 0) // A - Atomic Instructions extension
| (riscv::XLEN'(CVA6Cfg.RVC) << 2) // C - Compressed extension
| (riscv::XLEN'(CVA6Cfg.RVD) << 3) // D - Double precsision floating-point extension
| (riscv::XLEN'(CVA6Cfg.RVF) << 5) // F - Single precsision floating-point extension
| (riscv::XLEN'(1 ) << 8) // I - RV32I/64I/128I base ISA
| (riscv::XLEN'(1 ) << 12) // M - Integer Multiply/Divide extension
| (riscv::XLEN'(0 ) << 13) // N - User level interrupts supported
| (riscv::XLEN'(1 ) << 18) // S - Supervisor mode implemented
| (riscv::XLEN'(1 ) << 20) // U - User mode implemented
| (riscv::XLEN'(1) << 8) // I - RV32I/64I/128I base ISA
| (riscv::XLEN'(1) << 12) // M - Integer Multiply/Divide extension
| (riscv::XLEN'(0) << 13) // N - User level interrupts supported
| (riscv::XLEN'(1) << 18) // S - Supervisor mode implemented
| (riscv::XLEN'(1) << 20) // U - User mode implemented
| (riscv::XLEN'(CVA6Cfg.RVV) << 21) // V - Vector extension
| (riscv::XLEN'(CVA6Cfg.NSX) << 23) // X - Non-standard extensions present
| ((riscv::XLEN == 64 ? 2 : 1) << riscv::XLEN-2); // MXL
| ((riscv::XLEN == 64 ? 2 : 1) << riscv::XLEN - 2); // MXL
assign pmpcfg_o = pmpcfg_q[15:0];
assign pmpaddr_o = pmpaddr_q;
@ -193,44 +195,44 @@ module csr_regfile import ariane_pkg::*; #(
if (csr_read) begin
unique case (csr_addr.address)
riscv::CSR_FFLAGS: begin
if(CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN-5{1'b0}}, fcsr_q.fflags};
if (CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN - 5{1'b0}}, fcsr_q.fflags};
end else begin
read_access_exception = 1'b1;
end
end
riscv::CSR_FRM: begin
if(CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN-3{1'b0}}, fcsr_q.frm};
if (CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN - 3{1'b0}}, fcsr_q.frm};
end else begin
read_access_exception = 1'b1;
end
end
riscv::CSR_FCSR: begin
if(CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN-8{1'b0}}, fcsr_q.frm, fcsr_q.fflags};
if (CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN - 8{1'b0}}, fcsr_q.frm, fcsr_q.fflags};
end else begin
read_access_exception = 1'b1;
end
end
// non-standard extension
riscv::CSR_FTRAN: begin
if(CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN-7{1'b0}}, fcsr_q.fprec};
if (CVA6Cfg.FpPresent) begin
csr_rdata = {{riscv::XLEN - 7{1'b0}}, fcsr_q.fprec};
end else begin
read_access_exception = 1'b1;
end
end
// debug registers
riscv::CSR_DCSR: csr_rdata = {{riscv::XLEN-32{1'b0}}, dcsr_q};
riscv::CSR_DCSR: csr_rdata = {{riscv::XLEN - 32{1'b0}}, dcsr_q};
riscv::CSR_DPC: csr_rdata = dpc_q;
riscv::CSR_DSCRATCH0: csr_rdata = dscratch0_q;
riscv::CSR_DSCRATCH1: csr_rdata = dscratch1_q;
// trigger module registers
riscv::CSR_TSELECT:; // not implemented
riscv::CSR_TDATA1:; // not implemented
riscv::CSR_TDATA2:; // not implemented
riscv::CSR_TDATA3:; // not implemented
riscv::CSR_TSELECT: ; // not implemented
riscv::CSR_TDATA1: ; // not implemented
riscv::CSR_TDATA2: ; // not implemented
riscv::CSR_TDATA3: ; // not implemented
// supervisor registers
riscv::CSR_SSTATUS: begin
csr_rdata = mstatus_extended & ariane_pkg::SMODE_STATUS_READ_MASK[riscv::XLEN-1:0];
@ -253,7 +255,9 @@ module csr_regfile import ariane_pkg::*; #(
end
// machine mode registers
riscv::CSR_MSTATUS: csr_rdata = mstatus_extended;
riscv::CSR_MSTATUSH: if (riscv::XLEN == 32) csr_rdata = '0; else read_access_exception = 1'b1;
riscv::CSR_MSTATUSH:
if (riscv::XLEN == 32) csr_rdata = '0;
else read_access_exception = 1'b1;
riscv::CSR_MISA: csr_rdata = IsaCode;
riscv::CSR_MEDELEG: csr_rdata = medeleg_q;
riscv::CSR_MIDELEG: csr_rdata = mideleg_q;
@ -270,16 +274,25 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MIMPID: csr_rdata = '0; // not implemented
riscv::CSR_MHARTID: csr_rdata = hart_id_i;
riscv::CSR_MCONFIGPTR: csr_rdata = '0; // not implemented
riscv::CSR_MCOUNTINHIBIT: csr_rdata = {{(riscv::XLEN-(MHPMCounterNum+3)){1'b0}}, mcountinhibit_q};
riscv::CSR_MCOUNTINHIBIT:
csr_rdata = {{(riscv::XLEN - (MHPMCounterNum + 3)) {1'b0}}, mcountinhibit_q};
// Counters and Timers
riscv::CSR_MCYCLE: csr_rdata = cycle_q[riscv::XLEN-1:0];
riscv::CSR_MCYCLEH: if (riscv::XLEN == 32) csr_rdata = cycle_q[63:32]; else read_access_exception = 1'b1;
riscv::CSR_MCYCLEH:
if (riscv::XLEN == 32) csr_rdata = cycle_q[63:32];
else read_access_exception = 1'b1;
riscv::CSR_MINSTRET: csr_rdata = instret_q[riscv::XLEN-1:0];
riscv::CSR_MINSTRETH: if (riscv::XLEN == 32) csr_rdata = instret_q[63:32]; else read_access_exception = 1'b1;
riscv::CSR_MINSTRETH:
if (riscv::XLEN == 32) csr_rdata = instret_q[63:32];
else read_access_exception = 1'b1;
riscv::CSR_CYCLE: csr_rdata = cycle_q[riscv::XLEN-1:0];
riscv::CSR_CYCLEH: if (riscv::XLEN == 32) csr_rdata = cycle_q[63:32]; else read_access_exception = 1'b1;
riscv::CSR_CYCLEH:
if (riscv::XLEN == 32) csr_rdata = cycle_q[63:32];
else read_access_exception = 1'b1;
riscv::CSR_INSTRET: csr_rdata = instret_q[riscv::XLEN-1:0];
riscv::CSR_INSTRETH: if (riscv::XLEN == 32) csr_rdata = instret_q[63:32]; else read_access_exception = 1'b1;
riscv::CSR_INSTRETH:
if (riscv::XLEN == 32) csr_rdata = instret_q[63:32];
else read_access_exception = 1'b1;
//Event Selector
riscv::CSR_MHPM_EVENT_3,
riscv::CSR_MHPM_EVENT_4,
@ -309,7 +322,8 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MHPM_EVENT_28,
riscv::CSR_MHPM_EVENT_29,
riscv::CSR_MHPM_EVENT_30,
riscv::CSR_MHPM_EVENT_31 : csr_rdata = perf_data_i;
riscv::CSR_MHPM_EVENT_31 :
csr_rdata = perf_data_i;
riscv::CSR_MHPM_COUNTER_3,
riscv::CSR_MHPM_COUNTER_4,
@ -339,7 +353,8 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MHPM_COUNTER_28,
riscv::CSR_MHPM_COUNTER_29,
riscv::CSR_MHPM_COUNTER_30,
riscv::CSR_MHPM_COUNTER_31 : csr_rdata = perf_data_i;
riscv::CSR_MHPM_COUNTER_31 :
csr_rdata = perf_data_i;
riscv::CSR_MHPM_COUNTER_3H,
riscv::CSR_MHPM_COUNTER_4H,
@ -369,7 +384,9 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MHPM_COUNTER_28H,
riscv::CSR_MHPM_COUNTER_29H,
riscv::CSR_MHPM_COUNTER_30H,
riscv::CSR_MHPM_COUNTER_31H : if (riscv::XLEN == 32) csr_rdata = perf_data_i; else read_access_exception = 1'b1;
riscv::CSR_MHPM_COUNTER_31H :
if (riscv::XLEN == 32) csr_rdata = perf_data_i;
else read_access_exception = 1'b1;
// Performance counters (User Mode - R/O Shadows)
riscv::CSR_HPM_COUNTER_3,
@ -400,7 +417,8 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_HPM_COUNTER_28,
riscv::CSR_HPM_COUNTER_29,
riscv::CSR_HPM_COUNTER_30,
riscv::CSR_HPM_COUNTER_31 : csr_rdata = perf_data_i;
riscv::CSR_HPM_COUNTER_31 :
csr_rdata = perf_data_i;
riscv::CSR_HPM_COUNTER_3H,
riscv::CSR_HPM_COUNTER_4H,
@ -430,7 +448,9 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_HPM_COUNTER_28H,
riscv::CSR_HPM_COUNTER_29H,
riscv::CSR_HPM_COUNTER_30H,
riscv::CSR_HPM_COUNTER_31H : if (riscv::XLEN == 32) csr_rdata = perf_data_i; else read_access_exception = 1'b1;
riscv::CSR_HPM_COUNTER_31H :
if (riscv::XLEN == 32) csr_rdata = perf_data_i;
else read_access_exception = 1'b1;
// custom (non RISC-V) cache control
riscv::CSR_DCACHE: csr_rdata = dcache_q;
@ -445,9 +465,13 @@ module csr_regfile import ariane_pkg::*; #(
end
// PMPs
riscv::CSR_PMPCFG0: csr_rdata = pmpcfg_q[riscv::XLEN/8-1:0];
riscv::CSR_PMPCFG1: if (riscv::XLEN == 32) csr_rdata = pmpcfg_q[7:4]; else read_access_exception = 1'b1;
riscv::CSR_PMPCFG2: csr_rdata = pmpcfg_q[8 +: riscv::XLEN/8];
riscv::CSR_PMPCFG3: if (riscv::XLEN == 32) csr_rdata = pmpcfg_q[15:12]; else read_access_exception = 1'b1;
riscv::CSR_PMPCFG1:
if (riscv::XLEN == 32) csr_rdata = pmpcfg_q[7:4];
else read_access_exception = 1'b1;
riscv::CSR_PMPCFG2: csr_rdata = pmpcfg_q[8+:riscv::XLEN/8];
riscv::CSR_PMPCFG3:
if (riscv::XLEN == 32) csr_rdata = pmpcfg_q[15:12];
else read_access_exception = 1'b1;
// PMPADDR
riscv::CSR_PMPADDR0,
riscv::CSR_PMPADDR1,
@ -471,10 +495,8 @@ module csr_regfile import ariane_pkg::*; #(
// -> last bit of pmpaddr must be set 0/1 based on the mode:
// NA4, NAPOT: 1
// TOR, OFF: 0
if (pmpcfg_q[index].addr_mode[1] == 1'b1)
csr_rdata = pmpaddr_q[index][riscv::PLEN-3:0];
else
csr_rdata = {pmpaddr_q[index][riscv::PLEN-3:1], 1'b0};
if (pmpcfg_q[index].addr_mode[1] == 1'b1) csr_rdata = pmpaddr_q[index][riscv::PLEN-3:0];
else csr_rdata = {pmpaddr_q[index][riscv::PLEN-3:1], 1'b0};
end
default: read_access_exception = 1'b1;
endcase
@ -536,7 +558,7 @@ module csr_regfile import ariane_pkg::*; #(
// boot_addr_i will be assigned a constant
// on the top-level.
if (mtvec_rst_load_q) begin
mtvec_d = {{riscv::XLEN-riscv::VLEN{1'b0}}, boot_addr_i} + 'h40;
mtvec_d = {{riscv::XLEN - riscv::VLEN{1'b0}}, boot_addr_i} + 'h40;
end else begin
mtvec_d = mtvec_q;
end
@ -573,7 +595,7 @@ module csr_regfile import ariane_pkg::*; #(
unique case (csr_addr.address)
// Floating-Point
riscv::CSR_FFLAGS: begin
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
dirty_fp_state_csr = 1'b1;
fcsr_d.fflags = csr_wdata[4:0];
// this instruction has side-effects
@ -583,7 +605,7 @@ module csr_regfile import ariane_pkg::*; #(
end
end
riscv::CSR_FRM: begin
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
dirty_fp_state_csr = 1'b1;
fcsr_d.frm = csr_wdata[2:0];
// this instruction has side-effects
@ -593,7 +615,7 @@ module csr_regfile import ariane_pkg::*; #(
end
end
riscv::CSR_FCSR: begin
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
dirty_fp_state_csr = 1'b1;
fcsr_d[7:0] = csr_wdata[7:0]; // ignore writes to reserved space
// this instruction has side-effects
@ -603,7 +625,7 @@ module csr_regfile import ariane_pkg::*; #(
end
end
riscv::CSR_FTRAN: begin
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
dirty_fp_state_csr = 1'b1;
fcsr_d.fprec = csr_wdata[6:0]; // ignore writes to reserved space
// this instruction has side-effects
@ -626,10 +648,10 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_DSCRATCH0: dscratch0_d = csr_wdata;
riscv::CSR_DSCRATCH1: dscratch1_d = csr_wdata;
// trigger module CSRs
riscv::CSR_TSELECT:; // not implemented
riscv::CSR_TDATA1:; // not implemented
riscv::CSR_TDATA2:; // not implemented
riscv::CSR_TDATA3:; // not implemented
riscv::CSR_TSELECT: ; // not implemented
riscv::CSR_TDATA1: ; // not implemented
riscv::CSR_TDATA2: ; // not implemented
riscv::CSR_TDATA3: ; // not implemented
// sstatus is a subset of mstatus - mask it accordingly
riscv::CSR_SSTATUS: begin
mask = ariane_pkg::SMODE_STATUS_WRITE_MASK[riscv::XLEN-1:0];
@ -659,7 +681,7 @@ module csr_regfile import ariane_pkg::*; #(
end
riscv::CSR_STVEC: stvec_d = {csr_wdata[riscv::XLEN-1:2], 1'b0, csr_wdata[0]};
riscv::CSR_SCOUNTEREN: scounteren_d = {{riscv::XLEN-32{1'b0}}, csr_wdata[31:0]};
riscv::CSR_SCOUNTEREN: scounteren_d = {{riscv::XLEN - 32{1'b0}}, csr_wdata[31:0]};
riscv::CSR_SSCRATCH: sscratch_d = csr_wdata;
riscv::CSR_SEPC: sepc_d = {csr_wdata[riscv::XLEN-1:1], 1'b0};
riscv::CSR_SCAUSE: scause_d = csr_wdata;
@ -667,15 +689,15 @@ module csr_regfile import ariane_pkg::*; #(
// supervisor address translation and protection
riscv::CSR_SATP: begin
// intercept SATP writes if in S-Mode and TVM is enabled
if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm)
update_access_exception = 1'b1;
if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm) update_access_exception = 1'b1;
else begin
satp = riscv::satp_t'(csr_wdata);
// only make ASID_LEN - 1 bit stick, that way software can figure out how many ASID bits are supported
satp.asid = satp.asid & {{(riscv::ASIDW-AsidWidth){1'b0}}, {AsidWidth{1'b1}}};
satp.asid = satp.asid & {{(riscv::ASIDW - AsidWidth) {1'b0}}, {AsidWidth{1'b1}}};
// only update if we actually support this mode
if (riscv::vm_mode_t'(satp.mode) == riscv::ModeOff ||
riscv::vm_mode_t'(satp.mode) == riscv::MODE_SV) satp_d = satp;
riscv::vm_mode_t'(satp.mode) == riscv::MODE_SV)
satp_d = satp;
end
// changing the mode can have side-effects on address translation (e.g.: other instructions), re-fetch
// the next instruction by executing a flush
@ -683,7 +705,7 @@ module csr_regfile import ariane_pkg::*; #(
end
riscv::CSR_MSTATUS: begin
mstatus_d = {{64-riscv::XLEN{1'b0}}, csr_wdata};
mstatus_d = {{64 - riscv::XLEN{1'b0}}, csr_wdata};
mstatus_d.xs = riscv::Off;
if (!CVA6Cfg.FpPresent) begin
mstatus_d.fs = riscv::Off;
@ -700,7 +722,7 @@ module csr_regfile import ariane_pkg::*; #(
end
riscv::CSR_MSTATUSH: if (riscv::XLEN != 32) update_access_exception = 1'b1;
// MISA is WARL (Write Any Value, Reads Legal Value)
riscv::CSR_MISA:;
riscv::CSR_MISA: ;
// machine exception delegation register
// 0 - 15 exceptions supported
riscv::CSR_MEDELEG: begin
@ -730,7 +752,7 @@ module csr_regfile import ariane_pkg::*; #(
// alignment constraint of 64 * 4 bytes
if (csr_wdata[0]) mtvec_d = {csr_wdata[riscv::XLEN-1:8], 7'b0, csr_wdata[0]};
end
riscv::CSR_MCOUNTEREN: mcounteren_d = {{riscv::XLEN-32{1'b0}}, csr_wdata[31:0]};
riscv::CSR_MCOUNTEREN: mcounteren_d = {{riscv::XLEN - 32{1'b0}}, csr_wdata[31:0]};
riscv::CSR_MSCRATCH: mscratch_d = csr_wdata;
riscv::CSR_MEPC: mepc_d = {csr_wdata[riscv::XLEN-1:1], 1'b0};
@ -740,12 +762,17 @@ module csr_regfile import ariane_pkg::*; #(
mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP;
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
end
riscv::CSR_MCOUNTINHIBIT: mcountinhibit_d = {csr_wdata[MHPMCounterNum+2:2], 1'b0, csr_wdata[0]};
riscv::CSR_MCOUNTINHIBIT:
mcountinhibit_d = {csr_wdata[MHPMCounterNum+2:2], 1'b0, csr_wdata[0]};
// performance counters
riscv::CSR_MCYCLE: cycle_d[riscv::XLEN-1:0] = csr_wdata;
riscv::CSR_MCYCLEH: if (riscv::XLEN == 32) cycle_d[63:32] = csr_wdata; else update_access_exception = 1'b1;
riscv::CSR_MCYCLEH:
if (riscv::XLEN == 32) cycle_d[63:32] = csr_wdata;
else update_access_exception = 1'b1;
riscv::CSR_MINSTRET: instret_d[riscv::XLEN-1:0] = csr_wdata;
riscv::CSR_MINSTRETH: if (riscv::XLEN == 32) instret_d[63:32] = csr_wdata; else update_access_exception = 1'b1;
riscv::CSR_MINSTRETH:
if (riscv::XLEN == 32) instret_d[63:32] = csr_wdata;
else update_access_exception = 1'b1;
//Event Selector
riscv::CSR_MHPM_EVENT_3,
riscv::CSR_MHPM_EVENT_4,
@ -775,7 +802,10 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MHPM_EVENT_28,
riscv::CSR_MHPM_EVENT_29,
riscv::CSR_MHPM_EVENT_30,
riscv::CSR_MHPM_EVENT_31 : begin perf_we_o = 1'b1; perf_data_o = csr_wdata;end
riscv::CSR_MHPM_EVENT_31 : begin
perf_we_o = 1'b1;
perf_data_o = csr_wdata;
end
riscv::CSR_MHPM_COUNTER_3,
riscv::CSR_MHPM_COUNTER_4,
@ -805,7 +835,10 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MHPM_COUNTER_28,
riscv::CSR_MHPM_COUNTER_29,
riscv::CSR_MHPM_COUNTER_30,
riscv::CSR_MHPM_COUNTER_31 : begin perf_we_o = 1'b1; perf_data_o = csr_wdata;end
riscv::CSR_MHPM_COUNTER_31 : begin
perf_we_o = 1'b1;
perf_data_o = csr_wdata;
end
riscv::CSR_MHPM_COUNTER_3H,
riscv::CSR_MHPM_COUNTER_4H,
@ -835,13 +868,17 @@ module csr_regfile import ariane_pkg::*; #(
riscv::CSR_MHPM_COUNTER_28H,
riscv::CSR_MHPM_COUNTER_29H,
riscv::CSR_MHPM_COUNTER_30H,
riscv::CSR_MHPM_COUNTER_31H : begin perf_we_o = 1'b1; if (riscv::XLEN == 32) perf_data_o = csr_wdata;else update_access_exception = 1'b1;end
riscv::CSR_MHPM_COUNTER_31H : begin
perf_we_o = 1'b1;
if (riscv::XLEN == 32) perf_data_o = csr_wdata;
else update_access_exception = 1'b1;
end
riscv::CSR_DCACHE: dcache_d = {{riscv::XLEN-1{1'b0}}, csr_wdata[0]}; // enable bit
riscv::CSR_ICACHE: icache_d = {{riscv::XLEN-1{1'b0}}, csr_wdata[0]}; // enable bit
riscv::CSR_DCACHE: dcache_d = {{riscv::XLEN - 1{1'b0}}, csr_wdata[0]}; // enable bit
riscv::CSR_ICACHE: icache_d = {{riscv::XLEN - 1{1'b0}}, csr_wdata[0]}; // enable bit
riscv::CSR_ACC_CONS: begin
if (CVA6Cfg.EnableAccelerator) begin
acc_cons_d = {{riscv::XLEN-1{1'b0}}, csr_wdata[0]}; // enable bit
acc_cons_d = {{riscv::XLEN - 1{1'b0}}, csr_wdata[0]}; // enable bit
end else begin
update_access_exception = 1'b1;
end
@ -850,18 +887,24 @@ module csr_regfile import ariane_pkg::*; #(
// 1. refuse to update any locked entry
// 2. also refuse to update the entry below a locked TOR entry
// Note that writes to pmpcfg below a locked TOR entry are valid
riscv::CSR_PMPCFG0: for (int i = 0; i < (riscv::XLEN/8); i++) if (!pmpcfg_q[i].locked) pmpcfg_d[i] = csr_wdata[i*8+:8];
riscv::CSR_PMPCFG0:
for (int i = 0; i < (riscv::XLEN / 8); i++)
if (!pmpcfg_q[i].locked) pmpcfg_d[i] = csr_wdata[i*8+:8];
riscv::CSR_PMPCFG1: begin
if (riscv::XLEN == 32) begin
for (int i = 0; i < 4; i++) if (!pmpcfg_q[i+4].locked) pmpcfg_d[i+4] = csr_wdata[i*8+:8];
for (int i = 0; i < 4; i++)
if (!pmpcfg_q[i+4].locked) pmpcfg_d[i+4] = csr_wdata[i*8+:8];
end else begin
update_access_exception = 1'b1;
end
end
riscv::CSR_PMPCFG2: for (int i = 0; i < (riscv::XLEN/8); i++) if (!pmpcfg_q[i+8].locked) pmpcfg_d[i+8] = csr_wdata[i*8+:8];
riscv::CSR_PMPCFG2:
for (int i = 0; i < (riscv::XLEN / 8); i++)
if (!pmpcfg_q[i+8].locked) pmpcfg_d[i+8] = csr_wdata[i*8+:8];
riscv::CSR_PMPCFG3: begin
if (riscv::XLEN == 32) begin
for (int i = 0; i < 4; i++) if (!pmpcfg_q[i+12].locked) pmpcfg_d[i+12] = csr_wdata[i*8+:8];
for (int i = 0; i < 4; i++)
if (!pmpcfg_q[i+12].locked) pmpcfg_d[i+12] = csr_wdata[i*8+:8];
end else begin
update_access_exception = 1'b1;
end
@ -950,8 +993,11 @@ module csr_regfile import ariane_pkg::*; #(
// a m-mode trap might be delegated if we are taking it in S mode
// first figure out if this was an exception or an interrupt e.g.: look at bit (XLEN-1)
// the cause register can only be $clog2(riscv::XLEN) bits long (as we only support XLEN exceptions)
if ((ex_i.cause[riscv::XLEN-1] && mideleg_q[ex_i.cause[$clog2(riscv::XLEN)-1:0]]) ||
(~ex_i.cause[riscv::XLEN-1] && medeleg_q[ex_i.cause[$clog2(riscv::XLEN)-1:0]])) begin
if ((ex_i.cause[riscv::XLEN-1] && mideleg_q[ex_i.cause[$clog2(
riscv::XLEN
)-1:0]]) || (~ex_i.cause[riscv::XLEN-1] && medeleg_q[ex_i.cause[$clog2(
riscv::XLEN
)-1:0]])) begin
// traps never transition from a more-privileged mode to a less privileged mode
// so if we are already in M mode, stay there
trap_to_priv_lvl = (priv_lvl_o == riscv::PRIV_LVL_M) ? riscv::PRIV_LVL_M : riscv::PRIV_LVL_S;
@ -967,7 +1013,7 @@ module csr_regfile import ariane_pkg::*; #(
// set cause
scause_d = ex_i.cause;
// set epc
sepc_d = {{riscv::XLEN-riscv::VLEN{pc_i[riscv::VLEN-1]}},pc_i};
sepc_d = {{riscv::XLEN - riscv::VLEN{pc_i[riscv::VLEN-1]}}, pc_i};
// set mtval or stval
stval_d = (ariane_pkg::ZERO_TVAL
&& (ex_i.cause inside {
@ -986,7 +1032,7 @@ module csr_regfile import ariane_pkg::*; #(
mstatus_d.mpp = priv_lvl_q;
mcause_d = ex_i.cause;
// set epc
mepc_d = {{riscv::XLEN-riscv::VLEN{pc_i[riscv::VLEN-1]}},pc_i};
mepc_d = {{riscv::XLEN - riscv::VLEN{pc_i[riscv::VLEN-1]}}, pc_i};
// set mtval or stval
mtval_d = (ariane_pkg::ZERO_TVAL
&& (ex_i.cause inside {
@ -1032,10 +1078,10 @@ module csr_regfile import ariane_pkg::*; #(
debug_mode_d = dcsr_q.ebreaku;
set_debug_pc_o = dcsr_q.ebreaku;
end
default:;
default: ;
endcase
// save PC of next this instruction e.g.: the next one to be executed
dpc_d = {{riscv::XLEN-riscv::VLEN{pc_i[riscv::VLEN-1]}},pc_i};
dpc_d = {{riscv::XLEN - riscv::VLEN{pc_i[riscv::VLEN-1]}}, pc_i};
dcsr_d.cause = ariane_pkg::CauseBreakpoint;
end
@ -1043,7 +1089,7 @@ module csr_regfile import ariane_pkg::*; #(
if (ex_i.valid && ex_i.cause == riscv::DEBUG_REQUEST) begin
dcsr_d.prv = priv_lvl_o;
// save the PC
dpc_d = {{riscv::XLEN-riscv::VLEN{pc_i[riscv::VLEN-1]}},pc_i};
dpc_d = {{riscv::XLEN - riscv::VLEN{pc_i[riscv::VLEN-1]}}, pc_i};
// enter debug mode
debug_mode_d = 1'b1;
// jump to the base address
@ -1058,16 +1104,22 @@ module csr_regfile import ariane_pkg::*; #(
// valid CTRL flow change
if (commit_instr_i[0].fu == CTRL_FLOW) begin
// we saved the correct target address during execute
dpc_d = {{riscv::XLEN-riscv::VLEN{commit_instr_i[0].bp.predict_address[riscv::VLEN-1]}}, commit_instr_i[0].bp.predict_address};
dpc_d = {
{riscv::XLEN - riscv::VLEN{commit_instr_i[0].bp.predict_address[riscv::VLEN-1]}},
commit_instr_i[0].bp.predict_address
};
// exception valid
end else if (ex_i.valid) begin
dpc_d = {{riscv::XLEN-riscv::VLEN{1'b0}},trap_vector_base_o};
dpc_d = {{riscv::XLEN - riscv::VLEN{1'b0}}, trap_vector_base_o};
// return from environment
end else if (eret_o) begin
dpc_d = {{riscv::XLEN-riscv::VLEN{1'b0}},epc_o};
dpc_d = {{riscv::XLEN - riscv::VLEN{1'b0}}, epc_o};
// consecutive PC
end else begin
dpc_d = {{riscv::XLEN-riscv::VLEN{commit_instr_i[0].pc[riscv::VLEN-1]}}, commit_instr_i[0].pc + (commit_instr_i[0].is_compressed ? 'h2 : 'h4)};
dpc_d = {
{riscv::XLEN - riscv::VLEN{commit_instr_i[0].pc[riscv::VLEN-1]}},
commit_instr_i[0].pc + (commit_instr_i[0].is_compressed ? 'h2 : 'h4)
};
end
debug_mode_d = 1'b1;
set_debug_pc_o = 1'b1;
@ -1207,11 +1259,12 @@ module csr_regfile import ariane_pkg::*; #(
end
// check counter-enabled counter CSR accesses
// counter address range is C00 to C1F
if (csr_addr_i inside {[riscv::CSR_CYCLE:riscv::CSR_HPM_COUNTER_31]}) begin
if (csr_addr_i inside {[riscv::CSR_CYCLE : riscv::CSR_HPM_COUNTER_31]}) begin
unique case (priv_lvl_o)
riscv::PRIV_LVL_M: privilege_violation = 1'b0;
riscv::PRIV_LVL_S: privilege_violation = ~mcounteren_q[csr_addr_i[4:0]];
riscv::PRIV_LVL_U: privilege_violation = ~mcounteren_q[csr_addr_i[4:0]] & ~scounteren_q[csr_addr_i[4:0]];
riscv::PRIV_LVL_U:
privilege_violation = ~mcounteren_q[csr_addr_i[4:0]] & ~scounteren_q[csr_addr_i[4:0]];
endcase
end
end
@ -1220,11 +1273,7 @@ module csr_regfile import ariane_pkg::*; #(
// CSR Exception Control
// ----------------------
always_comb begin : exception_ctrl
csr_exception_o = {
{riscv::XLEN{1'b0}},
{riscv::XLEN{1'b0}},
1'b0
};
csr_exception_o = {{riscv::XLEN{1'b0}}, {riscv::XLEN{1'b0}}, 1'b0};
// ----------------------------------
// Illegal Access (decode exception)
// ----------------------------------
@ -1305,13 +1354,14 @@ module csr_regfile import ariane_pkg::*; #(
csr_rdata_o = csr_rdata;
unique case (csr_addr.address)
riscv::CSR_MIP: csr_rdata_o = csr_rdata | ({{riscv::XLEN-1{1'b0}}, irq_i[1]} << riscv::IRQ_S_EXT);
riscv::CSR_MIP:
csr_rdata_o = csr_rdata | ({{riscv::XLEN - 1{1'b0}}, irq_i[1]} << riscv::IRQ_S_EXT);
// in supervisor mode we also need to check whether we delegated this bit
riscv::CSR_SIP: begin
csr_rdata_o = csr_rdata
| ({{riscv::XLEN-1{1'b0}}, (irq_i[1] & mideleg_q[riscv::IRQ_S_EXT])} << riscv::IRQ_S_EXT);
end
default:;
default: ;
endcase
end
@ -1347,7 +1397,7 @@ module csr_regfile import ariane_pkg::*; #(
assign mprv = (debug_mode_q && !dcsr_q.mprven) ? 1'b0 : mstatus_q.mprv;
assign debug_mode_o = debug_mode_q;
assign single_step_o = dcsr_q.step;
assign mcountinhibit_o = {{29-MHPMCounterNum{1'b0}}, mcountinhibit_q};
assign mcountinhibit_o = {{29 - MHPMCounterNum{1'b0}}, mcountinhibit_q};
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
@ -1377,10 +1427,10 @@ module csr_regfile import ariane_pkg::*; #(
mcounteren_q <= {riscv::XLEN{1'b0}};
mscratch_q <= {riscv::XLEN{1'b0}};
mtval_q <= {riscv::XLEN{1'b0}};
dcache_q <= {{riscv::XLEN-1{1'b0}}, 1'b1};
icache_q <= {{riscv::XLEN-1{1'b0}}, 1'b1};
dcache_q <= {{riscv::XLEN - 1{1'b0}}, 1'b1};
icache_q <= {{riscv::XLEN - 1{1'b0}}, 1'b1};
mcountinhibit_q <= '0;
acc_cons_q <= {{riscv::XLEN-1{1'b0}}, CVA6Cfg.EnableAccelerator};
acc_cons_q <= {{riscv::XLEN - 1{1'b0}}, CVA6Cfg.EnableAccelerator};
// supervisor mode registers
sepc_q <= {riscv::XLEN{1'b0}};
scause_q <= {riscv::XLEN{1'b0}};
@ -1442,8 +1492,8 @@ module csr_regfile import ariane_pkg::*; #(
// wait for interrupt
wfi_q <= wfi_d;
// pmp
for(int i = 0; i < 16; i++) begin
if(i < CVA6Cfg.NrPMPEntries) begin
for (int i = 0; i < 16; i++) begin
if (i < CVA6Cfg.NrPMPEntries) begin
// We only support >=8-byte granularity, NA4 is disabled
if(pmpcfg_d[i].addr_mode != riscv::NA4 && !(pmpcfg_d[i].access_type.r == '0 && pmpcfg_d[i].access_type.w == '1)) begin
pmpcfg_q[i] <= pmpcfg_d[i];
@ -1464,8 +1514,10 @@ module csr_regfile import ariane_pkg::*; #(
//-------------
//pragma translate_off
// check that eret and ex are never valid together
assert property (
@(posedge clk_i) disable iff (!rst_ni !== '0) !(eret_o && ex_i.valid))
else begin $error("eret and exception should never be valid at the same time"); $stop(); end
assert property (@(posedge clk_i) disable iff (!rst_ni !== '0) !(eret_o && ex_i.valid))
else begin
$error("eret and exception should never be valid at the same time");
$stop();
end
//pragma translate_on
endmodule

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,9 @@
// Module stub for the cva6_accel_first_pass_decoder. Replace this with your accelerator's
// first pass decoder.
module cva6_accel_first_pass_decoder import ariane_pkg::*; (
module cva6_accel_first_pass_decoder
import ariane_pkg::*;
(
input logic [31:0] instruction_i, // instruction from IF
input riscv::xs_t fs_i, // floating point extension status
input riscv::xs_t vs_i, // vector extension status

View file

@ -9,8 +9,10 @@
// Example coprocessor adds rs1,rs2(,rs3) together and gives back the result to the CPU via the CoreV-X-Interface.
// Coprocessor delays the sending of the result depending on result least significant bits.
module cvxif_example_coprocessor import cvxif_pkg::*;
import cvxif_instr_pkg::*;(
module cvxif_example_coprocessor
import cvxif_pkg::*;
import cvxif_instr_pkg::*;
(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input cvxif_req_t cvxif_req_i,
@ -70,12 +72,12 @@ module cvxif_example_coprocessor import cvxif_pkg::*;
assign x_compressed_resp_o.accept = '0;
instr_decoder #(
.NbInstr ( cvxif_instr_pkg::NbInstr ),
.CoproInstr ( cvxif_instr_pkg::CoproInstr )
.NbInstr (cvxif_instr_pkg::NbInstr),
.CoproInstr(cvxif_instr_pkg::CoproInstr)
) instr_decoder_i (
.clk_i ( clk_i ),
.x_issue_req_i ( x_issue_req_i ),
.x_issue_resp_o ( x_issue_resp_o )
.clk_i (clk_i),
.x_issue_req_i (x_issue_req_i),
.x_issue_resp_o(x_issue_resp_o)
);
typedef struct packed {
@ -91,7 +93,7 @@ module cvxif_example_coprocessor import cvxif_pkg::*;
assign instr_push = x_issue_resp_o.accept ? 1 : 0 ;
assign instr_push = x_issue_resp_o.accept ? 1 : 0;
assign instr_pop = (x_commit_i.x_commit_kill && x_commit_valid_i) || x_result_valid_o;
assign x_issue_ready_q = ~fifo_full; // if something is in the fifo, the instruction is being processed
// so we can't receive anything else
@ -99,7 +101,7 @@ module cvxif_example_coprocessor import cvxif_pkg::*;
assign req_i.resp = x_issue_resp_o;
always_ff @(posedge clk_i or negedge rst_ni) begin : regs
if(!rst_ni) begin
if (!rst_ni) begin
x_issue_ready_o <= 1;
end else begin
x_issue_ready_o <= x_issue_ready_q;
@ -107,41 +109,41 @@ module cvxif_example_coprocessor import cvxif_pkg::*;
end
fifo_v3 #(
.FALL_THROUGH ( 1 ), //data_o ready and pop in the same cycle
.DATA_WIDTH ( 64 ),
.DEPTH ( 8 ),
.dtype ( x_issue_t )
.FALL_THROUGH(1), //data_o ready and pop in the same cycle
.DATA_WIDTH (64),
.DEPTH (8),
.dtype (x_issue_t)
) fifo_commit_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.full_o ( fifo_full ),
.empty_o ( fifo_empty ),
.usage_o ( ),
.data_i ( req_i ),
.push_i ( instr_push ),
.data_o ( req_o ),
.pop_i ( instr_pop )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i(1'b0),
.full_o (fifo_full),
.empty_o (fifo_empty),
.usage_o (),
.data_i (req_i),
.push_i (instr_push),
.data_o (req_o),
.pop_i (instr_pop)
);
logic [3:0] c;
counter #(
.WIDTH(4)
) counter_i(
.clk_i ( clk_i),
.rst_ni ( rst_ni),
.clear_i ( ~x_commit_i.x_commit_kill && x_commit_valid_i ),
.en_i ( 1'b1 ),
.load_i ( ),
.down_i ( ),
.d_i ( ),
.q_o ( c ),
.overflow_o ( )
) counter_i (
.clk_i (clk_i),
.rst_ni (rst_ni),
.clear_i (~x_commit_i.x_commit_kill && x_commit_valid_i),
.en_i (1'b1),
.load_i (),
.down_i (),
.d_i (),
.q_o (c),
.overflow_o()
);
always_comb begin
x_result_o.data = req_o.req.rs[0] + req_o.req.rs[1] + ( X_NUM_RS == 3 ? req_o.req.rs[2] : 0);
x_result_o.data = req_o.req.rs[0] + req_o.req.rs[1] + (X_NUM_RS == 3 ? req_o.req.rs[2] : 0);
x_result_valid_o = (c == x_result_o.data[3:0]) && ~fifo_empty ? 1 : 0;
x_result_o.id = req_o.req.id;
x_result_o.rd = req_o.req.instr[11:7];
@ -150,4 +152,4 @@ module cvxif_example_coprocessor import cvxif_pkg::*;
x_result_o.exccode = 0;
end
endmodule
endmodule

View file

@ -19,8 +19,8 @@ package cvxif_instr_pkg;
parameter int unsigned NbInstr = 2;
parameter copro_issue_resp_t CoproInstr[NbInstr] = '{
'{
instr: 32'b 00000_00_00000_00000_0_00_00000_0101011, // custom1 opcode
mask: 32'b 00000_00_00000_00000_0_00_00000_1111111,
instr: 32'b00000_00_00000_00000_0_00_00000_0101011, // custom1 opcode
mask: 32'b00000_00_00000_00000_0_00_00000_1111111,
resp : '{
accept : 1'b1,
writeback : 1'b0,
@ -31,8 +31,8 @@ package cvxif_instr_pkg;
}
},
'{
instr: 32'b 00000_00_00000_00000_0_00_00000_1011011, // custom2 opcode
mask: 32'b 00000_00_00000_00000_0_00_00000_1111111,
instr: 32'b00000_00_00000_00000_0_00_00000_1011011, // custom2 opcode
mask: 32'b00000_00_00000_00000_0_00_00000_1111111,
resp : '{
accept : 1'b1,
writeback : 1'b1,

View file

@ -7,11 +7,12 @@
//
// Original Author: Guillaume Chauvon (guillaume.chauvon@thalesgroup.com)
module instr_decoder import cvxif_pkg::*; #(
module instr_decoder
import cvxif_pkg::*;
#(
parameter int NbInstr = 1,
parameter cvxif_instr_pkg::copro_issue_resp_t CoproInstr[NbInstr] = {0}
)
(
) (
input logic clk_i,
input x_issue_req_t x_issue_req_i,
output x_issue_resp_t x_issue_resp_o
@ -20,8 +21,7 @@ module instr_decoder import cvxif_pkg::*; #(
logic [NbInstr-1:0] sel;
for (genvar i = 0; i < NbInstr; i++) begin : gen_predecoder_selector
assign sel[i] =
((CoproInstr[i].mask & x_issue_req_i.instr) == CoproInstr[i].instr);
assign sel[i] = ((CoproInstr[i].mask & x_issue_req_i.instr) == CoproInstr[i].instr);
end
always_comb begin
@ -43,6 +43,7 @@ module instr_decoder import cvxif_pkg::*; #(
end
end
assert property( @(posedge clk_i) $onehot0(sel)) else $warning("This offloaded instruction is valid for multiple coprocessor instructions !");
assert property (@(posedge clk_i) $onehot0(sel))
else $warning("This offloaded instruction is valid for multiple coprocessor instructions !");
endmodule

View file

@ -10,7 +10,9 @@
// Functional Unit for the logic of the CoreV-X-Interface
module cvxif_fu import ariane_pkg::*; #(
module cvxif_fu
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -20,7 +22,7 @@ module cvxif_fu import ariane_pkg::*; #(
//from issue
input logic x_valid_i,
output logic x_ready_o,
input logic [31:0] x_off_instr_i,
input logic [ 31:0] x_off_instr_i,
//to writeback
output logic [TRANS_ID_BITS-1:0] x_trans_id_o,
output exception_t x_exception_o,

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,9 @@
// Description: Instantiation of all functional units residing in the execute stage
module ex_stage import ariane_pkg::*; #(
module ex_stage
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned ASID_WIDTH = 1
) (
@ -101,7 +103,7 @@ module ex_stage import ariane_pkg::*; #(
input logic sum_i,
input logic mxr_i,
input logic [riscv::PPNW-1:0] satp_ppn_i,
input logic [ASID_WIDTH-1:0] asid_i,
input logic [ ASID_WIDTH-1:0] asid_i,
// icache translation requests
input icache_arsp_t icache_areq_i,
output icache_areq_t icache_areq_o,
@ -118,13 +120,13 @@ module ex_stage import ariane_pkg::*; #(
output logic dtlb_miss_o,
// PMPs
input riscv::pmpcfg_t [15:0] pmpcfg_i,
input logic[15:0][riscv::PLEN-3:0] pmpaddr_i,
input logic [15:0][riscv::PLEN-3:0] pmpaddr_i,
// RVFI
output [riscv::VLEN-1:0] lsu_addr_o,
output [riscv::PLEN-1:0] mem_paddr_o,
output [(riscv::XLEN/8)-1:0] lsu_rmask_o,
output [(riscv::XLEN/8)-1:0] lsu_wmask_o,
output [ riscv::VLEN-1:0] lsu_addr_o,
output [ riscv::PLEN-1:0] mem_paddr_o,
output [ (riscv::XLEN/8)-1:0] lsu_rmask_o,
output [ (riscv::XLEN/8)-1:0] lsu_wmask_o,
output [ariane_pkg::TRANS_ID_BITS-1:0] lsu_addr_trans_id_o
);
@ -169,20 +171,20 @@ module ex_stage import ariane_pkg::*; #(
assign alu_data = (alu_valid_i | branch_valid_i) ? fu_data_i : '0;
alu #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) alu_i (
.clk_i,
.rst_ni,
.fu_data_i ( alu_data ),
.result_o ( alu_result ),
.alu_branch_res_o ( alu_branch_res )
.fu_data_i (alu_data),
.result_o (alu_result),
.alu_branch_res_o(alu_branch_res)
);
// 2. Branch Unit (combinatorial)
// we don't silence the branch unit as this is already critical and we do
// not want to add another layer of logic
branch_unit #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) branch_unit_i (
.clk_i,
.rst_ni,
@ -193,25 +195,25 @@ module ex_stage import ariane_pkg::*; #(
// any functional unit is valid, check that there is no accidental mis-predict
.fu_valid_i ( alu_valid_i || lsu_valid_i || csr_valid_i || mult_valid_i || fpu_valid_i || acc_valid_i ) ,
.branch_valid_i,
.branch_comp_res_i ( alu_branch_res ),
.branch_result_o ( branch_result ),
.branch_comp_res_i(alu_branch_res),
.branch_result_o(branch_result),
.branch_predict_i,
.resolved_branch_o,
.resolve_branch_o,
.branch_exception_o ( flu_exception_o )
.branch_exception_o(flu_exception_o)
);
// 3. CSR (sequential)
csr_buffer #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) csr_buffer_i (
.clk_i,
.rst_ni,
.flush_i,
.fu_data_i,
.csr_valid_i,
.csr_ready_o ( csr_ready ),
.csr_result_o ( csr_result ),
.csr_ready_o (csr_ready),
.csr_result_o(csr_result),
.csr_commit_i,
.csr_addr_o
);
@ -221,7 +223,7 @@ module ex_stage import ariane_pkg::*; #(
// result MUX
always_comb begin
// Branch result as default case
flu_result_o = {{riscv::XLEN-riscv::VLEN{1'b0}}, branch_result};
flu_result_o = {{riscv::XLEN - riscv::VLEN{1'b0}}, branch_result};
flu_trans_id_o = fu_data_i.trans_id;
// ALU result
if (alu_valid_i) begin
@ -246,17 +248,17 @@ module ex_stage import ariane_pkg::*; #(
assign mult_data = mult_valid_i ? fu_data_i : '0;
mult #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_mult (
.clk_i,
.rst_ni,
.flush_i,
.mult_valid_i,
.fu_data_i ( mult_data ),
.result_o ( mult_result ),
.mult_valid_o ( mult_valid ),
.mult_ready_o ( mult_ready ),
.mult_trans_id_o ( mult_trans_id )
.fu_data_i (mult_data),
.result_o (mult_result),
.mult_valid_o (mult_valid),
.mult_ready_o (mult_ready),
.mult_trans_id_o(mult_trans_id)
);
// ----------------
@ -268,20 +270,20 @@ module ex_stage import ariane_pkg::*; #(
assign fpu_data = fpu_valid_i ? fu_data_i : '0;
fpu_wrap #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) fpu_i (
.clk_i,
.rst_ni,
.flush_i,
.fpu_valid_i,
.fpu_ready_o,
.fu_data_i ( fpu_data ),
.fu_data_i(fpu_data),
.fpu_fmt_i,
.fpu_rm_i,
.fpu_frm_i,
.fpu_prec_i,
.fpu_trans_id_o,
.result_o ( fpu_result_o ),
.result_o (fpu_result_o),
.fpu_valid_o,
.fpu_exception_o
);
@ -302,15 +304,15 @@ module ex_stage import ariane_pkg::*; #(
assign lsu_data = lsu_valid_i ? fu_data_i : '0;
load_store_unit #(
.CVA6Cfg ( CVA6Cfg ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.ASID_WIDTH(ASID_WIDTH)
) lsu_i (
.clk_i,
.rst_ni,
.flush_i,
.stall_st_pending_i,
.no_st_pending_o,
.fu_data_i ( lsu_data ),
.fu_data_i (lsu_data),
.lsu_ready_o,
.lsu_valid_i,
.load_trans_id_o,
@ -321,8 +323,8 @@ module ex_stage import ariane_pkg::*; #(
.store_result_o,
.store_valid_o,
.store_exception_o,
.commit_i ( lsu_commit_i ),
.commit_ready_o ( lsu_commit_ready_o ),
.commit_i (lsu_commit_i),
.commit_ready_o (lsu_commit_ready_o),
.commit_tran_id_i,
.enable_translation_i,
.en_ld_st_translation_i,
@ -335,7 +337,7 @@ module ex_stage import ariane_pkg::*; #(
.satp_ppn_i,
.asid_i,
.asid_to_be_flushed_i (asid_to_be_flushed),
.vaddr_to_be_flushed_i (vaddr_to_be_flushed),
.vaddr_to_be_flushed_i(vaddr_to_be_flushed),
.flush_tlb_i,
.itlb_miss_o,
.dtlb_miss_o,
@ -359,12 +361,12 @@ module ex_stage import ariane_pkg::*; #(
fu_data_t cvxif_data;
assign cvxif_data = x_valid_i ? fu_data_i : '0;
cvxif_fu #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) cvxif_fu_i (
.clk_i,
.rst_ni,
.fu_data_i,
.priv_lvl_i (ld_st_priv_lvl_i),
.priv_lvl_i(ld_st_priv_lvl_i),
.x_valid_i,
.x_ready_o,
.x_off_instr_i,

View file

@ -13,7 +13,9 @@
// Description: Wrapper for the floating-point unit
module fpu_wrap import ariane_pkg::*; #(
module fpu_wrap
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -23,19 +25,23 @@ module fpu_wrap import ariane_pkg::*; #(
output logic fpu_ready_o,
input fu_data_t fu_data_i,
input logic [1:0] fpu_fmt_i,
input logic [2:0] fpu_rm_i,
input logic [2:0] fpu_frm_i,
input logic [6:0] fpu_prec_i,
input logic [ 1:0] fpu_fmt_i,
input logic [ 2:0] fpu_rm_i,
input logic [ 2:0] fpu_frm_i,
input logic [ 6:0] fpu_prec_i,
output logic [TRANS_ID_BITS-1:0] fpu_trans_id_o,
output logic [CVA6Cfg.FLen-1:0] result_o,
output logic [ CVA6Cfg.FLen-1:0] result_o,
output logic fpu_valid_o,
output exception_t fpu_exception_o
);
// this is a workaround
// otherwise compilation might issue an error if FLEN=0
enum logic {READY, STALL} state_q, state_d;
enum logic {
READY,
STALL
}
state_q, state_d;
if (CVA6Cfg.FpPresent) begin : fpu_gen
logic [CVA6Cfg.FLen-1:0] operand_a_i;
logic [CVA6Cfg.FLen-1:0] operand_b_i;
@ -57,24 +63,34 @@ module fpu_wrap import ariane_pkg::*; #(
EnableVectors: CVA6Cfg.XFVec,
EnableNanBox: 1'b1,
FpFmtMask: {CVA6Cfg.RVF, CVA6Cfg.RVD, CVA6Cfg.XF16, CVA6Cfg.XF8, CVA6Cfg.XF16ALT},
IntFmtMask: {CVA6Cfg.XFVec && CVA6Cfg.XF8, CVA6Cfg.XFVec && (CVA6Cfg.XF16 || CVA6Cfg.XF16ALT), 1'b1, 1'b1}
IntFmtMask: {
CVA6Cfg.XFVec && CVA6Cfg.XF8,
CVA6Cfg.XFVec && (CVA6Cfg.XF16 || CVA6Cfg.XF16ALT),
1'b1,
1'b1
}
};
// Implementation (number of registers etc)
localparam fpnew_pkg::fpu_implementation_t FPU_IMPLEMENTATION = '{
PipeRegs: '{// FP32, FP64, FP16, FP8, FP16alt
'{unsigned'(LAT_COMP_FP32 ),
unsigned'(LAT_COMP_FP64 ),
unsigned'(LAT_COMP_FP16 ),
unsigned'(LAT_COMP_FP8 ),
unsigned'(LAT_COMP_FP16ALT)}, // ADDMUL
PipeRegs: '{ // FP32, FP64, FP16, FP8, FP16alt
'{
unsigned'(LAT_COMP_FP32),
unsigned'(LAT_COMP_FP64),
unsigned'(LAT_COMP_FP16),
unsigned'(LAT_COMP_FP8),
unsigned'(LAT_COMP_FP16ALT)
}, // ADDMUL
'{default: unsigned'(LAT_DIVSQRT)}, // DIVSQRT
'{default: unsigned'(LAT_NONCOMP)}, // NONCOMP
'{default: unsigned'(LAT_CONV)}}, // CONV
UnitTypes: '{'{default: fpnew_pkg::PARALLEL}, // ADDMUL
'{default: unsigned'(LAT_CONV)}
}, // CONV
UnitTypes: '{
'{default: fpnew_pkg::PARALLEL}, // ADDMUL
'{default: fpnew_pkg::MERGED}, // DIVSQRT
'{default: fpnew_pkg::PARALLEL}, // NONCOMP
'{default: fpnew_pkg::MERGED}}, // CONV
'{default: fpnew_pkg::MERGED}
}, // CONV
PipeConfig: fpnew_pkg::DISTRIBUTED
};
@ -129,12 +145,10 @@ module fpu_wrap import ariane_pkg::*; #(
check_ah = 1'b0; // whether set scalar AH encoding from MSB of rm_i
// Scalar Rounding Modes - some ops encode inside RM but use smaller range
if (!(fpu_rm_i inside {[3'b000:3'b100]}))
fpu_rm_d = fpu_frm_i;
if (!(fpu_rm_i inside {[3'b000 : 3'b100]})) fpu_rm_d = fpu_frm_i;
// Vectorial ops always consult FRM
if (fpu_vec_op_d)
fpu_rm_d = fpu_frm_i;
if (fpu_vec_op_d) fpu_rm_d = fpu_frm_i;
// Formats
unique case (fpu_fmt_i)
@ -144,10 +158,8 @@ module fpu_wrap import ariane_pkg::*; #(
2'b01: fpu_dstfmt_d = fpu_vec_op_d ? fpnew_pkg::FP16ALT : fpnew_pkg::FP64;
// FP16 or FP16ALT (scalar)
2'b10: begin
if (!fpu_vec_op_d && fpu_rm_i==3'b101)
fpu_dstfmt_d = fpnew_pkg::FP16ALT;
else
fpu_dstfmt_d = fpnew_pkg::FP16;
if (!fpu_vec_op_d && fpu_rm_i == 3'b101) fpu_dstfmt_d = fpnew_pkg::FP16ALT;
else fpu_dstfmt_d = fpnew_pkg::FP16;
end
// FP8
default: fpu_dstfmt_d = fpnew_pkg::FP8;
@ -204,17 +216,14 @@ module fpu_wrap import ariane_pkg::*; #(
vec_replication = 1'b0; // no replication, R bit used for op
unique case (fpu_fmt_i)
2'b00: fpu_ifmt_d = fpnew_pkg::INT32;
2'b01,
2'b10: fpu_ifmt_d = fpnew_pkg::INT16;
2'b01, 2'b10: fpu_ifmt_d = fpnew_pkg::INT16;
2'b11: fpu_ifmt_d = fpnew_pkg::INT8;
endcase
// Scalar casts encoded in imm
end else begin
fpu_op_mod_d = operand_c_i[0];
if (operand_c_i[1])
fpu_ifmt_d = fpnew_pkg::INT64;
else
fpu_ifmt_d = fpnew_pkg::INT32;
if (operand_c_i[1]) fpu_ifmt_d = fpnew_pkg::INT64;
else fpu_ifmt_d = fpnew_pkg::INT32;
end
end
// Int to Float Cast - Op encoded in lowest two imm bits or rm
@ -226,17 +235,14 @@ module fpu_wrap import ariane_pkg::*; #(
vec_replication = 1'b0; // no replication, R bit used for op
unique case (fpu_fmt_i)
2'b00: fpu_ifmt_d = fpnew_pkg::INT32;
2'b01,
2'b10: fpu_ifmt_d = fpnew_pkg::INT16;
2'b01, 2'b10: fpu_ifmt_d = fpnew_pkg::INT16;
2'b11: fpu_ifmt_d = fpnew_pkg::INT8;
endcase
// Scalar casts encoded in imm
end else begin
fpu_op_mod_d = operand_c_i[0];
if (operand_c_i[1])
fpu_ifmt_d = fpnew_pkg::INT64;
else
fpu_ifmt_d = fpnew_pkg::INT32;
if (operand_c_i[1]) fpu_ifmt_d = fpnew_pkg::INT64;
else fpu_ifmt_d = fpnew_pkg::INT32;
end
end
// Float to Float Cast - Source format encoded in lowest two/three imm bits
@ -293,7 +299,9 @@ module fpu_wrap import ariane_pkg::*; #(
// Classification
FCLASS: begin
fpu_op_d = fpnew_pkg::CLASSIFY;
fpu_rm_d = {1'b0, fpu_rm_i[1:0]}; // mask out AH encoding bit - CLASS doesn't care anyways
fpu_rm_d = {
1'b0, fpu_rm_i[1:0]
}; // mask out AH encoding bit - CLASS doesn't care anyways
check_ah = 1'b1; // AH has RM MSB encoding
end
// Vectorial Minimum - set up scalar encoding in rm
@ -387,26 +395,26 @@ module fpu_wrap import ariane_pkg::*; #(
endcase
// Scalar AH encoding fixing
if (!fpu_vec_op_d && check_ah)
if (fpu_rm_i[2])
fpu_dstfmt_d = fpnew_pkg::FP16ALT;
if (!fpu_vec_op_d && check_ah) if (fpu_rm_i[2]) fpu_dstfmt_d = fpnew_pkg::FP16ALT;
// Replication
if (fpu_vec_op_d && vec_replication) begin
if (replicate_c) begin
unique case (fpu_dstfmt_d)
fpnew_pkg::FP32: operand_c_d = CVA6Cfg.RVD ? {2{operand_c_i[31:0]}} : operand_c_i;
fpnew_pkg::FP16,
fpnew_pkg::FP16ALT: operand_c_d = CVA6Cfg.RVD ? {4{operand_c_i[15:0]}} : {2{operand_c_i[15:0]}};
fpnew_pkg::FP8: operand_c_d = CVA6Cfg.RVD ? {8{operand_c_i[7:0]}} : {4{operand_c_i[7:0]}};
fpnew_pkg::FP16, fpnew_pkg::FP16ALT:
operand_c_d = CVA6Cfg.RVD ? {4{operand_c_i[15:0]}} : {2{operand_c_i[15:0]}};
fpnew_pkg::FP8:
operand_c_d = CVA6Cfg.RVD ? {8{operand_c_i[7:0]}} : {4{operand_c_i[7:0]}};
default: ; // Do nothing
endcase // fpu_dstfmt_d
end else begin
unique case (fpu_dstfmt_d)
fpnew_pkg::FP32: operand_b_d = CVA6Cfg.RVD ? {2{operand_b_i[31:0]}} : operand_b_i;
fpnew_pkg::FP16,
fpnew_pkg::FP16ALT: operand_b_d = CVA6Cfg.RVD ? {4{operand_b_i[15:0]}} : {2{operand_b_i[15:0]}};
fpnew_pkg::FP8: operand_b_d = CVA6Cfg.RVD ? {8{operand_b_i[7:0]}} : {4{operand_b_i[7:0]}};
fpnew_pkg::FP16, fpnew_pkg::FP16ALT:
operand_b_d = CVA6Cfg.RVD ? {4{operand_b_i[15:0]}} : {2{operand_b_i[15:0]}};
fpnew_pkg::FP8:
operand_b_d = CVA6Cfg.RVD ? {8{operand_b_i[7:0]}} : {4{operand_b_i[7:0]}};
default: ; // Do nothing
endcase // fpu_dstfmt_d
end
@ -462,7 +470,7 @@ module fpu_wrap import ariane_pkg::*; #(
// Buffer register and FSM state holding
always_ff @(posedge clk_i or negedge rst_ni) begin : fp_hold_reg
if(~rst_ni) begin
if (~rst_ni) begin
state_q <= READY;
operand_a_q <= '0;
operand_b_q <= '0;
@ -519,31 +527,31 @@ module fpu_wrap import ariane_pkg::*; #(
//---------------
fpnew_top #(
.Features ( FPU_FEATURES ),
.Implementation ( FPU_IMPLEMENTATION ),
.TagType ( logic [TRANS_ID_BITS-1:0] )
.Features (FPU_FEATURES),
.Implementation(FPU_IMPLEMENTATION),
.TagType (logic [TRANS_ID_BITS-1:0])
) i_fpnew_bulk (
.clk_i,
.rst_ni,
.operands_i ( fpu_operands ),
.rnd_mode_i ( fpnew_pkg::roundmode_e'(fpu_rm) ),
.op_i ( fpnew_pkg::operation_e'(fpu_op) ),
.op_mod_i ( fpu_op_mod ),
.src_fmt_i ( fpnew_pkg::fp_format_e'(fpu_srcfmt) ),
.dst_fmt_i ( fpnew_pkg::fp_format_e'(fpu_dstfmt) ),
.int_fmt_i ( fpnew_pkg::int_format_e'(fpu_ifmt) ),
.vectorial_op_i ( fpu_vec_op ),
.tag_i ( fpu_tag ),
.simd_mask_i ( 1'b1 ),
.in_valid_i ( fpu_in_valid ),
.in_ready_o ( fpu_in_ready ),
.operands_i (fpu_operands),
.rnd_mode_i (fpnew_pkg::roundmode_e'(fpu_rm)),
.op_i (fpnew_pkg::operation_e'(fpu_op)),
.op_mod_i (fpu_op_mod),
.src_fmt_i (fpnew_pkg::fp_format_e'(fpu_srcfmt)),
.dst_fmt_i (fpnew_pkg::fp_format_e'(fpu_dstfmt)),
.int_fmt_i (fpnew_pkg::int_format_e'(fpu_ifmt)),
.vectorial_op_i(fpu_vec_op),
.tag_i (fpu_tag),
.simd_mask_i (1'b1),
.in_valid_i (fpu_in_valid),
.in_ready_o (fpu_in_ready),
.flush_i,
.result_o,
.status_o ( fpu_status ),
.tag_o ( fpu_trans_id_o ),
.out_valid_o ( fpu_out_valid ),
.out_ready_i ( fpu_out_ready ),
.busy_o ( /* unused */ )
.status_o (fpu_status),
.tag_o (fpu_trans_id_o),
.out_valid_o (fpu_out_valid),
.out_ready_i (fpu_out_ready),
.busy_o ( /* unused */)
);
// Pack status flag into exception cause, tval ignored in wb, exception is always invalid

View file

@ -21,12 +21,12 @@
module bht #(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NR_ENTRIES = 1024
)(
) (
input logic clk_i,
input logic rst_ni,
input logic flush_i,
input logic debug_mode_i,
input logic [riscv::VLEN-1:0] vpc_i,
input logic [ riscv::VLEN-1:0] vpc_i,
input ariane_pkg::bht_update_t bht_update_i,
// we potentially need INSTR_PER_FETCH predictions/cycle
output ariane_pkg::bht_prediction_t [ariane_pkg::INSTR_PER_FETCH-1:0] bht_prediction_o
@ -46,15 +46,17 @@ module bht #(
struct packed {
logic valid;
logic [1:0] saturation_counter;
} bht_d[NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0], bht_q[NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0];
}
bht_d[NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0],
bht_q[NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0];
logic [$clog2(NR_ROWS)-1:0] index, update_pc;
logic [ROW_INDEX_BITS-1:0] update_row_index;
assign index = vpc_i[PREDICTION_BITS - 1:ROW_ADDR_BITS + OFFSET];
assign update_pc = bht_update_i.pc[PREDICTION_BITS - 1:ROW_ADDR_BITS + OFFSET];
assign index = vpc_i[PREDICTION_BITS-1:ROW_ADDR_BITS+OFFSET];
assign update_pc = bht_update_i.pc[PREDICTION_BITS-1:ROW_ADDR_BITS+OFFSET];
if (CVA6Cfg.RVC) begin : gen_update_row_index
assign update_row_index = bht_update_i.pc[ROW_ADDR_BITS + OFFSET - 1:OFFSET];
assign update_row_index = bht_update_i.pc[ROW_ADDR_BITS+OFFSET-1:OFFSET];
end else begin
assign update_row_index = '0;
end
@ -87,8 +89,7 @@ module bht #(
end else begin // otherwise we are not in any boundaries and can decrease or increase it
if (bht_update_i.taken)
bht_d[update_pc][update_row_index].saturation_counter = saturation_counter + 1;
else
bht_d[update_pc][update_row_index].saturation_counter = saturation_counter - 1;
else bht_d[update_pc][update_row_index].saturation_counter = saturation_counter - 1;
end
end
end
@ -119,20 +120,20 @@ module bht #(
// number of bits par word in the bram
localparam BRAM_WORD_BITS = $bits(ariane_pkg::bht_t);
logic [ROW_INDEX_BITS-1:0] row_index;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] bht_ram_we;
logic [ ROW_INDEX_BITS-1:0] row_index;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] bht_ram_we;
logic [ariane_pkg::INSTR_PER_FETCH*$clog2(NR_ROWS)-1:0] bht_ram_read_address_0;
logic [ariane_pkg::INSTR_PER_FETCH*$clog2(NR_ROWS)-1:0] bht_ram_read_address_1;
logic [ariane_pkg::INSTR_PER_FETCH*$clog2(NR_ROWS)-1:0] bht_ram_write_address;
logic [ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] bht_ram_wdata;
logic [ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] bht_ram_rdata_0;
logic [ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] bht_ram_rdata_1;
logic [ ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] bht_ram_wdata;
logic [ ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] bht_ram_rdata_0;
logic [ ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] bht_ram_rdata_1;
ariane_pkg::bht_t [ariane_pkg::INSTR_PER_FETCH-1:0] bht;
ariane_pkg::bht_t [ariane_pkg::INSTR_PER_FETCH-1:0] bht_updated;
ariane_pkg::bht_t [ ariane_pkg::INSTR_PER_FETCH-1:0] bht;
ariane_pkg::bht_t [ ariane_pkg::INSTR_PER_FETCH-1:0] bht_updated;
if (CVA6Cfg.RVC) begin : gen_row_index
assign row_index = vpc_i[ROW_ADDR_BITS + OFFSET - 1:OFFSET];
assign row_index = vpc_i[ROW_ADDR_BITS+OFFSET-1:OFFSET];
end else begin
assign row_index = '0;
end
@ -145,49 +146,48 @@ module bht #(
bht_ram_read_address_0 = '0;
bht_ram_read_address_1 = '0;
bht_ram_write_address = '0;
bht_ram_wdata ='0;
bht_ram_wdata = '0;
bht_updated = '0;
bht = '0;
for (int i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin
if (row_index == i) begin
bht_ram_read_address_0[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] = index;
bht_prediction_o[i].valid = bht_ram_rdata_0[i*BRAM_WORD_BITS+2] ;
bht_prediction_o[i].taken = bht_ram_rdata_0[i*BRAM_WORD_BITS+1] ;
bht_ram_read_address_0[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)] = index;
bht_prediction_o[i].valid = bht_ram_rdata_0[i*BRAM_WORD_BITS+2];
bht_prediction_o[i].taken = bht_ram_rdata_0[i*BRAM_WORD_BITS+1];
end
end
if (bht_update_i.valid && !debug_mode_i) begin
for (int i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin
if (update_row_index == i) begin
bht_ram_read_address_1[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] = update_pc;
bht[i].saturation_counter = bht_ram_rdata_1[i*BRAM_WORD_BITS +: 2];
bht_ram_read_address_1[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)] = update_pc;
bht[i].saturation_counter = bht_ram_rdata_1[i*BRAM_WORD_BITS+:2];
if (bht[i].saturation_counter == 2'b11) begin
// we can safely decrease it
if (!bht_update_i.taken)
bht_updated[i].saturation_counter = bht[i].saturation_counter - 1;
else
bht_updated[i].saturation_counter = 2'b11;
else bht_updated[i].saturation_counter = 2'b11;
// then check if it saturated in the negative regime e.g.: branch not taken
end else if (bht[i].saturation_counter == 2'b00) begin
// we can safely increase it
if (bht_update_i.taken)
bht_updated[i].saturation_counter = bht[i].saturation_counter + 1;
else
bht_updated[i].saturation_counter = 2'b00;
else bht_updated[i].saturation_counter = 2'b00;
end else begin // otherwise we are not in any boundaries and can decrease or increase it
if (bht_update_i.taken)
bht_updated[i].saturation_counter = bht[i].saturation_counter + 1;
else
bht_updated[i].saturation_counter = bht[i].saturation_counter - 1;
else bht_updated[i].saturation_counter = bht[i].saturation_counter - 1;
end
bht_updated[i].valid = 1'b1;
bht_ram_we[i] = 1'b1;
bht_ram_write_address[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] = update_pc;
bht_ram_write_address[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)] = update_pc;
//bht_ram_wdata[(i+1)*BRAM_WORD_BITS-1] = 1'b1; //valid
bht_ram_wdata[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] = {bht_updated[i].valid , bht_updated[i].saturation_counter};
bht_ram_wdata[i*BRAM_WORD_BITS+:BRAM_WORD_BITS] = {
bht_updated[i].valid, bht_updated[i].saturation_counter
};
end
end
@ -197,17 +197,17 @@ module bht #(
for (genvar i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin : gen_bht_ram
AsyncThreePortRam #(
.ADDR_WIDTH($clog2(NR_ROWS)),
.DATA_DEPTH (NR_ROWS),
.DATA_DEPTH(NR_ROWS),
.DATA_WIDTH(BRAM_WORD_BITS)
) i_bht_ram (
.Clk_CI ( clk_i ),
.WrEn_SI ( bht_ram_we[i] ),
.WrAddr_DI ( bht_ram_write_address[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] ),
.WrData_DI ( bht_ram_wdata[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] ),
.RdAddr_DI_0 ( bht_ram_read_address_0[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] ),
.RdAddr_DI_1 ( bht_ram_read_address_1[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] ),
.RdData_DO_0 ( bht_ram_rdata_0[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] ),
.RdData_DO_1 ( bht_ram_rdata_1[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] )
.Clk_CI (clk_i),
.WrEn_SI (bht_ram_we[i]),
.WrAddr_DI (bht_ram_write_address[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)]),
.WrData_DI (bht_ram_wdata[i*BRAM_WORD_BITS+:BRAM_WORD_BITS]),
.RdAddr_DI_0(bht_ram_read_address_0[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)]),
.RdAddr_DI_1(bht_ram_read_address_1[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)]),
.RdData_DO_0(bht_ram_rdata_0[i*BRAM_WORD_BITS+:BRAM_WORD_BITS]),
.RdData_DO_1(bht_ram_rdata_1[i*BRAM_WORD_BITS+:BRAM_WORD_BITS])
);
end

View file

@ -28,7 +28,7 @@
module btb #(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int NR_ENTRIES = 8
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic flush_i, // flush the btb
@ -58,33 +58,33 @@ module btb #(
logic [$clog2(NR_ROWS)-1:0] index, update_pc;
logic [ROW_INDEX_BITS-1:0] update_row_index;
assign index = vpc_i[PREDICTION_BITS - 1:ROW_ADDR_BITS + OFFSET];
assign update_pc = btb_update_i.pc[PREDICTION_BITS - 1:ROW_ADDR_BITS + OFFSET];
assign index = vpc_i[PREDICTION_BITS-1:ROW_ADDR_BITS+OFFSET];
assign update_pc = btb_update_i.pc[PREDICTION_BITS-1:ROW_ADDR_BITS+OFFSET];
if (CVA6Cfg.RVC) begin : gen_update_row_index
assign update_row_index = btb_update_i.pc[ROW_ADDR_BITS + OFFSET - 1:OFFSET];
assign update_row_index = btb_update_i.pc[ROW_ADDR_BITS+OFFSET-1:OFFSET];
end else begin
assign update_row_index = '0;
end
if (ariane_pkg::FPGA_EN) begin : gen_fpga_btb //FPGA TARGETS
logic [ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_csel_prediction;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_we_prediction;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_csel_prediction;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_we_prediction;
logic [ariane_pkg::INSTR_PER_FETCH*$clog2(NR_ROWS)-1:0] btb_ram_addr_prediction;
logic [ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] btb_ram_wdata_prediction;
logic [ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] btb_ram_rdata_prediction;
logic [ ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] btb_ram_wdata_prediction;
logic [ ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] btb_ram_rdata_prediction;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_csel_update;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_we_update;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_csel_update;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] btb_ram_we_update;
logic [ariane_pkg::INSTR_PER_FETCH*$clog2(NR_ROWS)-1:0] btb_ram_addr_update;
logic [ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] btb_ram_wdata_update;
logic [ ariane_pkg::INSTR_PER_FETCH*BRAM_WORD_BITS-1:0] btb_ram_wdata_update;
// output matching prediction
for (genvar i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin : gen_btb_output
assign btb_ram_csel_prediction[i] = 1'b1;
assign btb_ram_we_prediction[i] = 1'b0;
assign btb_ram_wdata_prediction = '0;
assign btb_ram_addr_prediction[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] = index;
assign btb_prediction_o[i] = btb_ram_rdata_prediction[i*BRAM_WORD_BITS +: BRAM_WORD_BITS];
assign btb_ram_addr_prediction[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)] = index;
assign btb_prediction_o[i] = btb_ram_rdata_prediction[i*BRAM_WORD_BITS+:BRAM_WORD_BITS];
end
// -------------------------
@ -102,8 +102,10 @@ module btb #(
if (update_row_index == i) begin
btb_ram_csel_update[i] = 1'b1;
btb_ram_we_update[i] = 1'b1;
btb_ram_addr_update[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] = update_pc;
btb_ram_wdata_update[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] = {1'b1 , btb_update_i.target_address};
btb_ram_addr_update[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)] = update_pc;
btb_ram_wdata_update[i*BRAM_WORD_BITS+:BRAM_WORD_BITS] = {
1'b1, btb_update_i.target_address
};
end
end
end
@ -117,20 +119,20 @@ module btb #(
.OUT_REGS (0),
.SIM_INIT (1)
) i_btb_ram (
.Clk_CI ( clk_i ),
.Rst_RBI ( rst_ni ),
.Clk_CI (clk_i),
.Rst_RBI (rst_ni),
//----------------------------
.CSelA_SI ( btb_ram_csel_update[i] ),
.WrEnA_SI ( btb_ram_we_update[i] ),
.AddrA_DI ( btb_ram_addr_update[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] ),
.WrDataA_DI ( btb_ram_wdata_update[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] ),
.RdDataA_DO ( ),
.CSelA_SI (btb_ram_csel_update[i]),
.WrEnA_SI (btb_ram_we_update[i]),
.AddrA_DI (btb_ram_addr_update[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)]),
.WrDataA_DI(btb_ram_wdata_update[i*BRAM_WORD_BITS+:BRAM_WORD_BITS]),
.RdDataA_DO(),
//-----------------------------
.CSelB_SI ( btb_ram_csel_prediction[i] ),
.WrEnB_SI ( btb_ram_we_prediction[i] ),
.AddrB_DI ( btb_ram_addr_prediction[i*$clog2(NR_ROWS) +: $clog2(NR_ROWS)] ),
.WrDataB_DI ( btb_ram_wdata_prediction[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] ),
.RdDataB_DO ( btb_ram_rdata_prediction[i*BRAM_WORD_BITS +: BRAM_WORD_BITS] )
.CSelB_SI (btb_ram_csel_prediction[i]),
.WrEnB_SI (btb_ram_we_prediction[i]),
.AddrB_DI (btb_ram_addr_prediction[i*$clog2(NR_ROWS)+:$clog2(NR_ROWS)]),
.WrDataB_DI(btb_ram_wdata_prediction[i*BRAM_WORD_BITS+:BRAM_WORD_BITS]),
.RdDataB_DO(btb_ram_rdata_prediction[i*BRAM_WORD_BITS+:BRAM_WORD_BITS])
);
end
@ -138,8 +140,9 @@ module btb #(
// typedef for all branch target entries
// we may want to try to put a tag field that fills the rest of the PC in-order to mitigate aliasing effects
ariane_pkg::btb_prediction_t btb_d [NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0],
btb_q [NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0];
ariane_pkg::btb_prediction_t
btb_d[NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0],
btb_q[NR_ROWS-1:0][ariane_pkg::INSTR_PER_FETCH-1:0];
// output matching prediction
for (genvar i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin : gen_btb_output
@ -164,8 +167,7 @@ module btb #(
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
// Bias the branches to be taken upon first arrival
for (int i = 0; i < NR_ROWS; i++)
btb_q[i] <= '{default: 0};
for (int i = 0; i < NR_ROWS; i++) btb_q[i] <= '{default: 0};
end else begin
// evict all entries
if (flush_i) begin

View file

@ -15,7 +15,9 @@
// This module interfaces with the instruction cache, handles control
// change request from the back-end and does branch prediction.
module frontend import ariane_pkg::*; #(
module frontend
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -47,10 +49,10 @@ module frontend import ariane_pkg::*; #(
input logic fetch_entry_ready_i // ID acknowledged this instruction
);
// Instruction Cache Registers, from I$
logic [FETCH_WIDTH-1:0] icache_data_q;
logic [ FETCH_WIDTH-1:0] icache_data_q;
logic icache_valid_q;
ariane_pkg::frontend_exception_t icache_ex_valid_q;
logic [riscv::VLEN-1:0] icache_vaddr_q;
logic [ riscv::VLEN-1:0] icache_vaddr_q;
logic instr_queue_ready;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] instr_queue_consumed;
// upper-most branch-prediction from last cycle
@ -64,7 +66,7 @@ module frontend import ariane_pkg::*; #(
logic npc_rst_load_q;
logic replay;
logic [riscv::VLEN-1:0] replay_addr;
logic [ riscv::VLEN-1:0] replay_addr;
// shift amount
logic [$clog2(ariane_pkg::INSTR_PER_FETCH)-1:0] shamt;
@ -79,15 +81,13 @@ module frontend import ariane_pkg::*; #(
// Ctrl Flow Speculation
// -----------------------
// RVI ctrl flow prediction
logic [INSTR_PER_FETCH-1:0] rvi_return, rvi_call, rvi_branch,
rvi_jalr, rvi_jump;
logic [INSTR_PER_FETCH-1:0] rvi_return, rvi_call, rvi_branch, rvi_jalr, rvi_jump;
logic [INSTR_PER_FETCH-1:0][riscv::VLEN-1:0] rvi_imm;
// RVC branching
logic [INSTR_PER_FETCH-1:0] rvc_branch, rvc_jump, rvc_jr, rvc_return,
rvc_jalr, rvc_call;
logic [INSTR_PER_FETCH-1:0] rvc_branch, rvc_jump, rvc_jr, rvc_return, rvc_jalr, rvc_call;
logic [INSTR_PER_FETCH-1:0][riscv::VLEN-1:0] rvc_imm;
// re-aligned instruction and address (coming from cache - combinationally)
logic [INSTR_PER_FETCH-1:0][31:0] instr;
logic [INSTR_PER_FETCH-1:0][ 31:0] instr;
logic [INSTR_PER_FETCH-1:0][riscv::VLEN-1:0] addr;
logic [INSTR_PER_FETCH-1:0] instruction_valid;
// BHT, BTB and RAS prediction
@ -96,15 +96,15 @@ module frontend import ariane_pkg::*; #(
bht_prediction_t [INSTR_PER_FETCH-1:0] bht_prediction_shifted;
btb_prediction_t [INSTR_PER_FETCH-1:0] btb_prediction_shifted;
ras_t ras_predict;
logic [riscv::VLEN-1:0] vpc_btb;
logic [ riscv::VLEN-1:0] vpc_btb;
// branch-predict update
logic is_mispredict;
logic ras_push, ras_pop;
logic [riscv::VLEN-1:0] ras_update;
logic [ riscv::VLEN-1:0] ras_update;
// Instruction FIFO
logic [riscv::VLEN-1:0] predict_address;
logic [ riscv::VLEN-1:0] predict_address;
cf_t [ariane_pkg::INSTR_PER_FETCH-1:0] cf_type;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] taken_rvi_cf;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] taken_rvc_cf;
@ -112,18 +112,18 @@ module frontend import ariane_pkg::*; #(
logic serving_unaligned;
// Re-align instructions
instr_realign #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_instr_realign (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( icache_dreq_o.kill_s2 ),
.valid_i ( icache_valid_q ),
.serving_unaligned_o ( serving_unaligned ),
.address_i ( icache_vaddr_q ),
.data_i ( icache_data_q ),
.valid_o ( instruction_valid ),
.addr_o ( addr ),
.instr_o ( instr )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (icache_dreq_o.kill_s2),
.valid_i (icache_valid_q),
.serving_unaligned_o(serving_unaligned),
.address_i (icache_vaddr_q),
.data_i (icache_data_q),
.valid_o (instruction_valid),
.addr_o (addr),
.instr_o (instr)
);
// --------------------
@ -133,8 +133,12 @@ module frontend import ariane_pkg::*; #(
// in case we are serving an unaligned instruction in instr[0] we need to take
// the prediction we saved from the previous fetch
if (CVA6Cfg.RVC) begin : gen_btb_prediction_shifted
assign bht_prediction_shifted[0] = (serving_unaligned) ? bht_q : bht_prediction[addr[0][$clog2(INSTR_PER_FETCH):1]];
assign btb_prediction_shifted[0] = (serving_unaligned) ? btb_q : btb_prediction[addr[0][$clog2(INSTR_PER_FETCH):1]];
assign bht_prediction_shifted[0] = (serving_unaligned) ? bht_q : bht_prediction[addr[0][$clog2(
INSTR_PER_FETCH
):1]];
assign btb_prediction_shifted[0] = (serving_unaligned) ? btb_q : btb_prediction[addr[0][$clog2(
INSTR_PER_FETCH
):1]];
// for all other predictions we can use the generated address to index
// into the branch prediction data structures
@ -145,7 +149,8 @@ module frontend import ariane_pkg::*; #(
end else begin
assign bht_prediction_shifted[0] = (serving_unaligned) ? bht_q : bht_prediction[addr[0][1]];
assign btb_prediction_shifted[0] = (serving_unaligned) ? btb_q : btb_prediction[addr[0][1]];
end;
end
;
// for the return address stack it doens't matter as we have the
// address of the call/return already
@ -183,9 +188,11 @@ module frontend import ariane_pkg::*; #(
ras_update = '0;
// lower most prediction gets precedence
for (int i = INSTR_PER_FETCH - 1; i >= 0 ; i--) begin
unique case ({is_branch[i], is_return[i], is_jump[i], is_jalr[i]})
4'b0000:; // regular instruction e.g.: no branch
for (int i = INSTR_PER_FETCH - 1; i >= 0; i--) begin
unique case ({
is_branch[i], is_return[i], is_jump[i], is_jalr[i]
})
4'b0000: ; // regular instruction e.g.: no branch
// unconditional jump to register, we need the BTB to resolve this
4'b0001: begin
ras_pop = 1'b0;
@ -229,7 +236,7 @@ module frontend import ariane_pkg::*; #(
cf_type[i] = ariane_pkg::Branch;
end
end
default:;
default: ;
// default: $error("Decoded more than one control flow");
endcase
// if this instruction, in addition, is a call, save the resulting address
@ -250,7 +257,8 @@ module frontend import ariane_pkg::*; #(
// BP cannot be valid if we have a return instruction and the RAS is not giving a valid address
// Check that we encountered a control flow and that for a return the RAS
// contains a valid prediction.
for (int i = 0; i < INSTR_PER_FETCH; i++) bp_valid |= ((cf_type[i] != NoCF & cf_type[i] != Return) | ((cf_type[i] == Return) & ras_predict.valid));
for (int i = 0; i < INSTR_PER_FETCH; i++)
bp_valid |= ((cf_type[i] != NoCF & cf_type[i] != Return) | ((cf_type[i] == Return) & ras_predict.valid));
end
assign is_mispredict = resolved_branch_i.valid & resolved_branch_i.is_mispredict;
@ -271,7 +279,7 @@ module frontend import ariane_pkg::*; #(
btb_update_t btb_update;
// assert on branch, deassert when resolved
logic speculative_q,speculative_d;
logic speculative_q, speculative_d;
assign speculative_d = (speculative_q && !resolved_branch_i.valid || |is_branch || |is_return || |is_jalr) && !flush_i;
assign icache_dreq_o.spec = speculative_d;
@ -349,11 +357,12 @@ module frontend import ariane_pkg::*; #(
// instruction in the commit stage
// TODO(zarubaf) This adder can at least be merged with the one in the csr_regfile stage
if (set_pc_commit_i) begin
npc_d = pc_commit_i + (halt_i ? '0 : {{riscv::VLEN-3{1'b0}}, 3'b100});
npc_d = pc_commit_i + (halt_i ? '0 : {{riscv::VLEN - 3{1'b0}}, 3'b100});
end
// 7. Debug
// enter debug on a hard-coded base-address
if (set_debug_pc_i) npc_d = CVA6Cfg.DmBaseAddress[riscv::VLEN-1:0] + CVA6Cfg.HaltAddress[riscv::VLEN-1:0];
if (set_debug_pc_i)
npc_d = CVA6Cfg.DmBaseAddress[riscv::VLEN-1:0] + CVA6Cfg.HaltAddress[riscv::VLEN-1:0];
icache_dreq_o.vaddr = fetch_address;
end
@ -399,16 +408,16 @@ module frontend import ariane_pkg::*; #(
assign ras_predict = '0;
end else begin : ras_gen
ras #(
.CVA6Cfg ( CVA6Cfg ),
.DEPTH ( CVA6Cfg.RASDepth )
.CVA6Cfg(CVA6Cfg),
.DEPTH (CVA6Cfg.RASDepth)
) i_ras (
.clk_i,
.rst_ni,
.flush_i( flush_bp_i ),
.push_i ( ras_push ),
.pop_i ( ras_pop ),
.data_i ( ras_update ),
.data_o ( ras_predict )
.flush_i(flush_bp_i),
.push_i (ras_push),
.pop_i (ras_pop),
.data_i (ras_update),
.data_o (ras_predict)
);
end
@ -421,16 +430,16 @@ module frontend import ariane_pkg::*; #(
assign btb_prediction = '0;
end else begin : btb_gen
btb #(
.CVA6Cfg ( CVA6Cfg ),
.NR_ENTRIES ( CVA6Cfg.BTBEntries )
.CVA6Cfg (CVA6Cfg),
.NR_ENTRIES(CVA6Cfg.BTBEntries)
) i_btb (
.clk_i,
.rst_ni,
.flush_i ( flush_bp_i ),
.flush_i (flush_bp_i),
.debug_mode_i,
.vpc_i ( vpc_btb ),
.btb_update_i ( btb_update ),
.btb_prediction_o ( btb_prediction )
.vpc_i (vpc_btb),
.btb_update_i (btb_update),
.btb_prediction_o(btb_prediction)
);
end
@ -438,16 +447,16 @@ module frontend import ariane_pkg::*; #(
assign bht_prediction = '0;
end else begin : bht_gen
bht #(
.CVA6Cfg ( CVA6Cfg ),
.NR_ENTRIES ( CVA6Cfg.BHTEntries )
.CVA6Cfg (CVA6Cfg),
.NR_ENTRIES(CVA6Cfg.BHTEntries)
) i_bht (
.clk_i,
.rst_ni,
.flush_i ( flush_bp_i ),
.flush_i (flush_bp_i),
.debug_mode_i,
.vpc_i ( icache_vaddr_q ),
.bht_update_i ( bht_update ),
.bht_prediction_o ( bht_prediction )
.vpc_i (icache_vaddr_q),
.bht_update_i (bht_update),
.bht_prediction_o(bht_prediction)
);
end
@ -455,52 +464,53 @@ module frontend import ariane_pkg::*; #(
// and jumps
for (genvar i = 0; i < INSTR_PER_FETCH; i++) begin : gen_instr_scan
instr_scan #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_instr_scan (
.instr_i ( instr[i] ),
.rvi_return_o ( rvi_return[i] ),
.rvi_call_o ( rvi_call[i] ),
.rvi_branch_o ( rvi_branch[i] ),
.rvi_jalr_o ( rvi_jalr[i] ),
.rvi_jump_o ( rvi_jump[i] ),
.rvi_imm_o ( rvi_imm[i] ),
.rvc_branch_o ( rvc_branch[i] ),
.rvc_jump_o ( rvc_jump[i] ),
.rvc_jr_o ( rvc_jr[i] ),
.rvc_return_o ( rvc_return[i] ),
.rvc_jalr_o ( rvc_jalr[i] ),
.rvc_call_o ( rvc_call[i] ),
.rvc_imm_o ( rvc_imm[i] )
.instr_i (instr[i]),
.rvi_return_o(rvi_return[i]),
.rvi_call_o (rvi_call[i]),
.rvi_branch_o(rvi_branch[i]),
.rvi_jalr_o (rvi_jalr[i]),
.rvi_jump_o (rvi_jump[i]),
.rvi_imm_o (rvi_imm[i]),
.rvc_branch_o(rvc_branch[i]),
.rvc_jump_o (rvc_jump[i]),
.rvc_jr_o (rvc_jr[i]),
.rvc_return_o(rvc_return[i]),
.rvc_jalr_o (rvc_jalr[i]),
.rvc_call_o (rvc_call[i]),
.rvc_imm_o (rvc_imm[i])
);
end
instr_queue #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_instr_queue (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_i ),
.instr_i ( instr ), // from re-aligner
.addr_i ( addr ), // from re-aligner
.exception_i ( icache_ex_valid_q ), // from I$
.exception_addr_i ( icache_vaddr_q ),
.predict_address_i ( predict_address ),
.cf_type_i ( cf_type ),
.valid_i ( instruction_valid ), // from re-aligner
.consumed_o ( instr_queue_consumed ),
.ready_o ( instr_queue_ready ),
.replay_o ( replay ),
.replay_addr_o ( replay_addr ),
.fetch_entry_o ( fetch_entry_o ), // to back-end
.fetch_entry_valid_o ( fetch_entry_valid_o ), // to back-end
.fetch_entry_ready_i ( fetch_entry_ready_i ) // to back-end
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (flush_i),
.instr_i (instr), // from re-aligner
.addr_i (addr), // from re-aligner
.exception_i (icache_ex_valid_q), // from I$
.exception_addr_i (icache_vaddr_q),
.predict_address_i (predict_address),
.cf_type_i (cf_type),
.valid_i (instruction_valid), // from re-aligner
.consumed_o (instr_queue_consumed),
.ready_o (instr_queue_ready),
.replay_o (replay),
.replay_addr_o (replay_addr),
.fetch_entry_o (fetch_entry_o), // to back-end
.fetch_entry_valid_o(fetch_entry_valid_o), // to back-end
.fetch_entry_ready_i(fetch_entry_ready_i) // to back-end
);
// pragma translate_off
`ifndef VERILATOR
`ifndef VERILATOR
initial begin
assert (FETCH_WIDTH == 32 || FETCH_WIDTH == 64) else $fatal(1, "[frontend] fetch width != not supported");
assert (FETCH_WIDTH == 32 || FETCH_WIDTH == 64)
else $fatal(1, "[frontend] fetch width != not supported");
end
`endif
`endif
// pragma translate_on
endmodule

View file

@ -43,7 +43,9 @@
// the replay mechanism gets more complicated as it can be that a 32 bit instruction
// can not be pushed at once.
module instr_queue import ariane_pkg::*; #(
module instr_queue
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -78,17 +80,18 @@ module instr_queue import ariane_pkg::*; #(
logic [ariane_pkg::LOG2_INSTR_PER_FETCH-1:0] branch_index;
// instruction queues
logic [ariane_pkg::INSTR_PER_FETCH-1:0]
[$clog2(ariane_pkg::FETCH_FIFO_DEPTH)-1:0] instr_queue_usage;
logic [ariane_pkg::INSTR_PER_FETCH-1:0][$clog2(
ariane_pkg::FETCH_FIFO_DEPTH
)-1:0] instr_queue_usage;
instr_data_t [ariane_pkg::INSTR_PER_FETCH-1:0] instr_data_in, instr_data_out;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] push_instr, push_instr_fifo;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] pop_instr;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] instr_queue_full;
logic [ariane_pkg::INSTR_PER_FETCH-1:0] instr_queue_empty;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] pop_instr;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] instr_queue_full;
logic [ ariane_pkg::INSTR_PER_FETCH-1:0] instr_queue_empty;
logic instr_overflow;
// address queue
logic [$clog2(ariane_pkg::FETCH_FIFO_DEPTH)-1:0] address_queue_usage;
logic [riscv::VLEN-1:0] address_out;
logic [ riscv::VLEN-1:0] address_out;
logic pop_address;
logic push_address;
logic full_address;
@ -129,12 +132,12 @@ module instr_queue import ariane_pkg::*; #(
// calculate a branch mask, e.g.: get the first taken branch
lzc #(
.WIDTH ( ariane_pkg::INSTR_PER_FETCH ),
.MODE ( 0 ) // count trailing zeros
.WIDTH(ariane_pkg::INSTR_PER_FETCH),
.MODE (0) // count trailing zeros
) i_lzc_branch_index (
.in_i ( taken ), // we want to count trailing zeros
.cnt_o ( branch_index ), // first branch on branch_index
.empty_o ( branch_empty )
.in_i (taken), // we want to count trailing zeros
.cnt_o (branch_index), // first branch on branch_index
.empty_o(branch_empty)
);
@ -154,10 +157,10 @@ module instr_queue import ariane_pkg::*; #(
assign consumed_o = consumed_extended[ariane_pkg::INSTR_PER_FETCH-1:0];
// count the numbers of valid instructions we've pushed from this package
popcount #(
.INPUT_WIDTH ( ariane_pkg::INSTR_PER_FETCH )
.INPUT_WIDTH(ariane_pkg::INSTR_PER_FETCH)
) i_popcount (
.data_i ( push_instr_fifo ),
.popcount_o ( popcount )
.data_i (push_instr_fifo),
.popcount_o(popcount)
);
assign shamt = popcount[$bits(shamt)-1:0];
@ -168,7 +171,7 @@ module instr_queue import ariane_pkg::*; #(
// Input interface
// ----------------------
// rotate left by the current position
assign fifo_pos_extended = { valid, valid } << idx_is_q;
assign fifo_pos_extended = {valid, valid} << idx_is_q;
// we just care about the upper bits
assign fifo_pos = fifo_pos_extended[ariane_pkg::INSTR_PER_FETCH*2-1:ariane_pkg::INSTR_PER_FETCH];
// the fifo_position signal can directly be used to guide the push signal of each FIFO
@ -178,16 +181,16 @@ module instr_queue import ariane_pkg::*; #(
// duplicate the entries for easier selection e.g.: 3 2 1 0 3 2 1 0
for (genvar i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin : gen_duplicate_instr_input
assign instr[i] = instr_i[i];
assign instr[i + ariane_pkg::INSTR_PER_FETCH] = instr_i[i];
assign instr[i+ariane_pkg::INSTR_PER_FETCH] = instr_i[i];
assign cf[i] = cf_type_i[i];
assign cf[i + ariane_pkg::INSTR_PER_FETCH] = cf_type_i[i];
assign cf[i+ariane_pkg::INSTR_PER_FETCH] = cf_type_i[i];
end
// shift the inputs
for (genvar i = 0; i < ariane_pkg::INSTR_PER_FETCH; i++) begin : gen_fifo_input_select
/* verilator lint_off WIDTH */
assign instr_data_in[i].instr = instr[i + idx_is_q];
assign instr_data_in[i].cf = cf[i + idx_is_q];
assign instr_data_in[i].instr = instr[i+idx_is_q];
assign instr_data_in[i].cf = cf[i+idx_is_q];
assign instr_data_in[i].ex = exception_i; // exceptions hold for the whole fetch packet
assign instr_data_in[i].ex_vaddr = exception_addr_i;
/* verilator lint_on WIDTH */
@ -279,14 +282,18 @@ module instr_queue import ariane_pkg::*; #(
end
fetch_entry_o.instruction = instr_data_out[i].instr;
fetch_entry_o.ex.valid = instr_data_out[i].ex != ariane_pkg::FE_NONE;
fetch_entry_o.ex.tval = {{(riscv::XLEN-riscv::VLEN){1'b0}}, instr_data_out[i].ex_vaddr};
fetch_entry_o.ex.tval = {
{(riscv::XLEN - riscv::VLEN) {1'b0}}, instr_data_out[i].ex_vaddr
};
fetch_entry_o.branch_predict.cf = instr_data_out[i].cf;
pop_instr[i] = fetch_entry_valid_o & fetch_entry_ready_i;
end
end
// rotate the pointer left
if (fetch_entry_ready_i) begin
idx_ds_d = {idx_ds_q[ariane_pkg::INSTR_PER_FETCH-2:0], idx_ds_q[ariane_pkg::INSTR_PER_FETCH-1]};
idx_ds_d = {
idx_ds_q[ariane_pkg::INSTR_PER_FETCH-2:0], idx_ds_q[ariane_pkg::INSTR_PER_FETCH-1]
};
end
end
end else begin : gen_downstream_itf_without_c
@ -302,7 +309,7 @@ module instr_queue import ariane_pkg::*; #(
end else begin
fetch_entry_o.ex.cause = riscv::INSTR_PAGE_FAULT;
end
fetch_entry_o.ex.tval = {{64-riscv::VLEN{1'b0}}, instr_data_out[0].ex_vaddr};
fetch_entry_o.ex.tval = {{64 - riscv::VLEN{1'b0}}, instr_data_out[0].ex_vaddr};
fetch_entry_o.branch_predict.predict_address = address_out;
fetch_entry_o.branch_predict.cf = instr_data_out[0].cf;
@ -347,20 +354,20 @@ module instr_queue import ariane_pkg::*; #(
// Make sure we don't save any instructions if we couldn't save the address
assign push_instr_fifo[i] = push_instr[i] & ~address_overflow;
fifo_v3 #(
.DEPTH ( ariane_pkg::FETCH_FIFO_DEPTH ),
.dtype ( instr_data_t )
.DEPTH(ariane_pkg::FETCH_FIFO_DEPTH),
.dtype(instr_data_t)
) i_fifo_instr_data (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_i ),
.testmode_i ( 1'b0 ),
.full_o ( instr_queue_full[i] ),
.empty_o ( instr_queue_empty[i] ),
.usage_o ( instr_queue_usage[i] ),
.data_i ( instr_data_in[i] ),
.push_i ( push_instr_fifo[i] ),
.data_o ( instr_data_out[i] ),
.pop_i ( pop_instr[i] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (flush_i),
.testmode_i(1'b0),
.full_o (instr_queue_full[i]),
.empty_o (instr_queue_empty[i]),
.usage_o (instr_queue_usage[i]),
.data_i (instr_data_in[i]),
.push_i (push_instr_fifo[i]),
.data_o (instr_data_out[i]),
.pop_i (pop_instr[i])
);
end
// or reduce and check whether we are retiring a taken branch (might be that the corresponding)
@ -374,20 +381,20 @@ module instr_queue import ariane_pkg::*; #(
end
fifo_v3 #(
.DEPTH ( ariane_pkg::FETCH_FIFO_DEPTH ), // TODO(zarubaf): Fork out to separate param
.DATA_WIDTH ( riscv::VLEN )
.DEPTH (ariane_pkg::FETCH_FIFO_DEPTH), // TODO(zarubaf): Fork out to separate param
.DATA_WIDTH(riscv::VLEN)
) i_fifo_address (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_i ),
.testmode_i ( 1'b0 ),
.full_o ( full_address ),
.empty_o ( empty_address ),
.usage_o ( address_queue_usage ),
.data_i ( predict_address_i ),
.push_i ( push_address & ~full_address ),
.data_o ( address_out ),
.pop_i ( pop_address )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (flush_i),
.testmode_i(1'b0),
.full_o (full_address),
.empty_o (empty_address),
.usage_o (address_queue_usage),
.data_i (predict_address_i),
.push_i (push_address & ~full_address),
.data_o (address_out),
.pop_i (pop_address)
);
unread i_unread_address_fifo (.d_i(|{empty_address, address_queue_usage}));
@ -436,14 +443,17 @@ module instr_queue import ariane_pkg::*; #(
end
// pragma translate_off
`ifndef VERILATOR
replay_address_fifo: assert property (
@(posedge clk_i) disable iff (!rst_ni) replay_o |-> !i_fifo_address.push_i
) else $fatal(1,"[instr_queue] Pushing address although replay asserted");
`ifndef VERILATOR
replay_address_fifo :
assert property (@(posedge clk_i) disable iff (!rst_ni) replay_o |-> !i_fifo_address.push_i)
else $fatal(1, "[instr_queue] Pushing address although replay asserted");
output_select_onehot: assert property (
@(posedge clk_i) $onehot0(idx_ds_q)
) else begin $error("Output select should be one-hot encoded"); $stop(); end
`endif
output_select_onehot :
assert property (@(posedge clk_i) $onehot0(idx_ds_q))
else begin
$error("Output select should be one-hot encoded");
$stop();
end
`endif
// pragma translate_on
endmodule

View file

@ -18,7 +18,7 @@
module instr_scan #(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic [31:0] instr_i, // expect aligned instruction, compressed or not
input logic [ 31:0] instr_i, // expect aligned instruction, compressed or not
output logic rvi_return_o,
output logic rvi_call_o,
output logic rvi_branch_o,
@ -48,7 +48,11 @@ module instr_scan #(
// Opocde is JAL[R] and destination register is either x1 or x5
assign rvi_call_o = (rvi_jalr_o | rvi_jump_o) & ((instr_i[11:7] == 5'd1) | instr_i[11:7] == 5'd5);
// differentiates between JAL and BRANCH opcode, JALR comes from BHT
assign rvi_imm_o = is_xret ? '0 : (instr_i[3]) ? ariane_pkg::uj_imm(instr_i) : ariane_pkg::sb_imm(instr_i);
assign rvi_imm_o = is_xret ? '0 : (instr_i[3]) ? ariane_pkg::uj_imm(
instr_i
) : ariane_pkg::sb_imm(
instr_i
);
assign rvi_branch_o = (instr_i[6:0] == riscv::OpcodeBranch);
assign rvi_jalr_o = (instr_i[6:0] == riscv::OpcodeJalr);
assign rvi_jump_o = logic'(instr_i[6:0] == riscv::OpcodeJal) | is_xret;
@ -71,7 +75,7 @@ module instr_scan #(
& (instr_i[1:0] == riscv::OpcodeC1)
& is_rvc;
// check that rs1 is x1 or x5
assign rvc_return_o = ((instr_i[11:7] == 5'd1) | (instr_i[11:7] == 5'd5)) & rvc_jr_o ;
assign rvc_return_o = ((instr_i[11:7] == 5'd1) | (instr_i[11:7] == 5'd5)) & rvc_jr_o;
// differentiates between JAL and BRANCH opcode, JALR comes from BHT
assign rvc_imm_o = (instr_i[14]) ? {{56+riscv::VLEN-64{instr_i[12]}}, instr_i[6:5], instr_i[2], instr_i[11:10], instr_i[4:3], 1'b0}

View file

@ -17,7 +17,7 @@
module ras #(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned DEPTH = 2
)(
) (
input logic clk_i,
input logic rst_ni,
input logic flush_i,

View file

@ -62,12 +62,12 @@ module id_stage #(
// 1. Check if they are compressed and expand in case they are
// ---------------------------------------------------------
compressed_decoder #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) compressed_decoder_i (
.instr_i ( fetch_entry_i.instruction ),
.instr_o ( instruction ),
.illegal_instr_o ( is_illegal ),
.is_compressed_o ( is_compressed )
.instr_i (fetch_entry_i.instruction),
.instr_o (instruction),
.illegal_instr_o(is_illegal),
.is_compressed_o(is_compressed)
);
end else begin
assign instruction = fetch_entry_i.instruction;
@ -78,28 +78,28 @@ module id_stage #(
// 2. Decode and emit instruction to issue stage
// ---------------------------------------------------------
decoder #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) decoder_i (
.debug_req_i,
.irq_ctrl_i,
.irq_i,
.pc_i ( fetch_entry_i.address ),
.is_compressed_i ( is_compressed ),
.is_illegal_i ( is_illegal ),
.instruction_i ( instruction ),
.compressed_instr_i ( fetch_entry_i.instruction[15:0] ),
.branch_predict_i ( fetch_entry_i.branch_predict ),
.ex_i ( fetch_entry_i.ex ),
.priv_lvl_i ( priv_lvl_i ),
.debug_mode_i ( debug_mode_i ),
.pc_i (fetch_entry_i.address),
.is_compressed_i (is_compressed),
.is_illegal_i (is_illegal),
.instruction_i (instruction),
.compressed_instr_i (fetch_entry_i.instruction[15:0]),
.branch_predict_i (fetch_entry_i.branch_predict),
.ex_i (fetch_entry_i.ex),
.priv_lvl_i (priv_lvl_i),
.debug_mode_i (debug_mode_i),
.fs_i,
.frm_i,
.vs_i,
.tvm_i,
.tw_i,
.tsr_i,
.instruction_o ( decoded_instruction ),
.is_control_flow_instr_o ( is_control_flow_instr )
.instruction_o (decoded_instruction),
.is_control_flow_instr_o(is_control_flow_instr)
);
// ------------------
@ -114,8 +114,7 @@ module id_stage #(
fetch_entry_ready_o = 1'b0;
// Clear the valid flag if issue has acknowledged the instruction
if (issue_instr_ack_i)
issue_n.valid = 1'b0;
if (issue_instr_ack_i) issue_n.valid = 1'b0;
// if we have a space in the register and the fetch is valid, go get it
// or the issue stage is currently acknowledging an instruction, which means that we will have space
@ -126,14 +125,13 @@ module id_stage #(
end
// invalidate the pipeline register on a flush
if (flush_i)
issue_n.valid = 1'b0;
if (flush_i) issue_n.valid = 1'b0;
end
// -------------------------
// Registers (ID <-> Issue)
// -------------------------
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
if (~rst_ni) begin
issue_q <= '0;
end else begin
issue_q <= issue_n;

View file

@ -20,7 +20,7 @@
// configuration in case Ariane is
// instantiated in OpenPiton
`ifdef PITON_ARIANE
`include "l15.tmp.h"
`include "l15.tmp.h"
`endif
/// This package contains `functions` and global defines for CVA6.
@ -30,7 +30,9 @@ package ariane_pkg;
// TODO: Slowly move those parameters to the new system.
localparam NR_SB_ENTRIES = cva6_config_pkg::CVA6ConfigNrScoreboardEntries; // number of scoreboard entries
localparam TRANS_ID_BITS = $clog2(NR_SB_ENTRIES); // depending on the number of scoreboard entries we need that many bits
localparam TRANS_ID_BITS = $clog2(
NR_SB_ENTRIES
); // depending on the number of scoreboard entries we need that many bits
// to uniquely identify the entry in the scoreboard
localparam ASID_WIDTH = (riscv::XLEN == 64) ? 16 : 1;
localparam BITS_SATURATION_COUNTER = 2;
@ -63,8 +65,8 @@ package ariane_pkg;
localparam int unsigned LAT_NONCOMP = 'd1;
localparam int unsigned LAT_CONV = 'd2;
localparam riscv::xlen_t OPENHWGROUP_MVENDORID = {{riscv::XLEN-32{1'b0}}, 32'h0602};
localparam riscv::xlen_t ARIANE_MARCHID = {{riscv::XLEN-32{1'b0}}, 32'd3};
localparam riscv::xlen_t OPENHWGROUP_MVENDORID = {{riscv::XLEN - 32{1'b0}}, 32'h0602};
localparam riscv::xlen_t ARIANE_MARCHID = {{riscv::XLEN - 32{1'b0}}, 32'd3};
// 32 registers
localparam REG_ADDR_SIZE = 5;
@ -154,7 +156,10 @@ package ariane_pkg;
localparam DATA_USER_EN = cva6_config_pkg::CVA6ConfigDataUserEn;
localparam FETCH_USER_EN = cva6_config_pkg::CVA6ConfigFetchUserEn;
typedef enum logic { SINGLE_REQ, CACHE_LINE_REQ } ad_req_t;
typedef enum logic {
SINGLE_REQ,
CACHE_LINE_REQ
} ad_req_t;
// ---------------
// Fetch Stage
@ -165,7 +170,9 @@ package ariane_pkg;
localparam int unsigned FETCH_WIDTH = 32;
// maximum instructions we can fetch on one request (we support compressed instructions)
localparam int unsigned INSTR_PER_FETCH = RVC == 1'b1 ? (FETCH_WIDTH / 16) : 1;
localparam int unsigned LOG2_INSTR_PER_FETCH = RVC == 1'b1 ? $clog2(ariane_pkg::INSTR_PER_FETCH) : 1;
localparam int unsigned LOG2_INSTR_PER_FETCH = RVC == 1'b1 ? $clog2(
ariane_pkg::INSTR_PER_FETCH
) : 1;
// ---------------
// Enable BITMANIP
@ -242,7 +249,7 @@ package ariane_pkg;
logic [1:0] saturation_counter;
} bht_t;
typedef enum logic[3:0] {
typedef enum logic [3:0] {
NONE, // 0
LOAD, // 1
STORE, // 2
@ -275,7 +282,7 @@ package ariane_pkg;
// Cache config
// ---------------
// for usage in OpenPiton we have to propagate the openpiton L15 configuration from l15.h
// for usage in OpenPiton we have to propagate the openpiton L15 configuration from l15.h
`ifdef PITON_ARIANE
`ifndef CONFIG_L1I_CACHELINE_WIDTH
@ -325,15 +332,19 @@ package ariane_pkg;
// I$
localparam int unsigned CONFIG_L1I_SIZE = cva6_config_pkg::CVA6ConfigIcacheByteSize; // in byte
localparam int unsigned ICACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigIcacheSetAssoc; // number of ways
localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(CONFIG_L1I_SIZE / ICACHE_SET_ASSOC); // in bit, contains also offset width
localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN-ICACHE_INDEX_WIDTH; // in bit
localparam int unsigned ICACHE_INDEX_WIDTH = $clog2(
CONFIG_L1I_SIZE / ICACHE_SET_ASSOC
); // in bit, contains also offset width
localparam int unsigned ICACHE_TAG_WIDTH = riscv::PLEN - ICACHE_INDEX_WIDTH; // in bit
localparam int unsigned ICACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit
localparam int unsigned ICACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigIcacheLineWidth; // in bit
// D$
localparam int unsigned CONFIG_L1D_SIZE = cva6_config_pkg::CVA6ConfigDcacheByteSize; // in byte
localparam int unsigned DCACHE_SET_ASSOC = cva6_config_pkg::CVA6ConfigDcacheSetAssoc; // number of ways
localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(CONFIG_L1D_SIZE / DCACHE_SET_ASSOC); // in bit, contains also offset width
localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN-DCACHE_INDEX_WIDTH; // in bit
localparam int unsigned DCACHE_INDEX_WIDTH = $clog2(
CONFIG_L1D_SIZE / DCACHE_SET_ASSOC
); // in bit, contains also offset width
localparam int unsigned DCACHE_TAG_WIDTH = riscv::PLEN - DCACHE_INDEX_WIDTH; // in bit
localparam int unsigned DCACHE_LINE_WIDTH = cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit
localparam int unsigned DCACHE_USER_LINE_WIDTH = (AXI_USER_WIDTH == 1) ? 4 : cva6_config_pkg::CVA6ConfigDcacheLineWidth; // in bit
localparam int unsigned DCACHE_USER_WIDTH = DATA_USER_WIDTH;
@ -350,69 +361,209 @@ package ariane_pkg;
// ---------------
typedef enum logic [7:0] { // basic ALU op
ADD, SUB, ADDW, SUBW,
ADD,
SUB,
ADDW,
SUBW,
// logic operations
XORL, ORL, ANDL,
XORL,
ORL,
ANDL,
// shifts
SRA, SRL, SLL, SRLW, SLLW, SRAW,
SRA,
SRL,
SLL,
SRLW,
SLLW,
SRAW,
// comparisons
LTS, LTU, GES, GEU, EQ, NE,
LTS,
LTU,
GES,
GEU,
EQ,
NE,
// jumps
JALR, BRANCH,
JALR,
BRANCH,
// set lower than operations
SLTS, SLTU,
SLTS,
SLTU,
// CSR functions
MRET, SRET, DRET, ECALL, WFI, FENCE, FENCE_I, SFENCE_VMA, CSR_WRITE, CSR_READ, CSR_SET, CSR_CLEAR,
MRET,
SRET,
DRET,
ECALL,
WFI,
FENCE,
FENCE_I,
SFENCE_VMA,
CSR_WRITE,
CSR_READ,
CSR_SET,
CSR_CLEAR,
// LSU functions
LD, SD, LW, LWU, SW, LH, LHU, SH, LB, SB, LBU,
LD,
SD,
LW,
LWU,
SW,
LH,
LHU,
SH,
LB,
SB,
LBU,
// Atomic Memory Operations
AMO_LRW, AMO_LRD, AMO_SCW, AMO_SCD,
AMO_SWAPW, AMO_ADDW, AMO_ANDW, AMO_ORW, AMO_XORW, AMO_MAXW, AMO_MAXWU, AMO_MINW, AMO_MINWU,
AMO_SWAPD, AMO_ADDD, AMO_ANDD, AMO_ORD, AMO_XORD, AMO_MAXD, AMO_MAXDU, AMO_MIND, AMO_MINDU,
AMO_LRW,
AMO_LRD,
AMO_SCW,
AMO_SCD,
AMO_SWAPW,
AMO_ADDW,
AMO_ANDW,
AMO_ORW,
AMO_XORW,
AMO_MAXW,
AMO_MAXWU,
AMO_MINW,
AMO_MINWU,
AMO_SWAPD,
AMO_ADDD,
AMO_ANDD,
AMO_ORD,
AMO_XORD,
AMO_MAXD,
AMO_MAXDU,
AMO_MIND,
AMO_MINDU,
// Multiplications
MUL, MULH, MULHU, MULHSU, MULW,
MUL,
MULH,
MULHU,
MULHSU,
MULW,
// Divisions
DIV, DIVU, DIVW, DIVUW, REM, REMU, REMW, REMUW,
DIV,
DIVU,
DIVW,
DIVUW,
REM,
REMU,
REMW,
REMUW,
// Floating-Point Load and Store Instructions
FLD, FLW, FLH, FLB, FSD, FSW, FSH, FSB,
FLD,
FLW,
FLH,
FLB,
FSD,
FSW,
FSH,
FSB,
// Floating-Point Computational Instructions
FADD, FSUB, FMUL, FDIV, FMIN_MAX, FSQRT, FMADD, FMSUB, FNMSUB, FNMADD,
FADD,
FSUB,
FMUL,
FDIV,
FMIN_MAX,
FSQRT,
FMADD,
FMSUB,
FNMSUB,
FNMADD,
// Floating-Point Conversion and Move Instructions
FCVT_F2I, FCVT_I2F, FCVT_F2F, FSGNJ, FMV_F2X, FMV_X2F,
FCVT_F2I,
FCVT_I2F,
FCVT_F2F,
FSGNJ,
FMV_F2X,
FMV_X2F,
// Floating-Point Compare Instructions
FCMP,
// Floating-Point Classify Instruction
FCLASS,
// Vectorial Floating-Point Instructions that don't directly map onto the scalar ones
VFMIN, VFMAX, VFSGNJ, VFSGNJN, VFSGNJX, VFEQ, VFNE, VFLT, VFGE, VFLE, VFGT, VFCPKAB_S, VFCPKCD_S, VFCPKAB_D, VFCPKCD_D,
VFMIN,
VFMAX,
VFSGNJ,
VFSGNJN,
VFSGNJX,
VFEQ,
VFNE,
VFLT,
VFGE,
VFLE,
VFGT,
VFCPKAB_S,
VFCPKCD_S,
VFCPKAB_D,
VFCPKCD_D,
// Offload Instructions to be directed into cv_x_if
OFFLOAD,
// Or-Combine and REV8
ORCB, REV8,
ORCB,
REV8,
// Bitwise Rotation
ROL, ROLW, ROR, RORI, RORIW, RORW,
ROL,
ROLW,
ROR,
RORI,
RORIW,
RORW,
// Sign and Zero Extend
SEXTB, SEXTH, ZEXTH,
SEXTB,
SEXTH,
ZEXTH,
// Count population
CPOP, CPOPW,
CPOP,
CPOPW,
// Count Leading/Training Zeros
CLZ, CLZW, CTZ, CTZW,
CLZ,
CLZW,
CTZ,
CTZW,
// Carry less multiplication Op's
CLMUL, CLMULH, CLMULR,
CLMUL,
CLMULH,
CLMULR,
// Single bit instructions Op's
BCLR, BCLRI, BEXT, BEXTI, BINV, BINVI, BSET, BSETI,
BCLR,
BCLRI,
BEXT,
BEXTI,
BINV,
BINVI,
BSET,
BSETI,
// Integer minimum/maximum
MAX, MAXU, MIN, MINU,
MAX,
MAXU,
MIN,
MINU,
// Shift with Add Unsigned Word and Unsigned Word Op's (Bitmanip)
SH1ADDUW, SH2ADDUW, SH3ADDUW, ADDUW, SLLIUW,
SH1ADDUW,
SH2ADDUW,
SH3ADDUW,
ADDUW,
SLLIUW,
// Shift with Add (Bitmanip)
SH1ADD, SH2ADD, SH3ADD,
SH1ADD,
SH2ADD,
SH3ADD,
// Bitmanip Logical with negate op (Bitmanip)
ANDN, ORN, XNOR,
ANDN,
ORN,
XNOR,
// Accelerator operations
ACCEL_OP, ACCEL_OP_FS1, ACCEL_OP_FD, ACCEL_OP_LOAD, ACCEL_OP_STORE,
ACCEL_OP,
ACCEL_OP_FS1,
ACCEL_OP_FD,
ACCEL_OP_LOAD,
ACCEL_OP_STORE,
// Zicond instruction
CZERO_EQZ, CZERO_NEZ
CZERO_EQZ,
CZERO_NEZ
} fu_op;
typedef struct packed {
@ -424,10 +575,10 @@ package ariane_pkg;
logic [TRANS_ID_BITS-1:0] trans_id;
} fu_data_t;
function automatic logic op_is_branch (input fu_op op);
function automatic logic op_is_branch(input fu_op op);
unique case (op) inside
EQ, NE, LTS, GES, LTU, GEU: return 1'b1;
default : return 1'b0; // all other ops
default: return 1'b0; // all other ops
endcase
endfunction
@ -436,68 +587,72 @@ package ariane_pkg;
// -------------------------------
// function used in instr_trace svh
// is_rs1_fpr function is kept to allow cva6 compilation with instr_trace feature
function automatic logic is_rs1_fpr (input fu_op op);
function automatic logic is_rs1_fpr(input fu_op op);
unique case (op) inside
[FMUL:FNMADD], // Computational Operations (except ADD/SUB)
[FMUL : FNMADD], // Computational Operations (except ADD/SUB)
FCVT_F2I, // Float-Int Casts
FCVT_F2F, // Float-Float Casts
FSGNJ, // Sign Injections
FMV_F2X, // FPR-GPR Moves
FCMP, // Comparisons
FCLASS, // Classifications
[VFMIN:VFCPKCD_D], // Additional Vectorial FP ops
ACCEL_OP_FS1 : return 1'b1; // Accelerator instructions
default : return 1'b0; // all other ops
[VFMIN : VFCPKCD_D], // Additional Vectorial FP ops
ACCEL_OP_FS1:
return 1'b1; // Accelerator instructions
default: return 1'b0; // all other ops
endcase
endfunction
// function used in instr_trace svh
// is_rs2_fpr function is kept to allow cva6 compilation with instr_trace feature
function automatic logic is_rs2_fpr (input fu_op op);
function automatic logic is_rs2_fpr(input fu_op op);
unique case (op) inside
[FSD:FSB], // FP Stores
[FADD:FMIN_MAX], // Computational Operations (no sqrt)
[FMADD:FNMADD], // Fused Computational Operations
[FSD : FSB], // FP Stores
[FADD : FMIN_MAX], // Computational Operations (no sqrt)
[FMADD : FNMADD], // Fused Computational Operations
FCVT_F2F, // Vectorial F2F Conversions requrie target
[FSGNJ:FMV_F2X], // Sign Injections and moves mapped to SGNJ
[FSGNJ : FMV_F2X], // Sign Injections and moves mapped to SGNJ
FCMP, // Comparisons
[VFMIN:VFCPKCD_D] : return 1'b1; // Additional Vectorial FP ops
default : return 1'b0; // all other ops
[VFMIN : VFCPKCD_D]:
return 1'b1; // Additional Vectorial FP ops
default: return 1'b0; // all other ops
endcase
endfunction
// function used in instr_trace svh
// is_imm_fpr function is kept to allow cva6 compilation with instr_trace feature
// ternary operations encode the rs3 address in the imm field, also add/sub
function automatic logic is_imm_fpr (input fu_op op);
function automatic logic is_imm_fpr(input fu_op op);
unique case (op) inside
[FADD:FSUB], // ADD/SUB need inputs as Operand B/C
[FMADD:FNMADD], // Fused Computational Operations
[VFCPKAB_S:VFCPKCD_D] : return 1'b1; // Vectorial FP cast and pack ops
default : return 1'b0; // all other ops
[FADD : FSUB], // ADD/SUB need inputs as Operand B/C
[FMADD : FNMADD], // Fused Computational Operations
[VFCPKAB_S : VFCPKCD_D]:
return 1'b1; // Vectorial FP cast and pack ops
default: return 1'b0; // all other ops
endcase
endfunction
// function used in instr_trace svh
// is_rd_fpr function is kept to allow cva6 compilation with instr_trace feature
function automatic logic is_rd_fpr (input fu_op op);
function automatic logic is_rd_fpr(input fu_op op);
unique case (op) inside
[FLD:FLB], // FP Loads
[FADD:FNMADD], // Computational Operations
[FLD : FLB], // FP Loads
[FADD : FNMADD], // Computational Operations
FCVT_I2F, // Int-Float Casts
FCVT_F2F, // Float-Float Casts
FSGNJ, // Sign Injections
FMV_X2F, // GPR-FPR Moves
[VFMIN:VFSGNJX], // Vectorial MIN/MAX and SGNJ
[VFCPKAB_S:VFCPKCD_D], // Vectorial FP cast and pack ops
ACCEL_OP_FD : return 1'b1; // Accelerator instructions
default : return 1'b0; // all other ops
[VFMIN : VFSGNJX], // Vectorial MIN/MAX and SGNJ
[VFCPKAB_S : VFCPKCD_D], // Vectorial FP cast and pack ops
ACCEL_OP_FD:
return 1'b1; // Accelerator instructions
default: return 1'b0; // all other ops
endcase
endfunction
function automatic logic is_amo (fu_op op);
function automatic logic is_amo(fu_op op);
case (op) inside
[AMO_LRW:AMO_MINDU]: begin
[AMO_LRW : AMO_MINDU]: begin
return 1'b1;
end
default: return 1'b0;
@ -580,20 +735,20 @@ package ariane_pkg;
// Atomics
// --------------------
typedef enum logic [3:0] {
AMO_NONE =4'b0000,
AMO_LR =4'b0001,
AMO_SC =4'b0010,
AMO_SWAP =4'b0011,
AMO_ADD =4'b0100,
AMO_AND =4'b0101,
AMO_OR =4'b0110,
AMO_XOR =4'b0111,
AMO_MAX =4'b1000,
AMO_MAXU =4'b1001,
AMO_MIN =4'b1010,
AMO_MINU =4'b1011,
AMO_CAS1 =4'b1100, // unused, not part of riscv spec, but provided in OpenPiton
AMO_CAS2 =4'b1101 // unused, not part of riscv spec, but provided in OpenPiton
AMO_NONE = 4'b0000,
AMO_LR = 4'b0001,
AMO_SC = 4'b0010,
AMO_SWAP = 4'b0011,
AMO_ADD = 4'b0100,
AMO_AND = 4'b0101,
AMO_OR = 4'b0110,
AMO_XOR = 4'b0111,
AMO_MAX = 4'b1000,
AMO_MAXU = 4'b1001,
AMO_MIN = 4'b1010,
AMO_MINU = 4'b1011,
AMO_CAS1 = 4'b1100, // unused, not part of riscv spec, but provided in OpenPiton
AMO_CAS2 = 4'b1101 // unused, not part of riscv spec, but provided in OpenPiton
} amo_t;
typedef struct packed {
@ -700,38 +855,54 @@ package ariane_pkg;
// ----------------------
// Arithmetic Functions
// ----------------------
function automatic riscv::xlen_t sext32 (logic [31:0] operand);
return {{riscv::XLEN-32{operand[31]}}, operand[31:0]};
function automatic riscv::xlen_t sext32(logic [31:0] operand);
return {{riscv::XLEN - 32{operand[31]}}, operand[31:0]};
endfunction
// ----------------------
// Immediate functions
// ----------------------
function automatic logic [riscv::VLEN-1:0] uj_imm (logic [31:0] instruction_i);
return { {44+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[19:12], instruction_i[20], instruction_i[30:21], 1'b0 };
function automatic logic [riscv::VLEN-1:0] uj_imm(logic [31:0] instruction_i);
return {
{44 + riscv::VLEN - 64{instruction_i[31]}},
instruction_i[19:12],
instruction_i[20],
instruction_i[30:21],
1'b0
};
endfunction
function automatic logic [riscv::VLEN-1:0] i_imm (logic [31:0] instruction_i);
return { {52+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[31:20] };
function automatic logic [riscv::VLEN-1:0] i_imm(logic [31:0] instruction_i);
return {{52 + riscv::VLEN - 64{instruction_i[31]}}, instruction_i[31:20]};
endfunction
function automatic logic [riscv::VLEN-1:0] sb_imm (logic [31:0] instruction_i);
return { {51+riscv::VLEN-64 {instruction_i[31]}}, instruction_i[31], instruction_i[7], instruction_i[30:25], instruction_i[11:8], 1'b0 };
function automatic logic [riscv::VLEN-1:0] sb_imm(logic [31:0] instruction_i);
return {
{51 + riscv::VLEN - 64{instruction_i[31]}},
instruction_i[31],
instruction_i[7],
instruction_i[30:25],
instruction_i[11:8],
1'b0
};
endfunction
// ----------------------
// LSU Functions
// ----------------------
// align data to address e.g.: shift data to be naturally 64
function automatic riscv::xlen_t data_align (logic [2:0] addr, logic [63:0] data);
function automatic riscv::xlen_t data_align(logic [2:0] addr, logic [63:0] data);
// Set addr[2] to 1'b0 when 32bits
logic [2:0] addr_tmp = {(addr[2] && riscv::IS_XLEN64), addr[1:0]};
logic [ 2:0] addr_tmp = {(addr[2] && riscv::IS_XLEN64), addr[1:0]};
logic [63:0] data_tmp = {64{1'b0}};
case (addr_tmp)
3'b000: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-1:0]};
3'b001: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-9:0], data[riscv::XLEN-1:riscv::XLEN-8]};
3'b010: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-17:0], data[riscv::XLEN-1:riscv::XLEN-16]};
3'b011: data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-25:0], data[riscv::XLEN-1:riscv::XLEN-24]};
3'b001:
data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-9:0], data[riscv::XLEN-1:riscv::XLEN-8]};
3'b010:
data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-17:0], data[riscv::XLEN-1:riscv::XLEN-16]};
3'b011:
data_tmp[riscv::XLEN-1:0] = {data[riscv::XLEN-25:0], data[riscv::XLEN-1:riscv::XLEN-24]};
3'b100: data_tmp = {data[31:0], data[63:32]};
3'b101: data_tmp = {data[23:0], data[63:24]};
3'b110: data_tmp = {data[15:0], data[63:16]};

View file

@ -31,7 +31,7 @@ package config_pkg;
WB = 0,
WT = 1,
HPDCACHE = 2
} cache_type_t ;
} cache_type_t;
localparam NrMaxRules = 16;
@ -117,29 +117,29 @@ package config_pkg;
/// Utility function being called to check parameters. Not all values make
/// sense for all parameters, here is the place to sanity check them.
function automatic void check_cfg (cva6_cfg_t Cfg);
function automatic void check_cfg(cva6_cfg_t Cfg);
// pragma translate_off
`ifndef VERILATOR
assert(Cfg.RASDepth > 0);
assert(2**$clog2(Cfg.BTBEntries) == Cfg.BTBEntries);
assert(2**$clog2(Cfg.BHTEntries) == Cfg.BHTEntries);
assert(Cfg.NrNonIdempotentRules <= NrMaxRules);
assert(Cfg.NrExecuteRegionRules <= NrMaxRules);
assert(Cfg.NrCachedRegionRules <= NrMaxRules);
assert(Cfg.NrPMPEntries <= 16);
`endif
`ifndef VERILATOR
assert (Cfg.RASDepth > 0);
assert (2 ** $clog2(Cfg.BTBEntries) == Cfg.BTBEntries);
assert (2 ** $clog2(Cfg.BHTEntries) == Cfg.BHTEntries);
assert (Cfg.NrNonIdempotentRules <= NrMaxRules);
assert (Cfg.NrExecuteRegionRules <= NrMaxRules);
assert (Cfg.NrCachedRegionRules <= NrMaxRules);
assert (Cfg.NrPMPEntries <= 16);
`endif
// pragma translate_on
endfunction
function automatic logic range_check(logic[63:0] base, logic[63:0] len, logic[63:0] address);
function automatic logic range_check(logic [63:0] base, logic [63:0] len, logic [63:0] address);
// if len is a power of two, and base is properly aligned, this check could be simplified
// Extend base by one bit to prevent an overflow.
return (address >= base) && (({1'b0, address}) < (65'(base)+len));
return (address >= base) && (({1'b0, address}) < (65'(base) + len));
endfunction : range_check
function automatic logic is_inside_nonidempotent_regions (cva6_cfg_t Cfg, logic[63:0] address);
logic[NrMaxRules-1:0] pass;
function automatic logic is_inside_nonidempotent_regions(cva6_cfg_t Cfg, logic [63:0] address);
logic [NrMaxRules-1:0] pass;
pass = '0;
for (int unsigned k = 0; k < Cfg.NrNonIdempotentRules; k++) begin
pass[k] = range_check(Cfg.NonIdempotentAddrBase[k], Cfg.NonIdempotentLength[k], address);
@ -147,9 +147,9 @@ package config_pkg;
return |pass;
endfunction : is_inside_nonidempotent_regions
function automatic logic is_inside_execute_regions (cva6_cfg_t Cfg, logic[63:0] address);
function automatic logic is_inside_execute_regions(cva6_cfg_t Cfg, logic [63:0] address);
// if we don't specify any region we assume everything is accessible
logic[NrMaxRules-1:0] pass;
logic [NrMaxRules-1:0] pass;
pass = '0;
for (int unsigned k = 0; k < Cfg.NrExecuteRegionRules; k++) begin
pass[k] = range_check(Cfg.ExecuteRegionAddrBase[k], Cfg.ExecuteRegionLength[k], address);
@ -157,8 +157,8 @@ package config_pkg;
return |pass;
endfunction : is_inside_execute_regions
function automatic logic is_inside_cacheable_regions (cva6_cfg_t Cfg, logic[63:0] address);
automatic logic[NrMaxRules-1:0] pass;
function automatic logic is_inside_cacheable_regions(cva6_cfg_t Cfg, logic [63:0] address);
automatic logic [NrMaxRules-1:0] pass;
pass = '0;
for (int unsigned k = 0; k < Cfg.NrCachedRegionRules; k++) begin
pass[k] = range_check(Cfg.CachedRegionAddrBase[k], Cfg.CachedRegionLength[k], address);

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -90,7 +90,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -111,15 +114,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -90,7 +90,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -111,15 +114,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_L15_BIG_ENDIAN,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -98,7 +98,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -119,15 +122,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_L15_BIG_ENDIAN,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -91,7 +91,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -112,15 +115,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -90,7 +90,10 @@ package cva6_config_pkg;
CvxifEn: bit'(CVA6ConfigCvxifEn),
ZiCondExtEn: bit'(0),
// Extended
RVF: bit'(0),
RVF:
bit'(
0
),
RVD: bit'(0),
FpPresent: bit'(0),
NSX: bit'(0),
@ -111,15 +114,24 @@ package cva6_config_pkg;
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
// idempotent region
NrNonIdempotentRules: unsigned'(2),
NrNonIdempotentRules:
unsigned'(
2
),
NonIdempotentAddrBase: 1024'({64'b0, 64'b0}),
NonIdempotentLength: 1024'({64'b0, 64'b0}),
NrExecuteRegionRules: unsigned'(3),
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}),
ExecuteRegionAddrBase:
1024'(
{64'h8000_0000, 64'h1_0000, 64'h0}
),
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
// cached region
NrCachedRegionRules: unsigned'(1),
NrCachedRegionRules:
unsigned'(
1
),
CachedRegionAddrBase: 1024'({64'h8000_0000}),
CachedRegionLength: 1024'({64'h40000000})
};

View file

@ -27,11 +27,9 @@ package hpdcache_params_pkg;
// Definition of constants used only in this file
// {{{
localparam int unsigned __BYTES_PER_WAY =
CVA6ConfigDcacheByteSize/CVA6ConfigDcacheSetAssoc;
localparam int unsigned __BYTES_PER_WAY = CVA6ConfigDcacheByteSize / CVA6ConfigDcacheSetAssoc;
localparam int unsigned __BYTES_PER_CACHELINE =
CVA6ConfigDcacheLineWidth/8;
localparam int unsigned __BYTES_PER_CACHELINE = CVA6ConfigDcacheLineWidth / 8;
// }}}
// Definition of global constants for the HPDcache data and directory
@ -40,7 +38,7 @@ package hpdcache_params_pkg;
localparam int unsigned PARAM_PA_WIDTH = riscv::PLEN;
// HPDcache number of sets
localparam int unsigned PARAM_SETS = __BYTES_PER_WAY/__BYTES_PER_CACHELINE;
localparam int unsigned PARAM_SETS = __BYTES_PER_WAY / __BYTES_PER_CACHELINE;
// HPDcache number of ways
localparam int unsigned PARAM_WAYS = CVA6ConfigDcacheSetAssoc;
@ -49,7 +47,7 @@ package hpdcache_params_pkg;
localparam int unsigned PARAM_WORD_WIDTH = CVA6ConfigXlen;
// HPDcache cache-line width (bits)
localparam int unsigned PARAM_CL_WORDS = CVA6ConfigDcacheLineWidth/PARAM_WORD_WIDTH;
localparam int unsigned PARAM_CL_WORDS = CVA6ConfigDcacheLineWidth / PARAM_WORD_WIDTH;
// HPDcache number of words in the request data channels (request and response)
localparam int unsigned PARAM_REQ_WORDS = 1;
@ -63,7 +61,7 @@ package hpdcache_params_pkg;
// Definition of constants and types for HPDcache data memory
// {{{
localparam int unsigned PARAM_DATA_WAYS_PER_RAM_WORD = 128/PARAM_WORD_WIDTH;
localparam int unsigned PARAM_DATA_WAYS_PER_RAM_WORD = 128 / PARAM_WORD_WIDTH;
localparam int unsigned PARAM_DATA_SETS_PER_RAM = PARAM_SETS;
// HPDcache DATA RAM macros whether implements:
@ -75,7 +73,7 @@ package hpdcache_params_pkg;
// simultaneously from the cache.
// - This limits the maximum width for the data channel from requesters
// - This impacts the refill latency (more ACCESS_WORDS -> less REFILL LATENCY)
localparam int unsigned PARAM_ACCESS_WORDS = PARAM_CL_WORDS/2;
localparam int unsigned PARAM_ACCESS_WORDS = PARAM_CL_WORDS / 2;
// }}}
// Definition of constants and types for the Miss Status Holding Register (MSHR)
@ -98,7 +96,7 @@ package hpdcache_params_pkg;
localparam bit PARAM_MSHR_RAM_WBYTEENABLE = 1'b1;
// HPDcache MSHR whether uses FFs or SRAM
localparam bit PARAM_MSHR_USE_REGBANK = (PARAM_MSHR_SETS*PARAM_MSHR_WAYS) <= 16;
localparam bit PARAM_MSHR_USE_REGBANK = (PARAM_MSHR_SETS * PARAM_MSHR_WAYS) <= 16;
// }}}
// Definition of constants and types for the Write Buffer (WBUF)

View file

@ -71,7 +71,7 @@ package cvxif_pkg;
logic [X_ID_WIDTH-1:0] id;
logic [X_MEM_WIDTH-1:0] rdata;
logic err;
} x_mem_result_t ;
} x_mem_result_t;
typedef struct packed {
logic [X_ID_WIDTH-1:0] id;
@ -80,7 +80,7 @@ package cvxif_pkg;
logic we;
logic exc;
logic [5:0] exccode;
} x_result_t ;
} x_result_t;
typedef struct packed {
logic x_compressed_valid;

View file

@ -17,125 +17,125 @@ package instr_tracer_pkg;
parameter INSTR_NOP = 32'h00_00_00_13;
parameter INSTR_LUI = { 25'b?, riscv::OpcodeLui };
parameter INSTR_AUIPC = { 25'b?, riscv::OpcodeAuipc };
parameter INSTR_JAL = { 25'b?, riscv::OpcodeJal };
parameter INSTR_JALR = { 17'b?, 3'b000, 5'b?, riscv::OpcodeJalr };
parameter INSTR_LUI = {25'b?, riscv::OpcodeLui};
parameter INSTR_AUIPC = {25'b?, riscv::OpcodeAuipc};
parameter INSTR_JAL = {25'b?, riscv::OpcodeJal};
parameter INSTR_JALR = {17'b?, 3'b000, 5'b?, riscv::OpcodeJalr};
// BRANCH
parameter INSTR_BEQZ = { 7'b?, 5'b0, 5'b?, 3'b000, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BEQ = { 7'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BNEZ = { 7'b?, 5'b0, 5'b?, 3'b001, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BNE = { 7'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BLTZ = { 7'b?, 5'b0, 5'b?, 3'b100, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BLT = { 7'b?, 5'b?, 5'b?, 3'b100, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BGEZ = { 7'b?, 5'b0, 5'b?, 3'b101, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BGE = { 7'b?, 5'b?, 5'b?, 3'b101, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BLTU = { 7'b?, 5'b?, 5'b?, 3'b110, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BGEU = { 7'b?, 5'b?, 5'b?, 3'b111, 5'b?, riscv::OpcodeBranch };
parameter INSTR_BEQZ = {7'b?, 5'b0, 5'b?, 3'b000, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BEQ = {7'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BNEZ = {7'b?, 5'b0, 5'b?, 3'b001, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BNE = {7'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BLTZ = {7'b?, 5'b0, 5'b?, 3'b100, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BLT = {7'b?, 5'b?, 5'b?, 3'b100, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BGEZ = {7'b?, 5'b0, 5'b?, 3'b101, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BGE = {7'b?, 5'b?, 5'b?, 3'b101, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BLTU = {7'b?, 5'b?, 5'b?, 3'b110, 5'b?, riscv::OpcodeBranch};
parameter INSTR_BGEU = {7'b?, 5'b?, 5'b?, 3'b111, 5'b?, riscv::OpcodeBranch};
// OP-IMM
parameter INSTR_LI = { 12'b?, 5'b0, 3'b000, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_ADDI = { 17'b?, 3'b000, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_SLTI = { 17'b?, 3'b010, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_SLTIU = { 17'b?, 3'b011, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_XORI = { 17'b?, 3'b100, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_ORI = { 17'b?, 3'b110, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_ANDI = { 17'b?, 3'b111, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_SLLI = { 6'b000000, 11'b?, 3'b001, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_SRLI = { 6'b000000, 11'b?, 3'b101, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_SRAI = { 6'b010000, 11'b?, 3'b101, 5'b?, riscv::OpcodeOpImm };
parameter INSTR_LI = {12'b?, 5'b0, 3'b000, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_ADDI = {17'b?, 3'b000, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_SLTI = {17'b?, 3'b010, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_SLTIU = {17'b?, 3'b011, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_XORI = {17'b?, 3'b100, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_ORI = {17'b?, 3'b110, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_ANDI = {17'b?, 3'b111, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_SLLI = {6'b000000, 11'b?, 3'b001, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_SRLI = {6'b000000, 11'b?, 3'b101, 5'b?, riscv::OpcodeOpImm};
parameter INSTR_SRAI = {6'b010000, 11'b?, 3'b101, 5'b?, riscv::OpcodeOpImm};
// OP-IMM-32
parameter INSTR_ADDIW = { 17'b?, 3'b000, 5'b?, riscv::OpcodeOpImm32 };
parameter INSTR_SLLIW = { 7'b0000000, 10'b?, 3'b001, 5'b?, riscv::OpcodeOpImm32 };
parameter INSTR_SRLIW = { 7'b0000000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOpImm32 };
parameter INSTR_SRAIW = { 7'b0100000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOpImm32 };
parameter INSTR_ADDIW = {17'b?, 3'b000, 5'b?, riscv::OpcodeOpImm32};
parameter INSTR_SLLIW = {7'b0000000, 10'b?, 3'b001, 5'b?, riscv::OpcodeOpImm32};
parameter INSTR_SRLIW = {7'b0000000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOpImm32};
parameter INSTR_SRAIW = {7'b0100000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOpImm32};
// OP
parameter INSTR_ADD = { 7'b0000000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp };
parameter INSTR_SUB = { 7'b0100000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp };
parameter INSTR_SLL = { 7'b0000000, 10'b?, 3'b001, 5'b?, riscv::OpcodeOp };
parameter INSTR_SLT = { 7'b0000000, 10'b?, 3'b010, 5'b?, riscv::OpcodeOp };
parameter INSTR_SLTU = { 7'b0000000, 10'b?, 3'b011, 5'b?, riscv::OpcodeOp };
parameter INSTR_XOR = { 7'b0000000, 10'b?, 3'b100, 5'b?, riscv::OpcodeOp };
parameter INSTR_SRL = { 7'b0000000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp };
parameter INSTR_SRA = { 7'b0100000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp };
parameter INSTR_OR = { 7'b0000000, 10'b?, 3'b110, 5'b?, riscv::OpcodeOp };
parameter INSTR_AND = { 7'b0000000, 10'b?, 3'b111, 5'b?, riscv::OpcodeOp };
parameter INSTR_MUL = { 7'b0000001, 10'b?, 3'b???, 5'b?, riscv::OpcodeOp };
parameter INSTR_ADD = {7'b0000000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp};
parameter INSTR_SUB = {7'b0100000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp};
parameter INSTR_SLL = {7'b0000000, 10'b?, 3'b001, 5'b?, riscv::OpcodeOp};
parameter INSTR_SLT = {7'b0000000, 10'b?, 3'b010, 5'b?, riscv::OpcodeOp};
parameter INSTR_SLTU = {7'b0000000, 10'b?, 3'b011, 5'b?, riscv::OpcodeOp};
parameter INSTR_XOR = {7'b0000000, 10'b?, 3'b100, 5'b?, riscv::OpcodeOp};
parameter INSTR_SRL = {7'b0000000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp};
parameter INSTR_SRA = {7'b0100000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp};
parameter INSTR_OR = {7'b0000000, 10'b?, 3'b110, 5'b?, riscv::OpcodeOp};
parameter INSTR_AND = {7'b0000000, 10'b?, 3'b111, 5'b?, riscv::OpcodeOp};
parameter INSTR_MUL = {7'b0000001, 10'b?, 3'b???, 5'b?, riscv::OpcodeOp};
// OP32
parameter INSTR_ADDW = { 7'b0000000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp32 };
parameter INSTR_SUBW = { 7'b0100000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp32 };
parameter INSTR_SLLW = { 7'b0000000, 10'b?, 3'b001, 5'b?, riscv::OpcodeOp32 };
parameter INSTR_SRLW = { 7'b0000000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp32 };
parameter INSTR_SRAW = { 7'b0100000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp32 };
parameter INSTR_MULW = { 7'b0000001, 10'b?, 3'b???, 5'b?, riscv::OpcodeOp32 };
parameter INSTR_ADDW = {7'b0000000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp32};
parameter INSTR_SUBW = {7'b0100000, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp32};
parameter INSTR_SLLW = {7'b0000000, 10'b?, 3'b001, 5'b?, riscv::OpcodeOp32};
parameter INSTR_SRLW = {7'b0000000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp32};
parameter INSTR_SRAW = {7'b0100000, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp32};
parameter INSTR_MULW = {7'b0000001, 10'b?, 3'b???, 5'b?, riscv::OpcodeOp32};
// MISC-MEM
parameter INSTR_FENCE = { 4'b0, 8'b?, 13'b0, riscv::OpcodeMiscMem };
parameter INSTR_FENCEI = { 17'b0, 3'b001, 5'b0, riscv::OpcodeMiscMem };
parameter INSTR_FENCE = {4'b0, 8'b?, 13'b0, riscv::OpcodeMiscMem};
parameter INSTR_FENCEI = {17'b0, 3'b001, 5'b0, riscv::OpcodeMiscMem};
// SYSTEM
parameter INSTR_CSRW = { 12'b?, 5'b?, 3'b001, 5'b0, riscv::OpcodeSystem };
parameter INSTR_CSRRW = { 12'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRR = { 12'b?, 5'b0, 3'b010, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRRS = { 12'b?, 5'b?, 3'b010, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRS = { 12'b?, 5'b?, 3'b010, 5'b0, riscv::OpcodeSystem };
parameter INSTR_CSRRC = { 12'b?, 5'b?, 3'b011, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRC = { 12'b?, 5'b?, 3'b011, 5'b0, riscv::OpcodeSystem };
parameter INSTR_CSRW = {12'b?, 5'b?, 3'b001, 5'b0, riscv::OpcodeSystem};
parameter INSTR_CSRRW = {12'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeSystem};
parameter INSTR_CSRR = {12'b?, 5'b0, 3'b010, 5'b?, riscv::OpcodeSystem};
parameter INSTR_CSRRS = {12'b?, 5'b?, 3'b010, 5'b?, riscv::OpcodeSystem};
parameter INSTR_CSRS = {12'b?, 5'b?, 3'b010, 5'b0, riscv::OpcodeSystem};
parameter INSTR_CSRRC = {12'b?, 5'b?, 3'b011, 5'b?, riscv::OpcodeSystem};
parameter INSTR_CSRC = {12'b?, 5'b?, 3'b011, 5'b0, riscv::OpcodeSystem};
parameter INSTR_CSRWI = { 17'b?, 3'b101, 5'b0, riscv::OpcodeSystem };
parameter INSTR_CSRRWI = { 17'b?, 3'b101, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRSI = { 17'b?, 3'b110, 5'b0, riscv::OpcodeSystem };
parameter INSTR_CSRRSI = { 17'b?, 3'b110, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRCI = { 17'b?, 3'b111, 5'b0, riscv::OpcodeSystem };
parameter INSTR_CSRRCI = { 17'b?, 3'b111, 5'b?, riscv::OpcodeSystem };
parameter INSTR_CSRWI = {17'b?, 3'b101, 5'b0, riscv::OpcodeSystem};
parameter INSTR_CSRRWI = {17'b?, 3'b101, 5'b?, riscv::OpcodeSystem};
parameter INSTR_CSRSI = {17'b?, 3'b110, 5'b0, riscv::OpcodeSystem};
parameter INSTR_CSRRSI = {17'b?, 3'b110, 5'b?, riscv::OpcodeSystem};
parameter INSTR_CSRCI = {17'b?, 3'b111, 5'b0, riscv::OpcodeSystem};
parameter INSTR_CSRRCI = {17'b?, 3'b111, 5'b?, riscv::OpcodeSystem};
parameter INSTR_ECALL = { 12'b000000000000, 13'b0, riscv::OpcodeSystem };
parameter INSTR_EBREAK = { 12'b000000000001, 13'b0, riscv::OpcodeSystem };
parameter INSTR_MRET = { 12'b001100000010, 13'b0, riscv::OpcodeSystem };
parameter INSTR_SRET = { 12'b000100000010, 13'b0, riscv::OpcodeSystem };
parameter INSTR_DRET = { 12'b011110110010, 13'b0, riscv::OpcodeSystem };
parameter INSTR_WFI = { 12'b000100000101, 13'b0, riscv::OpcodeSystem };
parameter INSTR_SFENCE = { 12'b0001001?????, 13'b?, riscv::OpcodeSystem };
parameter INSTR_ECALL = {12'b000000000000, 13'b0, riscv::OpcodeSystem};
parameter INSTR_EBREAK = {12'b000000000001, 13'b0, riscv::OpcodeSystem};
parameter INSTR_MRET = {12'b001100000010, 13'b0, riscv::OpcodeSystem};
parameter INSTR_SRET = {12'b000100000010, 13'b0, riscv::OpcodeSystem};
parameter INSTR_DRET = {12'b011110110010, 13'b0, riscv::OpcodeSystem};
parameter INSTR_WFI = {12'b000100000101, 13'b0, riscv::OpcodeSystem};
parameter INSTR_SFENCE = {12'b0001001?????, 13'b?, riscv::OpcodeSystem};
// RV32M
parameter INSTR_PMUL = { 7'b0000001, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp };
parameter INSTR_DIV = { 7'b0000001, 10'b?, 3'b100, 5'b?, riscv::OpcodeOp };
parameter INSTR_DIVU = { 7'b0000001, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp };
parameter INSTR_REM = { 7'b0000001, 10'b?, 3'b110, 5'b?, riscv::OpcodeOp };
parameter INSTR_REMU = { 7'b0000001, 10'b?, 3'b111, 5'b?, riscv::OpcodeOp };
parameter INSTR_PMUL = {7'b0000001, 10'b?, 3'b000, 5'b?, riscv::OpcodeOp};
parameter INSTR_DIV = {7'b0000001, 10'b?, 3'b100, 5'b?, riscv::OpcodeOp};
parameter INSTR_DIVU = {7'b0000001, 10'b?, 3'b101, 5'b?, riscv::OpcodeOp};
parameter INSTR_REM = {7'b0000001, 10'b?, 3'b110, 5'b?, riscv::OpcodeOp};
parameter INSTR_REMU = {7'b0000001, 10'b?, 3'b111, 5'b?, riscv::OpcodeOp};
// RVFD
parameter INSTR_FMADD = { 5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeMadd};
parameter INSTR_FMSUB = { 5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeMsub};
parameter INSTR_FNSMSUB = { 5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeNmsub};
parameter INSTR_FNMADD = { 5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeNmadd};
parameter INSTR_FMADD = {5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeMadd};
parameter INSTR_FMSUB = {5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeMsub};
parameter INSTR_FNSMSUB = {5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeNmsub};
parameter INSTR_FNMADD = {5'b?, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeNmadd};
parameter INSTR_FADD = { 5'b00000, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSUB = { 5'b00001, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMUL = { 5'b00010, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FDIV = { 5'b00011, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSQRT = { 5'b01011, 2'b?, 5'b0, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSGNJ = { 5'b00100, 2'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSGNJN = { 5'b00100, 2'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSGNJX = { 5'b00100, 2'b?, 5'b?, 5'b?, 3'b010, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMIN = { 5'b00101, 2'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMAX = { 5'b00101, 2'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FLE = { 5'b10100, 2'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FLT = { 5'b10100, 2'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FEQ = { 5'b10100, 2'b?, 5'b?, 5'b?, 3'b010, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FADD = {5'b00000, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSUB = {5'b00001, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMUL = {5'b00010, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FDIV = {5'b00011, 2'b?, 5'b?, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSQRT = {5'b01011, 2'b?, 5'b0, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSGNJ = {5'b00100, 2'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSGNJN = {5'b00100, 2'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FSGNJX = {5'b00100, 2'b?, 5'b?, 5'b?, 3'b010, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMIN = {5'b00101, 2'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMAX = {5'b00101, 2'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FLE = {5'b10100, 2'b?, 5'b?, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FLT = {5'b10100, 2'b?, 5'b?, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FEQ = {5'b10100, 2'b?, 5'b?, 5'b?, 3'b010, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCVT_F2F = { 5'b01000, 2'b?, 5'b000??, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMV_F2X = { 5'b11100, 2'b?, 5'b0, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCLASS = { 5'b11100, 2'b?, 5'b0, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMV_X2F = { 5'b11110, 2'b?, 5'b0, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCVT_F2I = { 5'b11000, 2'b?, 5'b000??, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCVT_I2F = { 5'b11010, 2'b?, 5'b000??, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCVT_F2F = {5'b01000, 2'b?, 5'b000??, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMV_F2X = {5'b11100, 2'b?, 5'b0, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCLASS = {5'b11100, 2'b?, 5'b0, 5'b?, 3'b001, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FMV_X2F = {5'b11110, 2'b?, 5'b0, 5'b?, 3'b000, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCVT_F2I = {5'b11000, 2'b?, 5'b000??, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
parameter INSTR_FCVT_I2F = {5'b11010, 2'b?, 5'b000??, 5'b?, 3'b?, 5'b?, riscv::OpcodeOpFp};
// A
parameter INSTR_AMO = {25'b?, riscv::OpcodeAmo };
parameter INSTR_AMO = {25'b?, riscv::OpcodeAmo};
// Load/Stores
parameter [31:0] LB = 32'b?????????????????000?????0000011;

View file

@ -47,15 +47,15 @@ package riscv;
localparam PPNW = (XLEN == 32) ? 22 : 44;
localparam vm_mode_t MODE_SV = (XLEN == 32) ? ModeSv32 : ModeSv39;
localparam SV = (MODE_SV == ModeSv32) ? 32 : 39;
localparam VPN2 = (VLEN-31 < 8) ? VLEN-31 : 8;
localparam XLEN_ALIGN_BYTES = $clog2(XLEN/8);
localparam VPN2 = (VLEN - 31 < 8) ? VLEN - 31 : 8;
localparam XLEN_ALIGN_BYTES = $clog2(XLEN / 8);
typedef logic [XLEN-1:0] xlen_t;
// --------------------
// Privilege Spec
// --------------------
typedef enum logic[1:0] {
typedef enum logic [1:0] {
PRIV_LVL_M = 2'b11,
PRIV_LVL_S = 2'b01,
PRIV_LVL_U = 2'b00
@ -354,12 +354,12 @@ package riscv;
localparam logic [XLEN-1:0] MIP_SEIP = 1 << IRQ_S_EXT;
localparam logic [XLEN-1:0] MIP_MEIP = 1 << IRQ_M_EXT;
localparam logic [XLEN-1:0] S_SW_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_SOFT);
localparam logic [XLEN-1:0] M_SW_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_SOFT);
localparam logic [XLEN-1:0] S_TIMER_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_TIMER);
localparam logic [XLEN-1:0] M_TIMER_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_TIMER);
localparam logic [XLEN-1:0] S_EXT_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_S_EXT);
localparam logic [XLEN-1:0] M_EXT_INTERRUPT = (1 << (XLEN-1)) | XLEN'(IRQ_M_EXT);
localparam logic [XLEN-1:0] S_SW_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_SOFT);
localparam logic [XLEN-1:0] M_SW_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_SOFT);
localparam logic [XLEN-1:0] S_TIMER_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_TIMER);
localparam logic [XLEN-1:0] M_TIMER_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_TIMER);
localparam logic [XLEN-1:0] S_EXT_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_S_EXT);
localparam logic [XLEN-1:0] M_EXT_INTERRUPT = (1 << (XLEN - 1)) | XLEN'(IRQ_M_EXT);
// -----
// CSRs
@ -721,91 +721,97 @@ package riscv;
} dcsr_t;
// Instruction Generation *incomplete*
function automatic logic [31:0] jal (logic[4:0] rd, logic [20:0] imm);
function automatic logic [31:0] jal(logic [4:0] rd, logic [20:0] imm);
// OpCode Jal
return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f};
endfunction
function automatic logic [31:0] jalr (logic[4:0] rd, logic[4:0] rs1, logic [11:0] offset);
function automatic logic [31:0] jalr(logic [4:0] rd, logic [4:0] rs1, logic [11:0] offset);
// OpCode Jal
return {offset[11:0], rs1, 3'b0, rd, 7'h67};
endfunction
function automatic logic [31:0] andi (logic[4:0] rd, logic[4:0] rs1, logic [11:0] imm);
function automatic logic [31:0] andi(logic [4:0] rd, logic [4:0] rs1, logic [11:0] imm);
// OpCode andi
return {imm[11:0], rs1, 3'h7, rd, 7'h13};
endfunction
function automatic logic [31:0] slli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt);
function automatic logic [31:0] slli(logic [4:0] rd, logic [4:0] rs1, logic [5:0] shamt);
// OpCode slli
return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13};
endfunction
function automatic logic [31:0] srli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt);
function automatic logic [31:0] srli(logic [4:0] rd, logic [4:0] rs1, logic [5:0] shamt);
// OpCode srli
return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13};
endfunction
function automatic logic [31:0] load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset);
function automatic logic [31:0] load(logic [2:0] size, logic [4:0] dest, logic [4:0] base,
logic [11:0] offset);
// OpCode Load
return {offset[11:0], base, size, dest, 7'h03};
endfunction
function automatic logic [31:0] auipc (logic[4:0] rd, logic [20:0] imm);
function automatic logic [31:0] auipc(logic [4:0] rd, logic [20:0] imm);
// OpCode Auipc
return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17};
endfunction
function automatic logic [31:0] store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset);
function automatic logic [31:0] store(logic [2:0] size, logic [4:0] src, logic [4:0] base,
logic [11:0] offset);
// OpCode Store
return {offset[11:5], src, base, size, offset[4:0], 7'h23};
endfunction
function automatic logic [31:0] float_load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset);
function automatic logic [31:0] float_load(logic [2:0] size, logic [4:0] dest, logic [4:0] base,
logic [11:0] offset);
// OpCode Load
return {offset[11:0], base, size, dest, 7'b00_001_11};
endfunction
function automatic logic [31:0] float_store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset);
function automatic logic [31:0] float_store(logic [2:0] size, logic [4:0] src, logic [4:0] base,
logic [11:0] offset);
// OpCode Store
return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11};
endfunction
function automatic logic [31:0] csrw (csr_reg_t csr, logic[4:0] rs1);
function automatic logic [31:0] csrw(csr_reg_t csr, logic [4:0] rs1);
// CSRRW, rd, OpCode System
return {csr, rs1, 3'h1, 5'h0, 7'h73};
endfunction
function automatic logic [31:0] csrr (csr_reg_t csr, logic [4:0] dest);
function automatic logic [31:0] csrr(csr_reg_t csr, logic [4:0] dest);
// rs1, CSRRS, rd, OpCode System
return {csr, 5'h0, 3'h2, dest, 7'h73};
endfunction
function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, logic [11:0] offset);
function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3,
logic [11:0] offset);
// OpCode Branch
return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11};
endfunction
function automatic logic [31:0] ebreak ();
function automatic logic [31:0] ebreak();
return 32'h00100073;
endfunction
function automatic logic [31:0] wfi ();
function automatic logic [31:0] wfi();
return 32'h10500073;
endfunction
function automatic logic [31:0] nop ();
function automatic logic [31:0] nop();
return 32'h00000013;
endfunction
function automatic logic [31:0] illegal ();
function automatic logic [31:0] illegal();
return 32'h00000000;
endfunction
// trace log compatible to spikes commit log feature
// pragma translate_off
function string spikeCommitLog(logic [63:0] pc, priv_lvl_t priv_lvl, logic [31:0] instr, logic [4:0] rd, logic [63:0] result, logic rd_fpr);
function string spikeCommitLog(logic [63:0] pc, priv_lvl_t priv_lvl, logic [31:0] instr,
logic [4:0] rd, logic [63:0] result, logic rd_fpr);
string rd_s;
string instr_word;

View file

@ -18,9 +18,9 @@
package std_cache_pkg;
// Calculated parameter
localparam DCACHE_BYTE_OFFSET = $clog2(ariane_pkg::DCACHE_LINE_WIDTH/8);
localparam DCACHE_NUM_WORDS = 2**(ariane_pkg::DCACHE_INDEX_WIDTH-DCACHE_BYTE_OFFSET);
localparam DCACHE_DIRTY_WIDTH = ariane_pkg::DCACHE_SET_ASSOC*2;
localparam DCACHE_BYTE_OFFSET = $clog2(ariane_pkg::DCACHE_LINE_WIDTH / 8);
localparam DCACHE_NUM_WORDS = 2 ** (ariane_pkg::DCACHE_INDEX_WIDTH - DCACHE_BYTE_OFFSET);
localparam DCACHE_DIRTY_WIDTH = ariane_pkg::DCACHE_SET_ASSOC * 2;
// localparam DECISION_BIT = 30; // bit on which to decide whether the request is cache-able or not
typedef struct packed {
@ -75,18 +75,15 @@ package std_cache_pkg;
} cl_be_t;
// convert one hot to bin for -> needed for cache replacement
function automatic logic [$clog2(ariane_pkg::DCACHE_SET_ASSOC)-1:0] one_hot_to_bin (
input logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] in
);
function automatic logic [$clog2(ariane_pkg::DCACHE_SET_ASSOC)-1:0] one_hot_to_bin(
input logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] in);
for (int unsigned i = 0; i < ariane_pkg::DCACHE_SET_ASSOC; i++) begin
if (in[i])
return i;
if (in[i]) return i;
end
endfunction
// get the first bit set, returns one hot value
function automatic logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] get_victim_cl (
input logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] valid_dirty
);
function automatic logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] get_victim_cl(
input logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] valid_dirty);
// one-hot return vector
logic [ariane_pkg::DCACHE_SET_ASSOC-1:0] oh = '0;
for (int unsigned i = 0; i < ariane_pkg::DCACHE_SET_ASSOC; i++) begin

View file

@ -16,8 +16,8 @@
// configuration in case Ariane is
// instantiated in OpenPiton
`ifdef PITON_ARIANE
`include "l15.tmp.h"
`include "define.tmp.h"
`include "l15.tmp.h"
`include "define.tmp.h"
`endif
package wt_cache_pkg;
@ -51,20 +51,20 @@ package wt_cache_pkg;
// Calculated parameter
localparam ICACHE_OFFSET_WIDTH = $clog2(ariane_pkg::ICACHE_LINE_WIDTH/8);
localparam ICACHE_NUM_WORDS = 2**(ariane_pkg::ICACHE_INDEX_WIDTH-ICACHE_OFFSET_WIDTH);
localparam ICACHE_CL_IDX_WIDTH = $clog2(ICACHE_NUM_WORDS);// excluding byte offset
localparam ICACHE_OFFSET_WIDTH = $clog2(ariane_pkg::ICACHE_LINE_WIDTH / 8);
localparam ICACHE_NUM_WORDS = 2 ** (ariane_pkg::ICACHE_INDEX_WIDTH - ICACHE_OFFSET_WIDTH);
localparam ICACHE_CL_IDX_WIDTH = $clog2(ICACHE_NUM_WORDS); // excluding byte offset
localparam DCACHE_OFFSET_WIDTH = $clog2(ariane_pkg::DCACHE_LINE_WIDTH/8);
localparam DCACHE_NUM_WORDS = 2**(ariane_pkg::DCACHE_INDEX_WIDTH-DCACHE_OFFSET_WIDTH);
localparam DCACHE_CL_IDX_WIDTH = $clog2(DCACHE_NUM_WORDS);// excluding byte offset
localparam DCACHE_OFFSET_WIDTH = $clog2(ariane_pkg::DCACHE_LINE_WIDTH / 8);
localparam DCACHE_NUM_WORDS = 2 ** (ariane_pkg::DCACHE_INDEX_WIDTH - DCACHE_OFFSET_WIDTH);
localparam DCACHE_CL_IDX_WIDTH = $clog2(DCACHE_NUM_WORDS); // excluding byte offset
localparam DCACHE_NUM_BANKS = ariane_pkg::DCACHE_LINE_WIDTH/riscv::XLEN;
localparam DCACHE_NUM_BANKS = ariane_pkg::DCACHE_LINE_WIDTH / riscv::XLEN;
localparam DCACHE_NUM_BANKS_WIDTH = $clog2(DCACHE_NUM_BANKS);
// write buffer parameterization
localparam DCACHE_WBUF_DEPTH = ariane_pkg::WT_DCACHE_WBUF_DEPTH;
localparam DCACHE_MAX_TX = 2**L15_TID_WIDTH;
localparam DCACHE_MAX_TX = 2 ** L15_TID_WIDTH;
localparam CACHE_ID_WIDTH = L15_TID_WIDTH;
@ -99,7 +99,7 @@ package wt_cache_pkg;
typedef enum logic [2:0] {
DCACHE_INV_REQ, // no ack from the core required
DCACHE_STORE_ACK,// note: this may contain an invalidation vector, too
DCACHE_STORE_ACK, // note: this may contain an invalidation vector, too
DCACHE_LOAD_ACK,
DCACHE_ATOMIC_ACK,
DCACHE_INT_ACK
@ -249,78 +249,64 @@ package wt_cache_pkg;
} l15_rtrn_t;
// swap endianess in a 64bit word
function automatic logic[63:0] swendian64(input logic[63:0] in);
automatic logic[63:0] out;
for(int k=0; k<64;k+=8)begin
out[k +: 8] = in[63-k -: 8];
function automatic logic [63:0] swendian64(input logic [63:0] in);
automatic logic [63:0] out;
for (int k = 0; k < 64; k += 8) begin
out[k+:8] = in[63-k-:8];
end
return out;
endfunction
function automatic logic [5:0] popcnt64 (
input logic [63:0] in
);
logic [5:0] cnt= 0;
function automatic logic [5:0] popcnt64(input logic [63:0] in);
logic [5:0] cnt = 0;
foreach (in[k]) begin
cnt += 6'(in[k]);
end
return cnt;
endfunction : popcnt64
function automatic logic [7:0] to_byte_enable8(
input logic [2:0] offset,
input logic [1:0] size
);
function automatic logic [7:0] to_byte_enable8(input logic [2:0] offset, input logic [1:0] size);
logic [7:0] be;
be = '0;
unique case(size)
unique case (size)
2'b00: be[offset] = '1; // byte
2'b01: be[offset +:2 ] = '1; // hword
2'b10: be[offset +:4 ] = '1; // word
2'b01: be[offset+:2] = '1; // hword
2'b10: be[offset+:4] = '1; // word
default: be = '1; // dword
endcase // size
return be;
endfunction : to_byte_enable8
function automatic logic [3:0] to_byte_enable4(
input logic [1:0] offset,
input logic [1:0] size
);
function automatic logic [3:0] to_byte_enable4(input logic [1:0] offset, input logic [1:0] size);
logic [3:0] be;
be = '0;
unique case(size)
unique case (size)
2'b00: be[offset] = '1; // byte
2'b01: be[offset +:2 ] = '1; // hword
2'b01: be[offset+:2] = '1; // hword
default: be = '1; // word
endcase // size
return be;
endfunction : to_byte_enable4
// openpiton requires the data to be replicated in case of smaller sizes than dwords
function automatic logic [63:0] repData64(
input logic [63:0] data,
input logic [2:0] offset,
input logic [1:0] size
);
function automatic logic [63:0] repData64(input logic [63:0] data, input logic [2:0] offset,
input logic [1:0] size);
logic [63:0] out;
unique case(size)
2'b00: for(int k=0; k<8; k++) out[k*8 +: 8] = data[offset*8 +: 8]; // byte
2'b01: for(int k=0; k<4; k++) out[k*16 +: 16] = data[offset*8 +: 16]; // hword
2'b10: for(int k=0; k<2; k++) out[k*32 +: 32] = data[offset*8 +: 32]; // word
unique case (size)
2'b00: for (int k = 0; k < 8; k++) out[k*8+:8] = data[offset*8+:8]; // byte
2'b01: for (int k = 0; k < 4; k++) out[k*16+:16] = data[offset*8+:16]; // hword
2'b10: for (int k = 0; k < 2; k++) out[k*32+:32] = data[offset*8+:32]; // word
default: out = data; // dword
endcase // size
return out;
endfunction : repData64
function automatic logic [31:0] repData32(
input logic [31:0] data,
input logic [1:0] offset,
input logic [1:0] size
);
function automatic logic [31:0] repData32(input logic [31:0] data, input logic [1:0] offset,
input logic [1:0] size);
logic [31:0] out;
unique case(size)
2'b00: for(int k=0; k<4; k++) out[k*8 +: 8] = data[offset*8 +: 8]; // byte
2'b01: for(int k=0; k<2; k++) out[k*16 +: 16] = data[offset*8 +: 16]; // hword
unique case (size)
2'b00: for (int k = 0; k < 4; k++) out[k*8+:8] = data[offset*8+:8]; // byte
2'b01: for (int k = 0; k < 2; k++) out[k*16+:16] = data[offset*8+:16]; // hword
default: out = data; // word
endcase // size
return out;
@ -329,11 +315,9 @@ package wt_cache_pkg;
// note: this is openpiton specific. cannot transmit unaligned words.
// hence we default to individual bytes in that case, and they have to be transmitted
// one after the other
function automatic logic [1:0] toSize64(
input logic [7:0] be
);
function automatic logic [1:0] toSize64(input logic [7:0] be);
logic [1:0] size;
unique case(be)
unique case (be)
8'b1111_1111: size = 2'b11; // dword
8'b0000_1111, 8'b1111_0000: size = 2'b10; // word
8'b1100_0000, 8'b0011_0000, 8'b0000_1100, 8'b0000_0011: size = 2'b01; // hword
@ -343,11 +327,9 @@ package wt_cache_pkg;
endfunction : toSize64
function automatic logic [1:0] toSize32(
input logic [3:0] be
);
function automatic logic [1:0] toSize32(input logic [3:0] be);
logic [1:0] size;
unique case(be)
unique case (be)
4'b1111: size = 2'b10; // word
4'b1100, 4'b0011: size = 2'b01; // hword
default: size = 2'b00; // individual bytes

View file

@ -20,7 +20,9 @@
// instruction e.g. a branch.
module instr_realign import ariane_pkg::*; #(
module instr_realign
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -37,9 +39,9 @@ module instr_realign import ariane_pkg::*; #(
// as a maximum we support a fetch width of 64-bit, hence there can be 4 compressed instructions
logic [3:0] instr_is_compressed;
for (genvar i = 0; i < INSTR_PER_FETCH; i ++) begin
for (genvar i = 0; i < INSTR_PER_FETCH; i++) begin
// LSB != 2'b11
assign instr_is_compressed[i] = ~&data_i[i * 16 +: 2];
assign instr_is_compressed[i] = ~&data_i[i*16+:2];
end
// save the unaligned part of the instruction to this ff

View file

@ -14,10 +14,12 @@
// This also includes all the forwarding logic
module issue_read_operands import ariane_pkg::*; #(
module issue_read_operands
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter type rs3_len_t = logic
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// flush
@ -86,15 +88,13 @@ module issue_read_operands import ariane_pkg::*; #(
riscv::xlen_t operand_a_regfile, operand_b_regfile; // operands coming from regfile
rs3_len_t operand_c_regfile; // third operand from fp regfile or gp regfile if NR_RGPR_PORTS == 3
// output flipflop (ID <-> EX)
riscv::xlen_t operand_a_n, operand_a_q,
operand_b_n, operand_b_q,
imm_n, imm_q;
riscv::xlen_t operand_a_n, operand_a_q, operand_b_n, operand_b_q, imm_n, imm_q;
logic alu_valid_q;
logic mult_valid_q;
logic fpu_valid_q;
logic [1:0] fpu_fmt_q;
logic [2:0] fpu_rm_q;
logic [ 1:0] fpu_fmt_q;
logic [ 2:0] fpu_rm_q;
logic lsu_valid_q;
logic csr_valid_q;
logic branch_valid_q;
@ -142,21 +142,15 @@ module issue_read_operands import ariane_pkg::*; #(
// this obviously depends on the functional unit we need
always_comb begin : unit_busy
unique case (issue_instr_i.fu)
NONE:
fu_busy = 1'b0;
ALU, CTRL_FLOW, CSR, MULT:
fu_busy = ~flu_ready_i;
NONE: fu_busy = 1'b0;
ALU, CTRL_FLOW, CSR, MULT: fu_busy = ~flu_ready_i;
FPU, FPU_VEC:
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
fu_busy = ~fpu_ready_i;
end
else fu_busy = 1'b0;
LOAD, STORE:
fu_busy = ~lsu_ready_i;
CVXIF:
fu_busy = ~cvxif_ready_i;
default:
fu_busy = 1'b0;
end else fu_busy = 1'b0;
LOAD, STORE: fu_busy = ~lsu_ready_i;
CVXIF: fu_busy = ~cvxif_ready_i;
default: fu_busy = 1'b0;
endcase
end
@ -180,22 +174,32 @@ module issue_read_operands import ariane_pkg::*; #(
// as this is an immediate we do not have to wait on anything here
// 1. check if the source registers are clobbered --> check appropriate clobber list (gpr/fpr)
// 2. poll the scoreboard
if (!issue_instr_i.use_zimm && ((CVA6Cfg.FpPresent && is_rs1_fpr(issue_instr_i.op)) ? rd_clobber_fpr_i[issue_instr_i.rs1] != NONE
: rd_clobber_gpr_i[issue_instr_i.rs1] != NONE)) begin
if (!issue_instr_i.use_zimm && ((CVA6Cfg.FpPresent && is_rs1_fpr(
issue_instr_i.op
)) ? rd_clobber_fpr_i[issue_instr_i.rs1] != NONE :
rd_clobber_gpr_i[issue_instr_i.rs1] != NONE)) begin
// check if the clobbering instruction is not a CSR instruction, CSR instructions can only
// be fetched through the register file since they can't be forwarded
// if the operand is available, forward it. CSRs don't write to/from FPR
if (rs1_valid_i && (CVA6Cfg.FpPresent && is_rs1_fpr(issue_instr_i.op) ? 1'b1 : ((rd_clobber_gpr_i[issue_instr_i.rs1] != CSR) || (issue_instr_i.op == SFENCE_VMA)))) begin
if (rs1_valid_i && (CVA6Cfg.FpPresent && is_rs1_fpr(
issue_instr_i.op
) ? 1'b1 : ((rd_clobber_gpr_i[issue_instr_i.rs1] != CSR) ||
(issue_instr_i.op == SFENCE_VMA)))) begin
forward_rs1 = 1'b1;
end else begin // the operand is not available -> stall
stall = 1'b1;
end
end
if ((CVA6Cfg.FpPresent && is_rs2_fpr(issue_instr_i.op)) ? rd_clobber_fpr_i[issue_instr_i.rs2] != NONE
: rd_clobber_gpr_i[issue_instr_i.rs2] != NONE) begin
if ((CVA6Cfg.FpPresent && is_rs2_fpr(
issue_instr_i.op
)) ? rd_clobber_fpr_i[issue_instr_i.rs2] != NONE :
rd_clobber_gpr_i[issue_instr_i.rs2] != NONE) begin
// if the operand is available, forward it. CSRs don't write to/from FPR
if (rs2_valid_i && (CVA6Cfg.FpPresent && is_rs2_fpr(issue_instr_i.op) ? 1'b1 : ( (rd_clobber_gpr_i[issue_instr_i.rs2] != CSR) || (issue_instr_i.op == SFENCE_VMA)))) begin
if (rs2_valid_i && (CVA6Cfg.FpPresent && is_rs2_fpr(
issue_instr_i.op
) ? 1'b1 : ((rd_clobber_gpr_i[issue_instr_i.rs2] != CSR) ||
(issue_instr_i.op == SFENCE_VMA)))) begin
forward_rs2 = 1'b1;
end else begin // the operand is not available -> stall
stall = 1'b1;
@ -203,8 +207,11 @@ module issue_read_operands import ariane_pkg::*; #(
end
// Only check clobbered gpr for OFFLOADED instruction
if ((CVA6Cfg.FpPresent && is_imm_fpr(issue_instr_i.op)) ? rd_clobber_fpr_i[issue_instr_i.result[REG_ADDR_SIZE-1:0]] != NONE
: issue_instr_i.op == OFFLOAD && CVA6Cfg.NrRgprPorts == 3 ? rd_clobber_gpr_i[issue_instr_i.result[REG_ADDR_SIZE-1:0]] != NONE : 0) begin
if ((CVA6Cfg.FpPresent && is_imm_fpr(
issue_instr_i.op
)) ? rd_clobber_fpr_i[issue_instr_i.result[REG_ADDR_SIZE-1:0]] != NONE :
issue_instr_i.op == OFFLOAD && CVA6Cfg.NrRgprPorts == 3 ?
rd_clobber_gpr_i[issue_instr_i.result[REG_ADDR_SIZE-1:0]] != NONE : 0) begin
// if the operand is available, forward it. CSRs don't write to/from FPR so no need to check
if (rs3_valid_i) begin
forward_rs3 = 1'b1;
@ -222,10 +229,12 @@ module issue_read_operands import ariane_pkg::*; #(
// immediates are the third operands in the store case
// for FP operations, the imm field can also be the third operand from the regfile
if (CVA6Cfg.NrRgprPorts == 3) begin
imm_n = (CVA6Cfg.FpPresent && is_imm_fpr(issue_instr_i.op)) ? {{riscv::XLEN-CVA6Cfg.FLen{1'b0}}, operand_c_regfile} :
imm_n = (CVA6Cfg.FpPresent && is_imm_fpr(issue_instr_i.op)) ?
{{riscv::XLEN - CVA6Cfg.FLen{1'b0}}, operand_c_regfile} :
issue_instr_i.op == OFFLOAD ? operand_c_regfile : issue_instr_i.result;
end else begin
imm_n = (CVA6Cfg.FpPresent && is_imm_fpr(issue_instr_i.op)) ? {{riscv::XLEN-CVA6Cfg.FLen{1'b0}}, operand_c_regfile} : issue_instr_i.result;
imm_n = (CVA6Cfg.FpPresent && is_imm_fpr(issue_instr_i.op)) ?
{{riscv::XLEN - CVA6Cfg.FLen{1'b0}}, operand_c_regfile} : issue_instr_i.result;
end
trans_id_n = issue_instr_i.trans_id;
fu_n = issue_instr_i.fu;
@ -240,22 +249,27 @@ module issue_read_operands import ariane_pkg::*; #(
end
if (forward_rs3) begin
imm_n = CVA6Cfg.NrRgprPorts == 3 ? rs3_i : {{riscv::XLEN-CVA6Cfg.FLen{1'b0}}, rs3_i};;
imm_n = CVA6Cfg.NrRgprPorts == 3 ? rs3_i : {{riscv::XLEN - CVA6Cfg.FLen{1'b0}}, rs3_i};
;
end
// use the PC as operand a
if (issue_instr_i.use_pc) begin
operand_a_n = {{riscv::XLEN-riscv::VLEN{issue_instr_i.pc[riscv::VLEN-1]}}, issue_instr_i.pc};
operand_a_n = {
{riscv::XLEN - riscv::VLEN{issue_instr_i.pc[riscv::VLEN-1]}}, issue_instr_i.pc
};
end
// use the zimm as operand a
if (issue_instr_i.use_zimm) begin
// zero extend operand a
operand_a_n = {{riscv::XLEN-5{1'b0}}, issue_instr_i.rs1[4:0]};
operand_a_n = {{riscv::XLEN - 5{1'b0}}, issue_instr_i.rs1[4:0]};
end
// or is it an immediate (including PC), this is not the case for a store, control flow, and accelerator instructions
// also make sure operand B is not already used as an FP operand
if (issue_instr_i.use_imm && (issue_instr_i.fu != STORE) && (issue_instr_i.fu != CTRL_FLOW) && (issue_instr_i.fu != ACCEL) && !(CVA6Cfg.FpPresent && is_rs2_fpr(issue_instr_i.op))) begin
if (issue_instr_i.use_imm && (issue_instr_i.fu != STORE) && (issue_instr_i.fu != CTRL_FLOW) && (issue_instr_i.fu != ACCEL) && !(CVA6Cfg.FpPresent && is_rs2_fpr(
issue_instr_i.op
))) begin
operand_b_n = issue_instr_i.result;
end
end
@ -295,15 +309,15 @@ module issue_read_operands import ariane_pkg::*; #(
MULT: begin
mult_valid_q <= 1'b1;
end
FPU : begin
if(CVA6Cfg.FpPresent) begin
FPU: begin
if (CVA6Cfg.FpPresent) begin
fpu_valid_q <= 1'b1;
fpu_fmt_q <= orig_instr.rftype.fmt; // fmt bits from instruction
fpu_rm_q <= orig_instr.rftype.rm; // rm bits from instruction
end
end
FPU_VEC : begin
if(CVA6Cfg.FpPresent) begin
FPU_VEC: begin
if (CVA6Cfg.FpPresent) begin
fpu_valid_q <= 1'b1;
fpu_fmt_q <= orig_instr.rvftype.vfmt; // vfmt bits from instruction
fpu_rm_q <= {2'b0, orig_instr.rvftype.repl}; // repl bit from instruction
@ -315,7 +329,7 @@ module issue_read_operands import ariane_pkg::*; #(
CSR: begin
csr_valid_q <= 1'b1;
end
default:;
default: ;
endcase
end
// if we got a flush request, de-assert the valid flag, otherwise we will start this
@ -345,7 +359,7 @@ module issue_read_operands import ariane_pkg::*; #(
cvxif_valid_q <= 1'b1;
cvxif_off_instr_q <= orig_instr;
end
default:;
default: ;
endcase
end
if (flush_i) begin
@ -371,15 +385,19 @@ module issue_read_operands import ariane_pkg::*; #(
// WAW - Write After Write Dependency Check
// -----------------------------------------
// no other instruction has the same destination register -> issue the instruction
if ((CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(issue_instr_i.op)) ? (rd_clobber_fpr_i[issue_instr_i.rd] == NONE)
: (rd_clobber_gpr_i[issue_instr_i.rd] == NONE)) begin
if ((CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(
issue_instr_i.op
)) ? (rd_clobber_fpr_i[issue_instr_i.rd] == NONE) :
(rd_clobber_gpr_i[issue_instr_i.rd] == NONE)) begin
issue_ack_o = 1'b1;
end
// or check that the target destination register will be written in this cycle by the
// commit stage
for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++)
if ((CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(issue_instr_i.op)) ? (we_fpr_i[i] && waddr_i[i] == issue_instr_i.rd[4:0])
: (we_gpr_i[i] && waddr_i[i] == issue_instr_i.rd[4:0])) begin
if ((CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(
issue_instr_i.op
)) ? (we_fpr_i[i] && waddr_i[i] == issue_instr_i.rd[4:0]) :
(we_gpr_i[i] && waddr_i[i] == issue_instr_i.rd[4:0])) begin
issue_ack_o = 1'b1;
end
@ -407,11 +425,11 @@ module issue_read_operands import ariane_pkg::*; #(
// ----------------------
// Integer Register File
// ----------------------
logic [CVA6Cfg.NrRgprPorts-1:0][riscv::XLEN-1:0] rdata;
logic [CVA6Cfg.NrRgprPorts-1:0][4:0] raddr_pack;
logic [ CVA6Cfg.NrRgprPorts-1:0][riscv::XLEN-1:0] rdata;
logic [ CVA6Cfg.NrRgprPorts-1:0][ 4:0] raddr_pack;
// pack signals
logic [CVA6Cfg.NrCommitPorts-1:0][4:0] waddr_pack;
logic [CVA6Cfg.NrCommitPorts-1:0][ 4:0] waddr_pack;
logic [CVA6Cfg.NrCommitPorts-1:0][riscv::XLEN-1:0] wdata_pack;
logic [CVA6Cfg.NrCommitPorts-1:0] we_pack;
assign raddr_pack = CVA6Cfg.NrRgprPorts == 3 ? {issue_instr_i.result[4:0], issue_instr_i.rs2[4:0], issue_instr_i.rs1[4:0]}
@ -423,32 +441,32 @@ module issue_read_operands import ariane_pkg::*; #(
end
if (ariane_pkg::FPGA_EN) begin : gen_fpga_regfile
ariane_regfile_fpga #(
.CVA6Cfg ( CVA6Cfg ),
.DATA_WIDTH ( riscv::XLEN ),
.NR_READ_PORTS ( CVA6Cfg.NrRgprPorts ),
.ZERO_REG_ZERO ( 1 )
.CVA6Cfg (CVA6Cfg),
.DATA_WIDTH (riscv::XLEN),
.NR_READ_PORTS(CVA6Cfg.NrRgprPorts),
.ZERO_REG_ZERO(1)
) i_ariane_regfile_fpga (
.test_en_i ( 1'b0 ),
.raddr_i ( raddr_pack ),
.rdata_o ( rdata ),
.waddr_i ( waddr_pack ),
.wdata_i ( wdata_pack ),
.we_i ( we_pack ),
.test_en_i(1'b0),
.raddr_i (raddr_pack),
.rdata_o (rdata),
.waddr_i (waddr_pack),
.wdata_i (wdata_pack),
.we_i (we_pack),
.*
);
end else begin : gen_asic_regfile
ariane_regfile #(
.CVA6Cfg ( CVA6Cfg ),
.DATA_WIDTH ( riscv::XLEN ),
.NR_READ_PORTS ( CVA6Cfg.NrRgprPorts ),
.ZERO_REG_ZERO ( 1 )
.CVA6Cfg (CVA6Cfg),
.DATA_WIDTH (riscv::XLEN),
.NR_READ_PORTS(CVA6Cfg.NrRgprPorts),
.ZERO_REG_ZERO(1)
) i_ariane_regfile (
.test_en_i ( 1'b0 ),
.raddr_i ( raddr_pack ),
.rdata_o ( rdata ),
.waddr_i ( waddr_pack ),
.wdata_i ( wdata_pack ),
.we_i ( we_pack ),
.test_en_i(1'b0),
.raddr_i (raddr_pack),
.rdata_o (rdata),
.waddr_i (waddr_pack),
.wdata_i (wdata_pack),
.we_i (we_pack),
.*
);
end
@ -464,38 +482,40 @@ module issue_read_operands import ariane_pkg::*; #(
generate
if (CVA6Cfg.FpPresent) begin : float_regfile_gen
assign fp_raddr_pack = {issue_instr_i.result[4:0], issue_instr_i.rs2[4:0], issue_instr_i.rs1[4:0]};
assign fp_raddr_pack = {
issue_instr_i.result[4:0], issue_instr_i.rs2[4:0], issue_instr_i.rs1[4:0]
};
for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin : gen_fp_wdata_pack
assign fp_wdata_pack[i] = {wdata_i[i][CVA6Cfg.FLen-1:0]};
end
if (ariane_pkg::FPGA_EN) begin : gen_fpga_fp_regfile
ariane_regfile_fpga #(
.CVA6Cfg ( CVA6Cfg ),
.DATA_WIDTH ( CVA6Cfg.FLen ),
.NR_READ_PORTS ( 3 ),
.ZERO_REG_ZERO ( 0 )
.CVA6Cfg (CVA6Cfg),
.DATA_WIDTH (CVA6Cfg.FLen),
.NR_READ_PORTS(3),
.ZERO_REG_ZERO(0)
) i_ariane_fp_regfile_fpga (
.test_en_i ( 1'b0 ),
.raddr_i ( fp_raddr_pack ),
.rdata_o ( fprdata ),
.waddr_i ( waddr_pack ),
.wdata_i ( fp_wdata_pack ),
.we_i ( we_fpr_i ),
.test_en_i(1'b0),
.raddr_i (fp_raddr_pack),
.rdata_o (fprdata),
.waddr_i (waddr_pack),
.wdata_i (fp_wdata_pack),
.we_i (we_fpr_i),
.*
);
end else begin : gen_asic_fp_regfile
ariane_regfile #(
.CVA6Cfg ( CVA6Cfg ),
.DATA_WIDTH ( CVA6Cfg.FLen ),
.NR_READ_PORTS ( 3 ),
.ZERO_REG_ZERO ( 0 )
.CVA6Cfg (CVA6Cfg),
.DATA_WIDTH (CVA6Cfg.FLen),
.NR_READ_PORTS(3),
.ZERO_REG_ZERO(0)
) i_ariane_fp_regfile (
.test_en_i ( 1'b0 ),
.raddr_i ( fp_raddr_pack ),
.rdata_o ( fprdata ),
.waddr_i ( waddr_pack ),
.wdata_i ( fp_wdata_pack ),
.we_i ( we_fpr_i ),
.test_en_i(1'b0),
.raddr_i (fp_raddr_pack),
.rdata_o (fprdata),
.waddr_i (waddr_pack),
.wdata_i (fp_wdata_pack),
.we_i (we_fpr_i),
.*
);
end
@ -504,10 +524,15 @@ module issue_read_operands import ariane_pkg::*; #(
end
endgenerate
assign operand_a_regfile = (CVA6Cfg.FpPresent && is_rs1_fpr(issue_instr_i.op)) ? {{riscv::XLEN-CVA6Cfg.FLen{1'b0}}, fprdata[0]} : rdata[0];
assign operand_b_regfile = (CVA6Cfg.FpPresent && is_rs2_fpr(issue_instr_i.op)) ? {{riscv::XLEN-CVA6Cfg.FLen{1'b0}}, fprdata[1]} : rdata[1];
assign operand_c_regfile = CVA6Cfg.NrRgprPorts == 3 ? ((CVA6Cfg.FpPresent && is_imm_fpr(issue_instr_i.op)) ? {{riscv::XLEN-CVA6Cfg.FLen{1'b0}}, fprdata[2]} : rdata[2])
: fprdata[2];
assign operand_a_regfile = (CVA6Cfg.FpPresent && is_rs1_fpr(
issue_instr_i.op
)) ? {{riscv::XLEN - CVA6Cfg.FLen{1'b0}}, fprdata[0]} : rdata[0];
assign operand_b_regfile = (CVA6Cfg.FpPresent && is_rs2_fpr(
issue_instr_i.op
)) ? {{riscv::XLEN - CVA6Cfg.FLen{1'b0}}, fprdata[1]} : rdata[1];
assign operand_c_regfile = CVA6Cfg.NrRgprPorts == 3 ? ((CVA6Cfg.FpPresent && is_imm_fpr(
issue_instr_i.op
)) ? {{riscv::XLEN - CVA6Cfg.FLen{1'b0}}, fprdata[2]} : rdata[2]) : fprdata[2];
// ----------------------
// Registers (ID <-> EX)
@ -539,12 +564,19 @@ module issue_read_operands import ariane_pkg::*; #(
//pragma translate_off
initial begin
assert (CVA6Cfg.NrRgprPorts == 2 || (CVA6Cfg.NrRgprPorts == 3 && CVA6Cfg.CvxifEn))
else $fatal(1, "If CVXIF is enable, ariane regfile can have either 2 or 3 read ports. Else it has 2 read ports.");
else
$fatal(
1,
"If CVXIF is enable, ariane regfile can have either 2 or 3 read ports. Else it has 2 read ports."
);
end
assert property (
@(posedge clk_i) (branch_valid_q) |-> (!$isunknown(operand_a_q) && !$isunknown(operand_b_q)))
else $warning ("Got unknown value in one of the operands");
assert property (@(posedge clk_i) (branch_valid_q) |-> (!$isunknown(
operand_a_q
) && !$isunknown(
operand_b_q
)))
else $warning("Got unknown value in one of the operands");
//pragma translate_on
endmodule

View file

@ -14,11 +14,13 @@
// in a scoreboard like data-structure.
module issue_stage import ariane_pkg::*; #(
module issue_stage
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter bit IsRVFI = bit'(0),
parameter int unsigned NR_ENTRIES = 8
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
@ -76,7 +78,7 @@ module issue_stage import ariane_pkg::*; #(
input logic x_we_i,
// commit port
input logic [CVA6Cfg.NrCommitPorts-1:0][4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][ 4:0] waddr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0][riscv::XLEN-1:0] wdata_i,
input logic [CVA6Cfg.NrCommitPorts-1:0] we_gpr_i,
input logic [CVA6Cfg.NrCommitPorts-1:0] we_fpr_i,
@ -87,9 +89,9 @@ module issue_stage import ariane_pkg::*; #(
output logic stall_issue_o, // Used in Performance Counters
//RVFI
input [riscv::VLEN-1:0] lsu_addr_i,
input [(riscv::XLEN/8)-1:0] lsu_rmask_i,
input [(riscv::XLEN/8)-1:0] lsu_wmask_i,
input [ riscv::VLEN-1:0] lsu_addr_i,
input [ (riscv::XLEN/8)-1:0] lsu_rmask_i,
input [ (riscv::XLEN/8)-1:0] lsu_wmask_i,
input [ariane_pkg::TRANS_ID_BITS-1:0] lsu_addr_trans_id_i
);
// ---------------------------------------------------
@ -100,15 +102,15 @@ module issue_stage import ariane_pkg::*; #(
fu_t [2**REG_ADDR_SIZE-1:0] rd_clobber_gpr_sb_iro;
fu_t [2**REG_ADDR_SIZE-1:0] rd_clobber_fpr_sb_iro;
logic [REG_ADDR_SIZE-1:0] rs1_iro_sb;
logic [ REG_ADDR_SIZE-1:0] rs1_iro_sb;
riscv::xlen_t rs1_sb_iro;
logic rs1_valid_sb_iro;
logic [REG_ADDR_SIZE-1:0] rs2_iro_sb;
logic [ REG_ADDR_SIZE-1:0] rs2_iro_sb;
riscv::xlen_t rs2_sb_iro;
logic rs2_valid_iro_sb;
logic [REG_ADDR_SIZE-1:0] rs3_iro_sb;
logic [ REG_ADDR_SIZE-1:0] rs3_iro_sb;
rs3_len_t rs3_sb_iro;
logic rs3_valid_iro_sb;
@ -130,42 +132,42 @@ module issue_stage import ariane_pkg::*; #(
// 2. Manage instructions in a scoreboard
// ---------------------------------------------------------
scoreboard #(
.CVA6Cfg ( CVA6Cfg ),
.IsRVFI ( IsRVFI ),
.rs3_len_t ( rs3_len_t ),
.NR_ENTRIES (NR_ENTRIES )
.CVA6Cfg (CVA6Cfg),
.IsRVFI (IsRVFI),
.rs3_len_t (rs3_len_t),
.NR_ENTRIES(NR_ENTRIES)
) i_scoreboard (
.sb_full_o ( sb_full_o ),
.unresolved_branch_i ( 1'b0 ),
.rd_clobber_gpr_o ( rd_clobber_gpr_sb_iro ),
.rd_clobber_fpr_o ( rd_clobber_fpr_sb_iro ),
.rs1_i ( rs1_iro_sb ),
.rs1_o ( rs1_sb_iro ),
.rs1_valid_o ( rs1_valid_sb_iro ),
.rs2_i ( rs2_iro_sb ),
.rs2_o ( rs2_sb_iro ),
.rs2_valid_o ( rs2_valid_iro_sb ),
.rs3_i ( rs3_iro_sb ),
.rs3_o ( rs3_sb_iro ),
.rs3_valid_o ( rs3_valid_iro_sb ),
.sb_full_o (sb_full_o),
.unresolved_branch_i(1'b0),
.rd_clobber_gpr_o (rd_clobber_gpr_sb_iro),
.rd_clobber_fpr_o (rd_clobber_fpr_sb_iro),
.rs1_i (rs1_iro_sb),
.rs1_o (rs1_sb_iro),
.rs1_valid_o (rs1_valid_sb_iro),
.rs2_i (rs2_iro_sb),
.rs2_o (rs2_sb_iro),
.rs2_valid_o (rs2_valid_iro_sb),
.rs3_i (rs3_iro_sb),
.rs3_o (rs3_sb_iro),
.rs3_valid_o (rs3_valid_iro_sb),
.decoded_instr_i ( decoded_instr_i ),
.decoded_instr_valid_i ( decoded_instr_valid_i ),
.decoded_instr_ack_o ( decoded_instr_ack_o ),
.issue_instr_o ( issue_instr_sb_iro ),
.issue_instr_valid_o ( issue_instr_valid_sb_iro ),
.issue_ack_i ( issue_ack_iro_sb ),
.decoded_instr_i (decoded_instr_i),
.decoded_instr_valid_i(decoded_instr_valid_i),
.decoded_instr_ack_o (decoded_instr_ack_o),
.issue_instr_o (issue_instr_sb_iro),
.issue_instr_valid_o (issue_instr_valid_sb_iro),
.issue_ack_i (issue_ack_iro_sb),
.resolved_branch_i ( resolved_branch_i ),
.trans_id_i ( trans_id_i ),
.wbdata_i ( wbdata_i ),
.ex_i ( ex_ex_i ),
.lsu_addr_i ( lsu_addr_i ),
.lsu_rmask_i ( lsu_rmask_i ),
.lsu_wmask_i ( lsu_wmask_i ),
.lsu_addr_trans_id_i ( lsu_addr_trans_id_i ),
.rs1_forwarding_i ( rs1_forwarding_xlen ),
.rs2_forwarding_i ( rs2_forwarding_xlen ),
.resolved_branch_i (resolved_branch_i),
.trans_id_i (trans_id_i),
.wbdata_i (wbdata_i),
.ex_i (ex_ex_i),
.lsu_addr_i (lsu_addr_i),
.lsu_rmask_i (lsu_rmask_i),
.lsu_wmask_i (lsu_wmask_i),
.lsu_addr_trans_id_i(lsu_addr_trans_id_i),
.rs1_forwarding_i (rs1_forwarding_xlen),
.rs2_forwarding_i (rs2_forwarding_xlen),
.*
);
@ -173,36 +175,36 @@ module issue_stage import ariane_pkg::*; #(
// 3. Issue instruction and read operand, also commit
// ---------------------------------------------------------
issue_read_operands #(
.CVA6Cfg ( CVA6Cfg ),
.rs3_len_t ( rs3_len_t )
)i_issue_read_operands (
.flush_i ( flush_unissued_instr_i ),
.issue_instr_i ( issue_instr_sb_iro ),
.issue_instr_valid_i ( issue_instr_valid_sb_iro ),
.issue_ack_o ( issue_ack_iro_sb ),
.fu_data_o ( fu_data_o ),
.flu_ready_i ( flu_ready_i ),
.rs1_o ( rs1_iro_sb ),
.rs1_i ( rs1_sb_iro ),
.rs1_valid_i ( rs1_valid_sb_iro ),
.rs2_o ( rs2_iro_sb ),
.rs2_i ( rs2_sb_iro ),
.rs2_valid_i ( rs2_valid_iro_sb ),
.rs3_o ( rs3_iro_sb ),
.rs3_i ( rs3_sb_iro ),
.rs3_valid_i ( rs3_valid_iro_sb ),
.rd_clobber_gpr_i ( rd_clobber_gpr_sb_iro ),
.rd_clobber_fpr_i ( rd_clobber_fpr_sb_iro ),
.alu_valid_o ( alu_valid_o ),
.branch_valid_o ( branch_valid_o ),
.csr_valid_o ( csr_valid_o ),
.cvxif_valid_o ( x_issue_valid_o ),
.cvxif_ready_i ( x_issue_ready_i ),
.cvxif_off_instr_o ( x_off_instr_o ),
.mult_valid_o ( mult_valid_o ),
.rs1_forwarding_o ( rs1_forwarding_xlen ),
.rs2_forwarding_o ( rs2_forwarding_xlen ),
.stall_issue_o ( stall_issue_o ),
.CVA6Cfg (CVA6Cfg),
.rs3_len_t(rs3_len_t)
) i_issue_read_operands (
.flush_i (flush_unissued_instr_i),
.issue_instr_i (issue_instr_sb_iro),
.issue_instr_valid_i(issue_instr_valid_sb_iro),
.issue_ack_o (issue_ack_iro_sb),
.fu_data_o (fu_data_o),
.flu_ready_i (flu_ready_i),
.rs1_o (rs1_iro_sb),
.rs1_i (rs1_sb_iro),
.rs1_valid_i (rs1_valid_sb_iro),
.rs2_o (rs2_iro_sb),
.rs2_i (rs2_sb_iro),
.rs2_valid_i (rs2_valid_iro_sb),
.rs3_o (rs3_iro_sb),
.rs3_i (rs3_sb_iro),
.rs3_valid_i (rs3_valid_iro_sb),
.rd_clobber_gpr_i (rd_clobber_gpr_sb_iro),
.rd_clobber_fpr_i (rd_clobber_fpr_sb_iro),
.alu_valid_o (alu_valid_o),
.branch_valid_o (branch_valid_o),
.csr_valid_o (csr_valid_o),
.cvxif_valid_o (x_issue_valid_o),
.cvxif_ready_i (x_issue_ready_i),
.cvxif_off_instr_o (x_off_instr_o),
.mult_valid_o (mult_valid_o),
.rs1_forwarding_o (rs1_forwarding_xlen),
.rs2_forwarding_o (rs2_forwarding_xlen),
.stall_issue_o (stall_issue_o),
.*
);

View file

@ -13,10 +13,12 @@
// Description: Load Store Unit, handles address calculation and memory interface signals
module load_store_unit import ariane_pkg::*; #(
module load_store_unit
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned ASID_WIDTH = 1
)(
) (
input logic clk_i,
input logic rst_ni,
input logic flush_i,
@ -54,8 +56,8 @@ module load_store_unit import ariane_pkg::*; #(
input logic sum_i, // From CSR register file
input logic mxr_i, // From CSR register file
input logic [riscv::PPNW-1:0] satp_ppn_i, // From CSR register file
input logic [ASID_WIDTH-1:0] asid_i, // From CSR register file
input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i,
input logic [ ASID_WIDTH-1:0] asid_i, // From CSR register file
input logic [ ASID_WIDTH-1:0] asid_to_be_flushed_i,
input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i,
input logic flush_tlb_i,
// Performance counters
@ -63,8 +65,8 @@ module load_store_unit import ariane_pkg::*; #(
output logic dtlb_miss_o,
// interface to dcache
input dcache_req_o_t [2:0] dcache_req_ports_i,
output dcache_req_i_t [2:0] dcache_req_ports_o,
input dcache_req_o_t [ 2:0] dcache_req_ports_i,
output dcache_req_i_t [ 2:0] dcache_req_ports_o,
input logic dcache_wbuffer_empty_i,
input logic dcache_wbuffer_not_ni_i,
// AMO interface
@ -75,10 +77,10 @@ module load_store_unit import ariane_pkg::*; #(
input logic [15:0][riscv::PLEN-3:0] pmpaddr_i,
//RVFI
output [riscv::VLEN-1:0] lsu_addr_o,
output [riscv::PLEN-1:0] mem_paddr_o,
output [(riscv::XLEN/8)-1:0] lsu_rmask_o,
output [(riscv::XLEN/8)-1:0] lsu_wmask_o,
output [ riscv::VLEN-1:0] lsu_addr_o,
output [ riscv::PLEN-1:0] mem_paddr_o,
output [ (riscv::XLEN/8)-1:0] lsu_rmask_o,
output [ (riscv::XLEN/8)-1:0] lsu_wmask_o,
output [ariane_pkg::TRANS_ID_BITS-1:0] lsu_addr_trans_id_o
);
// data is misaligned
@ -97,7 +99,7 @@ module load_store_unit import ariane_pkg::*; #(
// Address Generation Unit (AGU)
// ------------------------------
// virtual address as calculated by the AGU in the first cycle
logic [riscv::VLEN-1:0] vaddr_i;
logic [ riscv::VLEN-1:0] vaddr_i;
riscv::xlen_t vaddr_xlen;
logic overflow;
logic [(riscv::XLEN/8)-1:0] be_i;
@ -119,7 +121,7 @@ module load_store_unit import ariane_pkg::*; #(
logic [riscv::PLEN-1:0] mmu_paddr, mmu_vaddr_plen, fetch_vaddr_plen;
exception_t mmu_exception;
logic dtlb_hit;
logic [riscv::PPNW-1:0] dtlb_ppn;
logic [ riscv::PPNW-1:0] dtlb_ppn;
logic ld_valid;
logic [TRANS_ID_BITS-1:0] ld_trans_id;
@ -128,7 +130,7 @@ module load_store_unit import ariane_pkg::*; #(
logic [TRANS_ID_BITS-1:0] st_trans_id;
riscv::xlen_t st_result;
logic [11:0] page_offset;
logic [ 11:0] page_offset;
logic page_offset_matches;
exception_t misaligned_exception;
@ -140,58 +142,58 @@ module load_store_unit import ariane_pkg::*; #(
// -------------------
if (MMU_PRESENT && (riscv::XLEN == 64)) begin : gen_mmu_sv39
mmu #(
.CVA6Cfg ( CVA6Cfg ),
.INSTR_TLB_ENTRIES ( ariane_pkg::INSTR_TLB_ENTRIES ),
.DATA_TLB_ENTRIES ( ariane_pkg::DATA_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES),
.DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES),
.ASID_WIDTH (ASID_WIDTH)
) i_cva6_mmu (
// misaligned bypass
.misaligned_ex_i ( misaligned_exception ),
.lsu_is_store_i ( st_translation_req ),
.lsu_req_i ( translation_req ),
.lsu_vaddr_i ( mmu_vaddr ),
.lsu_valid_o ( translation_valid ),
.lsu_paddr_o ( mmu_paddr ),
.lsu_exception_o ( mmu_exception ),
.lsu_dtlb_hit_o ( dtlb_hit ), // send in the same cycle as the request
.lsu_dtlb_ppn_o ( dtlb_ppn ), // send in the same cycle as the request
.misaligned_ex_i(misaligned_exception),
.lsu_is_store_i (st_translation_req),
.lsu_req_i (translation_req),
.lsu_vaddr_i (mmu_vaddr),
.lsu_valid_o (translation_valid),
.lsu_paddr_o (mmu_paddr),
.lsu_exception_o(mmu_exception),
.lsu_dtlb_hit_o (dtlb_hit), // send in the same cycle as the request
.lsu_dtlb_ppn_o (dtlb_ppn), // send in the same cycle as the request
// connecting PTW to D$ IF
.req_port_i ( dcache_req_ports_i [0] ),
.req_port_o ( dcache_req_ports_o [0] ),
.req_port_i (dcache_req_ports_i[0]),
.req_port_o (dcache_req_ports_o[0]),
// icache address translation requests
.icache_areq_i ( icache_areq_i ),
.icache_areq_i (icache_areq_i),
.asid_to_be_flushed_i,
.vaddr_to_be_flushed_i,
.icache_areq_o ( icache_areq_o ),
.icache_areq_o (icache_areq_o),
.pmpcfg_i,
.pmpaddr_i,
.*
);
end else if (MMU_PRESENT && (riscv::XLEN == 32)) begin : gen_mmu_sv32
cva6_mmu_sv32 #(
.CVA6Cfg ( CVA6Cfg ),
.INSTR_TLB_ENTRIES ( ariane_pkg::INSTR_TLB_ENTRIES ),
.DATA_TLB_ENTRIES ( ariane_pkg::DATA_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES),
.DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES),
.ASID_WIDTH (ASID_WIDTH)
) i_cva6_mmu (
// misaligned bypass
.misaligned_ex_i ( misaligned_exception ),
.lsu_is_store_i ( st_translation_req ),
.lsu_req_i ( translation_req ),
.lsu_vaddr_i ( mmu_vaddr ),
.lsu_valid_o ( translation_valid ),
.lsu_paddr_o ( mmu_paddr ),
.lsu_exception_o ( mmu_exception ),
.lsu_dtlb_hit_o ( dtlb_hit ), // send in the same cycle as the request
.lsu_dtlb_ppn_o ( dtlb_ppn ), // send in the same cycle as the request
.misaligned_ex_i(misaligned_exception),
.lsu_is_store_i (st_translation_req),
.lsu_req_i (translation_req),
.lsu_vaddr_i (mmu_vaddr),
.lsu_valid_o (translation_valid),
.lsu_paddr_o (mmu_paddr),
.lsu_exception_o(mmu_exception),
.lsu_dtlb_hit_o (dtlb_hit), // send in the same cycle as the request
.lsu_dtlb_ppn_o (dtlb_ppn), // send in the same cycle as the request
// connecting PTW to D$ IF
.req_port_i ( dcache_req_ports_i [0] ),
.req_port_o ( dcache_req_ports_o [0] ),
.req_port_i (dcache_req_ports_i[0]),
.req_port_o (dcache_req_ports_o[0]),
// icache address translation requests
.icache_areq_i ( icache_areq_i ),
.icache_areq_i (icache_areq_i),
.asid_to_be_flushed_i,
.vaddr_to_be_flushed_i,
.icache_areq_o ( icache_areq_o ),
.icache_areq_o (icache_areq_o),
.pmpcfg_i,
.pmpaddr_i,
.*
@ -202,8 +204,8 @@ module load_store_unit import ariane_pkg::*; #(
assign mmu_vaddr_plen = mmu_vaddr[riscv::PLEN-1:0];
assign fetch_vaddr_plen = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0];
end else begin
assign mmu_vaddr_plen = {{{riscv::PLEN-riscv::VLEN}{1'b0}}, mmu_vaddr};
assign fetch_vaddr_plen = {{{riscv::PLEN-riscv::VLEN}{1'b0}}, icache_areq_i.fetch_vaddr};
assign mmu_vaddr_plen = {{{riscv::PLEN - riscv::VLEN} {1'b0}}, mmu_vaddr};
assign fetch_vaddr_plen = {{{riscv::PLEN - riscv::VLEN} {1'b0}}, icache_areq_i.fetch_vaddr};
end
assign icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
@ -244,72 +246,72 @@ module load_store_unit import ariane_pkg::*; #(
// Store Unit
// ------------------
store_unit #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_store_unit (
.clk_i,
.rst_ni,
.flush_i,
.stall_st_pending_i,
.no_st_pending_o,
.store_buffer_empty_o ( store_buffer_empty ),
.store_buffer_empty_o(store_buffer_empty),
.valid_i ( st_valid_i ),
.lsu_ctrl_i ( lsu_ctrl ),
.pop_st_o ( pop_st ),
.valid_i (st_valid_i),
.lsu_ctrl_i(lsu_ctrl),
.pop_st_o (pop_st),
.commit_i,
.commit_ready_o,
.amo_valid_commit_i,
.valid_o ( st_valid ),
.trans_id_o ( st_trans_id ),
.result_o ( st_result ),
.ex_o ( st_ex ),
.valid_o (st_valid),
.trans_id_o (st_trans_id),
.result_o (st_result),
.ex_o (st_ex),
// MMU port
.translation_req_o ( st_translation_req ),
.vaddr_o ( st_vaddr ),
.mem_paddr_o ( mem_paddr_o ),
.paddr_i ( mmu_paddr ),
.ex_i ( mmu_exception ),
.dtlb_hit_i ( dtlb_hit ),
.translation_req_o (st_translation_req),
.vaddr_o (st_vaddr),
.mem_paddr_o (mem_paddr_o),
.paddr_i (mmu_paddr),
.ex_i (mmu_exception),
.dtlb_hit_i (dtlb_hit),
// Load Unit
.page_offset_i ( page_offset ),
.page_offset_matches_o ( page_offset_matches ),
.page_offset_i (page_offset),
.page_offset_matches_o(page_offset_matches),
// AMOs
.amo_req_o,
.amo_resp_i,
// to memory arbiter
.req_port_i ( dcache_req_ports_i [2] ),
.req_port_o ( dcache_req_ports_o [2] )
.req_port_i (dcache_req_ports_i[2]),
.req_port_o (dcache_req_ports_o[2])
);
// ------------------
// Load Unit
// ------------------
load_unit #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_load_unit (
.valid_i ( ld_valid_i ),
.lsu_ctrl_i ( lsu_ctrl ),
.pop_ld_o ( pop_ld ),
.valid_i (ld_valid_i),
.lsu_ctrl_i(lsu_ctrl),
.pop_ld_o (pop_ld),
.valid_o ( ld_valid ),
.trans_id_o ( ld_trans_id ),
.result_o ( ld_result ),
.ex_o ( ld_ex ),
.valid_o (ld_valid),
.trans_id_o (ld_trans_id),
.result_o (ld_result),
.ex_o (ld_ex),
// MMU port
.translation_req_o ( ld_translation_req ),
.vaddr_o ( ld_vaddr ),
.paddr_i ( mmu_paddr ),
.ex_i ( mmu_exception ),
.dtlb_hit_i ( dtlb_hit ),
.dtlb_ppn_i ( dtlb_ppn ),
.translation_req_o (ld_translation_req),
.vaddr_o (ld_vaddr),
.paddr_i (mmu_paddr),
.ex_i (mmu_exception),
.dtlb_hit_i (dtlb_hit),
.dtlb_ppn_i (dtlb_ppn),
// to store unit
.page_offset_o ( page_offset ),
.page_offset_matches_i ( page_offset_matches ),
.store_buffer_empty_i ( store_buffer_empty ),
.page_offset_o (page_offset),
.page_offset_matches_i(page_offset_matches),
.store_buffer_empty_i (store_buffer_empty),
// to memory arbiter
.req_port_i ( dcache_req_ports_i [1] ),
.req_port_o ( dcache_req_ports_o [1] ),
.req_port_i (dcache_req_ports_i[1]),
.req_port_o (dcache_req_ports_o[1]),
.dcache_wbuffer_not_ni_i,
.commit_tran_id_i,
.*
@ -323,23 +325,23 @@ module load_store_unit import ariane_pkg::*; #(
// can be tuned to trade-off IPC vs. cycle time
shift_reg #(
.dtype ( logic[$bits(ld_valid) + $bits(ld_trans_id) + $bits(ld_result) + $bits(ld_ex) - 1: 0]),
.Depth ( cva6_config_pkg::CVA6ConfigNrLoadPipeRegs )
.dtype(logic [$bits(ld_valid) + $bits(ld_trans_id) + $bits(ld_result) + $bits(ld_ex) - 1:0]),
.Depth(cva6_config_pkg::CVA6ConfigNrLoadPipeRegs)
) i_pipe_reg_load (
.clk_i,
.rst_ni,
.d_i ( {ld_valid, ld_trans_id, ld_result, ld_ex} ),
.d_o ( {load_valid_o, load_trans_id_o, load_result_o, load_exception_o} )
.d_i({ld_valid, ld_trans_id, ld_result, ld_ex}),
.d_o({load_valid_o, load_trans_id_o, load_result_o, load_exception_o})
);
shift_reg #(
.dtype ( logic[$bits(st_valid) + $bits(st_trans_id) + $bits(st_result) + $bits(st_ex) - 1: 0]),
.Depth ( cva6_config_pkg::CVA6ConfigNrStorePipeRegs )
.dtype(logic [$bits(st_valid) + $bits(st_trans_id) + $bits(st_result) + $bits(st_ex) - 1:0]),
.Depth(cva6_config_pkg::CVA6ConfigNrStorePipeRegs)
) i_pipe_reg_store (
.clk_i,
.rst_ni,
.d_i ( {st_valid, st_trans_id, st_result, st_ex} ),
.d_o ( {store_valid_o, store_trans_id_o, store_result_o, store_exception_o} )
.d_i({st_valid, st_trans_id, st_result, st_ex}),
.d_o({store_valid_o, store_trans_id_o, store_result_o, store_exception_o})
);
// determine whether this is a load or store
@ -377,8 +379,11 @@ module load_store_unit import ariane_pkg::*; #(
// we can generate the byte enable from the virtual address since the last
// 12 bit are the same anyway
// and we can always generate the byte enable from the address at hand
assign be_i = riscv::IS_XLEN64 ? be_gen(vaddr_i[2:0], extract_transfer_size(fu_data_i.operation)):
be_gen_32(vaddr_i[1:0], extract_transfer_size(fu_data_i.operation));
assign be_i = riscv::IS_XLEN64 ? be_gen(
vaddr_i[2:0], extract_transfer_size(fu_data_i.operation)
) : be_gen_32(
vaddr_i[1:0], extract_transfer_size(fu_data_i.operation)
);
// ------------------------
// Misaligned Exception
@ -388,11 +393,7 @@ module load_store_unit import ariane_pkg::*; #(
// can augment the exception if other memory related exceptions like a page fault or access errors
always_comb begin : data_misaligned_detection
misaligned_exception = {
{riscv::XLEN{1'b0}},
{riscv::XLEN{1'b0}},
1'b0
};
misaligned_exception = {{riscv::XLEN{1'b0}}, {riscv::XLEN{1'b0}}, 1'b0};
data_misaligned = 1'b0;
@ -425,7 +426,7 @@ module load_store_unit import ariane_pkg::*; #(
end
end
// byte -> is always aligned
default:;
default: ;
endcase
end
@ -433,16 +434,12 @@ module load_store_unit import ariane_pkg::*; #(
if (lsu_ctrl.fu == LOAD) begin
misaligned_exception = {
riscv::LD_ADDR_MISALIGNED,
{{riscv::XLEN-riscv::VLEN{1'b0}},lsu_ctrl.vaddr},
1'b1
riscv::LD_ADDR_MISALIGNED, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_ctrl.vaddr}, 1'b1
};
end else if (lsu_ctrl.fu == STORE) begin
misaligned_exception = {
riscv::ST_ADDR_MISALIGNED,
{{riscv::XLEN-riscv::VLEN{1'b0}},lsu_ctrl.vaddr},
1'b1
riscv::ST_ADDR_MISALIGNED, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_ctrl.vaddr}, 1'b1
};
end
end
@ -451,16 +448,12 @@ module load_store_unit import ariane_pkg::*; #(
if (lsu_ctrl.fu == LOAD) begin
misaligned_exception = {
riscv::LD_ACCESS_FAULT,
{{riscv::XLEN-riscv::VLEN{1'b0}},lsu_ctrl.vaddr},
1'b1
riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_ctrl.vaddr}, 1'b1
};
end else if (lsu_ctrl.fu == STORE) begin
misaligned_exception = {
riscv::ST_ACCESS_FAULT,
{{riscv::XLEN-riscv::VLEN{1'b0}},lsu_ctrl.vaddr},
1'b1
riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_ctrl.vaddr}, 1'b1
};
end
end
@ -472,18 +465,27 @@ module load_store_unit import ariane_pkg::*; #(
// new data arrives here
lsu_ctrl_t lsu_req_i;
assign lsu_req_i = {lsu_valid_i, vaddr_i, overflow, fu_data_i.operand_b, be_i, fu_data_i.fu, fu_data_i.operation, fu_data_i.trans_id};
assign lsu_req_i = {
lsu_valid_i,
vaddr_i,
overflow,
fu_data_i.operand_b,
be_i,
fu_data_i.fu,
fu_data_i.operation,
fu_data_i.trans_id
};
lsu_bypass #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) lsu_bypass_i (
.lsu_req_i ( lsu_req_i ),
.lsu_req_valid_i ( lsu_valid_i ),
.pop_ld_i ( pop_ld ),
.pop_st_i ( pop_st ),
.lsu_req_i (lsu_req_i),
.lsu_req_valid_i(lsu_valid_i),
.pop_ld_i (pop_ld),
.pop_st_i (pop_st),
.lsu_ctrl_o ( lsu_ctrl ),
.ready_o ( lsu_ready_o ),
.lsu_ctrl_o(lsu_ctrl),
.ready_o (lsu_ready_o),
.*
);

View file

@ -18,7 +18,9 @@
// Modification: add support for multiple outstanding load operations
// to the data cache
module load_unit import ariane_pkg::*; #(
module load_unit
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -50,10 +52,18 @@ module load_unit import ariane_pkg::*; #(
output dcache_req_i_t req_port_o,
input logic dcache_wbuffer_not_ni_i
);
enum logic [3:0] { IDLE, WAIT_GNT, SEND_TAG, WAIT_PAGE_OFFSET,
ABORT_TRANSACTION, ABORT_TRANSACTION_NI, WAIT_TRANSLATION, WAIT_FLUSH,
enum logic [3:0] {
IDLE,
WAIT_GNT,
SEND_TAG,
WAIT_PAGE_OFFSET,
ABORT_TRANSACTION,
ABORT_TRANSACTION_NI,
WAIT_TRANSLATION,
WAIT_FLUSH,
WAIT_WB_EMPTY
} state_d, state_q;
}
state_d, state_q;
// in order to decouple the response interface from the request interface,
// we need a a buffer which can hold all inflight memory load requests
@ -69,8 +79,9 @@ module load_unit import ariane_pkg::*; #(
// adds a combinational path between the request and response interfaces
// towards the cache.
localparam logic LDBUF_FALLTHROUGH = (CVA6Cfg.NrLoadBufEntries == 1);
localparam int unsigned REQ_ID_BITS = CVA6Cfg.NrLoadBufEntries > 1 ?
$clog2(CVA6Cfg.NrLoadBufEntries) : 1;
localparam int unsigned REQ_ID_BITS = CVA6Cfg.NrLoadBufEntries > 1 ? $clog2(
CVA6Cfg.NrLoadBufEntries
) : 1;
typedef logic [REQ_ID_BITS-1:0] ldbuf_id_t;
@ -96,12 +107,12 @@ module load_unit import ariane_pkg::*; #(
generate
if (CVA6Cfg.NrLoadBufEntries > 1) begin : ldbuf_free_index_multi_gen
lzc #(
.WIDTH (CVA6Cfg.NrLoadBufEntries),
.WIDTH(CVA6Cfg.NrLoadBufEntries),
.MODE (1'b0) // Count leading zeros
) lzc_windex_i (
.in_i (~ldbuf_valid_q),
.cnt_o (ldbuf_free_index),
.empty_o (ldbuf_empty)
.empty_o(ldbuf_empty)
);
end else begin : ldbuf_free_index_single_gen
assign ldbuf_free_index = 1'b0;
@ -110,8 +121,7 @@ module load_unit import ariane_pkg::*; #(
assign ldbuf_windex = (LDBUF_FALLTHROUGH && ldbuf_r) ? ldbuf_rindex : ldbuf_free_index;
always_comb
begin : ldbuf_comb
always_comb begin : ldbuf_comb
ldbuf_flushed_d = ldbuf_flushed_q;
ldbuf_valid_d = ldbuf_valid_q;
@ -131,8 +141,7 @@ module load_unit import ariane_pkg::*; #(
end
end
always_ff @(posedge clk_i or negedge rst_ni)
begin : ldbuf_ff
always_ff @(posedge clk_i or negedge rst_ni) begin : ldbuf_ff
if (!rst_ni) begin
ldbuf_flushed_q <= '0;
ldbuf_valid_q <= '0;
@ -156,7 +165,9 @@ module load_unit import ariane_pkg::*; #(
assign req_port_o.data_we = 1'b0;
assign req_port_o.data_wdata = '0;
// compose the load buffer write data, control is handled in the FSM
assign ldbuf_wdata = {lsu_ctrl_i.trans_id, lsu_ctrl_i.vaddr[riscv::XLEN_ALIGN_BYTES-1:0], lsu_ctrl_i.operation};
assign ldbuf_wdata = {
lsu_ctrl_i.trans_id, lsu_ctrl_i.vaddr[riscv::XLEN_ALIGN_BYTES-1:0], lsu_ctrl_i.operation
};
// output address
// we can now output the lower 12 bit as the index to the cache
assign req_port_o.address_index = lsu_ctrl_i.vaddr[ariane_pkg::DCACHE_INDEX_WIDTH-1:0];
@ -175,7 +186,7 @@ module load_unit import ariane_pkg::*; #(
logic not_commit_time;
logic inflight_stores;
logic stall_ni;
assign paddr_ni = config_pkg::is_inside_nonidempotent_regions(CVA6Cfg, {dtlb_ppn_i,12'd0});
assign paddr_ni = config_pkg::is_inside_nonidempotent_regions(CVA6Cfg, {dtlb_ppn_i, 12'd0});
assign not_commit_time = commit_tran_id_i != lsu_ctrl_i.trans_id;
assign inflight_stores = (!dcache_wbuffer_not_ni_i || !store_buffer_empty_i);
assign stall_ni = (inflight_stores || not_commit_time) && paddr_ni;
@ -263,8 +274,7 @@ module load_unit import ariane_pkg::*; #(
WAIT_TRANSLATION: begin
translation_req_o = 1'b1;
// we've got a hit and we can continue with the request process
if (dtlb_hit_i)
state_d = WAIT_GNT;
if (dtlb_hit_i) state_d = WAIT_GNT;
// we got an exception
if (ex_i.valid) begin
@ -382,8 +392,7 @@ module load_unit import ariane_pkg::*; #(
// we got an rvalid and it's corresponding request was not flushed
if (req_port_i.data_rvalid && !ldbuf_flushed_q[ldbuf_rindex]) begin
// if the response corresponds to the last request, check that we are not killing it
if((ldbuf_last_id_q != ldbuf_rindex) || !req_port_o.kill_req)
valid_o = 1'b1;
if ((ldbuf_last_id_q != ldbuf_rindex) || !req_port_o.kill_req) valid_o = 1'b1;
// the output is also valid if we got an exception. An exception arrives one cycle after
// dtlb_hit_i is asserted, i.e. when we are in SEND_TAG. Otherwise, the exception
// corresponds to the next request that is already being translated (see below).
@ -422,7 +431,7 @@ module load_unit import ariane_pkg::*; #(
// realign as needed
assign shifted_data = req_port_i.data_rdata >> {ldbuf_rdata.address_offset, 3'b000};
/* // result mux (leaner code, but more logic stages.
/* // result mux (leaner code, but more logic stages.
// can be used instead of the code below (in between //result mux fast) if timing is not so critical)
always_comb begin
unique case (ldbuf_rdata.operation)
@ -437,7 +446,7 @@ module load_unit import ariane_pkg::*; #(
end */
// result mux fast
logic [(riscv::XLEN/8)-1:0] rdata_sign_bits;
logic [ (riscv::XLEN/8)-1:0] rdata_sign_bits;
logic [riscv::XLEN_ALIGN_BYTES-1:0] rdata_offset;
logic rdata_sign_bit, rdata_is_signed, rdata_is_fp_signed;
@ -449,7 +458,7 @@ module load_unit import ariane_pkg::*; #(
( ldbuf_rdata.operation inside {ariane_pkg::LH, ariane_pkg::FLH}) ? ldbuf_rdata.address_offset + 1 :
ldbuf_rdata.address_offset;
for (genvar i = 0; i < (riscv::XLEN/8); i++) begin : gen_sign_bits
for (genvar i = 0; i < (riscv::XLEN / 8); i++) begin : gen_sign_bits
assign rdata_sign_bits[i] = req_port_i.data_rdata[(i+1)*8-1];
end
@ -461,22 +470,25 @@ module load_unit import ariane_pkg::*; #(
// result mux
always_comb begin
unique case (ldbuf_rdata.operation)
ariane_pkg::LW, ariane_pkg::LWU: result_o = {{riscv::XLEN-32{rdata_sign_bit}}, shifted_data[31:0]};
ariane_pkg::LH, ariane_pkg::LHU: result_o = {{riscv::XLEN-32+16{rdata_sign_bit}}, shifted_data[15:0]};
ariane_pkg::LB, ariane_pkg::LBU: result_o = {{riscv::XLEN-32+24{rdata_sign_bit}}, shifted_data[7:0]};
ariane_pkg::LW, ariane_pkg::LWU:
result_o = {{riscv::XLEN - 32{rdata_sign_bit}}, shifted_data[31:0]};
ariane_pkg::LH, ariane_pkg::LHU:
result_o = {{riscv::XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0]};
ariane_pkg::LB, ariane_pkg::LBU:
result_o = {{riscv::XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0]};
ariane_pkg::FLW: begin
if(CVA6Cfg.FpPresent) begin
result_o = {{riscv::XLEN-32{rdata_sign_bit}}, shifted_data[31:0]};
if (CVA6Cfg.FpPresent) begin
result_o = {{riscv::XLEN - 32{rdata_sign_bit}}, shifted_data[31:0]};
end
end
ariane_pkg::FLH: begin
if(CVA6Cfg.FpPresent) begin
result_o = {{riscv::XLEN-32+16{rdata_sign_bit}}, shifted_data[15:0]};
if (CVA6Cfg.FpPresent) begin
result_o = {{riscv::XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0]};
end
end
ariane_pkg::FLB: begin
if(CVA6Cfg.FpPresent) begin
result_o = {{riscv::XLEN-32+24{rdata_sign_bit}}, shifted_data[7:0]};
if (CVA6Cfg.FpPresent) begin
result_o = {{riscv::XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0]};
end
end
@ -489,21 +501,25 @@ module load_unit import ariane_pkg::*; #(
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
//pragma translate_off
`ifndef VERILATOR
initial assert (ariane_pkg::DCACHE_TID_WIDTH >= REQ_ID_BITS) else
$fatal(1, "CVA6ConfigDcacheIdWidth parameter is not wide enough to encode pending loads");
initial
assert (ariane_pkg::DCACHE_TID_WIDTH >= REQ_ID_BITS)
else $fatal(1, "CVA6ConfigDcacheIdWidth parameter is not wide enough to encode pending loads");
// check invalid offsets, but only issue a warning as these conditions actually trigger a load address misaligned exception
addr_offset0: assert property (@(posedge clk_i) disable iff (~rst_ni)
ldbuf_w |-> (ldbuf_wdata.operation inside {ariane_pkg::LW, ariane_pkg::LWU}) |-> ldbuf_wdata.address_offset < 5) else
$fatal(1, "invalid address offset used with {LW, LWU}");
addr_offset1: assert property (@(posedge clk_i) disable iff (~rst_ni)
ldbuf_w |-> (ldbuf_wdata.operation inside {ariane_pkg::LH, ariane_pkg::LHU}) |-> ldbuf_wdata.address_offset < 7) else
$fatal(1, "invalid address offset used with {LH, LHU}");
addr_offset2: assert property (@(posedge clk_i) disable iff (~rst_ni)
ldbuf_w |-> (ldbuf_wdata.operation inside {ariane_pkg::LB, ariane_pkg::LBU}) |-> ldbuf_wdata.address_offset < 8) else
$fatal(1, "invalid address offset used with {LB, LBU}");
addr_offset0 :
assert property (@(posedge clk_i) disable iff (~rst_ni)
ldbuf_w |-> (ldbuf_wdata.operation inside {ariane_pkg::LW, ariane_pkg::LWU}) |-> ldbuf_wdata.address_offset < 5)
else $fatal(1, "invalid address offset used with {LW, LWU}");
addr_offset1 :
assert property (@(posedge clk_i) disable iff (~rst_ni)
ldbuf_w |-> (ldbuf_wdata.operation inside {ariane_pkg::LH, ariane_pkg::LHU}) |-> ldbuf_wdata.address_offset < 7)
else $fatal(1, "invalid address offset used with {LH, LHU}");
addr_offset2 :
assert property (@(posedge clk_i) disable iff (~rst_ni)
ldbuf_w |-> (ldbuf_wdata.operation inside {ariane_pkg::LB, ariane_pkg::LBU}) |-> ldbuf_wdata.address_offset < 8)
else $fatal(1, "invalid address offset used with {LB, LBU}");
`endif
//pragma translate_on
//pragma translate_on
endmodule

View file

@ -23,7 +23,9 @@
// the LSU control should sample it and store it for later application to the units. It does so, by storing it in a
// two element FIFO. This is necessary as we only know very late in the cycle whether the load/store will succeed (address check,
// TLB hit mainly). So we better unconditionally allow another request to arrive and store this request in case we need to.
module lsu_bypass import ariane_pkg::*; #(
module lsu_bypass
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -37,7 +39,7 @@ module lsu_bypass import ariane_pkg::*; #(
output lsu_ctrl_t lsu_ctrl_o,
output logic ready_o
);
);
lsu_ctrl_t [1:0] mem_n, mem_q;
logic read_pointer_n, read_pointer_q;
@ -79,8 +81,7 @@ module lsu_bypass import ariane_pkg::*; #(
status_cnt--;
end
if (pop_st_i && pop_ld_i)
mem_n = '0;
if (pop_st_i && pop_ld_i) mem_n = '0;
if (flush_i) begin
status_cnt = '0;

View file

@ -26,7 +26,9 @@
// 2020-02-17 0.1 S.Jacq MMU Sv32 for CV32A6
// =========================================================================== //
module cva6_mmu_sv32 import ariane_pkg::*; #(
module cva6_mmu_sv32
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned INSTR_TLB_ENTRIES = 2,
parameter int unsigned DATA_TLB_ENTRIES = 2,
@ -111,136 +113,136 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
cva6_tlb_sv32 #(
.CVA6Cfg ( CVA6Cfg ),
.TLB_ENTRIES ( INSTR_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.TLB_ENTRIES(INSTR_TLB_ENTRIES),
.ASID_WIDTH (ASID_WIDTH)
) i_itlb (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_tlb_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(flush_tlb_i),
.update_i ( update_itlb ),
.update_i(update_itlb),
.lu_access_i ( itlb_lu_access ),
.lu_asid_i ( asid_i ),
.asid_to_be_flushed_i ( asid_to_be_flushed_i ),
.vaddr_to_be_flushed_i ( vaddr_to_be_flushed_i ),
.lu_vaddr_i ( icache_areq_i.fetch_vaddr ),
.lu_content_o ( itlb_content ),
.lu_access_i (itlb_lu_access),
.lu_asid_i (asid_i),
.asid_to_be_flushed_i (asid_to_be_flushed_i),
.vaddr_to_be_flushed_i(vaddr_to_be_flushed_i),
.lu_vaddr_i (icache_areq_i.fetch_vaddr),
.lu_content_o (itlb_content),
.lu_is_4M_o ( itlb_is_4M ),
.lu_hit_o ( itlb_lu_hit )
.lu_is_4M_o(itlb_is_4M),
.lu_hit_o (itlb_lu_hit)
);
cva6_tlb_sv32 #(
.CVA6Cfg ( CVA6Cfg ),
.TLB_ENTRIES ( DATA_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.TLB_ENTRIES(DATA_TLB_ENTRIES),
.ASID_WIDTH (ASID_WIDTH)
) i_dtlb (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_tlb_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(flush_tlb_i),
.update_i ( update_dtlb ),
.update_i(update_dtlb),
.lu_access_i ( dtlb_lu_access ),
.lu_asid_i ( asid_i ),
.asid_to_be_flushed_i ( asid_to_be_flushed_i ),
.vaddr_to_be_flushed_i ( vaddr_to_be_flushed_i ),
.lu_vaddr_i ( lsu_vaddr_i ),
.lu_content_o ( dtlb_content ),
.lu_access_i (dtlb_lu_access),
.lu_asid_i (asid_i),
.asid_to_be_flushed_i (asid_to_be_flushed_i),
.vaddr_to_be_flushed_i(vaddr_to_be_flushed_i),
.lu_vaddr_i (lsu_vaddr_i),
.lu_content_o (dtlb_content),
.lu_is_4M_o ( dtlb_is_4M ),
.lu_hit_o ( dtlb_lu_hit )
.lu_is_4M_o(dtlb_is_4M),
.lu_hit_o (dtlb_lu_hit)
);
cva6_shared_tlb_sv32 #(
.CVA6Cfg ( CVA6Cfg ),
.SHARED_TLB_DEPTH ( 64 ),
.SHARED_TLB_WAYS ( 2 ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.SHARED_TLB_DEPTH(64),
.SHARED_TLB_WAYS (2),
.ASID_WIDTH (ASID_WIDTH)
) i_shared_tlb (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_tlb_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(flush_tlb_i),
.enable_translation_i ( enable_translation_i ),
.en_ld_st_translation_i ( en_ld_st_translation_i),
.enable_translation_i (enable_translation_i),
.en_ld_st_translation_i(en_ld_st_translation_i),
.asid_i (asid_i ),
.asid_i (asid_i),
// from TLBs
// did we miss?
.itlb_access_i ( itlb_lu_access ),
.itlb_hit_i ( itlb_lu_hit ),
.itlb_vaddr_i ( icache_areq_i.fetch_vaddr ),
.itlb_access_i(itlb_lu_access),
.itlb_hit_i (itlb_lu_hit),
.itlb_vaddr_i (icache_areq_i.fetch_vaddr),
.dtlb_access_i ( dtlb_lu_access ),
.dtlb_hit_i ( dtlb_lu_hit ),
.dtlb_vaddr_i ( lsu_vaddr_i ),
.dtlb_access_i(dtlb_lu_access),
.dtlb_hit_i (dtlb_lu_hit),
.dtlb_vaddr_i (lsu_vaddr_i),
// to TLBs, update logic
.itlb_update_o ( update_itlb ),
.dtlb_update_o ( update_dtlb ),
.itlb_update_o(update_itlb),
.dtlb_update_o(update_dtlb),
// Performance counters
.itlb_miss_o (itlb_miss_o ),
.dtlb_miss_o (dtlb_miss_o ),
.itlb_miss_o(itlb_miss_o),
.dtlb_miss_o(dtlb_miss_o),
.shared_tlb_access_o ( shared_tlb_access ),
.shared_tlb_hit_o ( shared_tlb_hit ),
.shared_tlb_vaddr_o ( shared_tlb_vaddr ),
.shared_tlb_access_o(shared_tlb_access),
.shared_tlb_hit_o (shared_tlb_hit),
.shared_tlb_vaddr_o (shared_tlb_vaddr),
.itlb_req_o ( itlb_req ),
.itlb_req_o (itlb_req),
// to update shared tlb
.shared_tlb_update_i (update_shared_tlb )
.shared_tlb_update_i(update_shared_tlb)
);
cva6_ptw_sv32 #(
.CVA6Cfg ( CVA6Cfg ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.ASID_WIDTH(ASID_WIDTH)
) i_ptw (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(flush_i),
.ptw_active_o ( ptw_active ),
.walking_instr_o ( walking_instr ),
.ptw_error_o ( ptw_error ),
.ptw_access_exception_o ( ptw_access_exception ),
.ptw_active_o (ptw_active),
.walking_instr_o (walking_instr),
.ptw_error_o (ptw_error),
.ptw_access_exception_o(ptw_access_exception),
.lsu_is_store_i ( lsu_is_store_i ),
.lsu_is_store_i(lsu_is_store_i),
// PTW memory interface
.req_port_i ( req_port_i ),
.req_port_o ( req_port_o ),
.req_port_i (req_port_i),
.req_port_o (req_port_o),
// to Shared TLB, update logic
.shared_tlb_update_o ( update_shared_tlb ),
.shared_tlb_update_o(update_shared_tlb),
.update_vaddr_o ( update_vaddr ),
.update_vaddr_o(update_vaddr),
.asid_i ( asid_i ),
.asid_i(asid_i),
// from shared TLB
// did we miss?
.shared_tlb_access_i ( shared_tlb_access ),
.shared_tlb_hit_i ( shared_tlb_hit ),
.shared_tlb_vaddr_i ( shared_tlb_vaddr ),
.shared_tlb_access_i(shared_tlb_access),
.shared_tlb_hit_i (shared_tlb_hit),
.shared_tlb_vaddr_i (shared_tlb_vaddr),
.itlb_req_i ( itlb_req ),
.itlb_req_i(itlb_req),
// from CSR file
.satp_ppn_i ( satp_ppn_i ), // ppn from satp
.mxr_i ( mxr_i ),
.satp_ppn_i(satp_ppn_i), // ppn from satp
.mxr_i (mxr_i),
// Performance counters
.shared_tlb_miss_o ( ), //open for now
.shared_tlb_miss_o(), //open for now
// PMP
.pmpcfg_i ( pmpcfg_i ),
.pmpaddr_i ( pmpaddr_i ),
.bad_paddr_o ( ptw_bad_paddr )
.pmpcfg_i (pmpcfg_i),
.pmpaddr_i (pmpaddr_i),
.bad_paddr_o(ptw_bad_paddr)
);
);
// ila_1 i_ila_1 (
// .clk(clk_i), // input wire clk
@ -272,7 +274,9 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// MMU disabled: just pass through
icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
if (riscv::PLEN > riscv::VLEN)
icache_areq_o.fetch_paddr = {{riscv::PLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr};// play through in case we disabled address translation
icache_areq_o.fetch_paddr = {
{riscv::PLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr
}; // play through in case we disabled address translation
else
icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[riscv::PLEN-1:0];// play through in case we disabled address translation
// two potential exception sources:
@ -290,7 +294,11 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
if (enable_translation_i) begin
// we work with SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal
if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1};
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT,
{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
1'b1
};
end
icache_areq_o.fetch_valid = 1'b0;
@ -312,9 +320,15 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// we got an access error
if (iaccess_err) begin
// throw a page fault
icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1};//to check on wave --> not connected
icache_areq_o.fetch_exception = {
riscv::INSTR_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
1'b1
}; //to check on wave --> not connected
end else if (!pmp_instr_allow) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1};//to check on wave --> not connected
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1
}; //to check on wave --> not connected
end
end else
// ---------
@ -323,35 +337,45 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// watch out for exceptions happening during walking the page table
if (ptw_active && walking_instr) begin
icache_areq_o.fetch_valid = ptw_error | ptw_access_exception;
if (ptw_error) icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, 1'b1};//to check on wave
if (ptw_error)
icache_areq_o.fetch_exception = {
riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1
}; //to check on wave
// TODO(moschn,zarubaf): What should the value of tval be in this case?
else icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1};//to check on wave --> not connected
else
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1
}; //to check on wave --> not connected
end
end
// if it didn't match any execute region throw an `Instruction Access Fault`
// or: if we are not translating, check PMPs immediately on the paddr
if (!match_any_execute_region || (!enable_translation_i && !pmp_instr_allow)) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1};//to check on wave --> not connected
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr[riscv::PLEN-1:2], 1'b1
}; //to check on wave --> not connected
end
end
// check for execute flag on memory
assign match_any_execute_region = config_pkg::is_inside_execute_regions(CVA6Cfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr});
assign match_any_execute_region = config_pkg::is_inside_execute_regions(
CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}
);
// Instruction fetch
pmp #(
.PLEN ( riscv::PLEN ),
.PMP_LEN ( riscv::PLEN - 2 ),
.NR_ENTRIES ( CVA6Cfg.NrPMPEntries )
.PLEN (riscv::PLEN),
.PMP_LEN (riscv::PLEN - 2),
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
) i_pmp_if (
.addr_i ( icache_areq_o.fetch_paddr ),
.addr_i (icache_areq_o.fetch_paddr),
.priv_lvl_i,
// we will always execute on the instruction fetch port
.access_type_i ( riscv::ACCESS_EXEC ),
.access_type_i(riscv::ACCESS_EXEC),
// Configuration
.conf_addr_i ( pmpaddr_i ),
.conf_i ( pmpcfg_i ),
.allow_o ( pmp_instr_allow )
.conf_addr_i (pmpaddr_i),
.conf_i (pmpcfg_i),
.allow_o (pmp_instr_allow)
);
//-----------------------
@ -371,7 +395,7 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// Wires to PMP checks
riscv::pmp_access_t pmp_access_type;
logic pmp_data_allow;
localparam PPNWMin = (riscv::PPNW-1 > 29) ? 29 : riscv::PPNW-1;
localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1;
// The data interface is simpler and only consists of a request/response interface
always_comb begin : data_interface
// save request and DTLB response
@ -384,8 +408,8 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
dtlb_is_4M_n = dtlb_is_4M;
if (riscv::PLEN > riscv::VLEN) begin
lsu_paddr_o = {{riscv::PLEN-riscv::VLEN{1'b0}}, lsu_vaddr_q};
lsu_dtlb_ppn_o = {{riscv::PLEN-riscv::VLEN{1'b0}},lsu_vaddr_n[riscv::VLEN-1:12]};
lsu_paddr_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_q};
lsu_dtlb_ppn_o = {{riscv::PLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n[riscv::VLEN-1:12]};
end else begin
lsu_paddr_o = lsu_vaddr_q[riscv::PLEN-1:0];
lsu_dtlb_ppn_o = lsu_vaddr_n[riscv::PPNW-1:0];
@ -427,20 +451,32 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// check if the page is write-able and we are not violating privileges
// also check if the dirty flag is set
if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin
lsu_exception_o = {riscv::STORE_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},lsu_vaddr_q}, 1'b1}; //to check on wave
lsu_exception_o = {
riscv::STORE_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q},
1'b1
}; //to check on wave
// Check if any PMPs are violated
end else if (!pmp_data_allow) begin
lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; //only 32 bits on 34b of lsu_paddr_o are returned.
lsu_exception_o = {
riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1
}; //only 32 bits on 34b of lsu_paddr_o are returned.
end
// this is a load
end else begin
// check for sufficient access privileges - throw a page fault if necessary
if (daccess_err) begin
lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},lsu_vaddr_q}, 1'b1};
lsu_exception_o = {
riscv::LOAD_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q},
1'b1
};
// Check if any PMPs are violated
end else if (!pmp_data_allow) begin
lsu_exception_o = {riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1}; //only 32 bits on 34b of lsu_paddr_o are returned.
lsu_exception_o = {
riscv::LD_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1
}; //only 32 bits on 34b of lsu_paddr_o are returned.
end
end
end else
@ -456,9 +492,17 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
lsu_valid_o = 1'b1;
// the page table walker can only throw page faults
if (lsu_is_store_q) begin
lsu_exception_o = {riscv::STORE_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},update_vaddr}, 1'b1};
lsu_exception_o = {
riscv::STORE_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr},
1'b1
};
end else begin
lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},update_vaddr}, 1'b1};
lsu_exception_o = {
riscv::LOAD_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr},
1'b1
};
end
end
@ -469,8 +513,7 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
lsu_exception_o = {riscv::LD_ACCESS_FAULT, ptw_bad_paddr[riscv::PLEN-1:2], 1'b1};
end
end
end
// If translation is not enabled, check the paddr immediately against PMPs
end // If translation is not enabled, check the paddr immediately against PMPs
else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin
if (lsu_is_store_q) begin
lsu_exception_o = {riscv::ST_ACCESS_FAULT, lsu_paddr_o[riscv::PLEN-1:2], 1'b1};
@ -482,17 +525,17 @@ module cva6_mmu_sv32 import ariane_pkg::*; #(
// Load/store PMP check
pmp #(
.PLEN ( riscv::PLEN ),
.PMP_LEN ( riscv::PLEN - 2 ),
.NR_ENTRIES ( CVA6Cfg.NrPMPEntries )
.PLEN (riscv::PLEN),
.PMP_LEN (riscv::PLEN - 2),
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
) i_pmp_data (
.addr_i ( lsu_paddr_o ),
.priv_lvl_i ( ld_st_priv_lvl_i ),
.access_type_i ( pmp_access_type ),
.addr_i (lsu_paddr_o),
.priv_lvl_i (ld_st_priv_lvl_i),
.access_type_i(pmp_access_type),
// Configuration
.conf_addr_i ( pmpaddr_i ),
.conf_i ( pmpcfg_i ),
.allow_o ( pmp_data_allow )
.conf_addr_i (pmpaddr_i),
.conf_i (pmpcfg_i),
.allow_o (pmp_data_allow)
);
// ----------

View file

@ -26,7 +26,9 @@
/* verilator lint_off WIDTH */
module cva6_ptw_sv32 import ariane_pkg::*; #(
module cva6_ptw_sv32
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int ASID_WIDTH = 1
) (
@ -81,7 +83,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
assign pte = riscv::pte_sv32_t'(data_rdata_q);
enum logic[2:0] {
enum logic [2:0] {
IDLE,
WAIT_GRANT,
PTE_LOOKUP,
@ -89,12 +91,15 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
PROPAGATE_ERROR,
PROPAGATE_ACCESS_ERROR,
LATENCY
} state_q, state_d;
}
state_q, state_d;
// SV32 defines two levels of page tables
enum logic {
LVL1, LVL2
} ptw_lvl_q, ptw_lvl_n;
LVL1,
LVL2
}
ptw_lvl_q, ptw_lvl_n;
// is this an instruction page table walk?
logic is_instr_ptw_q, is_instr_ptw_n;
@ -143,24 +148,24 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
assign bad_paddr_o = ptw_access_exception_o ? ptw_pptr_q : 'b0;
pmp #(
.CVA6Cfg ( CVA6Cfg ),
.PLEN ( riscv::PLEN ),
.PMP_LEN ( riscv::PLEN - 2 ),
.NR_ENTRIES ( CVA6Cfg.NrPMPEntries )
.CVA6Cfg (CVA6Cfg),
.PLEN (riscv::PLEN),
.PMP_LEN (riscv::PLEN - 2),
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
) i_pmp_ptw (
.addr_i ( ptw_pptr_q ),
.addr_i (ptw_pptr_q),
// PTW access are always checked as if in S-Mode...
.priv_lvl_i ( riscv::PRIV_LVL_S ),
.priv_lvl_i (riscv::PRIV_LVL_S),
// ...and they are always loads
.access_type_i ( riscv::ACCESS_READ ),
.access_type_i(riscv::ACCESS_READ),
// Configuration
.conf_addr_i ( pmpaddr_i ),
.conf_i ( pmpcfg_i ),
.allow_o ( allow_access )
.conf_addr_i (pmpaddr_i),
.conf_i (pmpcfg_i),
.allow_o (allow_access)
);
assign req_port_o.data_be = be_gen_32(req_port_o.address_index[1:0],req_port_o.data_size );
assign req_port_o.data_be = be_gen_32(req_port_o.address_index[1:0], req_port_o.data_size);
//-------------------
// Page table walker
@ -215,7 +220,9 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
is_instr_ptw_n = 1'b0;
// if we got a Shared TLB miss
if (shared_tlb_access_i & ~shared_tlb_hit_i) begin
ptw_pptr_n = {satp_ppn_i, shared_tlb_vaddr_i[riscv::SV-1:22], 2'b0}; // SATP.PPN * PAGESIZE + VPN*PTESIZE = SATP.PPN * 2^(12) + VPN*4
ptw_pptr_n = {
satp_ppn_i, shared_tlb_vaddr_i[riscv::SV-1:22], 2'b0
}; // SATP.PPN * PAGESIZE + VPN*PTESIZE = SATP.PPN * 2^(12) + VPN*4
is_instr_ptw_n = itlb_req_i;
tlb_update_asid_n = asid_i;
vaddr_n = shared_tlb_vaddr_i;
@ -240,15 +247,13 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
if (data_rvalid_q) begin
// check if the global mapping bit is set
if (pte.g)
global_mapping_n = 1'b1;
if (pte.g) global_mapping_n = 1'b1;
// -------------
// Invalid PTE
// -------------
// If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise a page-fault exception.
if (!pte.v || (!pte.r && pte.w))
state_d = PROPAGATE_ERROR;
if (!pte.v || (!pte.r && pte.w)) state_d = PROPAGATE_ERROR;
// -----------
// Valid PTE
// -----------
@ -266,10 +271,8 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
// If page is not executable, we can directly raise an error. This
// doesn't put a useless entry into the TLB. The same idea applies
// to the access flag since we let the access flag be managed by SW.
if (!pte.x || !pte.a)
state_d = PROPAGATE_ERROR;
else
shared_tlb_update_o.valid = 1'b1;
if (!pte.x || !pte.a) state_d = PROPAGATE_ERROR;
else shared_tlb_update_o.valid = 1'b1;
end else begin
// ------------
@ -340,8 +343,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
end
// wait for the rvalid before going back to IDLE
WAIT_RVALID: begin
if (data_rvalid_q)
state_d = IDLE;
if (data_rvalid_q) state_d = IDLE;
end
LATENCY: begin
state_d = IDLE;
@ -363,8 +365,7 @@ module cva6_ptw_sv32 import ariane_pkg::*; #(
if (((state_q inside {PTE_LOOKUP, WAIT_RVALID}) && !data_rvalid_q) ||
((state_q == WAIT_GRANT) && req_port_i.data_gnt))
state_d = WAIT_RVALID;
else
state_d = LATENCY;
else state_d = LATENCY;
end
end

View file

@ -17,7 +17,9 @@
/* verilator lint_off WIDTH */
module cva6_shared_tlb_sv32 import ariane_pkg::*; #(
module cva6_shared_tlb_sv32
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int SHARED_TLB_DEPTH = 64,
parameter int SHARED_TLB_WAYS = 2,
@ -61,7 +63,8 @@ module cva6_shared_tlb_sv32 import ariane_pkg::*; #(
);
function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh ( input logic [$clog2(SHARED_TLB_WAYS)-1:0] in);
function logic [SHARED_TLB_WAYS-1:0] shared_tlb_way_bin2oh(input logic [$clog2(SHARED_TLB_WAYS
)-1:0] in);
logic [SHARED_TLB_WAYS-1:0] out;
out = '0;
out[in] = 1'b1;
@ -78,32 +81,32 @@ module cva6_shared_tlb_sv32 import ariane_pkg::*; #(
shared_tag_t shared_tag_wr;
shared_tag_t [SHARED_TLB_WAYS-1:0] shared_tag_rd;
logic [SHARED_TLB_DEPTH-1:0][SHARED_TLB_WAYS-1:0] shared_tag_valid_q, shared_tag_valid_d ;
logic [SHARED_TLB_DEPTH-1:0][SHARED_TLB_WAYS-1:0] shared_tag_valid_q, shared_tag_valid_d;
logic [SHARED_TLB_WAYS-1:0] shared_tag_valid;
logic [ SHARED_TLB_WAYS-1:0] shared_tag_valid;
logic [SHARED_TLB_WAYS-1:0] tag_wr_en;
logic [ SHARED_TLB_WAYS-1:0] tag_wr_en;
logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_wr_addr;
logic [$bits(shared_tag_t)-1:0] tag_wr_data;
logic [ $bits(shared_tag_t)-1:0] tag_wr_data;
logic [SHARED_TLB_WAYS-1:0] tag_rd_en;
logic [ SHARED_TLB_WAYS-1:0] tag_rd_en;
logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_rd_addr;
logic [$bits(shared_tag_t)-1:0] tag_rd_data [SHARED_TLB_WAYS-1:0];
logic [ $bits(shared_tag_t)-1:0] tag_rd_data [SHARED_TLB_WAYS-1:0];
logic [SHARED_TLB_WAYS-1:0] tag_req;
logic [SHARED_TLB_WAYS-1:0] tag_we;
logic [ SHARED_TLB_WAYS-1:0] tag_req;
logic [ SHARED_TLB_WAYS-1:0] tag_we;
logic [$clog2(SHARED_TLB_DEPTH)-1:0] tag_addr;
logic [SHARED_TLB_WAYS-1:0] pte_wr_en;
logic [ SHARED_TLB_WAYS-1:0] pte_wr_en;
logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_wr_addr;
logic [$bits(riscv::pte_sv32_t)-1:0] pte_wr_data;
logic [SHARED_TLB_WAYS-1:0] pte_rd_en;
logic [ SHARED_TLB_WAYS-1:0] pte_rd_en;
logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_rd_addr;
logic [$bits(riscv::pte_sv32_t)-1:0] pte_rd_data [SHARED_TLB_WAYS-1:0];
logic [SHARED_TLB_WAYS-1:0] pte_req;
logic [SHARED_TLB_WAYS-1:0] pte_we;
logic [ SHARED_TLB_WAYS-1:0] pte_req;
logic [ SHARED_TLB_WAYS-1:0] pte_we;
logic [$clog2(SHARED_TLB_DEPTH)-1:0] pte_addr;
logic [9:0] vpn0_d, vpn1_d, vpn0_q, vpn1_q;
@ -292,21 +295,21 @@ module cva6_shared_tlb_sv32 import ariane_pkg::*; #(
assign repl_way_oh_d = (shared_tlb_update_i.valid) ? shared_tlb_way_bin2oh(repl_way) : '0;
lzc #(
.WIDTH ( SHARED_TLB_WAYS )
.WIDTH(SHARED_TLB_WAYS)
) i_lzc (
.in_i ( ~way_valid ),
.cnt_o ( inv_way ),
.empty_o ( all_ways_valid )
.in_i (~way_valid),
.cnt_o (inv_way),
.empty_o(all_ways_valid)
);
lfsr #(
.LfsrWidth ( 8 ),
.OutWidth ( $clog2(SHARED_TLB_WAYS))
.LfsrWidth(8),
.OutWidth ($clog2(SHARED_TLB_WAYS))
) i_lfsr (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.en_i ( update_lfsr ),
.out_o ( rnd_way )
.clk_i (clk_i),
.rst_ni(rst_ni),
.en_i (update_lfsr),
.out_o (rnd_way)
);
///////////////////////////////////////////////////////
@ -324,38 +327,38 @@ module cva6_shared_tlb_sv32 import ariane_pkg::*; #(
for (genvar i = 0; i < SHARED_TLB_WAYS; i++) begin : gen_sram
// Tag RAM
sram #(
.DATA_WIDTH ( $bits(shared_tag_t) ),
.NUM_WORDS ( SHARED_TLB_DEPTH )
.DATA_WIDTH($bits(shared_tag_t)),
.NUM_WORDS (SHARED_TLB_DEPTH)
) tag_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( tag_req[i] ),
.we_i ( tag_we[i] ),
.addr_i ( tag_addr ),
.wuser_i ( '0 ),
.wdata_i ( tag_wr_data ),
.be_i ( '1 ),
.ruser_o ( ),
.rdata_o ( tag_rd_data[i] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (tag_req[i]),
.we_i (tag_we[i]),
.addr_i (tag_addr),
.wuser_i('0),
.wdata_i(tag_wr_data),
.be_i ('1),
.ruser_o(),
.rdata_o(tag_rd_data[i])
);
assign shared_tag_rd[i] = shared_tag_t'(tag_rd_data[i]);
// PTE RAM
sram #(
.DATA_WIDTH ( $bits(riscv::pte_sv32_t) ),
.NUM_WORDS ( SHARED_TLB_DEPTH )
.DATA_WIDTH($bits(riscv::pte_sv32_t)),
.NUM_WORDS (SHARED_TLB_DEPTH)
) pte_sram (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( pte_req[i] ),
.we_i ( pte_we[i] ),
.addr_i ( pte_addr ),
.wuser_i ( '0 ),
.wdata_i ( pte_wr_data ),
.be_i ( '1 ),
.ruser_o ( ),
.rdata_o ( pte_rd_data[i] )
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (pte_req[i]),
.we_i (pte_we[i]),
.addr_i (pte_addr),
.wuser_i('0),
.wdata_i(pte_wr_data),
.be_i ('1),
.ruser_o(),
.rdata_o(pte_rd_data[i])
);
assign pte[i] = riscv::pte_sv32_t'(pte_rd_data[i]);
end

View file

@ -24,11 +24,13 @@
// 2020-02-17 0.1 S.Jacq TLB Sv32 for CV32A6
// =========================================================================== //
module cva6_tlb_sv32 import ariane_pkg::*; #(
module cva6_tlb_sv32
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned TLB_ENTRIES = 4,
parameter int unsigned ASID_WIDTH = 1
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic flush_i, // Flush signal
@ -52,7 +54,8 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
logic [9:0] vpn0; //10 bits wide
logic is_4M;
logic valid;
} [TLB_ENTRIES-1:0] tags_q, tags_n;
} [TLB_ENTRIES-1:0]
tags_q, tags_n;
riscv::pte_sv32_t [TLB_ENTRIES-1:0] content_q, content_n;
logic [9:0] vpn0, vpn1;
@ -110,8 +113,7 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
if (flush_i) begin
// invalidate logic
// flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case)
if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0 )
tags_n[i].valid = 1'b0;
if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0;
// flush vaddr in all addressing space ("SFENCE.VMA vaddr x0" case), it should happen only for leaf pages
else if (asid_to_be_flushed_is0 && ( (vaddr_vpn0_match[i] && vaddr_vpn1_match[i]) || (vaddr_vpn1_match[i] && tags_q[i].is_4M) ) && (~vaddr_to_be_flushed_is0))
tags_n[i].valid = 1'b0;
@ -126,8 +128,8 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
// update tag array
tags_n[i] = '{
asid: update_i.asid,
vpn1: update_i.vpn [19:10],
vpn0: update_i.vpn [9:0],
vpn1: update_i.vpn[19:10],
vpn0: update_i.vpn[9:0],
is_4M: update_i.is_4M,
valid: 1'b1
};
@ -140,7 +142,7 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
// -----------------------------------------------
// PLRU - Pseudo Least Recently Used Replacement
// -----------------------------------------------
logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
logic [2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
logic en;
int unsigned idx_base, shift, new_index;
always_comb begin : plru_replacement
@ -172,17 +174,19 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
// lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
// default: begin /* No hit */ end
// endcase
for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
for (
int unsigned i = 0; i < TLB_ENTRIES; i++
) begin
// we got a hit so update the pointer as it was least recently used
if (lu_hit[i] & lu_access_i) begin
// Set the nodes to the values we would expect
for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
idx_base = $unsigned((2**lvl)-1);
idx_base = $unsigned((2 ** lvl) - 1);
// lvl0 <=> MSB, lvl1 <=> MSB-1, ...
shift = $clog2(TLB_ENTRIES) - lvl;
// to circumvent the 32 bit integer arithmetic assignment
new_index = ~((i >> (shift-1)) & 32'b1);
plru_tree_n[idx_base + (i >> shift)] = new_index[0];
new_index = ~((i >> (shift - 1)) & 32'b1);
plru_tree_n[idx_base+(i>>shift)] = new_index[0];
end
end
end
@ -203,16 +207,16 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
en = 1'b1;
for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
idx_base = $unsigned((2**lvl)-1);
idx_base = $unsigned((2 ** lvl) - 1);
// lvl0 <=> MSB, lvl1 <=> MSB-1, ...
shift = $clog2(TLB_ENTRIES) - lvl;
// en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
new_index = (i >> (shift-1)) & 32'b1;
new_index = (i >> (shift - 1)) & 32'b1;
if (new_index[0]) begin
en &= plru_tree_q[idx_base + (i>>shift)];
en &= plru_tree_q[idx_base+(i>>shift)];
end else begin
en &= ~plru_tree_q[idx_base + (i>>shift)];
en &= ~plru_tree_q[idx_base+(i>>shift)];
end
end
replace_en[i] = en;
@ -221,7 +225,7 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
if (~rst_ni) begin
tags_q <= '{default: 0};
content_q <= '{default: 0};
plru_tree_q <= '{default: 0};
@ -236,17 +240,23 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
//--------------
//pragma translate_off
`ifndef VERILATOR
`ifndef VERILATOR
initial begin : p_assertions
assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
else begin
$error("TLB size must be a multiple of 2 and greater than 1");
$stop();
end
assert (ASID_WIDTH >= 1)
else begin $error("ASID width must be at least 1"); $stop(); end
else begin
$error("ASID width must be at least 1");
$stop();
end
end
// Just for checking
function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
function int countSetBits(logic [TLB_ENTRIES-1:0] vector);
automatic int count = 0;
foreach (vector[idx]) begin
count += vector[idx];
@ -254,12 +264,18 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
return count;
endfunction
assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
else begin $error("More then one hit in TLB!"); $stop(); end
assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1))
else begin
$error("More then one hit in TLB!");
$stop();
end
assert property (@(posedge clk_i) (countSetBits(replace_en) <= 1))
else begin
$error("More then one TLB entry selected for next replace!");
$stop();
end
`endif
`endif
//pragma translate_on
endmodule

View file

@ -15,7 +15,9 @@
// privilege specification 1.11-WIP
module mmu import ariane_pkg::*; #(
module mmu
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned INSTR_TLB_ENTRIES = 4,
parameter int unsigned DATA_TLB_ENTRIES = 4,
@ -96,81 +98,81 @@ module mmu import ariane_pkg::*; #(
tlb #(
.CVA6Cfg ( CVA6Cfg ),
.TLB_ENTRIES ( INSTR_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.TLB_ENTRIES(INSTR_TLB_ENTRIES),
.ASID_WIDTH (ASID_WIDTH)
) i_itlb (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_tlb_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(flush_tlb_i),
.update_i ( update_ptw_itlb ),
.update_i(update_ptw_itlb),
.lu_access_i ( itlb_lu_access ),
.lu_asid_i ( asid_i ),
.asid_to_be_flushed_i ( asid_to_be_flushed_i ),
.vaddr_to_be_flushed_i ( vaddr_to_be_flushed_i ),
.lu_vaddr_i ( icache_areq_i.fetch_vaddr ),
.lu_content_o ( itlb_content ),
.lu_access_i (itlb_lu_access),
.lu_asid_i (asid_i),
.asid_to_be_flushed_i (asid_to_be_flushed_i),
.vaddr_to_be_flushed_i(vaddr_to_be_flushed_i),
.lu_vaddr_i (icache_areq_i.fetch_vaddr),
.lu_content_o (itlb_content),
.lu_is_2M_o ( itlb_is_2M ),
.lu_is_1G_o ( itlb_is_1G ),
.lu_hit_o ( itlb_lu_hit )
.lu_is_2M_o(itlb_is_2M),
.lu_is_1G_o(itlb_is_1G),
.lu_hit_o (itlb_lu_hit)
);
tlb #(
.CVA6Cfg ( CVA6Cfg ),
.TLB_ENTRIES ( DATA_TLB_ENTRIES ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.TLB_ENTRIES(DATA_TLB_ENTRIES),
.ASID_WIDTH (ASID_WIDTH)
) i_dtlb (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( flush_tlb_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(flush_tlb_i),
.update_i ( update_ptw_dtlb ),
.update_i(update_ptw_dtlb),
.lu_access_i ( dtlb_lu_access ),
.lu_asid_i ( asid_i ),
.asid_to_be_flushed_i ( asid_to_be_flushed_i ),
.vaddr_to_be_flushed_i ( vaddr_to_be_flushed_i ),
.lu_vaddr_i ( lsu_vaddr_i ),
.lu_content_o ( dtlb_content ),
.lu_access_i (dtlb_lu_access),
.lu_asid_i (asid_i),
.asid_to_be_flushed_i (asid_to_be_flushed_i),
.vaddr_to_be_flushed_i(vaddr_to_be_flushed_i),
.lu_vaddr_i (lsu_vaddr_i),
.lu_content_o (dtlb_content),
.lu_is_2M_o ( dtlb_is_2M ),
.lu_is_1G_o ( dtlb_is_1G ),
.lu_hit_o ( dtlb_lu_hit )
.lu_is_2M_o(dtlb_is_2M),
.lu_is_1G_o(dtlb_is_1G),
.lu_hit_o (dtlb_lu_hit)
);
ptw #(
.CVA6Cfg ( CVA6Cfg ),
.ASID_WIDTH ( ASID_WIDTH )
.CVA6Cfg (CVA6Cfg),
.ASID_WIDTH(ASID_WIDTH)
) i_ptw (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.ptw_active_o ( ptw_active ),
.walking_instr_o ( walking_instr ),
.ptw_error_o ( ptw_error ),
.ptw_access_exception_o ( ptw_access_exception ),
.enable_translation_i ( enable_translation_i ),
.clk_i (clk_i),
.rst_ni (rst_ni),
.ptw_active_o (ptw_active),
.walking_instr_o (walking_instr),
.ptw_error_o (ptw_error),
.ptw_access_exception_o(ptw_access_exception),
.enable_translation_i (enable_translation_i),
.update_vaddr_o ( update_vaddr ),
.itlb_update_o ( update_ptw_itlb ),
.dtlb_update_o ( update_ptw_dtlb ),
.update_vaddr_o(update_vaddr),
.itlb_update_o (update_ptw_itlb),
.dtlb_update_o (update_ptw_dtlb),
.itlb_access_i ( itlb_lu_access ),
.itlb_hit_i ( itlb_lu_hit ),
.itlb_vaddr_i ( icache_areq_i.fetch_vaddr ),
.itlb_access_i(itlb_lu_access),
.itlb_hit_i (itlb_lu_hit),
.itlb_vaddr_i (icache_areq_i.fetch_vaddr),
.dtlb_access_i ( dtlb_lu_access ),
.dtlb_hit_i ( dtlb_lu_hit ),
.dtlb_vaddr_i ( lsu_vaddr_i ),
.dtlb_access_i(dtlb_lu_access),
.dtlb_hit_i (dtlb_lu_hit),
.dtlb_vaddr_i (lsu_vaddr_i),
.req_port_i ( req_port_i ),
.req_port_o ( req_port_o ),
.req_port_i (req_port_i),
.req_port_o (req_port_o),
.pmpcfg_i,
.pmpaddr_i,
.bad_paddr_o ( ptw_bad_paddr ),
.bad_paddr_o(ptw_bad_paddr),
.*
);
@ -220,7 +222,11 @@ module mmu import ariane_pkg::*; #(
if (enable_translation_i) begin
// we work with SV39 or SV32, so if VM is enabled, check that all bits [riscv::VLEN-1:riscv::SV-1] are equal
if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[riscv::VLEN-1:riscv::SV-1]) == 1'b0)) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1};
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT,
{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
1'b1
};
end
icache_areq_o.fetch_valid = 1'b0;
@ -245,9 +251,17 @@ module mmu import ariane_pkg::*; #(
// we got an access error
if (iaccess_err) begin
// throw a page fault
icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1};
icache_areq_o.fetch_exception = {
riscv::INSTR_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
1'b1
};
end else if (!pmp_instr_allow) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr}, 1'b1};
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT,
{{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_i.fetch_vaddr},
1'b1
};
end
end else
// ---------
@ -256,35 +270,47 @@ module mmu import ariane_pkg::*; #(
// watch out for exceptions happening during walking the page table
if (ptw_active && walking_instr) begin
icache_areq_o.fetch_valid = ptw_error | ptw_access_exception;
if (ptw_error) icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, 1'b1};
else icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, update_vaddr}, 1'b1};
if (ptw_error)
icache_areq_o.fetch_exception = {
riscv::INSTR_PAGE_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1
};
else
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, update_vaddr}, 1'b1
};
end
end
// if it didn't match any execute region throw an `Instruction Access Fault`
// or: if we are not translating, check PMPs immediately on the paddr
if ((!match_any_execute_region && !ptw_error) || (!enable_translation_i && !pmp_instr_allow)) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, {{riscv::XLEN-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}, 1'b1};
icache_areq_o.fetch_exception = {
riscv::INSTR_ACCESS_FAULT,
{{riscv::XLEN - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr},
1'b1
};
end
end
// check for execute flag on memory
assign match_any_execute_region = config_pkg::is_inside_execute_regions(CVA6Cfg, {{64-riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr});
assign match_any_execute_region = config_pkg::is_inside_execute_regions(
CVA6Cfg, {{64 - riscv::PLEN{1'b0}}, icache_areq_o.fetch_paddr}
);
// Instruction fetch
pmp #(
.CVA6Cfg ( CVA6Cfg ),
.PLEN ( riscv::PLEN ),
.PMP_LEN ( riscv::PLEN - 2 ),
.NR_ENTRIES ( CVA6Cfg.NrPMPEntries )
.CVA6Cfg (CVA6Cfg),
.PLEN (riscv::PLEN),
.PMP_LEN (riscv::PLEN - 2),
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
) i_pmp_if (
.addr_i ( icache_areq_o.fetch_paddr ),
.addr_i (icache_areq_o.fetch_paddr),
.priv_lvl_i,
// we will always execute on the instruction fetch port
.access_type_i ( riscv::ACCESS_EXEC ),
.access_type_i(riscv::ACCESS_EXEC),
// Configuration
.conf_addr_i ( pmpaddr_i ),
.conf_i ( pmpcfg_i ),
.allow_o ( pmp_instr_allow )
.conf_addr_i (pmpaddr_i),
.conf_i (pmpcfg_i),
.allow_o (pmp_instr_allow)
);
//-----------------------
@ -305,7 +331,7 @@ module mmu import ariane_pkg::*; #(
// Wires to PMP checks
riscv::pmp_access_t pmp_access_type;
logic pmp_data_allow;
localparam PPNWMin = (riscv::PPNW-1 > 29) ? 29 : riscv::PPNW-1;
localparam PPNWMin = (riscv::PPNW - 1 > 29) ? 29 : riscv::PPNW - 1;
// The data interface is simpler and only consists of a request/response interface
always_comb begin : data_interface
// save request and DTLB response
@ -362,20 +388,36 @@ module mmu import ariane_pkg::*; #(
// check if the page is write-able and we are not violating privileges
// also check if the dirty flag is set
if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin
lsu_exception_o = {riscv::STORE_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},lsu_vaddr_q}, 1'b1};
lsu_exception_o = {
riscv::STORE_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q},
1'b1
};
// Check if any PMPs are violated
end else if (!pmp_data_allow) begin
lsu_exception_o = {riscv::ST_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},lsu_vaddr_q}, 1'b1};
lsu_exception_o = {
riscv::ST_ACCESS_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q},
1'b1
};
end
// this is a load
end else begin
// check for sufficient access privileges - throw a page fault if necessary
if (daccess_err) begin
lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},lsu_vaddr_q}, 1'b1};
lsu_exception_o = {
riscv::LOAD_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q},
1'b1
};
// Check if any PMPs are violated
end else if (!pmp_data_allow) begin
lsu_exception_o = {riscv::LD_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},lsu_vaddr_q}, 1'b1};
lsu_exception_o = {
riscv::LD_ACCESS_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, lsu_vaddr_q},
1'b1
};
end
end
end else
@ -391,9 +433,17 @@ module mmu import ariane_pkg::*; #(
lsu_valid_o = 1'b1;
// the page table walker can only throw page faults
if (lsu_is_store_q) begin
lsu_exception_o = {riscv::STORE_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},update_vaddr}, 1'b1};
lsu_exception_o = {
riscv::STORE_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr},
1'b1
};
end else begin
lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {{riscv::XLEN-riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}},update_vaddr}, 1'b1};
lsu_exception_o = {
riscv::LOAD_PAGE_FAULT,
{{riscv::XLEN - riscv::VLEN{lsu_vaddr_q[riscv::VLEN-1]}}, update_vaddr},
1'b1
};
end
end
@ -402,37 +452,44 @@ module mmu import ariane_pkg::*; #(
lsu_valid_o = 1'b1;
// Any fault of the page table walk should be based of the original access type
if (lsu_is_store_q) begin
lsu_exception_o = {riscv::ST_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1};
lsu_exception_o = {
riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1
};
end else begin
lsu_exception_o = {riscv::LD_ACCESS_FAULT, {{riscv::XLEN-riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1};
lsu_exception_o = {
riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::VLEN{1'b0}}, lsu_vaddr_n}, 1'b1
};
end
end
end
end
// If translation is not enabled, check the paddr immediately against PMPs
end // If translation is not enabled, check the paddr immediately against PMPs
else if (lsu_req_q && !misaligned_ex_q.valid && !pmp_data_allow) begin
if (lsu_is_store_q) begin
lsu_exception_o = {riscv::ST_ACCESS_FAULT, {{riscv::XLEN-riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1};
lsu_exception_o = {
riscv::ST_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1
};
end else begin
lsu_exception_o = {riscv::LD_ACCESS_FAULT, {{riscv::XLEN-riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1};
lsu_exception_o = {
riscv::LD_ACCESS_FAULT, {{riscv::XLEN - riscv::PLEN{1'b0}}, lsu_paddr_o}, 1'b1
};
end
end
end
// Load/store PMP check
pmp #(
.CVA6Cfg ( CVA6Cfg ),
.PLEN ( riscv::PLEN ),
.PMP_LEN ( riscv::PLEN - 2 ),
.NR_ENTRIES ( CVA6Cfg.NrPMPEntries )
.CVA6Cfg (CVA6Cfg),
.PLEN (riscv::PLEN),
.PMP_LEN (riscv::PLEN - 2),
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
) i_pmp_data (
.addr_i ( lsu_paddr_o ),
.priv_lvl_i ( ld_st_priv_lvl_i ),
.access_type_i ( pmp_access_type ),
.addr_i (lsu_paddr_o),
.priv_lvl_i (ld_st_priv_lvl_i),
.access_type_i(pmp_access_type),
// Configuration
.conf_addr_i ( pmpaddr_i ),
.conf_i ( pmpcfg_i ),
.allow_o ( pmp_data_allow )
.conf_addr_i (pmpaddr_i),
.conf_i (pmpcfg_i),
.allow_o (pmp_data_allow)
);
// ----------

View file

@ -15,7 +15,9 @@
/* verilator lint_off WIDTH */
module ptw import ariane_pkg::*; #(
module ptw
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int ASID_WIDTH = 1
) (
@ -43,7 +45,7 @@ module ptw import ariane_pkg::*; #(
output logic [riscv::VLEN-1:0] update_vaddr_o,
input logic [ASID_WIDTH-1:0] asid_i,
input logic [ ASID_WIDTH-1:0] asid_i,
// from TLBs
// did we miss?
input logic itlb_access_i,
@ -74,19 +76,23 @@ module ptw import ariane_pkg::*; #(
riscv::pte_t pte;
assign pte = riscv::pte_t'(data_rdata_q);
enum logic[2:0] {
enum logic [2:0] {
IDLE,
WAIT_GRANT,
PTE_LOOKUP,
WAIT_RVALID,
PROPAGATE_ERROR,
PROPAGATE_ACCESS_ERROR
} state_q, state_d;
}
state_q, state_d;
// SV39 defines three levels of page tables
enum logic [1:0] {
LVL1, LVL2, LVL3
} ptw_lvl_q, ptw_lvl_n;
LVL1,
LVL2,
LVL3
}
ptw_lvl_q, ptw_lvl_n;
// is this an instruction page table walk?
logic is_instr_ptw_q, is_instr_ptw_n;
@ -117,8 +123,8 @@ module ptw import ariane_pkg::*; #(
// -----------
// TLB Update
// -----------
assign itlb_update_o.vpn = {{39-riscv::SV{1'b0}}, vaddr_q[riscv::SV-1:12]};
assign dtlb_update_o.vpn = {{39-riscv::SV{1'b0}}, vaddr_q[riscv::SV-1:12]};
assign itlb_update_o.vpn = {{39 - riscv::SV{1'b0}}, vaddr_q[riscv::SV-1:12]};
assign dtlb_update_o.vpn = {{39 - riscv::SV{1'b0}}, vaddr_q[riscv::SV-1:12]};
// update the correct page table level
assign itlb_update_o.is_2M = (ptw_lvl_q == LVL2);
assign itlb_update_o.is_1G = (ptw_lvl_q == LVL1);
@ -138,20 +144,20 @@ module ptw import ariane_pkg::*; #(
assign bad_paddr_o = ptw_access_exception_o ? ptw_pptr_q : 'b0;
pmp #(
.CVA6Cfg ( CVA6Cfg ),
.PLEN ( riscv::PLEN ),
.PMP_LEN ( riscv::PLEN - 2 ),
.NR_ENTRIES ( CVA6Cfg.NrPMPEntries )
.CVA6Cfg (CVA6Cfg),
.PLEN (riscv::PLEN),
.PMP_LEN (riscv::PLEN - 2),
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
) i_pmp_ptw (
.addr_i ( ptw_pptr_q ),
.addr_i (ptw_pptr_q),
// PTW access are always checked as if in S-Mode...
.priv_lvl_i ( riscv::PRIV_LVL_S ),
.priv_lvl_i (riscv::PRIV_LVL_S),
// ...and they are always loads
.access_type_i ( riscv::ACCESS_READ ),
.access_type_i(riscv::ACCESS_READ),
// Configuration
.conf_addr_i ( pmpaddr_i ),
.conf_i ( pmpcfg_i ),
.allow_o ( allow_access )
.conf_addr_i (pmpaddr_i),
.conf_i (pmpcfg_i),
.allow_o (allow_access)
);
//-------------------
@ -242,15 +248,13 @@ module ptw import ariane_pkg::*; #(
if (data_rvalid_q) begin
// check if the global mapping bit is set
if (pte.g)
global_mapping_n = 1'b1;
if (pte.g) global_mapping_n = 1'b1;
// -------------
// Invalid PTE
// -------------
// If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise a page-fault exception.
if (!pte.v || (!pte.r && pte.w))
state_d = PROPAGATE_ERROR;
if (!pte.v || (!pte.r && pte.w)) state_d = PROPAGATE_ERROR;
// -----------
// Valid PTE
// -----------
@ -267,10 +271,8 @@ module ptw import ariane_pkg::*; #(
// If page is not executable, we can directly raise an error. This
// doesn't put a useless entry into the TLB. The same idea applies
// to the access flag since we let the access flag be managed by SW.
if (!pte.x || !pte.a)
state_d = PROPAGATE_ERROR;
else
itlb_update_o.valid = 1'b1;
if (!pte.x || !pte.a) state_d = PROPAGATE_ERROR;
else itlb_update_o.valid = 1'b1;
end else begin
// ------------
@ -353,8 +355,7 @@ module ptw import ariane_pkg::*; #(
end
// wait for the rvalid before going back to IDLE
WAIT_RVALID: begin
if (data_rvalid_q)
state_d = IDLE;
if (data_rvalid_q) state_d = IDLE;
end
default: begin
state_d = IDLE;
@ -373,8 +374,7 @@ module ptw import ariane_pkg::*; #(
if (((state_q inside {PTE_LOOKUP, WAIT_RVALID}) && !data_rvalid_q) ||
((state_q == WAIT_GRANT) && req_port_i.data_gnt))
state_d = WAIT_RVALID;
else
state_d = IDLE;
else state_d = IDLE;
end
end

View file

@ -15,11 +15,13 @@
// fully set-associative
module tlb import ariane_pkg::*; #(
module tlb
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned TLB_ENTRIES = 4,
parameter int unsigned ASID_WIDTH = 1
)(
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic flush_i, // Flush signal
@ -27,10 +29,10 @@ module tlb import ariane_pkg::*; #(
input tlb_update_t update_i,
// Lookup signals
input logic lu_access_i,
input logic [ASID_WIDTH-1:0] lu_asid_i,
input logic [ ASID_WIDTH-1:0] lu_asid_i,
input logic [riscv::VLEN-1:0] lu_vaddr_i,
output riscv::pte_t lu_content_o,
input logic [ASID_WIDTH-1:0] asid_to_be_flushed_i,
input logic [ ASID_WIDTH-1:0] asid_to_be_flushed_i,
input logic [riscv::VLEN-1:0] vaddr_to_be_flushed_i,
output logic lu_is_2M_o,
output logic lu_is_1G_o,
@ -46,11 +48,12 @@ module tlb import ariane_pkg::*; #(
logic is_2M;
logic is_1G;
logic valid;
} [TLB_ENTRIES-1:0] tags_q, tags_n;
} [TLB_ENTRIES-1:0]
tags_q, tags_n;
riscv::pte_t [TLB_ENTRIES-1:0] content_q, content_n;
logic [8:0] vpn0, vpn1;
logic [riscv::VPN2:0] vpn2;
logic [ riscv::VPN2:0] vpn2;
logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
//-------------
@ -120,8 +123,7 @@ module tlb import ariane_pkg::*; #(
if (flush_i) begin
// invalidate logic
// flush everything if ASID is 0 and vaddr is 0 ("SFENCE.VMA x0 x0" case)
if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0 )
tags_n[i].valid = 1'b0;
if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0;
// flush vaddr in all addressing space ("SFENCE.VMA vaddr x0" case), it should happen only for leaf pages
else if (asid_to_be_flushed_is0 && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_2M) ) && (~vaddr_to_be_flushed_is0))
tags_n[i].valid = 1'b0;
@ -136,9 +138,9 @@ module tlb import ariane_pkg::*; #(
// update tag array
tags_n[i] = '{
asid: update_i.asid,
vpn2: update_i.vpn [18+riscv::VPN2:18],
vpn1: update_i.vpn [17:9],
vpn0: update_i.vpn [8:0],
vpn2: update_i.vpn[18+riscv::VPN2:18],
vpn1: update_i.vpn[17:9],
vpn0: update_i.vpn[8:0],
is_1G: update_i.is_1G,
is_2M: update_i.is_2M,
valid: 1'b1
@ -152,7 +154,7 @@ module tlb import ariane_pkg::*; #(
// -----------------------------------------------
// PLRU - Pseudo Least Recently Used Replacement
// -----------------------------------------------
logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
logic [2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
always_comb begin : plru_replacement
plru_tree_n = plru_tree_q;
// The PLRU-tree indexing:
@ -178,18 +180,20 @@ module tlb import ariane_pkg::*; #(
// lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
// default: begin /* No hit */ end
// endcase
for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
for (
int unsigned i = 0; i < TLB_ENTRIES; i++
) begin
automatic int unsigned idx_base, shift, new_index;
// we got a hit so update the pointer as it was least recently used
if (lu_hit[i] & lu_access_i) begin
// Set the nodes to the values we would expect
for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
idx_base = $unsigned((2**lvl)-1);
idx_base = $unsigned((2 ** lvl) - 1);
// lvl0 <=> MSB, lvl1 <=> MSB-1, ...
shift = $clog2(TLB_ENTRIES) - lvl;
// to circumvent the 32 bit integer arithmetic assignment
new_index = ~((i >> (shift-1)) & 32'b1);
plru_tree_n[idx_base + (i >> shift)] = new_index[0];
new_index = ~((i >> (shift - 1)) & 32'b1);
plru_tree_n[idx_base+(i>>shift)] = new_index[0];
end
end
end
@ -212,16 +216,16 @@ module tlb import ariane_pkg::*; #(
automatic int unsigned idx_base, shift, new_index;
en = 1'b1;
for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
idx_base = $unsigned((2**lvl)-1);
idx_base = $unsigned((2 ** lvl) - 1);
// lvl0 <=> MSB, lvl1 <=> MSB-1, ...
shift = $clog2(TLB_ENTRIES) - lvl;
// en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
new_index = (i >> (shift-1)) & 32'b1;
new_index = (i >> (shift - 1)) & 32'b1;
if (new_index[0]) begin
en &= plru_tree_q[idx_base + (i>>shift)];
en &= plru_tree_q[idx_base+(i>>shift)];
end else begin
en &= ~plru_tree_q[idx_base + (i>>shift)];
en &= ~plru_tree_q[idx_base+(i>>shift)];
end
end
replace_en[i] = en;
@ -230,7 +234,7 @@ module tlb import ariane_pkg::*; #(
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
if (~rst_ni) begin
tags_q <= '{default: 0};
content_q <= '{default: 0};
plru_tree_q <= '{default: 0};
@ -245,17 +249,23 @@ module tlb import ariane_pkg::*; #(
//--------------
//pragma translate_off
`ifndef VERILATOR
`ifndef VERILATOR
initial begin : p_assertions
assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
else begin
$error("TLB size must be a multiple of 2 and greater than 1");
$stop();
end
assert (ASID_WIDTH >= 1)
else begin $error("ASID width must be at least 1"); $stop(); end
else begin
$error("ASID width must be at least 1");
$stop();
end
end
// Just for checking
function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
function int countSetBits(logic [TLB_ENTRIES-1:0] vector);
automatic int count = 0;
foreach (vector[idx]) begin
count += vector[idx];
@ -263,12 +273,18 @@ module tlb import ariane_pkg::*; #(
return count;
endfunction
assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
else begin $error("More then one hit in TLB!"); $stop(); end
assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1))
else begin
$error("More then one hit in TLB!");
$stop();
end
assert property (@(posedge clk_i) (countSetBits(replace_en) <= 1))
else begin
$error("More then one TLB entry selected for next replace!");
$stop();
end
`endif
`endif
//pragma translate_on
endmodule

View file

@ -1,6 +1,8 @@
module mult import ariane_pkg::*; #(
module mult
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -44,25 +46,27 @@ module mult import ariane_pkg::*; #(
// Multiplication
// ---------------------
multiplier #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_multiplier (
.clk_i,
.rst_ni,
.trans_id_i ( fu_data_i.trans_id ),
.operation_i ( fu_data_i.operation ),
.operand_a_i ( fu_data_i.operand_a ),
.operand_b_i ( fu_data_i.operand_b ),
.result_o ( mul_result ),
.mult_valid_i ( mul_valid_op ),
.mult_valid_o ( mul_valid ),
.mult_trans_id_o ( mul_trans_id ),
.mult_ready_o ( ) // this unit is unconditionally ready
.trans_id_i (fu_data_i.trans_id),
.operation_i (fu_data_i.operation),
.operand_a_i (fu_data_i.operand_a),
.operand_b_i (fu_data_i.operand_b),
.result_o (mul_result),
.mult_valid_i (mul_valid_op),
.mult_valid_o (mul_valid),
.mult_trans_id_o(mul_trans_id),
.mult_ready_o () // this unit is unconditionally ready
);
// ---------------------
// Division
// ---------------------
riscv::xlen_t operand_b, operand_a; // input operands after input MUX (input silencing, word operations or full inputs)
riscv::xlen_t
operand_b,
operand_a; // input operands after input MUX (input silencing, word operations or full inputs)
riscv::xlen_t result; // result before result mux
logic div_signed; // signed or unsigned division
@ -110,22 +114,22 @@ module mult import ariane_pkg::*; #(
// Serial Divider
// ---------------------
serdiv #(
.CVA6Cfg ( CVA6Cfg ),
.WIDTH ( riscv::XLEN )
.CVA6Cfg(CVA6Cfg),
.WIDTH (riscv::XLEN)
) i_div (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.id_i ( fu_data_i.trans_id ),
.op_a_i ( operand_a ),
.op_b_i ( operand_b ),
.opcode_i ( {rem, div_signed} ), // 00: udiv, 10: urem, 01: div, 11: rem
.in_vld_i ( div_valid_op ),
.in_rdy_o ( mult_ready_o ),
.flush_i ( flush_i ),
.out_vld_o ( div_valid ),
.out_rdy_i ( div_ready_i ),
.id_o ( div_trans_id ),
.res_o ( result )
.clk_i (clk_i),
.rst_ni (rst_ni),
.id_i (fu_data_i.trans_id),
.op_a_i (operand_a),
.op_b_i (operand_b),
.opcode_i ({rem, div_signed}), // 00: udiv, 10: urem, 01: div, 11: rem
.in_vld_i (div_valid_op),
.in_rdy_o (mult_ready_o),
.flush_i (flush_i),
.out_vld_o(div_valid),
.out_rdy_i(div_ready_i),
.id_o (div_trans_id),
.res_o (result)
);
// Result multiplexer
@ -136,7 +140,7 @@ module mult import ariane_pkg::*; #(
// Registers
// ---------------------
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
if (~rst_ni) begin
word_op_q <= '0;
end else begin
word_op_q <= word_op_d;

View file

@ -15,7 +15,9 @@
//
module multiplier import ariane_pkg::*; #(
module multiplier
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i,
@ -31,7 +33,8 @@ module multiplier import ariane_pkg::*; #(
output logic [TRANS_ID_BITS-1:0] mult_trans_id_o
);
// Carry-less multiplication
logic [riscv::XLEN-1:0] clmul_q, clmul_d, clmulr_q, clmulr_d, operand_a, operand_b, operand_a_rev, operand_b_rev;
logic [riscv::XLEN-1:0]
clmul_q, clmul_d, clmulr_q, clmulr_d, operand_a, operand_b, operand_a_rev, operand_b_rev;
logic clmul_rmode, clmul_hmode;
if (ariane_pkg::BITMANIP) begin : gen_bitmanip
@ -41,8 +44,8 @@ module multiplier import ariane_pkg::*; #(
// operand_a and b reverse generator
for (genvar i = 0; i < riscv::XLEN; i++) begin
assign operand_a_rev[i] = operand_a_i[(riscv::XLEN-1) -i];
assign operand_b_rev[i] = operand_b_i[(riscv::XLEN-1) -i];
assign operand_a_rev[i] = operand_a_i[(riscv::XLEN-1)-i];
assign operand_b_rev[i] = operand_b_i[(riscv::XLEN-1)-i];
end
// operand_a and operand_b selection
@ -101,8 +104,11 @@ module multiplier import ariane_pkg::*; #(
// single stage version
assign mult_result_d = $signed({operand_a_i[riscv::XLEN-1] & sign_a, operand_a_i}) *
$signed({operand_b_i[riscv::XLEN-1] & sign_b, operand_b_i});
assign mult_result_d = $signed(
{operand_a_i[riscv::XLEN-1] & sign_a, operand_a_i}
) * $signed(
{operand_b_i[riscv::XLEN-1] & sign_b, operand_b_i}
);
assign operator_d = operation_i;
@ -115,7 +121,7 @@ module multiplier import ariane_pkg::*; #(
CLMULH: result_o = clmulr_q >> 1;
CLMULR: result_o = clmulr_q;
// MUL performs an XLEN-bit×XLEN-bit multiplication and places the lower XLEN bits in the destination register
default: result_o = mult_result_q[riscv::XLEN-1:0];// including MUL
default: result_o = mult_result_q[riscv::XLEN-1:0]; // including MUL
endcase
end
if (ariane_pkg::BITMANIP) begin

View file

@ -13,7 +13,9 @@
// Description: Performance counters
module perf_counters import ariane_pkg::*; #(
module perf_counters
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter int unsigned NumPorts = 3 // number of miss ports
) (
@ -45,18 +47,18 @@ module perf_counters import ariane_pkg::*; #(
// for newly added events
input exception_t branch_exceptions_i, //Branch exceptions->execute unit-> branch_exception_o
input icache_dreq_t l1_icache_access_i,
input dcache_req_i_t[2:0] l1_dcache_access_i,
input dcache_req_i_t [2:0] l1_dcache_access_i,
input logic [NumPorts-1:0][DCACHE_SET_ASSOC-1:0]miss_vld_bits_i, //For Cache eviction (3ports-LOAD,STORE,PTW)
input logic i_tlb_flush_i,
input logic stall_issue_i, //stall-read operands
input logic[31:0] mcountinhibit_i
input logic [31:0] mcountinhibit_i
);
logic [63:0] generic_counter_d[6:1];
logic [63:0] generic_counter_q[6:1];
//internal signal to keep track of exception
logic read_access_exception,update_access_exception;
logic read_access_exception, update_access_exception;
logic events[6:1];
//internal signal for MUX select line input
@ -65,35 +67,56 @@ module perf_counters import ariane_pkg::*; #(
//Multiplexer
always_comb begin : Mux
events[6:1]='{default:0};
events[6:1] = '{default: 0};
for(int unsigned i = 1; i <= 6; i++) begin
case(mhpmevent_q[i])
5'b00000 : events[i] = 0;
5'b00001 : events[i] = l1_icache_miss_i;//L1 I-Cache misses
5'b00010 : events[i] = l1_dcache_miss_i;//L1 D-Cache misses
5'b00011 : events[i] = itlb_miss_i;//ITLB misses
5'b00100 : events[i] = dtlb_miss_i;//DTLB misses
5'b00101 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == LOAD;//Load accesses
5'b00110 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == STORE;//Store accesses
5'b00111 : events[i] = ex_i.valid;//Exceptions
5'b01000 : events[i] = eret_i;//Exception handler returns
5'b01001 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == CTRL_FLOW;//Branch instructions
5'b01010 : events[i] = resolved_branch_i.valid && resolved_branch_i.is_mispredict;//Branch mispredicts
5'b01011 : events[i] = branch_exceptions_i.valid;//Branch exceptions
for (int unsigned i = 1; i <= 6; i++) begin
case (mhpmevent_q[i])
5'b00000: events[i] = 0;
5'b00001: events[i] = l1_icache_miss_i; //L1 I-Cache misses
5'b00010: events[i] = l1_dcache_miss_i; //L1 D-Cache misses
5'b00011: events[i] = itlb_miss_i; //ITLB misses
5'b00100: events[i] = dtlb_miss_i; //DTLB misses
5'b00101:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == LOAD; //Load accesses
5'b00110:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == STORE; //Store accesses
5'b00111: events[i] = ex_i.valid; //Exceptions
5'b01000: events[i] = eret_i; //Exception handler returns
5'b01001:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == CTRL_FLOW; //Branch instructions
5'b01010:
events[i] = resolved_branch_i.valid && resolved_branch_i.is_mispredict;//Branch mispredicts
5'b01011: events[i] = branch_exceptions_i.valid; //Branch exceptions
// The standard software calling convention uses register x1 to hold the return address on a call
// the unconditional jump is decoded as ADD op
5'b01100 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == CTRL_FLOW && (commit_instr_i[j].op == ADD || commit_instr_i[j].op == JALR) && (commit_instr_i[j].rd == 'd1 || commit_instr_i[j].rd == 'd5);//Call
5'b01101 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].op == JALR && commit_instr_i[j].rd == 'd0;//Return
5'b01110 : events[i] = sb_full_i;//MSB Full
5'b01111 : events[i] = if_empty_i;//Instruction fetch Empty
5'b10000 : events[i] = l1_icache_access_i.req;//L1 I-Cache accesses
5'b10001 : events[i] = l1_dcache_access_i[0].data_req || l1_dcache_access_i[1].data_req || l1_dcache_access_i[2].data_req;//L1 D-Cache accesses
5'b10010 : events[i] = (l1_dcache_miss_i && miss_vld_bits_i[0] == 8'hFF) || (l1_dcache_miss_i && miss_vld_bits_i[1] == 8'hFF) || (l1_dcache_miss_i && miss_vld_bits_i[2] == 8'hFF);//eviction
5'b10011 : events[i] = i_tlb_flush_i;//I-TLB flush
5'b10100 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == ALU || commit_instr_i[j].fu == MULT;//Integer instructions
5'b10101 : for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) if (commit_ack_i[j]) events[i] = commit_instr_i[j].fu == FPU || commit_instr_i[j].fu == FPU_VEC;//Floating Point Instructions
5'b10110 : events[i] = stall_issue_i;//Pipeline bubbles
5'b01100:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j])
events[i] = commit_instr_i[j].fu == CTRL_FLOW && (commit_instr_i[j].op == ADD || commit_instr_i[j].op == JALR) && (commit_instr_i[j].rd == 'd1 || commit_instr_i[j].rd == 'd5);//Call
5'b01101:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j])
events[i] = commit_instr_i[j].op == JALR && commit_instr_i[j].rd == 'd0; //Return
5'b01110: events[i] = sb_full_i; //MSB Full
5'b01111: events[i] = if_empty_i; //Instruction fetch Empty
5'b10000: events[i] = l1_icache_access_i.req; //L1 I-Cache accesses
5'b10001:
events[i] = l1_dcache_access_i[0].data_req || l1_dcache_access_i[1].data_req || l1_dcache_access_i[2].data_req;//L1 D-Cache accesses
5'b10010:
events[i] = (l1_dcache_miss_i && miss_vld_bits_i[0] == 8'hFF) || (l1_dcache_miss_i && miss_vld_bits_i[1] == 8'hFF) || (l1_dcache_miss_i && miss_vld_bits_i[2] == 8'hFF);//eviction
5'b10011: events[i] = i_tlb_flush_i; //I-TLB flush
5'b10100:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j])
events[i] = commit_instr_i[j].fu == ALU || commit_instr_i[j].fu == MULT;//Integer instructions
5'b10101:
for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++)
if (commit_ack_i[j])
events[i] = commit_instr_i[j].fu == FPU || commit_instr_i[j].fu == FPU_VEC;//Floating Point Instructions
5'b10110: events[i] = stall_issue_i; //Pipeline bubbles
default: events[i] = 0;
endcase
end
@ -107,12 +130,13 @@ module perf_counters import ariane_pkg::*; #(
read_access_exception = 1'b0;
update_access_exception = 1'b0;
for(int unsigned i = 1; i <= 6; i++) begin
for (int unsigned i = 1; i <= 6; i++) begin
if ((!debug_mode_i) && (!we_i)) begin
if ((events[i]) == 1 && (!mcountinhibit_i[i+2]))begin
generic_counter_d[i] = generic_counter_q[i] + 1'b1;end
else begin
generic_counter_d[i] = 'b0;end
if ((events[i]) == 1 && (!mcountinhibit_i[i+2])) begin
generic_counter_d[i] = generic_counter_q[i] + 1'b1;
end else begin
generic_counter_d[i] = 'b0;
end
end
end
@ -123,53 +147,70 @@ module perf_counters import ariane_pkg::*; #(
riscv::CSR_MHPM_COUNTER_5,
riscv::CSR_MHPM_COUNTER_6,
riscv::CSR_MHPM_COUNTER_7,
riscv::CSR_MHPM_COUNTER_8 :begin if (riscv::XLEN == 32) data_o = generic_counter_q[addr_i-riscv::CSR_MHPM_COUNTER_3 + 1][31:0]; else data_o = generic_counter_q[addr_i-riscv::CSR_MHPM_COUNTER_3 + 1];end
riscv::CSR_MHPM_COUNTER_8 :begin
if (riscv::XLEN == 32) data_o = generic_counter_q[addr_i-riscv::CSR_MHPM_COUNTER_3+1][31:0];
else data_o = generic_counter_q[addr_i-riscv::CSR_MHPM_COUNTER_3+1];
end
riscv::CSR_MHPM_COUNTER_3H,
riscv::CSR_MHPM_COUNTER_4H,
riscv::CSR_MHPM_COUNTER_5H,
riscv::CSR_MHPM_COUNTER_6H,
riscv::CSR_MHPM_COUNTER_7H,
riscv::CSR_MHPM_COUNTER_8H :begin if (riscv::XLEN == 32) data_o = generic_counter_q[addr_i-riscv::CSR_MHPM_COUNTER_3H + 1][63:32]; else read_access_exception = 1'b1;end
riscv::CSR_MHPM_COUNTER_8H :begin
if (riscv::XLEN == 32)
data_o = generic_counter_q[addr_i-riscv::CSR_MHPM_COUNTER_3H+1][63:32];
else read_access_exception = 1'b1;
end
riscv::CSR_MHPM_EVENT_3,
riscv::CSR_MHPM_EVENT_4,
riscv::CSR_MHPM_EVENT_5,
riscv::CSR_MHPM_EVENT_6,
riscv::CSR_MHPM_EVENT_7,
riscv::CSR_MHPM_EVENT_8 : data_o = mhpmevent_q[addr_i-riscv::CSR_MHPM_EVENT_3 + 1] ;
riscv::CSR_MHPM_EVENT_8 :
data_o = mhpmevent_q[addr_i-riscv::CSR_MHPM_EVENT_3+1];
default: data_o = 'b0;
endcase
//Write
if(we_i) begin
unique case(addr_i)
if (we_i) begin
unique case (addr_i)
riscv::CSR_MHPM_COUNTER_3,
riscv::CSR_MHPM_COUNTER_4,
riscv::CSR_MHPM_COUNTER_5,
riscv::CSR_MHPM_COUNTER_6,
riscv::CSR_MHPM_COUNTER_7,
riscv::CSR_MHPM_COUNTER_8 :begin if (riscv::XLEN == 32) generic_counter_d[addr_i-riscv::CSR_MHPM_COUNTER_3 + 1][31:0] = data_i; else generic_counter_d[addr_i-riscv::CSR_MHPM_COUNTER_3 + 1] = data_i; end
riscv::CSR_MHPM_COUNTER_8 :begin
if (riscv::XLEN == 32)
generic_counter_d[addr_i-riscv::CSR_MHPM_COUNTER_3+1][31:0] = data_i;
else generic_counter_d[addr_i-riscv::CSR_MHPM_COUNTER_3+1] = data_i;
end
riscv::CSR_MHPM_COUNTER_3H,
riscv::CSR_MHPM_COUNTER_4H,
riscv::CSR_MHPM_COUNTER_5H,
riscv::CSR_MHPM_COUNTER_6H,
riscv::CSR_MHPM_COUNTER_7H,
riscv::CSR_MHPM_COUNTER_8H :begin if (riscv::XLEN == 32) generic_counter_d[addr_i-riscv::CSR_MHPM_COUNTER_3H + 1][63:32] = data_i; else update_access_exception = 1'b1;end
riscv::CSR_MHPM_COUNTER_8H :begin
if (riscv::XLEN == 32)
generic_counter_d[addr_i-riscv::CSR_MHPM_COUNTER_3H+1][63:32] = data_i;
else update_access_exception = 1'b1;
end
riscv::CSR_MHPM_EVENT_3,
riscv::CSR_MHPM_EVENT_4,
riscv::CSR_MHPM_EVENT_5,
riscv::CSR_MHPM_EVENT_6,
riscv::CSR_MHPM_EVENT_7,
riscv::CSR_MHPM_EVENT_8 : mhpmevent_d[addr_i-riscv::CSR_MHPM_EVENT_3 + 1] = data_i;
riscv::CSR_MHPM_EVENT_8 :
mhpmevent_d[addr_i-riscv::CSR_MHPM_EVENT_3+1] = data_i;
default: update_access_exception = 1'b1;
endcase
end
end
//Registers
//Registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
generic_counter_q <= '{default:0};
mhpmevent_q <= '{default:0};
generic_counter_q <= '{default: 0};
mhpmevent_q <= '{default: 0};
end else begin
generic_counter_q <= generic_counter_d;
mhpmevent_q <= mhpmevent_d;

View file

@ -16,7 +16,7 @@ package riscv;
// --------------------
// Privilege Spec
// --------------------
typedef enum logic[1:0] {
typedef enum logic [1:0] {
PRIV_LVL_M = 2'b11,
PRIV_LVL_S = 2'b01,
PRIV_LVL_U = 2'b00

View file

@ -38,15 +38,15 @@ module pmp #(
assign conf_addr_prev = (i == 0) ? '0 : conf_addr_i[i-1];
pmp_entry #(
.CVA6Cfg ( CVA6Cfg ),
.PLEN ( PLEN ),
.PMP_LEN ( PMP_LEN )
) i_pmp_entry(
.addr_i ( addr_i ),
.conf_addr_i ( conf_addr_i[i] ),
.conf_addr_prev_i ( conf_addr_prev ),
.conf_addr_mode_i ( conf_i[i].addr_mode ),
.match_o ( match[i] )
.CVA6Cfg(CVA6Cfg),
.PLEN (PLEN),
.PMP_LEN(PMP_LEN)
) i_pmp_entry (
.addr_i (addr_i),
.conf_addr_i (conf_addr_i[i]),
.conf_addr_prev_i(conf_addr_prev),
.conf_addr_mode_i(conf_i[i].addr_mode),
.match_o (match[i])
);
end
@ -69,7 +69,8 @@ module pmp #(
// allow all accesses from M-mode for no pmp match
if (priv_lvl_i == riscv::PRIV_LVL_M) allow_o = 1'b1;
// disallow accesses for all other modes
else allow_o = 1'b0;
else
allow_o = 1'b0;
end
end
end else assign allow_o = 1'b1;
@ -78,14 +79,14 @@ module pmp #(
always_comb begin
logic no_locked;
no_locked = 1'b0;
if(priv_lvl_i == riscv::PRIV_LVL_M) begin
if (priv_lvl_i == riscv::PRIV_LVL_M) begin
no_locked = 1'b1;
for (int i = 0; i < NR_ENTRIES; i++) begin
if (conf_i[i].locked && conf_i[i].addr_mode != riscv::OFF) begin
no_locked &= 1'b0;
end else no_locked &= 1'b1;
end
if (no_locked == 1'b1) assert(allow_o == 1'b1);
if (no_locked == 1'b1) assert (allow_o == 1'b1);
end
end
// synthesis translate_on

View file

@ -34,10 +34,13 @@ module pmp_entry #(
logic [PLEN-1:0] mask;
int unsigned size;
assign conf_addr_n = {2'b11, ~conf_addr_i};
lzc #(.WIDTH(PLEN), .MODE(1'b0)) i_lzc(
.in_i ( conf_addr_n ),
.cnt_o ( trail_ones ),
.empty_o ( )
lzc #(
.WIDTH(PLEN),
.MODE (1'b0)
) i_lzc (
.in_i (conf_addr_n),
.cnt_o (trail_ones),
.empty_o()
);
always_comb begin
@ -54,9 +57,9 @@ module pmp_entry #(
// synthesis translate_off
if (match_o == 0) begin
assert(addr_i >= ({2'b0, conf_addr_i} << 2) || addr_i < ({2'b0, conf_addr_prev_i} << 2));
assert (addr_i >= ({2'b0, conf_addr_i} << 2) || addr_i < ({2'b0, conf_addr_prev_i} << 2));
end else begin
assert(addr_i < ({2'b0, conf_addr_i} << 2) && addr_i >= ({2'b0, conf_addr_prev_i} << 2));
assert (addr_i < ({2'b0, conf_addr_i} << 2) && addr_i >= ({2'b0, conf_addr_prev_i} << 2));
end
// synthesis translate_on
@ -66,7 +69,7 @@ module pmp_entry #(
if (conf_addr_mode_i == riscv::NA4) size = 2;
else begin
// use the extracted trailing ones
size = {{(32-$clog2(PLEN)){1'b0}}, trail_ones} + 3;
size = {{(32 - $clog2(PLEN)) {1'b0}}, trail_ones} + 3;
end
mask = '1 << size;
@ -75,29 +78,29 @@ module pmp_entry #(
// synthesis translate_off
// size extract checks
assert(size >= 2);
assert (size >= 2);
if (conf_addr_mode_i == riscv::NAPOT) begin
assert(size > 2);
if (size < PMP_LEN) assert(conf_addr_i[size - 3] == 0);
assert (size > 2);
if (size < PMP_LEN) assert (conf_addr_i[size-3] == 0);
for (int i = 0; i < PMP_LEN; i++) begin
if (size > 3 && i <= size - 4) begin
assert(conf_addr_i[i] == 1); // check that all the rest are ones
assert (conf_addr_i[i] == 1); // check that all the rest are ones
end
end
end
if (size < PLEN-1) begin
if (base + 2**size > base) begin // check for overflow
if (size < PLEN - 1) begin
if (base + 2 ** size > base) begin // check for overflow
if (match_o == 0) begin
assert(addr_i >= base + 2**size || addr_i < base);
assert (addr_i >= base + 2 ** size || addr_i < base);
end else begin
assert(addr_i < base + 2**size && addr_i >= base);
assert (addr_i < base + 2 ** size && addr_i >= base);
end
end else begin
if (match_o == 0) begin
assert(addr_i - 2**size >= base || addr_i < base);
assert (addr_i - 2 ** size >= base || addr_i < base);
end else begin
assert(addr_i - 2**size < base && addr_i >= base);
assert (addr_i - 2 ** size < base && addr_i >= base);
end
end
end

View file

@ -15,8 +15,7 @@
import tb_pkg::*;
module pmp_tb;
timeunit 1ns;
timeprecision 1ps;
timeunit 1ns; timeprecision 1ps;
localparam int unsigned WIDTH = 16;
localparam int unsigned PMP_LEN = 13;
@ -33,20 +32,20 @@ module pmp_tb;
logic allow;
// helper signals
logic[WIDTH-1:0] base;
logic [WIDTH-1:0] base;
int unsigned size;
pmp #(
.PLEN(WIDTH),
.PMP_LEN(PMP_LEN),
.NR_ENTRIES(NR_ENTRIES)
) i_pmp(
.addr_i ( addr ),
.access_type_i ( access_type ),
.priv_lvl_i ( riscv::PRIV_LVL_U ),
.conf_addr_i ( conf_addr ),
.conf_i ( conf ),
.allow_o ( allow )
) i_pmp (
.addr_i (addr),
.access_type_i(access_type),
.priv_lvl_i (riscv::PRIV_LVL_U),
.conf_addr_i (conf_addr),
.conf_i (conf),
.allow_o (allow)
);
@ -63,36 +62,51 @@ module pmp_tb;
// pmp 3
base = 16'b00011001_00000000;
size = 8;
conf_addr[2] = P#(.WIDTH(WIDTH), .PMP_LEN(PMP_LEN))::base_to_conf(base, size);
conf_addr[2] = P#(
.WIDTH (WIDTH),
.PMP_LEN(PMP_LEN)
)::base_to_conf(
base, size
);
conf[2].addr_mode = riscv::NAPOT;
conf[2].access_type = riscv::ACCESS_READ | riscv::ACCESS_WRITE | riscv::ACCESS_EXEC;
#5ns;
assert(allow == 1);
assert (allow == 1);
// add second PMP entry that disallows
// pmp 1
base = 16'b00011001_10110000;
size = 4;
conf_addr[1] = P#(.WIDTH(WIDTH), .PMP_LEN(PMP_LEN))::base_to_conf(base, size);
conf_addr[1] = P#(
.WIDTH (WIDTH),
.PMP_LEN(PMP_LEN)
)::base_to_conf(
base, size
);
conf[1].addr_mode = riscv::NAPOT;
conf[1].access_type = '0;
#5ns;
assert(allow == 0);
assert (allow == 0);
// add third PMP entry that allows again
// pmp 2
base = 16'b00011001_10111000;
size = 3;
conf_addr[0] = P#(.WIDTH(WIDTH), .PMP_LEN(PMP_LEN))::base_to_conf(base, size);
conf_addr[0] = P#(
.WIDTH (WIDTH),
.PMP_LEN(PMP_LEN)
)::base_to_conf(
base, size
);
conf[0].addr_mode = riscv::NAPOT;
conf[0].access_type = riscv::ACCESS_READ;
#5ns;
assert(allow == 1);
assert (allow == 1);
end
endmodule

View file

@ -14,15 +14,18 @@
package tb_pkg;
class P #(parameter WIDTH=32, parameter PMP_LEN=32);
static function logic[PMP_LEN-1:0] base_to_conf(logic[WIDTH-1:0] base, int unsigned size_i);
logic[PMP_LEN-1:0] pmp_reg;
class P #(
parameter WIDTH = 32,
parameter PMP_LEN = 32
);
static function logic [PMP_LEN-1:0] base_to_conf(logic [WIDTH-1:0] base, int unsigned size_i);
logic [PMP_LEN-1:0] pmp_reg;
pmp_reg = '0;
for (int i = 0; i < WIDTH-2 && i < PMP_LEN; i++) begin
if (i+3 > size_i) begin
for (int i = 0; i < WIDTH - 2 && i < PMP_LEN; i++) begin
if (i + 3 > size_i) begin
pmp_reg[i] = base[i+2];
end else if (i+3 == size_i) begin
end else if (i + 3 == size_i) begin
pmp_reg[i] = 1'b0;
end else begin
pmp_reg[i] = 1'b1;

View file

@ -65,9 +65,9 @@ module scoreboard #(
input logic x_we_i, // cvxif we for writeback
// RVFI
input [riscv::VLEN-1:0] lsu_addr_i,
input [(riscv::XLEN/8)-1:0] lsu_rmask_i,
input [(riscv::XLEN/8)-1:0] lsu_wmask_i,
input [ riscv::VLEN-1:0] lsu_addr_i,
input [ (riscv::XLEN/8)-1:0] lsu_rmask_i,
input [ (riscv::XLEN/8)-1:0] lsu_wmask_i,
input [ariane_pkg::TRANS_ID_BITS-1:0] lsu_addr_trans_id_i,
input riscv::xlen_t rs1_forwarding_i,
input riscv::xlen_t rs2_forwarding_i
@ -138,8 +138,11 @@ module scoreboard #(
// the decoded instruction we put in there is valid (1st bit)
// increase the issue counter and advance issue pointer
issue_en = 1'b1;
mem_n[issue_pointer_q] = {1'b1, // valid bit
(CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(decoded_instr_i.op)), // whether rd goes to the fpr
mem_n[issue_pointer_q] = {
1'b1, // valid bit
(CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(
decoded_instr_i.op
)), // whether rd goes to the fpr
decoded_instr // decoded instruction record
};
end
@ -149,8 +152,7 @@ module scoreboard #(
// ------------
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
// The FU is NONE -> this instruction is valid immediately
if (mem_q[i].sbe.fu == ariane_pkg::NONE && mem_q[i].issued)
mem_n[i].sbe.valid = 1'b1;
if (mem_q[i].sbe.fu == ariane_pkg::NONE && mem_q[i].issued) mem_n[i].sbe.valid = 1'b1;
end
// ------------
@ -179,8 +181,7 @@ module scoreboard #(
mem_n[trans_id_i[i]].sbe.rd = 5'b0;
end
// write the exception back if it is valid
if (ex_i[i].valid)
mem_n[trans_id_i[i]].sbe.ex = ex_i[i];
if (ex_i[i].valid) mem_n[trans_id_i[i]].sbe.ex = ex_i[i];
// write the fflags back from the FPU (exception valid is never set), leave tval intact
else if(CVA6Cfg.FpPresent && mem_q[trans_id_i[i]].sbe.fu inside {ariane_pkg::FPU, ariane_pkg::FPU_VEC}) begin
mem_n[trans_id_i[i]].sbe.ex.cause = ex_i[i].cause;
@ -221,7 +222,7 @@ module scoreboard #(
assign issue_pointer_n = (flush_i) ? '0 : issue_pointer_q + issue_en;
// precompute offsets for commit slots
for (genvar k=1; k < CVA6Cfg.NrCommitPorts; k++) begin : gen_cnt_incr
for (genvar k = 1; k < CVA6Cfg.NrCommitPorts; k++) begin : gen_cnt_incr
assign commit_pointer_n[k] = (flush_i) ? '0 : commit_pointer_n[0] + unsigned'(k);
end
@ -231,7 +232,7 @@ module scoreboard #(
// rd_clobber output: output currently clobbered destination registers
logic [2**ariane_pkg::REG_ADDR_SIZE-1:0][NR_ENTRIES:0] gpr_clobber_vld;
logic [2**ariane_pkg::REG_ADDR_SIZE-1:0][NR_ENTRIES:0] fpr_clobber_vld;
ariane_pkg::fu_t [NR_ENTRIES:0] clobber_fu;
ariane_pkg::fu_t [ NR_ENTRIES:0] clobber_fu;
always_comb begin : clobber_assign
gpr_clobber_vld = '0;
@ -239,7 +240,7 @@ module scoreboard #(
// default (highest entry hast lowest prio in arbiter tree below)
clobber_fu[NR_ENTRIES] = ariane_pkg::NONE;
for (int unsigned i = 0; i < 2**ariane_pkg::REG_ADDR_SIZE; i++) begin
for (int unsigned i = 0; i < 2 ** ariane_pkg::REG_ADDR_SIZE; i++) begin
gpr_clobber_vld[i][NR_ENTRIES] = 1'b1;
fpr_clobber_vld[i][NR_ENTRIES] = 1'b1;
end
@ -255,44 +256,44 @@ module scoreboard #(
gpr_clobber_vld[0] = '0;
end
for (genvar k = 0; k < 2**ariane_pkg::REG_ADDR_SIZE; k++) begin : gen_sel_clobbers
for (genvar k = 0; k < 2 ** ariane_pkg::REG_ADDR_SIZE; k++) begin : gen_sel_clobbers
// get fu that is going to clobber this register (there should be only one)
rr_arb_tree #(
.NumIn(NR_ENTRIES+1),
.NumIn(NR_ENTRIES + 1),
.DataType(ariane_pkg::fu_t),
.ExtPrio(1'b1),
.AxiVldRdy(1'b1)
) i_sel_gpr_clobbers (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.rr_i ( '0 ),
.req_i ( gpr_clobber_vld[k] ),
.gnt_o ( ),
.data_i ( clobber_fu ),
.gnt_i ( 1'b1 ),
.req_o ( ),
.data_o ( rd_clobber_gpr_o[k] ),
.idx_o ( )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(1'b0),
.rr_i ('0),
.req_i (gpr_clobber_vld[k]),
.gnt_o (),
.data_i (clobber_fu),
.gnt_i (1'b1),
.req_o (),
.data_o (rd_clobber_gpr_o[k]),
.idx_o ()
);
if(CVA6Cfg.FpPresent) begin
if (CVA6Cfg.FpPresent) begin
rr_arb_tree #(
.NumIn(NR_ENTRIES+1),
.NumIn(NR_ENTRIES + 1),
.DataType(ariane_pkg::fu_t),
.ExtPrio(1'b1),
.AxiVldRdy(1'b1)
) i_sel_fpr_clobbers (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.rr_i ( '0 ),
.req_i ( fpr_clobber_vld[k] ),
.gnt_o ( ),
.data_i ( clobber_fu ),
.gnt_i ( 1'b1 ),
.req_o ( ),
.data_o ( rd_clobber_fpr_o[k] ),
.idx_o ( )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(1'b0),
.rr_i ('0),
.req_i (fpr_clobber_vld[k]),
.gnt_o (),
.data_i (clobber_fu),
.gnt_i (1'b1),
.req_o (),
.data_o (rd_clobber_fpr_o[k]),
.idx_o ()
);
end
end
@ -307,82 +308,100 @@ module scoreboard #(
// WB ports have higher prio than entries
for (genvar k = 0; unsigned'(k) < CVA6Cfg.NrWbPorts; k++) begin : gen_rs_wb
assign rs1_fwd_req[k] = (mem_q[trans_id_i[k]].sbe.rd == rs1_i) & wt_valid_i[k] & (~ex_i[k].valid) & (mem_q[trans_id_i[k]].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs1_fpr(issue_instr_o.op)));
assign rs2_fwd_req[k] = (mem_q[trans_id_i[k]].sbe.rd == rs2_i) & wt_valid_i[k] & (~ex_i[k].valid) & (mem_q[trans_id_i[k]].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs2_fpr(issue_instr_o.op)));
assign rs3_fwd_req[k] = (mem_q[trans_id_i[k]].sbe.rd == rs3_i) & wt_valid_i[k] & (~ex_i[k].valid) & (mem_q[trans_id_i[k]].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_imm_fpr(issue_instr_o.op)));
assign rs1_fwd_req[k] = (mem_q[trans_id_i[k]].sbe.rd == rs1_i) & wt_valid_i[k] & (~ex_i[k].valid) & (mem_q[trans_id_i[k]].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs1_fpr(
issue_instr_o.op
)));
assign rs2_fwd_req[k] = (mem_q[trans_id_i[k]].sbe.rd == rs2_i) & wt_valid_i[k] & (~ex_i[k].valid) & (mem_q[trans_id_i[k]].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs2_fpr(
issue_instr_o.op
)));
assign rs3_fwd_req[k] = (mem_q[trans_id_i[k]].sbe.rd == rs3_i) & wt_valid_i[k] & (~ex_i[k].valid) & (mem_q[trans_id_i[k]].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_imm_fpr(
issue_instr_o.op
)));
assign rs_data[k] = wbdata_i[k];
end
for (genvar k = 0; unsigned'(k) < NR_ENTRIES; k++) begin : gen_rs_entries
assign rs1_fwd_req[k+CVA6Cfg.NrWbPorts] = (mem_q[k].sbe.rd == rs1_i) & mem_q[k].issued & mem_q[k].sbe.valid & (mem_q[k].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs1_fpr(issue_instr_o.op)));
assign rs2_fwd_req[k+CVA6Cfg.NrWbPorts] = (mem_q[k].sbe.rd == rs2_i) & mem_q[k].issued & mem_q[k].sbe.valid & (mem_q[k].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs2_fpr(issue_instr_o.op)));
assign rs3_fwd_req[k+CVA6Cfg.NrWbPorts] = (mem_q[k].sbe.rd == rs3_i) & mem_q[k].issued & mem_q[k].sbe.valid & (mem_q[k].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_imm_fpr(issue_instr_o.op)));
assign rs1_fwd_req[k+CVA6Cfg.NrWbPorts] = (mem_q[k].sbe.rd == rs1_i) & mem_q[k].issued & mem_q[k].sbe.valid & (mem_q[k].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs1_fpr(
issue_instr_o.op
)));
assign rs2_fwd_req[k+CVA6Cfg.NrWbPorts] = (mem_q[k].sbe.rd == rs2_i) & mem_q[k].issued & mem_q[k].sbe.valid & (mem_q[k].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_rs2_fpr(
issue_instr_o.op
)));
assign rs3_fwd_req[k+CVA6Cfg.NrWbPorts] = (mem_q[k].sbe.rd == rs3_i) & mem_q[k].issued & mem_q[k].sbe.valid & (mem_q[k].is_rd_fpr_flag == (CVA6Cfg.FpPresent && ariane_pkg::is_imm_fpr(
issue_instr_o.op
)));
assign rs_data[k+CVA6Cfg.NrWbPorts] = mem_q[k].sbe.result;
end
// check whether we are accessing GPR[0]
assign rs1_valid_o = rs1_valid & ((|rs1_i) | (CVA6Cfg.FpPresent && ariane_pkg::is_rs1_fpr(issue_instr_o.op)));
assign rs2_valid_o = rs2_valid & ((|rs2_i) | (CVA6Cfg.FpPresent && ariane_pkg::is_rs2_fpr(issue_instr_o.op)));
assign rs3_valid_o = CVA6Cfg.NrRgprPorts == 3 ? rs3_valid & ((|rs3_i) | (CVA6Cfg.FpPresent && ariane_pkg::is_imm_fpr(issue_instr_o.op))) : rs3_valid;
assign rs1_valid_o = rs1_valid & ((|rs1_i) | (CVA6Cfg.FpPresent && ariane_pkg::is_rs1_fpr(
issue_instr_o.op
)));
assign rs2_valid_o = rs2_valid & ((|rs2_i) | (CVA6Cfg.FpPresent && ariane_pkg::is_rs2_fpr(
issue_instr_o.op
)));
assign rs3_valid_o = CVA6Cfg.NrRgprPorts == 3 ? rs3_valid & ((|rs3_i) | (CVA6Cfg.FpPresent && ariane_pkg::is_imm_fpr(
issue_instr_o.op
))) : rs3_valid;
// use fixed prio here
// this implicitly gives higher prio to WB ports
rr_arb_tree #(
.NumIn(NR_ENTRIES+CVA6Cfg.NrWbPorts),
.NumIn(NR_ENTRIES + CVA6Cfg.NrWbPorts),
.DataWidth(riscv::XLEN),
.ExtPrio(1'b1),
.AxiVldRdy(1'b1)
) i_sel_rs1 (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.rr_i ( '0 ),
.req_i ( rs1_fwd_req ),
.gnt_o ( ),
.data_i ( rs_data ),
.gnt_i ( 1'b1 ),
.req_o ( rs1_valid ),
.data_o ( rs1_o ),
.idx_o ( )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(1'b0),
.rr_i ('0),
.req_i (rs1_fwd_req),
.gnt_o (),
.data_i (rs_data),
.gnt_i (1'b1),
.req_o (rs1_valid),
.data_o (rs1_o),
.idx_o ()
);
rr_arb_tree #(
.NumIn(NR_ENTRIES+CVA6Cfg.NrWbPorts),
.NumIn(NR_ENTRIES + CVA6Cfg.NrWbPorts),
.DataWidth(riscv::XLEN),
.ExtPrio(1'b1),
.AxiVldRdy(1'b1)
) i_sel_rs2 (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.rr_i ( '0 ),
.req_i ( rs2_fwd_req ),
.gnt_o ( ),
.data_i ( rs_data ),
.gnt_i ( 1'b1 ),
.req_o ( rs2_valid ),
.data_o ( rs2_o ),
.idx_o ( )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(1'b0),
.rr_i ('0),
.req_i (rs2_fwd_req),
.gnt_o (),
.data_i (rs_data),
.gnt_i (1'b1),
.req_o (rs2_valid),
.data_o (rs2_o),
.idx_o ()
);
riscv::xlen_t rs3;
rr_arb_tree #(
.NumIn(NR_ENTRIES+CVA6Cfg.NrWbPorts),
.NumIn(NR_ENTRIES + CVA6Cfg.NrWbPorts),
.DataWidth(riscv::XLEN),
.ExtPrio(1'b1),
.AxiVldRdy(1'b1)
) i_sel_rs3 (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.rr_i ( '0 ),
.req_i ( rs3_fwd_req ),
.gnt_o ( ),
.data_i ( rs_data ),
.gnt_i ( 1'b1 ),
.req_o ( rs3_valid ),
.data_o ( rs3 ),
.idx_o ( )
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i(1'b0),
.rr_i ('0),
.req_i (rs3_fwd_req),
.gnt_o (),
.data_i (rs_data),
.gnt_i (1'b1),
.req_o (rs3_valid),
.data_o (rs3),
.idx_o ()
);
if (CVA6Cfg.NrRgprPorts == 3) begin : gen_gp_three_port
@ -394,7 +413,7 @@ module scoreboard #(
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin : regs
if(!rst_ni) begin
if (!rst_ni) begin
mem_q <= '{default: sb_mem_t'(0)};
issue_cnt_q <= '0;
commit_pointer_q <= '0;
@ -409,26 +428,25 @@ module scoreboard #(
//pragma translate_off
initial begin
assert (NR_ENTRIES == 2**BITS_ENTRIES) else $fatal(1, "Scoreboard size needs to be a power of two.");
assert (NR_ENTRIES == 2 ** BITS_ENTRIES)
else $fatal(1, "Scoreboard size needs to be a power of two.");
end
// assert that zero is never set
assert property (
@(posedge clk_i) disable iff (!rst_ni) (rd_clobber_gpr_o[0] == ariane_pkg::NONE))
else $fatal (1,"RD 0 should not bet set");
assert property (@(posedge clk_i) disable iff (!rst_ni) (rd_clobber_gpr_o[0] == ariane_pkg::NONE))
else $fatal(1, "RD 0 should not bet set");
// assert that we never acknowledge a commit if the instruction is not valid
assert property (
@(posedge clk_i) disable iff (!rst_ni) commit_ack_i[0] |-> commit_instr_o[0].valid)
else $fatal (1,"Commit acknowledged but instruction is not valid");
else $fatal(1, "Commit acknowledged but instruction is not valid");
assert property (
@(posedge clk_i) disable iff (!rst_ni) commit_ack_i[1] |-> commit_instr_o[1].valid)
else $fatal (1,"Commit acknowledged but instruction is not valid");
else $fatal(1, "Commit acknowledged but instruction is not valid");
// assert that we never give an issue ack signal if the instruction is not valid
assert property (
@(posedge clk_i) disable iff (!rst_ni) issue_ack_i |-> issue_instr_valid_o)
else $fatal (1,"Issue acknowledged but instruction is not valid");
assert property (@(posedge clk_i) disable iff (!rst_ni) issue_ack_i |-> issue_instr_valid_o)
else $fatal(1, "Issue acknowledged but instruction is not valid");
// there should never be more than one instruction writing the same destination register (except x0)
// check that no functional unit is retiring with the same transaction id
@ -436,7 +454,11 @@ module scoreboard #(
for (genvar j = 0; j < CVA6Cfg.NrWbPorts; j++) begin
assert property (
@(posedge clk_i) disable iff (!rst_ni) wt_valid_i[i] && wt_valid_i[j] && (i != j) |-> (trans_id_i[i] != trans_id_i[j]))
else $fatal (1,"Two or more functional units are retiring instructions with the same transaction id!");
else
$fatal(
1,
"Two or more functional units are retiring instructions with the same transaction id!"
);
end
end
//pragma translate_on

View file

@ -15,7 +15,9 @@
// Description: simple 64bit serial divider
module serdiv import ariane_pkg::*; #(
module serdiv
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter WIDTH = 64,
parameter STABLE_HANDSHAKE = 0 // Guarantee a stable in_rdy_o during the input handshake. Keep it at 0 in CVA6
@ -38,11 +40,16 @@ module serdiv import ariane_pkg::*; #(
output logic [WIDTH-1:0] res_o
);
/////////////////////////////////////
// signal declarations
/////////////////////////////////////
/////////////////////////////////////
// signal declarations
/////////////////////////////////////
enum logic [1:0] {IDLE, DIVIDE, FINISH} state_d, state_q;
enum logic [1:0] {
IDLE,
DIVIDE,
FINISH
}
state_d, state_q;
logic [WIDTH-1:0] res_q, res_d;
logic [WIDTH-1:0] op_a_q, op_a_d;
@ -69,17 +76,17 @@ module serdiv import ariane_pkg::*; #(
logic [WIDTH-1:0] lzc_a_input, lzc_b_input, op_b;
logic [$clog2(WIDTH)-1:0] lzc_a_result, lzc_b_result;
logic [$clog2(WIDTH+1)-1:0] shift_a;
logic [$clog2(WIDTH+1):0] div_shift;
logic [ $clog2(WIDTH+1):0] div_shift;
logic a_reg_en, b_reg_en, res_reg_en, ab_comp, pm_sel, load_en;
logic lzc_a_no_one, lzc_b_no_one;
logic div_res_zero_d, div_res_zero_q;
/////////////////////////////////////
// align the input operands
// for faster division
/////////////////////////////////////
/////////////////////////////////////
// align the input operands
// for faster division
/////////////////////////////////////
assign op_a_sign = op_a_i[$high(op_a_i)];
assign op_b_sign = op_b_i[$high(op_b_i)];
@ -90,21 +97,21 @@ module serdiv import ariane_pkg::*; #(
assign lzc_b_input = (opcode_i[0] & op_b_sign) ? ~op_b_i : op_b_i;
lzc #(
.MODE ( 1 ), // count leading zeros
.WIDTH ( WIDTH )
.MODE (1), // count leading zeros
.WIDTH(WIDTH)
) i_lzc_a (
.in_i ( lzc_a_input ),
.cnt_o ( lzc_a_result ),
.empty_o ( lzc_a_no_one )
.in_i (lzc_a_input),
.cnt_o (lzc_a_result),
.empty_o(lzc_a_no_one)
);
lzc #(
.MODE ( 1 ), // count leading zeros
.WIDTH ( WIDTH )
.MODE (1), // count leading zeros
.WIDTH(WIDTH)
) i_lzc_b (
.in_i ( lzc_b_input ),
.cnt_o ( lzc_b_result ),
.empty_o ( lzc_b_no_one )
.in_i (lzc_b_input),
.cnt_o (lzc_b_result),
.empty_o(lzc_b_no_one)
);
assign shift_a = (lzc_a_no_one) ? WIDTH : {1'b0, lzc_a_result};
@ -115,9 +122,9 @@ module serdiv import ariane_pkg::*; #(
// the division is zero if |opB| > |opA| and can be terminated
assign div_res_zero_d = (load_en) ? div_shift[$high(div_shift)] : div_res_zero_q;
/////////////////////////////////////
// Datapath
/////////////////////////////////////
/////////////////////////////////////
// Datapath
/////////////////////////////////////
assign pm_sel = load_en & ~(opcode_i[0] & (op_a_sign ^ op_b_sign));
@ -140,13 +147,12 @@ module serdiv import ariane_pkg::*; #(
assign add_tmp = (load_en) ? 0 : op_a_q;
assign add_out = (pm_sel) ? add_tmp + add_mux : add_tmp - $signed(add_mux);
/////////////////////////////////////
// FSM, counter
/////////////////////////////////////
/////////////////////////////////////
// FSM, counter
/////////////////////////////////////
assign cnt_zero = (cnt_q == 0);
assign cnt_d = (load_en) ? div_shift[$clog2(WIDTH)-1:0] :
(~cnt_zero) ? cnt_q - 1 : cnt_q;
assign cnt_d = (load_en) ? div_shift[$clog2(WIDTH)-1:0] : (~cnt_zero) ? cnt_q - 1 : cnt_q;
always_comb begin : p_fsm
// default
@ -182,7 +188,7 @@ module serdiv import ariane_pkg::*; #(
if (div_res_zero_q | op_b_zero_q | op_b_neg_one_q) begin
out_vld_o = 1'b1;
state_d = FINISH;
if(out_rdy_i) begin
if (out_rdy_i) begin
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
state_d = IDLE;
end
@ -198,7 +204,7 @@ module serdiv import ariane_pkg::*; #(
state_d = IDLE;
end
end
default : state_d = IDLE;
default: state_d = IDLE;
endcase
if (flush_i) begin
@ -211,9 +217,9 @@ module serdiv import ariane_pkg::*; #(
end
end
/////////////////////////////////////
// regs, flags
/////////////////////////////////////
/////////////////////////////////////
// regs, flags
/////////////////////////////////////
// get flags
assign rem_sel_d = (load_en) ? opcode_i[1] : rem_sel_q;
@ -228,8 +234,7 @@ module serdiv import ariane_pkg::*; #(
assign op_a_d = (a_reg_en) ? add_out : op_a_q;
assign op_b_d = (b_reg_en) ? b_mux : op_b_q;
assign res_d = (load_en) ? '0 :
(res_reg_en) ? {res_q[$high(res_q)-1:0], ab_comp} : res_q;
assign res_d = (load_en) ? '0 : (res_reg_en) ? {res_q[$high(res_q)-1:0], ab_comp} : res_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (~rst_ni) begin

View file

@ -14,7 +14,9 @@
// if they are no longer speculative
module store_buffer import ariane_pkg::*; #(
module store_buffer
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -56,8 +58,11 @@ module store_buffer import ariane_pkg::*; #(
logic [(riscv::XLEN/8)-1:0] be;
logic [1:0] data_size;
logic valid; // this entry is valid, we need this for checking if the address offset matches
} speculative_queue_n [DEPTH_SPEC-1:0], speculative_queue_q [DEPTH_SPEC-1:0],
commit_queue_n [DEPTH_COMMIT-1:0], commit_queue_q [DEPTH_COMMIT-1:0];
}
speculative_queue_n[DEPTH_SPEC-1:0],
speculative_queue_q[DEPTH_SPEC-1:0],
commit_queue_n[DEPTH_COMMIT-1:0],
commit_queue_q[DEPTH_COMMIT-1:0];
// keep a status count for both buffers
logic [$clog2(DEPTH_SPEC):0] speculative_status_cnt_n, speculative_status_cnt_q;
@ -110,8 +115,7 @@ module store_buffer import ariane_pkg::*; #(
// when we flush evict the speculative stores
if (flush_i) begin
// reset all valid flags
for (int unsigned i = 0; i < DEPTH_SPEC; i++)
speculative_queue_n[i].valid = 1'b0;
for (int unsigned i = 0; i < DEPTH_SPEC; i++) speculative_queue_n[i].valid = 1'b0;
speculative_write_pointer_n = speculative_read_pointer_q;
// also reset the status count
@ -257,27 +261,28 @@ module store_buffer import ariane_pkg::*; #(
end
end
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// assertions
///////////////////////////////////////////////////////
//pragma translate_off
// assert that commit is never set when we are flushing this would be counter intuitive
// as flush and commit is decided in the same stage
commit_and_flush: assert property (
@(posedge clk_i) rst_ni && flush_i |-> !commit_i)
else $error ("[Commit Queue] You are trying to commit and flush in the same cycle");
commit_and_flush :
assert property (@(posedge clk_i) rst_ni && flush_i |-> !commit_i)
else $error("[Commit Queue] You are trying to commit and flush in the same cycle");
speculative_buffer_overflow: assert property (
@(posedge clk_i) rst_ni && (speculative_status_cnt_q == DEPTH_SPEC) |-> !valid_i)
else $error ("[Speculative Queue] You are trying to push new data although the buffer is not ready");
speculative_buffer_overflow :
assert property (@(posedge clk_i) rst_ni && (speculative_status_cnt_q == DEPTH_SPEC) |-> !valid_i)
else
$error("[Speculative Queue] You are trying to push new data although the buffer is not ready");
speculative_buffer_underflow: assert property (
@(posedge clk_i) rst_ni && (speculative_status_cnt_q == 0) |-> !commit_i)
else $error ("[Speculative Queue] You are committing although there are no stores to commit");
speculative_buffer_underflow :
assert property (@(posedge clk_i) rst_ni && (speculative_status_cnt_q == 0) |-> !commit_i)
else $error("[Speculative Queue] You are committing although there are no stores to commit");
commit_buffer_overflow: assert property (
@(posedge clk_i) rst_ni && (commit_status_cnt_q == DEPTH_COMMIT) |-> !commit_i)
commit_buffer_overflow :
assert property (@(posedge clk_i) rst_ni && (commit_status_cnt_q == DEPTH_COMMIT) |-> !commit_i)
else $error("[Commit Queue] You are trying to commit a store although the buffer is full");
//pragma translate_on
endmodule

View file

@ -13,7 +13,9 @@
// Description: Store Unit, takes care of all store requests and atomic memory operations (AMOs)
module store_unit import ariane_pkg::*; #(
module store_unit
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic clk_i, // Clock
@ -58,7 +60,8 @@ module store_unit import ariane_pkg::*; #(
VALID_STORE,
WAIT_TRANSLATION,
WAIT_STORE_READY
} state_d, state_q;
}
state_d, state_q;
// store buffer control signals
logic st_ready;
@ -112,8 +115,7 @@ module store_unit import ariane_pkg::*; #(
VALID_STORE: begin
valid_o = 1'b1;
// post this store to the store buffer if we are not flushing
if (!flush_i)
st_valid = 1'b1;
if (!flush_i) st_valid = 1'b1;
st_valid_without_flush = 1'b1;
@ -153,7 +155,7 @@ module store_unit import ariane_pkg::*; #(
// but we know that the store queue is not full as we could only have landed here if
// it wasn't full
WAIT_TRANSLATION: begin
if(ariane_pkg::MMU_PRESENT) begin
if (ariane_pkg::MMU_PRESENT) begin
translation_req_o = 1'b1;
if (dtlb_hit_i) begin
@ -175,8 +177,7 @@ module store_unit import ariane_pkg::*; #(
valid_o = 1'b1;
end
if (flush_i)
state_d = IDLE;
if (flush_i) state_d = IDLE;
end
// -----------
@ -186,8 +187,8 @@ module store_unit import ariane_pkg::*; #(
always_comb begin
st_be_n = lsu_ctrl_i.be;
// don't shift the data if we are going to perform an AMO as we still need to operate on this data
st_data_n = instr_is_amo ? lsu_ctrl_i.data[riscv::XLEN-1:0]
: data_align(lsu_ctrl_i.vaddr[2:0], lsu_ctrl_i.data);
st_data_n = instr_is_amo ? lsu_ctrl_i.data[riscv::XLEN-1:0] :
data_align(lsu_ctrl_i.vaddr[2:0], lsu_ctrl_i.data);
st_data_size_n = extract_transfer_size(lsu_ctrl_i.operation);
// save AMO op for next cycle
case (lsu_ctrl_i.operation)
@ -219,7 +220,7 @@ module store_unit import ariane_pkg::*; #(
// Store Queue
// ---------------
store_buffer #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) store_buffer_i (
.clk_i,
.rst_ni,
@ -231,39 +232,39 @@ module store_unit import ariane_pkg::*; #(
.page_offset_matches_o,
.commit_i,
.commit_ready_o,
.ready_o ( store_buffer_ready ),
.valid_i ( store_buffer_valid ),
.ready_o (store_buffer_ready),
.valid_i (store_buffer_valid),
// the flush signal can be critical and we need this valid
// signal to check whether the page_offset matches or not,
// functionaly it doesn't make a difference whether we use
// the correct valid signal or not as we are flushing
// the whole pipeline anyway
.valid_without_flush_i ( st_valid_without_flush ),
.valid_without_flush_i(st_valid_without_flush),
.paddr_i,
.mem_paddr_o ( mem_paddr_o ),
.data_i ( st_data_q ),
.be_i ( st_be_q ),
.data_size_i ( st_data_size_q ),
.req_port_i ( req_port_i ),
.req_port_o ( req_port_o )
.mem_paddr_o (mem_paddr_o),
.data_i (st_data_q),
.be_i (st_be_q),
.data_size_i (st_data_size_q),
.req_port_i (req_port_i),
.req_port_o (req_port_o)
);
amo_buffer #(
.CVA6Cfg ( CVA6Cfg )
.CVA6Cfg(CVA6Cfg)
) i_amo_buffer (
.clk_i,
.rst_ni,
.flush_i,
.valid_i ( amo_buffer_valid ),
.ready_o ( amo_buffer_ready ),
.paddr_i ( paddr_i ),
.amo_op_i ( amo_op_q ),
.data_i ( st_data_q ),
.data_size_i ( st_data_size_q ),
.amo_req_o ( amo_req_o ),
.amo_resp_i ( amo_resp_i ),
.amo_valid_commit_i ( amo_valid_commit_i ),
.no_st_pending_i ( no_st_pending_o )
.valid_i (amo_buffer_valid),
.ready_o (amo_buffer_ready),
.paddr_i (paddr_i),
.amo_op_i (amo_op_q),
.data_i (st_data_q),
.data_size_i (st_data_size_q),
.amo_req_o (amo_req_o),
.amo_resp_i (amo_resp_i),
.amo_valid_commit_i(amo_valid_commit_i),
.no_st_pending_i (no_st_pending_o)
);
// ---------------