| // 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; |
| |
| const char *vcd_path_ = "/tmp"; |
| |
| #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(vcd_path_) + "/" + 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) |
| } |
| |
| 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_ |