diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 61c9f0d..624a56f 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -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 diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 1f3ab78..1ebeb5a 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -11,9 +11,10 @@ The design top level is in `rtl/application_fpga.v`. It contains instances of all cores as well as the memory system. The memory system allows the CPU to access cores in different ways -given the current execution mode. There are two execution modes - -firmware and application. Basically, in application mode the access is -more restrictive. +given the current execution mode. There are three execution modes - +firmware, application and system call. Each mode give access to a +different set of resources. Where app mode is the most restrictive and +firmware mode is the least restrictive. The rest of the components are under `cores`. They typically have their own `README.md` file documenting them and their API in detail. @@ -24,17 +25,18 @@ and bitmasks, see the file `fw/tk1_mem.h`. Rough memory map: -| *name* | *prefix* | -|---------|----------| -| ROM | 0x00 | -| RAM | 0x40 | -| TRNG | 0xc0 | -| Timer | 0xc1 | -| UDS | 0xc2 | -| UART | 0xc3 | -| Touch | 0xc4 | -| FW\_RAM | 0xd0 | -| TK1 | 0xff | +| *name* | *prefix* | +|------------|----------| +| ROM | 0x00 | +| RAM | 0x40 | +| TRNG | 0xc0 | +| Timer | 0xc1 | +| UDS | 0xc2 | +| UART | 0xc3 | +| Touch | 0xc4 | +| FW\_RAM | 0xd0 | +| IRQ31\_SET | 0xe1 | +| TK1 | 0xff | ## `clk_reset_gen` @@ -96,6 +98,71 @@ hours, days) there is also a 32 bit prescaler. The timer is available to use by firmware and applications. +## `irq31_set` + +Interrupt 31 trigger area. A 32-bit write to the IRQ31\_SET memory +area will trigger interrupt 31. + +## Interrupts + +Triggering an interrupt will cause the CPU to execute the interrupt +handler att address 0x10. + +The interrupt handler is shared by all PicoRV32 interrupts but only +interrupt 31 is enabled on the Tkey. Register `x4` can be inspected to +determine the interrupt source. Each interrupt source is assigned one +bit in x4. Triggered interrupts have their bit set to `1`. + +| *Interrupt Name* | *Source* | *x4 Bit* | +|------------------|------------|----------| +| IRQ_SYSCALL | IRQ31\_SET | 31 | + +The return address is located in register `x3`. Calling the PicoRV32 +specific instruction `retirq` exits the interrupt handler and clears +the interrupt source. + +No registers are stored/restored when entering/exiting the interrupt +handler. It is up to the software to store/restore as necessary. + +Interrupts can be enabled/disabled using the PicoRV32 specific +`maskirq` instruction. + +## Restricted resources + +The following table shows resource availablility for each execution +mode: + +| *Execution Mode* | *ROM* | *FW RAM* | *SPI* | *Sensitive assets* | +|------------------|--------|----------|-------|--------------------| +| Firmware mode | r/x | r/w | r/w | r/w* | +| IRQ_SYSCALL | r/x | r/w | r/w | r* | +| Application mode | r | i | i | r* | + +Legend: +r = readable +w = writeable +x = executable +i = invisible +* = read-/writeability varies, see below + +These sensitive assets are only readable and/or writeable in firmware +mode: +- APP_START +- APP_SIZE +- CDI_FIRST +- CDI_LAST +- RAM_ADDR_RAND +- RAM_DATA_RAND +- UDI_FIRST +- UDI_LAST +- UDS_FIRST +- UDS_LAST + +Note that these assets have different properties, some are read-only +and some are write-only. The list above only shows if they are +restricted in app mode. See each individual API to find out more about +their properties. + ## `tk1` See [tk1 README](core/tk1/README.md) for details. @@ -107,7 +174,6 @@ Contains: - RGB LED control. - General purpose input/output (GPIO) pin control. - Application introspection: start address and size of binary. -- BLAKE2s function access. - Compound Device Identity (CDI). - Unique Device Identity (UDI). - RAM memory protection. diff --git a/hw/application_fpga/core/clk_reset_gen/rtl/clk_reset_gen.v b/hw/application_fpga/core/clk_reset_gen/rtl/clk_reset_gen.v index 1a68b10..b3a0ce9 100644 --- a/hw/application_fpga/core/clk_reset_gen/rtl/clk_reset_gen.v +++ b/hw/application_fpga/core/clk_reset_gen/rtl/clk_reset_gen.v @@ -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 ( diff --git a/hw/application_fpga/core/fw_ram/rtl/fw_ram.v b/hw/application_fpga/core/fw_ram/rtl/fw_ram.v index 150f6b5..f649914 100644 --- a/hw/application_fpga/core/fw_ram/rtl/fw_ram.v +++ b/hw/application_fpga/core/fw_ram/rtl/fw_ram.v @@ -17,8 +17,7 @@ module fw_ram ( input wire clk, input wire reset_n, - input wire system_mode, - + input wire en, input wire cs, input wire [ 3 : 0] we, input wire [ 8 : 0] address, @@ -31,21 +30,19 @@ module fw_ram ( //---------------------------------------------------------------- // Registers and wires. //---------------------------------------------------------------- - reg [31 : 0] tmp_read_data; - reg [31 : 0] mem_read_data0; - reg [31 : 0] mem_read_data1; - reg ready_reg; - wire system_mode_cs; - reg bank0; - reg bank1; + reg [31 : 0] tmp_read_data; + reg [31 : 0] mem_read_data0; + reg [31 : 0] mem_read_data1; + reg ready_reg; + reg bank0; + reg bank1; //---------------------------------------------------------------- // Concurrent assignment of ports. //---------------------------------------------------------------- - assign read_data = tmp_read_data; - assign ready = ready_reg; - assign system_mode_cs = cs && ~system_mode; + assign read_data = tmp_read_data; + assign ready = ready_reg; //---------------------------------------------------------------- @@ -56,12 +53,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank0), + .RE(en & cs & bank0), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[15 : 0]), - .WE((|we & system_mode_cs & bank0)), + .WE((|we & en & cs & bank0)), .MASK({{8{~we[1]}}, {8{~we[0]}}}) ); @@ -70,12 +67,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank0), + .RE(en & cs & bank0), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[31 : 16]), - .WE((|we & system_mode_cs & bank0)), + .WE((|we & en & cs & bank0)), .MASK({{8{~we[3]}}, {8{~we[2]}}}) ); @@ -85,12 +82,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank1), + .RE(en & cs & bank1), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[15 : 0]), - .WE((|we & system_mode_cs & bank1)), + .WE((|we & en & cs & bank1)), .MASK({{8{~we[1]}}, {8{~we[0]}}}) ); @@ -99,12 +96,12 @@ module fw_ram ( .RADDR({3'h0, address[7 : 0]}), .RCLK(clk), .RCLKE(1'h1), - .RE(system_mode_cs & bank1), + .RE(en & cs & bank1), .WADDR({3'h0, address[7 : 0]}), .WCLK(clk), .WCLKE(1'h1), .WDATA(write_data[31 : 16]), - .WE((|we & system_mode_cs & bank1)), + .WE((|we & en & cs & bank1)), .MASK({{8{~we[3]}}, {8{~we[2]}}}) ); @@ -129,7 +126,7 @@ module fw_ram ( bank1 = 1'h0; tmp_read_data = 32'h0; - if (system_mode_cs) begin + if (en & cs) begin if (address[8]) begin bank1 = 1'h1; tmp_read_data = mem_read_data1; diff --git a/hw/application_fpga/core/tk1/README.md b/hw/application_fpga/core/tk1/README.md index b53484b..d6d1e44 100644 --- a/hw/application_fpga/core/tk1/README.md +++ b/hw/application_fpga/core/tk1/README.md @@ -78,18 +78,6 @@ FW as part of the loading of the app. The registers can't be written when the `ADDR_SYSTEM_MODE_CTRL` has been set. -### Access to Blake2s - -``` -ADDR_BLAKE2S: 0x10 -``` - -This register provides the 32-bit function pointer address to the -Blake2s hash function in the FW. It is written by FW during boot. The -register can't be written to when the `ADDR_SYSTEM_MODE_CTRL` has been -set. - - ### Access to CDI ``` diff --git a/hw/application_fpga/core/tk1/rtl/tk1.v b/hw/application_fpga/core/tk1/rtl/tk1.v index 939897c..624a1e0 100644 --- a/hw/application_fpga/core/tk1/rtl/tk1.v +++ b/hw/application_fpga/core/tk1/rtl/tk1.v @@ -20,7 +20,7 @@ module tk1 #( input wire reset_n, input wire cpu_trap, - output wire system_mode, + output wire rw_locked, input wire [31 : 0] cpu_addr, input wire cpu_instr, @@ -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,15 +599,21 @@ module tk1 #( end if (address == ADDR_SPI_EN) begin - spi_enable_vld = 1'h1; + if (spi_access_en) begin + spi_enable_vld = 1'h1; + end end if (address == ADDR_SPI_XFER) begin - spi_start = 1'h1; + if (spi_access_en) begin + spi_start = 1'h1; + end end if (address == ADDR_SPI_DATA) begin - spi_tx_data_vld = 1'h1; + if (spi_access_en) begin + spi_tx_data_vld = 1'h1; + end end end @@ -619,26 +650,26 @@ module tk1 #( tmp_read_data = app_size_reg; end - if (address == ADDR_BLAKE2S) begin - tmp_read_data = blake2s_addr_reg; - end - if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin tmp_read_data = cdi_mem[address[2 : 0]]; end if ((address >= ADDR_UDI_FIRST) && (address <= ADDR_UDI_LAST)) begin - if (!system_mode_reg) begin + if (!rw_locked) begin tmp_read_data = udi_rdata; end end if (address == ADDR_SPI_XFER) begin - tmp_read_data[0] = spi_ready; + if (spi_access_en) begin + tmp_read_data[0] = spi_ready; + end end if (address == ADDR_SPI_DATA) begin - tmp_read_data[7 : 0] = spi_rx_data; + if (spi_access_en) begin + tmp_read_data[7 : 0] = spi_rx_data; + end end end diff --git a/hw/application_fpga/core/tk1/tb/tb_tk1.v b/hw/application_fpga/core/tk1/tb/tb_tk1.v index 2344849..4012ea4 100644 --- a/hw/application_fpga/core/tk1/tb/tb_tk1.v +++ b/hw/application_fpga/core/tk1/tb/tb_tk1.v @@ -18,7 +18,7 @@ module tb_tk1 (); //---------------------------------------------------------------- // Internal constant and parameter definitions. //---------------------------------------------------------------- - parameter DEBUG = 1; + parameter DEBUG = 0; parameter CLK_HALF_PERIOD = 1; parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD; @@ -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_cs = 1'h1; + 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 (read_data == expected) begin + if (DEBUG) begin $display("--- Reading 0x%08x from 0x%02x.", read_data, address); end - else begin - $display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data, - address, expected); - error_ctr = error_ctr + 1; - end - $display(""); + end + else begin + $display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data, + address, expected); + error_ctr = error_ctr + 1; end end endtask // read_check_word + //---------------------------------------------------------------- + // check_equal() + // + // Check that two values are equal + //---------------------------------------------------------------- + task check_equal(input [31 : 0] value, input [31 : 0] expected); + begin : check_equal + if (value != expected) begin + $display("--- Error: Got 0x%08x, expected 0x%08x", 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(""); diff --git a/hw/application_fpga/core/uart/rtl/uart.v b/hw/application_fpga/core/uart/rtl/uart.v index c37f5c9..1d3a6b1 100644 --- a/hw/application_fpga/core/uart/rtl/uart.v +++ b/hw/application_fpga/core/uart/rtl/uart.v @@ -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; diff --git a/hw/application_fpga/core/uds/rtl/uds.v b/hw/application_fpga/core/uds/rtl/uds.v index 2aaa112..8c8f1a3 100644 --- a/hw/application_fpga/core/uds/rtl/uds.v +++ b/hw/application_fpga/core/uds/rtl/uds.v @@ -17,8 +17,7 @@ module uds ( input wire clk, input wire reset_n, - input wire system_mode, - + input wire en, input wire cs, input wire [ 2 : 0] address, output wire [31 : 0] read_data, @@ -89,7 +88,7 @@ module uds ( if (cs) begin tmp_ready = 1'h1; - if (!system_mode) begin + if (en) begin if (uds_rd_reg[address[2 : 0]] == 1'h0) begin uds_rd_we = 1'h1; end diff --git a/hw/application_fpga/fw/irqpoc/Makefile b/hw/application_fpga/fw/irqpoc/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc/custom_ops.S b/hw/application_fpga/fw/irqpoc/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc/main.c b/hw/application_fpga/fw/irqpoc/main.c new file mode 100644 index 0000000..c98f474 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc/start.S b/hw/application_fpga/fw/irqpoc/start.S new file mode 100644 index 0000000..def6783 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc/start.S @@ -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. + diff --git a/hw/application_fpga/fw/irqpoc_c_example/Makefile b/hw/application_fpga/fw/irqpoc_c_example/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_c_example/custom_ops.S b/hw/application_fpga/fw/irqpoc_c_example/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_c_example/main.c b/hw/application_fpga/fw/irqpoc_c_example/main.c new file mode 100644 index 0000000..dc2a0d6 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/main.c @@ -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) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_c_example/start.S b/hw/application_fpga/fw/irqpoc_c_example/start.S new file mode 100644 index 0000000..d295ff6 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_c_example/start.S @@ -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: + diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/Makefile b/hw/application_fpga/fw/irqpoc_led_toggle/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S b/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/main.c b/hw/application_fpga/fw/irqpoc_led_toggle/main.c new file mode 100644 index 0000000..c98f474 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/start.S b/hw/application_fpga/fw/irqpoc_led_toggle/start.S new file mode 100644 index 0000000..1b1f558 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/start.S @@ -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. + diff --git a/hw/application_fpga/fw/irqpoc_with_app/Makefile b/hw/application_fpga/fw/irqpoc_with_app/Makefile new file mode 100644 index 0000000..82b9262 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_with_app/custom_ops.S b/hw/application_fpga/fw/irqpoc_with_app/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_with_app/main.c b/hw/application_fpga/fw/irqpoc_with_app/main.c new file mode 100644 index 0000000..c98f474 --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_with_app/start.S b/hw/application_fpga/fw/irqpoc_with_app/start.S new file mode 100644 index 0000000..cae085c --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_with_app/start.S @@ -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: + diff --git a/hw/application_fpga/fw/testapp/Makefile b/hw/application_fpga/fw/testapp/Makefile new file mode 100644 index 0000000..40c694c --- /dev/null +++ b/hw/application_fpga/fw/testapp/Makefile @@ -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) diff --git a/hw/application_fpga/fw/testapp/app.lds b/hw/application_fpga/fw/testapp/app.lds new file mode 100644 index 0000000..421122c --- /dev/null +++ b/hw/application_fpga/fw/testapp/app.lds @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2022 Tillitis AB + * SPDX-License-Identifier: BSD-2-Clause + */ + +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +MEMORY +{ + RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */ +} + +SECTIONS +{ + .text.init : + { + *(.text.init) + } >RAM + + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.srodata) /* .rodata sections (constants, strings, etc.) */ + *(.srodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + _etext = .; + _sidata = _etext; + } >RAM + + .data : AT (_etext) + { + . = ALIGN(4); + _sdata = .; + . = ALIGN(4); + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.sdata) /* .sdata sections */ + *(.sdata*) /* .sdata* sections */ + . = ALIGN(4); + _edata = .; + } >RAM + + /* Uninitialized data section */ + .bss : + { + . = ALIGN(4); + _sbss = .; + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; + } >RAM + + /* libcrt0/crt0.S inits stack to start just below end of RAM */ +} diff --git a/hw/application_fpga/fw/testapp/crt0.S b/hw/application_fpga/fw/testapp/crt0.S new file mode 100644 index 0000000..f484b7d --- /dev/null +++ b/hw/application_fpga/fw/testapp/crt0.S @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2022 Tillitis AB +// SPDX-License-Identifier: BSD-2-Clause + + .section ".text.init" + .global _start +_start: + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10,0 + li x11,0 + li x12,0 + li x13,0 + li x14,0 + li x15,0 + li x16,0 + li x17,0 + li x18,0 + li x19,0 + li x20,0 + li x21,0 + li x22,0 + li x23,0 + li x24,0 + li x25,0 + li x26,0 + li x27,0 + li x28,0 + li x29,0 + li x30,0 + li x31,0 + + /* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */ + li sp, 0x4001fff0 + + /* zero-init bss section */ + la a0, _sbss + la a1, _ebss + bge a0, a1, end_init_bss + +loop_init_bss: + sw zero, 0(a0) + addi a0, a0, 4 + blt a0, a1, loop_init_bss + +end_init_bss: + call main diff --git a/hw/application_fpga/fw/testapp/main.c b/hw/application_fpga/fw/testapp/main.c new file mode 100644 index 0000000..c8acdec --- /dev/null +++ b/hw/application_fpga/fw/testapp/main.c @@ -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); + } +} diff --git a/hw/application_fpga/fw/testapp/syscall.S b/hw/application_fpga/fw/testapp/syscall.S new file mode 100644 index 0000000..10db760 --- /dev/null +++ b/hw/application_fpga/fw/testapp/syscall.S @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2024 Tillitis AB +// SPDX-License-Identifier: BSD-2-Clause + +#include "../tk1/picorv32/custom_ops.S" + + .section ".text" + .globl syscall_enable + .globl syscall + +syscall_enable: + // Enable IRQs + li t0, 0x7fffffff // IRQ31 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + + ret + +syscall: + // Save registers to stack + addi sp, sp, -32*4 + sw x0, 0*4(sp) + sw x1, 1*4(sp) + // x2 (sp) is assumed to be preserved by the interrupt handler. + sw x3, 3*4(sp) + sw x4, 4*4(sp) + sw x5, 5*4(sp) + sw x6, 6*4(sp) + sw x7, 7*4(sp) + sw x8, 8*4(sp) + sw x9, 9*4(sp) + // x10 (a0) will contain syscall return value. And should not be saved. + sw x11, 11*4(sp) + sw x12, 12*4(sp) + sw x13, 13*4(sp) + sw x14, 14*4(sp) + sw x15, 15*4(sp) + sw x16, 16*4(sp) + sw x17, 17*4(sp) + sw x18, 18*4(sp) + sw x19, 19*4(sp) + sw x20, 20*4(sp) + sw x21, 21*4(sp) + sw x22, 22*4(sp) + sw x23, 23*4(sp) + sw x24, 24*4(sp) + sw x25, 25*4(sp) + sw x26, 26*4(sp) + sw x27, 27*4(sp) + sw x28, 28*4(sp) + sw x29, 29*4(sp) + sw x30, 30*4(sp) + sw x31, 31*4(sp) + + // Trigger syscall interrupt + li t0, (1 << 31) + and t0, a0, t0 + li t1, 0xe1000000 // Syscall interrupt trigger address + sw zero, 0(t1) // Trigger interrupt + + // Restore registers from stack + lw x0, 0*4(sp) + lw x1, 1*4(sp) + // x2 (sp) is assumed to be preserved by the interrupt handler. + lw x3, 3*4(sp) + lw x4, 4*4(sp) + lw x5, 5*4(sp) + lw x6, 6*4(sp) + lw x7, 7*4(sp) + lw x8, 8*4(sp) + lw x9, 9*4(sp) + // x10 (a0) contains syscall return value. And should not be destroyed. + lw x11, 11*4(sp) + lw x12, 12*4(sp) + lw x13, 13*4(sp) + lw x14, 14*4(sp) + lw x15, 15*4(sp) + lw x16, 16*4(sp) + lw x17, 17*4(sp) + lw x18, 18*4(sp) + lw x19, 19*4(sp) + lw x20, 20*4(sp) + lw x21, 21*4(sp) + lw x22, 22*4(sp) + lw x23, 23*4(sp) + lw x24, 24*4(sp) + lw x25, 25*4(sp) + lw x26, 26*4(sp) + lw x27, 27*4(sp) + lw x28, 28*4(sp) + lw x29, 29*4(sp) + lw x30, 30*4(sp) + lw x31, 31*4(sp) + addi sp, sp, 32*4 + + ret diff --git a/hw/application_fpga/fw/testapp/syscall.h b/hw/application_fpga/fw/testapp/syscall.h new file mode 100644 index 0000000..6fc3dc1 --- /dev/null +++ b/hw/application_fpga/fw/testapp/syscall.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2024 Tillitis AB +// SPDX-License-Identifier: BSD-2-Clause + +#include "../tk1/types.h" + +#ifndef TKEY_APP_SYSCALL_H +#define TKEY_APP_SYSCALL_H + +void syscall_enable(void); +int syscall(uint32_t number, uint32_t arg1); + +#endif diff --git a/hw/application_fpga/fw/testfw/main.c b/hw/application_fpga/fw/testfw/main.c index fe97d53..7d65ddc 100644 --- a/hw/application_fpga/fw/testfw/main.c +++ b/hw/application_fpga/fw/testfw/main.c @@ -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) { diff --git a/hw/application_fpga/fw/tk1/Makefile b/hw/application_fpga/fw/tk1/Makefile index 68b8261..2ac03fa 100644 --- a/hw/application_fpga/fw/tk1/Makefile +++ b/hw/application_fpga/fw/tk1/Makefile @@ -1,5 +1,6 @@ # Uses ../.clang-format -FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c led.h +FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c \ + led.h syscall.c spi.c spi.h flash.c flash.h .PHONY: fmt fmt: clang-format --dry-run --ferror-limit=0 $(FMTFILES) diff --git a/hw/application_fpga/fw/tk1/flash.c b/hw/application_fpga/fw/tk1/flash.c new file mode 100644 index 0000000..efa8c47 --- /dev/null +++ b/hw/application_fpga/fw/tk1/flash.c @@ -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 + +// clang-format off +static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER; +static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER; +static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS; +static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL; +// clang-format on + +// CPU clock frequency in Hz +#define CPUFREQ 21000000 +#define PAGE_SIZE 256 + +static void delay(int timeout_ms) +{ + // Tick once every centisecond + *timer_prescaler = CPUFREQ / 100; + *timer = timeout_ms / 10; + + *timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT); + + while (*timer_status != 0) { + } + + // Stop timer + *timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT); +} + +bool flash_is_busy(void) +{ + uint8_t tx_buf = READ_STATUS_REG_1; + uint8_t rx_buf = {0x00}; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf, sizeof(rx_buf)); + + if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) { + return true; + } + + return false; +} + +// Blocking until !busy +void flash_wait_busy(void) +{ + while (flash_is_busy()) { + delay(10); + } +} + +void flash_write_enable(void) +{ + uint8_t tx_buf = WRITE_ENABLE; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_write_disable(void) +{ + uint8_t tx_buf = WRITE_DISABLE; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_sector_erase(uint32_t address) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = SECTOR_ERASE; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + /* tx_buf[3] is within a sector, and hence does not make a difference */ + + flash_write_enable(); + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); + flash_wait_busy(); +} + +void flash_block_32_erase(uint32_t address) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = BLOCK_ERASE_32K; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF; + + flash_write_enable(); + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); + flash_wait_busy(); +} + +// 64 KiB block erase, only cares about address bits 16 and above. +void flash_block_64_erase(uint32_t address) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = BLOCK_ERASE_64K; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + /* tx_buf[2] and tx_buf[3] is within a block, and hence does not make a + * difference */ + + flash_write_enable(); + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); + flash_wait_busy(); +} + +void flash_release_powerdown(void) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = RELEASE_POWER_DOWN; + + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_powerdown(void) +{ + uint8_t tx_buf = POWER_DOWN; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0); +} + +void flash_read_manufacturer_device_id(uint8_t *device_id) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = READ_MANUFACTURER_ID; + + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2); +} + +void flash_read_jedec_id(uint8_t *jedec_id) +{ + uint8_t tx_buf = READ_JEDEC_ID; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3); +} + +void flash_read_unique_id(uint8_t *unique_id) +{ + uint8_t tx_buf[5] = {0x00}; + tx_buf[0] = READ_UNIQUE_ID; + + spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8); +} + +void flash_read_status(uint8_t *status_reg) +{ + uint8_t tx_buf = READ_STATUS_REG_1; + + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1); + + tx_buf = READ_STATUS_REG_2; + spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1, 1); +} + +int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size) +{ + uint8_t tx_buf[4] = {0x00}; + tx_buf[0] = READ_DATA; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF; + + return spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, dest_buf, size); +} + +// Only handles writes where the least significant byte of the start address is +// zero. +int flash_write_data(uint32_t address, uint8_t *data, size_t size) +{ + if (size <= 0 || size > 4096) { + return -1; + } + + size_t left = size; + uint8_t *p_data = data; + size_t n_bytes = 0; + + uint8_t tx_buf[4] = { + PAGE_PROGRAM, /* tx_buf[0] */ + (address >> ADDR_BYTE_3_BIT) & 0xFF, /* tx_buf[1] */ + (address >> ADDR_BYTE_2_BIT) & 0xFF, /* tx_buf[2] */ + 0x00, /* tx_buf[3] */ + }; + + while (left > 0) { + if (left >= PAGE_SIZE) { + n_bytes = PAGE_SIZE; + } else { + n_bytes = left; + } + + flash_write_enable(); + + if (spi_transfer(tx_buf, sizeof(tx_buf), p_data, n_bytes, NULL, + 0) != 0) { + return -1; + } + + left -= n_bytes; + p_data += n_bytes; + + address += n_bytes; + tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF; + tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF; + + flash_wait_busy(); + } + + return 0; +} diff --git a/hw/application_fpga/fw/tk1/flash.h b/hw/application_fpga/fw/tk1/flash.h new file mode 100644 index 0000000..1c60cc5 --- /dev/null +++ b/hw/application_fpga/fw/tk1/flash.h @@ -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 + +#define WRITE_ENABLE 0x06 +#define WRITE_DISABLE 0x04 + +#define READ_STATUS_REG_1 0x05 +#define READ_STATUS_REG_2 0x35 +#define WRITE_STATUS_REG 0x01 + +#define PAGE_PROGRAM 0x02 +#define SECTOR_ERASE 0x20 +#define BLOCK_ERASE_32K 0x52 +#define BLOCK_ERASE_64K 0xD8 +#define CHIP_ERASE 0xC7 + +#define POWER_DOWN 0xB9 +#define READ_DATA 0x03 +#define RELEASE_POWER_DOWN 0xAB + +#define READ_MANUFACTURER_ID 0x90 +#define READ_JEDEC_ID 0x9F +#define READ_UNIQUE_ID 0x4B + +#define ENABLE_RESET 0x66 +#define RESET 0x99 + +#define ADDR_BYTE_3_BIT 16 +#define ADDR_BYTE_2_BIT 8 +#define ADDR_BYTE_1_BIT 0 + +#define STATUS_REG_BUSY_BIT 0 +#define STATUS_REG_WEL_BIT 1 + +bool flash_is_busy(void); +void flash_wait_busy(void); +void flash_write_enable(void); +void flash_write_disable(void); +void flash_sector_erase(uint32_t address); +void flash_block_32_erase(uint32_t address); +void flash_block_64_erase(uint32_t address); +void flash_release_powerdown(void); +void flash_powerdown(void); +void flash_read_manufacturer_device_id(uint8_t *device_id); +void flash_read_jedec_id(uint8_t *jedec_id); +void flash_read_unique_id(uint8_t *unique_id); +void flash_read_status(uint8_t *status_reg); +int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size); +int flash_write_data(uint32_t address, uint8_t *data, size_t size); + +#endif diff --git a/hw/application_fpga/fw/tk1/picorv32/README.md b/hw/application_fpga/fw/tk1/picorv32/README.md new file mode 100644 index 0000000..761be04 --- /dev/null +++ b/hw/application_fpga/fw/tk1/picorv32/README.md @@ -0,0 +1,7 @@ +# PicoRV32 + +## custom_ops.S + +Custom PicoRV32 instructions are located in `custom_ops.S`. +`custom_ops.S` is imported from upstream PicoRV32 commit: +YosysHQ/picorv32@70f3c33 diff --git a/hw/application_fpga/fw/tk1/picorv32/custom_ops.S b/hw/application_fpga/fw/tk1/picorv32/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/hw/application_fpga/fw/tk1/picorv32/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/tk1/spi.c b/hw/application_fpga/fw/tk1/spi.c new file mode 100644 index 0000000..ba50e35 --- /dev/null +++ b/hw/application_fpga/fw/tk1/spi.c @@ -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; +} diff --git a/hw/application_fpga/fw/tk1/spi.h b/hw/application_fpga/fw/tk1/spi.h new file mode 100644 index 0000000..0db6555 --- /dev/null +++ b/hw/application_fpga/fw/tk1/spi.h @@ -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 diff --git a/hw/application_fpga/fw/tk1/start.S b/hw/application_fpga/fw/tk1/start.S index 93a2fb0..dc892f0 100644 --- a/hw/application_fpga/fw/tk1/start.S +++ b/hw/application_fpga/fw/tk1/start.S @@ -1,11 +1,102 @@ /* - * Copyright (C) 2022, 2023 - Tillitis AB + * Copyright (C) 2022, 2023, 2024, 2025 - Tillitis AB * SPDX-License-Identifier: GPL-2.0-only */ +#include "../tk1_mem.h" +#include "picorv32/custom_ops.S" // PicoRV32 custom instructions + +#define illegal_insn() .word 0 + +#define FW_SP_STORAGE (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 4) +#define FW_STACK_TOP (TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE - 2*4) + .section ".text.init" .globl _start _start: + j init + +/* + * IRQ handler +*/ + .=0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + li t4, (1 << 31) + beq x4, t4, irq_source_ok +unexpected_irq_source: + illegal_insn() + j unexpected_irq_source +irq_source_ok: + + // Save app stack pointer. App is responsible for saving the rest of + // the registers. + li t0, FW_SP_STORAGE + sw sp, 0(t0) + + // Setup firmware stack pointer + li sp, FW_STACK_TOP + + // Run syscall handler + call syscall_handler + + // Restore app stack pointer + li t0, FW_SP_STORAGE + lw sp, 0(t0) + + // Verify that interrupt return address (x3) is in app RAM + li t0, TK1_RAM_BASE // 0x40000000 + blt x3, t0, x3_invalid + li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000 + bge x3, t0, x3_invalid + j x3_valid +x3_invalid: + illegal_insn() + j x3_invalid +x3_valid: + + // Remove data left over from the syscall handling + mv x0, zero + mv x1, zero + // x2 (sp) is assumed to be preserved by the interrupt handler + // x3 (interrupt return address) assumed preserved + mv x4, zero + mv x5, zero + mv x6, zero + mv x7, zero + mv x8, zero + mv x9, zero + // x10 (a0) contains syscall return value. And should not be destroyed. + mv x11, zero + mv x12, zero + mv x13, zero + mv x14, zero + mv x15, zero + mv x16, zero + mv x17, zero + mv x18, zero + mv x19, zero + mv x20, zero + mv x21, zero + mv x22, zero + mv x23, zero + mv x24, zero + mv x25, zero + mv x26, zero + mv x27, zero + mv x28, zero + mv x29, zero + mv x30, zero + mv x31, zero + + picorv32_retirq_insn() // Return from interrupt + +/* + * Init +*/ + .=0x100 +init: li x1, 0 li x2, 0 li x3, 0 diff --git a/hw/application_fpga/fw/tk1/syscall.c b/hw/application_fpga/fw/tk1/syscall.c new file mode 100644 index 0000000..7dfcda8 --- /dev/null +++ b/hw/application_fpga/fw/tk1/syscall.c @@ -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 +} diff --git a/hw/application_fpga/fw/tk1/syscall_nrs.h b/hw/application_fpga/fw/tk1/syscall_nrs.h new file mode 100644 index 0000000..d27a933 --- /dev/null +++ b/hw/application_fpga/fw/tk1/syscall_nrs.h @@ -0,0 +1,13 @@ +// Copyright (C) 2025 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef TKEY_SYSCALL_H +#define TKEY_SYSCALL_H + +enum { + TK1_SYSCALL_RESET = 1, + TK1_SYSCALL_SET_LED = 10, + TK1_SYSCALL_GET_FLASH_CAPACITY = 11, +}; + +#endif diff --git a/hw/application_fpga/fw/tk1/types.h b/hw/application_fpga/fw/tk1/types.h index 9a1fcff..2ed09d5 100644 --- a/hw/application_fpga/fw/tk1/types.h +++ b/hw/application_fpga/fw/tk1/types.h @@ -14,7 +14,7 @@ typedef long long int64_t; typedef unsigned char uint8_t; typedef unsigned long size_t; -#define NULL ((char *)0) +#define NULL ((void *)0) #define FALSE 0 #define TRUE !FALSE diff --git a/hw/application_fpga/rtl/application_fpga.v b/hw/application_fpga/rtl/application_fpga.v index 4d86e10..3e084bf 100644 --- a/hw/application_fpga/rtl/application_fpga.v +++ b/hw/application_fpga/rtl/application_fpga.v @@ -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), - .CATCH_MISALIGN (0), - .COMPRESSED_ISA (1), - .ENABLE_FAST_MUL(1), - .BARREL_SHIFTER (1) + .ENABLE_COUNTERS (0), + .TWO_STAGE_SHIFT (0), + .CATCH_MISALIGN (0), + .COMPRESSED_ISA (1), + .ENABLE_FAST_MUL (1), + .BARREL_SHIFTER (1), + .ENABLE_IRQ (1), + .ENABLE_IRQ_QREGS(0), + .ENABLE_IRQ_TIMER(0), + .MASKED_IRQ (~IRQ31_IRQ_MASK), + .LATCHED_IRQ (IRQ31_IRQ_MASK) ) cpu ( .clk(clk), .resetn(reset_n), @@ -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; diff --git a/hw/application_fpga/tb/application_fpga_sim.v b/hw/application_fpga/tb/application_fpga_sim.v index 94e13ee..c3e41fc 100644 --- a/hw/application_fpga/tb/application_fpga_sim.v +++ b/hw/application_fpga/tb/application_fpga_sim.v @@ -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), - .CATCH_MISALIGN (0), - .COMPRESSED_ISA (1), - .ENABLE_FAST_MUL(1), - .BARREL_SHIFTER (1) + .ENABLE_COUNTERS (0), + .TWO_STAGE_SHIFT (0), + .CATCH_MISALIGN (0), + .COMPRESSED_ISA (1), + .ENABLE_FAST_MUL (1), + .BARREL_SHIFTER (1), + .ENABLE_IRQ (1), + .ENABLE_IRQ_QREGS(0), + .ENABLE_IRQ_TIMER(0), + .MASKED_IRQ (~IRQ31_IRQ_MASK), + .LATCHED_IRQ (IRQ31_IRQ_MASK) ) cpu ( .clk(clk), .resetn(reset_n), @@ -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"; diff --git a/hw/application_fpga/tb/tb_application_fpga_sim.v b/hw/application_fpga/tb/tb_application_fpga_sim.v index df3173d..5bddf11 100644 --- a/hw/application_fpga/tb/tb_application_fpga_sim.v +++ b/hw/application_fpga/tb/tb_application_fpga_sim.v @@ -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