Skip to content

Commit

Permalink
targets: add implementation for Tillitis TKey device (#4631)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
deadprogram authored Dec 14, 2024
1 parent f246599 commit 17302ca
Show file tree
Hide file tree
Showing 12 changed files with 550 additions and 3 deletions.
2 changes: 2 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func TestClangAttributes(t *testing.T) {
"k210",
"nintendoswitch",
"riscv-qemu",
"tkey",
"wasip1",
"wasip2",
"wasm",
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/rand/rand_baremetal.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
139 changes: 139 additions & 0 deletions src/device/tkey/tkey.go
Original file line number Diff line number Diff line change
@@ -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
)
234 changes: 234 additions & 0 deletions src/machine/machine_tkey.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 17302ca

Please sign in to comment.