From 29c90fece42a6d2094f0893d6871cc4616e58b18 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/15] 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 31a8f906..61335b5c 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: @@ -402,11 +412,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 \ @@ -430,7 +442,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, $^) @@ -438,6 +450,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. #------------------------------------------------------------------- @@ -484,6 +505,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 1f3ab788..ac98d433 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 7e39ce04..a4dd8995 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 00000000..82b9262e --- /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 00000000..71889b9e --- /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 00000000..c98f474b --- /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 00000000..cb67e022 --- /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 39650d88..9f883dd6 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 00da22d0..ad5c1ef5 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 32605fb4..3a6f1da5 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 0300a8674c082abe3fe25c83b31633e391b29588 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/15] 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 61335b5c..2dab05c1 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) > $@ @@ -506,6 +514,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 00000000..82b9262e --- /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 00000000..71889b9e --- /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 00000000..c98f474b --- /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 00000000..12670b1f --- /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 4fb519887172618c050898a1ab812964831d2a15 Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Thu, 14 Nov 2024 14:02:53 +0100 Subject: [PATCH 03/15] 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 a4dd8995..4908ec3d 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'h00001fff; //---------------------------------------------------------------- // 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 1f94b396c1c5b0eeebd9dbeff1092f1b9c6265f4 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/15] 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 cd80f145..1aa0b0f9 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 4908ec3d..8c8951d3 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 9ebd18635458bddf28a1e40b8aa155469594b130 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/15] 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 ac98d433..58800407 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 8c8951d3..658b3847 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 9f883dd6..d937ec75 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 ad5c1ef5..72b528f4 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 a593d7b834a62a70441ae55a8590cb18a64b271b 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/15] 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 2dab05c1..ebf0085b 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: @@ -466,6 +477,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. @@ -515,6 +530,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 00000000..82b9262e --- /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 00000000..71889b9e --- /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 00000000..c98f474b --- /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 00000000..05b1f3ae --- /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 3a6f1da5..a3d5d6d6 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 cef5c904b1fee89375424b5d047d45dd53311188 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/15] 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 58800407..44e300cf 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 150f6b56..f649914f 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 658b3847..67acf517 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 05b1f3ae..67860ebd 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 d937ec75..113a362d 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 72b528f4..cbe95d23 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 a3d5d6d6..78c4ebdb 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 6ae73a3067c67b9843d283c3031c516f34200c63 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/15] 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 ebf0085b..1d1c5fed 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 @@ -481,6 +493,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. @@ -531,6 +547,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 00000000..82b9262e --- /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 00000000..71889b9e --- /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 00000000..9531da78 --- /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 00000000..016a3d8c --- /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 78c4ebdb..6024ad9e 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 9ffd7bc0f7a8fe78ef67698739924a2c3092262a Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Wed, 13 Nov 2024 16:13:16 +0100 Subject: [PATCH 09/15] 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 44e300cf..80fd9bab 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 67acf517..ba9aa9b3 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 1475a38d52b8e3f07cc1dce6dcdaaa155d54874a 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/15] 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 | 24 +++-------------- hw/application_fpga/tb/application_fpga_sim.v | 26 +++---------------- 4 files changed, 14 insertions(+), 61 deletions(-) diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 80fd9bab..ee25709f 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 ba9aa9b3..bc6981b6 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 113a362d..e4ec20bf 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), @@ -369,7 +363,6 @@ module application_fpga ( .gpio4(app_gpio4), .access_level_hi (irq31_eoi), - .access_level_med(irq30_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 cbe95d23..41655545 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), @@ -381,7 +375,6 @@ module application_fpga_sim ( .gpio4(app_gpio4), .access_level_hi (irq31_eoi), - .access_level_med(irq30_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 b45da9bf0cf301ad98ea78265d77f6d46d6230a3 Mon Sep 17 00:00:00 2001 From: Daniel Jobson Date: Fri, 15 Nov 2024 11:19:40 +0100 Subject: [PATCH 11/15] 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 ee25709f..1ebeb5a5 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 bc6981b6..efc2fd80 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 2aaa112d..8c8f1a3e 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 e4ec20bf..5ed2453a 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 41655545..a11c94d1 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 5638969f4f37843eaef873b8632b87f042b8ca29 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/15] 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 cb67e022..def67836 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 a7c9250166316f02a96fd86a4a0fca5d63f25f67 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/15] 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 12670b1f..1b1f5588 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 c9ae734aabfb5da787a1b6ba7c16b673e0e05cde 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/15] 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 67860ebd..cae085cf 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 6023d97ba3fd70308998f846982dbb745904be6b 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/15] 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 9531da78..dc2a0d60 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) { - 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) { +int32_t syscall_handler(uint32_t syscall_nr, uint32_t arg1) { switch (syscall_nr) { - case SYSCALL_HI_SET_LED: + case SYSCALL_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 016a3d8c..d295ff65 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: