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 <cyy@cyyself.name>
This commit is contained in:
Yangyu Chen
2024-08-09 00:41:35 +09:00
committed by GitHub
parent 86f7fae86b
commit ce07203c5f
6 changed files with 70 additions and 52 deletions

View File

@@ -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<IllegalInstFault>(
"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 {

View File

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

View File

@@ -173,7 +173,7 @@ def template CJumpExecute {{
%(class_name)s::branchTarget(ThreadContext *tc) const
{
PCStateBase *pc_ptr = tc->pcState().clone();
pc_ptr->as<PCState>().set(rvZext(tc->getReg(srcRegIdx(0)) & ~0x1));
pc_ptr->as<PCState>().set(rvSext(tc->getReg(srcRegIdx(0)) & ~0x1));
return std::unique_ptr<PCStateBase>{pc_ptr};
}

View File

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

View File

@@ -199,7 +199,7 @@ def template BranchExecute {{
{
auto &rpc = branch_pc.as<RiscvISA::PCState>();
std::unique_ptr<PCState> npc(dynamic_cast<PCState*>(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<PCState>().set(
rvZext((tc->getReg(srcRegIdx(0)) + imm) & ~0x1));
rvSext((tc->getReg(srcRegIdx(0)) + imm) & ~0x1));
return std::unique_ptr<PCStateBase>{pc_ptr};
}

View File

@@ -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<GenericPageTableFault>(req->getVaddr());
req->setPaddr(paddr);
return NoFault;
}