|  | // Copyright 2023 Google LLC | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use 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 TESTS_VERILATOR_SIM_SYSC_TB_H_ | 
|  | #define TESTS_VERILATOR_SIM_SYSC_TB_H_ | 
|  |  | 
|  | // A SystemC baseclass for constrained random testing of Verilated RTL. | 
|  | #include <systemc.h> | 
|  |  | 
|  | #include <iostream> | 
|  | #include <string> | 
|  |  | 
|  | #include "tests/verilator_sim/fifo.h" | 
|  | // sc_core needs to be included before verilator header | 
|  | using namespace sc_core;      // NOLINT(build/namespaces) | 
|  | #include "verilated_fst_c.h"  // NOLINT(build/include_subdir): From verilator. | 
|  |  | 
|  | using sc_dt::sc_bv; | 
|  |  | 
|  | #define BIND(a, b) a.b(b) | 
|  | #define BIND2(a, b, c) \ | 
|  | BIND(a, c);          \ | 
|  | BIND(b, c) | 
|  |  | 
|  | template <typename T> | 
|  | struct sc_signal_vrb { | 
|  | sc_signal<bool> valid; | 
|  | sc_signal<bool> ready; | 
|  | sc_signal<T> bits; | 
|  | }; | 
|  |  | 
|  | template <typename T> | 
|  | struct sc_in_vrb { | 
|  | sc_in<bool> valid; | 
|  | sc_out<bool> ready; | 
|  | sc_in<T> bits; | 
|  |  | 
|  | void bind(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) { | 
|  | valid.bind(v); | 
|  | ready.bind(r); | 
|  | bits.bind(b); | 
|  | } | 
|  |  | 
|  | void bind(sc_signal_vrb<T> &vrb) { | 
|  | valid.bind(vrb.valid); | 
|  | ready.bind(vrb.ready); | 
|  | bits.bind(vrb.bits); | 
|  | } | 
|  |  | 
|  | void operator()(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) { | 
|  | bind(v, r, b); | 
|  | } | 
|  |  | 
|  | void operator()(sc_signal_vrb<T> &vrb) { bind(vrb); } | 
|  | }; | 
|  |  | 
|  | template <typename T> | 
|  | struct sc_out_vrb { | 
|  | sc_out<bool> valid; | 
|  | sc_in<bool> ready; | 
|  | sc_out<T> bits; | 
|  |  | 
|  | void bind(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) { | 
|  | valid.bind(v); | 
|  | ready.bind(r); | 
|  | bits.bind(b); | 
|  | } | 
|  |  | 
|  | void bind(sc_signal_vrb<T> &vrb) { | 
|  | valid.bind(vrb.valid); | 
|  | ready.bind(vrb.ready); | 
|  | bits.bind(vrb.bits); | 
|  | } | 
|  |  | 
|  | void operator()(sc_signal<bool> &v, sc_signal<bool> &r, sc_signal<T> &b) { | 
|  | bind(v, r, b); | 
|  | } | 
|  |  | 
|  | void operator()(sc_signal_vrb<T> &vrb) { bind(vrb); } | 
|  | }; | 
|  |  | 
|  | // eg. struct message : base {...}; | 
|  | struct base { | 
|  | inline bool operator==(const base &rhs) const { return false; } | 
|  |  | 
|  | inline friend std::ostream &operator<<(std::ostream &os, base const &v) { | 
|  | return os; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Base class for testbench {posedge & negedge}. | 
|  | struct Sysc_tb : public sc_module { | 
|  | sc_clock clock; | 
|  | sc_signal<bool> reset; | 
|  | sc_signal<bool> resetn; | 
|  |  | 
|  | SC_HAS_PROCESS(Sysc_tb); | 
|  |  | 
|  | Sysc_tb(sc_module_name n, int loops, bool random = true) | 
|  | : sc_module(n), | 
|  | clock("clock", 1, SC_NS), | 
|  | reset("reset"), | 
|  | resetn("resetn"), | 
|  | random_(random), | 
|  | loops_(loops) { | 
|  | loop_ = 0; | 
|  | error_ = false; | 
|  |  | 
|  | SC_METHOD(tb_posedge); | 
|  | sensitive << clock_.pos(); | 
|  |  | 
|  | SC_METHOD(tb_negedge); | 
|  | sensitive << clock_.neg(); | 
|  |  | 
|  | SC_METHOD(tb_stop); | 
|  | sensitive << clock_.neg(); | 
|  |  | 
|  | clock_(clock); | 
|  |  | 
|  | // Verilated::commandArgs(argc, argv); | 
|  | tf_ = new VerilatedFstC; | 
|  | } | 
|  |  | 
|  | ~Sysc_tb() { | 
|  | if (tf_) { | 
|  | tf_->dump(sim_time_);  // last falling edge | 
|  | tf_->close(); | 
|  | delete tf_; | 
|  | tf_ = nullptr; | 
|  | } | 
|  | if (error_) { | 
|  | exit(23); | 
|  | } | 
|  | } | 
|  |  | 
|  | void start() { | 
|  | init(); | 
|  |  | 
|  | reset = 1; | 
|  | resetn = 0; | 
|  | sc_start(4.75, SC_NS);  // falling edge of clock | 
|  | reset = 0; | 
|  | resetn = 1; | 
|  |  | 
|  | started_ = true; | 
|  | sc_start(); | 
|  |  | 
|  | if (tf_) { | 
|  | tf_->dump(sim_time_++);  // last falling edge | 
|  | tf_->close(); | 
|  | delete tf_; | 
|  | tf_ = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | void trace(T* design, const char *name = "") { | 
|  | if (!strlen(name)) { | 
|  | name = design->name(); | 
|  | } | 
|  | std::string path = std::string("/tmp/") + name; | 
|  |  | 
|  | reset = 1; | 
|  | resetn = 0; | 
|  | sc_start(SC_ZERO_TIME); | 
|  | reset = 0; | 
|  | resetn = 1; | 
|  |  | 
|  | design->trace(tf_, 99); | 
|  | path += ".fst"; | 
|  | Verilated::traceEverOn(true); | 
|  | tf_->open(path.c_str()); | 
|  | printf("\nInfo: default timescale unit used for tracing: 1 ps (%s)\n", | 
|  | path.c_str()); | 
|  | } | 
|  |  | 
|  | static char *get_name(char *s) { | 
|  | const int len = strlen(s); | 
|  | char *p = s; | 
|  | for (int i = 0; i < len; ++i) { | 
|  | if (s[i] == '/') { | 
|  | p = s + i + 1; | 
|  | } | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | virtual void init() {} | 
|  | virtual void posedge() {} | 
|  | virtual void negedge() {} | 
|  |  | 
|  | bool check(bool v, const char *s = "") { | 
|  | const char *KRED = "\x1B[31m"; | 
|  | const char *KRST = "\033[0m"; | 
|  | if (!v) { | 
|  | sc_stop(); | 
|  | printf("%s", KRED); | 
|  | if (strlen(s)) { | 
|  | printf("***ERROR[%s]::VERIFY \"%s\"\n", this->name(), s); | 
|  | } else { | 
|  | printf("***ERROR[%s]::VERIFY\n", this->name()); | 
|  | } | 
|  | printf("%s", KRST); | 
|  | error_ = true; | 
|  | } | 
|  | return v; | 
|  | } | 
|  |  | 
|  | bool rand_bool() { | 
|  | // Do not allow any 'io_in_valid' controls to be set during reset. | 
|  | return !reset && | 
|  | (!random_ || (rand() & 1));  // NOLINT(runtime/threadsafe_fn) | 
|  | } | 
|  |  | 
|  | // Generates a number on the range [min, max]. | 
|  | int rand_int(int min = 0, int max = (1 << 31)) { | 
|  | return (rand() % (max - min + 1)) + min;  // NOLINT(runtime/threadsafe_fn) | 
|  | } | 
|  |  | 
|  | uint32_t rand_uint32(uint32_t min = 0, uint32_t max = 0xffffffffu) { | 
|  | uint32_t r = (rand() & 0xffff) |  // NOLINT(runtime/threadsafe_fn) | 
|  | (rand() << 16);      // NOLINT(runtime/threadsafe_fn) | 
|  | if (min == 0 && max == 0xffffffff) return r; | 
|  | return (r % (max - min + 1)) + min; | 
|  | } | 
|  |  | 
|  | uint64_t rand_uint64(uint64_t min = 0, uint64_t max = 0xffffffffffffffffull) { | 
|  | uint64_t r = rand_uint32() | (uint64_t(rand_uint32()) << 32); | 
|  | if (min == 0 && max == 0xffffffffffffffffull) return r; | 
|  | return (r % (max - min + 1)) + min; | 
|  | } | 
|  |  | 
|  | uint32_t cycle() { | 
|  | return sim_time_ / 2;  // posedge + negedge | 
|  | } | 
|  |  | 
|  | private: | 
|  | const bool random_; | 
|  | const int loops_; | 
|  | int loop_; | 
|  | bool error_; | 
|  | bool started_; | 
|  |  | 
|  | sc_in<bool> clock_; | 
|  |  | 
|  | uint32_t sim_time_ = 0; | 
|  | VerilatedFstC *tf_ = nullptr; | 
|  |  | 
|  | void tb_posedge() { | 
|  | if (tf_ && started_) { tf_->dump(sim_time_++); tf_->flush(); } | 
|  | if (reset) return; | 
|  | posedge(); | 
|  | } | 
|  |  | 
|  | void tb_negedge() { | 
|  | if (tf_ && started_) { tf_->dump(sim_time_++); tf_->flush(); } | 
|  | if (reset) return; | 
|  | negedge(); | 
|  | } | 
|  |  | 
|  | void tb_stop() { | 
|  | // LessThanEqual for one more edge (end - start + 1). | 
|  | if (loop_ <= loops_) { | 
|  | loop_++; | 
|  | } else { | 
|  | printf("\nInfo: loop limit \"%d\" reached\n", loops_); | 
|  | sc_stop(); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif  // TESTS_VERILATOR_SIM_SYSC_TB_H_ |