blob: 4819dc61cc4c7f09c4c5b71468d28e7d1f6190bd [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: ./testb.h
//
// Project: WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose:
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020, Gisselquist Technology, LLC
//
// This file is part of the WB2AXIP project.
//
// The WB2AXIP project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or this file, except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef TESTB_H
#define TESTB_H
// #define TRACE_FST
#include <stdio.h>
#include <stdint.h>
#ifdef TRACE_FST
#define TRACECLASS VerilatedFstC
#include <verilated_fst_c.h>
#else // TRACE_FST
#define TRACECLASS VerilatedVcdC
#include <verilated_vcd_c.h>
#endif
//
// The TESTB class is a useful wrapper for interacting with a Verilator
// based design. Key to its capabilities are the tick() method for
// advancing the simulation timestep, and the opentrace() and
// closetrace() methods for handling VCD tracefile generation. To
// use a non-VCD trace, redefine TRACECLASS before calling this
// function to the trace class you wish to use.
//
template <class VA> class TESTB {
public:
VA *m_core;
bool m_changed;
TRACECLASS* m_trace;
bool m_done, m_paused_trace;
uint64_t m_time_ps;
//
// Since design has only one clock within it, we won't need to use the
// multiclock techniques, and so those aren't included here at this time.
//
TESTB(void) {
m_core = new VA;
m_time_ps = 0ul;
m_trace = NULL;
m_done = false;
m_paused_trace = false;
Verilated::traceEverOn(true);
}
virtual ~TESTB(void) {
if (m_trace) m_trace->close();
delete m_core;
m_core = NULL;
}
//
// opentrace()
//
// Useful for beginning a (VCD) trace. To open such a trace, just call
// opentrace() with the name of the VCD file you'd like to trace
// everything into
virtual void opentrace(const char *vcdname, int depth=99) {
if (!m_trace) {
m_trace = new TRACECLASS;
m_core->trace(m_trace, 99);
m_trace->spTrace()->set_time_resolution("ps");
m_trace->spTrace()->set_time_unit("ps");
m_trace->open(vcdname);
m_paused_trace = false;
}
}
//
// trace()
//
// A synonym for opentrace() above.
//
void trace(const char *vcdname) {
opentrace(vcdname);
}
//
// pausetrace(pause)
//
// Set/clear a flag telling us whether or not to write to the VCD trace
// file. The default is to write to the file, but this can be changed
// by calling pausetrace. pausetrace(false) will resume tracing,
// whereas pausetrace(true) will stop all calls to Verilator's trace()
// function
//
virtual bool pausetrace(bool pausetrace) {
m_paused_trace = pausetrace;
return m_paused_trace;
}
//
// pausetrace()
//
// Like pausetrace(bool) above, except that pausetrace() will return
// the current status of the pausetrace flag above. Specifically, it
// will return true if the trace has been paused or false otherwise.
virtual bool pausetrace(void) {
return m_paused_trace;
}
//
// closetrace()
//
// Closes the open trace file. No more information will be written
// to it
virtual void closetrace(void) {
if (m_trace) {
m_trace->close();
delete m_trace;
m_trace = NULL;
}
}
//
// eval()
//
// This is a synonym for Verilator's eval() function. It evaluates all
// of the logic within the design. AutoFPGA based designs shouldn't
// need to be calling this, they should call tick() instead. However,
// in the off chance that your design inputs depend upon combinatorial
// expressions that would be output based upon other input expressions,
// you might need to call this function.
virtual void eval(void) {
m_core->eval();
}
//
// tick()
//
// tick() is the main entry point into this helper core. In general,
// tick() will advance the clock by one clock tick. In a multiple clock
// design, this will advance the clocks up until the nearest clock
// transition.
virtual void tick(void) {
// Pre-evaluate, to give verilator a chance
// to settle any combinatorial logic that
// that may have changed since the last clock
// evaluation, and then record that in the
// trace.
eval();
if (m_trace && !m_paused_trace) m_trace->dump(m_time_ps+2500);
// Advance the one simulation clock, clk
m_time_ps+= 5000;
m_core->S_AXI_ACLK = 1;
eval();
// If we are keeping a trace, dump the current state to that
// trace now
if (m_trace && !m_paused_trace) {
m_trace->dump(m_time_ps);
m_trace->flush();
}
// <SINGLE CLOCK ONLY>:
// Advance the clock again, so that it has its negative edge
m_core->S_AXI_ACLK = 0;
m_time_ps+= 5000;
eval();
if (m_trace && !m_paused_trace) m_trace->dump(m_time_ps);
// Call to see if any simulation components need
// to advance their inputs based upon this clock
sim_clk_tick();
}
virtual void sim_clk_tick(void) {
// AutoFPGA will override this method within main_tb.cpp if any
// @SIM.TICK key is present within a design component also
// containing a @SIM.CLOCK key identifying this clock. That
// component must also set m_changed to true.
m_changed = false;
}
virtual bool done(void) {
if (m_done)
return true;
if (Verilated::gotFinish())
m_done = true;
return m_done;
}
//
// reset()
//
// Sets the i_reset input for one clock tick. It's really just a
// function for the capabilies shown below. You'll want to reset any
// external input values before calling this though.
virtual void reset(void) {
m_core->S_AXI_ARESETN = 0;
tick();
m_core->S_AXI_ARESETN = 1;
// printf("RESET\n");
}
};
#endif // TESTB