Open-source project
JulianKemmerer/PipelineC avatar
JulianKemmerer/PipelineC

PipelineC, an HDL where the compiler adds the pipeline for you

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

754 stars64 forksPythonGPL-3.0

At a glance

What is it?
JulianKemmerer/PipelineC is a hardware description language that treats pipelining as a language construct rather than a manual timing exercise, and it now has a Python-based successor called Pypeline that generates the same VHDL and Verilog. The C-based original is still there, and the tool flow reaches Vivado, Quartus, Yosys and Verilator.
Who is it for?
Adopt Pypeline if you write FPGA designs where the hard part is timing rather than logic, and you want the compiler to insert pipeline registers and tell you the resulting latency. Skip it if your design needs multiple clock domains, because the Python version does not support them, or if you need a mature simulator ecosystem and native simulation support from the C version.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The core idea: pipelining as a language feature

PipelineC is a hardware description language that adds high level synthesis-like automatic pipelining as a language construct and compiler feature. The rule is simple: if a computation can be written as a pure function without side effects, meaning no registers and no static variables, then it can be pipelined automatically.

The README compares this to Intel's variable latency hyper-pipelining and Xilinx's retiming options, both of which are tool features you turn on and then live with the results. PipelineC moves the decision into the source language, which means the pipeline is visible to the programmer rather than inferred by a synthesis tool from a netlist.

It also names its intellectual relatives: the compiler-driven pipelining design goals of Google's XLS project, the DFIantHDL language, and certain CIRCT dialects. That is a useful pointer for anyone who wants to know what the design is derived from.

The repository is two projects. Pypeline is the new, Python-based one. PipelineC is the legacy C-based one, kept with its own wiki. The README says Pypeline is a work in progress towards feature parity with PipelineC but already has many features PipelineC lacks, and the comparison table that follows is a candid, item by item account of where each stands.

What the table shows is that Pypeline wins on the newer items. It has native simulation support, which PipelineC does not. It exposes the automatically discovered pipeline latency to the user, and PipelineC does not. It has automatic multi-cycle path tuning, operator overloading that is not hacky, and a set of experimental features for combinational area and delay optimisation and for automatically resource-sharing state machines, none of which exist in the C version.

Where PipelineC still wins is on the older infrastructure. It supports multiple clock domains and clock domain crossings, has SoC system bus helpers, and can generate derived FSM style code. Pypeline does not support Verilog based existing module import either, which is a limit of both.

The two languages side by side, in a blinking LED

The README shows the same LED blink in both languages, which makes the design difference concrete.

In Pypeline, the module is a typed Python function with a decorator setting the clock:

python
@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

The PipelineC equivalent is the same logic in C with a pragma for the clock:

c
#pragma MAIN_MHZ blink 25.0
uint1_t blink()
{
  static uint25_t counter = 0;
  static uint1_t led = 0;

  if(counter==(25000000-1))
  {
    led = !led;
    counter = 0;
  }
  else
  {
    counter += 1;
  }
  return led;
}

The two are close enough that the correspondence is easy to follow, and the difference is mostly in the declaration of state. In Pypeline, the type annotation Reg says explicitly that this is a register, and the ordinary locals are combinational wires. In the C version, state is expressed with static variables, and the compiler infers which of them need to be registers.

That inference is exactly what the Pypeline design is trying to remove. The README describes the execution model as stating exactly what becomes wiring, a multiplexer, a register, or a module instance, and notes that there are no sensitivity lists and no separate combinational and clocked process templates to maintain, which is the ceremony that makes traditional RTL verbose.

The other structural difference is the parameterisation. The README describes reusable, parameterized hardware created with ordinary compile-time Python, including width and type-parameterised structs, arrays, streams, FIFOs and arithmetic blocks, and interface functions that bundle bidirectional ports with forward payloads and reverse ready and credit signals, and that can generate reverse-path wiring instead of requiring every signal to be connected by hand.

The two languages and where the gap still matters

The comparison table in the README is the most useful document in the repository, and it is worth reading as a list of decisions rather than as a feature matrix.

Pypeline's advantages cluster around visibility and automation. The user-visible automatic pipeline latency is the one that changes how you work, because instead of finding out that your design got three cycles slower after synthesis, you read the depth off the compile. Automatic multi-cycle path tuning is the same idea applied to paths that legitimately take more than one cycle, which in a design with a divider or a multiplier is the difference between a clean compile and a timing failure.

The experimental items are the interesting risk. Combinational area and delay optimisation, and automatic resource-shared state machines, are the kind of thing that is very useful when it works and very hard to debug when it does not. The README marks them experimental, and the sensible approach is to enable them on a design where you can check the result, not on the one that has to work.

PipelineC's advantages are all in the older infrastructure. Multiple clock domains and clock domain crossings are supported in the C version and not in the Python one, and for a system design with a slow management domain talking to a fast datapath that is a real difference. The SoC system bus helpers and the derived FSM style code generation are the same kind of thing: a decade of accumulated convenience for a specific kind of design.

Documentation and testing are worth noting too. The README describes the Pypeline documentation as comprehensive and planned, against ad-hoc and organic documentation for PipelineC, and Pypeline has many automated compiler tests against limited hand-run tests for the C version. That difference in test coverage is what makes a rewrite of a compiler language tractable at all, and it is the more important signal in the table.

The practical guidance is straightforward. Start with Pypeline for new work, since the direction of travel is clear and the automation is better. Use PipelineC when clock domains matter or when you need its bus helpers, and when you do, plan for the fact that the C version is legacy and receives less work. The two coexist in the same repository, and the examples directory shows both being used.

The tool flow and what the compiler actually produces

The supported tools are listed for Linux only, and the breadth is the point.

For synthesis there is Xilinx Vivado, OpenXC7 which is a GHDL plus Yosys plus nextpnr-xilinx plus Project X-Ray combination covering Xilinx 7-series devices, Intel Quartus, Lattice Diamond, a GHDL plus Yosys plus nextpnr flow for ECP5 and iCE40, Gowin EDA, Efinix Efinity, the Cologne Chip toolchain, and PyRTL models as a target.

For simulation there is Modelsim, Verilator, cocotb, CXXRTL and EDAPlayground. cocotb is the notable inclusion, because it means the generated design can be verified with a Python testbench rather than a Verilog one, which suits a project whose input language is already Python.

The output side is worth being precise about. Both languages emit VHDL and Verilog. The README describes the VHDL output as human readable and the Verilog output as machine converted, which tells you which of the two is the first-class target. It also notes that VHDL based existing modules can be imported into both, while Verilog based existing module import is not supported in either.

The README also points at timing feedback from the synthesis and place-and-route tools as a feature of both languages, and the top level entry points documentation as the place to look. The generated code includes host-type export declarations, that is, helper code on the software side, in both.

For Pypeline specifically there is a native simulation path that does not need a third-party simulator at all, which combined with the PyRTL model target makes it possible to check a design before committing to a vendor toolchain. That is a meaningful difference for iteration speed, and it is one of the concrete reasons the newer language is worth preferring even while it is incomplete.

Repository layout, examples and the state of the work

The repository top level shows a project split between the two languages. There is a src/ directory for the compiler, include/ for the headers, config/ and cache/ for build configuration, docs/ for the guides, examples/ for the sample designs, nix/ and a default.nix for the development environment, a pyproject.toml and a ruff.toml for the Python side, and a .pre-commit-config.yaml.

The examples directory is where the ambition of the project shows. Beyond the blink example there are adder_tree.c, aes, aof25 which is an authenticated encryption construction, bit_slicing.c, bram.c, camera, chacha20poly1305, a cordic.h, a dct, and an axi directory. There are also c_puzzle, cloud and edge oriented examples such as NexusProofOfWork and aws-fpga-dma, timing experiments including delay_compare_experiment.c and div_pipeline_experiment.c, and clock domain crossing examples in clock_crossing.c and async_clock_crossing.c. There is a cocotb directory, an arty directory for a specific development board, and a vhdl import example in the examples listing.

That list is a good proxy for what the language is for. The cryptographic constructions and the discrete cosine transform are the kind of arithmetic where a combinational implementation is enormous and a pipelined one is compact, and the timing experiment files are the kind of thing you write to measure what the automatic pipelining actually did.

The build is managed by poetry with dynamic versioning from git, producing a version string of the form 0.0.0.dev0 plus a timestamp and commit hash. The project requires Python 3.8 or newer and depends on PyRTL, which is the Python RTL library used for the model targets. Ruff and pre-commit handle linting.

The licence is GPL-3.0, the last push was on 2026-10-18, and there are no GitHub releases. The version numbers in the tools section describe the C-based project; the Python one is the one to watch, since that is where the new work is.

Editorial conclusion

Adopt Pypeline if you write FPGA designs where the hard part is timing rather than logic, and you want the compiler to insert pipeline registers and tell you the resulting latency. Skip it if your design needs multiple clock domains, because the Python version does not support them, or if you need a mature simulator ecosystem and native simulation support from the C version. Verify first by compiling the blink example and reading the reported pipeline latency, then check that the generated VHDL matches what your synthesis tool expects before you build a larger design on it.

Frequently asked questions

What is PipelineC and what does automatic pipelining mean here?

PipelineC is a hardware description language that treats pipelining as a language construct and compiler feature. If a computation can be written as a pure function without side effects, no registers or static variables, the compiler can pipeline it automatically, which is comparable to Intel variable latency hyper-pipelining or Xilinx retiming but decided in the source rather than inferred by the tool.

What is the difference between Pypeline and PipelineC?

Pypeline is the new Python-based rewrite and PipelineC is the legacy C-based original. Pypeline is a work in progress towards feature parity but already has native simulation, user-visible pipeline latency, automatic multi-cycle path tuning and experimental area and delay optimisation, while PipelineC retains multiple clock domains, clock domain crossing support and SoC bus helpers.

Which synthesis and simulation tools does Pypeline support?

On Linux, synthesis through Xilinx Vivado, OpenXC7 using GHDL, Yosys and nextpnr-xilinx, Intel Quartus, Lattice Diamond, a GHDL, Yosys and nextpnr flow for ECP5 and iCE40, Gowin EDA, Efinix Efinity, the Cologne Chip toolchain and PyRTL models. Simulation through Modelsim, Verilator, cocotb, CXXRTL and EDAPlayground.

What output formats do the Pypeline and PipelineC compilers produce?

Both generate VHDL and Verilog. The README describes the VHDL output as human readable and the Verilog output as machine converted, and both support importing existing VHDL modules, while Verilog based module import is not supported in either language.

How do you build Pypeline?

With poetry, using dynamic versioning from git to produce a development version string. The project requires Python 3.8 or newer and depends on PyRTL, the Python RTL library used for the model targets, with ruff and pre-commit for linting.

Official sources

  1. Issues
  2. JulianKemmerer/PipelineC on GitHub
  3. License: GPL-3.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/juliankemmerer-pipelinec.svg)](https://hysenlabs.com/projects/juliankemmerer-pipelinec)

Was this useful?