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) {
|
func (uart UART) Configure(config UARTConfig) {
|
||||||
|
|
||||||
div := CPUFrequency()/115200 - 1
|
div := CPUFrequency()/115200 - 1
|
||||||
|
|
||||||
uart.Bus.DIV.Set(div)
|
uart.Bus.DIV.Set(div)
|
||||||
uart.Bus.TXCTRL.Set(kendryte.UARTHS_TXCTRL_TXEN)
|
uart.Bus.TXCTRL.Set(kendryte.UARTHS_TXCTRL_TXEN)
|
||||||
uart.Bus.RXCTRL.Set(kendryte.UARTHS_RXCTRL_RXEN)
|
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.SetPriority(5)
|
||||||
intr.Enable()*/
|
intr.Enable()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart UART) handleInterrupt(interrupt.Interrupt) {
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||||
rxdata := uart.Bus.RXDATA.Get()
|
rxdata := uart.Bus.RXDATA.Get()
|
||||||
c := byte(rxdata)
|
c := byte(rxdata)
|
||||||
if uint32(c) != rxdata {
|
if uint32(c) != rxdata {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build tinygo.riscv
|
// +build tinygo.riscv32
|
||||||
|
|
||||||
package runtime
|
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
|
//export main
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Only use one core for the moment
|
// Both harts should disable all interrupts on startup.
|
||||||
if riscv.MHARTID.Get() == 0 {
|
initPLIC()
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero the PLIC threshold bits to allow all interrupts.
|
// Only use one hart for the moment.
|
||||||
kendryte.PLIC.TARGETS[0].THRESHOLD.Set(0)
|
if riscv.MHARTID.Get() == 0 {
|
||||||
|
|
||||||
// Reset all interrupt source priorities to zero.
|
// Reset all interrupt source priorities to zero.
|
||||||
for i := 0; i < kendryte.IRQ_max; i++ {
|
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
|
//go:extern handleInterruptASM
|
||||||
var handleInterruptASM [0]uintptr
|
var handleInterruptASM [0]uintptr
|
||||||
|
|
||||||
//export handleInterrupt
|
//export handleInterrupt
|
||||||
func handleInterrupt() {
|
func handleInterrupt() {
|
||||||
cause := riscv.MCAUSE.Get()
|
cause := riscv.MCAUSE.Get()
|
||||||
code := uint(cause &^ (1 << 31))
|
code := uint64(cause &^ (1 << 63))
|
||||||
if cause&(1<<31) != 0 {
|
if cause&(1<<63) != 0 {
|
||||||
// Topmost bit is set, which means that it is an interrupt.
|
// Topmost bit is set, which means that it is an interrupt.
|
||||||
switch code {
|
switch code {
|
||||||
case 7: // Machine timer interrupt
|
case 7: // Machine timer interrupt
|
||||||
|
@ -73,12 +81,14 @@ func handleInterrupt() {
|
||||||
// this interrupt returns.
|
// this interrupt returns.
|
||||||
riscv.MIE.ClearBits(1 << 7) // MTIE bit
|
riscv.MIE.ClearBits(1 << 7) // MTIE bit
|
||||||
case 11: // Machine external interrupt
|
case 11: // Machine external interrupt
|
||||||
|
hartId := riscv.MHARTID.Get()
|
||||||
|
|
||||||
// Claim this interrupt.
|
// 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.
|
// Call the interrupt handler, if any is registered for this ID.
|
||||||
callInterruptHandler(int(0))
|
callInterruptHandler(int(id))
|
||||||
// Complete this interrupt.
|
// Complete this interrupt.
|
||||||
kendryte.PLIC.TARGETS[0].CLAIM.Set(id)
|
kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Topmost bit is clear, so it is an exception of some sort.
|
// 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.
|
// handleException is called from the interrupt handler for any exception.
|
||||||
// Exceptions can be things like illegal instructions, invalid memory
|
// Exceptions can be things like illegal instructions, invalid memory
|
||||||
// read/write, and similar issues.
|
// read/write, and similar issues.
|
||||||
func handleException(code uint) {
|
func handleException(code uint64) {
|
||||||
// For a list of exception codes, see:
|
// For a list of exception codes, see:
|
||||||
// https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49
|
// https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49
|
||||||
print("fatal error: exception with mcause=")
|
print("fatal error: exception with mcause=")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build tinygo.riscv
|
// +build tinygo.riscv32
|
||||||
|
|
||||||
package runtime
|
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": [
|
"ldflags": [
|
||||||
"--gc-sections"
|
"--gc-sections"
|
||||||
],
|
],
|
||||||
"extra-files": [
|
|
||||||
"src/device/riscv/start.S",
|
|
||||||
"src/runtime/scheduler_tinygoriscv.S"
|
|
||||||
],
|
|
||||||
"gdb": "riscv64-unknown-elf-gdb"
|
"gdb": "riscv64-unknown-elf-gdb"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"inherits": ["riscv"],
|
"inherits": ["riscv"],
|
||||||
"llvm-target": "riscv32--none",
|
"llvm-target": "riscv32--none",
|
||||||
|
"build-tags": ["tinygo.riscv32"],
|
||||||
"cflags": [
|
"cflags": [
|
||||||
"--target=riscv32--none",
|
"--target=riscv32--none",
|
||||||
"-march=rv32imac",
|
"-march=rv32imac",
|
||||||
|
@ -8,5 +9,9 @@
|
||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
"-melf32lriscv"
|
"-melf32lriscv"
|
||||||
|
],
|
||||||
|
"extra-files": [
|
||||||
|
"src/runtime/scheduler_tinygoriscv.S",
|
||||||
|
"src/device/riscv/start.S"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,9 @@
|
||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
"-melf64lriscv"
|
"-melf64lriscv"
|
||||||
|
],
|
||||||
|
"extra-files": [
|
||||||
|
"src/runtime/scheduler_tinygoriscv64.S",
|
||||||
|
"src/device/riscv/start64.S"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче