Support VPI parameter and localparam (#2370)

This commit is contained in:
Ludwig Rogiers 2020-06-13 08:38:01 +10:00 committed by GitHub
parent 35226d5e1a
commit c367b671b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 871 additions and 515 deletions

View File

@ -2603,7 +2603,7 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
}
}
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE {
// Grab dimensions
// In the future we may just create a large table at emit time and
@ -2611,7 +2611,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
if (!finalize) return;
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims);
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims, isParam);
va_list ap;
va_start(ap, dims);

View File

@ -335,8 +335,8 @@ public: // But internals only - called from VerilatedModule's
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
const char* identifier, vlsint8_t timeunit, const Type& type) VL_MT_UNSAFE;
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
void varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype,
int vlflags, int dims, ...) VL_MT_UNSAFE;
void varInsert(int finalize, const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
// ACCESSORS
const char* name() const { return m_namep; }
const char* identifier() const { return m_identifierp; }

View File

@ -232,13 +232,15 @@ class VerilatedVar : public VerilatedVarProps {
void* m_datap; // Location of data
const char* m_namep; // Name - slowpath
protected:
bool m_isParam;
friend class VerilatedScope;
// CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int dims)
VerilatedVarFlags vlflags, int dims, bool isParam)
: VerilatedVarProps(vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0))
, m_datap(datap)
, m_namep(namep) {}
, m_namep(namep)
, m_isParam(isParam) {}
public:
~VerilatedVar() {}
@ -247,6 +249,7 @@ public:
const VerilatedRange& range() const { return packed(); } // Deprecated
const VerilatedRange& array() const { return unpacked(); } // Deprecated
const char* name() const { return m_namep; }
bool isParam() const { return m_isParam; }
};
#endif // Guard

File diff suppressed because it is too large Load Diff

View File

@ -3053,6 +3053,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
// support them. They also cause problems with GDB under GCC2.95.
if (varp->isWide()) { // Unsupported for output
putsDecoration("// enum WData " + varp->nameProtect() + " //wide");
} else if (varp->isString()) {
puts("const std::string " + protect("var_" + varp->name()) + " = ");
iterateAndNextNull(varp->valuep());
puts(";");
} else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output
// putsDecoration("// enum ..... "+varp->nameProtect()
// +"not simple value, see variable above instead");
@ -3060,11 +3064,16 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
&& VN_CAST(varp->dtypep(), BasicDType)
->isOpaque()) { // Can't put out e.g. doubles
} else {
puts("enum ");
puts(varp->isQuad() ? "_QData" : "_IData");
// enum
puts(varp->isQuad() ? "enum _QData" : "enum _IData");
puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = ");
iterateAndNextNull(varp->valuep());
puts("};");
puts("};\n");
// var
puts(varp->isQuad() ? "const QData" : "const IData");
puts(" var_" + varp->nameProtect() + " = ");
iterateAndNextNull(varp->valuep());
puts(";");
}
puts("\n");
}

View File

@ -332,10 +332,7 @@ class EmitCSyms : EmitCBaseVisitor {
virtual void visit(AstVar* nodep) VL_OVERRIDE {
nameCheck(nodep);
iterateChildren(nodep);
if (nodep->isSigUserRdPublic()
// The VPI functions require a pointer to allow modification,
// but parameters are constants
&& !nodep->isParam()) {
if (nodep->isSigUserRdPublic()) {
m_modVars.push_back(make_pair(m_modp, nodep));
}
}
@ -794,16 +791,32 @@ void EmitCSyms::emitSymImp() {
}
puts(protect("__Vscope_" + it->second.m_scopeName) + ".varInsert(__Vfinal,");
putsQuoted(protect(it->second.m_varBasePretty));
puts(", &(");
std::string varName;
if (modp->isTop()) {
puts(protectIf(scopep->nameDotless() + "p", scopep->protect()));
puts("->");
varName += (protectIf(scopep->nameDotless() + "p", scopep->protect()) + "->");
} else {
puts(protectIf(scopep->nameDotless(), scopep->protect()));
puts(".");
varName += (protectIf(scopep->nameDotless(), scopep->protect()) + ".");
}
puts(varp->nameProtect());
puts("), ");
if (varp->isParam()) {
varName += protect("var_" + varp->name());
} else {
varName += protect(varp->name());
}
if (varp->isParam() && (varp->vlEnumType() == "VLVT_STRING")) {
puts(", const_cast<void*>(static_cast<const void*>(");
puts(varName.c_str());
puts(".c_str())), ");
} else {
puts(", const_cast<void*>(static_cast<const void*>(&(");
puts(varName.c_str());
puts("))), ");
}
puts(varp->isParam() ? "true" : "false");
puts(", ");
puts(varp->vlEnumType()); // VLVT_UINT32 etc
puts(",");
puts(varp->vlEnumDir()); // VLVD_IN etc

View File

@ -81,7 +81,9 @@ unsigned int main_time = 0;
#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp)
static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int scalar, int type) {
s_vpi_value value = {.format = vpiIntVal, .value = {.integer = 0}};
s_vpi_value value;
value.format = vpiIntVal;
value.value.integer = 0;
// check size of object
int vpisize = vpi_get(vpiSize, handle);
CHECK_RESULT(vpisize, size);

View File

@ -82,7 +82,9 @@ unsigned int main_time = 0;
int _mon_check_range(TestVpiHandle& handle, int size, int left, int right) {
TestVpiHandle iter_h, left_h, right_h;
s_vpi_value value = {.format = vpiIntVal, .value = {.integer = 0}};
s_vpi_value value;
value.format = vpiIntVal;
value.value.integer = 0;
// check size of object
int vpisize = vpi_get(vpiSize, handle);
CHECK_RESULT(vpisize, size);
@ -108,7 +110,11 @@ int _mon_check_memory() {
int cnt;
TestVpiHandle mem_h, lcl_h, side_h;
vpiHandle iter_h; // Icarus does not like auto free of iterator handles
s_vpi_value value = {.format = vpiIntVal, .value = {.integer = 0}};
s_vpi_value value;
value.format = vpiIntVal;
value.value.integer = 0;
s_vpi_error_info e;
vpi_printf((PLI_BYTE8*)"Check memory vpi ...\n");
mem_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted("mem0"), NULL);
CHECK_RESULT_NZ(mem_h);
@ -169,6 +175,10 @@ int _mon_check_memory() {
vpi_get_value(side_h, &value);
CHECK_RESULT(value.value.integer, 1);
// check writing to vpiConstant
vpi_put_value(side_h, &value, NULL, vpiNoDelay);
CHECK_RESULT_NZ(vpi_chk_error(&e));
return 0; // Ok
}

View File

@ -0,0 +1,276 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2010-2011 by Wilson Snyder. 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 "vpi_user.h"
#include <cstdlib>
#else
#include "Vt_vpi_param.h"
#include "verilated.h"
#include "svdpi.h"
#include "Vt_vpi_param__Dpi.h"
#include "verilated_vpi.h"
#include "verilated_vcd_c.h"
#endif
#include <cstdio>
#include <cstring>
#include <iostream>
#include "TestSimulator.h"
#include "TestVpi.h"
// __FILE__ is too long
#define FILENM "t_vpi_param.cpp"
#define DEBUG \
if (0) printf
unsigned int main_time = 0;
//======================================================================
#define CHECK_RESULT_VH(got, exp) \
if ((got) != (exp)) { \
printf("%%Error: %s:%d: GOT = %p EXP = %p\n", FILENM, __LINE__, (got), (exp)); \
return __LINE__; \
}
#define CHECK_RESULT_NZ(got) \
if (!(got)) { \
printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM, __LINE__); \
return __LINE__; \
}
// Use cout to avoid issues with %d/%lx etc
#define CHECK_RESULT(got, exp) \
if ((got) != (exp)) { \
std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << ": GOT = " << (got) \
<< " EXP = " << (exp) << std::endl; \
return __LINE__; \
}
#define CHECK_RESULT_HEX(got, exp) \
if ((got) != (exp)) { \
std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << std::hex \
<< ": GOT = " << (got) << " EXP = " << (exp) << std::endl; \
return __LINE__; \
}
#define CHECK_RESULT_CSTR(got, exp) \
if (strcmp((got), (exp))) { \
printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", FILENM, __LINE__, \
(got) ? (got) : "<null>", (exp) ? (exp) : "<null>"); \
return __LINE__; \
}
#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp)
int check_param_int(std::string name, PLI_INT32 format, int exp_value, bool verbose) {
int vpi_type;
TestVpiHandle param_h;
s_vpi_value value;
value.format = format;
value.value.integer = 0;
s_vpi_error_info e;
const char* p;
vpi_printf((PLI_BYTE8*)"Check parameter %s vpi ...\n", name.c_str());
param_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted(name.c_str()), NULL);
CHECK_RESULT_NZ(param_h);
vpi_type = vpi_get(vpiType, param_h);
CHECK_RESULT(vpi_type, vpiParameter);
if (verbose) {vpi_printf((PLI_BYTE8*)" vpiType: %s (%d)\n", vpi_get_str(vpiType, param_h), vpi_type); }
// attributes
p = vpi_get_str(vpiName, param_h);
CHECK_RESULT_CSTR(p, name.c_str());
p = vpi_get_str(vpiFullName, param_h);
CHECK_RESULT_CSTR(p, std::string("t." + name).c_str());
p = vpi_get_str(vpiType, param_h);
CHECK_RESULT_CSTR(p, "vpiParameter");
vpi_type = vpi_get(vpiLocalParam, param_h);
CHECK_RESULT_NZ(vpi_chk_error(&e));
if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); }
// values
if (verbose) {vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); }
value.value.integer = exp_value;
vpi_put_value(param_h, &value, NULL, vpiNoDelay);
CHECK_RESULT_NZ(vpi_chk_error(&e));
if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); }
if (verbose) {vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); }
vpi_get_value(param_h, &value);
CHECK_RESULT_NZ(!vpi_chk_error(&e));
if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); }
if (verbose) {vpi_printf((PLI_BYTE8*)" value of %s: %d\n", name.c_str(), value.value.integer); }
CHECK_RESULT(value.value.integer, exp_value);
return 0;
}
int check_param_str(std::string name, PLI_INT32 format, std::string exp_value, bool verbose) {
int vpi_type;
TestVpiHandle param_h;
s_vpi_value value;
value.format = format;
value.value.integer = 0;
s_vpi_error_info e;
const char* p;
vpi_printf((PLI_BYTE8*)"Check parameter %s vpi ...\n", name.c_str());
param_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted(name.c_str()), NULL);
CHECK_RESULT_NZ(param_h);
vpi_type = vpi_get(vpiType, param_h);
CHECK_RESULT(vpi_type, vpiParameter);
if (verbose) {vpi_printf((PLI_BYTE8*)" vpiType: %s (%d)\n", vpi_get_str(vpiType, param_h), vpi_type); }
// attributes
p = vpi_get_str(vpiName, param_h);
CHECK_RESULT_CSTR(p, name.c_str());
p = vpi_get_str(vpiFullName, param_h);
CHECK_RESULT_CSTR(p, std::string("t." + name).c_str());
p = vpi_get_str(vpiType, param_h);
CHECK_RESULT_CSTR(p, "vpiParameter");
vpi_type = vpi_get(vpiLocalParam, param_h);
CHECK_RESULT_NZ(vpi_chk_error(&e));
if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); }
// values
if (verbose) {vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); }
value.value.str = (PLI_BYTE8*) exp_value.c_str();
vpi_put_value(param_h, &value, NULL, vpiNoDelay);
CHECK_RESULT_NZ(vpi_chk_error(&e));
if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); }
if (verbose) {vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); }
vpi_get_value(param_h, &value);
CHECK_RESULT_NZ(!vpi_chk_error(&e));
if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); }
if (verbose) {vpi_printf((PLI_BYTE8*)" value of %s: %s\n", name.c_str(), value.value.str); }
CHECK_RESULT_CSTR(value.value.str, exp_value.c_str());
return 0;
}
int _mon_check_param() {
int status = 0;
#ifdef TEST_VERBOSE
bool verbose = true;
#else
bool verbose = false;
#endif
status += check_param_int("WIDTH", vpiIntVal, 32, verbose);
status += check_param_int("DEPTH", vpiIntVal, 16, verbose);
status += check_param_str("PARAM_LONG", vpiHexStrVal, "fedcba9876543210", verbose);
status += check_param_str("PARAM_STR", vpiStringVal, "'some string value'", verbose);
return status;
}
int mon_check() {
// Callback from initial block in monitor
if (int status = _mon_check_param()) return status;
return 0; // Ok
}
//======================================================================
#ifdef IS_VPI
static int mon_check_vpi() {
vpiHandle 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, char** env) {
double sim_time = 1100;
Verilated::commandArgs(argc, argv);
Verilated::debug(0);
// we're going to be checking for these errors do don't crash out
Verilated::fatalOnVpiError(0);
VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out
#ifdef VERILATOR
#ifdef TEST_VERBOSE
Verilated::scopesDump();
#endif
#endif
#if VM_TRACE
Verilated::traceEverOn(true);
VL_PRINTF("Enabling waves...\n");
VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99);
tfp->open(VL_STRINGIFY(TEST_OBJ_DIR) "/simx.vcd");
#endif
topp->eval();
topp->clk = 0;
main_time += 10;
while (sc_time_stamp() < sim_time && !Verilated::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 (!Verilated::gotFinish()) {
vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish");
}
topp->final();
#if VM_TRACE
if (tfp) tfp->close();
#endif
VL_DO_DANGLING(delete topp, topp);
exit(0L);
}
#endif

31
test_regress/t/t_vpi_param.pl Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2010 by Wilson Snyder. 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
scenarios(simulator => 1);
skip("Known compiler limitation")
if $Self->cxx_version =~ /\(GCC\) 4.4/;
compile(
make_top_shell => 0,
make_main => 0,
make_pli => 1,
iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI"],
v_flags2 => ["+define+USE_VPI_NOT_DPI"],
verilator_flags2 => ["-CFLAGS '-DVL_DEBUG -ggdb' --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_param.cpp"],
);
execute(
iv_pli => 1,
check_finished => 1
);
ok(1);
1;

View File

@ -0,0 +1,58 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Copyright 2010 by Wilson Snyder. 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 USE_VPI_NOT_DPI
//We call it via $c so we can verify DPI isn't required - see bug572
`else
import "DPI-C" context function integer mon_check();
`endif
module t #(
parameter int WIDTH /* verilator public_flat_rd */ = 32
) (/*AUTOARG*/
// Inputs
clk
);
`ifdef VERILATOR
`systemc_header
extern "C" int mon_check();
`verilog
`endif
input clk;
localparam int DEPTH /* verilator public_flat_rd */ = 16;
localparam longint PARAM_LONG /* verilator public_flat_rd */ = 64'hFEDCBA9876543210;
localparam string PARAM_STR /* verilator public_flat_rd */ = "'some string value'";
reg [WIDTH-1:0] mem0 [DEPTH:1] /*verilator public_flat_rw @(posedge clk) */;
integer i, status;
// Test loop
initial begin
`ifdef VERILATOR
status = $c32("mon_check()");
`endif
`ifdef IVERILOG
status = $mon_check();
`endif
`ifndef USE_VPI_NOT_DPI
status = mon_check();
`endif
if (status!=0) begin
$write("%%Error: t_vpi_param.cpp:%0d: C Test failed\n", status);
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule : t