aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRon Evans <[email protected]>2024-12-14 14:26:03 +0100
committerGitHub <[email protected]>2024-12-14 14:26:03 +0100
commit17302ca762d7f8948dcb21cc675070eea445271f (patch)
treef357f9e1d3181beb4234d12f8f51c8d8f8c89d1e /src
parentf2465992533adcf9f98dfea359aba9171a80902f (diff)
downloadtinygo-17302ca762d7f8948dcb21cc675070eea445271f.tar.gz
tinygo-17302ca762d7f8948dcb21cc675070eea445271f.zip
targets: add implementation for Tillitis TKey device (#4631)
* initial implementation for Tillitis TKey device * add UART implementation for TKey * add Pin interface implementation for TKey touch sensor * add RNG interface implementation for TKey * add helpful machine package functions to return identifiers such as name and version for TKey * use built-in timer for sleep timing on TKey * modify UART implementation for TKey to implement Serialer interface * implement BLAKE2s ROM function call for TKey device * handle abort by triggering TKey device fault using illegal instruction to halt CPU * simplify TKey implementation by inheriting from existing riscv32 target * return error for trying to configure invalid baudrates on UART * add tkey to builder test * be very specific for features passed to LLVM for specific config in use for TKey * handle feedback items from TKey device code review Signed-off-by: deadprogram <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/crypto/rand/rand_baremetal.go2
-rw-r--r--src/device/tkey/tkey.go139
-rw-r--r--src/machine/machine_tkey.go234
-rw-r--r--src/machine/machine_tkey_rom.go59
-rw-r--r--src/runtime/rand_hwrng.go2
-rw-r--r--src/runtime/rand_norng.go2
-rw-r--r--src/runtime/runtime_tkey.go64
-rw-r--r--src/runtime/runtime_tkey_baremetal.go24
8 files changed, 523 insertions, 3 deletions
diff --git a/src/crypto/rand/rand_baremetal.go b/src/crypto/rand/rand_baremetal.go
index 15fc916ca..30b2c3b23 100644
--- a/src/crypto/rand/rand_baremetal.go
+++ b/src/crypto/rand/rand_baremetal.go
@@ -1,4 +1,4 @@
-//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3
+//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey
// If you update the above build constraint, you'll probably also need to update
// src/runtime/rand_hwrng.go.
diff --git a/src/device/tkey/tkey.go b/src/device/tkey/tkey.go
new file mode 100644
index 000000000..89a370414
--- /dev/null
+++ b/src/device/tkey/tkey.go
@@ -0,0 +1,139 @@
+//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.Register32
+ _ [88]byte
+ ENTROPY volatile.Register32
+}
+
+type TIMER_Type struct {
+ _ [32]byte
+ CTRL volatile.Register32
+ STATUS volatile.Register32
+ PRESCALER volatile.Register32
+ TIMER volatile.Register32
+}
+
+type UDS_Type struct {
+ _ [64]byte
+ DATA [8]volatile.Register32
+}
+
+type UART_Type struct {
+ _ [128]byte
+ RX_STATUS volatile.Register32
+ RX_DATA volatile.Register32
+ RX_BYTES volatile.Register32
+ _ [116]byte
+ TX_STATUS volatile.Register32
+ TX_DATA volatile.Register32
+}
+
+type TOUCH_Type struct {
+ _ [36]byte
+ STATUS volatile.Register32
+}
+
+type TK1_Type struct {
+ NAME0 volatile.Register32
+ NAME1 volatile.Register32
+ VERSION volatile.Register32
+ _ [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
+ _ [72]byte
+ CDI_FIRST [8]volatile.Register32
+ _ [32]byte
+ UDI_FIRST [2]volatile.Register32
+ _ [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.Register32
+ SPI_XFER volatile.Register32
+ SPI_DATA volatile.Register32
+}
+
+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
+
+ TK1_MMIO_TK1_GPIO1_BIT = 0
+ TK1_MMIO_TK1_GPIO2_BIT = 1
+ TK1_MMIO_TK1_GPIO3_BIT = 2
+ TK1_MMIO_TK1_GPIO4_BIT = 3
+)
diff --git a/src/machine/machine_tkey.go b/src/machine/machine_tkey.go
new file mode 100644
index 000000000..78863d845
--- /dev/null
+++ b/src/machine/machine_tkey.go
@@ -0,0 +1,234 @@
+//go:build tkey
+
+package machine
+
+import (
+ "device/tkey"
+ "errors"
+ "strconv"
+)
+
+const deviceName = "TKey"
+
+// GPIO pins modes are only here to match the Pin interface.
+// The actual configuration is fixed in the hardware.
+const (
+ PinOutput PinMode = iota
+ PinInput
+ PinInputPullup
+ PinInputPulldown
+)
+
+const (
+ 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
+
+ TKEY_TOUCH = Pin(3) // 3 is unused, but we need a value here to match the Pin interface.
+ BUTTON = TKEY_TOUCH
+
+ GPIO1 = Pin(tkey.TK1_MMIO_TK1_GPIO1_BIT + 8)
+ GPIO2 = Pin(tkey.TK1_MMIO_TK1_GPIO2_BIT + 8)
+ GPIO3 = Pin(tkey.TK1_MMIO_TK1_GPIO3_BIT + 8)
+ GPIO4 = Pin(tkey.TK1_MMIO_TK1_GPIO4_BIT + 8)
+)
+
+var touchConfig, gpio1Config, gpio2Config PinConfig
+
+// No config needed for TKey, just to match the Pin interface.
+func (p Pin) Configure(config PinConfig) {
+ switch p {
+ case BUTTON:
+ touchConfig = config
+
+ // Clear any pending touch events.
+ tkey.TOUCH.STATUS.Set(0)
+ case GPIO1:
+ gpio1Config = config
+ case GPIO2:
+ gpio2Config = config
+ }
+}
+
+// Set 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))
+ }
+ case GPIO3, GPIO4:
+ if high {
+ tkey.TK1.GPIO.SetBits(1 << uint(p-8))
+ } else {
+ tkey.TK1.GPIO.ClearBits(1 << uint(p-8))
+ }
+ }
+}
+
+// Get returns the current value of a pin.
+func (p Pin) Get() bool {
+ pushed := false
+ mode := PinInput
+
+ switch p {
+ case BUTTON:
+ mode = touchConfig.Mode
+ if tkey.TOUCH.STATUS.HasBits(1) {
+ tkey.TOUCH.STATUS.Set(0)
+ pushed = true
+ }
+ case GPIO1:
+ mode = gpio1Config.Mode
+ pushed = tkey.TK1.GPIO.HasBits(1 << uint(p-8))
+ case GPIO2:
+ mode = gpio2Config.Mode
+ pushed = tkey.TK1.GPIO.HasBits(1 << uint(p-8))
+ case GPIO3, GPIO4:
+ mode = PinOutput
+ pushed = tkey.TK1.GPIO.HasBits(1 << uint(p-8))
+ case LED_BLUE, LED_GREEN, LED_RED:
+ mode = PinOutput
+ pushed = tkey.TK1.LED.HasBits(1 << uint(p))
+ }
+
+ switch mode {
+ case PinInputPullup:
+ return !pushed
+ case PinInput, PinInputPulldown, PinOutput:
+ return pushed
+ }
+
+ return false
+}
+
+type UART struct {
+ Bus *tkey.UART_Type
+}
+
+var (
+ DefaultUART = UART0
+ UART0 = &_UART0
+ _UART0 = UART{Bus: tkey.UART}
+)
+
+// The TKey UART is fixed at 62500 baud, 8N1.
+func (uart *UART) Configure(config UARTConfig) error {
+ if !(config.BaudRate == 62500 || config.BaudRate == 0) {
+ return errors.New("uart: only 62500 baud rate is supported")
+ }
+
+ return nil
+}
+
+// Write a slice of data bytes to the UART.
+func (uart *UART) Write(data []byte) (n int, err error) {
+ for _, c := range data {
+ if err := uart.WriteByte(c); err != nil {
+ return n, err
+ }
+ }
+ return len(data), nil
+}
+
+// WriteByte writes a byte of data to the UART.
+func (uart *UART) WriteByte(c byte) error {
+ for uart.Bus.TX_STATUS.Get() == 0 {
+ }
+
+ uart.Bus.TX_DATA.Set(uint32(c))
+
+ return nil
+}
+
+// Buffered returns the number of bytes buffered in the UART.
+func (uart *UART) Buffered() int {
+ return int(uart.Bus.RX_BYTES.Get())
+}
+
+// ReadByte reads a byte of data from the UART.
+func (uart *UART) ReadByte() (byte, error) {
+ for uart.Bus.RX_STATUS.Get() == 0 {
+ }
+
+ return byte(uart.Bus.RX_DATA.Get()), nil
+}
+
+// DTR is not available on the TKey.
+func (uart *UART) DTR() bool {
+ return false
+}
+
+// RTS is not available on the TKey.
+func (uart *UART) RTS() bool {
+ return false
+}
+
+// GetRNG returns 32 bits of cryptographically secure random data
+func GetRNG() (uint32, error) {
+ for tkey.TRNG.STATUS.Get() == 0 {
+ }
+
+ return uint32(tkey.TRNG.ENTROPY.Get()), nil
+}
+
+// DesignName returns the FPGA design name.
+func DesignName() (string, string) {
+ n0 := tkey.TK1.NAME0.Get()
+ name0 := string([]byte{byte(n0 >> 24), byte(n0 >> 16), byte(n0 >> 8), byte(n0)})
+ n1 := tkey.TK1.NAME1.Get()
+ name1 := string([]byte{byte(n1 >> 24), byte(n1 >> 16), byte(n1 >> 8), byte(n1)})
+
+ return name0, name1
+}
+
+// DesignVersion returns the FPGA design version.
+func DesignVersion() string {
+ version := tkey.TK1.VERSION.Get()
+
+ return strconv.Itoa(int(version))
+}
+
+// CDI returns 8 words of Compound Device Identifier (CDI) generated and written by the firmware when the application is loaded.
+func CDI() []byte {
+ cdi := make([]byte, 32)
+ for i := 0; i < 8; i++ {
+ c := tkey.TK1.CDI_FIRST[i].Get()
+ cdi[i*4] = byte(c >> 24)
+ cdi[i*4+1] = byte(c >> 16)
+ cdi[i*4+2] = byte(c >> 8)
+ cdi[i*4+3] = byte(c)
+ }
+ return cdi
+}
+
+// UDI returns 2 words of Unique Device Identifier (UDI). Only available in firmware mode.
+func UDI() []byte {
+ udi := make([]byte, 8)
+ for i := 0; i < 2; i++ {
+ c := tkey.TK1.UDI_FIRST[i].Get()
+ udi[i*4] = byte(c >> 24)
+ udi[i*4+1] = byte(c >> 16)
+ udi[i*4+2] = byte(c >> 8)
+ udi[i*4+3] = byte(c)
+ }
+ return udi
+}
+
+// UDS returns 8 words of Unique Device Secret. Part of the FPGA design, changed when provisioning a TKey.
+// Only available in firmware mode. UDS is only readable once per power cycle.
+func UDS() []byte {
+ uds := make([]byte, 32)
+ for i := 0; i < 8; i++ {
+ c := tkey.UDS.DATA[i].Get()
+ uds[i*4] = byte(c >> 24)
+ uds[i*4+1] = byte(c >> 16)
+ uds[i*4+2] = byte(c >> 8)
+ uds[i*4+3] = byte(c)
+ }
+ return uds
+}
diff --git a/src/machine/machine_tkey_rom.go b/src/machine/machine_tkey_rom.go
new file mode 100644
index 000000000..bc7162e04
--- /dev/null
+++ b/src/machine/machine_tkey_rom.go
@@ -0,0 +1,59 @@
+//go:build tkey
+
+package machine
+
+/*
+ #define TK1_MMIO_TK1_BLAKE2S 0xff000040
+
+ typedef unsigned char uint8_t;
+ typedef unsigned long uint32_t;
+ typedef unsigned long size_t;
+
+ // blake2s state context
+ typedef struct {
+ uint8_t b[64]; // input buffer
+ uint32_t h[8]; // chained state
+ uint32_t t[2]; // total number of bytes
+ size_t c; // pointer for b[]
+ size_t outlen; // digest size
+ } blake2s_ctx;
+
+ typedef int (*fw_blake2s_p)(void *out, unsigned long outlen, const void *key,
+ unsigned long keylen, const void *in,
+ unsigned long inlen, blake2s_ctx *ctx);
+
+ int blake2s(void *out, unsigned long outlen, const void *key, unsigned long keylen, const void *in, unsigned long inlen)
+ {
+ fw_blake2s_p const fw_blake2s =
+ (fw_blake2s_p) * (volatile uint32_t *)TK1_MMIO_TK1_BLAKE2S;
+ blake2s_ctx ctx;
+
+ return fw_blake2s(out, outlen, key, keylen, in, inlen, &ctx);
+ }
+*/
+import "C"
+import (
+ "errors"
+ "unsafe"
+)
+
+var (
+ ErrBLAKE2sInvalid = errors.New("invalid params for call to BLAKE2s")
+ ErrBLAKE2sFailed = errors.New("call to BLAKE2s failed")
+)
+
+func BLAKE2s(output []byte, key []byte, input []byte) error {
+ if len(output) == 0 || len(input) == 0 {
+ return ErrBLAKE2sInvalid
+ }
+
+ op := unsafe.Pointer(&output[0])
+ kp := unsafe.Pointer(&key[0])
+ ip := unsafe.Pointer(&input[0])
+
+ if res := C.blake2s(op, C.size_t(len(output)), kp, C.size_t(len(key)), ip, C.size_t(len(input))); res != 0 {
+ return ErrBLAKE2sFailed
+ }
+
+ return nil
+}
diff --git a/src/runtime/rand_hwrng.go b/src/runtime/rand_hwrng.go
index 7154ffd79..2c690b449 100644
--- a/src/runtime/rand_hwrng.go
+++ b/src/runtime/rand_hwrng.go
@@ -1,4 +1,4 @@
-//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3)
+//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey)
// If you update the above build constraint, you'll probably also need to update
// src/crypto/rand/rand_baremetal.go.
diff --git a/src/runtime/rand_norng.go b/src/runtime/rand_norng.go
index e6045008e..a86cdc542 100644
--- a/src/runtime/rand_norng.go
+++ b/src/runtime/rand_norng.go
@@ -1,4 +1,4 @@
-//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3)
+//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey)
package runtime
diff --git a/src/runtime/runtime_tkey.go b/src/runtime/runtime_tkey.go
new file mode 100644
index 000000000..ba8c5e944
--- /dev/null
+++ b/src/runtime/runtime_tkey.go
@@ -0,0 +1,64 @@
+//go:build tkey
+
+// This file implements target-specific things for the TKey.
+
+package runtime
+
+import (
+ "device/tkey"
+ "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() {
+ // prescaler value that results in 0.00001-second timer-ticks.
+ // given an 18 MHz processor, a millisecond is about 18,000 cycles.
+ tkey.TIMER.PRESCALER.Set(18 * machine.MHz / 100000)
+
+ 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 := uint32(ticks() + d)
+
+ tkey.TIMER.TIMER.Set(uint32(d))
+ tkey.TIMER.CTRL.SetBits(tkey.TK1_MMIO_TIMER_CTRL_START)
+ for tkey.TIMER.STATUS.Get() != 0 {
+ }
+ timestamp.Set(target)
+}
diff --git a/src/runtime/runtime_tkey_baremetal.go b/src/runtime/runtime_tkey_baremetal.go
new file mode 100644
index 000000000..a83bd4408
--- /dev/null
+++ b/src/runtime/runtime_tkey_baremetal.go
@@ -0,0 +1,24 @@
+//go:build tkey && !qemu
+
+package runtime
+
+import "device/riscv"
+
+// ticksToNanoseconds converts ticks (at 18MHz) to 10 µs.
+func ticksToNanoseconds(ticks timeUnit) int64 {
+ return int64(ticks) * 10000
+}
+
+// nanosecondsToTicks converts 10 µs to ticks (at 18MHz).
+func nanosecondsToTicks(ns int64) timeUnit {
+ return timeUnit(ns / 10000)
+}
+
+func exit(code int) {
+ abort()
+}
+
+func abort() {
+ // Force illegal instruction to halt CPU
+ riscv.Asm("unimp")
+}