Last active
July 31, 2020 19:28
-
-
Save btashton/4aa7ed42bc81ff4716821636997d9df9 to your computer and use it in GitHub Desktop.
ecall debugging for SERV core
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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 |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.