From ca28b45e758e2f36be5a93c5ae0a5cdfe91d5db9 Mon Sep 17 00:00:00 2001 From: cheapie Date: Fri, 26 Jun 2026 09:55:56 -0500 Subject: Add basic mesecons/digilines interrupt support --- assembly/extint-test/Makefile | 24 ++++++ assembly/extint-test/extint-test.S | 145 +++++++++++++++++++++++++++++++++++ assembly/extint-test/extint-test.elf | Bin 0 -> 6376 bytes assembly/extint-test/extint-test.hex | 46 +++++++++++ assembly/extint-test/extint-test.o | Bin 0 -> 3064 bytes assembly/extint-test/rvcontroller.ld | 1 + rvcontroller.lua | 53 +++++++++++-- 7 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 assembly/extint-test/Makefile create mode 100644 assembly/extint-test/extint-test.S create mode 100755 assembly/extint-test/extint-test.elf create mode 100644 assembly/extint-test/extint-test.hex create mode 100644 assembly/extint-test/extint-test.o create mode 120000 assembly/extint-test/rvcontroller.ld diff --git a/assembly/extint-test/Makefile b/assembly/extint-test/Makefile new file mode 100644 index 0000000..2464c5e --- /dev/null +++ b/assembly/extint-test/Makefile @@ -0,0 +1,24 @@ +MARCH ?= rv32imacbzicntr_zicond_zicsr_zifencei_zihintpause_zilsd_zclsd_zabha_zacas_zbkb_zbkx_zcb_zcmp_zcmt + +.PHONY: all dump load clean + +all: extint-test.hex + +extint-test.o: extint-test.S + riscv32-none-elf-as -I../rvcontroller-libraries -march=${MARCH} -o extint-test.o extint-test.S + +extint-test.elf: extint-test.o + riscv32-none-elf-ld -T rvcontroller.ld --no-warn-rwx-segments -o extint-test.elf extint-test.o + +dump: extint-test.elf + riscv32-none-elf-objdump -d extint-test.elf + +extint-test.hex: extint-test.elf + riscv32-none-elf-objcopy -O ihex extint-test.elf extint-test.hex + +load: extint-test.hex + bash -c "wl-copy < extint-test.hex" + +clean: + rm -f extint-test.hex extint-test.elf extint-test.o + diff --git a/assembly/extint-test/extint-test.S b/assembly/extint-test/extint-test.S new file mode 100644 index 0000000..c1b872d --- /dev/null +++ b/assembly/extint-test/extint-test.S @@ -0,0 +1,145 @@ +li t0,0xffff0000 +csrw 0x801,t0 # Map the MMIO region somewhere +la t0,handler +csrw mtvec,t0 # Set up trap handler +li t0,0x400 +csrc mstatush,t0 # Turn off MDT +li t0,0x30000 +csrw mie,t0 # Mask all interrupts except mesecons/digilines +csrw mip,zero # Clear any pending interrupts +csrsi mstatus,0x8 # Turn on MIE to enable interrupts + +mainloop: +li a7,4 +la a0,waitingmsg +ecall # Print waiting message +wfi # And do nothing else, this program is entirely interrupt-driven +j mainloop + + + +.align 4 +handler: +csrrw x31,mscratch,x31 # Free up x31 to hold the context address +la x31,hcontext # Using x register numbers here since trap handlers don't use the usual calling convention +sw x1,0(x31) # Save all the registers to the context +sd x2,4(x31) # Two at a time since we have Zilsd/Zclsd +sd x4,12(x31) # Note that I don't actually _need_ all of the registers in the handler, +sd x6,20(x31) # but I wanted to try this anyway to see how it goes +sd x8,28(x31) +sd x10,36(x31) +sd x12,44(x31) +sd x14,52(x31) +sd x16,60(x31) +sd x18,68(x31) +sd x20,76(x31) +sd x22,84(x31) +sd x24,92(x31) +sd x26,100(x31) +sd x28,108(x31) +sw x30,116(x31) +csrr x30,mscratch # Get the old x31 value back into a register +sw x30,120(x31) # and write that back out too + +csrr t0,mcause # Read why the trap occurred + +li t1,0x80000010 # Interrupt 16 = mesecons +beq t0,t1,handler_mesecons + +li t1,0x80000011 # Interrupt 17 = digilines +beq t0,t1,handler_digilines + +j handler_other # Something else + +handler_mesecons: +li a7,4 +la a0,meseconsmsg +ecall # Show that this was a mesecons event + +li a7,1 +li t0,0xffff0000 +lb a0,1(t0) # Read the mesecons I/O port value +ecall # And print that + +li a7,11 +li a0,'\n' +ecall # Line feed + +j handler_epilogue + +handler_digilines: +li a7,4 +la a0,digilinesmsg +ecall # Show that this was a digilines event + +li a7,135 +la a0,channelbuf +li a1,21 +la a2,msgbuf +li a3,21 +ecall # Read what the event was + +li a7,4 +la a0,channelbuf +ecall # Print the channel + +li a7,4 +la a0,digilinesmsg2 +ecall # "Message:", this has its own line feeds in it + +li a7,4 +la a0,msgbuf +ecall # Print the message + +li a7,11 +li a0,'\n' +ecall # Line feed + +li a7,134 +ecall # Clear out any other digilines messages + +j handler_epilogue + +handler_other: +li a7,4 +la a0,unimplementedmsg +ecall # Display unimplemented trap message + +csrr a0,mcause # Read what the cause was +li a7,1 +ecall # Tell the user + +li a7,10 +ecall # Then halt + +handler_epilogue: +csrw mip,zero # Clear out all pending interrupts +la x31,hcontext # x numbers here for the same reason as during the save +lw x1,0(x31) # Load all of the registers from the context +ld x2,4(x31) # Two at a time since we have Zilsd/Zclsd +ld x4,12(x31) +ld x6,20(x31) +ld x8,28(x31) +ld x10,36(x31) +ld x12,44(x31) +ld x14,52(x31) +ld x16,60(x31) +ld x18,68(x31) +ld x20,76(x31) +ld x22,84(x31) +ld x24,92(x31) +ld x26,100(x31) +ld x28,108(x31) +ld x30,116(x31) +mret # Return to the program (not that the rest of the program does much) + +hcontext: .skip 32*4 + + +waitingmsg: .asciz "Waiting...\n" +meseconsmsg: .asciz "Mesecons event!\nInput value: " +digilinesmsg: .asciz "Digilines event!\nChannel:\n" +digilinesmsg2: .asciz "\nMessage:\n" +unimplementedmsg: .asciz "Unimplemented trap!\nmcause: " +channelbuf: .asciz " " +msgbuf: .asciz " " diff --git a/assembly/extint-test/extint-test.elf b/assembly/extint-test/extint-test.elf new file mode 100755 index 0000000..15f2f80 Binary files /dev/null and b/assembly/extint-test/extint-test.elf differ diff --git a/assembly/extint-test/extint-test.hex b/assembly/extint-test/extint-test.hex new file mode 100644 index 0000000..c3382c4 --- /dev/null +++ b/assembly/extint-test/extint-test.hex @@ -0,0 +1,46 @@ +:10000000C17273901280970200009382A2037390D2 +:1000100052309302004073B00231B7020300739074 +:10002000423073104034736004309148170500006B +:100030001305A51F7300000073005010FDB70100E9 +:10004000F39F0F34970F0000938F2F1623A01F00EC +:1000500023B22F0023B64F0023BA6F0023BE8F00B8 +:1000600023B2AF0223B6CF0223BAEF0223BE0F039F +:1000700023B22F0523B64F0523BA6F0523BE8F0584 +:1000800023B2AF0723B6CF0723AAEF07732F00349D +:1000900023ACEF07F3222034370300804103638849 +:1000A00062003703008045036385620271A09148B6 +:1000B0001705000013052518730000008548C1725C +:1000C0000385120073000000AD482945730000004D +:1000D00059A09148170500001305C51773000000CB +:1000E00093087008170500001305F51AD545170683 +:1000F00000001306A61BD5467300000091481705A3 +:100100000000130555197300000091481705000001 +:100110001305F5157300000091481705000013053D +:10012000E51873000000AD482945730000009308EE +:1001300060087300000005A0914817050000130532 +:10014000C513730000007325203485487300000038 +:10015000A9487300000073104034970F0000938F7C +:10016000CF0483A00F0003B14F0003B2CF0003B34D +:100170004F0103B4CF0103B54F0203B6CF0203B75B +:100180004F0303B8CF0303B94F0403BACF0403BB33 +:100190004F0503BCCF0503BD4F0603BECF0603BF0B +:1001A0004F07730020300000000000000000000036 +:1001B000000000000000000000000000000000003F +:1001C000000000000000000000000000000000002F +:1001D000000000000000000000000000000000001F +:1001E000000000000000000000000000000000000F +:1001F00000000000000000000000000000000000FF +:1002000000000000000000000000000000000000EE +:1002100000000000000000000000000000000000DE +:1002200000000000000057616974696E672E2E2E71 +:100230000A004D657365636F6E73206576656E7435 +:10024000210A496E7075742076616C75653A2000DC +:10025000446967696C696E6573206576656E7421A3 +:100260000A4368616E6E656C3A0A000A4D657373E5 +:100270006167653A0A00556E696D706C656D656EF3 +:100280007465642074726170210A6D6361757365B1 +:100290003A20002020202020202020202020202064 +:1002A000202020202020200020202020202020206E +:1002B00020202020202020202020202000000100BD +:0402C0001300000027 +:00000001FF diff --git a/assembly/extint-test/extint-test.o b/assembly/extint-test/extint-test.o new file mode 100644 index 0000000..3aba761 Binary files /dev/null and b/assembly/extint-test/extint-test.o differ diff --git a/assembly/extint-test/rvcontroller.ld b/assembly/extint-test/rvcontroller.ld new file mode 120000 index 0000000..bc01402 --- /dev/null +++ b/assembly/extint-test/rvcontroller.ld @@ -0,0 +1 @@ +../../rvcontroller.ld \ No newline at end of file diff --git a/rvcontroller.lua b/rvcontroller.lua index 8711a51..e6e86e5 100644 --- a/rvcontroller.lua +++ b/rvcontroller.lua @@ -207,6 +207,14 @@ Base + 10: mtimecmp (see RISC-V Machine Level ISA specification) Base + 18 - Base + 255: Reserved +Interrupts: + +Bits 7/16/17 of mie/mip are currently implemented. + +Bit 7: MTIP (machine timer interrupt), standard +Bit 16: MCIP (mesecons input interrupt), custom for RVController +Bit 17: DGIP (digilines receive interrupt), custom for RVController + ]] --Settings @@ -765,15 +773,17 @@ end local function checkinterrupts() local miebits = explodebits(mem.csr[0x304],32) - local mipbits = {} + local mipbits = explodebits(mem.csr[0x344],32) 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 + else + mipbits[7] = false end mem.csr[0x344] = implodebits(mipbits,32) if not mem.mie then return end - local order = {11,3,7,9,1,5,13} + local order = {11,3,7,9,1,5,13,16,17} for _,i in ipairs(order) do if miebits[i] and mipbits[i] then return trap(0x80000000+i) @@ -1211,30 +1221,39 @@ local operations = { return trap(3,0) end, csrrw = function(rd,rs1,imm) - setreg(rd,readcsr(imm)) + local out = readcsr(imm) writecsr(imm,getreg(rs1)) + setreg(rd,out) return checkinterrupts() end, csrrs = function(rd,rs1,imm) - setreg(rd,readcsr(imm)) - if rs1 == 0 then return end + local out = readcsr(imm) + if rs1 == 0 then + setreg(rd,out) + return + end local csrbits = explodebits(readcsr(imm),32) local rs1bits = explodebits(getreg(rs1),32) for i=0,31 do csrbits[i] = csrbits[i] or rs1bits[i] end writecsr(imm,implodebits(csrbits,32)) + setreg(rd,out) return checkinterrupts() end, csrrc = function(rd,rs1,imm) - setreg(rd,readcsr(imm)) - if rs1 == 0 then return end + local out = readcsr(imm) + if rs1 == 0 then + setreg(rd,out) + return + end local csrbits = explodebits(readcsr(imm),32) local rs1bits = explodebits(getreg(rs1),32) for i=0,31 do csrbits[i] = csrbits[i] and not rs1bits[i] end writecsr(imm,implodebits(csrbits,32)) + setreg(rd,out) return checkinterrupts() end, csrrwi = function(rd,rs1,imm) @@ -3041,6 +3060,16 @@ elseif event.type == "on" or event.type == "off" then --MMIO is enabled writeram(readcsr(0x801)+1,0,1,true) --Invalidate any existing reservation set end + local mipbits = explodebits(mem.csr[0x344],32) + mipbits[16] = true + mem.csr[0x344] = implodebits(mipbits,32) + local miebits = explodebits(mem.csr[0x304],32) + if miebits[16] and mem.interruptwaiting then + mem.interruptwaiting = false + mem.running = true + digiline_send("monitordisp","CPU started") + interrupt(1/CLOCK_SPEED,"tick") + end end elseif event.channel == "reset" then mem.running = false @@ -3345,6 +3374,16 @@ elseif event.type == "digiline" then if mem.csr[0x800]%2 == 1 then interrupt(0,"tick") end + local mipbits = explodebits(mem.csr[0x344],32) + mipbits[17] = true + mem.csr[0x344] = implodebits(mipbits,32) + local miebits = explodebits(mem.csr[0x304],32) + if miebits[17] and mem.interruptwaiting then + mem.interruptwaiting = false + mem.running = true + digiline_send("monitordisp","CPU started") + interrupt(1/CLOCK_SPEED,"tick") + end elseif event.iid == "tick" and mem.running then run(INSTRUCTIONS_PER_CLOCK) if mem.csr[0x800]%2 == 1 then -- cgit v1.2.3