Peeker Options

Once the simulation has run and all the peekers have gathered their signal traces, you can show the waveforms in various ways. The options that affect the waveform display will be illustrated using the hierarchical adder example shown below.


In [14]:
from myhdl import *
from myhdlpeek import Peeker

def adder_bit(a, b, c_in, sum_, c_out):
    '''Single bit adder.'''
    @always_comb
    def adder_logic():
        sum_.next = a ^ b ^ c_in
        c_out.next = (a & b) | (a & c_in) | (b & c_in)
    
    # Add some peekers to monitor the inputs and outputs.
    Peeker(a, 'a')
    Peeker(b, 'b')
    Peeker(c_in, 'c_in')
    Peeker(sum_, 'sum')
    Peeker(c_out, 'c_out')
    
    return adder_logic

def adder(a, b, sum_):
    '''Connect single-bit adders to create a complete adder.'''
    c = [Signal(bool(0)) for _ in range(len(a)+1)] # Carry signals between stages.
    s = [Signal(bool(0)) for _ in range(len(a))] # Sum bit for each stage.
    stages = []  # Storage for adder bit instances.
    # Create the adder bits and connect them together.
    for i in range(len(a)):
        stages.append( adder_bit(a=a(i), b=b(i), sum_=s[i], c_in=c[i], c_out=c[i+1]) )
    # Concatenate the sum bits and send them out on the sum_ output.
    @always_comb
    def make_sum():
        sum_.next = ConcatSignal(*reversed(s))
    return instances()  # Return all the adder stage instances.

# Create signals for interfacing to the adder.
a, b, sum_ = [Signal(intbv(0,0,8)) for _ in range(3)]

# Clear-out any existing peeker stuff before instantiating the adder.
Peeker.clear()

# Instantiate the adder.
add_1 = adder(a=a, b=b, sum_=sum_)

# Create some more peekers to monitor the top-level buses.
Peeker(a, 'a_bus')
Peeker(b, 'b_bus')
Peeker(sum_, 'sum_bus')

# Create a testbench generator that applies random inputs to the adder.
from random import randrange
def test(duration):
    for _ in range(duration):
        a.next, b.next = randrange(0, a.max), randrange(0, a.max)
        yield delay(1)

# Simulate the adder, testbench and peekers.
Simulation(add_1, test(8), *Peeker.instances()).run()


<class 'myhdl.StopSimulation'>: No more events
Out[14]:
0

Selecting Waveforms to Display

By default, to_wavedrom shows all the captured waveforms. But you can also specify a subset of the waveforms for display:


In [15]:
Peeker.to_wavedrom('a_bus', 'b_bus', 'sum_bus', 'sum[2]', 'sum[1]', 'sum[0]')


If you don't like typing all those quotation marks, you can place multiple, space-separated peeker names inside a string:


In [16]:
Peeker.to_wavedrom('a_bus b_bus sum_bus sum[2] sum[1] sum[0]')


Spacing the Waveforms

If you want to place some space between the waveforms, just insert a string that doesn't match any peeker's name (I like to use a | character for that):


In [17]:
Peeker.to_wavedrom('a_bus b_bus | sum_bus sum[2] sum[1] sum[0]')


Specifying a Time Window

To show only a segment of the waveforms, use the start_time and stop_time options:


In [18]:
signals = 'a_bus b_bus | sum_bus sum[2] sum[1] sum[0]'
Peeker.to_wavedrom(signals, start_time=5, stop_time=15)


Showing Cycle Times

If it's hard to tell the interval covered by the waveforms, you can turn on the display of cycle times using the tick or tock options:


In [19]:
Peeker.to_wavedrom(signals, start_time=5, stop_time=15, tock=True)


Adding Titles and Captions

You can also add a title and caption to your work:


In [20]:
Peeker.to_wavedrom(signals, start_time=5, stop_time=15, tock=True,
                   title='Multi-Bit, Hierarchical Adder', caption='It really works!')


Setting the Display Size

Possibly you have less screen real estate available. The width option lets you reduce the entire waveform display:


In [21]:
Peeker.to_wavedrom(signals, start_time=5, stop_time=15, tock=True,
                   title='Multi-Bit, Hierarchical Adder', caption='It reall works!', width=400)


Sometimes you'll have a long simulation that creates an unreadable display because it's squeezed into the width of the page. You can restore legibility by setting width wider than the page and then using the scroll bars to view the waveforms:


In [22]:
Peeker.clear_traces()
Simulation(add_1, test(100), *Peeker.instances()).run()
Peeker.to_wavedrom(signals, width=4000)
Peeker.clear_traces()


<class 'myhdl.StopSimulation'>: No more events

Skinning It

The skin option lets you choose the set of graphic elements that are used to draw the waveforms. Currently, the only allowed values are default and narrow.


In [23]:
Simulation(add_1, test(8), *Peeker.instances()).run()
Peeker.to_wavedrom(signals, skin='narrow')


<class 'myhdl.StopSimulation'>: No more events

Accessing the WaveJSON Data

Finally, you might want to get hold of the WaveJSON data directly to get more control over the waveform display. This is done with the to_wavejson() method:


In [24]:
wavejson = Peeker.to_wavejson(signals)
wavejson


Out[24]:
{'signal': [{'data': ['7', '5', '7'], 'name': 'a_bus', 'wave': '=...=..='},
  {'data': ['6', '5', '2', '7', '4', '6', '2', '3'],
   'name': 'b_bus',
   'wave': '========'},
  {},
  {'data': ['5', '4', '1', '6', '1', '3', '7', '2'],
   'name': 'sum_bus',
   'wave': '========'},
  {'name': 'sum[2]', 'wave': '1.010.10'},
  {'name': 'sum[1]', 'wave': '0..101..'},
  {'name': 'sum[0]', 'wave': '10101..0'}]}

After you manipulate the WaveJSON data, you can display it using the wavejson_to_wavedrom() function:


In [25]:
from myhdlpeek import wavejson_to_wavedrom
wavejson_to_wavedrom(wavejson)