Posted on

MyHDL and (p)yosys: direct synthesis using Python

MyHDL as of now provides a few powerful conversion features through AST (abstract syntax tree) parsing. So Python code modules can be parsed ‘inline’ and emit VHDL Verilog, or my own XML based concoction XHDL (work in progress).

Simple counter example

Let’s have a look at a MyHDL code snippet. This is a simple counter, that increments when ce is true (high). When it hits a certain value, the dout output is asserted to a specific value.

@block
def test_counter(clk, ce, reset, dout, debug):
    counter = Signal(modbv(0)[8:])
    d = Signal(intbv(3)[2:])

    @always_seq(clk.posedge, reset)
    def worker():
        if ce:
            counter.next = counter + 1

    @always_comb
    def assign():
        if counter == 14:
            dout.next = 1
            debug.next = 1
        elif counter == 16:
            debug.next = 1
            dout.next = 3
        else:
            debug.next = 0
            dout.next = 0

    return instances()

When we run a simple test bench which provides a clock signal clk, and some pseudo random assertion of the ce pin, we get this:

Waveform of simulation

Now, how do we pass this simple logic on to yosys for synthesis?

Pyosys python wrapper

The pyosys module is a generated python wrapper, covering almost all functionality from the RTLIL yosys API. In short, it allows us to instanciate a design and hardware modules and add hardware primitives. It’s like wiring up 74xx TTL logic chips, but the abstract and virtual way.

Means, we don’t have to create Verilog or VHDL from Python and run it through the classic yosys passes, we can emit synthesizeable structures directly from the self-parsing HDL.

Way to synthesis

Now, how do we get to synthesizeable hardware, and how can we control it?

We do have a signal representation after running the analysis routines of MyHDL. Like we used to convert to the desired transfer language, we convert to a design, like:

a = test_counter(clk, ce, reset, dout, debug)
a.convert("verilog")
a.convert("yosys_module")

The yosys specific convert function, as of now, calls the pyosys interface to populate a design with logic and translates the pre-analysed MyHDL signals into yosys Wire objects and Signals, that are finally needed to create the fully functional chain of the logic zoo. The powerful ‘dot’ output allows us to look at what’s being created from the above counter example (right click on image and choose ‘view’ to see it in full size):

Schematic of synthesis, first output stage

You might recognize the primitives from the hardware description. A compare node if counter == 14 translates directly to the $eq primitive with ID $2. A Data flip flop ($dff) however is generated somewhat implicit by the @always_seq decorator from the output of a multiplexer. And note: This $dff is only emitted, because we have declared the reset signal as synchronous from the top level definition. Otherwise, a specific asynchronous reset $adff would be instanciated.

The multiplexers finally are those nasty omnipresent elements that route signals or represent decisions made upon a state variable, etc.

You can see a $mux instanciated for the reset circuit of the worker() function, appended to another $mux taking the decision for the ce pin whether to keep the counter at its present value or whether to increment it ($11). The $pmux units are parallel editions that cover multiple cases of an input signal. Together with the $eq elements, they actually convert well to a lookup table — the actual basic hardware element of the FPGA.

Verification

Now, how would we verify if the synthesized output from the MyHDL snippet works correctly? We could do that using yosys’ formal verification, but there’s a step we can take beforehand: Co-Simulation from within MyHDL against a known working reference, like a Verilog simulation.

In short, this is what’s happening:

  • Functional MyHDL simulation of the unit under test with random stimulation
  • Generation of Verilog code of the synthesized result
  • Comparison of the MyHDL model output against the Verilog simulation output by the cycle-synchronous Co-Simulation functionality of MyHDL

There are some advantages to this approach:

  • We can verify the basic correctness of direct Python HDL to yosys synthesis
  • We can match against a known good reference of a Verilog simulator by emitting Verilog code via MyHDL
  • Likewise, we can also verify against emitted VHDL code

Roadmap

  • Set up more complex test suite and coverage scenarios (github ‘yosys’ myhdl fork in preparation)
  • Run a really big project through it
  • Acquire alpha testers and developers
  • [ Extensions/Changes of MyHDL to modularize for large projects ]
Posted on

MaSoCist support of OpenSource synthesis for ECP5

Find the a (currently unstable) development branch here:

https://github.com/hackfin/MaSoCist/tree/ghdlsynth_release

Configurations that work (from those appearing when you run ‘make which’ in the masocist top dir):

  • *-zpu-ghdlsynth: ZPUng setup with ‘beatrix’ configuration.
  • *-pyrv32-ghdlsynth: RISC-V 32 bit basic configuration, proof of concept only, not fully functional as SoC in synthesis (as of now)
    Note: You need to explicitely install the rv32 toolchain (inside the docker container) for this config:
    sudo apt-get install riscv32-binutils riscv32-gcc riscv32-newlib-libc

Instructions

This can be done online in a browser, if you don’t run Linux, see also https://section5.ch/index.php/2019/10/24/risc-v-in-the-loop/.

Note: Since this setup depends on external packages, there is no guarantee it will build smoothly.

  1. Run docker container with exported USB devices (if you want to program the plugged in board right away):
    docker run -it --device=/dev/bus/usb hackfin/masocist:synth
  2. Pull synthesis self-extracting build script:
    wget https://section5.ch/downloads/masocist-synth_sfx.sh && sh masocist-synth_sfx.sh
  3. Pull packages and build:
    make all
  4. Configure platform:
    cd src/vhdl/masocist-opensource;
    make versa_ecp5-zpu-ghdlsynth
  5. Build for synthesis:
    make clean sw syn
  6. When successful, you’ll end up with a $(PLATFORM).SVF file in syn/.
  7. See next paragraph on how to program the board (this procedure will be simplified)

Supported boards

Currently, the following ECP5 based boards are supported/under scrutiny:

Programming with on board FT2232H JTAG interface:

Note that programming will only work when the container is run/started after plugging in the board.

To program the FPGA SRAM on the board with the produced SVF file:

  1. Make sure board is connected via USB and powered up.
  2. You may need to restart the container from above:
    docker start -i <id>
    where ‘id’ is the container id of the above stopped container (retrieve from shell history or with docker ps -a)
  3. Install openocd:
    sudo apt-get install openocd
  4. Make sure board is connected via USB and powered up, then run, inside $(MASOCIST)/syn:
    make download OPENOCD="sudo openocd"
  5. If you see a lot scrolling by, board programming tends to be successful (interface was recognized). You can ignore errors like:
    Error: tdo check error at line 26780
    as they are due to the changed USERCODE.
  6. You should see the segment display on the board ‘spinning’. Also, you can talk to the SoC through the UART at 115200, 8N1, for example using minicom:
    minicom -o -D /dev/ttyUSB1
    The output upon booting of the SoC should be:
Probing flash…
Flash Type: m25p128         
Booting beatrix HW rev: 04 @25 MHZ
------------- test shell -------------                                          
--        SoC for Versa ECP5        --                                          
            arch: ZPUng                                                          
--  (c) 2012-2020  www.section5.ch  --                                          
--     type 'h' for help            --                                          
 

Quirks

Short summary on what works in particular and what does not:

Ram inference

RAM inference is currently problematic and needs to be investigated further. TODO:

  • Make synthesis recognize more variants of RAM with init values
  • Implement dual process true dual port RAM
  • Fully eliminate Verilog RAM wrapper workarounds

FSM optimization

Some FSM seem to optimize away in yosys. Needs to be investigated if it’s a VHDL synthesis or internal Yosys issue.

Vendor primitives

Vendor specific Black Box primitives currently need to be wrapped explicitely via Verilog, see also:

https://github.com/tgingold/ghdlsynth-beta/issues/46

You’ll find an example in this repo in examples/ecp5_versa. See also library/wrappers on how to integrate black boxes. An ecp5um.components package was added to the above library.

So far tested primitives within MaSoCist:

  • JTAGG: Test access port to ZPUng and pyrv32 for JTAG debugging or automated in circuit emulation hardware tests.
  • EHXPLLL: PLL primitive for clock frequency conversion
  • USRMCLK: Access to SPI master clock on ECP5

Not working

  • System Interrupt Controller (CONFIG_SIC) is currently not supported: #1140
    Fixed. Make sure to install the up to date debian GHDL packages when reusing an old container:
    sudo apt-get update; sudo apt-get install ghdl ghdl-libs
    You also have to rebuild the ghdl.so module in src/ghdlsynth-beta:
    make clean all; sudo make install
  • FLiX DSP and JPEG core unsupported (due to true dual port RAM issues)
  • Under scrutiny: pktfifo (CONFIG_MAC) problematic (TDP BRAM issues)
  • Post map simulation does not work with DP16KD primitives, due to missing ‘whitebox’ model, see also #32. You will have to separately use the supplied vendor model from the Diamond libraries.
Posted on

netpp 0.5 repo migration

This took a while: quite a bit of migration work had to be done to move on with current opensource state of the art: Gladly announcing hereby that the main opensource development is now going to take place on gitlab:

https://gitlab.com/hackfin/netpp

Some of the major changes involved:

  • All code now hopefully LGPL compatible
  • Vendor specific protocol details mangled into ‘compatibility mode’
  • Dynamic property and basic proxy support
  • Automated testing integrated into gitlab Continuous Integration pipeline
  • Protocol revision 0x2 with improved up/downward compatibility: Note: Vendor specific protocol extensions are no longer supported in this branch.

Work in progress / road map:

  • Extended memory regression tests to be merged into gitlab CI (valgrind, etc.)
  • Python 3.0 support (volunteers welcome)
  • Migration of more examples and reference implementations into OpenSource
  • Total elimination of any future dual licensing issues and corresponding maintenance overhead

SCADA integration

The pvdevelop rapid prototyping tool from the pvbrowser SCADA software suite was patched to allow direct integration with a netpp property manager framework. Creating a GUI is very simple: Place a widget, give it the ObjectName of a netpp Property, compile, run and open up the connection to a netpp device. The GUI for the example server (devices/example/slave) supports almost all standard widgets from the pvserver Qt widget library.