Open David-Horner opened 4 years ago
This is the final version of preemptible interrupt handler of section 7.2.
It is possible by allowing mnxti to handle pending interrupts at the same level as current. This code assumes that functionality will be enabled by using "csrrsi a1,mnxti,1".
The greatest benefit from using this functionality is reduced time to handle very high interrupt loads. Specifically, 12 instructions with no stack r/w and no interrupt trapping.
I hope the comments are comprehensive and self explanatory.
.p2align 3
# Inline preemptible interrupt handler.
# Only safe for horizontal interrupts.
#
# In this version B 2.0:
# Selectively hardware vectored interrupts will potentially be substantially
# delayed as no interrupts are enable until epilogue.
# The worst case delay is from an intrpt2 section bnez to intrpt1
# through to interrupts enabled in the epilogue. 24 instructions.
#
# Note: This code uses an extension instruction:
# Branch Bit Set (bbs) proposed for the Fast Interrupt Extension
# Also, a variant of the mnxti csr that allows same level pil selection,
# also proposed for Fast Interrupt
#
# This version does not handle the type 1 interrupt processing through an
# interrupt, but through a mnxti branch sequence. As a result the INT1
# value is not enabled in the sp, but rather INC2 is always used to indicate
# second (and subsequent) time through the primary entry point.
#
#
# Non preempted code path is 28 instructions including:
# 5 stores, 4 loads, one amo, mret and 6 csr:
# 2 csr read, 2 csr write,
# 1 csr modify (MIE on) and 1 special (a0,mnxti,1).
#
# Minimum handle code is 12 instructions, with no stack lw/sw nor hardware trap.
# This is the code path from intrpt1 through pending check back to intrpt1.
# It is during highest interrupt frequency that this code is in play,
# which is a very favourable characteristic.
# It is also the worst latency case as 12 instructions execute between checks.
#
# The total count for one pass through each of the 3 paths is
# 28 + /*first entry to epilogue mret (worst case, best is 19)*/
# 3 + /*2nd type preemption (no pending) */
# 13 + /*1st type interrupt (pending int flag clear & accum)*/
# 19 /* continue to complete current int flag clear and accum and return*/
# Total 63 instructions, for an of average 21.
#
# Note: With prerequisites met, a single copy of the code handles the low
# 2K vectored interrupts; All 4K can be handled by replacing the
# andi with a slli;srli pair.(and increase the code path by one instruction)
# Lower values in the andi are possible to handle 1K, 512, etc. counters.
#
#
# Prerequisites
# 0. sp is used in non-standard ways in this code but no restrictions on where
# sp points in memory.
# Minimum alignment is 4 bytes but 8 or more optimizes compressed load/stores.
# 1. Normal sp management preserving alignment must be performed by other code.
# 2. It is assumed that routines that use this handler are at the highest
# interrupt priority. Thus this code will only be preempted by this code.
# If this code can be preempted by selectively hardware vectored interrupts,
# their handler must also accommodate the INT2 flag in the sp.
# (That is tolerate 1/2 the normal sp alignment).
# 3. Interrupt flag and counter offsets from their base
# are both scaled from exccode in mcause
# 4. The base for INTERRUPT_FLAGS and COUNTERS should be 4K aligned
# this save 2 instructions using lui to provide base and offsets.
# 5. This code assumes all 2K/4K interrupts are COUNTER accumulators.
# Any interrupts can be used for other purposes but there will be
# corresponding holes in the COUNTER array.
#
#
.equ STCK_ALIGN,16 # in bytes (minimum of 4 is required)
# with 8 byte aligned stack lw & sw are compressed
.equ INC2,STCK_ALIGN/2 #
.set COUNTERS,COUNTER_ARRAY #
.set INTRPT_FLAGS,INTERRUPT_FLAGS_ARRAY
#
foo:
#----- Interrupts disabled on entry ---#
bbs sp, INC2, intrpt2 # do subsequent time through processing
#----- First time in interrupt context ----#
addi sp, sp, -FRAMESIZE+INC2 # advance sp and flag for intrpt2
sw a0, OFFSETa0-INC2(sp) # Save working register.
sw a1, OFFSETa1-INC2(sp) # Save working register.
csrr a0, mepc # Read epc.
sw a0, OFFSETepc-INC2(sp) # save epc
csrr a0, mcause # Read cause.
sw a0, OFFSETcause-INC2(sp) # save mcause
# if interrupt pending: to intrpt1 and try to catch up
csrrsi a1,mnxti,1 # new variant to allow equal pil pending interrupt
bnez intrpt1 # process pending interrupt.
advance:
# a0 = current mcause
andi a0,a0,0x7FF # mask off low 2K entries of exccode
la a1, INTRPT_FLAGS
add a1, a0, a1
sw x0, (a1) # Clear current interrupt flag.
la a1, COUNTERS #
slli a0,2 # scale to words for counters
add a1, a1, a0
li a0,1
amoadd.w x0, (a1), a0 # increment current counter in memory.
# ---- we've caught up (or had no interrupts) #
csrrsi x0, mstatus, MIE # Enable interrupts.
#----- Interrupts enabled ---------#
#----- No other critical section until interrupt or mret ----#
lw a1, OFFSETa1-INC2(sp) # Restore a1.
lw a0, OFFSETepc-INC2(sp) # Restore original epc
csrw mepc, a0 # Put epc back.
lw a0, OFFSETcause-INC2(sp) # Restore original cause
csrw mcause, a0 # Put cause back.
lw a0, OFFSETa0-INC2(sp) # Restore a0.
addi sp, sp, FRAMESIZE-INC2 # Restore sp
mret # Return from handler
#
# following code executes as a result of preemption
#
intrpt1:
#----- Interrupts disabled ---------#
# a0 = prev mcause
andi a0,a0,0x7FF # mask off low 2K entries of exccode
# if all 4K entries are desired replace andi here and above with
#slli a0,a0,20 # mask off all 4K entries of exccode
#srli a0,a0,20
la a1, INTRPT_FLAGS
add a1, a0, a1
sw x0, (a1) # Clear previous interrupt flag.
la a1, COUNTERS #
slli a0,2 # scale to words for counters
add a1, a1, a0
li a0,1
amoadd.w x0, (a1), a0 # increment previous counter in memory.
intrpt2:
csrr a0, mcause # Read cause.
csrrsi a1,mnxti,1 # new variant to allow equal pil pending interrupt
bnez intrpt1 # process pending interrupt.
b advance
#------------------------------------#
test1