-
-
Save btashton/4aa7ed42bc81ff4716821636997d9df9 to your computer and use it in GitHub Desktop.
/* uartblink.S -- Brennan Ashton <bashton@brennanashton.com> | |
* | |
*************************************************************************** | |
* This is free and unencumbered software released into the public domain. | |
* | |
* Anyone is free to copy, modify, publish, use, compile, sell, or | |
* distribute this software, either in source code form or as a compiled | |
* binary, for any purpose, commercial or non-commercial, and by any | |
* means. | |
*************************************************************************** | |
* | |
* ABOUT: | |
* This is a test for handling interrupts and ecall for the SERV core | |
* it can be run like this: | |
* fusesoc run --target=verilator_tb servant --uart_baudrate=115200 | |
* --firmware=uartblink.hex --timeout=10000000 | |
* | |
* The expected result is a stream of 'a\n' with some 'b\n' on the console. | |
* the a is a busy loop, while the b indicates an interrupt is handled. | |
* right now the interrupts should be disabled until ecall is issued in | |
* which case machine interrupts are re-enabled via the mpie bit in mstatus. | |
* | |
* It appears that this bit does not actually work with this core. So you | |
* will see a single 'b\n' from the ecall, but no interrupts from the timer. | |
* The expected behavior can be seen when the mie bit is set before ecall | |
* This line is currently commented out. | |
*/ | |
#ifndef UART_BASE | |
#define UART_BASE 0x40000000 | |
#endif | |
#ifndef TIMER_BASE | |
#define TIMER_BASE 0x80000000 | |
#endif | |
#ifndef DELAY | |
#define DELAY 10 /* Loop 10 times before writing UART */ | |
#endif | |
#ifndef TICK_COUNT | |
#define TICK_COUNT 40000 /* 40000 cycles per tick */ | |
#endif | |
/* | |
* "RESERVED" registers: | |
* a0 = UART Base address | |
* a1 = Timer Base address | |
* a2 = One | |
* t1 = delay max value | |
* t2 = Current timer value | |
* t3 = timer tick | |
*/ | |
.globl _start | |
.globl __trap_vec | |
_start: | |
/* Load GPIO base address to a0 */ | |
li a0, UART_BASE | |
/* Load TIMER base address to a1 */ | |
li a1, TIMER_BASE | |
/* Set busy timer value to control blink speed */ | |
li t1, DELAY | |
/* Initialize UART (stop bit) */ | |
li a2, 1 | |
sb a2, 0(a0) | |
nop | |
nop | |
/* Initialize Timer */ | |
li t3, TICK_COUNT | |
lw t4, 0(a1) | |
add t4, t4, t3 | |
sw t4, 0(a1) | |
/* Disable Interrupts */ | |
csrw mstatus, zero | |
/* setup trap */ | |
lui t0, %hi(__trap_vec) | |
addi t0, t0, %lo(__trap_vec) | |
csrw mtvec, t0 | |
/* Initialize timer interrupt */ | |
li t6, 0x80 | |
csrs mie, t6 | |
/* We should be able to enable interrupts via the MPIE bit of mstatus | |
* in the exception handler logic. These two instructions will enable | |
* interrupts ahead of the exception handler being called. | |
*/ | |
//li t5, 0x8 | |
//csrs mstatus, t5 | |
ecall | |
blinky: | |
/* "blink 'a\n' on the UART */ | |
/* Reset timer */ | |
and t2, zero, zero | |
/* Enter critical */ | |
li t0, 0x8 | |
csrrc t4, mstatus, t0 | |
jal uart_a | |
jal uart_nl | |
/* Leave critical */ | |
csrw mstatus, t4 | |
/* Delay loop */ | |
time1: | |
addi t2, t2, 1 | |
bne t1, t2, time1 | |
j blinky | |
/* Trap Vector */ | |
__trap_vec: | |
/* Increment the PC (only for ecall?) */ | |
csrr t4, mepc | |
addi t4, t4, 4 | |
csrw mepc, t4 | |
/* add to the timer offset */ | |
li t3, TICK_COUNT | |
lw t4, 0(a1) | |
add t4, t4, t3 | |
sw t4, 0(a1) | |
/* "blink" 'b\n' on UART */ | |
jal uart_b | |
jal uart_nl | |
/* Let's see if the mstatus mpie works. | |
* Interrupts should be enabled after this returns | |
*/ | |
li t4, 0x80 | |
csrw mstatus, t4 | |
mret | |
/* uart utility functions */ | |
uart_a: | |
/* 'a' */ | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
ret | |
uart_b: | |
/* 'b' */ | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
ret | |
uart_nl: | |
/* '\n' */ | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb zero, 0(a0) | |
nop | |
nop | |
sb a2, 0(a0) | |
nop | |
nop | |
ret |
@DaveBerkeley have you tried running the assembly in this gist? It was broken in the way mentioned in description, but was resolved by my PR olofk/serv#31. @olofk implemented a different approach that should also fix this issue, as well as another one I found and mentioned in the PR. I have not validating the change that he made in master, but you might try my PR and we can get to the bottom of all of this.
Just tried your mpie branch. Still not working for my case.
I'm new to risc-v, so I may be missing something. I've been wrapping SERV with a SoC to allow XIP (execute in place) directly from Flash on the icebreaker.
https://github.com/DaveBerkeley/fpga/tree/master/serv
This is all working fine, except I can't enable the timer interrupt.
Oh excellent work! I can take a look later today. Do you have a full code listing where you use these functions I could look at?
https://github.com/DaveBerkeley/fpga/blob/master/serv/firmware.c
But I'm using my own timer hardware
https://github.com/DaveBerkeley/fpga/blob/master/serv/timer.v
I was embarrassed to find that it adds 500 LUTs to the usage (including the irq code in SERV). My timer has 2 x 64-registers and a 32-bit temp register.
If you compile with -O1
optimisation gcc does a great job of producing minimal code.
I'm also using a hardware uart for tx which you might like. It delays ack until the next byte can be sent, which removes the need for bit-banging or polling the uart for busy.
I have not been able to set the mie.mtie bit. Is this a related problem? I'm using asm from C to read / write the csr regs, eg
I can set the interrupt enable bit in mstatus, but not in mie, eg :
This compiles to :
If I set the mie.mtie bit permanently in rtl/serv_csr.v the timer interrupts are enabled :
The system then runs as expected.