From 1bbb8a686bf9fb6a1bd6d7df20a630136aaa00b9 Mon Sep 17 00:00:00 2001 From: Filip Kosecek Date: Thu, 17 Jul 2025 15:05:10 +0200 Subject: [PATCH] Add support for the Olimex iCE40HX8K-EVB board --- FemtoRV/BOARDS/ice40hx8k_evb.mk | 26 ++ FemtoRV/BOARDS/ice40hx8k_evb.pcf | 22 ++ .../FIRMWARE/CRT/spiflash_ice40hx8k_evb.ld | 100 ++++++++ FemtoRV/FIRMWARE/makefile.inc | 10 +- FemtoRV/Makefile | 1 + FemtoRV/RTL/CONFIGS/ice40hx8k_evb_config.v | 35 +++ FemtoRV/RTL/DEVICES/MappedSPIFlash.v | 15 ++ FemtoRV/RTL/PLL/femtopll.v | 2 + FemtoRV/RTL/PLL/gen_plls.sh | 5 +- FemtoRV/RTL/PLL/pll_ice40hx8k_evb.v | 238 ++++++++++++++++++ FemtoRV/RTL/femtosoc_config.v | 8 + FemtoRV/RTL/get_config.v | 3 + 12 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 FemtoRV/BOARDS/ice40hx8k_evb.mk create mode 100644 FemtoRV/BOARDS/ice40hx8k_evb.pcf create mode 100644 FemtoRV/FIRMWARE/CRT/spiflash_ice40hx8k_evb.ld create mode 100644 FemtoRV/RTL/CONFIGS/ice40hx8k_evb_config.v create mode 100644 FemtoRV/RTL/PLL/pll_ice40hx8k_evb.v diff --git a/FemtoRV/BOARDS/ice40hx8k_evb.mk b/FemtoRV/BOARDS/ice40hx8k_evb.mk new file mode 100644 index 00000000..abe5655d --- /dev/null +++ b/FemtoRV/BOARDS/ice40hx8k_evb.mk @@ -0,0 +1,26 @@ +YOSYS_ICE40HX8K_EVB_OPT=-DICE40HX8K_EVB -q -p "synth_ice40 -relut -top $(PROJECTNAME) -json $(PROJECTNAME).json" +NEXTPNR_ICE40HX8K_EVB_OPT=--json $(PROJECTNAME).json --pcf BOARDS/ice40hx8k_evb.pcf --asc $(PROJECTNAME).asc \ + --freq 40 --hx8k --package ct256 --opt-timing + +ICE40HX8K_EVB: ICE40HX8K_EVB.firmware_config ICE40HX8K_EVB.synth ICE40HX8K_EVB.prog + +ICE40HX8K_EVB.synth: + yosys $(YOSYS_ICE40HX8K_EVB_OPT) $(VERILOGS) + nextpnr-ice40 $(NEXTPNR_ICE40HX8K_EVB_OPT) + icetime -p BOARDS/ice40hx8k_evb.pcf -P ct256 -r $(PROJECTNAME).timings -d hx8k -t $(PROJECTNAME).asc + icepack -s $(PROJECTNAME).asc $(PROJECTNAME).bin + +ICE40HX8K_EVB.show: + yosys $(YOSYS_ICE40HX8K_EVB_OPT) $(VERILOGS) + nextpnr-ice40 $(NEXTPNR_ICE40HX8K_EVB_OPT) --gui + +ICE40HX8K_EVB.prog: + iceprogduino $(PROJECTNAME).bin + +ICE40HX8K_EVB.firmware_config: + BOARD=ice40hx8k_evb TOOLS/make_config.sh -DICE40HX8K_EVB + (cd FIRMWARE; make libs) + +ICE40HX8K_EVB.lint: + verilator -DICE40HX8K_EVB -DBENCH --lint-only --top-module $(PROJECTNAME) \ + -IRTL -IRTL/PROCESSOR -IRTL/DEVICES -IRTL/PLL $(VERILOGS) diff --git a/FemtoRV/BOARDS/ice40hx8k_evb.pcf b/FemtoRV/BOARDS/ice40hx8k_evb.pcf new file mode 100644 index 00000000..0342df08 --- /dev/null +++ b/FemtoRV/BOARDS/ice40hx8k_evb.pcf @@ -0,0 +1,22 @@ +set_io pclk J3 + +set_io D1 M12 +set_io D2 E4 +set_io D3 B2 +set_io D4 F5 +set_io D5 R16 + +# UART communication doesn't work when Olimexino 32U4 is used as a bridge +# which forwards the communication to a PC +# (https://www.olimex.com/wiki/ICE40HX1K-EVB#Get_started_under_Linux). +# There might be a bug in Olimexino's firmware, as UART works with a UART-USB +# converter. +set_io TXD T16 +set_io RXD L11 + +set_io spi_cs_n R12 +set_io spi_miso P11 +set_io spi_mosi P12 +set_io spi_clk R11 + +set_io RESET B1 diff --git a/FemtoRV/FIRMWARE/CRT/spiflash_ice40hx8k_evb.ld b/FemtoRV/FIRMWARE/CRT/spiflash_ice40hx8k_evb.ld new file mode 100644 index 00000000..901c4c08 --- /dev/null +++ b/FemtoRV/FIRMWARE/CRT/spiflash_ice40hx8k_evb.ld @@ -0,0 +1,100 @@ +/* Linker script for programs stored in SPI flash */ +/* Inspired from picorv32/picosoc/sections.lds */ +/* */ +/* text and rodata sections are sent to flash */ +/* bss sections are sent to BRAM */ +/* data sections are sent to BRAM and have */ +/* initialization data in flash. */ +/* AT keyword specifies LMA (Load Memory Address) */ + +MEMORY { + FLASH (rx) : ORIGIN = 0x00830000, LENGTH = 0x100000 /* 1 MB in flash */ + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x3800 /* 14 kB in RAM */ +} + +SECTIONS { + + /* + * This is the initialized data and fastcode section + * The program executes knowing that the data is in the RAM + * but the loader puts the initial values in the FLASH (inidata). + * It is one task of the startup (crt0_spiflash.S) to copy the initial values from FLASH to RAM. + */ + .data_and_fastcode : AT ( _sidata ) { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ + _ram_start = .; /* create a global symbol at ram start (e.g., for garbage collector) */ + + /* Initialized data */ + *(.data) + *(.data*) + *(.sdata) + *(.sdata*) + + /* functions with attribute((section(".fastcode"))) */ + /* (e.g., some functions in femtoGL) */ + *(.fastcode*) + + /* integer mul and div */ + */libgcc.a:muldi3.o(.text) + */libgcc.a:div.o(.text) + + /* low-level graphics functions */ + */libfemtorv32.a:ssd1351_1331.o(.text) + + /* timing */ + wait_cycles.o(.text) + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ + } > RAM + + /* The (non fastcode) program code and other data goes into FLASH */ + .text : { + . = ALIGN(4); + crt0_spiflash.o(.text) /* c runtime initialization (code) */ + + + /* + * I do not understand why, but if I do not put that here, I got + * an overlapping sections error with some programs (for instance pi.c + * or C++ programs) + */ + *(.eh_frame) + *(.eh_frame_hdr) + *(.init_array) + *(.gcc_except_table*) + + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.srodata) /* .rodata sections (constants, strings, etc.) */ + *(.srodata*) /* .rodata* sections (constants, strings, etc.) */ + _etext = .; /* define a global symbol at end of code */ + _sidata = _etext; /* This is used by the startup in order to initialize the .data section */ + } >FLASH + + /* Uninitialized data section */ + .bss : { + . = ALIGN(4); + _sbss = .; /* define a global symbol at bss start; used by startup code */ + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end; used by startup code */ + } >RAM + + /* this is to define the start of the heap, and make sure we have a minimum size */ + .heap : { + . = ALIGN(4); + _heap_start = .; /* define a global symbol at heap start */ + _end = .; /* as expected by syscalls.c */ + } >RAM + + +} diff --git a/FemtoRV/FIRMWARE/makefile.inc b/FemtoRV/FIRMWARE/makefile.inc index 84b82224..79909a30 100644 --- a/FemtoRV/FIRMWARE/makefile.inc +++ b/FemtoRV/FIRMWARE/makefile.inc @@ -86,7 +86,15 @@ FIRMWARE_WORDS=$(FIRMWARE_DIR)/TOOLS/firmware_words ifeq ($(BOARD),icesugar_nano) TOOLCHAIN_PROG_CMD=icesprog -o 0x20000 else - TOOLCHAIN_PROG_CMD=iceprog -o 128k + # iCE40HX8K-EVB needs a special programmer. + # The bitstream size is 136,448 bytes, and the flash block size is 64 kB. + # Therefore, the executable is placed at the next block-aligned address + # in the flash memory. + ifeq ($(BOARD),ice40hx8k_evb) + TOOLCHAIN_PROG_CMD=iceprogduino -o 0x30000 + else + TOOLCHAIN_PROG_CMD=iceprog -o 128k + endif endif ################################################################################ diff --git a/FemtoRV/Makefile b/FemtoRV/Makefile index 020916ee..d51276de 100644 --- a/FemtoRV/Makefile +++ b/FemtoRV/Makefile @@ -16,6 +16,7 @@ include BOARDS/ecp5_evn.mk #include BOARDS/arty35_symbiflow.mk include BOARDS/arty35_yosys_nextpnr.mk include BOARDS/cmod_a7_yosys_nextpnr.mk +include BOARDS/ice40hx8k_evb.mk .PHONY: all clean terminal testbench diff --git a/FemtoRV/RTL/CONFIGS/ice40hx8k_evb_config.v b/FemtoRV/RTL/CONFIGS/ice40hx8k_evb_config.v new file mode 100644 index 00000000..e7a0a63d --- /dev/null +++ b/FemtoRV/RTL/CONFIGS/ice40hx8k_evb_config.v @@ -0,0 +1,35 @@ +// Default femtosoc configuration file for ICE40HX8K-EVB + +/************************* Devices **********************************************************************************/ + +`define NRV_IO_LEDS // Mapped IO, LEDs D1,D2,D3,D4 (D5 is used to display errors) +`define NRV_IO_UART // Mapped IO, virtual UART (USB) +//`define NRV_IO_SSD1351 // Mapped IO, 128x128x64K OLED screen +//`define NRV_IO_MAX7219 // Mapped IO, 8x8 led matrix +`define NRV_MAPPED_SPI_FLASH // SPI flash mapped in address space. Can be used to run code from SPI flash. + +/************************* Processor configuration *******************************************************************/ + +`define NRV_FEMTORV32_QUARK +`define NRV_FREQ 40 +`define NRV_RESET_ADDR 32'h00830000 // Jump execution to SPI Flash (800000h, +192k(30000h) for FPGA bitstream) + // Maximum bitstream size is 136448 bytes and the firmware address is + // aligned to the block size 64KB, hence 192KB. +`define NRV_COUNTER_WIDTH 24 // Number of bits in cycles counter +`define NRV_TWOLEVEL_SHIFTER // Faster shifts + + +/************************* RAM (in bytes, needs to be a multiple of 4)***********************************************/ + +`define NRV_RAM 14336 + + +/************************* Advanced devices configuration ***********************************************************/ + +`define NRV_RUN_FROM_SPI_FLASH // Do not 'readmemh()' firmware from '.hex' file +`define NRV_IO_HARDWARE_CONFIG // Comment-out to disable hardware config registers mapped in IO-Space + // (note: firmware libfemtorv32 depends on it) + +/********************************************************************************************************************/ + +`define NRV_CONFIGURED diff --git a/FemtoRV/RTL/DEVICES/MappedSPIFlash.v b/FemtoRV/RTL/DEVICES/MappedSPIFlash.v index 8258982d..16d11a7a 100644 --- a/FemtoRV/RTL/DEVICES/MappedSPIFlash.v +++ b/FemtoRV/RTL/DEVICES/MappedSPIFlash.v @@ -45,6 +45,21 @@ `define SPI_FLASH_CONFIGURED `endif +/* + * TODO: There are two generations of the board, and each has a different flash. + * The datasheets for the respective generations: + * https://www.alldatasheet.com/datasheet-pdf/pdf/443790/WINBOND/W25Q16BVSSIG.html + * https://www.alldatasheet.com/datasheet-pdf/pdf/675618/EON/EN25Q16B.html + * Dual and quad IO modes are supported by the flashes, + * but the instruction format is different from e.g. Icestick's flash. + * The fast read mode is compatible with the existing implementation (only the + * second generation was tested though). + */ +`ifdef ICE40HX8K_EVB + `define SPI_FLASH_FAST_READ + `define SPI_FLASH_CONFIGURED +`endif + `ifdef ICE4PI `undef SPI_FLASH_FAST_READ_DUAL_IO `undef SPI_FLASH_CONFIGURED diff --git a/FemtoRV/RTL/PLL/femtopll.v b/FemtoRV/RTL/PLL/femtopll.v index 7ec6b8b4..57e0e241 100644 --- a/FemtoRV/RTL/PLL/femtopll.v +++ b/FemtoRV/RTL/PLL/femtopll.v @@ -40,6 +40,8 @@ endmodule `include "pll_arty.v" `elsif CMODA7 `include "pll_cmod_a7.v" + `elsif ICE40HX8K_EVB + `include "pll_ice40hx8k_evb.v" `endif `endif diff --git a/FemtoRV/RTL/PLL/gen_plls.sh b/FemtoRV/RTL/PLL/gen_plls.sh index 7153ac85..eb1d752c 100755 --- a/FemtoRV/RTL/PLL/gen_plls.sh +++ b/FemtoRV/RTL/PLL/gen_plls.sh @@ -15,4 +15,7 @@ echo Generating PLL for ULX3S ./gen_pll.sh ECP5 25 > pll_ulx3s.v echo Generating PLL for ECP5 evaluation board -./gen_pll.sh ECP5 12 > pll_ecp5_evn.v \ No newline at end of file +./gen_pll.sh ECP5 12 > pll_ecp5_evn.v + +echo Generating PLL for ICE40HX8K-EVB +./gen_pll.sh ICE40 100 > pll_ice40hx8k_evb.v diff --git a/FemtoRV/RTL/PLL/pll_ice40hx8k_evb.v b/FemtoRV/RTL/PLL/pll_ice40hx8k_evb.v new file mode 100644 index 00000000..64a6c53b --- /dev/null +++ b/FemtoRV/RTL/PLL/pll_ice40hx8k_evb.v @@ -0,0 +1,238 @@ +/* + * Do not edit this file, it was generated by gen_pll.sh + * + * FPGA kind : ICE40 + * Input frequency: 100 MHz + */ + + module femtoPLL #( + parameter freq = 40 + ) ( + input wire pclk, + output wire clk + ); + SB_PLL40_CORE pll ( + .REFERENCECLK(pclk), + .PLLOUTCORE(clk), + .RESETB(1'b1), + .BYPASS(1'b0) + ); + defparam pll.FEEDBACK_PATH="SIMPLE"; + defparam pll.PLLOUT_SELECT="GENCLK"; + generate + case(freq) + 16: begin + defparam pll.DIVR = 4'b0011; + defparam pll.DIVF = 7'b0101000; + defparam pll.DIVQ = 3'b110; + defparam pll.FILTER_RANGE = 3'b010; + end + 20: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011111; + defparam pll.DIVQ = 3'b101; + defparam pll.FILTER_RANGE = 3'b010; + end + 24: begin + defparam pll.DIVR = 4'b0010; + defparam pll.DIVF = 7'b0010110; + defparam pll.DIVQ = 3'b101; + defparam pll.FILTER_RANGE = 3'b011; + end + 25: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0000111; + defparam pll.DIVQ = 3'b101; + defparam pll.FILTER_RANGE = 3'b101; + end + 30: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101111; + defparam pll.DIVQ = 3'b101; + defparam pll.FILTER_RANGE = 3'b010; + end + 35: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011011; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b010; + end + 40: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011111; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b010; + end + 45: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100011; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b010; + end + 48: begin + defparam pll.DIVR = 4'b0010; + defparam pll.DIVF = 7'b0010110; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b011; + end + 50: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0000111; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b101; + end + 55: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101011; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b010; + end + 60: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101111; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b010; + end + 65: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0110011; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b010; + end + 66: begin + defparam pll.DIVR = 4'b1000; + defparam pll.DIVF = 7'b1011110; + defparam pll.DIVQ = 3'b100; + defparam pll.FILTER_RANGE = 3'b001; + end + 70: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011011; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 75: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0000101; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b101; + end + 80: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011111; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 85: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100001; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 90: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100011; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 95: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100101; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 100: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0000111; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b101; + end + 105: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101001; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 110: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101011; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 115: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101101; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 120: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0101111; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 125: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0001001; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b101; + end + 130: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0110011; + defparam pll.DIVQ = 3'b011; + defparam pll.FILTER_RANGE = 3'b010; + end + 135: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011010; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b010; + end + 140: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011011; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b010; + end + 150: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0000101; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b101; + end + 160: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0011111; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b010; + end + 170: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100001; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b010; + end + 180: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100011; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b010; + end + 190: begin + defparam pll.DIVR = 4'b0100; + defparam pll.DIVF = 7'b0100101; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b010; + end + 200: begin + defparam pll.DIVR = 4'b0000; + defparam pll.DIVF = 7'b0000111; + defparam pll.DIVQ = 3'b010; + defparam pll.FILTER_RANGE = 3'b101; + end + default: UNKNOWN_FREQUENCY unknown_frequency(); + endcase + endgenerate + +endmodule diff --git a/FemtoRV/RTL/femtosoc_config.v b/FemtoRV/RTL/femtosoc_config.v index 6ce68f9f..00092642 100644 --- a/FemtoRV/RTL/femtosoc_config.v +++ b/FemtoRV/RTL/femtosoc_config.v @@ -32,6 +32,10 @@ `include "CONFIGS/cmod_a7_config.v" `endif +`ifdef ICE40HX8K_EVB +`include "CONFIGS/ice40hx8k_evb_config.v" +`endif + `ifdef BENCH_VERILATOR `include "CONFIGS/bench_config.v" `endif @@ -107,6 +111,10 @@ `define ECP5 `endif +`ifdef ICE40HX8K_EVB + `define ICE40 +`endif + /******************************************************************************************************************/ /* Processor */ diff --git a/FemtoRV/RTL/get_config.v b/FemtoRV/RTL/get_config.v index 4c89f451..017919cb 100644 --- a/FemtoRV/RTL/get_config.v +++ b/FemtoRV/RTL/get_config.v @@ -47,6 +47,9 @@ initial begin `endif `ifdef ICE_SUGAR_NANO $write(" -DICE_SUGAR_NANO=1"); +`endif +`ifdef ICE40HX8K_EVB + $write(" -DICE40HX8K_EVB=1"); `endif $write("\n");