mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-22 05:07:21 -04:00
parent
9ecdaa1408
commit
86e1408666
44 changed files with 4213 additions and 365 deletions
20
Bender.yml
20
Bender.yml
|
@ -50,6 +50,26 @@ sources:
|
|||
- core/mmu_sv39/ptw.sv
|
||||
- core/cva6_accel_first_pass_decoder_stub.sv
|
||||
|
||||
- target: cv64a6_imafdch_sv39
|
||||
files:
|
||||
- core/include/cv64a6_imafdch_sv39_config_pkg.sv
|
||||
- core/include/riscv_pkg.sv
|
||||
- core/include/ariane_pkg.sv
|
||||
- core/mmu_sv39x4/cva6_tlb_sv39x4.sv
|
||||
- core/mmu_sv39x4/cva6_mmu_sv39x4.sv
|
||||
- core/mmu_sv39x4/cva6_ptw_sv39x4.sv
|
||||
- core/cva6_accel_first_pass_decoder_stub.sv
|
||||
|
||||
- target: cv64a6_imafdch_sv39_wb
|
||||
files:
|
||||
- core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv
|
||||
- core/include/riscv_pkg.sv
|
||||
- core/include/ariane_pkg.sv
|
||||
- core/mmu_sv39x4/cva6_tlb_sv39x4.sv
|
||||
- core/mmu_sv39x4/cva6_mmu_sv39x4.sv
|
||||
- core/mmu_sv39x4/cva6_ptw_sv39x4.sv
|
||||
- core/cva6_accel_first_pass_decoder_stub.sv
|
||||
|
||||
- target: cv32a6_imac_sv0
|
||||
files:
|
||||
- core/include/cv32a6_imac_sv0_config_pkg.sv
|
||||
|
|
|
@ -84,11 +84,13 @@ core/load_unit.sv
|
|||
core/load_store_unit.sv
|
||||
core/lsu_bypass.sv
|
||||
core/mmu_sv39/mmu.sv
|
||||
core/mmu_sv39x4/cva6_mmu_sv39x4.sv
|
||||
core/mult.sv
|
||||
core/multiplier.sv
|
||||
core/serdiv.sv
|
||||
core/perf_counters.sv
|
||||
core/mmu_sv39/ptw.sv
|
||||
core/mmu_sv39x4/ptw_sv39x4.sv
|
||||
core/ariane_regfile_ff.sv
|
||||
core/re_name.sv
|
||||
core/scoreboard.sv
|
||||
|
@ -96,6 +98,7 @@ core/store_buffer.sv
|
|||
core/amo_buffer.sv
|
||||
core/store_unit.sv
|
||||
core/mmu_sv39/tlb.sv
|
||||
core/mmu_sv39x4/tlb_sv39x4.sv
|
||||
core/commit_stage.sv
|
||||
core/cache_subsystem/wt_dcache_ctrl.sv
|
||||
core/cache_subsystem/wt_dcache_mem.sv
|
||||
|
|
16
ci/build-hyp-riscv-tests.sh
Executable file
16
ci/build-hyp-riscv-tests.sh
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
VERSION="920c1379cf6ca2374c6c5207dca425a933d8c7fd"
|
||||
|
||||
cd $ROOT/tmp
|
||||
|
||||
if [ -z ${NUM_JOBS} ]; then
|
||||
NUM_JOBS=1
|
||||
fi
|
||||
|
||||
[ -d $ROOT/tmp/riscv-hyp-tests ] || git clone https://github.com/ninolomata/riscv-hyp-tests
|
||||
cd riscv-hyp-tests
|
||||
git checkout $VERSION
|
||||
git submodule update --init --recursive
|
||||
make PLAT=cva6
|
|
@ -189,6 +189,11 @@ ${CVA6_REPO_DIR}/core/mmu_sv39/mmu.sv
|
|||
${CVA6_REPO_DIR}/core/mmu_sv39/ptw.sv
|
||||
${CVA6_REPO_DIR}/core/mmu_sv39/tlb.sv
|
||||
|
||||
// MMU Sv39x4
|
||||
${CVA6_REPO_DIR}/core/mmu_sv39x4/cva6_mmu_sv39x4.sv
|
||||
${CVA6_REPO_DIR}/core/mmu_sv39x4/cva6_ptw_sv39x4.sv
|
||||
${CVA6_REPO_DIR}/core/mmu_sv39x4/cva6_tlb_sv39x4.sv
|
||||
|
||||
// MMU Sv32
|
||||
${CVA6_REPO_DIR}/core/mmu_sv32/cva6_mmu_sv32.sv
|
||||
${CVA6_REPO_DIR}/core/mmu_sv32/cva6_ptw_sv32.sv
|
||||
|
|
|
@ -297,22 +297,29 @@ module acc_dispatcher
|
|||
logic acc_st_disp;
|
||||
|
||||
// Unpack the accelerator response
|
||||
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_fflags_valid_o = acc_resp_i.fflags_valid;
|
||||
assign acc_fflags_o = acc_resp_i.fflags;
|
||||
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,
|
||||
tval2 : '0,
|
||||
tinst : '0,
|
||||
gva : '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
|
||||
assign acc_req_o.resp_ready = 1'b1;
|
||||
|
||||
// Signal dispatched load/store to issue stage
|
||||
assign acc_ld_disp = acc_req_valid && (acc_insn_queue_o.operation == ACCEL_OP_LOAD);
|
||||
assign acc_st_disp = acc_req_valid && (acc_insn_queue_o.operation == ACCEL_OP_STORE);
|
||||
assign acc_ld_disp = acc_req_valid && (acc_insn_queue_o.operation == ACCEL_OP_LOAD);
|
||||
assign acc_st_disp = acc_req_valid && (acc_insn_queue_o.operation == ACCEL_OP_STORE);
|
||||
|
||||
// Cache invalidation
|
||||
assign inval_valid_o = acc_resp_i.inval_valid;
|
||||
assign inval_addr_o = acc_resp_i.inval_addr;
|
||||
assign inval_valid_o = acc_resp_i.inval_valid;
|
||||
assign inval_addr_o = acc_resp_i.inval_addr;
|
||||
|
||||
/**************************
|
||||
* Accelerator commit *
|
||||
|
|
|
@ -23,6 +23,8 @@ module branch_unit #(
|
|||
input logic clk_i,
|
||||
// Asynchronous reset active low - SUBSYSTEM
|
||||
input logic rst_ni,
|
||||
// Virtualization mode state - CSR_REGFILE
|
||||
input logic v_i,
|
||||
// Debug mode state - CSR_REGFILE
|
||||
input logic debug_mode_i,
|
||||
// FU data needed to execute instruction - ISSUE_STAGE
|
||||
|
@ -115,6 +117,9 @@ module branch_unit #(
|
|||
if (CVA6Cfg.TvalEn)
|
||||
branch_exception_o.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i};
|
||||
else branch_exception_o.tval = '0;
|
||||
branch_exception_o.tval2 = {CVA6Cfg.GPLEN{1'b0}};
|
||||
branch_exception_o.tinst = '0;
|
||||
branch_exception_o.gva = CVA6Cfg.RVH ? v_i : 1'b0;
|
||||
// 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] || (!CVA6Cfg.RVC && target_address[1])) && jump_taken) begin
|
||||
|
|
|
@ -81,7 +81,9 @@ module commit_stage
|
|||
// Request a pipeline flush - CONTROLLER
|
||||
output logic flush_commit_o,
|
||||
// Flush TLBs and pipeline - CONTROLLER
|
||||
output logic sfence_vma_o
|
||||
output logic sfence_vma_o,
|
||||
output logic hfence_vvma_o,
|
||||
output logic hfence_gvma_o
|
||||
);
|
||||
|
||||
// ila_0 i_ila_commit (
|
||||
|
@ -142,6 +144,8 @@ module commit_stage
|
|||
fence_i_o = 1'b0;
|
||||
fence_o = 1'b0;
|
||||
sfence_vma_o = 1'b0;
|
||||
hfence_vvma_o = 1'b0;
|
||||
hfence_gvma_o = 1'b0;
|
||||
csr_write_fflags_o = 1'b0;
|
||||
flush_commit_o = 1'b0;
|
||||
|
||||
|
@ -212,6 +216,30 @@ module commit_stage
|
|||
commit_ack_o[0] = no_st_pending_i;
|
||||
end
|
||||
// ------------------
|
||||
// HFENCE.VVMA Logic
|
||||
// ------------------
|
||||
// hfence.vvma is idempotent so we can safely re-execute it after returning
|
||||
// from interrupt service routine
|
||||
// check if this instruction was a HFENCE_VVMA
|
||||
if (CVA6Cfg.RVH && commit_instr_i[0].op == HFENCE_VVMA) begin
|
||||
// no store pending so we can flush the TLBs and pipeline
|
||||
hfence_vvma_o = no_st_pending_i;
|
||||
// wait for the store buffer to drain until flushing the pipeline
|
||||
commit_ack_o[0] = no_st_pending_i;
|
||||
end
|
||||
// ------------------
|
||||
// HFENCE.GVMA Logic
|
||||
// ------------------
|
||||
// hfence.gvma is idempotent so we can safely re-execute it after returning
|
||||
// from interrupt service routine
|
||||
// check if this instruction was a HFENCE_GVMA
|
||||
if (CVA6Cfg.RVH && commit_instr_i[0].op == HFENCE_GVMA) begin
|
||||
// no store pending so we can flush the TLBs and pipeline
|
||||
hfence_gvma_o = no_st_pending_i;
|
||||
// wait for the store buffer to drain until flushing the pipeline
|
||||
commit_ack_o[0] = no_st_pending_i;
|
||||
end
|
||||
// ------------------
|
||||
// FENCE.I Logic
|
||||
// ------------------
|
||||
// fence.i is idempotent so we can safely re-execute it after returning
|
||||
|
@ -308,6 +336,10 @@ module commit_stage
|
|||
exception_o.valid = 1'b0;
|
||||
exception_o.cause = '0;
|
||||
exception_o.tval = '0;
|
||||
exception_o.tval2 = '0;
|
||||
exception_o.tinst = '0;
|
||||
exception_o.gva = 1'b0;
|
||||
|
||||
// we need a valid instruction in the commit stage
|
||||
if (commit_instr_i[0].valid) begin
|
||||
// ------------------------
|
||||
|
@ -319,6 +351,11 @@ module commit_stage
|
|||
// the instruction bits from the ID stage. If a earlier exception happened we don't care
|
||||
// as we will overwrite it anyway in the next IF bl
|
||||
exception_o.tval = commit_instr_i[0].ex.tval;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
exception_o.tinst = commit_instr_i[0].ex.tinst;
|
||||
exception_o.tval2 = commit_instr_i[0].ex.tval2;
|
||||
exception_o.gva = commit_instr_i[0].ex.gva;
|
||||
end
|
||||
end
|
||||
// ------------------------
|
||||
// Earlier Exceptions
|
||||
|
|
|
@ -23,6 +23,8 @@ module controller
|
|||
input logic clk_i,
|
||||
// Asynchronous reset active low - SUBSYSTEM
|
||||
input logic rst_ni,
|
||||
// Virtualization mode - CSR_REGFILE
|
||||
input logic v_i,
|
||||
// Set PC om PC Gen - FRONTEND
|
||||
output logic set_pc_commit_o,
|
||||
// Flush the IF stage - FRONTEND
|
||||
|
@ -43,6 +45,8 @@ module controller
|
|||
input logic flush_dcache_ack_i,
|
||||
// Flush TLBs - EX_STAGE
|
||||
output logic flush_tlb_o,
|
||||
output logic flush_tlb_vvma_o,
|
||||
output logic flush_tlb_gvma_o,
|
||||
// Halt request from CSR (WFI instruction) - CSR_REGFILE
|
||||
input logic halt_csr_i,
|
||||
// Halt request from accelerator dispatcher - ACC_DISPATCHER
|
||||
|
@ -65,6 +69,8 @@ module controller
|
|||
input logic fence_i,
|
||||
// We got an instruction to flush the TLBs and pipeline - COMMIT_STAGE
|
||||
input logic sfence_vma_i,
|
||||
input logic hfence_vvma_i,
|
||||
input logic hfence_gvma_i,
|
||||
// Flush request from commit stage - COMMIT_STAGE
|
||||
input logic flush_commit_i,
|
||||
// Flush request from accelerator - ACC_DISPATCHER
|
||||
|
@ -88,6 +94,8 @@ module controller
|
|||
flush_dcache = 1'b0;
|
||||
flush_icache_o = 1'b0;
|
||||
flush_tlb_o = 1'b0;
|
||||
flush_tlb_vvma_o = 1'b0;
|
||||
flush_tlb_gvma_o = 1'b0;
|
||||
flush_bp_o = 1'b0;
|
||||
// ------------
|
||||
// Mis-predict
|
||||
|
@ -157,7 +165,34 @@ module controller
|
|||
flush_id_o = 1'b1;
|
||||
flush_ex_o = 1'b1;
|
||||
|
||||
flush_tlb_o = 1'b1;
|
||||
if (CVA6Cfg.RVH && v_i) flush_tlb_vvma_o = 1'b1;
|
||||
else flush_tlb_o = 1'b1;
|
||||
end
|
||||
|
||||
// ---------------------------------
|
||||
// HFENCE.VVMA
|
||||
// ---------------------------------
|
||||
if (CVA6Cfg.RVH && hfence_vvma_i) begin
|
||||
set_pc_commit_o = 1'b1;
|
||||
flush_if_o = 1'b1;
|
||||
flush_unissued_instr_o = 1'b1;
|
||||
flush_id_o = 1'b1;
|
||||
flush_ex_o = 1'b1;
|
||||
|
||||
flush_tlb_vvma_o = 1'b1;
|
||||
end
|
||||
|
||||
// ---------------------------------
|
||||
// HFENCE.GVMA
|
||||
// ---------------------------------
|
||||
if (CVA6Cfg.RVH && hfence_gvma_i) begin
|
||||
set_pc_commit_o = 1'b1;
|
||||
flush_if_o = 1'b1;
|
||||
flush_unissued_instr_o = 1'b1;
|
||||
flush_id_o = 1'b1;
|
||||
flush_ex_o = 1'b1;
|
||||
|
||||
flush_tlb_gvma_o = 1'b1;
|
||||
end
|
||||
|
||||
// ---------------------------------
|
||||
|
|
File diff suppressed because it is too large
Load diff
332
core/cva6.sv
332
core/cva6.sv
|
@ -42,6 +42,9 @@ module cva6
|
|||
logic [CVA6Cfg.XLEN-1:0] cause; // cause of exception
|
||||
logic [CVA6Cfg.XLEN-1:0] tval; // additional information of causing exception (e.g.: instruction causing it),
|
||||
// address of LD/ST fault
|
||||
logic [CVA6Cfg.GPLEN-1:0] tval2; // additional information when the causing exception in a guest exception
|
||||
logic [31:0] tinst; // transformed instruction information
|
||||
logic gva; // signals when a guest virtual address is written to tval
|
||||
logic valid;
|
||||
},
|
||||
|
||||
|
@ -130,6 +133,7 @@ module cva6
|
|||
logic [CVA6Cfg.XLEN-1:0] mie;
|
||||
logic [CVA6Cfg.XLEN-1:0] mip;
|
||||
logic [CVA6Cfg.XLEN-1:0] mideleg;
|
||||
logic [CVA6Cfg.XLEN-1:0] hideleg;
|
||||
logic sie;
|
||||
logic global_enable;
|
||||
},
|
||||
|
@ -137,7 +141,11 @@ module cva6
|
|||
localparam type lsu_ctrl_t = struct packed {
|
||||
logic valid;
|
||||
logic [CVA6Cfg.VLEN-1:0] vaddr;
|
||||
logic [31:0] tinst;
|
||||
logic hs_ld_st_inst;
|
||||
logic hlvx_inst;
|
||||
logic overflow;
|
||||
logic g_overflow;
|
||||
logic [CVA6Cfg.XLEN-1:0] data;
|
||||
logic [(CVA6Cfg.XLEN/8)-1:0] be;
|
||||
fu_t fu;
|
||||
|
@ -297,20 +305,28 @@ module cva6
|
|||
|
||||
localparam type interrupts_t = struct packed {
|
||||
logic [CVA6Cfg.XLEN-1:0] S_SW;
|
||||
logic [CVA6Cfg.XLEN-1:0] VS_SW;
|
||||
logic [CVA6Cfg.XLEN-1:0] M_SW;
|
||||
logic [CVA6Cfg.XLEN-1:0] S_TIMER;
|
||||
logic [CVA6Cfg.XLEN-1:0] VS_TIMER;
|
||||
logic [CVA6Cfg.XLEN-1:0] M_TIMER;
|
||||
logic [CVA6Cfg.XLEN-1:0] S_EXT;
|
||||
logic [CVA6Cfg.XLEN-1:0] VS_EXT;
|
||||
logic [CVA6Cfg.XLEN-1:0] M_EXT;
|
||||
logic [CVA6Cfg.XLEN-1:0] HS_EXT;
|
||||
};
|
||||
|
||||
localparam interrupts_t INTERRUPTS = '{
|
||||
S_SW: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_SOFT),
|
||||
VS_SW: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_SOFT),
|
||||
M_SW: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_SOFT),
|
||||
S_TIMER: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_TIMER),
|
||||
VS_TIMER: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_TIMER),
|
||||
M_TIMER: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_TIMER),
|
||||
S_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_EXT),
|
||||
M_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_EXT)
|
||||
VS_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_EXT),
|
||||
M_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_EXT),
|
||||
HS_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_HS_EXT)
|
||||
};
|
||||
|
||||
// ------------------------------------------
|
||||
|
@ -318,6 +334,7 @@ module cva6
|
|||
// Signals connecting more than one module
|
||||
// ------------------------------------------
|
||||
riscv::priv_lvl_t priv_lvl;
|
||||
logic v;
|
||||
exception_t ex_commit; // exception from commit stage
|
||||
bp_resolve_t resolved_branch;
|
||||
logic [ CVA6Cfg.VLEN-1:0] pc_commit;
|
||||
|
@ -359,6 +376,7 @@ module cva6
|
|||
fu_data_t fu_data_id_ex;
|
||||
logic [CVA6Cfg.VLEN-1:0] pc_id_ex;
|
||||
logic is_compressed_instr_id_ex;
|
||||
logic [31:0] tinst_ex;
|
||||
// fixed latency units
|
||||
logic flu_ready_ex_id;
|
||||
logic [CVA6Cfg.TRANS_ID_BITS-1:0] flu_trans_id_ex_id;
|
||||
|
@ -410,6 +428,7 @@ module cva6
|
|||
logic single_step_acc_commit;
|
||||
// CSR
|
||||
logic csr_valid_id_ex;
|
||||
logic csr_hs_ld_st_inst_ex;
|
||||
// CVXIF
|
||||
logic [CVA6Cfg.TRANS_ID_BITS-1:0] x_trans_id_ex_id;
|
||||
logic [CVA6Cfg.XLEN-1:0] x_result_ex_id;
|
||||
|
@ -457,16 +476,26 @@ module cva6
|
|||
// --------------
|
||||
logic [4:0] fflags_csr_commit;
|
||||
riscv::xs_t fs;
|
||||
riscv::xs_t vfs;
|
||||
logic [2:0] frm_csr_id_issue_ex;
|
||||
logic [6:0] fprec_csr_ex;
|
||||
riscv::xs_t vs;
|
||||
logic enable_translation_csr_ex;
|
||||
logic enable_g_translation_csr_ex;
|
||||
logic en_ld_st_translation_csr_ex;
|
||||
logic en_ld_st_g_translation_csr_ex;
|
||||
riscv::priv_lvl_t ld_st_priv_lvl_csr_ex;
|
||||
logic ld_st_v_csr_ex;
|
||||
logic sum_csr_ex;
|
||||
logic vs_sum_csr_ex;
|
||||
logic mxr_csr_ex;
|
||||
logic vmxr_csr_ex;
|
||||
logic [CVA6Cfg.PPNW-1:0] satp_ppn_csr_ex;
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] asid_csr_ex;
|
||||
logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_csr_ex;
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_csr_ex;
|
||||
logic [CVA6Cfg.PPNW-1:0] hgatp_ppn_csr_ex;
|
||||
logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_csr_ex;
|
||||
logic [11:0] csr_addr_ex_csr;
|
||||
fu_op csr_op_commit_csr;
|
||||
logic [CVA6Cfg.XLEN-1:0] csr_wdata_commit_csr;
|
||||
|
@ -474,7 +503,9 @@ module cva6
|
|||
exception_t csr_exception_csr_commit;
|
||||
logic tvm_csr_id;
|
||||
logic tw_csr_id;
|
||||
logic vtw_csr_id;
|
||||
logic tsr_csr_id;
|
||||
logic hu;
|
||||
irq_ctrl_t irq_ctrl_csr_id;
|
||||
logic dcache_en_csr_nbdcache;
|
||||
logic csr_write_fflags_commit_cs;
|
||||
|
@ -510,9 +541,13 @@ module cva6
|
|||
logic flush_ctrl_ex;
|
||||
logic flush_ctrl_bp;
|
||||
logic flush_tlb_ctrl_ex;
|
||||
logic flush_tlb_vvma_ctrl_ex;
|
||||
logic flush_tlb_gvma_ctrl_ex;
|
||||
logic fence_i_commit_controller;
|
||||
logic fence_commit_controller;
|
||||
logic sfence_vma_commit_controller;
|
||||
logic hfence_vvma_commit_controller;
|
||||
logic hfence_gvma_commit_controller;
|
||||
logic halt_ctrl;
|
||||
logic halt_csr_ctrl;
|
||||
logic dcache_flush_ctrl_cache;
|
||||
|
@ -613,7 +648,9 @@ module cva6
|
|||
.rvfi_is_compressed_o(rvfi_is_compressed),
|
||||
|
||||
.priv_lvl_i (priv_lvl),
|
||||
.v_i (v),
|
||||
.fs_i (fs),
|
||||
.vfs_i (vfs),
|
||||
.frm_i (frm_csr_id_issue_ex),
|
||||
.vs_i (vs),
|
||||
.irq_i (irq_i),
|
||||
|
@ -621,7 +658,9 @@ module cva6
|
|||
.debug_mode_i(debug_mode),
|
||||
.tvm_i (tvm_csr_id),
|
||||
.tw_i (tw_csr_id),
|
||||
.tsr_i (tsr_csr_id)
|
||||
.vtw_i (vtw_csr_id),
|
||||
.tsr_i (tsr_csr_id),
|
||||
.hu_i (hu)
|
||||
);
|
||||
|
||||
logic [CVA6Cfg.NrWbPorts-1:0][CVA6Cfg.TRANS_ID_BITS-1:0] trans_id_ex_id;
|
||||
|
@ -717,6 +756,7 @@ module cva6
|
|||
.fu_data_o (fu_data_id_ex),
|
||||
.pc_o (pc_id_ex),
|
||||
.is_compressed_instr_o (is_compressed_instr_id_ex),
|
||||
.tinst_o (tinst_ex),
|
||||
// fixed latency unit ready
|
||||
.flu_ready_i (flu_ready_ex_id),
|
||||
// ALU
|
||||
|
@ -783,37 +823,39 @@ module cva6
|
|||
.icache_drsp_t(icache_drsp_t),
|
||||
.lsu_ctrl_t(lsu_ctrl_t)
|
||||
) ex_stage_i (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.debug_mode_i (debug_mode),
|
||||
.flush_i (flush_ctrl_ex),
|
||||
.rs1_forwarding_i (rs1_forwarding_id_ex),
|
||||
.rs2_forwarding_i (rs2_forwarding_id_ex),
|
||||
.fu_data_i (fu_data_id_ex),
|
||||
.pc_i (pc_id_ex),
|
||||
.clk_i(clk_i),
|
||||
.rst_ni(rst_ni),
|
||||
.debug_mode_i(debug_mode),
|
||||
.flush_i(flush_ctrl_ex),
|
||||
.rs1_forwarding_i(rs1_forwarding_id_ex),
|
||||
.rs2_forwarding_i(rs2_forwarding_id_ex),
|
||||
.fu_data_i(fu_data_id_ex),
|
||||
.pc_i(pc_id_ex),
|
||||
.is_compressed_instr_i(is_compressed_instr_id_ex),
|
||||
.tinst_i(tinst_ex),
|
||||
// fixed latency units
|
||||
.flu_result_o (flu_result_ex_id),
|
||||
.flu_trans_id_o (flu_trans_id_ex_id),
|
||||
.flu_valid_o (flu_valid_ex_id),
|
||||
.flu_exception_o (flu_exception_ex_id),
|
||||
.flu_ready_o (flu_ready_ex_id),
|
||||
.flu_result_o(flu_result_ex_id),
|
||||
.flu_trans_id_o(flu_trans_id_ex_id),
|
||||
.flu_valid_o(flu_valid_ex_id),
|
||||
.flu_exception_o(flu_exception_ex_id),
|
||||
.flu_ready_o(flu_ready_ex_id),
|
||||
// ALU
|
||||
.alu_valid_i (alu_valid_id_ex),
|
||||
.alu_valid_i(alu_valid_id_ex),
|
||||
// Branches and Jumps
|
||||
.branch_valid_i (branch_valid_id_ex),
|
||||
.branch_predict_i (branch_predict_id_ex), // branch predict to ex
|
||||
.resolved_branch_o (resolved_branch),
|
||||
.resolve_branch_o (resolve_branch_ex_id),
|
||||
.branch_valid_i(branch_valid_id_ex),
|
||||
.branch_predict_i(branch_predict_id_ex), // branch predict to ex
|
||||
.resolved_branch_o(resolved_branch),
|
||||
.resolve_branch_o(resolve_branch_ex_id),
|
||||
// CSR
|
||||
.csr_valid_i (csr_valid_id_ex),
|
||||
.csr_addr_o (csr_addr_ex_csr),
|
||||
.csr_commit_i (csr_commit_commit_ex), // from commit
|
||||
.csr_valid_i(csr_valid_id_ex),
|
||||
.csr_addr_o(csr_addr_ex_csr),
|
||||
.csr_commit_i(csr_commit_commit_ex), // from commit
|
||||
.csr_hs_ld_st_inst_o(csr_hs_ld_st_inst_ex), // signals a Hypervisor Load/Store Instruction
|
||||
// MULT
|
||||
.mult_valid_i (mult_valid_id_ex),
|
||||
.mult_valid_i(mult_valid_id_ex),
|
||||
// LSU
|
||||
.lsu_ready_o (lsu_ready_ex_id),
|
||||
.lsu_valid_i (lsu_valid_id_ex),
|
||||
.lsu_ready_o(lsu_ready_ex_id),
|
||||
.lsu_valid_i(lsu_valid_id_ex),
|
||||
|
||||
.load_result_o (load_result_ex_id),
|
||||
.load_trans_id_o (load_trans_id_ex_id),
|
||||
|
@ -825,64 +867,76 @@ module cva6
|
|||
.store_valid_o (store_valid_ex_id),
|
||||
.store_exception_o(store_exception_ex_id),
|
||||
|
||||
.lsu_commit_i (lsu_commit_commit_ex), // from commit
|
||||
.lsu_commit_ready_o (lsu_commit_ready_ex_commit), // to commit
|
||||
.commit_tran_id_i (lsu_commit_trans_id), // from commit
|
||||
.stall_st_pending_i (stall_st_pending_ex),
|
||||
.no_st_pending_o (no_st_pending_ex),
|
||||
.lsu_commit_i (lsu_commit_commit_ex), // from commit
|
||||
.lsu_commit_ready_o (lsu_commit_ready_ex_commit), // to commit
|
||||
.commit_tran_id_i (lsu_commit_trans_id), // from commit
|
||||
.stall_st_pending_i (stall_st_pending_ex),
|
||||
.no_st_pending_o (no_st_pending_ex),
|
||||
// FPU
|
||||
.fpu_ready_o (fpu_ready_ex_id),
|
||||
.fpu_valid_i (fpu_valid_id_ex),
|
||||
.fpu_fmt_i (fpu_fmt_id_ex),
|
||||
.fpu_rm_i (fpu_rm_id_ex),
|
||||
.fpu_frm_i (frm_csr_id_issue_ex),
|
||||
.fpu_prec_i (fprec_csr_ex),
|
||||
.fpu_trans_id_o (fpu_trans_id_ex_id),
|
||||
.fpu_result_o (fpu_result_ex_id),
|
||||
.fpu_valid_o (fpu_valid_ex_id),
|
||||
.fpu_exception_o (fpu_exception_ex_id),
|
||||
.amo_valid_commit_i (amo_valid_commit),
|
||||
.amo_req_o (amo_req),
|
||||
.amo_resp_i (amo_resp),
|
||||
.fpu_ready_o (fpu_ready_ex_id),
|
||||
.fpu_valid_i (fpu_valid_id_ex),
|
||||
.fpu_fmt_i (fpu_fmt_id_ex),
|
||||
.fpu_rm_i (fpu_rm_id_ex),
|
||||
.fpu_frm_i (frm_csr_id_issue_ex),
|
||||
.fpu_prec_i (fprec_csr_ex),
|
||||
.fpu_trans_id_o (fpu_trans_id_ex_id),
|
||||
.fpu_result_o (fpu_result_ex_id),
|
||||
.fpu_valid_o (fpu_valid_ex_id),
|
||||
.fpu_exception_o (fpu_exception_ex_id),
|
||||
.amo_valid_commit_i (amo_valid_commit),
|
||||
.amo_req_o (amo_req),
|
||||
.amo_resp_i (amo_resp),
|
||||
// CoreV-X-Interface
|
||||
.x_valid_i (x_issue_valid_id_ex),
|
||||
.x_ready_o (x_issue_ready_ex_id),
|
||||
.x_off_instr_i (x_off_instr_id_ex),
|
||||
.x_trans_id_o (x_trans_id_ex_id),
|
||||
.x_exception_o (x_exception_ex_id),
|
||||
.x_result_o (x_result_ex_id),
|
||||
.x_valid_o (x_valid_ex_id),
|
||||
.x_we_o (x_we_ex_id),
|
||||
.cvxif_req_o (cvxif_req),
|
||||
.cvxif_resp_i (cvxif_resp),
|
||||
.x_valid_i (x_issue_valid_id_ex),
|
||||
.x_ready_o (x_issue_ready_ex_id),
|
||||
.x_off_instr_i (x_off_instr_id_ex),
|
||||
.x_trans_id_o (x_trans_id_ex_id),
|
||||
.x_exception_o (x_exception_ex_id),
|
||||
.x_result_o (x_result_ex_id),
|
||||
.x_valid_o (x_valid_ex_id),
|
||||
.x_we_o (x_we_ex_id),
|
||||
.cvxif_req_o (cvxif_req),
|
||||
.cvxif_resp_i (cvxif_resp),
|
||||
// Accelerator
|
||||
.acc_valid_i (acc_valid_acc_ex),
|
||||
.acc_valid_i (acc_valid_acc_ex),
|
||||
// Performance counters
|
||||
.itlb_miss_o (itlb_miss_ex_perf),
|
||||
.dtlb_miss_o (dtlb_miss_ex_perf),
|
||||
.itlb_miss_o (itlb_miss_ex_perf),
|
||||
.dtlb_miss_o (dtlb_miss_ex_perf),
|
||||
// Memory Management
|
||||
.enable_translation_i (enable_translation_csr_ex), // from CSR
|
||||
.en_ld_st_translation_i (en_ld_st_translation_csr_ex),
|
||||
.flush_tlb_i (flush_tlb_ctrl_ex),
|
||||
.priv_lvl_i (priv_lvl), // from CSR
|
||||
.ld_st_priv_lvl_i (ld_st_priv_lvl_csr_ex), // from CSR
|
||||
.sum_i (sum_csr_ex), // from CSR
|
||||
.mxr_i (mxr_csr_ex), // from CSR
|
||||
.satp_ppn_i (satp_ppn_csr_ex), // from CSR
|
||||
.asid_i (asid_csr_ex), // from CSR
|
||||
.icache_areq_i (icache_areq_cache_ex),
|
||||
.icache_areq_o (icache_areq_ex_cache),
|
||||
.enable_translation_i (enable_translation_csr_ex), // from CSR
|
||||
.enable_g_translation_i (enable_g_translation_csr_ex), // from CSR
|
||||
.en_ld_st_translation_i (en_ld_st_translation_csr_ex),
|
||||
.en_ld_st_g_translation_i(en_ld_st_g_translation_csr_ex),
|
||||
.flush_tlb_i (flush_tlb_ctrl_ex),
|
||||
.flush_tlb_vvma_i (flush_tlb_vvma_ctrl_ex),
|
||||
.flush_tlb_gvma_i (flush_tlb_gvma_ctrl_ex),
|
||||
.priv_lvl_i (priv_lvl), // from CSR
|
||||
.v_i (v), // from CSR
|
||||
.ld_st_priv_lvl_i (ld_st_priv_lvl_csr_ex), // from CSR
|
||||
.ld_st_v_i (ld_st_v_csr_ex), // from CSR
|
||||
.sum_i (sum_csr_ex), // from CSR
|
||||
.vs_sum_i (vs_sum_csr_ex), // from CSR
|
||||
.mxr_i (mxr_csr_ex), // from CSR
|
||||
.vmxr_i (vmxr_csr_ex), // from CSR
|
||||
.satp_ppn_i (satp_ppn_csr_ex), // from CSR
|
||||
.asid_i (asid_csr_ex), // from CSR
|
||||
.vsatp_ppn_i (vsatp_ppn_csr_ex), // from CSR
|
||||
.vs_asid_i (vs_asid_csr_ex), // from CSR
|
||||
.hgatp_ppn_i (hgatp_ppn_csr_ex), // from CSR
|
||||
.vmid_i (vmid_csr_ex), // from CSR
|
||||
.icache_areq_i (icache_areq_cache_ex),
|
||||
.icache_areq_o (icache_areq_ex_cache),
|
||||
// DCACHE interfaces
|
||||
.dcache_req_ports_i (dcache_req_ports_cache_ex),
|
||||
.dcache_req_ports_o (dcache_req_ports_ex_cache),
|
||||
.dcache_wbuffer_empty_i (dcache_commit_wbuffer_empty),
|
||||
.dcache_wbuffer_not_ni_i(dcache_commit_wbuffer_not_ni),
|
||||
.dcache_req_ports_i (dcache_req_ports_cache_ex),
|
||||
.dcache_req_ports_o (dcache_req_ports_ex_cache),
|
||||
.dcache_wbuffer_empty_i (dcache_commit_wbuffer_empty),
|
||||
.dcache_wbuffer_not_ni_i (dcache_commit_wbuffer_not_ni),
|
||||
// PMP
|
||||
.pmpcfg_i (pmpcfg),
|
||||
.pmpaddr_i (pmpaddr),
|
||||
.pmpcfg_i (pmpcfg),
|
||||
.pmpaddr_i (pmpaddr),
|
||||
//RVFI
|
||||
.rvfi_lsu_ctrl_o (rvfi_lsu_ctrl),
|
||||
.rvfi_mem_paddr_o (rvfi_mem_paddr)
|
||||
.rvfi_lsu_ctrl_o (rvfi_lsu_ctrl),
|
||||
.rvfi_mem_paddr_o (rvfi_mem_paddr)
|
||||
);
|
||||
|
||||
// ---------
|
||||
|
@ -928,6 +982,8 @@ module cva6
|
|||
.fence_i_o (fence_i_commit_controller),
|
||||
.fence_o (fence_commit_controller),
|
||||
.sfence_vma_o (sfence_vma_commit_controller),
|
||||
.hfence_vvma_o (hfence_vvma_commit_controller),
|
||||
.hfence_gvma_o (hfence_gvma_commit_controller),
|
||||
.flush_commit_o (flush_commit),
|
||||
.*
|
||||
);
|
||||
|
@ -943,59 +999,73 @@ module cva6
|
|||
.rvfi_probes_csr_t (rvfi_probes_csr_t),
|
||||
.MHPMCounterNum (MHPMCounterNum)
|
||||
) csr_regfile_i (
|
||||
.flush_o (flush_csr_ctrl),
|
||||
.halt_csr_o (halt_csr_ctrl),
|
||||
.commit_instr_i (commit_instr_id_commit),
|
||||
.commit_ack_i (commit_macro_ack),
|
||||
.boot_addr_i (boot_addr_i[CVA6Cfg.VLEN-1:0]),
|
||||
.hart_id_i (hart_id_i[CVA6Cfg.XLEN-1:0]),
|
||||
.ex_i (ex_commit),
|
||||
.csr_op_i (csr_op_commit_csr),
|
||||
.csr_write_fflags_i (csr_write_fflags_commit_cs),
|
||||
.dirty_fp_state_i (dirty_fp_state),
|
||||
.dirty_v_state_i (dirty_v_state),
|
||||
.csr_addr_i (csr_addr_ex_csr),
|
||||
.csr_wdata_i (csr_wdata_commit_csr),
|
||||
.csr_rdata_o (csr_rdata_csr_commit),
|
||||
.pc_i (pc_commit),
|
||||
.csr_exception_o (csr_exception_csr_commit),
|
||||
.epc_o (epc_commit_pcgen),
|
||||
.eret_o (eret),
|
||||
.set_debug_pc_o (set_debug_pc),
|
||||
.trap_vector_base_o (trap_vector_base_commit_pcgen),
|
||||
.priv_lvl_o (priv_lvl),
|
||||
.acc_fflags_ex_i (acc_resp_fflags),
|
||||
.acc_fflags_ex_valid_i (acc_resp_fflags_valid),
|
||||
.fs_o (fs),
|
||||
.fflags_o (fflags_csr_commit),
|
||||
.frm_o (frm_csr_id_issue_ex),
|
||||
.fprec_o (fprec_csr_ex),
|
||||
.vs_o (vs),
|
||||
.irq_ctrl_o (irq_ctrl_csr_id),
|
||||
.ld_st_priv_lvl_o (ld_st_priv_lvl_csr_ex),
|
||||
.en_translation_o (enable_translation_csr_ex),
|
||||
.en_ld_st_translation_o(en_ld_st_translation_csr_ex),
|
||||
.sum_o (sum_csr_ex),
|
||||
.mxr_o (mxr_csr_ex),
|
||||
.satp_ppn_o (satp_ppn_csr_ex),
|
||||
.asid_o (asid_csr_ex),
|
||||
.tvm_o (tvm_csr_id),
|
||||
.tw_o (tw_csr_id),
|
||||
.tsr_o (tsr_csr_id),
|
||||
.debug_mode_o (debug_mode),
|
||||
.single_step_o (single_step_csr_commit),
|
||||
.dcache_en_o (dcache_en_csr_nbdcache),
|
||||
.icache_en_o (icache_en_csr),
|
||||
.acc_cons_en_o (acc_cons_en_csr),
|
||||
.perf_addr_o (addr_csr_perf),
|
||||
.perf_data_o (data_csr_perf),
|
||||
.perf_data_i (data_perf_csr),
|
||||
.perf_we_o (we_csr_perf),
|
||||
.pmpcfg_o (pmpcfg),
|
||||
.pmpaddr_o (pmpaddr),
|
||||
.mcountinhibit_o (mcountinhibit_csr_perf),
|
||||
.flush_o (flush_csr_ctrl),
|
||||
.halt_csr_o (halt_csr_ctrl),
|
||||
.commit_instr_i (commit_instr_id_commit),
|
||||
.commit_ack_i (commit_macro_ack),
|
||||
.boot_addr_i (boot_addr_i[CVA6Cfg.VLEN-1:0]),
|
||||
.hart_id_i (hart_id_i[CVA6Cfg.XLEN-1:0]),
|
||||
.ex_i (ex_commit),
|
||||
.csr_op_i (csr_op_commit_csr),
|
||||
.csr_write_fflags_i (csr_write_fflags_commit_cs),
|
||||
.dirty_fp_state_i (dirty_fp_state),
|
||||
.dirty_v_state_i (dirty_v_state),
|
||||
.csr_addr_i (csr_addr_ex_csr),
|
||||
.csr_wdata_i (csr_wdata_commit_csr),
|
||||
.csr_rdata_o (csr_rdata_csr_commit),
|
||||
.pc_i (pc_commit),
|
||||
.csr_exception_o (csr_exception_csr_commit),
|
||||
.epc_o (epc_commit_pcgen),
|
||||
.eret_o (eret),
|
||||
.set_debug_pc_o (set_debug_pc),
|
||||
.trap_vector_base_o (trap_vector_base_commit_pcgen),
|
||||
.priv_lvl_o (priv_lvl),
|
||||
.v_o (v),
|
||||
.acc_fflags_ex_i (acc_resp_fflags),
|
||||
.acc_fflags_ex_valid_i (acc_resp_fflags_valid),
|
||||
.fs_o (fs),
|
||||
.vfs_o (vfs),
|
||||
.fflags_o (fflags_csr_commit),
|
||||
.frm_o (frm_csr_id_issue_ex),
|
||||
.fprec_o (fprec_csr_ex),
|
||||
.vs_o (vs),
|
||||
.irq_ctrl_o (irq_ctrl_csr_id),
|
||||
.ld_st_priv_lvl_o (ld_st_priv_lvl_csr_ex),
|
||||
.ld_st_v_o (ld_st_v_csr_ex),
|
||||
.csr_hs_ld_st_inst_i (csr_hs_ld_st_inst_ex),
|
||||
.en_translation_o (enable_translation_csr_ex),
|
||||
.en_g_translation_o (enable_g_translation_csr_ex),
|
||||
.en_ld_st_translation_o (en_ld_st_translation_csr_ex),
|
||||
.en_ld_st_g_translation_o(en_ld_st_g_translation_csr_ex),
|
||||
.sum_o (sum_csr_ex),
|
||||
.vs_sum_o (vs_sum_csr_ex),
|
||||
.mxr_o (mxr_csr_ex),
|
||||
.vmxr_o (vmxr_csr_ex),
|
||||
.satp_ppn_o (satp_ppn_csr_ex),
|
||||
.asid_o (asid_csr_ex),
|
||||
.vsatp_ppn_o (vsatp_ppn_csr_ex),
|
||||
.vs_asid_o (vs_asid_csr_ex),
|
||||
.hgatp_ppn_o (hgatp_ppn_csr_ex),
|
||||
.vmid_o (vmid_csr_ex),
|
||||
.tvm_o (tvm_csr_id),
|
||||
.tw_o (tw_csr_id),
|
||||
.vtw_o (vtw_csr_id),
|
||||
.tsr_o (tsr_csr_id),
|
||||
.hu_o (hu),
|
||||
.debug_mode_o (debug_mode),
|
||||
.single_step_o (single_step_csr_commit),
|
||||
.dcache_en_o (dcache_en_csr_nbdcache),
|
||||
.icache_en_o (icache_en_csr),
|
||||
.acc_cons_en_o (acc_cons_en_csr),
|
||||
.perf_addr_o (addr_csr_perf),
|
||||
.perf_data_o (data_csr_perf),
|
||||
.perf_data_i (data_perf_csr),
|
||||
.perf_we_o (we_csr_perf),
|
||||
.pmpcfg_o (pmpcfg),
|
||||
.pmpaddr_o (pmpaddr),
|
||||
.mcountinhibit_o (mcountinhibit_csr_perf),
|
||||
//RVFI
|
||||
.rvfi_csr_o (rvfi_csr),
|
||||
.rvfi_csr_o (rvfi_csr),
|
||||
.debug_req_i,
|
||||
.ipi_i,
|
||||
.irq_i,
|
||||
|
@ -1056,6 +1126,8 @@ module cva6
|
|||
.CVA6Cfg(CVA6Cfg),
|
||||
.bp_resolve_t(bp_resolve_t)
|
||||
) controller_i (
|
||||
// virtualization mode
|
||||
.v_i (v),
|
||||
// flush ports
|
||||
.set_pc_commit_o (set_pc_ctrl_pcgen),
|
||||
.flush_unissued_instr_o(flush_unissued_instr_ctrl_id),
|
||||
|
@ -1064,6 +1136,8 @@ module cva6
|
|||
.flush_ex_o (flush_ctrl_ex),
|
||||
.flush_bp_o (flush_ctrl_bp),
|
||||
.flush_tlb_o (flush_tlb_ctrl_ex),
|
||||
.flush_tlb_vvma_o (flush_tlb_vvma_ctrl_ex),
|
||||
.flush_tlb_gvma_o (flush_tlb_gvma_ctrl_ex),
|
||||
.flush_dcache_o (dcache_flush_ctrl_cache),
|
||||
.flush_dcache_ack_i (dcache_flush_ack_cache_ctrl),
|
||||
|
||||
|
@ -1079,6 +1153,8 @@ module cva6
|
|||
.fence_i_i (fence_i_commit_controller),
|
||||
.fence_i (fence_commit_controller),
|
||||
.sfence_vma_i (sfence_vma_commit_controller),
|
||||
.hfence_vvma_i (hfence_vvma_commit_controller),
|
||||
.hfence_gvma_i (hfence_gvma_commit_controller),
|
||||
.flush_commit_i (flush_commit),
|
||||
.flush_acc_i (flush_acc),
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ module cva6_rvfi
|
|||
| (CVA6Cfg.XLEN'(CVA6Cfg.RVC) << 2) // C - Compressed extension
|
||||
| (CVA6Cfg.XLEN'(CVA6Cfg.RVD) << 3) // D - Double precision floating-point extension
|
||||
| (CVA6Cfg.XLEN'(CVA6Cfg.RVF) << 5) // F - Single precision floating-point extension
|
||||
| (CVA6Cfg.XLEN'(CVA6Cfg.RVH) << 7) // H - Hypervisor extension
|
||||
| (CVA6Cfg.XLEN'(1) << 8) // I - RV32I/64I/128I base ISA
|
||||
| (CVA6Cfg.XLEN'(1) << 12) // M - Integer Multiply/Divide extension
|
||||
| (CVA6Cfg.XLEN'(0) << 13) // N - User level interrupts supported
|
||||
|
|
|
@ -95,6 +95,9 @@ module cvxif_fu
|
|||
x_exception_o.cause = x_valid_o ? {{(CVA6Cfg.XLEN-6){1'b0}}, cvxif_resp_i.x_result.exccode} : '0;
|
||||
x_exception_o.valid = x_valid_o ? cvxif_resp_i.x_result.exc : '0;
|
||||
x_exception_o.tval = '0;
|
||||
x_exception_o.tinst = '0;
|
||||
x_exception_o.tval2 = '0;
|
||||
x_exception_o.gva = '0;
|
||||
x_we_o = x_valid_o ? cvxif_resp_i.x_result.we : '0;
|
||||
if (illegal_n) begin
|
||||
if (~x_valid_o) begin
|
||||
|
@ -104,6 +107,9 @@ module cvxif_fu
|
|||
x_exception_o.cause = riscv::ILLEGAL_INSTR;
|
||||
x_exception_o.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn) x_exception_o.tval = illegal_instr_n;
|
||||
x_exception_o.tinst = '0;
|
||||
x_exception_o.tval2 = '0;
|
||||
x_exception_o.gva = '0;
|
||||
x_we_o = '0;
|
||||
illegal_n = '0; // Reset flag for illegal instr. illegal_id and illegal instr values are a don't care, no need to reset it.
|
||||
end
|
||||
|
|
233
core/decoder.sv
233
core/decoder.sv
|
@ -58,10 +58,14 @@ module decoder
|
|||
input irq_ctrl_t irq_ctrl_i,
|
||||
// Current privilege level - CSR_REGFILE
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
// Current virtualization mode - CSR_REGFILE
|
||||
input logic v_i,
|
||||
// Is debug mode - CSR_REGFILE
|
||||
input logic debug_mode_i,
|
||||
// Floating point extension status - CSR_REGFILE
|
||||
input riscv::xs_t fs_i,
|
||||
// Virtual floating point extension status - CSR_REGFILE
|
||||
input riscv::xs_t vfs_i,
|
||||
// Floating-point dynamic rounding mode - CSR_REGFILE
|
||||
input logic [2:0] frm_i,
|
||||
// Vector extension status - CSR_REGFILE
|
||||
|
@ -70,8 +74,12 @@ module decoder
|
|||
input logic tvm_i,
|
||||
// Timeout wait - CSR_REGFILE
|
||||
input logic tw_i,
|
||||
// Virtual timeout wait - CSR_REGFILE
|
||||
input logic vtw_i,
|
||||
// Trap sret - CSR_REGFILE
|
||||
input logic tsr_i,
|
||||
// Hypervisor user mode - CSR_REGFILE
|
||||
input logic hu_i,
|
||||
// Instruction to be added to scoreboard entry - ISSUE_STAGE
|
||||
output scoreboard_entry_t instruction_o,
|
||||
// Instruction - ISSUE_STAGE
|
||||
|
@ -83,6 +91,7 @@ module decoder
|
|||
logic illegal_instr_bm;
|
||||
logic illegal_instr_zic;
|
||||
logic illegal_instr_non_bm;
|
||||
logic virtual_illegal_instr;
|
||||
// this instruction is an environment call (ecall), it is handled like an exception
|
||||
logic ecall;
|
||||
// this instruction is a software break-point
|
||||
|
@ -91,6 +100,8 @@ module decoder
|
|||
logic check_fprm;
|
||||
riscv::instruction_t instr;
|
||||
assign instr = riscv::instruction_t'(instruction_i);
|
||||
// transformed instruction
|
||||
logic [31:0] tinst;
|
||||
// --------------------
|
||||
// Immediate select
|
||||
// --------------------
|
||||
|
@ -150,6 +161,7 @@ module decoder
|
|||
illegal_instr_non_bm = 1'b0;
|
||||
illegal_instr_bm = 1'b0;
|
||||
illegal_instr_zic = 1'b0;
|
||||
virtual_illegal_instr = 1'b0;
|
||||
instruction_o.pc = pc_i;
|
||||
instruction_o.trans_id = '0;
|
||||
instruction_o.fu = NONE;
|
||||
|
@ -165,6 +177,7 @@ module decoder
|
|||
instruction_o.use_zimm = 1'b0;
|
||||
instruction_o.bp = branch_predict_i;
|
||||
instruction_o.vfp = 1'b0;
|
||||
tinst = '0;
|
||||
ecall = 1'b0;
|
||||
ebreak = 1'b0;
|
||||
check_fprm = 1'b0;
|
||||
|
@ -180,7 +193,13 @@ module decoder
|
|||
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) begin
|
||||
if (CVA6Cfg.RVH && v_i) begin
|
||||
virtual_illegal_instr = 1'b1;
|
||||
end else begin
|
||||
illegal_instr = 1'b1;
|
||||
end
|
||||
end
|
||||
// decode the immiediate field
|
||||
case (instr.itype.imm)
|
||||
// ECALL -> inject exception
|
||||
|
@ -194,13 +213,21 @@ module decoder
|
|||
// check privilege level, SRET can only be executed in S and M mode
|
||||
// we'll just decode an illegal instruction if we are in the wrong privilege level
|
||||
if (CVA6Cfg.RVU && priv_lvl_i == riscv::PRIV_LVL_U) begin
|
||||
illegal_instr = 1'b1;
|
||||
if (CVA6Cfg.RVH && v_i) begin
|
||||
virtual_illegal_instr = 1'b1;
|
||||
end else begin
|
||||
illegal_instr = 1'b1;
|
||||
end
|
||||
// do not change privilege level if this is an illegal instruction
|
||||
instruction_o.op = ariane_pkg::ADD;
|
||||
end
|
||||
// if we are in S-Mode and Trap SRET (tsr) is set -> trap on illegal instruction
|
||||
if (priv_lvl_i == riscv::PRIV_LVL_S && tsr_i) begin
|
||||
illegal_instr = 1'b1;
|
||||
if (CVA6Cfg.RVH && v_i) begin
|
||||
virtual_illegal_instr = 1'b1;
|
||||
end else begin
|
||||
illegal_instr = 1'b1;
|
||||
end
|
||||
// do not change privilege level if this is an illegal instruction
|
||||
instruction_o.op = ariane_pkg::ADD;
|
||||
end
|
||||
|
@ -236,9 +263,14 @@ module decoder
|
|||
illegal_instr = 1'b1;
|
||||
instruction_o.op = ariane_pkg::ADD;
|
||||
end
|
||||
if (CVA6Cfg.RVH && priv_lvl_i == riscv::PRIV_LVL_S && v_i && vtw_i && !tw_i) begin
|
||||
virtual_illegal_instr = 1'b1;
|
||||
instruction_o.op = ariane_pkg::ADD;
|
||||
end
|
||||
// we don't support U mode interrupts so WFI is illegal in this context
|
||||
if (CVA6Cfg.RVU && priv_lvl_i == riscv::PRIV_LVL_U) begin
|
||||
illegal_instr = 1'b1;
|
||||
if (CVA6Cfg.RVH && v_i) virtual_illegal_instr = 1'b1;
|
||||
else illegal_instr = 1'b1;
|
||||
instruction_o.op = ariane_pkg::ADD;
|
||||
end
|
||||
end
|
||||
|
@ -248,17 +280,110 @@ module decoder
|
|||
// check privilege level, SFENCE.VMA can only be executed in M/S mode
|
||||
// only if S mode is supported
|
||||
// otherwise decode an illegal instruction
|
||||
illegal_instr = (CVA6Cfg.RVS && (priv_lvl_i inside {riscv::PRIV_LVL_M, riscv::PRIV_LVL_S}) && instr.itype.rd == '0) ? 1'b0 : 1'b1;
|
||||
if (CVA6Cfg.RVH && v_i) begin
|
||||
virtual_illegal_instr = (priv_lvl_i == riscv::PRIV_LVL_S) ? 1'b0 : 1'b1;
|
||||
end else begin
|
||||
illegal_instr = (CVA6Cfg.RVS && (priv_lvl_i inside {riscv::PRIV_LVL_M, riscv::PRIV_LVL_S}) && instr.itype.rd == '0) ? 1'b0 : 1'b1;
|
||||
end
|
||||
instruction_o.op = ariane_pkg::SFENCE_VMA;
|
||||
// check TVM flag and intercept SFENCE.VMA call if necessary
|
||||
if (CVA6Cfg.RVS && priv_lvl_i == riscv::PRIV_LVL_S && tvm_i)
|
||||
illegal_instr = 1'b1;
|
||||
end else begin
|
||||
illegal_instr = 1'b1;
|
||||
if (CVA6Cfg.RVS && priv_lvl_i == riscv::PRIV_LVL_S && tvm_i) begin
|
||||
if (CVA6Cfg.RVH && v_i) virtual_illegal_instr = 1'b1;
|
||||
else illegal_instr = 1'b1;
|
||||
end
|
||||
end
|
||||
if (CVA6Cfg.RVH) begin
|
||||
if (instr.instr[31:25] == 7'b10001) begin
|
||||
// check privilege level, HFENCE.VVMA can only be executed in M/S mode
|
||||
// otherwise decode an illegal instruction or virtual illegal instruction
|
||||
if (v_i) begin
|
||||
virtual_illegal_instr = 1'b1;
|
||||
end else begin
|
||||
illegal_instr = (priv_lvl_i inside {riscv::PRIV_LVL_M, riscv::PRIV_LVL_S}) ? 1'b0 : 1'b1;
|
||||
end
|
||||
instruction_o.op = ariane_pkg::HFENCE_VVMA;
|
||||
end
|
||||
if (instr.instr[31:25] == 7'b110001) begin
|
||||
// check privilege level, HFENCE.GVMA can only be executed in M/S mode
|
||||
// otherwise decode an illegal instruction or virtual illegal instruction
|
||||
if (v_i) begin
|
||||
virtual_illegal_instr = 1'b1;
|
||||
end else begin
|
||||
illegal_instr = (priv_lvl_i inside {riscv::PRIV_LVL_M, riscv::PRIV_LVL_S}) ? 1'b0 : 1'b1;
|
||||
end
|
||||
instruction_o.op = ariane_pkg::HFENCE_GVMA;
|
||||
// check TVM flag and intercept HFENCE.GVMA call if necessary
|
||||
if (priv_lvl_i == riscv::PRIV_LVL_S && !v_i && tvm_i) illegal_instr = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
3'b100: begin
|
||||
if (instr.instr[25] != 1'b0) begin
|
||||
instruction_o.fu = STORE;
|
||||
imm_select = NOIMM;
|
||||
instruction_o.rs1[4:0] = instr.stype.rs1;
|
||||
instruction_o.rs2[4:0] = instr.stype.rs2;
|
||||
end else begin
|
||||
instruction_o.fu = LOAD;
|
||||
imm_select = NOIMM;
|
||||
instruction_o.rs1[4:0] = instr.itype.rs1;
|
||||
instruction_o.rd[4:0] = instr.itype.rd;
|
||||
end
|
||||
// Hypervisor load/store instructions when V=1 cause virtual instruction
|
||||
if (CVA6Cfg.RVH) begin
|
||||
if (v_i) virtual_illegal_instr = 1'b1;
|
||||
// Hypervisor load/store instructions in U-mode when hstatus.HU=0 cause an illegal instruction trap.
|
||||
else if (!hu_i && priv_lvl_i == riscv::PRIV_LVL_U) illegal_instr = 1'b1;
|
||||
unique case (instr.rtype.funct7)
|
||||
7'b011_0000: begin
|
||||
if (instr.rtype.rs2 == 5'b0) begin
|
||||
instruction_o.op = ariane_pkg::HLV_B;
|
||||
end
|
||||
if (instr.rtype.rs2 == 5'b1) begin
|
||||
instruction_o.op = ariane_pkg::HLV_BU;
|
||||
end
|
||||
end
|
||||
7'b011_0010: begin
|
||||
if (instr.rtype.rs2 == 5'b0) begin
|
||||
instruction_o.op = ariane_pkg::HLV_H;
|
||||
end
|
||||
if (instr.rtype.rs2 == 5'b1) begin
|
||||
instruction_o.op = ariane_pkg::HLV_HU;
|
||||
end
|
||||
if (instr.rtype.rs2 == 5'b11) begin
|
||||
instruction_o.op = ariane_pkg::HLVX_HU;
|
||||
end
|
||||
end
|
||||
7'b011_0100: begin
|
||||
if (instr.rtype.rs2 == 5'b0) begin
|
||||
instruction_o.op = ariane_pkg::HLV_W;
|
||||
end
|
||||
if (instr.rtype.rs2 == 5'b1) begin
|
||||
instruction_o.op = ariane_pkg::HLV_WU;
|
||||
end
|
||||
if (instr.rtype.rs2 == 5'b11) begin
|
||||
instruction_o.op = ariane_pkg::HLVX_WU;
|
||||
end
|
||||
end
|
||||
7'b011_0001: instruction_o.op = ariane_pkg::HSV_B;
|
||||
7'b011_0011: instruction_o.op = ariane_pkg::HSV_H;
|
||||
7'b011_0101: instruction_o.op = ariane_pkg::HSV_W;
|
||||
7'b011_0110: instruction_o.op = ariane_pkg::HLV_D;
|
||||
7'b011_0111: instruction_o.op = ariane_pkg::HSV_D;
|
||||
|
||||
endcase
|
||||
tinst = {
|
||||
instr.rtype.funct7,
|
||||
instr.rtype.rs2,
|
||||
5'b0,
|
||||
instr.rtype.funct3,
|
||||
instr.rtype.rd,
|
||||
instr.rtype.opcode
|
||||
};
|
||||
end
|
||||
end
|
||||
// atomically swaps values in the CSR and integer register
|
||||
3'b001: begin // CSRRW
|
||||
imm_select = IIMM;
|
||||
|
@ -331,7 +456,7 @@ module decoder
|
|||
// --------------------------------------------
|
||||
if (instr.rvftype.funct2 == 2'b10) begin // Prefix 10 for all Xfvec ops
|
||||
// only generate decoder if FP extensions are enabled (static)
|
||||
if (CVA6Cfg.FpPresent && CVA6Cfg.XFVec && fs_i != riscv::Off) begin
|
||||
if (CVA6Cfg.FpPresent && CVA6Cfg.XFVec && fs_i != riscv::Off && ((CVA6Cfg.RVH && (!v_i || vfs_i != riscv::Off)) || !CVA6Cfg.RVH)) begin
|
||||
automatic logic allow_replication; // control honoring of replication flag
|
||||
|
||||
instruction_o.fu = FPU_VEC; // Same unit, but sets 'vectorial' signal
|
||||
|
@ -852,6 +977,10 @@ module decoder
|
|||
else illegal_instr = 1'b1;
|
||||
default: illegal_instr = 1'b1;
|
||||
endcase
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst = {7'b0, instr.stype.rs2, 5'b0, instr.stype.funct3, 5'b0, instr.stype.opcode};
|
||||
tinst[1] = is_compressed_i ? 1'b0 : 'b1;
|
||||
end
|
||||
end
|
||||
|
||||
riscv::OpcodeLoad: begin
|
||||
|
@ -874,13 +1003,17 @@ module decoder
|
|||
else illegal_instr = 1'b1;
|
||||
default: illegal_instr = 1'b1;
|
||||
endcase
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst = {17'b0, instr.itype.funct3, instr.itype.rd, instr.itype.opcode};
|
||||
tinst[1] = is_compressed_i ? 1'b0 : 'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// --------------------------------
|
||||
// Floating-Point Load/store
|
||||
// --------------------------------
|
||||
riscv::OpcodeStoreFp: begin
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off) begin // only generate decoder if FP extensions are enabled (static)
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off && ((CVA6Cfg.RVH && (!v_i || vfs_i != riscv::Off)) || !CVA6Cfg.RVH)) begin // only generate decoder if FP extensions are enabled (static)
|
||||
instruction_o.fu = STORE;
|
||||
imm_select = SIMM;
|
||||
instruction_o.rs1[4:0] = instr.stype.rs1;
|
||||
|
@ -902,11 +1035,15 @@ module decoder
|
|||
else illegal_instr = 1'b1;
|
||||
default: illegal_instr = 1'b1;
|
||||
endcase
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst = {7'b0, instr.stype.rs2, 5'b0, instr.stype.funct3, 5'b0, instr.stype.opcode};
|
||||
tinst[1] = is_compressed_i ? 1'b0 : 'b1;
|
||||
end
|
||||
end else illegal_instr = 1'b1;
|
||||
end
|
||||
|
||||
riscv::OpcodeLoadFp: begin
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off) begin // only generate decoder if FP extensions are enabled (static)
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off && ((CVA6Cfg.RVH && (!v_i || vfs_i != riscv::Off)) || !CVA6Cfg.RVH)) begin // only generate decoder if FP extensions are enabled (static)
|
||||
instruction_o.fu = LOAD;
|
||||
imm_select = IIMM;
|
||||
instruction_o.rs1[4:0] = instr.itype.rs1;
|
||||
|
@ -928,6 +1065,10 @@ module decoder
|
|||
else illegal_instr = 1'b1;
|
||||
default: illegal_instr = 1'b1;
|
||||
endcase
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst = {17'b0, instr.itype.funct3, instr.itype.rd, instr.itype.opcode};
|
||||
tinst[1] = is_compressed_i ? 1'b0 : 'b1;
|
||||
end
|
||||
end else illegal_instr = 1'b1;
|
||||
end
|
||||
|
||||
|
@ -935,7 +1076,7 @@ module decoder
|
|||
// Floating-Point Reg-Reg Operations
|
||||
// ----------------------------------
|
||||
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)
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off && ((CVA6Cfg.RVH && (!v_i || vfs_i != riscv::Off)) || !CVA6Cfg.RVH)) begin // only generate decoder if FP extensions are enabled (static)
|
||||
instruction_o.fu = FPU;
|
||||
instruction_o.rs1[4:0] = instr.r4type.rs1;
|
||||
instruction_o.rs2[4:0] = instr.r4type.rs2;
|
||||
|
@ -990,7 +1131,7 @@ module decoder
|
|||
end
|
||||
|
||||
riscv::OpcodeOpFp: begin
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off) begin // only generate decoder if FP extensions are enabled (static)
|
||||
if (CVA6Cfg.FpPresent && fs_i != riscv::Off && ((CVA6Cfg.RVH && (!v_i || vfs_i != riscv::Off)) || !CVA6Cfg.RVH)) begin // only generate decoder if FP extensions are enabled (static)
|
||||
instruction_o.fu = FPU;
|
||||
instruction_o.rs1[4:0] = instr.rftype.rs1;
|
||||
instruction_o.rs2[4:0] = instr.rftype.rs2;
|
||||
|
@ -1186,6 +1327,16 @@ module decoder
|
|||
end else begin
|
||||
illegal_instr = 1'b1;
|
||||
end
|
||||
tinst = {
|
||||
instr.atype.funct5,
|
||||
instr.atype.aq,
|
||||
instr.atype.rl,
|
||||
instr.atype.rs2,
|
||||
5'b0,
|
||||
instr.atype.funct3,
|
||||
instr.atype.rd,
|
||||
instr.atype.opcode
|
||||
};
|
||||
end
|
||||
|
||||
// --------------------------------
|
||||
|
@ -1366,6 +1517,8 @@ module decoder
|
|||
if (CVA6Cfg.TvalEn)
|
||||
instruction_o.ex.tval = (is_compressed_i) ? {{CVA6Cfg.XLEN-16{1'b0}}, compressed_instr_i} : {{CVA6Cfg.XLEN-32{1'b0}}, instruction_i};
|
||||
else instruction_o.ex.tval = '0;
|
||||
if (CVA6Cfg.RVH) instruction_o.ex.tinst = tinst;
|
||||
else instruction_o.ex.tinst = '0;
|
||||
// instructions which will throw an exception are marked as valid
|
||||
// e.g.: they can be committed anytime and do not need to wait for any functional unit
|
||||
// check here if we decoded an invalid instruction or if the compressed decoder already decoded
|
||||
|
@ -1374,13 +1527,17 @@ module decoder
|
|||
if (!CVA6Cfg.CvxifEn) instruction_o.ex.valid = 1'b1;
|
||||
// we decoded an illegal exception here
|
||||
instruction_o.ex.cause = riscv::ILLEGAL_INSTR;
|
||||
end else if (CVA6Cfg.RVH && virtual_illegal_instr) begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
// we decoded an virtual illegal exception here
|
||||
instruction_o.ex.cause = riscv::VIRTUAL_INSTRUCTION;
|
||||
// we got an ecall, set the correct cause depending on the current privilege level
|
||||
end else if (ecall) begin
|
||||
// this exception is valid
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
// depending on the privilege mode, set the appropriate cause
|
||||
if (priv_lvl_i == riscv::PRIV_LVL_S && CVA6Cfg.RVS) begin
|
||||
instruction_o.ex.cause = riscv::ENV_CALL_SMODE;
|
||||
instruction_o.ex.cause = (CVA6Cfg.RVH && v_i) ? riscv::ENV_CALL_VSMODE : riscv::ENV_CALL_SMODE;
|
||||
end else if (priv_lvl_i == riscv::PRIV_LVL_U && CVA6Cfg.RVU) begin
|
||||
instruction_o.ex.cause = riscv::ENV_CALL_UMODE;
|
||||
end else if (priv_lvl_i == riscv::PRIV_LVL_M) begin
|
||||
|
@ -1391,6 +1548,9 @@ module decoder
|
|||
instruction_o.ex.valid = 1'b1;
|
||||
// set breakpoint cause
|
||||
instruction_o.ex.cause = riscv::BREAKPOINT;
|
||||
// set gva bit
|
||||
if (CVA6Cfg.RVH) instruction_o.ex.gva = v_i;
|
||||
else instruction_o.ex.gva = 1'b0;
|
||||
end
|
||||
// -----------------
|
||||
// Interrupt Control
|
||||
|
@ -1399,6 +1559,24 @@ module decoder
|
|||
// throw any previous exception.
|
||||
// we have three interrupt sources: external interrupts, software interrupts, timer interrupts (order of precedence)
|
||||
// for two privilege levels: Supervisor and Machine Mode
|
||||
// Virtual Supervisor Timer Interrupt
|
||||
if (CVA6Cfg.RVH) begin
|
||||
if (irq_ctrl_i.mie[riscv::IRQ_VS_TIMER] && irq_ctrl_i.mip[riscv::IRQ_VS_TIMER]) begin
|
||||
interrupt_cause = INTERRUPTS.VS_TIMER;
|
||||
end
|
||||
// Virtual Supervisor Software Interrupt
|
||||
if (irq_ctrl_i.mie[riscv::IRQ_VS_SOFT] && irq_ctrl_i.mip[riscv::IRQ_VS_SOFT]) begin
|
||||
interrupt_cause = INTERRUPTS.VS_SW;
|
||||
end
|
||||
// Virtual Supervisor External Interrupt
|
||||
if (irq_ctrl_i.mie[riscv::IRQ_VS_EXT] && (irq_ctrl_i.mip[riscv::IRQ_VS_EXT])) begin
|
||||
interrupt_cause = INTERRUPTS.VS_EXT;
|
||||
end
|
||||
// Hypervisor Guest External Interrupts
|
||||
if (irq_ctrl_i.mie[riscv::IRQ_HS_EXT] && irq_ctrl_i.mip[riscv::IRQ_HS_EXT]) begin
|
||||
interrupt_cause = INTERRUPTS.HS_EXT;
|
||||
end
|
||||
end
|
||||
// Supervisor Timer Interrupt
|
||||
if (irq_ctrl_i.mie[riscv::IRQ_S_TIMER] && irq_ctrl_i.mip[riscv::IRQ_S_TIMER]) begin
|
||||
interrupt_cause = INTERRUPTS.S_TIMER;
|
||||
|
@ -1431,9 +1609,28 @@ module decoder
|
|||
// mode equals the delegated privilege mode (S or U) and that mode’s interrupt enable bit
|
||||
// (SIE or UIE in mstatus) is set, or if the current privilege mode is less than the delegated privilege mode.
|
||||
if (irq_ctrl_i.mideleg[interrupt_cause[$clog2(CVA6Cfg.XLEN)-1:0]]) begin
|
||||
if ((CVA6Cfg.RVS && irq_ctrl_i.sie && priv_lvl_i == riscv::PRIV_LVL_S) || (CVA6Cfg.RVU && priv_lvl_i == riscv::PRIV_LVL_U)) begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
instruction_o.ex.cause = interrupt_cause;
|
||||
if (CVA6Cfg.RVH) begin : hyp_int_gen
|
||||
if (v_i && irq_ctrl_i.hideleg[interrupt_cause[$clog2(CVA6Cfg.XLEN)-1:0]]) begin
|
||||
if ((irq_ctrl_i.sie && priv_lvl_i == riscv::PRIV_LVL_S) || priv_lvl_i == riscv::PRIV_LVL_U) begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
instruction_o.ex.cause = interrupt_cause;
|
||||
end
|
||||
end else if (v_i && ~irq_ctrl_i.hideleg[interrupt_cause[$clog2(
|
||||
CVA6Cfg.XLEN
|
||||
)-1:0]]) begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
instruction_o.ex.cause = interrupt_cause;
|
||||
end else if (!v_i && ((irq_ctrl_i.sie && priv_lvl_i == riscv::PRIV_LVL_S) || priv_lvl_i == riscv::PRIV_LVL_U) && ~irq_ctrl_i.hideleg[interrupt_cause[$clog2(
|
||||
CVA6Cfg.XLEN
|
||||
)-1:0]]) begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
instruction_o.ex.cause = interrupt_cause;
|
||||
end
|
||||
end else begin
|
||||
if ((CVA6Cfg.RVS && irq_ctrl_i.sie && priv_lvl_i == riscv::PRIV_LVL_S) || (CVA6Cfg.RVU && priv_lvl_i == riscv::PRIV_LVL_U)) begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
instruction_o.ex.cause = interrupt_cause;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
instruction_o.ex.valid = 1'b1;
|
||||
|
|
144
core/ex_stage.sv
144
core/ex_stage.sv
|
@ -48,6 +48,8 @@ module ex_stage
|
|||
input logic [CVA6Cfg.VLEN-1:0] pc_i,
|
||||
// Report whether isntruction is compressed - ISSUE_STAGE
|
||||
input logic is_compressed_instr_i,
|
||||
// Report instruction encoding - ISSUE_STAGE
|
||||
input logic [31:0] tinst_i,
|
||||
// Fixed Latency Unit result - ISSUE_STAGE
|
||||
output logic [CVA6Cfg.XLEN-1:0] flu_result_o,
|
||||
// ID of the scoreboard entry at which a=to write back - ISSUE_STAGE
|
||||
|
@ -152,22 +154,46 @@ module ex_stage
|
|||
input logic acc_valid_i,
|
||||
// Enable virtual memory translation - CSR_REGFILE
|
||||
input logic enable_translation_i,
|
||||
// Enable G-Stage memory translation - CSR_REGFILE
|
||||
input logic enable_g_translation_i,
|
||||
// Enable virtual memory translation for load/stores - CSR_REGFILE
|
||||
input logic en_ld_st_translation_i,
|
||||
// Enable G-Stage memory translation for load/stores - CSR_REGFILE
|
||||
input logic en_ld_st_g_translation_i,
|
||||
// Flush TLB - CONTROLLER
|
||||
input logic flush_tlb_i,
|
||||
input logic flush_tlb_vvma_i,
|
||||
input logic flush_tlb_gvma_i,
|
||||
// Privilege mode - CSR_REGFILE
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
// Virtualization mode - CSR_REGFILE
|
||||
input logic v_i,
|
||||
// Privilege level at which load and stores should happen - CSR_REGFILE
|
||||
input riscv::priv_lvl_t ld_st_priv_lvl_i,
|
||||
// Virtualization mode at which load and stores should happen - CSR_REGFILE
|
||||
input logic ld_st_v_i,
|
||||
// Instruction is hypervisor load/store - CSR_REGFILE
|
||||
output logic csr_hs_ld_st_inst_o,
|
||||
// Supervisor user memory - CSR_REGFILE
|
||||
input logic sum_i,
|
||||
// Virtual Supervisor user memory - CSR_REGFILE
|
||||
input logic vs_sum_i,
|
||||
// Make executable readable - CSR_REGFILE
|
||||
input logic mxr_i,
|
||||
// Make executable readable Virtual Supervisor - CSR_REGFILE
|
||||
input logic vmxr_i,
|
||||
// TO_BE_COMPLETED - CSR_REGFILE
|
||||
input logic [CVA6Cfg.PPNW-1:0] satp_ppn_i,
|
||||
// TO_BE_COMPLETED - CSR_REGFILE
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_i,
|
||||
// TO_BE_COMPLETED - CSR_REGFILE
|
||||
input logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_i,
|
||||
// TO_BE_COMPLETED - CSR_REGFILE
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_i,
|
||||
// TO_BE_COMPLETED - CSR_REGFILE
|
||||
input logic [CVA6Cfg.PPNW-1:0] hgatp_ppn_i,
|
||||
// TO_BE_COMPLETED - CSR_REGFILE
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_i,
|
||||
// icache translation response - CACHE
|
||||
input icache_arsp_t icache_areq_i,
|
||||
// icache translation request - CACHE
|
||||
|
@ -220,10 +246,14 @@ module ex_stage
|
|||
|
||||
|
||||
logic current_instruction_is_sfence_vma;
|
||||
logic current_instruction_is_hfence_vvma;
|
||||
logic current_instruction_is_hfence_gvma;
|
||||
// These two register store the rs1 and rs2 parameters in case of `SFENCE_VMA`
|
||||
// instruction to be used for TLB flush in the next clock cycle.
|
||||
logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_to_be_flushed;
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] asid_to_be_flushed;
|
||||
logic [CVA6Cfg.VLEN-1:0] vaddr_to_be_flushed;
|
||||
logic [CVA6Cfg.GPLEN-1:0] gpaddr_to_be_flushed;
|
||||
|
||||
// from ALU to branch unit
|
||||
logic alu_branch_res; // branch comparison result
|
||||
|
@ -261,6 +291,7 @@ module ex_stage
|
|||
) branch_unit_i (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.v_i,
|
||||
.debug_mode_i,
|
||||
.fu_data_i,
|
||||
.pc_i,
|
||||
|
@ -397,7 +428,7 @@ module ex_stage
|
|||
.flush_i,
|
||||
.stall_st_pending_i,
|
||||
.no_st_pending_o,
|
||||
.fu_data_i (lsu_data),
|
||||
.fu_data_i (lsu_data),
|
||||
.lsu_ready_o,
|
||||
.lsu_valid_i,
|
||||
.load_trans_id_o,
|
||||
|
@ -408,22 +439,37 @@ module ex_stage
|
|||
.store_result_o,
|
||||
.store_valid_o,
|
||||
.store_exception_o,
|
||||
.commit_i (lsu_commit_i),
|
||||
.commit_ready_o (lsu_commit_ready_o),
|
||||
.commit_i (lsu_commit_i),
|
||||
.commit_ready_o (lsu_commit_ready_o),
|
||||
.commit_tran_id_i,
|
||||
.enable_translation_i,
|
||||
.enable_g_translation_i,
|
||||
.en_ld_st_translation_i,
|
||||
.en_ld_st_g_translation_i,
|
||||
.icache_areq_i,
|
||||
.icache_areq_o,
|
||||
.priv_lvl_i,
|
||||
.v_i,
|
||||
.ld_st_priv_lvl_i,
|
||||
.ld_st_v_i,
|
||||
.csr_hs_ld_st_inst_o,
|
||||
.sum_i,
|
||||
.vs_sum_i,
|
||||
.mxr_i,
|
||||
.vmxr_i,
|
||||
.satp_ppn_i,
|
||||
.vsatp_ppn_i,
|
||||
.hgatp_ppn_i,
|
||||
.asid_i,
|
||||
.asid_to_be_flushed_i (asid_to_be_flushed),
|
||||
.vaddr_to_be_flushed_i(vaddr_to_be_flushed),
|
||||
.vs_asid_i,
|
||||
.asid_to_be_flushed_i (asid_to_be_flushed),
|
||||
.vmid_i,
|
||||
.vmid_to_be_flushed_i (vmid_to_be_flushed),
|
||||
.vaddr_to_be_flushed_i (vaddr_to_be_flushed),
|
||||
.gpaddr_to_be_flushed_i(gpaddr_to_be_flushed),
|
||||
.flush_tlb_i,
|
||||
.flush_tlb_vvma_i,
|
||||
.flush_tlb_gvma_i,
|
||||
.itlb_miss_o,
|
||||
.dtlb_miss_o,
|
||||
.dcache_req_ports_i,
|
||||
|
@ -433,6 +479,7 @@ module ex_stage
|
|||
.amo_valid_commit_i,
|
||||
.amo_req_o,
|
||||
.amo_resp_i,
|
||||
.tinst_i,
|
||||
.pmpcfg_i,
|
||||
.pmpaddr_i,
|
||||
.rvfi_lsu_ctrl_o,
|
||||
|
@ -471,33 +518,80 @@ module ex_stage
|
|||
end
|
||||
|
||||
if (CVA6Cfg.RVS) begin
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
current_instruction_is_sfence_vma <= 1'b0;
|
||||
end else begin
|
||||
if (flush_i) begin
|
||||
if (CVA6Cfg.RVH) begin
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
current_instruction_is_sfence_vma <= 1'b0;
|
||||
current_instruction_is_hfence_vvma <= 1'b0;
|
||||
current_instruction_is_hfence_gvma <= 1'b0;
|
||||
end else begin
|
||||
if (flush_i) begin
|
||||
current_instruction_is_sfence_vma <= 1'b0;
|
||||
current_instruction_is_hfence_vvma <= 1'b0;
|
||||
current_instruction_is_hfence_gvma <= 1'b0;
|
||||
end else if ((fu_data_i.operation == SFENCE_VMA && !v_i) && csr_valid_i) begin
|
||||
current_instruction_is_sfence_vma <= 1'b1;
|
||||
end else if (((fu_data_i.operation == SFENCE_VMA && v_i) || fu_data_i.operation == HFENCE_VVMA) && csr_valid_i) begin
|
||||
current_instruction_is_hfence_vvma <= 1'b1;
|
||||
end else if ((fu_data_i.operation == HFENCE_GVMA) && csr_valid_i) begin
|
||||
current_instruction_is_hfence_gvma <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
assign current_instruction_is_hfence_vvma = 1'b0;
|
||||
assign current_instruction_is_hfence_gvma = 1'b0;
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
current_instruction_is_sfence_vma <= 1'b0;
|
||||
end else if ((fu_data_i.operation == SFENCE_VMA) && csr_valid_i) begin
|
||||
current_instruction_is_sfence_vma <= 1'b1;
|
||||
end else begin
|
||||
if (flush_i) begin
|
||||
current_instruction_is_sfence_vma <= 1'b0;
|
||||
end else if (fu_data_i.operation == SFENCE_VMA && csr_valid_i) begin
|
||||
current_instruction_is_sfence_vma <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// This process stores the rs1 and rs2 parameters of a SFENCE_VMA instruction.
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
asid_to_be_flushed <= '0;
|
||||
vaddr_to_be_flushed <= '0;
|
||||
// if the current instruction in EX_STAGE is a sfence.vma, in the next cycle no writes will happen
|
||||
end else if ((~current_instruction_is_sfence_vma) && (~((fu_data_i.operation == SFENCE_VMA) && csr_valid_i))) begin
|
||||
vaddr_to_be_flushed <= rs1_forwarding_i;
|
||||
asid_to_be_flushed <= rs2_forwarding_i[CVA6Cfg.ASID_WIDTH-1:0];
|
||||
if (CVA6Cfg.RVH) begin
|
||||
// This process stores the rs1 and rs2 parameters of a SFENCE_VMA instruction.
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
vmid_to_be_flushed <= '0;
|
||||
asid_to_be_flushed <= '0;
|
||||
vaddr_to_be_flushed <= '0;
|
||||
gpaddr_to_be_flushed <= '0;
|
||||
// if the current instruction in EX_STAGE is a sfence.vma, in the next cycle no writes will happen
|
||||
end else if ((~(current_instruction_is_sfence_vma || current_instruction_is_hfence_vvma || current_instruction_is_hfence_gvma)) && (~((fu_data_i.operation == SFENCE_VMA || fu_data_i.operation == HFENCE_VVMA || fu_data_i.operation == HFENCE_GVMA ) && csr_valid_i))) begin
|
||||
vaddr_to_be_flushed <= rs1_forwarding_i;
|
||||
gpaddr_to_be_flushed <= {2'b00, rs1_forwarding_i[CVA6Cfg.GPLEN-1:2]};
|
||||
asid_to_be_flushed <= rs2_forwarding_i[CVA6Cfg.ASID_WIDTH-1:0];
|
||||
vmid_to_be_flushed <= rs2_forwarding_i[CVA6Cfg.VMID_WIDTH-1:0];
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
assign vmid_to_be_flushed = '0;
|
||||
assign gpaddr_to_be_flushed = '0;
|
||||
// This process stores the rs1 and rs2 parameters of a SFENCE_VMA instruction.
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
asid_to_be_flushed <= '0;
|
||||
vaddr_to_be_flushed <= '0;
|
||||
// if the current instruction in EX_STAGE is a sfence.vma, in the next cycle no writes will happen
|
||||
end else if ((~current_instruction_is_sfence_vma) && (~((fu_data_i.operation == SFENCE_VMA) && csr_valid_i))) begin
|
||||
vaddr_to_be_flushed <= rs1_forwarding_i;
|
||||
asid_to_be_flushed <= rs2_forwarding_i[CVA6Cfg.ASID_WIDTH-1:0];
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
assign current_instruction_is_sfence_vma = 1'b0;
|
||||
assign asid_to_be_flushed = '0;
|
||||
assign vaddr_to_be_flushed = '0;
|
||||
assign current_instruction_is_sfence_vma = 1'b0;
|
||||
assign current_instruction_is_hfence_vvma = 1'b0;
|
||||
assign current_instruction_is_hfence_gvma = 1'b0;
|
||||
assign asid_to_be_flushed = '0;
|
||||
assign vaddr_to_be_flushed = '0;
|
||||
assign vmid_to_be_flushed = '0;
|
||||
assign gpaddr_to_be_flushed = '0;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -93,6 +93,9 @@ module frontend
|
|||
logic icache_valid_q;
|
||||
ariane_pkg::frontend_exception_t icache_ex_valid_q;
|
||||
logic [ CVA6Cfg.VLEN-1:0] icache_vaddr_q;
|
||||
logic [ CVA6Cfg.GPLEN-1:0] icache_gpaddr_q;
|
||||
logic [ 31:0] icache_tinst_q;
|
||||
logic icache_gva_q;
|
||||
logic instr_queue_ready;
|
||||
logic [CVA6Cfg.INSTR_PER_FETCH-1:0] instr_queue_consumed;
|
||||
// upper-most branch-prediction from last cycle
|
||||
|
@ -165,7 +168,6 @@ module frontend
|
|||
.addr_o (addr),
|
||||
.instr_o (instr)
|
||||
);
|
||||
|
||||
// --------------------
|
||||
// Branch Prediction
|
||||
// --------------------
|
||||
|
@ -418,6 +420,9 @@ module frontend
|
|||
icache_data_q <= '0;
|
||||
icache_valid_q <= 1'b0;
|
||||
icache_vaddr_q <= 'b0;
|
||||
icache_gpaddr_q <= 'b0;
|
||||
icache_tinst_q <= 'b0;
|
||||
icache_gva_q <= 1'b0;
|
||||
icache_ex_valid_q <= ariane_pkg::FE_NONE;
|
||||
btb_q <= '0;
|
||||
bht_q <= '0;
|
||||
|
@ -429,8 +434,20 @@ module frontend
|
|||
if (icache_dreq_i.valid) begin
|
||||
icache_data_q <= icache_data;
|
||||
icache_vaddr_q <= icache_dreq_i.vaddr;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
icache_gpaddr_q <= icache_dreq_i.ex.tval2[CVA6Cfg.GPLEN-1:0];
|
||||
icache_tinst_q <= icache_dreq_i.ex.tinst;
|
||||
icache_gva_q <= icache_dreq_i.ex.gva;
|
||||
end else begin
|
||||
icache_gpaddr_q <= 'b0;
|
||||
icache_tinst_q <= 'b0;
|
||||
icache_gva_q <= 1'b0;
|
||||
end
|
||||
|
||||
// Map the only three exceptions which can occur in the frontend to a two bit enum
|
||||
if (ariane_pkg::MMU_PRESENT && icache_dreq_i.ex.cause == riscv::INSTR_PAGE_FAULT) begin
|
||||
if (ariane_pkg::MMU_PRESENT && icache_dreq_i.ex.cause == riscv::INSTR_GUEST_PAGE_FAULT) begin
|
||||
icache_ex_valid_q <= ariane_pkg::FE_INSTR_GUEST_PAGE_FAULT;
|
||||
end else if (ariane_pkg::MMU_PRESENT && icache_dreq_i.ex.cause == riscv::INSTR_PAGE_FAULT) begin
|
||||
icache_ex_valid_q <= ariane_pkg::FE_INSTR_PAGE_FAULT;
|
||||
end else if (icache_dreq_i.ex.cause == riscv::INSTR_ACCESS_FAULT) begin
|
||||
icache_ex_valid_q <= ariane_pkg::FE_INSTR_ACCESS_FAULT;
|
||||
|
@ -538,6 +555,9 @@ module frontend
|
|||
.addr_i (addr), // from re-aligner
|
||||
.exception_i (icache_ex_valid_q), // from I$
|
||||
.exception_addr_i (icache_vaddr_q),
|
||||
.exception_gpaddr_i (icache_gpaddr_q),
|
||||
.exception_tinst_i (icache_tinst_q),
|
||||
.exception_gva_i (icache_gva_q),
|
||||
.predict_address_i (predict_address),
|
||||
.cf_type_i (cf_type),
|
||||
.valid_i (instruction_valid), // from re-aligner
|
||||
|
|
|
@ -69,6 +69,9 @@ module instr_queue
|
|||
input ariane_pkg::frontend_exception_t exception_i,
|
||||
// Exception address - CACHE
|
||||
input logic [CVA6Cfg.VLEN-1:0] exception_addr_i,
|
||||
input logic [CVA6Cfg.GPLEN-1:0] exception_gpaddr_i,
|
||||
input logic [31:0] exception_tinst_i,
|
||||
input logic exception_gva_i,
|
||||
// Branch predict - FRONTEND
|
||||
input logic [CVA6Cfg.VLEN-1:0] predict_address_i,
|
||||
// Instruction predict address - FRONTEND
|
||||
|
@ -86,10 +89,13 @@ module instr_queue
|
|||
);
|
||||
|
||||
typedef struct packed {
|
||||
logic [31:0] instr; // instruction word
|
||||
ariane_pkg::cf_t cf; // branch was taken
|
||||
ariane_pkg::frontend_exception_t ex; // exception happened
|
||||
logic [CVA6Cfg.VLEN-1:0] ex_vaddr; // lower CVA6Cfg.VLEN bits of tval for exception
|
||||
logic [31:0] instr; // instruction word
|
||||
ariane_pkg::cf_t cf; // branch was taken
|
||||
ariane_pkg::frontend_exception_t ex; // exception happened
|
||||
logic [CVA6Cfg.VLEN-1:0] ex_vaddr; // lower VLEN bits of tval for exception
|
||||
logic [CVA6Cfg.GPLEN-1:0] ex_gpaddr; // lower GPLEN bits of tval2 for exception
|
||||
logic [31:0] ex_tinst; // tinst of exception
|
||||
logic ex_gva;
|
||||
} instr_data_t;
|
||||
|
||||
logic [CVA6Cfg.LOG2_INSTR_PER_FETCH-1:0] branch_index;
|
||||
|
@ -207,6 +213,15 @@ ariane_pkg::FETCH_FIFO_DEPTH
|
|||
assign instr_data_in[i].cf = cf[i+idx_is_q];
|
||||
assign instr_data_in[i].ex = exception_i; // exceptions hold for the whole fetch packet
|
||||
assign instr_data_in[i].ex_vaddr = exception_addr_i;
|
||||
if (CVA6Cfg.RVH) begin : gen_hyp_ex_with_C
|
||||
assign instr_data_in[i].ex_gpaddr = exception_gpaddr_i;
|
||||
assign instr_data_in[i].ex_tinst = exception_tinst_i;
|
||||
assign instr_data_in[i].ex_gva = exception_gva_i;
|
||||
end else begin : gen_no_hyp_ex_with_C
|
||||
assign instr_data_in[i].ex_gpaddr = '0;
|
||||
assign instr_data_in[i].ex_tinst = '0;
|
||||
assign instr_data_in[i].ex_gva = 1'b0;
|
||||
end
|
||||
/* verilator lint_on WIDTH */
|
||||
end
|
||||
end else begin : gen_multiple_instr_per_fetch_without_C
|
||||
|
@ -236,6 +251,15 @@ ariane_pkg::FETCH_FIFO_DEPTH
|
|||
assign instr_data_in[0].cf = cf_type_i[0];
|
||||
assign instr_data_in[0].ex = exception_i; // exceptions hold for the whole fetch packet
|
||||
assign instr_data_in[0].ex_vaddr = exception_addr_i;
|
||||
if (CVA6Cfg.RVH) begin : gen_hyp_ex_without_C
|
||||
assign instr_data_in[0].ex_gpaddr = exception_gpaddr_i;
|
||||
assign instr_data_in[0].ex_tinst = exception_tinst_i;
|
||||
assign instr_data_in[0].ex_gva = exception_gva_i;
|
||||
end else begin : gen_no_hyp_ex_without_C
|
||||
assign instr_data_in[0].ex_gpaddr = '0;
|
||||
assign instr_data_in[0].ex_tinst = '0;
|
||||
assign instr_data_in[0].ex_gva = 1'b0;
|
||||
end
|
||||
/* verilator lint_on WIDTH */
|
||||
end
|
||||
|
||||
|
@ -284,6 +308,9 @@ ariane_pkg::FETCH_FIFO_DEPTH
|
|||
fetch_entry_o.ex.cause = '0;
|
||||
|
||||
fetch_entry_o.ex.tval = '0;
|
||||
fetch_entry_o.ex.tval2 = '0;
|
||||
fetch_entry_o.ex.gva = 1'b0;
|
||||
fetch_entry_o.ex.tinst = '0;
|
||||
fetch_entry_o.branch_predict.predict_address = address_out;
|
||||
fetch_entry_o.branch_predict.cf = ariane_pkg::NoCF;
|
||||
// output mux select
|
||||
|
@ -291,6 +318,8 @@ ariane_pkg::FETCH_FIFO_DEPTH
|
|||
if (idx_ds_q[i]) begin
|
||||
if (instr_data_out[i].ex == ariane_pkg::FE_INSTR_ACCESS_FAULT) begin
|
||||
fetch_entry_o.ex.cause = riscv::INSTR_ACCESS_FAULT;
|
||||
end else if (CVA6Cfg.RVH && instr_data_out[i].ex == ariane_pkg::FE_INSTR_GUEST_PAGE_FAULT) begin
|
||||
fetch_entry_o.ex.cause = riscv::INSTR_GUEST_PAGE_FAULT;
|
||||
end else begin
|
||||
fetch_entry_o.ex.cause = riscv::INSTR_PAGE_FAULT;
|
||||
end
|
||||
|
@ -300,6 +329,11 @@ ariane_pkg::FETCH_FIFO_DEPTH
|
|||
fetch_entry_o.ex.tval = {
|
||||
{(CVA6Cfg.XLEN - CVA6Cfg.VLEN) {1'b0}}, instr_data_out[i].ex_vaddr
|
||||
};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
fetch_entry_o.ex.tval2 = instr_data_out[i].ex_gpaddr;
|
||||
fetch_entry_o.ex.tinst = instr_data_out[i].ex_tinst;
|
||||
fetch_entry_o.ex.gva = instr_data_out[i].ex_gva;
|
||||
end
|
||||
fetch_entry_o.branch_predict.cf = instr_data_out[i].cf;
|
||||
pop_instr[i] = fetch_entry_valid_o & fetch_entry_ready_i;
|
||||
end
|
||||
|
@ -325,6 +359,16 @@ ariane_pkg::FETCH_FIFO_DEPTH
|
|||
if (CVA6Cfg.TvalEn)
|
||||
fetch_entry_o.ex.tval = {{64 - CVA6Cfg.VLEN{1'b0}}, instr_data_out[0].ex_vaddr};
|
||||
else fetch_entry_o.ex.tval = '0;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
fetch_entry_o.ex.tval2 = instr_data_out[0].ex_gpaddr;
|
||||
fetch_entry_o.ex.tinst = instr_data_out[0].ex_tinst;
|
||||
fetch_entry_o.ex.gva = instr_data_out[0].ex_gva;
|
||||
end else begin
|
||||
fetch_entry_o.ex.tval2 = '0;
|
||||
fetch_entry_o.ex.tinst = '0;
|
||||
fetch_entry_o.ex.gva = 1'b0;
|
||||
end
|
||||
|
||||
fetch_entry_o.branch_predict.predict_address = address_out;
|
||||
fetch_entry_o.branch_predict.cf = instr_data_out[0].cf;
|
||||
|
||||
|
|
|
@ -51,8 +51,12 @@ module id_stage #(
|
|||
output logic rvfi_is_compressed_o,
|
||||
// Current privilege level - CSR_REGFILE
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
// Current virtualization mode - CSR_REGFILE
|
||||
input logic v_i,
|
||||
// Floating point extension status - CSR_REGFILE
|
||||
input riscv::xs_t fs_i,
|
||||
// Floating point extension virtual status - CSR_REGFILE
|
||||
input riscv::xs_t vfs_i,
|
||||
// Floating point dynamic rounding mode - CSR_REGFILE
|
||||
input logic [2:0] frm_i,
|
||||
// Vector extension status - CSR_REGFILE
|
||||
|
@ -67,8 +71,12 @@ module id_stage #(
|
|||
input logic tvm_i,
|
||||
// Timeout wait - CSR_REGFILE
|
||||
input logic tw_i,
|
||||
// Virtual timeout wait - CSR_REGFILE
|
||||
input logic vtw_i,
|
||||
// Trap sret - CSR_REGFILE
|
||||
input logic tsr_i
|
||||
input logic tsr_i,
|
||||
// Hypervisor user mode - CSR_REGFILE
|
||||
input logic hu_i
|
||||
);
|
||||
// ID/ISSUE register stage
|
||||
typedef struct packed {
|
||||
|
@ -169,13 +177,17 @@ module id_stage #(
|
|||
.branch_predict_i (fetch_entry_i.branch_predict),
|
||||
.ex_i (fetch_entry_i.ex),
|
||||
.priv_lvl_i (priv_lvl_i),
|
||||
.v_i (v_i),
|
||||
.debug_mode_i (debug_mode_i),
|
||||
.fs_i,
|
||||
.vfs_i,
|
||||
.frm_i,
|
||||
.vs_i,
|
||||
.tvm_i,
|
||||
.tw_i,
|
||||
.vtw_i,
|
||||
.tsr_i,
|
||||
.hu_i,
|
||||
.instruction_o (decoded_instruction),
|
||||
.orig_instr_o (orig_instr),
|
||||
.is_control_flow_instr_o (is_control_flow_instr)
|
||||
|
|
|
@ -135,6 +135,26 @@ package ariane_pkg;
|
|||
| riscv::SSTATUS_FS
|
||||
| riscv::SSTATUS_SUM
|
||||
| riscv::SSTATUS_MXR;
|
||||
|
||||
localparam logic [63:0] HSTATUS_WRITE_MASK = riscv::HSTATUS_VSBE
|
||||
| riscv::HSTATUS_GVA
|
||||
| riscv::HSTATUS_SPV
|
||||
| riscv::HSTATUS_SPVP
|
||||
| riscv::HSTATUS_HU
|
||||
| riscv::HSTATUS_VTVM
|
||||
| riscv::HSTATUS_VTW
|
||||
| riscv::HSTATUS_VTSR;
|
||||
|
||||
// hypervisor delegable interrupts
|
||||
function automatic logic [31:0] hs_deleg_interrupts(config_pkg::cva6_cfg_t Cfg);
|
||||
return riscv::MIP_VSSIP | riscv::MIP_VSTIP | riscv::MIP_VSEIP;
|
||||
endfunction
|
||||
|
||||
// virtual supervisor delegable interrupts
|
||||
function automatic logic [31:0] vs_deleg_interrupts(config_pkg::cva6_cfg_t Cfg);
|
||||
return riscv::MIP_VSSIP | riscv::MIP_VSTIP | riscv::MIP_VSEIP;
|
||||
endfunction
|
||||
|
||||
// ---------------
|
||||
// AXI
|
||||
// ---------------
|
||||
|
@ -286,6 +306,8 @@ package ariane_pkg;
|
|||
FENCE,
|
||||
FENCE_I,
|
||||
SFENCE_VMA,
|
||||
HFENCE_VVMA,
|
||||
HFENCE_GVMA,
|
||||
CSR_WRITE,
|
||||
CSR_READ,
|
||||
CSR_SET,
|
||||
|
@ -302,6 +324,20 @@ package ariane_pkg;
|
|||
LB,
|
||||
SB,
|
||||
LBU,
|
||||
// Hypervisor Virtual-Machine Load and Store Instructions
|
||||
HLV_B,
|
||||
HLV_BU,
|
||||
HLV_H,
|
||||
HLV_HU,
|
||||
HLVX_HU,
|
||||
HLV_W,
|
||||
HLVX_WU,
|
||||
HSV_B,
|
||||
HSV_H,
|
||||
HSV_W,
|
||||
HLV_WU,
|
||||
HLV_D,
|
||||
HSV_D,
|
||||
// Atomic Memory Operations
|
||||
AMO_LRW,
|
||||
AMO_LRD,
|
||||
|
@ -587,7 +623,8 @@ package ariane_pkg;
|
|||
typedef enum logic [1:0] {
|
||||
FE_NONE,
|
||||
FE_INSTR_ACCESS_FAULT,
|
||||
FE_INSTR_PAGE_FAULT
|
||||
FE_INSTR_PAGE_FAULT,
|
||||
FE_INSTR_GUEST_PAGE_FAULT
|
||||
} frontend_exception_t;
|
||||
|
||||
// AMO request going to cache. this request is unconditionally valid as soon
|
||||
|
@ -695,7 +732,7 @@ package ariane_pkg;
|
|||
// ----------------------
|
||||
function automatic logic [1:0] extract_transfer_size(fu_op op);
|
||||
case (op)
|
||||
LD, SD, FLD, FSD,
|
||||
LD, HLV_D, SD, HSV_D, FLD, FSD,
|
||||
AMO_LRD, AMO_SCD,
|
||||
AMO_SWAPD, AMO_ADDD,
|
||||
AMO_ANDD, AMO_ORD,
|
||||
|
@ -704,7 +741,8 @@ package ariane_pkg;
|
|||
AMO_MINDU: begin
|
||||
return 2'b11;
|
||||
end
|
||||
LW, LWU, SW, FLW, FSW,
|
||||
LW, LWU, HLV_W, HLV_WU, HLVX_WU,
|
||||
SW, HSV_W, FLW, FSW,
|
||||
AMO_LRW, AMO_SCW,
|
||||
AMO_SWAPW, AMO_ADDW,
|
||||
AMO_ANDW, AMO_ORW,
|
||||
|
@ -713,9 +751,60 @@ package ariane_pkg;
|
|||
AMO_MINWU: begin
|
||||
return 2'b10;
|
||||
end
|
||||
LH, LHU, SH, FLH, FSH: return 2'b01;
|
||||
LB, LBU, SB, FLB, FSB: return 2'b00;
|
||||
default: return 2'b11;
|
||||
LH, LHU, HLV_H, HLV_HU, HLVX_HU, SH, HSV_H, FLH, FSH: return 2'b01;
|
||||
LB, LBU, HLV_B, HLV_BU, SB, HSV_B, FLB, FSB: return 2'b00;
|
||||
default: return 2'b11;
|
||||
endcase
|
||||
endfunction
|
||||
// ----------------------
|
||||
// MMU Functions
|
||||
// ----------------------
|
||||
|
||||
// checks if final translation page size is 1G when H-extension is enabled
|
||||
function automatic logic is_trans_1G(input logic s_st_enbl, input logic g_st_enbl,
|
||||
input logic is_s_1G, input logic is_g_1G);
|
||||
return (((is_s_1G && s_st_enbl) || !s_st_enbl) && ((is_g_1G && g_st_enbl) || !g_st_enbl));
|
||||
endfunction : is_trans_1G
|
||||
|
||||
// checks if final translation page size is 2M when H-extension is enabled
|
||||
function automatic logic is_trans_2M(input logic s_st_enbl, input logic g_st_enbl,
|
||||
input logic is_s_1G, input logic is_s_2M,
|
||||
input logic is_g_1G, input logic is_g_2M);
|
||||
return (s_st_enbl && g_st_enbl) ?
|
||||
((is_s_2M && (is_g_1G || is_g_2M)) || (is_g_2M && (is_s_1G || is_s_2M))) :
|
||||
((is_s_2M && s_st_enbl) || (is_g_2M && g_st_enbl));
|
||||
endfunction : is_trans_2M
|
||||
|
||||
// computes the paddr based on the page size, ppn and offset
|
||||
function automatic logic [40:0] make_gpaddr(input logic s_st_enbl, input logic is_1G,
|
||||
input logic is_2M, input logic [63:0] vaddr,
|
||||
input riscv::pte_t pte);
|
||||
logic [40:0] gpaddr;
|
||||
if (s_st_enbl) begin
|
||||
gpaddr = {pte.ppn[28:0], vaddr[11:0]};
|
||||
// Giga page
|
||||
if (is_1G) gpaddr[29:12] = vaddr[29:12];
|
||||
// Mega page
|
||||
if (is_2M) gpaddr[20:12] = vaddr[20:12];
|
||||
end else begin
|
||||
gpaddr = vaddr[40:0];
|
||||
end
|
||||
return gpaddr;
|
||||
endfunction : make_gpaddr
|
||||
|
||||
// computes the final gppn based on the guest physical address
|
||||
function automatic logic [28:0] make_gppn(input logic s_st_enbl, input logic is_1G,
|
||||
input logic is_2M, input logic [28:0] vpn,
|
||||
input riscv::pte_t pte);
|
||||
logic [28:0] gppn;
|
||||
if (s_st_enbl) begin
|
||||
gppn = pte.ppn[28:0];
|
||||
if (is_2M) gppn[8:0] = vpn[8:0];
|
||||
if (is_1G) gppn[17:0] = vpn[17:0];
|
||||
end else begin
|
||||
gppn = vpn;
|
||||
end
|
||||
return gppn;
|
||||
endfunction : make_gppn
|
||||
|
||||
endpackage
|
||||
|
|
|
@ -32,10 +32,12 @@ package build_config_pkg;
|
|||
cfg.XLEN = CVA6Cfg.XLEN;
|
||||
cfg.VLEN = (CVA6Cfg.XLEN == 32) ? 32 : 64;
|
||||
cfg.PLEN = (CVA6Cfg.XLEN == 32) ? 34 : 56;
|
||||
cfg.GPLEN = (CVA6Cfg.XLEN == 32) ? 34 : 41;
|
||||
cfg.IS_XLEN32 = IS_XLEN32;
|
||||
cfg.IS_XLEN64 = IS_XLEN64;
|
||||
cfg.XLEN_ALIGN_BYTES = $clog2(CVA6Cfg.XLEN / 8);
|
||||
cfg.ASID_WIDTH = (CVA6Cfg.XLEN == 64) ? 16 : 1;
|
||||
cfg.VMID_WIDTH = (CVA6Cfg.XLEN == 64) ? 14 : 1;
|
||||
|
||||
cfg.FPGA_EN = CVA6Cfg.FPGA_EN;
|
||||
cfg.NrCommitPorts = CVA6Cfg.NrCommitPorts;
|
||||
|
@ -53,6 +55,7 @@ package build_config_pkg;
|
|||
cfg.RVB = CVA6Cfg.RVB;
|
||||
cfg.RVV = CVA6Cfg.RVV;
|
||||
cfg.RVC = CVA6Cfg.RVC;
|
||||
cfg.RVH = CVA6Cfg.RVH;
|
||||
cfg.RVZCB = CVA6Cfg.RVZCB;
|
||||
cfg.RVZCMP = CVA6Cfg.RVZCMP;
|
||||
cfg.XFVec = CVA6Cfg.XFVec;
|
||||
|
@ -131,9 +134,12 @@ package build_config_pkg;
|
|||
|
||||
cfg.ModeW = (CVA6Cfg.XLEN == 32) ? 1 : 4;
|
||||
cfg.ASIDW = (CVA6Cfg.XLEN == 32) ? 9 : 16;
|
||||
cfg.VMIDW = (CVA6Cfg.XLEN == 32) ? 7 : 14;
|
||||
cfg.PPNW = (CVA6Cfg.XLEN == 32) ? 22 : 44;
|
||||
cfg.GPPNW = (CVA6Cfg.XLEN == 32) ? 22 : 29;
|
||||
cfg.MODE_SV = (CVA6Cfg.XLEN == 32) ? config_pkg::ModeSv32 : config_pkg::ModeSv39;
|
||||
cfg.SV = (cfg.MODE_SV == config_pkg::ModeSv32) ? 32 : 39;
|
||||
cfg.SVX = (cfg.MODE_SV == config_pkg::ModeSv32) ? 34 : 41;
|
||||
|
||||
return cfg;
|
||||
endfunction
|
||||
|
|
|
@ -80,6 +80,8 @@ package config_pkg;
|
|||
bit RVV;
|
||||
// Compress RISC-V extension
|
||||
bit RVC;
|
||||
// Hypervisor RISC-V extension
|
||||
bit RVH;
|
||||
// Zcb RISC-V extension
|
||||
bit RVZCB;
|
||||
// Zcmp RISC-V extension
|
||||
|
@ -168,10 +170,12 @@ package config_pkg;
|
|||
int unsigned XLEN;
|
||||
int unsigned VLEN;
|
||||
int unsigned PLEN;
|
||||
int unsigned GPLEN;
|
||||
bit IS_XLEN32;
|
||||
bit IS_XLEN64;
|
||||
int unsigned XLEN_ALIGN_BYTES;
|
||||
int unsigned ASID_WIDTH;
|
||||
int unsigned VMID_WIDTH;
|
||||
|
||||
bit FPGA_EN;
|
||||
/// Number of commit ports, i.e., maximum number of instructions that the
|
||||
|
@ -194,6 +198,7 @@ package config_pkg;
|
|||
bit RVB;
|
||||
bit RVV;
|
||||
bit RVC;
|
||||
bit RVH;
|
||||
bit RVZCB;
|
||||
bit RVZCMP;
|
||||
bit XFVec;
|
||||
|
@ -273,9 +278,12 @@ package config_pkg;
|
|||
|
||||
int unsigned ModeW;
|
||||
int unsigned ASIDW;
|
||||
int unsigned VMIDW;
|
||||
int unsigned PPNW;
|
||||
int unsigned GPPNW;
|
||||
vm_mode_t MODE_SV;
|
||||
int unsigned SV;
|
||||
int unsigned SVX;
|
||||
} cva6_cfg_t;
|
||||
|
||||
/// Empty configuration to sanity check proper parameter passing. Whenever
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 1;
|
||||
localparam CVA6ConfigZcmpExtEn = 1;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 1;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -22,6 +22,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 1;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -92,6 +93,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -22,6 +22,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 1;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -92,6 +93,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 0;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 0;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 0;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 0;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -24,6 +24,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 1;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 1;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 1;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -32,6 +32,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 1;
|
||||
|
||||
localparam CVA6ConfigAxiIdWidth = 4;
|
||||
|
@ -100,6 +101,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -23,6 +23,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcbExtEn = 0;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 0; // always disabled
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -25,6 +25,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 1;
|
||||
|
||||
localparam CVA6ConfigAxiIdWidth = 4;
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
141
core/include/cv64a6_imafdch_sv39_config_pkg.sv
Normal file
141
core/include/cv64a6_imafdch_sv39_config_pkg.sv
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2021 Thales DIS design services SAS
|
||||
//
|
||||
// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0
|
||||
// You may obtain a copy of the License at https://solderpad.org/licenses/
|
||||
//
|
||||
// Original Author: Jean-Roch COULON - Thales
|
||||
|
||||
|
||||
package cva6_config_pkg;
|
||||
|
||||
localparam CVA6ConfigXlen = 64;
|
||||
|
||||
localparam CVA6ConfigFpuEn = 1;
|
||||
localparam CVA6ConfigF16En = 0;
|
||||
localparam CVA6ConfigF16AltEn = 0;
|
||||
localparam CVA6ConfigF8En = 0;
|
||||
localparam CVA6ConfigFVecEn = 0;
|
||||
|
||||
localparam CVA6ConfigCvxifEn = 1;
|
||||
localparam CVA6ConfigCExtEn = 1;
|
||||
localparam CVA6ConfigZcbExtEn = 1;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigHExtEn = 1;
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigZiCondExtEn = 1;
|
||||
|
||||
localparam CVA6ConfigAxiIdWidth = 4;
|
||||
localparam CVA6ConfigAxiAddrWidth = 64;
|
||||
localparam CVA6ConfigAxiDataWidth = 64;
|
||||
localparam CVA6ConfigFetchUserEn = 0;
|
||||
localparam CVA6ConfigFetchUserWidth = CVA6ConfigXlen;
|
||||
localparam CVA6ConfigDataUserEn = 0;
|
||||
localparam CVA6ConfigDataUserWidth = CVA6ConfigXlen;
|
||||
|
||||
localparam CVA6ConfigIcacheByteSize = 16384;
|
||||
localparam CVA6ConfigIcacheSetAssoc = 4;
|
||||
localparam CVA6ConfigIcacheLineWidth = 128;
|
||||
localparam CVA6ConfigDcacheByteSize = 32768;
|
||||
localparam CVA6ConfigDcacheSetAssoc = 8;
|
||||
localparam CVA6ConfigDcacheLineWidth = 128;
|
||||
|
||||
localparam CVA6ConfigDcacheIdWidth = 1;
|
||||
localparam CVA6ConfigMemTidWidth = 2;
|
||||
|
||||
localparam CVA6ConfigWtDcacheWbufDepth = 8;
|
||||
|
||||
localparam CVA6ConfigNrCommitPorts = 2;
|
||||
localparam CVA6ConfigNrScoreboardEntries = 8;
|
||||
|
||||
localparam CVA6ConfigFPGAEn = 0;
|
||||
|
||||
localparam CVA6ConfigNrLoadPipeRegs = 1;
|
||||
localparam CVA6ConfigNrStorePipeRegs = 0;
|
||||
localparam CVA6ConfigNrLoadBufEntries = 2;
|
||||
|
||||
localparam CVA6ConfigInstrTlbEntries = 16;
|
||||
localparam CVA6ConfigDataTlbEntries = 16;
|
||||
|
||||
localparam CVA6ConfigRASDepth = 2;
|
||||
localparam CVA6ConfigBTBEntries = 32;
|
||||
localparam CVA6ConfigBHTEntries = 128;
|
||||
|
||||
localparam CVA6ConfigTvalEn = 1;
|
||||
|
||||
localparam CVA6ConfigNrPMPEntries = 8;
|
||||
|
||||
localparam CVA6ConfigPerfCounterEn = 1;
|
||||
|
||||
localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::WT;
|
||||
|
||||
localparam CVA6ConfigMmuPresent = 1;
|
||||
|
||||
localparam CVA6ConfigRvfiTrace = 1;
|
||||
|
||||
localparam config_pkg::cva6_user_cfg_t cva6_cfg = '{
|
||||
NrCommitPorts: unsigned'(CVA6ConfigNrCommitPorts),
|
||||
AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth),
|
||||
AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth),
|
||||
AxiIdWidth: unsigned'(CVA6ConfigAxiIdWidth),
|
||||
AxiUserWidth: unsigned'(CVA6ConfigDataUserWidth),
|
||||
MemTidWidth: unsigned'(CVA6ConfigMemTidWidth),
|
||||
NrLoadBufEntries: unsigned'(CVA6ConfigNrLoadBufEntries),
|
||||
FpuEn: bit'(CVA6ConfigFpuEn),
|
||||
XF16: bit'(CVA6ConfigF16En),
|
||||
XF16ALT: bit'(CVA6ConfigF16AltEn),
|
||||
XF8: bit'(CVA6ConfigF8En),
|
||||
RVA: bit'(CVA6ConfigAExtEn),
|
||||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
CvxifEn: bit'(CVA6ConfigCvxifEn),
|
||||
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
|
||||
RVS: bit'(1),
|
||||
RVU: bit'(1),
|
||||
HaltAddress: 64'h800,
|
||||
ExceptionAddress: 64'h808,
|
||||
RASDepth: unsigned'(CVA6ConfigRASDepth),
|
||||
BTBEntries: unsigned'(CVA6ConfigBTBEntries),
|
||||
BHTEntries: unsigned'(CVA6ConfigBHTEntries),
|
||||
DmBaseAddress: 64'h0,
|
||||
TvalEn: bit'(CVA6ConfigTvalEn),
|
||||
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
|
||||
PMPCfgRstVal: {16{64'h0}},
|
||||
PMPAddrRstVal: {16{64'h0}},
|
||||
PMPEntryReadOnly: 16'd0,
|
||||
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
|
||||
// idempotent region
|
||||
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}
|
||||
),
|
||||
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
|
||||
// cached region
|
||||
NrCachedRegionRules:
|
||||
unsigned'(
|
||||
1
|
||||
),
|
||||
CachedRegionAddrBase: 1024'({64'h8000_0000}),
|
||||
CachedRegionLength: 1024'({64'h40000000}),
|
||||
MaxOutstandingStores: unsigned'(7),
|
||||
DebugEn: bit'(1),
|
||||
AxiBurstWriteEn: bit'(0)
|
||||
};
|
||||
|
||||
endpackage
|
141
core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv
Normal file
141
core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2021 Thales DIS design services SAS
|
||||
//
|
||||
// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0
|
||||
// You may obtain a copy of the License at https://solderpad.org/licenses/
|
||||
//
|
||||
// Original Author: Jean-Roch COULON - Thales
|
||||
|
||||
|
||||
package cva6_config_pkg;
|
||||
|
||||
localparam CVA6ConfigXlen = 64;
|
||||
|
||||
localparam CVA6ConfigFpuEn = 1;
|
||||
localparam CVA6ConfigF16En = 0;
|
||||
localparam CVA6ConfigF16AltEn = 0;
|
||||
localparam CVA6ConfigF8En = 0;
|
||||
localparam CVA6ConfigFVecEn = 0;
|
||||
|
||||
localparam CVA6ConfigCvxifEn = 1;
|
||||
localparam CVA6ConfigCExtEn = 1;
|
||||
localparam CVA6ConfigZcbExtEn = 1;
|
||||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigBExtEn = 1;
|
||||
localparam CVA6ConfigVExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 1;
|
||||
localparam CVA6ConfigZiCondExtEn = 1;
|
||||
|
||||
localparam CVA6ConfigAxiIdWidth = 4;
|
||||
localparam CVA6ConfigAxiAddrWidth = 64;
|
||||
localparam CVA6ConfigAxiDataWidth = 64;
|
||||
localparam CVA6ConfigFetchUserEn = 0;
|
||||
localparam CVA6ConfigFetchUserWidth = CVA6ConfigXlen;
|
||||
localparam CVA6ConfigDataUserEn = 0;
|
||||
localparam CVA6ConfigDataUserWidth = CVA6ConfigXlen;
|
||||
|
||||
localparam CVA6ConfigIcacheByteSize = 16384;
|
||||
localparam CVA6ConfigIcacheSetAssoc = 4;
|
||||
localparam CVA6ConfigIcacheLineWidth = 128;
|
||||
localparam CVA6ConfigDcacheByteSize = 32768;
|
||||
localparam CVA6ConfigDcacheSetAssoc = 8;
|
||||
localparam CVA6ConfigDcacheLineWidth = 128;
|
||||
|
||||
localparam CVA6ConfigDcacheIdWidth = 1;
|
||||
localparam CVA6ConfigMemTidWidth = 2;
|
||||
|
||||
localparam CVA6ConfigWtDcacheWbufDepth = 8;
|
||||
|
||||
localparam CVA6ConfigNrCommitPorts = 2;
|
||||
localparam CVA6ConfigNrScoreboardEntries = 8;
|
||||
|
||||
localparam CVA6ConfigFPGAEn = 0;
|
||||
|
||||
localparam CVA6ConfigNrLoadPipeRegs = 1;
|
||||
localparam CVA6ConfigNrStorePipeRegs = 0;
|
||||
localparam CVA6ConfigNrLoadBufEntries = 2;
|
||||
|
||||
localparam CVA6ConfigInstrTlbEntries = 16;
|
||||
localparam CVA6ConfigDataTlbEntries = 16;
|
||||
|
||||
localparam CVA6ConfigRASDepth = 2;
|
||||
localparam CVA6ConfigBTBEntries = 32;
|
||||
localparam CVA6ConfigBHTEntries = 128;
|
||||
|
||||
localparam CVA6ConfigTvalEn = 1;
|
||||
|
||||
localparam CVA6ConfigNrPMPEntries = 8;
|
||||
|
||||
localparam CVA6ConfigPerfCounterEn = 1;
|
||||
|
||||
localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::WB;
|
||||
|
||||
localparam CVA6ConfigMmuPresent = 1;
|
||||
|
||||
localparam CVA6ConfigRvfiTrace = 1;
|
||||
|
||||
localparam config_pkg::cva6_user_cfg_t cva6_cfg = '{
|
||||
NrCommitPorts: unsigned'(CVA6ConfigNrCommitPorts),
|
||||
AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth),
|
||||
AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth),
|
||||
AxiIdWidth: unsigned'(CVA6ConfigAxiIdWidth),
|
||||
AxiUserWidth: unsigned'(CVA6ConfigDataUserWidth),
|
||||
MemTidWidth: unsigned'(CVA6ConfigMemTidWidth),
|
||||
NrLoadBufEntries: unsigned'(CVA6ConfigNrLoadBufEntries),
|
||||
FpuEn: bit'(CVA6ConfigFpuEn),
|
||||
XF16: bit'(CVA6ConfigF16En),
|
||||
XF16ALT: bit'(CVA6ConfigF16AltEn),
|
||||
XF8: bit'(CVA6ConfigF8En),
|
||||
RVA: bit'(CVA6ConfigAExtEn),
|
||||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
CvxifEn: bit'(CVA6ConfigCvxifEn),
|
||||
ZiCondExtEn: bit'(CVA6ConfigZiCondExtEn),
|
||||
RVS: bit'(1),
|
||||
RVU: bit'(1),
|
||||
HaltAddress: 64'h800,
|
||||
ExceptionAddress: 64'h808,
|
||||
RASDepth: unsigned'(CVA6ConfigRASDepth),
|
||||
BTBEntries: unsigned'(CVA6ConfigBTBEntries),
|
||||
BHTEntries: unsigned'(CVA6ConfigBHTEntries),
|
||||
DmBaseAddress: 64'h0,
|
||||
TvalEn: bit'(CVA6ConfigTvalEn),
|
||||
NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries),
|
||||
PMPCfgRstVal: {16{64'h0}},
|
||||
PMPAddrRstVal: {16{64'h0}},
|
||||
PMPEntryReadOnly: 16'd0,
|
||||
NOCType: config_pkg::NOC_TYPE_AXI4_ATOP,
|
||||
// idempotent region
|
||||
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}
|
||||
),
|
||||
ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}),
|
||||
// cached region
|
||||
NrCachedRegionRules:
|
||||
unsigned'(
|
||||
1
|
||||
),
|
||||
CachedRegionAddrBase: 1024'({64'h8000_0000}),
|
||||
CachedRegionLength: 1024'({64'h40000000}),
|
||||
MaxOutstandingStores: unsigned'(7),
|
||||
DebugEn: bit'(1),
|
||||
AxiBurstWriteEn: bit'(0)
|
||||
};
|
||||
|
||||
endpackage
|
|
@ -24,6 +24,7 @@ package cva6_config_pkg;
|
|||
localparam CVA6ConfigZcmpExtEn = 0;
|
||||
localparam CVA6ConfigAExtEn = 1;
|
||||
localparam CVA6ConfigBExtEn = 0;
|
||||
localparam CVA6ConfigHExtEn = 0;
|
||||
localparam CVA6ConfigVExtEn = 1;
|
||||
localparam CVA6ConfigZiCondExtEn = 0;
|
||||
|
||||
|
@ -93,6 +94,7 @@ package cva6_config_pkg;
|
|||
RVB: bit'(CVA6ConfigBExtEn),
|
||||
RVV: bit'(CVA6ConfigVExtEn),
|
||||
RVC: bit'(CVA6ConfigCExtEn),
|
||||
RVH: bit'(CVA6ConfigHExtEn),
|
||||
RVZCB: bit'(CVA6ConfigZcbExtEn),
|
||||
RVZCMP: bit'(CVA6ConfigZcmpExtEn),
|
||||
XFVec: bit'(CVA6ConfigFVecEn),
|
||||
|
|
|
@ -30,9 +30,10 @@ package riscv;
|
|||
// Privilege Spec
|
||||
// --------------------
|
||||
typedef enum logic [1:0] {
|
||||
PRIV_LVL_M = 2'b11,
|
||||
PRIV_LVL_S = 2'b01,
|
||||
PRIV_LVL_U = 2'b00
|
||||
PRIV_LVL_M = 2'b11,
|
||||
PRIV_LVL_HS = 2'b10,
|
||||
PRIV_LVL_S = 2'b01,
|
||||
PRIV_LVL_U = 2'b00
|
||||
} priv_lvl_t;
|
||||
|
||||
// type which holds xlen
|
||||
|
@ -53,7 +54,7 @@ package riscv;
|
|||
logic sd; // signal dirty state - read-only
|
||||
logic [62:34] wpri6; // writes preserved reads ignored
|
||||
xlen_e uxl; // variable user mode xlen - hardwired to zero
|
||||
logic [12:0] wpri5; // writes preserved reads ignored
|
||||
logic [11:0] wpri5; // writes preserved reads ignored
|
||||
logic mxr; // make executable readable
|
||||
logic sum; // permit supervisor user memory access
|
||||
logic wpri4; // writes preserved reads ignored
|
||||
|
@ -63,16 +64,39 @@ package riscv;
|
|||
xs_t vs; // vector extension register
|
||||
logic spp; // holds the previous privilege mode up to supervisor
|
||||
logic wpri2; // writes preserved reads ignored
|
||||
logic mpie; // machine interrupts enable bit active prior to trap
|
||||
logic ube; // UBE controls whether explicit load and store memory accesses made from U-mode are little-endian (UBE=0) or big-endian (UBE=1)
|
||||
logic spie; // supervisor interrupts enable bit active prior to trap
|
||||
logic [1:0] wpri1; // writes preserved reads ignored
|
||||
logic [2:0] wpri1; // writes preserved reads ignored
|
||||
logic sie; // supervisor interrupts enable
|
||||
logic wpri0; // writes preserved reads ignored
|
||||
} sstatus_rv_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [63:34] wpri4; // writes preserved reads ignored
|
||||
xlen_e vsxl; // variable virtual supervisor mode xlen - hardwired to zero
|
||||
logic [8:0] wpri3; // floating point extension register
|
||||
logic vtsr; // virtual trap sret
|
||||
logic vtw; // virtual time wait
|
||||
logic vtvm; // virtual trap virtual memory
|
||||
logic [1:0] wpri2; // writes preserved reads ignored
|
||||
logic [5:0] vgein; // virtual guest external interrupt number
|
||||
logic [1:0] wpri1; // writes preserved reads ignored
|
||||
logic hu; // virtual-machine load/store instructions enable in U-mode
|
||||
logic spvp; // supervisor previous virtual privilege
|
||||
logic spv; // supervisor previous virtualization mode
|
||||
logic gva; // variable set when trap writes to stval
|
||||
logic vsbe; // endianness of explicit memory accesses made from VS-mode
|
||||
logic [4:0] wpri0; // writes preserved reads ignored
|
||||
} hstatus_rv_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic sd; // signal dirty state - read-only
|
||||
logic [62:36] wpri4; // writes preserved reads ignored
|
||||
logic [62:40] wpri4; // writes preserved reads ignored
|
||||
logic mpv; // machine previous virtualization mode
|
||||
logic gva; // variable set when trap writes to stval
|
||||
logic mbe; // endianness memory accesses made from M-mode
|
||||
logic sbe; // endianness memory accesses made from S-mode
|
||||
xlen_e sxl; // variable supervisor mode xlen - hardwired to zero
|
||||
xlen_e uxl; // variable user mode xlen - hardwired to zero
|
||||
logic [8:0] wpri3; // writes preserved reads ignored
|
||||
|
@ -97,6 +121,17 @@ package riscv;
|
|||
logic wpri0; // writes preserved reads ignored
|
||||
} mstatus_rv_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic stce; // not implemented - requires Sctc extension
|
||||
logic pbmte; // not implemented - requires Svpbmt extension
|
||||
logic [61:8] wpri1; // writes preserved reads ignored
|
||||
logic cbze; // not implemented - requires Zicboz extension
|
||||
logic cbcfe; // not implemented - requires Zicbom extension
|
||||
logic [1:0] cbie; // not implemented - requires Zicbom extension
|
||||
logic [2:0] wpri0; // writes preserved reads ignored
|
||||
logic fiom; // fence of I/O implies memory
|
||||
} envcfg_rv_t;
|
||||
|
||||
// --------------------
|
||||
// Instruction Types
|
||||
// --------------------
|
||||
|
@ -300,27 +335,48 @@ package riscv;
|
|||
localparam logic [XLEN-1:0] LD_ACCESS_FAULT = 5; // Illegal access as governed by PMPs and PMAs
|
||||
localparam logic [XLEN-1:0] ST_ADDR_MISALIGNED = 6;
|
||||
localparam logic [XLEN-1:0] ST_ACCESS_FAULT = 7; // Illegal access as governed by PMPs and PMAs
|
||||
localparam logic [XLEN-1:0] ENV_CALL_UMODE = 8; // environment call from user mode
|
||||
localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from supervisor mode
|
||||
localparam logic [XLEN-1:0] ENV_CALL_UMODE = 8; // environment call from user mode or virtual user mode
|
||||
localparam logic [XLEN-1:0] ENV_CALL_SMODE = 9; // environment call from hypervisor-extended supervisor mode
|
||||
localparam logic [XLEN-1:0] ENV_CALL_VSMODE = 10; // environment call from virtual supervisor mode
|
||||
localparam logic [XLEN-1:0] ENV_CALL_MMODE = 11; // environment call from machine mode
|
||||
localparam logic [XLEN-1:0] INSTR_PAGE_FAULT = 12; // Instruction page fault
|
||||
localparam logic [XLEN-1:0] LOAD_PAGE_FAULT = 13; // Load page fault
|
||||
localparam logic [XLEN-1:0] STORE_PAGE_FAULT = 15; // Store page fault
|
||||
localparam logic [XLEN-1:0] INSTR_GUEST_PAGE_FAULT = 20; // Instruction guest-page fault
|
||||
localparam logic [XLEN-1:0] LOAD_GUEST_PAGE_FAULT = 21; // Load guest-page fault
|
||||
localparam logic [XLEN-1:0] VIRTUAL_INSTRUCTION = 22; // virtual instruction
|
||||
localparam logic [XLEN-1:0] STORE_GUEST_PAGE_FAULT = 23; // Store guest-page fault
|
||||
localparam logic [XLEN-1:0] DEBUG_REQUEST = 24; // Debug request
|
||||
|
||||
localparam int unsigned IRQ_S_SOFT = 1;
|
||||
localparam int unsigned IRQ_VS_SOFT = 2;
|
||||
localparam int unsigned IRQ_M_SOFT = 3;
|
||||
localparam int unsigned IRQ_S_TIMER = 5;
|
||||
localparam int unsigned IRQ_VS_TIMER = 6;
|
||||
localparam int unsigned IRQ_M_TIMER = 7;
|
||||
localparam int unsigned IRQ_S_EXT = 9;
|
||||
localparam int unsigned IRQ_VS_EXT = 10;
|
||||
localparam int unsigned IRQ_M_EXT = 11;
|
||||
localparam int unsigned IRQ_HS_EXT = 12;
|
||||
|
||||
localparam logic [31:0] MIP_SSIP = 1 << IRQ_S_SOFT;
|
||||
localparam logic [31:0] MIP_VSSIP = 1 << IRQ_VS_SOFT;
|
||||
localparam logic [31:0] MIP_MSIP = 1 << IRQ_M_SOFT;
|
||||
localparam logic [31:0] MIP_STIP = 1 << IRQ_S_TIMER;
|
||||
localparam logic [31:0] MIP_VSTIP = 1 << IRQ_VS_TIMER;
|
||||
localparam logic [31:0] MIP_MTIP = 1 << IRQ_M_TIMER;
|
||||
localparam logic [31:0] MIP_SEIP = 1 << IRQ_S_EXT;
|
||||
localparam logic [31:0] MIP_VSEIP = 1 << IRQ_VS_EXT;
|
||||
localparam logic [31:0] MIP_MEIP = 1 << IRQ_M_EXT;
|
||||
localparam logic [31:0] MIP_SGEIP = 1 << IRQ_HS_EXT;
|
||||
|
||||
// ----------------------
|
||||
// PseudoInstructions Codes
|
||||
// ----------------------
|
||||
localparam logic [31:0] READ_32_PSEUDOINSTRUCTION = 32'h00002000;
|
||||
localparam logic [31:0] WRITE_32_PSEUDOINSTRUCTION = 32'h00002020;
|
||||
localparam logic [31:0] READ_64_PSEUDOINSTRUCTION = 32'h00003000;
|
||||
localparam logic [31:0] WRITE_64_PSEUDOINSTRUCTION = 32'h00003020;
|
||||
|
||||
// -----
|
||||
// CSRs
|
||||
|
@ -339,17 +395,46 @@ package riscv;
|
|||
CSR_VL = 12'hC20,
|
||||
CSR_VTYPE = 12'hC21,
|
||||
CSR_VLENB = 12'hC22,
|
||||
// Virtual Supervisor Mode CSRs
|
||||
CSR_VSSTATUS = 12'h200,
|
||||
CSR_VSIE = 12'h204,
|
||||
CSR_VSTVEC = 12'h205,
|
||||
CSR_VSSCRATCH = 12'h240,
|
||||
CSR_VSEPC = 12'h241,
|
||||
CSR_VSCAUSE = 12'h242,
|
||||
CSR_VSTVAL = 12'h243,
|
||||
CSR_VSIP = 12'h244,
|
||||
CSR_VSATP = 12'h280,
|
||||
// Supervisor Mode CSRs
|
||||
CSR_SSTATUS = 12'h100,
|
||||
CSR_SIE = 12'h104,
|
||||
CSR_STVEC = 12'h105,
|
||||
CSR_SCOUNTEREN = 12'h106,
|
||||
CSR_SENVCFG = 12'h10A,
|
||||
CSR_SSCRATCH = 12'h140,
|
||||
CSR_SEPC = 12'h141,
|
||||
CSR_SCAUSE = 12'h142,
|
||||
CSR_STVAL = 12'h143,
|
||||
CSR_SIP = 12'h144,
|
||||
CSR_SATP = 12'h180,
|
||||
// Hypervisor-extended Supervisor Mode CSRs
|
||||
CSR_HSTATUS = 12'h600,
|
||||
CSR_HEDELEG = 12'h602,
|
||||
CSR_HIDELEG = 12'h603,
|
||||
CSR_HIE = 12'h604,
|
||||
CSR_HCOUNTEREN = 12'h606,
|
||||
CSR_HGEIE = 12'h607,
|
||||
CSR_HTVAL = 12'h643,
|
||||
CSR_HIP = 12'h644,
|
||||
CSR_HVIP = 12'h645,
|
||||
CSR_HTINST = 12'h64A,
|
||||
CSR_HGEIP = 12'hE12,
|
||||
CSR_HENVCFG = 12'h60A,
|
||||
CSR_HENVCFGH = 12'h61A,
|
||||
CSR_HGATP = 12'h680,
|
||||
CSR_HCONTEXT = 12'h6A8,
|
||||
CSR_HTIMEDELTA = 12'h605,
|
||||
CSR_HTIMEDELTAH = 12'h615,
|
||||
// Machine Mode CSRs
|
||||
CSR_MSTATUS = 12'h300,
|
||||
CSR_MISA = 12'h301,
|
||||
|
@ -394,6 +479,8 @@ package riscv;
|
|||
CSR_MCAUSE = 12'h342,
|
||||
CSR_MTVAL = 12'h343,
|
||||
CSR_MIP = 12'h344,
|
||||
CSR_MTINST = 12'h34A,
|
||||
CSR_MTVAL2 = 12'h34B,
|
||||
CSR_MENVCFG = 12'h30A,
|
||||
CSR_MENVCFGH = 12'h31A,
|
||||
CSR_PMPCFG0 = 12'h3A0,
|
||||
|
@ -582,6 +669,17 @@ package riscv;
|
|||
return {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000};
|
||||
endfunction
|
||||
|
||||
localparam logic [63:0] HSTATUS_VSBE = 'h00000020;
|
||||
localparam logic [63:0] HSTATUS_GVA = 'h00000040;
|
||||
localparam logic [63:0] HSTATUS_SPV = 'h00000080;
|
||||
localparam logic [63:0] HSTATUS_SPVP = 'h00000100;
|
||||
localparam logic [63:0] HSTATUS_HU = 'h00000200;
|
||||
localparam logic [63:0] HSTATUS_VGEIN = 'h0003F000;
|
||||
localparam logic [63:0] HSTATUS_VTVM = 'h00100000;
|
||||
localparam logic [63:0] HSTATUS_VTW = 'h00200000;
|
||||
localparam logic [63:0] HSTATUS_VTSR = 'h00400000;
|
||||
localparam logic [63:0] HSTATUS_VSXL = 64'h0000000300000000;
|
||||
|
||||
localparam logic [63:0] MSTATUS_UIE = 'h00000001;
|
||||
localparam logic [63:0] MSTATUS_SIE = 'h00000002;
|
||||
localparam logic [63:0] MSTATUS_HIE = 'h00000004;
|
||||
|
@ -611,6 +709,15 @@ package riscv;
|
|||
return {IS_XLEN64, 31'h00000000, ~IS_XLEN64, 31'h00000000};
|
||||
endfunction
|
||||
|
||||
localparam logic [63:0] MENVCFG_FIOM = 'h00000001;
|
||||
localparam logic [63:0] MENVCFG_CBIE = 'h00000030;
|
||||
localparam logic [63:0] MENVCFG_CBFE = 'h00000040;
|
||||
localparam logic [63:0] MENVCFG_CBZE = 'h00000080;
|
||||
localparam logic [63:0] MENVCFG_PBMTE = 64'h4000000000000000;
|
||||
localparam logic [63:0] MENVCFG_STCE = 64'h8000000000000000;
|
||||
|
||||
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
CSRRW = 3'h1,
|
||||
CSRRS = 3'h2,
|
||||
|
@ -675,7 +782,9 @@ package riscv;
|
|||
// -----
|
||||
typedef struct packed {
|
||||
logic [31:28] xdebugver;
|
||||
logic [27:16] zero2;
|
||||
logic [27:18] zero2;
|
||||
logic ebreakvs;
|
||||
logic ebreakvu;
|
||||
logic ebreakm;
|
||||
logic zero1;
|
||||
logic ebreaks;
|
||||
|
@ -684,7 +793,7 @@ package riscv;
|
|||
logic stopcount;
|
||||
logic stoptime;
|
||||
logic [8:6] cause;
|
||||
logic zero0;
|
||||
logic v;
|
||||
logic mprven;
|
||||
logic nmip;
|
||||
logic step;
|
||||
|
@ -778,6 +887,22 @@ package riscv;
|
|||
return 32'h00000000;
|
||||
endfunction
|
||||
|
||||
// This functions converts S-mode CSR addresses into VS-mode CSR addresses
|
||||
// when V=1 (i.e., running in VS-mode).
|
||||
function automatic csr_t convert_vs_access_csr(csr_t csr_addr, logic v);
|
||||
csr_t ret;
|
||||
ret = csr_addr;
|
||||
unique case (csr_addr.address) inside
|
||||
[CSR_SSTATUS : CSR_STVEC], [CSR_SSCRATCH : CSR_SATP]: begin
|
||||
if (v) begin
|
||||
ret.csr_decode.priv_lvl = PRIV_LVL_HS;
|
||||
end
|
||||
return ret;
|
||||
end
|
||||
default: return ret;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
|
||||
// trace log compatible to spikes commit log feature
|
||||
// pragma translate_off
|
||||
|
|
|
@ -78,6 +78,8 @@ module issue_read_operands
|
|||
output logic alu_valid_o,
|
||||
// Branch instruction is valid - TO_BE_COMPLETED
|
||||
output logic branch_valid_o,
|
||||
// Transformed instruction - TO_BE_COMPLETED
|
||||
output logic [31:0] tinst_o,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
output branchpredict_sbe_t branch_predict_o,
|
||||
// Load Store Unit is ready - TO_BE_COMPLETED
|
||||
|
@ -139,6 +141,7 @@ module issue_read_operands
|
|||
logic [CVA6Cfg.TRANS_ID_BITS-1:0] trans_id_n, trans_id_q;
|
||||
fu_op operator_n, operator_q; // operation to perform
|
||||
fu_t fu_n, fu_q; // functional unit to use
|
||||
logic [31:0] tinst_n, tinst_q; // transformed instruction
|
||||
|
||||
// forwarding signals
|
||||
logic forward_rs1, forward_rs2, forward_rs3;
|
||||
|
@ -169,6 +172,7 @@ module issue_read_operands
|
|||
assign cvxif_valid_o = CVA6Cfg.CvxifEn ? cvxif_valid_q : '0;
|
||||
assign cvxif_off_instr_o = CVA6Cfg.CvxifEn ? cvxif_off_instr_q : '0;
|
||||
assign stall_issue_o = stall;
|
||||
assign tinst_o = CVA6Cfg.RVH ? tinst_q : '0;
|
||||
// ---------------
|
||||
// Issue Stage
|
||||
// ---------------
|
||||
|
@ -283,6 +287,9 @@ module issue_read_operands
|
|||
trans_id_n = issue_instr_i.trans_id;
|
||||
fu_n = issue_instr_i.fu;
|
||||
operator_n = issue_instr_i.op;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst_n = issue_instr_i.ex.tinst;
|
||||
end
|
||||
// or should we forward
|
||||
if (forward_rs1) begin
|
||||
operand_a_n = rs1_i;
|
||||
|
@ -591,22 +598,28 @@ module issue_read_operands
|
|||
// ----------------------
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
operand_a_q <= '{default: 0};
|
||||
operand_b_q <= '{default: 0};
|
||||
imm_q <= '0;
|
||||
fu_q <= NONE;
|
||||
operator_q <= ADD;
|
||||
trans_id_q <= '0;
|
||||
operand_a_q <= '{default: 0};
|
||||
operand_b_q <= '{default: 0};
|
||||
imm_q <= '0;
|
||||
fu_q <= NONE;
|
||||
operator_q <= ADD;
|
||||
trans_id_q <= '0;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst_q <= '0;
|
||||
end
|
||||
pc_o <= '0;
|
||||
is_compressed_instr_o <= 1'b0;
|
||||
branch_predict_o <= {cf_t'(0), {CVA6Cfg.VLEN{1'b0}}};
|
||||
end else begin
|
||||
operand_a_q <= operand_a_n;
|
||||
operand_b_q <= operand_b_n;
|
||||
imm_q <= imm_n;
|
||||
fu_q <= fu_n;
|
||||
operator_q <= operator_n;
|
||||
trans_id_q <= trans_id_n;
|
||||
operand_a_q <= operand_a_n;
|
||||
operand_b_q <= operand_b_n;
|
||||
imm_q <= imm_n;
|
||||
fu_q <= fu_n;
|
||||
operator_q <= operator_n;
|
||||
trans_id_q <= trans_id_n;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
tinst_q <= tinst_n;
|
||||
end
|
||||
pc_o <= issue_instr_i.pc;
|
||||
is_compressed_instr_o <= issue_instr_i.is_compressed;
|
||||
branch_predict_o <= issue_instr_i.bp;
|
||||
|
|
|
@ -56,6 +56,8 @@ module issue_stage
|
|||
output logic [CVA6Cfg.VLEN-1:0] pc_o,
|
||||
// Is compressed instruction - EX_STAGE
|
||||
output logic is_compressed_instr_o,
|
||||
// Transformed trap instruction - EX_STAGE
|
||||
output logic [31:0] tinst_o,
|
||||
// Fixed Latency Unit is ready - EX_STAGE
|
||||
input logic flu_ready_i,
|
||||
// ALU FU is valid - EX_STAGE
|
||||
|
@ -235,6 +237,7 @@ module issue_stage
|
|||
.rs1_forwarding_o (rs1_forwarding_xlen),
|
||||
.rs2_forwarding_o (rs2_forwarding_xlen),
|
||||
.stall_issue_o (stall_issue_o),
|
||||
.tinst_o (tinst_o),
|
||||
.*
|
||||
);
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ module load_store_unit
|
|||
output logic no_st_pending_o,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic amo_valid_commit_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [31:0] tinst_i,
|
||||
// FU data needed to execute instruction - ISSUE_STAGE
|
||||
input fu_data_t fu_data_i,
|
||||
// Load Store Unit is ready - ISSUE_STAGE
|
||||
|
@ -73,8 +75,12 @@ module load_store_unit
|
|||
|
||||
// Enable virtual memory translation - TO_BE_COMPLETED
|
||||
input logic enable_translation_i,
|
||||
// Enable G-Stage memory translation - TO_BE_COMPLETED
|
||||
input logic enable_g_translation_i,
|
||||
// Enable virtual memory translation for load/stores - TO_BE_COMPLETED
|
||||
input logic en_ld_st_translation_i,
|
||||
// Enable G-Stage memory translation for load/stores - TO_BE_COMPLETED
|
||||
input logic en_ld_st_g_translation_i,
|
||||
|
||||
// Instruction cache input request - CACHES
|
||||
input icache_arsp_t icache_areq_i,
|
||||
|
@ -83,22 +89,46 @@ module load_store_unit
|
|||
|
||||
// Current privilege mode - CSR_REGFILE
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
// Current virtualization mode - CSR_REGFILE
|
||||
input logic v_i,
|
||||
// Privilege level at which load and stores should happen - CSR_REGFILE
|
||||
input riscv::priv_lvl_t ld_st_priv_lvl_i,
|
||||
// Virtualization mode at which load and stores should happen - CSR_REGFILE
|
||||
input logic ld_st_v_i,
|
||||
// Instruction is a hyp load/store - CSR_REGFILE
|
||||
output logic csr_hs_ld_st_inst_o,
|
||||
// Supervisor User Memory - CSR_REGFILE
|
||||
input logic sum_i,
|
||||
// Virtual Supervisor User Memory - CSR_REGFILE
|
||||
input logic vs_sum_i,
|
||||
// Make Executable Readable - CSR_REGFILE
|
||||
input logic mxr_i,
|
||||
// Make Executable Readable Virtual Supervisor - CSR_REGFILE
|
||||
input logic vmxr_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [ CVA6Cfg.PPNW-1:0] satp_ppn_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [ CVA6Cfg.PPNW-1:0] vsatp_ppn_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [ CVA6Cfg.PPNW-1:0] hgatp_ppn_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_to_be_flushed_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_to_be_flushed_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [ CVA6Cfg.VLEN-1:0] vaddr_to_be_flushed_i,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
input logic [ CVA6Cfg.GPLEN-1:0] gpaddr_to_be_flushed_i,
|
||||
// TLB flush - CONTROLLER
|
||||
input logic flush_tlb_i,
|
||||
input logic flush_tlb_vvma_i,
|
||||
input logic flush_tlb_gvma_i,
|
||||
// Instruction TLB miss - PERF_COUNTERS
|
||||
output logic itlb_miss_o,
|
||||
// Data TLB miss - PERF_COUNTERS
|
||||
|
@ -146,23 +176,38 @@ module load_store_unit
|
|||
logic [ CVA6Cfg.VLEN-1:0] vaddr_i;
|
||||
logic [ CVA6Cfg.XLEN-1:0] vaddr_xlen;
|
||||
logic overflow;
|
||||
logic g_overflow;
|
||||
logic [(CVA6Cfg.XLEN/8)-1:0] be_i;
|
||||
|
||||
assign vaddr_xlen = $unsigned($signed(fu_data_i.imm) + $signed(fu_data_i.operand_a));
|
||||
assign vaddr_i = vaddr_xlen[CVA6Cfg.VLEN-1:0];
|
||||
// we work with SV39 or SV32, so if VM is enabled, check that all bits [CVA6Cfg.XLEN-1:38] or [CVA6Cfg.XLEN-1:31] are equal
|
||||
// we work with SV39 or SV32, so if VM is enabled, check that all bits [XLEN-1:38] or [XLEN-1:31] are equal
|
||||
assign overflow = (CVA6Cfg.IS_XLEN64 && (!((&vaddr_xlen[CVA6Cfg.XLEN-1:CVA6Cfg.SV-1]) == 1'b1 || (|vaddr_xlen[CVA6Cfg.XLEN-1:CVA6Cfg.SV-1]) == 1'b0)));
|
||||
if (CVA6Cfg.RVH) begin : gen_g_overflow_hyp
|
||||
assign g_overflow = (CVA6Cfg.IS_XLEN64 && (!((|vaddr_xlen[CVA6Cfg.XLEN-1:CVA6Cfg.SVX]) == 1'b0)));
|
||||
end else begin : gen_g_overflow_no_hyp
|
||||
assign g_overflow = 1'b0;
|
||||
end
|
||||
|
||||
logic st_valid_i;
|
||||
logic ld_valid_i;
|
||||
logic ld_translation_req;
|
||||
logic st_translation_req;
|
||||
logic [CVA6Cfg.VLEN-1:0] ld_vaddr;
|
||||
logic [ 31:0] ld_tinst;
|
||||
logic ld_hs_ld_st_inst;
|
||||
logic ld_hlvx_inst;
|
||||
logic [CVA6Cfg.VLEN-1:0] st_vaddr;
|
||||
logic [ 31:0] st_tinst;
|
||||
logic st_hs_ld_st_inst;
|
||||
logic st_hlvx_inst;
|
||||
logic translation_req;
|
||||
logic translation_valid;
|
||||
logic [CVA6Cfg.VLEN-1:0] mmu_vaddr;
|
||||
logic [CVA6Cfg.PLEN-1:0] mmu_paddr, mmu_vaddr_plen, fetch_vaddr_plen;
|
||||
logic [ 31:0] mmu_tinst;
|
||||
logic mmu_hs_ld_st_inst;
|
||||
logic mmu_hlvx_inst;
|
||||
exception_t mmu_exception;
|
||||
logic dtlb_hit;
|
||||
logic [ CVA6Cfg.PPNW-1:0] dtlb_ppn;
|
||||
|
@ -181,10 +226,53 @@ module load_store_unit
|
|||
exception_t ld_ex;
|
||||
exception_t st_ex;
|
||||
|
||||
logic hs_ld_st_inst;
|
||||
logic hlvx_inst;
|
||||
// -------------------
|
||||
// MMU e.g.: TLBs/PTW
|
||||
// -------------------
|
||||
if (MMU_PRESENT && (CVA6Cfg.XLEN == 64)) begin : gen_mmu_sv39
|
||||
if (MMU_PRESENT && CVA6Cfg.RVH && (CVA6Cfg.XLEN == 64)) begin : gen_mmu_sv39x4
|
||||
cva6_mmu_sv39x4 #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.exception_t (exception_t),
|
||||
.icache_areq_t (icache_areq_t),
|
||||
.icache_arsp_t (icache_arsp_t),
|
||||
.icache_dreq_t (icache_dreq_t),
|
||||
.icache_drsp_t (icache_drsp_t),
|
||||
.dcache_req_i_t (dcache_req_i_t),
|
||||
.dcache_req_o_t (dcache_req_o_t),
|
||||
.INSTR_TLB_ENTRIES(ariane_pkg::INSTR_TLB_ENTRIES),
|
||||
.DATA_TLB_ENTRIES (ariane_pkg::DATA_TLB_ENTRIES)
|
||||
) i_cva6_mmu (
|
||||
// misaligned bypass
|
||||
.misaligned_ex_i(misaligned_exception),
|
||||
.lsu_is_store_i (st_translation_req),
|
||||
.lsu_req_i (translation_req),
|
||||
.lsu_vaddr_i (mmu_vaddr),
|
||||
.lsu_tinst_i (mmu_tinst),
|
||||
.lsu_valid_o (translation_valid),
|
||||
.lsu_paddr_o (mmu_paddr),
|
||||
.lsu_exception_o(mmu_exception),
|
||||
.lsu_dtlb_hit_o (dtlb_hit), // send in the same cycle as the request
|
||||
.lsu_dtlb_ppn_o (dtlb_ppn), // send in the same cycle as the request
|
||||
// connecting PTW to D$ IF
|
||||
.req_port_i (dcache_req_ports_i[0]),
|
||||
.req_port_o (dcache_req_ports_o[0]),
|
||||
// icache address translation requests
|
||||
.icache_areq_i (icache_areq_i),
|
||||
.asid_to_be_flushed_i,
|
||||
.vmid_to_be_flushed_i,
|
||||
.vaddr_to_be_flushed_i,
|
||||
.gpaddr_to_be_flushed_i,
|
||||
.icache_areq_o (icache_areq_o),
|
||||
.pmpcfg_i,
|
||||
.pmpaddr_i,
|
||||
// Hypervisor load/store signals
|
||||
.hlvx_inst_i (mmu_hlvx_inst),
|
||||
.hs_ld_st_inst_i(mmu_hs_ld_st_inst),
|
||||
.*
|
||||
);
|
||||
end else if (MMU_PRESENT && (CVA6Cfg.XLEN == 64)) begin : gen_mmu_sv39
|
||||
mmu #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.exception_t (exception_t),
|
||||
|
@ -330,6 +418,9 @@ module load_store_unit
|
|||
.translation_req_o (st_translation_req),
|
||||
.vaddr_o (st_vaddr),
|
||||
.rvfi_mem_paddr_o (rvfi_mem_paddr_o),
|
||||
.tinst_o (st_tinst),
|
||||
.hs_ld_st_inst_o (st_hs_ld_st_inst),
|
||||
.hlvx_inst_o (st_hlvx_inst),
|
||||
.paddr_i (mmu_paddr),
|
||||
.ex_i (mmu_exception),
|
||||
.dtlb_hit_i (dtlb_hit),
|
||||
|
@ -365,6 +456,9 @@ module load_store_unit
|
|||
// MMU port
|
||||
.translation_req_o (ld_translation_req),
|
||||
.vaddr_o (ld_vaddr),
|
||||
.tinst_o (ld_tinst),
|
||||
.hs_ld_st_inst_o (ld_hs_ld_st_inst),
|
||||
.hlvx_inst_o (ld_hlvx_inst),
|
||||
.paddr_i (mmu_paddr),
|
||||
.ex_i (mmu_exception),
|
||||
.dtlb_hit_i (dtlb_hit),
|
||||
|
@ -411,11 +505,14 @@ module load_store_unit
|
|||
// determine whether this is a load or store
|
||||
always_comb begin : which_op
|
||||
|
||||
ld_valid_i = 1'b0;
|
||||
st_valid_i = 1'b0;
|
||||
ld_valid_i = 1'b0;
|
||||
st_valid_i = 1'b0;
|
||||
|
||||
translation_req = 1'b0;
|
||||
mmu_vaddr = {CVA6Cfg.VLEN{1'b0}};
|
||||
translation_req = 1'b0;
|
||||
mmu_vaddr = {CVA6Cfg.VLEN{1'b0}};
|
||||
mmu_tinst = {32{1'b0}};
|
||||
mmu_hs_ld_st_inst = 1'b0;
|
||||
mmu_hlvx_inst = 1'b0;
|
||||
|
||||
// check the operation to activate the right functional unit accordingly
|
||||
unique case (lsu_ctrl.fu)
|
||||
|
@ -424,18 +521,53 @@ module load_store_unit
|
|||
ld_valid_i = lsu_ctrl.valid;
|
||||
translation_req = ld_translation_req;
|
||||
mmu_vaddr = ld_vaddr;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
mmu_tinst = ld_tinst;
|
||||
mmu_hs_ld_st_inst = ld_hs_ld_st_inst;
|
||||
mmu_hlvx_inst = ld_hlvx_inst;
|
||||
end
|
||||
end
|
||||
// all stores go here
|
||||
STORE: begin
|
||||
st_valid_i = lsu_ctrl.valid;
|
||||
translation_req = st_translation_req;
|
||||
mmu_vaddr = st_vaddr;
|
||||
if (CVA6Cfg.RVH) begin
|
||||
mmu_tinst = st_tinst;
|
||||
mmu_hs_ld_st_inst = st_hs_ld_st_inst;
|
||||
mmu_hlvx_inst = st_hlvx_inst;
|
||||
end
|
||||
end
|
||||
// not relevant for the LSU
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ------------------------
|
||||
// Hypervisor Load/Store
|
||||
// ------------------------
|
||||
// determine whether this is a hypervisor load or store
|
||||
if (CVA6Cfg.RVH) begin
|
||||
always_comb begin : hyp_ld_st
|
||||
// check the operator to activate the right functional unit accordingly
|
||||
hs_ld_st_inst = 1'b0;
|
||||
hlvx_inst = 1'b0;
|
||||
case (lsu_ctrl.operation)
|
||||
// all loads go here
|
||||
HLV_B, HLV_BU, HLV_H, HLV_HU, HLV_W, HSV_B, HSV_H, HSV_W, HLV_WU, HLV_D, HSV_D: begin
|
||||
hs_ld_st_inst = 1'b1;
|
||||
end
|
||||
HLVX_WU, HLVX_HU: begin
|
||||
hs_ld_st_inst = 1'b1;
|
||||
hlvx_inst = 1'b1;
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
end else begin
|
||||
assign hs_ld_st_inst = 1'b0;
|
||||
assign hlvx_inst = 1'b0;
|
||||
end
|
||||
|
||||
// ---------------
|
||||
// Byte Enable
|
||||
|
@ -457,9 +589,9 @@ module load_store_unit
|
|||
// the misaligned exception is passed to the functional unit via the MMU, which in case
|
||||
// can augment the exception if other memory related exceptions like a page fault or access errors
|
||||
always_comb begin : data_misaligned_detection
|
||||
|
||||
misaligned_exception = {{CVA6Cfg.XLEN{1'b0}}, {CVA6Cfg.XLEN{1'b0}}, 1'b0};
|
||||
|
||||
misaligned_exception = {
|
||||
{CVA6Cfg.XLEN{1'b0}}, {CVA6Cfg.XLEN{1'b0}}, {CVA6Cfg.GPLEN{1'b0}}, {32{1'b0}}, 1'b0, 1'b0
|
||||
};
|
||||
data_misaligned = 1'b0;
|
||||
|
||||
if (lsu_ctrl.valid) begin
|
||||
|
@ -469,7 +601,7 @@ module load_store_unit
|
|||
AMO_LRD, AMO_SCD,
|
||||
AMO_SWAPD, AMO_ADDD, AMO_ANDD, AMO_ORD,
|
||||
AMO_XORD, AMO_MAXD, AMO_MAXDU, AMO_MIND,
|
||||
AMO_MINDU: begin
|
||||
AMO_MINDU, HLV_D, HSV_D: begin
|
||||
if (CVA6Cfg.IS_XLEN64 && lsu_ctrl.vaddr[2:0] != 3'b000) begin
|
||||
data_misaligned = 1'b1;
|
||||
end
|
||||
|
@ -479,13 +611,13 @@ module load_store_unit
|
|||
AMO_LRW, AMO_SCW,
|
||||
AMO_SWAPW, AMO_ADDW, AMO_ANDW, AMO_ORW,
|
||||
AMO_XORW, AMO_MAXW, AMO_MAXWU, AMO_MINW,
|
||||
AMO_MINWU: begin
|
||||
AMO_MINWU, HLV_W, HLV_WU, HLVX_WU, HSV_W: begin
|
||||
if (lsu_ctrl.vaddr[1:0] != 2'b00) begin
|
||||
data_misaligned = 1'b1;
|
||||
end
|
||||
end
|
||||
// half word
|
||||
LH, LHU, SH, FLH, FSH: begin
|
||||
LH, LHU, SH, FLH, FSH, HLV_H, HLV_HU, HLVX_HU, HSV_H: begin
|
||||
if (lsu_ctrl.vaddr[0] != 1'b0) begin
|
||||
data_misaligned = 1'b1;
|
||||
end
|
||||
|
@ -502,12 +634,22 @@ module load_store_unit
|
|||
misaligned_exception.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn)
|
||||
misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
misaligned_exception.tval2 = '0;
|
||||
misaligned_exception.tinst = lsu_ctrl.tinst;
|
||||
misaligned_exception.gva = ld_st_v_i;
|
||||
end
|
||||
|
||||
end else if (lsu_ctrl.fu == STORE) begin
|
||||
misaligned_exception.cause = riscv::ST_ADDR_MISALIGNED;
|
||||
misaligned_exception.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn)
|
||||
misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
misaligned_exception.tval2 = '0;
|
||||
misaligned_exception.tinst = lsu_ctrl.tinst;
|
||||
misaligned_exception.gva = ld_st_v_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -518,12 +660,47 @@ module load_store_unit
|
|||
misaligned_exception.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn)
|
||||
misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
misaligned_exception.tval2 = '0;
|
||||
misaligned_exception.tinst = lsu_ctrl.tinst;
|
||||
misaligned_exception.gva = ld_st_v_i;
|
||||
end
|
||||
|
||||
end else if (lsu_ctrl.fu == STORE) begin
|
||||
misaligned_exception.cause = riscv::ST_ACCESS_FAULT;
|
||||
misaligned_exception.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn)
|
||||
misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
misaligned_exception.tval2 = '0;
|
||||
misaligned_exception.tinst = lsu_ctrl.tinst;
|
||||
misaligned_exception.gva = ld_st_v_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (ariane_pkg::MMU_PRESENT && CVA6Cfg.RVH && en_ld_st_g_translation_i && !en_ld_st_translation_i && lsu_ctrl.g_overflow) begin
|
||||
|
||||
if (lsu_ctrl.fu == LOAD) begin
|
||||
misaligned_exception.cause = riscv::LOAD_GUEST_PAGE_FAULT;
|
||||
misaligned_exception.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn)
|
||||
misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
misaligned_exception.tval2 = '0;
|
||||
misaligned_exception.tinst = lsu_ctrl.tinst;
|
||||
misaligned_exception.gva = ld_st_v_i;
|
||||
end
|
||||
end else if (lsu_ctrl.fu == STORE) begin
|
||||
misaligned_exception.cause = riscv::STORE_GUEST_PAGE_FAULT;
|
||||
misaligned_exception.valid = 1'b1;
|
||||
if (CVA6Cfg.TvalEn)
|
||||
misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr};
|
||||
if (CVA6Cfg.RVH) begin
|
||||
misaligned_exception.tval2 = '0;
|
||||
misaligned_exception.tinst = lsu_ctrl.tinst;
|
||||
misaligned_exception.gva = ld_st_v_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -537,7 +714,11 @@ module load_store_unit
|
|||
assign lsu_req_i = {
|
||||
lsu_valid_i,
|
||||
vaddr_i,
|
||||
tinst_i,
|
||||
hs_ld_st_inst,
|
||||
hlvx_inst,
|
||||
overflow,
|
||||
g_overflow,
|
||||
fu_data_i.operand_b,
|
||||
be_i,
|
||||
fu_data_i.fu,
|
||||
|
|
|
@ -51,6 +51,12 @@ module load_unit
|
|||
output logic translation_req_o,
|
||||
// Virtual address - TO_BE_COMPLETED
|
||||
output logic [CVA6Cfg.VLEN-1:0] vaddr_o,
|
||||
// Transformed trap instruction out - TO_BE_COMPLETED
|
||||
output logic [31:0] tinst_o,
|
||||
// Instruction is a hyp load store instruction - TO_BE_COMPLETED
|
||||
output logic hs_ld_st_inst_o,
|
||||
// Hyp load store with execute permissions - TO_BE_COMPLETED
|
||||
output logic hlvx_inst_o,
|
||||
// Physical address - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.PLEN-1:0] paddr_i,
|
||||
// Excepted which appears before load - TO_BE_COMPLETED
|
||||
|
@ -183,6 +189,10 @@ module load_unit
|
|||
assign page_offset_o = lsu_ctrl_i.vaddr[11:0];
|
||||
// feed-through the virtual address for VA translation
|
||||
assign vaddr_o = lsu_ctrl_i.vaddr;
|
||||
assign hs_ld_st_inst_o = CVA6Cfg.RVH ? lsu_ctrl_i.hs_ld_st_inst : 1'b0;
|
||||
assign hlvx_inst_o = CVA6Cfg.RVH ? lsu_ctrl_i.hlvx_inst : 1'b0;
|
||||
// feed-through the transformed instruction for mmu
|
||||
assign tinst_o = CVA6Cfg.RVH ? lsu_ctrl_i.tinst : '0;
|
||||
// this is a read-only interface so set the write enable to 0
|
||||
assign req_port_o.data_we = 1'b0;
|
||||
assign req_port_o.data_wdata = '0;
|
||||
|
@ -202,6 +212,9 @@ module load_unit
|
|||
// directly forward exception fields (valid bit is set below)
|
||||
assign ex_o.cause = ex_i.cause;
|
||||
assign ex_o.tval = ex_i.tval;
|
||||
assign ex_o.tval2 = CVA6Cfg.RVH ? ex_i.tval2 : '0;
|
||||
assign ex_o.tinst = CVA6Cfg.RVH ? ex_i.tinst : '0;
|
||||
assign ex_o.gva = CVA6Cfg.RVH ? ex_i.gva : 1'b0;
|
||||
|
||||
// Check that NI operations follow the necessary conditions
|
||||
logic paddr_ni;
|
||||
|
@ -479,10 +492,10 @@ module load_unit
|
|||
|
||||
|
||||
// prepare these signals for faster selection in the next cycle
|
||||
assign rdata_is_signed = ldbuf_rdata.operation inside {ariane_pkg::LW, ariane_pkg::LH, ariane_pkg::LB};
|
||||
assign rdata_is_signed = ldbuf_rdata.operation inside {ariane_pkg::LW, ariane_pkg::LH, ariane_pkg::LB, ariane_pkg::HLV_W, ariane_pkg::HLV_H, ariane_pkg::HLV_B};
|
||||
assign rdata_is_fp_signed = ldbuf_rdata.operation inside {ariane_pkg::FLW, ariane_pkg::FLH, ariane_pkg::FLB};
|
||||
assign rdata_offset = ((ldbuf_rdata.operation inside {ariane_pkg::LW, ariane_pkg::FLW}) & CVA6Cfg.IS_XLEN64) ? ldbuf_rdata.address_offset + 3 :
|
||||
( ldbuf_rdata.operation inside {ariane_pkg::LH, ariane_pkg::FLH}) ? ldbuf_rdata.address_offset + 1 :
|
||||
assign rdata_offset = ((ldbuf_rdata.operation inside {ariane_pkg::LW, ariane_pkg::FLW, ariane_pkg::HLV_W}) & CVA6Cfg.IS_XLEN64) ? ldbuf_rdata.address_offset + 3 :
|
||||
( ldbuf_rdata.operation inside {ariane_pkg::LH, ariane_pkg::FLH, ariane_pkg::HLV_H}) ? ldbuf_rdata.address_offset + 1 :
|
||||
ldbuf_rdata.address_offset;
|
||||
|
||||
for (genvar i = 0; i < (CVA6Cfg.XLEN / 8); i++) begin : gen_sign_bits
|
||||
|
@ -497,11 +510,11 @@ module load_unit
|
|||
// result mux
|
||||
always_comb begin
|
||||
unique case (ldbuf_rdata.operation)
|
||||
ariane_pkg::LW, ariane_pkg::LWU:
|
||||
ariane_pkg::LW, ariane_pkg::LWU, ariane_pkg::HLV_W, ariane_pkg::HLV_WU, ariane_pkg::HLVX_WU:
|
||||
result_o = {{CVA6Cfg.XLEN - 32{rdata_sign_bit}}, shifted_data[31:0]};
|
||||
ariane_pkg::LH, ariane_pkg::LHU:
|
||||
ariane_pkg::LH, ariane_pkg::LHU, ariane_pkg::HLV_H, ariane_pkg::HLV_HU, ariane_pkg::HLVX_HU:
|
||||
result_o = {{CVA6Cfg.XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0]};
|
||||
ariane_pkg::LB, ariane_pkg::LBU:
|
||||
ariane_pkg::LB, ariane_pkg::LBU, ariane_pkg::HLV_B, ariane_pkg::HLV_BU:
|
||||
result_o = {{CVA6Cfg.XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0]};
|
||||
default: begin
|
||||
// FLW, FLH and FLB have been defined here in default case to improve Code Coverage
|
||||
|
|
731
core/mmu_sv39x4/cva6_mmu_sv39x4.sv
Normal file
731
core/mmu_sv39x4/cva6_mmu_sv39x4.sv
Normal file
|
@ -0,0 +1,731 @@
|
|||
// Copyright (c) 2022 Bruno Sá and Zero-Day Labs.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
//
|
||||
// Author: Bruno Sá
|
||||
// Date: 14/08/2022
|
||||
// Acknowledges: Technology Innovation Institute (TII)
|
||||
//
|
||||
// Description: Memory Management Unit for CV32A6, contains TLB and
|
||||
// address translation unit. Sv39x4 as defined in RISC-V
|
||||
// privilege specification 1.12.
|
||||
// This module is an adaptation of the MMU Sv39x4 developed
|
||||
// by Florian Zaruba to the Sv39x4 standard.
|
||||
|
||||
|
||||
module cva6_mmu_sv39x4
|
||||
import ariane_pkg::*;
|
||||
#(
|
||||
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
|
||||
parameter type icache_areq_t = logic,
|
||||
parameter type icache_arsp_t = logic,
|
||||
parameter type icache_dreq_t = logic,
|
||||
parameter type icache_drsp_t = logic,
|
||||
parameter type dcache_req_i_t = logic,
|
||||
parameter type dcache_req_o_t = logic,
|
||||
parameter type exception_t = logic,
|
||||
parameter int unsigned INSTR_TLB_ENTRIES = 4,
|
||||
parameter int unsigned DATA_TLB_ENTRIES = 4
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic flush_i,
|
||||
input logic enable_translation_i,
|
||||
input logic enable_g_translation_i,
|
||||
input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores
|
||||
input logic en_ld_st_g_translation_i, // enable G-Stage translation for load/stores
|
||||
// IF interface
|
||||
input icache_arsp_t icache_areq_i,
|
||||
output icache_areq_t icache_areq_o,
|
||||
// LSU interface
|
||||
// this is a more minimalistic interface because the actual addressing logic is handled
|
||||
// in the LSU as we distinguish load and stores, what we do here is simple address translation
|
||||
input exception_t misaligned_ex_i,
|
||||
input logic lsu_req_i, // request address translation
|
||||
input logic [CVA6Cfg.VLEN-1:0] lsu_vaddr_i, // virtual address in
|
||||
input logic [31:0] lsu_tinst_i, // transformed instruction in
|
||||
input logic lsu_is_store_i, // the translation is requested by a store
|
||||
output logic csr_hs_ld_st_inst_o, // hyp load store instruction
|
||||
// if we need to walk the page table we can't grant in the same cycle
|
||||
// Cycle 0
|
||||
output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB
|
||||
output logic [CVA6Cfg.PPNW-1:0] lsu_dtlb_ppn_o, // ppn (send same cycle as hit)
|
||||
// Cycle 1
|
||||
output logic lsu_valid_o, // translation is valid
|
||||
output logic [riscv::PLEN-1:0] lsu_paddr_o, // translated address
|
||||
output exception_t lsu_exception_o, // address translation threw an exception
|
||||
// General control signals
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
input logic v_i,
|
||||
input riscv::priv_lvl_t ld_st_priv_lvl_i,
|
||||
input logic ld_st_v_i,
|
||||
input logic sum_i,
|
||||
input logic vs_sum_i,
|
||||
input logic mxr_i,
|
||||
input logic vmxr_i,
|
||||
input logic hlvx_inst_i,
|
||||
input logic hs_ld_st_inst_i,
|
||||
// input logic flag_mprv_i,
|
||||
input logic [CVA6Cfg.PPNW-1:0] satp_ppn_i,
|
||||
input logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_i,
|
||||
input logic [CVA6Cfg.PPNW-1:0] hgatp_ppn_i,
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_i,
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_i,
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_to_be_flushed_i,
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_i,
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_to_be_flushed_i,
|
||||
input logic [CVA6Cfg.VLEN-1:0] vaddr_to_be_flushed_i,
|
||||
input logic [CVA6Cfg.GPLEN-1:0] gpaddr_to_be_flushed_i,
|
||||
input logic flush_tlb_i,
|
||||
input logic flush_tlb_vvma_i,
|
||||
input logic flush_tlb_gvma_i,
|
||||
// Performance counters
|
||||
output logic itlb_miss_o,
|
||||
output logic dtlb_miss_o,
|
||||
// PTW memory interface
|
||||
input dcache_req_o_t req_port_i,
|
||||
output dcache_req_i_t req_port_o,
|
||||
// PMP
|
||||
input riscv::pmpcfg_t [15:0] pmpcfg_i,
|
||||
input logic [15:0][riscv::PLEN-3:0] pmpaddr_i
|
||||
);
|
||||
localparam type tlb_update_t = struct packed {
|
||||
logic valid; // valid flag
|
||||
logic is_s_2M;
|
||||
logic is_s_1G;
|
||||
logic is_g_2M;
|
||||
logic is_g_1G;
|
||||
logic [28:0] vpn;
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] asid;
|
||||
logic [CVA6Cfg.VMID_WIDTH-1:0] vmid;
|
||||
riscv::pte_t content;
|
||||
riscv::pte_t g_content;
|
||||
};
|
||||
|
||||
logic iaccess_err; // insufficient privilege to access this instruction page
|
||||
logic i_g_st_access_err; // insufficient privilege at g stage to access this instruction page
|
||||
logic daccess_err; // insufficient privilege to access this data page
|
||||
logic d_g_st_access_err; // insufficient privilege to access this data page
|
||||
logic ptw_active; // PTW is currently walking a page table
|
||||
logic walking_instr; // PTW is walking because of an ITLB miss
|
||||
logic ptw_error; // PTW threw an exception
|
||||
logic ptw_error_at_g_st; // PTW threw an exception at the G-Stage
|
||||
logic ptw_err_at_g_int_st; // PTW threw an exception at the G-Stage during S-Stage translation
|
||||
logic ptw_access_exception; // PTW threw an access exception (PMPs)
|
||||
logic [CVA6Cfg.GPLEN-1:0] ptw_bad_gpaddr; // PTW guest page fault bad guest physical addr
|
||||
|
||||
logic [CVA6Cfg.VLEN-1:0] update_vaddr;
|
||||
tlb_update_t update_ptw_itlb, update_ptw_dtlb;
|
||||
|
||||
logic itlb_lu_access;
|
||||
riscv::pte_t itlb_content;
|
||||
logic itlb_is_2M;
|
||||
logic itlb_is_1G;
|
||||
// data from G-stage translation
|
||||
riscv::pte_t itlb_g_content;
|
||||
logic itlb_lu_hit;
|
||||
logic [ CVA6Cfg.GPLEN-1:0] itlb_gpaddr;
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] itlb_lu_asid;
|
||||
|
||||
logic dtlb_lu_access;
|
||||
riscv::pte_t dtlb_content;
|
||||
logic dtlb_is_2M;
|
||||
logic dtlb_is_1G;
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] dtlb_lu_asid;
|
||||
// data from G-stage translation
|
||||
riscv::pte_t dtlb_g_content;
|
||||
logic dtlb_lu_hit;
|
||||
logic [ CVA6Cfg.GPLEN-1:0] dtlb_gpaddr;
|
||||
|
||||
|
||||
// Assignments
|
||||
assign itlb_lu_access = icache_areq_i.fetch_req;
|
||||
assign dtlb_lu_access = lsu_req_i;
|
||||
assign itlb_lu_asid = v_i ? vs_asid_i : asid_i;
|
||||
assign dtlb_lu_asid = (ld_st_v_i || flush_tlb_vvma_i) ? vs_asid_i : asid_i;
|
||||
|
||||
|
||||
cva6_tlb_sv39x4 #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.tlb_update_t(tlb_update_t),
|
||||
.TLB_ENTRIES (INSTR_TLB_ENTRIES)
|
||||
) i_itlb (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.flush_i (flush_tlb_i),
|
||||
.flush_vvma_i(flush_tlb_vvma_i),
|
||||
.flush_gvma_i(flush_tlb_gvma_i),
|
||||
.s_st_enbl_i (enable_translation_i),
|
||||
.g_st_enbl_i (enable_g_translation_i),
|
||||
.v_i (v_i),
|
||||
|
||||
.update_i(update_ptw_itlb),
|
||||
|
||||
.lu_access_i (itlb_lu_access),
|
||||
.lu_asid_i (itlb_lu_asid),
|
||||
.lu_vmid_i (vmid_i),
|
||||
.asid_to_be_flushed_i (asid_to_be_flushed_i),
|
||||
.vmid_to_be_flushed_i (vmid_to_be_flushed_i),
|
||||
.vaddr_to_be_flushed_i (vaddr_to_be_flushed_i),
|
||||
.gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i),
|
||||
.lu_vaddr_i (icache_areq_i.fetch_vaddr),
|
||||
.lu_content_o (itlb_content),
|
||||
.lu_g_content_o (itlb_g_content),
|
||||
.lu_gpaddr_o (itlb_gpaddr),
|
||||
|
||||
.lu_is_2M_o(itlb_is_2M),
|
||||
.lu_is_1G_o(itlb_is_1G),
|
||||
.lu_hit_o (itlb_lu_hit)
|
||||
);
|
||||
|
||||
cva6_tlb_sv39x4 #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.tlb_update_t(tlb_update_t),
|
||||
.TLB_ENTRIES (DATA_TLB_ENTRIES)
|
||||
) i_dtlb (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.flush_i (flush_tlb_i),
|
||||
.flush_vvma_i(flush_tlb_vvma_i),
|
||||
.flush_gvma_i(flush_tlb_gvma_i),
|
||||
.s_st_enbl_i (en_ld_st_translation_i),
|
||||
.g_st_enbl_i (en_ld_st_g_translation_i),
|
||||
.v_i (ld_st_v_i),
|
||||
|
||||
.update_i(update_ptw_dtlb),
|
||||
|
||||
.lu_access_i (dtlb_lu_access),
|
||||
.lu_asid_i (dtlb_lu_asid),
|
||||
.lu_vmid_i (vmid_i),
|
||||
.asid_to_be_flushed_i (asid_to_be_flushed_i),
|
||||
.vmid_to_be_flushed_i (vmid_to_be_flushed_i),
|
||||
.vaddr_to_be_flushed_i (vaddr_to_be_flushed_i),
|
||||
.gpaddr_to_be_flushed_i(gpaddr_to_be_flushed_i),
|
||||
.lu_vaddr_i (lsu_vaddr_i),
|
||||
.lu_content_o (dtlb_content),
|
||||
.lu_g_content_o (dtlb_g_content),
|
||||
.lu_gpaddr_o (dtlb_gpaddr),
|
||||
|
||||
.lu_is_2M_o(dtlb_is_2M),
|
||||
.lu_is_1G_o(dtlb_is_1G),
|
||||
.lu_hit_o (dtlb_lu_hit)
|
||||
);
|
||||
|
||||
|
||||
cva6_ptw_sv39x4 #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.dcache_req_i_t(dcache_req_i_t),
|
||||
.dcache_req_o_t(dcache_req_o_t),
|
||||
.tlb_update_t(tlb_update_t)
|
||||
) i_ptw (
|
||||
.clk_i (clk_i),
|
||||
.rst_ni (rst_ni),
|
||||
.ptw_active_o (ptw_active),
|
||||
.walking_instr_o (walking_instr),
|
||||
.ptw_error_o (ptw_error),
|
||||
.ptw_error_at_g_st_o (ptw_error_at_g_st),
|
||||
.ptw_err_at_g_int_st_o (ptw_err_at_g_int_st),
|
||||
.ptw_access_exception_o(ptw_access_exception),
|
||||
.enable_translation_i (enable_translation_i),
|
||||
.enable_g_translation_i(enable_g_translation_i),
|
||||
|
||||
.update_vaddr_o(update_vaddr),
|
||||
.itlb_update_o (update_ptw_itlb),
|
||||
.dtlb_update_o (update_ptw_dtlb),
|
||||
|
||||
.itlb_access_i(itlb_lu_access),
|
||||
.itlb_hit_i (itlb_lu_hit),
|
||||
.itlb_vaddr_i (icache_areq_i.fetch_vaddr),
|
||||
|
||||
.dtlb_access_i(dtlb_lu_access),
|
||||
.dtlb_hit_i (dtlb_lu_hit),
|
||||
.dtlb_vaddr_i (lsu_vaddr_i),
|
||||
.hlvx_inst_i (hlvx_inst_i),
|
||||
|
||||
.req_port_i (req_port_i),
|
||||
.req_port_o (req_port_o),
|
||||
.pmpcfg_i,
|
||||
.pmpaddr_i,
|
||||
.bad_gpaddr_o(ptw_bad_gpaddr),
|
||||
.*
|
||||
);
|
||||
|
||||
// ila_1 i_ila_1 (
|
||||
// .clk(clk_i), // input wire clk
|
||||
// .probe0({req_port_o.address_tag, req_port_o.address_index}),
|
||||
// .probe1(req_port_o.data_req), // input wire [63:0] probe1
|
||||
// .probe2(req_port_i.data_gnt), // input wire [0:0] probe2
|
||||
// .probe3(req_port_i.data_rdata), // input wire [0:0] probe3
|
||||
// .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4
|
||||
// .probe5(ptw_error), // input wire [1:0] probe5
|
||||
// .probe6(update_vaddr), // input wire [0:0] probe6
|
||||
// .probe7(update_ptw_itlb.valid), // input wire [0:0] probe7
|
||||
// .probe8(update_ptw_dtlb.valid), // input wire [0:0] probe8
|
||||
// .probe9(dtlb_lu_access), // input wire [0:0] probe9
|
||||
// .probe10(lsu_vaddr_i), // input wire [0:0] probe10
|
||||
// .probe11(dtlb_lu_hit), // input wire [0:0] probe11
|
||||
// .probe12(itlb_lu_access), // input wire [0:0] probe12
|
||||
// .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13
|
||||
// .probe14(itlb_lu_hit) // input wire [0:0] probe13
|
||||
// );
|
||||
|
||||
//-----------------------
|
||||
// Instruction Interface
|
||||
//-----------------------
|
||||
logic match_any_execute_region;
|
||||
logic pmp_instr_allow;
|
||||
// The instruction interface is a simple request response interface
|
||||
always_comb begin : instr_interface
|
||||
// MMU disabled: just pass through
|
||||
icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
|
||||
icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr[CVA6Cfg.PLEN-1:0]; // play through in case we disabled address translation
|
||||
// two potential exception sources:
|
||||
// 1. HPTW threw an exception -> signal with a page fault exception
|
||||
// 2. We got an access error because of insufficient permissions -> throw an access exception
|
||||
icache_areq_o.fetch_exception = '0;
|
||||
// Check whether we are allowed to access this memory region from a fetch perspective
|
||||
iaccess_err = icache_areq_i.fetch_req && enable_translation_i && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u)
|
||||
|| ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u));
|
||||
|
||||
i_g_st_access_err = icache_areq_i.fetch_req && enable_g_translation_i && !itlb_g_content.u;
|
||||
// MMU enabled: address from TLB, request delayed until hit. Error when TLB
|
||||
// hit and no access right or TLB hit and translated address not valid (e.g.
|
||||
// AXI decode error), or when PTW performs walk due to ITLB miss and raises
|
||||
// an error.
|
||||
if ((enable_translation_i || enable_g_translation_i)) begin
|
||||
// we work with SV39 or SV32, so if VM is enabled, check that all bits [CVA6Cfg.VLEN-1:CVA6Cfg.SV-1] are equal
|
||||
if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[CVA6Cfg.VLEN-1:CVA6Cfg.SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[CVA6Cfg.VLEN-1:CVA6Cfg.SV-1]) == 1'b0)) begin
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
{{32{1'b0}}},
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
|
||||
icache_areq_o.fetch_valid = 1'b0;
|
||||
|
||||
// 4K page
|
||||
icache_areq_o.fetch_paddr = {
|
||||
enable_g_translation_i ? itlb_g_content.ppn : itlb_content.ppn,
|
||||
icache_areq_i.fetch_vaddr[11:0]
|
||||
};
|
||||
// Mega page
|
||||
if (itlb_is_2M) begin
|
||||
icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12];
|
||||
end
|
||||
// Giga page
|
||||
if (itlb_is_1G) begin
|
||||
icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12];
|
||||
end
|
||||
// ---------
|
||||
// ITLB Hit
|
||||
// --------
|
||||
// if we hit the ITLB output the request signal immediately
|
||||
if (itlb_lu_hit) begin
|
||||
icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
|
||||
if (i_g_st_access_err) begin
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_GUEST_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
|
||||
itlb_gpaddr[CVA6Cfg.GPLEN-1:0],
|
||||
{{32{1'b0}}},
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
// we got an access error
|
||||
end else if (iaccess_err) begin
|
||||
// throw a page fault
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
{{32{1'b0}}},
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
end else if (!pmp_instr_allow) begin
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, icache_areq_i.fetch_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
{{32{1'b0}}},
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end else
|
||||
// ---------
|
||||
// ITLB Miss
|
||||
// ---------
|
||||
// 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) begin
|
||||
if (ptw_error_at_g_st) begin
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_GUEST_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, update_vaddr},
|
||||
ptw_bad_gpaddr,
|
||||
(ptw_err_at_g_int_st ? (CVA6Cfg.IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {{32{1'b0}}}),
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
end else begin
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
{{32{1'b0}}},
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end // TODO(moschn,zarubaf): What should the value of tval be in this case?
|
||||
else
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
{32{1'b0}},
|
||||
v_i,
|
||||
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 || enable_g_translation_i) && !pmp_instr_allow)) begin
|
||||
icache_areq_o.fetch_exception = {
|
||||
riscv::INSTR_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.PLEN{1'b0}}, icache_areq_o.fetch_paddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
{{32{1'b0}}},
|
||||
v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end
|
||||
|
||||
// check for execute flag on memory
|
||||
assign match_any_execute_region = config_pkg::is_inside_execute_regions(
|
||||
CVA6Cfg, {{64 - CVA6Cfg.PLEN{1'b0}}, icache_areq_o.fetch_paddr}
|
||||
);
|
||||
|
||||
// Instruction fetch
|
||||
pmp #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.PLEN (CVA6Cfg.PLEN),
|
||||
.PMP_LEN (CVA6Cfg.PLEN - 2),
|
||||
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
|
||||
) i_pmp_if (
|
||||
.addr_i (icache_areq_o.fetch_paddr),
|
||||
.priv_lvl_i,
|
||||
// we will always execute on the instruction fetch port
|
||||
.access_type_i(riscv::ACCESS_EXEC),
|
||||
// Configuration
|
||||
.conf_addr_i (pmpaddr_i),
|
||||
.conf_i (pmpcfg_i),
|
||||
.allow_o (pmp_instr_allow)
|
||||
);
|
||||
|
||||
//-----------------------
|
||||
// Data Interface
|
||||
//-----------------------
|
||||
logic [CVA6Cfg.VLEN-1:0] lsu_vaddr_n, lsu_vaddr_q;
|
||||
logic [CVA6Cfg.GPLEN-1:0] lsu_gpaddr_n, lsu_gpaddr_q;
|
||||
logic [31:0] lsu_tinst_n, lsu_tinst_q;
|
||||
logic hs_ld_st_inst_n, hs_ld_st_inst_q;
|
||||
riscv::pte_t dtlb_pte_n, dtlb_pte_q;
|
||||
riscv::pte_t dtlb_gpte_n, dtlb_gpte_q;
|
||||
exception_t misaligned_ex_n, misaligned_ex_q;
|
||||
logic lsu_req_n, lsu_req_q;
|
||||
logic lsu_is_store_n, lsu_is_store_q;
|
||||
logic dtlb_hit_n, dtlb_hit_q;
|
||||
logic dtlb_is_2M_n, dtlb_is_2M_q;
|
||||
logic dtlb_is_1G_n, dtlb_is_1G_q;
|
||||
|
||||
// check if we need to do translation or if we are always ready (e.g.: we are not translating anything)
|
||||
assign lsu_dtlb_hit_o = (en_ld_st_translation_i || en_ld_st_g_translation_i) ? dtlb_lu_hit : 1'b1;
|
||||
|
||||
// Wires to PMP checks
|
||||
riscv::pmp_access_t pmp_access_type;
|
||||
logic pmp_data_allow;
|
||||
localparam PPNWMin = (CVA6Cfg.PPNW - 1 > 29) ? 29 : CVA6Cfg.PPNW - 1;
|
||||
// The data interface is simpler and only consists of a request/response interface
|
||||
always_comb begin : data_interface
|
||||
// save request and DTLB response
|
||||
lsu_vaddr_n = lsu_vaddr_i;
|
||||
lsu_tinst_n = lsu_tinst_i;
|
||||
lsu_gpaddr_n = dtlb_gpaddr;
|
||||
lsu_req_n = lsu_req_i;
|
||||
hs_ld_st_inst_n = hs_ld_st_inst_i;
|
||||
misaligned_ex_n = misaligned_ex_i;
|
||||
dtlb_pte_n = dtlb_content;
|
||||
dtlb_gpte_n = dtlb_g_content;
|
||||
dtlb_hit_n = dtlb_lu_hit;
|
||||
lsu_is_store_n = lsu_is_store_i;
|
||||
dtlb_is_2M_n = dtlb_is_2M;
|
||||
dtlb_is_1G_n = dtlb_is_1G;
|
||||
|
||||
lsu_paddr_o = lsu_vaddr_q[CVA6Cfg.PLEN-1:0];
|
||||
lsu_dtlb_ppn_o = lsu_vaddr_n[CVA6Cfg.PLEN-1:12];
|
||||
lsu_valid_o = lsu_req_q;
|
||||
lsu_exception_o = misaligned_ex_q;
|
||||
csr_hs_ld_st_inst_o = hs_ld_st_inst_i || hs_ld_st_inst_q;
|
||||
pmp_access_type = lsu_is_store_q ? riscv::ACCESS_WRITE : riscv::ACCESS_READ;
|
||||
|
||||
// mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions
|
||||
misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i;
|
||||
|
||||
// Check if the User flag is set, then we may only access it in supervisor mode
|
||||
// if SUM is enabled
|
||||
daccess_err = en_ld_st_translation_i &&
|
||||
((ld_st_priv_lvl_i == riscv::PRIV_LVL_S && (ld_st_v_i ? !vs_sum_i : !sum_i ) && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode
|
||||
(ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u));
|
||||
d_g_st_access_err = en_ld_st_g_translation_i && !dtlb_gpte_q.u;
|
||||
// translation is enabled and no misaligned exception occurred
|
||||
if ((en_ld_st_translation_i || en_ld_st_g_translation_i) && !misaligned_ex_q.valid) begin
|
||||
lsu_valid_o = 1'b0;
|
||||
// 4K page
|
||||
lsu_paddr_o = {
|
||||
(en_ld_st_g_translation_i) ? dtlb_gpte_q.ppn : dtlb_pte_q.ppn, lsu_vaddr_q[11:0]
|
||||
};
|
||||
lsu_dtlb_ppn_o = (en_ld_st_g_translation_i) ? dtlb_g_content.ppn : dtlb_content.ppn;
|
||||
// Mega page
|
||||
if (dtlb_is_2M_q) begin
|
||||
lsu_paddr_o[20:12] = lsu_vaddr_q[20:12];
|
||||
lsu_dtlb_ppn_o[20:12] = lsu_vaddr_n[20:12];
|
||||
end
|
||||
// Giga page
|
||||
if (dtlb_is_1G_q) begin
|
||||
lsu_paddr_o[PPNWMin:12] = lsu_vaddr_q[PPNWMin:12];
|
||||
lsu_dtlb_ppn_o[PPNWMin:12] = lsu_vaddr_n[PPNWMin:12];
|
||||
end
|
||||
// ---------
|
||||
// DTLB Hit
|
||||
// --------
|
||||
if (dtlb_hit_q && lsu_req_q) begin
|
||||
lsu_valid_o = 1'b1;
|
||||
// exception priority:
|
||||
// PAGE_FAULTS have higher priority than ACCESS_FAULTS
|
||||
// virtual memory based exceptions are PAGE_FAULTS
|
||||
// physical memory based exceptions are ACCESS_FAULTS (PMA/PMP)
|
||||
|
||||
// this is a store
|
||||
if (lsu_is_store_q) begin
|
||||
// check if the page is write-able and we are not violating privileges
|
||||
// also check if the dirty flag is set
|
||||
if(en_ld_st_g_translation_i && (!dtlb_gpte_q.w || d_g_st_access_err || !dtlb_gpte_q.d)) begin
|
||||
lsu_exception_o = {
|
||||
riscv::STORE_GUEST_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, lsu_vaddr_q},
|
||||
lsu_gpaddr_q,
|
||||
{32{1'b0}},
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end else if (en_ld_st_translation_i && (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d)) begin
|
||||
lsu_exception_o = {
|
||||
riscv::STORE_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, lsu_vaddr_q},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
// Check if any PMPs are violated
|
||||
end else if (!pmp_data_allow) begin
|
||||
lsu_exception_o = {
|
||||
riscv::ST_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.PLEN{1'b0}}, lsu_paddr_o},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
|
||||
// this is a load
|
||||
end else begin
|
||||
if (d_g_st_access_err) begin
|
||||
lsu_exception_o = {
|
||||
riscv::LOAD_GUEST_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, lsu_vaddr_q},
|
||||
lsu_gpaddr_q,
|
||||
{{32{1'b0}}},
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
// check for sufficient access privileges - throw a page fault if necessary
|
||||
end else if (daccess_err) begin
|
||||
lsu_exception_o = {
|
||||
riscv::LOAD_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, lsu_vaddr_q},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
// Check if any PMPs are violated
|
||||
end else if (!pmp_data_allow) begin
|
||||
lsu_exception_o = {
|
||||
riscv::LD_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, lsu_vaddr_q},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end
|
||||
end else
|
||||
|
||||
// ---------
|
||||
// DTLB Miss
|
||||
// ---------
|
||||
// watch out for exceptions
|
||||
if (ptw_active && !walking_instr) begin
|
||||
// page table walker threw an exception
|
||||
if (ptw_error) begin
|
||||
// an error makes the translation valid
|
||||
lsu_valid_o = 1'b1;
|
||||
// the page table walker can only throw page faults
|
||||
if (lsu_is_store_q) begin
|
||||
if (ptw_error_at_g_st) begin
|
||||
lsu_exception_o = {
|
||||
riscv::STORE_GUEST_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
ptw_bad_gpaddr,
|
||||
(ptw_err_at_g_int_st ? (CVA6Cfg.IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {{32{1'b0}}}),
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end else begin
|
||||
lsu_exception_o = {
|
||||
riscv::STORE_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end else begin
|
||||
if (ptw_error_at_g_st) begin
|
||||
lsu_exception_o = {
|
||||
riscv::LOAD_GUEST_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
ptw_bad_gpaddr,
|
||||
(ptw_err_at_g_int_st ? (CVA6Cfg.IS_XLEN64 ? riscv::READ_64_PSEUDOINSTRUCTION : riscv::READ_32_PSEUDOINSTRUCTION) : {{32{1'b0}}}),
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end else begin
|
||||
lsu_exception_o = {
|
||||
riscv::LOAD_PAGE_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (ptw_access_exception) begin
|
||||
// an error makes the translation valid
|
||||
lsu_valid_o = 1'b1;
|
||||
// the page table walker can only throw page faults
|
||||
lsu_exception_o = {
|
||||
riscv::LD_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end
|
||||
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,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end else begin
|
||||
lsu_exception_o = {
|
||||
riscv::LD_ACCESS_FAULT,
|
||||
{{CVA6Cfg.XLEN - CVA6Cfg.VLEN{lsu_vaddr_q[CVA6Cfg.VLEN-1]}}, update_vaddr},
|
||||
{CVA6Cfg.GPLEN{1'b0}},
|
||||
lsu_tinst_q,
|
||||
ld_st_v_i,
|
||||
1'b1
|
||||
};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Load/store PMP check
|
||||
pmp #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.PLEN (CVA6Cfg.PLEN),
|
||||
.PMP_LEN (CVA6Cfg.PLEN - 2),
|
||||
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
|
||||
) i_pmp_data (
|
||||
.addr_i (lsu_paddr_o),
|
||||
.priv_lvl_i (ld_st_priv_lvl_i),
|
||||
.access_type_i(pmp_access_type),
|
||||
// Configuration
|
||||
.conf_addr_i (pmpaddr_i),
|
||||
.conf_i (pmpcfg_i),
|
||||
.allow_o (pmp_data_allow)
|
||||
);
|
||||
|
||||
// ----------
|
||||
// Registers
|
||||
// ----------
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
lsu_vaddr_q <= '0;
|
||||
lsu_gpaddr_q <= '0;
|
||||
lsu_tinst_q <= '0;
|
||||
hs_ld_st_inst_q <= '0;
|
||||
lsu_req_q <= '0;
|
||||
misaligned_ex_q <= '0;
|
||||
dtlb_pte_q <= '0;
|
||||
dtlb_gpte_q <= '0;
|
||||
dtlb_hit_q <= '0;
|
||||
lsu_is_store_q <= '0;
|
||||
dtlb_is_2M_q <= '0;
|
||||
dtlb_is_1G_q <= '0;
|
||||
end else begin
|
||||
lsu_vaddr_q <= lsu_vaddr_n;
|
||||
lsu_gpaddr_q <= lsu_gpaddr_n;
|
||||
lsu_tinst_q <= lsu_tinst_n;
|
||||
hs_ld_st_inst_q <= hs_ld_st_inst_n;
|
||||
lsu_req_q <= lsu_req_n;
|
||||
misaligned_ex_q <= misaligned_ex_n;
|
||||
dtlb_pte_q <= dtlb_pte_n;
|
||||
dtlb_gpte_q <= dtlb_gpte_n;
|
||||
dtlb_hit_q <= dtlb_hit_n;
|
||||
lsu_is_store_q <= lsu_is_store_n;
|
||||
dtlb_is_2M_q <= dtlb_is_2M_n;
|
||||
dtlb_is_1G_q <= dtlb_is_1G_n;
|
||||
end
|
||||
end
|
||||
endmodule
|
641
core/mmu_sv39x4/cva6_ptw_sv39x4.sv
Normal file
641
core/mmu_sv39x4/cva6_ptw_sv39x4.sv
Normal file
|
@ -0,0 +1,641 @@
|
|||
// Copyright (c) 2022 Bruno Sá and Zero-Day Labs.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
//
|
||||
// Author: Bruno Sá
|
||||
// Date: 14/08/2022
|
||||
// Acknowledges: Technology Innovation Institute (TII)
|
||||
//
|
||||
// Description: Hardware-PTW (Page-Table-Walker) for MMU Sv39x4.
|
||||
// This module is an adaptation of the Sv39 PTW developed
|
||||
// by Florian Zaruba and David Schaffenrath to the Sv39x4 standard.
|
||||
//
|
||||
|
||||
/* verilator lint_off WIDTH */
|
||||
|
||||
module cva6_ptw_sv39x4
|
||||
import ariane_pkg::*;
|
||||
#(
|
||||
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
|
||||
parameter type dcache_req_i_t = logic,
|
||||
parameter type dcache_req_o_t = logic,
|
||||
parameter type tlb_update_t = logic
|
||||
) (
|
||||
input logic clk_i, // Clock
|
||||
input logic rst_ni, // Asynchronous reset active low
|
||||
input logic flush_i, // flush everything, we need to do this because
|
||||
// actually everything we do is speculative at this stage
|
||||
// e.g.: there could be a CSR instruction that changes everything
|
||||
output logic ptw_active_o,
|
||||
output logic walking_instr_o, // set when walking for TLB
|
||||
output logic ptw_error_o, // set when an error occurred
|
||||
output logic ptw_error_at_g_st_o, // set when an error occurred at the G-Stage
|
||||
output logic ptw_err_at_g_int_st_o, // set when an error occurred at the G-Stage during S-Stage translation
|
||||
output logic ptw_access_exception_o, // set when an PMP access exception occured
|
||||
input logic enable_translation_i, // CSRs indicate to enable SV39 VS-Stage translation
|
||||
input logic enable_g_translation_i, // CSRs indicate to enable SV39 G-Stage translation
|
||||
input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores
|
||||
input logic en_ld_st_g_translation_i, // enable G-Stage translation for load/stores
|
||||
input logic v_i, // current virtualization mode bit
|
||||
input logic ld_st_v_i, // load/store virtualization mode bit
|
||||
input logic hlvx_inst_i, // is a HLVX load/store instruction
|
||||
|
||||
input logic lsu_is_store_i, // this translation was triggered by a store
|
||||
// PTW memory interface
|
||||
input dcache_req_o_t req_port_i,
|
||||
output dcache_req_i_t req_port_o,
|
||||
|
||||
|
||||
// to TLBs, update logic
|
||||
output tlb_update_t itlb_update_o,
|
||||
output tlb_update_t dtlb_update_o,
|
||||
|
||||
output logic [CVA6Cfg.VLEN-1:0] update_vaddr_o,
|
||||
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_i,
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_i,
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_i,
|
||||
// from TLBs
|
||||
// did we miss?
|
||||
input logic itlb_access_i,
|
||||
input logic itlb_hit_i,
|
||||
input logic [ CVA6Cfg.VLEN-1:0] itlb_vaddr_i,
|
||||
|
||||
input logic dtlb_access_i,
|
||||
input logic dtlb_hit_i,
|
||||
input logic [CVA6Cfg.VLEN-1:0] dtlb_vaddr_i,
|
||||
// from CSR file
|
||||
input logic [CVA6Cfg.PPNW-1:0] satp_ppn_i, // ppn from satp
|
||||
input logic [CVA6Cfg.PPNW-1:0] vsatp_ppn_i, // ppn from satp
|
||||
input logic [CVA6Cfg.PPNW-1:0] hgatp_ppn_i, // ppn from hgatp
|
||||
input logic mxr_i,
|
||||
input logic vmxr_i,
|
||||
// Performance counters
|
||||
output logic itlb_miss_o,
|
||||
output logic dtlb_miss_o,
|
||||
// PMP
|
||||
|
||||
input riscv::pmpcfg_t [15:0] pmpcfg_i,
|
||||
input logic [15:0][CVA6Cfg.PLEN-3:0] pmpaddr_i,
|
||||
output logic [CVA6Cfg.GPLEN-1:0] bad_gpaddr_o
|
||||
|
||||
);
|
||||
|
||||
// input registers
|
||||
logic data_rvalid_q;
|
||||
logic [63:0] data_rdata_q;
|
||||
|
||||
riscv::pte_t pte;
|
||||
// register to perform context switch between stages
|
||||
riscv::pte_t gpte_q, gpte_d;
|
||||
assign pte = riscv::pte_t'(data_rdata_q);
|
||||
|
||||
enum logic [2:0] {
|
||||
IDLE,
|
||||
WAIT_GRANT,
|
||||
PTE_LOOKUP,
|
||||
WAIT_RVALID,
|
||||
PROPAGATE_ERROR,
|
||||
PROPAGATE_ACCESS_ERROR
|
||||
}
|
||||
state_q, state_d;
|
||||
|
||||
// SV39 defines three levels of page tables
|
||||
enum logic [1:0] {
|
||||
LVL1,
|
||||
LVL2,
|
||||
LVL3
|
||||
}
|
||||
ptw_lvl_q, ptw_lvl_n, gptw_lvl_n, gptw_lvl_q;
|
||||
|
||||
// define 3 PTW stages
|
||||
// S_STAGE -> S/VS-stage normal translation controlled by the satp/vsatp CSRs
|
||||
// G_INTERMED_STAGE -> Converts the S/VS-stage non-leaf GPA pointers to HPA (controlled by hgatp)
|
||||
// G_FINAL_STAGE -> Converts the S/VS-stage final GPA to HPA (controlled by hgatp)
|
||||
enum logic [1:0] {
|
||||
S_STAGE,
|
||||
G_INTERMED_STAGE,
|
||||
G_FINAL_STAGE
|
||||
}
|
||||
ptw_stage_q, ptw_stage_d;
|
||||
|
||||
// is this an instruction page table walk?
|
||||
logic is_instr_ptw_q, is_instr_ptw_n;
|
||||
logic global_mapping_q, global_mapping_n;
|
||||
// latched tag signal
|
||||
logic tag_valid_n, tag_valid_q;
|
||||
// register the ASID
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] tlb_update_asid_q, tlb_update_asid_n;
|
||||
// register the VMID
|
||||
logic [CVA6Cfg.VMID_WIDTH-1:0] tlb_update_vmid_q, tlb_update_vmid_n;
|
||||
// register the VPN we need to walk, SV39 defines a 39 bit virtual address
|
||||
logic [CVA6Cfg.VLEN-1:0] vaddr_q, vaddr_n;
|
||||
// register the VPN we need to walk, SV39x4 defines a 41 bit virtual address for the G-Stage
|
||||
logic [CVA6Cfg.GPLEN-1:0] gpaddr_q, gpaddr_n;
|
||||
// 4 byte aligned physical pointer
|
||||
logic [CVA6Cfg.PLEN-1:0] ptw_pptr_q, ptw_pptr_n;
|
||||
logic [CVA6Cfg.PLEN-1:0] gptw_pptr_q, gptw_pptr_n;
|
||||
|
||||
// Assignments
|
||||
assign update_vaddr_o = vaddr_q;
|
||||
|
||||
assign ptw_active_o = (state_q != IDLE);
|
||||
assign walking_instr_o = is_instr_ptw_q;
|
||||
// directly output the correct physical address
|
||||
assign req_port_o.address_index = ptw_pptr_q[CVA6Cfg.DCACHE_INDEX_WIDTH-1:0];
|
||||
assign req_port_o.address_tag = ptw_pptr_q[CVA6Cfg.DCACHE_INDEX_WIDTH+CVA6Cfg.DCACHE_TAG_WIDTH-1:CVA6Cfg.DCACHE_INDEX_WIDTH];
|
||||
// we are never going to kill this request
|
||||
assign req_port_o.kill_req = '0;
|
||||
// we are never going to write with the HPTW
|
||||
assign req_port_o.data_wdata = 64'b0;
|
||||
|
||||
// -----------
|
||||
// TLB Update
|
||||
// -----------
|
||||
always_comb begin : tlb_update
|
||||
|
||||
itlb_update_o.vpn = {{41 - CVA6Cfg.SVX{1'b0}}, vaddr_q[CVA6Cfg.SVX-1:12]};
|
||||
dtlb_update_o.vpn = {{41 - CVA6Cfg.SVX{1'b0}}, vaddr_q[CVA6Cfg.SVX-1:12]};
|
||||
// update the correct page table level
|
||||
if (enable_g_translation_i && enable_translation_i) begin
|
||||
itlb_update_o.is_s_2M = (gptw_lvl_q == LVL2);
|
||||
itlb_update_o.is_s_1G = (gptw_lvl_q == LVL1);
|
||||
itlb_update_o.is_g_2M = (ptw_lvl_q == LVL2);
|
||||
itlb_update_o.is_g_1G = (ptw_lvl_q == LVL1);
|
||||
end else if (enable_translation_i) begin
|
||||
itlb_update_o.is_s_2M = (ptw_lvl_q == LVL2);
|
||||
itlb_update_o.is_s_1G = (ptw_lvl_q == LVL1);
|
||||
itlb_update_o.is_g_2M = 1'b0;
|
||||
itlb_update_o.is_g_1G = 1'b0;
|
||||
end else begin
|
||||
itlb_update_o.is_s_2M = 1'b0;
|
||||
itlb_update_o.is_s_1G = 1'b0;
|
||||
itlb_update_o.is_g_2M = (ptw_lvl_q == LVL2);
|
||||
itlb_update_o.is_g_1G = (ptw_lvl_q == LVL1);
|
||||
end
|
||||
|
||||
if (en_ld_st_g_translation_i && en_ld_st_translation_i) begin
|
||||
dtlb_update_o.is_s_2M = (gptw_lvl_q == LVL2);
|
||||
dtlb_update_o.is_s_1G = (gptw_lvl_q == LVL1);
|
||||
dtlb_update_o.is_g_2M = (ptw_lvl_q == LVL2);
|
||||
dtlb_update_o.is_g_1G = (ptw_lvl_q == LVL1);
|
||||
end else if (en_ld_st_translation_i) begin
|
||||
dtlb_update_o.is_s_2M = (ptw_lvl_q == LVL2);
|
||||
dtlb_update_o.is_s_1G = (ptw_lvl_q == LVL1);
|
||||
dtlb_update_o.is_g_2M = 1'b0;
|
||||
dtlb_update_o.is_g_1G = 1'b0;
|
||||
end else begin
|
||||
dtlb_update_o.is_s_2M = 1'b0;
|
||||
dtlb_update_o.is_s_1G = 1'b0;
|
||||
dtlb_update_o.is_g_2M = (ptw_lvl_q == LVL2);
|
||||
dtlb_update_o.is_g_1G = (ptw_lvl_q == LVL1);
|
||||
end
|
||||
// output the correct ASID
|
||||
itlb_update_o.asid = tlb_update_asid_q;
|
||||
dtlb_update_o.asid = tlb_update_asid_q;
|
||||
// output the correct VMID
|
||||
itlb_update_o.vmid = tlb_update_vmid_q;
|
||||
dtlb_update_o.vmid = tlb_update_vmid_q;
|
||||
// set the global mapping bit
|
||||
if (enable_g_translation_i) begin
|
||||
itlb_update_o.content = gpte_q | (global_mapping_q << 5);
|
||||
itlb_update_o.g_content = pte;
|
||||
end else begin
|
||||
itlb_update_o.content = pte | (global_mapping_q << 5);
|
||||
itlb_update_o.g_content = '0;
|
||||
end
|
||||
if (en_ld_st_g_translation_i) begin
|
||||
dtlb_update_o.content = gpte_q | (global_mapping_q << 5);
|
||||
dtlb_update_o.g_content = pte;
|
||||
end else begin
|
||||
dtlb_update_o.content = pte | (global_mapping_q << 5);
|
||||
dtlb_update_o.g_content = '0;
|
||||
end
|
||||
end
|
||||
|
||||
assign req_port_o.tag_valid = tag_valid_q;
|
||||
|
||||
logic allow_access;
|
||||
|
||||
assign bad_gpaddr_o = ptw_error_at_g_st_o ? ((ptw_stage_q == G_INTERMED_STAGE) ? gptw_pptr_q[CVA6Cfg.GPLEN:0] : gpaddr_q) : 'b0;
|
||||
|
||||
pmp #(
|
||||
.CVA6Cfg (CVA6Cfg),
|
||||
.PLEN (CVA6Cfg.PLEN),
|
||||
.PMP_LEN (CVA6Cfg.PLEN - 2),
|
||||
.NR_ENTRIES(CVA6Cfg.NrPMPEntries)
|
||||
) i_pmp_ptw (
|
||||
.addr_i (ptw_pptr_q),
|
||||
// PTW access are always checked as if in S-Mode...
|
||||
.priv_lvl_i (riscv::PRIV_LVL_S),
|
||||
// ...and they are always loads
|
||||
.access_type_i(riscv::ACCESS_READ),
|
||||
// Configuration
|
||||
.conf_addr_i (pmpaddr_i),
|
||||
.conf_i (pmpcfg_i),
|
||||
.allow_o (allow_access)
|
||||
);
|
||||
|
||||
//-------------------
|
||||
// Page table walker
|
||||
//-------------------
|
||||
// A virtual address va is translated into a physical address pa as follows:
|
||||
// 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39,
|
||||
// PAGESIZE=2^12 and LEVELS=3.)
|
||||
// 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE. (For
|
||||
// Sv32, PTESIZE=4.)
|
||||
// 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, or if any bits or encodings
|
||||
// that are reserved for future standard use are set within pte, stop and raise
|
||||
// a page-fault exception corresponding to the original access type.
|
||||
// 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5.
|
||||
// Otherwise, this PTE is a pointer to the next level of the page table.
|
||||
// Let i=i-1. If i < 0, stop and raise an access exception. Otherwise, let
|
||||
// a = pte.ppn × PAGESIZE and go to step 2.
|
||||
// 5. A leaf PTE has been found. Determine if the requested memory access
|
||||
// is allowed by the pte.r, pte.w, and pte.x bits. If not, stop and
|
||||
// raise an access exception. Otherwise, the translation is successful.
|
||||
// Set pte.a to 1, and, if the memory access is a store, set pte.d to 1.
|
||||
// The translated physical address is given as follows:
|
||||
// - pa.pgoff = va.pgoff.
|
||||
// - If i > 0, then this is a superpage translation and
|
||||
// pa.ppn[i-1:0] = va.vpn[i-1:0].
|
||||
// - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
|
||||
always_comb begin : ptw
|
||||
automatic logic [ CVA6Cfg.PLEN-1:0] pptr;
|
||||
automatic logic [CVA6Cfg.GPLEN-1:0] gpaddr;
|
||||
// default assignments
|
||||
// PTW memory interface
|
||||
tag_valid_n = 1'b0;
|
||||
req_port_o.data_req = 1'b0;
|
||||
req_port_o.data_be = 8'hFF;
|
||||
req_port_o.data_size = 2'b11;
|
||||
req_port_o.data_we = 1'b0;
|
||||
ptw_error_o = 1'b0;
|
||||
ptw_error_at_g_st_o = 1'b0;
|
||||
ptw_err_at_g_int_st_o = 1'b0;
|
||||
ptw_access_exception_o = 1'b0;
|
||||
itlb_update_o.valid = 1'b0;
|
||||
dtlb_update_o.valid = 1'b0;
|
||||
is_instr_ptw_n = is_instr_ptw_q;
|
||||
ptw_lvl_n = ptw_lvl_q;
|
||||
gptw_lvl_n = gptw_lvl_q;
|
||||
ptw_pptr_n = ptw_pptr_q;
|
||||
gptw_pptr_n = gptw_pptr_q;
|
||||
state_d = state_q;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
gpte_d = gpte_q;
|
||||
global_mapping_n = global_mapping_q;
|
||||
// input registers
|
||||
tlb_update_asid_n = tlb_update_asid_q;
|
||||
tlb_update_vmid_n = tlb_update_vmid_q;
|
||||
vaddr_n = vaddr_q;
|
||||
gpaddr_n = gpaddr_q;
|
||||
pptr = ptw_pptr_q;
|
||||
gpaddr = gpaddr_q;
|
||||
|
||||
itlb_miss_o = 1'b0;
|
||||
dtlb_miss_o = 1'b0;
|
||||
|
||||
case (state_q)
|
||||
|
||||
IDLE: begin
|
||||
// by default we start with the top-most page table
|
||||
ptw_lvl_n = LVL1;
|
||||
gptw_lvl_n = LVL1;
|
||||
global_mapping_n = 1'b0;
|
||||
is_instr_ptw_n = 1'b0;
|
||||
gpaddr_n = '0;
|
||||
gpte_d = '0;
|
||||
// if we got an ITLB miss
|
||||
if ((enable_translation_i | enable_g_translation_i) & itlb_access_i & ~itlb_hit_i & ~dtlb_access_i) begin
|
||||
if (enable_translation_i && enable_g_translation_i) begin
|
||||
ptw_stage_d = G_INTERMED_STAGE;
|
||||
pptr = {vsatp_ppn_i, itlb_vaddr_i[CVA6Cfg.SV-1:30], 3'b0};
|
||||
gptw_pptr_n = pptr;
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], pptr[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
end else if (!enable_translation_i && enable_g_translation_i) begin
|
||||
ptw_stage_d = G_FINAL_STAGE;
|
||||
gpaddr_n = itlb_vaddr_i[CVA6Cfg.SVX-1:0];
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], itlb_vaddr_i[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
end else begin
|
||||
ptw_stage_d = S_STAGE;
|
||||
if (v_i) ptw_pptr_n = {vsatp_ppn_i, itlb_vaddr_i[CVA6Cfg.SV-1:30], 3'b0};
|
||||
else ptw_pptr_n = {satp_ppn_i, itlb_vaddr_i[CVA6Cfg.SV-1:30], 3'b0};
|
||||
end
|
||||
is_instr_ptw_n = 1'b1;
|
||||
tlb_update_asid_n = v_i ? vs_asid_i : asid_i;
|
||||
tlb_update_vmid_n = vmid_i;
|
||||
vaddr_n = itlb_vaddr_i;
|
||||
state_d = WAIT_GRANT;
|
||||
itlb_miss_o = 1'b1;
|
||||
// we got an DTLB miss
|
||||
end else if ((en_ld_st_translation_i || en_ld_st_g_translation_i) & dtlb_access_i & ~dtlb_hit_i) begin
|
||||
if (en_ld_st_translation_i && en_ld_st_g_translation_i) begin
|
||||
ptw_stage_d = G_INTERMED_STAGE;
|
||||
pptr = {vsatp_ppn_i, dtlb_vaddr_i[CVA6Cfg.SV-1:30], 3'b0};
|
||||
gptw_pptr_n = pptr;
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], pptr[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
end else if (!en_ld_st_translation_i && en_ld_st_g_translation_i) begin
|
||||
ptw_stage_d = G_FINAL_STAGE;
|
||||
gpaddr_n = dtlb_vaddr_i[CVA6Cfg.SVX-1:0];
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], dtlb_vaddr_i[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
end else begin
|
||||
ptw_stage_d = S_STAGE;
|
||||
if (ld_st_v_i) ptw_pptr_n = {vsatp_ppn_i, dtlb_vaddr_i[CVA6Cfg.SV-1:30], 3'b0};
|
||||
else ptw_pptr_n = {satp_ppn_i, dtlb_vaddr_i[CVA6Cfg.SV-1:30], 3'b0};
|
||||
end
|
||||
tlb_update_asid_n = ld_st_v_i ? vs_asid_i : asid_i;
|
||||
tlb_update_vmid_n = vmid_i;
|
||||
vaddr_n = dtlb_vaddr_i;
|
||||
state_d = WAIT_GRANT;
|
||||
dtlb_miss_o = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
WAIT_GRANT: begin
|
||||
// send a request out
|
||||
req_port_o.data_req = 1'b1;
|
||||
// wait for the WAIT_GRANT
|
||||
if (req_port_i.data_gnt) begin
|
||||
// send the tag valid signal one cycle later
|
||||
tag_valid_n = 1'b1;
|
||||
state_d = PTE_LOOKUP;
|
||||
end
|
||||
end
|
||||
|
||||
PTE_LOOKUP: begin
|
||||
// we wait for the valid signal
|
||||
if (data_rvalid_q) begin
|
||||
|
||||
// check if the global mapping bit is set
|
||||
if (pte.g && ptw_stage_q == S_STAGE) 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) || (|pte.reserved)) state_d = PROPAGATE_ERROR;
|
||||
// -----------
|
||||
// Valid PTE
|
||||
// -----------
|
||||
else begin
|
||||
state_d = IDLE;
|
||||
// it is a valid PTE
|
||||
// if pte.r = 1 or pte.x = 1 it is a valid PTE
|
||||
if (pte.r || pte.x) begin
|
||||
case (ptw_stage_q)
|
||||
S_STAGE: begin
|
||||
if ((is_instr_ptw_q && enable_g_translation_i) || (!is_instr_ptw_q && en_ld_st_g_translation_i)) begin
|
||||
state_d = WAIT_GRANT;
|
||||
ptw_stage_d = G_FINAL_STAGE;
|
||||
gpte_d = pte;
|
||||
gptw_lvl_n = ptw_lvl_q;
|
||||
gpaddr = {pte.ppn[CVA6Cfg.GPPNW-1:0], vaddr_q[11:0]};
|
||||
if (ptw_lvl_q == LVL2) gpaddr[20:0] = vaddr_q[20:0];
|
||||
if (ptw_lvl_q == LVL1) gpaddr[29:0] = vaddr_q[29:0];
|
||||
gpaddr_n = gpaddr;
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], gpaddr[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
ptw_lvl_n = LVL1;
|
||||
end
|
||||
end
|
||||
G_INTERMED_STAGE: begin
|
||||
state_d = WAIT_GRANT;
|
||||
ptw_stage_d = S_STAGE;
|
||||
ptw_lvl_n = gptw_lvl_q;
|
||||
pptr = {pte.ppn[CVA6Cfg.GPPNW-1:0], gptw_pptr_q[11:0]};
|
||||
if (ptw_lvl_q == LVL2) pptr[20:0] = gptw_pptr_q[20:0];
|
||||
if (ptw_lvl_q == LVL1) pptr[29:0] = gptw_pptr_q[29:0];
|
||||
ptw_pptr_n = pptr;
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
// Valid translation found (either 1G, 2M or 4K entry)
|
||||
if (is_instr_ptw_q) begin
|
||||
// ------------
|
||||
// Update ITLB
|
||||
// ------------
|
||||
// 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) begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
end else if ((ptw_stage_q == G_FINAL_STAGE) || !enable_g_translation_i)
|
||||
itlb_update_o.valid = 1'b1;
|
||||
|
||||
end else begin
|
||||
// ------------
|
||||
// Update DTLB
|
||||
// ------------
|
||||
// Check if the access flag has been set, otherwise throw a page-fault
|
||||
// and let the software handle those bits.
|
||||
// If page is not readable (there are no write-only pages)
|
||||
// we can directly raise an error. This doesn't put a useless
|
||||
// entry into the TLB.
|
||||
if (pte.a && ((pte.r && !hlvx_inst_i) || (pte.x && (mxr_i || hlvx_inst_i || (ptw_stage_q == S_STAGE && vmxr_i && ld_st_v_i))))) begin
|
||||
if ((ptw_stage_q == G_FINAL_STAGE) || !en_ld_st_g_translation_i)
|
||||
dtlb_update_o.valid = 1'b1;
|
||||
end else begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
end
|
||||
// Request is a store: perform some additional checks
|
||||
// If the request was a store and the page is not write-able, raise an error
|
||||
// the same applies if the dirty flag is not set
|
||||
if (lsu_is_store_i && (!pte.w || !pte.d)) begin
|
||||
dtlb_update_o.valid = 1'b0;
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
end
|
||||
end
|
||||
// check if the ppn is correctly aligned:
|
||||
// 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage; stop and raise a page-fault
|
||||
// exception.
|
||||
if (ptw_lvl_q == LVL1 && pte.ppn[17:0] != '0) begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
dtlb_update_o.valid = 1'b0;
|
||||
itlb_update_o.valid = 1'b0;
|
||||
end else if (ptw_lvl_q == LVL2 && pte.ppn[8:0] != '0) begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
dtlb_update_o.valid = 1'b0;
|
||||
itlb_update_o.valid = 1'b0;
|
||||
end
|
||||
// check if 63:41 are all zeros
|
||||
if (((v_i && is_instr_ptw_q) || (ld_st_v_i && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte.ppn[CVA6Cfg.PPNW-1:CVA6Cfg.GPPNW]) == 1'b0)) begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = G_FINAL_STAGE;
|
||||
end
|
||||
// this is a pointer to the next TLB level
|
||||
end else begin
|
||||
// pointer to next level of page table
|
||||
if (ptw_lvl_q == LVL1) begin
|
||||
// we are in the second level now
|
||||
ptw_lvl_n = LVL2;
|
||||
case (ptw_stage_q)
|
||||
S_STAGE: begin
|
||||
if ((is_instr_ptw_q && enable_g_translation_i) || (!is_instr_ptw_q && en_ld_st_g_translation_i)) begin
|
||||
ptw_stage_d = G_INTERMED_STAGE;
|
||||
gpte_d = pte;
|
||||
gptw_lvl_n = LVL2;
|
||||
pptr = {pte.ppn, vaddr_q[29:21], 3'b0};
|
||||
gptw_pptr_n = pptr;
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], pptr[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
ptw_lvl_n = LVL1;
|
||||
end else begin
|
||||
ptw_pptr_n = {pte.ppn, vaddr_q[29:21], 3'b0};
|
||||
end
|
||||
end
|
||||
G_INTERMED_STAGE: begin
|
||||
ptw_pptr_n = {pte.ppn, gptw_pptr_q[29:21], 3'b0};
|
||||
end
|
||||
G_FINAL_STAGE: begin
|
||||
ptw_pptr_n = {pte.ppn, gpaddr_q[29:21], 3'b0};
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
|
||||
if (ptw_lvl_q == LVL2) begin
|
||||
// here we received a pointer to the third level
|
||||
ptw_lvl_n = LVL3;
|
||||
unique case (ptw_stage_q)
|
||||
S_STAGE: begin
|
||||
if ((is_instr_ptw_q && enable_g_translation_i) || (!is_instr_ptw_q && en_ld_st_g_translation_i)) begin
|
||||
ptw_stage_d = G_INTERMED_STAGE;
|
||||
gpte_d = pte;
|
||||
gptw_lvl_n = LVL3;
|
||||
pptr = {pte.ppn, vaddr_q[20:12], 3'b0};
|
||||
gptw_pptr_n = pptr;
|
||||
ptw_pptr_n = {hgatp_ppn_i[CVA6Cfg.PPNW-1:2], pptr[CVA6Cfg.SVX-1:30], 3'b0};
|
||||
ptw_lvl_n = LVL1;
|
||||
end else begin
|
||||
ptw_pptr_n = {pte.ppn, vaddr_q[20:12], 3'b0};
|
||||
end
|
||||
end
|
||||
G_INTERMED_STAGE: begin
|
||||
ptw_pptr_n = {pte.ppn, gptw_pptr_q[20:12], 3'b0};
|
||||
end
|
||||
G_FINAL_STAGE: begin
|
||||
ptw_pptr_n = {pte.ppn, gpaddr_q[20:12], 3'b0};
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
|
||||
state_d = WAIT_GRANT;
|
||||
// check if reserved bits are cleared for non-leaf entries
|
||||
if (pte.a || pte.d || pte.u) begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
end
|
||||
if (ptw_lvl_q == LVL3) begin
|
||||
// Should already be the last level page table => Error
|
||||
ptw_lvl_n = LVL3;
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
end
|
||||
// check if 63:41 are all zeros
|
||||
if (((v_i && is_instr_ptw_q) || (ld_st_v_i && !is_instr_ptw_q)) && ptw_stage_q == S_STAGE && !((|pte.ppn[CVA6Cfg.PPNW-1:CVA6Cfg.GPPNW]) == 1'b0)) begin
|
||||
state_d = PROPAGATE_ERROR;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Check if this access was actually allowed from a PMP perspective
|
||||
if (!allow_access) begin
|
||||
itlb_update_o.valid = 1'b0;
|
||||
dtlb_update_o.valid = 1'b0;
|
||||
// we have to return the failed address in bad_addr
|
||||
ptw_pptr_n = ptw_pptr_q;
|
||||
ptw_stage_d = ptw_stage_q;
|
||||
state_d = PROPAGATE_ACCESS_ERROR;
|
||||
end
|
||||
end
|
||||
// we've got a data WAIT_GRANT so tell the cache that the tag is valid
|
||||
end
|
||||
// Propagate error to MMU/LSU
|
||||
PROPAGATE_ERROR: begin
|
||||
state_d = IDLE;
|
||||
ptw_error_o = 1'b1;
|
||||
ptw_error_at_g_st_o = (ptw_stage_q != S_STAGE) ? 1'b1 : 1'b0;
|
||||
ptw_err_at_g_int_st_o = (ptw_stage_q == G_INTERMED_STAGE) ? 1'b1 : 1'b0;
|
||||
end
|
||||
PROPAGATE_ACCESS_ERROR: begin
|
||||
state_d = IDLE;
|
||||
ptw_access_exception_o = 1'b1;
|
||||
end
|
||||
// wait for the rvalid before going back to IDLE
|
||||
WAIT_RVALID: begin
|
||||
if (data_rvalid_q) state_d = IDLE;
|
||||
end
|
||||
default: begin
|
||||
state_d = IDLE;
|
||||
end
|
||||
endcase
|
||||
|
||||
// -------
|
||||
// Flush
|
||||
// -------
|
||||
// should we have flushed before we got an rvalid, wait for it until going back to IDLE
|
||||
if (flush_i) begin
|
||||
// on a flush check whether we are
|
||||
// 1. in the PTE Lookup check whether we still need to wait for an rvalid
|
||||
// 2. waiting for a grant, if so: wait for it
|
||||
// if not, go back to idle
|
||||
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;
|
||||
end
|
||||
end
|
||||
|
||||
// sequential process
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
state_q <= IDLE;
|
||||
ptw_stage_q <= S_STAGE;
|
||||
is_instr_ptw_q <= 1'b0;
|
||||
ptw_lvl_q <= LVL1;
|
||||
gptw_lvl_q <= LVL1;
|
||||
tag_valid_q <= 1'b0;
|
||||
tlb_update_asid_q <= '0;
|
||||
tlb_update_vmid_q <= '0;
|
||||
vaddr_q <= '0;
|
||||
gpaddr_q <= '0;
|
||||
ptw_pptr_q <= '0;
|
||||
gptw_pptr_q <= '0;
|
||||
global_mapping_q <= 1'b0;
|
||||
data_rdata_q <= '0;
|
||||
gpte_q <= '0;
|
||||
data_rvalid_q <= 1'b0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
ptw_stage_q <= ptw_stage_d;
|
||||
ptw_pptr_q <= ptw_pptr_n;
|
||||
gptw_pptr_q <= gptw_pptr_n;
|
||||
is_instr_ptw_q <= is_instr_ptw_n;
|
||||
ptw_lvl_q <= ptw_lvl_n;
|
||||
gptw_lvl_q <= gptw_lvl_n;
|
||||
tag_valid_q <= tag_valid_n;
|
||||
tlb_update_asid_q <= tlb_update_asid_n;
|
||||
tlb_update_vmid_q <= tlb_update_vmid_n;
|
||||
vaddr_q <= vaddr_n;
|
||||
gpaddr_q <= gpaddr_n;
|
||||
global_mapping_q <= global_mapping_n;
|
||||
data_rdata_q <= req_port_i.data_rdata;
|
||||
gpte_q <= gpte_d;
|
||||
data_rvalid_q <= req_port_i.data_rvalid;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
/* verilator lint_on WIDTH */
|
415
core/mmu_sv39x4/cva6_tlb_sv39x4.sv
Normal file
415
core/mmu_sv39x4/cva6_tlb_sv39x4.sv
Normal file
|
@ -0,0 +1,415 @@
|
|||
// Copyright (c) 2022 Bruno Sá and Zero-Day Labs.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
//
|
||||
// Author: Bruno Sá
|
||||
// Date: 14/08/2022
|
||||
// Acknowledges: Technology Innovation Institute (TII)
|
||||
//
|
||||
// Description: Translation Lookaside Buffer, Sv39x4 , fully set-associative
|
||||
// This module is an adaptation of the Sv39 TLB developed
|
||||
// by Florian Zaruba and David Schaffenrath to the Sv39x4 standard.
|
||||
|
||||
|
||||
module cva6_tlb_sv39x4
|
||||
import ariane_pkg::*;
|
||||
#(
|
||||
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
|
||||
parameter type tlb_update_t = logic,
|
||||
parameter int unsigned TLB_ENTRIES = 4
|
||||
) (
|
||||
input logic clk_i, // Clock
|
||||
input logic rst_ni, // Asynchronous reset active low
|
||||
input logic flush_i, // Flush normal translations signal
|
||||
input logic flush_vvma_i, // Flush vs stage signal
|
||||
input logic flush_gvma_i, // Flush g stage signal
|
||||
input logic s_st_enbl_i, // s-stage enabled
|
||||
input logic g_st_enbl_i, // g-stage enabled
|
||||
input logic v_i, // virtualization mode
|
||||
// Update TLB
|
||||
input tlb_update_t update_i,
|
||||
// Lookup signals
|
||||
input logic lu_access_i,
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] lu_asid_i,
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] lu_vmid_i,
|
||||
input logic [CVA6Cfg.VLEN-1:0] lu_vaddr_i,
|
||||
output logic [CVA6Cfg.GPLEN-1:0] lu_gpaddr_o,
|
||||
output riscv::pte_t lu_content_o,
|
||||
output riscv::pte_t lu_g_content_o,
|
||||
input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_to_be_flushed_i,
|
||||
input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_to_be_flushed_i,
|
||||
input logic [CVA6Cfg.VLEN-1:0] vaddr_to_be_flushed_i,
|
||||
input logic [CVA6Cfg.GPLEN-1:0] gpaddr_to_be_flushed_i,
|
||||
output logic lu_is_2M_o,
|
||||
output logic lu_is_1G_o,
|
||||
output logic lu_hit_o
|
||||
);
|
||||
localparam VPN2 = (CVA6Cfg.VLEN - 31 < 8) ? CVA6Cfg.VLEN - 31 : 8;
|
||||
localparam GPPN2 = (CVA6Cfg.XLEN == 32) ? CVA6Cfg.VLEN - 33 : 10;
|
||||
|
||||
// SV39 defines three levels of page tables
|
||||
struct packed {
|
||||
logic [CVA6Cfg.ASID_WIDTH-1:0] asid;
|
||||
logic [CVA6Cfg.VMID_WIDTH-1:0] vmid;
|
||||
logic [GPPN2:0] vpn2;
|
||||
logic [8:0] vpn1;
|
||||
logic [8:0] vpn0;
|
||||
logic is_s_2M;
|
||||
logic is_s_1G;
|
||||
logic is_g_2M;
|
||||
logic is_g_1G;
|
||||
logic s_st_enbl; // s-stage translation
|
||||
logic g_st_enbl; // g-stage translation
|
||||
logic v; // virtualization mode
|
||||
logic valid;
|
||||
} [TLB_ENTRIES-1:0]
|
||||
tags_q, tags_n;
|
||||
|
||||
struct packed {
|
||||
riscv::pte_t pte;
|
||||
riscv::pte_t gpte;
|
||||
} [TLB_ENTRIES-1:0]
|
||||
content_q, content_n;
|
||||
|
||||
logic [8:0] vpn0, vpn1;
|
||||
logic [GPPN2:0] vpn2;
|
||||
logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
|
||||
logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
|
||||
logic [TLB_ENTRIES-1:0] match_vmid;
|
||||
logic [TLB_ENTRIES-1:0] match_asid;
|
||||
logic [TLB_ENTRIES-1:0] is_1G;
|
||||
logic [TLB_ENTRIES-1:0] is_2M;
|
||||
logic [TLB_ENTRIES-1:0] match_stage;
|
||||
riscv::pte_t g_content;
|
||||
//-------------
|
||||
// Translation
|
||||
//-------------
|
||||
always_comb begin : translation
|
||||
automatic logic [GPPN2:0] mask_pn2;
|
||||
mask_pn2 = s_st_enbl_i ? ((2 ** (VPN2 + 1)) - 1) : ((2 ** (GPPN2 + 1)) - 1);
|
||||
vpn0 = lu_vaddr_i[20:12];
|
||||
vpn1 = lu_vaddr_i[29:21];
|
||||
vpn2 = lu_vaddr_i[30+GPPN2:30] & mask_pn2;
|
||||
|
||||
// default assignment
|
||||
lu_hit = '{default: 0};
|
||||
lu_hit_o = 1'b0;
|
||||
lu_content_o = '{default: 0};
|
||||
lu_g_content_o = '{default: 0};
|
||||
lu_is_1G_o = 1'b0;
|
||||
lu_is_2M_o = 1'b0;
|
||||
match_asid = '{default: 0};
|
||||
match_vmid = '{default: 0};
|
||||
match_stage = '{default: 0};
|
||||
is_1G = '{default: 0};
|
||||
is_2M = '{default: 0};
|
||||
g_content = '{default: 0};
|
||||
lu_gpaddr_o = '{default: 0};
|
||||
|
||||
|
||||
for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
|
||||
// first level match, this may be a giga page, check the ASID flags as well
|
||||
// if the entry is associated to a global address, don't match the ASID (ASID is don't care)
|
||||
match_asid[i] = (((lu_asid_i == tags_q[i].asid) || content_q[i].pte.g) && s_st_enbl_i) || !s_st_enbl_i;
|
||||
match_vmid[i] = (lu_vmid_i == tags_q[i].vmid && g_st_enbl_i) || !g_st_enbl_i;
|
||||
is_1G[i] = is_trans_1G(s_st_enbl_i, g_st_enbl_i, tags_q[i].is_s_1G, tags_q[i].is_g_1G);
|
||||
is_2M[i] = is_trans_2M(
|
||||
s_st_enbl_i,
|
||||
g_st_enbl_i,
|
||||
tags_q[i].is_s_1G,
|
||||
tags_q[i].is_s_2M,
|
||||
tags_q[i].is_g_1G,
|
||||
tags_q[i].is_g_2M
|
||||
);
|
||||
// check if translation is a: S-Stage and G-Stage, S-Stage only or G-Stage only translation and virtualization mode is on/off
|
||||
match_stage[i] = (tags_q[i].v == v_i) && (tags_q[i].g_st_enbl == g_st_enbl_i) && (tags_q[i].s_st_enbl == s_st_enbl_i);
|
||||
if (tags_q[i].valid && match_asid[i] && match_vmid[i] && match_stage[i] && (vpn2 == (tags_q[i].vpn2 & mask_pn2))) begin
|
||||
lu_gpaddr_o = make_gpaddr(s_st_enbl_i, tags_q[i].is_s_1G, tags_q[i].is_s_2M, lu_vaddr_i,
|
||||
content_q[i].pte);
|
||||
if (is_1G[i]) begin
|
||||
lu_is_1G_o = is_1G[i];
|
||||
lu_content_o = content_q[i].pte;
|
||||
lu_g_content_o = content_q[i].gpte;
|
||||
lu_hit_o = 1'b1;
|
||||
lu_hit[i] = 1'b1;
|
||||
// not a giga page hit so check further
|
||||
end else if (vpn1 == tags_q[i].vpn1) begin
|
||||
// this could be a 2 mega page hit or a 4 kB hit
|
||||
// output accordingly
|
||||
if (is_2M[i] || vpn0 == tags_q[i].vpn0) begin
|
||||
lu_is_2M_o = is_2M[i];
|
||||
// Compute G-Stage PPN based on the gpaddr
|
||||
g_content = content_q[i].gpte;
|
||||
if (tags_q[i].is_g_2M) g_content.ppn[8:0] = lu_gpaddr_o[20:12];
|
||||
if (tags_q[i].is_g_1G) g_content.ppn[17:0] = lu_gpaddr_o[29:12];
|
||||
// Output G-stage and S-stage content
|
||||
lu_g_content_o = g_content;
|
||||
lu_content_o = content_q[i].pte;
|
||||
lu_hit_o = 1'b1;
|
||||
lu_hit[i] = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
logic asid_to_be_flushed_is0; // indicates that the ASID provided by SFENCE.VMA (rs2)is 0, active high
|
||||
logic vaddr_to_be_flushed_is0; // indicates that the VADDR provided by SFENCE.VMA (rs1)is 0, active high
|
||||
logic vmid_to_be_flushed_is0; // indicates that the VMID provided is 0, active high
|
||||
logic gpaddr_to_be_flushed_is0; // indicates that the GPADDR provided is 0, active high
|
||||
logic [TLB_ENTRIES-1:0] vaddr_vpn0_match;
|
||||
logic [TLB_ENTRIES-1:0] vaddr_vpn1_match;
|
||||
logic [TLB_ENTRIES-1:0] vaddr_vpn2_match;
|
||||
logic [TLB_ENTRIES-1:0] gpaddr_gppn0_match;
|
||||
logic [TLB_ENTRIES-1:0] gpaddr_gppn1_match;
|
||||
logic [TLB_ENTRIES-1:0] gpaddr_gppn2_match;
|
||||
logic [TLB_ENTRIES-1:0][(CVA6Cfg.GPPNW-1):0] gppn;
|
||||
|
||||
|
||||
assign asid_to_be_flushed_is0 = ~(|asid_to_be_flushed_i);
|
||||
assign vaddr_to_be_flushed_is0 = ~(|vaddr_to_be_flushed_i);
|
||||
assign vmid_to_be_flushed_is0 = ~(|vmid_to_be_flushed_i);
|
||||
assign gpaddr_to_be_flushed_is0 = ~(|gpaddr_to_be_flushed_i);
|
||||
|
||||
// ------------------
|
||||
// Update and Flush
|
||||
// ------------------
|
||||
always_comb begin : update_flush
|
||||
tags_n = tags_q;
|
||||
content_n = content_q;
|
||||
|
||||
for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
|
||||
|
||||
vaddr_vpn0_match[i] = (vaddr_to_be_flushed_i[20:12] == tags_q[i].vpn0);
|
||||
vaddr_vpn1_match[i] = (vaddr_to_be_flushed_i[29:21] == tags_q[i].vpn1);
|
||||
vaddr_vpn2_match[i] = (vaddr_to_be_flushed_i[30+VPN2:30] == tags_q[i].vpn2[VPN2:0]);
|
||||
|
||||
gppn[i] = make_gppn(
|
||||
tags_q[i].s_st_enbl,
|
||||
tags_q[i].is_s_1G,
|
||||
tags_q[i].is_s_2M,
|
||||
{
|
||||
tags_q[i].vpn2, tags_q[i].vpn1, tags_q[i].vpn0
|
||||
},
|
||||
content_q[i].pte
|
||||
);
|
||||
gpaddr_gppn0_match[i] = (gpaddr_to_be_flushed_i[20:12] == gppn[i][8:0]);
|
||||
gpaddr_gppn1_match[i] = (gpaddr_to_be_flushed_i[29:21] == gppn[i][17:9]);
|
||||
gpaddr_gppn2_match[i] = (gpaddr_to_be_flushed_i[30+GPPN2:30] == gppn[i][18+GPPN2:18]);
|
||||
|
||||
if (flush_i) begin
|
||||
if (!tags_q[i].v) 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;
|
||||
// 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_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M) ) && (~vaddr_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// the entry is flushed if it's not global and asid and vaddr both matches with the entry to be flushed ("SFENCE.VMA vaddr asid" case)
|
||||
else if ((!content_q[i].pte.g) && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M)) && (asid_to_be_flushed_i == tags_q[i].asid) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// the entry is flushed if it's not global, and the asid matches and vaddr is 0. ("SFENCE.VMA 0 asid" case)
|
||||
else if ((!content_q[i].pte.g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid) && (!asid_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
end
|
||||
end else if (flush_vvma_i) begin
|
||||
if (tags_q[i].v && tags_q[i].s_st_enbl) begin
|
||||
// invalidate logic
|
||||
// flush everything if current VMID matches and ASID is 0 and vaddr is 0 ("SFENCE.VMA/HFENCE.VVMA x0 x0" case)
|
||||
if (asid_to_be_flushed_is0 && vaddr_to_be_flushed_is0 && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// flush vaddr in all addressing space if current VMID matches ("SFENCE.VMA/HFENCE.VVMA 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_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M) ) && (~vaddr_to_be_flushed_is0) && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// the entry is flushed if it's not global and asid and vaddr and current VMID matches with the entry to be flushed ("SFENCE.VMA/HFENCE.VVMA vaddr asid" case)
|
||||
else if ((!content_q[i].pte.g) && ((vaddr_vpn0_match[i] && vaddr_vpn1_match[i] && vaddr_vpn2_match[i]) || (vaddr_vpn2_match[i] && tags_q[i].is_s_1G) || (vaddr_vpn1_match[i] && vaddr_vpn2_match[i] && tags_q[i].is_s_2M)) && (asid_to_be_flushed_i == tags_q[i].asid && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl)) && (!vaddr_to_be_flushed_is0) && (!asid_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// the entry is flushed if it's not global, and the asid and the current VMID matches and vaddr is 0. ("SFENCE.VMA/HFENCE.VVMA 0 asid" case)
|
||||
else if ((!content_q[i].pte.g) && (vaddr_to_be_flushed_is0) && (asid_to_be_flushed_i == tags_q[i].asid && ((tags_q[i].g_st_enbl && lu_vmid_i == tags_q[i].vmid) || !tags_q[i].g_st_enbl)) && (!asid_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
end
|
||||
end else if (flush_gvma_i) begin
|
||||
if (tags_q[i].g_st_enbl) begin
|
||||
// invalidate logic
|
||||
// flush everything if vmid is 0 and addr is 0 ("HFENCE.GVMA x0 x0" case)
|
||||
if (vmid_to_be_flushed_is0 && gpaddr_to_be_flushed_is0) tags_n[i].valid = 1'b0;
|
||||
// flush gpaddr in all addressing space ("HFENCE.GVMA gpaddr x0" case), it should happen only for leaf pages
|
||||
else if (vmid_to_be_flushed_is0 && ((gpaddr_gppn0_match[i] && gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i]) || (gpaddr_gppn2_match[i] && tags_q[i].is_g_1G) || (gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i] && tags_q[i].is_g_2M) ) && (~gpaddr_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// the entry vmid and gpaddr both matches with the entry to be flushed ("HFENCE.GVMA gpaddr vmid" case)
|
||||
else if (((gpaddr_gppn0_match[i] && gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i]) || (gpaddr_gppn2_match[i] && tags_q[i].is_g_1G) || (gpaddr_gppn1_match[i] && gpaddr_gppn2_match[i] && tags_q[i].is_g_2M)) && (vmid_to_be_flushed_i == tags_q[i].vmid) && (~gpaddr_to_be_flushed_is0) && (~vmid_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
// the entry is flushed if the vmid matches and gpaddr is 0. ("HFENCE.GVMA 0 vmid" case)
|
||||
else if ((gpaddr_to_be_flushed_is0) && (vmid_to_be_flushed_i == tags_q[i].vmid) && (!vmid_to_be_flushed_is0))
|
||||
tags_n[i].valid = 1'b0;
|
||||
end
|
||||
// normal replacement
|
||||
end else if (update_i.valid & replace_en[i]) begin
|
||||
// update tag array
|
||||
tags_n[i] = '{
|
||||
asid: update_i.asid,
|
||||
vmid: update_i.vmid,
|
||||
vpn2: update_i.vpn[18+GPPN2:18],
|
||||
vpn1: update_i.vpn[17:9],
|
||||
vpn0: update_i.vpn[8:0],
|
||||
s_st_enbl: s_st_enbl_i,
|
||||
g_st_enbl: g_st_enbl_i,
|
||||
v: v_i,
|
||||
is_s_1G: update_i.is_s_1G,
|
||||
is_s_2M: update_i.is_s_2M,
|
||||
is_g_1G: update_i.is_g_1G,
|
||||
is_g_2M: update_i.is_g_2M,
|
||||
valid: 1'b1
|
||||
};
|
||||
// and content as well
|
||||
content_n[i].pte = update_i.content;
|
||||
content_n[i].gpte = update_i.g_content;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// -----------------------------------------------
|
||||
// PLRU - Pseudo Least Recently Used Replacement
|
||||
// -----------------------------------------------
|
||||
logic [2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
|
||||
always_comb begin : plru_replacement
|
||||
plru_tree_n = plru_tree_q;
|
||||
// The PLRU-tree indexing:
|
||||
// lvl0 0
|
||||
// / \
|
||||
// / \
|
||||
// lvl1 1 2
|
||||
// / \ / \
|
||||
// lvl2 3 4 5 6
|
||||
// / \ /\/\ /\
|
||||
// ... ... ... ...
|
||||
// Just predefine which nodes will be set/cleared
|
||||
// E.g. for a TLB with 8 entries, the for-loop is semantically
|
||||
// equivalent to the following pseudo-code:
|
||||
// unique case (1'b1)
|
||||
// lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1};
|
||||
// lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0};
|
||||
// lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1};
|
||||
// lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0};
|
||||
// lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1};
|
||||
// lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0};
|
||||
// lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1};
|
||||
// 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
|
||||
automatic int unsigned idx_base, shift, new_index;
|
||||
// we got a hit so update the pointer as it was least recently used
|
||||
if (lu_hit[i] & lu_access_i) begin
|
||||
// Set the nodes to the values we would expect
|
||||
for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
|
||||
idx_base = $unsigned((2 ** lvl) - 1);
|
||||
// lvl0 <=> MSB, lvl1 <=> MSB-1, ...
|
||||
shift = $clog2(TLB_ENTRIES) - lvl;
|
||||
// to circumvent the 32 bit integer arithmetic assignment
|
||||
new_index = ~((i >> (shift - 1)) & 32'b1);
|
||||
plru_tree_n[idx_base+(i>>shift)] = new_index[0];
|
||||
end
|
||||
end
|
||||
end
|
||||
// Decode tree to write enable signals
|
||||
// Next for-loop basically creates the following logic for e.g. an 8 entry
|
||||
// TLB (note: pseudo-code obviously):
|
||||
// replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
|
||||
// replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
|
||||
// replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
|
||||
// replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
|
||||
// replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
|
||||
// replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
|
||||
// replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
|
||||
// replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
|
||||
// For each entry traverse the tree. If every tree-node matches,
|
||||
// the corresponding bit of the entry's index, this is
|
||||
// the next entry to replace.
|
||||
for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
|
||||
automatic logic en;
|
||||
automatic int unsigned idx_base, shift, new_index;
|
||||
en = 1'b1;
|
||||
for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
|
||||
idx_base = $unsigned((2 ** lvl) - 1);
|
||||
// lvl0 <=> MSB, lvl1 <=> MSB-1, ...
|
||||
shift = $clog2(TLB_ENTRIES) - lvl;
|
||||
|
||||
// en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
|
||||
new_index = (i >> (shift - 1)) & 32'b1;
|
||||
if (new_index[0]) begin
|
||||
en &= plru_tree_q[idx_base+(i>>shift)];
|
||||
end else begin
|
||||
en &= ~plru_tree_q[idx_base+(i>>shift)];
|
||||
end
|
||||
end
|
||||
replace_en[i] = en;
|
||||
end
|
||||
end
|
||||
|
||||
// sequential process
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
tags_q <= '{default: 0};
|
||||
content_q <= '{default: 0};
|
||||
plru_tree_q <= '{default: 0};
|
||||
end else begin
|
||||
tags_q <= tags_n;
|
||||
content_q <= content_n;
|
||||
plru_tree_q <= plru_tree_n;
|
||||
end
|
||||
end
|
||||
//--------------
|
||||
// Sanity checks
|
||||
//--------------
|
||||
|
||||
//pragma translate_off
|
||||
`ifndef VERILATOR
|
||||
|
||||
initial begin : p_assertions
|
||||
assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
|
||||
else begin
|
||||
$error("TLB size must be a multiple of 2 and greater than 1");
|
||||
$stop();
|
||||
end
|
||||
assert (CVA6Cfg.ASID_WIDTH >= 1)
|
||||
else begin
|
||||
$error("ASID width must be at least 1");
|
||||
$stop();
|
||||
end
|
||||
end
|
||||
|
||||
// Just for checking
|
||||
function int countSetBits(logic [TLB_ENTRIES-1:0] vector);
|
||||
automatic int count = 0;
|
||||
foreach (vector[idx]) begin
|
||||
count += vector[idx];
|
||||
end
|
||||
return count;
|
||||
endfunction
|
||||
|
||||
assert property (@(posedge clk_i) (countSetBits(lu_hit) <= 1))
|
||||
else begin
|
||||
$error("More then one hit in TLB!");
|
||||
$stop();
|
||||
end
|
||||
assert property (@(posedge clk_i) (countSetBits(replace_en) <= 1))
|
||||
else begin
|
||||
$error("More then one TLB entry selected for next replace!");
|
||||
$stop();
|
||||
end
|
||||
|
||||
`endif
|
||||
//pragma translate_on
|
||||
|
||||
endmodule
|
|
@ -60,6 +60,12 @@ module store_unit
|
|||
output logic [CVA6Cfg.VLEN-1:0] vaddr_o,
|
||||
// RVFI information - RVFI
|
||||
output [CVA6Cfg.PLEN-1:0] rvfi_mem_paddr_o,
|
||||
// Transformed trap instruction out - TO_BE_COMPLETED
|
||||
output logic [31:0] tinst_o,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
output logic hs_ld_st_inst_o,
|
||||
// TO_BE_COMPLETED - TO_BE_COMPLETED
|
||||
output logic hlvx_inst_o,
|
||||
// Physical address - TO_BE_COMPLETED
|
||||
input logic [CVA6Cfg.PLEN-1:0] paddr_i,
|
||||
// Exception raised before store - TO_BE_COMPLETED
|
||||
|
@ -127,8 +133,11 @@ module store_unit
|
|||
logic [CVA6Cfg.TRANS_ID_BITS-1:0] trans_id_n, trans_id_q;
|
||||
|
||||
// output assignments
|
||||
assign vaddr_o = lsu_ctrl_i.vaddr; // virtual address
|
||||
assign trans_id_o = trans_id_q; // transaction id from previous cycle
|
||||
assign vaddr_o = lsu_ctrl_i.vaddr; // virtual address
|
||||
assign hs_ld_st_inst_o = CVA6Cfg.RVH ? lsu_ctrl_i.hs_ld_st_inst : 1'b0;
|
||||
assign hlvx_inst_o = CVA6Cfg.RVH ? lsu_ctrl_i.hlvx_inst : 1'b0;
|
||||
assign tinst_o = CVA6Cfg.RVH ? lsu_ctrl_i.tinst : '0; // transformed instruction
|
||||
assign trans_id_o = trans_id_q; // transaction id from previous cycle
|
||||
|
||||
always_comb begin : store_control
|
||||
translation_req_o = 1'b0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue