mirror of https://github.com/XS-MLVP/picker.git
470 lines
12 KiB
C++
470 lines
12 KiB
C++
#include "dut_base.hpp"
|
|
#include <dlfcn.h>
|
|
#include <unistd.h>
|
|
|
|
#define likely(x) __builtin_expect(!!(x), 1)
|
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
|
|
DutBase::DutBase()
|
|
{
|
|
cycle = 0;
|
|
argc = 0;
|
|
argv = nullptr;
|
|
}
|
|
|
|
#if defined(USE_VCS)
|
|
|
|
DutVcsBase::DutVcsBase()
|
|
{
|
|
// XXFatal("VCS does not support no-args constructor");
|
|
exit(-1);
|
|
}
|
|
|
|
DutVcsBase::DutVcsBase(int argc, char **argv)
|
|
{
|
|
// save argc and argv for debug
|
|
this->argc = argc;
|
|
this->argv = argv;
|
|
this->init(argc, argv);
|
|
};
|
|
|
|
void DutVcsBase::init(int argc, char **argv)
|
|
{
|
|
// initialize VCS context
|
|
VcsMain(argc, argv);
|
|
|
|
// set VCS cycle to 0
|
|
VcsInit();
|
|
svSetScope(svGetScopeFromName("{{__TOP_MODULE_NAME__}}_top"));
|
|
|
|
// set cycle pointer to 0
|
|
this->cycle = 0;
|
|
this->cycle_hl = 0;
|
|
this->vcs_clock_period[0] = {{__VCS_CLOCK_PERIOD_HIGH__}};
|
|
this->vcs_clock_period[1] = {{__VCS_CLOCK_PERIOD_LOW__}};
|
|
this->vcs_clock_period[2] = {{__VCS_CLOCK_PERIOD_LOW__}}
|
|
+ {{__VCS_CLOCK_PERIOD_HIGH__}};
|
|
}
|
|
|
|
DutVcsBase::~DutVcsBase() {};
|
|
|
|
int DutVcsBase::Step(uint64_t ncycle, bool dump)
|
|
{
|
|
if (!dump) {
|
|
// assert(ncycle == 0);
|
|
VcsSimUntil(&cycle);
|
|
return 0;
|
|
}
|
|
|
|
// set cycle pointer
|
|
cycle_hl += ncycle;
|
|
if (likely(ncycle == 1))
|
|
cycle += vcs_clock_period[cycle_hl & 1];
|
|
else
|
|
cycle += (ncycle >> 1) * vcs_clock_period[2]
|
|
+ (ncycle & 1) * vcs_clock_period[cycle_hl & 1];
|
|
|
|
// run simulation
|
|
VcsSimUntil(&cycle);
|
|
return 0;
|
|
};
|
|
|
|
int DutVcsBase::Finish()
|
|
{
|
|
// Finish VCS context
|
|
return 0;
|
|
};
|
|
|
|
void DutVcsBase::SetWaveform(const char *filename)
|
|
{
|
|
XInfo("VCS waveform is not supported");
|
|
};
|
|
void DutVcsBase::SetCoverage(const char *filename)
|
|
{
|
|
XInfo("VCS coverage is not supported");
|
|
};
|
|
|
|
#endif
|
|
|
|
#if defined(USE_VERILATOR)
|
|
#include "verilated.h"
|
|
#include "V{{__TOP_MODULE_NAME__}}.h"
|
|
|
|
#if defined(VL_TRACE)
|
|
#include "V{{__TOP_MODULE_NAME__}}___024root.h"
|
|
#include "V{{__TOP_MODULE_NAME__}}__Syms.h"
|
|
#endif
|
|
|
|
DutVerilatorBase::DutVerilatorBase()
|
|
{
|
|
this->init(0, nullptr);
|
|
}
|
|
|
|
DutVerilatorBase::DutVerilatorBase(int argc, char **argv)
|
|
{
|
|
// Warn("Shared DPI Library is required for Verilator");
|
|
// save argc and argv for debug
|
|
this->argc = argc;
|
|
this->argv = argv;
|
|
this->init(argc, argv);
|
|
};
|
|
|
|
void DutVerilatorBase::init(int argc, char **argv)
|
|
{
|
|
// save argc and argv for debug
|
|
this->argc = argc;
|
|
this->argv = argv;
|
|
|
|
// create top module
|
|
VerilatedContext *contextp = new VerilatedContext;
|
|
contextp->randReset(2);
|
|
contextp->debug(0);
|
|
contextp->commandArgs(argc, argv);
|
|
|
|
#if defined(VL_TRACE)
|
|
contextp->traceEverOn(true);
|
|
#endif
|
|
|
|
// create top module
|
|
this->top = new V{{__TOP_MODULE_NAME__}} {contextp};
|
|
|
|
svSetScope(svGetScopeFromName("TOP.{{__TOP_MODULE_NAME__}}_top"));
|
|
|
|
// set cycle pointer to 0
|
|
this->cycle = 0;
|
|
};
|
|
|
|
DutVerilatorBase::~DutVerilatorBase()
|
|
{
|
|
// Finish Verilator context
|
|
this->Finish();
|
|
};
|
|
|
|
int DutVerilatorBase::Step(uint64_t ncycle, bool dump)
|
|
{
|
|
cycle += ncycle;
|
|
if (dump) {
|
|
for (int i = 0; i < ncycle; i++) {
|
|
((V{{__TOP_MODULE_NAME__}} *)(top))->eval();
|
|
((V{{__TOP_MODULE_NAME__}} *)(top))->contextp()->timeInc(1);
|
|
}
|
|
} else {
|
|
assert(ncycle == 1);
|
|
((V{{__TOP_MODULE_NAME__}} *)(top))->eval_step();
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
int DutVerilatorBase::Finish()
|
|
{
|
|
// Finish Verilator context
|
|
if (this->top != nullptr) {
|
|
VerilatedContext *contextp =
|
|
((V{{__TOP_MODULE_NAME__}} *)(this->top))->contextp();
|
|
#if defined(VL_COVERAGE)
|
|
if (this->coverage_file_path.size() > 0)
|
|
contextp->coveragep()->write(this->coverage_file_path.c_str());
|
|
else
|
|
contextp->coveragep()->write(
|
|
"V{{__TOP_MODULE_NAME__}}_coverage.dat");
|
|
#endif
|
|
((V{{__TOP_MODULE_NAME__}} *)(this->top))->final();
|
|
delete (V{{__TOP_MODULE_NAME__}} *)(this->top);
|
|
delete contextp;
|
|
this->top = nullptr;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
void DutVerilatorBase::SetWaveform(const char *filename)
|
|
{
|
|
#if defined(VL_TRACE)
|
|
((V{{__TOP_MODULE_NAME__}} *)(this->top))->contextp()->dumpfile(filename);
|
|
((V{{__TOP_MODULE_NAME__}} *)(this->top))->rootp->vlSymsp->_traceDumpOpen();
|
|
#else
|
|
std::cerr << "Verilator waveform is not enabled";
|
|
exit(-1);
|
|
#endif
|
|
};
|
|
|
|
void DutVerilatorBase::SetCoverage(const char *filename)
|
|
{
|
|
#if defined(VL_COVERAGE)
|
|
this->coverage_file_path = filename;
|
|
#else
|
|
std::cerr << "Verilator coverage is not enabled";
|
|
exit(-1);
|
|
#endif
|
|
};
|
|
|
|
DutVerilatorBase *dlcreates(int argc, char **argv)
|
|
{
|
|
DutVerilatorBase *res = new DutVerilatorBase(argc, argv);
|
|
return res;
|
|
};
|
|
typedef DutVerilatorBase *dlcreates_t(int argc, char **argv);
|
|
|
|
void dlstep(DutVerilatorBase *dut, uint64_t ncycle, bool dump)
|
|
{
|
|
dut->Step(ncycle, dump);
|
|
};
|
|
typedef void step_t(DutVerilatorBase *, uint64_t, bool);
|
|
|
|
#endif
|
|
|
|
char *locateLibPath()
|
|
{
|
|
Dl_info info;
|
|
if (dladdr((char *)locateLibPath, &info) == 0) {
|
|
XFatal("Failed to find the shared library path");
|
|
}
|
|
|
|
std::string lib_path = info.dli_fname;
|
|
XInfo("Shared DPI Library Path: %s", lib_path.c_str());
|
|
|
|
// get PWD
|
|
char *pwd = get_current_dir_name();
|
|
|
|
// get relative path
|
|
std::string rel_path;
|
|
if (lib_path.find(pwd) == std::string::npos) {
|
|
rel_path = lib_path;
|
|
} else {
|
|
rel_path = lib_path.substr(strlen(pwd) + 1);
|
|
}
|
|
|
|
char *res = (char *)malloc(rel_path.size() + 128);
|
|
strcpy(res, rel_path.c_str());
|
|
return res;
|
|
}
|
|
|
|
inline void vcsLibPathConvert(char *path)
|
|
{
|
|
// locate 'libUT' and replace it with 'libDPI' for VCS
|
|
// move the other char to next position
|
|
char *p = path;
|
|
int plib = 0, pend = strlen(path) + 1;
|
|
plib = strstr(path, "libUT") - p;
|
|
while (pend > plib) {
|
|
p[pend + 1] = p[pend];
|
|
pend--;
|
|
}
|
|
strncpy(p + plib, "libDPI", 6);
|
|
XInfo("vcsLibPath %s", path);
|
|
}
|
|
|
|
int DutUnifiedBase::lib_count = 0;
|
|
bool DutUnifiedBase::main_ns_flag = false;
|
|
|
|
DutUnifiedBase::DutUnifiedBase()
|
|
{
|
|
this->init(0, nullptr);
|
|
}
|
|
|
|
DutUnifiedBase::DutUnifiedBase(int argc, char **argv)
|
|
{
|
|
this->init(argc, (const char**)argv);
|
|
}
|
|
|
|
DutUnifiedBase::DutUnifiedBase(char *filename)
|
|
{
|
|
char *name = (char *)malloc(strlen(filename) + 1);
|
|
strcpy(name, filename);
|
|
char *argv[] = {name};
|
|
this->init(1, (const char**)argv);
|
|
free(name);
|
|
};
|
|
|
|
DutUnifiedBase::DutUnifiedBase(char *filename, int argc, char **argv)
|
|
{
|
|
char *name = (char *)malloc(strlen(filename) + 1);
|
|
strcpy(name, filename);
|
|
argv[0] = name;
|
|
this->init(argc, (const char**)argv);
|
|
free(name);
|
|
};
|
|
|
|
DutUnifiedBase::DutUnifiedBase(std::initializer_list<const char *> args)
|
|
{
|
|
int argc = 0;
|
|
const char **argv = (const char **)malloc(sizeof(char *) * argc);
|
|
for (auto arg : args) { argv[argc++] = arg; }
|
|
this->init(argc, argv);
|
|
free(argv);
|
|
};
|
|
|
|
DutUnifiedBase::DutUnifiedBase(std::vector<std::string> args)
|
|
{
|
|
int argc = 0;
|
|
const char **argv = (const char **)malloc(sizeof(char *) * argc);
|
|
for (auto arg : args) {
|
|
argv[argc] = (char *)malloc(arg.size() + 1);
|
|
strcpy((char *)argv[argc], arg.c_str());
|
|
argc++;
|
|
}
|
|
this->init(argc, argv);
|
|
for (int i = 0; i < argc; i++) {
|
|
XInfo("Initial Args %d: %s", i, argv[i]);
|
|
free((char *)argv[i]);
|
|
}
|
|
free(argv);
|
|
}
|
|
|
|
void DutUnifiedBase::init(int argc, const char **argv)
|
|
{
|
|
// hold argc and argv for later use
|
|
this->argc = argc;
|
|
this->argv = (char **)malloc(sizeof(char *) * (argc + 128));
|
|
memset(this->argv, -1, sizeof(char *) * (argc + 128));
|
|
for (size_t i = 0; i < argc; i++) {
|
|
this->argv[i] = (char *)malloc(strlen(argv[i]) + 1);
|
|
strcpy(this->argv[i], argv[i]);
|
|
}
|
|
|
|
// find whether the shared library path is provided
|
|
if (argc == 0 || !std::string(this->argv[0]).ends_with(".so")) {
|
|
// add the shared library path to argv
|
|
for (int i = argc; i > 0; i--) { this->argv[i] = this->argv[i - 1]; }
|
|
this->argv[0] = locateLibPath();
|
|
#if defined(USE_VCS)
|
|
vcsLibPathConvert(this->argv[0]);
|
|
#endif
|
|
this->argc++;
|
|
}
|
|
|
|
|
|
// the main namespace instance doesn't need to load the shared library
|
|
if (!main_ns_flag) {
|
|
XInfo("Using main namespace");
|
|
#if defined(USE_VERILATOR)
|
|
this->dut = new DutVerilatorBase(this->argc, this->argv);
|
|
#elif defined(USE_VCS)
|
|
this->dut = new DutVcsBase(this->argc, this->argv);
|
|
#endif
|
|
main_ns_flag = true;
|
|
lib_handle = nullptr;
|
|
return;
|
|
}
|
|
|
|
#ifndef USE_VCS
|
|
// get dynamic library path from argv
|
|
if (this->argc == 0) {
|
|
XFatal("Shared DPI Library Path is required for Simulator");
|
|
}
|
|
|
|
this->lib_handle =
|
|
dlmopen(LM_ID_NEWLM, this->argv[0], RTLD_NOW | RTLD_DEEPBIND);
|
|
if (!this->lib_handle) {
|
|
XFatal("Failed to open shared DPI library %s, %s", this->argv[0], dlerror());
|
|
}
|
|
this->lib_count++;
|
|
|
|
// create top module
|
|
dlcreates_t *dlcreates =
|
|
(dlcreates_t *)dlsym(this->lib_handle, "dlcreates");
|
|
if (!dlcreates) { XFatal("Failed to find dlcreates function"); }
|
|
this->dut = dlcreates(this->argc, this->argv);
|
|
#endif
|
|
}
|
|
|
|
uint64_t DutUnifiedBase::GetDPIHandle(std::string name, int towards)
|
|
{
|
|
return this->GetDPIHandle((char *)name.c_str(), towards);
|
|
}
|
|
|
|
uint64_t DutUnifiedBase::GetDPIHandle(char *name, int towards)
|
|
{
|
|
char *func_name = (char *)malloc(strlen(name) + 128);
|
|
if (towards == 0) {
|
|
sprintf(func_name, "get_%sxx{{__LIB_DPI_FUNC_NAME_HASH__}}", name);
|
|
} else if (towards == 1) {
|
|
sprintf(func_name, "set_%sxx{{__LIB_DPI_FUNC_NAME_HASH__}}", name);
|
|
} else if (towards == -1) {
|
|
strcpy(func_name, name);
|
|
} else {
|
|
XFatal("Invalid DPI function request %s %d", name, towards);
|
|
}
|
|
|
|
void *func;
|
|
if (this->lib_handle != nullptr) {
|
|
func = dlsym(this->lib_handle, func_name);
|
|
} else {
|
|
func = dlsym(RTLD_DEFAULT, func_name);
|
|
}
|
|
|
|
// internal only support read
|
|
if (func == nullptr && towards == 0) {
|
|
XFatal("Failed to find DPI function %s", func_name);
|
|
}
|
|
free(func_name);
|
|
return (uint64_t)func;
|
|
}
|
|
|
|
int DutUnifiedBase::simStep()
|
|
{
|
|
return this->simStep(1, 1);
|
|
}
|
|
int DutUnifiedBase::stepNoDump()
|
|
{
|
|
return this->simStep(1, 0);
|
|
}
|
|
int DutUnifiedBase::simStep(bool dump)
|
|
{
|
|
return this->simStep(1, dump);
|
|
}
|
|
int DutUnifiedBase::simStep(uint64_t cycle, bool dump)
|
|
{
|
|
// if (this->lib_handle == nullptr) {
|
|
// return this->dut->Step(cycle, dump);
|
|
// }
|
|
// step_t *simStep = (step_t *)dlsym(this->lib_handle, "dlstep");
|
|
// if (!simStep) {
|
|
// XFatal("Failed to find simStep function");
|
|
// }
|
|
// simStep(this->dut, cycle, dump);
|
|
return this->dut->Step(cycle, dump);
|
|
}
|
|
int DutUnifiedBase::RefreshComb()
|
|
{
|
|
return this->simStep(1, 0);
|
|
}
|
|
int DutUnifiedBase::Finish()
|
|
{
|
|
if (!this->dut) { return 0; }
|
|
this->dut->Finish();
|
|
delete this->dut;
|
|
this->dut = nullptr;
|
|
// this class maintain the other namespace
|
|
if (this->lib_handle != nullptr) {
|
|
dlclose(this->lib_handle);
|
|
this->lib_count--;
|
|
} else { // this class is using the main namespace
|
|
this->main_ns_flag = false;
|
|
}
|
|
for (int i = 0; i < this->argc; i++) { free(this->argv[i]); }
|
|
return 0;
|
|
}
|
|
void DutUnifiedBase::SetWaveform(const char *filename)
|
|
{
|
|
return this->dut->SetWaveform(filename);
|
|
}
|
|
void DutUnifiedBase::SetCoverage(const std::string filename)
|
|
{
|
|
return this->dut->SetCoverage(filename.c_str());
|
|
}
|
|
void DutUnifiedBase::SetCoverage(const char *filename)
|
|
{
|
|
return this->dut->SetCoverage(filename);
|
|
}
|
|
void DutUnifiedBase::SetWaveform(const std::string filename)
|
|
{
|
|
return this->dut->SetWaveform(filename.c_str());
|
|
}
|
|
|
|
DutUnifiedBase::~DutUnifiedBase()
|
|
{
|
|
// Clean up the new instance with the shared library
|
|
this->Finish();
|
|
}
|