diff --git a/.gitignore b/.gitignore index 0a75d3568..f2b46f035 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__ *.bin *.coe *.mem +*.mif *.o *.elf *.asm diff --git a/README.md b/README.md index 477171336..f05b42869 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,7 @@ This overview provides some *quick links* to the most important sections of the * [Software Framework Documentation](https://stnolting.github.io/neorv32/sw/files.html) - _doxygen_-based * [Application Makefile](https://stnolting.github.io/neorv32/#_application_makefile) - turning _your_ application into an executable * [Bootloader](https://stnolting.github.io/neorv32/#_bootloader) - the build-in NEORV32 bootloader +* [Image Generator](https://stnolting.github.io/neorv32/#_executable_image_format) - create (FPGA) memory initialization files from your application ### :rocket: [User Guide](https://stnolting.github.io/neorv32/ug/) - Getting Started! diff --git a/docs/datasheet/software.adoc b/docs/datasheet/software.adoc index 0b7271fff..b40a34a4d 100644 --- a/docs/datasheet/software.adoc +++ b/docs/datasheet/software.adoc @@ -163,10 +163,12 @@ Targets: gdb - run GNU debugging session asm - compile and generate assembly listing file for manual debugging elf - compile and generate ELF file - bin - compile and generate RAW executable file (binary file, no header) - hex - compile and generate RAW executable file (hex char file, no header) - coe - compile and generate > RAW executable file (COE file, no header) - mem - compile and generate > RAW executable file (MEM file, no header) + exe - compile and generate executable image file for upload via default bootloader (binary file) + bin - compile and generate RAW executable memory image (binary file) + hex - compile and generate RAW executable memory image (hex char file) + coe - compile and generate RAW executable memory image (COE file) + mem - compile and generate RAW executable memory image (MEM file) + mif - compile and generate RAW executable memory image (MIF file) image - compile and generate VHDL IMEM boot image (for application, no header) in local folder install - compile, generate and install VHDL IMEM boot image (for application, no header) sim - in-console simulation using default/simple testbench and GHDL @@ -288,7 +290,7 @@ The makefile's `CC_OPTS` is exported as **define** to be available within a C pr | `-g` | Include debugging information/symbols in ELF. | `-mstrict-align` | Unaligned memory accesses cannot be resolved by the hardware and require emulation. | `-mbranch-cost=10` | Branching costs a lot of cycles. -| `-ffp-contract=off` | Do not allow contraction of floatind-point operations (no fused operations as they are not supported). +| `-ffp-contract=off` | Do not allow contraction of floating-point operations (no fused operations as they are not supported). |======================= :sectnums: @@ -321,15 +323,23 @@ override USER_FLAGS += "-g -Wl,--__neorv32_heap_size,__heap_size=4096" === Executable Image Format In order to generate an executable for the processors all source files have to be compiled, linked -and packed into a final executable. +and packed into a final executable. This executable can be further converted into several image formats. + +.Memory Image Formats +[TIP] +The NEORV32 software framework includes an <<_executable_image_generator>> than can convert an application +into several different file formats. These include raw hex files, a proprietary format for uploading via the +default <<_bootloader>> as well as several standard FPGA memory initialization file types (e.g. `*.coe`, +`*.mem` and `*.mif`). These image file formats are generated by the according <<_makefile_targets>>. + :sectnums: ==== Linker Script After all the application sources have been compiled, they need to be _linked_. -For this purpose the makefile uses the NEORV32-specific linker scThe linker script defines several sections +For this purpose the makefile uses the NEORV32-specific linker script. This linker script defines several sections for the final executable (compare with <<_address_space>>). However, only the `ram` and `rom` sections are -relevant for the executable itself; the raining sections are just listed fro completeness. +relevant for the executable itself; the remaining sections are just listed for completeness. .Linker script - memory sections [cols="<2,<8"] @@ -486,21 +496,21 @@ The image generator can generate several types of executable file formats select [cols="<2,<8"] [grid="none"] |======================= -| `-app_bin` | Generates an executable binary file `neorv32_exe.bin` (including header) for UART uploading via the bootloader. -| `-app_img` | Generates an executable VHDL memory initialization image (no header) for the processor-internal IMEM. This option generates the `rtl/core/neorv32_application_image.vhd` file. -| `-bld_img` | Generates an executable VHDL memory initialization image (no header) for the processor-internal BOOT ROM. This option generates the `rtl/core/neorv32_bootloader_image.vhd` file. -| `-raw_hex` | Generates a plain 8x ASCII hex-char file `neorv32_raw_exe.hex` (no header) for custom purpose. -| `-raw_bin` | Generates a plain binary file `neorv32_raw_exe.bin` (no header) for custom purpose. -| `-raw_coe` | Generates a COE file `neorv32_raw_exe.coe` (no header) for FPGA memory initialization. -| `-raw_mem` | Generates a MEM file `neorv32_raw_exe.mem` (no header) for FPGA memory initialization. +| `-app_bin` | Generates an executable binary file `neorv32_exe.bin` (including bootloader header) for uploading via the bootloader. +| `-app_img` | Generates an executable VHDL memory initialization image for the processor-internal IMEM. This option regenerates the `rtl/core/neorv32_application_image.vhd` file. +| `-bld_img` | Generates an executable VHDL memory initialization image for the processor-internal BOOT ROM. This option regenerates the `rtl/core/neorv32_bootloader_image.vhd` file. +| `-raw_hex` | Generates a raw 8x ASCII hex-char file `neorv32_raw_exe.hex` for custom purpose. +| `-raw_bin` | Generates a raw binary file `neorv32_raw_exe.bin` for custom purpose. +| `-raw_coe` | Generates a raw COE file `neorv32_raw_exe.coe` for FPGA memory initialization. +| `-raw_mem` | Generates a raw MEM file `neorv32_raw_exe.mem` for FPGA memory initialization. +| `-raw_mif` | Generates a raw MIF file `neorv32_raw_exe.mif` for FPGA memory initialization. |======================= -All these options are managed by the makefile. The normal application compilation flow will generate the `neorv32_exe.bin` -executable designated for uploading via the default NEORV32 bootloader. +**All these options are managed by the makefile (see <<_makefile_targets>>).** .Image Generator Compilation [NOTE] -The sources of the image generator are automatically compiled when invoking the makefile (requiring a native GCC installation). +The sources of the image generator are automatically compiled when invoking the makefile (requiring a _native_ GCC installation). .Executable Header [NOTE] diff --git a/sw/common/common.mk b/sw/common/common.mk index dd0a45684..17fcdc0d0 100644 --- a/sw/common/common.mk +++ b/sw/common/common.mk @@ -77,6 +77,7 @@ APP_HEX = neorv32_raw_exe.hex APP_BIN = neorv32_raw_exe.bin APP_COE = neorv32_raw_exe.coe APP_MEM = neorv32_raw_exe.mem +APP_MIF = neorv32_raw_exe.mif APP_ASM = main.asm APP_IMG = neorv32_application_image.vhd BOOT_IMG = neorv32_bootloader_image.vhd @@ -142,9 +143,10 @@ hex: $(APP_HEX) bin: $(APP_BIN) coe: $(APP_COE) mem: $(APP_MEM) +mif: $(APP_MIF) image: $(APP_IMG) install: image install-$(APP_IMG) -all: $(APP_ASM) $(APP_EXE) $(APP_HEX) $(APP_BIN) $(APP_COE) $(APP_MEM) $(APP_IMG) install hex bin +all: $(APP_ASM) $(APP_EXE) $(APP_HEX) $(APP_BIN) $(APP_COE) $(APP_MEM) $(APP_MIF) $(APP_IMG) install hex bin # Check if making bootloader # Use different base address and length for instruction memory/"rom" (BOOTROM instead of IMEM) @@ -236,6 +238,11 @@ $(APP_COE): main.bin $(IMAGE_GEN) @set -e @$(IMAGE_GEN) -raw_coe $< $@ $(shell basename $(CURDIR)) +# Generate NEORV32 RAW executable image in MIF format +$(APP_MIF): main.bin $(IMAGE_GEN) + @set -e + @$(IMAGE_GEN) -raw_mif $< $@ $(shell basename $(CURDIR)) + # Generate NEORV32 RAW executable image in MEM format $(APP_MEM): main.bin $(IMAGE_GEN) @set -e @@ -314,7 +321,7 @@ gdb: # Clean up # ----------------------------------------------------------------------------- clean: - @rm -f *.elf *.o *.bin *.coe *.mem *.out *.asm *.vhd *.hex .gdb_history + @rm -f *.elf *.o *.bin *.coe *.mem *.mif *.out *.asm *.vhd *.hex .gdb_history clean_all: clean @rm -f $(OBJ) $(IMAGE_GEN) @@ -372,11 +379,12 @@ help: @echo " gdb - run GNU debugging session" @echo " asm - compile and generate <$(APP_ASM)> assembly listing file for manual debugging" @echo " elf - compile and generate <$(APP_ELF)> ELF file" - @echo " exe - compile and generate <$(APP_EXE)> executable for upload via default bootloader (binary file, with header)" - @echo " bin - compile and generate <$(APP_BIN)> RAW executable file (binary file, no header)" - @echo " hex - compile and generate <$(APP_HEX)> RAW executable file (hex char file, no header)" - @echo " coe - compile and generate <$(APP_COE)> RAW executable file (COE file, no header)" - @echo " mem - compile and generate <$(APP_MEM)> RAW executable file (MEM file, no header)" + @echo " exe - compile and generate <$(APP_EXE)> executable image file for upload via default bootloader (binary file)" + @echo " bin - compile and generate <$(APP_BIN)> RAW executable memory image (binary file)" + @echo " hex - compile and generate <$(APP_HEX)> RAW executable memory image (hex char file)" + @echo " coe - compile and generate <$(APP_COE)> RAW executable memory image (COE file)" + @echo " mem - compile and generate <$(APP_MEM)> RAW executable memory image (MEM file)" + @echo " mif - compile and generate <$(APP_MIF)> RAW executable memory image (MIF file)" @echo " image - compile and generate VHDL IMEM boot image (for application, no header) in local folder" @echo " install - compile, generate and install VHDL IMEM boot image (for application, no header)" @echo " sim - in-console simulation using default/simple testbench and GHDL" diff --git a/sw/image_gen/image_gen.c b/sw/image_gen/image_gen.c index 476102642..d904fd7c1 100644 --- a/sw/image_gen/image_gen.c +++ b/sw/image_gen/image_gen.c @@ -17,7 +17,17 @@ // executable signature ("magic word") const uint32_t signature = 0x4788CAFE; -enum operation_enum {OP_APP_BIN, OP_APP_IMG, OP_BLD_IMG, OP_RAW_HEX, OP_RAW_BIN, OP_RAW_COE, OP_RAW_MEM}; +// output file types (operation select) +enum operation_enum { + OP_APP_BIN, + OP_APP_IMG, + OP_BLD_IMG, + OP_RAW_HEX, + OP_RAW_BIN, + OP_RAW_COE, + OP_RAW_MEM, + OP_RAW_MIF +}; int main(int argc, char *argv[]) { @@ -32,6 +42,7 @@ int main(int argc, char *argv[]) { " -raw_bin : Generate application raw executable (binary file, no header)\n" " -raw_coe : Generate application raw executable (COE file, no header)\n" " -raw_mem : Generate application raw executable (MEM file, no header)\n" + " -raw_mif : Generate application raw executable (MIF file, no header)\n" "2nd: Input file (raw binary image)\n" "3rd: Output file\n" "4th: Project name or folder (optional)\n"); @@ -53,6 +64,7 @@ int main(int argc, char *argv[]) { else if (strcmp(argv[1], "-raw_bin") == 0) { operation = OP_RAW_BIN; } else if (strcmp(argv[1], "-raw_coe") == 0) { operation = OP_RAW_COE; } else if (strcmp(argv[1], "-raw_mem") == 0) { operation = OP_RAW_MEM; } + else if (strcmp(argv[1], "-raw_mif") == 0) { operation = OP_RAW_MIF; } else { printf("Invalid operation!"); return -1; @@ -87,8 +99,7 @@ int main(int argc, char *argv[]) { } // -------------------------------------------------------------------------- - // Try to find out targeted CPU configuration - // via MARCH environment variable + // Try to find out targeted CPU configuration via MARCH environment variable // -------------------------------------------------------------------------- char string_march[64] = "default"; char *envvar_march = "MARCH"; @@ -130,7 +141,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate BINARY executable (with header) for bootloader upload + // Generate BINARY executable for bootloader upload (with header) // -------------------------------------------------------------------------- if (operation == OP_APP_BIN) { @@ -189,7 +200,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate APPLICATION's executable memory initialization file (no header) + // Generate RAW APPLICATION's executable memory initialization file // -> VHDL package body // -------------------------------------------------------------------------- else if (operation == OP_APP_IMG) { @@ -246,7 +257,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate BOOTLOADER's executable memory initialization file (no header) + // Generate RAW BOOTLOADER's executable memory initialization file // -> VHDL package body // -------------------------------------------------------------------------- else if (operation == OP_BLD_IMG) { @@ -303,7 +314,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate raw APPLICATION's executable ASCII hex file (no header) + // Generate RAW APPLICATION's executable ASCII hex file // -------------------------------------------------------------------------- else if (operation == OP_RAW_HEX) { @@ -319,7 +330,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate raw APPLICATION's executable binary file (no header) + // Generate RAW APPLICATION's executable binary file // -------------------------------------------------------------------------- else if (operation == OP_RAW_BIN) { @@ -330,7 +341,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate raw APPLICATION's executable COE file (no header) + // Generate RAW APPLICATION's executable COE file // -------------------------------------------------------------------------- else if (operation == OP_RAW_COE) { @@ -363,7 +374,7 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- - // Generate raw APPLICATION's executable MEM file (no header) + // Generate RAW APPLICATION's executable MEM file // -------------------------------------------------------------------------- else if (operation == OP_RAW_MEM) { @@ -380,6 +391,42 @@ int main(int argc, char *argv[]) { } + // -------------------------------------------------------------------------- + // Generate RAW APPLICATION's executable MIF file + // -------------------------------------------------------------------------- + else if (operation == OP_RAW_MIF) { + + // header + sprintf(tmp_string, "DEPTH = %lu;\n", raw_exe_size/4); // memory depth in words + fputs(tmp_string, output); + sprintf(tmp_string, "WIDTH = 32;\n"); // bits per data word + fputs(tmp_string, output); + sprintf(tmp_string, "ADDRESS_RADIX = HEX;\n"); // hexadecimal address format + fputs(tmp_string, output); + sprintf(tmp_string, "DATA_RADIX = HEX;\n"); // hexadecimal data format + fputs(tmp_string, output); + + sprintf(tmp_string, "CONTENT\n"); + fputs(tmp_string, output); + sprintf(tmp_string, "BEGIN\n"); + fputs(tmp_string, output); + i = 0; + while(fread(&buffer, sizeof(unsigned char), 4, input) != 0) { + tmp = (uint32_t)(buffer[0] << 0); + tmp |= (uint32_t)(buffer[1] << 8); + tmp |= (uint32_t)(buffer[2] << 16); + tmp |= (uint32_t)(buffer[3] << 24); + sprintf(tmp_string, "%08x : %08x;\n", (unsigned int)i, (unsigned int)tmp); + fputs(tmp_string, output); + i++; + } + + // footer + sprintf(tmp_string, "END;\n"); + fputs(tmp_string, output); + } + + // -------------------------------------------------------------------------- // Invalid operation // -------------------------------------------------------------------------- @@ -394,7 +441,6 @@ int main(int argc, char *argv[]) { // -------------------------------------------------------------------------- // Done, clean up // -------------------------------------------------------------------------- - fclose(input); fclose(output);