1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2024-2025 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;
};
|