-
-
Save btashton/4aa7ed42bc81ff4716821636997d9df9 to your computer and use it in GitHub Desktop.
| /* uartblink.S -- Brennan Ashton <[email protected]> | |
| * | |
| *************************************************************************** | |
| * 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 |
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.
@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.