maixbit (uart): serial is working with echo example
Этот коммит содержится в:
родитель
75bcbbe6d8
коммит
dfab1aa717
11 изменённых файлов: 263 добавлений и 27 удалений
60
src/device/riscv/start64.S
Обычный файл
60
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
|
|
@ -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 {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build tinygo.riscv
|
||||
// +build tinygo.riscv32
|
||||
|
||||
package runtime
|
||||
|
||||
|
|
92
src/runtime/arch_tinygoriscv64.go
Обычный файл
92
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)
|
||||
}
|
|
@ -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=")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build tinygo.riscv
|
||||
// +build tinygo.riscv32
|
||||
|
||||
package runtime
|
||||
|
||||
|
|
38
src/runtime/runtime_tinygoriscv64.go
Обычный файл
38
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)
|
||||
}
|
||||
}
|
32
src/runtime/scheduler_tinygoriscv64.S
Обычный файл
32
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
|
|
@ -16,9 +16,5 @@
|
|||
"ldflags": [
|
||||
"--gc-sections"
|
||||
],
|
||||
"extra-files": [
|
||||
"src/device/riscv/start.S",
|
||||
"src/runtime/scheduler_tinygoriscv.S"
|
||||
],
|
||||
"gdb": "riscv64-unknown-elf-gdb"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -9,5 +9,9 @@
|
|||
],
|
||||
"ldflags": [
|
||||
"-melf64lriscv"
|
||||
],
|
||||
"extra-files": [
|
||||
"src/runtime/scheduler_tinygoriscv64.S",
|
||||
"src/device/riscv/start64.S"
|
||||
]
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче