Close

Parts and the simulation thereof

A project log for hdlpy

Hardware definition language in Python.

willemijn-coeneWillemijn Coene 03/27/2021 at 08:320 Comments

One of the more interesting things to do with a hardware definition language is of course the simulation of logic. Due to hefty build times when targeting an FPGA, simulation is the major way to develop and refine HDL projects. So of course hdlpy has to have a simulator.

Parts and signals

First I needed a way for hdlpy to know which logic signals belong together. VHDL calls this an entity, Verilog a module; hdlpy will call this a part. Of course, the bundling of code and logic signals readily maps onto a Python class, so that's what I'll use.

As an example, this is how you’d specify the signals of a simple flipflop in hdlpy:

@part
class Flipflop:
    clk: logic         # clock input
    en: logic         # input enable
    d: logic         # data input
    o: logic         # positive output
    no: logic         # negative output

Blocks

Blocks to execute whenever a certain triggering condition is met can be specified using @when, like so:

@part
class Flipflop:
    # ...
    @when(rising = (‘clk’, ‘rst’))
    def sync(self):
        if self.rst:
            self.o = 0
        elif self.clk and self.en:
            self.o = self.d

Blocks that are executed whenever any of their input signals change, used to simulate asynchronous logic, can be specified using @always:

@part
class Flipflop:
    # ...
    @always
    def async(self):
        self.no = ~self.o

Simulation

All this is fairly straightforward. Simulation then becomes the far more difficult task of deciding when to execute these blocks and actually executing them.

I ended up making use of Python's coroutine support. A coroutine is basically a generator that yields objects being awaited upon. Every block then becomes an infinite loop of first waiting for its triggers (or for a signal to change) and then executing the actual block. By implementing my own set of awaitable objects as well as an executor to handle them, I then had control over when blocks were executed.

As always, the code is on GitHub for those that are interested.

Discussions