1
\$\begingroup\$

I have written a Verilog model for a RISC-V CPU. I am simulating it using Verilator. I have copied the generic testbench class written by Gisselquist whose tick function, responsible for creating the rising and falling edge of the clock and getting Verilator to evaluate the design, looks as follows.

virtual void tick() {
    // Increment our own internal time reference
    m_tickcount++;

    // Make sure any combinatorial logic depending upon
    // inputs that may have changed before we called tick()
    // has settled before the rising edge of the clock.
    m_core->clock = 0;
    m_core->eval();

    // Toggle the clock

    // Rising edge
    m_core->clock = 1;
    m_core->eval();

    // Falling edge
    m_core->clock = 0;
    m_core->eval();
}

I am not sure I understand why we need the first m_core->clock = 0; followed by m_core->eval(); since this is done at the end of the function. Surely evaluating it twice changes nothing.

The only thing I can think of which makes sense of it, is that it is doubling the time the clock signal is low relative to when it is high. The comment would then make sense, since combinational circuits would have more time to overcome their propagation delays.

               --------              --------
               |      |              |      |
               |      |              |      |
----------------      ----------------      ----------------

Above represents the clock signal where it is low for twice as long as it is high.

But I thought Verilator was a cycle-based simulator that doesn't care about timings of things within a cycle, so I don't think propagation delay is even taken into account. My question boils down to: why can't we just use the following as the tick function?

virtual void tick() {
    // Increment our own internal time reference
    m_tickcount++;

    // Toggle the clock

    // Rising edge
    m_core->clock = 1;
    m_core->eval();

    // Falling edge
    m_core->clock = 0;
    m_core->eval();
}

Note that when I use the tick function directly above, my outputs make no sense. So it seems as though the extra eval when the clock is low is indeed necessary.

\$\endgroup\$
4
  • \$\begingroup\$ The comment before the first evaluation is telling why it is needed - to re-evaluate the circuit in case some signals have changed since the last tick(). The second one is needed to simulate the falling edge. \$\endgroup\$
    – Eugene Sh.
    Sep 16 at 22:01
  • \$\begingroup\$ Is my reasoning about why it is needed correct? @EugeneSh. \$\endgroup\$
    – Tom Finet
    Sep 16 at 22:02
  • \$\begingroup\$ The first evaluation is right before the rising edge. The second evaluation is right after the falling edge. So you have the full low half cycle in between, where some values might propagate and change. \$\endgroup\$
    – Eugene Sh.
    Sep 16 at 22:07
  • \$\begingroup\$ @TomFinet Your first link in your question is one of my important bookmarks!! Very good! Unfortunately, I haven't yet had time to read through all the material. That will happen in a few months' time. But not now. But that site is IMPRESSIVE!! Some aspects of the zipcpu, certain ways of optimizing when writing FPGA code, are the very reason that site deserves a LOT of my time. Best wishes and I'll be looking forward to any good answers here. So I'll +1 it. \$\endgroup\$
    – jonk
    Sep 17 at 7:40

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy