From 5535323b06abf6c678d4a5b43e19fb5135d2b028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 3 Dec 2024 17:33:59 +0100 Subject: [PATCH 01/32] PoC: PicoRV32 interrupts A proof-of-concept of enabling PicoRV32 interrupts. Two interrupt sources, which can be triggered by writes to memory addresses, are added. The design has only been simulated, not run on hardware. Synthesis: Ice40 LC utilization is 93% (4934/5280) when built using tkey-builder:4 Simulation: A `tb_application_fpga_irqpoc` target is added. Running `make tb_application_fpga_irqpoc` creates `tb_application_fpga_sim.fst` which can be inspected in GTKWave or Surfer. Firmware: A simple firmware is added in `fw/irqpoc`. It enables both interrupts and triggers each interrupt once. Custom PicoRV32 instructions are located in `custom_ops.S`. It is imported from upstream PicoRV32 commit: https://github.com/YosysHQ/picorv32/commit/70f3c33ac8348a46eeca92796721dcf8cbcc326c --- hw/application_fpga/Makefile | 26 ++++- hw/application_fpga/README.md | 60 ++++++++--- hw/application_fpga/core/tk1/rtl/tk1.v | 1 + hw/application_fpga/fw/irqpoc/Makefile | 9 ++ hw/application_fpga/fw/irqpoc/custom_ops.S | 102 ++++++++++++++++++ hw/application_fpga/fw/irqpoc/main.c | 10 ++ hw/application_fpga/fw/irqpoc/start.S | 42 ++++++++ hw/application_fpga/rtl/application_fpga.v | 69 ++++++++++-- hw/application_fpga/tb/application_fpga_sim.v | 73 +++++++++++-- .../tb/tb_application_fpga_sim.v | 2 +- 10 files changed, 363 insertions(+), 31 deletions(-) create mode 100644 hw/application_fpga/fw/irqpoc/Makefile create mode 100644 hw/application_fpga/fw/irqpoc/custom_ops.S create mode 100644 hw/application_fpga/fw/irqpoc/main.c create mode 100644 hw/application_fpga/fw/irqpoc/start.S diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 097ba76..4379059 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -144,6 +144,10 @@ TESTFW_OBJS = \ $(P)/fw/tk1/lib.o \ $(P)/fw/tk1/blake2s/blake2s.o +IRQPOC_OBJS = \ + $(P)/fw/irqpoc/main.o \ + $(P)/fw/irqpoc/start.o + #------------------------------------------------------------------- # All: Complete build of HW and FW. #------------------------------------------------------------------- @@ -180,6 +184,7 @@ LDFLAGS = -T $(P)/fw/tk1/firmware.lds $(FIRMWARE_OBJS): $(FIRMWARE_DEPS) $(TESTFW_OBJS): $(FIRMWARE_DEPS) +$(IRQPOC_OBJS): $(FIRMWARE_DEPS) firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ @@ -222,6 +227,9 @@ splint: testfw.elf: $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@ +irqpoc.elf: $(IRQPOC_OBJS) $(P)/fw/tk1/firmware.lds + $(CC) $(CFLAGS) $(IRQPOC_OBJS) $(LDFLAGS) -o $@ + # Generate a fake BRAM file that will be filled in later after place-n-route bram_fw.hex: $(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@ @@ -232,6 +240,8 @@ simfirmware.hex: simfirmware.bin simfirmware_size_mismatch python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ testfw.hex: testfw.bin testfw_size_mismatch python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ +irqpoc.hex: irqpoc.bin + python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ .PHONY: check-binary-hashes check-binary-hashes: @@ -401,11 +411,13 @@ application_fpga_testfw.bin: application_fpga.asc bram_fw.hex testfw.hex #------------------------------------------------------------------- # Build testbench simulation for the design #------------------------------------------------------------------- +SIMFIRMWARE = simfirmware.hex + tb_application_fpga: $(SIM_VERILOG_SRCS) \ $(VERILOG_SRCS) \ $(PICORV32_SRCS) \ $(ICE40_SIM_CELLS) \ - simfirmware.hex + $(SIMFIRMWARE) python3 ./tools/app_bin_to_spram_hex.py \ ./tb/app.bin \ ./tb/output_spram0.hex \ @@ -429,7 +441,7 @@ tb_application_fpga: $(SIM_VERILOG_SRCS) \ -DNO_ICE40_DEFAULT_ASSIGNMENTS \ -DAPP_SIZE=$(shell ls -l tb/app.bin| awk '{print $$5}') \ -DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \ - -DFIRMWARE_HEX=\"$(P)/simfirmware.hex\" \ + -DFIRMWARE_HEX=\"$(P)/$(SIMFIRMWARE)\" \ -DUDS_HEX=\"$(P)/data/uds.hex\" \ -DUDI_HEX=\"$(P)/data/udi.hex\" \ $(filter %.v, $^) @@ -437,6 +449,15 @@ tb_application_fpga: $(SIM_VERILOG_SRCS) \ ./tb_verilated/Vtb_application_fpga_sim \ && { echo -e "\n -- Wave simulation saved to tb_application_fpga_sim.fst\n"; true; } +.PHONY: emptyapp +emptyapp: + dd if=/dev/zero of=tb/app.bin bs=1024 count=128 + +.PHONY: tb_application_fpga_irqpoc +tb_application_fpga_irqpoc: SIMFIRMWARE=irqpoc.hex +tb_application_fpga_irqpoc: irqpoc.hex emptyapp tb_application_fpga + + #------------------------------------------------------------------- # FPGA device programming. #------------------------------------------------------------------- @@ -483,6 +504,7 @@ clean_fw: rm -f $(FIRMWARE_OBJS) rm -f testfw.{elf,elf.map,bin,hex} rm -f $(TESTFW_OBJS) + rm -f $(IRQPOC_OBJS) rm -f qemu_firmware.elf .PHONY: clean_fw diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 1f3ab78..ac98d43 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -24,17 +24,19 @@ and bitmasks, see the file `fw/tk1_mem.h`. Rough memory map: -| *name* | *prefix* | -|---------|----------| -| ROM | 0x00 | -| RAM | 0x40 | -| TRNG | 0xc0 | -| Timer | 0xc1 | -| UDS | 0xc2 | -| UART | 0xc3 | -| Touch | 0xc4 | -| FW\_RAM | 0xd0 | -| TK1 | 0xff | +| *name* | *prefix* | +|------------|----------| +| ROM | 0x00 | +| RAM | 0x40 | +| TRNG | 0xc0 | +| Timer | 0xc1 | +| UDS | 0xc2 | +| UART | 0xc3 | +| Touch | 0xc4 | +| FW\_RAM | 0xd0 | +| IRQ30\_SET | 0xe0 | +| IRQ31\_SET | 0xe1 | +| TK1 | 0xff | ## `clk_reset_gen` @@ -96,6 +98,41 @@ hours, days) there is also a 32 bit prescaler. The timer is available to use by firmware and applications. +## `irq30_set` + +Interrupt 30 trigger area. A 32-bit write to the IRQ30\_SET memory +area will trigger interrupt 30. + +## `irq31_set` + +Interrupt 31 trigger area. A 32-bit write to the IRQ31\_SET memory +area will trigger interrupt 31. + +## Interrupts + +Triggering an interrupt will cause the CPU to execute the interrupt +handler att address 0x10. + +The interrupt handler is shared by IRQ30 and IRQ31. Register `x4` can +be inspected to determine the interrupt source. Each interrupt source +is assigned one bit in x4. Triggered interrupts have their bit set to +`1`. + +| *Interrupt source* | *x4 bit* | +|--------------------|----------| +| IRQ30\_SET | 30 | +| IRQ31\_SET | 31 | + +The return address is located in register `x3`. Calling the PicoRV32 +specific instruction `retirq` exits the interrupt handler and clears +the interrupt source. + +No registers are stored/restored when entering/exiting the interrupt +handler. It is up to the software to store/restore as necessary. + +Interrupts can be enabled/disabled using the PicoRV32 specific +`maskirq` instruction. + ## `tk1` See [tk1 README](core/tk1/README.md) for details. @@ -107,7 +144,6 @@ Contains: - RGB LED control. - General purpose input/output (GPIO) pin control. - Application introspection: start address and size of binary. -- BLAKE2s function access. - Compound Device Identity (CDI). - Unique Device Identity (UDI). - RAM memory protection. diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index 7e39ce0..a4dd899 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -181,6 +181,7 @@ module tk1 #( wire spi_ready; wire [ 7 : 0] spi_rx_data; + //---------------------------------------------------------------- // Concurrent connectivity for ports etc. //---------------------------------------------------------------- diff --git a/hw/application_fpga/fw/irqpoc/Makefile b/hw/application_fpga/fw/irqpoc/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc/custom_ops.S b/hw/application_fpga/fw/irqpoc/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc/main.c b/hw/application_fpga/fw/irqpoc/main.c new file mode 100644 index 0000000..c98f474 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc/start.S b/hw/application_fpga/fw/irqpoc/start.S new file mode 100644 index 0000000..cb67e02 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/start.S @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only +*/ + +#include "custom_ops.S" // PicoRV32 custom instructions + + .section ".text.init" + .globl _start +_start: + j init + + .=0x10 // IRQ handler at fixed address 0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + // If bit 30 is 1: IRQ30 was triggered. + + nop // NOPs are not necessary. Only added to make it easier to find + nop // when simulating. + nop + picorv32_retirq_insn() // Return from interrupt + + .=0x20 // Setting location of init to 0x20. Makes it easier to find when + // simulating. +init: + li t0, 0x3fffffff // IRQ31 & IRQ30 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + + li t0, 0xe1000000 // IRQ31 trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + + li t0, 0xe0000000 // IRQ30 trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. +loop: + j loop + + .align 4 // Padding to please makehex.py which requires even 4-byte file + // sizes. + diff --git a/hw/application_fpga/rtl/application_fpga.v b/hw/application_fpga/rtl/application_fpga.v index 39650d8..9f883dd 100644 --- a/hw/application_fpga/rtl/application_fpga.v +++ b/hw/application_fpga/rtl/application_fpga.v @@ -54,11 +54,15 @@ module application_fpga ( localparam UART_PREFIX = 6'h03; localparam TOUCH_SENSE_PREFIX = 6'h04; localparam FW_RAM_PREFIX = 6'h10; + localparam IRQ30_PREFIX = 6'h20; + localparam IRQ31_PREFIX = 6'h21; localparam TK1_PREFIX = 6'h3f; // Instruction used to cause a trap. localparam ILLEGAL_INSTRUCTION = 32'h0; + localparam IRQ30_IRQ_MASK = 2 ** 30; + localparam IRQ31_IRQ_MASK = 2 ** 31; //---------------------------------------------------------------- // Registers, memories with associated wires. @@ -77,11 +81,13 @@ module application_fpga ( wire reset_n; /* verilator lint_off UNOPTFLAT */ + reg [31 : 0] cpu_irq; wire cpu_trap; wire cpu_valid; wire cpu_instr; wire [03 : 0] cpu_wstrb; /* verilator lint_off UNUSED */ + wire [31 : 0] cpu_eoi; wire [31 : 0] cpu_addr; wire [31 : 0] cpu_wdata; @@ -136,6 +142,14 @@ module application_fpga ( wire [31 : 0] touch_sense_read_data; wire touch_sense_ready; + reg irq30_cs; + reg irq30_we; + (* keep *)reg irq30_eoi; + + reg irq31_cs; + reg irq31_we; + (* keep *)reg irq31_eoi; + reg tk1_cs; reg tk1_we; reg [ 7 : 0] tk1_address; @@ -163,12 +177,17 @@ module application_fpga ( picorv32 #( - .ENABLE_COUNTERS(0), - .TWO_STAGE_SHIFT(0), - .CATCH_MISALIGN (0), - .COMPRESSED_ISA (1), - .ENABLE_FAST_MUL(1), - .BARREL_SHIFTER (1) + .ENABLE_COUNTERS (0), + .TWO_STAGE_SHIFT (0), + .CATCH_MISALIGN (0), + .COMPRESSED_ISA (1), + .ENABLE_FAST_MUL (1), + .BARREL_SHIFTER (1), + .ENABLE_IRQ (1), + .ENABLE_IRQ_QREGS(0), + .ENABLE_IRQ_TIMER(0), + .MASKED_IRQ (~(IRQ31_IRQ_MASK | IRQ30_IRQ_MASK)), + .LATCHED_IRQ (IRQ31_IRQ_MASK | IRQ30_IRQ_MASK) ) cpu ( .clk(clk), .resetn(reset_n), @@ -182,11 +201,12 @@ module application_fpga ( .mem_rdata(muxed_rdata_reg), .mem_instr(cpu_instr), + .irq(cpu_irq), + .eoi(cpu_eoi), + // Defined unused ports. Makes lint happy. But // we still needs to help lint with empty ports. /* verilator lint_off PINCONNECTEMPTY */ - .irq(32'h0), - .eoi(), .trace_valid(), .trace_data(), .mem_la_read(), @@ -373,6 +393,23 @@ module application_fpga ( end + //---------------------------------------------------------------- + // irq_ctrl + // Interrupt logic + //---------------------------------------------------------------- + always @* begin : irq_ctrl + reg irq31_set; + reg irq30_set; + + irq31_set = irq31_cs & irq31_we; + irq30_set = irq30_cs & irq30_we; + cpu_irq = {irq31_set, irq30_set, 30'h0}; + + irq31_eoi = cpu_eoi[31]; + irq30_eoi = cpu_eoi[30]; + end + + //---------------------------------------------------------------- // cpu_mem_ctrl // CPU memory decode and control logic. @@ -422,6 +459,12 @@ module application_fpga ( touch_sense_we = |cpu_wstrb; touch_sense_address = cpu_addr[9 : 2]; + irq30_cs = 1'h0; + irq30_we = |cpu_wstrb; + + irq31_cs = 1'h0; + irq31_we = |cpu_wstrb; + tk1_cs = 1'h0; tk1_we = |cpu_wstrb; tk1_address = cpu_addr[9 : 2]; @@ -494,6 +537,16 @@ module application_fpga ( muxed_ready_new = fw_ram_ready; end + IRQ30_PREFIX: begin + irq30_cs = 1'h1; + muxed_ready_new = 1'h1; + end + + IRQ31_PREFIX: begin + irq31_cs = 1'h1; + muxed_ready_new = 1'h1; + end + TK1_PREFIX: begin tk1_cs = 1'h1; muxed_rdata_new = tk1_read_data; diff --git a/hw/application_fpga/tb/application_fpga_sim.v b/hw/application_fpga/tb/application_fpga_sim.v index 00da22d..ad5c1ef 100644 --- a/hw/application_fpga/tb/application_fpga_sim.v +++ b/hw/application_fpga/tb/application_fpga_sim.v @@ -67,11 +67,15 @@ module application_fpga_sim ( localparam UART_PREFIX = 6'h03; localparam TOUCH_SENSE_PREFIX = 6'h04; localparam FW_RAM_PREFIX = 6'h10; + localparam IRQ30_PREFIX = 6'h20; + localparam IRQ31_PREFIX = 6'h21; localparam TK1_PREFIX = 6'h3f; // Instruction used to cause a trap. localparam ILLEGAL_INSTRUCTION = 32'h0; + localparam IRQ30_IRQ_MASK = 2 ** 30; + localparam IRQ31_IRQ_MASK = 2 ** 31; //---------------------------------------------------------------- // Registers, memories with associated wires. @@ -89,11 +93,13 @@ module application_fpga_sim ( wire reset_n; /* verilator lint_off UNOPTFLAT */ + reg [31 : 0] cpu_irq; wire cpu_trap; wire cpu_valid; wire cpu_instr; wire [ 3 : 0] cpu_wstrb; /* verilator lint_off UNUSED */ + wire [31 : 0] cpu_eoi; wire [31 : 0] cpu_addr; wire [31 : 0] cpu_wdata; @@ -148,6 +154,14 @@ module application_fpga_sim ( wire [31 : 0] touch_sense_read_data; wire touch_sense_ready; + reg irq30_cs; + reg irq30_we; + (* keep *)reg irq30_eoi; + + reg irq31_cs; + reg irq31_we; + (* keep *)reg irq31_eoi; + reg tk1_cs; reg tk1_we; reg [ 7 : 0] tk1_address; @@ -174,12 +188,17 @@ module application_fpga_sim ( picorv32 #( - .ENABLE_COUNTERS(0), - .TWO_STAGE_SHIFT(0), - .CATCH_MISALIGN (0), - .COMPRESSED_ISA (1), - .ENABLE_FAST_MUL(1), - .BARREL_SHIFTER (1) + .ENABLE_COUNTERS (0), + .TWO_STAGE_SHIFT (0), + .CATCH_MISALIGN (0), + .COMPRESSED_ISA (1), + .ENABLE_FAST_MUL (1), + .BARREL_SHIFTER (1), + .ENABLE_IRQ (1), + .ENABLE_IRQ_QREGS(0), + .ENABLE_IRQ_TIMER(0), + .MASKED_IRQ (~(IRQ31_IRQ_MASK | IRQ30_IRQ_MASK)), + .LATCHED_IRQ (IRQ31_IRQ_MASK | IRQ30_IRQ_MASK) ) cpu ( .clk(clk), .resetn(reset_n), @@ -193,11 +212,12 @@ module application_fpga_sim ( .mem_rdata(muxed_rdata_reg), .mem_instr(cpu_instr), + .irq(cpu_irq), + .eoi(cpu_eoi), + // Defined unused ports. Makes lint happy. But // we still needs to help lint with empty ports. /* verilator lint_off PINCONNECTEMPTY */ - .irq(32'h0), - .eoi(), .trace_valid(), .trace_data(), .mem_la_read(), @@ -385,6 +405,23 @@ module application_fpga_sim ( end + //---------------------------------------------------------------- + // irq_ctrl + // Interrupt logic + //---------------------------------------------------------------- + always @* begin : irq_ctrl + reg irq31_set; + reg irq30_set; + + irq31_set = irq31_cs & irq31_we; + irq30_set = irq30_cs & irq30_we; + cpu_irq = {irq31_set, irq30_set, 30'h0}; + + irq31_eoi = cpu_eoi[31]; + irq30_eoi = cpu_eoi[30]; + end + + //---------------------------------------------------------------- // cpu_mem_ctrl // CPU memory decode and control logic. @@ -436,6 +473,12 @@ module application_fpga_sim ( touch_sense_we = |cpu_wstrb; touch_sense_address = cpu_addr[9 : 2]; + irq30_cs = 1'h0; + irq30_we = |cpu_wstrb; + + irq31_cs = 1'h0; + irq31_we = |cpu_wstrb; + tk1_cs = 1'h0; tk1_we = |cpu_wstrb; tk1_address = cpu_addr[9 : 2]; @@ -528,6 +571,20 @@ module application_fpga_sim ( muxed_ready_new = fw_ram_ready; end + IRQ30_PREFIX: begin + `verbose($display("Access to blake2s interrupt trigger");) + ascii_state = "Blake2s IRQ trigger"; + irq30_cs = 1'h1; + muxed_ready_new = 1'h1; + end + + IRQ31_PREFIX: begin + `verbose($display("Access to syscall interrupt trigger");) + ascii_state = "Syscall IRQ trigger"; + irq31_cs = 1'h1; + muxed_ready_new = 1'h1; + end + TK1_PREFIX: begin `verbose($display("Access to TK1 core");) ascii_state = "TK1 core"; diff --git a/hw/application_fpga/tb/tb_application_fpga_sim.v b/hw/application_fpga/tb/tb_application_fpga_sim.v index 32605fb..3a6f1da 100644 --- a/hw/application_fpga/tb/tb_application_fpga_sim.v +++ b/hw/application_fpga/tb/tb_application_fpga_sim.v @@ -80,7 +80,7 @@ module tb_application_fpga_sim (); //---------------------------------------------------------------- initial begin // End simulation after XXX time units (set by timescale) - #20000000; + #700; $display("TIMEOUT"); $finish; end From d36e9c9e3dd59bc5ff562f5241376300290ad8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Fri, 6 Dec 2024 16:45:24 +0100 Subject: [PATCH 02/32] PoC: Add LED toggling interrupt example Add example firmware for demoing interrupts on Tkey hardware. --- hw/application_fpga/Makefile | 9 ++ .../fw/irqpoc_led_toggle/Makefile | 9 ++ .../fw/irqpoc_led_toggle/custom_ops.S | 102 ++++++++++++++++++ .../fw/irqpoc_led_toggle/main.c | 10 ++ .../fw/irqpoc_led_toggle/start.S | 91 ++++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 hw/application_fpga/fw/irqpoc_led_toggle/Makefile create mode 100644 hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S create mode 100644 hw/application_fpga/fw/irqpoc_led_toggle/main.c create mode 100644 hw/application_fpga/fw/irqpoc_led_toggle/start.S diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 4379059..4e65d97 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -148,6 +148,10 @@ IRQPOC_OBJS = \ $(P)/fw/irqpoc/main.o \ $(P)/fw/irqpoc/start.o +IRQPOC_LED_TOGGLE_OBJS = \ + $(P)/fw/irqpoc_led_toggle/main.o \ + $(P)/fw/irqpoc_led_toggle/start.o + #------------------------------------------------------------------- # All: Complete build of HW and FW. #------------------------------------------------------------------- @@ -185,6 +189,7 @@ LDFLAGS = -T $(P)/fw/tk1/firmware.lds $(FIRMWARE_OBJS): $(FIRMWARE_DEPS) $(TESTFW_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_OBJS): $(FIRMWARE_DEPS) +$(IRQPOC_LED_TOGGLE_OBJS): $(FIRMWARE_DEPS) firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ @@ -230,6 +235,9 @@ testfw.elf: $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds irqpoc.elf: $(IRQPOC_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(IRQPOC_OBJS) $(LDFLAGS) -o $@ +irqpoc_led_toggle.elf: $(IRQPOC_LED_TOGGLE_OBJS) $(P)/fw/tk1/firmware.lds + $(CC) $(CFLAGS) $(IRQPOC_LED_TOGGLE_OBJS) $(LDFLAGS) -o $@ + # Generate a fake BRAM file that will be filled in later after place-n-route bram_fw.hex: $(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@ @@ -505,6 +513,7 @@ clean_fw: rm -f testfw.{elf,elf.map,bin,hex} rm -f $(TESTFW_OBJS) rm -f $(IRQPOC_OBJS) + rm -f $(IRQPOC_LED_TOGGLE_OBJS) rm -f qemu_firmware.elf .PHONY: clean_fw diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/Makefile b/hw/application_fpga/fw/irqpoc_led_toggle/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S b/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/main.c b/hw/application_fpga/fw/irqpoc_led_toggle/main.c new file mode 100644 index 0000000..c98f474 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/start.S b/hw/application_fpga/fw/irqpoc_led_toggle/start.S new file mode 100644 index 0000000..12670b1 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/start.S @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only +*/ + +// Triggers both interrupts, one after the other, in an endless loop. The +// interrupt handler updates the LED color depending on which interrupt +// was triggered. +// The LED color will alterate between blue and green. +// +// | IRQ number | Color | +// |------------|-------| +// | 30 | Blue | +// | 31 | Green | +// | Unexpected | Red | +// + +#include "custom_ops.S" // PicoRV32 custom instructions + + .section ".text.init" + .globl _start +_start: + j init + + + .=0x10 // IRQ handler at fixed address 0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + // If bit 30 is 1: IRQ30 was triggered. +irq30_check: + li t4, (1 << 30) + bne x4, t4, irq31_check + call led_blue + call delay + j irq_source_check_done +irq31_check: + li t4, (1 << 31) + bne x4, t4, unexpected_irq + call led_green + call delay + j irq_source_check_done +unexpected_irq: + call led_red + call delay +irq_source_check_done: + picorv32_retirq_insn() // Return from interrupt + + +init: + li t0, 0x3fffffff // IRQ31 & IRQ30 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + +irq_trigger_loop: + li t0, 0xe1000000 // IRQ31 trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + + li t0, 0xe0000000 // IRQ30 trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + j irq_trigger_loop + +led_red: + li t2, 0xff000024 + li t3, (1 << 2) + sw t3, 0(t2) + ret + +led_green: + li t2, 0xff000024 + li t3, (1 << 1) + sw t3, 0(t2) + ret + +led_blue: + li t2, 0xff000024 + li t3, (1 << 0) + sw t3, 0(t2) + ret + +delay: + li t5, 0x100000 +delay_dec: + addi t5, t5, -1 + bne t5, zero, delay_dec + ret + + .align 4 // Padding to please makehex.py which requires even 4-byte file + // sizes. + From 2d762faba745384505f8d2348c96831447987368 Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Thu, 14 Nov 2024 14:02:53 +0100 Subject: [PATCH 03/32] PoC: Automatically control system_mode in hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of manually switching to app mode using the system mode register, app mode will be enabled when executing outside of firmware ROM. Co-authored-by: Mikael Ă…gren --- hw/application_fpga/core/tk1/rtl/tk1.v | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index a4dd899..dc7682a 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -107,6 +107,7 @@ module tk1 #( localparam FW_RAM_FIRST = 32'hd0000000; localparam FW_RAM_LAST = 32'hd00007ff; + localparam FW_ROM_LAST = 32'h000017ff; //---------------------------------------------------------------- // Registers including update variables and write enable. @@ -115,6 +116,7 @@ module tk1 #( reg cdi_mem_we; reg system_mode_reg; + reg system_mode_new; reg system_mode_we; reg [ 2 : 0] led_reg; @@ -291,7 +293,7 @@ module tk1 #( gpio2_reg[1] <= gpio2_reg[0]; if (system_mode_we) begin - system_mode_reg <= 1'h1; + system_mode_reg <= system_mode_new; end if (led_we) begin @@ -412,12 +414,27 @@ module tk1 #( end end + //---------------------------------------------------------------- + // system_mode_ctrl + // + // Automatically lower privilege when executing above ROM. + // ---------------------------------------------------------------- + always @* begin : system_mode_ctrl + system_mode_new = 1'h0; + system_mode_we = 1'h0; + + if (cpu_valid & cpu_instr) begin + if (cpu_addr > FW_ROM_LAST) begin + system_mode_new = 1'h1; + system_mode_we = 1'h1; + end + end + end //---------------------------------------------------------------- // api //---------------------------------------------------------------- always @* begin : api - system_mode_we = 1'h0; led_we = 1'h0; gpio3_we = 1'h0; gpio4_we = 1'h0; @@ -444,10 +461,6 @@ module tk1 #( if (cs) begin tmp_ready = 1'h1; if (we) begin - if (address == ADDR_SYSTEM_MODE_CTRL) begin - system_mode_we = 1'h1; - end - if (address == ADDR_LED) begin led_we = 1'h1; end From b53666e497520f137413e1fdab228f0fed041c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 10 Dec 2024 19:08:55 +0100 Subject: [PATCH 04/32] PoC: Remove Blake2s register --- hw/application_fpga/core/tk1/README.md | 12 ------------ hw/application_fpga/core/tk1/rtl/tk1.v | 21 --------------------- 2 files changed, 33 deletions(-) diff --git a/hw/application_fpga/core/tk1/README.md b/hw/application_fpga/core/tk1/README.md index cd80f14..1aa0b0f 100644 --- a/hw/application_fpga/core/tk1/README.md +++ b/hw/application_fpga/core/tk1/README.md @@ -78,18 +78,6 @@ FW as part of the loading of the app. The registers can't be written when the `ADDR_SYSTEM_MODE_CTRL` has been set. -### Access to Blake2s - -``` -ADDR_BLAKE2S: 0x10 -``` - -This register provides the 32-bit function pointer address to the -Blake2s hash function in the FW. It is written by FW during boot. The -register can't be written to when the `ADDR_SYSTEM_MODE_CTRL` has been -set. - - ### Access to CDI ``` diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index dc7682a..d947046 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -79,8 +79,6 @@ module tk1 #( localparam ADDR_APP_START = 8'h0c; localparam ADDR_APP_SIZE = 8'h0d; - localparam ADDR_BLAKE2S = 8'h10; - localparam ADDR_CDI_FIRST = 8'h20; localparam ADDR_CDI_LAST = 8'h27; @@ -135,9 +133,6 @@ module tk1 #( reg [31 : 0] app_size_reg; reg app_size_we; - reg [31 : 0] blake2s_addr_reg; - reg blake2s_addr_we; - reg [23 : 0] cpu_trap_ctr_reg; reg [23 : 0] cpu_trap_ctr_new; reg [ 2 : 0] cpu_trap_led_reg; @@ -261,7 +256,6 @@ module tk1 #( gpio4_reg <= 1'h0; app_start_reg <= 32'h0; app_size_reg <= APP_SIZE; - blake2s_addr_reg <= 32'h0; cdi_mem[0] <= 32'h0; cdi_mem[1] <= 32'h0; cdi_mem[2] <= 32'h0; @@ -316,10 +310,6 @@ module tk1 #( app_size_reg <= write_data; end - if (blake2s_addr_we) begin - blake2s_addr_reg <= write_data; - end - if (cdi_mem_we) begin cdi_mem[address[2 : 0]] <= write_data; end @@ -440,7 +430,6 @@ module tk1 #( gpio4_we = 1'h0; app_start_we = 1'h0; app_size_we = 1'h0; - blake2s_addr_we = 1'h0; cdi_mem_we = 1'h0; ram_addr_rand_we = 1'h0; ram_data_rand_we = 1'h0; @@ -486,12 +475,6 @@ module tk1 #( system_reset_new = 1'h1; end - if (address == ADDR_BLAKE2S) begin - if (!system_mode_reg) begin - blake2s_addr_we = 1'h1; - end - end - if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin if (!system_mode_reg) begin cdi_mem_we = 1'h1; @@ -572,10 +555,6 @@ module tk1 #( tmp_read_data = app_size_reg; end - if (address == ADDR_BLAKE2S) begin - tmp_read_data = blake2s_addr_reg; - end - if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin tmp_read_data = cdi_mem[address[2 : 0]]; end From e4d19e83cee5b4552d395165a5cb2dd8fb2a302b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 11 Dec 2024 11:49:41 +0100 Subject: [PATCH 05/32] PoC: Trap when executing from ROM in app mode Only allow executing from ROM when in one of the following execution contexts: - Firmware mode - IRQ_SYSCALL_LO - IRQ_SYSCALL_HI Co-authored-by: Daniel Jobson --- hw/application_fpga/README.md | 31 ++++++++++++++----- hw/application_fpga/core/tk1/rtl/tk1.v | 14 +++++++++ hw/application_fpga/rtl/application_fpga.v | 7 +++-- hw/application_fpga/tb/application_fpga_sim.v | 7 +++-- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index ac98d43..5880040 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -11,9 +11,10 @@ The design top level is in `rtl/application_fpga.v`. It contains instances of all cores as well as the memory system. The memory system allows the CPU to access cores in different ways -given the current execution mode. There are two execution modes - -firmware and application. Basically, in application mode the access is -more restrictive. +given the current execution mode. There are three execution modes - +firmware, application and system call. Each mode give access to a +different set of resources. Where app mode is the most restrictive and +firmware mode is the least restrictive. The rest of the components are under `cores`. They typically have their own `README.md` file documenting them and their API in detail. @@ -118,10 +119,10 @@ be inspected to determine the interrupt source. Each interrupt source is assigned one bit in x4. Triggered interrupts have their bit set to `1`. -| *Interrupt source* | *x4 bit* | -|--------------------|----------| -| IRQ30\_SET | 30 | -| IRQ31\_SET | 31 | +| *Interrupt Name* | *Source* | *x4 Bit* | +|------------------|------------|----------| +| IRQ_SYSCALL_LO | IRQ30\_SET | 30 | +| IRQ_SYSCALL_HI | IRQ31\_SET | 31 | The return address is located in register `x3`. Calling the PicoRV32 specific instruction `retirq` exits the interrupt handler and clears @@ -133,6 +134,22 @@ handler. It is up to the software to store/restore as necessary. Interrupts can be enabled/disabled using the PicoRV32 specific `maskirq` instruction. +## Restricted resources + +The following table shows resource availablility for each execution +mode: + +| *Execution Mode* | *ROM* | +|---------------------|--------| +| Firmware mode | r/x | +| App mode | r | +| IRQ_SYSCALL_LO | r/x | +| IRQ_SYSCALL_HI | r/x | + +Legend: +r = readable +x = executable + ## `tk1` See [tk1 README](core/tk1/README.md) for details. diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index d947046..84666ac 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -45,6 +45,9 @@ module tk1 #( output wire gpio3, output wire gpio4, + input wire access_level_hi, + input wire access_level_med, + input wire cs, input wire we, input wire [ 7 : 0] address, @@ -178,6 +181,7 @@ module tk1 #( wire spi_ready; wire [ 7 : 0] spi_rx_data; + wire rom_exec_en; //---------------------------------------------------------------- // Concurrent connectivity for ports etc. @@ -197,6 +201,7 @@ module tk1 #( assign system_reset = system_reset_reg; + assign rom_exec_en = !system_mode | access_level_med | access_level_hi; //---------------------------------------------------------------- // Module instance. @@ -378,6 +383,9 @@ module tk1 #( // // Trying to execute instructions in FW-RAM. // + // Executing instructions in ROM, while ROM is marked as not + // executable. + // // Trying to execute code in mem area set to be data access only. // This requires execution monitor to have been setup and // enabled. @@ -395,6 +403,12 @@ module tk1 #( force_trap_set = 1'h1; end + if (!rom_exec_en) begin + if (cpu_addr <= FW_ROM_LAST) begin // Only valid as long as ROM starts at address 0x00. + force_trap_set = 1'h1; + end + end + if (cpu_mon_en_reg) begin if ((cpu_addr >= cpu_mon_first_reg) && (cpu_addr <= cpu_mon_last_reg)) begin force_trap_set = 1'h1; diff --git a/hw/application_fpga/rtl/application_fpga.v b/hw/application_fpga/rtl/application_fpga.v index 9f883dd..d937ec7 100644 --- a/hw/application_fpga/rtl/application_fpga.v +++ b/hw/application_fpga/rtl/application_fpga.v @@ -144,11 +144,11 @@ module application_fpga ( reg irq30_cs; reg irq30_we; - (* keep *)reg irq30_eoi; + reg irq30_eoi; reg irq31_cs; reg irq31_we; - (* keep *)reg irq31_eoi; + reg irq31_eoi; reg tk1_cs; reg tk1_we; @@ -367,6 +367,9 @@ module application_fpga ( .gpio3(app_gpio3), .gpio4(app_gpio4), + .access_level_hi (irq31_eoi), + .access_level_med(irq30_eoi), + .cs(tk1_cs), .we(tk1_we), .address(tk1_address), diff --git a/hw/application_fpga/tb/application_fpga_sim.v b/hw/application_fpga/tb/application_fpga_sim.v index ad5c1ef..72b528f 100644 --- a/hw/application_fpga/tb/application_fpga_sim.v +++ b/hw/application_fpga/tb/application_fpga_sim.v @@ -156,11 +156,11 @@ module application_fpga_sim ( reg irq30_cs; reg irq30_we; - (* keep *)reg irq30_eoi; + reg irq30_eoi; reg irq31_cs; reg irq31_we; - (* keep *)reg irq31_eoi; + reg irq31_eoi; reg tk1_cs; reg tk1_we; @@ -380,6 +380,9 @@ module application_fpga_sim ( .gpio3(app_gpio3), .gpio4(app_gpio4), + .access_level_hi (irq31_eoi), + .access_level_med(irq30_eoi), + .cs(tk1_cs), .we(tk1_we), .address(tk1_address), From a871d23d5df939b790e2fbd7ec06e06190177895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 11 Dec 2024 12:02:42 +0100 Subject: [PATCH 06/32] PoC: Add basic syscall example firmware Adds a basic example firmware that copies an app to app RAM. The app triggers syscall interrupts and tries to execute ROM code from app mode. A make target (`tb_application_fpga_irqpoc_with_app`) that simulates a Tkey running the firmware is added. --- hw/application_fpga/Makefile | 16 +++ .../fw/irqpoc_with_app/Makefile | 9 ++ .../fw/irqpoc_with_app/custom_ops.S | 102 ++++++++++++++++++ hw/application_fpga/fw/irqpoc_with_app/main.c | 10 ++ .../fw/irqpoc_with_app/start.S | 75 +++++++++++++ .../tb/tb_application_fpga_sim.v | 2 +- 6 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 hw/application_fpga/fw/irqpoc_with_app/Makefile create mode 100644 hw/application_fpga/fw/irqpoc_with_app/custom_ops.S create mode 100644 hw/application_fpga/fw/irqpoc_with_app/main.c create mode 100644 hw/application_fpga/fw/irqpoc_with_app/start.S diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 4e65d97..e042527 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -152,6 +152,10 @@ IRQPOC_LED_TOGGLE_OBJS = \ $(P)/fw/irqpoc_led_toggle/main.o \ $(P)/fw/irqpoc_led_toggle/start.o +IRQPOC_WITH_APP_OBJS = \ + $(P)/fw/irqpoc_with_app/main.o \ + $(P)/fw/irqpoc_with_app/start.o + #------------------------------------------------------------------- # All: Complete build of HW and FW. #------------------------------------------------------------------- @@ -190,6 +194,7 @@ $(FIRMWARE_OBJS): $(FIRMWARE_DEPS) $(TESTFW_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_LED_TOGGLE_OBJS): $(FIRMWARE_DEPS) +$(IRQPOC_WITH_APP_OBJS): $(FIRMWARE_DEPS) firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ @@ -238,6 +243,9 @@ irqpoc.elf: $(IRQPOC_OBJS) $(P)/fw/tk1/firmware.lds irqpoc_led_toggle.elf: $(IRQPOC_LED_TOGGLE_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(IRQPOC_LED_TOGGLE_OBJS) $(LDFLAGS) -o $@ +irqpoc_with_app.elf: $(IRQPOC_WITH_APP_OBJS) $(P)/fw/tk1/firmware.lds + $(CC) $(CFLAGS) $(IRQPOC_WITH_APP_OBJS) $(LDFLAGS) -o $@ + # Generate a fake BRAM file that will be filled in later after place-n-route bram_fw.hex: $(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@ @@ -250,6 +258,9 @@ testfw.hex: testfw.bin testfw_size_mismatch python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ irqpoc.hex: irqpoc.bin python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ +irqpoc_with_app.hex: irqpoc_with_app.bin + python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ + .PHONY: check-binary-hashes check-binary-hashes: @@ -465,6 +476,10 @@ emptyapp: tb_application_fpga_irqpoc: SIMFIRMWARE=irqpoc.hex tb_application_fpga_irqpoc: irqpoc.hex emptyapp tb_application_fpga +.PHONY: tb_application_fpga_irqpoc_with_app +tb_application_fpga_irqpoc_with_app: SIMFIRMWARE=irqpoc_with_app.hex +tb_application_fpga_irqpoc_with_app: irqpoc_with_app.hex emptyapp tb_application_fpga + #------------------------------------------------------------------- # FPGA device programming. @@ -514,6 +529,7 @@ clean_fw: rm -f $(TESTFW_OBJS) rm -f $(IRQPOC_OBJS) rm -f $(IRQPOC_LED_TOGGLE_OBJS) + rm -f $(IRQPOC_WITH_APP_OBJS) rm -f qemu_firmware.elf .PHONY: clean_fw diff --git a/hw/application_fpga/fw/irqpoc_with_app/Makefile b/hw/application_fpga/fw/irqpoc_with_app/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_with_app/custom_ops.S b/hw/application_fpga/fw/irqpoc_with_app/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_with_app/main.c b/hw/application_fpga/fw/irqpoc_with_app/main.c new file mode 100644 index 0000000..c98f474 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_with_app/start.S b/hw/application_fpga/fw/irqpoc_with_app/start.S new file mode 100644 index 0000000..05b1f3a --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/start.S @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only +*/ + +// This firmware copies an app from ROM to app RAM. The app triggers both +// IRQ_SYSCALL_HI and IRQ_SYSCALL_LO. One after the other. Finally, the +// app tries to jump firmware. This should result in a trap since the +// app in executing in app mode. +// + +#include "custom_ops.S" // PicoRV32 custom instructions + + .section ".text.init" + .globl _start +_start: + j init + +// +// IRQ handler +// + .=0x10 // IRQ handler at fixed address 0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + // If bit 30 is 1: IRQ30 was triggered. + + nop // NOPs are not necessary. Only added to make it easier to find + nop // when simulating. + nop + picorv32_retirq_insn() // Return from interrupt + +// +// Init +// + .=0x20 // Setting location of init to 0x20. Makes it easier to find when + // simulating. +init: + li t0, 0x3fffffff // IRQ31 & IRQ30 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + + // Copy app to App RAM + la t0, app_start + la t1, app_end + li t2, 0x40000000 // 0x40000000: App RAM +copy_app: + lw t3, 0(t0) + sw t3, 0(t2) + addi t0, t0, 4 + addi t2, t2, 4 + bleu t0, t1, copy_app + + // Jump to app + li t2, 0x40000000 // 0x40000000: App RAM + jalr zero, 0(t2) + +// +// App +// + .align 4 +app_start: + li t0, 0xe1000000 // IRQ_SYSCALL_HI (IRQ31) trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + + li t0, 0xe0000000 // IRQ_SYSCALL_LO (IRQ30) trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + + jalr zero, 0(zero) // Jumping to firmware. Expecting trap +app_loop: + j app_loop + .align 4 +app_end: + diff --git a/hw/application_fpga/tb/tb_application_fpga_sim.v b/hw/application_fpga/tb/tb_application_fpga_sim.v index 3a6f1da..a3d5d6d 100644 --- a/hw/application_fpga/tb/tb_application_fpga_sim.v +++ b/hw/application_fpga/tb/tb_application_fpga_sim.v @@ -80,7 +80,7 @@ module tb_application_fpga_sim (); //---------------------------------------------------------------- initial begin // End simulation after XXX time units (set by timescale) - #700; + #1600; $display("TIMEOUT"); $finish; end From 62dba7c4feaa77316aaf3dddd6f7d0c75c82da48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 11 Dec 2024 14:29:04 +0100 Subject: [PATCH 07/32] PoC: Control access to FW RAM Allow FW RAM access only in the following execution contexts: - Firmware mode - IRQ_SYSCALL_HI Input port `system_mode` of the `fw_ram` module is replaced with an enable port. Since access to FW RAM not longer depend only on system_mode --- hw/application_fpga/README.md | 14 +++-- hw/application_fpga/core/fw_ram/rtl/fw_ram.v | 39 ++++++------ hw/application_fpga/core/tk1/rtl/tk1.v | 3 + .../fw/irqpoc_with_app/start.S | 60 +++++++++++++++++-- hw/application_fpga/rtl/application_fpga.v | 5 +- hw/application_fpga/tb/application_fpga_sim.v | 6 +- .../tb/tb_application_fpga_sim.v | 2 +- 7 files changed, 92 insertions(+), 37 deletions(-) diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 5880040..44e300c 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -139,16 +139,18 @@ Interrupts can be enabled/disabled using the PicoRV32 specific The following table shows resource availablility for each execution mode: -| *Execution Mode* | *ROM* | -|---------------------|--------| -| Firmware mode | r/x | -| App mode | r | -| IRQ_SYSCALL_LO | r/x | -| IRQ_SYSCALL_HI | r/x | +| *Execution Mode* | *ROM* | *FW RAM* | +|---------------------|--------|----------| +| Firmware mode | r/x | r/w | +| App mode | r | i | +| IRQ_SYSCALL_LO | r/x | i | +| IRQ_SYSCALL_HI | r/x | r/w | Legend: r = readable +w = writeable x = executable +i = invisible ## `tk1` diff --git a/hw/application_fpga/core/fw_ram/rtl/fw_ram.v b/hw/application_fpga/core/fw_ram/rtl/fw_ram.v index 150f6b5..f649914 100644 --- a/hw/application_fpga/core/fw_ram/rtl/fw_ram.v +++ b/hw/application_fpga/core/fw_ram/rtl/fw_ram.v @@ -17,8 +17,7 @@ module fw_ram ( input wire clk, input wire reset_n, - input wire system_mode, - + input wire en, input wire cs, input wire [ 3 : 0] we, input wire [ 8 : 0] address, @@ -31,21 +30,19 @@ module fw_ram ( //---------------------------------------------------------------- // Registers and wires. //---------------------------------------------------------------- - reg [31 : 0] tmp_read_data; - reg [31 : 0] mem_read_data0; - reg [31 : 0] mem_read_data1; - reg ready_reg; - wire system_mode_cs; - reg bank0; - reg bank1; + reg [31 : 0] tmp_read_data; + reg [31 : 0] mem_read_data0; + reg [31 : 0] mem_read_data1; + reg ready_reg; + reg bank0; + reg bank1; //---------------------------------------------------------------- // Concurrent assignment of ports. //---------------------------------------------------------------- - assign read_data = tmp_read_data; - assign ready = ready_reg; - assign system_mode_cs = cs && ~system_mode; + assign read_data = tmp_read_data; + assign ready = ready_reg; //---------------------------------------------------------------- @@ -56,12 +53,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank0), + .RE(en & cs & bank0), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[15 : 0]), - .WE((|we & system_mode_cs & bank0)), + .WE((|we & en & cs & bank0)), .MASK({{8{~we[1]}}, {8{~we[0]}}}) ); @@ -70,12 +67,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank0), + .RE(en & cs & bank0), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[31 : 16]), - .WE((|we & system_mode_cs & bank0)), + .WE((|we & en & cs & bank0)), .MASK({{8{~we[3]}}, {8{~we[2]}}}) ); @@ -85,12 +82,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank1), + .RE(en & cs & bank1), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[15 : 0]), - .WE((|we & system_mode_cs & bank1)), + .WE((|we & en & cs & bank1)), .MASK({{8{~we[1]}}, {8{~we[0]}}}) ); @@ -99,12 +96,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank1), + .RE(en & cs & bank1), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[31 : 16]), - .WE((|we & system_mode_cs & bank1)), + .WE((|we & en & cs & bank1)), .MASK({{8{~we[3]}}, {8{~we[2]}}}) ); @@ -129,7 +126,7 @@ module fw_ram ( bank1 = 1'h0; tmp_read_data = 32'h0; - if (system_mode_cs) begin + if (en & cs) begin if (address[8]) begin bank1 = 1'h1; tmp_read_data = mem_read_data1; diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index 84666ac..6366075 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -48,6 +48,8 @@ module tk1 #( input wire access_level_hi, input wire access_level_med, + output wire fw_ram_en, + input wire cs, input wire we, input wire [ 7 : 0] address, @@ -202,6 +204,7 @@ module tk1 #( assign system_reset = system_reset_reg; assign rom_exec_en = !system_mode | access_level_med | access_level_hi; + assign fw_ram_en = !system_mode | access_level_hi; //---------------------------------------------------------------- // Module instance. diff --git a/hw/application_fpga/fw/irqpoc_with_app/start.S b/hw/application_fpga/fw/irqpoc_with_app/start.S index 05b1f3a..67860eb 100644 --- a/hw/application_fpga/fw/irqpoc_with_app/start.S +++ b/hw/application_fpga/fw/irqpoc_with_app/start.S @@ -11,6 +11,8 @@ #include "custom_ops.S" // PicoRV32 custom instructions +#define illegal_insn() .word 0 + .section ".text.init" .globl _start _start: @@ -24,18 +26,38 @@ irq_handler: // PicoRV32 stores the IRQ bitmask in x4. // If bit 31 is 1: IRQ31 was triggered. // If bit 30 is 1: IRQ30 was triggered. - - nop // NOPs are not necessary. Only added to make it easier to find - nop // when simulating. - nop +irq_syscall_lo_check: + li t4, (1 << 30) + bne x4, t4, irq_syscall_hi_check + // Firmware RAM should not be readable from IRQ_SYSCALL_LO + call check_cannot_read_test_val_from_fw_ram + j irq_source_check_done +irq_syscall_hi_check: + li t4, (1 << 31) + bne x4, t4, unexpected_irq + // Firmware RAM should be readable from IRQ_SYSCALL_HI + call check_can_read_test_val_from_fw_ram + j irq_source_check_done +unexpected_irq: + illegal_insn() +irq_source_check_done: picorv32_retirq_insn() // Return from interrupt // // Init // - .=0x20 // Setting location of init to 0x20. Makes it easier to find when - // simulating. + .=0x100 init: + // Save test value in firmware RAM + li t0, 0xd0000000 + li t1, 0x5555aaaa + sw t1, 0(t0) + + // Firmware RAM should be readable from firmware mode + call check_can_read_test_val_from_fw_ram + + + // Enable IRQs li t0, 0x3fffffff // IRQ31 & IRQ30 mask picorv32_maskirq_insn(zero, t0) // Enable IRQs @@ -59,10 +81,15 @@ copy_app: // .align 4 app_start: + // Firmware RAM should not be readable from app mode + call check_cannot_read_test_val_from_fw_ram + + // Raise IRQ_SYSCALL_HI li t0, 0xe1000000 // IRQ_SYSCALL_HI (IRQ31) trigger address sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. // Writing any data triggers an interrupt. + // Raise IRQ_SYSCALL_LO li t0, 0xe0000000 // IRQ_SYSCALL_LO (IRQ30) trigger address sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. // Writing any data triggers an interrupt. @@ -70,6 +97,27 @@ app_start: jalr zero, 0(zero) // Jumping to firmware. Expecting trap app_loop: j app_loop + + +check_cannot_read_test_val_from_fw_ram: + li t0, 0xd0000000 + lw t1, 0(t0) + li t2, 0 + bne t1, t2, cannot_read_test_val_from_fw_ram_fail + ret +cannot_read_test_val_from_fw_ram_fail: + illegal_insn() + +check_can_read_test_val_from_fw_ram: + // Check that saved test value can not be read while in app mode + li t0, 0xd0000000 + lw t1, 0(t0) + li t2, 0x5555aaaa + bne t1, t2, can_read_test_val_from_fw_ram_fail + ret +can_read_test_val_from_fw_ram_fail: + illegal_insn() + .align 4 app_end: diff --git a/hw/application_fpga/rtl/application_fpga.v b/hw/application_fpga/rtl/application_fpga.v index d937ec7..113a362 100644 --- a/hw/application_fpga/rtl/application_fpga.v +++ b/hw/application_fpga/rtl/application_fpga.v @@ -135,6 +135,7 @@ module application_fpga ( reg [31 : 0] fw_ram_write_data; wire [31 : 0] fw_ram_read_data; wire fw_ram_ready; + wire fw_ram_en; reg touch_sense_cs; reg touch_sense_we; @@ -257,7 +258,7 @@ module application_fpga ( .clk(clk), .reset_n(reset_n), - .system_mode(system_mode), + .en(fw_ram_en), .cs(fw_ram_cs), .we(fw_ram_we), @@ -370,6 +371,8 @@ module application_fpga ( .access_level_hi (irq31_eoi), .access_level_med(irq30_eoi), + .fw_ram_en(fw_ram_en), + .cs(tk1_cs), .we(tk1_we), .address(tk1_address), diff --git a/hw/application_fpga/tb/application_fpga_sim.v b/hw/application_fpga/tb/application_fpga_sim.v index 72b528f..cbe95d2 100644 --- a/hw/application_fpga/tb/application_fpga_sim.v +++ b/hw/application_fpga/tb/application_fpga_sim.v @@ -147,6 +147,7 @@ module application_fpga_sim ( reg [31 : 0] fw_ram_write_data; wire [31 : 0] fw_ram_read_data; wire fw_ram_ready; + wire fw_ram_en; reg touch_sense_cs; reg touch_sense_we; @@ -268,8 +269,7 @@ module application_fpga_sim ( .clk(clk), .reset_n(reset_n), - .system_mode(system_mode), - + .en(fw_ram_en), .cs(fw_ram_cs), .we(fw_ram_we), .address(fw_ram_address), @@ -383,6 +383,8 @@ module application_fpga_sim ( .access_level_hi (irq31_eoi), .access_level_med(irq30_eoi), + .fw_ram_en(fw_ram_en), + .cs(tk1_cs), .we(tk1_we), .address(tk1_address), diff --git a/hw/application_fpga/tb/tb_application_fpga_sim.v b/hw/application_fpga/tb/tb_application_fpga_sim.v index a3d5d6d..78c4ebd 100644 --- a/hw/application_fpga/tb/tb_application_fpga_sim.v +++ b/hw/application_fpga/tb/tb_application_fpga_sim.v @@ -80,7 +80,7 @@ module tb_application_fpga_sim (); //---------------------------------------------------------------- initial begin // End simulation after XXX time units (set by timescale) - #1600; + #3000; $display("TIMEOUT"); $finish; end From 4877e0ab99542c2ea7c7f5cfd6370b172e33798c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Fri, 13 Dec 2024 15:41:59 +0100 Subject: [PATCH 08/32] PoC: Add example firmware with embedded that calls syscalls implemented in C App is embedded in firmware and is loaded into app RAM when firmware starts. App continuously calls SET_LED syscalls. Simulation: `make tb_application_fpga_irqpoc_c_example` --- hw/application_fpga/Makefile | 17 ++ .../fw/irqpoc_c_example/Makefile | 9 + .../fw/irqpoc_c_example/custom_ops.S | 102 +++++++ .../fw/irqpoc_c_example/main.c | 58 ++++ .../fw/irqpoc_c_example/start.S | 261 ++++++++++++++++++ .../tb/tb_application_fpga_sim.v | 2 +- 6 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 hw/application_fpga/fw/irqpoc_c_example/Makefile create mode 100644 hw/application_fpga/fw/irqpoc_c_example/custom_ops.S create mode 100644 hw/application_fpga/fw/irqpoc_c_example/main.c create mode 100644 hw/application_fpga/fw/irqpoc_c_example/start.S diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index e042527..56a1a1e 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -156,6 +156,12 @@ IRQPOC_WITH_APP_OBJS = \ $(P)/fw/irqpoc_with_app/main.o \ $(P)/fw/irqpoc_with_app/start.o +IRQPOC_C_EXAMPLE_OBJS = \ + $(P)/fw/irqpoc_c_example/main.o \ + $(P)/fw/irqpoc_c_example/start.o \ + $(P)/fw/tk1/led.o \ + $(P)/fw/tk1/assert.o + #------------------------------------------------------------------- # All: Complete build of HW and FW. #------------------------------------------------------------------- @@ -195,6 +201,7 @@ $(TESTFW_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_LED_TOGGLE_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_WITH_APP_OBJS): $(FIRMWARE_DEPS) +$(IRQPOC_C_EXAMPLE_OBJS): $(FIRMWARE_DEPS) firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ @@ -246,6 +253,9 @@ irqpoc_led_toggle.elf: $(IRQPOC_LED_TOGGLE_OBJS) $(P)/fw/tk1/firmware.lds irqpoc_with_app.elf: $(IRQPOC_WITH_APP_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(IRQPOC_WITH_APP_OBJS) $(LDFLAGS) -o $@ +irqpoc_c_example.elf: $(IRQPOC_C_EXAMPLE_OBJS) $(P)/fw/tk1/firmware.lds + $(CC) $(CFLAGS) $(IRQPOC_C_EXAMPLE_OBJS) $(LDFLAGS) -o $@ + # Generate a fake BRAM file that will be filled in later after place-n-route bram_fw.hex: $(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@ @@ -260,6 +270,8 @@ irqpoc.hex: irqpoc.bin python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ irqpoc_with_app.hex: irqpoc_with_app.bin python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ +irqpoc_c_example.hex: irqpoc_c_example.bin + python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@ .PHONY: check-binary-hashes @@ -480,6 +492,10 @@ tb_application_fpga_irqpoc: irqpoc.hex emptyapp tb_application_fpga tb_application_fpga_irqpoc_with_app: SIMFIRMWARE=irqpoc_with_app.hex tb_application_fpga_irqpoc_with_app: irqpoc_with_app.hex emptyapp tb_application_fpga +.PHONY: tb_application_fpga_irqpoc_c_example +tb_application_fpga_irqpoc_c_example: SIMFIRMWARE=irqpoc_c_example.hex +tb_application_fpga_irqpoc_c_example: irqpoc_c_example.hex emptyapp tb_application_fpga + #------------------------------------------------------------------- # FPGA device programming. @@ -530,6 +546,7 @@ clean_fw: rm -f $(IRQPOC_OBJS) rm -f $(IRQPOC_LED_TOGGLE_OBJS) rm -f $(IRQPOC_WITH_APP_OBJS) + rm -f $(IRQPOC_C_EXAMPLE_OBJS) rm -f qemu_firmware.elf .PHONY: clean_fw diff --git a/hw/application_fpga/fw/irqpoc_c_example/Makefile b/hw/application_fpga/fw/irqpoc_c_example/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_c_example/custom_ops.S b/hw/application_fpga/fw/irqpoc_c_example/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_c_example/main.c b/hw/application_fpga/fw/irqpoc_c_example/main.c new file mode 100644 index 0000000..9531da7 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "../tk1/types.h" +#include "../tk1/led.h" +#include "../tk1/assert.h" + +// Proof-of-concept firmware for handling syscalls. +// This is NOT a best-practice example of secure syscall implementation. + +#define SYSCALL_HI (1 << 31) +#define SYSCALL_LO 0 + +#define SYSCALL_HI_SET_LED (SYSCALL_HI | 10) +#define SYSCALL_LO_SET_LED (SYSCALL_LO | 10) + +static void delay(int32_t count) { + volatile int32_t c = count; + while (c > 0) { + c--; + } +} + +int32_t syscall_lo_handler(uint32_t syscall_nr, uint32_t arg1) { + switch (syscall_nr) { + case SYSCALL_LO_SET_LED: + set_led(arg1); + //delay(1000000); + return 0; + default: + assert(1 == 2); + } + + assert(1 == 2); + return -1; // This should never run +} + +int32_t syscall_hi_handler(uint32_t syscall_nr, uint32_t arg1) { + switch (syscall_nr) { + case SYSCALL_HI_SET_LED: + set_led(arg1); + //delay(500000); + return 0; + default: + assert(1 == 2); + } + + assert(1 == 2); + return -1; // This should never run +} + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_c_example/start.S b/hw/application_fpga/fw/irqpoc_c_example/start.S new file mode 100644 index 0000000..016a3d8 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/start.S @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only +*/ + +// This firmware copies an app from ROM to app RAM. +// The app continuosly calls the SET_LED syscalls in firmware (main.c). +// +#include "../tk1_mem.h" +#include "custom_ops.S" // PicoRV32 custom instructions + +#define illegal_insn() .word 0 + +#define FW_SP_STORAGE (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 4) +#define FW_STACK_TOP (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 2*4) + +#define LED_RED (1 << TK1_MMIO_TK1_LED_R_BIT) +#define LED_GREEN (1 << TK1_MMIO_TK1_LED_G_BIT) +#define LED_BLUE (1 << TK1_MMIO_TK1_LED_B_BIT) + + .section ".text.init" + .globl _start +_start: + j init + +// +// IRQ handler +// + .=0x10 // IRQ handler at fixed address 0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + // If bit 30 is 1: IRQ30 was triggered. +irq_syscall_lo_check: + li t4, (1 << 30) + bne x4, t4, irq_syscall_hi_check + + // Run low privilege syscall handler + call syscall_lo_handler + + j irq_source_check_done +irq_syscall_hi_check: + li t4, (1 << 31) + bne x4, t4, unexpected_irq + + // Save app stack pointer. App is responsible for saving the rest of + // the registers. + la t0, FW_SP_STORAGE + sw sp, 0(t0) + + // Setup firmware stack pointer + la sp, FW_STACK_TOP + + // Run high privilege syscall handler + call syscall_hi_handler + + // Restore app stack pointer + la t0, FW_SP_STORAGE + lw sp, 0(t0) + + j irq_source_check_done +unexpected_irq: + illegal_insn() +irq_source_check_done: + // Verify that interrupt return address is in app RAM + li t0, TK1_RAM_BASE // 0x40000000 + blt x3, t0, x3_invalid + li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000 + bge x3, t0, x3_invalid + j x3_valid +x3_invalid: + illegal_insn() + j x3_invalid +x3_valid: + + // Remove data left over from the syscall handling + mv x0, zero + mv x1, zero + // x2 (sp) is assumed to be preserved by the interrupt handler + // x3 (interrupt return address) assumed preserved + mv x4, zero + mv x5, zero + mv x6, zero + mv x7, zero + mv x8, zero + mv x9, zero + // x10 (a0) contains syscall return value. And should not be destroyed. + mv x11, zero + mv x12, zero + mv x13, zero + mv x14, zero + mv x15, zero + mv x16, zero + mv x17, zero + mv x18, zero + mv x19, zero + mv x20, zero + mv x21, zero + mv x22, zero + mv x23, zero + mv x24, zero + mv x25, zero + mv x26, zero + mv x27, zero + mv x28, zero + mv x29, zero + mv x30, zero + mv x31, zero + + picorv32_retirq_insn() // Return from interrupt + +// +// Init +// + .=0x100 +init: + li t0, TK1_MMIO_TK1_LED + li t1, LED_RED + sw t1, 0(t0) + + // Enable IRQs + li t0, 0x3fffffff // IRQ31 & IRQ30 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + + // Copy app to App RAM + la t0, app_start + la t1, app_end + li t2, 0x40000000 // 0x40000000: App RAM +copy_app: + lw t3, 0(t0) + sw t3, 0(t2) + addi t0, t0, 4 + addi t2, t2, 4 + bleu t0, t1, copy_app + + // Jump to app + li t2, 0x40000000 // 0x40000000: App RAM + jalr zero, 0(t2) + +// +// App +// +#define APP_SYSCALL_HI (1 << 31) +#define APP_SYSCALL_LO 0 +#define APP_SYSCALL_HI_SET_LED (APP_SYSCALL_HI | 10) +#define APP_SYSCALL_LO_SET_LED (APP_SYSCALL_LO | 10) + + .=0x1000 +app_start: + // Set stack pointer to end of app RAM + li sp, 0x4001fffc + +app_loop: + li a0, APP_SYSCALL_LO_SET_LED + li a1, LED_GREEN + call app_syscall + + li a0, APP_SYSCALL_LO_SET_LED + li a1, LED_BLUE + call app_syscall + + li a0, APP_SYSCALL_HI_SET_LED + li a1, LED_RED | LED_GREEN + call app_syscall + + li a0, APP_SYSCALL_HI_SET_LED + li a1, LED_RED | LED_BLUE + call app_syscall + + j app_loop + +app_syscall: + // Save registers to stack + addi sp, sp, -32*4 + sw x0, 0*4(sp) + sw x1, 1*4(sp) + // x2 (sp) is assumed to be preserved by the interrupt handler. + sw x3, 3*4(sp) + sw x4, 4*4(sp) + sw x5, 5*4(sp) + sw x6, 6*4(sp) + sw x7, 7*4(sp) + sw x8, 8*4(sp) + sw x9, 9*4(sp) + // x10 (a0) will contain syscall return value. And should not be saved. + sw x11, 11*4(sp) + sw x12, 12*4(sp) + sw x13, 13*4(sp) + sw x14, 14*4(sp) + sw x15, 15*4(sp) + sw x16, 16*4(sp) + sw x17, 17*4(sp) + sw x18, 18*4(sp) + sw x19, 19*4(sp) + sw x20, 20*4(sp) + sw x21, 21*4(sp) + sw x22, 22*4(sp) + sw x23, 23*4(sp) + sw x24, 24*4(sp) + sw x25, 25*4(sp) + sw x26, 26*4(sp) + sw x27, 27*4(sp) + sw x28, 28*4(sp) + sw x29, 29*4(sp) + sw x30, 30*4(sp) + sw x31, 31*4(sp) + + // Raise interrupt depending on bit 31 in a0 + // 0: Low privilege syscall + // 1: High privilege syscall + li t0, (1 << 31) + and t0, a0, t0 + beqz t0, app_syscall_low_priv + li t1, 0xe1000000 // High privilege interrupt + sw zero, 0(t1) // Trigger interrupt + j app_syscall_restore_registers +app_syscall_low_priv: + li t1, 0xe0000000 // Low privelege interrupt + sw zero, 0(t1) // Trigger interrupt + +app_syscall_restore_registers: + // Restore registers from stack + lw x0, 0*4(sp) + lw x1, 1*4(sp) + // x2 (sp) is assumed to be preserved by the interrupt handler. + lw x3, 3*4(sp) + lw x4, 4*4(sp) + lw x5, 5*4(sp) + lw x6, 6*4(sp) + lw x7, 7*4(sp) + lw x8, 8*4(sp) + lw x9, 9*4(sp) + // x10 (a0) contains syscall return value. And should not be destroyed. + lw x11, 11*4(sp) + lw x12, 12*4(sp) + lw x13, 13*4(sp) + lw x14, 14*4(sp) + lw x15, 15*4(sp) + lw x16, 16*4(sp) + lw x17, 17*4(sp) + lw x18, 18*4(sp) + lw x19, 19*4(sp) + lw x20, 20*4(sp) + lw x21, 21*4(sp) + lw x22, 22*4(sp) + lw x23, 23*4(sp) + lw x24, 24*4(sp) + lw x25, 25*4(sp) + lw x26, 26*4(sp) + lw x27, 27*4(sp) + lw x28, 28*4(sp) + lw x29, 29*4(sp) + lw x30, 30*4(sp) + lw x31, 31*4(sp) + addi sp, sp, 32*4 + + ret + + .align 4 +app_end: + diff --git a/hw/application_fpga/tb/tb_application_fpga_sim.v b/hw/application_fpga/tb/tb_application_fpga_sim.v index 78c4ebd..6024ad9 100644 --- a/hw/application_fpga/tb/tb_application_fpga_sim.v +++ b/hw/application_fpga/tb/tb_application_fpga_sim.v @@ -80,7 +80,7 @@ module tb_application_fpga_sim (); //---------------------------------------------------------------- initial begin // End simulation after XXX time units (set by timescale) - #3000; + #14000; $display("TIMEOUT"); $finish; end From ecdbb2501366e9628bb86c90d514649c6e85e5ef Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Wed, 13 Nov 2024 16:13:16 +0100 Subject: [PATCH 09/32] PoC: Deny access to the SPI master in app mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikael Ă…gren --- hw/application_fpga/README.md | 12 ++++++------ hw/application_fpga/core/tk1/rtl/tk1.v | 26 +++++++++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 44e300c..80fd9ba 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -139,12 +139,12 @@ Interrupts can be enabled/disabled using the PicoRV32 specific The following table shows resource availablility for each execution mode: -| *Execution Mode* | *ROM* | *FW RAM* | -|---------------------|--------|----------| -| Firmware mode | r/x | r/w | -| App mode | r | i | -| IRQ_SYSCALL_LO | r/x | i | -| IRQ_SYSCALL_HI | r/x | r/w | +| *Execution Mode* | *ROM* | *FW RAM* | *SPI* | +|---------------------|--------|----------|-------| +| Firmware mode | r/x | r/w | r/w | +| App mode | r | i | i | +| IRQ_SYSCALL_LO | r/x | i | i | +| IRQ_SYSCALL_HI | r/x | r/w | r/w | Legend: r = readable diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index 6366075..a3856c6 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -182,6 +182,7 @@ module tk1 #( reg spi_tx_data_vld; wire spi_ready; wire [ 7 : 0] spi_rx_data; + wire spi_access_en; wire rom_exec_en; @@ -205,6 +206,7 @@ module tk1 #( assign rom_exec_en = !system_mode | access_level_med | access_level_hi; assign fw_ram_en = !system_mode | access_level_hi; + assign spi_access_en = !system_mode | access_level_hi; //---------------------------------------------------------------- // Module instance. @@ -461,8 +463,8 @@ module tk1 #( spi_start = 1'h0; spi_tx_data_vld = 1'h0; - spi_enable = write_data[0]; - spi_tx_data = write_data[7 : 0]; + spi_enable = write_data[0] & spi_access_en; + spi_tx_data = write_data[7 : 0] & {8{spi_access_en}}; if (cs) begin tmp_ready = 1'h1; @@ -527,15 +529,21 @@ module tk1 #( end if (address == ADDR_SPI_EN) begin - spi_enable_vld = 1'h1; + if (spi_access_en) begin + spi_enable_vld = 1'h1; + end end if (address == ADDR_SPI_XFER) begin - spi_start = 1'h1; + if (spi_access_en) begin + spi_start = 1'h1; + end end if (address == ADDR_SPI_DATA) begin - spi_tx_data_vld = 1'h1; + if (spi_access_en) begin + spi_tx_data_vld = 1'h1; + end end end @@ -583,11 +591,15 @@ module tk1 #( end if (address == ADDR_SPI_XFER) begin - tmp_read_data[0] = spi_ready; + if (spi_access_en) begin + tmp_read_data[0] = spi_ready; + end end if (address == ADDR_SPI_DATA) begin - tmp_read_data[7 : 0] = spi_rx_data; + if (spi_access_en) begin + tmp_read_data[7 : 0] = spi_rx_data; + end end end From 7f34f5db911d9300e68b5e52c54d4fa1a41d9b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Mon, 16 Dec 2024 13:00:54 +0100 Subject: [PATCH 10/32] PoC: Remove low privilege syscall --- hw/application_fpga/README.md | 22 +++++---------- hw/application_fpga/core/tk1/rtl/tk1.v | 3 +- hw/application_fpga/rtl/application_fpga.v | 26 +++-------------- hw/application_fpga/tb/application_fpga_sim.v | 28 +++---------------- 4 files changed, 16 insertions(+), 63 deletions(-) diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 80fd9ba..ee25709 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -35,7 +35,6 @@ Rough memory map: | UART | 0xc3 | | Touch | 0xc4 | | FW\_RAM | 0xd0 | -| IRQ30\_SET | 0xe0 | | IRQ31\_SET | 0xe1 | | TK1 | 0xff | @@ -99,11 +98,6 @@ hours, days) there is also a 32 bit prescaler. The timer is available to use by firmware and applications. -## `irq30_set` - -Interrupt 30 trigger area. A 32-bit write to the IRQ30\_SET memory -area will trigger interrupt 30. - ## `irq31_set` Interrupt 31 trigger area. A 32-bit write to the IRQ31\_SET memory @@ -114,15 +108,14 @@ area will trigger interrupt 31. Triggering an interrupt will cause the CPU to execute the interrupt handler att address 0x10. -The interrupt handler is shared by IRQ30 and IRQ31. Register `x4` can -be inspected to determine the interrupt source. Each interrupt source -is assigned one bit in x4. Triggered interrupts have their bit set to -`1`. +The interrupt handler is shared by all PicoRV32 interrupts but only +interrupt 31 is enabled on the Tkey. Register `x4` can be inspected to +determine the interrupt source. Each interrupt source is assigned one +bit in x4. Triggered interrupts have their bit set to `1`. | *Interrupt Name* | *Source* | *x4 Bit* | |------------------|------------|----------| -| IRQ_SYSCALL_LO | IRQ30\_SET | 30 | -| IRQ_SYSCALL_HI | IRQ31\_SET | 31 | +| IRQ_SYSCALL | IRQ31\_SET | 31 | The return address is located in register `x3`. Calling the PicoRV32 specific instruction `retirq` exits the interrupt handler and clears @@ -142,9 +135,8 @@ mode: | *Execution Mode* | *ROM* | *FW RAM* | *SPI* | |---------------------|--------|----------|-------| | Firmware mode | r/x | r/w | r/w | -| App mode | r | i | i | -| IRQ_SYSCALL_LO | r/x | i | i | -| IRQ_SYSCALL_HI | r/x | r/w | r/w | +| IRQ_SYSCALL | r/x | r/w | r/w | +| Application mode | r | i | i | Legend: r = readable diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index a3856c6..41b9579 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -46,7 +46,6 @@ module tk1 #( output wire gpio4, input wire access_level_hi, - input wire access_level_med, output wire fw_ram_en, @@ -204,7 +203,7 @@ module tk1 #( assign system_reset = system_reset_reg; - assign rom_exec_en = !system_mode | access_level_med | access_level_hi; + assign rom_exec_en = !system_mode | access_level_hi; assign fw_ram_en = !system_mode | access_level_hi; assign spi_access_en = !system_mode | access_level_hi; diff --git a/hw/application_fpga/rtl/application_fpga.v b/hw/application_fpga/rtl/application_fpga.v index 113a362..779d1bb 100644 --- a/hw/application_fpga/rtl/application_fpga.v +++ b/hw/application_fpga/rtl/application_fpga.v @@ -54,14 +54,12 @@ module application_fpga ( localparam UART_PREFIX = 6'h03; localparam TOUCH_SENSE_PREFIX = 6'h04; localparam FW_RAM_PREFIX = 6'h10; - localparam IRQ30_PREFIX = 6'h20; localparam IRQ31_PREFIX = 6'h21; localparam TK1_PREFIX = 6'h3f; // Instruction used to cause a trap. localparam ILLEGAL_INSTRUCTION = 32'h0; - localparam IRQ30_IRQ_MASK = 2 ** 30; localparam IRQ31_IRQ_MASK = 2 ** 31; //---------------------------------------------------------------- @@ -143,10 +141,6 @@ module application_fpga ( wire [31 : 0] touch_sense_read_data; wire touch_sense_ready; - reg irq30_cs; - reg irq30_we; - reg irq30_eoi; - reg irq31_cs; reg irq31_we; reg irq31_eoi; @@ -187,8 +181,8 @@ module application_fpga ( .ENABLE_IRQ (1), .ENABLE_IRQ_QREGS(0), .ENABLE_IRQ_TIMER(0), - .MASKED_IRQ (~(IRQ31_IRQ_MASK | IRQ30_IRQ_MASK)), - .LATCHED_IRQ (IRQ31_IRQ_MASK | IRQ30_IRQ_MASK) + .MASKED_IRQ (~IRQ31_IRQ_MASK), + .LATCHED_IRQ (IRQ31_IRQ_MASK) ) cpu ( .clk(clk), .resetn(reset_n), @@ -368,8 +362,7 @@ module application_fpga ( .gpio3(app_gpio3), .gpio4(app_gpio4), - .access_level_hi (irq31_eoi), - .access_level_med(irq30_eoi), + .access_level_hi(irq31_eoi), .fw_ram_en(fw_ram_en), @@ -405,14 +398,11 @@ module application_fpga ( //---------------------------------------------------------------- always @* begin : irq_ctrl reg irq31_set; - reg irq30_set; irq31_set = irq31_cs & irq31_we; - irq30_set = irq30_cs & irq30_we; - cpu_irq = {irq31_set, irq30_set, 30'h0}; + cpu_irq = {irq31_set, 31'h0}; irq31_eoi = cpu_eoi[31]; - irq30_eoi = cpu_eoi[30]; end @@ -465,9 +455,6 @@ module application_fpga ( touch_sense_we = |cpu_wstrb; touch_sense_address = cpu_addr[9 : 2]; - irq30_cs = 1'h0; - irq30_we = |cpu_wstrb; - irq31_cs = 1'h0; irq31_we = |cpu_wstrb; @@ -543,11 +530,6 @@ module application_fpga ( muxed_ready_new = fw_ram_ready; end - IRQ30_PREFIX: begin - irq30_cs = 1'h1; - muxed_ready_new = 1'h1; - end - IRQ31_PREFIX: begin irq31_cs = 1'h1; muxed_ready_new = 1'h1; diff --git a/hw/application_fpga/tb/application_fpga_sim.v b/hw/application_fpga/tb/application_fpga_sim.v index cbe95d2..926768c 100644 --- a/hw/application_fpga/tb/application_fpga_sim.v +++ b/hw/application_fpga/tb/application_fpga_sim.v @@ -67,14 +67,12 @@ module application_fpga_sim ( localparam UART_PREFIX = 6'h03; localparam TOUCH_SENSE_PREFIX = 6'h04; localparam FW_RAM_PREFIX = 6'h10; - localparam IRQ30_PREFIX = 6'h20; localparam IRQ31_PREFIX = 6'h21; localparam TK1_PREFIX = 6'h3f; // Instruction used to cause a trap. localparam ILLEGAL_INSTRUCTION = 32'h0; - localparam IRQ30_IRQ_MASK = 2 ** 30; localparam IRQ31_IRQ_MASK = 2 ** 31; //---------------------------------------------------------------- @@ -155,10 +153,6 @@ module application_fpga_sim ( wire [31 : 0] touch_sense_read_data; wire touch_sense_ready; - reg irq30_cs; - reg irq30_we; - reg irq30_eoi; - reg irq31_cs; reg irq31_we; reg irq31_eoi; @@ -198,8 +192,8 @@ module application_fpga_sim ( .ENABLE_IRQ (1), .ENABLE_IRQ_QREGS(0), .ENABLE_IRQ_TIMER(0), - .MASKED_IRQ (~(IRQ31_IRQ_MASK | IRQ30_IRQ_MASK)), - .LATCHED_IRQ (IRQ31_IRQ_MASK | IRQ30_IRQ_MASK) + .MASKED_IRQ (~IRQ31_IRQ_MASK), + .LATCHED_IRQ (IRQ31_IRQ_MASK) ) cpu ( .clk(clk), .resetn(reset_n), @@ -380,8 +374,7 @@ module application_fpga_sim ( .gpio3(app_gpio3), .gpio4(app_gpio4), - .access_level_hi (irq31_eoi), - .access_level_med(irq30_eoi), + .access_level_hi(irq31_eoi), .fw_ram_en(fw_ram_en), @@ -416,14 +409,11 @@ module application_fpga_sim ( //---------------------------------------------------------------- always @* begin : irq_ctrl reg irq31_set; - reg irq30_set; irq31_set = irq31_cs & irq31_we; - irq30_set = irq30_cs & irq30_we; - cpu_irq = {irq31_set, irq30_set, 30'h0}; + cpu_irq = {irq31_set, 31'h0}; irq31_eoi = cpu_eoi[31]; - irq30_eoi = cpu_eoi[30]; end @@ -478,9 +468,6 @@ module application_fpga_sim ( touch_sense_we = |cpu_wstrb; touch_sense_address = cpu_addr[9 : 2]; - irq30_cs = 1'h0; - irq30_we = |cpu_wstrb; - irq31_cs = 1'h0; irq31_we = |cpu_wstrb; @@ -576,13 +563,6 @@ module application_fpga_sim ( muxed_ready_new = fw_ram_ready; end - IRQ30_PREFIX: begin - `verbose($display("Access to blake2s interrupt trigger");) - ascii_state = "Blake2s IRQ trigger"; - irq30_cs = 1'h1; - muxed_ready_new = 1'h1; - end - IRQ31_PREFIX: begin `verbose($display("Access to syscall interrupt trigger");) ascii_state = "Syscall IRQ trigger"; From 2ec2196e92c26547805fce2ccb078fc1e4e96d15 Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Fri, 15 Nov 2024 11:19:40 +0100 Subject: [PATCH 11/32] PoC: Make sensitive assets only readable/writable before system_mode is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the first time system_mode is set to one, the assets will no longer be read- or writeable, even if system_mode is set to zero at a later syscall. This is to make sure syscalls does not have the same privilege as the firmware has at first boot. We need to monitor when system_mode is set to one, otherwise we might accedentially lock the assets before actually leaving firmware, for example if firmware would use a function set in any of the registers used in system_mode_ctrl. Co-authored-by: Mikael Ă…gren --- hw/application_fpga/README.md | 29 +++++++++++++++---- hw/application_fpga/core/tk1/rtl/tk1.v | 21 ++++++++------ hw/application_fpga/core/uds/rtl/uds.v | 5 ++-- hw/application_fpga/rtl/application_fpga.v | 7 ++--- hw/application_fpga/tb/application_fpga_sim.v | 6 ++-- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index ee25709..1ebeb5a 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -132,17 +132,36 @@ Interrupts can be enabled/disabled using the PicoRV32 specific The following table shows resource availablility for each execution mode: -| *Execution Mode* | *ROM* | *FW RAM* | *SPI* | -|---------------------|--------|----------|-------| -| Firmware mode | r/x | r/w | r/w | -| IRQ_SYSCALL | r/x | r/w | r/w | -| Application mode | r | i | i | +| *Execution Mode* | *ROM* | *FW RAM* | *SPI* | *Sensitive assets* | +|------------------|--------|----------|-------|--------------------| +| Firmware mode | r/x | r/w | r/w | r/w* | +| IRQ_SYSCALL | r/x | r/w | r/w | r* | +| Application mode | r | i | i | r* | Legend: r = readable w = writeable x = executable i = invisible +* = read-/writeability varies, see below + +These sensitive assets are only readable and/or writeable in firmware +mode: +- APP_START +- APP_SIZE +- CDI_FIRST +- CDI_LAST +- RAM_ADDR_RAND +- RAM_DATA_RAND +- UDI_FIRST +- UDI_LAST +- UDS_FIRST +- UDS_LAST + +Note that these assets have different properties, some are read-only +and some are write-only. The list above only shows if they are +restricted in app mode. See each individual API to find out more about +their properties. ## `tk1` diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index 41b9579..af09b28 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -20,7 +20,7 @@ module tk1 #( input wire reset_n, input wire cpu_trap, - output wire system_mode, + output wire rw_locked, input wire [31 : 0] cpu_addr, input wire cpu_instr, @@ -185,14 +185,14 @@ module tk1 #( wire rom_exec_en; + wire system_mode; + //---------------------------------------------------------------- // Concurrent connectivity for ports etc. //---------------------------------------------------------------- assign read_data = tmp_read_data; assign ready = tmp_ready; - assign system_mode = system_mode_reg; - assign force_trap = force_trap_reg; assign gpio3 = gpio3_reg; @@ -203,9 +203,12 @@ module tk1 #( assign system_reset = system_reset_reg; + assign system_mode = system_mode_reg; + assign rom_exec_en = !system_mode | access_level_hi; assign fw_ram_en = !system_mode | access_level_hi; assign spi_access_en = !system_mode | access_level_hi; + assign rw_locked = system_mode; //---------------------------------------------------------------- // Module instance. @@ -478,13 +481,13 @@ module tk1 #( end if (address == ADDR_APP_START) begin - if (!system_mode_reg) begin + if (!rw_locked) begin app_start_we = 1'h1; end end if (address == ADDR_APP_SIZE) begin - if (!system_mode_reg) begin + if (!rw_locked) begin app_size_we = 1'h1; end end @@ -494,19 +497,19 @@ module tk1 #( end if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin - if (!system_mode_reg) begin + if (!rw_locked) begin cdi_mem_we = 1'h1; end end if (address == ADDR_RAM_ADDR_RAND) begin - if (!system_mode_reg) begin + if (!rw_locked) begin ram_addr_rand_we = 1'h1; end end if (address == ADDR_RAM_DATA_RAND) begin - if (!system_mode_reg) begin + if (!rw_locked) begin ram_data_rand_we = 1'h1; end end @@ -584,7 +587,7 @@ module tk1 #( end if ((address >= ADDR_UDI_FIRST) && (address <= ADDR_UDI_LAST)) begin - if (!system_mode_reg) begin + if (!rw_locked) begin tmp_read_data = udi_rdata; end end diff --git a/hw/application_fpga/core/uds/rtl/uds.v b/hw/application_fpga/core/uds/rtl/uds.v index 2aaa112..8c8f1a3 100644 --- a/hw/application_fpga/core/uds/rtl/uds.v +++ b/hw/application_fpga/core/uds/rtl/uds.v @@ -17,8 +17,7 @@ module uds ( input wire clk, input wire reset_n, - input wire system_mode, - + input wire en, input wire cs, input wire [ 2 : 0] address, output wire [31 : 0] read_data, @@ -89,7 +88,7 @@ module uds ( if (cs) begin tmp_ready = 1'h1; - if (!system_mode) begin + if (en) begin if (uds_rd_reg[address[2 : 0]] == 1'h0) begin uds_rd_we = 1'h1; end diff --git a/hw/application_fpga/rtl/application_fpga.v b/hw/application_fpga/rtl/application_fpga.v index 779d1bb..13a7827 100644 --- a/hw/application_fpga/rtl/application_fpga.v +++ b/hw/application_fpga/rtl/application_fpga.v @@ -151,7 +151,7 @@ module application_fpga ( reg [31 : 0] tk1_write_data; wire [31 : 0] tk1_read_data; wire tk1_ready; - wire system_mode; + wire rw_locked; wire force_trap; wire [14 : 0] ram_addr_rand; wire [31 : 0] ram_data_rand; @@ -253,7 +253,6 @@ module application_fpga ( .reset_n(reset_n), .en(fw_ram_en), - .cs(fw_ram_cs), .we(fw_ram_we), .address(fw_ram_address), @@ -292,7 +291,7 @@ module application_fpga ( .clk(clk), .reset_n(reset_n), - .system_mode(system_mode), + .en(~rw_locked), .cs(uds_cs), .address(uds_address), @@ -335,7 +334,7 @@ module application_fpga ( .clk(clk), .reset_n(reset_n), - .system_mode(system_mode), + .rw_locked(rw_locked), .cpu_addr (cpu_addr), .cpu_instr (cpu_instr), diff --git a/hw/application_fpga/tb/application_fpga_sim.v b/hw/application_fpga/tb/application_fpga_sim.v index 926768c..2fa40e2 100644 --- a/hw/application_fpga/tb/application_fpga_sim.v +++ b/hw/application_fpga/tb/application_fpga_sim.v @@ -163,7 +163,7 @@ module application_fpga_sim ( reg [31 : 0] tk1_write_data; wire [31 : 0] tk1_read_data; wire tk1_ready; - wire system_mode; + wire rw_locked; wire force_trap; wire [14 : 0] ram_addr_rand; wire [31 : 0] ram_data_rand; @@ -302,7 +302,7 @@ module application_fpga_sim ( .clk(clk), .reset_n(reset_n), - .system_mode(system_mode), + .en(~rw_locked), .cs(uds_cs), .address(uds_address), @@ -347,7 +347,7 @@ module application_fpga_sim ( .clk(clk), .reset_n(reset_n), - .system_mode(system_mode), + .rw_locked(rw_locked), .cpu_addr (cpu_addr), .cpu_instr (cpu_instr), From 052029236d84ab142387ab77d3d8049f07f81945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 17 Dec 2024 12:45:48 +0100 Subject: [PATCH 12/32] PoC: Remove IRQ30 from fw/irqpoc Removing IRQ30 since it us no longer exist --- hw/application_fpga/fw/irqpoc/start.S | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/application_fpga/fw/irqpoc/start.S b/hw/application_fpga/fw/irqpoc/start.S index cb67e02..def6783 100644 --- a/hw/application_fpga/fw/irqpoc/start.S +++ b/hw/application_fpga/fw/irqpoc/start.S @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ +// Example firmware that demonstrates how to raise interrupts +// + #include "custom_ops.S" // PicoRV32 custom instructions .section ".text.init" @@ -14,7 +17,6 @@ _start: irq_handler: // PicoRV32 stores the IRQ bitmask in x4. // If bit 31 is 1: IRQ31 was triggered. - // If bit 30 is 1: IRQ30 was triggered. nop // NOPs are not necessary. Only added to make it easier to find nop // when simulating. @@ -24,16 +26,12 @@ irq_handler: .=0x20 // Setting location of init to 0x20. Makes it easier to find when // simulating. init: - li t0, 0x3fffffff // IRQ31 & IRQ30 mask + li t0, 0x7fffffff // IRQ31 mask picorv32_maskirq_insn(zero, t0) // Enable IRQs li t0, 0xe1000000 // IRQ31 trigger address sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. // Writing any data triggers an interrupt. - - li t0, 0xe0000000 // IRQ30 trigger address - sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. - // Writing any data triggers an interrupt. loop: j loop From 14f266e5064dab0c7096417ccdb3f6508a56998d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 17 Dec 2024 13:09:24 +0100 Subject: [PATCH 13/32] PoC: Remove IRQ30 from fw/irqpoc_led_toggle Removing IRQ30 since it us no longer exist --- .../fw/irqpoc_led_toggle/start.S | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/start.S b/hw/application_fpga/fw/irqpoc_led_toggle/start.S index 12670b1..1b1f558 100644 --- a/hw/application_fpga/fw/irqpoc_led_toggle/start.S +++ b/hw/application_fpga/fw/irqpoc_led_toggle/start.S @@ -3,16 +3,15 @@ * SPDX-License-Identifier: GPL-2.0-only */ -// Triggers both interrupts, one after the other, in an endless loop. The -// interrupt handler updates the LED color depending on which interrupt -// was triggered. +// Example firmware demonstrating setting the LED from the interrupt handler. // The LED color will alterate between blue and green. +// The color will be RED if an interrupt is triggered by an unexpected source. // -// | IRQ number | Color | -// |------------|-------| -// | 30 | Blue | -// | 31 | Green | -// | Unexpected | Red | +// | Color | Execution context | +// |-------|-------------------| +// | Blue | Firmware loop | +// | Green | IRQ31 | +// | Red | Unexpected IRQ | // #include "custom_ops.S" // PicoRV32 custom instructions @@ -27,14 +26,6 @@ _start: irq_handler: // PicoRV32 stores the IRQ bitmask in x4. // If bit 31 is 1: IRQ31 was triggered. - // If bit 30 is 1: IRQ30 was triggered. -irq30_check: - li t4, (1 << 30) - bne x4, t4, irq31_check - call led_blue - call delay - j irq_source_check_done -irq31_check: li t4, (1 << 31) bne x4, t4, unexpected_irq call led_green @@ -48,17 +39,17 @@ irq_source_check_done: init: - li t0, 0x3fffffff // IRQ31 & IRQ30 mask + li t0, 0x7fffffff // IRQ31 mask picorv32_maskirq_insn(zero, t0) // Enable IRQs irq_trigger_loop: + call led_blue + call delay + li t0, 0xe1000000 // IRQ31 trigger address sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. // Writing any data triggers an interrupt. - li t0, 0xe0000000 // IRQ30 trigger address - sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. - // Writing any data triggers an interrupt. j irq_trigger_loop led_red: From 82d408f405beb3ef65c516e0b7c26004335271b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 17 Dec 2024 13:40:31 +0100 Subject: [PATCH 14/32] PoC: Remove IRQ30 from fw/irqpoc_with_app Removing IRQ30 since it us no longer exist --- .../fw/irqpoc_with_app/start.S | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/hw/application_fpga/fw/irqpoc_with_app/start.S b/hw/application_fpga/fw/irqpoc_with_app/start.S index 67860eb..cae085c 100644 --- a/hw/application_fpga/fw/irqpoc_with_app/start.S +++ b/hw/application_fpga/fw/irqpoc_with_app/start.S @@ -3,10 +3,11 @@ * SPDX-License-Identifier: GPL-2.0-only */ -// This firmware copies an app from ROM to app RAM. The app triggers both -// IRQ_SYSCALL_HI and IRQ_SYSCALL_LO. One after the other. Finally, the -// app tries to jump firmware. This should result in a trap since the -// app in executing in app mode. +// This firmware copies an app from ROM to app RAM. The app triggers IRQ_SYSCALL +// Checks are done to make sure that firmware RAM can/cannot be read depending +// on wether we're executing from firmware, syscall or app. +// Finally the app tries to jump firmware. This should result in a trap since +// the app is executing in app mode. // #include "custom_ops.S" // PicoRV32 custom instructions @@ -25,17 +26,9 @@ _start: irq_handler: // PicoRV32 stores the IRQ bitmask in x4. // If bit 31 is 1: IRQ31 was triggered. - // If bit 30 is 1: IRQ30 was triggered. -irq_syscall_lo_check: - li t4, (1 << 30) - bne x4, t4, irq_syscall_hi_check - // Firmware RAM should not be readable from IRQ_SYSCALL_LO - call check_cannot_read_test_val_from_fw_ram - j irq_source_check_done -irq_syscall_hi_check: li t4, (1 << 31) bne x4, t4, unexpected_irq - // Firmware RAM should be readable from IRQ_SYSCALL_HI + // Firmware RAM should be readable from IRQ_SYSCALL call check_can_read_test_val_from_fw_ram j irq_source_check_done unexpected_irq: @@ -56,9 +49,8 @@ init: // Firmware RAM should be readable from firmware mode call check_can_read_test_val_from_fw_ram - // Enable IRQs - li t0, 0x3fffffff // IRQ31 & IRQ30 mask + li t0, 0x7fffffff // IRQ31 picorv32_maskirq_insn(zero, t0) // Enable IRQs // Copy app to App RAM @@ -84,13 +76,8 @@ app_start: // Firmware RAM should not be readable from app mode call check_cannot_read_test_val_from_fw_ram - // Raise IRQ_SYSCALL_HI - li t0, 0xe1000000 // IRQ_SYSCALL_HI (IRQ31) trigger address - sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. - // Writing any data triggers an interrupt. - - // Raise IRQ_SYSCALL_LO - li t0, 0xe0000000 // IRQ_SYSCALL_LO (IRQ30) trigger address + // Raise IRQ_SYSCALL + li t0, 0xe1000000 // IRQ_SYSCALL (IRQ31) trigger address sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. // Writing any data triggers an interrupt. From 7f95e0912fb1e5d29ce71a51432dd34ada090f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 17 Dec 2024 14:38:01 +0100 Subject: [PATCH 15/32] PoC: Remove IRQ30 from fw/irqpoc_c_example Removing IRQ30 since it us no longer exist --- .../fw/irqpoc_c_example/main.c | 32 +-------- .../fw/irqpoc_c_example/start.S | 68 +++++++------------ 2 files changed, 27 insertions(+), 73 deletions(-) diff --git a/hw/application_fpga/fw/irqpoc_c_example/main.c b/hw/application_fpga/fw/irqpoc_c_example/main.c index 9531da7..dc2a0d6 100644 --- a/hw/application_fpga/fw/irqpoc_c_example/main.c +++ b/hw/application_fpga/fw/irqpoc_c_example/main.c @@ -10,38 +10,12 @@ // Proof-of-concept firmware for handling syscalls. // This is NOT a best-practice example of secure syscall implementation. -#define SYSCALL_HI (1 << 31) -#define SYSCALL_LO 0 +#define SYSCALL_SET_LED 10 -#define SYSCALL_HI_SET_LED (SYSCALL_HI | 10) -#define SYSCALL_LO_SET_LED (SYSCALL_LO | 10) - -static void delay(int32_t count) { - volatile int32_t c = count; - while (c > 0) { - c--; - } -} - -int32_t syscall_lo_handler(uint32_t syscall_nr, uint32_t arg1) { +int32_t syscall_handler(uint32_t syscall_nr, uint32_t arg1) { switch (syscall_nr) { - case SYSCALL_LO_SET_LED: + case SYSCALL_SET_LED: set_led(arg1); - //delay(1000000); - return 0; - default: - assert(1 == 2); - } - - assert(1 == 2); - return -1; // This should never run -} - -int32_t syscall_hi_handler(uint32_t syscall_nr, uint32_t arg1) { - switch (syscall_nr) { - case SYSCALL_HI_SET_LED: - set_led(arg1); - //delay(500000); return 0; default: assert(1 == 2); diff --git a/hw/application_fpga/fw/irqpoc_c_example/start.S b/hw/application_fpga/fw/irqpoc_c_example/start.S index 016a3d8..d295ff6 100644 --- a/hw/application_fpga/fw/irqpoc_c_example/start.S +++ b/hw/application_fpga/fw/irqpoc_c_example/start.S @@ -4,7 +4,7 @@ */ // This firmware copies an app from ROM to app RAM. -// The app continuosly calls the SET_LED syscalls in firmware (main.c). +// The app continuosly calls the SET_LED syscall in firmware (main.c). // #include "../tk1_mem.h" #include "custom_ops.S" // PicoRV32 custom instructions @@ -30,18 +30,12 @@ _start: irq_handler: // PicoRV32 stores the IRQ bitmask in x4. // If bit 31 is 1: IRQ31 was triggered. - // If bit 30 is 1: IRQ30 was triggered. -irq_syscall_lo_check: - li t4, (1 << 30) - bne x4, t4, irq_syscall_hi_check - - // Run low privilege syscall handler - call syscall_lo_handler - - j irq_source_check_done -irq_syscall_hi_check: li t4, (1 << 31) - bne x4, t4, unexpected_irq + beq x4, t4, irq_source_ok +unexpected_irq_source: + illegal_insn() + j unexpected_irq_source +irq_source_ok: // Save app stack pointer. App is responsible for saving the rest of // the registers. @@ -51,18 +45,14 @@ irq_syscall_hi_check: // Setup firmware stack pointer la sp, FW_STACK_TOP - // Run high privilege syscall handler - call syscall_hi_handler + // Run syscall handler + call syscall_handler // Restore app stack pointer la t0, FW_SP_STORAGE lw sp, 0(t0) - j irq_source_check_done -unexpected_irq: - illegal_insn() -irq_source_check_done: - // Verify that interrupt return address is in app RAM + // Verify that interrupt return address (x3) is in app RAM li t0, TK1_RAM_BASE // 0x40000000 blt x3, t0, x3_invalid li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000 @@ -119,7 +109,7 @@ init: sw t1, 0(t0) // Enable IRQs - li t0, 0x3fffffff // IRQ31 & IRQ30 mask + li t0, 0x7fffffff // IRQ31 mask picorv32_maskirq_insn(zero, t0) // Enable IRQs // Copy app to App RAM @@ -140,10 +130,7 @@ copy_app: // // App // -#define APP_SYSCALL_HI (1 << 31) -#define APP_SYSCALL_LO 0 -#define APP_SYSCALL_HI_SET_LED (APP_SYSCALL_HI | 10) -#define APP_SYSCALL_LO_SET_LED (APP_SYSCALL_LO | 10) +#define SYSCALL_SET_LED 10 .=0x1000 app_start: @@ -151,21 +138,15 @@ app_start: li sp, 0x4001fffc app_loop: - li a0, APP_SYSCALL_LO_SET_LED + li a0, SYSCALL_SET_LED li a1, LED_GREEN call app_syscall + call app_delay - li a0, APP_SYSCALL_LO_SET_LED + li a0, SYSCALL_SET_LED li a1, LED_BLUE call app_syscall - - li a0, APP_SYSCALL_HI_SET_LED - li a1, LED_RED | LED_GREEN - call app_syscall - - li a0, APP_SYSCALL_HI_SET_LED - li a1, LED_RED | LED_BLUE - call app_syscall + call app_delay j app_loop @@ -205,20 +186,12 @@ app_syscall: sw x30, 30*4(sp) sw x31, 31*4(sp) - // Raise interrupt depending on bit 31 in a0 - // 0: Low privilege syscall - // 1: High privilege syscall + // Trigger syscall interrupt li t0, (1 << 31) and t0, a0, t0 - beqz t0, app_syscall_low_priv - li t1, 0xe1000000 // High privilege interrupt - sw zero, 0(t1) // Trigger interrupt - j app_syscall_restore_registers -app_syscall_low_priv: - li t1, 0xe0000000 // Low privelege interrupt + li t1, 0xe1000000 // Syscall interrupt trigger address sw zero, 0(t1) // Trigger interrupt -app_syscall_restore_registers: // Restore registers from stack lw x0, 0*4(sp) lw x1, 1*4(sp) @@ -256,6 +229,13 @@ app_syscall_restore_registers: ret +app_delay: + li t5, 0x100000 +app_delay_dec: + addi t5, t5, -1 + bne t5, zero, app_delay_dec + ret + .align 4 app_end: From 3269d25617fbcb7da60546fe3f4643bc2776c5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Fri, 22 Nov 2024 14:16:50 +0100 Subject: [PATCH 16/32] PoC: tb: Write data only once per call to write_word() in tb_tk1 Keep WE and CS high for one clock cycle instead of two. To avoid writing the same address twice. --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 2344849..148e234 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -301,7 +301,7 @@ module tb_tk1 (); tb_write_data = word; tb_cs = 1; tb_we = 1; - #(2 * CLK_PERIOD); + #(CLK_PERIOD); tb_cs = 0; tb_we = 0; end From 93a74bcd1b0987509b6789bea5c44a007c901172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Fri, 22 Nov 2024 14:21:54 +0100 Subject: [PATCH 17/32] tb: Display errors in tb_tk1 even if DEBUG is 0 Always display errors to make them easy to find and troubleshoot. --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 148e234..3d71604 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -18,7 +18,7 @@ module tb_tk1 (); //---------------------------------------------------------------- // Internal constant and parameter definitions. //---------------------------------------------------------------- - parameter DEBUG = 1; + parameter DEBUG = 0; parameter CLK_HALF_PERIOD = 1; parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD; @@ -354,16 +354,15 @@ module tb_tk1 (); #(CLK_PERIOD); tb_cs = 1'h0; - if (DEBUG) begin - if (read_data == expected) begin + if (read_data == expected) begin + if (DEBUG) begin $display("--- Reading 0x%08x from 0x%02x.", read_data, address); end - else begin - $display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data, - address, expected); - error_ctr = error_ctr + 1; - end - $display(""); + end + else begin + $display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data, + address, expected); + error_ctr = error_ctr + 1; end end endtask // read_check_word From 007aa6905262d787d6d2b721c54ef12886c65320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 18 Dec 2024 08:57:49 +0100 Subject: [PATCH 18/32] PoC: tb: Update tk1 test bench with new ports Fixing tests that broke when adding interrupt based syscalls --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 3d71604..7624c27 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -76,7 +76,7 @@ module tb_tk1 (); reg tb_clk; reg tb_reset_n; reg tb_cpu_trap; - wire tb_system_mode; + wire tb_rw_locked; reg [31 : 0] tb_cpu_addr; reg tb_cpu_instr; @@ -95,6 +95,10 @@ module tb_tk1 (); wire tb_gpio3; wire tb_gpio4; + reg tb_access_level_hi; + + wire tb_fw_ram_en; + wire tb_spi_ss; wire tb_spi_sck; wire tb_spi_mosi; @@ -122,7 +126,7 @@ module tb_tk1 (); .reset_n(tb_reset_n), .cpu_trap(tb_cpu_trap), - .system_mode(tb_system_mode), + .rw_locked(tb_rw_locked), .cpu_addr (tb_cpu_addr), .cpu_instr (tb_cpu_instr), @@ -141,6 +145,10 @@ module tb_tk1 (); .gpio3(tb_gpio3), .gpio4(tb_gpio4), + .access_level_hi(tb_access_level_hi), + + .fw_ram_en(tb_fw_ram_en), + .spi_ss (tb_spi_ss), .spi_sck (tb_spi_sck), .spi_mosi(tb_spi_mosi), @@ -192,7 +200,7 @@ module tb_tk1 (); $display("------------"); if (tb_main_monitor) begin $display("Inputs and outputs:"); - $display("tb_cpu_trap: 0x%1x, system_mode: 0x%1x", tb_cpu_trap, tb_system_mode); + $display("tb_cpu_trap: 0x%1x, system_mode: 0x%1x", tb_cpu_trap, dut.system_mode); $display("cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x, force_tap: 0x%1x", tb_cpu_addr, tb_cpu_instr, tb_cpu_valid, tb_force_trap); $display("ram_addr_rand: 0x%08x, ram_data_rand: 0x%08x", tb_ram_addr_rand, @@ -277,6 +285,8 @@ module tb_tk1 (); tb_gpio1 = 1'h0; tb_gpio2 = 1'h0; + tb_access_level_hi = 1'h0; + tb_cs = 1'h0; tb_we = 1'h0; tb_address = 8'h0; From a646c8bfb4f7ea3a71c0e9f901b3afb5081f422d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 18 Dec 2024 09:05:38 +0100 Subject: [PATCH 19/32] PoC: tb: Remove tb_tk1 blake2s test Removing the blake2s test since the blake2s registers are removed. --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 35 ------------------------ 1 file changed, 35 deletions(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 7624c27..871456d 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -478,40 +478,6 @@ module tb_tk1 (); endtask // test3 - //---------------------------------------------------------------- - // test4() - // Write and read blake2s entry point. - //---------------------------------------------------------------- - task test4; - begin - tc_ctr = tc_ctr + 1; - - $display(""); - $display("--- test4: Write and read blake2s entry point in fw mode started."); - $display("--- test4: Reset DUT to switch to fw mode."); - reset_dut(); - - $display("--- test4: Write Blake2s entry point."); - write_word(ADDR_BLAKE2S, 32'hcafebabe); - - $display("--- test4: Read Blake2s entry point."); - read_check_word(ADDR_BLAKE2S, 32'hcafebabe); - - $display("--- test4: Switch to app mode."); - write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf00ff00f); - - $display("--- test4: Write Blake2s entry point again."); - write_word(ADDR_BLAKE2S, 32'hdeadbeef); - - $display("--- test4: Read Blake2s entry point again"); - read_check_word(ADDR_BLAKE2S, 32'hcafebabe); - - $display("--- test4: completed."); - $display(""); - end - endtask // test4 - - //---------------------------------------------------------------- // test5() // Write and read APP start address end size. @@ -755,7 +721,6 @@ module tb_tk1 (); test1(); test2(); test3(); - test4(); test5(); test6(); test7(); From 5c56304b5dedfb1b58dce2547eb2c77c8868e725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Thu, 6 Feb 2025 12:33:04 +0100 Subject: [PATCH 20/32] PoC: tb: Add fetch instruction helper task to tb_tk1 --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 871456d..22ed927 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -378,6 +378,24 @@ module tb_tk1 (); endtask // read_check_word + //---------------------------------------------------------------- + // fetch_instruction() + // + // Simulate fetch of an instruction at specified address. + //---------------------------------------------------------------- + task fetch_instruction(input [31 : 0] address); + begin : fetch_instruction + tb_cpu_addr = address; + tb_cpu_instr = 1'h1; + tb_cpu_valid = 1'h1; + #(CLK_PERIOD); + tb_cpu_addr = 32'h0; + tb_cpu_instr = 1'h0; + tb_cpu_valid = 1'h0; + end + endtask // fetch_instruction + + //---------------------------------------------------------------- // test1() // Read out name and version. @@ -634,10 +652,7 @@ module tb_tk1 (); $display("--- test9: force_trap before illegal access: 0x%1x", tb_force_trap); $display("--- test9: Creating an illegal access."); - tb_cpu_addr = 32'h13371337; - tb_cpu_instr = 1'h1; - tb_cpu_valid = 1'h1; - #(2 * CLK_PERIOD); + fetch_instruction(32'h13371337); $display("--- test9: cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x", tb_cpu_addr, tb_cpu_instr, tb_cpu_valid); $display("--- test9: force_trap: 0x%1x", tb_force_trap); From 6c2a7ef6c7ef838855725b50c1c3c3081a4f602e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 18 Dec 2024 09:02:39 +0100 Subject: [PATCH 21/32] PoC: tb: Fix tb_tk1 test3 (CDI) Fix test1. It broke while implementing interrupt based syscalls. Instead of writing to ADDR_SYSTEM_MODE_CTRL, app mode is now entered automatically when executing outside of ROM. --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 22ed927..6262856 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -62,6 +62,7 @@ module tb_tk1 (); localparam ADDR_SPI_XFER = 8'h81; localparam ADDR_SPI_DATA = 8'h82; + localparam APP_RAM_START = 32'h40000000; //---------------------------------------------------------------- // Register and Wire declarations. @@ -468,7 +469,7 @@ module tb_tk1 (); read_check_word(ADDR_CDI_LAST + 0, 32'h70717273); $display("--- test3: Switch to app mode."); - write_word(ADDR_SYSTEM_MODE_CTRL, 32'hdeadbeef); + fetch_instruction(APP_RAM_START); $display("--- test3: Try to write CDI again."); write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc); From 8c6e4a3352d0b1eb49be8750f0d0eb6cda05a3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 18 Dec 2024 09:11:50 +0100 Subject: [PATCH 22/32] PoC: tb: Fix tb_tk1 test5 (APP_START/APP_SIZE) Fix test1. It broke while implementing interrupt based syscalls. Instead of writing to ADDR_SYSTEM_MODE_CTRL, app mode is now entered automatically when executing outside of ROM. --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 6262856..d4026e5 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -519,7 +519,7 @@ module tb_tk1 (); read_check_word(ADDR_APP_SIZE, 32'h47114711); $display("--- test5: Switch to app mode."); - write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf000000); + fetch_instruction(APP_RAM_START); $display("--- test5: Write app start address and size again."); write_word(ADDR_APP_START, 32'hdeadbeef); From 97005bb83c76c8afcc1c06c93021092b25fdfeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Wed, 29 Jan 2025 14:55:26 +0100 Subject: [PATCH 23/32] PoC: tb: Fix tb_tk1 test10 (SPI loop back test) Fix test10. It broke while implementing interrupt based syscalls. Cleaning up after the previous test. We reset the memory bus to a known idle state. We also reset the DUT to make the SPI master visible. --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index d4026e5..a8d0fc0 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -296,6 +296,25 @@ module tb_tk1 (); endtask // init_sim + //---------------------------------------------------------------- + // restore_mem_bus() + // + // Restore memory bus to its initial state + //---------------------------------------------------------------- + task restore_mem_bus(); + begin : restore_mem_bus + tb_cpu_addr = 32'h0; + tb_cpu_instr = 1'h0; + tb_cpu_valid = 1'h0; + + tb_cs = 1'h0; + tb_we = 1'h0; + tb_address = 8'h0; + tb_write_data = 32'h0; + end + endtask + + //---------------------------------------------------------------- // write_word() // @@ -674,6 +693,9 @@ module tb_tk1 (); tb_monitor = 0; tb_spi_monitor = 0; + restore_mem_bus(); + reset_dut(); + $display(""); $display("--- test10: Loopback in SPI Master started."); From e851efd35e32f38277883c2f549d0c4ae20f2a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Thu, 6 Feb 2025 14:23:03 +0100 Subject: [PATCH 24/32] PoC: tb: Expand existing tests with access checks in app mode and syscalls Checks availability of: - CDI - UDI - RAM - SPI --- hw/application_fpga/core/tk1/tb/tb_tk1.v | 193 ++++++++++++++++++++--- 1 file changed, 174 insertions(+), 19 deletions(-) diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index a8d0fc0..a24c2f2 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -398,6 +398,21 @@ module tb_tk1 (); endtask // read_check_word + //---------------------------------------------------------------- + // check_equal() + // + // Check that two values are equal + //---------------------------------------------------------------- + task check_equal(input [31 : 0] value, input [31 : 0] expected); + begin : check_equal + if (value != expected) begin + $display("--- Error: Got 0x%08x, expected 0x%08x", expected, value); + error_ctr = error_ctr + 1; + end + end + endtask // check_equal + + //---------------------------------------------------------------- // fetch_instruction() // @@ -447,10 +462,27 @@ module tb_tk1 (); $display(""); $display("--- test2: Read out UDI started."); + tb_access_level_hi = 0; + reset_dut(); read_check_word(ADDR_UDI_FIRST, 32'h00010203); read_check_word(ADDR_UDI_LAST, 32'h04050607); + $display("--- test2: Switch to app mode."); + fetch_instruction(APP_RAM_START); + + read_check_word(ADDR_UDI_FIRST, 32'h0); + read_check_word(ADDR_UDI_LAST, 32'h0); + + $display("--- test2: Enter syscall."); + tb_access_level_hi = 1; + + read_check_word(ADDR_UDI_FIRST, 32'h0); + read_check_word(ADDR_UDI_LAST, 32'h0); + + $display("--- test2: Leave syscall."); + tb_access_level_hi = 0; + $display("--- test2: completed."); $display(""); end @@ -465,6 +497,10 @@ module tb_tk1 (); begin tc_ctr = tc_ctr + 1; + $display("--- test5: Reset DUT to switch to fw mode."); + tb_access_level_hi = 0; + reset_dut(); + $display(""); $display("--- test3: Write and read CDI started."); $display("--- test3: Write CDI."); @@ -490,7 +526,7 @@ module tb_tk1 (); $display("--- test3: Switch to app mode."); fetch_instruction(APP_RAM_START); - $display("--- test3: Try to write CDI again."); + $display("--- test3: Try to write CDI from app mode."); write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc); write_word(ADDR_CDI_FIRST + 1, 32'hefeeedec); write_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc); @@ -500,7 +536,7 @@ module tb_tk1 (); write_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c); write_word(ADDR_CDI_FIRST + 7, 32'h7f7e7d7c); - $display("--- test3: Read CDI again."); + $display("--- test3: Read CDI from app mode."); read_check_word(ADDR_CDI_FIRST + 0, 32'hf0f1f2f3); read_check_word(ADDR_CDI_FIRST + 1, 32'he0e1e2e3); read_check_word(ADDR_CDI_FIRST + 2, 32'hd0d1d2d3); @@ -510,6 +546,32 @@ module tb_tk1 (); read_check_word(ADDR_CDI_FIRST + 6, 32'h80818283); read_check_word(ADDR_CDI_LAST + 0, 32'h70717273); + $display("--- test3: Enter syscall."); + tb_access_level_hi = 1; + + $display("--- test3: Try to write CDI from syscall."); + write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc); + write_word(ADDR_CDI_FIRST + 1, 32'hefeeedec); + write_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc); + write_word(ADDR_CDI_FIRST + 3, 32'hcfcecdcc); + write_word(ADDR_CDI_FIRST + 4, 32'hafaeadac); + write_word(ADDR_CDI_FIRST + 5, 32'h9f9e9d9c); + write_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c); + write_word(ADDR_CDI_FIRST + 7, 32'h7f7e7d7c); + + $display("--- test3: Read CDI from syscall."); + read_check_word(ADDR_CDI_FIRST + 0, 32'hf0f1f2f3); + read_check_word(ADDR_CDI_FIRST + 1, 32'he0e1e2e3); + read_check_word(ADDR_CDI_FIRST + 2, 32'hd0d1d2d3); + read_check_word(ADDR_CDI_FIRST + 3, 32'hc0c1c2c3); + read_check_word(ADDR_CDI_FIRST + 4, 32'ha0a1a2a3); + read_check_word(ADDR_CDI_FIRST + 5, 32'h90919293); + read_check_word(ADDR_CDI_FIRST + 6, 32'h80818283); + read_check_word(ADDR_CDI_LAST + 0, 32'h70717273); + + $display("--- test3: Leave syscall."); + tb_access_level_hi = 0; + $display("--- test3: completed."); $display(""); end @@ -556,7 +618,7 @@ module tb_tk1 (); //---------------------------------------------------------------- // test6() - // Write RAM address and data randomizatio in fw mode. + // Write and read RAM-address and data randomization. //---------------------------------------------------------------- task test6; begin @@ -565,6 +627,7 @@ module tb_tk1 (); $display(""); $display("--- test6: Write RAM addr and data randomization in fw mode."); $display("--- test6: Reset DUT to switch to fw mode."); + tb_access_level_hi = 0; reset_dut(); $display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND ."); @@ -575,9 +638,14 @@ module tb_tk1 (); "--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers."); $display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x", dut.ram_addr_rand, dut.ram_data_rand); + check_equal(dut.ram_addr_rand, 15'h1337); + check_equal(dut.ram_data_rand, 32'h47114711); + read_check_word(ADDR_RAM_ADDR_RAND, 32'h0); + read_check_word(ADDR_RAM_DATA_RAND, 32'h0); + $display("--- test6: Switch to app mode."); - write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf000000); + fetch_instruction(APP_RAM_START); $display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND again."); write_word(ADDR_RAM_ADDR_RAND, 32'hdeadbeef); @@ -587,6 +655,30 @@ module tb_tk1 (); "--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers."); $display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x", dut.ram_addr_rand, dut.ram_data_rand); + check_equal(dut.ram_addr_rand, 15'h1337); + check_equal(dut.ram_data_rand, 32'h47114711); + read_check_word(ADDR_RAM_ADDR_RAND, 32'h0); + read_check_word(ADDR_RAM_DATA_RAND, 32'h0); + + + $display("--- test6: Enter syscall."); + tb_access_level_hi = 1; + + $display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND again."); + write_word(ADDR_RAM_ADDR_RAND, 32'hdeadbeef); + write_word(ADDR_RAM_DATA_RAND, 32'hf00ff00f); + + $display( + "--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers."); + $display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x", + dut.ram_addr_rand, dut.ram_data_rand); + check_equal(dut.ram_addr_rand, 15'h1337); + check_equal(dut.ram_data_rand, 32'h47114711); + read_check_word(ADDR_RAM_ADDR_RAND, 32'h0); + read_check_word(ADDR_RAM_DATA_RAND, 32'h0); + + $display("--- test6: Leave syscall."); + tb_access_level_hi = 0; $display("--- test6: completed."); $display(""); @@ -668,6 +760,8 @@ module tb_tk1 (); write_word(ADDR_CPU_MON_LAST, 32'hdeadcafe); $display("--- test9: cpu_mon_first_reg: 0x%08x, cpu_mon_last_reg: 0x%08x", dut.cpu_mon_first_reg, dut.cpu_mon_last_reg); + check_equal(dut.cpu_mon_first_reg, 32'h10000000); + check_equal(dut.cpu_mon_last_reg, 32'h20000000); $display("--- test9: force_trap before illegal access: 0x%1x", tb_force_trap); $display("--- test9: Creating an illegal access."); @@ -675,7 +769,11 @@ module tb_tk1 (); fetch_instruction(32'h13371337); $display("--- test9: cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x", tb_cpu_addr, tb_cpu_instr, tb_cpu_valid); + check_equal(dut.cpu_mon_first_reg, 32'h10000000); + check_equal(dut.cpu_mon_last_reg, 32'h20000000); + $display("--- test9: force_trap: 0x%1x", tb_force_trap); + check_equal(tb_force_trap, 1); $display("--- test9: completed."); $display(""); @@ -683,6 +781,66 @@ module tb_tk1 (); endtask // test9 + //---------------------------------------------------------------- + // check_inverting_spi_loopback_transfer_succeeds() + // Do an SPI tranfer. Check that the received value is the inverse + // of the value sent. + //---------------------------------------------------------------- + task check_inverting_spi_loopback_transfer_succeeds(input [32 : 0] data); + begin : check_inverting_spi_loopback_transfer + $display("--- test10: Sending a byte."); + write_word(ADDR_SPI_EN, 32'h1); + write_word(ADDR_SPI_DATA, data); + write_word(ADDR_SPI_XFER, 32'h1); + + // Ready ready flag in SPI until it is set. + read_word(ADDR_SPI_XFER); + while (!tb_read_data) begin + read_word(ADDR_SPI_XFER); + end + $display("--- test10: Byte should have been sent."); + + #(2 * CLK_PERIOD); + read_check_word(ADDR_SPI_DATA, ~data[7 : 0] & 8'hff); + write_word(ADDR_SPI_EN, 32'h0); + end + endtask + + + //---------------------------------------------------------------- + // check_spi_does_not_transfer() + // Do an SPI transfer. Check that the SS, SCK and MISO signal are + // not active. + //---------------------------------------------------------------- + task check_spi_does_not_transfer; + begin : check_spi_does_not_transfer + reg [31 : 0] wait_ctr; + reg error; + localparam CLK_PER_SPI_BIT = 3; + localparam WAIT_MARGIN = 10; + + error = 0; + wait_ctr = CLK_PER_SPI_BIT * 8 * WAIT_MARGIN; + + $display("--- test10: Sending a byte."); + write_word(ADDR_SPI_EN, 32'h1); + write_word(ADDR_SPI_DATA, 32'haa); + write_word(ADDR_SPI_XFER, 32'h1); + + $display("--- test10: Waiting to see if SPI signals change state."); + while (!error && (wait_ctr != 0)) begin + if (~tb_spi_ss || tb_spi_sck || tb_spi_mosi) begin + $display("--- Error: SPI signals changed state"); + error_ctr = error_ctr + 1; + error = 1; + end + #(CLK_PERIOD); + wait_ctr = wait_ctr - 1; + end + end + endtask + + //---------------------------------------------------------------- // test10() // SPI master loopback test. @@ -701,23 +859,20 @@ module tb_tk1 (); #(CLK_PERIOD); - // Sending 0xa7 trough the inverting loopback. - $display("--- test10: Sending a byte."); - write_word(ADDR_SPI_EN, 32'h1); - write_word(ADDR_SPI_DATA, 32'ha7); - write_word(ADDR_SPI_XFER, 32'h1); + check_inverting_spi_loopback_transfer_succeeds(32'ha7); - // Ready ready flag in SPI until it is set. - read_word(ADDR_SPI_XFER); - while (!tb_read_data) begin - read_word(ADDR_SPI_XFER); - end - $display("--- test10: Byte should have been sent."); + $display("--- test10: Switch to app mode."); + fetch_instruction(APP_RAM_START); - // 0x58 is the inverse of 0xa7. - #(2 * CLK_PERIOD); - read_check_word(ADDR_SPI_DATA, 32'h58); - write_word(ADDR_SPI_EN, 32'h0); + check_spi_does_not_transfer(); + + $display("--- test10: Enter syscall."); + tb_access_level_hi = 1; + + check_inverting_spi_loopback_transfer_succeeds(32'hc8); + + $display("--- test10: Leave syscall."); + tb_access_level_hi = 0; tb_monitor = 0; tb_spi_monitor = 0; From 926f3c68ed13a9414efc54dad742461a0b24c368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Thu, 30 Jan 2025 17:28:19 +0100 Subject: [PATCH 25/32] PoC: Add experimental syscalls to firmware Adds: - SYSCALL_RESET - SYSCALL_SET_LED --- hw/application_fpga/Makefile | 6 +- hw/application_fpga/fw/tk1/Makefile | 3 +- hw/application_fpga/fw/tk1/picorv32/README.md | 7 ++ .../fw/tk1/picorv32/custom_ops.S | 102 ++++++++++++++++++ hw/application_fpga/fw/tk1/start.S | 93 +++++++++++++++- hw/application_fpga/fw/tk1/syscall.c | 33 ++++++ hw/application_fpga/fw/tk1/syscall_nrs.h | 13 +++ 7 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 hw/application_fpga/fw/tk1/picorv32/README.md create mode 100644 hw/application_fpga/fw/tk1/picorv32/custom_ops.S create mode 100644 hw/application_fpga/fw/tk1/syscall.c create mode 100644 hw/application_fpga/fw/tk1/syscall_nrs.h diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 56a1a1e..c934d9c 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -127,7 +127,8 @@ FIRMWARE_OBJS = \ $(P)/fw/tk1/lib.o \ $(P)/fw/tk1/assert.o \ $(P)/fw/tk1/led.o \ - $(P)/fw/tk1/blake2s/blake2s.o + $(P)/fw/tk1/blake2s/blake2s.o \ + $(P)/fw/tk1/syscall.o FIRMWARE_SOURCES = \ $(P)/fw/tk1/main.c \ @@ -135,7 +136,8 @@ FIRMWARE_SOURCES = \ $(P)/fw/tk1/lib.c \ $(P)/fw/tk1/assert.c \ $(P)/fw/tk1/led.c \ - $(P)/fw/tk1/blake2s/blake2s.c + $(P)/fw/tk1/blake2s/blake2s.c \ + $(P)/fw/tk1/syscall.c TESTFW_OBJS = \ $(P)/fw/testfw/main.o \ diff --git a/hw/application_fpga/fw/tk1/Makefile b/hw/application_fpga/fw/tk1/Makefile index 68b8261..3c1cd5f 100644 --- a/hw/application_fpga/fw/tk1/Makefile +++ b/hw/application_fpga/fw/tk1/Makefile @@ -1,5 +1,6 @@ # Uses ../.clang-format -FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c led.h +FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c \ + led.h syscall.c .PHONY: fmt fmt: clang-format --dry-run --ferror-limit=0 $(FMTFILES) diff --git a/hw/application_fpga/fw/tk1/picorv32/README.md b/hw/application_fpga/fw/tk1/picorv32/README.md new file mode 100644 index 0000000..761be04 --- /dev/null +++ b/hw/application_fpga/fw/tk1/picorv32/README.md @@ -0,0 +1,7 @@ +# PicoRV32 + +## custom_ops.S + +Custom PicoRV32 instructions are located in `custom_ops.S`. +`custom_ops.S` is imported from upstream PicoRV32 commit: +YosysHQ/picorv32@70f3c33 diff --git a/hw/application_fpga/fw/tk1/picorv32/custom_ops.S b/hw/application_fpga/fw/tk1/picorv32/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/tk1/picorv32/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/tk1/start.S b/hw/application_fpga/fw/tk1/start.S index 93a2fb0..dc892f0 100644 --- a/hw/application_fpga/fw/tk1/start.S +++ b/hw/application_fpga/fw/tk1/start.S @@ -1,11 +1,102 @@ /* - * Copyright (C) 2022, 2023 - Tillitis AB + * Copyright (C) 2022, 2023, 2024, 2025 - Tillitis AB * SPDX-License-Identifier: GPL-2.0-only */ +#include "../tk1_mem.h" +#include "picorv32/custom_ops.S" // PicoRV32 custom instructions + +#define illegal_insn() .word 0 + +#define FW_SP_STORAGE (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 4) +#define FW_STACK_TOP (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 2*4) + .section ".text.init" .globl _start _start: + j init + +/* + * IRQ handler +*/ + .=0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + li t4, (1 << 31) + beq x4, t4, irq_source_ok +unexpected_irq_source: + illegal_insn() + j unexpected_irq_source +irq_source_ok: + + // Save app stack pointer. App is responsible for saving the rest of + // the registers. + li t0, FW_SP_STORAGE + sw sp, 0(t0) + + // Setup firmware stack pointer + li sp, FW_STACK_TOP + + // Run syscall handler + call syscall_handler + + // Restore app stack pointer + li t0, FW_SP_STORAGE + lw sp, 0(t0) + + // Verify that interrupt return address (x3) is in app RAM + li t0, TK1_RAM_BASE // 0x40000000 + blt x3, t0, x3_invalid + li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000 + bge x3, t0, x3_invalid + j x3_valid +x3_invalid: + illegal_insn() + j x3_invalid +x3_valid: + + // Remove data left over from the syscall handling + mv x0, zero + mv x1, zero + // x2 (sp) is assumed to be preserved by the interrupt handler + // x3 (interrupt return address) assumed preserved + mv x4, zero + mv x5, zero + mv x6, zero + mv x7, zero + mv x8, zero + mv x9, zero + // x10 (a0) contains syscall return value. And should not be destroyed. + mv x11, zero + mv x12, zero + mv x13, zero + mv x14, zero + mv x15, zero + mv x16, zero + mv x17, zero + mv x18, zero + mv x19, zero + mv x20, zero + mv x21, zero + mv x22, zero + mv x23, zero + mv x24, zero + mv x25, zero + mv x26, zero + mv x27, zero + mv x28, zero + mv x29, zero + mv x30, zero + mv x31, zero + + picorv32_retirq_insn() // Return from interrupt + +/* + * Init +*/ + .=0x100 +init: li x1, 0 li x2, 0 li x3, 0 diff --git a/hw/application_fpga/fw/tk1/syscall.c b/hw/application_fpga/fw/tk1/syscall.c new file mode 100644 index 0000000..9567620 --- /dev/null +++ b/hw/application_fpga/fw/tk1/syscall.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "../tk1/assert.h" +#include "../tk1/led.h" +#include "../tk1/syscall_nrs.h" +#include "../tk1/types.h" + +// clang-format off +static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET; +// clang-format on + +// Proof-of-concept firmware for handling syscalls. +// This is NOT a best-practice example of secure syscall implementation. + +int32_t syscall_handler(uint32_t syscall_nr, uint32_t arg1) +{ + switch (syscall_nr) { + case TK1_SYSCALL_RESET: + *system_reset = 1; + return 0; + case TK1_SYSCALL_SET_LED: + set_led(arg1); + return 0; + default: + assert(1 == 2); + } + + assert(1 == 2); + return -1; // This should never run +} diff --git a/hw/application_fpga/fw/tk1/syscall_nrs.h b/hw/application_fpga/fw/tk1/syscall_nrs.h new file mode 100644 index 0000000..d27a933 --- /dev/null +++ b/hw/application_fpga/fw/tk1/syscall_nrs.h @@ -0,0 +1,13 @@ +// Copyright (C) 2025 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef TKEY_SYSCALL_H +#define TKEY_SYSCALL_H + +enum { + TK1_SYSCALL_RESET = 1, + TK1_SYSCALL_SET_LED = 10, + TK1_SYSCALL_GET_FLASH_CAPACITY = 11, +}; + +#endif From 1a505f4a212f12d52c50963f6ab8d6d38bc58c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Fri, 31 Jan 2025 07:53:42 +0100 Subject: [PATCH 26/32] PoC: testfw: Remove blake2s test Removing the blake2s test since the possibility for the firmware to expose a blake2s function to the app has been removed. --- hw/application_fpga/Makefile | 3 +-- hw/application_fpga/fw/testfw/main.c | 36 ---------------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index c934d9c..3dafab6 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -143,8 +143,7 @@ TESTFW_OBJS = \ $(P)/fw/testfw/main.o \ $(P)/fw/testfw/start.o \ $(P)/fw/tk1/proto.o \ - $(P)/fw/tk1/lib.o \ - $(P)/fw/tk1/blake2s/blake2s.o + $(P)/fw/tk1/lib.o IRQPOC_OBJS = \ $(P)/fw/irqpoc/main.o \ diff --git a/hw/application_fpga/fw/testfw/main.c b/hw/application_fpga/fw/testfw/main.c index 5ed6076..eaaf268 100644 --- a/hw/application_fpga/fw/testfw/main.c +++ b/hw/application_fpga/fw/testfw/main.c @@ -3,7 +3,6 @@ * SPDX-License-Identifier: GPL-2.0-only */ -#include "../tk1/blake2s/blake2s.h" #include "../tk1/lib.h" #include "../tk1/proto.h" #include "../tk1/types.h" @@ -23,7 +22,6 @@ 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; -volatile uint32_t *fw_blake2s_addr = (volatile uint32_t *)TK1_MMIO_TK1_BLAKE2S; // clang-format on #define UDS_WORDS 8 @@ -151,11 +149,6 @@ void failmsg(char *s) int main(void) { - // Function pointer to blake2s() - volatile int (*fw_blake2s)(void *, unsigned long, const void *, - unsigned long, const void *, unsigned long, - blake2s_ctx *); - uint8_t in; // Hard coded test UDS in ../../data/uds.hex // clang-format off @@ -263,9 +256,6 @@ int main(void) anyfailed = 1; } - // Store function pointer to blake2s() so it's reachable from app - *fw_blake2s_addr = (uint32_t)blake2s; - // Turn on application mode. // ------------------------- @@ -346,32 +336,6 @@ int main(void) anyfailed = 1; } - // Testing the blake2s MMIO in app mode - - fw_blake2s = (volatile int (*)(void *, unsigned long, const void *, - unsigned long, const void *, - unsigned long, blake2s_ctx *)) * - fw_blake2s_addr; - - char msg[17] = "dldlkjsdkljdslsdj"; - uint32_t digest0[8]; - uint32_t digest1[8]; - blake2s_ctx b2s_ctx; - - blake2s(&digest0[0], 32, NULL, 0, &msg, 17, &b2s_ctx); - fw_blake2s(&digest1[0], 32, NULL, 0, &msg, 17, &b2s_ctx); - - puts("\r\ndigest #0: \r\n"); - hexdump((uint8_t *)digest0, 32); - - puts("digest #1: \r\n"); - hexdump((uint8_t *)digest1, 32); - - if (!memeq(digest0, digest1, 32)) { - failmsg("Digests not the same"); - anyfailed = 1; - } - // Check and display test results. puts("\r\n--> "); if (anyfailed) { From d1b8b6eee897637b78390a39e123c60f23464953 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 27/32] PoC: testfw: 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 | 68 ++++++ hw/application_fpga/fw/testapp/app.lds | 64 ++++++ hw/application_fpga/fw/testapp/crt0.S | 53 +++++ hw/application_fpga/fw/testapp/main.c | 282 ++++++++++++++++++++++++ hw/application_fpga/fw/testfw/main.c | 44 ---- 5 files changed, 467 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..5509877 --- /dev/null +++ b/hw/application_fpga/fw/testapp/Makefile @@ -0,0 +1,68 @@ +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)/../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..ab16f80 --- /dev/null +++ b/hw/application_fpga/fw/testapp/main.c @@ -0,0 +1,282 @@ +/* + * 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" + +// 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 uint32_t *system_mode_ctrl = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_MODE_CTRL; +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 + +void *memcpy(void *dest, const void *src, size_t n) +{ + uint8_t *src_byte = (uint8_t *)src; + uint8_t *dest_byte = (uint8_t *)dest; + + for (int i = 0; i < n; i++) { + dest_byte[i] = src_byte[i]; + } + + return dest; +} + +void puts(char *reason) +{ + for (char *c = reason; *c != '\0'; c++) { + writebyte(*c); + } +} + +void putsn(char *p, int n) +{ + for (int i = 0; i < n; i++) { + writebyte(p[i]); + } +} + +void puthex(uint8_t c) +{ + unsigned int upper = (c >> 4) & 0xf; + unsigned int lower = c & 0xf; + writebyte(upper < 10 ? '0' + upper : 'a' - 10 + upper); + writebyte(lower < 10 ? '0' + lower : 'a' - 10 + lower); +} + +void puthexn(uint8_t *p, int n) +{ + for (int i = 0; i < n; i++) { + puthex(p[i]); + } +} + +void hexdump(void *buf, int len) +{ + uint8_t *byte_buf = (uint8_t *)buf; + + for (int i = 0; i < len; i++) { + puthex(byte_buf[i]); + if (i % 2 == 1) { + writebyte(' '); + } + + if (i != 1 && i % 16 == 1) { + puts("\r\n"); + } + } + + puts("\r\n"); +} + +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 zero_fwram(void) +{ + for (int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) { + fw_ram[i] = 0x00; + } +} + +int check_fwram_zero_except(unsigned int offset, uint8_t expected_val) +{ + int failed = 0; + for (unsigned int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) { + uint32_t addr = TK1_MMIO_FW_RAM_BASE + i; + uint8_t *p = (uint8_t *)addr; + uint8_t val = *(volatile uint8_t *)p; + int failed_now = 0; + if (i == offset) { + if (val != expected_val) { + failed_now = 1; + puts(" wrong value at: "); + } + } else { + if (val != 0) { + failed_now = 1; + puts(" not zero at: "); + } + } + if (failed_now) { + failed = 1; + reverseword(&addr); + puthexn((uint8_t *)&addr, 4); + puts("\r\n"); + } + } + return failed; +} + +void failmsg(char *s) +{ + puts("FAIL: "); + puts(s); + puts("\r\n"); +} + +int main(void) +{ + uint8_t in; + + set_led(LED_BLUE); + + // Wait for terminal program and a character to be typed + in = readbyte(); + + 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); + putsn((char *)&name, 4); + puts(" "); + wordcpy_s(&name, 1, (void *)tk1name1, 1); + reverseword(&name); + putsn((char *)&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]; + + uint32_t sw = *system_mode_ctrl; + if (sw != 0xffffffff) { + failmsg("system_mode_ctrl is not 0xffffffff"); + 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 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(); // blocks + writebyte(in); + } +} diff --git a/hw/application_fpga/fw/testfw/main.c b/hw/application_fpga/fw/testfw/main.c index eaaf268..0ec2a6a 100644 --- a/hw/application_fpga/fw/testfw/main.c +++ b/hw/application_fpga/fw/testfw/main.c @@ -256,50 +256,6 @@ int main(void) anyfailed = 1; } - // Turn on application mode. - // ------------------------- - - *system_mode_ctrl = 1; - - sw = *system_mode_ctrl; - if (sw != 0xffffffff) { - failmsg("system_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 18 MHz, giving us timer in seconds *timer_prescaler = 18 * 1000000; From ea7f7107f058a5b3b80081de5d7b5e9a838d05d9 Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Fri, 30 Aug 2024 14:10:40 +0200 Subject: [PATCH 28/32] PoC: fw: Import spi.[ch] and flash.[ch] --- hw/application_fpga/fw/tk1/flash.c | 217 +++++++++++++++++++++++++++++ hw/application_fpga/fw/tk1/flash.h | 58 ++++++++ hw/application_fpga/fw/tk1/spi.c | 91 ++++++++++++ hw/application_fpga/fw/tk1/spi.h | 14 ++ 4 files changed, 380 insertions(+) create mode 100644 hw/application_fpga/fw/tk1/flash.c create mode 100644 hw/application_fpga/fw/tk1/flash.h create mode 100644 hw/application_fpga/fw/tk1/spi.c create mode 100644 hw/application_fpga/fw/tk1/spi.h diff --git a/hw/application_fpga/fw/tk1/flash.c b/hw/application_fpga/fw/tk1/flash.c new file mode 100644 index 0000000..3be9431 --- /dev/null +++ b/hw/application_fpga/fw/tk1/flash.c @@ -0,0 +1,217 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#include "flash.h" +#include "../tk1_mem.h" +#include "spi.h" + +#include +#include +#include + +// clang-format off +static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER; +static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER; +static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS; +static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL; +// clang-format on + +// CPU clock frequency in Hz +#define CPUFREQ 21000000 +#define PAGE_SIZE 256 + +static void delay(int timeout_ms) +{ + // Tick once every centisecond + *timer_prescaler = CPUFREQ / 100; + *timer = timeout_ms / 10; + + *timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT); + + while (*timer_status != 0) { + } + + // Stop timer + *timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT); +} + +bool flash_is_busy(void) +{ + uint8_t tx_buf = READ_STATUS_REG_1; + uint8_t rx_buf = {0x00}; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf, sizeof(rx_buf)); + + if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) { + return true; + } + + return false; +} + +// Blocking until !busy +void flash_wait_busy(void) +{ + while (flash_is_busy()) { + delay(10); + } +} + +void flash_write_enable(void) +{ + uint8_t tx_buf = WRITE_ENABLE; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_write_disable(void) +{ + uint8_t tx_buf = WRITE_DISABLE; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_sector_erase(uint32_t address) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = SECTOR_ERASE; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + /* tx_buf[3] is within a sector, and hence does not make a difference */ + + flash_write_enable(); + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); + flash_wait_busy(); +} + +void flash_block_32_erase(uint32_t address) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = BLOCK_ERASE_32K; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF; + + flash_write_enable(); + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); + flash_wait_busy(); +} + +// 64 KiB block erase, only cares about address bits 16 and above. +void flash_block_64_erase(uint32_t address) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = BLOCK_ERASE_64K; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + /* tx_buf[2] and tx_buf[3] is within a block, and hence does not make a + * difference */ + + flash_write_enable(); + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); + flash_wait_busy(); +} + +void flash_release_powerdown(void) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = RELEASE_POWER_DOWN; + + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_powerdown(void) +{ + uint8_t tx_buf = POWER_DOWN; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_read_manufacturer_device_id(uint8_t *device_id) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = READ_MANUFACTURER_ID; + + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2); +} + +void flash_read_jedec_id(uint8_t *jedec_id) +{ + uint8_t tx_buf = READ_JEDEC_ID; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3); +} + +void flash_read_unique_id(uint8_t *unique_id) +{ + uint8_t tx_buf[5] = {0x00}; + tx_buf[0] = READ_UNIQUE_ID; + + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8); +} + +void flash_read_status(uint8_t *status_reg) +{ + uint8_t tx_buf = READ_STATUS_REG_1; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1); + + tx_buf = READ_STATUS_REG_2; + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1, 1); +} + +int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = READ_DATA; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF; + + return spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, dest_buf, size); +} + +// Only handles writes where the least significant byte of the start address is +// zero. +int flash_write_data(uint32_t address, uint8_t *data, size_t size) +{ + if (size <= 0 || size > 4096) { + return -1; + } + + size_t left = size; + uint8_t *p_data = data; + size_t n_bytes = 0; + + uint8_t tx_buf[4] = { + PAGE_PROGRAM, /* tx_buf[0] */ + (address >> ADDR_BYTE_3_BIT) & 0xFF, /* tx_buf[1] */ + (address >> ADDR_BYTE_2_BIT) & 0xFF, /* tx_buf[2] */ + 0x00, /* tx_buf[3] */ + }; + + while (left > 0) { + if (left >= PAGE_SIZE) { + n_bytes = PAGE_SIZE; + } else { + n_bytes = left; + } + + flash_write_enable(); + + if (spi_transfer(tx_buf, sizeof(tx_buf), p_data, n_bytes, NULL, + 0) != 0) { + return -1; + } + + left -= n_bytes; + p_data += n_bytes; + + address += n_bytes; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + + flash_wait_busy(); + } + + return 0; +} diff --git a/hw/application_fpga/fw/tk1/flash.h b/hw/application_fpga/fw/tk1/flash.h new file mode 100644 index 0000000..cad6f07 --- /dev/null +++ b/hw/application_fpga/fw/tk1/flash.h @@ -0,0 +1,58 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef TKEY_FLASH_H +#define TKEY_FLASH_H + +#include +#include +#include + +#define WRITE_ENABLE 0x06 +#define WRITE_DISABLE 0x04 + +#define READ_STATUS_REG_1 0x05 +#define READ_STATUS_REG_2 0x35 +#define WRITE_STATUS_REG 0x01 + +#define PAGE_PROGRAM 0x02 +#define SECTOR_ERASE 0x20 +#define BLOCK_ERASE_32K 0x52 +#define BLOCK_ERASE_64K 0xD8 +#define CHIP_ERASE 0xC7 + +#define POWER_DOWN 0xB9 +#define READ_DATA 0x03 +#define RELEASE_POWER_DOWN 0xAB + +#define READ_MANUFACTURER_ID 0x90 +#define READ_JEDEC_ID 0x9F +#define READ_UNIQUE_ID 0x4B + +#define ENABLE_RESET 0x66 +#define RESET 0x99 + +#define ADDR_BYTE_3_BIT 16 +#define ADDR_BYTE_2_BIT 8 +#define ADDR_BYTE_1_BIT 0 + +#define STATUS_REG_BUSY_BIT 0 +#define STATUS_REG_WEL_BIT 1 + +bool flash_is_busy(void); +void flash_wait_busy(void); +void flash_write_enable(void); +void flash_write_disable(void); +void flash_sector_erase(uint32_t address); +void flash_block_32_erase(uint32_t address); +void flash_block_64_erase(uint32_t address); +void flash_release_powerdown(void); +void flash_powerdown(void); +void flash_read_manufacturer_device_id(uint8_t *device_id); +void flash_read_jedec_id(uint8_t *jedec_id); +void flash_read_unique_id(uint8_t *unique_id); +void flash_read_status(uint8_t *status_reg); +int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size); +int flash_write_data(uint32_t address, uint8_t *data, size_t size); + +#endif diff --git a/hw/application_fpga/fw/tk1/spi.c b/hw/application_fpga/fw/tk1/spi.c new file mode 100644 index 0000000..4246ccb --- /dev/null +++ b/hw/application_fpga/fw/tk1/spi.c @@ -0,0 +1,91 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#include "spi.h" +#include "../tk1_mem.h" + +#include +#include + +// clang-format off +static volatile uint32_t *spi_en = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x200); +static volatile uint32_t *spi_xfer = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x204); +static volatile uint32_t *spi_data = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x208); +// clang-format on + +// returns non-zero when the SPI-master is ready, and zero if not +// ready. This can be used to check if the SPI-master is available +// in the hardware. +int spi_ready(void) +{ + return *spi_xfer; +} + +static void spi_enable(void) +{ + *spi_en = 1; +} + +static void spi_disable(void) +{ + *spi_en = 0; +} + +static void _spi_write(uint8_t *cmd, size_t size) +{ + for (size_t i = 0; i < size; i++) { + while (!spi_ready()) { + } + + *spi_data = cmd[i]; + *spi_xfer = 1; + } + + while (!spi_ready()) { + } +} + +static void _spi_read(uint8_t *buf, size_t size) +{ + + while (!spi_ready()) { + } + + for (size_t i = 0; i < size; i++) { + + *spi_data = 0x00; + *spi_xfer = 1; + + // wait until spi master is done + while (!spi_ready()) { + } + + buf[i] = (*spi_data & 0xff); + } +} + + +// Function to both read and write data to the connected SPI flash. +int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size, + uint8_t *rx_buf, size_t rx_size) +{ + if (cmd == NULL || cmd_size == 0) { + return -1; + } + + spi_enable(); + + _spi_write(cmd, cmd_size); + + if (tx_buf != NULL || tx_size != 0) { + _spi_write(tx_buf, tx_size); + } + + if (rx_buf != NULL && rx_size != 0) { + _spi_read(rx_buf, rx_size); + } + + spi_disable(); + + return 0; +} diff --git a/hw/application_fpga/fw/tk1/spi.h b/hw/application_fpga/fw/tk1/spi.h new file mode 100644 index 0000000..2c59f4c --- /dev/null +++ b/hw/application_fpga/fw/tk1/spi.h @@ -0,0 +1,14 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef TKEY_SPI_H +#define TKEY_SPI_H + +#include +#include + +int spi_ready(void); +int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size, + uint8_t *rx_buf, size_t rx_size); + +#endif From 52694b5c4feb6049987d44e6cb755f27151a4cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Mon, 3 Feb 2025 17:46:25 +0100 Subject: [PATCH 29/32] PoC: Integrate spi and flash code --- hw/application_fpga/Makefile | 8 ++++++-- hw/application_fpga/fw/tk1/Makefile | 2 +- hw/application_fpga/fw/tk1/flash.c | 3 +-- hw/application_fpga/fw/tk1/spi.c | 5 +---- hw/application_fpga/fw/tk1/spi.h | 3 +-- hw/application_fpga/fw/tk1/types.h | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 3dafab6..1281b35 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -128,7 +128,9 @@ FIRMWARE_OBJS = \ $(P)/fw/tk1/assert.o \ $(P)/fw/tk1/led.o \ $(P)/fw/tk1/blake2s/blake2s.o \ - $(P)/fw/tk1/syscall.o + $(P)/fw/tk1/syscall.o \ + $(P)/fw/tk1/spi.o \ + $(P)/fw/tk1/flash.o FIRMWARE_SOURCES = \ $(P)/fw/tk1/main.c \ @@ -137,7 +139,9 @@ FIRMWARE_SOURCES = \ $(P)/fw/tk1/assert.c \ $(P)/fw/tk1/led.c \ $(P)/fw/tk1/blake2s/blake2s.c \ - $(P)/fw/tk1/syscall.c + $(P)/fw/tk1/syscall.c \ + $(P)/fw/tk1/spi.c \ + $(P)/fw/tk1/flash.c TESTFW_OBJS = \ $(P)/fw/testfw/main.o \ diff --git a/hw/application_fpga/fw/tk1/Makefile b/hw/application_fpga/fw/tk1/Makefile index 3c1cd5f..2ac03fa 100644 --- a/hw/application_fpga/fw/tk1/Makefile +++ b/hw/application_fpga/fw/tk1/Makefile @@ -1,6 +1,6 @@ # Uses ../.clang-format FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c \ - led.h syscall.c + led.h syscall.c spi.c spi.h flash.c flash.h .PHONY: fmt fmt: clang-format --dry-run --ferror-limit=0 $(FMTFILES) diff --git a/hw/application_fpga/fw/tk1/flash.c b/hw/application_fpga/fw/tk1/flash.c index 3be9431..efa8c47 100644 --- a/hw/application_fpga/fw/tk1/flash.c +++ b/hw/application_fpga/fw/tk1/flash.c @@ -2,12 +2,11 @@ // SPDX-License-Identifier: GPL-2.0-only #include "flash.h" +#include "../tk1/types.h" #include "../tk1_mem.h" #include "spi.h" #include -#include -#include // clang-format off static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER; diff --git a/hw/application_fpga/fw/tk1/spi.c b/hw/application_fpga/fw/tk1/spi.c index 4246ccb..ba50e35 100644 --- a/hw/application_fpga/fw/tk1/spi.c +++ b/hw/application_fpga/fw/tk1/spi.c @@ -2,11 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include "spi.h" +#include "../tk1/types.h" #include "../tk1_mem.h" -#include -#include - // clang-format off static volatile uint32_t *spi_en = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x200); static volatile uint32_t *spi_xfer = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x204); @@ -64,7 +62,6 @@ static void _spi_read(uint8_t *buf, size_t size) } } - // Function to both read and write data to the connected SPI flash. int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size, uint8_t *rx_buf, size_t rx_size) diff --git a/hw/application_fpga/fw/tk1/spi.h b/hw/application_fpga/fw/tk1/spi.h index 2c59f4c..0db6555 100644 --- a/hw/application_fpga/fw/tk1/spi.h +++ b/hw/application_fpga/fw/tk1/spi.h @@ -4,8 +4,7 @@ #ifndef TKEY_SPI_H #define TKEY_SPI_H -#include -#include +#include "../tk1/types.h" int spi_ready(void); int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size, diff --git a/hw/application_fpga/fw/tk1/types.h b/hw/application_fpga/fw/tk1/types.h index 9a1fcff..2ed09d5 100644 --- a/hw/application_fpga/fw/tk1/types.h +++ b/hw/application_fpga/fw/tk1/types.h @@ -14,7 +14,7 @@ typedef long long int64_t; typedef unsigned char uint8_t; typedef unsigned long size_t; -#define NULL ((char *)0) +#define NULL ((void *)0) #define FALSE 0 #define TRUE !FALSE From 3665c3e245ebbd5d5bc1ca4383eae313df31bda2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Mon, 3 Feb 2025 09:38:53 +0100 Subject: [PATCH 30/32] PoC: testfw: Check that SPI flash is available in firmware mode --- hw/application_fpga/Makefile | 4 +++- hw/application_fpga/fw/testfw/main.c | 11 +++++++++++ hw/application_fpga/fw/tk1/flash.h | 3 +-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 1281b35..2a011ca 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -147,7 +147,9 @@ TESTFW_OBJS = \ $(P)/fw/testfw/main.o \ $(P)/fw/testfw/start.o \ $(P)/fw/tk1/proto.o \ - $(P)/fw/tk1/lib.o + $(P)/fw/tk1/lib.o \ + $(P)/fw/tk1/spi.o \ + $(P)/fw/tk1/flash.o IRQPOC_OBJS = \ $(P)/fw/irqpoc/main.o \ diff --git a/hw/application_fpga/fw/testfw/main.c b/hw/application_fpga/fw/testfw/main.c index 0ec2a6a..66a5b41 100644 --- a/hw/application_fpga/fw/testfw/main.c +++ b/hw/application_fpga/fw/testfw/main.c @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ +#include "../tk1/flash.h" #include "../tk1/lib.h" #include "../tk1/proto.h" #include "../tk1/types.h" @@ -239,6 +240,16 @@ int main(void) anyfailed = 1; } + uint8_t jedec_id[3]; + puts("\r\nReading SPI flash capacity...\r\n"); + flash_release_powerdown(); + flash_read_jedec_id(jedec_id); + + if (jedec_id[2] != 0x14) { + failmsg("Expected SPI flash capacity: 0x14 (1 MByte)"); + anyfailed = 1; + } + // Test FW_RAM. puts("\r\nTesting FW_RAM (takes 15s on hw)...\r\n"); for (unsigned int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) { diff --git a/hw/application_fpga/fw/tk1/flash.h b/hw/application_fpga/fw/tk1/flash.h index cad6f07..1c60cc5 100644 --- a/hw/application_fpga/fw/tk1/flash.h +++ b/hw/application_fpga/fw/tk1/flash.h @@ -4,9 +4,8 @@ #ifndef TKEY_FLASH_H #define TKEY_FLASH_H +#include "types.h" #include -#include -#include #define WRITE_ENABLE 0x06 #define WRITE_DISABLE 0x04 From f34b4c3eb14523072577f6858d2fad9073031d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 4 Feb 2025 08:42:52 +0100 Subject: [PATCH 31/32] PoC: testapp: Call syscall accessing SPI flash --- hw/application_fpga/fw/testapp/Makefile | 2 + hw/application_fpga/fw/testapp/main.c | 12 +++ hw/application_fpga/fw/testapp/syscall.S | 94 ++++++++++++++++++++++++ hw/application_fpga/fw/testapp/syscall.h | 12 +++ hw/application_fpga/fw/tk1/syscall.c | 8 ++ 5 files changed, 128 insertions(+) create mode 100644 hw/application_fpga/fw/testapp/syscall.S create mode 100644 hw/application_fpga/fw/testapp/syscall.h diff --git a/hw/application_fpga/fw/testapp/Makefile b/hw/application_fpga/fw/testapp/Makefile index 5509877..1d1bb2f 100644 --- a/hw/application_fpga/fw/testapp/Makefile +++ b/hw/application_fpga/fw/testapp/Makefile @@ -43,10 +43,12 @@ all: testapp.bin TESTAPP_FMTFILES = \ $(P)/main.c \ + $(P)/syscall.h TESTAPP_OBJS = \ $(P)/main.o \ $(P)/crt0.o \ + $(P)/syscall.o \ $(P)/../tk1/led.o \ $(P)/../tk1/lib.o \ $(P)/../tk1/proto.o diff --git a/hw/application_fpga/fw/testapp/main.c b/hw/application_fpga/fw/testapp/main.c index ab16f80..c593547 100644 --- a/hw/application_fpga/fw/testapp/main.c +++ b/hw/application_fpga/fw/testapp/main.c @@ -6,8 +6,10 @@ #include "../tk1/led.h" #include "../tk1/lib.h" #include "../tk1/proto.h" +#include "../tk1/syscall_nrs.h" #include "../tk1/types.h" #include "../tk1_mem.h" +#include "syscall.h" // clang-format off volatile uint32_t *tk1name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0; @@ -209,6 +211,16 @@ int main(void) anyfailed = 1; } + syscall_enable(); + + // Syscall should be able to access flash + puts("\r\nReading SPI flash capacity using syscall...\r\n"); + int flash_capacity = syscall(TK1_SYSCALL_GET_FLASH_CAPACITY, 0); + if (flash_capacity != 0x14) { + failmsg("Expected SPI flash capacity: 0x14 (1 MByte)"); + anyfailed = 1; + } + // Test FW_RAM. *fw_ram = 0x21; if (*fw_ram == 0x21) { diff --git a/hw/application_fpga/fw/testapp/syscall.S b/hw/application_fpga/fw/testapp/syscall.S new file mode 100644 index 0000000..10db760 --- /dev/null +++ b/hw/application_fpga/fw/testapp/syscall.S @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2024 Tillitis AB +// SPDX-License-Identifier: BSD-2-Clause + +#include "../tk1/picorv32/custom_ops.S" + + .section ".text" + .globl syscall_enable + .globl syscall + +syscall_enable: + // Enable IRQs + li t0, 0x7fffffff // IRQ31 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + + ret + +syscall: + // Save registers to stack + addi sp, sp, -32*4 + sw x0, 0*4(sp) + sw x1, 1*4(sp) + // x2 (sp) is assumed to be preserved by the interrupt handler. + sw x3, 3*4(sp) + sw x4, 4*4(sp) + sw x5, 5*4(sp) + sw x6, 6*4(sp) + sw x7, 7*4(sp) + sw x8, 8*4(sp) + sw x9, 9*4(sp) + // x10 (a0) will contain syscall return value. And should not be saved. + sw x11, 11*4(sp) + sw x12, 12*4(sp) + sw x13, 13*4(sp) + sw x14, 14*4(sp) + sw x15, 15*4(sp) + sw x16, 16*4(sp) + sw x17, 17*4(sp) + sw x18, 18*4(sp) + sw x19, 19*4(sp) + sw x20, 20*4(sp) + sw x21, 21*4(sp) + sw x22, 22*4(sp) + sw x23, 23*4(sp) + sw x24, 24*4(sp) + sw x25, 25*4(sp) + sw x26, 26*4(sp) + sw x27, 27*4(sp) + sw x28, 28*4(sp) + sw x29, 29*4(sp) + sw x30, 30*4(sp) + sw x31, 31*4(sp) + + // Trigger syscall interrupt + li t0, (1 << 31) + and t0, a0, t0 + li t1, 0xe1000000 // Syscall interrupt trigger address + sw zero, 0(t1) // Trigger interrupt + + // Restore registers from stack + lw x0, 0*4(sp) + lw x1, 1*4(sp) + // x2 (sp) is assumed to be preserved by the interrupt handler. + lw x3, 3*4(sp) + lw x4, 4*4(sp) + lw x5, 5*4(sp) + lw x6, 6*4(sp) + lw x7, 7*4(sp) + lw x8, 8*4(sp) + lw x9, 9*4(sp) + // x10 (a0) contains syscall return value. And should not be destroyed. + lw x11, 11*4(sp) + lw x12, 12*4(sp) + lw x13, 13*4(sp) + lw x14, 14*4(sp) + lw x15, 15*4(sp) + lw x16, 16*4(sp) + lw x17, 17*4(sp) + lw x18, 18*4(sp) + lw x19, 19*4(sp) + lw x20, 20*4(sp) + lw x21, 21*4(sp) + lw x22, 22*4(sp) + lw x23, 23*4(sp) + lw x24, 24*4(sp) + lw x25, 25*4(sp) + lw x26, 26*4(sp) + lw x27, 27*4(sp) + lw x28, 28*4(sp) + lw x29, 29*4(sp) + lw x30, 30*4(sp) + lw x31, 31*4(sp) + addi sp, sp, 32*4 + + ret diff --git a/hw/application_fpga/fw/testapp/syscall.h b/hw/application_fpga/fw/testapp/syscall.h new file mode 100644 index 0000000..6fc3dc1 --- /dev/null +++ b/hw/application_fpga/fw/testapp/syscall.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2024 Tillitis AB +// SPDX-License-Identifier: BSD-2-Clause + +#include "../tk1/types.h" + +#ifndef TKEY_APP_SYSCALL_H +#define TKEY_APP_SYSCALL_H + +void syscall_enable(void); +int syscall(uint32_t number, uint32_t arg1); + +#endif diff --git a/hw/application_fpga/fw/tk1/syscall.c b/hw/application_fpga/fw/tk1/syscall.c index 9567620..7dfcda8 100644 --- a/hw/application_fpga/fw/tk1/syscall.c +++ b/hw/application_fpga/fw/tk1/syscall.c @@ -4,6 +4,7 @@ */ #include "../tk1/assert.h" +#include "../tk1/flash.h" #include "../tk1/led.h" #include "../tk1/syscall_nrs.h" #include "../tk1/types.h" @@ -24,6 +25,13 @@ int32_t syscall_handler(uint32_t syscall_nr, uint32_t arg1) case TK1_SYSCALL_SET_LED: set_led(arg1); return 0; + case TK1_SYSCALL_GET_FLASH_CAPACITY: { + uint8_t jedec_id[3]; + flash_release_powerdown(); + flash_read_jedec_id(jedec_id); + flash_powerdown(); + return jedec_id[2]; + } default: assert(1 == 2); } From c0a98196b6192dedd06cd99f96bc1ce8440db12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85gren?= Date: Tue, 4 Feb 2025 09:09:15 +0100 Subject: [PATCH 32/32] PoC: testapp: Call reset syscall --- hw/application_fpga/fw/testapp/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/application_fpga/fw/testapp/main.c b/hw/application_fpga/fw/testapp/main.c index c593547..67e4115 100644 --- a/hw/application_fpga/fw/testapp/main.c +++ b/hw/application_fpga/fw/testapp/main.c @@ -286,9 +286,12 @@ int main(void) } puts("\r\n"); - puts("Now echoing what you type...\r\n"); + puts("Now echoing what you type...Type + to reset device\r\n"); for (;;) { in = readbyte(); // blocks + if (in == '+') { + syscall(TK1_SYSCALL_RESET, 0); + } writebyte(in); } }