Skip to content

Instantly share code, notes, and snippets.

@Kenny2github
Last active December 18, 2023 01:41
Show Gist options
  • Save Kenny2github/da1f1f35f1e0606c11358df9cbbc2ed5 to your computer and use it in GitHub Desktop.
Save Kenny2github/da1f1f35f1e0606c11358df9cbbc2ed5 to your computer and use it in GitHub Desktop.
ECE253 Past Finals ARM to RISC-V translations

ECE253 Past Finals ARM to RISC-V translations

These are an attempt to translate ARM code appearing in ECE253 past finals and/or their solutions into RISC-V due to the use of the latter in newer iterations of the course. Each file is named as follows: YYYYM-Q##.s (or YYYYM-Q##X.s where X is a version letter) and may be updated as errors are discovered and corrected. If you discover an error or have feedback, leave a comment.

Translations

Legend: ☑️ Done 🟨 WIP 🟦 To-do

* Includes solutions.

Translation Methodology

For finals without my translations, you can apply the following principles to translate them yourself. This is a living document and may be updated as I go along.

Instruction Equivalents

In these, Rx, Ry, Rz are ARM registers and rX, rY, rZ are their calling-convention-approved translations. ARM arithmetic instructions have a two-argument form that simply duplicates the first argument. So ADD Rx, Ry is the same as ADD Rx, Rx, Ry; and same for SUB, etc. #x is an ARM immediate and x is its RISC-V equivalent. LABEL is a label in the program text/data.

Immediates, copies, and memory:

  • MOV Rx, Rymv rX, rY
  • MOV Rx, #x or LDR Rx, =xli rX, x
  • MOV Rx, #LABEL or LDR Rx, =LABELla rX, LABEL
  • LDR/STR/LDRH/STRH/LDRB/STRB Rx, [Ry]lw/sw/lh/sh/lb/sb rX, 0(rY) respectively
  • LDR/STR/LDRH/STRH/LDRB/STRB Rx, [Ry, #x]lw/sw/lh/sh/lb/sb rX, x(rY) respectively

Arithmetic instructions:

  • ADD/SUB/AND/ORR/EOR/LSL/LSR/ASR Rx, Ry, Rzadd/sub/and/or/xor/sll/srl/sla rX, rY, rZ respectively
  • ADD/AND/ORR/EOR/LSL/LSR/ASR Rx, Ry, #xaddi/andi/ori/xori/slli/srli/slai rX, rY, x respectively
  • SUB Rx, Ry, #xaddi rX, rY, -x

Calling Conventions

The ARM calling convention uses R0-R3 as registers for arguments, scratch, and return values simultaneously. The rest (except R12) are saved or special registers. Therefore I translate registers as follows:

  • For programs using subroutines, R4-R11s0-s7. For programs not using subroutines, R4-R6t4-t6 and R7-R11s0-s4.
  • For subroutine calls, R0-R3a0-a3. For all other cases, R0-R3t0-t3.
  • Directly equivalent special registers: SP (R13) → sp, LR (R14) → ra
  • For completion, PCpc, though pc cannot be directly accessed in RISC-V.

Also, ARM assembly includes the pseudo-instructions PUSH and POP which push and pop from the stack. These have no equivalent in RISC-V and must be implemented manually.

PUSH {R4, R6-R8, LR}
# subroutine body...
# This shorthands MOV PC, LR by popping LR into PC directly.
# That's not legal in RISC-V, unfortunately.
POP {R4, R6-R8, PC}

becomes

addi sp, sp, -20
sw s0, 0(sp)
sw s2, 4(sp)
sw s3, 8(sp)
sw s4, 12(sp)
sw ra, 16(sp)
# subroutine body...
# The order of the lw's here doesn't really matter.
# I put them in LIFO order here to better emulate what ARM does,
# but in the actual translations I tend to use FIFO order instead.
lw ra, 16(sp)
lw s4, 12(sp)
lw s3, 8(sp)
lw s2, 4(sp)
lw s0, 0(sp)
addi sp, sp, 20
jr ra

Branching/Jumping

Easy ones out of the way first:

  • B LABELj LABEL
  • BL LABELjal LABEL
  • BX LR or MOV PC, LRjr ra

Additionally, END/ERROR: B END/ERROR is translated as END/ERROR: ebreak because that is more typical of modern ECE253 code.

Due to the way ARM conditional branching works, the translation of such to RISC-V is not so simple. For example, the ARM instruction BEQ is merely a B instruction with the condition flag EQ, which means the B only happens if the Z ALU flag ("equal to zero") is set, or else it is skipped. ALU flags can be set in various ways, but the most common is the CMP instruction. Hence I translate conditional branching as follows:

  • CMP Rx, Ry followed by n BEQ/BNE/BLT/BGE/BGT/BLE LABEL instructions → n beq/bne/blt/bge/bgt/ble rX, rY, LABEL instructions, respectively
  • CMP Rx, #0 followed by n BEQ/BNE/BLT/BGE/BGT/BLE LABEL instructions → n beqz/bnez/bltz/bgez/bgtz/blez rX, LABEL instructions, respectively
  • CMP Rx, #x (where x != 0) followed by n BEQ/BNE/BLT/BGE/BGT/BLE LABEL instructions:
    • If we need to keep the value of Rx, → li rW, x followed by n beq/bne/blt/bge/bgt/ble rX, rW, LABEL instructions, respectively. rW is an otherwise unused register, or in general one where setting it in this situation won't clobber it for other purposes. Remember to add the register to the prologue/epilogue if necessary.
    • If we can destroy the value of Rx, → addi rX, rX, -x followed by n beqz/bnez/bltz/bgez/bgtz/blez rX, LABEL instructions, respectively. This avoids having to use an extra register.
.text
.global _start
_start:
la t1, LIST
li t2, 0x0
mv t4, t2
LOOP:
add t5, t1, t2
lw t3, 0(t5)
add t4, t4, t3
beqz t3, END
bltz t3, END
addi t2, t2, 0x4
j LOOP
END:
ebreak
.data
LIST:
.word 5, 8, 9, -2, 6, -1, 0
#######################################################################
# WARNING: UNTESTED #
# This question is specific to the DE1-SoC, which uses memory-mapped #
# I/O that rars does not simulate. Hence I have not been able to test #
# whether this translation is valid and am relying on pure theory. #
# Use this for reference only and not as ground truth. #
#######################################################################
#### part (a)
.text
.global _start
_start:
# KEYs enable
li t1, 0xFF200058 # interrupt mask for KEYs
li t2, 0x9 # enable for KEY3 and KEY0
sw t2, 0(t1) # store to enable interrupts
#### NOTE: RISC-V does not require setting up SP, nor does it have
#### multiple of them. So that ARM-specific section is omitted.
# enable user-level interrupts (UIE)
csrrsi zero, ustatus, 0x1
# enable user-level external interrupts (UEIE)
csrrsi zero, uie, 0x100
#### NOTE: The question does not ask you to configure the ARM GIC.
#### So these instructions are not a necessary part of the answer
#### and are included here only to explain how the processor knows
#### to jump to SERVICE_IRQ when an interrupt happens.
#la t0, SERVICE_IRQ
#csrrw zero, utvec, t0
# continuous loop
li t2, 0xFF200000 # addr of LEDR
la t3, CURR_VALUE # addr of curr_value
LOOP:
lw t1, 0(t3) # load curr_value
sw t1, 0(t2) # store to LEDR
j LOOP
.data
CURR_VALUE:
.word 512 # initial value
#### part (b)
.text
.global SERVICE_IRQ
SERVICE_IRQ:
addi sp, sp, -28
sw t0, 0(sp)
sw t1, 4(sp)
sw t2, 8(sp)
sw t3, 12(sp)
sw s0, 16(sp)
sw s1, 20(sp)
sw ra, 24(sp)
# end prologue
# clear user-level external-interrupt-pending (UEIP)
# flag, to avoid retriggering the interrupt upon uret
# ASSUMPTION: that we even need to do this. Do we?
# Timer interrupts don't seem to require this.
csrrci zero, uip, 0x100
#############################################################################
# WARNING: ASSUMPTIONS #
# I am assuming that RISC-V [mhsu]cause values are analogous to ARM IRQ IDs #
# and that checking them against immediates is a sane thing to do. If my #
# assumptions are wrong, you know better than I do, and should be able to #
# insert the correct method of checking whether this is the interrupt ID #
# we're looking for - or to remove this code entirely. #
#############################################################################
# get cause of exception
csrr s1, ucause
slli s1, s1, 1 # clear the
srli s1, s1, 1 # Interrupt bit
# WRITE YOUR CODE HERE
# check if interrupt ID matches
addi s1, s1, -73 # avoid using another register
bnez s1, ERROR # check if Key IRQ
jal KEY_ISR # must be a Key IRQ, jal to subroutine
j EXIT_IRQ # exit after handling IRQ
ERROR:
ebreak # unknown IRQ!
EXIT_IRQ:
# begin epilogue
lw t0, 0(sp)
lw t1, 4(sp)
lw t2, 8(sp)
lw t3, 12(sp)
lw s0, 16(sp)
lw s1, 20(sp)
lw ra, 24(sp)
addi sp, sp, 28
uret # return from user-level interrupt handler
#### part (c)
KEY_ISR:
addi sp, sp, -20
sw t1, 0(sp)
sw t2, 4(sp)
sw t3, 8(sp)
sw s0, 12(sp)
sw s1, 16(sp)
# end prologue
la s1, CURR_VALUE # address of curr_value
lw s0, 0(s1) # current value to s0
li t2, 0xFC20005C # edge capture addr
lw t3, 0(t2) # edge capture value
addi t3, -0x8 # check KEY3
bnez t3, KEY0 # if !KEY3, must be KEY0
beqz s0, ENDISR # check if curr_value 0, branch if 0
addi s0, s0, -1 # decrease value
sw s0, 0(s1) # store
j ENDISR
KEY0:
la t1, 0x3FF # check if curr_value is 0b11_1111_1111
beq s0, t1, ENDISR # clamping check
addi s0, s0, 1 # increase value
sw s0, 0(s1) # store
ENDISR:
# begin epilogue
lw t1, 0(sp)
lw t2, 4(sp)
lw t3, 8(sp)
lw s0, 12(sp)
lw s1, 16(sp)
addi sp, sp, 20
jr ra
#### NOTE: I don't think the original ARM solution got it right.
#### Somehow what they have implemented is not FIBONACCI(N) but instead
#### FIBONACCI(N + 1) - 1. I have opted to faithfully translate their
#### wrong solution, but I encourage you to create a solution of your
#### own and actually test it, which evidently they did not.
.data
N: .word 10
.text
.global _start
_start:
#li sp, 0x20000 # not needed in RISC-V
la s0, N
lw a0, 0(s0) # load N from mem
li a1, 0
jal FIBONACCI # use jal
END:
ebreak
FIBONACCI: # return in a1
addi sp, sp, -8
sw a0, 0(sp)
sw ra, 4(sp)
# end prologue
mv a2, a0
li t0, 2
blt a2, t0, return
addi a0, a0, -1
jal FIBONACCI
add a1, a1, a2
addi a0, a0, -1
jal FIBONACCI
add a1, a1, a2
return:
# begin epilogue
lw a0, 0(sp)
lw ra, 4(sp)
addi sp, sp, 8
jr ra
.text
.global _start
_start:
li a0, 0xFF
li a1, -1
li s2, 0x8F000000
la s3, LIST
lw s4, 8(s3)
addi s3, s3, 4
la s5, RESULT
#### NOTE: This is not needed in RISC-V (and in fact invalid in RARS)
#### but _is_ needed to match the memory addrs shown in the solution.
#### It is commented out for the sake of successful simulation, but
#### you should assume it is here when doing the question.
#li sp, 0x20000 # not needed in RISC-V
jal FUNC1
sw s2, 0(s3)
END:
ebreak
FUNC1:
addi sp, sp, -16
sw s2, 0(sp)
sw s3, 4(sp)
sw s4, 8(sp)
sw ra, 12(sp)
# end prologue
slli a2, a0, 4
xor a3, a2, a1
srai a1, s2, 3
jal FUNC2
# begin epilogue
lw s2, 0(sp)
lw s3, 4(sp)
lw s4, 8(sp)
lw ra, 12(sp)
addi sp, sp, 16
jr ra
FUNC2:
addi sp, sp, -16
sw a2, 0(sp)
sw a3, 4(sp)
sw s5, 8(sp)
sw ra, 12(sp)
# end prologue
THIS: add a2, a2, a3
# begin epilogue
lw a2, 0(sp)
lw a3, 4(sp)
lw s5, 8(sp)
lw ra, 12(sp)
jr ra
.data
LIST: .word -2, -3, -4, -5, 3, 4
RESULT: .word 0
#### NOTE: This question doesn't make as much sense in RISC-V because
#### the .text and .data segments are actually separate in memory.
#### Here are the answers when run in RARS:
#### +------------+------------+-----+
#### | Mem Addr | Data (hex) | Reg |
#### +------------+------------+-----+
#### | 0x7fffefdc | 0x00000ff0 | a2 | # actual data value
#### | 0x7fffefe0 | 0xfffff00f | a3 | # actual data value
#### | 0x7fffefe4 | 0x10010018 | s5 | # .data segment addr
#### | 0x7fffefe8 | 0x00400058 | ra | # .text segment addr
#### | 0x7fffefec | 0x8f000000 | s2 | # actual data value
#### | 0x7fffeff0 | 0x10010004 | s3 | # .data segment addr
#### | 0x7fffeff4 | 0xfffffffc | s4 | # actual data value
#### | 0x7fffeff8 | 0x0040002c | ra | # .text segment addr
#### +------------+------------+-----+

Translator's note: I have decided to translate the entire question as a whole. These may not be representative of actual questions you could get on a modern ECE253 final exam.

(a) Why must we save all registers, even registers saved by the caller in the RISC-V calling convention?

(b) (no equivalent in RISC-V)

(c) Why is jal used to jump to a subroutine instead of j?

(d) Consider the following short program (the first instruction is at memory address 0x0):

_start:
    li t1, 0
    li t2, 8
    addi t1, t1, 1
    jr t2

What does this program do?

(e) Imagine a simple processor with 6-bit instructions and 4 registers. Give one reason this processor has fewer registers than the RISC-V processor.

Click for Solutions

(a) An interrupt handler can be executed anywhere in a program. If we don't save all registers we use, we might clobber registers used at the point the program was interrupted.

(b) (no equivalent in RISC-V)

(c) We need to save the instruction to return to in the return address register ra.

(d) The program loops infinitely between the addi and jr instructions.

(e) The instructions are narrower and thus don't have enough bits for more registers.

#### code given:
.data
LIST: .word 1, NEXT1
NEXT1: .word 2, NEXT2
NEXT2: .word 3, NEXT3
NEXT3: .word 6, -1
.text
.global _start
_start:
#li sp, 0x20000 # not needed in RISC-V
la a0, LIST
li a1, 6 # value searching for
jal SEARCH_LIST
END: ebreak
#### solution:
SEARCH_LIST:
addi sp, sp, -8
sw s0, 0(sp)
sw s1, 4(sp)
# end SEARCH_LIST prologue
li s1, -1
CHECK_END:
beq a0, s1, END_LIST
lw s0, 0(a0)
beq s0, a1, RETURN # item found, return addr in a0
lw a0, 4(a0)
j CHECK_END
END_LIST:
li a0, -1
j RETURN
RETURN:
# begin SEARCH_LIST epilogue
lw s0, 0(sp)
lw s1, 4(sp)
addi sp, sp, 8
jr ra
.text
.global _start
_start:
#### NOTE: This is not needed in RISC-V (and in fact invalid in RARS)
#### but _is_ needed to match the SP value shown in the solution.
#### It is commented out for the sake of successful simulation, but
#### you should assume it is here when doing the question.
#li sp, 0x20000
la a0, ARR
li a1, 0 # Left
la s0, SIZE
lw s0, 0(s0)
addi a2, s0, -1 # Right
li a3, 2 # value searching for
#### NOTE: These values differed based on the version of the Quercus
#### exam used. Version A is used but you can replace it with the
#### Version B values given below.
li s1, 0xAA # or 0xDD
li s2, 0xBB # or 0xEE
li s3, 0xCC # or 0xFF
CALL1: jal BinarySearch
END: ebreak
BinarySearch:
addi sp, sp, -16
sw s1, 0(sp)
sw s2, 4(sp)
sw s3, 8(sp)
sw ra, 12(sp)
# end BinarySearch prologue
bge a2, a1, FIND_MID
li a0, -1
j RETURN
FIND_MID:
sub s1, a2, a1
srli s1, s1, 1
add s1, s1, a1
slli s2, s1, 2
add s2, s2, a0
lw s3, 0(s2)
bgt s3, a3, SEARCH_LEFT
blt s3, a3, SEARCH_RIGHT
HERE: mv a0, s1
j RETURN
SEARCH_LEFT:
addi a2, s1, -1
CALL2: jal BinarySearch
j RETURN
SEARCH_RIGHT:
addi a1, s1, 1
CALL3: jal BinarySearch
j RETURN
RETURN:
# begin BinarySearch epilogue
lw s1, 0(sp)
lw s2, 4(sp)
lw s3, 8(sp)
lw ra, 12(sp)
addi sp, sp, 16
jr ra
.data
ARR: .word 1, 2, 3, 4, 7, 9, 10, 12, 14
SIZE: .word 9
#######################################################################
# WARNING: UNTESTED #
# This question uses fictional hardware with a made up MMI/O address. #
# By nature, nothing can simulate this, in ARM or in RISC-V. Hence I #
# have not been able to test whether this translation is valid and am #
# relying on pure theory. Use this for reference only and not as #
# ground truth. #
#######################################################################
# NOTE: MULTIPLE VERSIONS #
# This question has two different versions, A and B, with different #
# MMI/O layouts and addresses for the device. This file contains both.#
#######################################################################
############################## VERSION A ###############################
.data
CONVERTED_DATA: .half 0, 0, 0, 0, 0, 0, 0, 0
.text
.global _start
_start:
li t0, 0xEE201000
la t1, CONVERTED_DATA
li t2, 0 # channel number
LOOP:
mv t3, t2
ori t3, t3, 8 # to set Go bit
sw t3, 0(t0)
POLL:
lw t4, 0(t0)
andi t4, t4, 16
beqz t4, POLL # check done bit
sw t4, 0(t0) # clear done bit
lw t5, 4(t0) #Load converted data
srli t5, t5, 4
slli t6, t2, 1 # offset to store halfword
add t6, t6, t1 # add offset to base addr
sh t5, 0(t6) # store data
addi t2, t2, 1 # increment channel
addi t2, t2, -8 # translator's note: this avoids the use of another register
bltz t2, LOOP
li t2, 0
j LOOP
############################## VERSION B ###############################
.data
CONVERTED_DATA: .half 0, 0, 0, 0, 0, 0, 0, 0
.text
.global _start
_start:
li t0, 0xCE201000
la t1, CONVERTED_DATA
li t2, 0 # channel number
LOOP:
mv t3, t2
slli t3, t3, 3
ori t3, t3, 1 # to set Go bit
sw t3, 0(t0)
POLL:
lw t4, 0(t0)
andi t4, t4, 2
beqz t4, POLL # check done bit
sw t4, 0(t0) # clear done bit
lh t5, 6(t0) # Load converted data
srli t5, t5, 4
slli t6, t2, 1 # offset to store halfword
add t6, t6, t1 # add offset to base addr
sh t5, 0(t6) # store data
addi t2, t2, 1 # increment channel
addi t2, t2, -8 # translator's note: this avoids the use of another register
bltz t2, LOOP
li t2, 0
j LOOP
.data
LIST: .word 2, 1, 4, 6, 8, 7
N: .word 6
.text
.global _start
_start:
li t0, 0 # longest initially 0
la t1, LIST
li t2, 0 # current count of even sequence
la t3, N
lw t3, 0(t3) # t3 <= N
LOOP:
beqz t3, END # check if the for loop is done
lw t4, 0(t1) # load a value from the LIST
andi t4, t4, 1 # AND the LSB of the value with 1
li t5, 1 # check if ODD
beq t4, t5, ODD # branch if ODD
addi t2, t2, 1 # bump up the current count
ble t2, t0, NEXT # see if current count is > MAX count
mv t0, t2 # replace MAX count (we have a new MAX)
j NEXT
ODD:
li t2, 0 # reset current count
NEXT:
addi t1, t1, 4 # increment array pointer
addi t3, t3, -1 # decrement N
j LOOP
END:
ebreak
.data
LIST: .word 2, 1, 4, 6, 8, 7
N: .word 6
.text
.global _start
_start:
li t0, 0 # longest initially 0
la t1, LIST
li t2, 0 # current count of ascending sequence length
li t5, -1 # set t5 to -1 initially... t5 will hold the previous list value
la t3, N
lw t3, 0(t3) # t3 <= N
LOOP:
beqz t3, END # check if the for loop is done
lw t4, 0(t1) # load a value from the LIST
ble t4, t5, NONASCENDING # compare it with t5 (the previous list value)
addi t2, t2, 1 # bump up the current count
ble t2, t0, NEXT # see if current count is > MAX count
mv t0, t2 # replace MAX count (we have a new MAX)
j NEXT
NONASCENDING:
li t2, 1 # reset current count to 1 (as this is a new sequence)
NEXT:
mv t5, t4
addi t1, t1, 4 # increment array pointer
addi t3, t3, -1 # decrement N
j LOOP
END:
ebreak
#######################################################################
# WARNING: UNTESTED #
# This question uses fictional hardware with a made up IRQ ID. By #
# nature, nothing can simulate this, in ARM or in RISC-V. Hence I #
# have not been able to test whether this translation is valid and am #
# relying on pure theory. Use this for reference only and not as #
# ground truth. #
#######################################################################
#### part (a)
.global _start
_start:
# write your here code to setup the SPs
#### NOTE: RISC-V does not require setting up SP, nor does it have
#### multiple of them. So this ARM-specific section is omitted.
#jal CONFIG_GIC # configure the ARM generic interrupt controller
#### NOTE: there is no "generic interrupt controller" in RISC-V like
#### there is in ARM. So instead of the call to a magical CONFIG_GIC
#### subroutine, here is the actual code for configuring RISC-V
#### external hardware interrupts.
# set interrupt handler vector
la t0, IRQ_HANDLER
csrrw zero, utvec, t0
# enable user-level external interrupts (UEIE)
csrrsi zero, uie, 0x100
# write your code here to enable interrupts for the airbag deployment device and for the ARM
li t0, 0xFFFEC800 # base address for airbag control
li t1, 0x180 # set I = 1, set bit 7 to 1 so the threshold is 128.
sw t1, 0(t0)
# enable user-level interrupts (UIE)
csrrsi zero, ustatus, 0x1
#### part (b)
.global IRQ_HANDLER
IRQ_HANDLER:
# prologue: save ALL registers used because we are interrupting other code
addi sp, sp, -24
sw s0, 0(sp)
sw t0, 4(sp) # translator's note: ARM only has four scratch registers
sw t1, 8(sp) # (R0-R3). In RISC-V properly we should be saving ALL t*
sw t2, 12(sp) # AND a* registers, but since ARM only has four we only
sw t3, 16(sp) # bother with these four.
sw ra, 20(sp) # due to subroutine call
# handler:
# clear user-level external-interrupt-pending (UEIP)
# flag, to avoid retriggering the interrupt upon uret
# ASSUMPTION: that we even need to do this. Do we?
# Timer interrupts don't seem to require this.
csrrci zero, uip, 0x100
#############################################################################
# WARNING: ASSUMPTIONS #
# I am assuming that RISC-V [mhsu]cause values are analogous to ARM IRQ IDs #
# and that checking them against immediates is a sane thing to do. If my #
# assumptions are wrong, you know better than I do, and should be able to #
# insert the correct method of checking whether this is the interrupt ID #
# we're looking for - or to remove this code entirely. #
#############################################################################
# get cause of exception
csrr s0, ucause
slli s0, s0, 1 # clear the
srli s0, s0, 1 # Interrupt bit
# check if interrupt ID matches
addi s0, s0, -101 # avoid using another register
bnez s0, EXIT_IRQ # if ucause == 101,
jal AIRBAG_ISR # call AIRBAG_ISR
EXIT_IRQ: # epilogue: restore registers and return
lw s0, 0(sp)
lw t0, 4(sp)
lw t1, 8(sp)
lw t2, 12(sp)
lw t3, 16(sp)
lw ra, 20(sp)
addi sp, sp, 24
uret # return from user-level interrupt handler
#### part (c)
AIRBAG_ISR:
# no saving needed, only t* registers used
li t0, 0xFFFEC900
li t1, 0xFFFEC800
lw t2, 4(t1) # load the intensity field
sw t2, 4(t1) # clear the interrupt
li t3, 0x80 # compare intensity with 128
blt t2, t3, EXIT_ISR # should never happen
li t3, 1
sw t3, 0(t0) # deploy the airbag
EXIT_ISR:
jr ra
.data
XYZ: .word 0, 0xFFABCDFF, 1, PTR, 2
PTR: .word -2, -4
.text
.global _start
_start:
la a0, XYZ
lw a1, 4(a0)
lw a2, 12(a0)
lw a2, 0(a2)
andi a2, a2, 0xFF
bgez a2, ABC # if a2 >= 0 then ABC
li a3, 0x008945FF # a3 = 0x008945FF
xor a4, a1, a3 # a4 = a1 ^ a3
lw a5, 8(a0)
slli a5, a5, 5
j END
ABC:
li a3, 0xF # a3 = 0xF
sw a3, 0(a0)
slli a3, a3, 16
and a4, a1, a3 # a4 = a1 & a3
srai a5, a1, 4
END:
ebreak
DIVIDE:
li a2, 0 # remainder
li a4, 0 # quotient
li a5, 0
li a6, 32
LOOP: beq a5, a6, RETURN # All bits of dividend consumed?
addi a5, a5, 1
slli a2, a2, 1
srli a3, a1, 31 # MSb of dividend
or a2, a2, a3
slli a1, a1, 1
sub a2, a2, a0
slli a4, a4, 1 # shift quotient by 1
bgez a2, ONE
add a2, a2, a0 # if negative, restore remainder
j LOOP
ONE: ori a4, a4, 1 # if remainder positive, put 1 in LSb
j LOOP
RETURN: mv a1, a2 # move remainder into a1
mv a0, a4 # move quotient into a0
jr ra
#######################################################################
# WARNING: UNTESTED #
# This question is specific to the DE1-SoC, which uses memory-mapped #
# I/O that rars does not simulate. Hence I have not been able to test #
# whether this translation is valid and am relying on pure theory. #
# Use this for reference only and not as ground truth. #
#######################################################################
.data
LEDR: .word 0xFF200000
SW: .word 0xFF200040
.text
.global _start
_start:
lw a0, LEDR
lw a1, SW
OUTERLOOP:
lw a2, 0(a1) # load from SW
andi a2, a2, 7 # mask SW[2:0]
li a3, 0 # holds result
INNERLOOP:
beqz a2, BREAKOUT # if (a2) SW is zero, breakout
slli a3, a3, 1 # shift a3 left
ori a3, a3, 1 # insert a 1 to a3 LSb
addi a2, a2, -1 # subtract 1 from a2
j INNERLOOP
BREAKOUT:
sw a3, (a0)
j OUTERLOOP
#######################################################################
# WARNING: UNTESTED #
# This question uses fictional hardware with a made up IRQ ID. By #
# nature, nothing can simulate this, in ARM or in RISC-V. Hence I #
# have not been able to test whether this translation is valid and am #
# relying on pure theory. Use this for reference only and not as #
# ground truth. #
#######################################################################
#### part (a)
_start:
# set interrupt handler vector
la t0, SERVICE_IRQ
csrrw zero, utvec, t0
# enable user-level interrupts (UIE)
csrrsi zero, ustatus, 0x1
# enable user-level external interrupts (UEIE)
csrrsi zero, uie, 0x100
#### part (b)
SERVICE_IRQ:
# prologue: save registers
addi sp, sp, -8
sw s0, 0(sp)
sw ra, 4(sp) # due to subroutine call
# handler:
# clear user-level external-interrupt-pending (UEIP)
# flag, to avoid retriggering the interrupt upon uret
# ASSUMPTION: that we even need to do this. Do we?
# Timer interrupts don't seem to require this.
csrrci zero, uip, 0x100
#############################################################################
# WARNING: ASSUMPTIONS #
# I am assuming that RISC-V [mhsu]cause values are analogous to ARM IRQ IDs #
# and that checking them against immediates is a sane thing to do. If my #
# assumptions are wrong, you know better than I do, and should be able to #
# insert the correct method of checking whether this is the interrupt ID #
# we're looking for - or to remove this code entirely. #
#############################################################################
# get cause of exception
csrr s0, ucause
slli s0, s0, 1 # clear the
srli s0, s0, 1 # Interrupt bit
# check if interrupt ID matches
addi s0, s0, -88 # avoid using another register
bnez s0, EPILOGUE # if ucause == 88,
jal WIPER_ISR # call WIPER_ISR
EPILOGUE: # restore registers and return
lw s0, 0(sp)
lw ra, 4(sp)
addi sp, sp, 8
uret # return from user-level interrupt handler
### part (c)
WIPER_ISR:
addi sp, sp, -8
sw s0, 0(sp)
sw s1, 4(sp)
li s0, 0xFFFEC700 # base address of wiper
lw s1, 4(s0) # load rain bits
sw s1, 4(s0) # clear interrupt on sensor's end
slli s1, s1, 2 # s1 *= 4
sw s1, 8(s0) # store to speed bits
lw s0, 0(sp)
lw s1, 4(sp)
addi sp, sp, 8
jr ra
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment