summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assembly/crosswalk/crosswalk.S1
-rw-r--r--assembly/multitask/multitask.S1
-rw-r--r--assembly/rrxing2/rrxing2.S19
-rw-r--r--doc/_start_here.txt1
-rw-r--r--doc/csr.txt8
-rw-r--r--rvcontroller.lua58
6 files changed, 84 insertions, 4 deletions
diff --git a/assembly/crosswalk/crosswalk.S b/assembly/crosswalk/crosswalk.S
index 86a0813..4baeaa8 100644
--- a/assembly/crosswalk/crosswalk.S
+++ b/assembly/crosswalk/crosswalk.S
@@ -1,3 +1,4 @@
+csrsi mnstatus,8 # Set NMIE so that interrupts can work (not actually setting up a real NMI handler)
la t0,handler
csrw mtvec,t0 # Set up trap handler
li t0,0x400
diff --git a/assembly/multitask/multitask.S b/assembly/multitask/multitask.S
index e46dd81..29df46c 100644
--- a/assembly/multitask/multitask.S
+++ b/assembly/multitask/multitask.S
@@ -1,3 +1,4 @@
+csrsi mnstatus,8 # Set NMIE so that interrupts can work (not actually setting up a real NMI handler)
la t0,handler
csrw mtvec,t0 # Set trap handler address and mode (0/direct)
li t0,0x400
diff --git a/assembly/rrxing2/rrxing2.S b/assembly/rrxing2/rrxing2.S
index c94edc0..bb2c4dc 100644
--- a/assembly/rrxing2/rrxing2.S
+++ b/assembly/rrxing2/rrxing2.S
@@ -8,6 +8,9 @@ j _start
.include "util.S"
_start:
+la t0,nmihandler
+csrw 0xbc0,t0 # Set up RNMI handler
+csrsi mnstatus,8 # Turn on NMIE
li t0,0x400
csrs mseccfg,t0 # Turn on MLPE (enable Zicfilp)
li sp,0x10000 # Set up stack pointer
@@ -187,6 +190,19 @@ ecall # Tell the user
li a7,10
ecall # Then halt
+.align 2
+nmihandler:
+li a7,4
+la a0,uinmi
+ecall # Display NMI message
+
+csrr a0,mncause # Read what the cause was
+li a7,1
+ecall # Tell the user
+
+li a7,10
+ecall # Then halt
+
states: .word destate0,destate1,destate2,destate3,destate4,destate5,destate6,0,0,0,0,0,0,0,0,0
.word ukstate0,ukstate1,ukstate2,ukstate3,ukstate4,ukstate5,ukstate6,ukstate7,0,0,0,0,0,0,0,0
.word usstate0,usstate1,usstate2,usstate3,usstate4,0,0,0,0,0,0,0,0,0,0,0
@@ -364,7 +380,8 @@ activetime: .byte 15
extensiontime: .byte 25
traincounter: .word 0
-uiunimplementedtrap: .asciz "Unimplemented trap!\nmcause: "
+uiunimplementedtrap: .asciz "\nUnimplemented trap!\nmcause: "
+uinmi: .asciz "\nUnexpected NMI!\nmncause: "
channelbuf: .skip 16
msgbuf: .skip 16
lightchannel: .asciz "light"
diff --git a/doc/_start_here.txt b/doc/_start_here.txt
index b9dc6d0..bf83857 100644
--- a/doc/_start_here.txt
+++ b/doc/_start_here.txt
@@ -89,6 +89,7 @@ RVController is intended to be compliant with the following specifications:
- 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
* "Smcntrpmf" Cycle and Instret Privilege Mode Filtering, Version 1.0
+* "Smrnmi" Extension for Resumable Non-Maskable Interrupts, Version 1.0
* "Smdbltrp" Double Trap Extension, Version 1.0
For full specification text, visit https://docs.riscv.org/reference/home/index.html
diff --git a/doc/csr.txt b/doc/csr.txt
index 2e88f67..cb129b2 100644
--- a/doc/csr.txt
+++ b/doc/csr.txt
@@ -21,6 +21,10 @@ The following standard CSRs are available:
* 0x34b: mtval2
* 0x721: mcyclecfgh
* 0x722: minstretcfgh
+* 0x740: mnscratch
+* 0x741: mnepc
+* 0x742: mncause
+* 0x744: mnstatus
* 0x747: mseccfg
* 0xb00: mcycle
* 0xb02: minstret
@@ -76,6 +80,10 @@ the Address field selects the base address of the 256-byte MMIO region.
When set to 0x00000000, MMIO is disabled.
See mmio.txt for MMIO region contents.
+0xbc0 - mnvec (RNMI Handler Address)
+
+Contains the address of the RNMI handler (see Smrnmi extension). Default is 0x8000.
+
0xbf1 - h3.misa
Stores enhanced ISA identification information, see the Hazard3 reference manual for more information: https://wren.wtf/hazard3/doc/#reg-h3.misa
diff --git a/rvcontroller.lua b/rvcontroller.lua
index c4e27d6..bf9877f 100644
--- a/rvcontroller.lua
+++ b/rvcontroller.lua
@@ -640,6 +640,13 @@ local function writecsr(address,data)
elseif address == 0x722 then --minstretcfgh
mem.instretminh = explodebits(data,32)[30]
return
+ elseif address == 0x744 then --mnstatus
+ local oldbits = explodebits(mem.csr[0x744],32)
+ local newbits = explodebits(data,32)
+ oldbits[9] = newbits[9]
+ oldbits[3] = oldbits[3] or newbits[3]
+ mem.csr[0x744] = implodebits(oldbits,32)
+ return
elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then --mcycle(h)/minstret(h)
--Transform into cycle(h)/instret(h) (but allow the write)
address = address + 0x100
@@ -710,6 +717,20 @@ local function writecsr(address,data)
mem.csr[address] = data
end
+local function rnmi(reason)
+ mem.rnmi = true
+ local mnstatusbits = explodebits(mem.csr[0x744],32)
+ writecsr(0x741,mem.registers.pc) --mnepc
+ writecsr(0x742,reason) --mncause
+ mnstatusbits[11] = 1 --mnpp[0]
+ mnstatusbits[12] = 1 --mnpp[1]
+ mnstatusbits[9] = mem.elp --mnpelp
+ mem.elp = false
+ mnstatusbits[3] = false --nmie
+ mem.registers.pc = mem.csr[0xbc0] --mnvec
+ mem.csr[0x744] = implodebits(mnstatusbits,32)
+end
+
local function trap(reason,val)
if reason >= 0x8000000 then
--Interrupt
@@ -725,7 +746,7 @@ local function trap(reason,val)
end
end
local oldreason = reason
- if mem.mdt then
+ if mem.mdt and not mem.rnmi then
writecsr(0x34b,reason) --mtval2
reason = 0x10
end
@@ -753,8 +774,15 @@ local function trap(reason,val)
mem.registers.pc = mem.trapbase
return true,false
end
+ elseif mem.csr[0x744]%16 >= 8 and not mem.rnmi then
+ rnmi(oldreason)
+ return true,false
else
- digiline_send("monitordisp","Unexpected trap!")
+ if mem.rnmi then
+ digiline_send("monitordisp","Trap during NMI!")
+ else
+ digiline_send("monitordisp","Unexpected trap!")
+ end
if not mem.mtvecwritten then
digiline_send("monitordisp",traptypes[oldreason] or "Unknown reason")
if oldreason == 18 then
@@ -783,6 +811,7 @@ local function trap(reason,val)
end
local function checkinterrupts()
+ local mnstatusbits = explodebits(mem.csr[0x744],32)
local miebits = explodebits(mem.csr[0x304],32)
local mipbits = explodebits(mem.csr[0x344],32)
if mem.mtimecmphigh == mem.csr[0xc81] and (os.time() - mem.starttime) >= mem.mtimecmplow then
@@ -794,6 +823,7 @@ local function checkinterrupts()
end
mem.csr[0x344] = implodebits(mipbits,32)
if not mem.mie then return end
+ if not mnstatusbits[3] then return end --nmie
local order = {11,3,7,9,1,5,13,16,17,18}
for _,i in ipairs(order) do
if miebits[i] and mipbits[i] then
@@ -2062,6 +2092,15 @@ local operations = {
checkinterrupts()
return true
end,
+ mnret = function()
+ mem.registers.pc = readcsr(0x741)
+ local mnstatusbits = explodebits(mem.csr[0x744],32)
+ mnstatusbits[3] = 1 --nmie
+ mem.elp = mem.mlpe and mnstatusbits[9] --mnpelp
+ mem.csr[0x741] = implodebits(mnstatusbits,32)
+ mem.rnmi = false
+ return true
+ end,
wfi = function()
mem.interruptwaiting = true
mem.running = false
@@ -2524,6 +2563,8 @@ local function runinst(instruction)
return operations.wrssto()
elseif imm == 0x302 and rd == 0 and rs1 == 0 then
return operations.mret()
+ elseif imm == 0x702 and rd == 0 and rs1 == 0 then
+ return operations.mnret()
elseif imm == 0x105 and rd == 0 and rs1 == 0 then
return operations.wfi()
else
@@ -2820,7 +2861,7 @@ local function runinst(instruction)
return trap(2,instruction)
end
else
- trap(2,instruction)
+ return trap(2,instruction)
end
end
@@ -3405,6 +3446,11 @@ local csraliases = {
minstretcfg = 0x322,
mcyclecfgh = 0x721,
minstretcfgh = 0x722,
+ mnscratch = 0x740,
+ mnepc = 0x741,
+ mncause = 0x742,
+ mnstatus = 0x744,
+ mnvec = 0xbc0,
}
if event.type == "program" or event.iid == "reset" then
@@ -3458,6 +3504,11 @@ if event.type == "program" or event.iid == "reset" then
[0xc84] = 0, --hpmcounter4h
[0xc85] = 0, --hpmcounter5h
[0xc86] = 0, --hpmcounter6h
+ [0x740] = 0, --mnscratch
+ [0x741] = 0, --mnepc
+ [0x742] = 0, --mncause
+ [0x744] = 0, --mnstatus
+ [0xbc0] = 0x8000, --mnvec
}
for i=1,6 do mem.stdout[i] = "" end
local mdisp = event.type == "program" and "\nReset: Cold" or "\nReset: Warm"
@@ -3497,6 +3548,7 @@ if event.type == "program" or event.iid == "reset" then
mem.mcountinhibit = {}
mem.cycleminh = false
mem.instretminh = false
+ mem.rnmi = false
elseif event.type == "on" or event.type == "off" then
local pname = string.lower(event.pin.name)
local pstate = pin[pname]