diff options
Diffstat (limited to 'rvcontroller.lua')
| -rw-r--r-- | rvcontroller.lua | 191 |
1 files changed, 168 insertions, 23 deletions
diff --git a/rvcontroller.lua b/rvcontroller.lua index 1e60474..1acb1f1 100644 --- a/rvcontroller.lua +++ b/rvcontroller.lua @@ -206,6 +206,46 @@ local CLOCK_SPEED = 20 --Clock speed in Hz when not in lightweight mode local INSTRUCTIONS_PER_CLOCK = 50 --Maximum number of instructions that will be run on each clock cycle local STDOUT_TO_TERMINAL = false --Whether to show standard output on the Luacontroller terminal in addition to the screen +local traptypes = { + --Not all of these are implemented, some are impossible on RVController + [0x0] = "Inst addr misaligned", + [0x1] = "Inst access fault", + [0x2] = "Illegal instruction", + [0x3] = "Breakpoint", + [0x4] = "Load addr misaligned", + [0x5] = "Load access fault", + [0x6] = "St/AMO addr misalign", + [0x7] = "St/AMO access fault", + [0x8] = "U-mode env call", + [0x9] = "S-mode env call", + --0xa = reserved + [0xb] = "M-mode env call", + [0xc] = "Instr page fault", + [0xd] = "Load page fault", + --0xe = reserved + [0xf] = "St/AMO page fault", + [0x10] = "Double trap", + --0x11 = reserved + [0x12] = "Software check", + [0x13] = "Hardware error", + --0x14-0x80000000 = reserved/custom + [0x8000001] = "Supv soft interrupt", + --0x80000002 = reserved + [0x8000003] = "Mach soft interrupt", + --0x80000004 = reserved + [0x8000005] = "Supv timer interrupt", + --0x80000006 = reserved + [0x8000007] = "Mach timer interrupt", + --0x80000008 = reserved + [0x8000009] = "Supv ext interrupt", + --0x8000000a = reserved + [0x800000b] = "Mach ext interrupt", + --0x8000000c = reserved + [0x800000d] = "Count over interrupt", + --0x8000000e-0xffffffff = reserved/platform +} + +local fault --non-nil values are for deferred faults that will trap after the instruction finishes local function implodebits(bits,count,signed) local negative = false @@ -281,6 +321,7 @@ end local function getreg(reg) if reg > 15 and not mem.isa.i then digiline_send("monitordisp",string.format("Attempted read from\nregister x%d\nin E mode\nPC: %08X",reg,mem.registers.pc)) + fault = 2 return 0 end return reg == 0 and 0 or mem.registers[reg] @@ -289,6 +330,7 @@ end local function setreg(reg,val) if reg > 15 and not mem.isa.i then digiline_send("monitordisp",string.format("Attempted write to\nregister x%d\nin E mode\nPC: %08X",reg,mem.registers.pc)) + fault = 2 return end if val < 0 then val = val + (2^32) end @@ -298,9 +340,8 @@ end local function readram(address,bytes,instfetch) local bigendian = mem.bigendian 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] + 1) then - digiline_send("monitordisp",string.format("Out-of-bounds\nmemory read\nAddress: %08X\nPC: %08X\nSystem halted",address,mem.registers.pc)) - mem.running = false - return 0 + fault = instfetch and 1 or 5 + return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0 end local out = 0 for i=0,bytes-1 do @@ -341,8 +382,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] + 1) then - digiline_send("monitordisp",string.format("Out-of-bounds\nmemory write\nAddress: %08X\nPC: %08X\nSystem halted",address,mem.registers.pc)) - mem.running = false + fault = 7 return end if mem.reservationset then @@ -453,7 +493,8 @@ end local function readcsr(address) if not mem.csr[address] then - digiline_send("monitordisp",string.format("W: Attempted read\nfrom unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) + digiline_send("monitordisp",string.format("Attempted read\nfrom unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) + fault = 2 return 0 end return mem.csr[address] @@ -498,14 +539,45 @@ local function writecsr(address,data) writeram(data,0,1,true) --Invalidate reservation set at new address end return + elseif address == 0x305 then + --mtvec + mem.trapmode = data%4 + mem.trapbase = data-mem.trapmode end if not mem.csr[address] then - digiline_send("monitordisp",string.format("W: Attempted write\nto unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) + digiline_send("monitordisp",string.format("Attempted write\nto unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) + fault = 2 return end mem.csr[address] = data end +local function trap(reason) + writecsr(0x342,reason) --mcause + writecsr(0x341,mem.registers.pc) --mepc + writecsr(0x343,0) --mtval + if mem.trapbase then + if mem.trapmode == 1 then + --vectored mode + local int = reason >= 0x8000000 + reason = reason%0x80000000 + mem.registers.pc = reason*4+mem.trapbase + return true,false + else + --direct or reserved (treated as direct) mode + mem.registers.pc = mem.trapbase + return true,false + end + else + digiline_send("monitordisp","Unhandled trap!") + digiline_send("monitordisp",traptypes[reason] or "Unknown reason") + digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc)) + digiline_send("monitordisp","System halted") + mem.running = false + return true,true + end +end + local cmpushpopreglists = { [4] = {registers = {1},stack = 16}, [5] = {registers = {1,8},stack = 16}, @@ -933,10 +1005,7 @@ local operations = { end end, ebreak = function() - mem.running = false - digiline_send("monitordisp","Hit breakpoint") - digiline_send("montiordisp","System halted") - digiline_send("monitordisp",string.format("PC:%08X",mem.registers.pc)) + return trap(3) end, csrrw = function(rd,rs1,imm) setreg(rd,readcsr(imm)) @@ -1716,6 +1785,10 @@ local operations = { end setreg(rd,implodebits(outbits,32)) end, + mret = function() + mem.registers.pc = readcsr(0x341) + return true + end, } local function runinst(instruction) @@ -1818,6 +1891,8 @@ local function runinst(instruction) operations.czeroeqz(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x7 then operations.czeronez(rd,rs1,rs2) + else + return trap(2) end elseif opcode == 0x13 or opcode == 0x3 or opcode == 0x67 or opcode == 0x73 then --I-type @@ -1878,6 +1953,8 @@ local function runinst(instruction) operations.zip(rd,rs1,imm) elseif f3 == 0x5 and imm == 0x8f then operations.unzip(rd,rs1,imm) + else + return trap(2) end elseif opcode == 0x3 then if f3 == 0x0 then @@ -1892,21 +1969,29 @@ local function runinst(instruction) operations.lbu(rd,rs1,imm) elseif f3 == 0x5 then operations.lhu(rd,rs1,imm) + else + return trap(2) end elseif opcode == 0x67 then if f3 == 0x0 then return operations.jalr(rd,rs1,imm) + else + return trap(2) end elseif opcode == 0x73 then if f3 == 0x0 then if imm == 0x0 then operations.ecall() elseif imm == 0x1 then - operations.ebreak() + return operations.ebreak() elseif rs1 == 0 and rd == 0 and imm == 0x0d and mem.isa.a then return operations.wrsnto() elseif rs1 == 0 and rd == 0 and imm == 0x1d and mem.isa.a then return operations.wrssto() + elseif imm == 0x302 and rd == 0 and rs1 == 0 then + return operations.mret() + else + return trap(2) end elseif f3 == 0x1 then operations.csrrw(rd,rs1,imm) @@ -1926,6 +2011,8 @@ local function runinst(instruction) operations.csrrsi(rd,rs1,imm) elseif f3 == 0x7 then operations.csrrci(rd,rs1,imm) + else + return trap(2) end end elseif opcode == 0x23 then @@ -1946,6 +2033,8 @@ local function runinst(instruction) operations.sw(rs1,rs2,imm) elseif f3 == 0x3 then operations.sd(rs1,rs2,imm) + else + return trap(2) end elseif opcode == 0x63 then --B-type @@ -1969,6 +2058,8 @@ local function runinst(instruction) return operations.bltu(rs1,rs2,imm) elseif f3 == 0x7 then return operations.bgeu(rs1,rs2,imm) + else + return trap(2) end elseif opcode == 0x37 or opcode == 0x17 then --U-type @@ -1981,6 +2072,8 @@ local function runinst(instruction) operations.lui(rd,imm) elseif opcode == 0x17 then operations.auipc(rd,imm) + else + return trap(2) end elseif opcode == 0x6f then --J-type @@ -2043,11 +2136,15 @@ local function runinst(instruction) --sw.rl/sw.aqrl --The normal sw instruction implementation already meets these requirements operations.sw(rs1,rs2,0) + else + return trap(2) end elseif f3 == 3 then if f5 == 5 then --amocas.d operations.amocasd(rd,rs1,rs2) + else + return trap(2) end elseif f3 == 0 then if f5 == 1 then @@ -2088,6 +2185,8 @@ local function runinst(instruction) --sb.rl/sb.aqrl --The normal sb instruction implementation already meets these requirements operations.sb(rs1,rs2,0) + else + return trap(2) end elseif f3 == 1 then if f5 == 1 then @@ -2128,6 +2227,8 @@ local function runinst(instruction) --sh.rl/sh.aqrl --The normal sh instruction implementation already meets these requirements operations.sh(rs1,rs2,0) + else + return trap(2) end end elseif opcode == 0x0f then @@ -2138,7 +2239,7 @@ local function runinst(instruction) return false,true end elseif opcode == 0x0b then - --Custom instruction (currently all R-type) + --Custom0 instruction (currently all R-type) local f3 = implodebits({[0] = bits[12],bits[13],bits[14]},3) local xh3bextmsize = implodebits({[0] = bits[26],bits[27],bits[28]},3)+1 local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) @@ -2148,10 +2249,11 @@ local function runinst(instruction) operations.h3bextm(rd,rs1,rs2,xh3bextmsize) elseif f3 == 4 then operations.h3bextmi(rd,rs1,rs2,xh3bextmsize) + else + return trap(2) end else - mem.running = false - digiline_send("monitordisp",string.format("Invalid opcode %02X,\nhalted",opcode)) + trap(2) end end @@ -2228,7 +2330,7 @@ local function runcinst(instruction) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6]},5) if rd == 0 and rs2 == 0 then --c.ebreak (CR) - operations.ebreak() + return operations.ebreak() elseif rs2 == 0 then --c.jalr (CR) return operations.jalr(1,rs1,0,true) @@ -2271,6 +2373,8 @@ local function runcinst(instruction) elseif rd%2 == 1 and rd <= 15 then --c.mop operations.cmop(rd,imm) + else + return trap(2) end elseif opcode == 1 and f3 == 0 then --c.addi (CI) @@ -2463,8 +2567,7 @@ local function runcinst(instruction) return true end else - mem.running = false - digiline_send("monitordisp","Invalid compressed\ninstruction, halted") + trap(2) end end @@ -2485,6 +2588,7 @@ local function run(limit) local first = true repeat + fault = nil if mem.registers.pc == mem.breakpoint and not first then digiline_send("monitordisp","Hit breakpoint") mem.running = false @@ -2494,15 +2598,20 @@ local function run(limit) local instruction = readram(mem.registers.pc,4,true) if instruction%4 == 3 or not mem.isa.c then if mem.registers.pc%4 ~= 0 and not mem.isa.c then - digiline_send("monitordisp",string.format("Misaligned\ninstruction fetch,\nSystem halted\nPC: %08X",mem.registers.pc)) - mem.running = false - break + local _,stop = trap(0) + if stop then break end end local jumped,stop = runinst(instruction) + if fault then + jumped,stop = trap(fault) + 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) + end if not jumped then mem.registers.pc = (mem.registers.pc + 2) % 2^32 end if stop then break end end @@ -2554,6 +2663,35 @@ local regaliases = { t6 = 31, } +local csraliases = { + cycle = 0xc00, + time = 0xc01, + instret = 0xc02, + cycleh = 0xc80, + timeh = 0xc81, + instreth = 0xc82, + mvendorid = 0xf11, + marchid = 0xf12, + mimpid = 0xf13, + mhartid = 0xf14, + mstatus = 0x300, + misa = 0x301, + medeleg = 0x302, + mideleg = 0x303, + mie = 0x304, + mtvec = 0x305, + mcounteren = 0x306, + mstatush = 0x310, + medelegh = 0x312, + mscratch = 0x340, + mepc = 0x341, + mcause = 0x342, + mtval = 0x343, + mip = 0x344, + mtinst = 0x34a, + mtval2 = 0x34b, +} + if event.type == "program" or event.iid == "reset" then mem.ram = {} for i=0,(RAM_SIZE/256)-1 do @@ -2575,7 +2713,12 @@ if event.type == "program" or event.iid == "reset" then [0xf14] = 0, --mhartid [0x300] = 0, --mstatus [0x301] = 0x40001107, --misa (RV32IMACB) + [0x305] = 0, --mtvec [0x310] = 0, --mstatush + [0x340] = 0, --mscratch + [0x341] = 0, --mepc + [0x342] = 0, --mcause + [0x343] = 0, --mtval [0x800] = 0, --Clock Controls [0x801] = 0, --MMIO Base Address [0xc00] = 0, --CYCLE @@ -2605,6 +2748,8 @@ if event.type == "program" or event.iid == "reset" then mem.meseconsdir = {} mem.meseconsdata = {} port = {} + mem.trapbase = nil + mem.trapmode = nil elseif event.type == "on" or event.type == "off" then local pname = string.lower(event.pin.name) local pstate = pin[pname] @@ -2756,14 +2901,14 @@ elseif event.channel == "monitorkb" then elseif argv[1] == "clearbreak" then mem.breakpoint = -1 elseif argv[1] == "rdcsr" then - local address = validateandclamp(argv[2],0,0xfff) + local address = csraliases[argv[2]] or validateandclamp(argv[2],0,0xfff) if not address then digiline_send("monitordisp","Bad CSR address") return end digiline_send("monitordisp",string.format("%03X: %08X",address,readcsr(address))) elseif argv[1] == "wrcsr" then - local address = validateandclamp(argv[2],0,0xfff) + local address = csraliases[argv[2]] or validateandclamp(argv[2],0,0xfff) if not address then digiline_send("monitordisp","Bad CSR address") return |
