Skip to content

Commit 80ffd77

Browse files
authored
[Synth][AIG][BLIF] Adding new Synth Dialect and BLIF Importer and Exporter (#756)
The following PR adds a new dialect Synth to represent AIG at MLIR level. Additionally, it adds two new passes to import a BLIF file and represent it with the Synth dialect and to convert the Synth Dialect into a BLIF file. For more information related to this PR, please look at the newly added docs.
1 parent 6b27a46 commit 80ffd77

48 files changed

Lines changed: 9643 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/unittests.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: unittests
2+
3+
on:
4+
# Make sure that settings are set to require permission
5+
# to run workflows by external contributors!
6+
pull_request:
7+
paths-ignore:
8+
- 'docs/**'
9+
branches: ["main"]
10+
types: [opened, synchronize, reopened, ready_for_review]
11+
12+
push:
13+
paths-ignore:
14+
- 'docs/**'
15+
branches: ["main"]
16+
17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
unittests:
23+
runs-on: ubuntu-latest
24+
25+
steps:
26+
- name: cleanup
27+
run: |
28+
echo "Emptying folder"
29+
rm -rf -- * .*
30+
echo "Folder contents"
31+
ls -la
32+
33+
- name: Checkout repository
34+
uses: actions/checkout@v4
35+
36+
- name: Update apt and install prerequisites
37+
run: |
38+
sudo apt-get -y update
39+
sudo apt-get -y install software-properties-common
40+
sudo add-apt-repository ppa:deadsnakes/ppa
41+
42+
- name: Install system dependencies
43+
run: |
44+
sudo apt-get install -y \
45+
--option APT::Immediate-Configure=false \
46+
sudo vim clang lld ccache cmake wget \
47+
ninja-build python3 graphviz git curl \
48+
gzip libreadline-dev \
49+
libboost-all-dev pkg-config python3.12 \
50+
python3.12-venv python3.12-dev \
51+
ghdl verilator
52+
53+
- name: Verify Python 3.12
54+
run: python3.12 --version
55+
56+
- name: build
57+
run: ./build.sh --release --use-prebuilt-llvm --enable-abc --enable-cbc --force
58+
59+
- name: check-blif-importer-exporter
60+
if: steps.build.outputs.exit_code == 0
61+
run: ninja run-blif-equiv-tests
62+
working-directory: build
63+
64+

CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,32 @@ if(DYNAMATIC_ENABLE_CBC)
245245
endif()
246246
endif()
247247

248+
#-------------------------------------------------------------------------------
249+
# ABC setup
250+
#-------------------------------------------------------------------------------
251+
252+
if(DYNAMATIC_ENABLE_ABC)
253+
if (UNIX)
254+
255+
# Disable reproducibility warning for third-party code
256+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=date-time -Wno-date-time")
257+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=date-time -Wno-date-time")
258+
259+
FetchContent_Declare(
260+
abc
261+
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
262+
SOURCE_DIR ${CMAKE_BINARY_DIR}/abc
263+
URL https://github.com/ETHZ-DYNAMO/abc/archive/refs/tags/v1.0.0.tar.gz
264+
SYSTEM
265+
)
266+
267+
FetchContent_MakeAvailable(abc)
268+
add_compile_definitions(DYNAMATIC_ENABLE_ABC)
269+
else()
270+
message(FATAL_ERROR "ABC integration is not tested on the OS you use!")
271+
endif()
272+
endif()
273+
248274
#-------------------------------------------------------------------------------
249275
# Python (Virtual Environment)
250276
#-------------------------------------------------------------------------------

build.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ List of options:
3030
checking
3131
--use-prebuilt-llvm : download and use the prebuilt LLVM
3232
--enable-cbc : enable the CBC milp solver
33+
--enable-abc : enable the ABC logic synthesis tool
3334
--build-legacy-lsq : build the legacy chisel-based lsq
3435
--check | -c : run tests during build
3536
--help | -h : display this help message
@@ -143,6 +144,7 @@ PREBUILT_LLVM=0
143144
BUILD_CHIESEL_LSQ=0
144145
ENABLE_CBC=0
145146
CMAKE_DYNAMATIC_ENABLE_CBC=""
147+
CMAKE_DYNAMATIC_ENABLE_ABC=""
146148
LLVM_DIR="$PWD/llvm-project/build"
147149

148150
# Loop over command line arguments and update script variables
@@ -206,6 +208,9 @@ do
206208
CMAKE_DYNAMATIC_ENABLE_CBC="-DDYNAMATIC_ENABLE_CBC=ON"
207209
ENABLE_CBC=1
208210
;;
211+
"--enable-abc")
212+
CMAKE_DYNAMATIC_ENABLE_ABC="-DDYNAMATIC_ENABLE_ABC=ON"
213+
;;
209214
"--build-legacy-lsq")
210215
BUILD_CHIESEL_LSQ=1
211216
;;
@@ -359,6 +364,7 @@ if should_run_cmake ; then
359364
$CMAKE_LLVM_BUILD_OPTIMIZATIONS \
360365
$CMAKE_DYNAMATIC_ENABLE_XLS \
361366
$CMAKE_DYNAMATIC_ENABLE_CBC \
367+
$CMAKE_DYNAMATIC_ENABLE_ABC \
362368
$CMAKE_DYNAMATIC_ENABLE_LEQ_BINARIES
363369

364370
LLVM_DIR="../build/llvm-project"
@@ -375,6 +381,7 @@ if should_run_cmake ; then
375381
$CMAKE_DYNAMATIC_BUILD_OPTIMIZATIONS \
376382
$CMAKE_DYNAMATIC_ENABLE_XLS \
377383
$CMAKE_DYNAMATIC_ENABLE_CBC \
384+
$CMAKE_DYNAMATIC_ENABLE_ABC \
378385
$CMAKE_DYNAMATIC_ENABLE_LEQ_BINARIES
379386
fi
380387
exit_on_fail "Failed to cmake dynamatic"
@@ -458,12 +465,14 @@ create_symlink ../build/bin/dynamatic
458465
create_symlink ../build/bin/dynamatic-mlir-lsp-server
459466
create_symlink ../build/bin/dynamatic-opt
460467
create_symlink ../build/bin/elastic-miter
468+
create_symlink ../build/bin/export-blif
461469
create_symlink ../build/bin/export-dot
462470
create_symlink ../build/bin/export-cfg
463471
create_symlink ../build/bin/export-rtl
464472
create_symlink ../build/bin/exp-frequency-profiler
465473
create_symlink ../build/bin/handshake-simulator
466474
create_symlink ../build/bin/hls-verifier
475+
create_symlink ../build/bin/import-blif
467476
create_symlink ../build/bin/log2csv
468477
create_symlink "../build/bin/rigidification-testbench"
469478
create_generator_symlink build/bin/rtl-cmpf-generator
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# BLIF Exporter
2+
3+
The **BLIF Exporter** is an binary that serializes a Synth dialect circuit contained inside `hw.module` operations into a BLIF file. It serves as the inverse of the BLIF Importer, allowing Dynamatic's IR to be exported back to a standard BLIF representation for use with external synthesis tools.
4+
5+
---
6+
7+
## Code Structure
8+
9+
The binary `export-blif` can be called as follows:
10+
```bash
11+
./bin/export-blif <input-mlir-file> <output-blif-file>
12+
```
13+
14+
where `input-mlir-file` is the file that contains the Synth circuit to be exported and `output-blif-file` is the file containing the exported BLIF.
15+
16+
The core functionality is the following:
17+
18+
- It iterates over all `hw.HWModuleOp` operations in the module and calls `exportBlifCircuit` on each of them to generate a BLIF module.
19+
20+
The core function `exportBlifCircuit` executes the following steps:
21+
22+
1. It writes the `.model`, `.inputs`, and `.outputs` lines, and collects all port names into the `inputPorts` and `outputPorts` vectors using the `generateBlifHeader` function.
23+
2. It iterates over the ops inside the module body and emit the corresponding BLIF statements using the `generateBlifCircuitFromSynth` function.
24+
3. Writes the `.end` terminator to close the BLIF model.
25+
26+
---
27+
28+
## Support Functions
29+
30+
In this subsection of the doc, we highlight the key support functions.
31+
32+
### Generate BLIF Header
33+
34+
The core function that writes the header section of the BLIF file for a given `hw.HWModuleOp` is `generateBlifHeader`. It:
35+
36+
1. Writes the `.model` line using the `hw.module` name.
37+
2. Iterates over the module's port list and writes all input ports on the `.inputs` line. It populates the vector `inputPorts` which contains the same list.
38+
3. Iterates over the module's port list and writes all output ports on the `.outputs` line. It populates the vector `outputPorts` which contains the same list.
39+
40+
### Generate BLIF Functionality
41+
42+
The core function that translates the Synth operations inside an `hw.HWModuleOp` into BLIF statements is `generateBlifCircuitFromSynth`. It iterates over all ops in the module body and handles three operation types:
43+
44+
- **`synth.latch`**: the function emits a `.latch` statement with the format `.latch <input> <output> [type control] [init]`. The input and output operand names are resolved via `getValueName`. Optional fields are emitted only when present: if a control signal exists, the latch type (defaulting to `"re"` if unset) and control signal name are written; if an init value is set, it is appended.
45+
46+
- **`synth.aig.and_inv`**: the function emits a `.names` statement listing both input operand names and the output result name, followed by a truth-table row. Each input position in the row is `"0"` if that input is inverted and `"1"` otherwise.
47+
48+
- **`hw.constant`**: the function emits a single-node `.names` statement (`.names <output>`) followed by either `1` or `0` on the next line, corresponding to the constant's value. Only 1-bit constants are supported.
49+
50+
- **`hw.output`**: after all ops are processed, the function checks whether any output port is directly connected to an input port (i.e., a pass-through with no Synth op in between). For each such case, a two-node `.names` wire statement is emitted (`1 1`), connecting the input port name to the output port name. At the end, the function asserts that every output port has been accounted for.
51+
52+
Any other operation type is reported as unsupported via an error message.
53+
54+
55+
### Value Naming in BLIF
56+
57+
Value names in the BLIF output are resolved by the internal `getValueName` lambda, which applies the following priority:
58+
59+
1. If the value is a block argument (input port), return its name from `inputPorts` using the argument index.
60+
2. If the value is used as an operand of `hw.output` (output port), return its name from `outputPorts` using the operand index offset by the number of input ports.
61+
3. Otherwise, print the value using MLIR's `AsmState` (e.g., `%4`), strip the leading `%`, and prepend `n` to produce a valid BLIF node name (e.g., `n4`).
62+
63+
This function ensure uniqueness and consistency of names inside a BLIF module.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Blif Importer
2+
3+
4+
The BLIF Importer is a binary that reads a BLIF file and generates a corresponding Synth dialect circuit inside an `hw.module`. It serves as a standalone entry point for importing externally synthesized logic directly into Dynamatic's IR.
5+
6+
This code interprets each BLIF model as an AIG-style circuit composed of 1-bit registers and AND-with-inverter gates, and re-expresses it directly in the Synth dialect while preserving the instance’s interface.
7+
8+
---
9+
10+
## Code Structure
11+
12+
The binary `import-blif` can be called as follows:
13+
```bash
14+
./bin/export-blif <output-mlir-file> <intput-blif-file>
15+
```
16+
17+
where `output-mlir-file` is the file that will contain the Synth circuit to be imported and `input-blif-file` is the file containing imported BLIF.
18+
19+
The code core function is `importBlifCircuit(moduleOp, loc, blifFilePath)` which imports the blif specified in `blifFilePath` in the module operation `moduleOp`.
20+
21+
This function executes the following steps:
22+
23+
1. Extract the module name, input and output port names of the module from the BLIF file using the `getBlifModuleHeader` function.
24+
2. Create an hw module operation `hw.HWModuleOp` with input and output ports corresponding to the one of the model described in the BLIF file using the `createHWModuleShell`.
25+
3. Create all synth operations inside the `hw.HWModuleOp` using the function `populateHWModuleShell`. Then, attach all the outputs of the synth circuit to the terminator of the `hw.HWModuleOp`.
26+
27+
---
28+
29+
## Support Functions
30+
31+
In this subsection of the doc, we highlight the key support functions.
32+
33+
34+
### Generate Synth Operations
35+
36+
The core function to generate synth operations is `populateHWModuleShell`. It parses the BLIF body line by line and emits the corresponding Synth operations. It maintains two maps throughout:
37+
38+
- `nodeValuesMap` which maps a BLIF node name to its Synth Value. Pre-populated with all input port signals before parsing begins.
39+
- `tmpValuesMap` which maps a BLIF node name to a temporary `hw.constant 0` placeholder, created when a node's output is referenced before defining the node. Placeholders are replaced and erased once the real value is available.
40+
41+
**IMPORTANT**: `tmpValuesMap` may contain a large number of temporary `hw` constants which should be cleaned up during synth graph construction. For BLIF generated by tools such as [ABC](https://github.com/berkeley-abc/abc), these temporaries arise primarily from latches since since latches appear at the start of the BLIF file and their inputs are not yet defined at generation time. As a result, performance impact scales mainly with latch count.
42+
43+
It handles two types of units defined in the BLIF:
44+
45+
1. `.latch` from which it parses input node, output node, and oprtional fields (`latchType`, `controlName`, `initVal`). The optional fields follow the BLIF syntax `[type control] [init]` and are decoded based on the number of tokens. A `synth.latch` of type `i1` is created and registered via `updateOutputSynthSignalMapping` in order to record its output value.
46+
2. `.names` from which it parses the input and output node names and the truth-table function. Depending on the number of node names, the behaviour is different:
47+
- 1 node name: it is a constant and an `hw.constant` is generated
48+
- 2 node names: it is a wire or an inverter and it is handled by `createSynthWire`
49+
- 3+ node names: it is a logic gate and it is handled by `createSynthLogicGate`
50+
51+
After parsing, all the outputs of the synth circuit are appended in the `synthOutputs` variable.
52+
53+
54+
### Retrieve the Value of the Input of a BLIF Node
55+
56+
The function that retrieves the input of a synth node is `getInputMappingSynthSignal`. It resolves a BLIF node name to a Synth Value applying the following steps:
57+
58+
1. Return the value from `nodeValuesMap` if already defined.
59+
2. Return the existing placeholder from `tmpValuesMap` if one exists.
60+
3. Create a new `hw.constant 0` placeholder, store it in `tmpValuesMap`, and return it.
61+
62+
### Save the Value of the Output of a BLIF Node
63+
64+
The function that saves the MLIR Value corresponding to the output of a BLIF node is `updateOutputSynthSignalMapping`. It registers a newly created Synth value as the definitive signal for a given node name. If a temporary placeholder exists in `tmpValuesMap` for that name, all its uses are replaced with the new value, its defining `hw.constant` op is erased, and it is removed from `tmpValuesMap`. The new value is then recorded in `nodeValuesMap`.
65+
66+
### Create a Wire
67+
68+
The function that creates a wire in the Synth circuit is `createSynthWire`. It handles two-node `.names` blocks. Compares the input and output bits of the truth-table function string (format: "X Y"):
69+
70+
- If equal, the output node is aliased directly to the input value via `updateOutputSynthSignalMapping` and no new op is created.
71+
- If different, a `synth.aig.and_inv` is created with the input inverted and `ANDed` with a constant 1, effectively modelling a NOT gate.
72+
73+
### Create a Logic Gate
74+
75+
The function that creates a logic gate in the Synth circuit is `createSynthLogicGate`. It handles multi-input `.names` blocks. Currently asserts that exactly 2 inputs are present, reflecting that the code operates on already-binarized AIG-form BLIF. The truth-table function string (format: "XY Z") determines the inversion flags for each input. A `synth.aig.and_inv` is created with the appropriate inversion flags. If the output bit is `0`, a second `synth.aig.and_inv` ANDing with constant 1 and inverting the first input is appended to negate the result.
76+
77+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Synth dialect
2+
3+
4+
The **Synth** dialect is a low-level synthesis-oriented dialect that provides a common in-MLIR interface for logic synthesis operations and data structures. It is intended as an intermediate representation between higher-level hardware IR (e.g., Handshake or HW) and concrete synthesis backends, capturing both logic networks (such as AIGs) and meta-operations that steer synthesis decisions.
5+
6+
In Dynamatic, operations of the synth dialect are always described inside an `hw.module` operation of the hw dialect.
7+
8+
---
9+
10+
## Design and role in the flow
11+
12+
The Synth dialect has a core responsibility:
13+
14+
- Support **logic network representations** such as AIG (And-Inverter Graph) and sequential elements as latches.
15+
16+
In other words, the Synth dialect is not tied to a particular hardware backend or HDL; instead, it serves as an abstraction layer where logic-level manipulations can be expressed, analyzed, and transformed before being committed to a specific RTL or gate-level format.
17+
18+
---
19+
20+
## Operations
21+
22+
This dialect describes two core operations:
23+
24+
- `AndInverterOp` which describes the nodes of an AIG.
25+
- `LatchOp` which describes the presence of a latch.
26+
27+
### And-Inverter Node
28+
29+
It represents a node in an AIG. It computes the bitwise AND of all inputs, each of which may be individually inverted.
30+
31+
```mlir
32+
%r1 = synth.aig.and_inv %a, %b : i1
33+
%r2 = synth.aig.and_inv not %a, %b : i1
34+
%r3 = synth.aig.and_inv not %a, not %b : i1
35+
```
36+
37+
### Latch Node
38+
39+
It models a storage element, directly corresponding to the BLIF `.latch` construct. Accepts three optional parameters: a `latchType` string ("fe", "re", "ah", "al", "as"), a control clock/enable signal, and an initVal encoding the initial state (0, 1, 2 = don't care, 3 = unknown).
40+
41+
```mlir
42+
%q = synth.latch %d : i1
43+
%q = synth.latch %d clock %clk init 0 : i1
44+
```
45+

docs/SUMMARY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@
7777
- [Commit Unit Placement Algorithm](DeveloperGuide/DynamaticFeaturesAndOptimizations/Speculation/CommitUnitPlacementAlgorithm.md)
7878
- [Integration Tests](DeveloperGuide/DynamaticFeaturesAndOptimizations/Speculation/IntegrationTests.md)
7979
- [Save Commit Behavior](DeveloperGuide/DynamaticFeaturesAndOptimizations/Speculation/SaveCommitBehavior.md)
80+
- [Synth]()
81+
- [Synth Dialect](DeveloperGuide/DynamaticFeaturesAndOptimizations/Synth/Synth.md)
82+
- [Blif Importer](DeveloperGuide/DynamaticFeaturesAndOptimizations/Synth/BlifImporter.md)
83+
- [Blif Exporter](DeveloperGuide/DynamaticFeaturesAndOptimizations/Synth/BlifExporter.md)
8084

8185
- [Specs]()
8286
- [Floating Point Units](DeveloperGuide/Specs/FloatingPointUnits.md)

experimental/tools/elastic-miter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ target_link_libraries(elastic-miter
1818
DynamaticSupport
1919
DynamaticTransforms
2020
DynamaticExperimentalSupport
21+
DynamaticSynth
2122

2223
MLIRIR
2324
MLIRMemRefTransforms

experimental/tools/rigidification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ target_link_libraries(rigidification-testbench
1111
PRIVATE
1212
DynamaticHandshake
1313
DynamaticExperimentalSupport
14+
DynamaticSynth
1415

1516
MLIRIR
1617
MLIRMemRefTransforms
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
add_subdirectory(Handshake)
22
add_subdirectory(HW)
3+
add_subdirectory(Synth)

0 commit comments

Comments
 (0)