mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-06-28 09:16:22 -04:00
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:
parent
3d47805dfc
commit
7cd183b710
94 changed files with 21423 additions and 19841 deletions
|
@ -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.");
|
||||
|
||||
|
|
68
core/alu.sv
68
core/alu.sv
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
// }}}
|
||||
|
|
|
@ -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
|
||||
// }}}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
96
core/cva6.sv
96
core/cva6.sv
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
337
core/decoder.sv
337
core/decoder.sv
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
) (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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})
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue