From fed9354fe9eb92e0100447e741f8051481e49e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Fri, 31 Jan 2025 13:41:11 +0100 Subject: [PATCH] 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. --- hw/application_fpga/fw/testapp/Makefile | 70 +++++++ hw/application_fpga/fw/testapp/app.lds | 64 ++++++ hw/application_fpga/fw/testapp/crt0.S | 53 +++++ hw/application_fpga/fw/testapp/main.c | 258 ++++++++++++++++++++++++ hw/application_fpga/fw/testfw/main.c | 44 ---- 5 files changed, 445 insertions(+), 44 deletions(-) create mode 100644 hw/application_fpga/fw/testapp/Makefile create mode 100644 hw/application_fpga/fw/testapp/app.lds create mode 100644 hw/application_fpga/fw/testapp/crt0.S create mode 100644 hw/application_fpga/fw/testapp/main.c diff --git a/hw/application_fpga/fw/testapp/Makefile b/hw/application_fpga/fw/testapp/Makefile new file mode 100644 index 0000000..8e1e1c6 --- /dev/null +++ b/hw/application_fpga/fw/testapp/Makefile @@ -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) diff --git a/hw/application_fpga/fw/testapp/app.lds b/hw/application_fpga/fw/testapp/app.lds new file mode 100644 index 0000000..421122c --- /dev/null +++ b/hw/application_fpga/fw/testapp/app.lds @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2022 Tillitis AB + * 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 */ +} diff --git a/hw/application_fpga/fw/testapp/crt0.S b/hw/application_fpga/fw/testapp/crt0.S new file mode 100644 index 0000000..f484b7d --- /dev/null +++ b/hw/application_fpga/fw/testapp/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 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 diff --git a/hw/application_fpga/fw/testapp/main.c b/hw/application_fpga/fw/testapp/main.c new file mode 100644 index 0000000..c2a84b6 --- /dev/null +++ b/hw/application_fpga/fw/testapp/main.c @@ -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); + } +} diff --git a/hw/application_fpga/fw/testfw/main.c b/hw/application_fpga/fw/testfw/main.c index 9aab9a7..7eb89d3 100644 --- a/hw/application_fpga/fw/testfw/main.c +++ b/hw/application_fpga/fw/testfw/main.c @@ -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;