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.
Legend: ☑️ Done 🟨 WIP 🟦 To-do
* Includes solutions.
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.
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, Ry
→mv rX, rY
MOV Rx, #x
orLDR Rx, =x
→li rX, x
MOV Rx, #LABEL
orLDR Rx, =LABEL
→la rX, LABEL
LDR/STR/LDRH/STRH/LDRB/STRB Rx, [Ry]
→lw/sw/lh/sh/lb/sb rX, 0(rY)
respectivelyLDR/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, Rz
→add/sub/and/or/xor/sll/srl/sla rX, rY, rZ
respectivelyADD/AND/ORR/EOR/LSL/LSR/ASR Rx, Ry, #x
→addi/andi/ori/xori/slli/srli/slai rX, rY, x
respectivelySUB Rx, Ry, #x
→addi rX, rY, -x
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-R11
→s0-s7
. For programs not using subroutines,R4-R6
→t4-t6
andR7-R11
→s0-s4
. - For subroutine calls,
R0-R3
→a0-a3
. For all other cases,R0-R3
→t0-t3
. - Directly equivalent special registers:
SP
(R13
) →sp
,LR
(R14
) →ra
- For completion,
PC
→pc
, thoughpc
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
Easy ones out of the way first:
B LABEL
→j LABEL
BL LABEL
→jal LABEL
BX LR
orMOV PC, LR
→jr 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 nBEQ/BNE/BLT/BGE/BGT/BLE LABEL
instructions → nbeq/bne/blt/bge/bgt/ble rX, rY, LABEL
instructions, respectivelyCMP Rx, #0
followed by nBEQ/BNE/BLT/BGE/BGT/BLE LABEL
instructions → nbeqz/bnez/bltz/bgez/bgtz/blez rX, LABEL
instructions, respectivelyCMP Rx, #x
(wherex != 0
) followed by nBEQ/BNE/BLT/BGE/BGT/BLE LABEL
instructions:- If we need to keep the value of
Rx
, →li rW, x
followed by nbeq/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 nbeqz/bnez/bltz/bgez/bgtz/blez rX, LABEL
instructions, respectively. This avoids having to use an extra register.
- If we need to keep the value of