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

Subversion Repositories s80186

[/] [s80186/] [trunk/] [tests/] [rtl/] [VerilogDriver.h] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 jamieiles
// Copyright Jamie Iles, 2017
2
//
3
// This file is part of s80x86.
4
//
5
// s80x86 is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// s80x86 is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with s80x86.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
#pragma once
19
 
20
#include <verilated.h>
21
#include <verilated_vcd_c.h>
22
#include <verilated_cov.h>
23
 
24
#include <boost/algorithm/string/replace.hpp>
25
#include <boost/format.hpp>
26
#include <boost/type_index.hpp>
27
#include <functional>
28
#include <map>
29
#include <vector>
30
#include <errno.h>
31
 
32
#include <sys/stat.h>
33
 
34
enum PeriodicEventType {
35
    // Run at the end of the cycle once the DUT has been clocked and
36
    // evaluated.  Signals should be stable and ready for capturing by the
37
    // test driver.
38
    ClockCapture,
39
    // Run immediately before the positive clock edge, any changed signals
40
    // will be delivered on the clock edge and not evaluated before.  Use this
41
    // when sending data into the DUT that would normally be registered in the
42
    // real system.
43
    ClockSetup,
44
};
45
 
46
const int evals_per_cycle = 10;
47
 
48
extern double sc_time_stamp();
49
extern double cur_time_stamp;
50
 
51
static_assert(evals_per_cycle % 2 == 0,
52
              "evals_per_cycle must be divisible by 2");
53
 
54
#ifdef DEBUG
55
const bool verilator_debug_enabled = true;
56
#else
57
const bool verilator_debug_enabled = false;
58
#endif
59
 
60
#ifdef VM_COVERAGE
61
const bool verilator_coverage_enabled = true;
62
#else
63
const bool verilator_coverage_enabled = false;
64
#endif
65
 
66
template <typename T, bool debug_enabled = verilator_debug_enabled>
67
class VerilogDriver
68
{
69
public:
70
    VerilogDriver();
71
    explicit VerilogDriver(const std::string &instance_name);
72
    VerilogDriver(const VerilogDriver &rhs) = delete;
73
    virtual ~VerilogDriver();
74
    void reset(int count = 2);
75
    void after_n_cycles(vluint64_t delta, const std::function<void()> &cb)
76
    {
77
        at_cycle(cycle_num + delta, cb);
78
    }
79
    void periodic(PeriodicEventType edge_type, std::function<void()> fn)
80
    {
81
        periodic_events[edge_type].push_back(fn);
82
    }
83
    virtual void cycle(int count = 1);
84
    vluint64_t cur_cycle() const
85
    {
86
        return cycle_num;
87
    }
88
 
89
protected:
90
    T dut;
91
 
92
private:
93
    void at_cycle(vluint64_t cycle_num, std::function<void()> cb);
94
    void run_deferred_events();
95
    void run_periodic_events(PeriodicEventType edge_type);
96
    void setup_trace();
97
    void teardown_trace();
98
    VerilatedVcdC tracer;
99
    vluint64_t cur_time;
100
    vluint64_t cycle_num;
101
    std::map<vluint64_t, std::vector<std::function<void()>>> deferred_events;
102
    std::map<PeriodicEventType, vector<std::function<void()>>> periodic_events;
103
    std::string instance_name;
104
};
105
template <typename T, bool debug_enabled>
106
VerilogDriver<T, debug_enabled>::VerilogDriver()
107
    : VerilogDriver<T, debug_enabled>(
108
          boost::typeindex::type_id<T>().pretty_name())
109
{
110
}
111
 
112
template <typename T, bool debug_enabled>
113
VerilogDriver<T, debug_enabled>::VerilogDriver(const std::string &instance_name)
114
    : cycle_num(0), instance_name(instance_name)
115
{
116
    dut.reset = 0;
117
    dut.clk = 0;
118
    cur_time = 0;
119
    cur_time_stamp = 0;
120
    if (debug_enabled)
121
        setup_trace();
122
}
123
 
124
template <typename T, bool debug_enabled>
125
struct tracer_impl {
126
};
127
 
128
template <typename T>
129
struct tracer_impl<T, true> {
130
    static void trace_dut(T *dut, VerilatedVcdC *tracer)
131
    {
132
        dut->trace(tracer, 99);
133
    }
134
};
135
 
136
template <typename T>
137
struct tracer_impl<T, false> {
138
    static void trace_dut(T *, VerilatedVcdC *)
139
    {
140
    }
141
};
142
 
143
template <typename T, bool debug_enabled>
144
void VerilogDriver<T, debug_enabled>::setup_trace()
145
{
146
    if (debug_enabled) {
147
        Verilated::traceEverOn(true);
148
        tracer_impl<T, debug_enabled>::trace_dut(&dut, &tracer);
149
 
150
        auto filename = (boost::format("%s.vcd") % instance_name).str();
151
        boost::replace_all(filename, "/", "_");
152
        tracer.open(filename.c_str());
153
    }
154
}
155
 
156
template <typename T, bool debug_enabled>
157
void VerilogDriver<T, debug_enabled>::teardown_trace()
158
{
159
    if (debug_enabled)
160
        tracer.close();
161
}
162
 
163
template <typename T, bool debug_enabled>
164
VerilogDriver<T, debug_enabled>::~VerilogDriver()
165
{
166
    if (debug_enabled)
167
        teardown_trace();
168
 
169
    dut.final();
170
 
171
    if (verilator_coverage_enabled) {
172
        auto filename = (boost::format("%s.dat") % instance_name).str();
173
 
174
        if (mkdir("coverage", 0755) && errno != EEXIST)
175
            throw std::runtime_error("Failed to create coverage dir");
176
 
177
        boost::replace_all(filename, "/", "_");
178
        VerilatedCov::write(("coverage/" + filename).c_str());
179
        VerilatedCov::clear();
180
    }
181
}
182
 
183
template <typename T, bool debug_enabled>
184
void VerilogDriver<T, debug_enabled>::reset(int count)
185
{
186
    this->dut.reset = 1;
187
    after_n_cycles(count, [&] { this->dut.reset = 0; });
188
    do {
189
        cycle();
190
    } while (dut.reset);
191
}
192
 
193
template <typename T, bool debug_enabled>
194
void VerilogDriver<T, debug_enabled>::cycle(int count)
195
{
196
    for (int i = 0; i < count; ++i) {
197
        for (auto j = 0; j < evals_per_cycle; ++j) {
198
            if (debug_enabled)
199
                tracer.dump(cur_time);
200
            if (j == 0) {
201
                run_periodic_events(ClockSetup);
202
                dut.clk = !dut.clk;
203
                dut.eval();
204
                run_deferred_events();
205
                dut.eval();
206
                run_periodic_events(ClockCapture);
207
            } else if (j == evals_per_cycle / 2) {
208
                dut.clk = !dut.clk;
209
                dut.eval();
210
            } else {
211
                dut.eval();
212
            }
213
            ++cur_time;
214
        }
215
 
216
        ++cycle_num;
217
        ++cur_time_stamp;
218
    }
219
}
220
 
221
template <typename T, bool debug_enabled>
222
void VerilogDriver<T, debug_enabled>::run_periodic_events(
223
    PeriodicEventType edge_type)
224
{
225
    for (auto &e : periodic_events[edge_type])
226
        e();
227
}
228
 
229
template <typename T, bool debug_enabled>
230
void VerilogDriver<T, debug_enabled>::run_deferred_events()
231
{
232
    auto events = deferred_events[cycle_num];
233
    deferred_events.erase(cycle_num);
234
    for (auto &e : events)
235
        e();
236
}
237
 
238
template <typename T, bool debug_enabled>
239
void VerilogDriver<T, debug_enabled>::at_cycle(vluint64_t target_cycle_num,
240
                                               std::function<void()> cb)
241
{
242
    assert(target_cycle_num >= cycle_num);
243
 
244
    deferred_events[target_cycle_num].push_back(cb);
245
}

powered by: WebSVN 2.1.0

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