Exercise solutions

This commit is contained in:
David Harris 2025-05-27 03:14:16 -07:00
parent 0fabccf384
commit b01fe7a38b
16 changed files with 686 additions and 0 deletions

View file

@ -0,0 +1,77 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
# set up PMP so all of memory is accessible and we don't trap when entering supervisor mode
# Define region 0 to cover all addresses as RWX
nop
csrw pmpcfg0, 0xF # configure PMP0 to TOR RWX
li t0, 0xFFFFFFFF
csrw pmpaddr0, t0 # configure PMP0 top of range to 0xFFFFFFFF to allow all 32-bit addresses
# switch to supervisor mode
# Set mstatus.MPP to 01, set MPEC to a trampoline address where supervisor should begin, do mret
la t0, supervisorstart
csrw mepc, t0 # set address for supervisor code to starting
li t0, 1
slli t1, t0, 11 # 1 in bit 11
csrs mstatus, t1
slli t1, t0, 12 # 1 in bit 12
csrc mstatus, t1 # change mstatus.MPP to 01 (for supervisor mode)
mret # enter supervisor mode at supervisorstart
nop
supervisorstart:
la t0, pagetable # get address of root page table
srli t0, t0, 12 # extract PPN of root page table
li t1, 1
slli t1, t1, 31 # 1 in bit 31
or t0, t0, t1 # satp value to enable SV32 with root page table
csrw satp, t0 # enable virtual memory
# now we are execuiting on virtual page 0x80000, which is also physical page 0x80000
li t0, 0x90000300
li t1, 42
sw t1, 0(t0)
la t0, testcode # address of a routine to run
lui t1, 0x10000
add t0, t0, t1 # address of testcode on virtual page 0x90000
jr t0 # jump to the testcode on Virtual page 0x90000,
# which still points to same code on physical page 0x80000
nop # shouldn't be executed
testcode:
li t0, 42 # do something
write_tohost:
la s1, tohost # terminate with write tohost
li t0, 1 # 1 for success, 3 for failure
sw t0, 0(s1) # send success code
sw zero, 4(s1) # not obvious why Sail isn't terminating unless this write is done
self_loop:
j self_loop
tohost:
.word 0
.data
.align 16
# root (L1) Page table situated at 0x80010000
pagetable:
.space 2048 # skip over addresses below 0x80000000
.4byte 0x20004401 # VPN1 = 512 (VA = 0x80000000) points to L0 page table at 80011000
.space 252 # skip over addresses below 0x90000000
.4byte 0x20004401 # VPN 576 (VA = 0x90000000) points to L0 page table at 0x80011000
.align 12
# L0 page table situated at 0x80011000
.4byte 0x200000CF # VPN 0 points to physical kilopage at 0x80000000 with Dirty, Access, XWR, Valid

View file

@ -0,0 +1,17 @@
TARGET = 11p6
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32i_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
wsim rv32gc $(TARGET).elf --lockstepverbose
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,9 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
li t0, 32 # 1 in bit 5
csrs mstatush, t0 # set bit 5 (mstatush.MBE)
self_loop:
j self_loop

View file

@ -0,0 +1,17 @@
TARGET = 8p3
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32i_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
wsim rv32gc $(TARGET).elf --lockstepverbose
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,20 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
csrr a0, cycle # read cycle register before computation
# add up numbers from 1 to 10. Place result in s0
li s0, 0 # initialize sum to 0 in s0
li s1, 1 # initialize loop variable i to 1 in s1
li t0, 10 # temporary for maximum number
loop:
add s0, s0, s1 # sum = sum + i
addi s1, s1, 1 # i = i + 1
ble s1, t0, loop # repeat while i <= 10
csrr a1, cycle # read cycle register after computation
sub a0, a1, a0 # compute difference in a0
self_loop:
j self_loop

View file

@ -0,0 +1,17 @@
TARGET = 8p4
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32i_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
wsim rv32gc $(TARGET).elf --lockstepverbose
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,29 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
# set up trap trap_handler
la t0, trap_handler # address of trap trap_handler
csrw mtvec, t0 # mtvec = pointer to trap handler
la t0, trapstack # address of trap stack
csrw mscratch, t0 # mscratch = pointer to trap stack
lw t0, 1(zero) # cause access or misaligned load fault to invoke trap handler
self_loop:
j self_loop
trap_handler:
csrrw tp, mscratch, tp # swap tp and mscratch to put a trap stack pointer in tp
sw t0, 0(tp) # save t0 on trap stack
csrr t0, mepc # read mepc
addi t0, t0, 4 # mepc + 4
csrw mepc, t0 # mepc = mpec + 4 (return to next instruction)
lw t0, 0(tp) # restore t0 from trap stack
csrrw tp, mscratch, tp # restore tp and trap stack pointer
mret
trapstack:
.word 0 # room to save a register

View file

@ -0,0 +1,17 @@
TARGET = 8p5
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32i_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
wsim rv32gc $(TARGET).elf --lockstepverbose
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,164 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
# set up trap trap_handler
la t0, trap_handler # address of trap trap_handler
csrw mtvec, t0 # mtvec = pointer to trap handler
la t0, trapstack # address of trap stack
csrw mscratch, t0 # mscratch = pointer to trap stack
li t0, 7
li t1, 9
mul t2, t0, t1 # try 7 * 9. It will trap and invoke trap handler
self_loop:
j self_loop
trap_handler:
csrrw tp, mscratch, tp # swap tp and mscratch to put a trap stack pointer in tp
# save all registers on trap stack. We will need to index into them to find the arguments to emulate multiply
sw x0, 0(tp) # x0 is 0, but we might want to use it
sw x1, 4(tp)
sw x2, 8(tp)
sw x3, 12(tp)
sw x4, 16(tp)
sw x5, 20(tp)
sw x6, 24(tp)
sw x7, 28(tp)
sw x8, 32(tp)
sw x9, 36(tp)
sw x10, 40(tp)
sw x11, 44(tp)
sw x12, 48(tp)
sw x13, 52(tp)
sw x14, 56(tp)
sw x15, 60(tp)
sw x16, 64(tp)
sw x17, 68(tp)
sw x18, 72(tp)
sw x19, 76(tp)
sw x20, 80(tp)
sw x21, 84(tp)
sw x22, 88(tp)
sw x23, 92(tp)
sw x24, 96(tp)
sw x25, 100(tp)
sw x26, 104(tp)
sw x27, 108(tp)
sw x28, 112(tp)
sw x29, 116(tp)
sw x30, 120(tp)
sw x31, 124(tp)
csrr t0, mcause # check cause of trap
li t1, 2 # cause 2 is illegal instruction
bne t0, t1, exit # exit for any other trap than illegal instruction
# check if instruction is mul (op = 0110011, funct3 = 000, funct7 = 0000001)
csrr t0, mtval # fetch instruction that caused trap
andi t1, t0, 127 # get op field (instr[7:0])
xori t1, t1, 0b0110011 # set to 0 if op is 0110011
srli t2, t0, 12 # get funct3 field (instr[14:12])
andi t2, t2, 7 # mask off other bits. Should be 0 if funct3 = 000
srli t3, t0, 25 # get funct7 field (instr[31:25]). No need to mask
xori t3, t3, 0b0000001 # set to 0 if funct7 = 0000001
or t1, t1, t2 # nonzero if op or funct3 mismatch
or t1, t1, t3 # nonzero if instruction is not mul
bnez t1, exit # exit for any other instruction than mul
# emulate mul: fetch arguments
srli t1, t0, 15 # extract rs1 from instr[19:15]
andi t1, t1, 31 # mask off other bits
slli t1, t1, 2 # multiply rs1 by 4 to make it a word index
add t1, tp, t1 # find location of rs1 on trap stack
lw t1, 0(t1) # read value of rs1
srli t2, t0, 20 # extract rs2 from instr[24:20]
andi t2, t2, 31 # mask off other bits
slli t2, t2, 2 # multiply rs2 by 4 to make it a word index
add t2, tp, t2 # find location of rs2 on trap stack
lw t2, 0(t2) # read value of rs2
# emulate mul p = x * y: shift and add
# x in t1, y in t2, p in t3
// p = 0
// while (y != 0)) { # iterate until all bits of y are consumed
// if (y%2) p = p + x # add x to running total
// y = y >> 1 # go on to next bit
// x = x << 1 # shift x to double
// }
li t3, 0 # p = 0
mulloop:
beqz t2, muldone # done if y == 0
andi t4, t2, 1 # t4 = y % 2
beqz t4, skipadd # don't increment p if y%2 == 0
add t3, t3, t1 # otherwise p = p + x0
skipadd:
srli t2, t2, 1 # y = y >> 1
slli t1, t1, 1 # x = x << 1
j mulloop # repeat until done
muldone:
# find rd and put result there
srli t1, t0, 7 # extract rd from instr[11:7]
andi t1, t1, 31 # mask off other bits
slli t1, t1, 2 # multiply rd by 4 to make it a word index
add t1, tp, t1 # find location of rd on trap stack
sw t3, 0(t1) # store result into rd storage on trap stack
# return to next instruction
csrr t0, mepc # read mepc
addi t0, t0, 4 # mepc + 4
csrw mepc, t0 # mepc = mpec + 4 (return to next instruction)
# restore all of the registers from the trap stack (rd could be in any one)
lw x1, 4(tp)
lw x2, 8(tp)
lw x3, 12(tp)
lw x4, 16(tp)
lw x5, 20(tp)
lw x6, 24(tp)
lw x7, 28(tp)
lw x8, 32(tp)
lw x9, 36(tp)
lw x10, 40(tp)
lw x11, 44(tp)
lw x12, 48(tp)
lw x13, 52(tp)
lw x14, 56(tp)
lw x15, 60(tp)
lw x16, 64(tp)
lw x17, 68(tp)
lw x18, 72(tp)
lw x19, 76(tp)
lw x20, 80(tp)
lw x21, 84(tp)
lw x22, 88(tp)
lw x23, 92(tp)
lw x24, 96(tp)
lw x25, 100(tp)
lw x26, 104(tp)
lw x27, 108(tp)
lw x28, 112(tp)
lw x29, 116(tp)
lw x30, 120(tp)
lw x31, 124(tp)
csrrw tp, mscratch, tp # restore tp and trap stack pointer
mret
exit:
la t1, tohost
li t0, 3 # 1 for success, 3 for failure
sw t0, 0(t1) # send fail code
j self_loop # wait
.section .tohost
tohost: # write to HTIF
.dword 0
trapstack:
.fill 32, 4 # room to save registers

View file

@ -0,0 +1,17 @@
TARGET = 8p6
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32im_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
spike --isa=rv32i_zicsr -d $(TARGET).elf
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,134 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
# set up trap trap_handler
la t0, trap_handler # address of trap trap_handler
csrw mtvec, t0 # mtvec = pointer to trap handler
la t0, trapstack # address of trap stack
csrw mscratch, t0 # mscratch = pointer to trap stack
la t0, destination # get address to make a load
lw t0, 3(t0) # misaligned load will invoke trap handler
# should return 0x23456789 in t0
self_loop:
j self_loop
trap_handler:
csrrw tp, mscratch, tp # swap tp and mscratch to put a trap stack pointer in tp
# save all registers on trap stack. We will need to index into them to find the arguments to emulate multiply
sw x0, 0(tp) # x0 is 0, but we might want to use it
sw x1, 4(tp)
sw x2, 8(tp)
sw x3, 12(tp)
sw x4, 16(tp)
sw x5, 20(tp)
sw x6, 24(tp)
sw x7, 28(tp)
sw x8, 32(tp)
sw x9, 36(tp)
sw x10, 40(tp)
sw x11, 44(tp)
sw x12, 48(tp)
sw x13, 52(tp)
sw x14, 56(tp)
sw x15, 60(tp)
sw x16, 64(tp)
sw x17, 68(tp)
sw x18, 72(tp)
sw x19, 76(tp)
sw x20, 80(tp)
sw x21, 84(tp)
sw x22, 88(tp)
sw x23, 92(tp)
sw x24, 96(tp)
sw x25, 100(tp)
sw x26, 104(tp)
sw x27, 108(tp)
sw x28, 112(tp)
sw x29, 116(tp)
sw x30, 120(tp)
sw x31, 124(tp)
csrr t0, mcause # check cause of trap
li t1, 4 # cause 4 is misaligned load
bne t0, t1, trap_return # return for any other cause
# check if instruction is lw (op=0000011, funct3 = 010)
csrr t0, mepc # address of faulting instruction
lw t3, 0(t0) # fetch the instruction. It must have been a load.
srli t1, t3, 12 # get funct3 field (instr[14:12])
andi t1, t1, 7 # mask off other bits.
xori t1, t1, 0b010 # should produce 0 if funct3 = 010
bnez t1, trap_return # return if any other kind of load
# emulate lw by performing four byte loads
csrr t0, mtval # address of load instruction
lbu t1, 0(t0) # read zeroth byte
lbu t2, 1(t0) # read the first byte
slli t2, t2, 8 # shift into position
or t1, t1, t2 # merge with zeroth byte
lbu t2, 2(t0) # read the second byte
slli t2, t2, 16 # shift into position
or t1, t1, t2 # merge with previous two bytes
lbu t2, 3(t0) # read the third byte
slli t2, t2, 24 # shift into position
or t2, t1, t2 # merge with previous three bytes
# find rd and put result there
srli t1, t3, 7 # extract rd from instr[11:7]
andi t1, t1, 31 # mask off other bits
slli t1, t1, 2 # multiply rd by 4 to make it a word index
add t1, tp, t1 # find location of rd on trap stack
sw t2, 0(t1) # store result into rd storage on trap stack
# return to next instruction
trap_return:
csrr t0, mepc # read mepc
addi t0, t0, 4 # mepc + 4
csrw mepc, t0 # mepc = mpec + 4 (return to next instruction)
# restore all of the registers from the trap stack (rd could be in any one)
lw x1, 4(tp)
lw x2, 8(tp)
lw x3, 12(tp)
lw x4, 16(tp)
lw x5, 20(tp)
lw x6, 24(tp)
lw x7, 28(tp)
lw x8, 32(tp)
lw x9, 36(tp)
lw x10, 40(tp)
lw x11, 44(tp)
lw x12, 48(tp)
lw x13, 52(tp)
lw x14, 56(tp)
lw x15, 60(tp)
lw x16, 64(tp)
lw x17, 68(tp)
lw x18, 72(tp)
lw x19, 76(tp)
lw x20, 80(tp)
lw x21, 84(tp)
lw x22, 88(tp)
lw x23, 92(tp)
lw x24, 96(tp)
lw x25, 100(tp)
lw x26, 104(tp)
lw x27, 108(tp)
lw x28, 112(tp)
lw x29, 116(tp)
lw x30, 120(tp)
lw x31, 124(tp)
csrrw tp, mscratch, tp # restore tp and trap stack pointer
mret
destination:
.dword 0x0123456789ABCDEF # fill destination with some stuff
trapstack:
.fill 32, 4 # room to save registers

View file

@ -0,0 +1,17 @@
TARGET = 8p7
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32im_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
spike --isa=rv32i_zicsr -d $(TARGET).elf
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,56 @@
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
# set up trap trap_handler
la t0, trap_handler # address of trap trap_handler
csrw mtvec, t0 # mtvec = pointer to trap handler
la t0, trapstack # address of trap stack
csrw mscratch, t0 # mscratch = pointer to trap stack
sw zero, 12(t0) # size of buffer
wait:
nop
j wait # wait for uart communication
self_loop:
j self_loop
trap_handler:
csrrw tp, mscratch, tp # swap tp and mscratch to put a trap stack pointer in tp
# save some registers on trap stack. ß
sw t0, 0(tp)
sw t1, 4(tp)
sw t2, 8(tp)
lw t0, 12(tp) # get current length of buffer
li t1, 0x10000000 # UART base address
lbu t1, 0(t1) # fetch next character
add t2, tp, t0 # address in buffer
sb t1, 0(t2) # store character in buffer
li t2, 79 # maximum buffer length
beq t0, t2, skip # is buffer full?
addi t0, t0, 1 # increase buffer pointer
skip:
sw t0, 12(tp) # update buffer length
trap_return: # return to next instruction
csrr t0, mepc # read mepc
addi t0, t0, 4 # mepc + 4
csrw mepc, t0 # mepc = mpec + 4 (return to next instruction)
# restore all of the registers from the trap stack (rd could be in any one)
lw t0, 0(tp)
lw t1, 4(tp)
lw t2, 8(tp)
csrrw tp, mscratch, tp # restore tp and trap stack pointer
mret
buffer:
.fill 80, 1 # room for buffer
trapstack:
.fill 34, 4 # room to save registers and buffer length

View file

@ -0,0 +1,17 @@
TARGET = 8p8
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32im_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
spike --isa=rv32i_zicsr -d $(TARGET).elf
clean:
rm -f $(TARGET).elf*

View file

@ -0,0 +1,61 @@
.section .text.init
.globl rvtest_entry_point
# register to write for GPIO output pins
.equ GPIO_OUTPUT_VAL, 0x1006000C
.equ CLINT_MTIMECMP, 0x02004000
.equ PERIOD, 500
# register use:
# s0: address of GPIO_OUTPUT_VAL
# s1: adress of CLINT_MTIME_CMP
# s2: PERIOD
rvtest_entry_point:
# initialize LED to off
li s0, GPIO_OUTPUT_VAL
sw zero, 0(s0) # LEDs off
# configure timer interrupt
li s2, PERIOD
csrr t0, time # read lower 32 bits of timer
csrr t1, timeh # read upper 32 bits of timer
add t0, t0, s2 # increment by PERIOD
li s1, CLINT_MTIMECMP # set timer for next toggle
sw t0, 0(s1) # CLINT_MTIMECMP = time + PERIOD
sw zero, 4(s1) # upper word = 0 (this is only because program is just starting)
# csrci mstatus, 8 # clear mstatus.MIE so interrupts are globally disabled
li t0, 128 # 1 in mie.MTIE
csrw mie, t0 # enable timer interrupts
li s3, 4 # loop counter
/*
# enter user mode
li t0, 0b11 # 3
slli t0, t0, 11 # 11 in bits 12:11
csrc mstatus, t0 # mstatus.MPP = 00 (for user mode)
la t0, user_start #
csrw mepc, t0 # where to go when entering user mode
mret
*/
#user_start: # loop with wfi
wait_loop:
csrr t0, time # check time before timer fires
wfi # wait until timer interrupt fires.
csrr t0, time # check time again after timer fires for debugging
# interrupts are globally disabled, so when the timer fires,
# wfi will advance here rather than going to an interrupt handler
lw t0, 0(s0) # read GPIO_OUTPUT_VAL
xori t0, t0, 1 # toggle least significant bits
sw t0, 0(s0) # update GPIO_OUTPUT_VAL to turn LED off->on or on->off
lw t0, 0(s1) # read CLINT_MTIME_CMP
add t0, t0, s2 # add PERIOD
sw t0, 0(s1) # CLINT_MTIME_CMP = CLINT_MTIME_CMP + PERIOD
addi s3, s3, -1 # decrement loop counter
bnez s3, wait_loop # repeat
self_loop:
j self_loop

View file

@ -0,0 +1,17 @@
TARGET = 8p9
$(TARGET).elf.objdump: $(TARGET).elf
riscv64-unknown-elf-objdump -D $(TARGET).elf > $(TARGET).elf.objdump
$(TARGET).elf: $(TARGET).S Makefile
riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32im_zicsr -mabi=ilp32 -mcmodel=medany \
-nostartfiles -T../../link/link.ld $(TARGET).S -o $(TARGET).elf
# simulate in lockstep with ImperasDV
sim: $(TARGET).elf.objdump
wsim rv32gc 8p9.elf --lockstepverbose > log
clean:
rm -f $(TARGET).elf*