mirror of
https://github.com/verilator/verilator.git
synced 2025-01-19 12:54:02 +00:00
413 lines
11 KiB
C++
413 lines
11 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2010-2023 by Wilson Snyder and Marlon James. This program is free software; you can
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
// Version 2.0.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
|
|
#ifdef IS_VPI
|
|
|
|
#include "sv_vpi_user.h"
|
|
|
|
#else
|
|
|
|
#include "verilated.h"
|
|
#include "verilated_vcd_c.h"
|
|
#include "verilated_vpi.h"
|
|
|
|
#include "Vt_vpi_escape.h"
|
|
#include "Vt_vpi_escape__Dpi.h"
|
|
#include "svdpi.h"
|
|
|
|
#endif
|
|
|
|
#include <iostream>
|
|
|
|
// These require the above. Comment prevents clang-format moving them
|
|
#include "TestCheck.h"
|
|
#include "TestSimulator.h"
|
|
#include "TestVpi.h"
|
|
|
|
int errors = 0;
|
|
unsigned int main_time = 0;
|
|
|
|
//======================================================================
|
|
|
|
// We cannot replace those with VL_STRINGIFY, not available when PLI is build
|
|
#define STRINGIFY(x) STRINGIFY2(x)
|
|
#define STRINGIFY2(x) #x
|
|
|
|
const char* _sim_top() {
|
|
if (TestSimulator::is_verilator() || TestSimulator::is_icarus()) {
|
|
return "\\t.has.dots ";
|
|
} else {
|
|
return "top.\\t.has.dots ";
|
|
}
|
|
}
|
|
|
|
const char* _my_rooted(const char* obj) {
|
|
static std::string buf;
|
|
std::ostringstream os;
|
|
os << _sim_top();
|
|
if (*obj) os << "." << obj;
|
|
buf = os.str();
|
|
return buf.c_str();
|
|
}
|
|
|
|
#define MY_VPI_HANDLE(signal) vpi_handle_by_name(const_cast<PLI_BYTE8*>(_my_rooted(signal)), NULL);
|
|
|
|
int _mon_check_var() {
|
|
#ifdef TEST_VERBOSE
|
|
printf("-mon_check_var()\n");
|
|
#endif
|
|
TestVpiHandle vh1 = vpi_handle_by_name(const_cast<PLI_BYTE8*>(_sim_top()), NULL);
|
|
TEST_CHECK_NZ(vh1);
|
|
|
|
TestVpiHandle vh2 = MY_VPI_HANDLE("\\check;alias ");
|
|
TEST_CHECK_NZ(vh2);
|
|
|
|
// scope attributes
|
|
const char* p;
|
|
p = vpi_get_str(vpiName, vh1);
|
|
TEST_CHECK_CSTR(p, "\\t.has.dots ");
|
|
p = vpi_get_str(vpiFullName, vh1);
|
|
TEST_CHECK_CSTR(p, _sim_top());
|
|
p = vpi_get_str(vpiType, vh1);
|
|
TEST_CHECK_CSTR(p, "vpiModule");
|
|
|
|
TestVpiHandle vh3 = vpi_handle_by_name(const_cast<PLI_BYTE8*>("escaped_normal"), vh1);
|
|
TEST_CHECK_NZ(vh3);
|
|
|
|
// onebit attributes
|
|
PLI_INT32 d;
|
|
d = vpi_get(vpiType, vh3);
|
|
if (TestSimulator::is_verilator()) {
|
|
TEST_CHECK_EQ(d, vpiReg);
|
|
} else {
|
|
TEST_CHECK_EQ(d, vpiNet);
|
|
}
|
|
if (TestSimulator::has_get_scalar()) {
|
|
d = vpi_get(vpiVector, vh3);
|
|
TEST_CHECK_EQ(d, 0);
|
|
}
|
|
|
|
p = vpi_get_str(vpiName, vh3);
|
|
TEST_CHECK_CSTR(p, "escaped_normal");
|
|
p = vpi_get_str(vpiFullName, vh3);
|
|
// Toplevel port returns TOP.xxxxx
|
|
TEST_CHECK_CSTR(p, "TOP.escaped_normal");
|
|
p = vpi_get_str(vpiType, vh3);
|
|
if (TestSimulator::is_verilator()) {
|
|
TEST_CHECK_CSTR(p, "vpiReg");
|
|
} else {
|
|
TEST_CHECK_CSTR(p, "vpiNet");
|
|
}
|
|
|
|
TestVpiHandle vh4 = MY_VPI_HANDLE("\\x.y ");
|
|
TEST_CHECK_NZ(vh4);
|
|
|
|
// Test that the toplevel TOP.xxxxx search is skipped
|
|
// when the path to the scope has more than one level.
|
|
TestVpiHandle vh5 = MY_VPI_HANDLE("\\mod.with_dot .\\b.c ");
|
|
TEST_CHECK_NZ(vh5);
|
|
p = vpi_get_str(vpiFullName, vh5);
|
|
TEST_CHECK_CSTR(p, "\\t.has.dots .\\mod.with_dot .\\b.c ");
|
|
|
|
return errors;
|
|
}
|
|
|
|
int _mon_check_iter() {
|
|
#ifdef TEST_VERBOSE
|
|
printf("-mon_check_iter()\n");
|
|
#endif
|
|
const char* p;
|
|
|
|
TestVpiHandle vh2 = MY_VPI_HANDLE("\\mod.with_dot ");
|
|
TEST_CHECK_NZ(vh2);
|
|
p = vpi_get_str(vpiName, vh2);
|
|
TEST_CHECK_CSTR(p, "\\mod.with_dot ");
|
|
if (TestSimulator::is_verilator()) {
|
|
p = vpi_get_str(vpiDefName, vh2);
|
|
TEST_CHECK_CSTR(p, "sub");
|
|
}
|
|
|
|
TestVpiHandle vh_null_name = MY_VPI_HANDLE("___0_");
|
|
TEST_CHECK_NZ(vh_null_name);
|
|
p = vpi_get_str(vpiName, vh_null_name);
|
|
TEST_CHECK_CSTR(p, "___0_");
|
|
|
|
TestVpiHandle vh_hex_name = MY_VPI_HANDLE("___0F_");
|
|
TEST_CHECK_NZ(vh_hex_name);
|
|
p = vpi_get_str(vpiName, vh_hex_name);
|
|
TEST_CHECK_CSTR(p, "___0F_");
|
|
|
|
TestVpiHandle vh10 = vpi_iterate(vpiReg, vh2);
|
|
TEST_CHECK_NZ(vh10);
|
|
TEST_CHECK_EQ(vpi_get(vpiType, vh10), vpiIterator);
|
|
|
|
{
|
|
TestVpiHandle vh11 = vpi_scan(vh10);
|
|
TEST_CHECK_NZ(vh11);
|
|
p = vpi_get_str(vpiFullName, vh11);
|
|
#ifdef TEST_VERBOSE
|
|
printf(" scanned %s\n", p);
|
|
#endif
|
|
TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .\\b.c "));
|
|
}
|
|
{
|
|
TestVpiHandle vh12 = vpi_scan(vh10);
|
|
TEST_CHECK_NZ(vh12);
|
|
p = vpi_get_str(vpiFullName, vh12);
|
|
#ifdef TEST_VERBOSE
|
|
printf(" scanned %s\n", p);
|
|
#endif
|
|
TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .cyc"));
|
|
}
|
|
{
|
|
TestVpiHandle vh13 = vpi_scan(vh10);
|
|
TEST_CHECK_NZ(vh13);
|
|
p = vpi_get_str(vpiFullName, vh13);
|
|
#ifdef TEST_VERBOSE
|
|
printf(" scanned %s\n", p);
|
|
#endif
|
|
TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .subsig1"));
|
|
}
|
|
{
|
|
TestVpiHandle vh14 = vpi_scan(vh10);
|
|
TEST_CHECK_NZ(vh14);
|
|
p = vpi_get_str(vpiFullName, vh14);
|
|
#ifdef TEST_VERBOSE
|
|
printf(" scanned %s\n", p);
|
|
#endif
|
|
TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .subsig2"));
|
|
}
|
|
{
|
|
TestVpiHandle vh15 = vpi_scan(vh10);
|
|
vh10.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
|
TEST_CHECK_EQ(vh15, 0);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
int _mon_check_ports() {
|
|
#ifdef TEST_VERBOSE
|
|
printf("-mon_check_ports()\n");
|
|
#endif
|
|
// test writing to input port
|
|
TestVpiHandle vh1 = MY_VPI_HANDLE("a");
|
|
TEST_CHECK_NZ(vh1);
|
|
|
|
PLI_INT32 d;
|
|
d = vpi_get(vpiType, vh1);
|
|
if (TestSimulator::is_verilator()) {
|
|
TEST_CHECK_EQ(d, vpiReg);
|
|
} else {
|
|
TEST_CHECK_EQ(d, vpiNet);
|
|
}
|
|
|
|
const char* portFullName;
|
|
if (TestSimulator::is_verilator()) {
|
|
portFullName = "TOP.a";
|
|
} else {
|
|
portFullName = "t.a";
|
|
}
|
|
|
|
const char* name = vpi_get_str(vpiFullName, vh1);
|
|
TEST_CHECK_EQ(strcmp(name, portFullName), 0);
|
|
std::string handleName1 = name;
|
|
|
|
s_vpi_value v;
|
|
v.format = vpiIntVal;
|
|
vpi_get_value(vh1, &v);
|
|
TEST_CHECK_EQ(v.value.integer, 0);
|
|
|
|
s_vpi_time t;
|
|
t.type = vpiSimTime;
|
|
t.high = 0;
|
|
t.low = 0;
|
|
v.value.integer = 2;
|
|
vpi_put_value(vh1, &v, &t, vpiNoDelay);
|
|
v.value.integer = 100;
|
|
vpi_get_value(vh1, &v);
|
|
TEST_CHECK_EQ(v.value.integer, 2);
|
|
|
|
// get handle of toplevel module
|
|
TestVpiHandle vht = MY_VPI_HANDLE("");
|
|
TEST_CHECK_NZ(vht);
|
|
|
|
d = vpi_get(vpiType, vht);
|
|
TEST_CHECK_EQ(d, vpiModule);
|
|
|
|
TestVpiHandle vhi = vpi_iterate(vpiReg, vht);
|
|
TEST_CHECK_NZ(vhi);
|
|
|
|
TestVpiHandle vh11;
|
|
std::string handleName2;
|
|
while ((vh11 = vpi_scan(vhi))) {
|
|
const char* fn = vpi_get_str(vpiFullName, vh11);
|
|
#ifdef TEST_VERBOSE
|
|
printf(" scanned %s\n", fn);
|
|
#endif
|
|
if (0 == strcmp(fn, portFullName)) {
|
|
handleName2 = fn;
|
|
break;
|
|
}
|
|
}
|
|
TEST_CHECK_NZ(vh11); // If get zero we never found the variable
|
|
vhi.release();
|
|
TEST_CHECK_EQ(vpi_get(vpiType, vh11), vpiReg);
|
|
|
|
TEST_CHECK_EQ(handleName1, handleName2);
|
|
|
|
TestVpiHandle vh2 = MY_VPI_HANDLE("\\b.c ");
|
|
TEST_CHECK_NZ(vh2);
|
|
|
|
if (TestSimulator::is_verilator()) {
|
|
portFullName = "TOP.\\b.c ";
|
|
} else {
|
|
portFullName = "t.\\b.c ";
|
|
}
|
|
|
|
name = vpi_get_str(vpiFullName, vh2);
|
|
TEST_CHECK_EQ(strcmp(name, portFullName), 0);
|
|
handleName1 = name;
|
|
|
|
v.format = vpiIntVal;
|
|
vpi_get_value(vh2, &v);
|
|
TEST_CHECK_EQ(v.value.integer, 0);
|
|
|
|
t.type = vpiSimTime;
|
|
t.high = 0;
|
|
t.low = 0;
|
|
v.value.integer = 1;
|
|
vpi_put_value(vh2, &v, &t, vpiNoDelay);
|
|
v.value.integer = 0;
|
|
vpi_get_value(vh2, &v);
|
|
TEST_CHECK_EQ(v.value.integer, 1);
|
|
|
|
vhi = vpi_iterate(vpiReg, vht);
|
|
TEST_CHECK_NZ(vhi);
|
|
|
|
while ((vh11 = vpi_scan(vhi))) {
|
|
const char* fn = vpi_get_str(vpiFullName, vh11);
|
|
#ifdef TEST_VERBOSE
|
|
printf(" scanned %s\n", fn);
|
|
#endif
|
|
if (0 == strcmp(fn, portFullName)) {
|
|
handleName2 = fn;
|
|
break;
|
|
}
|
|
}
|
|
TEST_CHECK_NZ(vh11); // If get zero we never found the variable
|
|
vhi.release();
|
|
TEST_CHECK_EQ(vpi_get(vpiType, vh11), vpiReg);
|
|
|
|
TEST_CHECK_EQ(handleName1, handleName2);
|
|
|
|
return errors;
|
|
}
|
|
|
|
extern "C" int mon_check() {
|
|
// Callback from initial block in monitor
|
|
#ifdef TEST_VERBOSE
|
|
printf("-mon_check()\n");
|
|
#endif
|
|
|
|
if (int status = _mon_check_var()) return status;
|
|
if (int status = _mon_check_iter()) return status;
|
|
if (int status = _mon_check_ports()) return status;
|
|
#ifndef IS_VPI
|
|
VerilatedVpi::selfTest();
|
|
#endif
|
|
return 0; // Ok
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
#ifdef IS_VPI
|
|
|
|
static int mon_check_vpi() {
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
s_vpi_value vpi_value;
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
vpi_value.value.integer = mon_check();
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check",
|
|
(PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0},
|
|
0};
|
|
|
|
// cver entry
|
|
void vpi_compat_bootstrap(void) {
|
|
p_vpi_systf_data systf_data_p;
|
|
systf_data_p = &(vpi_systf_data[0]);
|
|
while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++);
|
|
}
|
|
|
|
// icarus entry
|
|
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
|
|
|
|
#else
|
|
|
|
double sc_time_stamp() { return main_time; }
|
|
int main(int argc, char** argv) {
|
|
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
|
|
|
uint64_t sim_time = 1100;
|
|
contextp->debug(0);
|
|
contextp->commandArgs(argc, argv);
|
|
|
|
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get(),
|
|
// Note null name - we're flattening it out
|
|
""}};
|
|
|
|
#ifdef VERILATOR
|
|
#ifdef TEST_VERBOSE
|
|
contextp->scopesDump();
|
|
#endif
|
|
#endif
|
|
|
|
#if VM_TRACE
|
|
contextp->traceEverOn(true);
|
|
VL_PRINTF("Enabling waves...\n");
|
|
VerilatedVcdC* tfp = new VerilatedVcdC;
|
|
topp->trace(tfp, 99);
|
|
tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd");
|
|
#endif
|
|
|
|
topp->eval();
|
|
topp->clk = 0;
|
|
main_time += 10;
|
|
|
|
while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) {
|
|
main_time += 1;
|
|
topp->eval();
|
|
VerilatedVpi::callValueCbs();
|
|
topp->clk = !topp->clk;
|
|
// mon_do();
|
|
#if VM_TRACE
|
|
if (tfp) tfp->dump(main_time);
|
|
#endif
|
|
}
|
|
if (!contextp->gotFinish()) {
|
|
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
|
|
}
|
|
topp->final();
|
|
|
|
#if VM_TRACE
|
|
if (tfp) tfp->close();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|