testfw/testapp: Break out tests running in app mode into separate app

App mode can no longer be controlled from software. So the tests have to
run from firmware RAM.
This commit is contained in:
Mikael Ågren 2025-01-31 13:41:11 +01:00
parent d82c3a706e
commit fed9354fe9
No known key found for this signature in database
GPG Key ID: E02DA3D397792C46
5 changed files with 445 additions and 44 deletions

View File

@ -0,0 +1,70 @@
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
OBJCOPY ?= llvm-objcopy
CC = clang
CFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mcmodel=medany \
-static \
-std=gnu99 \
-O2 \
-ffast-math \
-fno-common \
-fno-builtin-printf \
-fno-builtin-putchar \
-fno-builtin-memcpy \
-nostdlib \
-mno-relax \
-Wall \
-Wpedantic \
-Wno-language-extension-token \
-Werror \
-flto \
-g
AS = clang
ASFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mno-relax
LDFLAGS=-T $(P)/app.lds
.PHONY: all
all: testapp.bin
# Turn elf into bin for device
%.bin: %.elf
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
chmod a-x $@
TESTAPP_FMTFILES = \
$(P)/main.c \
TESTAPP_OBJS = \
$(P)/main.o \
$(P)/crt0.o \
$(P)/syscall.o \
$(P)/../tk1/assert.o \
$(P)/../tk1/led.o \
$(P)/../tk1/lib.o \
$(P)/../tk1/proto.o
testapp.elf: $(TESTAPP_OBJS)
$(CC) $(CFLAGS) $(TESTAPP_OBJS) $(LDFLAGS) -o $@
.PHONY: fmt
fmt:
clang-format --dry-run --ferror-limit=0 $(TESTAPP_FMTFILES)
clang-format --verbose -i $(TESTAPP_FMTFILES)
.PHONY: checkfmt
checkfmt:
clang-format --dry-run --ferror-limit=0 --Werror $(TESTAPP_FMTFILES)
.PHONY: clean
clean:
rm -f testapp.bin testapp.elf $(TESTAPP_OBJS)

View File

@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
* SPDX-License-Identifier: BSD-2-Clause
*/
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
MEMORY
{
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}
SECTIONS
{
.text.init :
{
*(.text.init)
} >RAM
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.srodata) /* .rodata sections (constants, strings, etc.) */
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} >RAM
.data : AT (_etext)
{
. = ALIGN(4);
_sdata = .;
. = ALIGN(4);
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.sdata) /* .sdata sections */
*(.sdata*) /* .sdata* sections */
. = ALIGN(4);
_edata = .;
} >RAM
/* Uninitialized data section */
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
/* libcrt0/crt0.S inits stack to start just below end of RAM */
}

View File

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
// 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 below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
li sp, 0x4001fff0
/* 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

View File

@ -0,0 +1,258 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../tk1/led.h"
#include "../tk1/lib.h"
#include "../tk1/proto.h"
#include "../tk1/types.h"
#include "../tk1_mem.h"
#define USBMODE_PACKET_SIZE 64
// clang-format off
volatile uint32_t *tk1name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0;
volatile uint32_t *tk1name1 = (volatile uint32_t *)TK1_MMIO_TK1_NAME1;
volatile uint32_t *uds = (volatile uint32_t *)TK1_MMIO_UDS_FIRST;
volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
volatile uint8_t *fw_ram = (volatile uint8_t *)TK1_MMIO_FW_RAM_BASE;
volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
// clang-format on
#define UDS_WORDS 8
#define UDI_WORDS 2
#define CDI_WORDS 8
static void write_with_header(const uint8_t *buf, size_t nbytes, enum mode mode)
{
// Append USB Mode Protocol header:
// 1 byte mode
// 1 byte length
writebyte(mode);
writebyte(nbytes);
for (int i = 0; i < nbytes; i++) {
writebyte(buf[i]);
}
}
static void write(const uint8_t *buf, size_t nbytes)
{
uint8_t len;
while (nbytes > 0) {
// We split the data into chunks that will fit in the
// USB Mode Protocol with some spare change.
len =
nbytes < USBMODE_PACKET_SIZE ? nbytes : USBMODE_PACKET_SIZE;
write_with_header((const uint8_t *)buf, len, MODE_CDC);
buf += len;
nbytes -= len;
}
}
unsigned strlen(const char *str)
{
const char *s;
for (s = str; *s; ++s)
;
return (s - str);
}
void puts(char *buf)
{
size_t nbytes = strlen(buf);
write((const uint8_t *)buf, nbytes);
}
void hex(uint8_t buf[2], const uint8_t c)
{
unsigned int upper = (c >> 4) & 0xf;
unsigned int lower = c & 0xf;
buf[0] = upper < 10 ? '0' + upper : 'a' - 10 + upper;
buf[1] = lower < 10 ? '0' + lower : 'a' - 10 + lower;
}
void puthex(uint8_t c)
{
uint8_t buf[2];
hex(buf, c);
write(buf, 2);
}
void puthexn(uint8_t *p, int n)
{
for (int i = 0; i < n; i++) {
puthex(p[i]);
}
}
void reverseword(uint32_t *wordp)
{
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
((*wordp & 0x0000ff00) << 8) | ((*wordp & 0x000000ff) << 24);
}
uint32_t wait_timer_tick(uint32_t last_timer)
{
uint32_t newtimer;
for (;;) {
newtimer = *timer;
if (newtimer != last_timer) {
return newtimer;
}
}
}
void failmsg(char *s)
{
puts("FAIL: ");
puts(s);
puts("\r\n");
}
int main(void)
{
uint8_t in = 0;
uint8_t mode = 0;
uint8_t mode_bytes_left = 0;
set_led(LED_BLUE);
// Wait for terminal program and a character to be typed
in = readbyte(&mode, &mode_bytes_left);
puts("\r\nI'm testapp on:");
// Output the TK1 core's NAME0 and NAME1
uint32_t name;
wordcpy_s(&name, 1, (void *)tk1name0, 1);
reverseword(&name);
write((const uint8_t *)&name, 4);
puts(" ");
wordcpy_s(&name, 1, (void *)tk1name1, 1);
reverseword(&name);
write((const uint8_t *)&name, 4);
puts("\r\n");
uint32_t zeros[8];
memset(zeros, 0, 8 * 4);
int anyfailed = 0;
uint32_t uds_local[UDS_WORDS];
uint32_t udi_local[UDI_WORDS];
// Should NOT be able to read from UDS in app-mode.
wordcpy_s(uds_local, UDS_WORDS, (void *)uds, UDS_WORDS);
if (!memeq(uds_local, zeros, UDS_WORDS * 4)) {
failmsg("Read from UDS in app-mode");
anyfailed = 1;
}
// Should NOT be able to read from UDI in app-mode.
wordcpy_s(udi_local, UDI_WORDS, (void *)udi, UDI_WORDS);
if (!memeq(udi_local, zeros, UDI_WORDS * 4)) {
failmsg("Read from UDI in app-mode");
anyfailed = 1;
}
uint32_t cdi_local[CDI_WORDS];
uint32_t cdi_local2[CDI_WORDS];
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
// Write to CDI should NOT have any effect in app mode.
wordcpy_s((void *)cdi, CDI_WORDS, zeros, CDI_WORDS);
wordcpy_s(cdi_local2, CDI_WORDS, (void *)cdi, CDI_WORDS);
if (!memeq(cdi_local, cdi_local2, CDI_WORDS * 4)) {
failmsg("Write to CDI in app-mode");
anyfailed = 1;
}
// Test FW_RAM.
*fw_ram = 0x21;
if (*fw_ram == 0x21) {
failmsg("Write and read FW RAM in app-mode");
anyfailed = 1;
}
puts("\r\nTesting timer... 3");
// Matching clock at 21 MHz, giving us timer in seconds
*timer_prescaler = 21 * 1000000;
// Test timer expiration after 1s
*timer = 1;
// Start the timer
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
while (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
}
// Now timer has expired and is ready to run again
puts(" 2");
// Test to interrupt a timer - and reads from timer register
// Starting 10s timer and interrupting it in 3s...
*timer = 10;
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
uint32_t last_timer = 10;
for (int i = 0; i < 3; i++) {
last_timer = wait_timer_tick(last_timer);
}
// Stop the timer
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
puts(" 1. done.\r\n");
if (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
failmsg("Timer didn't stop");
anyfailed = 1;
}
if (*timer != 10) {
failmsg("Timer didn't reset to 10");
anyfailed = 1;
}
// Check and display test results.
puts("\r\n--> ");
if (anyfailed) {
puts("Some test FAILED!\r\n");
} else {
puts("All tests passed.\r\n");
}
puts("\r\nHere are 256 bytes from the TRNG:\r\n");
for (int j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++) {
while ((*trng_status &
(1 << TK1_MMIO_TRNG_STATUS_READY_BIT)) == 0) {
}
uint32_t rnd = *trng_entropy;
puthexn((uint8_t *)&rnd, 4);
puts(" ");
}
puts("\r\n");
}
puts("\r\n");
puts("Now echoing what you type...\r\n");
for (;;) {
in = readbyte(&mode, &mode_bytes_left);
writebyte(MODE_CDC);
writebyte(1);
writebyte(in);
}
}

View File

@ -303,50 +303,6 @@ int main(void)
anyfailed = 1;
}
// Turn on application mode.
// -------------------------
*app_mode_ctrl = 1;
sw = *app_mode_ctrl;
if (sw != 0xffffffff) {
failmsg("app_mode_ctrl is not 0xffffffff in app mode");
anyfailed = 1;
}
// Should NOT be able to read from UDS in app-mode.
wordcpy_s(uds_local, UDS_WORDS, (void *)uds, UDS_WORDS);
if (!memeq(uds_local, zeros, UDS_WORDS * 4)) {
failmsg("Read from UDS in app-mode");
anyfailed = 1;
}
// Should NOT be able to read from UDI in app-mode.
wordcpy_s(udi_local, UDI_WORDS, (void *)udi, UDI_WORDS);
if (!memeq(udi_local, zeros, UDI_WORDS * 4)) {
failmsg("Read from UDI in app-mode");
anyfailed = 1;
}
uint32_t cdi_local[CDI_WORDS];
uint32_t cdi_local2[CDI_WORDS];
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
// Write to CDI should NOT have any effect in app mode.
wordcpy_s((void *)cdi, CDI_WORDS, zeros, CDI_WORDS);
wordcpy_s(cdi_local2, CDI_WORDS, (void *)cdi, CDI_WORDS);
if (!memeq(cdi_local, cdi_local2, CDI_WORDS * 4)) {
failmsg("Write to CDI in app-mode");
anyfailed = 1;
}
// Test FW_RAM.
*fw_ram = 0x21;
if (*fw_ram == 0x21) {
failmsg("Write and read FW RAM in app-mode");
anyfailed = 1;
}
puts("\r\nTesting timer... 3");
// Matching clock at 24 MHz, giving us timer in seconds
*timer_prescaler = 24 * 1000000;