OpenCores
URL https://opencores.org/ocsvn/s80186/s80186/trunk

Subversion Repositories s80186

[/] [s80186/] [trunk/] [tests/] [rtl/] [VerilogDriver.h] - Rev 2

Compare with Previous | Blame | View Log

// Copyright Jamie Iles, 2017
//
// This file is part of s80x86.
//
// s80x86 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// s80x86 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with s80x86.  If not, see <http://www.gnu.org/licenses/>.
 
#pragma once
 
#include <verilated.h>
#include <verilated_vcd_c.h>
#include <verilated_cov.h>
 
#include <boost/algorithm/string/replace.hpp>
#include <boost/format.hpp>
#include <boost/type_index.hpp>
#include <functional>
#include <map>
#include <vector>
#include <errno.h>
 
#include <sys/stat.h>
 
enum PeriodicEventType {
    // Run at the end of the cycle once the DUT has been clocked and
    // evaluated.  Signals should be stable and ready for capturing by the
    // test driver.
    ClockCapture,
    // Run immediately before the positive clock edge, any changed signals
    // will be delivered on the clock edge and not evaluated before.  Use this
    // when sending data into the DUT that would normally be registered in the
    // real system.
    ClockSetup,
};
 
const int evals_per_cycle = 10;
 
extern double sc_time_stamp();
extern double cur_time_stamp;
 
static_assert(evals_per_cycle % 2 == 0,
              "evals_per_cycle must be divisible by 2");
 
#ifdef DEBUG
const bool verilator_debug_enabled = true;
#else
const bool verilator_debug_enabled = false;
#endif
 
#ifdef VM_COVERAGE
const bool verilator_coverage_enabled = true;
#else
const bool verilator_coverage_enabled = false;
#endif
 
template <typename T, bool debug_enabled = verilator_debug_enabled>
class VerilogDriver
{
public:
    VerilogDriver();
    explicit VerilogDriver(const std::string &instance_name);
    VerilogDriver(const VerilogDriver &rhs) = delete;
    virtual ~VerilogDriver();
    void reset(int count = 2);
    void after_n_cycles(vluint64_t delta, const std::function<void()> &cb)
    {
        at_cycle(cycle_num + delta, cb);
    }
    void periodic(PeriodicEventType edge_type, std::function<void()> fn)
    {
        periodic_events[edge_type].push_back(fn);
    }
    virtual void cycle(int count = 1);
    vluint64_t cur_cycle() const
    {
        return cycle_num;
    }
 
protected:
    T dut;
 
private:
    void at_cycle(vluint64_t cycle_num, std::function<void()> cb);
    void run_deferred_events();
    void run_periodic_events(PeriodicEventType edge_type);
    void setup_trace();
    void teardown_trace();
    VerilatedVcdC tracer;
    vluint64_t cur_time;
    vluint64_t cycle_num;
    std::map<vluint64_t, std::vector<std::function<void()>>> deferred_events;
    std::map<PeriodicEventType, vector<std::function<void()>>> periodic_events;
    std::string instance_name;
};
template <typename T, bool debug_enabled>
VerilogDriver<T, debug_enabled>::VerilogDriver()
    : VerilogDriver<T, debug_enabled>(
          boost::typeindex::type_id<T>().pretty_name())
{
}
 
template <typename T, bool debug_enabled>
VerilogDriver<T, debug_enabled>::VerilogDriver(const std::string &instance_name)
    : cycle_num(0), instance_name(instance_name)
{
    dut.reset = 0;
    dut.clk = 0;
    cur_time = 0;
    cur_time_stamp = 0;
    if (debug_enabled)
        setup_trace();
}
 
template <typename T, bool debug_enabled>
struct tracer_impl {
};
 
template <typename T>
struct tracer_impl<T, true> {
    static void trace_dut(T *dut, VerilatedVcdC *tracer)
    {
        dut->trace(tracer, 99);
    }
};
 
template <typename T>
struct tracer_impl<T, false> {
    static void trace_dut(T *, VerilatedVcdC *)
    {
    }
};
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::setup_trace()
{
    if (debug_enabled) {
        Verilated::traceEverOn(true);
        tracer_impl<T, debug_enabled>::trace_dut(&dut, &tracer);
 
        auto filename = (boost::format("%s.vcd") % instance_name).str();
        boost::replace_all(filename, "/", "_");
        tracer.open(filename.c_str());
    }
}
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::teardown_trace()
{
    if (debug_enabled)
        tracer.close();
}
 
template <typename T, bool debug_enabled>
VerilogDriver<T, debug_enabled>::~VerilogDriver()
{
    if (debug_enabled)
        teardown_trace();
 
    dut.final();
 
    if (verilator_coverage_enabled) {
        auto filename = (boost::format("%s.dat") % instance_name).str();
 
        if (mkdir("coverage", 0755) && errno != EEXIST)
            throw std::runtime_error("Failed to create coverage dir");
 
        boost::replace_all(filename, "/", "_");
        VerilatedCov::write(("coverage/" + filename).c_str());
        VerilatedCov::clear();
    }
}
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::reset(int count)
{
    this->dut.reset = 1;
    after_n_cycles(count, [&] { this->dut.reset = 0; });
    do {
        cycle();
    } while (dut.reset);
}
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::cycle(int count)
{
    for (int i = 0; i < count; ++i) {
        for (auto j = 0; j < evals_per_cycle; ++j) {
            if (debug_enabled)
                tracer.dump(cur_time);
            if (j == 0) {
                run_periodic_events(ClockSetup);
                dut.clk = !dut.clk;
                dut.eval();
                run_deferred_events();
                dut.eval();
                run_periodic_events(ClockCapture);
            } else if (j == evals_per_cycle / 2) {
                dut.clk = !dut.clk;
                dut.eval();
            } else {
                dut.eval();
            }
            ++cur_time;
        }
 
        ++cycle_num;
        ++cur_time_stamp;
    }
}
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::run_periodic_events(
    PeriodicEventType edge_type)
{
    for (auto &e : periodic_events[edge_type])
        e();
}
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::run_deferred_events()
{
    auto events = deferred_events[cycle_num];
    deferred_events.erase(cycle_num);
    for (auto &e : events)
        e();
}
 
template <typename T, bool debug_enabled>
void VerilogDriver<T, debug_enabled>::at_cycle(vluint64_t target_cycle_num,
                                               std::function<void()> cb)
{
    assert(target_cycle_num >= cycle_num);
 
    deferred_events[target_cycle_num].push_back(cb);
}
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.