From 90cdbc8fb06494b41f3b11f3eb9755e1e6e19d23 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Mon, 25 Nov 2024 18:10:28 +0100 Subject: [PATCH] targets: initial implementation for Tillitis TKey device Signed-off-by: deadprogram --- GNUmakefile | 2 + src/device/tkey/crt0.S | 53 ++++++++++ src/device/tkey/tkey.go | 146 ++++++++++++++++++++++++++ src/machine/machine_tkey.go | 70 ++++++++++++ src/runtime/runtime_tkey.go | 60 +++++++++++ src/runtime/runtime_tkey_baremetal.go | 24 +++++ targets/tkey.json | 46 ++++++++ targets/tkey.ld | 78 ++++++++++++++ 8 files changed, 479 insertions(+) create mode 100644 src/device/tkey/crt0.S create mode 100644 src/device/tkey/tkey.go create mode 100644 src/machine/machine_tkey.go create mode 100644 src/runtime/runtime_tkey.go create mode 100644 src/runtime/runtime_tkey_baremetal.go create mode 100644 targets/tkey.json create mode 100644 targets/tkey.ld diff --git a/GNUmakefile b/GNUmakefile index 5f82599e79..132be1e567 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -853,6 +853,8 @@ endif @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=tkey examples/blinky1 + @$(MD5SUM) test.hex ifneq ($(WASM), 0) $(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export $(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main diff --git a/src/device/tkey/crt0.S b/src/device/tkey/crt0.S new file mode 100644 index 0000000000..94295ccba8 --- /dev/null +++ b/src/device/tkey/crt0.S @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2022 Tillitis AB +// SPDX-License-Identifier: BSD-2-Clause + + .section ".text.init" + .global _start +_start: + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10,0 + li x11,0 + li x12,0 + li x13,0 + li x14,0 + li x15,0 + li x16,0 + li x17,0 + li x18,0 + li x19,0 + li x20,0 + li x21,0 + li x22,0 + li x23,0 + li x24,0 + li x25,0 + li x26,0 + li x27,0 + li x28,0 + li x29,0 + li x30,0 + li x31,0 + + /* init stack */ + la sp, _stack_top + + /* zero-init bss section */ + la a0, _sbss + la a1, _ebss + bge a0, a1, end_init_bss + +loop_init_bss: + sw zero, 0(a0) + addi a0, a0, 4 + blt a0, a1, loop_init_bss + +end_init_bss: + call main diff --git a/src/device/tkey/tkey.go b/src/device/tkey/tkey.go new file mode 100644 index 0000000000..de46d7b030 --- /dev/null +++ b/src/device/tkey/tkey.go @@ -0,0 +1,146 @@ +//go:build tkey + +// Hand written file based on https://github.com/tillitis/tkey-libs/blob/main/include/tkey/tk1_mem.h + +package tkey + +import ( + "runtime/volatile" + "unsafe" +) + +// Peripherals +var ( + TRNG = (*TRNG_Type)(unsafe.Pointer(TK1_MMIO_TRNG_BASE)) + + TIMER = (*TIMER_Type)(unsafe.Pointer(TK1_MMIO_TIMER_BASE)) + + UDS = (*UDS_Type)(unsafe.Pointer(TK1_MMIO_UDS_BASE)) + + UART = (*UART_Type)(unsafe.Pointer(TK1_MMIO_UART_BASE)) + + TOUCH = (*TOUCH_Type)(unsafe.Pointer(TK1_MMIO_TOUCH_BASE)) + + TK1 = (*TK1_Type)(unsafe.Pointer(TK1_MMIO_TK1_BASE)) +) + +// Memory sections +const ( + TK1_ROM_BASE uintptr = 0x00000000 + + TK1_RAM_BASE uintptr = 0x40000000 + + TK1_MMIO_BASE uintptr = 0xc0000000 + + TK1_MMIO_TRNG_BASE uintptr = 0xc0000000 + + TK1_MMIO_TIMER_BASE uintptr = 0xc1000000 + + TK1_MMIO_UDS_BASE uintptr = 0xc2000000 + + TK1_MMIO_UART_BASE uintptr = 0xc3000000 + + TK1_MMIO_TOUCH_BASE uintptr = 0xc4000000 + + TK1_MMIO_FW_RAM_BASE uintptr = 0xd0000000 + + TK1_MMIO_TK1_BASE uintptr = 0xff000000 +) + +// Memory section sizes +const ( + TK1_RAM_SIZE uintptr = 0x20000 + + TK1_MMIO_SIZE uintptr = 0x3fffffff +) + +type TRNG_Type struct { + _ [36]byte + STATUS volatile.Register16 + _ [108]byte + ENTROPY volatile.Register16 +} + +type TIMER_Type struct { + _ [32]byte + CTRL volatile.Register16 + _ [2]byte + STATUS volatile.Register16 + _ [2]byte + PRESCALER volatile.Register32 + TIMER volatile.Register32 +} + +type UDS_Type struct { + DATA [8]volatile.Register16 +} + +type UART_Type struct { + _ [40]byte + BIT_RATE volatile.Register16 + _ [2]byte + DATA_BITS volatile.Register16 + _ [2]byte + STOP_BITS volatile.Register16 + _ [58]byte + RX_STATUS volatile.Register16 + _ [2]byte + RX_DATA volatile.Register16 + _ [2]byte + RX_BYTES volatile.Register16 + _ [16]byte + TX_STATUS volatile.Register16 + _ [2]byte + TX_DATA volatile.Register16 +} + +type TOUCH_Type struct { + _ [36]byte + STATUS volatile.Register16 +} + +type TK1_Type struct { + NAME0 [4]volatile.Register8 + NAME1 [4]volatile.Register8 + VERSION [4]volatile.Register8 + _ [16]byte + SWITCH_APP volatile.Register32 + _ [4]byte + LED volatile.Register32 + GPIO volatile.Register16 + APP_ADDR volatile.Register32 + APP_SIZE volatile.Register32 + BLAKE2S volatile.Register32 + _ [56]byte + CDI_FIRST [8]volatile.Register16 + _ [38]byte + UDI_FIRST [2]volatile.Register16 + _ [62]byte + RAM_ADDR_RAND volatile.Register16 + _ [2]byte + RAM_DATA_RAND volatile.Register16 + _ [126]byte + CPU_MON_CTRL volatile.Register16 + _ [2]byte + CPU_MON_FIRST volatile.Register32 + CPU_MON_LAST volatile.Register32 + _ [60]byte + SYSTEM_RESET volatile.Register16 + _ [66]byte + SPI_EN volatile.Register16 + _ [2]byte + SPI_XFER volatile.Register16 + _ [2]byte + SPI_DATA volatile.Register16 +} + +const ( + TK1_MMIO_TIMER_CTRL_START_BIT = 0 + TK1_MMIO_TIMER_CTRL_STOP_BIT = 1 + TK1_MMIO_TIMER_CTRL_START = 1 << TK1_MMIO_TIMER_CTRL_START_BIT + TK1_MMIO_TIMER_CTRL_STOP = 1 << TK1_MMIO_TIMER_CTRL_STOP_BIT + + TK1_MMIO_TK1_LED_R_BIT = 2 + TK1_MMIO_TK1_LED_G_BIT = 1 + TK1_MMIO_TK1_LED_B_BIT = 0 +) diff --git a/src/machine/machine_tkey.go b/src/machine/machine_tkey.go new file mode 100644 index 0000000000..d399ed43fe --- /dev/null +++ b/src/machine/machine_tkey.go @@ -0,0 +1,70 @@ +//go:build tkey + +package machine + +import ( + "device/tkey" +) + +const deviceName = "TKey" + +const ( + PinOutput PinMode = iota +) + +var ( + LED_BLUE = Pin(tkey.TK1_MMIO_TK1_LED_B_BIT) + LED_GREEN = Pin(tkey.TK1_MMIO_TK1_LED_G_BIT) + LED_RED = Pin(tkey.TK1_MMIO_TK1_LED_R_BIT) + + LED = LED_GREEN +) + +// No config needed for TKey, just to match the Pin interface. +func (p Pin) Configure(config PinConfig) { +} + +// Set GPIO pin to high or low. +func (p Pin) Set(high bool) { + switch p { + case LED_BLUE, LED_GREEN, LED_RED: + if high { + tkey.TK1.LED.SetBits(1 << uint(p)) + } else { + tkey.TK1.LED.ClearBits(1 << uint(p)) + } + } +} + +type UART struct { + Bus *tkey.UART_Type + Buffer *RingBuffer +} + +var ( + UART0 = &_UART0 + _UART0 = UART{Bus: tkey.UART, Buffer: NewRingBuffer()} +) + +func (uart *UART) Configure(config UARTConfig) { + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + + uart.SetBaudRate(config.BaudRate) +} + +func (uart *UART) SetBaudRate(br uint32) { + uart.Bus.BIT_RATE.Set(uint16(18e6 / br)) +} + +func (uart *UART) writeByte(c byte) error { + for uart.Bus.TX_STATUS.Get() == 0 { + } + + uart.Bus.TX_DATA.Set(uint16(c)) + + return nil +} + +func (uart *UART) flush() {} diff --git a/src/runtime/runtime_tkey.go b/src/runtime/runtime_tkey.go new file mode 100644 index 0000000000..41eb9473e1 --- /dev/null +++ b/src/runtime/runtime_tkey.go @@ -0,0 +1,60 @@ +//go:build tkey + +// This file implements target-specific things for the TKey. + +package runtime + +import ( + "machine" + "runtime/volatile" +) + +type timeUnit int64 + +//export main +func main() { + preinit() + initPeripherals() + run() + exit(0) +} + +// initPeripherals configures peripherals the way the runtime expects them. +func initPeripherals() { + machine.InitSerial() +} + +func putchar(c byte) { + machine.Serial.WriteByte(c) +} + +func getchar() byte { + for machine.Serial.Buffered() == 0 { + Gosched() + } + v, _ := machine.Serial.ReadByte() + return v +} + +func buffered() int { + return machine.Serial.Buffered() +} + +var timestamp volatile.Register32 + +// ticks returns the current value of the timer in ticks. +func ticks() timeUnit { + return timeUnit(timestamp.Get()) +} + +// sleepTicks sleeps for at least the duration d. +func sleepTicks(d timeUnit) { + target := uint64(ticks() + d) + + for { + if uint64(ticks()) >= target { + break + } + timestamp.Set(timestamp.Get() + 1) + } +} diff --git a/src/runtime/runtime_tkey_baremetal.go b/src/runtime/runtime_tkey_baremetal.go new file mode 100644 index 0000000000..c232fcf619 --- /dev/null +++ b/src/runtime/runtime_tkey_baremetal.go @@ -0,0 +1,24 @@ +//go:build tkey && !qemu + +package runtime + +// ticksToNanoseconds converts ticks (at 18MHz) to nanoseconds. +func ticksToNanoseconds(ticks timeUnit) int64 { + return int64(ticks) * 1800 +} + +// nanosecondsToTicks converts nanoseconds to ticks (at 18MHz). +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns / 1800) +} + +func exit(code int) { + abort() +} + +func abort() { + // lock up forever + for { + // TODO: something here? + } +} diff --git a/targets/tkey.json b/targets/tkey.json new file mode 100644 index 0000000000..814e103ce4 --- /dev/null +++ b/targets/tkey.json @@ -0,0 +1,46 @@ +{ + "goos": "linux", + "goarch": "arm", + "build-tags": [ + "tkey", + "tinygo.riscv32", + "tinygo.riscv", + "baremetal", + "linux", + "arm" + ], + "llvm-target": "riscv32-unknown-none", + "cpu": "generic-rv32", + "target-abi": "ilp32", + "features": "+c,+zmmul", + "cflags": [ + "-mcmodel=medany", + "-march=rv32iczmmul", + "-Werror", + "-mno-relax", + "-fno-exceptions", + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + "-ffunction-sections", + "-fdata-sections" + ], + "ldflags": [ + "-melf32lriscv", + "--gc-sections" + ], + "linkerscript": "targets/tkey.ld", + "gdb": [ + "gdb-multiarch" + ], + "scheduler": "none", + "default-stack-size": 2048, + "gc": "leaking", + "linker": "ld.lld", + "rtlib": "compiler-rt", + "libc": "picolibc", + "extra-files": [ + "src/runtime/asm_riscv.S", + "src/device/tkey/crt0.S" + ], + "flash-command": "tkey-runapp {bin}" +} diff --git a/targets/tkey.ld b/targets/tkey.ld new file mode 100644 index 0000000000..aae511e120 --- /dev/null +++ b/targets/tkey.ld @@ -0,0 +1,78 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */ +} + +_stack_size = 2K; + +SECTIONS +{ + .text.init : + { + *(.text.init) + } >RAM + + .text : + { + . = ALIGN(4); + KEEP(*(.init)) + . = ALIGN(4); + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + . = ALIGN(4); + } >RAM + + /* Put the stack at the bottom of RAM, so that the application will + * crash on stack overflow instead of silently corrupting memory. + * See: http://blog.japaric.io/stack-overflow-protection/ */ + .stack (NOLOAD) : + { + . = ALIGN(16); + . += _stack_size; + _stack_top = .; + } >RAM + + /* Start address of .data, used by startup code. */ + _sidata = LOADADDR(.data); + + /* Globals with initial value */ + .data : + { + . = ALIGN(4); + /* see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register */ + PROVIDE( __global_pointer$ = . + (4K / 2) ); + _sdata = .; /* used by startup code */ + *(.sdata) + *(.data .data.*) + . = ALIGN(4); + _edata = .; /* used by startup code */ + } >RAM + + /* Zero-initialized globals */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* used by startup code */ + *(.sbss) + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* used by startup code */ + } >RAM + + /DISCARD/ : + { + *(.eh_frame) /* causes 'no memory region specified' error in lld */ + } +} + +/* For the memory allocator. */ +_heap_start = ALIGN(_ebss, 16); +_heap_end = ORIGIN(RAM) + LENGTH(RAM); +_globals_start = _sdata; +_globals_end = _ebss;