mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 04:57:25 -04:00
[DV] Add Verilator memory initialization from ELF
Support initialization of memory by loading content from ELF files. All segments of the ELF file with the type `PT_LOAD` are merged into a temporary buffer and then forwarded to a predefined memory. This is an addition for setting the memories with VMEM files. Memories must implement `simutil_verilator_set_mem` to support the setting of values with a width of 32 bits. A return value of 0 must indicate a successful operation and 1 an error. Memories are defined by a call to `RegisterMemoryArea()` before the execution of the simulation, at which point the arguments are parsed and the initialization is started. The memories are identified by unique name. The design specific location is used to set the SystemVerilog scope. Registered memories can be printed by using `-l list`. The unique name is used for `-l name,file.elf` together with the file path. An optional part of the argument is the type of the file, `elf` or `vmem`, and if not provided it is attempted to detect the type by looking at the file extension. The memory specific arguments which already existed accept now also ELF files. They use predefined names and are included to keep the interface stable. Contents of an ELF segment which has a bigger memory size than file size are not set. This is typically required for BSS sections for zero-ing the memory.
This commit is contained in:
parent
11749c7e4d
commit
ce7e38351e
11 changed files with 516 additions and 229 deletions
|
@ -45,6 +45,7 @@ jobs:
|
|||
flex \
|
||||
bison \
|
||||
curl \
|
||||
libelf-dev \
|
||||
&& sudo pip3 install -U six fusesoc
|
||||
displayName: Install dependencies
|
||||
|
||||
|
|
|
@ -2,36 +2,20 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "Vtb_cs_registers.h"
|
||||
#include "verilated_toplevel.h"
|
||||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
tb_cs_registers *top;
|
||||
VerilatorSimCtrl *simctrl;
|
||||
|
||||
// dummy definition since this DPI call doesn't exist
|
||||
// TODO : remove this - see Ibex #317
|
||||
extern "C" {
|
||||
void simutil_verilator_memload(const char *file) {}
|
||||
int simutil_verilator_set_mem(int index, const svLogicVecVal *val) { return 1; }
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int retcode = 0;
|
||||
// init top verilog instance
|
||||
top = new tb_cs_registers;
|
||||
// Create SimCtrl instance
|
||||
simctrl = new VerilatorSimCtrl(top, top->clk_i, top->in_rst_ni,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
tb_cs_registers top;
|
||||
VerilatorSimCtrl simctrl(top, top.clk_i, top.in_rst_ni,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
|
||||
// Setup the simulation
|
||||
retcode = simctrl->SetupSimulation(argc, argv);
|
||||
|
||||
if (!retcode) {
|
||||
// Run the simulation
|
||||
simctrl->RunSimulation();
|
||||
}
|
||||
|
||||
delete top;
|
||||
delete simctrl;
|
||||
return retcode;
|
||||
return simctrl.Exec(argc, argv);
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ targets:
|
|||
- '--trace-params'
|
||||
- '--trace-max-array 1024'
|
||||
- '-CFLAGS "-std=c++14 -Wall -DTOPLEVEL_NAME=tb_cs_registers -DVM_TRACE_FMT_FST -g"'
|
||||
- '-LDFLAGS "-pthread -lutil"'
|
||||
- '-LDFLAGS "-pthread -lutil -lelf"'
|
||||
- "-Wall"
|
||||
- "-Wno-PINCONNECTEMPTY"
|
||||
# XXX: Cleanup all warnings and remove this option
|
||||
|
|
|
@ -2,36 +2,15 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "verilated_toplevel.h"
|
||||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
ibex_riscv_compliance *top;
|
||||
VerilatorSimCtrl *simctrl;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int retcode;
|
||||
top = new ibex_riscv_compliance;
|
||||
simctrl = new VerilatorSimCtrl(top, top->IO_CLK, top->IO_RST_N,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
ibex_riscv_compliance top;
|
||||
VerilatorSimCtrl simctrl(top, top.IO_CLK, top.IO_RST_N,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
|
||||
// Setup simctrl
|
||||
retcode = simctrl->SetupSimulation(argc, argv);
|
||||
if (retcode != 0) {
|
||||
goto free_return;
|
||||
}
|
||||
simctrl.RegisterMemoryArea("ram", "TOP.ibex_riscv_compliance.u_ram");
|
||||
|
||||
// Initialize RAM
|
||||
simctrl->InitRam("TOP.ibex_riscv_compliance.u_ram");
|
||||
|
||||
// Run the simulation
|
||||
simctrl->RunSimulation();
|
||||
|
||||
free_return:
|
||||
delete top;
|
||||
delete simctrl;
|
||||
return retcode;
|
||||
return simctrl.Exec(argc, argv);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ targets:
|
|||
- '--trace-params'
|
||||
- '--trace-max-array 1024'
|
||||
- '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=ibex_riscv_compliance -g"'
|
||||
- '-LDFLAGS "-pthread -lutil"'
|
||||
- '-LDFLAGS "-pthread -lutil -lelf"'
|
||||
- "-Wall"
|
||||
- "-Wno-PINCONNECTEMPTY"
|
||||
# XXX: Cleanup all warnings and remove this option
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
TOPLEVEL_NAME &VerilatedToplevel::dut() {
|
||||
// The static_cast below is generally unsafe, but we know the types involved.
|
||||
// It's safe for these.
|
||||
TOPLEVEL_NAME &dut = static_cast<TOPLEVEL_NAME&>(*this);
|
||||
TOPLEVEL_NAME &dut = static_cast<TOPLEVEL_NAME &>(*this);
|
||||
return dut;
|
||||
}
|
||||
|
|
|
@ -4,22 +4,31 @@
|
|||
|
||||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <getopt.h>
|
||||
#include <libelf.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <vltstd/svdpi.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
// This is defined by Verilator and passed through the command line
|
||||
#ifndef VM_TRACE
|
||||
#define VM_TRACE 0
|
||||
#endif
|
||||
|
||||
// Static pointer to a single simctrl instance
|
||||
// used by SignalHandler
|
||||
static VerilatorSimCtrl *simctrl;
|
||||
struct BufferDesc {
|
||||
uint8_t *data;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
// Static pointer to a single simctrl instance used by SignalHandler
|
||||
static VerilatorSimCtrl *simctrl = nullptr;
|
||||
|
||||
static void SignalHandler(int sig) {
|
||||
if (!simctrl) {
|
||||
|
@ -56,18 +65,16 @@ double sc_time_stamp() {
|
|||
// DPI Exports
|
||||
extern "C" {
|
||||
extern void simutil_verilator_memload(const char *file);
|
||||
extern int simutil_verilator_set_mem(int index, const svLogicVecVal *val);
|
||||
}
|
||||
|
||||
VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel *top, CData &sig_clk,
|
||||
VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel &top, CData &sig_clk,
|
||||
CData &sig_rst, VerilatorSimCtrlFlags flags)
|
||||
: top_(top),
|
||||
sig_clk_(sig_clk),
|
||||
sig_rst_(sig_rst),
|
||||
flags_(flags),
|
||||
time_(0),
|
||||
init_rom_(false),
|
||||
init_ram_(false),
|
||||
init_flash_(false),
|
||||
tracing_enabled_(false),
|
||||
tracing_enabled_changed_(false),
|
||||
tracing_ever_enabled_(false),
|
||||
|
@ -80,13 +87,22 @@ VerilatorSimCtrl::VerilatorSimCtrl(VerilatedToplevel *top, CData &sig_clk,
|
|||
term_after_cycles_(0),
|
||||
callback_(nullptr) {}
|
||||
|
||||
int VerilatorSimCtrl::SetupSimulation(int argc, char **argv) {
|
||||
int retval;
|
||||
// Setup the signal handler for this instance
|
||||
int VerilatorSimCtrl::Exec(int argc, char **argv) {
|
||||
RegisterSignalHandler();
|
||||
// Parse the command line argumanets
|
||||
if (!ParseCommandArgs(argc,argv,retval)) {
|
||||
return retval;
|
||||
|
||||
bool exit_app = false;
|
||||
if (!ParseCommandArgs(argc, argv, exit_app)) {
|
||||
return 1;
|
||||
}
|
||||
if (exit_app) {
|
||||
// Successful exit requested by command argument parsing
|
||||
return 0;
|
||||
}
|
||||
|
||||
RunSimulation();
|
||||
|
||||
if (!WasSimulationSuccessful()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -97,7 +113,6 @@ void VerilatorSimCtrl::RunSimulation() {
|
|||
std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:"
|
||||
<< std::endl
|
||||
<< "$ kill -USR1 " << getpid() << std::endl;
|
||||
|
||||
}
|
||||
// Run the simulation
|
||||
Run();
|
||||
|
@ -107,7 +122,7 @@ void VerilatorSimCtrl::RunSimulation() {
|
|||
if (TracingEverEnabled()) {
|
||||
std::cout << std::endl
|
||||
<< "You can view the simulation traces by calling" << std::endl
|
||||
<< "$ gtkwave " << GetSimulationFileName() << std::endl;
|
||||
<< "$ gtkwave " << GetTraceFileName() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,98 +166,241 @@ bool VerilatorSimCtrl::TraceOff() {
|
|||
}
|
||||
|
||||
void VerilatorSimCtrl::PrintHelp() const {
|
||||
std::cout << "Execute a simulation model for " << top_->name()
|
||||
std::cout << "Execute a simulation model for " << top_.name()
|
||||
<< "\n"
|
||||
"\n";
|
||||
if (tracing_possible_) {
|
||||
std::cout << "-t|--trace Write trace file from the start\n";
|
||||
std::cout << "-t|--trace\n"
|
||||
" Write a trace file from the start\n\n";
|
||||
}
|
||||
std::cout << "-r|--rominit=VMEMFILE Initialize the ROM with VMEMFILE\n"
|
||||
"-m|--raminit=VMEMFILE Initialize the RAM with VMEMFILE\n"
|
||||
"-f|--flashinit=VMEMFILE Initialize the FLASH with VMEMFILE\n"
|
||||
"-c|--term-after-cycles=N Terminate simulation after N cycles\n"
|
||||
"-h|--help Show help\n"
|
||||
"\n"
|
||||
std::cout << "-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"
|
||||
"-l list|--meminit=list\n"
|
||||
" Print registered memory regions\n\n"
|
||||
"-c|--term-after-cycles=N\n"
|
||||
" Terminate simulation after N cycles\n\n"
|
||||
"-h|--help\n"
|
||||
" Show help\n\n"
|
||||
"All further arguments are passed to the design and can be used "
|
||||
"in the \n"
|
||||
"design, e.g. by DPI modules.\n";
|
||||
"in the design, e.g. by DPI modules.\n";
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::InitRom(std::string rom) {
|
||||
if (!init_rom_) {
|
||||
return;
|
||||
bool VerilatorSimCtrl::RegisterMemoryArea(const std::string name,
|
||||
const std::string location) {
|
||||
MemArea mem = {.name = name, .location = location};
|
||||
|
||||
auto ret = mem_register_.emplace(name, mem);
|
||||
if (ret.second == false) {
|
||||
std::cerr << "ERROR: Can not register \"" << name << "\" at: \"" << location
|
||||
<< "\" (Previously defined at: \"" << ret.first->second.location
|
||||
<< "\")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MemImageType VerilatorSimCtrl::GetMemImageTypeByName(const std::string name) {
|
||||
if (name.compare("elf") == 0) {
|
||||
return kMemImageElf;
|
||||
}
|
||||
if (name.compare("vmem") == 0) {
|
||||
return kMemImageVmem;
|
||||
}
|
||||
return kMemImageUnknown;
|
||||
}
|
||||
|
||||
MemImageType VerilatorSimCtrl::DetectMemImageType(const std::string filepath) {
|
||||
size_t ext_pos = filepath.find_last_of(".");
|
||||
std::string ext = filepath.substr(ext_pos + 1);
|
||||
|
||||
if (ext_pos == std::string::npos) {
|
||||
// Assume ELF files if no file extension is given.
|
||||
// TODO: Make this more robust by actually checking the file contents.
|
||||
return kMemImageElf;
|
||||
}
|
||||
return GetMemImageTypeByName(ext);
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::PrintMemRegions() const {
|
||||
std::cout << "Registered memory regions:" << std::endl;
|
||||
for (const auto &m : mem_register_) {
|
||||
std::cout << "\t'" << m.second.name << "' at location: '"
|
||||
<< m.second.location << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::ParseMemArg(std::string mem_argument, std::string &name,
|
||||
std::string &filepath, MemImageType &type) {
|
||||
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::cerr << "ERROR: empty field in: " << mem_argument << std::endl;
|
||||
return false;
|
||||
}
|
||||
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::cerr << "ERROR: meminit must be in \"name,file[,type]\""
|
||||
<< " got: " << mem_argument << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
svScope scope;
|
||||
name = args[0];
|
||||
filepath = args[1];
|
||||
|
||||
scope = svGetScopeFromName(rom.data());
|
||||
if (i == 1) {
|
||||
// Type not set explicitly
|
||||
type = DetectMemImageType(filepath);
|
||||
} else {
|
||||
type = GetMemImageTypeByName(args[2]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::MemWrite(const std::string &name,
|
||||
const std::string &filepath) {
|
||||
MemImageType type = DetectMemImageType(filepath);
|
||||
if (type == kMemImageUnknown) {
|
||||
std::cerr << "ERROR: Unable to detect file type for: " << filepath
|
||||
<< std::endl;
|
||||
// Continuing for more error messages
|
||||
}
|
||||
return MemWrite(name, filepath, type);
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::MemWrite(const std::string &name,
|
||||
const std::string &filepath,
|
||||
MemImageType type) {
|
||||
// Search for corresponding registered memory based on the name
|
||||
auto it = mem_register_.find(name);
|
||||
if (it == mem_register_.end()) {
|
||||
std::cerr << "ERROR: Memory location not set for: '" << name << "'"
|
||||
<< std::endl;
|
||||
PrintMemRegions();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MemWrite(it->second, filepath, type)) {
|
||||
std::cerr << "ERROR: Setting memory '" << name << "' failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::MemWrite(const MemArea &m, const std::string &filepath,
|
||||
MemImageType type) {
|
||||
if (!IsFileReadable(filepath)) {
|
||||
std::cerr << "ERROR: Memory initialization file "
|
||||
<< "'" << filepath << "'"
|
||||
<< " is not readable." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
svScope scope = svGetScopeFromName(m.location.data());
|
||||
if (!scope) {
|
||||
std::cerr << "ERROR: No ROM found at " << rom << std::endl;
|
||||
exit(1);
|
||||
std::cerr << "ERROR: No memory found at " << m.location << std::endl;
|
||||
return false;
|
||||
}
|
||||
svSetScope(scope);
|
||||
|
||||
simutil_verilator_memload(rom_init_file_.data());
|
||||
|
||||
std::cout << std::endl
|
||||
<< "Rom initialized with program at " << rom_init_file_
|
||||
<< std::endl;
|
||||
switch (type) {
|
||||
case kMemImageElf:
|
||||
if (!WriteElfToMem(scope, filepath)) {
|
||||
std::cerr << "ERROR: Writing ELF file to memory \"" << m.name << "\" ("
|
||||
<< m.location << ") failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kMemImageVmem:
|
||||
if (!WriteVmemToMem(scope, filepath)) {
|
||||
std::cerr << "ERROR: Writing VMEM file to memory \"" << m.name << "\" ("
|
||||
<< m.location << ") failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kMemImageUnknown:
|
||||
default:
|
||||
std::cerr << "ERROR: Unknown file type for " << m.name << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::InitRam(std::string ram) {
|
||||
if (!init_ram_) {
|
||||
return;
|
||||
bool VerilatorSimCtrl::WriteElfToMem(const svScope &scope,
|
||||
const std::string &filepath) {
|
||||
bool retcode;
|
||||
svScope prev_scope = svSetScope(scope);
|
||||
|
||||
uint8_t *buf = nullptr;
|
||||
size_t len_bytes;
|
||||
|
||||
if (!ElfFileToBinary(filepath, &buf, len_bytes)) {
|
||||
std::cerr << "ERROR: Could not load: " << filepath << std::endl;
|
||||
retcode = false;
|
||||
goto ret;
|
||||
}
|
||||
for (int i = 0; i < len_bytes / 4; ++i) {
|
||||
if (simutil_verilator_set_mem(i, (svLogicVecVal *)&buf[4 * i])) {
|
||||
std::cerr << "ERROR: Could not set memory byte: " << i * 4 << "/"
|
||||
<< len_bytes << "" << std::endl;
|
||||
|
||||
retcode = false;
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
|
||||
svScope scope;
|
||||
retcode = true;
|
||||
|
||||
scope = svGetScopeFromName(ram.data());
|
||||
if (!scope) {
|
||||
std::cerr << "ERROR: No RAM found at " << ram << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
svSetScope(scope);
|
||||
|
||||
simutil_verilator_memload(ram_init_file_.data());
|
||||
|
||||
std::cout << std::endl
|
||||
<< "Ram initialized with program at " << ram_init_file_
|
||||
<< std::endl;
|
||||
ret:
|
||||
svSetScope(prev_scope);
|
||||
free(buf);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::InitFlash(std::string flash) {
|
||||
if (!init_flash_) {
|
||||
return;
|
||||
}
|
||||
bool VerilatorSimCtrl::WriteVmemToMem(const svScope &scope,
|
||||
const std::string &filepath) {
|
||||
svScope prev_scope = svSetScope(scope);
|
||||
|
||||
svScope scope;
|
||||
// TODO: Add error handling.
|
||||
simutil_verilator_memload(filepath.data());
|
||||
|
||||
scope = svGetScopeFromName(flash.data());
|
||||
if (!scope) {
|
||||
std::cerr << "ERROR: No FLASH found at " << flash << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
svSetScope(scope);
|
||||
|
||||
simutil_verilator_memload(flash_init_file_.data());
|
||||
|
||||
std::cout << std::endl
|
||||
<< "Flash initialized with program at " << flash_init_file_
|
||||
<< std::endl;
|
||||
svSetScope(prev_scope);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, int &retcode) {
|
||||
bool VerilatorSimCtrl::ParseCommandArgs(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'},
|
||||
{"meminit", required_argument, nullptr, 'l'},
|
||||
{"term-after-cycles", required_argument, nullptr, 'c'},
|
||||
{"trace", no_argument, nullptr, 't'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{nullptr, no_argument, nullptr, 0}};
|
||||
|
||||
while (1) {
|
||||
int c = getopt_long(argc, argv, ":r:m:f:c:th", long_options, nullptr);
|
||||
int c = getopt_long(argc, argv, ":r:m:f:l:c:th", long_options, nullptr);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -254,35 +412,43 @@ bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, int &retcode) {
|
|||
case 0:
|
||||
break;
|
||||
case 'r':
|
||||
rom_init_file_ = optarg;
|
||||
init_rom_ = true;
|
||||
if (!IsFileReadable(rom_init_file_)) {
|
||||
std::cerr << "ERROR: ROM initialization file "
|
||||
<< "'" << rom_init_file_ << "'"
|
||||
<< " is not readable." << std::endl;
|
||||
if (!MemWrite("rom", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
ram_init_file_ = optarg;
|
||||
init_ram_ = true;
|
||||
if (!IsFileReadable(ram_init_file_)) {
|
||||
std::cerr << "ERROR: Memory initialization file "
|
||||
<< "'" << ram_init_file_ << "'"
|
||||
<< " is not readable." << std::endl;
|
||||
if (!MemWrite("ram", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
flash_init_file_ = optarg;
|
||||
init_flash_ = true;
|
||||
if (!IsFileReadable(flash_init_file_)) {
|
||||
std::cerr << "ERROR: FLASH initialization file "
|
||||
<< "'" << flash_init_file_ << "'"
|
||||
<< " is not readable." << std::endl;
|
||||
if (!MemWrite("flash", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'l': {
|
||||
if (strcasecmp(optarg, "list") == 0) {
|
||||
PrintMemRegions();
|
||||
exit_app = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::string filepath;
|
||||
MemImageType type;
|
||||
if (!ParseMemArg(optarg, name, filepath, type)) {
|
||||
std::cerr << "ERROR: Unable to parse meminit arguments." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MemWrite(name, filepath, type)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case 't':
|
||||
if (!tracing_possible_) {
|
||||
std::cerr << "ERROR: Tracing has not been enabled at compile time."
|
||||
|
@ -296,10 +462,10 @@ bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, int &retcode) {
|
|||
break;
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
return false;
|
||||
exit_app = true;
|
||||
return true;
|
||||
case ':': // missing argument
|
||||
std::cerr << "ERROR: Missing argument." << std::endl;
|
||||
PrintHelp();
|
||||
std::cerr << "ERROR: Missing argument." << std::endl << std::endl;
|
||||
return false;
|
||||
case '?':
|
||||
default:;
|
||||
|
@ -330,15 +496,15 @@ void VerilatorSimCtrl::Trace() {
|
|||
}
|
||||
|
||||
if (!tracer_.isOpen()) {
|
||||
tracer_.open(GetSimulationFileName());
|
||||
std::cout << "Writing simulation traces to " << GetSimulationFileName()
|
||||
tracer_.open(GetTraceFileName());
|
||||
std::cout << "Writing simulation traces to " << GetTraceFileName()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
tracer_.dump(GetTime());
|
||||
}
|
||||
|
||||
const char *VerilatorSimCtrl::GetSimulationFileName() const {
|
||||
const char *VerilatorSimCtrl::GetTraceFileName() const {
|
||||
#ifdef VM_TRACE_FMT_FST
|
||||
return "sim.fst";
|
||||
#else
|
||||
|
@ -354,11 +520,11 @@ void VerilatorSimCtrl::Run() {
|
|||
// We always need to enable this as tracing can be enabled at runtime
|
||||
if (tracing_possible_) {
|
||||
Verilated::traceEverOn(true);
|
||||
top_->trace(tracer_, 99, 0);
|
||||
top_.trace(tracer_, 99, 0);
|
||||
}
|
||||
|
||||
// Evaluate all initial blocks, including the DPI setup routines
|
||||
top_->eval();
|
||||
top_.eval();
|
||||
|
||||
std::cout << std::endl
|
||||
<< "Simulation running, end by pressing CTRL-c." << std::endl;
|
||||
|
@ -380,7 +546,7 @@ void VerilatorSimCtrl::Run() {
|
|||
callback_(time_);
|
||||
}
|
||||
|
||||
top_->eval();
|
||||
top_.eval();
|
||||
time_++;
|
||||
|
||||
Trace();
|
||||
|
@ -402,7 +568,7 @@ void VerilatorSimCtrl::Run() {
|
|||
}
|
||||
}
|
||||
|
||||
top_->final();
|
||||
top_.final();
|
||||
time_end_ = std::chrono::steady_clock::now();
|
||||
|
||||
if (TracingEverEnabled()) {
|
||||
|
@ -434,12 +600,12 @@ void VerilatorSimCtrl::SetResetDuration(unsigned int cycles) {
|
|||
reset_duration_cycles_ = cycles;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::IsFileReadable(std::string filepath) {
|
||||
bool VerilatorSimCtrl::IsFileReadable(std::string filepath) const {
|
||||
struct stat statbuf;
|
||||
return stat(filepath.data(), &statbuf) == 0;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::FileSize(std::string filepath, int &size_byte) {
|
||||
bool VerilatorSimCtrl::FileSize(std::string filepath, int &size_byte) const {
|
||||
struct stat statbuf;
|
||||
if (stat(filepath.data(), &statbuf) != 0) {
|
||||
size_byte = 0;
|
||||
|
@ -450,13 +616,13 @@ bool VerilatorSimCtrl::FileSize(std::string filepath, int &size_byte) {
|
|||
return true;
|
||||
}
|
||||
|
||||
unsigned int VerilatorSimCtrl::GetExecutionTimeMs() {
|
||||
unsigned int VerilatorSimCtrl::GetExecutionTimeMs() const {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(time_end_ -
|
||||
time_begin_)
|
||||
.count();
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::PrintStatistics() {
|
||||
void VerilatorSimCtrl::PrintStatistics() const {
|
||||
double speed_hz = time_ / 2 / (GetExecutionTimeMs() / 1000.0);
|
||||
double speed_khz = speed_hz / 1000.0;
|
||||
|
||||
|
@ -470,7 +636,106 @@ void VerilatorSimCtrl::PrintStatistics() {
|
|||
<< "(" << speed_khz << " kHz)" << std::endl;
|
||||
|
||||
int trace_size_byte;
|
||||
if (tracing_enabled_ && FileSize(GetSimulationFileName(), trace_size_byte)) {
|
||||
if (tracing_enabled_ && FileSize(GetTraceFileName(), trace_size_byte)) {
|
||||
std::cout << "Trace file size: " << trace_size_byte << " B" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::ElfFileToBinary(const std::string &filepath,
|
||||
uint8_t **data,
|
||||
size_t &len_bytes) const {
|
||||
bool retval;
|
||||
std::list<BufferDesc> buffers;
|
||||
size_t offset = 0;
|
||||
(void)elf_errno();
|
||||
len_bytes = 0;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
std::cerr << elf_errmsg(-1) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = open(filepath.c_str(), O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
std::cerr << "Could not open file: " << filepath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
Elf *elf_desc;
|
||||
elf_desc = elf_begin(fd, ELF_C_READ, NULL);
|
||||
if (elf_desc == NULL) {
|
||||
std::cerr << elf_errmsg(-1) << " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_fd_end;
|
||||
}
|
||||
if (elf_kind(elf_desc) != ELF_K_ELF) {
|
||||
std::cerr << "Not a ELF file: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
// TODO: add support for ELFCLASS64
|
||||
if (gelf_getclass(elf_desc) != ELFCLASS32) {
|
||||
std::cerr << "Not a 32-bit ELF file: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
size_t phnum;
|
||||
if (elf_getphdrnum(elf_desc, &phnum) != 0) {
|
||||
std::cerr << elf_errmsg(-1) << " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
GElf_Phdr phdr;
|
||||
Elf_Data *elf_data;
|
||||
elf_data = NULL;
|
||||
for (size_t i = 0; i < phnum; i++) {
|
||||
if (gelf_getphdr(elf_desc, i, &phdr) == NULL) {
|
||||
std::cerr << elf_errmsg(-1) << " segment number: " << i
|
||||
<< " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
std::cout << "Program header number " << i << "is not of type PT_LOAD."
|
||||
<< "Continue." << std::endl;
|
||||
continue;
|
||||
}
|
||||
elf_data = elf_getdata_rawchunk(elf_desc, phdr.p_offset, phdr.p_filesz,
|
||||
ELF_T_BYTE);
|
||||
|
||||
if (elf_data == NULL) {
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
BufferDesc buf_data;
|
||||
buf_data.length = elf_data->d_size;
|
||||
len_bytes += buf_data.length;
|
||||
buf_data.data = (uint8_t *)malloc(elf_data->d_size);
|
||||
memcpy(buf_data.data, ((uint8_t *)elf_data->d_buf), buf_data.length);
|
||||
buffers.push_back(buf_data);
|
||||
}
|
||||
|
||||
// TODO: Check for the case that phdr.p_memsz > phdr.p_filesz
|
||||
|
||||
// Put the collected data into a continuous buffer
|
||||
// Memory is freed by the caller
|
||||
*data = (uint8_t *)malloc(len_bytes);
|
||||
for (std::list<BufferDesc>::iterator it = buffers.begin();
|
||||
it != buffers.end(); ++it) {
|
||||
memcpy(((uint8_t *)*data) + offset, it->data, it->length);
|
||||
offset += it->length;
|
||||
free(it->data);
|
||||
}
|
||||
buffers.clear();
|
||||
|
||||
retval = true;
|
||||
|
||||
return_elf_end:
|
||||
elf_end(elf_desc);
|
||||
return_fd_end:
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
#ifndef VERILATOR_SIM_CTRL_H_
|
||||
#define VERILATOR_SIM_CTRL_H_
|
||||
|
||||
#include <verilated.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <verilated.h>
|
||||
|
||||
#include "verilated_toplevel.h"
|
||||
|
||||
|
@ -22,22 +23,43 @@ enum VerilatorSimCtrlFlags {
|
|||
// The parameter is simulation time
|
||||
typedef std::function<void(int /* sim time */)> SimCtrlCallBack;
|
||||
|
||||
enum MemImageType {
|
||||
kMemImageUnknown = 0,
|
||||
kMemImageElf,
|
||||
kMemImageVmem,
|
||||
};
|
||||
|
||||
struct MemArea {
|
||||
std::string name; // Unique identifier
|
||||
std::string location; // Design scope location
|
||||
};
|
||||
|
||||
/**
|
||||
* Simulation controller for verilated simulations
|
||||
*/
|
||||
class VerilatorSimCtrl {
|
||||
public:
|
||||
VerilatorSimCtrl(VerilatedToplevel *top, CData &clk, CData &rst_n,
|
||||
VerilatorSimCtrl(VerilatedToplevel &top, CData &clk, CData &rst_n,
|
||||
VerilatorSimCtrlFlags flags = Defaults);
|
||||
|
||||
/**
|
||||
* A helper function to execute some standard setup commands.
|
||||
* Setup and run the simulation (all in one)
|
||||
*
|
||||
* This function performs the followind tasks:
|
||||
* Use this function as high-level entry point, suitable for most use cases.
|
||||
*
|
||||
* Exec() can be used only once per process as it registers a global signal
|
||||
* handler.
|
||||
*
|
||||
* This function performs the following tasks:
|
||||
* 1. Sets up a signal handler to enable tracing to be turned on/off during
|
||||
* a run by sending SIGUSR1 to the process
|
||||
* 2. Parses a C-style set of command line arguments (see ParseCommandArgs)
|
||||
* 2. Parses a C-style set of command line arguments.
|
||||
* 3. Runs the simulation
|
||||
*
|
||||
* @return return code (0 = success)
|
||||
* @return a main()-compatible process exit code: 0 for success, 1 in case
|
||||
* of an error.
|
||||
*/
|
||||
int SetupSimulation(int argc, char **argv);
|
||||
int Exec(int argc, char **argv);
|
||||
|
||||
/**
|
||||
* A helper function to execute a standard set of run commands.
|
||||
|
@ -50,27 +72,11 @@ class VerilatorSimCtrl {
|
|||
*/
|
||||
void RunSimulation();
|
||||
|
||||
/**
|
||||
* Register the signal handler
|
||||
*/
|
||||
void RegisterSignalHandler();
|
||||
|
||||
/**
|
||||
* Print help how to use this tool
|
||||
*/
|
||||
void PrintHelp() const;
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*
|
||||
* This removes all recognized command-line arguments from argc/argv.
|
||||
*
|
||||
* The return value of this method indicates if the program should exit with
|
||||
* retcode: if this method returns true, do *not* exit; if it returns *false*,
|
||||
* do exit.
|
||||
*/
|
||||
bool ParseCommandArgs(int argc, char **argv, int &retcode);
|
||||
|
||||
/**
|
||||
* Run the main loop of the simulation
|
||||
*
|
||||
|
@ -79,29 +85,35 @@ class VerilatorSimCtrl {
|
|||
void Run();
|
||||
|
||||
/**
|
||||
* Initialize Rom
|
||||
* Register a memory as instantiated by generic ram
|
||||
*
|
||||
* The |name| must be a unique identifier. The function will return false
|
||||
* if |name| is already used. |location| is the path to the scope of the
|
||||
* instantiated memory, which needs to support the DPI-C interfaces
|
||||
* 'simutil_verilator_memload' and 'simutil_verilator_set_mem' used for
|
||||
* 'vmem' and 'elf' files, respectively.
|
||||
*
|
||||
* Memories must be registered before command arguments are parsed by
|
||||
* ParseCommandArgs() in order for them to be known.
|
||||
*/
|
||||
void InitRom(const std::string mem);
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location);
|
||||
|
||||
/**
|
||||
* Initialize Ram
|
||||
* Print a list of all registered memory regions
|
||||
*
|
||||
* @see RegisterMemoryArea()
|
||||
*/
|
||||
void InitRam(const std::string mem);
|
||||
|
||||
/**
|
||||
* Initialize Flash
|
||||
*/
|
||||
void InitFlash(const std::string mem);
|
||||
void PrintMemRegions() const;
|
||||
|
||||
/**
|
||||
* Get the current time in ticks
|
||||
*/
|
||||
unsigned long GetTime() { return time_; }
|
||||
unsigned long GetTime() const { return time_; }
|
||||
|
||||
/**
|
||||
* Get the simulation result
|
||||
*/
|
||||
bool WasSimulationSuccessful() { return simulation_success_; }
|
||||
bool WasSimulationSuccessful() const { return simulation_success_; }
|
||||
|
||||
/**
|
||||
* Set the number of clock cycles (periods) before the reset signal is
|
||||
|
@ -139,26 +151,29 @@ class VerilatorSimCtrl {
|
|||
/**
|
||||
* Is tracing currently enabled?
|
||||
*/
|
||||
bool TracingEnabled() { return tracing_enabled_; }
|
||||
bool TracingEnabled() const { return tracing_enabled_; }
|
||||
|
||||
/**
|
||||
* Has tracing been ever enabled during the run?
|
||||
*
|
||||
* Tracing can be enabled and disabled at runtime.
|
||||
*/
|
||||
bool TracingEverEnabled() { return tracing_ever_enabled_; }
|
||||
bool TracingEverEnabled() const { return tracing_ever_enabled_; }
|
||||
|
||||
/**
|
||||
* Is tracing support compiled into the simulation?
|
||||
*/
|
||||
bool TracingPossible() { return tracing_possible_; }
|
||||
bool TracingPossible() const { return tracing_possible_; }
|
||||
|
||||
/**
|
||||
* Print statistics about the simulation run
|
||||
*/
|
||||
void PrintStatistics();
|
||||
void PrintStatistics() const;
|
||||
|
||||
const char *GetSimulationFileName() const;
|
||||
/**
|
||||
* Get the file name of the trace file
|
||||
*/
|
||||
const char *GetTraceFileName() const;
|
||||
|
||||
/**
|
||||
* Set a callback function to run every cycle
|
||||
|
@ -166,17 +181,11 @@ class VerilatorSimCtrl {
|
|||
void SetOnClockCallback(SimCtrlCallBack callback);
|
||||
|
||||
private:
|
||||
VerilatedToplevel *top_;
|
||||
VerilatedToplevel &top_;
|
||||
CData &sig_clk_;
|
||||
CData &sig_rst_;
|
||||
VerilatorSimCtrlFlags flags_;
|
||||
unsigned long time_;
|
||||
bool init_rom_;
|
||||
bool init_ram_;
|
||||
bool init_flash_;
|
||||
std::string rom_init_file_;
|
||||
std::string ram_init_file_;
|
||||
std::string flash_init_file_;
|
||||
bool tracing_enabled_;
|
||||
bool tracing_enabled_changed_;
|
||||
bool tracing_ever_enabled_;
|
||||
|
@ -188,15 +197,56 @@ class VerilatorSimCtrl {
|
|||
std::chrono::steady_clock::time_point time_begin_;
|
||||
std::chrono::steady_clock::time_point time_end_;
|
||||
VerilatedTracer tracer_;
|
||||
std::map<std::string, MemArea> mem_register_;
|
||||
int term_after_cycles_;
|
||||
SimCtrlCallBack callback_;
|
||||
|
||||
unsigned int GetExecutionTimeMs();
|
||||
/**
|
||||
* Register the signal handler
|
||||
*/
|
||||
void RegisterSignalHandler();
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*
|
||||
* This removes all recognized command-line arguments from argc/argv.
|
||||
*
|
||||
* The return value of this method indicates if the program should exit with
|
||||
* retcode: if this method returns true, do *not* exit; if it returns *false*,
|
||||
* do exit.
|
||||
*/
|
||||
bool ParseCommandArgs(int argc, char **argv, bool &exit_app);
|
||||
|
||||
/**
|
||||
* Parse argument section specific to memory initialization.
|
||||
*
|
||||
* Must be in the form of: name,file[,type].
|
||||
*/
|
||||
bool ParseMemArg(std::string mem_argument, std::string &name,
|
||||
std::string &filepath, MemImageType &type);
|
||||
MemImageType DetectMemImageType(const std::string filepath);
|
||||
MemImageType GetMemImageTypeByName(const std::string name);
|
||||
|
||||
unsigned int GetExecutionTimeMs() const;
|
||||
void SetReset();
|
||||
void UnsetReset();
|
||||
bool IsFileReadable(std::string filepath);
|
||||
bool FileSize(std::string filepath, int &size_byte);
|
||||
bool IsFileReadable(std::string filepath) const;
|
||||
bool FileSize(std::string filepath, int &size_byte) const;
|
||||
void Trace();
|
||||
|
||||
/**
|
||||
* Dump an ELF file into a raw binary
|
||||
*/
|
||||
bool ElfFileToBinary(const std::string &filepath, uint8_t **data,
|
||||
size_t &len_bytes) const;
|
||||
|
||||
bool MemWrite(const std::string &name, const std::string &filepath);
|
||||
bool MemWrite(const std::string &name, const std::string &filepath,
|
||||
MemImageType type);
|
||||
bool MemWrite(const MemArea &m, const std::string &filepath,
|
||||
MemImageType type);
|
||||
bool WriteElfToMem(const svScope &scope, const std::string &filepath);
|
||||
bool WriteVmemToMem(const svScope &scope, const std::string &filepath);
|
||||
};
|
||||
|
||||
#endif // VERILATOR_SIM_CTRL_H_
|
||||
|
|
|
@ -4,47 +4,40 @@
|
|||
|
||||
#include <signal.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
|
||||
#include "ibex_pcounts.h"
|
||||
#include "verilated_toplevel.h"
|
||||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
std::shared_ptr<VerilatorSimCtrl> simctrl(nullptr);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int retcode;
|
||||
ibex_simple_system top;
|
||||
VerilatorSimCtrl simctrl(top, top.IO_CLK, top.IO_RST_N,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
|
||||
auto top = std::make_shared<ibex_simple_system>();
|
||||
|
||||
simctrl = std::make_shared<VerilatorSimCtrl>(
|
||||
top.get(), top->IO_CLK, top->IO_RST_N,
|
||||
VerilatorSimCtrlFlags::ResetPolarityNegative);
|
||||
|
||||
retcode = simctrl->SetupSimulation(argc, argv);
|
||||
|
||||
if (retcode != 0)
|
||||
return retcode;
|
||||
|
||||
// Initialize RAM
|
||||
simctrl->InitRam("TOP.ibex_simple_system.u_ram");
|
||||
simctrl.RegisterMemoryArea("ram", "TOP.ibex_simple_system.u_ram");
|
||||
|
||||
std::cout << "Simulation of Ibex" << std::endl
|
||||
<< "==================" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
simctrl->RunSimulation();
|
||||
if (simctrl.Exec(argc, argv)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: Exec can return with "true" (e.g. with `-h`), but that does not mean
|
||||
// `RunSimulation()` was executed. The folllowing values will not be useful
|
||||
// in this case.
|
||||
std::cout << "\nPerformance Counters" << std::endl
|
||||
<< "====================" << std::endl;
|
||||
std::cout << ibex_pcount_string(
|
||||
top->ibex_simple_system__DOT__mhpmcounter_vals, false);
|
||||
std::cout << ibex_pcount_string(top.ibex_simple_system__DOT__mhpmcounter_vals,
|
||||
false);
|
||||
|
||||
std::ofstream pcount_csv("ibex_simple_system_pcount.csv");
|
||||
pcount_csv << ibex_pcount_string(
|
||||
top->ibex_simple_system__DOT__mhpmcounter_vals, true);
|
||||
top.ibex_simple_system__DOT__mhpmcounter_vals, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ targets:
|
|||
# Optimization levels have a large impact on the runtime performance of the
|
||||
# simulation model. -O2 and -O3 are pretty similar, -Os is slower than -O2/-O3
|
||||
- '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=ibex_simple_system -g -O0"'
|
||||
- '-LDFLAGS "-pthread -lutil"'
|
||||
- '-LDFLAGS "-pthread -lutil -lelf"'
|
||||
- "-Wall"
|
||||
- "-Wno-PINCONNECTEMPTY"
|
||||
# XXX: Cleanup all warnings and remove this option
|
||||
|
|
|
@ -51,12 +51,27 @@ module ram_1p #(
|
|||
end
|
||||
|
||||
`ifdef VERILATOR
|
||||
// Task for loading 'mem' with SystemVerilog system task readmemh
|
||||
export "DPI-C" task simutil_verilator_memload;
|
||||
// Function for setting a specific 32 bit element in 'mem'
|
||||
// Returns 0 for success, 1 for error
|
||||
export "DPI-C" function simutil_verilator_set_mem;
|
||||
|
||||
task simutil_verilator_memload;
|
||||
input string file;
|
||||
$readmemh(file, mem);
|
||||
endtask
|
||||
|
||||
// TODO: Allow 'val' to have other widths than 32 bit
|
||||
function int simutil_verilator_set_mem(input int index,
|
||||
input logic[31:0] val);
|
||||
if (index < Depth) begin
|
||||
mem[index] = val;
|
||||
return 0;
|
||||
end else begin
|
||||
return 1;
|
||||
end
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
`ifdef SRAM_INIT_FILE
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue