diff options
| author | cheapie <cheapiephp@gmail.com> | 2026-06-23 18:54:53 -0500 |
|---|---|---|
| committer | cheapie <cheapiephp@gmail.com> | 2026-06-23 18:54:53 -0500 |
| commit | 3acb10122050d710845a7c35e84423cc1aae5632 (patch) | |
| tree | 2eadd0c8514b7fd046621b26f79db156349c22e9 /rvcontroller.lua | |
| parent | 41a6282fb32a3a7dbeca64191ddd804585bca5f4 (diff) | |
| download | rvcontroller-3acb10122050d710845a7c35e84423cc1aae5632.tar rvcontroller-3acb10122050d710845a7c35e84423cc1aae5632.tar.gz rvcontroller-3acb10122050d710845a7c35e84423cc1aae5632.tar.bz2 rvcontroller-3acb10122050d710845a7c35e84423cc1aae5632.tar.xz rvcontroller-3acb10122050d710845a7c35e84423cc1aae5632.zip | |
Add Zicfilp extension and some CSR improvements
Diffstat (limited to 'rvcontroller.lua')
| -rw-r--r-- | rvcontroller.lua | 135 |
1 files changed, 111 insertions, 24 deletions
diff --git a/rvcontroller.lua b/rvcontroller.lua index 56b800b..8711a51 100644 --- a/rvcontroller.lua +++ b/rvcontroller.lua @@ -7,7 +7,7 @@ Emulated system specifications: -Single RISC-V core, RV32IMACBZicntr_Zicond_Zicsr_Zifencei_Zihintpause_Zilsd_Zimop_Zabha_Zacas_Zalasr_Zawrs_Zcb_Zclsd_Zcmop_Zcmp_Zcmt_Zbkb_Zbkx_Xh3bextm instruction set, switchable endianness (little-endian default) +Single RISC-V core, RV32IMACBZicfilp_Zicntr_Zicond_Zicsr_Zifencei_Zihintpause_Zilsd_Zimop_Zabha_Zacas_Zalasr_Zawrs_Zcb_Zclsd_Zcmop_Zcmp_Zcmt_Zbkb_Zbkx_Xh3bextm instruction set, switchable endianness (little-endian default) 65536 bytes of RAM (configurable in settings below) starting at base address 0 Intended to be compliant with the following specifications: @@ -48,6 +48,7 @@ Intended to be compliant with the following specifications: * "Zawrs" Extension for Wait-on-Reservation-Set instructions, Version 1.01 * Zbkb: Extension for Bit-manipulation for Cryptography, Version 1.0.0 * Zbkx: Extension for Crossbar permutations, Version 1.0.0 +* Zicfilp: Control Flow Integrity - Landing Pad * Xh3bextm: Hazard3 bit extract multiple - Note: Defined in the Hazard3 reference manual, available at https://github.com/Wren6991/Hazard3/releases/download/v1.1/hazard3.pdf * Machine-Level ISA, Version 1.13 @@ -254,6 +255,7 @@ local traptypes = { } local fault --non-nil values are for deferred faults that will trap after the instruction finishes +local fval local function implodebits(bits,count,signed) local negative = false @@ -349,7 +351,8 @@ local function readram(address,bytes,instfetch) local bigendian = mem.mbe and not instfetch --Instruction fetches must always be little-endian if address > RAM_SIZE-1 and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then fault = instfetch and 1 or 5 - return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0 + fval = address + return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0 (nop) end local out = 0 for i=0,bytes-1 do @@ -441,6 +444,7 @@ end local function writeram(address,data,bytes,fake) if address > RAM_SIZE-1 and not fake and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then fault = 7 + fval = address return end if mem.reservationset then @@ -610,12 +614,21 @@ local function readcsr(address) elseif address == 0x310 then --mstatush local bits = {} bits[5] = mem.mbe + bits[9] = mem.mpelp bits[10] = mem.mdt return implodebits(bits,32) + elseif address == 0x747 then --mseccfg + local bits = {} + bits[10] = mem.mlpe + return implodebits(bits,32) elseif not mem.csr[address] then digiline_send("monitordisp",string.format("Attempted read\nfrom unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) fault = 2 + fval = readram(mem.registers.pc,4) return 0 + elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then + --mcycle(h)/minstret(h) - redirect to cycle(h)/instret(h) + address = address + 0x100 end return mem.csr[address] end @@ -627,10 +640,12 @@ local function writecsr(address,data) elseif address == 0xc00 or address == 0xc01 or address == 0xc02 or address == 0xc80 or address == 0xc81 or address == 0xc82 then --One of the read-only CSRs from Zicntr fault = 2 + fval = readram(mem.registers.pc,4) return elseif address == 0xf11 or address == 0xf12 or address == 0xf13 or address == 0xf14 or address == 0xf15 then --Read-only machine information register fault = 2 + fval = readram(mem.registers.pc,4) return elseif address == 0x300 then --mstatus @@ -642,6 +657,7 @@ local function writecsr(address,data) --mstatush local bits = explodebits(data,32) mem.mbe = bits[5] + mem.mpelp = bits[9] mem.mdt = bits[10] return elseif address == 0x301 then @@ -673,16 +689,26 @@ local function writecsr(address,data) mem.trapmode = data%4 mem.trapbase = data-mem.trapmode mem.mtvecwritten = true + return + elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then + --mcycle(h)/minstret(h) - redirect to cycle(h)/instret(h) + address = address + 0x100 + elseif address == 0x747 then + --mseccfg then + local bits = explodebits(data,32) + mem.mlpe = bits[10] + return end if not mem.csr[address] then digiline_send("monitordisp",string.format("Attempted write\nto unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) fault = 2 + fval = readram(mem.registers.pc,4) return end mem.csr[address] = data end -local function trap(reason) +local function trap(reason,val) local oldreason = reason if mem.mdt then writecsr(0x34b,reason) --mtval2 @@ -690,9 +716,11 @@ local function trap(reason) end writecsr(0x342,reason) --mcause writecsr(0x341,mem.registers.pc) --mepc - writecsr(0x343,0) --mtval + writecsr(0x343,val or 0) --mtval mem.mpie = mem.mie mem.mie = false + mem.mpelp = mem.mlpe and mem.elp + mem.elp = false if not mem.mdt then mem.mdt = true if mem.trapmode == 1 then @@ -710,9 +738,23 @@ local function trap(reason) digiline_send("monitordisp","Unexpected trap!") if not mem.mtvecwritten then digiline_send("monitordisp",traptypes[oldreason] or "Unknown reason") + if oldreason == 18 then + local subtypes = { + [2] = "Landing pad fault", + [3] = "Shadow stack fault", + } + digiline_send("monitordisp",subtypes[val] or "Unknown cause") + end digiline_send("monitordisp","(no handler present)") else digiline_send("monitordisp",traptypes[reason] or "Unknown reason") + if reason == 18 then + local subtypes = { + [2] = "Landing pad fault", + [3] = "Shadow stack fault", + } + digiline_send("monitordisp",subtypes[val] or "Unknown cause") + end end digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc)) digiline_send("monitordisp","System halted") @@ -1166,7 +1208,7 @@ local operations = { end end, ebreak = function() - return trap(3) + return trap(3,0) end, csrrw = function(rd,rs1,imm) setreg(rd,readcsr(imm)) @@ -1957,6 +1999,8 @@ local operations = { mem.mdt = false mem.mie = mem.mpie mem.mpie = true + mem.elp = mem.mlpe and mem.mpelp + mem.mpelp = false mem.mpp = 3 checkinterrupts() return true @@ -2071,7 +2115,7 @@ local function runinst(instruction) elseif f3 == 0x7 and f7 == 0x7 then operations.czeronez(rd,rs1,rs2) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x13 or opcode == 0x3 or opcode == 0x67 or opcode == 0x73 then --I-type @@ -2133,7 +2177,7 @@ local function runinst(instruction) elseif f3 == 0x5 and imm == 0x8f then operations.unzip(rd,rs1,imm) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x3 then if f3 == 0x0 then @@ -2149,13 +2193,13 @@ local function runinst(instruction) elseif f3 == 0x5 then operations.lhu(rd,rs1,imm) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x67 then if f3 == 0x0 then return operations.jalr(rd,rs1,imm) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x73 then if f3 == 0x0 then @@ -2172,7 +2216,7 @@ local function runinst(instruction) elseif imm == 0x105 and rd == 0 and rs1 == 0 then return operations.wfi() else - return trap(2) + return trap(2,instruction) end elseif f3 == 0x1 then operations.csrrw(rd,rs1,imm) @@ -2193,7 +2237,7 @@ local function runinst(instruction) elseif f3 == 0x7 then operations.csrrci(rd,rs1,imm) else - return trap(2) + return trap(2,instruction) end end elseif opcode == 0x23 then @@ -2215,7 +2259,7 @@ local function runinst(instruction) elseif f3 == 0x3 then operations.sd(rs1,rs2,imm) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x63 then --B-type @@ -2240,7 +2284,7 @@ local function runinst(instruction) elseif f3 == 0x7 then return operations.bgeu(rs1,rs2,imm) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x37 or opcode == 0x17 then --U-type @@ -2254,7 +2298,7 @@ local function runinst(instruction) elseif opcode == 0x17 then operations.auipc(rd,imm) else - return trap(2) + return trap(2,instruction) end elseif opcode == 0x6f then --J-type @@ -2318,14 +2362,14 @@ local function runinst(instruction) --The normal sw instruction implementation already meets these requirements operations.sw(rs1,rs2,0) else - return trap(2) + return trap(2,instruction) end elseif f3 == 3 then if f5 == 5 then --amocas.d operations.amocasd(rd,rs1,rs2) else - return trap(2) + return trap(2,instruction) end elseif f3 == 0 then if f5 == 1 then @@ -2367,7 +2411,7 @@ local function runinst(instruction) --The normal sb instruction implementation already meets these requirements operations.sb(rs1,rs2,0) else - return trap(2) + return trap(2,instruction) end elseif f3 == 1 then if f5 == 1 then @@ -2409,7 +2453,7 @@ local function runinst(instruction) --The normal sh instruction implementation already meets these requirements operations.sh(rs1,rs2,0) else - return trap(2) + return trap(2,instruction) end end elseif opcode == 0x0f then @@ -2431,10 +2475,10 @@ local function runinst(instruction) elseif f3 == 4 then operations.h3bextmi(rd,rs1,rs2,xh3bextmsize) else - return trap(2) + return trap(2,instruction) end else - trap(2) + trap(2,instruction) end end @@ -2555,7 +2599,7 @@ local function runcinst(instruction) --c.mop operations.cmop(rd,imm) else - return trap(2) + return trap(2,instruction%2^16) end elseif opcode == 1 and f3 == 0 then --c.addi (CI) @@ -2748,10 +2792,34 @@ local function runcinst(instruction) return true end else - return trap(2) + return trap(2,instruction%2^16) end end +local function checklpe(instruction) + if not mem.mlpe then + return false + elseif instruction%4 == 3 then + --Uncompressed + local opcode = instruction%0x100 + local bits = explodebits(instruction,32) + if opcode == 0x67 and not (bits[12] or bits[13] or bits[14]) then + --jalr + local rs1 = math.floor(instruction/2^15)%2^5 + return (rs1 ~= 1 and rs1 ~= 5 and rs1 ~= 7) + end + else + --Compressed + local f4 = math.floor(instruction/2^12) + if instruction %4 == 2 and (f4 == 8 or f4 == 9) then + --c.jr or c.jalr + local rs1 = math.floor(instruction/2^7)%2^5 + return (rs1 ~= 1 and rs1 ~= 5 and rs1 ~= 7) + end + end + return false +end + local function run(limit) --0xc00 = CYCLE --0xc80 = CYCLEH @@ -2778,6 +2846,17 @@ local function run(limit) end first = false local instruction = readram(mem.registers.pc,4,true) + if mem.lpe and mem.mlpe then + if instruction%2^12 == 0x017 and mem.registers.pc%4 == 0 and (math.floor(instruction/2^12) == mem.lplabel or math.floor(instruction/2^12) == 0) then + mem.lpe = false + else + trap(18,2) + end + end + mem.lpe = mem.mlpe and checklpe(instruction) + if mem.lpe then + mem.lplabel = math.floor(getreg(7)/2^12) + end if instruction%4 == 3 or not mem.isa.c then if mem.registers.pc%4 ~= 0 and not mem.isa.c then local _,stop = trap(0) @@ -2785,14 +2864,14 @@ local function run(limit) end local jumped,stop = runinst(instruction) if fault then - jumped,stop = trap(fault) + jumped,stop = trap(fault,fval) end if not jumped then mem.registers.pc = (mem.registers.pc + 4) % 2^32 end if stop then break end else local jumped,stop = runcinst(instruction%2^16) if fault then - jumped,stop = trap(fault) + jumped,stop = trap(fault,fval) end if not jumped then mem.registers.pc = (mem.registers.pc + 2) % 2^32 end if stop then break end @@ -2872,6 +2951,11 @@ local csraliases = { mip = 0x344, mtinst = 0x34a, mtval2 = 0x34b, + mcycle = 0xb00, + minstret = 0xb02, + mcycleh = 0xb80, + minstreth = 0xb82, + mseccfg = 0x747, } if event.type == "program" or event.iid == "reset" then @@ -2944,6 +3028,9 @@ if event.type == "program" or event.iid == "reset" then mem.mtimecmphigh = 0 mem.mtimehigh = 0 mem.interruptwaiting = false + mem.mlpe = false + mem.elp = false + mem.mpelp = false elseif event.type == "on" or event.type == "off" then local pname = string.lower(event.pin.name) local pstate = pin[pname] |
