Compare commits

...

33 Commits

Author SHA1 Message Date
Mikael Ågren
54889f8dce
Merge c0a98196b6192dedd06cd99f96bc1ce8440db12e into a5ed3cfaa94bfeea074c4f8621be857d80095427 2025-02-07 11:55:35 +00:00
Mikael Ågren
c0a98196b6
PoC: testapp: Call reset syscall 2025-02-07 12:54:09 +01:00
Mikael Ågren
f34b4c3eb1
PoC: testapp: Call syscall accessing SPI flash 2025-02-07 12:54:09 +01:00
Mikael Ågren
3665c3e245
PoC: testfw: Check that SPI flash is available in firmware mode 2025-02-07 12:54:09 +01:00
Mikael Ågren
52694b5c4f
PoC: Integrate spi and flash code 2025-02-07 12:54:08 +01:00
Daniel Jobson
ea7f7107f0
PoC: fw: Import spi.[ch] and flash.[ch] 2025-02-07 12:54:08 +01:00
Mikael Ågren
d1b8b6eee8
PoC: testfw: Break out tests running in app mode into separate app
App mode can no longer be controlled from software. So the tests have to
run from firmware RAM.
2025-02-07 12:54:08 +01:00
Mikael Ågren
1a505f4a21
PoC: testfw: Remove blake2s test
Removing the blake2s test since the possibility for the firmware to
expose a blake2s function to the app has been removed.
2025-02-07 12:54:07 +01:00
Mikael Ågren
926f3c68ed
PoC: Add experimental syscalls to firmware
Adds:
- SYSCALL_RESET
- SYSCALL_SET_LED
2025-02-07 12:54:07 +01:00
Mikael Ågren
e851efd35e
PoC: tb: Expand existing tests with access checks in app mode and syscalls
Checks availability of:
- CDI
- UDI
- RAM
- SPI
2025-02-07 12:54:07 +01:00
Mikael Ågren
97005bb83c
PoC: tb: Fix tb_tk1 test10 (SPI loop back test)
Fix test10. It broke while implementing interrupt based syscalls.

Cleaning up after the previous test. We reset the memory bus to a known
idle state. We also reset the DUT to make the SPI master visible.
2025-02-07 12:54:06 +01:00
Mikael Ågren
8c6e4a3352
PoC: tb: Fix tb_tk1 test5 (APP_START/APP_SIZE)
Fix test1. It broke while implementing interrupt based syscalls.

Instead of writing to ADDR_SYSTEM_MODE_CTRL, app mode is now entered
automatically when executing outside of ROM.
2025-02-07 12:54:06 +01:00
Mikael Ågren
6c2a7ef6c7
PoC: tb: Fix tb_tk1 test3 (CDI)
Fix test1. It broke while implementing interrupt based syscalls.

Instead of writing to ADDR_SYSTEM_MODE_CTRL, app mode is now entered
automatically when executing outside of ROM.
2025-02-07 12:54:06 +01:00
Mikael Ågren
5c56304b5d
PoC: tb: Add fetch instruction helper task to tb_tk1 2025-02-07 12:54:05 +01:00
Mikael Ågren
a646c8bfb4
PoC: tb: Remove tb_tk1 blake2s test
Removing the blake2s test since the blake2s registers are removed.
2025-02-07 12:54:05 +01:00
Mikael Ågren
007aa69052
PoC: tb: Update tk1 test bench with new ports
Fixing tests that broke when adding interrupt based syscalls
2025-02-07 12:54:05 +01:00
Mikael Ågren
93a74bcd1b
tb: Display errors in tb_tk1 even if DEBUG is 0
Always display errors to make them easy to find and troubleshoot.
2025-02-07 12:54:04 +01:00
Mikael Ågren
3269d25617
PoC: tb: Write data only once per call to write_word() in tb_tk1
Keep WE and CS high for one clock cycle instead of two. To avoid writing
the same address twice.
2025-02-07 12:54:04 +01:00
Mikael Ågren
7f95e0912f
PoC: Remove IRQ30 from fw/irqpoc_c_example
Removing IRQ30 since it us no longer exist
2025-02-07 12:54:04 +01:00
Mikael Ågren
82d408f405
PoC: Remove IRQ30 from fw/irqpoc_with_app
Removing IRQ30 since it us no longer exist
2025-02-07 12:54:03 +01:00
Mikael Ågren
14f266e506
PoC: Remove IRQ30 from fw/irqpoc_led_toggle
Removing IRQ30 since it us no longer exist
2025-02-07 12:54:03 +01:00
Mikael Ågren
052029236d
PoC: Remove IRQ30 from fw/irqpoc
Removing IRQ30 since it us no longer exist
2025-02-07 12:54:03 +01:00
Daniel Jobson
2ec2196e92
PoC: Make sensitive assets only readable/writable before system_mode is set
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 <mikael@tillitis.se>
2025-02-07 12:54:02 +01:00
Mikael Ågren
7f34f5db91
PoC: Remove low privilege syscall 2025-02-07 12:54:02 +01:00
Daniel Jobson
ecdbb25013
PoC: Deny access to the SPI master in app mode
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-02-07 12:54:02 +01:00
Mikael Ågren
4877e0ab99
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`
2025-02-07 12:54:01 +01:00
Mikael Ågren
62dba7c4fe
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
2025-02-07 12:54:01 +01:00
Mikael Ågren
a871d23d5d
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.
2025-02-07 12:54:01 +01:00
Mikael Ågren
e4d19e83ce
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 <jobson@tillitis.se>
2025-02-07 12:54:00 +01:00
Mikael Ågren
b53666e497
PoC: Remove Blake2s register 2025-02-07 12:54:00 +01:00
Daniel Jobson
2d762faba7
PoC: Automatically control system_mode in hardware
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 <mikael@tillitis.se>
2025-02-07 12:54:00 +01:00
Mikael Ågren
d36e9c9e3d
PoC: Add LED toggling interrupt example
Add example firmware for demoing interrupts on Tkey hardware.
2025-02-04 12:26:01 +01:00
Mikael Ågren
5535323b06
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:
70f3c33ac8
2025-02-04 12:25:57 +01:00
44 changed files with 2805 additions and 282 deletions

View File

@ -127,7 +127,10 @@ FIRMWARE_OBJS = \
$(P)/fw/tk1/lib.o \
$(P)/fw/tk1/assert.o \
$(P)/fw/tk1/led.o \
$(P)/fw/tk1/blake2s/blake2s.o
$(P)/fw/tk1/blake2s/blake2s.o \
$(P)/fw/tk1/syscall.o \
$(P)/fw/tk1/spi.o \
$(P)/fw/tk1/flash.o
FIRMWARE_SOURCES = \
$(P)/fw/tk1/main.c \
@ -135,14 +138,36 @@ FIRMWARE_SOURCES = \
$(P)/fw/tk1/lib.c \
$(P)/fw/tk1/assert.c \
$(P)/fw/tk1/led.c \
$(P)/fw/tk1/blake2s/blake2s.c
$(P)/fw/tk1/blake2s/blake2s.c \
$(P)/fw/tk1/syscall.c \
$(P)/fw/tk1/spi.c \
$(P)/fw/tk1/flash.c
TESTFW_OBJS = \
$(P)/fw/testfw/main.o \
$(P)/fw/testfw/start.o \
$(P)/fw/tk1/proto.o \
$(P)/fw/tk1/lib.o \
$(P)/fw/tk1/blake2s/blake2s.o
$(P)/fw/tk1/spi.o \
$(P)/fw/tk1/flash.o
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
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.
@ -180,6 +205,10 @@ 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)
$(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 $@
@ -222,6 +251,18 @@ 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 $@
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 $@
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) > $@
@ -232,6 +273,13 @@ 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) > $@
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
check-binary-hashes:
@ -401,11 +449,13 @@ application_fpga_testfw.bin: application_fpga.asc bram_fw.hex testfw.hex
#-------------------------------------------------------------------
# Build testbench simulation for the design
#-------------------------------------------------------------------
SIMFIRMWARE = simfirmware.hex
tb_application_fpga: $(SIM_VERILOG_SRCS) \
$(VERILOG_SRCS) \
$(PICORV32_SRCS) \
$(ICE40_SIM_CELLS) \
simfirmware.hex
$(SIMFIRMWARE)
python3 ./tools/app_bin_to_spram_hex.py \
./tb/app.bin \
./tb/output_spram0.hex \
@ -429,7 +479,7 @@ tb_application_fpga: $(SIM_VERILOG_SRCS) \
-DNO_ICE40_DEFAULT_ASSIGNMENTS \
-DAPP_SIZE=$(shell ls -l tb/app.bin| awk '{print $$5}') \
-DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
-DFIRMWARE_HEX=\"$(P)/simfirmware.hex\" \
-DFIRMWARE_HEX=\"$(P)/$(SIMFIRMWARE)\" \
-DUDS_HEX=\"$(P)/data/uds.hex\" \
-DUDI_HEX=\"$(P)/data/udi.hex\" \
$(filter %.v, $^)
@ -437,6 +487,23 @@ 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
.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
.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.
#-------------------------------------------------------------------
@ -483,6 +550,10 @@ clean_fw:
rm -f $(FIRMWARE_OBJS)
rm -f testfw.{elf,elf.map,bin,hex}
rm -f $(TESTFW_OBJS)
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

View File

@ -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.
@ -24,17 +25,18 @@ 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 |
| IRQ31\_SET | 0xe1 |
| TK1 | 0xff |
## `clk_reset_gen`
@ -96,6 +98,71 @@ hours, days) there is also a 32 bit prescaler.
The timer is available to use by firmware and applications.
## `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 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 | 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.
## Restricted resources
The following table shows resource availablility for each execution
mode:
| *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`
See [tk1 README](core/tk1/README.md) for details.
@ -107,7 +174,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.

View File

@ -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;

View File

@ -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
```

View File

@ -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,
@ -45,6 +45,10 @@ module tk1 #(
output wire gpio3,
output wire gpio4,
input wire access_level_hi,
output wire fw_ram_en,
input wire cs,
input wire we,
input wire [ 7 : 0] address,
@ -79,8 +83,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;
@ -107,6 +109,7 @@ module tk1 #(
localparam FW_RAM_FIRST = 32'hd0000000;
localparam FW_RAM_LAST = 32'hd00007ff;
localparam FW_ROM_LAST = 32'h000017ff;
//----------------------------------------------------------------
// Registers including update variables and write enable.
@ -115,6 +118,7 @@ module tk1 #(
reg cdi_mem_we;
reg system_mode_reg;
reg system_mode_new;
reg system_mode_we;
reg [ 2 : 0] led_reg;
@ -133,9 +137,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;
@ -180,6 +181,11 @@ module tk1 #(
reg spi_tx_data_vld;
wire spi_ready;
wire [ 7 : 0] spi_rx_data;
wire spi_access_en;
wire rom_exec_en;
wire system_mode;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
@ -187,8 +193,6 @@ module tk1 #(
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;
@ -199,6 +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.
@ -258,7 +268,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;
@ -290,7 +299,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
@ -313,10 +322,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
@ -385,6 +390,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.
@ -402,6 +410,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;
@ -411,18 +425,32 @@ 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;
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;
@ -437,16 +465,12 @@ 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;
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
@ -457,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
@ -472,26 +496,20 @@ 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
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
@ -513,15 +531,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
@ -558,26 +582,26 @@ 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
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
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

View File

@ -18,7 +18,7 @@ module tb_tk1 ();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 1;
parameter DEBUG = 0;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
@ -62,6 +62,7 @@ module tb_tk1 ();
localparam ADDR_SPI_XFER = 8'h81;
localparam ADDR_SPI_DATA = 8'h82;
localparam APP_RAM_START = 32'h40000000;
//----------------------------------------------------------------
// Register and Wire declarations.
@ -76,7 +77,7 @@ module tb_tk1 ();
reg tb_clk;
reg tb_reset_n;
reg tb_cpu_trap;
wire tb_system_mode;
wire tb_rw_locked;
reg [31 : 0] tb_cpu_addr;
reg tb_cpu_instr;
@ -95,6 +96,10 @@ module tb_tk1 ();
wire tb_gpio3;
wire tb_gpio4;
reg tb_access_level_hi;
wire tb_fw_ram_en;
wire tb_spi_ss;
wire tb_spi_sck;
wire tb_spi_mosi;
@ -122,7 +127,7 @@ module tb_tk1 ();
.reset_n(tb_reset_n),
.cpu_trap(tb_cpu_trap),
.system_mode(tb_system_mode),
.rw_locked(tb_rw_locked),
.cpu_addr (tb_cpu_addr),
.cpu_instr (tb_cpu_instr),
@ -141,6 +146,10 @@ module tb_tk1 ();
.gpio3(tb_gpio3),
.gpio4(tb_gpio4),
.access_level_hi(tb_access_level_hi),
.fw_ram_en(tb_fw_ram_en),
.spi_ss (tb_spi_ss),
.spi_sck (tb_spi_sck),
.spi_mosi(tb_spi_mosi),
@ -192,7 +201,7 @@ module tb_tk1 ();
$display("------------");
if (tb_main_monitor) begin
$display("Inputs and outputs:");
$display("tb_cpu_trap: 0x%1x, system_mode: 0x%1x", tb_cpu_trap, tb_system_mode);
$display("tb_cpu_trap: 0x%1x, system_mode: 0x%1x", tb_cpu_trap, dut.system_mode);
$display("cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x, force_tap: 0x%1x",
tb_cpu_addr, tb_cpu_instr, tb_cpu_valid, tb_force_trap);
$display("ram_addr_rand: 0x%08x, ram_data_rand: 0x%08x", tb_ram_addr_rand,
@ -277,6 +286,8 @@ module tb_tk1 ();
tb_gpio1 = 1'h0;
tb_gpio2 = 1'h0;
tb_access_level_hi = 1'h0;
tb_cs = 1'h0;
tb_we = 1'h0;
tb_address = 8'h0;
@ -285,6 +296,25 @@ module tb_tk1 ();
endtask // init_sim
//----------------------------------------------------------------
// restore_mem_bus()
//
// Restore memory bus to its initial state
//----------------------------------------------------------------
task restore_mem_bus();
begin : restore_mem_bus
tb_cpu_addr = 32'h0;
tb_cpu_instr = 1'h0;
tb_cpu_valid = 1'h0;
tb_cs = 1'h0;
tb_we = 1'h0;
tb_address = 8'h0;
tb_write_data = 32'h0;
end
endtask
//----------------------------------------------------------------
// write_word()
//
@ -301,7 +331,7 @@ module tb_tk1 ();
tb_write_data = word;
tb_cs = 1;
tb_we = 1;
#(2 * CLK_PERIOD);
#(CLK_PERIOD);
tb_cs = 0;
tb_we = 0;
end
@ -354,21 +384,53 @@ module tb_tk1 ();
#(CLK_PERIOD);
tb_cs = 1'h0;
if (DEBUG) begin
if (read_data == expected) begin
if (read_data == expected) begin
if (DEBUG) begin
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
end
else begin
$display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data,
address, expected);
error_ctr = error_ctr + 1;
end
$display("");
end
else begin
$display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data,
address, expected);
error_ctr = error_ctr + 1;
end
end
endtask // read_check_word
//----------------------------------------------------------------
// check_equal()
//
// Check that two values are equal
//----------------------------------------------------------------
task check_equal(input [31 : 0] value, input [31 : 0] expected);
begin : check_equal
if (value != expected) begin
$display("--- Error: Got 0x%08x, expected 0x%08x", expected, value);
error_ctr = error_ctr + 1;
end
end
endtask // check_equal
//----------------------------------------------------------------
// fetch_instruction()
//
// Simulate fetch of an instruction at specified address.
//----------------------------------------------------------------
task fetch_instruction(input [31 : 0] address);
begin : fetch_instruction
tb_cpu_addr = address;
tb_cpu_instr = 1'h1;
tb_cpu_valid = 1'h1;
#(CLK_PERIOD);
tb_cpu_addr = 32'h0;
tb_cpu_instr = 1'h0;
tb_cpu_valid = 1'h0;
end
endtask // fetch_instruction
//----------------------------------------------------------------
// test1()
// Read out name and version.
@ -400,10 +462,27 @@ module tb_tk1 ();
$display("");
$display("--- test2: Read out UDI started.");
tb_access_level_hi = 0;
reset_dut();
read_check_word(ADDR_UDI_FIRST, 32'h00010203);
read_check_word(ADDR_UDI_LAST, 32'h04050607);
$display("--- test2: Switch to app mode.");
fetch_instruction(APP_RAM_START);
read_check_word(ADDR_UDI_FIRST, 32'h0);
read_check_word(ADDR_UDI_LAST, 32'h0);
$display("--- test2: Enter syscall.");
tb_access_level_hi = 1;
read_check_word(ADDR_UDI_FIRST, 32'h0);
read_check_word(ADDR_UDI_LAST, 32'h0);
$display("--- test2: Leave syscall.");
tb_access_level_hi = 0;
$display("--- test2: completed.");
$display("");
end
@ -418,6 +497,10 @@ module tb_tk1 ();
begin
tc_ctr = tc_ctr + 1;
$display("--- test5: Reset DUT to switch to fw mode.");
tb_access_level_hi = 0;
reset_dut();
$display("");
$display("--- test3: Write and read CDI started.");
$display("--- test3: Write CDI.");
@ -441,9 +524,9 @@ module tb_tk1 ();
read_check_word(ADDR_CDI_LAST + 0, 32'h70717273);
$display("--- test3: Switch to app mode.");
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hdeadbeef);
fetch_instruction(APP_RAM_START);
$display("--- test3: Try to write CDI again.");
$display("--- test3: Try to write CDI from app mode.");
write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc);
write_word(ADDR_CDI_FIRST + 1, 32'hefeeedec);
write_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc);
@ -453,7 +536,7 @@ module tb_tk1 ();
write_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c);
write_word(ADDR_CDI_FIRST + 7, 32'h7f7e7d7c);
$display("--- test3: Read CDI again.");
$display("--- test3: Read CDI from app mode.");
read_check_word(ADDR_CDI_FIRST + 0, 32'hf0f1f2f3);
read_check_word(ADDR_CDI_FIRST + 1, 32'he0e1e2e3);
read_check_word(ADDR_CDI_FIRST + 2, 32'hd0d1d2d3);
@ -463,46 +546,38 @@ module tb_tk1 ();
read_check_word(ADDR_CDI_FIRST + 6, 32'h80818283);
read_check_word(ADDR_CDI_LAST + 0, 32'h70717273);
$display("--- test3: Enter syscall.");
tb_access_level_hi = 1;
$display("--- test3: Try to write CDI from syscall.");
write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc);
write_word(ADDR_CDI_FIRST + 1, 32'hefeeedec);
write_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc);
write_word(ADDR_CDI_FIRST + 3, 32'hcfcecdcc);
write_word(ADDR_CDI_FIRST + 4, 32'hafaeadac);
write_word(ADDR_CDI_FIRST + 5, 32'h9f9e9d9c);
write_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c);
write_word(ADDR_CDI_FIRST + 7, 32'h7f7e7d7c);
$display("--- test3: Read CDI from syscall.");
read_check_word(ADDR_CDI_FIRST + 0, 32'hf0f1f2f3);
read_check_word(ADDR_CDI_FIRST + 1, 32'he0e1e2e3);
read_check_word(ADDR_CDI_FIRST + 2, 32'hd0d1d2d3);
read_check_word(ADDR_CDI_FIRST + 3, 32'hc0c1c2c3);
read_check_word(ADDR_CDI_FIRST + 4, 32'ha0a1a2a3);
read_check_word(ADDR_CDI_FIRST + 5, 32'h90919293);
read_check_word(ADDR_CDI_FIRST + 6, 32'h80818283);
read_check_word(ADDR_CDI_LAST + 0, 32'h70717273);
$display("--- test3: Leave syscall.");
tb_access_level_hi = 0;
$display("--- test3: completed.");
$display("");
end
endtask // test3
//----------------------------------------------------------------
// test4()
// Write and read blake2s entry point.
//----------------------------------------------------------------
task test4;
begin
tc_ctr = tc_ctr + 1;
$display("");
$display("--- test4: Write and read blake2s entry point in fw mode started.");
$display("--- test4: Reset DUT to switch to fw mode.");
reset_dut();
$display("--- test4: Write Blake2s entry point.");
write_word(ADDR_BLAKE2S, 32'hcafebabe);
$display("--- test4: Read Blake2s entry point.");
read_check_word(ADDR_BLAKE2S, 32'hcafebabe);
$display("--- test4: Switch to app mode.");
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf00ff00f);
$display("--- test4: Write Blake2s entry point again.");
write_word(ADDR_BLAKE2S, 32'hdeadbeef);
$display("--- test4: Read Blake2s entry point again");
read_check_word(ADDR_BLAKE2S, 32'hcafebabe);
$display("--- test4: completed.");
$display("");
end
endtask // test4
//----------------------------------------------------------------
// test5()
// Write and read APP start address end size.
@ -525,7 +600,7 @@ module tb_tk1 ();
read_check_word(ADDR_APP_SIZE, 32'h47114711);
$display("--- test5: Switch to app mode.");
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf000000);
fetch_instruction(APP_RAM_START);
$display("--- test5: Write app start address and size again.");
write_word(ADDR_APP_START, 32'hdeadbeef);
@ -543,7 +618,7 @@ module tb_tk1 ();
//----------------------------------------------------------------
// test6()
// Write RAM address and data randomizatio in fw mode.
// Write and read RAM-address and data randomization.
//----------------------------------------------------------------
task test6;
begin
@ -552,6 +627,7 @@ module tb_tk1 ();
$display("");
$display("--- test6: Write RAM addr and data randomization in fw mode.");
$display("--- test6: Reset DUT to switch to fw mode.");
tb_access_level_hi = 0;
reset_dut();
$display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND .");
@ -562,9 +638,14 @@ module tb_tk1 ();
"--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers.");
$display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x",
dut.ram_addr_rand, dut.ram_data_rand);
check_equal(dut.ram_addr_rand, 15'h1337);
check_equal(dut.ram_data_rand, 32'h47114711);
read_check_word(ADDR_RAM_ADDR_RAND, 32'h0);
read_check_word(ADDR_RAM_DATA_RAND, 32'h0);
$display("--- test6: Switch to app mode.");
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf000000);
fetch_instruction(APP_RAM_START);
$display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND again.");
write_word(ADDR_RAM_ADDR_RAND, 32'hdeadbeef);
@ -574,6 +655,30 @@ module tb_tk1 ();
"--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers.");
$display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x",
dut.ram_addr_rand, dut.ram_data_rand);
check_equal(dut.ram_addr_rand, 15'h1337);
check_equal(dut.ram_data_rand, 32'h47114711);
read_check_word(ADDR_RAM_ADDR_RAND, 32'h0);
read_check_word(ADDR_RAM_DATA_RAND, 32'h0);
$display("--- test6: Enter syscall.");
tb_access_level_hi = 1;
$display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND again.");
write_word(ADDR_RAM_ADDR_RAND, 32'hdeadbeef);
write_word(ADDR_RAM_DATA_RAND, 32'hf00ff00f);
$display(
"--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers.");
$display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x",
dut.ram_addr_rand, dut.ram_data_rand);
check_equal(dut.ram_addr_rand, 15'h1337);
check_equal(dut.ram_data_rand, 32'h47114711);
read_check_word(ADDR_RAM_ADDR_RAND, 32'h0);
read_check_word(ADDR_RAM_DATA_RAND, 32'h0);
$display("--- test6: Leave syscall.");
tb_access_level_hi = 0;
$display("--- test6: completed.");
$display("");
@ -655,17 +760,20 @@ module tb_tk1 ();
write_word(ADDR_CPU_MON_LAST, 32'hdeadcafe);
$display("--- test9: cpu_mon_first_reg: 0x%08x, cpu_mon_last_reg: 0x%08x",
dut.cpu_mon_first_reg, dut.cpu_mon_last_reg);
check_equal(dut.cpu_mon_first_reg, 32'h10000000);
check_equal(dut.cpu_mon_last_reg, 32'h20000000);
$display("--- test9: force_trap before illegal access: 0x%1x", tb_force_trap);
$display("--- test9: Creating an illegal access.");
tb_cpu_addr = 32'h13371337;
tb_cpu_instr = 1'h1;
tb_cpu_valid = 1'h1;
#(2 * CLK_PERIOD);
fetch_instruction(32'h13371337);
$display("--- test9: cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x", tb_cpu_addr,
tb_cpu_instr, tb_cpu_valid);
check_equal(dut.cpu_mon_first_reg, 32'h10000000);
check_equal(dut.cpu_mon_last_reg, 32'h20000000);
$display("--- test9: force_trap: 0x%1x", tb_force_trap);
check_equal(tb_force_trap, 1);
$display("--- test9: completed.");
$display("");
@ -673,6 +781,66 @@ module tb_tk1 ();
endtask // test9
//----------------------------------------------------------------
// check_inverting_spi_loopback_transfer_succeeds()
// Do an SPI tranfer. Check that the received value is the inverse
// of the value sent.
//----------------------------------------------------------------
task check_inverting_spi_loopback_transfer_succeeds(input [32 : 0] data);
begin : check_inverting_spi_loopback_transfer
$display("--- test10: Sending a byte.");
write_word(ADDR_SPI_EN, 32'h1);
write_word(ADDR_SPI_DATA, data);
write_word(ADDR_SPI_XFER, 32'h1);
// Ready ready flag in SPI until it is set.
read_word(ADDR_SPI_XFER);
while (!tb_read_data) begin
read_word(ADDR_SPI_XFER);
end
$display("--- test10: Byte should have been sent.");
#(2 * CLK_PERIOD);
read_check_word(ADDR_SPI_DATA, ~data[7 : 0] & 8'hff);
write_word(ADDR_SPI_EN, 32'h0);
end
endtask
//----------------------------------------------------------------
// check_spi_does_not_transfer()
// Do an SPI transfer. Check that the SS, SCK and MISO signal are
// not active.
//----------------------------------------------------------------
task check_spi_does_not_transfer;
begin : check_spi_does_not_transfer
reg [31 : 0] wait_ctr;
reg error;
localparam CLK_PER_SPI_BIT = 3;
localparam WAIT_MARGIN = 10;
error = 0;
wait_ctr = CLK_PER_SPI_BIT * 8 * WAIT_MARGIN;
$display("--- test10: Sending a byte.");
write_word(ADDR_SPI_EN, 32'h1);
write_word(ADDR_SPI_DATA, 32'haa);
write_word(ADDR_SPI_XFER, 32'h1);
$display("--- test10: Waiting to see if SPI signals change state.");
while (!error && (wait_ctr != 0)) begin
if (~tb_spi_ss || tb_spi_sck || tb_spi_mosi) begin
$display("--- Error: SPI signals changed state");
error_ctr = error_ctr + 1;
error = 1;
end
#(CLK_PERIOD);
wait_ctr = wait_ctr - 1;
end
end
endtask
//----------------------------------------------------------------
// test10()
// SPI master loopback test.
@ -683,28 +851,28 @@ module tb_tk1 ();
tb_monitor = 0;
tb_spi_monitor = 0;
restore_mem_bus();
reset_dut();
$display("");
$display("--- test10: Loopback in SPI Master started.");
#(CLK_PERIOD);
// Sending 0xa7 trough the inverting loopback.
$display("--- test10: Sending a byte.");
write_word(ADDR_SPI_EN, 32'h1);
write_word(ADDR_SPI_DATA, 32'ha7);
write_word(ADDR_SPI_XFER, 32'h1);
check_inverting_spi_loopback_transfer_succeeds(32'ha7);
// Ready ready flag in SPI until it is set.
read_word(ADDR_SPI_XFER);
while (!tb_read_data) begin
read_word(ADDR_SPI_XFER);
end
$display("--- test10: Byte should have been sent.");
$display("--- test10: Switch to app mode.");
fetch_instruction(APP_RAM_START);
// 0x58 is the inverse of 0xa7.
#(2 * CLK_PERIOD);
read_check_word(ADDR_SPI_DATA, 32'h58);
write_word(ADDR_SPI_EN, 32'h0);
check_spi_does_not_transfer();
$display("--- test10: Enter syscall.");
tb_access_level_hi = 1;
check_inverting_spi_loopback_transfer_succeeds(32'hc8);
$display("--- test10: Leave syscall.");
tb_access_level_hi = 0;
tb_monitor = 0;
tb_spi_monitor = 0;
@ -746,7 +914,6 @@ module tb_tk1 ();
test1();
test2();
test3();
test4();
test5();
test6();
test7();

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,10 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
int main(void)
{
while (1) {
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* 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"
.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.
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, 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.
loop:
j loop
.align 4 // Padding to please makehex.py which requires even 4-byte file
// sizes.

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,32 @@
/*
* 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_SET_LED 10
int32_t syscall_handler(uint32_t syscall_nr, uint32_t arg1) {
switch (syscall_nr) {
case SYSCALL_SET_LED:
set_led(arg1);
return 0;
default:
assert(1 == 2);
}
assert(1 == 2);
return -1; // This should never run
}
int main(void)
{
while (1) {
}
}

View File

@ -0,0 +1,241 @@
/*
* 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 syscall 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.
li t4, (1 << 31)
beq x4, t4, irq_source_ok
unexpected_irq_source:
illegal_insn()
j unexpected_irq_source
irq_source_ok:
// Save app stack pointer. App is responsible for saving the rest of
// the registers.
la t0, FW_SP_STORAGE
sw sp, 0(t0)
// Setup firmware stack pointer
la sp, FW_STACK_TOP
// Run syscall handler
call syscall_handler
// Restore app stack pointer
la t0, FW_SP_STORAGE
lw sp, 0(t0)
// Verify that interrupt return address (x3) is in app RAM
li t0, TK1_RAM_BASE // 0x40000000
blt x3, t0, x3_invalid
li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000
bge x3, t0, x3_invalid
j x3_valid
x3_invalid:
illegal_insn()
j x3_invalid
x3_valid:
// Remove data left over from the syscall handling
mv x0, zero
mv x1, zero
// x2 (sp) is assumed to be preserved by the interrupt handler
// x3 (interrupt return address) assumed preserved
mv x4, zero
mv x5, zero
mv x6, zero
mv x7, zero
mv x8, zero
mv x9, zero
// x10 (a0) contains syscall return value. And should not be destroyed.
mv x11, zero
mv x12, zero
mv x13, zero
mv x14, zero
mv x15, zero
mv x16, zero
mv x17, zero
mv x18, zero
mv x19, zero
mv x20, zero
mv x21, zero
mv x22, zero
mv x23, zero
mv x24, zero
mv x25, zero
mv x26, zero
mv x27, zero
mv x28, zero
mv x29, zero
mv x30, zero
mv x31, zero
picorv32_retirq_insn() // Return from interrupt
//
// Init
//
.=0x100
init:
li t0, TK1_MMIO_TK1_LED
li t1, LED_RED
sw t1, 0(t0)
// Enable IRQs
li t0, 0x7fffffff // IRQ31 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 SYSCALL_SET_LED 10
.=0x1000
app_start:
// Set stack pointer to end of app RAM
li sp, 0x4001fffc
app_loop:
li a0, SYSCALL_SET_LED
li a1, LED_GREEN
call app_syscall
call app_delay
li a0, SYSCALL_SET_LED
li a1, LED_BLUE
call app_syscall
call app_delay
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)
// Trigger syscall interrupt
li t0, (1 << 31)
and t0, a0, t0
li t1, 0xe1000000 // Syscall interrupt trigger address
sw zero, 0(t1) // Trigger interrupt
// Restore registers from stack
lw x0, 0*4(sp)
lw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
lw x3, 3*4(sp)
lw x4, 4*4(sp)
lw x5, 5*4(sp)
lw x6, 6*4(sp)
lw x7, 7*4(sp)
lw x8, 8*4(sp)
lw x9, 9*4(sp)
// x10 (a0) contains syscall return value. And should not be destroyed.
lw x11, 11*4(sp)
lw x12, 12*4(sp)
lw x13, 13*4(sp)
lw x14, 14*4(sp)
lw x15, 15*4(sp)
lw x16, 16*4(sp)
lw x17, 17*4(sp)
lw x18, 18*4(sp)
lw x19, 19*4(sp)
lw x20, 20*4(sp)
lw x21, 21*4(sp)
lw x22, 22*4(sp)
lw x23, 23*4(sp)
lw x24, 24*4(sp)
lw x25, 25*4(sp)
lw x26, 26*4(sp)
lw x27, 27*4(sp)
lw x28, 28*4(sp)
lw x29, 29*4(sp)
lw x30, 30*4(sp)
lw x31, 31*4(sp)
addi sp, sp, 32*4
ret
app_delay:
li t5, 0x100000
app_delay_dec:
addi t5, t5, -1
bne t5, zero, app_delay_dec
ret
.align 4
app_end:

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,10 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
int main(void)
{
while (1) {
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// 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.
//
// | Color | Execution context |
// |-------|-------------------|
// | Blue | Firmware loop |
// | Green | IRQ31 |
// | Red | Unexpected IRQ |
//
#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.
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, 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.
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.

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,10 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
int main(void)
{
while (1) {
}
}

View File

@ -0,0 +1,110 @@
/*
* 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 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
#define illegal_insn() .word 0
.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.
li t4, (1 << 31)
bne x4, t4, unexpected_irq
// Firmware RAM should be readable from IRQ_SYSCALL
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
//
.=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, 0x7fffffff // IRQ31
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:
// Firmware RAM should not be readable from app mode
call check_cannot_read_test_val_from_fw_ram
// 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.
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:

View File

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

View File

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

View File

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
.section ".text.init"
.global _start
_start:
li x1, 0
li x2, 0
li x3, 0
li x4, 0
li x5, 0
li x6, 0
li x7, 0
li x8, 0
li x9, 0
li x10,0
li x11,0
li x12,0
li x13,0
li x14,0
li x15,0
li x16,0
li x17,0
li x18,0
li x19,0
li x20,0
li x21,0
li x22,0
li x23,0
li x24,0
li x25,0
li x26,0
li x27,0
li x28,0
li x29,0
li x30,0
li x31,0
/* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
li sp, 0x4001fff0
/* zero-init bss section */
la a0, _sbss
la a1, _ebss
bge a0, a1, end_init_bss
loop_init_bss:
sw zero, 0(a0)
addi a0, a0, 4
blt a0, a1, loop_init_bss
end_init_bss:
call main

View File

@ -0,0 +1,297 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../tk1/led.h"
#include "../tk1/lib.h"
#include "../tk1/proto.h"
#include "../tk1/syscall_nrs.h"
#include "../tk1/types.h"
#include "../tk1_mem.h"
#include "syscall.h"
// clang-format off
volatile uint32_t *tk1name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0;
volatile uint32_t *tk1name1 = (volatile uint32_t *)TK1_MMIO_TK1_NAME1;
volatile uint32_t *uds = (volatile uint32_t *)TK1_MMIO_UDS_FIRST;
volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
volatile uint32_t *system_mode_ctrl = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_MODE_CTRL;
volatile uint8_t *fw_ram = (volatile uint8_t *)TK1_MMIO_FW_RAM_BASE;
volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
// clang-format on
#define UDS_WORDS 8
#define UDI_WORDS 2
#define CDI_WORDS 8
void *memcpy(void *dest, const void *src, size_t n)
{
uint8_t *src_byte = (uint8_t *)src;
uint8_t *dest_byte = (uint8_t *)dest;
for (int i = 0; i < n; i++) {
dest_byte[i] = src_byte[i];
}
return dest;
}
void puts(char *reason)
{
for (char *c = reason; *c != '\0'; c++) {
writebyte(*c);
}
}
void putsn(char *p, int n)
{
for (int i = 0; i < n; i++) {
writebyte(p[i]);
}
}
void puthex(uint8_t c)
{
unsigned int upper = (c >> 4) & 0xf;
unsigned int lower = c & 0xf;
writebyte(upper < 10 ? '0' + upper : 'a' - 10 + upper);
writebyte(lower < 10 ? '0' + lower : 'a' - 10 + lower);
}
void puthexn(uint8_t *p, int n)
{
for (int i = 0; i < n; i++) {
puthex(p[i]);
}
}
void hexdump(void *buf, int len)
{
uint8_t *byte_buf = (uint8_t *)buf;
for (int i = 0; i < len; i++) {
puthex(byte_buf[i]);
if (i % 2 == 1) {
writebyte(' ');
}
if (i != 1 && i % 16 == 1) {
puts("\r\n");
}
}
puts("\r\n");
}
void reverseword(uint32_t *wordp)
{
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
((*wordp & 0x0000ff00) << 8) | ((*wordp & 0x000000ff) << 24);
}
uint32_t wait_timer_tick(uint32_t last_timer)
{
uint32_t newtimer;
for (;;) {
newtimer = *timer;
if (newtimer != last_timer) {
return newtimer;
}
}
}
void zero_fwram(void)
{
for (int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) {
fw_ram[i] = 0x00;
}
}
int check_fwram_zero_except(unsigned int offset, uint8_t expected_val)
{
int failed = 0;
for (unsigned int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) {
uint32_t addr = TK1_MMIO_FW_RAM_BASE + i;
uint8_t *p = (uint8_t *)addr;
uint8_t val = *(volatile uint8_t *)p;
int failed_now = 0;
if (i == offset) {
if (val != expected_val) {
failed_now = 1;
puts(" wrong value at: ");
}
} else {
if (val != 0) {
failed_now = 1;
puts(" not zero at: ");
}
}
if (failed_now) {
failed = 1;
reverseword(&addr);
puthexn((uint8_t *)&addr, 4);
puts("\r\n");
}
}
return failed;
}
void failmsg(char *s)
{
puts("FAIL: ");
puts(s);
puts("\r\n");
}
int main(void)
{
uint8_t in;
set_led(LED_BLUE);
// Wait for terminal program and a character to be typed
in = readbyte();
puts("\r\nI'm testapp on:");
// Output the TK1 core's NAME0 and NAME1
uint32_t name;
wordcpy_s(&name, 1, (void *)tk1name0, 1);
reverseword(&name);
putsn((char *)&name, 4);
puts(" ");
wordcpy_s(&name, 1, (void *)tk1name1, 1);
reverseword(&name);
putsn((char *)&name, 4);
puts("\r\n");
uint32_t zeros[8];
memset(zeros, 0, 8 * 4);
int anyfailed = 0;
uint32_t uds_local[UDS_WORDS];
uint32_t udi_local[UDI_WORDS];
uint32_t sw = *system_mode_ctrl;
if (sw != 0xffffffff) {
failmsg("system_mode_ctrl is not 0xffffffff");
anyfailed = 1;
}
// Should NOT be able to read from UDS in app-mode.
wordcpy_s(uds_local, UDS_WORDS, (void *)uds, UDS_WORDS);
if (!memeq(uds_local, zeros, UDS_WORDS * 4)) {
failmsg("Read from UDS in app-mode");
anyfailed = 1;
}
// Should NOT be able to read from UDI in app-mode.
wordcpy_s(udi_local, UDI_WORDS, (void *)udi, UDI_WORDS);
if (!memeq(udi_local, zeros, UDI_WORDS * 4)) {
failmsg("Read from UDI in app-mode");
anyfailed = 1;
}
uint32_t cdi_local[CDI_WORDS];
uint32_t cdi_local2[CDI_WORDS];
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
// Write to CDI should NOT have any effect in app mode.
wordcpy_s((void *)cdi, CDI_WORDS, zeros, CDI_WORDS);
wordcpy_s(cdi_local2, CDI_WORDS, (void *)cdi, CDI_WORDS);
if (!memeq(cdi_local, cdi_local2, CDI_WORDS * 4)) {
failmsg("Write to CDI in app-mode");
anyfailed = 1;
}
syscall_enable();
// Syscall should be able to access flash
puts("\r\nReading SPI flash capacity using syscall...\r\n");
int flash_capacity = syscall(TK1_SYSCALL_GET_FLASH_CAPACITY, 0);
if (flash_capacity != 0x14) {
failmsg("Expected SPI flash capacity: 0x14 (1 MByte)");
anyfailed = 1;
}
// Test FW_RAM.
*fw_ram = 0x21;
if (*fw_ram == 0x21) {
failmsg("Write and read FW RAM in app-mode");
anyfailed = 1;
}
puts("\r\nTesting timer... 3");
// Matching clock at 21 MHz, giving us timer in seconds
*timer_prescaler = 21 * 1000000;
// Test timer expiration after 1s
*timer = 1;
// Start the timer
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
while (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
}
// Now timer has expired and is ready to run again
puts(" 2");
// Test to interrupt a timer - and reads from timer register
// Starting 10s timer and interrupting it in 3s...
*timer = 10;
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
uint32_t last_timer = 10;
for (int i = 0; i < 3; i++) {
last_timer = wait_timer_tick(last_timer);
}
// Stop the timer
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
puts(" 1. done.\r\n");
if (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
failmsg("Timer didn't stop");
anyfailed = 1;
}
if (*timer != 10) {
failmsg("Timer didn't reset to 10");
anyfailed = 1;
}
// Check and display test results.
puts("\r\n--> ");
if (anyfailed) {
puts("Some test FAILED!\r\n");
} else {
puts("All tests passed.\r\n");
}
puts("\r\nHere are 256 bytes from the TRNG:\r\n");
for (int j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++) {
while ((*trng_status &
(1 << TK1_MMIO_TRNG_STATUS_READY_BIT)) == 0) {
}
uint32_t rnd = *trng_entropy;
puthexn((uint8_t *)&rnd, 4);
puts(" ");
}
puts("\r\n");
}
puts("\r\n");
puts("Now echoing what you type...Type + to reset device\r\n");
for (;;) {
in = readbyte(); // blocks
if (in == '+') {
syscall(TK1_SYSCALL_RESET, 0);
}
writebyte(in);
}
}

View File

@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
#include "../tk1/picorv32/custom_ops.S"
.section ".text"
.globl syscall_enable
.globl syscall
syscall_enable:
// Enable IRQs
li t0, 0x7fffffff // IRQ31 mask
picorv32_maskirq_insn(zero, t0) // Enable IRQs
ret
syscall:
// Save registers to stack
addi sp, sp, -32*4
sw x0, 0*4(sp)
sw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
sw x3, 3*4(sp)
sw x4, 4*4(sp)
sw x5, 5*4(sp)
sw x6, 6*4(sp)
sw x7, 7*4(sp)
sw x8, 8*4(sp)
sw x9, 9*4(sp)
// x10 (a0) will contain syscall return value. And should not be saved.
sw x11, 11*4(sp)
sw x12, 12*4(sp)
sw x13, 13*4(sp)
sw x14, 14*4(sp)
sw x15, 15*4(sp)
sw x16, 16*4(sp)
sw x17, 17*4(sp)
sw x18, 18*4(sp)
sw x19, 19*4(sp)
sw x20, 20*4(sp)
sw x21, 21*4(sp)
sw x22, 22*4(sp)
sw x23, 23*4(sp)
sw x24, 24*4(sp)
sw x25, 25*4(sp)
sw x26, 26*4(sp)
sw x27, 27*4(sp)
sw x28, 28*4(sp)
sw x29, 29*4(sp)
sw x30, 30*4(sp)
sw x31, 31*4(sp)
// Trigger syscall interrupt
li t0, (1 << 31)
and t0, a0, t0
li t1, 0xe1000000 // Syscall interrupt trigger address
sw zero, 0(t1) // Trigger interrupt
// Restore registers from stack
lw x0, 0*4(sp)
lw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
lw x3, 3*4(sp)
lw x4, 4*4(sp)
lw x5, 5*4(sp)
lw x6, 6*4(sp)
lw x7, 7*4(sp)
lw x8, 8*4(sp)
lw x9, 9*4(sp)
// x10 (a0) contains syscall return value. And should not be destroyed.
lw x11, 11*4(sp)
lw x12, 12*4(sp)
lw x13, 13*4(sp)
lw x14, 14*4(sp)
lw x15, 15*4(sp)
lw x16, 16*4(sp)
lw x17, 17*4(sp)
lw x18, 18*4(sp)
lw x19, 19*4(sp)
lw x20, 20*4(sp)
lw x21, 21*4(sp)
lw x22, 22*4(sp)
lw x23, 23*4(sp)
lw x24, 24*4(sp)
lw x25, 25*4(sp)
lw x26, 26*4(sp)
lw x27, 27*4(sp)
lw x28, 28*4(sp)
lw x29, 29*4(sp)
lw x30, 30*4(sp)
lw x31, 31*4(sp)
addi sp, sp, 32*4
ret

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
#include "../tk1/types.h"
#ifndef TKEY_APP_SYSCALL_H
#define TKEY_APP_SYSCALL_H
void syscall_enable(void);
int syscall(uint32_t number, uint32_t arg1);
#endif

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../tk1/blake2s/blake2s.h"
#include "../tk1/flash.h"
#include "../tk1/lib.h"
#include "../tk1/proto.h"
#include "../tk1/types.h"
@ -23,7 +23,6 @@ volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS
volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
volatile uint32_t *fw_blake2s_addr = (volatile uint32_t *)TK1_MMIO_TK1_BLAKE2S;
// clang-format on
#define UDS_WORDS 8
@ -151,11 +150,6 @@ void failmsg(char *s)
int main(void)
{
// Function pointer to blake2s()
volatile int (*fw_blake2s)(void *, unsigned long, const void *,
unsigned long, const void *, unsigned long,
blake2s_ctx *);
uint8_t in;
// Hard coded test UDS in ../../data/uds.hex
// clang-format off
@ -246,6 +240,16 @@ int main(void)
anyfailed = 1;
}
uint8_t jedec_id[3];
puts("\r\nReading SPI flash capacity...\r\n");
flash_release_powerdown();
flash_read_jedec_id(jedec_id);
if (jedec_id[2] != 0x14) {
failmsg("Expected SPI flash capacity: 0x14 (1 MByte)");
anyfailed = 1;
}
// Test FW_RAM.
puts("\r\nTesting FW_RAM (takes 15s on hw)...\r\n");
for (unsigned int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) {
@ -263,53 +267,6 @@ int main(void)
anyfailed = 1;
}
// Store function pointer to blake2s() so it's reachable from app
*fw_blake2s_addr = (uint32_t)blake2s;
// Turn on application mode.
// -------------------------
*system_mode_ctrl = 1;
sw = *system_mode_ctrl;
if (sw != 0xffffffff) {
failmsg("system_mode_ctrl is not 0xffffffff in app mode");
anyfailed = 1;
}
// Should NOT be able to read from UDS in app-mode.
wordcpy_s(uds_local, UDS_WORDS, (void *)uds, UDS_WORDS);
if (!memeq(uds_local, zeros, UDS_WORDS * 4)) {
failmsg("Read from UDS in app-mode");
anyfailed = 1;
}
// Should NOT be able to read from UDI in app-mode.
wordcpy_s(udi_local, UDI_WORDS, (void *)udi, UDI_WORDS);
if (!memeq(udi_local, zeros, UDI_WORDS * 4)) {
failmsg("Read from UDI in app-mode");
anyfailed = 1;
}
uint32_t cdi_local[CDI_WORDS];
uint32_t cdi_local2[CDI_WORDS];
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
// Write to CDI should NOT have any effect in app mode.
wordcpy_s((void *)cdi, CDI_WORDS, zeros, CDI_WORDS);
wordcpy_s(cdi_local2, CDI_WORDS, (void *)cdi, CDI_WORDS);
if (!memeq(cdi_local, cdi_local2, CDI_WORDS * 4)) {
failmsg("Write to CDI in app-mode");
anyfailed = 1;
}
// Test FW_RAM.
*fw_ram = 0x21;
if (*fw_ram == 0x21) {
failmsg("Write and read FW RAM in app-mode");
anyfailed = 1;
}
puts("\r\nTesting timer... 3");
// Matching clock at 18 MHz, giving us timer in seconds
*timer_prescaler = 18 * 1000000;
@ -346,32 +303,6 @@ int main(void)
anyfailed = 1;
}
// Testing the blake2s MMIO in app mode
fw_blake2s = (volatile int (*)(void *, unsigned long, const void *,
unsigned long, const void *,
unsigned long, blake2s_ctx *)) *
fw_blake2s_addr;
char msg[17] = "dldlkjsdkljdslsdj";
uint32_t digest0[8];
uint32_t digest1[8];
blake2s_ctx b2s_ctx;
blake2s(&digest0[0], 32, NULL, 0, &msg, 17, &b2s_ctx);
fw_blake2s(&digest1[0], 32, NULL, 0, &msg, 17, &b2s_ctx);
puts("\r\ndigest #0: \r\n");
hexdump((uint8_t *)digest0, 32);
puts("digest #1: \r\n");
hexdump((uint8_t *)digest1, 32);
if (!memeq(digest0, digest1, 32)) {
failmsg("Digests not the same");
anyfailed = 1;
}
// Check and display test results.
puts("\r\n--> ");
if (anyfailed) {

View File

@ -1,5 +1,6 @@
# Uses ../.clang-format
FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c led.h
FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c \
led.h syscall.c spi.c spi.h flash.c flash.h
.PHONY: fmt
fmt:
clang-format --dry-run --ferror-limit=0 $(FMTFILES)

View File

@ -0,0 +1,216 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include "flash.h"
#include "../tk1/types.h"
#include "../tk1_mem.h"
#include "spi.h"
#include <stdbool.h>
// clang-format off
static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
// clang-format on
// CPU clock frequency in Hz
#define CPUFREQ 21000000
#define PAGE_SIZE 256
static void delay(int timeout_ms)
{
// Tick once every centisecond
*timer_prescaler = CPUFREQ / 100;
*timer = timeout_ms / 10;
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
while (*timer_status != 0) {
}
// Stop timer
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
}
bool flash_is_busy(void)
{
uint8_t tx_buf = READ_STATUS_REG_1;
uint8_t rx_buf = {0x00};
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf, sizeof(rx_buf));
if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) {
return true;
}
return false;
}
// Blocking until !busy
void flash_wait_busy(void)
{
while (flash_is_busy()) {
delay(10);
}
}
void flash_write_enable(void)
{
uint8_t tx_buf = WRITE_ENABLE;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_write_disable(void)
{
uint8_t tx_buf = WRITE_DISABLE;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_sector_erase(uint32_t address)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = SECTOR_ERASE;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
/* tx_buf[3] is within a sector, and hence does not make a difference */
flash_write_enable();
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
flash_wait_busy();
}
void flash_block_32_erase(uint32_t address)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = BLOCK_ERASE_32K;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
flash_write_enable();
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
flash_wait_busy();
}
// 64 KiB block erase, only cares about address bits 16 and above.
void flash_block_64_erase(uint32_t address)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = BLOCK_ERASE_64K;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
/* tx_buf[2] and tx_buf[3] is within a block, and hence does not make a
* difference */
flash_write_enable();
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
flash_wait_busy();
}
void flash_release_powerdown(void)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = RELEASE_POWER_DOWN;
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_powerdown(void)
{
uint8_t tx_buf = POWER_DOWN;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_read_manufacturer_device_id(uint8_t *device_id)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = READ_MANUFACTURER_ID;
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2);
}
void flash_read_jedec_id(uint8_t *jedec_id)
{
uint8_t tx_buf = READ_JEDEC_ID;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3);
}
void flash_read_unique_id(uint8_t *unique_id)
{
uint8_t tx_buf[5] = {0x00};
tx_buf[0] = READ_UNIQUE_ID;
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8);
}
void flash_read_status(uint8_t *status_reg)
{
uint8_t tx_buf = READ_STATUS_REG_1;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1);
tx_buf = READ_STATUS_REG_2;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1, 1);
}
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = READ_DATA;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
return spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, dest_buf, size);
}
// Only handles writes where the least significant byte of the start address is
// zero.
int flash_write_data(uint32_t address, uint8_t *data, size_t size)
{
if (size <= 0 || size > 4096) {
return -1;
}
size_t left = size;
uint8_t *p_data = data;
size_t n_bytes = 0;
uint8_t tx_buf[4] = {
PAGE_PROGRAM, /* tx_buf[0] */
(address >> ADDR_BYTE_3_BIT) & 0xFF, /* tx_buf[1] */
(address >> ADDR_BYTE_2_BIT) & 0xFF, /* tx_buf[2] */
0x00, /* tx_buf[3] */
};
while (left > 0) {
if (left >= PAGE_SIZE) {
n_bytes = PAGE_SIZE;
} else {
n_bytes = left;
}
flash_write_enable();
if (spi_transfer(tx_buf, sizeof(tx_buf), p_data, n_bytes, NULL,
0) != 0) {
return -1;
}
left -= n_bytes;
p_data += n_bytes;
address += n_bytes;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
flash_wait_busy();
}
return 0;
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_FLASH_H
#define TKEY_FLASH_H
#include "types.h"
#include <stdbool.h>
#define WRITE_ENABLE 0x06
#define WRITE_DISABLE 0x04
#define READ_STATUS_REG_1 0x05
#define READ_STATUS_REG_2 0x35
#define WRITE_STATUS_REG 0x01
#define PAGE_PROGRAM 0x02
#define SECTOR_ERASE 0x20
#define BLOCK_ERASE_32K 0x52
#define BLOCK_ERASE_64K 0xD8
#define CHIP_ERASE 0xC7
#define POWER_DOWN 0xB9
#define READ_DATA 0x03
#define RELEASE_POWER_DOWN 0xAB
#define READ_MANUFACTURER_ID 0x90
#define READ_JEDEC_ID 0x9F
#define READ_UNIQUE_ID 0x4B
#define ENABLE_RESET 0x66
#define RESET 0x99
#define ADDR_BYTE_3_BIT 16
#define ADDR_BYTE_2_BIT 8
#define ADDR_BYTE_1_BIT 0
#define STATUS_REG_BUSY_BIT 0
#define STATUS_REG_WEL_BIT 1
bool flash_is_busy(void);
void flash_wait_busy(void);
void flash_write_enable(void);
void flash_write_disable(void);
void flash_sector_erase(uint32_t address);
void flash_block_32_erase(uint32_t address);
void flash_block_64_erase(uint32_t address);
void flash_release_powerdown(void);
void flash_powerdown(void);
void flash_read_manufacturer_device_id(uint8_t *device_id);
void flash_read_jedec_id(uint8_t *jedec_id);
void flash_read_unique_id(uint8_t *unique_id);
void flash_read_status(uint8_t *status_reg);
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size);
int flash_write_data(uint32_t address, uint8_t *data, size_t size);
#endif

View File

@ -0,0 +1,7 @@
# PicoRV32
## custom_ops.S
Custom PicoRV32 instructions are located in `custom_ops.S`.
`custom_ops.S` is imported from upstream PicoRV32 commit:
YosysHQ/picorv32@70f3c33

View File

@ -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)

View File

@ -0,0 +1,88 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include "spi.h"
#include "../tk1/types.h"
#include "../tk1_mem.h"
// clang-format off
static volatile uint32_t *spi_en = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x200);
static volatile uint32_t *spi_xfer = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x204);
static volatile uint32_t *spi_data = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x208);
// clang-format on
// returns non-zero when the SPI-master is ready, and zero if not
// ready. This can be used to check if the SPI-master is available
// in the hardware.
int spi_ready(void)
{
return *spi_xfer;
}
static void spi_enable(void)
{
*spi_en = 1;
}
static void spi_disable(void)
{
*spi_en = 0;
}
static void _spi_write(uint8_t *cmd, size_t size)
{
for (size_t i = 0; i < size; i++) {
while (!spi_ready()) {
}
*spi_data = cmd[i];
*spi_xfer = 1;
}
while (!spi_ready()) {
}
}
static void _spi_read(uint8_t *buf, size_t size)
{
while (!spi_ready()) {
}
for (size_t i = 0; i < size; i++) {
*spi_data = 0x00;
*spi_xfer = 1;
// wait until spi master is done
while (!spi_ready()) {
}
buf[i] = (*spi_data & 0xff);
}
}
// Function to both read and write data to the connected SPI flash.
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
uint8_t *rx_buf, size_t rx_size)
{
if (cmd == NULL || cmd_size == 0) {
return -1;
}
spi_enable();
_spi_write(cmd, cmd_size);
if (tx_buf != NULL || tx_size != 0) {
_spi_write(tx_buf, tx_size);
}
if (rx_buf != NULL && rx_size != 0) {
_spi_read(rx_buf, rx_size);
}
spi_disable();
return 0;
}

View File

@ -0,0 +1,13 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_SPI_H
#define TKEY_SPI_H
#include "../tk1/types.h"
int spi_ready(void);
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
uint8_t *rx_buf, size_t rx_size);
#endif

View File

@ -1,11 +1,102 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* Copyright (C) 2022, 2023, 2024, 2025 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../tk1_mem.h"
#include "picorv32/custom_ops.S" // PicoRV32 custom instructions
#define illegal_insn() .word 0
#define FW_SP_STORAGE (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 4)
#define FW_STACK_TOP (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 2*4)
.section ".text.init"
.globl _start
_start:
j init
/*
* IRQ handler
*/
.=0x10
irq_handler:
// PicoRV32 stores the IRQ bitmask in x4.
// If bit 31 is 1: IRQ31 was triggered.
li t4, (1 << 31)
beq x4, t4, irq_source_ok
unexpected_irq_source:
illegal_insn()
j unexpected_irq_source
irq_source_ok:
// Save app stack pointer. App is responsible for saving the rest of
// the registers.
li t0, FW_SP_STORAGE
sw sp, 0(t0)
// Setup firmware stack pointer
li sp, FW_STACK_TOP
// Run syscall handler
call syscall_handler
// Restore app stack pointer
li t0, FW_SP_STORAGE
lw sp, 0(t0)
// Verify that interrupt return address (x3) is in app RAM
li t0, TK1_RAM_BASE // 0x40000000
blt x3, t0, x3_invalid
li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000
bge x3, t0, x3_invalid
j x3_valid
x3_invalid:
illegal_insn()
j x3_invalid
x3_valid:
// Remove data left over from the syscall handling
mv x0, zero
mv x1, zero
// x2 (sp) is assumed to be preserved by the interrupt handler
// x3 (interrupt return address) assumed preserved
mv x4, zero
mv x5, zero
mv x6, zero
mv x7, zero
mv x8, zero
mv x9, zero
// x10 (a0) contains syscall return value. And should not be destroyed.
mv x11, zero
mv x12, zero
mv x13, zero
mv x14, zero
mv x15, zero
mv x16, zero
mv x17, zero
mv x18, zero
mv x19, zero
mv x20, zero
mv x21, zero
mv x22, zero
mv x23, zero
mv x24, zero
mv x25, zero
mv x26, zero
mv x27, zero
mv x28, zero
mv x29, zero
mv x30, zero
mv x31, zero
picorv32_retirq_insn() // Return from interrupt
/*
* Init
*/
.=0x100
init:
li x1, 0
li x2, 0
li x3, 0

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2025 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../tk1/assert.h"
#include "../tk1/flash.h"
#include "../tk1/led.h"
#include "../tk1/syscall_nrs.h"
#include "../tk1/types.h"
// clang-format off
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
// clang-format on
// Proof-of-concept firmware for handling syscalls.
// This is NOT a best-practice example of secure syscall implementation.
int32_t syscall_handler(uint32_t syscall_nr, uint32_t arg1)
{
switch (syscall_nr) {
case TK1_SYSCALL_RESET:
*system_reset = 1;
return 0;
case TK1_SYSCALL_SET_LED:
set_led(arg1);
return 0;
case TK1_SYSCALL_GET_FLASH_CAPACITY: {
uint8_t jedec_id[3];
flash_release_powerdown();
flash_read_jedec_id(jedec_id);
flash_powerdown();
return jedec_id[2];
}
default:
assert(1 == 2);
}
assert(1 == 2);
return -1; // This should never run
}

View File

@ -0,0 +1,13 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_SYSCALL_H
#define TKEY_SYSCALL_H
enum {
TK1_SYSCALL_RESET = 1,
TK1_SYSCALL_SET_LED = 10,
TK1_SYSCALL_GET_FLASH_CAPACITY = 11,
};
#endif

View File

@ -14,7 +14,7 @@ typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned long size_t;
#define NULL ((char *)0)
#define NULL ((void *)0)
#define FALSE 0
#define TRUE !FALSE

View File

@ -54,11 +54,13 @@ module application_fpga (
localparam UART_PREFIX = 6'h03;
localparam TOUCH_SENSE_PREFIX = 6'h04;
localparam FW_RAM_PREFIX = 6'h10;
localparam IRQ31_PREFIX = 6'h21;
localparam TK1_PREFIX = 6'h3f;
// Instruction used to cause a trap.
localparam ILLEGAL_INSTRUCTION = 32'h0;
localparam IRQ31_IRQ_MASK = 2 ** 31;
//----------------------------------------------------------------
// Registers, memories with associated wires.
@ -77,11 +79,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;
@ -129,6 +133,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;
@ -136,13 +141,17 @@ module application_fpga (
wire [31 : 0] touch_sense_read_data;
wire touch_sense_ready;
reg irq31_cs;
reg irq31_we;
reg irq31_eoi;
reg tk1_cs;
reg tk1_we;
reg [ 7 : 0] tk1_address;
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;
@ -163,12 +172,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),
.LATCHED_IRQ (IRQ31_IRQ_MASK)
) cpu (
.clk(clk),
.resetn(reset_n),
@ -182,11 +196,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(),
@ -237,8 +252,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),
.address(fw_ram_address),
@ -277,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),
@ -320,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),
@ -347,6 +361,10 @@ module application_fpga (
.gpio3(app_gpio3),
.gpio4(app_gpio4),
.access_level_hi(irq31_eoi),
.fw_ram_en(fw_ram_en),
.cs(tk1_cs),
.we(tk1_we),
.address(tk1_address),
@ -373,6 +391,20 @@ module application_fpga (
end
//----------------------------------------------------------------
// irq_ctrl
// Interrupt logic
//----------------------------------------------------------------
always @* begin : irq_ctrl
reg irq31_set;
irq31_set = irq31_cs & irq31_we;
cpu_irq = {irq31_set, 31'h0};
irq31_eoi = cpu_eoi[31];
end
//----------------------------------------------------------------
// cpu_mem_ctrl
// CPU memory decode and control logic.
@ -422,6 +454,9 @@ module application_fpga (
touch_sense_we = |cpu_wstrb;
touch_sense_address = cpu_addr[9 : 2];
irq31_cs = 1'h0;
irq31_we = |cpu_wstrb;
tk1_cs = 1'h0;
tk1_we = |cpu_wstrb;
tk1_address = cpu_addr[9 : 2];
@ -494,6 +529,11 @@ module application_fpga (
muxed_ready_new = fw_ram_ready;
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;

View File

@ -67,11 +67,13 @@ module application_fpga_sim (
localparam UART_PREFIX = 6'h03;
localparam TOUCH_SENSE_PREFIX = 6'h04;
localparam FW_RAM_PREFIX = 6'h10;
localparam IRQ31_PREFIX = 6'h21;
localparam TK1_PREFIX = 6'h3f;
// Instruction used to cause a trap.
localparam ILLEGAL_INSTRUCTION = 32'h0;
localparam IRQ31_IRQ_MASK = 2 ** 31;
//----------------------------------------------------------------
// Registers, memories with associated wires.
@ -89,11 +91,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;
@ -141,6 +145,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;
@ -148,13 +153,17 @@ module application_fpga_sim (
wire [31 : 0] touch_sense_read_data;
wire touch_sense_ready;
reg irq31_cs;
reg irq31_we;
reg irq31_eoi;
reg tk1_cs;
reg tk1_we;
reg [ 7 : 0] tk1_address;
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;
@ -174,12 +183,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),
.LATCHED_IRQ (IRQ31_IRQ_MASK)
) cpu (
.clk(clk),
.resetn(reset_n),
@ -193,11 +207,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(),
@ -248,8 +263,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),
@ -288,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),
@ -333,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),
@ -360,6 +374,10 @@ module application_fpga_sim (
.gpio3(app_gpio3),
.gpio4(app_gpio4),
.access_level_hi(irq31_eoi),
.fw_ram_en(fw_ram_en),
.cs(tk1_cs),
.we(tk1_we),
.address(tk1_address),
@ -385,6 +403,20 @@ module application_fpga_sim (
end
//----------------------------------------------------------------
// irq_ctrl
// Interrupt logic
//----------------------------------------------------------------
always @* begin : irq_ctrl
reg irq31_set;
irq31_set = irq31_cs & irq31_we;
cpu_irq = {irq31_set, 31'h0};
irq31_eoi = cpu_eoi[31];
end
//----------------------------------------------------------------
// cpu_mem_ctrl
// CPU memory decode and control logic.
@ -436,6 +468,9 @@ module application_fpga_sim (
touch_sense_we = |cpu_wstrb;
touch_sense_address = cpu_addr[9 : 2];
irq31_cs = 1'h0;
irq31_we = |cpu_wstrb;
tk1_cs = 1'h0;
tk1_we = |cpu_wstrb;
tk1_address = cpu_addr[9 : 2];
@ -528,6 +563,13 @@ module application_fpga_sim (
muxed_ready_new = fw_ram_ready;
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";

View File

@ -80,7 +80,7 @@ module tb_application_fpga_sim ();
//----------------------------------------------------------------
initial begin
// End simulation after XXX time units (set by timescale)
#20000000;
#14000;
$display("TIMEOUT");
$finish;
end