Getting Started

sudo pip install pyrtl   or   pip install --user pyrtl

PyRTL Features

PyRTL provides a collection of classes for pythonic register-transfer level design, simulation, tracing, and testing suitable for teaching and research. Simplicity, usability, clarity, and extensibility rather than performance or optimization is the overarching goal. Features include:

The 10,000 Foot Overview

At a high level PyRTL builds the hardware structure that you explicitly define. If you are looking for a tool to take your random python code and turn it into hardware, you will have to look elsewhere -- this is not HLS. Instead PyRTL is designed to help you concisely and precisely describe a digitial hardware structure (that you already have worked out in detail) in python. PyRTL restricts you to a set of resonable digital designs practices -- the clock and resets are implicit, block memories are synchronous by default, there are no "undriven" states, and no weird un-registered feedbacks are allowed. Instead, of worrying about these "analog-ish" tricks that are horrible ideas in modern processes anyways, PyRTL let's you treat hardware design like a software problem -- build recursive hardware, write instrospective containers, and have fun building digital designs again!

To the user it provides a set of python classes that allow you to express their hardware designs reasonably pythonically. For example, with WireVector you get a structure that acts very much like a python list of 1-bit wires, so that mywire[0:-1] selects everything except the most-significant-bit. Of course you can add, subtract, and multiply these WireVectors or concat multiple bit-vectors end-to-end as well. You can then even make normal python collections of those WireVectors and do operations on them in bulk. For example, if you have a list of n different k-bit WireVectors (called "x") and you want to multiply each of them by 2 and put the sum of the result in a WireVector "y", it looks like the following: y = sum([elem * 2 for elem in x]). Hardware comprehensions are surprisingly useful. Below we get into an example in more detail, but if you just want to play around with PyRTL try Jupyter Notebooks on any of our examples on MyBinder.

Hello N-bit Ripple-Carry Adder!

While adders are a builtin primitive for PyRTL, most people doing RTL are familiar with the idea of a Ripple-Carry Adder and so it is useful to see how you might express one in PyRTL if you had to. Rather than the typical Verilog introduction to fixed 4-bit adders, let's go ahead and build an arbitrary bitwidth adder.

def one_bit_add(a, b, carry_in):
    assert len(a) == len(b) == 1  # len returns the bitwidth
    sum = a ^ b ^ carry_in  # operators on WireVectors build the hardware
    carry_out = a & b | a & carry_in | b & carry_in
    return sum, carry_out

def ripple_add(a, b, carry_in=0):
    a, b = pyrtl.match_bitwidth(a, b)
    if len(a) == 1:
        sumbits, carry_out = one_bit_add(a, b, carry_in)
    else:
        lsbit, ripplecarry = one_bit_add(a[0], b[0], carry_in)
        msbits, carry_out = ripple_add(a[1:], b[1:], ripplecarry)
        sumbits = pyrtl.concat(msbits, lsbit)
    return sumbits, carry_out

# instantiate an adder into a 3-bit counter
counter = pyrtl.Register(bitwidth=3, name='counter')
sum, carry_out = ripple_add(counter, pyrtl.Const("1'b1"))
counter.next <<= sum

# simulate the instantiated design for 15 cycles
sim_trace = pyrtl.SimulationTrace()
sim = pyrtl.Simulation(tracer=sim_trace)
for cycle in range(15):
    sim.step({})
sim_trace.render_trace()

The code above includes an adder generator with python-style slices on wires (ripple_add), an instantiation of a register (used as a counter with the generated adder), and all the code needed to simulate the design, generate a waveform, and render it to the terminal. The way this particular code works is described more in the examples directory. When you run it, it should look like this (you can see the counter going from 0 to 7 and repeating):

Command-line waveform for PyRTL counter

A Few Gotchas

While python is an amazing language, DSLs in python are always forced to make a few compromises which can sometime catch users in some unexpected ways. Watch out for these couple of "somewhat surprising features"

Command-line waveform for PyRTL state machine

Related Projects

MyHDL is a neat Python hardware project built around generators and decorators. The semantics of this embedded language are close to Verilog and unlike PyRTL, MyHDL allows asynchronous logic and higher level modeling. Much like Verilog, only a structural "convertible subset" of the language can be automatically synthesized into real hardware. PyRTL requires all logic to be both synchronous and synthesizable which avoids a common trap for beginners, it elaborates the design during execution allowing the full power of python in describing recursive or complex hardware structures, and allows for hardware synthesis, simulation, test bench creation, and optimization all in the same framework.

Chisel is a project with similar goals to PyRTL but is based instead in Scala. Scala provides some very helpful embedded language features and a rich type system. Chisel is (like PyRTL) a elaborate-through-execution hardware design language. With support for signed types, named hierarchies of wires useful for hardware protocols, and a neat control structure call "when" that inspired our conditional contexts, Chisel is a powerful tool used in some great research projects including OpenRISC. Unlike Chisel, PyRTL has concentrated on a complete tool chain which is useful for instructional projects, and provides a clearly defined and relatively easy to manipulate intermediate structure in the class Block (often times call pyrtl.core) which allows rapid prototyping of hardware analysis routines which can then be codesigned with the architecture.

Yosys is an open source tool for Verilog RTL synthesis. It supports a huge subset of the Verilog-2005 semantics and provides a basic set of synthesis algorithms. The goals of this this tool are quite different from PyRTL, but the two play very nicely together in that PyRTL can output Verilog that can then be synthesized through Yosys. Likewise Yosys can take Verilog designs and synthesize them to a very simple library of gates and output them as a "blif" file which can then be read in by PyRTL.

PyMTL is an alpha stage "open-source Python-based framework for multi-level hardware modeling". One of the neat things about this project is that they are trying to allow simulation and modeling at multiple different levels of the design from the functional level, the cycle-close level, and down to the register-transfer level (where PyRTL really is built to play). Like MyHDL they do some meta-programming tricks like parsing the Python AST to allow executable software descriptions to be (under certain restrictions -- sort of like verilog) automatically converted into implementable hardware. PyRTL, on the other hand, is about providing a limited and composable set of data structures to be used to specify an RTL implementation, thus avoiding the distinction between synthesizable and non-synthesizable code (the execution is the elaboration step).

C╬╗aSH is a hardware description embedded DSL in Haskell. Like PyRTL it provides an approach suitable for both combinational and synchronous sequential circuits and allows the transform of these high-level descriptions to low-level synthesizable Verilog HDL. Unlike PyRTL, designs are statically typed (like VHDL), yet with a very high degree of type inference, enabling both safe and fast prototying using concise descriptions. If you like functional programing and hardware I would also checkout out Lava.