mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-03-12 01:56:43 -04:00
Merge b02c2d67d6450cd1bea6942d293a6f6ea8a607f1 into d2c7fb0ba9b0cb11791c94b38d17372899479d6a
This commit is contained in:
commit
3cd2f6c2b5
@ -28,7 +28,7 @@ ICESTORM_PATH ?=
|
||||
|
||||
# FPGA target frequency. Should be in sync with the clock frequency
|
||||
# given by the parameters to the PLL in rtl/clk_reset_gen.v
|
||||
TARGET_FREQ ?= 24
|
||||
TARGET_FREQ ?= 21
|
||||
|
||||
# Size in 32-bit words, must be divisible by 256 (pairs of EBRs, because 16
|
||||
# bits wide; an EBR is 128 32-bits words)
|
||||
@ -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,15 +138,37 @@ 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/assert.o \
|
||||
$(P)/fw/tk1/blake2s/blake2s.o
|
||||
$(P)/fw/tk1/lib.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.
|
||||
@ -181,6 +206,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 $@
|
||||
@ -223,6 +252,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) > $@
|
||||
@ -233,6 +274,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:
|
||||
@ -371,7 +419,7 @@ synth.json: $(FPGA_VERILOG_SRCS) $(VERILOG_SRCS) $(PICORV32_SRCS) bram_fw.hex
|
||||
application_fpga_par.json: synth.json $(P)/data/$(PIN_FILE)
|
||||
$(NEXTPNR_PATH)nextpnr-ice40 \
|
||||
-l application_fpga_par.txt \
|
||||
--seed 9106179903728618585 \
|
||||
--seed 4127945014473118301 \
|
||||
--freq $(TARGET_FREQ) \
|
||||
--ignore-loops \
|
||||
--up5k \
|
||||
@ -406,11 +454,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 \
|
||||
@ -434,7 +484,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, $^)
|
||||
@ -442,6 +492,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.
|
||||
#-------------------------------------------------------------------
|
||||
@ -488,6 +555,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
|
||||
|
||||
|
@ -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.
|
||||
@ -25,7 +26,7 @@ and bitmasks, see the file `fw/tk1_mem.h`.
|
||||
Rough memory map:
|
||||
|
||||
| *name* | *prefix* |
|
||||
|---------|----------|
|
||||
|------------|----------|
|
||||
| ROM | 0x00 |
|
||||
| RAM | 0x40 |
|
||||
| TRNG | 0xc0 |
|
||||
@ -34,6 +35,7 @@ Rough memory map:
|
||||
| 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.
|
||||
|
@ -76,13 +76,13 @@ module clk_reset_gen #(
|
||||
//
|
||||
// F_pllout == (F_referenceclk * (DIVF + 1)) / (2^DIVQ * (DIVR + 1))
|
||||
//
|
||||
// Given the 12 MHz HFOSC clock set above, we get a final 24 MHz:
|
||||
// Given the 12 MHz HFOSC clock set above, we get a final 21 MHz:
|
||||
//
|
||||
// (12000000 * (63 + 1)) / (2^5 * (0 + 1)) = 24000000
|
||||
// (12000000 * (55 + 1)) / (2^5 * (0 + 1)) = 21000000
|
||||
SB_PLL40_CORE #(
|
||||
.FEEDBACK_PATH("SIMPLE"),
|
||||
.DIVR(4'd0), // DIVR = 0
|
||||
.DIVF(7'd63), // DIVF = 63
|
||||
.DIVF(7'd55), // DIVF = 55
|
||||
.DIVQ(3'd5), // DIVQ = 5
|
||||
.FILTER_RANGE(3'b001) // FILTER_RANGE = 1
|
||||
) pll_inst (
|
||||
|
@ -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,
|
||||
@ -35,7 +34,6 @@ module fw_ram (
|
||||
reg [31 : 0] mem_read_data0;
|
||||
reg [31 : 0] mem_read_data1;
|
||||
reg ready_reg;
|
||||
wire system_mode_cs;
|
||||
reg bank0;
|
||||
reg bank1;
|
||||
|
||||
@ -45,7 +43,6 @@ module fw_ram (
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = ready_reg;
|
||||
assign system_mode_cs = cs && ~system_mode;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
@ -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;
|
||||
|
@ -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
|
||||
|
||||
```
|
||||
|
@ -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
|
||||
@ -386,6 +391,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.
|
||||
@ -448,7 +456,14 @@ module tk1 #(
|
||||
end
|
||||
|
||||
// In unused space
|
||||
if ((cpu_addr[29 : 24] > 6'h10) && (cpu_addr[29 : 24] < 6'h3f)) begin
|
||||
if ((cpu_addr[29 : 24] > 6'h10) && (cpu_addr[29 : 24] < 6'h21)) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
// Entire IRQ31 trigger area is accessible
|
||||
|
||||
// In unused space
|
||||
if ((cpu_addr[29 : 24] > 6'h21) && (cpu_addr[29 : 24] < 6'h3f)) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
@ -463,6 +478,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;
|
||||
@ -472,18 +493,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;
|
||||
@ -498,16 +533,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
|
||||
@ -518,13 +549,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
|
||||
@ -533,26 +564,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
|
||||
@ -574,16 +599,22 @@ module tk1 #(
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_EN) begin
|
||||
if (spi_access_en) begin
|
||||
spi_enable_vld = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_XFER) begin
|
||||
if (spi_access_en) begin
|
||||
spi_start = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_DATA) begin
|
||||
if (spi_access_en) begin
|
||||
spi_tx_data_vld = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
else begin
|
||||
@ -619,27 +650,27 @@ 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
|
||||
if (spi_access_en) begin
|
||||
tmp_read_data[0] = spi_ready;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_DATA) begin
|
||||
if (spi_access_en) begin
|
||||
tmp_read_data[7 : 0] = spi_rx_data;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -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,
|
||||
@ -227,7 +236,6 @@ module tb_tk1 ();
|
||||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("--- Toggle reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
@ -277,6 +285,8 @@ module tb_tk1 ();
|
||||
tb_gpio1 = 1'h0;
|
||||
tb_gpio2 = 1'h0;
|
||||
|
||||
tb_access_level_hi = 1'h0;
|
||||
|
||||
tb_cs = 1'h0;
|
||||
tb_we = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
@ -285,6 +295,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 +330,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
|
||||
@ -320,12 +349,16 @@ module tb_tk1 ();
|
||||
reg [31 : 0] read_data;
|
||||
|
||||
tb_address = address;
|
||||
tb_cpu_instr = 1'h0;
|
||||
tb_cpu_valid = 1'h1;
|
||||
tb_we = 1'h0;
|
||||
tb_cs = 1'h1;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
tb_cpu_valid = 1'h0;
|
||||
tb_cs = 1'h0;
|
||||
end
|
||||
endtask // read_word
|
||||
@ -354,21 +387,138 @@ module tb_tk1 ();
|
||||
#(CLK_PERIOD);
|
||||
tb_cs = 1'h0;
|
||||
|
||||
if (DEBUG) begin
|
||||
if (read_data == expected) begin
|
||||
if (DEBUG) begin
|
||||
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
|
||||
end
|
||||
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
|
||||
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", value, expected);
|
||||
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
|
||||
|
||||
// cpu_read_word()
|
||||
//
|
||||
// Read a data word from the given CPU address in the DUT.
|
||||
// the word read will be available in the global variable
|
||||
// tb_read_data.
|
||||
//----------------------------------------------------------------
|
||||
task cpu_read_word(input [31 : 0] address);
|
||||
begin : cpu_read_word
|
||||
reg [31 : 0] read_data;
|
||||
|
||||
tb_cpu_addr = address;
|
||||
tb_address = tb_cpu_addr[13:2];
|
||||
tb_cpu_instr = 1'h0;
|
||||
tb_cpu_valid = 1'h1;
|
||||
tb_we = 1'h0;
|
||||
tb_cs = 1'h1;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
tb_cpu_addr = 32'h0;
|
||||
tb_cpu_valid = 1'h0;
|
||||
tb_address = 12'h0;
|
||||
tb_cs = 1'h0;
|
||||
end
|
||||
endtask // read_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_read_check_range_should_trap()
|
||||
//
|
||||
// Read data in a range of CPU addresses (32-bit addresses). Fail
|
||||
// if trap signal is not asserted.
|
||||
// Range is inclusive.
|
||||
//----------------------------------------------------------------
|
||||
task cpu_read_check_range_should_trap(input [31 : 0] start_address, input [31 : 0] end_address);
|
||||
begin : read_check_range_should_not_trap
|
||||
reg [32 : 0] address;
|
||||
reg error_detected;
|
||||
|
||||
address = start_address;
|
||||
error_detected = 0;
|
||||
|
||||
while (!error_detected && (address <= end_address)) begin
|
||||
reset_dut();
|
||||
cpu_read_word(address);
|
||||
if (tb_force_trap == 0) begin
|
||||
$display("--- Error: Expected trap when reading from address 0x%08x", address);
|
||||
error_ctr += 1;
|
||||
error_detected = 1;
|
||||
end
|
||||
address += 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_read_check_range_should_not_trap()
|
||||
//
|
||||
// Read data in a range of CPU addresses (32-bit addresses). Fail
|
||||
// if trap signal is asserted.
|
||||
// Range is inclusive.
|
||||
//----------------------------------------------------------------
|
||||
task cpu_read_check_range_should_not_trap(input [31 : 0] start_address, input [31 : 0] end_address);
|
||||
begin : read_check_should_not_trap
|
||||
reg [31 : 0] address;
|
||||
reg error_detected;
|
||||
|
||||
address = start_address;
|
||||
error_detected = 0;
|
||||
|
||||
while (!error_detected && (address <= end_address)) begin
|
||||
reset_dut();
|
||||
cpu_read_word(address);
|
||||
if (tb_force_trap == 1) begin
|
||||
$display("--- Error: Did not expected trap when reading from address 0x%08x", address);
|
||||
error_ctr += 1;
|
||||
error_detected = 1;
|
||||
end
|
||||
address += 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test1()
|
||||
// Read out name and version.
|
||||
@ -400,10 +550,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 +585,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 +612,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 +624,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 +634,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 +688,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 +706,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 +715,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 +726,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 +743,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 +848,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 +869,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 +939,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;
|
||||
@ -714,6 +970,106 @@ module tb_tk1 ();
|
||||
end
|
||||
endtask // test10
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test11()
|
||||
// Test security monitor trap ranges.
|
||||
// - Check that reading accessible areas does not trap
|
||||
// - Check that reading the start and end of inaccessible areas
|
||||
// trap
|
||||
//----------------------------------------------------------------
|
||||
task test11;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("");
|
||||
$display("--- test11: Test trap ranges.");
|
||||
|
||||
// ROM trap range: 0x00004000-0x3fffffff
|
||||
$display("--- test11: ROM");
|
||||
cpu_read_check_range_should_not_trap(32'h0, 32'h3fff);
|
||||
cpu_read_check_range_should_trap(32'h4000, 32'h400f);
|
||||
cpu_read_check_range_should_trap(32'h3ffffff0, 32'h3fffffff);
|
||||
|
||||
// RAM trap range: 0x40020000-0x7fffffff
|
||||
$display("--- test11: RAM");
|
||||
cpu_read_check_range_should_not_trap(32'h40000000, 32'h4000000f);
|
||||
cpu_read_check_range_should_trap(32'h40020000, 32'h4002000f);
|
||||
cpu_read_check_range_should_trap(32'h7ffffff0, 32'h7fffffff);
|
||||
|
||||
// Reserved trap range: 0x80000000-0xbfffffff
|
||||
$display("--- test11: Reserved");
|
||||
cpu_read_check_range_should_trap(32'h80000000, 32'h8000000f);
|
||||
cpu_read_check_range_should_trap(32'hbffffff0, 32'hbfffffff);
|
||||
|
||||
// TRNG trap range: 0xc0000400-0xc0ffffff
|
||||
$display("--- test11: TRNG");
|
||||
cpu_read_check_range_should_not_trap(32'hc0000000, 32'hc00003ff);
|
||||
cpu_read_check_range_should_trap(32'hc0000400, 32'hc000040f);
|
||||
cpu_read_check_range_should_trap(32'hc0fffff0, 32'hc0ffffff);
|
||||
|
||||
// TIMER trap range: 0xc1000400-0xc1ffffff
|
||||
$display("--- test11: TIMER");
|
||||
cpu_read_check_range_should_not_trap(32'hc1000000, 32'hc10003ff);
|
||||
cpu_read_check_range_should_trap(32'hc1000400, 32'hc100040f);
|
||||
cpu_read_check_range_should_trap(32'hc1fffff0, 32'hc1ffffff);
|
||||
|
||||
// UDS trap range: 0xc2000020-0xc2ffffff
|
||||
$display("--- test11: UDS");
|
||||
cpu_read_check_range_should_not_trap(32'hc2000000, 32'hc200001f);
|
||||
cpu_read_check_range_should_trap(32'hc2000020, 32'hc200002f);
|
||||
cpu_read_check_range_should_trap(32'hc2fffff0, 32'hc2ffffff);
|
||||
|
||||
// UART trap range: 0xc3000400-0xc3ffffff
|
||||
$display("--- test11: UART");
|
||||
cpu_read_check_range_should_not_trap(32'hc3000000, 32'hc30003ff);
|
||||
cpu_read_check_range_should_trap(32'hc3000400, 32'hc300040f);
|
||||
cpu_read_check_range_should_trap(32'hc3fffff0, 32'hc3ffffff);
|
||||
|
||||
// TOUCH_SENSE trap range: 0xc4000400-0xc4ffffff
|
||||
$display("--- test11: TOUCH_SENSE");
|
||||
cpu_read_check_range_should_not_trap(32'hc4000000, 32'hc40003ff);
|
||||
cpu_read_check_range_should_trap(32'hc4000400, 32'hc400040f);
|
||||
cpu_read_check_range_should_trap(32'hc4fffff0, 32'hc4ffffff);
|
||||
|
||||
// Unused trap range: 0xc5000000-0xcfffffff
|
||||
$display("--- test11: Unused");
|
||||
cpu_read_check_range_should_trap(32'hc5000000, 32'hc500000f);
|
||||
cpu_read_check_range_should_trap(32'hc5fffff0, 32'hc5ffffff);
|
||||
|
||||
// FW_RAM trap range: 0xd0000800-0xd0ffffff
|
||||
$display("--- test11: FW_RAM");
|
||||
cpu_read_check_range_should_not_trap(32'hd0000000, 32'hd00007ff);
|
||||
cpu_read_check_range_should_trap(32'hd0000800, 32'hd000080f);
|
||||
cpu_read_check_range_should_trap(32'hd0fffff0, 32'hd0ffffff);
|
||||
|
||||
// Unused trap range: 0xd1000000-0xfeffffff
|
||||
$display("--- test11: Unused");
|
||||
cpu_read_check_range_should_trap(32'hd1000000, 32'hd100000f);
|
||||
cpu_read_check_range_should_trap(32'he0fffff0, 32'he0ffffff);
|
||||
|
||||
// IRQ31 No trap range. Entire 0xe1 range is accessible.
|
||||
cpu_read_check_range_should_not_trap(32'he1000000, 32'he100000f);
|
||||
cpu_read_check_range_should_not_trap(32'he1fffff0, 32'he1ffffff);
|
||||
|
||||
// Unused trap range: 0xe2000000-0xfeffffff
|
||||
//
|
||||
$display("--- test11: Unused");
|
||||
cpu_read_check_range_should_trap(32'he2000000, 32'he200000f);
|
||||
cpu_read_check_range_should_trap(32'hfefffff0, 32'hfeffffff);
|
||||
|
||||
// TK1 trap range: 0xff000400-0xffffffff
|
||||
$display("--- test11: TK1");
|
||||
cpu_read_check_range_should_not_trap(32'hff000000, 32'hff0003ff);
|
||||
cpu_read_check_range_should_trap(32'hff000400, 32'hff00040f);
|
||||
cpu_read_check_range_should_trap(32'hfffffff0, 32'hffffffff);
|
||||
|
||||
$display("--- test11: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test11
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// exit_with_error_code()
|
||||
//
|
||||
@ -746,7 +1102,6 @@ module tb_tk1 ();
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
test7();
|
||||
@ -754,6 +1109,7 @@ module tb_tk1 ();
|
||||
test9();
|
||||
test9();
|
||||
test10();
|
||||
test11();
|
||||
|
||||
display_test_result();
|
||||
$display("");
|
||||
|
@ -82,10 +82,10 @@ module uart (
|
||||
// The default bit rate is based on target clock frequency
|
||||
// divided by the bit rate times in order to hit the
|
||||
// center of the bits. I.e.
|
||||
// Clock: 24 MHz, 500 kbps
|
||||
// Divisor = 24E6 / 500E3 = 48
|
||||
// Clock: 21 MHz, 500 kbps
|
||||
// Divisor = 21E6 / 500E3 = 42
|
||||
// This also satisfies 1E6 % bps == 0 for the CH552 MCU used for USB-serial
|
||||
localparam DEFAULT_BIT_RATE = 16'd48;
|
||||
localparam DEFAULT_BIT_RATE = 16'd42;
|
||||
localparam DEFAULT_DATA_BITS = 4'h8;
|
||||
localparam DEFAULT_STOP_BITS = 2'h1;
|
||||
|
||||
|
@ -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
|
||||
|
9
hw/application_fpga/fw/irqpoc/Makefile
Normal file
9
hw/application_fpga/fw/irqpoc/Makefile
Normal 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)
|
102
hw/application_fpga/fw/irqpoc/custom_ops.S
Normal file
102
hw/application_fpga/fw/irqpoc/custom_ops.S
Normal 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)
|
||||
|
10
hw/application_fpga/fw/irqpoc/main.c
Normal file
10
hw/application_fpga/fw/irqpoc/main.c
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
while (1) {
|
||||
}
|
||||
}
|
40
hw/application_fpga/fw/irqpoc/start.S
Normal file
40
hw/application_fpga/fw/irqpoc/start.S
Normal 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.
|
||||
|
9
hw/application_fpga/fw/irqpoc_c_example/Makefile
Normal file
9
hw/application_fpga/fw/irqpoc_c_example/Makefile
Normal 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)
|
102
hw/application_fpga/fw/irqpoc_c_example/custom_ops.S
Normal file
102
hw/application_fpga/fw/irqpoc_c_example/custom_ops.S
Normal 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)
|
||||
|
32
hw/application_fpga/fw/irqpoc_c_example/main.c
Normal file
32
hw/application_fpga/fw/irqpoc_c_example/main.c
Normal 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) {
|
||||
}
|
||||
}
|
241
hw/application_fpga/fw/irqpoc_c_example/start.S
Normal file
241
hw/application_fpga/fw/irqpoc_c_example/start.S
Normal 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:
|
||||
|
9
hw/application_fpga/fw/irqpoc_led_toggle/Makefile
Normal file
9
hw/application_fpga/fw/irqpoc_led_toggle/Makefile
Normal 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)
|
102
hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S
Normal file
102
hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S
Normal 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)
|
||||
|
10
hw/application_fpga/fw/irqpoc_led_toggle/main.c
Normal file
10
hw/application_fpga/fw/irqpoc_led_toggle/main.c
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
while (1) {
|
||||
}
|
||||
}
|
82
hw/application_fpga/fw/irqpoc_led_toggle/start.S
Normal file
82
hw/application_fpga/fw/irqpoc_led_toggle/start.S
Normal 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.
|
||||
|
9
hw/application_fpga/fw/irqpoc_with_app/Makefile
Normal file
9
hw/application_fpga/fw/irqpoc_with_app/Makefile
Normal 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)
|
102
hw/application_fpga/fw/irqpoc_with_app/custom_ops.S
Normal file
102
hw/application_fpga/fw/irqpoc_with_app/custom_ops.S
Normal 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)
|
||||
|
10
hw/application_fpga/fw/irqpoc_with_app/main.c
Normal file
10
hw/application_fpga/fw/irqpoc_with_app/main.c
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
while (1) {
|
||||
}
|
||||
}
|
110
hw/application_fpga/fw/irqpoc_with_app/start.S
Normal file
110
hw/application_fpga/fw/irqpoc_with_app/start.S
Normal 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:
|
||||
|
71
hw/application_fpga/fw/testapp/Makefile
Normal file
71
hw/application_fpga/fw/testapp/Makefile
Normal file
@ -0,0 +1,71 @@
|
||||
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/assert.o \
|
||||
$(P)/../tk1/led.o \
|
||||
$(P)/../tk1/lib.o \
|
||||
$(P)/../tk1/proto.o
|
||||
|
||||
testapp.elf: $(TESTAPP_OBJS)
|
||||
$(CC) $(CFLAGS) $(TESTAPP_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 $(TESTAPP_FMTFILES)
|
||||
clang-format --verbose -i $(TESTAPP_FMTFILES)
|
||||
|
||||
.PHONY: checkfmt
|
||||
checkfmt:
|
||||
clang-format --dry-run --ferror-limit=0 --Werror $(TESTAPP_FMTFILES)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f testapp.bin testapp.elf $(TESTAPP_OBJS)
|
64
hw/application_fpga/fw/testapp/app.lds
Normal file
64
hw/application_fpga/fw/testapp/app.lds
Normal 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 */
|
||||
}
|
53
hw/application_fpga/fw/testapp/crt0.S
Normal file
53
hw/application_fpga/fw/testapp/crt0.S
Normal 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
|
280
hw/application_fpga/fw/testapp/main.c
Normal file
280
hw/application_fpga/fw/testapp/main.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define USBMODE_PACKET_SIZE 64
|
||||
|
||||
// clang-format off
|
||||
volatile uint32_t *tk1name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0;
|
||||
volatile uint32_t *tk1name1 = (volatile uint32_t *)TK1_MMIO_TK1_NAME1;
|
||||
volatile uint32_t *uds = (volatile uint32_t *)TK1_MMIO_UDS_FIRST;
|
||||
volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
|
||||
volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
|
||||
volatile 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
|
||||
|
||||
static void write_with_header(const uint8_t *buf, size_t nbytes, enum mode mode)
|
||||
{
|
||||
// Append USB Mode Protocol header:
|
||||
// 1 byte mode
|
||||
// 1 byte length
|
||||
writebyte(mode);
|
||||
writebyte(nbytes);
|
||||
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
writebyte(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void write(const uint8_t *buf, size_t nbytes)
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
while (nbytes > 0) {
|
||||
// We split the data into chunks that will fit in the
|
||||
// USB Mode Protocol with some spare change.
|
||||
len =
|
||||
nbytes < USBMODE_PACKET_SIZE ? nbytes : USBMODE_PACKET_SIZE;
|
||||
|
||||
write_with_header((const uint8_t *)buf, len, MODE_CDC);
|
||||
|
||||
buf += len;
|
||||
nbytes -= len;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned strlen(const char *str)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
for (s = str; *s; ++s)
|
||||
;
|
||||
|
||||
return (s - str);
|
||||
}
|
||||
|
||||
void puts(char *buf)
|
||||
{
|
||||
size_t nbytes = strlen(buf);
|
||||
|
||||
write((const uint8_t *)buf, nbytes);
|
||||
}
|
||||
|
||||
void hex(uint8_t buf[2], const uint8_t c)
|
||||
{
|
||||
unsigned int upper = (c >> 4) & 0xf;
|
||||
unsigned int lower = c & 0xf;
|
||||
|
||||
buf[0] = upper < 10 ? '0' + upper : 'a' - 10 + upper;
|
||||
buf[1] = lower < 10 ? '0' + lower : 'a' - 10 + lower;
|
||||
}
|
||||
|
||||
void puthex(uint8_t c)
|
||||
{
|
||||
uint8_t buf[2];
|
||||
|
||||
hex(buf, c);
|
||||
write(buf, 2);
|
||||
}
|
||||
|
||||
void puthexn(uint8_t *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
puthex(p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void reverseword(uint32_t *wordp)
|
||||
{
|
||||
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
|
||||
((*wordp & 0x0000ff00) << 8) | ((*wordp & 0x000000ff) << 24);
|
||||
}
|
||||
|
||||
uint32_t wait_timer_tick(uint32_t last_timer)
|
||||
{
|
||||
uint32_t newtimer;
|
||||
for (;;) {
|
||||
newtimer = *timer;
|
||||
if (newtimer != last_timer) {
|
||||
return newtimer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void failmsg(char *s)
|
||||
{
|
||||
puts("FAIL: ");
|
||||
puts(s);
|
||||
puts("\r\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t in = 0;
|
||||
uint8_t mode = 0;
|
||||
uint8_t mode_bytes_left = 0;
|
||||
|
||||
set_led(LED_BLUE);
|
||||
|
||||
// Wait for terminal program and a character to be typed
|
||||
in = readbyte(&mode, &mode_bytes_left);
|
||||
|
||||
puts("\r\nI'm testapp on:");
|
||||
// Output the TK1 core's NAME0 and NAME1
|
||||
uint32_t name;
|
||||
wordcpy_s(&name, 1, (void *)tk1name0, 1);
|
||||
reverseword(&name);
|
||||
write((const uint8_t *)&name, 4);
|
||||
puts(" ");
|
||||
wordcpy_s(&name, 1, (void *)tk1name1, 1);
|
||||
reverseword(&name);
|
||||
write((const uint8_t *)&name, 4);
|
||||
puts("\r\n");
|
||||
|
||||
uint32_t zeros[8];
|
||||
memset(zeros, 0, 8 * 4);
|
||||
|
||||
int anyfailed = 0;
|
||||
|
||||
uint32_t uds_local[UDS_WORDS];
|
||||
uint32_t udi_local[UDI_WORDS];
|
||||
|
||||
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(&mode, &mode_bytes_left);
|
||||
if (in == '+') {
|
||||
syscall(TK1_SYSCALL_RESET, 0);
|
||||
}
|
||||
|
||||
writebyte(MODE_CDC);
|
||||
writebyte(1);
|
||||
writebyte(in);
|
||||
}
|
||||
}
|
94
hw/application_fpga/fw/testapp/syscall.S
Normal file
94
hw/application_fpga/fw/testapp/syscall.S
Normal 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
|
12
hw/application_fpga/fw/testapp/syscall.h
Normal file
12
hw/application_fpga/fw/testapp/syscall.h
Normal 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
|
@ -3,8 +3,7 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "../tk1/assert.h"
|
||||
#include "../tk1/blake2s/blake2s.h"
|
||||
#include "../tk1/flash.h"
|
||||
#include "../tk1/lib.h"
|
||||
#include "../tk1/proto.h"
|
||||
#include "../tk1/types.h"
|
||||
@ -26,7 +25,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
|
||||
@ -196,11 +194,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 = 0;
|
||||
uint8_t mode = 0;
|
||||
uint8_t mode_bytes_left = 0;
|
||||
@ -294,6 +287,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++) {
|
||||
@ -311,56 +314,9 @@ 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 24 MHz, giving us timer in seconds
|
||||
*timer_prescaler = 24 * 1000000;
|
||||
// Matching clock at 21 MHz, giving us timer in seconds
|
||||
*timer_prescaler = 21 * 1000000;
|
||||
|
||||
// Test timer expiration after 1s
|
||||
*timer = 1;
|
||||
@ -394,32 +350,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) {
|
||||
|
@ -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)
|
||||
|
216
hw/application_fpga/fw/tk1/flash.c
Normal file
216
hw/application_fpga/fw/tk1/flash.c
Normal 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;
|
||||
}
|
57
hw/application_fpga/fw/tk1/flash.h
Normal file
57
hw/application_fpga/fw/tk1/flash.h
Normal 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
|
7
hw/application_fpga/fw/tk1/picorv32/README.md
Normal file
7
hw/application_fpga/fw/tk1/picorv32/README.md
Normal 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
|
102
hw/application_fpga/fw/tk1/picorv32/custom_ops.S
Normal file
102
hw/application_fpga/fw/tk1/picorv32/custom_ops.S
Normal 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)
|
||||
|
88
hw/application_fpga/fw/tk1/spi.c
Normal file
88
hw/application_fpga/fw/tk1/spi.c
Normal 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;
|
||||
}
|
13
hw/application_fpga/fw/tk1/spi.h
Normal file
13
hw/application_fpga/fw/tk1/spi.h
Normal 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
|
@ -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
|
||||
|
41
hw/application_fpga/fw/tk1/syscall.c
Normal file
41
hw/application_fpga/fw/tk1/syscall.c
Normal 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
|
||||
}
|
13
hw/application_fpga/fw/tk1/syscall_nrs.h
Normal file
13
hw/application_fpga/fw/tk1/syscall_nrs.h
Normal 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
|
@ -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
|
||||
|
@ -57,11 +57,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.
|
||||
@ -80,11 +82,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;
|
||||
|
||||
@ -132,6 +136,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;
|
||||
@ -139,13 +144,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;
|
||||
@ -166,12 +175,17 @@ module application_fpga (
|
||||
|
||||
|
||||
picorv32 #(
|
||||
.ENABLE_COUNTERS(0),
|
||||
.TWO_STAGE_SHIFT(0),
|
||||
.ENABLE_COUNTERS (0),
|
||||
.TWO_STAGE_SHIFT (0),
|
||||
.CATCH_MISALIGN (0),
|
||||
.COMPRESSED_ISA (1),
|
||||
.ENABLE_FAST_MUL(1),
|
||||
.BARREL_SHIFTER (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),
|
||||
@ -185,11 +199,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(),
|
||||
@ -240,8 +255,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),
|
||||
@ -280,7 +294,7 @@ module application_fpga (
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.en(~rw_locked),
|
||||
|
||||
.cs(uds_cs),
|
||||
.address(uds_address),
|
||||
@ -326,7 +340,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),
|
||||
@ -353,6 +367,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),
|
||||
@ -379,6 +397,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.
|
||||
@ -428,6 +460,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];
|
||||
@ -500,6 +535,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;
|
||||
|
@ -70,11 +70,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.
|
||||
@ -92,11 +94,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;
|
||||
|
||||
@ -144,6 +148,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;
|
||||
@ -151,13 +156,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;
|
||||
@ -177,12 +186,17 @@ module application_fpga_sim (
|
||||
|
||||
|
||||
picorv32 #(
|
||||
.ENABLE_COUNTERS(0),
|
||||
.TWO_STAGE_SHIFT(0),
|
||||
.ENABLE_COUNTERS (0),
|
||||
.TWO_STAGE_SHIFT (0),
|
||||
.CATCH_MISALIGN (0),
|
||||
.COMPRESSED_ISA (1),
|
||||
.ENABLE_FAST_MUL(1),
|
||||
.BARREL_SHIFTER (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),
|
||||
@ -196,11 +210,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(),
|
||||
@ -251,8 +266,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),
|
||||
@ -291,7 +305,7 @@ module application_fpga_sim (
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.en(~rw_locked),
|
||||
|
||||
.cs(uds_cs),
|
||||
.address(uds_address),
|
||||
@ -339,7 +353,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),
|
||||
@ -366,6 +380,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),
|
||||
@ -391,6 +409,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.
|
||||
@ -442,6 +474,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];
|
||||
@ -534,6 +569,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";
|
||||
|
@ -84,7 +84,7 @@ module tb_application_fpga_sim ();
|
||||
//----------------------------------------------------------------
|
||||
initial begin
|
||||
// End simulation after XXX time units (set by timescale)
|
||||
#20000000;
|
||||
#14000;
|
||||
$display("TIMEOUT");
|
||||
$finish;
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user