// ################################################################################################# // # << NEORV32 - Executable image generator tool >> # // # ********************************************************************************************* # // # BSD 3-Clause License # // # # // # Copyright (c) 2023, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # // # # // # 1. Redistributions of source code must retain the above copyright notice, this list of # // # conditions and the following disclaimer. # // # # // # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # // # conditions and the following disclaimer in the documentation and/or other materials # // # provided with the distribution. # // # # // # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # // # endorse or promote products derived from this software without specific prior written # // # permission. # // # # // # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # // # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # // # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # // # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # // # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # // # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # // # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # // # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # // # OF THE POSSIBILITY OF SUCH DAMAGE. # // # ********************************************************************************************* # // # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # // ################################################################################################# #include #include #include #include #include // 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}; int main(int argc, char *argv[]) { if ((argc != 4) && (argc != 5)) { printf("NEORV32 executable image generator\n" "Three arguments are required.\n" "1st: Operation\n" " -app_bin : Generate application executable binary (binary file, little-endian, with header) \n" " -app_img : Generate application raw executable memory image (vhdl package body file, no header)\n" " -raw_hex : Generate application raw executable (ASCII hex file, no header)\n" " -raw_bin : Generate application raw executable (binary file, no header)\n" " -bld_img : Generate bootloader raw executable memory image (vhdl package body file, no header)\n" "2nd: Input file (raw binary image)\n" "3rd: Output file\n" "4th: Project name or folder (optional)\n"); return 0; } FILE *input, *output; unsigned char buffer[4]; char tmp_string[1024]; uint32_t tmp = 0, size = 0, checksum = 0; unsigned int i = 0; int operation = 0; unsigned long raw_exe_size = 0; if (strcmp(argv[1], "-app_bin") == 0) { operation = OP_APP_BIN; } else if (strcmp(argv[1], "-app_img") == 0) { operation = OP_APP_IMG; } else if (strcmp(argv[1], "-bld_img") == 0) { operation = OP_BLD_IMG; } else if (strcmp(argv[1], "-raw_hex") == 0) { operation = OP_RAW_HEX; } else if (strcmp(argv[1], "-raw_bin") == 0) { operation = OP_RAW_BIN; } else { printf("Invalid operation!"); return -1; } // open input file input = fopen(argv[2], "rb"); if(input == NULL) { printf("Input file error!"); return -2; } // get input file size fseek(input, 0L, SEEK_END); unsigned int input_size = (unsigned int)ftell(input); unsigned int input_words = input_size / 4; rewind(input); // input file empty? if(input_size == 0) { printf("Input file is empty!"); fclose(input); return -3; } // open output file output = fopen(argv[3], "wb"); if(output == NULL) { printf("Output file error!"); fclose(input); return -4; } // -------------------------------------------------------------------------- // Try to find out targeted CPU configuration // via MARCH environment variable // -------------------------------------------------------------------------- char string_march[64] = "default"; char *envvar_march = "MARCH"; if (getenv(envvar_march)) { if (snprintf(string_march, 64, "%s", getenv(envvar_march)) >= 64){ strcpy(string_march, "default"); } } // -------------------------------------------------------------------------- // Get image's compilation date and time // -------------------------------------------------------------------------- time_t time_current; time(&time_current); struct tm *time_local = localtime(&time_current); char compile_time[64]; snprintf(compile_time, 64, "%02d.%02d.%d %02d:%02d:%02d", time_local->tm_mday, time_local->tm_mon + 1, time_local->tm_year + 1900, time_local->tm_hour, time_local->tm_min, time_local->tm_sec ); // -------------------------------------------------------------------------- // Get size of application (in bytes) // -------------------------------------------------------------------------- fseek(input, 0L, SEEK_END); // get file size (raw executable) raw_exe_size = (unsigned long)ftell(input); // go back to beginning rewind(input); // -------------------------------------------------------------------------- // Generate BINARY executable (with header!) for bootloader upload // -------------------------------------------------------------------------- if (operation == OP_APP_BIN) { // reserve header space for signature fputc(0, output); fputc(0, output); fputc(0, output); fputc(0, output); // reserve header space for size fputc(0, output); fputc(0, output); fputc(0, output); fputc(0, output); // reserve header space for checksum fputc(0, output); fputc(0, output); fputc(0, output); fputc(0, output); checksum = 0; size = 0; rewind(input); 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); checksum += tmp; // checksum: sum complement fputc(buffer[0], output); fputc(buffer[1], output); fputc(buffer[2], output); fputc(buffer[3], output); size += 4; } rewind(output); // header: signature fputc((unsigned char)((signature >> 0) & 0xFF), output); fputc((unsigned char)((signature >> 8) & 0xFF), output); fputc((unsigned char)((signature >> 16) & 0xFF), output); fputc((unsigned char)((signature >> 24) & 0xFF), output); // header: size fputc((unsigned char)((size >> 0) & 0xFF), output); fputc((unsigned char)((size >> 8) & 0xFF), output); fputc((unsigned char)((size >> 16) & 0xFF), output); fputc((unsigned char)((size >> 24) & 0xFF), output); // header: checksum (sum complement) checksum = (~checksum) + 1; fputc((unsigned char)((checksum >> 0) & 0xFF), output); fputc((unsigned char)((checksum >> 8) & 0xFF), output); fputc((unsigned char)((checksum >> 16) & 0xFF), output); fputc((unsigned char)((checksum >> 24) & 0xFF), output); } // -------------------------------------------------------------------------- // Generate APPLICATION's executable memory initialization file (no header!) // => VHDL package body // -------------------------------------------------------------------------- if (operation == OP_APP_IMG) { // header sprintf(tmp_string, "-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32\n" "-- Auto-generated memory initialization file (for APPLICATION) from source file <%s/%s>\n" "-- Size: %lu bytes\n" "-- MARCH: %s\n" "-- Built: %s\n" "\n" "-- prototype defined in 'neorv32_package.vhd'\n" "package body neorv32_application_image is\n" "\n" "constant application_init_image : mem32_t := (\n", argv[4], argv[2], raw_exe_size, string_march, compile_time); fputs(tmp_string, output); i = 0; while (i < (input_words-1)) { if (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, "x\"%08x\",\n", (unsigned int)tmp); fputs(tmp_string, output); i++; } else { printf("Unexpected input file end!\n"); break; } } if (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, "x\"%08x\"\n", (unsigned int)tmp); fputs(tmp_string, output); i++; } else { printf("Unexpected input file end!\n"); } // end sprintf(tmp_string, ");\n" "\n" "end neorv32_application_image;\n"); fputs(tmp_string, output); } // -------------------------------------------------------------------------- // Generate BOOTLOADER's executable memory initialization file (no header!) // => VHDL package body // -------------------------------------------------------------------------- if (operation == OP_BLD_IMG) { // header sprintf(tmp_string, "-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32\n" "-- Auto-generated memory initialization file (for BOOTLOADER) from source file <%s/%s>\n" "-- Size: %lu bytes\n" "-- MARCH: %s\n" "-- Built: %s\n" "\n" "-- prototype defined in 'neorv32_package.vhd'\n" "package body neorv32_bootloader_image is\n" "\n" "constant bootloader_init_image : mem32_t := (\n", argv[4], argv[2], raw_exe_size, string_march, compile_time); fputs(tmp_string, output); i = 0; while (i < (input_words-1)) { if (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, "x\"%08x\",\n", (unsigned int)tmp); fputs(tmp_string, output); i++; } else { printf("Unexpected input file end!\n"); break; } } if (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, "x\"%08x\"\n", (unsigned int)tmp); fputs(tmp_string, output); i++; } else { printf("Unexpected input file end!\n"); } // end sprintf(tmp_string, ");\n" "\n" "end neorv32_bootloader_image;\n"); fputs(tmp_string, output); } // -------------------------------------------------------------------------- // Generate raw APPLICATION's executable ASCII hex file (no header!) // -------------------------------------------------------------------------- if (operation == OP_RAW_HEX) { 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\n", (unsigned int)tmp); fputs(tmp_string, output); } } // -------------------------------------------------------------------------- // Generate raw APPLICATION's executable binary file (no header!) // -------------------------------------------------------------------------- if (operation == OP_RAW_BIN) { while(fread(&buffer, sizeof(unsigned char), 1, input) != 0) { fputc(buffer[0], output); } } // -------------------------------------------------------------------------- // Done, clean up // -------------------------------------------------------------------------- fclose(input); fclose(output); return 0; }