Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoC: Implement syscalls using PicoRV32 interrupts #305

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions hw/application_fpga/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ TESTFW_OBJS = \
$(P)/fw/tk1/lib.o \
$(P)/fw/tk1/blake2s/blake2s.o

IRQPOC_OBJS = \
$(P)/fw/irqpoc/main.o \
$(P)/fw/irqpoc/start.o

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.
#-------------------------------------------------------------------
Expand Down Expand Up @@ -180,6 +198,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 $@
Expand Down Expand Up @@ -222,6 +244,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) > $@
Expand All @@ -232,6 +266,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:
Expand Down Expand Up @@ -402,11 +443,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 \
Expand All @@ -430,14 +473,31 @@ 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, $^)
make -C tb_verilated -f Vtb_application_fpga_sim.mk
./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.
#-------------------------------------------------------------------
Expand Down Expand Up @@ -484,6 +544,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

Expand Down
96 changes: 81 additions & 15 deletions hw/application_fpga/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -24,17 +25,18 @@ and bitmasks, see the file `fw/tk1_mem.h`.

Rough memory map:

| *name* | *prefix* |
|---------|----------|
| ROM | 0x00 |
| RAM | 0x40 |
| TRNG | 0xc0 |
| Timer | 0xc1 |
| UDS | 0xc2 |
| UART | 0xc3 |
| Touch | 0xc4 |
| FW\_RAM | 0xd0 |
| TK1 | 0xff |
| *name* | *prefix* |
|------------|----------|
| ROM | 0x00 |
| RAM | 0x40 |
| TRNG | 0xc0 |
| Timer | 0xc1 |
| UDS | 0xc2 |
| UART | 0xc3 |
| Touch | 0xc4 |
| FW\_RAM | 0xd0 |
| IRQ31\_SET | 0xe1 |
| TK1 | 0xff |

## `clk_reset_gen`

Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
39 changes: 18 additions & 21 deletions hw/application_fpga/core/fw_ram/rtl/fw_ram.v
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,21 +30,19 @@ module fw_ram (
//----------------------------------------------------------------
// Registers and wires.
//----------------------------------------------------------------
reg [31 : 0] tmp_read_data;
reg [31 : 0] mem_read_data0;
reg [31 : 0] mem_read_data1;
reg ready_reg;
wire system_mode_cs;
reg bank0;
reg bank1;
reg [31 : 0] tmp_read_data;
reg [31 : 0] mem_read_data0;
reg [31 : 0] mem_read_data1;
reg ready_reg;
reg bank0;
reg bank1;


//----------------------------------------------------------------
// Concurrent assignment of ports.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign ready = ready_reg;
assign system_mode_cs = cs && ~system_mode;
assign read_data = tmp_read_data;
assign ready = ready_reg;


//----------------------------------------------------------------
Expand All @@ -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]}}})
);

Expand All @@ -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]}}})
);

Expand All @@ -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]}}})
);

Expand All @@ -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]}}})
);

Expand All @@ -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;
Expand Down
12 changes: 0 additions & 12 deletions hw/application_fpga/core/tk1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

```
Expand Down
Loading
Loading