RISC-V interrupts, setting up MTIMECMP - riscv

I am trying to write a program in RISC-V assembly for HiFive1 board to wake up with timer interrupt
This is my interrupt setup routine
.section .text
.align 2
.globl setupINTERRUPT
.equ MTIMECMP, 0x2004000
setupINTERRUPT:
addi sp, sp, -16 # allocate a stack frame, moves the stack up by 16 bits
sw ra, 12(sp) # save return adress on stack
li t0, 0x8 # time interval at which to triger the interrupt
li t1, MTIMECMP # MTIMECMP register of the CLINT memmory map
sw t0, 0(t1) # store the interval in MTIMECMP memory location
li t0, 0x800 # make a mask for 3rd bit
csrrs t1, mstatus, t0 # use CRS READ/SET instruction to set 3rd bit using previously defined mask
li t0, 0x3 # make a mask for 0th and 1st bit
csrrc t1, mtvec, t0 # use CSR READ/CLEAR instruction to clear 0th and 1st bit
li t0, 0x80 # make a mask for 7th bit
csrrs t1, mie, t0 # set 7th bit for MACHINE TIMER INTERRUPT ENABLE
lw ra, 12(sp) # restore the return address
addi sp, sp, 16 # dealocating stack frame
ret
I am not too sure if im setting the MTIMECMP correctly, i know its a 64 bit memory location.
I am trying to use this interrupt as a delay timer for a blinking LED (just trying to make sure the interrupt works before i move onto writing a handler)
here is my setLED program. (not that all the GPIO register setup was done previously and is known to work). I have WFI instruction before each of the ON and OFF functions. The LED doesn't light up, even though in the debug mode it does. I think in LED it skips the WFI instruction as if the interrupt was asserted.
.section .text
.align 2
.globl setLED
#include "memoryMap.inc"
#include "GPIO.inc"
.equ NOERROR, 0x0
.equ ERROR, 0x1
.equ LEDON, 0x1
# which LED to set comes into register a0
# desired On/Off state comes into a1
setLED:
addi sp, sp, -16 # allocate a stack frame, moves the stack up by 16 bits
sw ra, 12(sp) # save return adress on stack
li t0, GPIO_CTRL_ADDR # load GPIO adress
lw t1, GPIO_OUTPUT_VAL(t0) # get the current value of the pins
beqz a1, ledOff # Branch off to turn off led if a1 requests it
li t2, LEDON # load up valued of LEDON into temp register
beq a1, t2, ledOn # branch if on requested
li a0, ERROR # we got a bad status request, return an error
j exit
ledOn:
wfi
xor t1, t1, a0 # doing xor to only change the value of requested LED
sw t1, GPIO_OUTPUT_VAL(t0) # write the new output value to GPIO out
li a0, NOERROR # no error
j exit
ledOff:
wfi
xor a0, a0, 0xffffffff # invert everything so that all bits are one except the LED we are turning off
and t1, t1, a0 # and a0 and t1 to get the LED we want to turn off
sw t1, GPIO_OUTPUT_VAL(t0) # write the new output value
li a0, NOERROR
exit:
lw ra, 12(sp) # restore the return address
addi sp, sp, 16 # dealocating stack frame
ret

Related

RISC V LD error - (.text+0xc4): relocation truncated to fit: R_RISCV_JAL against `*UND*'

Does any body has clue why I get below error :-
/tmp/cceP5axg.o: in function `.L0 ':
(.text+0xc4): relocation truncated to fit: R_RISCV_JAL against `*UND*'
collect2: error: ld returned 1 exit status
R_RISCV_JAL relocation can represent an even signed 21-bit offset (-1MiB to +1MiB-2). If your symbol is further than this limit , then you have this error.
This error can also happen as an odd result of branch instructions that use hard-coded offsets. I was getting the same exact error on a program that was far less than 2Mib. It turns out it was because I had several instructions that looked like bne rd, rs, offset, but the offset was a number literal like 0x8.
The solution was to remove the literal offset and replace it with a label from the code so it looks like
bne x7, x9, branch_to_here
[code to skip]
branch_to_here:
more code ...
instead of
bne x7, x9, 0x8
[code to skip]
more code ...
When I did that to every branch instruction, the error went away. Sorry to answer this 10 months late, but I hope it helps you, anonymous reader.
Since I've searched many resources to solve this issue, I think my attempt may help others.
There're 2 reasons may trigger this issue:
The target address is an odd:
bne ra, ra, <odd offset>
The target address is a specific value during compile time (not linking):
bne ra, ra, 0x80003000
My attempt to solve:
label:
addi x0, x0, 0x0
addi x0, x0, 0x0
bne ra, ra, label + 6 // Jump to an address that relates to a label
// This can generate Instruction Address Misaligned exception
sub_label:
addi x0, x0, 0x0
beq ra, ra, sub_label // Jump to a label directly
addi x0, x0, 0x0
nop

fp equal to sp at startup but when copied onto enlarged stack changes to zero - why?

I'm learning x32 ARM assembly on RaspberryPi with Raspbian. I wrote
the following code:
# Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified # modern syntax
.text
.align 2
.global main
.type main, %function
main:
mov r0, 1 # line added only for breakpoint purposes
sub sp, sp, 8 # space for fp, lr
str fp, [sp, 0] # save fp
str lr, [sp, 4] # and lr
add fp, sp, 4 # set our frame pointer
Build with gcc:
gcc -g test.s -o test
Use gdb to check values of fp and sp in lines 13 and 16 and
dereference them:
$ gdb ./test
(gdb) break 13
Breakpoint 3 at 0x103d4: file test.s, line 13.
(gdb) break 16
Breakpoint 4 at 0x103e0: file test.s, line 16.
(gdb) run
Starting program: /home/pi/assembly/nine/bob/test
Breakpoint 3, main () at test.s:13
13 sub sp, sp, 8 # space for fp, lr
(gdb) print {$sp, $fp}
$1 = {0x7efffae8, 0x7efffae8}
(gdb) x $sp
0x7efffae8: 0x76f9e000
(gdb) x $fp
0x7efffae8: 0x76f9e000
(gdb) continue
Continuing.
Breakpoint 4, main () at test.s:16
16 add fp, sp, 4 # set our frame pointer
(gdb) print {$sp, $fp}
$2 = {0x7efffae0, 0x7efffae0}
(gdb) x $sp
0x7efffae0: 0x00000000
(gdb) x $fp
0x7efffae0: 0x00000000
As you see fp is equal to sp at startup and non-zero:
(gdb) print {$sp, $fp}
$1 = {0x7efffae8, 0x7efffae8}
(gdb) x $sp
0x7efffae8: 0x76f9e000
(gdb) x $fp
0x7efffae8: 0x76f9e000
but when copied onto the enlarged stack it changes to zero
(gdb) x $fp
0x7efffae0: 0x00000000
Why does it change to zero? Why does it change value at all? Is
underlying implementation somehow linking values of fp and sp so
that when sp is moved down to the initialized memory that might be
all zeroes fp is changed as well? I only found
this:
fp
Is the frame pointer register. In the obsolete APCS variants that
use fp, this register contains either zero, or a pointer to the
most recently created stack backtrace data structure. As with the
stack pointer, the frame pointer must be preserved, but in
handwritten code it does not need to be available at every
instant. However, it must be valid whenever any strictly
conforming function is called. fp must always be preserved.
This
comment
says that lr is stored as the first element on the stack but it's
definitely not - it stays the same and is not zero:
(gdb) print {$sp, $fp, $lr}
$1 = {0x7efffae8, 0x7efffae8, 0x76e6b718 <__libc_start_main+268>}
and after sp changes:
(gdb) x/2xw $sp
0x7efffae0: 0x00000000 0x76e6b718
Ok, I'm answering myself - this happens in gdb/arm-tdep.c in GDB source code:
/* The frame size is just the distance from the frame register
to the original stack pointer. */
if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
{
/* Frame pointer is fp. */
framereg = ARM_FP_REGNUM;
framesize = -regs[ARM_FP_REGNUM].k;
}
else
{
/* Try the stack pointer... this is a bit desperate. */
framereg = ARM_SP_REGNUM;
framesize = -regs[ARM_SP_REGNUM].k;
}

Setting the mstatus register for RISC-V

I am trying to load mstatus with another register t1.
lw t1, mstatus # load mstatys register into t1
xori t1, t1, 0x8 # xor mstatus to set 3rd bit and leave everything else as is
lw mstatus, t1 # set mstatus
The initial lw t1, mstatus works just fine. However when trying to lw mstatus, t1 the assembler gives
Error: illegal operands 'lw mstatus, t1'
I have no idea what causes this error, mstatus register is a read/write register. It should work.
mstatus is not a memory part. Then it can't be loaded/stored with lw/sw instructions under general purpose registers (x1-x31).
mstatus is part of CSR (Configuration Status Registers) that been accessed with Control and Status Register Instruction (see chapter 2.8 of riscv-spec).
Then to load mstatus you should use csrrs/c instruction and to write csrrw instruction depending of what you want to do you can also just clear/set individual bit of register.
Write t1 in mstatus and don't care of mstatus old value (x0):
csrrw t1, mstatus, x0
Read mstatus in t1 and don't touch mstatus value:
csrrs x0, mstatus, t1
or
csrrc x0, mstatus, t1
In addition to #FabienM‘s answer, I would add a reference to the pseudo instructions for handling CSRs. E.g. csrr rd, csr which is short for csrrs rd, csr, x0 and simply reads the given CSR. Those can be found in chapter 9.1 "CSR Instructions", of the The RISC-V Instruction Set Manual Volume I: Unprivileged ISA.

Compare user-inputted string/character to another string/character

So I'm a bit of a beginner to ARM Assembly (assembly in general, too). Right now I'm writing a program and one of the biggest parts of it is that the user will need to type in a letter, and then I will compare that letter to some other pre-inputted letter to see if the user typed the same thing.
For instance, in my code I have
.balign 4 /* Forces the next data declaration to be on a 4 byte segment */
dime: .asciz "D\n"
at the top of the file and
addr_dime : .word dime
at the bottom of the file.
Also, based on what I've been reading online I put
.balign 4
inputChoice: .asciz "%d"
at the top of the file, and put
inputVal : .word 0
at the bottom of the file.
Near the middle of the file (just trust me that there is something wrong with this standalone code, and the rest of the file doesn't matter in this context) I have this block of code:
ldr r3, addr_dime
ldr r2, addr_inputChoice
cmp r2, r3 /*See if the user entered D*/
addeq r5, r5, #10 /*add 10 to the total if so*/
Which I THINK should load "D" into r3, load whatever String or character the user inputted into r2, and then add 10 to r5 if they are the same.
For some reason this doesn't work, and the r5, r5, #10 code only works if addne comes before it.
addr_dime : .word dime is poitlessly over-complicated. The address is already a link-time constant. Storing the address in memory (at another location which has its own address) doesn't help you at all, it just adds another layer of indirection. (Which is actually the source of your problem.)
Anyway, cmp doesn't dereference its register operands, so you're comparing pointers. If you single-step with a debugger, you'll see that the values in registers are pointers.
To load the single byte at dime, zero-extended into r3, do
ldrb r3, dime
Using ldr to do a 32-bit load would also get the \n byte, and a 32-bit comparison would have to match that too for eq to be true.
But this can only work if dime is close enough for a PC-relative addressing mode to fit; like most RISC machines, ARM can't use arbitrary absolute addresses because the instruction-width is fixed.
For the constant, the easiest way to avoid that is not to store it in memory in the first place. Use .equ dime, 'D' to define a numeric constant, then you can use
cmp r2, dime # compare with immediate operand
Or ldr r3, =dime to ask the assembler to get the constant into a register for you. You can do this with addresses, so you could do
ldr r2, =inputVal # r2 = &inputVal
ldrb r2, [r2] # load first byte of inputVal
This is the generic way to handle loading from static data that might be too far away for a PC-relative addressing mode.
You could avoid that by using a stack address (sub sp, #16 / mov r5, sp or something). Then you already have the address in a register.
This is exactly what a C compiler does:
char dime[4] = "D\n";
char input[4] = "xyz";
int foo(int start) {
if (dime[0] == input[0])
start += 10;
return start;
}
From ARM32 gcc6.3 on the Godbolt compiler explorer:
foo:
ldr r3, .L4 # load a pointer to the data section at dime / input
ldrb r2, [r3]
ldrb r3, [r3, #4]
cmp r2, r3
addeq r0, r0, #10
bx lr
.L4:
# gcc greated this "literal pool" next to the code
# holding a pointer it can use to access the data section,
# wherever the linker ends up putting it.
.word .LANCHOR0
.section .data
.p2align 2
### These are in a different section, near each other.
### On Godbolt, click the .text button to see full assembler directives.
.LANCHOR0: # actually defined with a .set directive, but same difference.
dime:
.ascii "D\012\000"
input:
.ascii "xyz\000"
Try changing the C to compare with a literal character instead of a global the compiler can't optimize into a constant, and see what you get.

MIPS assembly: print the Fibonacci sequence (with syscalls)

Good afternoon! I have a MIPS instruction program that simulates the Fibonacci sequence. My program works by entering in a number (n) that will determine how many iterations the program will work through. The program spits out the correct results but i was wondering how i could output each sequence of number(s) of the Fib method. Heres my code first :
.data
msg1:.asciiz "Give a number: " # message for fib(n)
.text
main:
li $v0,4 #read string
la $a0,msg1 # set iterations to msg1 value
syscall
li $v0,5 # read an int
syscall
add $a0,$v0,$zero #move to $a0
jal fib #call fib
add $a0,$v0,$zero # add result into argument
li $v0,1 # output integer
syscall
li $v0,10
syscall
fib:
#a0=y
#if (y==0) return 0;
#if (y==1) return 1;
#return( fib(y-1)+fib(y-2) );
addi $sp,$sp,-12 #save in stack
sw $ra,0($sp) # save return adress to stack
sw $s0,4($sp)# save msg value
sw $s1,8($sp)# save fib(y-1)
add $s0,$a0,$zero # store msg value into s0
addi $t1,$zero,1 # comparable value
beq $s0,$zero,return0 # if s0 == 0 jump to return 0 block
beq $s0,$t1,return1# if s0 == t1 (1) jump to return 1 block
addi $a0,$s0,-1 # else decrement initial value by 1
jal fib
add $s1,$zero,$v0 #s1=fib(y-1)############################syscall needed#########################################
addi $a0,$s0,-2 # subtract two
jal fib #v0=fib(n-2)
add $v0,$v0,$s1 #v0=fib(n-2)+$s1 ##########################syscall needed#################################
exitfib:
lw $ra,0($sp) #read return adress from stack
lw $s0,4($sp) # read msg value from stack
lw $s1,8($sp) # read fib(y-1) from stack
addi $sp,$sp,12 #bring back stack pointer
jr $ra
return1:
li $v0,10 # return 1 to result
j exitfib
return0 : li $v0,0 # return 0 to result
j exitfib
Hopefully you saw, but i have two lines commented where i believe a syscall would be necessary to output each number per sequence ($s1 = fib(y-1))($v0 = fib(n-2) - $s1). I tried loading the correct code into the result register to output an integer but it still gives me problems (not displaying the process before the final result). How could i correctly implement a syscall so that i can print each number the fib method calculates before reaching a final result?

Resources