blob: 96302ebc3756cbe5c590e08cc05479fdb861bf87 [file] [log] [blame]
Cindy Liuad06e602023-08-10 02:21:25 -07001// Copyright 2023 Google LLC
Cindy Liu0959e532023-10-19 11:45:06 -07002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Cindy Liuad06e602023-08-10 02:21:25 -070014
Derek Chow36ad2bd2023-08-01 11:10:05 -070015#ifndef TESTS_VERILATOR_SIM_SYSC_TB_H_
16#define TESTS_VERILATOR_SIM_SYSC_TB_H_
17
18// A SystemC baseclass for constrained random testing of Verilated RTL.
Cindy Liuad06e602023-08-10 02:21:25 -070019#include <systemc.h>
Derek Chow36ad2bd2023-08-01 11:10:05 -070020
Derek Chow36ad2bd2023-08-01 11:10:05 -070021#include <iostream>
Cindy Liuad06e602023-08-10 02:21:25 -070022#include <string>
Derek Chow36ad2bd2023-08-01 11:10:05 -070023
Cindy Liuad06e602023-08-10 02:21:25 -070024#include "tests/verilator_sim/fifo.h"
25// sc_core needs to be included before verilator header
26using namespace sc_core; // NOLINT(build/namespaces)
27#include "verilated_vcd_c.h" // NOLINT(build/include_subdir): From verilator.
28
29using sc_dt::sc_bv;
Derek Chow36ad2bd2023-08-01 11:10:05 -070030
Cindy Liu9e2fc992023-08-27 18:34:29 -070031const char *vcd_path_ = "/tmp";
Derek Chow36ad2bd2023-08-01 11:10:05 -070032
33#define BIND(a, b) a.b(b)
34#define BIND2(a, b, c) \
35 BIND(a, c); \
36 BIND(b, c)
37
38template <typename T>
39struct sc_signal_vrb {
40 sc_signal<bool> valid;
41 sc_signal<bool> ready;
42 sc_signal<T> bits;
43};
44
45template <typename T>
46struct sc_in_vrb {
47 sc_in<bool> valid;
48 sc_out<bool> ready;
49 sc_in<T> bits;
50
51 void bind(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) {
52 valid.bind(v);
53 ready.bind(r);
54 bits.bind(b);
55 }
56
57 void bind(sc_signal_vrb<T> &vrb) {
58 valid.bind(vrb.valid);
59 ready.bind(vrb.ready);
60 bits.bind(vrb.bits);
61 }
62
63 void operator()(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) {
64 bind(v, r, b);
65 }
66
67 void operator()(sc_signal_vrb<T> &vrb) { bind(vrb); }
68};
69
70template <typename T>
71struct sc_out_vrb {
72 sc_out<bool> valid;
73 sc_in<bool> ready;
74 sc_out<T> bits;
75
76 void bind(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) {
77 valid.bind(v);
78 ready.bind(r);
79 bits.bind(b);
80 }
81
82 void bind(sc_signal_vrb<T> &vrb) {
83 valid.bind(vrb.valid);
84 ready.bind(vrb.ready);
85 bits.bind(vrb.bits);
86 }
87
88 void operator()(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) {
89 bind(v, r, b);
90 }
91
92 void operator()(sc_signal_vrb<T> &vrb) { bind(vrb); }
93};
94
95// eg. struct message : base {...};
96struct base {
97 inline bool operator==(const base &rhs) const { return false; }
98
99 inline friend std::ostream &operator<<(std::ostream &os, base const &v) {
100 return os;
101 }
102};
103
104// Base class for testbench {posedge & negedge}.
105struct Sysc_tb : public sc_module {
106 sc_clock clock;
107 sc_signal<bool> reset;
108 sc_signal<bool> resetn;
109
110 SC_HAS_PROCESS(Sysc_tb);
111
112 Sysc_tb(sc_module_name n, int loops, bool random = true)
113 : sc_module(n),
114 clock("clock", 1, SC_NS),
115 reset("reset"),
116 resetn("resetn"),
Cindy Liuad06e602023-08-10 02:21:25 -0700117 random_(random),
118 loops_(loops) {
Derek Chow36ad2bd2023-08-01 11:10:05 -0700119 loop_ = 0;
120 error_ = false;
121
122 SC_METHOD(tb_posedge);
123 sensitive << clock_.pos();
124
125 SC_METHOD(tb_negedge);
126 sensitive << clock_.neg();
127
128 SC_METHOD(tb_stop);
129 sensitive << clock_.neg();
130
131 clock_(clock);
132
133 // Verilated::commandArgs(argc, argv);
134 tf_ = new VerilatedVcdC;
135 }
136
137 ~Sysc_tb() {
138 if (error_) {
139 exit(23);
140 }
141 }
142
143 void start() {
144 init();
145
146 reset = 1;
147 resetn = 0;
148 sc_start(4.75, SC_NS); // falling edge of clock
149 reset = 0;
150 resetn = 1;
151
152 sc_start();
153
154 if (tf_) {
155 tf_->dump(sim_time_++); // last falling edge
156 tf_->close();
157 delete tf_;
158 tf_ = nullptr;
159 }
160 }
161
162 template <typename T>
163 void trace(T &design, const char *name = "") {
164 if (!strlen(name)) {
165 name = design.name();
166 }
167 std::string path = std::string(vcd_path_) + "/" + name;
168 design.trace(tf_, 99);
169 path += ".vcd";
170 Verilated::traceEverOn(true);
171 tf_->open(path.c_str());
172 printf("\nInfo: default timescale unit used for tracing: 1 ps (%s)\n",
173 path.c_str());
174 }
175
176 static char *get_name(char *s) {
177 const int len = strlen(s);
178 char *p = s;
179 for (int i = 0; i < len; ++i) {
180 if (s[i] == '/') {
181 p = s + i + 1;
182 }
183 }
184 return p;
185 }
186
187 protected:
Cindy Liuad06e602023-08-10 02:21:25 -0700188 virtual void init() {}
189 virtual void posedge() {}
190 virtual void negedge() {}
Derek Chow36ad2bd2023-08-01 11:10:05 -0700191
192 bool check(bool v, const char *s = "") {
193 const char *KRED = "\x1B[31m";
194 const char *KRST = "\033[0m";
195 if (!v) {
196 sc_stop();
197 printf("%s", KRED);
198 if (strlen(s)) {
199 printf("***ERROR[%s]::VERIFY \"%s\"\n", this->name(), s);
200 } else {
201 printf("***ERROR[%s]::VERIFY\n", this->name());
202 }
203 printf("%s", KRST);
204 error_ = true;
205 }
206 return v;
207 }
208
209 bool rand_bool() {
210 // Do not allow any 'io_in_valid' controls to be set during reset.
Cindy Liuad06e602023-08-10 02:21:25 -0700211 return !reset &&
212 (!random_ || (rand() & 1)); // NOLINT(runtime/threadsafe_fn)
Derek Chow36ad2bd2023-08-01 11:10:05 -0700213 }
214
215 int rand_int(int min = 0, int max = (1 << 31)) {
Cindy Liuad06e602023-08-10 02:21:25 -0700216 return (rand() % (max - min + 1)) + min; // NOLINT(runtime/threadsafe_fn)
Derek Chow36ad2bd2023-08-01 11:10:05 -0700217 }
218
219 uint32_t rand_uint32(uint32_t min = 0, uint32_t max = 0xffffffffu) {
Cindy Liuad06e602023-08-10 02:21:25 -0700220 uint32_t r = (rand() & 0xffff) | // NOLINT(runtime/threadsafe_fn)
221 (rand() << 16); // NOLINT(runtime/threadsafe_fn)
Derek Chow36ad2bd2023-08-01 11:10:05 -0700222 if (min == 0 && max == 0xffffffff) return r;
223 return (r % (max - min + 1)) + min;
224 }
225
226 uint64_t rand_uint64(uint64_t min = 0, uint64_t max = 0xffffffffffffffffull) {
227 uint64_t r = rand_uint32() | (uint64_t(rand_uint32()) << 32);
228 if (min == 0 && max == 0xffffffffffffffffull) return r;
229 return (r % (max - min + 1)) + min;
230 }
231
232 uint32_t cycle() {
233 return sim_time_ / 2; // posedge + negedge
234 }
235
236 private:
237 const bool random_;
238 const int loops_;
239 int loop_;
240 bool error_;
241
242 sc_in<bool> clock_;
243
244 uint32_t sim_time_ = 0;
245 VerilatedVcdC *tf_ = nullptr;
246
247 void tb_posedge() {
248 if (tf_) tf_->dump(sim_time_++);
249 if (reset) return;
250 posedge();
251 }
252
253 void tb_negedge() {
254 if (tf_) tf_->dump(sim_time_++);
255 if (reset) return;
256 negedge();
257 }
258
259 void tb_stop() {
260 // LessThanEqual for one more edge (end - start + 1).
261 if (loop_ <= loops_) {
262 loop_++;
263 } else {
264 printf("\nInfo: loop limit \"%d\" reached\n", loops_);
265 sc_stop();
266 }
267 }
268};
269
270#endif // TESTS_VERILATOR_SIM_SYSC_TB_H_