summaryrefslogtreecommitdiff
path: root/rvcontroller.lua
diff options
context:
space:
mode:
authorcheapie <cheapiephp@gmail.com>2026-06-19 22:38:28 -0500
committercheapie <cheapiephp@gmail.com>2026-06-19 22:38:28 -0500
commit33e4744d1c73c9f5512abb4010e4cc81763ab3a2 (patch)
treef690b968f5feeb6e905aa1265c9b4b3584e6a668 /rvcontroller.lua
parent569bc33a90282d039a9d1701bee7d9cd5919a24e (diff)
downloadrvcontroller-33e4744d1c73c9f5512abb4010e4cc81763ab3a2.tar
rvcontroller-33e4744d1c73c9f5512abb4010e4cc81763ab3a2.tar.gz
rvcontroller-33e4744d1c73c9f5512abb4010e4cc81763ab3a2.tar.bz2
rvcontroller-33e4744d1c73c9f5512abb4010e4cc81763ab3a2.tar.xz
rvcontroller-33e4744d1c73c9f5512abb4010e4cc81763ab3a2.zip
Add machine timer interrupt
Diffstat (limited to 'rvcontroller.lua')
-rw-r--r--rvcontroller.lua183
1 files changed, 172 insertions, 11 deletions
diff --git a/rvcontroller.lua b/rvcontroller.lua
index fbab09d..6130659 100644
--- a/rvcontroller.lua
+++ b/rvcontroller.lua
@@ -200,6 +200,12 @@ Format: [0000dcba] (Input bits are RO, output bits are RW)
For inputs: Each bit contains the state of the corresponding input. Writes are ignored.
For outputs: Each bit contains the state of the corresponding output. Writing to these bits turns the output on/off.
+Base + 2: mtime (see RISC-V Machine Level ISA specification)
+
+Base + 10: mtimecmp (see RISC-V Machine Level ISA specification)
+
+Base + 18 - Base + 255: Reserved
+
]]
--Settings
@@ -341,7 +347,7 @@ end
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] + 1) then
+ 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
end
@@ -356,7 +362,7 @@ local function readram(address,bytes,instfetch)
if offsetaddr == mem.csr[0x801] and mem.csr[0x801] ~= 0 then
--Mesecons I/O Direction
local bits = {[0] = mem.meseconsdir.a,mem.meseconsdir.b,mem.meseconsdir.c,mem.meseconsdir.d}
- out = out + implodebits(bits,4)
+ out = out + implodebits(bits,4)*2^(8*i)
elseif offsetaddr == mem.csr[0x801]+1 and mem.csr[0x801] ~= 0 then
--Mesecons I/O Data
local bits = {[0] = pin.a,pin.b,pin.c,pin.d}
@@ -372,7 +378,57 @@ local function readram(address,bytes,instfetch)
if mem.meseconsdir.d then
bits[3] = mem.meseconsdata.d
end
- out = out + implodebits(bits,4)
+ out = out + implodebits(bits,4)*2^(8*i)
+ elseif offsetaddr >= mem.csr[0x801]+2 and offsetaddr < mem.csr[0x801]+2+8 and mem.csr[0x801] ~= 0 then
+ --mtime
+ local byteindex = offsetaddr - (mem.csr[0x801]+2)
+ if mem.mbe then byteindex = 7-byteindex end
+ local time = mem.csr[0xc01]
+ local timeh = mem.csr[0xc81]
+ local thisout = 0
+ if byteindex == 0 then
+ thisout = time%2^8
+ elseif byteindex == 1 then
+ thisout = math.floor(time/2^8)%2^8
+ elseif byteindex == 2 then
+ thisout = math.floor(time/2^16)%2^8
+ elseif byteindex == 3 then
+ thisout = math.floor(time/2^24)
+ elseif byteindex == 4 then
+ thisout = timeh%2^8
+ elseif byteindex == 5 then
+ thisout = math.floor(timeh/2^8)%2^8
+ elseif byteindex == 6 then
+ thisout = math.floor(timeh/2^16)%2^8
+ elseif byteindex == 7 then
+ thisout = math.floor(timeh/2^24)
+ end
+ thisout = thisout*2^(i*8)
+ out = out + thisout
+ elseif offsetaddr >= mem.csr[0x801]+10 and offsetaddr < mem.csr[0x801]+10+8 and mem.csr[0x801] ~= 0 then
+ --mtimecmp
+ local byteindex = offsetaddr - (mem.csr[0x801]+10)
+ if mem.mbe then byteindex = 7-byteindex end
+ local thisout = 0
+ if byteindex == 0 then
+ thisout = mem.mtimecmplow%2^8
+ elseif byteindex == 1 then
+ thisout = math.floor(mem.mtimecmplow/2^8)%2^8
+ elseif byteindex == 2 then
+ thisout = math.floor(mem.mtimecmplow/2^16)%2^8
+ elseif byteindex == 3 then
+ thisout = math.floor(mem.mtimecmplow/2^24)
+ elseif byteindex == 4 then
+ thisout = mem.mtimecmphigh%2^8
+ elseif byteindex == 5 then
+ thisout = math.floor(mem.mtimecmphigh/2^8)%2^8
+ elseif byteindex == 6 then
+ thisout = math.floor(mem.mtimecmphigh/2^16)%2^8
+ elseif byteindex == 7 then
+ thisout = math.floor(mem.mtimecmphigh/2^24)
+ end
+ thisout = thisout*2^(i*8)
+ out = out + thisout
elseif offsetaddr < RAM_SIZE then
local segment = math.floor(offsetaddr/256)
local offset = offsetaddr%256
@@ -383,7 +439,7 @@ local function readram(address,bytes,instfetch)
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
+ if address > RAM_SIZE-1 and not fake and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then
fault = 7
return
end
@@ -410,7 +466,7 @@ local function writeram(address,data,bytes,fake)
end
if offsetaddr == mem.csr[0x801] and mem.csr[0x801] ~= 0 then
--Mesecons I/O Direction
- local bits = explodebits(data,8)
+ local bits = explodebits(thisbyte,8)
mem.meseconsdir = {
a = bits[0],
b = bits[1],
@@ -443,7 +499,7 @@ local function writeram(address,data,bytes,fake)
end
elseif offsetaddr == mem.csr[0x801] + 1 and mem.csr[0x801] ~= 0 then
--Mesecons I/O Data
- local bits = explodebits(data,8)
+ local bits = explodebits(thisbyte,8)
if mem.meseconsdir.a then
mem.meseconsdata.a = bits[0]
port.a = bits[0]
@@ -460,6 +516,47 @@ local function writeram(address,data,bytes,fake)
mem.meseconsdata.d = bits[3]
port.d = bits[3]
end
+ elseif offsetaddr >= mem.csr[0x801]+2 and offsetaddr < mem.csr[0x801]+2+8 and mem.csr[0x801] ~= 0 then
+ --mtime
+ local byteindex = offsetaddr - (mem.csr[0x801]+2)
+ if mem.mbe then byteindex = 7-byteindex end
+ if byteindex >= 4 then
+ local bits = explodebits(mem.csr[0xc81],32)
+ local newbits = explodebits(thisbyte,8)
+ byteindex = byteindex - 4
+ for i=0,7 do
+ bits[byteindex*8+i] = newbits[i]
+ end
+ mem.csr[0xc81] = implodebits(bits,32)
+ else
+ local bits = explodebits(os.time()-mem.starttime,32)
+ local newbits = explodebits(thisbyte,8)
+ for i=0,7 do
+ bits[byteindex*8+i] = newbits[i]
+ end
+ local newmtime = implodebits(bits,32)
+ mem.starttime = os.time()-newmtime
+ end
+ elseif offsetaddr >= mem.csr[0x801]+10 and offsetaddr < mem.csr[0x801]+10+8 and mem.csr[0x801] ~= 0 then
+ --mtimecmp
+ local byteindex = offsetaddr - (mem.csr[0x801]+10)
+ if mem.mbe then byteindex = 7-byteindex end
+ if byteindex >= 4 then
+ local bits = explodebits(mem.mtimecmphigh,32)
+ local newbits = explodebits(thisbyte,8)
+ byteindex = byteindex - 4
+ for i=0,7 do
+ bits[byteindex*8+i] = newbits[i]
+ end
+ mem.mtimecmphigh = implodebits(bits,32)
+ else
+ local bits = explodebits(mem.mtimecmplow,32)
+ local newbits = explodebits(thisbyte,8)
+ for i=0,7 do
+ bits[byteindex*8+i] = newbits[i]
+ end
+ mem.mtimecmplow = implodebits(bits,32)
+ end
elseif address < RAM_SIZE then
local segment = math.floor(offsetaddr/256)
local offset = offsetaddr%256
@@ -574,6 +671,7 @@ local function writecsr(address,data)
--mtvec
mem.trapmode = data%4
mem.trapbase = data-mem.trapmode
+ mem.mtvecwritten = true
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))
@@ -584,13 +682,18 @@ local function writecsr(address,data)
end
local function trap(reason)
+ local oldreason = reason
+ if mem.mdt then
+ writecsr(0x34b,reason) --mtval2
+ reason = 0x10
+ end
writecsr(0x342,reason) --mcause
writecsr(0x341,mem.registers.pc) --mepc
writecsr(0x343,0) --mtval
mem.mpie = mem.mie
mem.mie = false
if not mem.mdt then
- mem.mdt = 1
+ mem.mdt = true
if mem.trapmode == 1 then
--vectored mode
local int = reason >= 0x8000000
@@ -604,7 +707,12 @@ local function trap(reason)
end
else
digiline_send("monitordisp","Unexpected trap!")
- digiline_send("monitordisp",traptypes[reason] or "Unknown reason")
+ if not mem.mtvecwritten then
+ digiline_send("monitordisp",traptypes[oldreason] or "Unknown reason")
+ digiline_send("monitordisp","(no handler present)")
+ else
+ digiline_send("monitordisp",traptypes[reason] or "Unknown reason")
+ end
digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc))
digiline_send("monitordisp","System halted")
mem.running = false
@@ -612,6 +720,24 @@ local function trap(reason)
end
end
+local function checkinterrupts()
+ local miebits = explodebits(mem.csr[0x304],32)
+ local mipbits = {}
+ if mem.mtimecmphigh == mem.csr[0xc81] and (os.time() - mem.starttime) >= mem.mtimecmplow then
+ mipbits[7] = true --mtip
+ elseif mem.mtimecmphigh < mem.csr[0xc81] then
+ mipbits[7] = true --mtip
+ end
+ mem.csr[0x344] = implodebits(mipbits,32)
+ if not mem.mie then return end
+ local order = {11,3,7,9,1,5,13}
+ for _,i in ipairs(order) do
+ if miebits[i] and mipbits[i] then
+ return trap(0x80000000+i)
+ end
+ end
+end
+
local cmpushpopreglists = {
[4] = {registers = {1},stack = 16},
[5] = {registers = {1,8},stack = 16},
@@ -1044,6 +1170,7 @@ local operations = {
csrrw = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
writecsr(imm,getreg(rs1))
+ return checkinterrupts()
end,
csrrs = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
@@ -1054,6 +1181,7 @@ local operations = {
csrbits[i] = csrbits[i] or rs1bits[i]
end
writecsr(imm,implodebits(csrbits,32))
+ return checkinterrupts()
end,
csrrc = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
@@ -1064,10 +1192,12 @@ local operations = {
csrbits[i] = csrbits[i] and not rs1bits[i]
end
writecsr(imm,implodebits(csrbits,32))
+ return checkinterrupts()
end,
csrrwi = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
writecsr(imm,rs1)
+ return checkinterrupts()
end,
csrrsi = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
@@ -1078,6 +1208,7 @@ local operations = {
csrbits[i] = csrbits[i] or rs1bits[i]
end
writecsr(imm,implodebits(csrbits,32))
+ return checkinterrupts()
end,
csrrci = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
@@ -1088,6 +1219,7 @@ local operations = {
csrbits[i] = csrbits[i] and not rs1bits[i]
end
writecsr(imm,implodebits(csrbits,32))
+ return checkinterrupts()
end,
mul = function(rd,rs1,rs2)
local plow = mul64(getreg(rs1),getreg(rs2),false,false)
@@ -1821,12 +1953,20 @@ local operations = {
end,
mret = function()
mem.registers.pc = readcsr(0x341)
+ mem.mdt = false
mem.mie = mem.mpie
mem.mpie = true
mem.mpp = 3
- mem.mdt = false
+ checkinterrupts()
return true
end,
+ wfi = function()
+ mem.interruptwaiting = true
+ mem.running = false
+ digiline_send("monitordisp","Waiting for\ninterrupt")
+ interrupt(1,"checkinterrupt")
+ return false,true
+ end,
}
local function runinst(instruction)
@@ -2028,6 +2168,8 @@ local function runinst(instruction)
return operations.wrssto()
elseif imm == 0x302 and rd == 0 and rs1 == 0 then
return operations.mret()
+ elseif imm == 0x105 and rd == 0 and rs1 == 0 then
+ return operations.wfi()
else
return trap(2)
end
@@ -2619,10 +2761,8 @@ local function run(limit)
end
--0xc01 = TIME
- --0xc81 = TIMEH
local timediff = os.time()-mem.starttime
mem.csr[0xc01] = timediff%2^32
- mem.csr[0xc81] = math.floor(timediff/2^32)
local first = true
repeat
@@ -2632,6 +2772,9 @@ local function run(limit)
mem.running = false
break
end
+ if first then
+ checkinterrupts()
+ end
first = false
local instruction = readram(mem.registers.pc,4,true)
if instruction%4 == 3 or not mem.isa.c then
@@ -2751,11 +2894,14 @@ if event.type == "program" or event.iid == "reset" then
[0xf14] = 0, --mhartid
[0x300] = 0, --mstatus
[0x301] = 0x40001107, --misa (RV32IMACB)
+ [0x304] = 0, --mie
[0x305] = 0, --mtvec
[0x340] = 0, --mscratch
[0x341] = 0, --mepc
[0x342] = 0, --mcause
[0x343] = 0, --mtval
+ [0x344] = 0, --mip
+ [0x34b] = 0, --mtval2
[0x800] = 0, --Clock Controls
[0x801] = 0, --MMIO Base Address
[0xc00] = 0, --CYCLE
@@ -2787,10 +2933,15 @@ if event.type == "program" or event.iid == "reset" then
port = {}
mem.trapbase = 0
mem.trapmode = 0
+ mem.mtvecwritten = false
mem.mie = false
mem.mpie = true
mem.mpp = 3
mem.mdt = true
+ mem.mtimecmplow = 0
+ mem.mtimecmphigh = 0
+ mem.mtimehigh = 0
+ mem.interruptwaiting = false
elseif event.type == "on" or event.type == "off" then
local pname = string.lower(event.pin.name)
local pstate = pin[pname]
@@ -3112,6 +3263,16 @@ elseif event.iid == "tick" and mem.running then
else
interrupt(1/CLOCK_SPEED,"tick")
end
+elseif event.iid == "checkinterrupt" and mem.interruptwaiting then
+ local restarted = checkinterrupts()
+ if restarted then
+ mem.interruptwaiting = false
+ mem.running = true
+ digiline_send("monitordisp","CPU started")
+ interrupt(1/CLOCK_SPEED,"tick")
+ else
+ interrupt(1,"checkinterrupt",mem.csr[0x800]%2 == 1)
+ end
end
if stdoutdirty then