From ce07203c5fb26ef30d4e70cf1d8c69846905def7 Mon Sep 17 00:00:00 2001 From: Yangyu Chen Date: Fri, 9 Aug 2024 00:41:35 +0900 Subject: [PATCH] arch-riscv: use sign-extend for all address generation (#1316) In gem5, we use the same code base for RISC-V 32 and 64. However, if we need to allow modifiable XLEN control on CSR.mstatus in the future, we should follow the RISC-V ISA manual to sign-extend all the register results, including PC and GPR. If this feature implemented, the simulator needs to handle user-mode in RV32 but CSR.SATP sets to Sv39. In this case, 0x80000000 and 0xffffffff80000000 are different addresses in the 64-bit S-Mode perspective, but they are the same in the 32-bit U-Mode perspective. We should avoid this wrong behavior happening before we implement this feature. Thus, we need to sign-extend the results of all the addresses, including the PC and memory addresses, which currently use zero-extend. As specified in the RISC-V ISA manual, we use zero-extend in narrow XLEN mode for the physical address implemented in TLB. Changes based on spec: 1. Sign-extend narrow XLEN: https://github.com/riscv/riscv-isa-manual/blob/riscv-isa-release-b7a445a-2024-07-02/src/machine.adoc?plain=1#L567 2. Zero-extend physical address: https://github.com/riscv/riscv-isa-manual/blob/riscv-isa-release-b7a445a-2024-07-02/src/supervisor.adoc?plain=1#L1670 Signed-off-by: Yangyu Chen --- src/arch/riscv/isa/decoder.isa | 76 +++++++++++------------ src/arch/riscv/isa/formats/amo.isa | 6 +- src/arch/riscv/isa/formats/compressed.isa | 2 +- src/arch/riscv/isa/formats/mem.isa | 6 +- src/arch/riscv/isa/formats/standard.isa | 4 +- src/arch/riscv/tlb.cc | 28 +++++++-- 6 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/arch/riscv/isa/decoder.isa b/src/arch/riscv/isa/decoder.isa index 6644d6fa93..763709b016 100644 --- a/src/arch/riscv/isa/decoder.isa +++ b/src/arch/riscv/isa/decoder.isa @@ -69,7 +69,7 @@ decode QUADRANT default Unknown::unknown() { Fp2_bits = Mem; }}, {{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x2: c_lw({{ offset = CIMM2<1:1> << 2 | @@ -78,7 +78,7 @@ decode QUADRANT default Unknown::unknown() { }}, {{ Rp2_sd = Mem_sw; }}, {{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x3: decode RVTYPE { 0x0: c_flw({{ @@ -97,7 +97,7 @@ decode QUADRANT default Unknown::unknown() { freg_t fd = freg(f32(Mem_uw)); Fp2_bits = fd.v; }}, {{ - EA = (uint32_t)(Rp1_uw + offset); + EA = rvSext(Rp1 + offset); }}); 0x1: c_ld({{ offset = CIMM3 << 3 | CIMM2 << 6; @@ -115,7 +115,7 @@ decode QUADRANT default Unknown::unknown() { }}, {{ Rp2 = Mem_ub; }}, {{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x1: decode CFUNCT1BIT6 { 0x0: c_lhu({{ @@ -123,14 +123,14 @@ decode QUADRANT default Unknown::unknown() { }}, {{ Rp2 = Mem_uh; }}, {{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x1: c_lh({{ offset = CIMM2<0:0> << 1; }}, {{ Rp2_sd = Mem_sh; }}, {{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); } } @@ -140,14 +140,14 @@ decode QUADRANT default Unknown::unknown() { }}, {{ Mem_ub = Rp2_ub; }}, ea_code={{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x3: c_sh({{ offset = (CIMM2<0:0> << 1); }}, {{ Mem_uh = Rp2_uh; }}, ea_code={{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); } } @@ -162,7 +162,7 @@ decode QUADRANT default Unknown::unknown() { Mem = Fp2_bits; }}, {{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x6: c_sw({{ offset = CIMM2<1:1> << 2 | @@ -171,7 +171,7 @@ decode QUADRANT default Unknown::unknown() { }}, {{ Mem_uw = Rp2_uw; }}, ea_code={{ - EA = rvZext(Rp1 + offset); + EA = rvSext(Rp1 + offset); }}); 0x7: decode RVTYPE { 0x0: c_fsw({{ @@ -186,7 +186,7 @@ decode QUADRANT default Unknown::unknown() { Mem_uw = unboxF32(boxF32(Fp2_bits)); }}, {{ - EA = (uint32_t)(Rp1_uw + offset); + EA = rvSext(Rp1_uw + offset); }}); 0x1: c_sd({{ offset = CIMM3 << 3 | CIMM2 << 6; @@ -213,8 +213,8 @@ decode QUADRANT default Unknown::unknown() { }}); 0x1: decode RVTYPE { 0x0: CJOp::c_jal({{ - ra_sw = NPC_uw; - NPC_uw = PC_uw + imm; + ra = rvSext(NPC); + NPC = rvSext(PC + imm); }}, IsDirectControl, IsUncondControl, IsCall); 0x1: CIOp::c_addiw({{ imm = sext<6>(CIMM5 | (CIMM1 << 5)); @@ -349,18 +349,18 @@ decode QUADRANT default Unknown::unknown() { } } 0x5: CJOp::c_j({{ - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); }}, IsDirectControl, IsUncondControl); format CBOp { 0x6: c_beqz({{ if (rvSext(Rp1) == 0) - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); else NPC = NPC; }}, IsDirectControl, IsCondControl); 0x7: c_bnez({{ if (rvSext(Rp1) != 0) - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); else NPC = NPC; }}, IsDirectControl, IsCondControl); @@ -396,7 +396,7 @@ decode QUADRANT default Unknown::unknown() { Fc1_bits = Mem; }}, {{ - EA = rvZext(sp + offset); + EA = rvSext(sp + offset); }}); 0x2: c_lwsp({{ offset = CIMM5<4:2> << 2 | @@ -409,7 +409,7 @@ decode QUADRANT default Unknown::unknown() { } Rc1_sw = Mem_sw; }}, {{ - EA = rvZext(sp + offset); + EA = rvSext(sp + offset); }}); 0x3: decode RVTYPE { 0x0: c_flwsp({{ @@ -429,7 +429,7 @@ decode QUADRANT default Unknown::unknown() { fd = freg(f32(Mem_uw)); Fc1_bits = fd.v; }}, {{ - EA = (uint32_t)(sp_uw + offset); + EA = rvSext(sp_uw + offset); }}); 0x1: c_ldsp({{ offset = CIMM5<4:3> << 3 | @@ -453,7 +453,7 @@ decode QUADRANT default Unknown::unknown() { return std::make_shared( "source reg x0", machInst); } - NPC = rvZext(Rc1 & (~0x1)); + NPC = rvSext(Rc1 & (~0x1)); }}, IsIndirectControl, IsUncondControl); default: CROp::c_mv({{ // RC1 == 0 is HINT @@ -470,7 +470,7 @@ decode QUADRANT default Unknown::unknown() { }}, IsSerializeAfter, IsNonSpeculative, No_OpClass); default: CJump::c_jalr({{ ra = rvSext(NPC); - NPC = rvZext(Rc1 & (~0x1)); + NPC = rvSext(Rc1 & (~0x1)); }}, IsIndirectControl, IsUncondControl, IsCall); } default: CompressedROp::c_add({{ @@ -491,7 +491,7 @@ decode QUADRANT default Unknown::unknown() { Mem_ud = Fc2_bits; }}, {{ - EA = rvZext(sp + offset); + EA = rvSext(sp + offset); }}); 0x6: c_swsp({{ offset = CIMM6<5:2> << 2 | @@ -499,7 +499,7 @@ decode QUADRANT default Unknown::unknown() { }}, {{ Mem_uw = Rc2_uw; }}, {{ - EA = rvZext(sp + offset); + EA = rvSext(sp + offset); }}); 0x7: decode RVTYPE { 0x0: c_fswsp({{ @@ -513,7 +513,7 @@ decode QUADRANT default Unknown::unknown() { Mem_uw = unboxF32(boxF32(Fc2_bits)); }}, {{ - EA = (uint32_t)(sp_uw + offset); + EA = rvSext(sp_uw + offset); }}); 0x1: c_sdsp({{ offset = CIMM6<5:3> << 3 | @@ -4755,44 +4755,44 @@ decode QUADRANT default Unknown::unknown() { format BOp { 0x0: beq({{ if (rvSext(Rs1) == rvSext(Rs2)) { - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); } else { - NPC = rvZext(NPC); + NPC = rvSext(NPC); } }}, IsDirectControl, IsCondControl); 0x1: bne({{ if (rvSext(Rs1) != rvSext(Rs2)) { - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); } else { - NPC = rvZext(NPC); + NPC = rvSext(NPC); } }}, IsDirectControl, IsCondControl); 0x4: blt({{ if (rvSext(Rs1_sd) < rvSext(Rs2_sd)) { - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); } else { - NPC = rvZext(NPC); + NPC = rvSext(NPC); } }}, IsDirectControl, IsCondControl); 0x5: bge({{ if (rvSext(Rs1_sd) >= rvSext(Rs2_sd)) { - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); } else { - NPC = rvZext(NPC); + NPC = rvSext(NPC); } }}, IsDirectControl, IsCondControl); 0x6: bltu({{ if (rvZext(Rs1) < rvZext(Rs2)) { - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); } else { - NPC = rvZext(NPC); + NPC = rvSext(NPC); } }}, IsDirectControl, IsCondControl); 0x7: bgeu({{ if (rvZext(Rs1) >= rvZext(Rs2)) { - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); } else { - NPC = rvZext(NPC); + NPC = rvSext(NPC); } }}, IsDirectControl, IsCondControl); } @@ -4801,13 +4801,13 @@ decode QUADRANT default Unknown::unknown() { 0x19: decode FUNCT3 { 0x0: Jump::jalr({{ Rd = rvSext(NPC); - NPC = rvZext((imm + Rs1) & (~0x1)); + NPC = rvSext((imm + Rs1) & (~0x1)); }}, IsIndirectControl, IsUncondControl); } 0x1b: JOp::jal({{ Rd = rvSext(NPC); - NPC = rvZext(PC + imm); + NPC = rvSext(PC + imm); }}, IsDirectControl, IsUncondControl); 0x1c: decode FUNCT3 { diff --git a/src/arch/riscv/isa/formats/amo.isa b/src/arch/riscv/isa/formats/amo.isa index 1c385fb357..7a1699cc22 100644 --- a/src/arch/riscv/isa/formats/amo.isa +++ b/src/arch/riscv/isa/formats/amo.isa @@ -429,7 +429,7 @@ def template AtomicMemOpRMWCompleteAcc {{ // LR/SC/AMO decode formats def format LoadReserved(memacc_code, postacc_code={{ }}, - ea_code={{EA = rvZext(Rs1);}}, mem_flags=[], inst_flags=[]) {{ + ea_code={{EA = rvSext(Rs1);}}, mem_flags=[], inst_flags=[]) {{ macro_ea_code = '' macro_inst_flags = [] macro_iop = InstObjParams(name, Name, 'LoadReserved', macro_ea_code, @@ -460,7 +460,7 @@ def format LoadReserved(memacc_code, postacc_code={{ }}, }}; def format StoreCond(memacc_code, postacc_code={{ }}, - ea_code={{EA = rvZext(Rs1);}}, mem_flags=[], inst_flags=[]) {{ + ea_code={{EA = rvSext(Rs1);}}, mem_flags=[], inst_flags=[]) {{ macro_ea_code = '' macro_inst_flags = [] macro_iop = InstObjParams(name, Name, 'StoreCond', macro_ea_code, @@ -491,7 +491,7 @@ def format StoreCond(memacc_code, postacc_code={{ }}, }}; def format AtomicMemOp(memacc_code, amoop_code, postacc_code={{ }}, - ea_code={{EA = rvZext(Rs1);}}, mem_flags=[], inst_flags=[]) {{ + ea_code={{EA = rvSext(Rs1);}}, mem_flags=[], inst_flags=[]) {{ macro_ea_code = '' macro_inst_flags = [] macro_iop = InstObjParams(name, Name, 'AtomicMemOp', macro_ea_code, diff --git a/src/arch/riscv/isa/formats/compressed.isa b/src/arch/riscv/isa/formats/compressed.isa index 8d6a125ef1..94a770d835 100644 --- a/src/arch/riscv/isa/formats/compressed.isa +++ b/src/arch/riscv/isa/formats/compressed.isa @@ -173,7 +173,7 @@ def template CJumpExecute {{ %(class_name)s::branchTarget(ThreadContext *tc) const { PCStateBase *pc_ptr = tc->pcState().clone(); - pc_ptr->as().set(rvZext(tc->getReg(srcRegIdx(0)) & ~0x1)); + pc_ptr->as().set(rvSext(tc->getReg(srcRegIdx(0)) & ~0x1)); return std::unique_ptr{pc_ptr}; } diff --git a/src/arch/riscv/isa/formats/mem.isa b/src/arch/riscv/isa/formats/mem.isa index e5da80a06c..1f7721cc78 100644 --- a/src/arch/riscv/isa/formats/mem.isa +++ b/src/arch/riscv/isa/formats/mem.isa @@ -301,7 +301,7 @@ def template CacheBlockBasedStoreCompleteAcc {{ } }}; -def format Load(memacc_code, ea_code = {{EA = rvZext(Rs1 + offset);}}, +def format Load(memacc_code, ea_code={{EA = rvSext(Rs1 + offset);}}, offset_code={{offset = sext<12>(IMM12);}}, mem_flags=[], inst_flags=[]) {{ (header_output, decoder_output, decode_block, exec_output) = \ @@ -309,7 +309,7 @@ def format Load(memacc_code, ea_code = {{EA = rvZext(Rs1 + offset);}}, inst_flags, 'Load', exec_template_base='Load') }}; -def format Store(memacc_code, ea_code={{EA = rvZext(Rs1 + offset);}}, +def format Store(memacc_code, ea_code={{EA = rvSext(Rs1 + offset);}}, offset_code={{offset = sext<12>(IMM5 | (IMM7 << 5));}}, mem_flags=[], inst_flags=[]) {{ (header_output, decoder_output, decode_block, exec_output) = \ @@ -317,7 +317,7 @@ def format Store(memacc_code, ea_code={{EA = rvZext(Rs1 + offset);}}, inst_flags, 'Store', exec_template_base='Store') }}; -def format CBMOp(memacc_code, ea_code={{EA = rvZext(Rs1);}}, +def format CBMOp(memacc_code, ea_code={{EA = rvSext(Rs1);}}, offset_code={{;}}, mem_flags=[], inst_flags=[]) {{ (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, offset_code, ea_code, memacc_code, mem_flags, diff --git a/src/arch/riscv/isa/formats/standard.isa b/src/arch/riscv/isa/formats/standard.isa index 83b135daf2..8e8c6ce0b2 100644 --- a/src/arch/riscv/isa/formats/standard.isa +++ b/src/arch/riscv/isa/formats/standard.isa @@ -199,7 +199,7 @@ def template BranchExecute {{ { auto &rpc = branch_pc.as(); std::unique_ptr npc(dynamic_cast(rpc.clone())); - npc->set(rvZext(rpc.pc() + imm)); + npc->set(rvSext(rpc.pc() + imm)); return npc; } @@ -307,7 +307,7 @@ def template JumpExecute {{ { PCStateBase *pc_ptr = tc->pcState().clone(); pc_ptr->as().set( - rvZext((tc->getReg(srcRegIdx(0)) + imm) & ~0x1)); + rvSext((tc->getReg(srcRegIdx(0)) + imm) & ~0x1)); return std::unique_ptr{pc_ptr}; } diff --git a/src/arch/riscv/tlb.cc b/src/arch/riscv/tlb.cc index f6d23a8480..d6538e7070 100644 --- a/src/arch/riscv/tlb.cc +++ b/src/arch/riscv/tlb.cc @@ -385,9 +385,16 @@ TLB::translate(const RequestPtr &req, ThreadContext *tc, if (fault == NoFault) { if (req->getFlags() & Request::PHYSICAL) { /** - * we simply set the virtual address to physical address + * we simply set the virtual address to physical address. + * + * For RV32, we follow what the specification said: + * When mapping between narrower and wider addresses, + * RISC-V zero-extends a narrower physical address to a + * wider size. */ - req->setPaddr(req->getVaddr()); + req->setPaddr(((ISA*) tc->getIsaPtr())->rvType() == RV32 ? + bits(req->getVaddr(), 31, 0) : + req->getVaddr()); } else { fault = doTranslate(req, tc, translation, mode, delayed); } @@ -418,9 +425,20 @@ TLB::translate(const RequestPtr &req, ThreadContext *tc, Process * p = tc->getProcessPtr(); - Fault fault = p->pTable->translate(req); - if (fault != NoFault) - return fault; + /* + * In RV32 Linux, as vaddr >= 0x80000000 is legal in userspace + * (except for COMPAT mode for RV32 Userspace in RV64 Linux), we + * need to ignore the upper bits beyond 32 bits. + */ + Addr vaddr = ((ISA*) tc->getIsaPtr())->rvType() == RV32 ? + bits(req->getVaddr(), 31, 0) : + req->getVaddr(); + Addr paddr; + + if (!p->pTable->translate(vaddr, paddr)) + return std::make_shared(req->getVaddr()); + + req->setPaddr(paddr); return NoFault; }