Timing Diagrams

Digital timing diagrams may be drawn using the schemdraw.logic.timing.TimingDiagram Element in the schemdraw.logic module. Bit Field diagrams may be drawn using schemdraw.logic.bitfield.BitField.

Timing diagrams and bit fields are set up using the WaveJSON syntax used by the WaveDrom JavaScript application. Schemdraw adds a number of additional parameters to extend the configurability of WaveDrom.

Timing Diagrams

from schemdraw import logic
logic.TimingDiagram(
    {'signal': [
        {'name': 'A', 'wave': '0..1..01.'},
        {'name': 'B', 'wave': '101..0...'}]})
../_images/timing_1_0.svg

The input is a dictionary containing a signal, which is a list of each wave to show in the diagram. Each signal is a dictionary which typically contains a name and wave. An empty dictionary leaves a blank row in the diagram.

Every character in the wave specifies the state of the wave for one period. For example, the ‘1’ and ‘0’ in the above wave strings indicate a logic high and low signal. The dot . means the previous state is repeated.

Waveform Types

All the waveform types are shown below.

logic.TimingDiagram(
    {'signal':[
        {'name': 'n', 'wave': 'n.......'},
        {'name': 'p', 'wave': 'p.......'},
        {'name': 'N', 'wave': 'N.......'},
        {'name': 'P', 'wave': 'P.......'},
        {'name': 'c', 'wave': 'c.......'},
        {'name': 'C', 'wave': 'C.......'},
        {'name': '0, 1', 'wave': '0.1.0.1.'},
        {'name': 'l, L, h, H', 'wave': 'l.h.L.H.'},
        {'name': '2-9, x', 'wave': '23456789x', 'data': '2 3 4 5 6 7 8 9 x'},
        {'name': 'z', 'wave': '0.z....1'},
        {'name': 'W, w', 'wave': '0wW.w.W0'},
        {'name': 'd, u', 'wave': '1.d..u..'},
        {'name': 'Q, b', 'wave': 'Q..0.bbb'},
    ]}
)
../_images/timing_2_0.svg

Wave Type

Description

n

Clock signal

n

Clock signal

p

Inverted clock signal

N

Clock signal with arrows

P

Inverted clock signal with arrows

c, C [1]

Clock signals with rise time

0

Low state, with rise time

1

High state, with rise time

l

Low state, no rise time

h

High state, no rise time

L

Low state with arrow

H

High state with arrow

2-9

Data states, with different colors

x

Invalid or don’t-care data state

z

High impedance state, halfway up

d

Low state with pull-down curve

u

High state with pull-up curve

Q, q [1]

Differential clock

W, w [1]

Differential signal

b [1]

Half-period bit state

e [1]

Empty state

Example:

logic.TimingDiagram(
    {'signal': [
        {'name': 'clk', 'wave': 'P......'},
        {'name': 'bus', 'wave': 'x.==.=x', 'data': ['head', 'body', 'tail']},
        {'name': 'wire', 'wave': '0.1..0.'}]})
../_images/timing_3_0.svg

Signal Parameters

In addition to wave, each signal row has a number of parameters.

  • name: Text to show to the left of the signal

  • data: Value to display inside data wave types. May be:
    • List of strings, one item per data block

    • Space separated string

    • String in the format {0, 1}, where the values 0 and 1 are repeated through the signal [1]

  • node: Define nodes for drawing arrows. See Nodes and Edges.

  • nodealign: Alignment of nodes, either signal or clock. See Nodes and Edges. [1]

  • phase: Introduce horizontal phase shift, as fraction of the period.

  • risetime: Rise/fall time for signal types 0-9 and x in drawing units (default 0.1). [1]

  • period: Change the period of the signal (default: 1)

  • level: String with same length as wave defining the maximum y-value. See Variable Voltage Levels. [1]

  • color: Color of the wave

  • lw: Line width of the wave [1]

  • fontsize: Fontsize of data labels [1]

Examples of some parameters are shown below. Note ‘wave’ may be omitted to only display data text.

logic.TimingDiagram(
    {'signal':[
        {'name': 'A', 'wave': '2.3.4.', 'data': 'X Y Z'},
        {'name': 'B', 'wave': '2.3.4.', 'data': 'X Y Z', 'fontsize': 8},
        {'name': 'C', 'wave': '0.1.0.', 'risetime': 0.5},
        {'name': 'D', 'wave': '0.1.0.', 'risetime': 0.9},
        {'name': 'E', 'wave': 'n.....', 'color': 'blue', 'lw': 2},
        {'name': 'F', 'wave': 'n.....', 'phase': 0.25},
        {'name': 'G', 'wave': 'n.....', 'period': 2},
        {'name': 'H', 'data': '{0, 1, 2}'},
    ]}
)
../_images/timing_4_0.svg

Signal Groups

Signals may also be nested into different groups:

logic.TimingDiagram(
    {'signal': ['Group',
      ['Set 1',
        {'name': 'A', 'wave': '0..1..01.'},
        {'name': 'B', 'wave': '101..0...'}],
      ['Set 2',
        {'name': 'C', 'wave': '0..1..01.'},
        {'name': 'D', 'wave': '101..0...'}]
               ]})
../_images/timing_5_0.svg

Nodes and Edges

The node keyword may be added to signals to define edge positions and labels within the wave. Within the node string, a lowercase letter indicates a node to be drawn on the wave. An uppercase letter defines a position without being drawn. A period leaves the position undefined.

Once nodes are defined, the edge parameter in the top-level dictionary provides a way to annotate transitions between different edges. The edge parameter is a list of strings, each defining an annotation. Each string starts and ends with a node letter, with a line type between. For example, “a->b” draws an arrow pointing from node a to b, and “c-d” draws a straight line from node c to d.

logic.TimingDiagram(
    {'signal': [
        {'name': 'A', 'wave': '0..1..01.', 'node': '...a.....'},
        {'name': 'B', 'wave': '101..0...', 'node': '.....b...'}],
     'edge': ['a~>b']
    })
../_images/timing_6_0.svg

Line and arrow types include:

  • -: Straight line

  • -> or <- or <->: Straight line with arrow heads

  • |-: Vertical then horizontal straight line

  • -|: Horizontal then vertical straight line

  • -|-: “Z” shape line

  • ~ or -~ or ~-: Various curved lines. May include arrow heads with ‘>’ or ‘<’.

Colors may be specified placing the color in braces after the edge string. A linestyle of ‘:’ or ‘–’ may be added after the color, separated by a comma. Edge types are illustrated below.

logic.TimingDiagram(
    {'signal':[
        {'wave': '222222', 'node': '.a.b.c'},
        {'wave': '222222', 'node': 'd.e...'},
    ],
    'edge': [
        'a-b',
        'b<->c{green}',
        'd-|-a{red}',
        'a-e{purple,:}',
        'e<~c{orange}',
    ]}
)
../_images/timing_7_0.svg

Extended Edge Notation

Schemdraw adds additional “edge” string notations for more complex labeling of edge timings, including asynchronous start and end times and labels just above or below a wave [1].

Each edge string using this syntax takes the form

'[WaveNum:Period]<->[WaveNum:Period]{color,ls} Label'

Everything after the first space will be drawn as the label in the center of the line. The values in square brackets designate the start and end position of the line. WaveNum is the integer row number (starting at 0) of the wave, and Period is the possibly fractional number of periods in time for the node. WaveNum may be appended by a ^ or v to designate notations just above, or just below, the wave, respectively.

Between the two square-bracket expressions is the standard line/arrow type designator. In optional curly braces, the line color and linestyle may be entered.

Some examples are shown here:

logic.TimingDiagram(
    {'signal': [
        {'name': 'A', 'wave': 'x3...x'},
        {'name': 'B', 'wave': 'x6.6.x'}],
     'edge': ['[0^:1]+[0^:5] $t_1$',
              '[1^:1]<->[1^:3] $t_o$',
              '[0^:3]-[1v:3]{gray,:}',
             ]},
    ygap=.5, grid=False)
../_images/timing_8_0.svg

When placing edge labels above or below the wave, it can be useful to add the ygap parameter to TimingDiagram to increase the spacing between waves.

By default, nodes are aligned with the end of the risetime of a signal. To shift the node to the clock edge, set the nodealign parameter to ‘clock’. The following examples show the difference.

logic.TimingDiagram(
    {'signal':[
        {'wave': '222222'},
        {'wave': '222222'},
    ],
    'edge': [
        '[0^:1]-[1v:1]{red}',
    ]},
    risetime=.3
)
../_images/timing_9_0.svg
logic.TimingDiagram(
    {'signal':[
        {'wave': '222222'},
        {'wave': '222222'},
    ],
    'edge': [
        '[0^:1]-[1v:1]{red}',
    ]},
    risetime=.3, nodealign='clock'
)
../_images/timing_10_0.svg

See the Timing Diagrams Gallery for more examples.

Variable Voltage Levels

Another Schemdraw extension [1] adds adjustable voltage levels within a signal using the level parameter. The value can take 10 different values, specified as digits in the level string, where a 1 corresponds to 10%, 2 to 20%, etc., with 0 meaning 100% of the normal high voltage level. As with the wave parameter, a period is used to repeat the previous level value. The level parameter only applies to wave type 1.

Here, the first pulse is 100%, the second at 50%, and the third at 20%.

logic.TimingDiagram(
    {'signal': [
        {'wave':  '0.1..0.1.0.1.',
         'level': '0......5...2.',
        }],
    })
../_images/timing_11_0.svg

Asynchronous Signals

WaveDrom does not have a means for defining asynchronous signals - all waves must transition on period boundaries. Schemdraw adds asyncrhonous signals using the async parameter, as a list of period multiples for each transition in the wave. Note the beginning and end time of the wave must also be specified, so the length of the async list must be one more than the length of wave.

logic.TimingDiagram(
    {'signal': [
        {'name': 'clk', 'wave': 'n......'},
        {'name': 'B', 'wave': '010', 'async': [0, 1.6, 4.25, 7]}]},
    risetime=.03)
../_images/timing_12_0.svg

Shading

Shading may be added to phases using the shade key in the top-level dictionary [1]. Each item in the shade list is a string with the format

'Phases Signals Color'

The Phases parameter may be ‘even’ or ‘odd’, to shade every other phase, or it may be a comma-separated list of phase numbers, such as ‘1,3’ to shade the first and third columns (starting at 0). The Signals parameter may be an asterisk * to shade every signal row, or it may be the first and last rows separated by a colon, for example “1:3” shades the first through third signal rows.

logic.TimingDiagram(
    {"signal": [
        {"name": "A", "wave": "n.........."},
        {"name": "B", "wave": "0.1.0.1.0.1"},
        {"name": "C", "wave": "01..01....."},
        {"name": "D", "wave": "1....0...1."},
    ],
    "shade": [
        "even * #eee",
        "5,7 1:2 #ddf",
    ]
    })
../_images/timing_13_0.svg

Titles

The head and foot dictionaries define items to display above and below the diagram, respectively. Titles or captions are added using the text key. Phases may be numbered using either tick, to number phases at the clock edge, or tock to number phases at their center, providing the starting value of the first phase. The every key defines which phases to number.

logic.TimingDiagram({
    'signal': [
        {'name': 'clk',    'wave': 'p....' },
        {'name': 'Data',   'wave': 'x333x', 'data': 'a b c' },
        {'name':' Enable', 'wave': '01..0' }
    ],
    'head': {
        'text': 'Header Title',
        'tick': 0,
        'every': 2
    },
    'foot': {
        'text': 'Footer Caption',
        'tock': 10
    },
})
../_images/timing_14_0.svg

Configuration

The config key provides a dictionary with hscale, which may be used to change the width of one period in the diagram:

logic.TimingDiagram(
    {'signal': [
        {'name': 'clk', 'wave': 'P......'},
        {'name': 'bus', 'wave': 'x.==.=x', 'data': ['head', 'body', 'tail']},
        {'name': 'wire', 'wave': '0.1..0.'}],
     'config': {'hscale': 2}})
../_images/timing_15_0.svg

Other diagram-level configuration options are specified directly as keyword arguments [1]. These may be overridden by values provided on specific signals.

  • yheight: Height of one waveform

  • ygap: Separation between two waveforms

  • risetime: Rise/fall time for wave transitions

  • fontsize: Size of label fonts

  • datafontsize: Size of data font

  • nodesize: Size of node labels

  • namecolor: Color for wave names

  • datacolor: Color for wave data text

  • nodecolor: Color for node text

  • gridcolor: Color of background grid

  • edgecolor: Color of edge notations (default blue)

  • tickcolor: Color of tick/tock labels in head/foot

  • grid: Enable grid lines (default True)

Using JSON

Because the examples from WaveDrom use JavaScript and JSON, they sometimes cannot be directly pasted into Python as dictionaries. The schemdraw.logic.timing.TimingDiagram.from_json() method allows input of the WaveJSON as a string pasted directly from the Javascript/JSON examples without modification.

Notice lack of quoting on the dictionary keys, requiring the from_json method to parse the string.

logic.TimingDiagram.from_json('''{ signal: [
  { name: "clk",  wave: "P......" },
  { name: "bus",  wave: "x.==.=x", data: ["head", "body", "tail", "data"] },
  { name: "wire", wave: "0.1..0." }
]}''')
../_images/timing_16_0.svg

Note

TimingDiagram is an Element, meaning it may be added to a schemdraw Drawing with other schematic components. To save a standalone TimingDiagram to an image file, first add it to a drawing, and save the drawing:

with schemdraw.Drawing(file='timing.svg'):
    logic.TimingDiagram(
        {'signal': [
            {'name': 'A', 'wave': '0..1..01.'},
            {'name': 'B', 'wave': '101..0...'}]})

Bit Field Diagrams

Bit Field diagrams may be drawn using schemdraw.logic.bitfield.BitField. They work similar to timing diagrams, with a single parameter dictionary defining the element, which may also be supplied in a from_json class method.

logic.BitField(
    {'reg': [
        { "name": "IPO",   "bits": 8, "attr": "RO" },
        {                  "bits": 7 },
        { "name": "BRK",   "bits": 5, "attr": "RW", "type": 4 },
        { "name": "CPK",   "bits": 2 },
        { "name": "Clear", "bits": 3 },
        { "bits": 8 }
    ],
    }
)
../_images/timing_17_0.svg

The dictionary passed to BitField may have two keys: reg and config. The reg key is a list of bit groups within the register. Each item in the list may have attributes:

  • name: Text to display within the bit group

  • bits: Number of bits within the group

  • attr: Label to show below the group. May be a string, or integer. If integer, the binary representation is shown. May also be a list of multiple lines.

  • type: 0-9 color code to fill the bit group. Or may be any valid color string.

Schemdraw adds these parameters not available in the original WaveDrom:
  • scale: Scale factor for bit width in the group

  • number: Show first and last bit numbers above the group

The config dictionary may include these key-value pairs:
  • lanes: Number of lanes

  • hflip: Reverse order of lanes

  • vflip: Reverse order of bits

  • compact: Remove whitespace between lanes

  • bits: Total number of bits to include (padded out if not included in the reg list)

  • label: Dictionary of either ‘left’ or ‘right’ and text to display left or right of the lanes.

Additional parameters may be passed directly to BitField. Values in the config dictionary above take precedence.

  • bitheight: Height of a bit register box in drawing units

  • width: Full width of the register box in drawing units

  • fontsize: Size of all text labels

  • lw: Line width for borders

  • ygap: Distance between lanes. Omit to auto-space based on label heights

  • vflip: Flip order of bits

  • hflip: Flip order of lanes

  • compact: Remove whitespace between lanes

Schemdraw’s implementation has these known differences compared to WaveDrom:

  • ‘type’ parameter, which is used to specify a fill color, can be the 0-9 code as in WaveDrom, or any valid color string

  • hspace defines the full width of the register in pixels, without including any labels

  • vspace defines the full width of a register in pixels, without including any labels or padding

  • margins are ignored (but can be set by adding the BitField to a schemdraw Drawing)

Examples are below, many borrowed from here.

logic.BitField.from_json(r'''
{reg:[
    {name: 'OP-IMM-32', bits: 7,  attr: 0b0011011},
    {name: 'rd',     bits: 5,  attr: 0},
    {name: 'func3',  bits: 3,  attr: ['SLLIW', 'SRLIW', 'SRAIW']},
    {bits: 10},
    {name: 'imm?',   bits: 7, attr: [0, 32, 32]}
]}
'''
)
../_images/timing_18_0.svg
logic.BitField.from_json(r'''
{reg: [
{bits: 8, name: "'S'", type: 4},
{bits: 8, name: "'1'", type: 4, attr: 'type'},
{bits: 8, name: "'0'", attr: 'count 0', type: 5},
{bits: 8, name: "'C'", attr: 'count 1', type: 5},
{bits: 8, attr: 'addr0', type: 2},
{bits: 8, attr: 'addr1', type: 2},
{bits: 8, attr: 'addr2', type: 2},
{bits: 8, attr: 'addr3', type: 2},
{bits: 48, name: 'data', type: 6},
{bits: 16, name: 'checksum', type: 7},
{bits: 8, name: "'\\r'"},
{bits: 8, name: "'\\n'"},
], config: {hspace: 800, lanes: 3, bits: 144, vflip: true, hflip: true}}
'''
)
../_images/timing_19_0.svg
logic.BitField.from_json('''
{'reg': [
    {'name': '0x3',   'bits': 7,  attr: 'CMD' },
    {'name': '0x001', 'bits': 1,  attr: 'REC', scale: 3 },
    {'name': 'OP_T',  'bits': 8,  attr: 'OP_T' },
    {'name': '0x0',   'bits': 13, attr: 'ARG_POINTER', scale: .5, number: false },
    {'name': '0x2',   'bits': 3,  attr: 'MODE' },
],
}
''')
../_images/timing_20_0.svg