commit 0dd2907986e5f654b25e79ee621668d70a7b724f
parent b35076df2a9c5a79f1916f62f50db938589c9cfd
Author: Brian Swetland <swetland@frotz.net>
Date: Wed, 11 May 2022 15:55:04 -0700
clean up a bunch of stuff
- move intrinsics from hw/riscv.h to hw/intrinsics.h
- add intrinsics for thread pointer access
- add intrinsics for interrupt state management
- add intrinsics for tlb management
- move xputc()/xputs()/xgetc() into hw/src/debug-io.c
- move xprintf() into hw/src/debug-printf.c
- move exception printing code into hw/src/print-exception.c
- remove dead code from boot.c
- adjust project files and sources as necessary
- add context-switch.h and context-switch.c
Diffstat:
19 files changed, 317 insertions(+), 159 deletions(-)
diff --git a/boot/boot.c b/boot/boot.c
@@ -2,55 +2,22 @@
// Licensed under the Apache License, Version 2.0
#include <hw/riscv.h>
-#include <hw/riscv-clint.h>
+#include <hw/intrinsics.h>
#include <hw/debug.h>
-#include "boot.h"
#include <hw/platform.h>
#include <hw/litex.h>
+#include "boot.h"
+
// we expect the supervisor program to be in memory after the end of the bootloader
#define SVC_ENTRY (DRAM_BASE + BOOTLOADER_SIZE)
-static const char* cause_name(uint32_t n) {
- if (n & 0x80000000U) {
- return "Interrupt";
- } else {
- switch(n & 0x7FFFFFFFU) {
- case 0: return "Instruction Address Misaligned";
- case 1: return "Instruction Address Fault";
- case 2: return "Illegal Instruction";
- case 3: return "Breakpoint";
- case 4: return "Load Address Misaligned";
- case 5: return "Load Address Fault";
- case 6: return "Store Address Misaligned";
- case 7: return "Store Address Fault";
- case 8: return "User Mode ECALL";
- case 9: return "Supervisor Mode ECALL";
- case 10: return "Machine Mode ECALL";
- case 12: return "Instruction Page Fault";
- case 13: return "Load Page Fault";
- case 14: return "Store Page Fault";
- }
- }
- return "Unknown";
-}
-
-static const char* mode_name(uint32_t n) {
- switch (n) {
- case 0: return "User";
- case 1: return "Supervisor";
- case 3: return "Machine Mode";
- default: return "???";
- }
-}
-
void mach_exception_handler(uint32_t regs[32]) {
+#if EMULATE_MISSING_CSR_READS
uint32_t mcause = csr_read(CSR_MCAUSE);
- uint32_t mstatus = csr_read(CSR_MSTATUS);
uint32_t mtval = csr_read(CSR_MTVAL);
-#if EMULATE_MISSING_CSR_READS
// fragile: no mtval on qemu
if (mcause == 2) mtval = *((uint32_t*) regs[0]);
@@ -64,24 +31,16 @@ void mach_exception_handler(uint32_t regs[32]) {
#endif
xprintf("\n** MACHINE EXCEPTION **\n");
- xprintf("** %s (in %s mode)\n\n", cause_name(mcause),
- mode_name((mstatus >> MSTATUS_MPP_SHIFT) & 3));
-
- xprintf("pc %08x ra %08x sp %08x gp %08x MSTATUS %08x\n",
- regs[0], regs[1], regs[2], regs[3], mstatus);
- xprintf("tp %08x t0 %08x t1 %08x t2 %08x MCAUSE %08x\n",
- regs[4], regs[5], regs[6], regs[7], mcause);
- xprintf("fp %08x s1 %08x a0 %08x a1 %08x MTVAL %08x\n",
- regs[8], regs[9], regs[10], regs[11], mtval);
- xprintf("a2 %08x a3 %08x a4 %08x a5 %08x\n",
- regs[12], regs[13], regs[14], regs[15]);
- xprintf("\n** HALT\n");
+ xprint_exception(regs);
+
+ xprintf("\nHALT\n");
for (;;) ;
}
// interrupts and exceptions to delegate to supervisor mode
#define INT_LIST (INTb_SVC_SW|INTb_SVC_TIMER|INTb_SVC_EXTERN)
-#define EXC_LIST (EXCb_ECALL_UMODE)
+//#define EXC_LIST (EXCb_ECALL_UMODE)
+#define EXC_LIST (0xFFFF)
void start(uint32_t hartid, uint32_t fdt) {
xprintf("\n** Frobozz Magic Bootloader v0.2 **\n\n");
@@ -94,28 +53,6 @@ void start(uint32_t hartid, uint32_t fdt) {
// use the free ram below the supervisor entry as our exception stack
csr_write(CSR_MSCRATCH, SVC_ENTRY);
-
-#if USE_CLINT_TIMER
-#define CLINT_BASE 0x2000000
-#define TIME_TICK 10000000
-
- // leaving room for the timer interrupt workspace above the stack
- csr_write(CSR_MSCRATCH, SVC_ENTRY - IWS_SIZE);
-
- uint32_t* iws = (void*) (SVC_ENTRY - IWS_SIZE);
- uint32_t mtimecmp = CLINT_BASE + CLINT_MTIMECMP(hartid);
- uint32_t mtime = CLINT_BASE + CLINT_MTIME;
-
- iws[IWS_TIMECMP/4] = mtimecmp;
- iws[IWS_TICKINC/4] = TIME_TICK;
-
- // set initial tick
- uint64_t next = *((uint64_t*) mtime) + TIME_TICK;
- *((uint64_t*) mtimecmp) = next;
-
- csr_set(CSR_MIE, INTb_MACH_TIMER);
-#endif
-
// QEMU currently emulates memory protection, which we must appease
if (qemu) {
// U/S allow access to all memory
@@ -131,6 +68,7 @@ void start(uint32_t hartid, uint32_t fdt) {
csr_write(CSR_MSTATUS, (PRIV_S << MSTATUS_MPP_SHIFT) | MSTATUS_MPIE);
xprintf("SVC ENTRY @0x%08x\n\n", SVC_ENTRY);
+
enter_mode_s(hartid, fdt, SVC_ENTRY, 0);
}
diff --git a/hw/inc/hw/context-switch.h b/hw/inc/hw/context-switch.h
@@ -0,0 +1,35 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0
+
+#pragma once
+
+#include <stdint.h>
+
+// this layout must match the assembly in context-switch.S
+typedef struct {
+ uint32_t pc;
+ uint32_t sp;
+ uint32_t gp;
+ uint32_t tp;
+ uint32_t s0;
+ uint32_t s1;
+ uint32_t s2;
+ uint32_t s3;
+ uint32_t s4;
+ uint32_t s5;
+ uint32_t s6;
+ uint32_t s7;
+ uint32_t s8;
+ uint32_t s9;
+ uint32_t s10;
+ uint32_t s11;
+} rv32_ctxt_t;
+
+// store the current state into prev
+// restore the new state from next
+void context_switch(rv32_ctxt_t* prev, rv32_ctxt_t* next);
+
+// bootstrap for a new context
+// will call s2(s0, s1)
+// and call context_exit() should it return
+void context_entry(void);
diff --git a/hw/inc/hw/debug.h b/hw/inc/hw/debug.h
@@ -3,8 +3,16 @@
#pragma once
+#include <stdint.h>
+
+// debug-io.c -- can be replaced with alternate impls
void xputc(unsigned c);
void xputs(const char* s);
-int xgetc(void);
+int xgetc(void);
+// debug-printf.c -- calls xputs()
void xprintf(const char* fmt, ...);
+
+// print-exception.c -- calls xprintf()
+void xprint_exception(uint32_t regs[32]);
+
diff --git a/hw/inc/hw/intrinsics.h b/hw/inc/hw/intrinsics.h
@@ -0,0 +1,93 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0
+
+// inline assembly helpers for use from C
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <hw/riscv.h>
+
+typedef uint32_t irqstate_t;
+
+#define __S(x) #x
+
+// CSR Accessors
+
+#define csr_read(csr) ({ \
+ uint32_t v; asm volatile ("csrr %0, " __S(csr) : "=r"(v)); v; })
+
+#define csr_write(csr, val) ({ \
+ asm volatile ("csrw " __S(csr) ", %0" :: "rK"(val)); });
+
+#define csr_swap(csr, val) ({ \
+ uint32_t v; asm volatile ("csrrw %0, " __S(csr) ", %1" : "=r"(v) : "rK"(val)); v; })
+
+#define csr_set(csr, bit) ({ \
+ asm volatile ("csrrs x0, " __S(csr) ", %0" :: "rK"(bit)); })
+
+#define csr_clr(csr, bit) ({ \
+ asm volatile ("csrrc x0, " __S(csr) ", %0" :: "rK"(bit)); })
+
+// Disable Interrupts
+static inline void irq_disable(void) {
+ asm volatile("csrrc x0, sstatus, %1" :: "i"(SSTATUS_SIE));
+}
+
+// Enable Interrupts
+static inline void irq_enable(void) {
+ asm volatile("csrrs x0, sstatus, %1" :: "i"(SSTATUS_SIE));
+}
+
+// Disable Interrupts and return the previous interrupt state
+static inline irqstate_t irq_disable_save(void) {
+ uint32_t v; asm volatile("csrrc %0, sstatus, %1" : "=r"(v) : "i"(SSTATUS_SIE));
+ return v & SSTATUS_SIE;
+}
+
+// Restore the previous interrupt state
+static inline void irq_restore(irqstate_t state) {
+ if (state) {
+ asm volatile("csrrs x0, sstatus, %0" :: "i"(SSTATUS_SIE));
+ }
+}
+
+// Thread Pointer Accessors
+
+static inline void* threadptr_get(void) {
+ void *ptr; asm ("mv %0, tp" : "=r"(ptr)); return ptr;
+}
+
+static inline void threadptr_set(void* ptr) {
+ asm ("mv tp, %0" :: "r"(ptr));
+}
+
+#define threadptr_getf(type, field) ({\
+ type T; typeof(T.field) v; asm ("lw %0, %1(tp)" : "=r"(v) : "i"(offsetof(type,field))); v; })
+
+#define threadptr_setf(type, field, val) ({\
+ asm ("sw %0, %1(tp)" :: "r"(val), "i"(offsetof(type,field))); })
+
+
+// TLB Cache Managemant
+
+static inline void tlb_flush_all(void) {
+ asm volatile("sfence.vma zero, zero");
+}
+
+static inline void tlb_fush_addr(uint32_t a) {
+ asm volatile("sfence.vma %0, zero" :: "r"(a));
+}
+
+// sfence.vma rs2, rs1 (asid, addr)
+// sfence.vma x0, x0 orders all r/w to any level page table
+// invalidates all TLB entries
+// sfence.vma x1, x0 orders all r/w to any level page table for ASID x1
+// invalidates all non-global ASID x1 TLB entries
+// sfence.vma x0, x2 orders all r/w to leaf PTEs for vaddr in x2
+// invalidates TLB entries for vaddr x2
+// sfence.vma x1, x2 as last but for ASID x1
+
+
diff --git a/hw/inc/hw/platform.h b/hw/inc/hw/platform.h
@@ -34,6 +34,7 @@
#ifndef __ASSEMBLY__
#include <stdint.h>
+#include <hw/intrinsics.h>
static inline void io_wr32(uint32_t a, uint32_t v) {
*((volatile uint32_t*) a) = v;
diff --git a/hw/inc/hw/riscv.h b/hw/inc/hw/riscv.h
@@ -1,6 +1,8 @@
// Copyright 2022, Brian Swetland <swetland@frotz.net>
// Licensed under the Apache License, Version 2.0
+// RISCV RV32 CSRs
+
#pragma once
#define CSR_FFLAGS 0x001 // FP Exceptions
@@ -159,42 +161,3 @@
#define PTE_RSW0 0x100 // Reserved For Software 0
#define PTE_RSW1 0x200 // Reserved For Software 1
-// inline assembly helpers for CSR access, etc
-#ifndef __ASSEMBLER__
-#include <stdint.h>
-
-#define __S(x) #x
-
-#define csr_read(csr) ({ \
- uint32_t v; asm volatile ("csrr %0, " __S(csr) : "=r"(v)); v; })
-
-#define csr_write(csr, val) ({ \
- asm volatile ("csrw " __S(csr) ", %0" :: "rK"(val)); });
-
-#define csr_swap(csr, val) ({ \
- uint32_t v; asm volatile ("csrrw %0, " __S(csr) ", %1" : "=r"(v) : "rK"(val)); v; })
-
-#define csr_set(csr, bit) ({ \
- uint32_t v; asm volatile ("csrrs %0, " __S(csr) ", %1" : "=r"(v) : "rK"(bit)); v; })
-
-#define csr_clr(csr, bit) ({ \
- uint32_t v; asm volatile ("csrrc %0, " __S(csr) ", %1" : "=r"(v) : "rK"(bit)); v; })
-
-static inline void tlb_flush_all(void) {
- asm volatile("sfence.vma zero, zero");
-}
-
-static inline void tlb_fush_addr(uint32_t a) {
- asm volatile("sfence.vma %0, zero" :: "r"(a));
-}
-
-// sfence.vma rs2, rs1 (asid, addr)
-// sfence.vma x0, x0 orders all r/w to any level page table
-// invalidates all TLB entries
-// sfence.vma x1, x0 orders all r/w to any level page table for ASID x1
-// invalidates all non-global ASID x1 TLB entries
-// sfence.vma x0, x2 orders all r/w to leaf PTEs for vaddr in x2
-// invalidates TLB entries for vaddr x2
-// sfence.vma x1, x2 as last but for ASID x1
-
-#endif
diff --git a/hw/src/context-switch.S b/hw/src/context-switch.S
@@ -0,0 +1,52 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0
+
+// this code must match the structure rv32_ctxt_t in context-switch.h
+
+// context_switch(rv32_ctxt_t* prev, rv32_ctxt_t* next)
+.globl context_switch
+context_switch:
+ // save old context to prev
+ sw ra, 0x00(a0)
+ sw sp, 0x04(a0)
+ sw gp, 0x08(a0)
+ sw tp, 0x0C(a0)
+ sw s0, 0x10(a0)
+ sw s1, 0x14(a0)
+ sw s2, 0x18(a0)
+ sw s3, 0x1C(a0)
+ sw s4, 0x20(a0)
+ sw s5, 0x24(a0)
+ sw s6, 0x28(a0)
+ sw s7, 0x2C(a0)
+ sw s8, 0x30(a0)
+ sw s9, 0x34(a0)
+ sw s10, 0x38(a0)
+ sw s11, 0x3C(a0)
+ // load new context from next
+ lw ra, 0x00(a1)
+ lw sp, 0x04(a1)
+ lw gp, 0x08(a1)
+ lw tp, 0x0C(a1)
+ lw s0, 0x10(a1)
+ lw s1, 0x14(a1)
+ lw s2, 0x18(a1)
+ lw s3, 0x1C(a1)
+ lw s4, 0x20(a1)
+ lw s5, 0x24(a1)
+ lw s6, 0x28(a1)
+ lw s7, 0x2C(a1)
+ lw s8, 0x30(a1)
+ lw s9, 0x34(a1)
+ lw s10, 0x38(a1)
+ lw s11, 0x3C(a1)
+ // return to new context
+ ret
+
+.globl context_entry
+context_entry:
+ mv a0, s0
+ mv a1, s1
+ jalr s2
+ j context_exit
+
diff --git a/hw/src/debug-io.c b/hw/src/debug-io.c
@@ -0,0 +1,32 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <hw/platform.h>
+#include <hw/litex.h>
+#include <hw/debug.h>
+
+#define uart_rd(a) io_rd32(UART0_BASE + LX_UART_ ## a)
+#define uart_wr(a,v) io_wr32(UART0_BASE + LX_UART_ ## a, v)
+
+void xputc(unsigned c) {
+ if (c == '\n') {
+ while (uart_rd(TXFULL)) ;
+ uart_wr(TX, '\r');
+ }
+ while (uart_rd(TXFULL)) ;
+ uart_wr(TX, c);
+}
+
+void xputs(const char* s) {
+ while (*s != 0) {
+ xputc(*s++);
+ }
+}
+
+int xgetc(void) {
+ return -1;
+}
+
diff --git a/hw/src/debug-printf.c b/hw/src/debug-printf.c
@@ -0,0 +1,17 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <hw/debug.h>
+
+void xprintf(const char* fmt, ...) {
+ char msg[128];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+ xputs(msg);
+}
+
diff --git a/hw/src/debug.c b/hw/src/debug.c
@@ -1,40 +0,0 @@
-// Copyright 2022, Brian Swetland <swetland@frotz.net>
-// Licensed under the Apache License, Version 2.0.
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#include <hw/platform.h>
-#include <hw/litex.h>
-
-#define uart_rd(a) io_rd32(UART0_BASE + LX_UART_ ## a)
-#define uart_wr(a,v) io_wr32(UART0_BASE + LX_UART_ ## a, v)
-
-void xputc(unsigned c) {
- if (c == '\n') {
- while (uart_rd(TXFULL)) ;
- uart_wr(TX, '\r');
- }
- while (uart_rd(TXFULL)) ;
- uart_wr(TX, c);
-}
-
-void xputs(const char* s) {
- while (*s != 0) {
- xputc(*s++);
- }
-}
-
-void xprintf(const char* fmt, ...) {
- char msg[128];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(msg, sizeof(msg), fmt, ap);
- va_end(ap);
- xputs(msg);
-}
-
-int xgetc(void) {
- return -1;
-}
-
diff --git a/hw/src/print-exception.c b/hw/src/print-exception.c
@@ -0,0 +1,57 @@
+// Copyright 2022, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0
+
+#include <hw/debug.h>
+#include <hw/riscv.h>
+#include <hw/intrinsics.h>
+
+static const char* cause_name(uint32_t n) {
+ if (n & 0x80000000U) {
+ return "Interrupt";
+ } else {
+ switch(n & 0x7FFFFFFFU) {
+ case 0: return "Instruction Address Misaligned";
+ case 1: return "Instruction Address Fault";
+ case 2: return "Illegal Instruction";
+ case 3: return "Breakpoint";
+ case 4: return "Load Address Misaligned";
+ case 5: return "Load Address Fault";
+ case 6: return "Store Address Misaligned";
+ case 7: return "Store Address Fault";
+ case 8: return "User Mode ECALL";
+ case 9: return "Supervisor Mode ECALL";
+ case 10: return "Machine Mode ECALL";
+ case 12: return "Instruction Page Fault";
+ case 13: return "Load Page Fault";
+ case 14: return "Store Page Fault";
+ }
+ }
+ return "Unknown";
+}
+
+static const char* mode_name(uint32_t n) {
+ switch (n) {
+ case 0: return "User";
+ case 1: return "Supervisor";
+ case 3: return "Machine Mode";
+ default: return "???";
+ }
+}
+
+void xprint_exception(uint32_t regs[32]) {
+ uint32_t mcause = csr_read(CSR_MCAUSE);
+ uint32_t mstatus = csr_read(CSR_MSTATUS);
+ uint32_t mtval = csr_read(CSR_MTVAL);
+
+ xprintf("** %s (in %s mode)\n\n", cause_name(mcause),
+ mode_name((mstatus >> MSTATUS_MPP_SHIFT) & 3));
+
+ xprintf("pc %08x ra %08x sp %08x gp %08x MSTATUS %08x\n",
+ regs[0], regs[1], regs[2], regs[3], mstatus);
+ xprintf("tp %08x t0 %08x t1 %08x t2 %08x MCAUSE %08x\n",
+ regs[4], regs[5], regs[6], regs[7], mcause);
+ xprintf("fp %08x s1 %08x a0 %08x a1 %08x MTVAL %08x\n",
+ regs[8], regs[9], regs[10], regs[11], mtval);
+ xprintf("a2 %08x a3 %08x a4 %08x a5 %08x\n",
+ regs[12], regs[13], regs[14], regs[15]);
+}
diff --git a/make/app.mk b/make/app.mk
@@ -22,7 +22,7 @@ $(MOD_ELF): $(MOD_OBJ) $(MOD_LDSCRIPT)
$(V)$(XGCC) $(_LDFLAGS) -o $@ $(_OBJ) $(_LIB)
$(MOD_LST): $(MOD_ELF)
- $(V)$(XOBJDUMP) -D $< > $@
+ $(V)$(XOBJDUMP) -d -z $< > $@
$(MOD_BIN): $(MOD_ELF)
$(V)$(XOBJCOPY) -O binary $< $@
diff --git a/misc/info.c b/misc/info.c
@@ -3,6 +3,7 @@
#include <hw/debug.h>
#include <hw/riscv.h>
+#include <hw/intrinsics.h>
void start(uint32_t hartid, uint32_t dtb) {
xprintf("MSTATUS %08x\n", csr_read(CSR_MSTATUS));
diff --git a/project/boot.app.mk b/project/boot.app.mk
@@ -1,7 +1,8 @@
MOD_NAME := boot
MOD_SRC := hw/src/start.S boot/entry.S boot/boot.c
-MOD_SRC += hw/src/debug.c
+MOD_SRC += hw/src/print-exception.c
+MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c
MOD_LIB := c
MOD_LDSCRIPT := hw/boot.ram.ld
include make/app.mk
diff --git a/project/devicetree.app.mk b/project/devicetree.app.mk
@@ -1,6 +1,6 @@
MOD_NAME := devicetree
MOD_SRC := hw/src/start.S misc/devicetree.c
-MOD_SRC += hw/src/debug.c
+MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c
MOD_LIB := c
include make/app.mk
diff --git a/project/info.app.mk b/project/info.app.mk
@@ -1,6 +1,6 @@
MOD_NAME := info
MOD_SRC := hw/src/start.S misc/info.c
-MOD_SRC += hw/src/debug.c
+MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c
MOD_LIB := c
include make/app.mk
diff --git a/project/mandelbrot-fb.app.mk b/project/mandelbrot-fb.app.mk
@@ -1,7 +1,7 @@
MOD_NAME := mandelbrot-fb
MOD_SRC := hw/src/start.S misc/mandelbrot-fb.c
-MOD_SRC += hw/src/debug.c
+MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c
MOD_QEMU_FB := 1
MOD_LIB := c
include make/app.mk
diff --git a/project/mandelbrot.app.mk b/project/mandelbrot.app.mk
@@ -1,6 +1,6 @@
MOD_NAME := mandelbrot
MOD_SRC := hw/src/start.S misc/mandelbrot.c
-MOD_SRC += hw/src/debug.c
+MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c
MOD_LIB := c
include make/app.mk
diff --git a/project/traps.app.mk b/project/traps.app.mk
@@ -1,6 +1,6 @@
MOD_NAME := traps
MOD_SRC := hw/src/start.S misc/traps-entry.S misc/traps.c
-MOD_SRC += hw/src/debug.c
+MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c
MOD_LIB := c
include make/app.mk