JulianKemmerer
PipelineC
JulianKemmerer/PipelineCPypelineC: A hardware description language (HDL) adding high level synthesis(HLS)-like automatic pipelining as a language construct/compiler feature.
██████╗ ██╗ ██╗██████╗ ███████╗██╗ ██╗███╗ ██╗███████╗ ██████╗
██╔══██╗╚██╗ ██╔╝██╔══██╗██╔════╝██║ ██║████╗ ██║██╔════╝██╔════╝
██████╔╝ ╚████╔╝ ██████╔╝█████╗ ██║ ██║██╔██╗ ██║█████╗ ██║
██╔═══╝ ╚██╔╝ ██╔═══╝ ██╔══╝ ██║ ██║██║╚██╗██║██╔══╝ ██║
██║ ██║ ██║ ███████╗███████╗██║██║ ╚████║███████╗╚██████╗
╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═════╝
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
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.
@interfacebundles 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 :).
社区
