blob: 66ee5d7a7fab07267f84eec69285f4be9f8fcfeb [file] [log] [blame]
// 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;
}