mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
b258f3424b
This can be useful for debugging critical bugs in code. I haven't added human-readable exceptions (such as "illegal instruction" or "stack overflow") yet, they can be added when they happen in practice (to avoid increasing code size too much).
170 lines
4.1 KiB
Go
170 lines
4.1 KiB
Go
// +build fe310
|
|
|
|
// This file implements target-specific things for the FE310 chip as used in the
|
|
// HiFive1.
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"machine"
|
|
"unsafe"
|
|
|
|
"device/riscv"
|
|
"device/sifive"
|
|
"runtime/volatile"
|
|
)
|
|
|
|
type timeUnit int64
|
|
|
|
//go:extern _sbss
|
|
var _sbss unsafe.Pointer
|
|
|
|
//go:extern _ebss
|
|
var _ebss unsafe.Pointer
|
|
|
|
//go:extern _sdata
|
|
var _sdata unsafe.Pointer
|
|
|
|
//go:extern _sidata
|
|
var _sidata unsafe.Pointer
|
|
|
|
//go:extern _edata
|
|
var _edata unsafe.Pointer
|
|
|
|
//go:export main
|
|
func main() {
|
|
// Zero the PLIC enable bits on startup: they are not zeroed at reset.
|
|
sifive.PLIC.ENABLE[0].Set(0)
|
|
sifive.PLIC.ENABLE[1].Set(0)
|
|
|
|
// Set the interrupt address.
|
|
// Note that this address must be aligned specially, otherwise the MODE bits
|
|
// of MTVEC won't be zero.
|
|
riscv.MTVEC.Set(uintptr(unsafe.Pointer(&handleInterruptASM)))
|
|
|
|
// Enable global interrupts now that they've been set up.
|
|
riscv.MSTATUS.SetBits(1 << 3) // MIE
|
|
|
|
preinit()
|
|
initAll()
|
|
callMain()
|
|
abort()
|
|
}
|
|
|
|
//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 {
|
|
// Topmost bit is set, which means that it is an interrupt.
|
|
switch code {
|
|
case 7: // Machine timer interrupt
|
|
// Signal timeout.
|
|
timerWakeup.Set(1)
|
|
// Disable the timer, to avoid triggering the interrupt right after
|
|
// this interrupt returns.
|
|
riscv.MIE.ClearBits(1 << 7) // MTIE bit
|
|
}
|
|
} else {
|
|
// Topmost bit is clear, so it is an exception of some sort.
|
|
// We could implement support for unsupported instructions here (such as
|
|
// misaligned loads). However, for now we'll just print a fatal error.
|
|
handleException(code)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
pric_init()
|
|
machine.UART0.Configure(machine.UARTConfig{})
|
|
}
|
|
|
|
func pric_init() {
|
|
// Make sure the HFROSC is on
|
|
sifive.PRCI.HFROSCCFG.SetBits(sifive.PRCI_HFROSCCFG_ENABLE)
|
|
|
|
// Run off 16 MHz Crystal for accuracy.
|
|
sifive.PRCI.PLLCFG.SetBits(sifive.PRCI_PLLCFG_REFSEL | sifive.PRCI_PLLCFG_BYPASS)
|
|
sifive.PRCI.PLLCFG.SetBits(sifive.PRCI_PLLCFG_SEL)
|
|
|
|
// Turn off HFROSC to save power
|
|
sifive.PRCI.HFROSCCFG.ClearBits(sifive.PRCI_HFROSCCFG_ENABLE)
|
|
|
|
// Enable the RTC.
|
|
sifive.RTC.RTCCFG.Set(sifive.RTC_RTCCFG_ENALWAYS)
|
|
}
|
|
|
|
func preinit() {
|
|
// Initialize .bss: zero-initialized global variables.
|
|
ptr := unsafe.Pointer(&_sbss)
|
|
for ptr != unsafe.Pointer(&_ebss) {
|
|
*(*uint32)(ptr) = 0
|
|
ptr = unsafe.Pointer(uintptr(ptr) + 4)
|
|
}
|
|
|
|
// Initialize .data: global variables initialized from flash.
|
|
src := unsafe.Pointer(&_sidata)
|
|
dst := unsafe.Pointer(&_sdata)
|
|
for dst != unsafe.Pointer(&_edata) {
|
|
*(*uint32)(dst) = *(*uint32)(src)
|
|
dst = unsafe.Pointer(uintptr(dst) + 4)
|
|
src = unsafe.Pointer(uintptr(src) + 4)
|
|
}
|
|
}
|
|
|
|
func putchar(c byte) {
|
|
machine.UART0.WriteByte(c)
|
|
}
|
|
|
|
const asyncScheduler = false
|
|
|
|
var timerWakeup volatile.Register8
|
|
|
|
func ticks() timeUnit {
|
|
// Combining the low bits and the high bits yields a time span of over 270
|
|
// years without counter rollover.
|
|
highBits := sifive.CLINT.MTIMEH.Get()
|
|
for {
|
|
lowBits := sifive.CLINT.MTIME.Get()
|
|
newHighBits := sifive.CLINT.MTIMEH.Get()
|
|
if newHighBits == highBits {
|
|
// High bits stayed the same.
|
|
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
|
|
}
|
|
// Retry, because there was a rollover in the low bits (happening every
|
|
// 1.5 days).
|
|
highBits = newHighBits
|
|
}
|
|
}
|
|
|
|
func sleepTicks(d timeUnit) {
|
|
target := uint64(ticks() + d)
|
|
sifive.CLINT.MTIMECMPH.Set(uint32(target >> 32))
|
|
sifive.CLINT.MTIMECMP.Set(uint32(target))
|
|
riscv.MIE.SetBits(1 << 7) // MTIE
|
|
for {
|
|
if timerWakeup.Get() != 0 {
|
|
timerWakeup.Set(0)
|
|
// Disable timer.
|
|
break
|
|
}
|
|
riscv.Asm("wfi")
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
// 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=")
|
|
print(code)
|
|
print(" pc=")
|
|
print(riscv.MEPC.Get())
|
|
println()
|
|
abort()
|
|
}
|