From dfab1aa717da88dfcdf8206a51b00681b0bd4a18 Mon Sep 17 00:00:00 2001 From: Yannis Huber Date: Tue, 16 Jun 2020 14:55:55 +0200 Subject: [PATCH] maixbit (uart): serial is working with echo example --- src/device/riscv/start64.S | 60 +++++++++++++++++ src/machine/machine_k210.go | 13 ++-- src/runtime/arch_tinygoriscv.go | 2 +- src/runtime/arch_tinygoriscv64.go | 92 +++++++++++++++++++++++++++ src/runtime/runtime_k210.go | 38 +++++++---- src/runtime/runtime_tinygoriscv.go | 2 +- src/runtime/runtime_tinygoriscv64.go | 38 +++++++++++ src/runtime/scheduler_tinygoriscv64.S | 32 ++++++++++ targets/riscv.json | 4 -- targets/riscv32.json | 5 ++ targets/riscv64.json | 4 ++ 11 files changed, 263 insertions(+), 27 deletions(-) create mode 100644 src/device/riscv/start64.S create mode 100644 src/runtime/arch_tinygoriscv64.go create mode 100644 src/runtime/runtime_tinygoriscv64.go create mode 100644 src/runtime/scheduler_tinygoriscv64.S diff --git a/src/device/riscv/start64.S b/src/device/riscv/start64.S new file mode 100644 index 00000000..b21bfb13 --- /dev/null +++ b/src/device/riscv/start64.S @@ -0,0 +1,60 @@ +.section .init +.global _start +.type _start,@function + +_start: + // Load the stack pointer. + la sp, _stack_top + + // Load the globals pointer. The program will load pointers relative to this + // register, so it must be set to the right value on startup. + // See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register + la gp, __global_pointer$ + + // Jump to runtime.main + call main + +.section .text.handleInterruptASM +.global handleInterruptASM +.type handleInterruptASM,@function +handleInterruptASM: + // Save and restore all registers, because the hardware only saves/restores + // the pc. + // Note: we have to do this in assembly because the "interrupt"="machine" + // attribute is broken in LLVM: https://bugs.llvm.org/show_bug.cgi?id=42984 + addi sp, sp, -128 + sd ra, 120(sp) + sd t0, 112(sp) + sd t1, 104(sp) + sd t2, 96(sp) + sd a0, 88(sp) + sd a1, 80(sp) + sd a2, 72(sp) + sd a3, 64(sp) + sd a4, 56(sp) + sd a5, 48(sp) + sd a6, 40(sp) + sd a7, 32(sp) + sd t3, 24(sp) + sd t4, 16(sp) + sd t5, 8(sp) + sd t6, 0(sp) + call handleInterrupt + ld t6, 0(sp) + ld t5, 8(sp) + ld t4, 16(sp) + ld t3, 24(sp) + ld a7, 32(sp) + ld a6, 40(sp) + ld a5, 48(sp) + ld a4, 56(sp) + ld a3, 64(sp) + ld a2, 72(sp) + ld a1, 80(sp) + ld a0, 88(sp) + ld t2, 96(sp) + ld t1, 104(sp) + ld t0, 112(sp) + ld ra, 120(sp) + addi sp, sp, 128 + mret diff --git a/src/machine/machine_k210.go b/src/machine/machine_k210.go index 9f040dc7..3e89305e 100644 --- a/src/machine/machine_k210.go +++ b/src/machine/machine_k210.go @@ -60,22 +60,21 @@ var ( ) func (uart UART) Configure(config UARTConfig) { - div := CPUFrequency()/115200 - 1 uart.Bus.DIV.Set(div) uart.Bus.TXCTRL.Set(kendryte.UARTHS_TXCTRL_TXEN) uart.Bus.RXCTRL.Set(kendryte.UARTHS_RXCTRL_RXEN) - //uart.Bus.IP.Set(kendryte.UARTHS_IP_TXWM | kendryte.UARTHS_IP_RXWM) - //uart.Bus.IE.Set(kendryte.UARTHS_IE_RXWM) - /*intr := interrupt.New(kendryte.IRQ_UARTHS, UART0.handleInterrupt) + // Enable interrupts on receive. + uart.Bus.IE.Set(kendryte.UARTHS_IE_RXWM) + + intr := interrupt.New(kendryte.IRQ_UARTHS, UART0.handleInterrupt) intr.SetPriority(5) - intr.Enable()*/ - + intr.Enable() } -func (uart UART) handleInterrupt(interrupt.Interrupt) { +func (uart *UART) handleInterrupt(interrupt.Interrupt) { rxdata := uart.Bus.RXDATA.Get() c := byte(rxdata) if uint32(c) != rxdata { diff --git a/src/runtime/arch_tinygoriscv.go b/src/runtime/arch_tinygoriscv.go index 7aa34b50..8bd0f076 100644 --- a/src/runtime/arch_tinygoriscv.go +++ b/src/runtime/arch_tinygoriscv.go @@ -1,4 +1,4 @@ -// +build tinygo.riscv +// +build tinygo.riscv32 package runtime diff --git a/src/runtime/arch_tinygoriscv64.go b/src/runtime/arch_tinygoriscv64.go new file mode 100644 index 00000000..17cddf60 --- /dev/null +++ b/src/runtime/arch_tinygoriscv64.go @@ -0,0 +1,92 @@ +// +build tinygo.riscv64 + +package runtime + +import "device/riscv" + +const GOARCH = "arm64" // riscv pretends to be arm + +// The bitness of the CPU (e.g. 8, 32, 64). +const TargetBits = 64 + +// Align on word boundary. +func align(ptr uintptr) uintptr { + return (ptr + 7) &^ 7 +} + +func getCurrentStackPointer() uintptr { + return riscv.AsmFull("mv {}, sp", nil) +} + +// Documentation: +// * https://llvm.org/docs/Atomics.html +// * https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html +// +// In the case of RISC-V, some operations may be implemented with libcalls if +// the operation is too big to be handled by assembly. Officially, these calls +// should be implemented with a lock-free algorithm but as (as of this time) all +// supported RISC-V chips have a single hart, we can simply disable interrupts +// to get the same behavior. + +//export __atomic_load_8 +func __atomic_load_8(ptr *uint64, ordering int32) uint64 { + mask := riscv.DisableInterrupts() + value := *ptr + riscv.EnableInterrupts(mask) + return value +} + +//export __atomic_store_8 +func __atomic_store_8(ptr *uint64, value uint64, ordering int32) { + mask := riscv.DisableInterrupts() + *ptr = value + riscv.EnableInterrupts(mask) +} + +//export __atomic_exchange_8 +func __atomic_exchange_8(ptr *uint64, value uint64, ordering int32) uint64 { + mask := riscv.DisableInterrupts() + oldValue := *ptr + *ptr = value + riscv.EnableInterrupts(mask) + return oldValue +} + +//export __atomic_compare_exchange_8 +func __atomic_compare_exchange_8(ptr, expected *uint64, desired uint64, success_ordering, failure_ordering int32) bool { + mask := riscv.DisableInterrupts() + oldValue := *ptr + success := oldValue == *expected + if success { + *ptr = desired + } else { + *expected = oldValue + } + riscv.EnableInterrupts(mask) + return success +} + +//export __atomic_fetch_add_8 +func __atomic_fetch_add_8(ptr *uint64, value uint64, ordering int32) uint64 { + mask := riscv.DisableInterrupts() + oldValue := *ptr + *ptr = oldValue + value + riscv.EnableInterrupts(mask) + return oldValue +} + +// The safest thing to do here would just be to disable interrupts for +// procPin/procUnpin. Note that a global variable is safe in this case, as any +// access to procPinnedMask will happen with interrupts disabled. + +var procPinnedMask uintptr + +//go:linkname procPin sync/atomic.runtime_procPin +func procPin() { + procPinnedMask = riscv.DisableInterrupts() +} + +//go:linkname procUnpin sync/atomic.runtime_procUnpin +func procUnpin() { + riscv.EnableInterrupts(procPinnedMask) +} diff --git a/src/runtime/runtime_k210.go b/src/runtime/runtime_k210.go index ea65890c..625cdcd0 100644 --- a/src/runtime/runtime_k210.go +++ b/src/runtime/runtime_k210.go @@ -20,15 +20,11 @@ func postinit() {} //export main func main() { - // Only use one core for the moment - if riscv.MHARTID.Get() == 0 { - // Zero the PLIC enable bits at startup. - for i := 0; i < ((kendryte.IRQ_max + 32) / 32); i++ { - kendryte.PLIC.TARGET_ENABLES[0].ENABLE[i].Set(0) // core 0 - } + // Both harts should disable all interrupts on startup. + initPLIC() - // Zero the PLIC threshold bits to allow all interrupts. - kendryte.PLIC.TARGETS[0].THRESHOLD.Set(0) + // Only use one hart for the moment. + if riscv.MHARTID.Get() == 0 { // Reset all interrupt source priorities to zero. for i := 0; i < kendryte.IRQ_max; i++ { @@ -56,14 +52,26 @@ func main() { } } +func initPLIC() { + hartId := riscv.MHARTID.Get() + + // Zero the PLIC enable bits at startup. + for i := 0; i < ((kendryte.IRQ_max + 32) / 32); i++ { + kendryte.PLIC.TARGET_ENABLES[hartId].ENABLE[i].Set(0) + } + + // Zero the PLIC threshold bits to allow all interrupts. + kendryte.PLIC.TARGETS[hartId].THRESHOLD.Set(0) +} + //go:extern handleInterruptASM var handleInterruptASM [0]uintptr //export handleInterrupt func handleInterrupt() { cause := riscv.MCAUSE.Get() - code := uint(cause &^ (1 << 31)) - if cause&(1<<31) != 0 { + code := uint64(cause &^ (1 << 63)) + if cause&(1<<63) != 0 { // Topmost bit is set, which means that it is an interrupt. switch code { case 7: // Machine timer interrupt @@ -73,12 +81,14 @@ func handleInterrupt() { // this interrupt returns. riscv.MIE.ClearBits(1 << 7) // MTIE bit case 11: // Machine external interrupt + hartId := riscv.MHARTID.Get() + // Claim this interrupt. - id := kendryte.PLIC.TARGETS[0].CLAIM.Get() + id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get() // Call the interrupt handler, if any is registered for this ID. - callInterruptHandler(int(0)) + callInterruptHandler(int(id)) // Complete this interrupt. - kendryte.PLIC.TARGETS[0].CLAIM.Set(id) + kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id) } } else { // Topmost bit is clear, so it is an exception of some sort. @@ -133,7 +143,7 @@ func sleepTicks(d timeUnit) { // handleException is called from the interrupt handler for any exception. // Exceptions can be things like illegal instructions, invalid memory // read/write, and similar issues. -func handleException(code uint) { +func handleException(code uint64) { // For a list of exception codes, see: // https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49 print("fatal error: exception with mcause=") diff --git a/src/runtime/runtime_tinygoriscv.go b/src/runtime/runtime_tinygoriscv.go index 93bf1e45..857b93a7 100644 --- a/src/runtime/runtime_tinygoriscv.go +++ b/src/runtime/runtime_tinygoriscv.go @@ -1,4 +1,4 @@ -// +build tinygo.riscv +// +build tinygo.riscv32 package runtime diff --git a/src/runtime/runtime_tinygoriscv64.go b/src/runtime/runtime_tinygoriscv64.go new file mode 100644 index 00000000..d995efce --- /dev/null +++ b/src/runtime/runtime_tinygoriscv64.go @@ -0,0 +1,38 @@ +// +build tinygo.riscv64 + +package runtime + +import "unsafe" + +//go:extern _sbss +var _sbss [0]byte + +//go:extern _ebss +var _ebss [0]byte + +//go:extern _sdata +var _sdata [0]byte + +//go:extern _sidata +var _sidata [0]byte + +//go:extern _edata +var _edata [0]byte + +func preinit() { + // Initialize .bss: zero-initialized global variables. + ptr := unsafe.Pointer(&_sbss) + for ptr != unsafe.Pointer(&_ebss) { + *(*uint64)(ptr) = 0 + ptr = unsafe.Pointer(uintptr(ptr) + 8) + } + + // Initialize .data: global variables initialized from flash. + src := unsafe.Pointer(&_sidata) + dst := unsafe.Pointer(&_sdata) + for dst != unsafe.Pointer(&_edata) { + *(*uint64)(dst) = *(*uint64)(src) + dst = unsafe.Pointer(uintptr(dst) + 8) + src = unsafe.Pointer(uintptr(src) + 8) + } +} diff --git a/src/runtime/scheduler_tinygoriscv64.S b/src/runtime/scheduler_tinygoriscv64.S new file mode 100644 index 00000000..204f964f --- /dev/null +++ b/src/runtime/scheduler_tinygoriscv64.S @@ -0,0 +1,32 @@ +.section .text.tinygo_scanCurrentStack +.global tinygo_scanCurrentStack +.type tinygo_scanCurrentStack, %function +tinygo_scanCurrentStack: + // Push callee-saved registers onto the stack. + addi sp, sp, -128 + sd ra, 120(sp) + sd s11, 112(sp) + sd s10, 104(sp) + sd s9, 96(sp) + sd s8, 88(sp) + sd s7, 80(sp) + sd s6, 72(sp) + sd s5, 64(sp) + sd s4, 56(sp) + sd s3, 48(sp) + sd s2, 40(sp) + sd s1, 32(sp) + sd s0, 24(sp) + + // Scan the stack. + mv a0, sp + call tinygo_scanstack + + // Restore return address. + ld ra, 60(sp) + + // Restore stack state. + addi sp, sp, 128 + + // Return to the caller. + ret diff --git a/targets/riscv.json b/targets/riscv.json index 313aa858..d78026b3 100644 --- a/targets/riscv.json +++ b/targets/riscv.json @@ -16,9 +16,5 @@ "ldflags": [ "--gc-sections" ], - "extra-files": [ - "src/device/riscv/start.S", - "src/runtime/scheduler_tinygoriscv.S" - ], "gdb": "riscv64-unknown-elf-gdb" } diff --git a/targets/riscv32.json b/targets/riscv32.json index dc07c209..4fd0d642 100644 --- a/targets/riscv32.json +++ b/targets/riscv32.json @@ -1,6 +1,7 @@ { "inherits": ["riscv"], "llvm-target": "riscv32--none", + "build-tags": ["tinygo.riscv32"], "cflags": [ "--target=riscv32--none", "-march=rv32imac", @@ -8,5 +9,9 @@ ], "ldflags": [ "-melf32lriscv" + ], + "extra-files": [ + "src/runtime/scheduler_tinygoriscv.S", + "src/device/riscv/start.S" ] } diff --git a/targets/riscv64.json b/targets/riscv64.json index a2a0641f..ec5cb503 100644 --- a/targets/riscv64.json +++ b/targets/riscv64.json @@ -9,5 +9,9 @@ ], "ldflags": [ "-melf64lriscv" + ], + "extra-files": [ + "src/runtime/scheduler_tinygoriscv64.S", + "src/device/riscv/start64.S" ] }