| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "verilator_memutil.h" |
| |
| #include <array> |
| #include <cassert> |
| #include <cstring> |
| #include <getopt.h> |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| namespace { |
| // An instruction to load the file at filepath to the memory called name. If |
| // name is the empty string then type must be kMemImageElf and this is an |
| // instruction to load an ELF file, picking memories by LMA. |
| struct LoadArg { |
| std::string name; |
| std::string filepath; |
| MemImageType type; |
| }; |
| } // namespace |
| |
| // Parse a meminit command-line argument. This should be of the form |
| // mem_area,file[,type]. Throw a std::runtime_error if something looks wrong. |
| static LoadArg ParseMemArg(std::string mem_argument) { |
| std::array<std::string, 3> args; |
| size_t pos = 0; |
| size_t end_pos = 0; |
| size_t i; |
| |
| for (i = 0; i < 3; ++i) { |
| end_pos = mem_argument.find(",", pos); |
| // Check for possible exit conditions |
| if (pos == end_pos) { |
| std::ostringstream oss; |
| oss << "empty field in: `" << mem_argument << "'."; |
| throw std::runtime_error(oss.str()); |
| } |
| if (end_pos == std::string::npos) { |
| args[i] = mem_argument.substr(pos); |
| break; |
| } |
| args[i] = mem_argument.substr(pos, end_pos - pos); |
| pos = end_pos + 1; |
| } |
| // mem_argument is not empty as getopt requires an argument, |
| // but not a valid argument for memory initialization |
| if (i == 0) { |
| std::ostringstream oss; |
| oss << "meminit must be in the format `name,file[,type]'. Got: `" |
| << mem_argument << "'."; |
| throw std::runtime_error(oss.str()); |
| } |
| |
| const char *str_type = (2 <= i) ? args[2].c_str() : nullptr; |
| MemImageType type = DpiMemUtil::GetMemImageType(args[1], str_type); |
| |
| return {.name = args[0], .filepath = args[1], .type = type}; |
| } |
| |
| // Print a usage message to stdout |
| static void PrintHelp() { |
| std::cout << "Simulation memory utilities:\n\n" |
| "-r|--rominit=FILE\n" |
| " Initialize the ROM with FILE (elf/vmem)\n\n" |
| "-m|--raminit=FILE\n" |
| " Initialize the RAM with FILE (elf/vmem)\n\n" |
| "-f|--flashinit=FILE\n" |
| " Initialize the FLASH with FILE (elf/vmem)\n\n" |
| "-l|--meminit=NAME,FILE[,TYPE]\n" |
| " Initialize memory region NAME with FILE [of TYPE]\n" |
| " TYPE is either 'elf' or 'vmem'\n\n" |
| "-E|--load-elf=FILE\n" |
| " Load ELF file, using segment LMAs to pick memory regions\n\n" |
| "-l list|--meminit=list\n" |
| " Print registered memory regions\n\n" |
| "--verbose-mem-load\n" |
| " Print a message for each memory load\n\n" |
| "-h|--help\n" |
| " Show help\n\n"; |
| } |
| |
| VerilatorMemUtil::VerilatorMemUtil() : allocation_(new DpiMemUtil()) { |
| mem_util_ = allocation_.get(); |
| } |
| |
| VerilatorMemUtil::VerilatorMemUtil(DpiMemUtil *mem_util) : mem_util_(mem_util) { |
| assert(mem_util); |
| } |
| |
| bool VerilatorMemUtil::ParseCLIArguments(int argc, char **argv, |
| bool &exit_app) { |
| const struct option long_options[] = { |
| {"rominit", required_argument, nullptr, 'r'}, |
| {"raminit", required_argument, nullptr, 'm'}, |
| {"flashinit", required_argument, nullptr, 'f'}, |
| {"otpinit", required_argument, nullptr, 'o'}, |
| {"meminit", required_argument, nullptr, 'l'}, |
| {"verbose-mem-load", no_argument, nullptr, 'V'}, |
| {"load-elf", required_argument, nullptr, 'E'}, |
| {"help", no_argument, nullptr, 'h'}, |
| {nullptr, no_argument, nullptr, 0}}; |
| |
| std::vector<LoadArg> load_args; |
| bool verbose = false; |
| |
| // Reset the command parsing index in-case other utils have already parsed |
| // some arguments |
| optind = 1; |
| while (1) { |
| int c = getopt_long(argc, argv, "-:r:m:f:l:E:h", long_options, nullptr); |
| if (c == -1) { |
| break; |
| } |
| |
| // Disable error reporting by getopt |
| opterr = 0; |
| |
| switch (c) { |
| case 0: |
| case 1: |
| break; |
| case 'r': |
| load_args.push_back( |
| {.name = "rom", .filepath = optarg, .type = kMemImageUnknown}); |
| break; |
| case 'm': |
| load_args.push_back( |
| {.name = "ram", .filepath = optarg, .type = kMemImageUnknown}); |
| break; |
| case 'f': |
| load_args.push_back( |
| {.name = "flash", .filepath = optarg, .type = kMemImageUnknown}); |
| break; |
| case 'o': |
| load_args.push_back( |
| {.name = "otp", .filepath = optarg, .type = kMemImageUnknown}); |
| break; |
| case 'l': |
| if (strcasecmp(optarg, "list") == 0) { |
| mem_util_->PrintMemRegions(); |
| exit_app = true; |
| return true; |
| } |
| |
| // --meminit / -l |
| try { |
| load_args.emplace_back(ParseMemArg(optarg)); |
| } catch (const std::runtime_error &err) { |
| std::cerr << "ERROR: " << err.what() << std::endl; |
| return false; |
| } |
| break; |
| case 'V': |
| verbose = true; |
| break; |
| case 'E': |
| load_args.push_back( |
| {.name = "", .filepath = optarg, .type = kMemImageElf}); |
| break; |
| case 'h': |
| PrintHelp(); |
| return true; |
| case ':': // missing argument |
| std::cerr << "ERROR: Missing argument." << std::endl << std::endl; |
| return false; |
| case '?': |
| default:; |
| // Ignore unrecognized options since they might be consumed by |
| // other utils |
| } |
| } |
| |
| for (const LoadArg &arg : load_args) { |
| try { |
| if (!arg.name.empty()) { |
| mem_util_->LoadFileToNamedMem(verbose, arg.name, arg.filepath, |
| arg.type); |
| } else { |
| assert(arg.type == kMemImageElf); |
| mem_util_->LoadElfToMemories(verbose, arg.filepath); |
| } |
| } catch (const std::exception &err) { |
| std::cerr << "ERROR: " << err.what() << std::endl; |
| return false; |
| } |
| } |
| |
| return true; |
| } |