verilator/test_regress/t/TestVpiMain.cpp

179 lines
5.4 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2024 by Andrew Nolte. 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
//
//*************************************************************************
// Copyright cocotb contributors
// Licensed under the Revised BSD License, see LICENSE for details.
// SPDX-License-Identifier: BSD-3-Clause
#include "verilated.h"
#include "verilated_vpi.h"
#include VM_PREFIX_INCLUDE
#include <cstdint>
#include <memory>
#ifndef VM_TRACE_FST
// emulate new verilator behavior for legacy versions
#define VM_TRACE_FST 0
#endif
#if VM_TRACE
#if VM_TRACE_FST
#include <verilated_fst_c.h>
#else
#include <verilated_vcd_c.h>
#endif
#endif
extern void (*vlog_startup_routines[])();
static bool settle_value_callbacks() {
bool cbs_called;
bool again;
// Call Value Change callbacks
// These can modify signal values so we loop
// until there are no more changes
cbs_called = again = VerilatedVpi::callValueCbs();
while (again) { again = VerilatedVpi::callValueCbs(); }
return cbs_called;
}
int main(int argc, char** argv) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
bool traceOn = false;
for (int i = 1; i < argc; ++i) {
const std::string arg = std::string(argv[i]);
if (arg == "--trace") {
traceOn = true;
} else if (arg == "--help") {
fprintf(stderr,
"usage: %s [--trace]\n"
"\n"
"Cocotb + Verilator sim\n"
"\n"
"options:\n"
" --trace Enables tracing (VCD or FST)\n",
basename(argv[0]));
return 0;
}
}
(void)traceOn; // Prevent unused if VM_TRACE not defined
contextp->commandArgs(argc, argv);
#ifdef VERILATOR_SIM_DEBUG
contextp->debug(99);
#endif
const std::unique_ptr<VM_PREFIX> top{new VM_PREFIX{contextp.get(),
// Note null name - we're flattening it out
""}};
contextp->fatalOnVpiError(false); // otherwise it will fail on systemtf
#ifdef VERILATOR_SIM_DEBUG
contextp->internalsDump();
#endif
for (auto it = &vlog_startup_routines[0]; *it != nullptr; it++) {
auto routine = *it;
routine();
}
VerilatedVpi::callCbs(cbStartOfSimulation);
#if VM_TRACE
#if VM_TRACE_FST
std::unique_ptr<VerilatedFstC> tfp(new VerilatedFstC);
const char* traceFile = "dump.fst";
#else
std::unique_ptr<VerilatedVcdC> tfp(new VerilatedVcdC);
const char* traceFile = "dump.vcd";
#endif
if (traceOn) {
contextp->traceEverOn(true);
top->trace(tfp.get(), 99);
tfp->open(traceFile);
}
#endif
while (!contextp->gotFinish()) {
do {
// We must evaluate whole design until we process all 'events' for
// this time step
do {
top->eval_step();
VerilatedVpi::clearEvalNeeded();
VerilatedVpi::doInertialPuts();
settle_value_callbacks();
} while (VerilatedVpi::evalNeeded());
// Run ReadWrite callback as we are done processing this eval step
VerilatedVpi::callCbs(cbReadWriteSynch);
VerilatedVpi::doInertialPuts();
settle_value_callbacks();
} while (VerilatedVpi::evalNeeded() || VerilatedVpi::hasCbs(cbReadWriteSynch));
top->eval_end_step();
// Call ReadOnly callbacks
VerilatedVpi::callCbs(cbReadOnlySynch);
#if VM_TRACE
if (traceOn) { tfp->dump(contextp->time()); }
#endif
// cocotb controls the clock inputs using cbAfterDelay so
// skip ahead to the next registered callback
const uint64_t NO_TOP_EVENTS_PENDING = static_cast<uint64_t>(~0ULL);
const uint64_t next_time_cocotb = VerilatedVpi::cbNextDeadline();
const uint64_t next_time_timing
= top->eventsPending() ? top->nextTimeSlot() : NO_TOP_EVENTS_PENDING;
const uint64_t next_time = std::min(next_time_cocotb, next_time_timing);
// If there are no more cbAfterDelay callbacks,
// the next deadline is max value, so end the simulation now
if (next_time == NO_TOP_EVENTS_PENDING) {
break;
} else {
contextp->time(next_time);
}
// Call registered NextSimTime
// It should be called in simulation cycle before everything else
// but not on first cycle
VerilatedVpi::callCbs(cbNextSimTime);
settle_value_callbacks();
// Call registered timed callbacks (e.g. clock timer)
// These are called at the beginning of the time step
// before the iterative regions (IEEE 1800-2012 4.4.1)
VerilatedVpi::callTimedCbs();
settle_value_callbacks();
}
VerilatedVpi::callCbs(cbEndOfSimulation);
top->final();
#if VM_TRACE
if (traceOn) { tfp->close(); }
#endif
// VM_COVERAGE is a define which is set if Verilator is
// instructed to collect coverage (when compiling the simulation)
#if VM_COVERAGE
VerilatedCov::write("coverage.dat");
#endif
return 0;
};