JulianKemmerer GitHub avatar

JulianKemmerer

PipelineC

JulianKemmerer/PipelineC

PypelineC: A hardware description language (HDL) adding high level synthesis(HLS)-like automatic pipelining as a language construct/compiler feature.

██████╗ ██╗   ██╗██████╗ ███████╗██╗     ██╗███╗   ██╗███████╗ ██████╗
██╔══██╗╚██╗ ██╔╝██╔══██╗██╔════╝██║     ██║████╗  ██║██╔════╝██╔════╝
██████╔╝ ╚████╔╝ ██████╔╝█████╗  ██║     ██║██╔██╗ ██║█████╗  ██║     
██╔═══╝   ╚██╔╝  ██╔═══╝ ██╔══╝  ██║     ██║██║╚██╗██║██╔══╝  ██║     
██║        ██║   ██║     ███████╗███████╗██║██║ ╚████║███████╗╚██████╗
╚═╝        ╚═╝   ╚═╝     ╚══════╝╚══════╝╚═╝╚═╝  ╚═══╝╚══════╝ ╚═════╝

Painted PypelineC

What is PypelineC?

A hardware description language (HDL) adding high level synthesis(HLS)-like automatic pipelining as a language construct/compiler feature.

If a computation can be written as a pure function without side effects (i.e. no registers/static variables) then it can be auto-pipelined. Conceptually similar to technologies like Intel's variable latency Hyper-Pipelining and Xilinx's retiming options. Sharing some of the compiler driven pipelining design goals of Google's XLS Project, the DFiantHDL language, and certain CIRCT dialects as well.

PypelineC consists of Pypeline (new, Python based) and PipelineC (legacy, C based). Pypeline is a work in progress in becoming feature complete with PipelineC, but already has many new features that PipelineC lacks.

Example code for blinking an LED:

Pypeline (examples/pypeline/blink.py) PipelineC (examples/blink.c)
# 'Called'/'Executing' every 40ns (25MHz)
@MAIN(25.0)
def blink() -> uint1_t:
    # 25000000 iterations * 40ns each = 1 sec
    counter: Reg[uint32_t] = 0

    # LED on/off state
    led: Reg[uint1_t] = 0

    # If reached 1 second
    if counter == (25000000 - 1):
        led = ~led  # Toggle led
        counter = 0  # Reset counter
    else:
        counter += 1 # one 40ns increment

    return led
// 'Called'/'Executing' every 40ns (25MHz)
#pragma MAIN_MHZ blink 25.0
uint1_t blink()
{
  // 25000000 iterations * 40ns each = 1sec
  static uint25_t counter = 0;

  // LED on off state
  static uint1_t led = 0;

  // If reached 1 second
  if(counter==(25000000-1))
  {
    // Toggle led
    led = !led;
    // Reset counter
    counter = 0;
  }
  else
  {
    counter += 1; // one 40ns increment
  }
  return led;
}
Pypeline PipelineC
Getting started /docs directory GitHub wiki
Easy to understand software-like syntax Yes Yes
Timing feedback from synthesis+pnr tools Yes Yes
Automatic pipelining of comb. logic Yes Yes
Dev board specific support packages Yes Yes
VHDL Output Yes (human readable) Yes (human readable)
VHDL based existing module import Yes Yes
Verilog Output Yes (machine converted) Yes (machine converted)
Verilog based existing module import No No
Traditional HDL simulator support Yes Yes
Native Simulation Yes No
Valid-Ready handshaking Yes Yes
Globally visible point to point wires Yes Yes
Multiple clock domains / Clock domain crossings Yes Yes
Parameterized/Template Functions+Types Yes No
Operator overloading Yes Yes (hacky)
User-visible automatic pipeline latency Yes No
Multi-cycle path constraints Yes Yes
Automatic multi-cycle path tuning (New) Yes No
Combinational area / delay optimization (New, experimental) Yes No
Automatic resource-shared state machines (New, experimental) Yes No
SoC system bus helpers No Yes
Generates software Helper Code Yes Yes
Derived FSM style code No Yes
Documentation Comprehensive, planned Ad-hoc, organic
Compiler tests Many, Automated Limited, Hand-run

Tools:

Currently Supported Tools (Linux only):
Synthesis: 
  Xilinx Vivado, 
  OpenXC7 (Xilinx 7-series: GHDL+Yosys+nextpnr-xilinx+Project X-Ray),
  Intel Quartus, 
  Lattice Diamond, 
  GHDL+Yosys+nextpnr (ECP5/iCE40),
  Gowin EDA, 
  Efinix Efinity,
  Cologne Chip Toolchain,
  PyRTL Models
Simulation: 
  Modelsim, 
  Verilator,
  cocotb,
  CXXRTL, 
  EDAPlayground

PypelineC Tool Flow

Why Pypeline?

Pypeline describes hardware directly, but replaces much of the ceremony of traditional RTL with compact Python syntax and reusable abstractions:

  • RTL semantics without event-driven boilerplate. A typed function is a hardware module, ordinary locals are combinational wires, and Reg[T] is explicit state. There are no sensitivity lists or separate combinational and clocked process templates to maintain; the execution model states exactly what becomes wiring, a MUX, a register, or a module instance.
  • Reusable, parameterized hardware. Ordinary compile-time Python can create specialized functions and types with factory functions, including width- and type-parameterized structs, arrays, streams, FIFOs, and arithmetic blocks.
  • Interfaces with direction and structure. @interface bundles bidirectional ports, including forward payloads and reverse ready/credit signals, while interface functions can generate reverse-path wiring instead of requiring every signal to be connected by hand.
  • Composition instead of port-map repetition. Calling a hardware function instantiates a module; types and return values carry connections through a hierarchy using normal function composition.
  • A real elaboration language. Compile-time Python supports loops, constants, type introspection, user-defined factories, and custom operators, enabling libraries to express higher-level hardware patterns rather than repeatedly manipulating individual wires.
  • Automation with conventional HDL output. Pure functions can use automatic pipelining and other implementation strategies, with synthesis timing feedback guiding the result. Higher-level parameterization is elaborated before Pypeline emits human-readable VHDL, rather than depending on every downstream tool to implement equivalent HDL language features. A raw VHDL escape hatch for existing IP or vendor-specific primitives keeps the generated design in established FPGA and simulation flows.

Fundamental design elements are state machines/stateful elements(registers, rams, etc), auto-pipelined stateless pure functions, and interconnects (wires,cdc,async fifos,etc).

By isolating complex logic into auto-pipelineable functions, and only writing literal clock by clock hardware description when absolutely necessary, PypelineC designs do not need to be rewritten for each new target device / operating frequency. The hope is to build shared, high performance, device agnostic, hardware designs described in a familiar and powerfully composable software-like look.

For software folks writing PypelineC should feel like solving a programming puzzle - the rules of the puzzle hide/imply hardware concepts. For hardware folks PypelineC is a better hardware description language trying to find middle ground between traditional RTL and HLS. It is my language of choice as an FPGA engineer :).

View README on GitHub

COMMUNITY

Save, rate, and discuss

Loading community data…