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,
@ -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
@ -166,11 +168,9 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
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,8 +179,7 @@ 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;
if (acc_req_o.req_valid) insn_ready_d[acc_req_o.trans_id] = 1'b0;
end : p_non_speculative_ff
/*************************
@ -232,7 +231,12 @@ 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),
@ -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
@ -284,10 +284,8 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
// issued yet
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
@ -360,7 +358,8 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
.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.");
@ -404,7 +403,8 @@ module acc_dispatcher import ariane_pkg::*; import riscv::*; #(
.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
@ -47,8 +49,7 @@ module alu import ariane_pkg::*; #(
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
@ -163,8 +162,7 @@ module alu import ariane_pkg::*; #(
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
@ -232,18 +231,14 @@ module alu import ariane_pkg::*; #(
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]};
// 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};
@ -254,14 +249,25 @@ module alu import ariane_pkg::*; #(
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]}};
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;
@ -275,7 +281,9 @@ module alu import ariane_pkg::*; #(
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};
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
@ -287,20 +295,26 @@ module alu import ariane_pkg::*; #(
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])));
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])));
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;
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

@ -54,8 +54,7 @@ module ariane_regfile_lol #(
// 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,8 +84,7 @@ 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),
@ -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

@ -52,10 +52,8 @@ module ariane_regfile #(
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

@ -110,13 +110,11 @@ 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
initial begin
for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
for (int j = 0; j < NUM_WORDS; j++) begin
mem[i][j] = $random();

View file

@ -72,8 +72,13 @@ module axi_shim #(
///////////////////////////////////////////////////////
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,7 +152,9 @@ 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;
@ -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?
@ -291,10 +300,10 @@ module axi_shim #(
//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");
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

View file

@ -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;
@ -98,9 +99,6 @@ module branch_unit #(
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

@ -49,16 +49,30 @@ module axi_adapter #(
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 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,12 +187,12 @@ 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;
@ -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
@ -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;
@ -428,14 +442,22 @@ module axi_adapter #(
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
@ -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
@ -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
@ -452,12 +451,20 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
//pragma translate_off
`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");
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

@ -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,19 +111,16 @@ 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_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)
ariane_pkg::AMO_LR: amo_op = HPDCACHE_REQ_AMO_LR;
@ -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

@ -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,
@ -460,11 +461,9 @@ module cva6_hpdcache_subsystem
.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
@ -563,24 +562,46 @@ module cva6_hpdcache_subsystem
// 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 (
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

@ -141,7 +141,9 @@ module cva6_hpdcache_subsystem_axi_arbiter
(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;
@ -173,8 +175,7 @@ module cva6_hpdcache_subsystem_axi_arbiter
.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,
@ -235,11 +236,9 @@ module cva6_hpdcache_subsystem_axi_arbiter
.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,8 +255,7 @@ 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 [63:0] icache_miss_word;
@ -334,8 +332,7 @@ module cva6_hpdcache_subsystem_axi_arbiter
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;
@ -447,8 +444,7 @@ module cva6_hpdcache_subsystem_axi_arbiter
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
@ -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
@ -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
@ -55,8 +58,7 @@ 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
);
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
@ -105,7 +109,14 @@ 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;
///////////////////////////////////////////////////////
@ -116,7 +127,9 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
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;
@ -141,8 +154,7 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
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
@ -340,9 +352,7 @@ end else begin : gen_piton_offset
// 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));
@ -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,9 +380,7 @@ 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;
@ -511,20 +520,26 @@ end else begin : gen_piton_offset
//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");
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");
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");
hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) (!inv_en) |-> cache_rden |=> cmp_en_q |-> $onehot0(cl_hit))
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!
@ -550,7 +565,8 @@ 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");

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
@ -62,7 +65,8 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
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).

View file

@ -16,7 +16,10 @@
// 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,
@ -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;
@ -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;
@ -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
@ -508,8 +513,8 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
//pragma translate_off
`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");
assert property (@(posedge clk_i) $onehot0(evict_way_q))
else $warning("Evict-way should be one-hot encoded");
`endif
//pragma translate_on
@ -632,7 +637,9 @@ module miss_handler import ariane_pkg::*; import std_cache_pkg::*; #(
// -----------------
// Replacement LFSR
// -----------------
lfsr_8bit #(.WIDTH (DCACHE_SET_ASSOC)) i_lfsr (
lfsr_8bit #(
.WIDTH(DCACHE_SET_ASSOC)
) i_lfsr (
.en_i (lfsr_enable),
.refill_way_oh (lfsr_oh),
.refill_way_bin(lfsr_bin),
@ -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;
@ -743,13 +754,22 @@ module axi_adapter_arbiter #(
`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
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
else begin
$error("address contains X when request is set");
$stop();
end
`endif
//pragma translate_on

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,
@ -268,21 +271,42 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
//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 (
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

View file

@ -13,7 +13,10 @@
// 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,
@ -269,7 +272,8 @@ import std_cache_pkg::*;
//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
endmodule

View file

@ -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,17 +79,18 @@ 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
// 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
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

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,11 +102,9 @@ 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;
@ -166,7 +168,8 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
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;
end
@ -194,10 +197,14 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
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
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
//////////////////////////////////////
@ -211,10 +218,14 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
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
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_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
@ -385,10 +417,14 @@ module wt_axi_adapter import ariane_pkg::*; import wt_cache_pkg::*; #(
// 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;

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,
@ -182,24 +185,46 @@ module wt_cache_subsystem import ariane_pkg::*; import wt_cache_pkg::*; #(
//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 (
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 (
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

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.
@ -322,7 +325,8 @@ module wt_dcache import ariane_pkg::*; import wt_cache_pkg::*; #(
//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");

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;
@ -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;
@ -266,14 +281,17 @@ module wt_dcache_ctrl import ariane_pkg::*; import wt_cache_pkg::*; #(
//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))
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");
else
$fatal(1, "[l1 dcache ctrl] cache index width can be maximum 12bit since VM uses 4kB pages");
end
`endif
//pragma translate_on

View file

@ -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
) (
@ -73,8 +76,7 @@ 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
);
input logic [DCACHE_NUM_BANKS_WIDTH-1:0] in);
logic [DCACHE_NUM_BANKS-1:0] out;
out = '0;
out[in] = 1'b1;
@ -83,7 +85,11 @@ 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;
@ -193,7 +199,8 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
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]);
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
@ -354,30 +361,37 @@ module wt_dcache_mem import ariane_pkg::*; import wt_cache_pkg::*; #(
//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))
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))
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))
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!
@ -403,7 +417,8 @@ 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");

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
@ -72,8 +75,7 @@ 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
);
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,7 +103,15 @@ 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
@ -207,9 +215,7 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
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;
@ -256,7 +262,9 @@ 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];
end
@ -376,16 +384,15 @@ module wt_dcache_missunit import ariane_pkg::*; import wt_cache_pkg::*; #(
assign wr_cl_nc_o = mshr_q.nc;
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] :
@ -592,22 +599,28 @@ end
//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");
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");
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");
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)");
else
$fatal(
1, "[l1 dcache missunit] at least two ports are required (one read port, one write port)"
);
end
`endif
//pragma translate_on

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;
@ -140,10 +144,22 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
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;
@ -186,21 +202,29 @@ 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
@ -480,7 +504,10 @@ 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
@ -540,43 +567,53 @@ module wt_dcache_wbuffer import ariane_pkg::*; import wt_cache_pkg::*; #(
//pragma translate_off
`ifndef VERILATOR
hot1: assert property (
@(posedge clk_i) disable iff (!rst_ni) req_port_i.data_req |-> $onehot0(wbuffer_hit_oh))
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");
tx_valid0: assert property (
@(posedge clk_i) disable iff (!rst_ni) evict |-> tx_stat_q[rtrn_id].vld)
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)
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");
unused0: assert property (
@(posedge clk_i) disable iff (!rst_ni) !req_port_i.tag_valid)
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)
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 (
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

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,
@ -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,8 +133,10 @@ 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;
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
@ -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
@ -348,36 +355,51 @@ l15_rtrn_t rtrn_fifo_data;
//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");
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)");
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)");
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");
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");

View file

@ -13,7 +13,9 @@
// 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,
@ -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
@ -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
// 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,7 +358,15 @@ 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
@ -179,7 +375,18 @@ module compressed_decoder #(
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,

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,8 +62,7 @@ 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

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
@ -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_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_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
@ -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}}};
// 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
@ -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,7 +868,11 @@ 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
@ -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;
@ -1058,7 +1104,10 @@ 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};
@ -1067,7 +1116,10 @@ module csr_regfile import ariane_pkg::*; #(
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;
@ -1211,7 +1263,8 @@ module csr_regfile import ariane_pkg::*; #(
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,7 +1354,8 @@ 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
@ -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

View file

@ -13,7 +13,9 @@
// Description: CVA6 Top-level module
module cva6 import ariane_pkg::*; #(
module cva6
import ariane_pkg::*;
#(
// CVA6 config
parameter config_pkg::cva6_cfg_t CVA6Cfg = cva6_config_pkg::cva6_cfg,
parameter bit IsRVFI = bit'(cva6_config_pkg::CVA6ConfigRvfiTrace),
@ -507,19 +509,57 @@ module cva6 import ariane_pkg::*; #(
logic [NrWbPorts-1:0] wt_valid_ex_id;
if (CVA6ExtendCfg.CvxifEn) begin
assign trans_id_ex_id = {x_trans_id_ex_id, flu_trans_id_ex_id, load_trans_id_ex_id, store_trans_id_ex_id, fpu_trans_id_ex_id};
assign wbdata_ex_id = {x_result_ex_id, flu_result_ex_id, load_result_ex_id, store_result_ex_id, fpu_result_ex_id};
assign ex_ex_ex_id = {x_exception_ex_id, flu_exception_ex_id, load_exception_ex_id, store_exception_ex_id, fpu_exception_ex_id};
assign wt_valid_ex_id = {x_valid_ex_id, flu_valid_ex_id, load_valid_ex_id, store_valid_ex_id, fpu_valid_ex_id};
assign trans_id_ex_id = {
x_trans_id_ex_id,
flu_trans_id_ex_id,
load_trans_id_ex_id,
store_trans_id_ex_id,
fpu_trans_id_ex_id
};
assign wbdata_ex_id = {
x_result_ex_id, flu_result_ex_id, load_result_ex_id, store_result_ex_id, fpu_result_ex_id
};
assign ex_ex_ex_id = {
x_exception_ex_id,
flu_exception_ex_id,
load_exception_ex_id,
store_exception_ex_id,
fpu_exception_ex_id
};
assign wt_valid_ex_id = {
x_valid_ex_id, flu_valid_ex_id, load_valid_ex_id, store_valid_ex_id, fpu_valid_ex_id
};
end else if (CVA6ExtendCfg.EnableAccelerator) begin
assign trans_id_ex_id = {flu_trans_id_ex_id, load_trans_id_ex_id, store_trans_id_ex_id, fpu_trans_id_ex_id, acc_trans_id_ex_id};
assign wbdata_ex_id = {flu_result_ex_id, load_result_ex_id, store_result_ex_id, fpu_result_ex_id, acc_result_ex_id};
assign ex_ex_ex_id = {flu_exception_ex_id, load_exception_ex_id, store_exception_ex_id, fpu_exception_ex_id, acc_exception_ex_id};
assign wt_valid_ex_id = {flu_valid_ex_id, load_valid_ex_id, store_valid_ex_id, fpu_valid_ex_id, acc_valid_ex_id};
assign trans_id_ex_id = {
flu_trans_id_ex_id,
load_trans_id_ex_id,
store_trans_id_ex_id,
fpu_trans_id_ex_id,
acc_trans_id_ex_id
};
assign wbdata_ex_id = {
flu_result_ex_id, load_result_ex_id, store_result_ex_id, fpu_result_ex_id, acc_result_ex_id
};
assign ex_ex_ex_id = {
flu_exception_ex_id,
load_exception_ex_id,
store_exception_ex_id,
fpu_exception_ex_id,
acc_exception_ex_id
};
assign wt_valid_ex_id = {
flu_valid_ex_id, load_valid_ex_id, store_valid_ex_id, fpu_valid_ex_id, acc_valid_ex_id
};
end else begin
assign trans_id_ex_id = {flu_trans_id_ex_id, load_trans_id_ex_id, store_trans_id_ex_id, fpu_trans_id_ex_id};
assign wbdata_ex_id = {flu_result_ex_id, load_result_ex_id, store_result_ex_id, fpu_result_ex_id};
assign ex_ex_ex_id = {flu_exception_ex_id, load_exception_ex_id, store_exception_ex_id, fpu_exception_ex_id};
assign trans_id_ex_id = {
flu_trans_id_ex_id, load_trans_id_ex_id, store_trans_id_ex_id, fpu_trans_id_ex_id
};
assign wbdata_ex_id = {
flu_result_ex_id, load_result_ex_id, store_result_ex_id, fpu_result_ex_id
};
assign ex_ex_ex_id = {
flu_exception_ex_id, load_exception_ex_id, store_exception_ex_id, fpu_exception_ex_id
};
assign wt_valid_ex_id = {flu_valid_ex_id, load_valid_ex_id, store_valid_ex_id, fpu_valid_ex_id};
end
@ -860,7 +900,8 @@ module cva6 import ariane_pkg::*; #(
.stall_issue_i (stall_issue),
.mcountinhibit_i (mcountinhibit_csr_perf)
);
end : gen_perf_counter else begin: gen_no_perf_counter
end : gen_perf_counter
else begin : gen_no_perf_counter
assign data_perf_csr = '0;
end : gen_no_perf_counter
@ -1114,7 +1155,8 @@ module cva6 import ariane_pkg::*; #(
.acc_req_o (cvxif_req_o),
.acc_resp_i (cvxif_resp_i)
);
end : gen_accelerator else begin: gen_no_accelerator
end : gen_accelerator
else begin : gen_no_accelerator
assign acc_trans_id_ex_id = '0;
assign acc_result_ex_id = '0;
assign acc_valid_ex_id = '0;
@ -1165,8 +1207,8 @@ module cva6 import ariane_pkg::*; #(
for (genvar i = 0; i < CVA6ExtendCfg.NrCommitPorts; i++) begin : gen_pc_fifo
fifo_v3 #(
.DATA_WIDTH(64),
.DEPTH(PC_QUEUE_DEPTH))
i_pc_fifo (
.DEPTH(PC_QUEUE_DEPTH)
) i_pc_fifo (
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i ('0),
@ -1183,8 +1225,8 @@ module cva6 import ariane_pkg::*; #(
rr_arb_tree #(
.NumIn(CVA6ExtendCfg.NrCommitPorts),
.DataWidth(64))
i_rr_arb_tree (
.DataWidth(64)
) i_rr_arb_tree (
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i('0),
@ -1268,15 +1310,21 @@ module cva6 import ariane_pkg::*; #(
end
for (int i = 0; i < CVA6ExtendCfg.NrCommitPorts; i++) begin
if (commit_ack[i] && !commit_instr_id_commit[i].ex.valid) begin
$fwrite(f, "%d 0x%0h %s (0x%h) DASM(%h)\n", cycles, commit_instr_id_commit[i].pc, mode, commit_instr_id_commit[i].ex.tval[31:0], commit_instr_id_commit[i].ex.tval[31:0]);
$fwrite(f, "%d 0x%0h %s (0x%h) DASM(%h)\n", cycles, commit_instr_id_commit[i].pc, mode,
commit_instr_id_commit[i].ex.tval[31:0], commit_instr_id_commit[i].ex.tval[31:0]);
end else if (commit_ack[i] && commit_instr_id_commit[i].ex.valid) begin
if (commit_instr_id_commit[i].ex.cause == 2) begin
$fwrite(f, "Exception Cause: Illegal Instructions, DASM(%h) PC=%h\n", commit_instr_id_commit[i].ex.tval[31:0], commit_instr_id_commit[i].pc);
$fwrite(f, "Exception Cause: Illegal Instructions, DASM(%h) PC=%h\n",
commit_instr_id_commit[i].ex.tval[31:0], commit_instr_id_commit[i].pc);
end else begin
if (debug_mode) begin
$fwrite(f, "%d 0x%0h %s (0x%h) DASM(%h)\n", cycles, commit_instr_id_commit[i].pc, mode, commit_instr_id_commit[i].ex.tval[31:0], commit_instr_id_commit[i].ex.tval[31:0]);
$fwrite(f, "%d 0x%0h %s (0x%h) DASM(%h)\n", cycles, commit_instr_id_commit[i].pc,
mode, commit_instr_id_commit[i].ex.tval[31:0],
commit_instr_id_commit[i].ex.tval[31:0]);
end else begin
$fwrite(f, "Exception Cause: %5d, DASM(%h) PC=%h\n", commit_instr_id_commit[i].ex.cause, commit_instr_id_commit[i].ex.tval[31:0], commit_instr_id_commit[i].pc);
$fwrite(f, "Exception Cause: %5d, DASM(%h) PC=%h\n",
commit_instr_id_commit[i].ex.cause, commit_instr_id_commit[i].ex.tval[31:0],
commit_instr_id_commit[i].pc);
end
end
end
@ -1321,7 +1369,9 @@ module cva6 import ariane_pkg::*; #(
rvfi_o[i].rs1_addr = commit_instr_id_commit[i].rs1[4:0];
rvfi_o[i].rs2_addr = commit_instr_id_commit[i].rs2[4:0];
rvfi_o[i].rd_addr = commit_instr_id_commit[i].rd[4:0];
rvfi_o[i].rd_wdata = (CVA6ExtendCfg.FpPresent && ariane_pkg::is_rd_fpr(commit_instr_id_commit[i].op)) ? commit_instr_id_commit[i].result : wdata_commit_id[i];
rvfi_o[i].rd_wdata =
(CVA6ExtendCfg.FpPresent && ariane_pkg::is_rd_fpr(commit_instr_id_commit[i].op)) ?
commit_instr_id_commit[i].result : wdata_commit_id[i];
rvfi_o[i].pc_rdata = commit_instr_id_commit[i].pc;
rvfi_o[i].mem_addr = commit_instr_id_commit[i].lsu_addr;

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,

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,

View file

@ -19,7 +19,9 @@
// This also includes all the forwarding logic
//
module decoder import ariane_pkg::*; #(
module decoder
import ariane_pkg::*;
#(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
) (
input logic debug_req_i, // external debug request
@ -59,9 +61,7 @@ module decoder import ariane_pkg::*; #(
// --------------------
// Immediate select
// --------------------
enum logic[3:0] {
NOIMM, IIMM, SIMM, SBIMM, UIMM, JIMM, RS3
} imm_select;
enum logic [3:0] {NOIMM, IIMM, SIMM, SBIMM, UIMM, JIMM, RS3} imm_select;
riscv::xlen_t imm_i_type;
riscv::xlen_t imm_s_type;
@ -91,7 +91,8 @@ module decoder import ariane_pkg::*; #(
.illegal_instr_o(acc_illegal_instr),
.is_control_flow_instr_o(acc_is_control_flow_instr)
);
end: gen_accel_decoder else begin
end : gen_accel_decoder
else begin
assign is_accel = 1'b0;
assign acc_instruction = '0;
assign acc_illegal_instr = 1'b1; // this should never propagate
@ -133,8 +134,7 @@ module decoder import ariane_pkg::*; #(
unique case (instr.itype.funct3)
3'b000: begin
// check if the RD and and RS1 fields are zero, this may be reset for the SENCE.VMA instruction
if (instr.itype.rs1 != '0 || instr.itype.rd != '0)
illegal_instr = 1'b1;
if (instr.itype.rs1 != '0 || instr.itype.rd != '0) illegal_instr = 1'b1;
// decode the immiediate field
case (instr.itype.imm)
// ECALL -> inject exception
@ -195,8 +195,7 @@ module decoder import ariane_pkg::*; #(
illegal_instr = ((priv_lvl_i inside {riscv::PRIV_LVL_M, riscv::PRIV_LVL_S}) && instr.itype.rd == '0) ? 1'b0 : 1'b1;
instruction_o.op = ariane_pkg::SFENCE_VMA;
// check TVM flag and intercept SFENCE.VMA call if necessary
if (priv_lvl_i == riscv::PRIV_LVL_S && tvm_i)
illegal_instr = 1'b1;
if (priv_lvl_i == riscv::PRIV_LVL_S && tvm_i) illegal_instr = 1'b1;
end else begin
illegal_instr = 1'b1;
end
@ -212,19 +211,15 @@ module decoder import ariane_pkg::*; #(
3'b010: begin // CSRRS
imm_select = IIMM;
// this is just a read
if (instr.itype.rs1 == 5'b0)
instruction_o.op = ariane_pkg::CSR_READ;
else
instruction_o.op = ariane_pkg::CSR_SET;
if (instr.itype.rs1 == 5'b0) instruction_o.op = ariane_pkg::CSR_READ;
else instruction_o.op = ariane_pkg::CSR_SET;
end
// atomically clear values in the CSR and write back to rd
3'b011: begin // CSRRC
imm_select = IIMM;
// this is just a read
if (instr.itype.rs1 == 5'b0)
instruction_o.op = ariane_pkg::CSR_READ;
else
instruction_o.op = ariane_pkg::CSR_CLEAR;
if (instr.itype.rs1 == 5'b0) instruction_o.op = ariane_pkg::CSR_READ;
else instruction_o.op = ariane_pkg::CSR_CLEAR;
end
// use zimm and iimm
3'b101: begin // CSRRWI
@ -238,20 +233,16 @@ module decoder import ariane_pkg::*; #(
imm_select = IIMM;
instruction_o.use_zimm = 1'b1;
// this is just a read
if (instr.itype.rs1 == 5'b0)
instruction_o.op = ariane_pkg::CSR_READ;
else
instruction_o.op = ariane_pkg::CSR_SET;
if (instr.itype.rs1 == 5'b0) instruction_o.op = ariane_pkg::CSR_READ;
else instruction_o.op = ariane_pkg::CSR_SET;
end
3'b111: begin // CSRRCI
instruction_o.rs1[4:0] = instr.itype.rs1;
imm_select = IIMM;
instruction_o.use_zimm = 1'b1;
// this is just a read
if (instr.itype.rs1 == 5'b0)
instruction_o.op = ariane_pkg::CSR_READ;
else
instruction_o.op = ariane_pkg::CSR_CLEAR;
if (instr.itype.rs1 == 5'b0) instruction_o.op = ariane_pkg::CSR_READ;
else instruction_o.op = ariane_pkg::CSR_CLEAR;
end
default: illegal_instr = 1'b1;
endcase
@ -306,8 +297,10 @@ module decoder import ariane_pkg::*; #(
instruction_o.rs2[4:0] = instr.rvftype.rs1; // Operand B is set to rs1
imm_select = IIMM; // Operand C is set to rs2
end
5'b00011 : instruction_o.op = ariane_pkg::FMUL; // vfmul.vfmt - Vectorial FP Multiplication
5'b00100 : instruction_o.op = ariane_pkg::FDIV; // vfdiv.vfmt - Vectorial FP Division
5'b00011:
instruction_o.op = ariane_pkg::FMUL; // vfmul.vfmt - Vectorial FP Multiplication
5'b00100:
instruction_o.op = ariane_pkg::FDIV; // vfdiv.vfmt - Vectorial FP Division
5'b00101: begin
instruction_o.op = ariane_pkg::VFMIN; // vfmin.vfmt - Vectorial FP Minimum
check_fprm = 1'b0; // rounding mode irrelevant
@ -335,8 +328,7 @@ module decoder import ariane_pkg::*; #(
instruction_o.rs2[4:0] = instr.rvftype.rs1; // set rs2 = rs1 so we can map FMV to SGNJ in the unit
if (instr.rvftype.repl)
instruction_o.op = ariane_pkg::FMV_X2F; // vfmv.vfmt.x - GPR to FPR Move
else
instruction_o.op = ariane_pkg::FMV_F2X; // vfmv.x.vfmt - FPR to GPR Move
else instruction_o.op = ariane_pkg::FMV_F2X; // vfmv.x.vfmt - FPR to GPR Move
check_fprm = 1'b0; // no rounding for moves
end
5'b00001: begin
@ -344,8 +336,10 @@ module decoder import ariane_pkg::*; #(
check_fprm = 1'b0; // no rounding for classification
allow_replication = 1'b0; // R must not be set
end
5'b00010 : instruction_o.op = ariane_pkg::FCVT_F2I; // vfcvt.x.vfmt - Vectorial FP to Int Conversion
5'b00011 : instruction_o.op = ariane_pkg::FCVT_I2F; // vfcvt.vfmt.x - Vectorial Int to FP Conversion
5'b00010:
instruction_o.op = ariane_pkg::FCVT_F2I; // vfcvt.x.vfmt - Vectorial FP to Int Conversion
5'b00011:
instruction_o.op = ariane_pkg::FCVT_I2F; // vfcvt.vfmt.x - Vectorial Int to FP Conversion
5'b001??: begin
instruction_o.op = ariane_pkg::FCVT_F2F; // vfcvt.vfmt.vfmt - Vectorial FP to FP Conversion
instruction_o.rs2[4:0] = instr.rvftype.rd; // set rs2 = rd as target vector for conversion
@ -403,22 +397,28 @@ module decoder import ariane_pkg::*; #(
5'b11000: begin
instruction_o.op = ariane_pkg::VFCPKAB_S; // vfcpka/b.vfmt.s - Vectorial FP Cast-and-Pack from 2x FP32, lowest 4 entries
imm_select = SIMM; // rd into result field (upper bits don't matter)
if (~CVA6Cfg.RVF) illegal_instr = 1'b1; // if we don't support RVF, we can't cast from FP32
if (~CVA6Cfg.RVF)
illegal_instr = 1'b1; // if we don't support RVF, we can't cast from FP32
// check destination format
unique case (instr.rvftype.vfmt)
// Only process instruction if corresponding extension is active and FLEN suffices (static)
2'b00: begin
if (~CVA6Cfg.RVFVec) illegal_instr = 1'b1; // destination vector not supported
if (instr.rvftype.repl) illegal_instr = 1'b1; // no entries 2/3 in vector of 2 fp32
if (~CVA6Cfg.RVFVec)
illegal_instr = 1'b1; // destination vector not supported
if (instr.rvftype.repl)
illegal_instr = 1'b1; // no entries 2/3 in vector of 2 fp32
end
2'b01: begin
if (~CVA6Cfg.XF16ALTVec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF16ALTVec)
illegal_instr = 1'b1; // destination vector not supported
end
2'b10: begin
if (~CVA6Cfg.XF16Vec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF16Vec)
illegal_instr = 1'b1; // destination vector not supported
end
2'b11: begin
if (~CVA6Cfg.XF8Vec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF8Vec)
illegal_instr = 1'b1; // destination vector not supported
end
default: illegal_instr = 1'b1;
endcase
@ -426,7 +426,8 @@ module decoder import ariane_pkg::*; #(
5'b11001: begin
instruction_o.op = ariane_pkg::VFCPKCD_S; // vfcpkc/d.vfmt.s - Vectorial FP Cast-and-Pack from 2x FP32, second 4 entries
imm_select = SIMM; // rd into result field (upper bits don't matter)
if (~CVA6Cfg.RVF) illegal_instr = 1'b1; // if we don't support RVF, we can't cast from FP32
if (~CVA6Cfg.RVF)
illegal_instr = 1'b1; // if we don't support RVF, we can't cast from FP32
// check destination format
unique case (instr.rvftype.vfmt)
// Only process instruction if corresponding extension is active and FLEN suffices (static)
@ -434,7 +435,8 @@ module decoder import ariane_pkg::*; #(
2'b01: illegal_instr = 1'b1; // no entries 4-7 in vector of 4 FP16ALT
2'b10: illegal_instr = 1'b1; // no entries 4-7 in vector of 4 FP16
2'b11: begin
if (~CVA6Cfg.XF8Vec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF8Vec)
illegal_instr = 1'b1; // destination vector not supported
end
default: illegal_instr = 1'b1;
endcase
@ -442,22 +444,28 @@ module decoder import ariane_pkg::*; #(
5'b11010: begin
instruction_o.op = ariane_pkg::VFCPKAB_D; // vfcpka/b.vfmt.d - Vectorial FP Cast-and-Pack from 2x FP64, lowest 4 entries
imm_select = SIMM; // rd into result field (upper bits don't matter)
if (~CVA6Cfg.RVD) illegal_instr = 1'b1; // if we don't support RVD, we can't cast from FP64
if (~CVA6Cfg.RVD)
illegal_instr = 1'b1; // if we don't support RVD, we can't cast from FP64
// check destination format
unique case (instr.rvftype.vfmt)
// Only process instruction if corresponding extension is active and FLEN suffices (static)
2'b00: begin
if (~CVA6Cfg.RVFVec) illegal_instr = 1'b1; // destination vector not supported
if (instr.rvftype.repl) illegal_instr = 1'b1; // no entries 2/3 in vector of 2 fp32
if (~CVA6Cfg.RVFVec)
illegal_instr = 1'b1; // destination vector not supported
if (instr.rvftype.repl)
illegal_instr = 1'b1; // no entries 2/3 in vector of 2 fp32
end
2'b01: begin
if (~CVA6Cfg.XF16ALTVec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF16ALTVec)
illegal_instr = 1'b1; // destination vector not supported
end
2'b10: begin
if (~CVA6Cfg.XF16Vec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF16Vec)
illegal_instr = 1'b1; // destination vector not supported
end
2'b11: begin
if (~CVA6Cfg.XF8Vec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF8Vec)
illegal_instr = 1'b1; // destination vector not supported
end
default: illegal_instr = 1'b1;
endcase
@ -465,7 +473,8 @@ module decoder import ariane_pkg::*; #(
5'b11011: begin
instruction_o.op = ariane_pkg::VFCPKCD_D; // vfcpka/b.vfmt.d - Vectorial FP Cast-and-Pack from 2x FP64, second 4 entries
imm_select = SIMM; // rd into result field (upper bits don't matter)
if (~CVA6Cfg.RVD) illegal_instr = 1'b1; // if we don't support RVD, we can't cast from FP64
if (~CVA6Cfg.RVD)
illegal_instr = 1'b1; // if we don't support RVD, we can't cast from FP64
// check destination format
unique case (instr.rvftype.vfmt)
// Only process instruction if corresponding extension is active and FLEN suffices (static)
@ -473,7 +482,8 @@ module decoder import ariane_pkg::*; #(
2'b01: illegal_instr = 1'b1; // no entries 4-7 in vector of 4 FP16ALT
2'b10: illegal_instr = 1'b1; // no entries 4-7 in vector of 4 FP16
2'b11: begin
if (~CVA6Cfg.XF8Vec) illegal_instr = 1'b1; // destination vector not supported
if (~CVA6Cfg.XF8Vec)
illegal_instr = 1'b1; // destination vector not supported
end
default: illegal_instr = 1'b1;
endcase
@ -519,11 +529,16 @@ module decoder import ariane_pkg::*; #(
instruction_o.rs2[4:0] = instr.rtype.rs2;
instruction_o.rd[4:0] = instr.rtype.rd;
unique case ({instr.rtype.funct7, instr.rtype.funct3})
unique case ({
instr.rtype.funct7, instr.rtype.funct3
})
{7'b000_0000, 3'b000} : instruction_o.op = ariane_pkg::ADD; // Add
{7'b010_0000, 3'b000} : instruction_o.op = ariane_pkg::SUB; // Sub
{7'b000_0000, 3'b010} : instruction_o.op = ariane_pkg::SLTS; // Set Lower Than
{7'b000_0000, 3'b011}: instruction_o.op = ariane_pkg::SLTU; // Set Lower Than Unsigned
{
7'b000_0000, 3'b011
} :
instruction_o.op = ariane_pkg::SLTU; // Set Lower Than Unsigned
{7'b000_0000, 3'b100} : instruction_o.op = ariane_pkg::XORL; // Xor
{7'b000_0000, 3'b110} : instruction_o.op = ariane_pkg::ORL; // Or
{7'b000_0000, 3'b111} : instruction_o.op = ariane_pkg::ANDL; // And
@ -544,7 +559,9 @@ module decoder import ariane_pkg::*; #(
end
endcase
if (ariane_pkg::BITMANIP) begin
unique case ({instr.rtype.funct7, instr.rtype.funct3})
unique case ({
instr.rtype.funct7, instr.rtype.funct3
})
//Logical with Negate
{7'b010_0000, 3'b111} : instruction_o.op = ariane_pkg::ANDN; // Andn
{7'b010_0000, 3'b110} : instruction_o.op = ariane_pkg::ORN; // Orn
@ -578,7 +595,9 @@ module decoder import ariane_pkg::*; #(
endcase
end
if (CVA6Cfg.ZiCondExtEn) begin
unique case ({instr.rtype.funct7, instr.rtype.funct3})
unique case ({
instr.rtype.funct7, instr.rtype.funct3
})
//Conditional move
{7'b000_0111, 3'b101} : instruction_o.op = ariane_pkg::CZERO_EQZ; // czero.eqz
{7'b000_0111, 3'b111} : instruction_o.op = ariane_pkg::CZERO_NEZ; // czero.nez
@ -588,7 +607,9 @@ module decoder import ariane_pkg::*; #(
endcase
end
//VCS coverage on
unique case ({ariane_pkg::BITMANIP, CVA6Cfg.ZiCondExtEn})
unique case ({
ariane_pkg::BITMANIP, CVA6Cfg.ZiCondExtEn
})
2'b00: illegal_instr = illegal_instr_non_bm;
2'b01: illegal_instr = illegal_instr_non_bm & illegal_instr_zic;
2'b10: illegal_instr = illegal_instr_non_bm & illegal_instr_bm;
@ -606,7 +627,9 @@ module decoder import ariane_pkg::*; #(
instruction_o.rs2[4:0] = instr.rtype.rs2;
instruction_o.rd[4:0] = instr.rtype.rd;
if (riscv::IS_XLEN64) begin
unique case ({instr.rtype.funct7, instr.rtype.funct3})
unique case ({
instr.rtype.funct7, instr.rtype.funct3
})
{7'b000_0000, 3'b000} : instruction_o.op = ariane_pkg::ADDW; // addw
{7'b010_0000, 3'b000} : instruction_o.op = ariane_pkg::SUBW; // subw
{7'b000_0000, 3'b001} : instruction_o.op = ariane_pkg::SLLW; // sllw
@ -621,7 +644,9 @@ module decoder import ariane_pkg::*; #(
default: illegal_instr_non_bm = 1'b1;
endcase
if (ariane_pkg::BITMANIP) begin
unique case ({instr.rtype.funct7, instr.rtype.funct3})
unique case ({
instr.rtype.funct7, instr.rtype.funct3
})
// Shift with Add (Unsigned Word)
{7'b001_0000, 3'b010}: instruction_o.op = ariane_pkg::SH1ADDUW; // sh1add.uw
{7'b001_0000, 3'b100}: instruction_o.op = ariane_pkg::SH2ADDUW; // sh2add.uw
@ -652,15 +677,15 @@ module decoder import ariane_pkg::*; #(
unique case (instr.itype.funct3)
3'b000: instruction_o.op = ariane_pkg::ADD; // Add Immediate
3'b010: instruction_o.op = ariane_pkg::SLTS; // Set to one if Lower Than Immediate
3'b011: instruction_o.op = ariane_pkg::SLTU; // Set to one if Lower Than Immediate Unsigned
3'b011:
instruction_o.op = ariane_pkg::SLTU; // Set to one if Lower Than Immediate Unsigned
3'b100: instruction_o.op = ariane_pkg::XORL; // Exclusive Or with Immediate
3'b110: instruction_o.op = ariane_pkg::ORL; // Or with Immediate
3'b111: instruction_o.op = ariane_pkg::ANDL; // And with Immediate
3'b001: begin
instruction_o.op = ariane_pkg::SLL; // Shift Left Logical by Immediate
if (instr.instr[31:26] != 6'b0)
illegal_instr_non_bm = 1'b1;
if (instr.instr[31:26] != 6'b0) illegal_instr_non_bm = 1'b1;
if (instr.instr[25] != 1'b0 && riscv::XLEN == 32) illegal_instr_non_bm = 1'b1;
end
@ -669,8 +694,7 @@ module decoder import ariane_pkg::*; #(
instruction_o.op = ariane_pkg::SRL; // Shift Right Logical by Immediate
else if (instr.instr[31:26] == 6'b010_000)
instruction_o.op = ariane_pkg::SRA; // Shift Right Arithmetically by Immediate
else
illegal_instr_non_bm = 1'b1;
else illegal_instr_non_bm = 1'b1;
if (instr.instr[25] != 1'b0 && riscv::XLEN == 32) illegal_instr_non_bm = 1'b1;
end
endcase
@ -678,37 +702,23 @@ module decoder import ariane_pkg::*; #(
unique case (instr.itype.funct3)
3'b001: begin
if (instr.instr[31:25] == 7'b0110000) begin
if (instr.instr[22:20] == 3'b100)
instruction_o.op = ariane_pkg::SEXTB;
else if (instr.instr[22:20] == 3'b101)
instruction_o.op = ariane_pkg::SEXTH;
else if (instr.instr[22:20] == 3'b010)
instruction_o.op = ariane_pkg::CPOP;
else if (instr.instr[22:20] == 3'b000)
instruction_o.op = ariane_pkg::CLZ;
else if (instr.instr[22:20] == 3'b001)
instruction_o.op = ariane_pkg::CTZ;
end
else if (instr.instr[31:26] == 6'b010010)
instruction_o.op = ariane_pkg::BCLRI;
else if (instr.instr[31:26] == 6'b011010)
instruction_o.op = ariane_pkg::BINVI;
else if (instr.instr[31:26] == 6'b001010)
instruction_o.op = ariane_pkg::BSETI;
else
illegal_instr_bm = 1'b1;
if (instr.instr[22:20] == 3'b100) instruction_o.op = ariane_pkg::SEXTB;
else if (instr.instr[22:20] == 3'b101) instruction_o.op = ariane_pkg::SEXTH;
else if (instr.instr[22:20] == 3'b010) instruction_o.op = ariane_pkg::CPOP;
else if (instr.instr[22:20] == 3'b000) instruction_o.op = ariane_pkg::CLZ;
else if (instr.instr[22:20] == 3'b001) instruction_o.op = ariane_pkg::CTZ;
end else if (instr.instr[31:26] == 6'b010010) instruction_o.op = ariane_pkg::BCLRI;
else if (instr.instr[31:26] == 6'b011010) instruction_o.op = ariane_pkg::BINVI;
else if (instr.instr[31:26] == 6'b001010) instruction_o.op = ariane_pkg::BSETI;
else illegal_instr_bm = 1'b1;
end
3'b101: begin
if (instr.instr[31:20] == 12'b001010000111)
instruction_o.op = ariane_pkg::ORCB;
if (instr.instr[31:20] == 12'b001010000111) instruction_o.op = ariane_pkg::ORCB;
else if (instr.instr[31:20] == 12'b011010111000 || instr.instr[31:20] == 12'b011010011000)
instruction_o.op = ariane_pkg::REV8;
else if (instr.instr[31:26] == 6'b010_010)
instruction_o.op = ariane_pkg::BEXTI;
else if (instr.instr[31:26] == 6'b011_000)
instruction_o.op = ariane_pkg::RORI;
else
illegal_instr_bm = 1'b1;
else if (instr.instr[31:26] == 6'b010_010) instruction_o.op = ariane_pkg::BEXTI;
else if (instr.instr[31:26] == 6'b011_000) instruction_o.op = ariane_pkg::RORI;
else illegal_instr_bm = 1'b1;
end
default: illegal_instr_bm = 1'b1;
endcase
@ -731,16 +741,14 @@ module decoder import ariane_pkg::*; #(
3'b000: instruction_o.op = ariane_pkg::ADDW; // Add Immediate
3'b001: begin
instruction_o.op = ariane_pkg::SLLW; // Shift Left Logical by Immediate
if (instr.instr[31:25] != 7'b0)
illegal_instr_non_bm = 1'b1;
if (instr.instr[31:25] != 7'b0) illegal_instr_non_bm = 1'b1;
end
3'b101: begin
if (instr.instr[31:25] == 7'b0)
instruction_o.op = ariane_pkg::SRLW; // Shift Right Logical by Immediate
else if (instr.instr[31:25] == 7'b010_0000)
instruction_o.op = ariane_pkg::SRAW; // Shift Right Arithmetically by Immediate
else
illegal_instr_non_bm = 1'b1;
else illegal_instr_non_bm = 1'b1;
end
default: illegal_instr_non_bm = 1'b1;
endcase
@ -748,22 +756,17 @@ module decoder import ariane_pkg::*; #(
unique case (instr.itype.funct3)
3'b001: begin
if (instr.instr[31:25] == 7'b0110000) begin
if (instr.instr[21:20] == 2'b10)
instruction_o.op = ariane_pkg::CPOPW;
else if (instr.instr[21:20] == 2'b00)
instruction_o.op = ariane_pkg::CLZW;
else if (instr.instr[21:20] == 2'b01)
instruction_o.op = ariane_pkg::CTZW;
if (instr.instr[21:20] == 2'b10) instruction_o.op = ariane_pkg::CPOPW;
else if (instr.instr[21:20] == 2'b00) instruction_o.op = ariane_pkg::CLZW;
else if (instr.instr[21:20] == 2'b01) instruction_o.op = ariane_pkg::CTZW;
else illegal_instr_bm = 1'b1;
end else if (instr.instr[31:26] == 6'b000010) begin
instruction_o.op = ariane_pkg::SLLIUW; // Shift Left Logic by Immediate (Unsigned Word)
end else illegal_instr_bm = 1'b1;
end
3'b101: begin
if (instr.instr[31:25] == 7'b011_0000)
instruction_o.op = ariane_pkg::RORIW;
else
illegal_instr_bm = 1'b1;
if (instr.instr[31:25] == 7'b011_0000) instruction_o.op = ariane_pkg::RORIW;
else illegal_instr_bm = 1'b1;
end
default: illegal_instr_bm = 1'b1;
endcase
@ -787,7 +790,8 @@ module decoder import ariane_pkg::*; #(
3'b000: instruction_o.op = ariane_pkg::SB;
3'b001: instruction_o.op = ariane_pkg::SH;
3'b010: instruction_o.op = ariane_pkg::SW;
3'b011: if (riscv::XLEN==64) instruction_o.op = ariane_pkg::SD;
3'b011:
if (riscv::XLEN == 64) instruction_o.op = ariane_pkg::SD;
else illegal_instr = 1'b1;
default: illegal_instr = 1'b1;
endcase
@ -805,9 +809,11 @@ module decoder import ariane_pkg::*; #(
3'b010: instruction_o.op = ariane_pkg::LW;
3'b100: instruction_o.op = ariane_pkg::LBU;
3'b101: instruction_o.op = ariane_pkg::LHU;
3'b110: if (riscv::XLEN==64) instruction_o.op = ariane_pkg::LWU;
3'b110:
if (riscv::XLEN == 64) instruction_o.op = ariane_pkg::LWU;
else illegal_instr = 1'b1;
3'b011: if (riscv::XLEN==64) instruction_o.op = ariane_pkg::LD;
3'b011:
if (riscv::XLEN == 64) instruction_o.op = ariane_pkg::LD;
else illegal_instr = 1'b1;
default: illegal_instr = 1'b1;
endcase
@ -825,18 +831,21 @@ module decoder import ariane_pkg::*; #(
// determine store size
unique case (instr.stype.funct3)
// Only process instruction if corresponding extension is active (static)
3'b000: if (CVA6Cfg.XF8) instruction_o.op = ariane_pkg::FSB;
3'b000:
if (CVA6Cfg.XF8) instruction_o.op = ariane_pkg::FSB;
else illegal_instr = 1'b1;
3'b001: if (CVA6Cfg.XF16 | CVA6Cfg.XF16ALT) instruction_o.op = ariane_pkg::FSH;
3'b001:
if (CVA6Cfg.XF16 | CVA6Cfg.XF16ALT) instruction_o.op = ariane_pkg::FSH;
else illegal_instr = 1'b1;
3'b010: if (CVA6Cfg.RVF) instruction_o.op = ariane_pkg::FSW;
3'b010:
if (CVA6Cfg.RVF) instruction_o.op = ariane_pkg::FSW;
else illegal_instr = 1'b1;
3'b011: if (CVA6Cfg.RVD) instruction_o.op = ariane_pkg::FSD;
3'b011:
if (CVA6Cfg.RVD) instruction_o.op = ariane_pkg::FSD;
else illegal_instr = 1'b1;
default: illegal_instr = 1'b1;
endcase
end else
illegal_instr = 1'b1;
end else illegal_instr = 1'b1;
end
riscv::OpcodeLoadFp: begin
@ -848,27 +857,27 @@ module decoder import ariane_pkg::*; #(
// determine load size
unique case (instr.itype.funct3)
// Only process instruction if corresponding extension is active (static)
3'b000: if (CVA6Cfg.XF8) instruction_o.op = ariane_pkg::FLB;
3'b000:
if (CVA6Cfg.XF8) instruction_o.op = ariane_pkg::FLB;
else illegal_instr = 1'b1;
3'b001: if (CVA6Cfg.XF16 | CVA6Cfg.XF16ALT) instruction_o.op = ariane_pkg::FLH;
3'b001:
if (CVA6Cfg.XF16 | CVA6Cfg.XF16ALT) instruction_o.op = ariane_pkg::FLH;
else illegal_instr = 1'b1;
3'b010: if (CVA6Cfg.RVF) instruction_o.op = ariane_pkg::FLW;
3'b010:
if (CVA6Cfg.RVF) instruction_o.op = ariane_pkg::FLW;
else illegal_instr = 1'b1;
3'b011: if (CVA6Cfg.RVD) instruction_o.op = ariane_pkg::FLD;
3'b011:
if (CVA6Cfg.RVD) instruction_o.op = ariane_pkg::FLD;
else illegal_instr = 1'b1;
default: illegal_instr = 1'b1;
endcase
end else
illegal_instr = 1'b1;
end else illegal_instr = 1'b1;
end
// ----------------------------------
// Floating-Point Reg-Reg Operations
// ----------------------------------
riscv::OpcodeMadd,
riscv::OpcodeMsub,
riscv::OpcodeNmsub,
riscv::OpcodeNmadd: begin
riscv::OpcodeMadd, riscv::OpcodeMsub, riscv::OpcodeNmsub, riscv::OpcodeNmadd: begin
if (CVA6Cfg.FpPresent && fs_i != riscv::Off) begin // only generate decoder if FP extensions are enabled (static)
instruction_o.fu = FPU;
instruction_o.rs1[4:0] = instr.r4type.rs1;
@ -879,9 +888,12 @@ module decoder import ariane_pkg::*; #(
// select the correct fused operation
unique case (instr.r4type.opcode)
default: instruction_o.op = ariane_pkg::FMADD; // fmadd.fmt - FP Fused multiply-add
riscv::OpcodeMsub: instruction_o.op = ariane_pkg::FMSUB; // fmsub.fmt - FP Fused multiply-subtract
riscv::OpcodeNmsub: instruction_o.op = ariane_pkg::FNMSUB; // fnmsub.fmt - FP Negated fused multiply-subtract
riscv::OpcodeNmadd: instruction_o.op = ariane_pkg::FNMADD; // fnmadd.fmt - FP Negated fused multiply-add
riscv::OpcodeMsub:
instruction_o.op = ariane_pkg::FMSUB; // fmsub.fmt - FP Fused multiply-subtract
riscv::OpcodeNmsub:
instruction_o.op = ariane_pkg::FNMSUB; // fnmsub.fmt - FP Negated fused multiply-subtract
riscv::OpcodeNmadd:
instruction_o.op = ariane_pkg::FNMADD; // fnmadd.fmt - FP Negated fused multiply-add
endcase
// determine fp format
@ -899,8 +911,7 @@ module decoder import ariane_pkg::*; #(
unique case (instr.rftype.rm) inside
[3'b000 : 3'b100]: ; //legal rounding modes
3'b101: begin // Alternative Half-Precsision encded as fmt=10 and rm=101
if (~CVA6Cfg.XF16ALT || instr.rftype.fmt != 2'b10)
illegal_instr = 1'b1;
if (~CVA6Cfg.XF16ALT || instr.rftype.fmt != 2'b10) illegal_instr = 1'b1;
unique case (frm_i) inside // actual rounding mode from frm csr
[3'b000 : 3'b100]: ; //legal rounding modes
default: illegal_instr = 1'b1;
@ -956,8 +967,7 @@ module decoder import ariane_pkg::*; #(
if (!(instr.rftype.rm inside {[3'b000 : 3'b010], [3'b100 : 3'b110]}))
illegal_instr = 1'b1;
end else begin
if (!(instr.rftype.rm inside {[3'b000:3'b010]}))
illegal_instr = 1'b1;
if (!(instr.rftype.rm inside {[3'b000 : 3'b010]})) illegal_instr = 1'b1;
end
end
5'b00101: begin
@ -967,15 +977,15 @@ module decoder import ariane_pkg::*; #(
if (!(instr.rftype.rm inside {[3'b000 : 3'b001], [3'b100 : 3'b101]}))
illegal_instr = 1'b1;
end else begin
if (!(instr.rftype.rm inside {[3'b000:3'b001]}))
illegal_instr = 1'b1;
if (!(instr.rftype.rm inside {[3'b000 : 3'b001]})) illegal_instr = 1'b1;
end
end
5'b01000: begin
instruction_o.op = ariane_pkg::FCVT_F2F; // fcvt.fmt.fmt - FP to FP Conversion
instruction_o.rs2[4:0] = instr.rvftype.rs1; // tie rs2 to rs1 to be safe (vectors use rs2)
imm_select = IIMM; // rs2 holds part of the intruction
if (|instr.rftype.rs2[24:23]) illegal_instr = 1'b1; // bits [22:20] used, other bits must be 0
if (|instr.rftype.rs2[24:23])
illegal_instr = 1'b1; // bits [22:20] used, other bits must be 0
// check source format
unique case (instr.rftype.rs2[22:20])
// Only process instruction if corresponding extension is active (static)
@ -994,19 +1004,20 @@ module decoder import ariane_pkg::*; #(
if (!(instr.rftype.rm inside {[3'b000 : 3'b010], [3'b100 : 3'b110]}))
illegal_instr = 1'b1;
end else begin
if (!(instr.rftype.rm inside {[3'b000:3'b010]}))
illegal_instr = 1'b1;
if (!(instr.rftype.rm inside {[3'b000 : 3'b010]})) illegal_instr = 1'b1;
end
end
5'b11000: begin
instruction_o.op = ariane_pkg::FCVT_F2I; // fcvt.ifmt.fmt - FP to Int Conversion
imm_select = IIMM; // rs2 holds part of the instruction
if (|instr.rftype.rs2[24:22]) illegal_instr = 1'b1; // bits [21:20] used, other bits must be 0
if (|instr.rftype.rs2[24:22])
illegal_instr = 1'b1; // bits [21:20] used, other bits must be 0
end
5'b11010: begin
instruction_o.op = ariane_pkg::FCVT_I2F; // fcvt.fmt.ifmt - Int to FP Conversion
imm_select = IIMM; // rs2 holds part of the instruction
if (|instr.rftype.rs2[24:22]) illegal_instr = 1'b1; // bits [21:20] used, other bits must be 0
if (|instr.rftype.rs2[24:22])
illegal_instr = 1'b1; // bits [21:20] used, other bits must be 0
end
5'b11100: begin
instruction_o.rs2[4:0] = instr.rftype.rs1; // set rs2 = rs1 so we can map FMV to SGNJ in the unit
@ -1046,8 +1057,7 @@ module decoder import ariane_pkg::*; #(
unique case (instr.rftype.rm) inside
[3'b000 : 3'b100]: ; //legal rounding modes
3'b101: begin // Alternative Half-Precsision encded as fmt=10 and rm=101
if (~CVA6Cfg.XF16ALT || instr.rftype.fmt != 2'b10)
illegal_instr = 1'b1;
if (~CVA6Cfg.XF16ALT || instr.rftype.fmt != 2'b10) illegal_instr = 1'b1;
unique case (frm_i) inside // actual rounding mode from frm csr
[3'b000 : 3'b100]: ; //legal rounding modes
default: illegal_instr = 1'b1;
@ -1213,9 +1223,24 @@ module decoder import ariane_pkg::*; #(
always_comb begin : sign_extend
imm_i_type = {{riscv::XLEN - 12{instruction_i[31]}}, instruction_i[31:20]};
imm_s_type = {{riscv::XLEN - 12{instruction_i[31]}}, instruction_i[31:25], instruction_i[11:7]};
imm_sb_type = { {riscv::XLEN-13{instruction_i[31]}}, instruction_i[31], instruction_i[7], instruction_i[30:25], instruction_i[11:8], 1'b0 };
imm_u_type = { {riscv::XLEN-32{instruction_i[31]}}, instruction_i[31:12], 12'b0 }; // JAL, AUIPC, sign extended to 64 bit
imm_uj_type = { {riscv::XLEN-20{instruction_i[31]}}, instruction_i[19:12], instruction_i[20], instruction_i[30:21], 1'b0 };
imm_sb_type = {
{riscv::XLEN - 13{instruction_i[31]}},
instruction_i[31],
instruction_i[7],
instruction_i[30:25],
instruction_i[11:8],
1'b0
};
imm_u_type = {
{riscv::XLEN - 32{instruction_i[31]}}, instruction_i[31:12], 12'b0
}; // JAL, AUIPC, sign extended to 64 bit
imm_uj_type = {
{riscv::XLEN - 20{instruction_i[31]}},
instruction_i[19:12],
instruction_i[20],
instruction_i[30:21],
1'b0
};
imm_bi_type = {{riscv::XLEN - 5{instruction_i[24]}}, instruction_i[24:20]};
// NOIMM, IIMM, SIMM, BIMM, UIMM, JIMM, RS3
@ -1310,29 +1335,53 @@ module decoder import ariane_pkg::*; #(
// we have three interrupt sources: external interrupts, software interrupts, timer interrupts (order of precedence)
// for two privilege levels: Supervisor and Machine Mode
// Supervisor Timer Interrupt
if (irq_ctrl_i.mie[riscv::S_TIMER_INTERRUPT[$clog2(riscv::XLEN)-1:0]] && irq_ctrl_i.mip[riscv::S_TIMER_INTERRUPT[$clog2(riscv::XLEN)-1:0]]) begin
if (irq_ctrl_i.mie[riscv::S_TIMER_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] && irq_ctrl_i.mip[riscv::S_TIMER_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]]) begin
interrupt_cause = riscv::S_TIMER_INTERRUPT;
end
// Supervisor Software Interrupt
if (irq_ctrl_i.mie[riscv::S_SW_INTERRUPT[$clog2(riscv::XLEN)-1:0]] && irq_ctrl_i.mip[riscv::S_SW_INTERRUPT[$clog2(riscv::XLEN)-1:0]]) begin
if (irq_ctrl_i.mie[riscv::S_SW_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] && irq_ctrl_i.mip[riscv::S_SW_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]]) begin
interrupt_cause = riscv::S_SW_INTERRUPT;
end
// Supervisor External Interrupt
// The logical-OR of the software-writable bit and the signal from the external interrupt controller is
// used to generate external interrupts to the supervisor
if (irq_ctrl_i.mie[riscv::S_EXT_INTERRUPT[$clog2(riscv::XLEN)-1:0]] && (irq_ctrl_i.mip[riscv::S_EXT_INTERRUPT[$clog2(riscv::XLEN)-1:0]] | irq_i[ariane_pkg::SupervisorIrq])) begin
if (irq_ctrl_i.mie[riscv::S_EXT_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] && (irq_ctrl_i.mip[riscv::S_EXT_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] | irq_i[ariane_pkg::SupervisorIrq])) begin
interrupt_cause = riscv::S_EXT_INTERRUPT;
end
// Machine Timer Interrupt
if (irq_ctrl_i.mip[riscv::M_TIMER_INTERRUPT[$clog2(riscv::XLEN)-1:0]] && irq_ctrl_i.mie[riscv::M_TIMER_INTERRUPT[$clog2(riscv::XLEN)-1:0]]) begin
if (irq_ctrl_i.mip[riscv::M_TIMER_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] && irq_ctrl_i.mie[riscv::M_TIMER_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]]) begin
interrupt_cause = riscv::M_TIMER_INTERRUPT;
end
// Machine Mode Software Interrupt
if (irq_ctrl_i.mip[riscv::M_SW_INTERRUPT[$clog2(riscv::XLEN)-1:0]] && irq_ctrl_i.mie[riscv::M_SW_INTERRUPT[$clog2(riscv::XLEN)-1:0]]) begin
if (irq_ctrl_i.mip[riscv::M_SW_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] && irq_ctrl_i.mie[riscv::M_SW_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]]) begin
interrupt_cause = riscv::M_SW_INTERRUPT;
end
// Machine Mode External Interrupt
if (irq_ctrl_i.mip[riscv::M_EXT_INTERRUPT[$clog2(riscv::XLEN)-1:0]] && irq_ctrl_i.mie[riscv::M_EXT_INTERRUPT[$clog2(riscv::XLEN)-1:0]]) begin
if (irq_ctrl_i.mip[riscv::M_EXT_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]] && irq_ctrl_i.mie[riscv::M_EXT_INTERRUPT[$clog2(
riscv::XLEN
)-1:0]]) begin
interrupt_cause = riscv::M_EXT_INTERRUPT;
end

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

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,
@ -35,7 +37,11 @@ module fpu_wrap import ariane_pkg::*; #(
// 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_FP32),
unsigned'(LAT_COMP_FP64),
unsigned'(LAT_COMP_FP16),
unsigned'(LAT_COMP_FP8),
unsigned'(LAT_COMP_FP16ALT)}, // ADDMUL
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

View file

@ -46,7 +46,9 @@ 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;
@ -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
@ -167,27 +168,26 @@ module bht #(
// 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_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

View file

@ -103,7 +103,9 @@ module btb #(
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_wdata_update[i*BRAM_WORD_BITS+:BRAM_WORD_BITS] = {
1'b1, btb_update_i.target_address
};
end
end
end
@ -138,7 +140,8 @@ 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],
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
@ -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
@ -79,12 +81,10 @@ 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;
@ -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
@ -184,7 +189,9 @@ module frontend import ariane_pkg::*; #(
// 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]})
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
@ -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;
@ -353,7 +361,8 @@ module frontend import ariane_pkg::*; #(
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
@ -499,7 +508,8 @@ module frontend import ariane_pkg::*; #(
// pragma translate_off
`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
// pragma translate_on

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,8 +80,9 @@ 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;
@ -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
@ -437,13 +444,16 @@ module instr_queue import ariane_pkg::*; #(
// 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");
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
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

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

View file

@ -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,8 +125,7 @@ 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)

View file

@ -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;
@ -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
@ -325,14 +332,18 @@ 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_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_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
@ -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 {
@ -446,7 +597,8 @@ package ariane_pkg;
FCMP, // Comparisons
FCLASS, // Classifications
[VFMIN : VFCPKCD_D], // Additional Vectorial FP ops
ACCEL_OP_FS1 : return 1'b1; // Accelerator instructions
ACCEL_OP_FS1:
return 1'b1; // Accelerator instructions
default: return 1'b0; // all other ops
endcase
endfunction
@ -461,7 +613,8 @@ package ariane_pkg;
FCVT_F2F, // Vectorial F2F Conversions requrie target
[FSGNJ : FMV_F2X], // Sign Injections and moves mapped to SGNJ
FCMP, // Comparisons
[VFMIN:VFCPKCD_D] : return 1'b1; // Additional Vectorial FP ops
[VFMIN : VFCPKCD_D]:
return 1'b1; // Additional Vectorial FP ops
default: return 1'b0; // all other ops
endcase
endfunction
@ -473,7 +626,8 @@ package ariane_pkg;
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
[VFCPKAB_S : VFCPKCD_D]:
return 1'b1; // Vectorial FP cast and pack ops
default: return 1'b0; // all other ops
endcase
endfunction
@ -490,7 +644,8 @@ package ariane_pkg;
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
ACCEL_OP_FD:
return 1'b1; // Accelerator instructions
default: return 1'b0; // all other ops
endcase
endfunction
@ -708,7 +863,13 @@ package ariane_pkg;
// 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 };
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);
@ -716,7 +877,14 @@ package ariane_pkg;
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 };
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
// ----------------------
@ -729,9 +897,12 @@ package ariane_pkg;
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

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

View file

@ -746,7 +746,8 @@ package riscv;
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
@ -756,17 +757,20 @@ package riscv;
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
@ -781,7 +785,8 @@ package riscv;
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
@ -805,7 +810,8 @@ package riscv;
// 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

@ -76,17 +76,14 @@ package std_cache_pkg;
// 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
);
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
);
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

@ -257,9 +257,7 @@ package wt_cache_pkg;
return out;
endfunction
function automatic logic [5:0] popcnt64 (
input logic [63:0] in
);
function automatic logic [5:0] popcnt64(input logic [63:0] in);
logic [5:0] cnt = 0;
foreach (in[k]) begin
cnt += 6'(in[k]);
@ -267,10 +265,7 @@ package wt_cache_pkg;
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)
@ -282,10 +277,7 @@ package wt_cache_pkg;
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)
@ -297,11 +289,8 @@ package wt_cache_pkg;
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
@ -312,11 +301,8 @@ package wt_cache_pkg;
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
@ -329,9 +315,7 @@ 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)
8'b1111_1111: size = 2'b11; // dword
@ -343,9 +327,7 @@ 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)
4'b1111: size = 2'b10; // word

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,

View file

@ -14,7 +14,9 @@
// 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
) (
@ -86,9 +88,7 @@ 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;
@ -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
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,12 +249,15 @@ 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
@ -255,7 +267,9 @@ module issue_read_operands import ariane_pkg::*; #(
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
@ -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
@ -464,7 +482,9 @@ 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
@ -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,11 +564,18 @@ 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)))
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

View file

@ -14,7 +14,9 @@
// 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

View file

@ -13,7 +13,9 @@
// 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
) (
@ -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;
@ -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,7 +465,16 @@ 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)

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;
@ -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];
@ -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).
@ -461,9 +470,12 @@ 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]};
@ -491,18 +503,22 @@ module load_unit import ariane_pkg::*; #(
//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

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,
@ -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,
@ -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,20 +337,30 @@ 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 #(
@ -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};

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

View file

@ -24,7 +24,9 @@
// 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
@ -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;
@ -172,7 +174,9 @@ 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
@ -240,9 +244,15 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
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
@ -255,9 +265,15 @@ module cva6_tlb_sv32 import ariane_pkg::*; #(
endfunction
assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1))
else begin $error("More then one hit in TLB!"); $stop(); end
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
else begin
$error("More then one TLB entry selected for next replace!");
$stop();
end
`endif
//pragma translate_on

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,
@ -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,19 +270,31 @@ 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 #(
@ -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,19 +452,26 @@ 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

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
) (
@ -81,12 +83,16 @@ module ptw import ariane_pkg::*; #(
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;
@ -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,7 +15,9 @@
// 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
@ -46,7 +48,8 @@ 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;
@ -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;
@ -178,7 +180,9 @@ 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
@ -249,9 +253,15 @@ module tlb import ariane_pkg::*; #(
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
@ -264,9 +274,15 @@ module tlb import ariane_pkg::*; #(
endfunction
assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1))
else begin $error("More then one hit in TLB!"); $stop(); end
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
else begin
$error("More then one TLB entry selected for next replace!");
$stop();
end
`endif
//pragma translate_on

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,
@ -62,7 +64,9 @@ module mult import ariane_pkg::*; #(
// ---------------------
// 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

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

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
) (
@ -74,25 +76,46 @@ module perf_counters import ariane_pkg::*; #(
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'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'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'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'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'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
@ -110,9 +133,10 @@ module perf_counters import ariane_pkg::*; #(
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
generic_counter_d[i] = generic_counter_q[i] + 1'b1;
end else begin
generic_counter_d[i] = 'b0;
end
end
end
@ -123,19 +147,27 @@ 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
@ -147,19 +179,28 @@ 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) 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

View file

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

View file

@ -34,7 +34,10 @@ 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(
lzc #(
.WIDTH(PLEN),
.MODE (1'b0)
) i_lzc (
.in_i (conf_addr_n),
.cnt_o (trail_ones),
.empty_o()

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;
@ -63,7 +62,12 @@ 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;
@ -75,7 +79,12 @@ module pmp_tb;
// 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;
@ -87,7 +96,12 @@ module pmp_tb;
// 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;

View file

@ -14,7 +14,10 @@
package tb_pkg;
class P #(parameter WIDTH=32, parameter PMP_LEN=32);
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;

View file

@ -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;
@ -307,22 +308,40 @@ 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
@ -409,12 +428,12 @@ 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))
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 (
@ -426,8 +445,7 @@ module scoreboard #(
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)
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)
@ -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
@ -42,7 +44,12 @@ module serdiv import ariane_pkg::*; #(
// 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;
@ -145,8 +152,7 @@ module serdiv import ariane_pkg::*; #(
/////////////////////////////////////
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
@ -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
@ -264,20 +268,21 @@ module store_buffer import ariane_pkg::*; #(
//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)
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)
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;
@ -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)