diff --git a/.gitignore b/.gitignore index 6e68d6fd..5fae92fd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,26 +7,22 @@ *.DS_Store build install +venv .vscode __pycache__ -regress/test_args/*.log -regress/adder/*.v -regress/vexriscv/*.v -regress/arm_core/*.v -regress/arm_core/*.log -regress/arm_core/*.gold -regress/arm_core/*.error -regress/*/abc.history +src/apps/naja_edit/examples/**/*.log +src/apps/naja_edit/examples/**/*.png +src/apps/naja_edit/examples/adder/*.v +src/apps/naja_edit/examples/vexriscv/*.v +src/apps/naja_edit/examples/arm_core/*.v +src/apps/naja_edit/examples/arm_core/*_snl +src/apps/naja_edit/examples/arm_core/*.gold +src/apps/naja_edit/examples/arm_core/*.error +src/apps/naja_edit/examples/arm_core/*.list +src/apps/naja_edit/examples/*/abc.history src/apps/naja_edit/examples/abc.history src/apps/naja_edit/examples/addaccu/addaccu_*.v -src/apps/naja_edit/examples/addaccu/*.log src/apps/naja_edit/examples/addaccu/*.txt src/apps/naja_edit/examples/addaccu/*_snl -regress/arm_core/comp/* -regress/arm_core/nodes.list -regress/arm_core/edges.list -regress/arm_core/arm_core_snl/* -regress/jpeg/*.log -regress/jpeg/jpeg_encoder_snl/* -regress/black_parrot/*.log -regress/black_parrot/black_parrot_snl/* \ No newline at end of file +src/apps/naja_edit/examples/test_args/top_out.v +src/apps/naja_edit/examples/adder/verilator.done diff --git a/.reuse/dep5 b/.reuse/dep5 index 0b153997..336666bc 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -7,7 +7,7 @@ Files: .github/workflows/* .gitmodules Copyright: 2022 The Naja Authors. License: Apache-2.0 -Files: README.md AUTHORS docs/* +Files: README.md README_pages/* AUTHORS docs/* Copyright: 2022 The Naja Authors. License: Apache-2.0 diff --git a/Dockerfile.regress b/Dockerfile.regress index 38476c20..c9836ff4 100644 --- a/Dockerfile.regress +++ b/Dockerfile.regress @@ -41,7 +41,7 @@ RUN rm -rf /naja-install WORKDIR /naja/build RUN cmake .. -DCMAKE_INSTALL_PREFIX=/naja-install && make -j$(nproc) && make install -WORKDIR /naja/regress +WORKDIR /naja/src/apps/naja_edit/examples ENV SET_PYTHONPATH=/naja-install/lib/python ENV LD_LIBRARY_PATH=/naja-install/lib ENV NAJA_EDIT=/naja-install/bin/naja_edit diff --git a/README.md b/README.md index 0cc177f7..a8511674 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,6 @@ Naja is an Electronic Design Automation (EDA) project that provides open source Naja best starting point is: [naja-edit](#naja_edit). - - ### Acknowledgement [](https://nlnet.nl/project/Naja) @@ -29,6 +27,8 @@ This project is supported and funded by NLNet through the [NGI0 Entrust](https:/ `naja_edit`, located in the `$NAJA_INSTALL/bin` directory, is a tool designed for optimizing, editing and translating netlists. +:tv: We presented naja_edit’s latest features and results at [ORConf 2024](https://fossi-foundation.org/orconf/2024). You can watch the full presentation [here](https://www.youtube.com/watch?v=JpwZGCuWekU). + ### Workflow Overview The workflow for `naja_edit` is outlined in the schema below. It's important to note that the only mandatory step in the process is the initial loading of the input netlist. @@ -71,85 +71,13 @@ naja_edit -f snl -t snl -i input.snl -o output.snl -a dle \ -e pre_script.py -z post_edit.py ``` -`naja_edit` editing script examples are available [here](https://github.com/najaeda/naja/blob/main/src/apps/edit/examples). +This [page](README_pages/naja-edit-python-examples.md) provides a collection of example Python scripts for using the naja_edit API. + +`naja_edit` editing script examples are also available [here](https://github.com/najaeda/naja/blob/main/src/apps/najae_edit/examples). The [Naja Regress](https://github.com/najaeda/naja-regress) repository features a collection of examples showcasing extensive use of `naja_edit`. -### Python API Examples - -To use the Python API in `naja_edit`, start by creating a Python script containing an `edit` function. - -```python -from naja import snl - -def edit(): - universe = snl.SNLUniverse.get() - top = universe.getTopDesign() - - # Do something with 'top' -``` - -#### Print All Design Content - -The following script recursively browses the design and prints instances, terminals, nets, and their connectivity. - -```python -from naja import snl - -def print_instance_tree(design): - for ins in design.getInstances(): - print(f"Instance: {ins.getName()}") - model = ins.getModel() - for term in design.getTerms(): - print(f" Terminal: {term}") - for net in design.getNets(): - print(f" Net: {net}") - for bit in net.getBits(): - for component in bit.getComponents(): - print(f" Component: {component}") - print_instance_tree(model) - -def edit(): - universe = snl.SNLUniverse.get() - top = universe.getTopDesign() - - print_instance_tree(top) -``` - -#### Remove Interface Buffers from an FPGA Design - -The following script removes interface buffers `'IBUF'`, `'OBUF'`, and `'BUFG'` from a design synthesized for an FPGA. - -```python -from naja import snl - -def delete_io_bufs(design): - for ins in design.getInstances(): - model = ins.getModel() - if model.isPrimitive(): - model_name = model.getName() - if model_name in ['IBUF', 'BUFG']: - input_net = ins.getInstTerm(model.getScalarTerm('I')).getNet() - output_net = ins.getInstTerm(model.getScalarTerm('O')).getNet() - for component in output_net.getComponents(): - component.setNet(input_net) - output_net.destroy() - ins.destroy() - elif model_name == 'OBUF': - input_net = ins.getInstTerm(model.getScalarTerm('I')).getNet() - output_net = ins.getInstTerm(model.getScalarTerm('O')).getNet() - for component in input_net.getComponents(): - component.setNet(output_net) - input_net.destroy() - ins.destroy() - -def edit(): - universe = snl.SNLUniverse.get() - top = universe.getTopDesign() - - delete_io_bufs(top) -```
[ ↑ Back to top ↑ ]
@@ -314,7 +242,7 @@ capnp decode --packed snl_implementation.capnp DBImplementation < snl/db_impleme ##### Verilog -For Verilog parsing, Naja relies on naja-verilog submodule (https://github.com/najaeda/naja-verilog). +For Verilog parsing, Naja relies on naja-verilog [submodule](https://github.com/najaeda/naja-verilog). Leaf primitives are loaded through the Python primitive loader: [SNLPrimitivesLoader](https://github.com/najaeda/naja/blob/main/src/snl/python/primitives/SNLPrimitivesLoader.h). An application snippet can be found [here](https://github.com/najaeda/naja/blob/main/src/snl/snippets/app/src/SNLVRLSnippet.cpp) and examples of primitive libraries described using the Python interface can be found in the diff --git a/README_pages/images/design_stats.png b/README_pages/images/design_stats.png new file mode 100644 index 00000000..86315790 Binary files /dev/null and b/README_pages/images/design_stats.png differ diff --git a/README_pages/naja-edit-python-examples.md b/README_pages/naja-edit-python-examples.md new file mode 100644 index 00000000..1b1ee3c3 --- /dev/null +++ b/README_pages/naja-edit-python-examples.md @@ -0,0 +1,122 @@ +# Naja Python API Examples + +Naja Python API allows users to: + +- browse the netlist data, collect informations, ... +- Apply ECO type transformations. + +To use the Python API in `naja_edit`, start by creating a Python script containing an `edit` function. + +```python +from naja import snl + +def edit(): + universe = snl.SNLUniverse.get() + top = universe.getTopDesign() + + # Do something with 'top' +``` + +## Print All Design Content + +The following script recursively browses the design and prints instances, terminals, nets, and their connectivity. + +```python +from naja import snl + +def print_instance_tree(design): + for ins in design.getInstances(): + print(f"Instance: {ins.getName()}") + model = ins.getModel() + for term in design.getTerms(): + print(f" Terminal: {term}") + for net in design.getNets(): + print(f" Net: {net}") + for bit in net.getBits(): + for component in bit.getComponents(): + print(f" Component: {component}") + print_instance_tree(model) + +def edit(): + universe = snl.SNLUniverse.get() + top = universe.getTopDesign() + + print_instance_tree(top) +``` + +## Remove Interface Buffers from an FPGA Design + +The following script removes interface buffers `'IBUF'`, `'OBUF'`, and `'BUFG'` from a design synthesized for an FPGA. + +```python +from naja import snl + +def delete_io_bufs(design): + for ins in design.getInstances(): + model = ins.getModel() + if model.isPrimitive(): + model_name = model.getName() + if model_name in ['IBUF', 'BUFG']: + input_net = ins.getInstTerm(model.getScalarTerm('I')).getNet() + output_net = ins.getInstTerm(model.getScalarTerm('O')).getNet() + for component in output_net.getComponents(): + component.setNet(input_net) + output_net.destroy() + ins.destroy() + elif model_name == 'OBUF': + input_net = ins.getInstTerm(model.getScalarTerm('I')).getNet() + output_net = ins.getInstTerm(model.getScalarTerm('O')).getNet() + for component in input_net.getComponents(): + component.setNet(output_net) + input_net.destroy() + ins.destroy() + +def edit(): + universe = snl.SNLUniverse.get() + top = universe.getTopDesign() + + delete_io_bufs(top) +``` + +## Browse all modules and display them based on their number of instances terms and nets + +```python +from naja import snl +import pandas as pd +import matplotlib.pyplot as plt + +def plot_design_stats(library): + data_list = [] + for design in library.getDesigns(): + nb_terms = sum(1 for _ in design.getBitTerms()) + nb_nets = sum(1 for _ in design.getBitNets()) + nb_instances = sum(1 for _ in design.getInstances()) + data_list.append({ + 'design': design.getName(), + 'nb_terms': nb_terms, + 'nb_nets': nb_nets, + 'nb_instances': nb_instances + }) + pandas_data = pd.DataFrame(data_list).set_index('design') + plot = pandas_data.plot.bar(y=['nb_terms', 'nb_nets', 'nb_instances'], stacked=True) + + # Set title and labels + plot.set_title('Design Statistics', fontsize=16, fontweight='bold') + plot.set_xlabel('Design Name', fontsize=12) + plot.set_ylabel('Count', fontsize=12) + + plot_figure = plot.get_figure() + plot_figure.tight_layout() + plot_figure.savefig('design_stats.png') + + +def edit(): + universe = snl.SNLUniverse.get() + topDesign = universe.getTopDesign() + topLibrary = topDesign.getLibrary() #top library contains top design + + plot_design_stats(topLibrary) +``` + +This script generates a plot of design statistics, as illustrated in the image below: +![Design Stats](./images/design_stats.png) diff --git a/regress/Makefile.inc b/regress/Makefile.inc deleted file mode 100644 index 7ebbc394..00000000 --- a/regress/Makefile.inc +++ /dev/null @@ -1,8 +0,0 @@ -YOSYS ?= yosys -EQY ?= eqy -VERILATOR ?= verilator -NAJA_EDIT ?= $(realpath ../../build/src/apps/edit/naja_edit) -PRIMITIVES ?= $(realpath ../../primitives/xilinx.py) -ASAP7_PRIMITIVES ?= $(realpath ../../primitives/asap7.py) -NANGATE45_PRIMITIVES ?= $(realpath ../../primitives/nangate45.py) -SET_PYTHONPATH ?= $(realpath ../../build/src/snl/python/snl_wrapping) diff --git a/regress/arm_core/src/add_error.py b/regress/arm_core/src/add_error.py deleted file mode 100644 index 1c200ac6..00000000 --- a/regress/arm_core/src/add_error.py +++ /dev/null @@ -1,46 +0,0 @@ -import logging -from naja import snl - -def edit(): - logging.basicConfig(filename='edit.log', filemode='w' ,level=logging.DEBUG) - universe = snl.SNLUniverse.get() - if universe is None: - logging.critical('No loaded SNLUniverse') - return 1 - top = universe.getTopDesign() - if top is None: - logging.critical('SNLUniverse does not contain any top SNLDesign') - return 1 - else: - logging.info('Found top design ' + str(top)) - - #get n instances and destroy - n = 20 - destroyed = 0 - for instance in top.getInstances(): - destroyed += 1 - instance.destroy() - if destroyed > n: - break - - #get n nets and destroy - n = 20 - destroyed = 0 - for net in top.getNets(): - destroyed += 1 - net.destroy() - if destroyed > n: - break - - - #lut14Mask = lut14.getInstParameter('INIT') - #if lut14Mask is None: - # logging.critical('cannot find \'INIT\' in ' + str(lut14)) - # return 1 - #else: - # logging.info('Found ' + str(lut14Mask)) - - #lut14Mask.setValue('8\'hfb') - - - diff --git a/regress/Makefile b/src/apps/naja_edit/examples/Makefile similarity index 100% rename from regress/Makefile rename to src/apps/naja_edit/examples/Makefile diff --git a/src/apps/naja_edit/examples/Makefile.inc b/src/apps/naja_edit/examples/Makefile.inc new file mode 100644 index 00000000..b88eca81 --- /dev/null +++ b/src/apps/naja_edit/examples/Makefile.inc @@ -0,0 +1,8 @@ +YOSYS ?= yosys +EQY ?= eqy +VERILATOR ?= verilator +NAJA_EDIT ?= ../../../../../install/bin/naja_edit +PRIMITIVES ?= ../../../../../primitives/xilinx.py +ASAP7_PRIMITIVES ?= $(realpath ../../../primitives/asap7.py) +NANGATE45_PRIMITIVES ?= $(realpath ../../../primitives/nangate45.py) +SET_PYTHONPATH ?= ../../../../../install/lib/python diff --git a/regress/adder/Makefile b/src/apps/naja_edit/examples/adder/Makefile similarity index 77% rename from regress/adder/Makefile rename to src/apps/naja_edit/examples/adder/Makefile index 8089ffbe..9f3b8a58 100644 --- a/regress/adder/Makefile +++ b/src/apps/naja_edit/examples/adder/Makefile @@ -1,9 +1,10 @@ include ../Makefile.inc -all: adder_snl.v primitives.v verilator +all: adder_snl.v primitives.v verilator.done -verilator: adder_snl.v primitives.v +verilator.done: adder_snl.v primitives.v ${VERILATOR} --top-module adder --lint-only primitives.v adder_snl.v + touch verilator.done adder_netlist.v: src/adder.v ${YOSYS} src/synth.ys diff --git a/regress/adder/src/adder.v b/src/apps/naja_edit/examples/adder/src/adder.v similarity index 100% rename from regress/adder/src/adder.v rename to src/apps/naja_edit/examples/adder/src/adder.v diff --git a/regress/adder/src/comp.ys b/src/apps/naja_edit/examples/adder/src/comp.ys similarity index 100% rename from regress/adder/src/comp.ys rename to src/apps/naja_edit/examples/adder/src/comp.ys diff --git a/regress/adder/src/synth.ys b/src/apps/naja_edit/examples/adder/src/synth.ys similarity index 100% rename from regress/adder/src/synth.ys rename to src/apps/naja_edit/examples/adder/src/synth.ys diff --git a/regress/arm_core/Makefile b/src/apps/naja_edit/examples/arm_core/Makefile similarity index 71% rename from regress/arm_core/Makefile rename to src/apps/naja_edit/examples/arm_core/Makefile index 41515719..28072a56 100644 --- a/regress/arm_core/Makefile +++ b/src/apps/naja_edit/examples/arm_core/Makefile @@ -1,10 +1,10 @@ include ../Makefile.inc -all: edges.list verif.gold verilator.gold verif.error +all: edges.list verif.gold verilator.gold -verif.error: arm_core_snl_error.v primitives.v - ${YOSYS} src/comp_error.ys - touch $@ +#verif.error: arm_core_snl_error.v primitives.v +# ${YOSYS} src/comp_error.ys +# touch $@ verif.gold: arm_core_snl.v primitives.v ${YOSYS} src/comp_gold.ys @@ -34,9 +34,9 @@ arm_core_snl.v primitives.v: arm_core_netlist.v export PYTHONPATH=${SET_PYTHONPATH}; \ ${NAJA_EDIT} -f verilog -t verilog -p ${PRIMITIVES} -i arm_core_netlist.v -o arm_core_snl.v -d primitives.v -arm_core_snl_error.v: arm_core_netlist.v src/add_error.py - export PYTHONPATH=${SET_PYTHONPATH}; \ - ${NAJA_EDIT} -e src/add_error.py -f verilog -t verilog -p ${PRIMITIVES} -i arm_core_netlist.v -o arm_core_snl_error.v -d primitives.v +arm_core_snl_error.v: arm_core_netlist.v ../scripts/plot_designs_stats.py + export PYTHONPATH=${SET_PYTHONPATH}:./venv/lib/python3.12/site-packages; \ + ${NAJA_EDIT} -e ../scripts/plot_designs_stats.py -f verilog -p ${PRIMITIVES} -i arm_core_netlist.v clean: -rm arm_core_snl.v primitives.v arm_core_netlist.v diff --git a/regress/arm_core/gen_edge_list.py b/src/apps/naja_edit/examples/arm_core/gen_edge_list.py similarity index 100% rename from regress/arm_core/gen_edge_list.py rename to src/apps/naja_edit/examples/arm_core/gen_edge_list.py diff --git a/regress/arm_core/src/arm_core.v b/src/apps/naja_edit/examples/arm_core/src/arm_core.v similarity index 100% rename from regress/arm_core/src/arm_core.v rename to src/apps/naja_edit/examples/arm_core/src/arm_core.v diff --git a/regress/arm_core/src/comp.eqy b/src/apps/naja_edit/examples/arm_core/src/comp.eqy similarity index 100% rename from regress/arm_core/src/comp.eqy rename to src/apps/naja_edit/examples/arm_core/src/comp.eqy diff --git a/regress/arm_core/src/comp_error.ys b/src/apps/naja_edit/examples/arm_core/src/comp_error.ys similarity index 100% rename from regress/arm_core/src/comp_error.ys rename to src/apps/naja_edit/examples/arm_core/src/comp_error.ys diff --git a/regress/arm_core/src/comp_gold.ys b/src/apps/naja_edit/examples/arm_core/src/comp_gold.ys similarity index 100% rename from regress/arm_core/src/comp_gold.ys rename to src/apps/naja_edit/examples/arm_core/src/comp_gold.ys diff --git a/regress/arm_core/src/synth.ys b/src/apps/naja_edit/examples/arm_core/src/synth.ys similarity index 100% rename from regress/arm_core/src/synth.ys rename to src/apps/naja_edit/examples/arm_core/src/synth.ys diff --git a/src/apps/naja_edit/examples/scripts/plot_designs_stats.py b/src/apps/naja_edit/examples/scripts/plot_designs_stats.py new file mode 100644 index 00000000..b362614f --- /dev/null +++ b/src/apps/naja_edit/examples/scripts/plot_designs_stats.py @@ -0,0 +1,36 @@ +from naja import snl +import pandas as pd +import matplotlib.pyplot as plt + + +def plot_design_stats(library): + data_list = [] + for design in library.getDesigns(): + nb_terms = sum(1 for _ in design.getBitTerms()) + nb_nets = sum(1 for _ in design.getBitNets()) + nb_instances = sum(1 for _ in design.getInstances()) + data_list.append({ + 'design': design.getName(), + 'nb_terms': nb_terms, + 'nb_nets': nb_nets, + 'nb_instances': nb_instances + }) + pandas_data = pd.DataFrame(data_list).set_index('design') + plot = pandas_data.plot.bar(y=['nb_terms', 'nb_nets', 'nb_instances'], stacked=True) + + # Set title and labels + plot.set_title('Design Statistics', fontsize=16, fontweight='bold') + plot.set_xlabel('Design Name', fontsize=12) + plot.set_ylabel('Count', fontsize=12) + + plot_figure = plot.get_figure() + plot_figure.tight_layout() + plot_figure.savefig('design_stats.png') + + +def edit(): + universe = snl.SNLUniverse.get() + topDesign = universe.getTopDesign() + topLibrary = topDesign.getLibrary() #top library contains top design + + plot_design_stats(topLibrary) \ No newline at end of file diff --git a/src/apps/naja_edit/examples/scripts/print_design_tree.py b/src/apps/naja_edit/examples/scripts/print_design_tree.py new file mode 100644 index 00000000..ac58beff --- /dev/null +++ b/src/apps/naja_edit/examples/scripts/print_design_tree.py @@ -0,0 +1,20 @@ +from naja import snl + +def print_design_tree(design): + for ins in design.getInstances(): + print(f"Instance: {ins.getName()}") + model = ins.getModel() + for term in design.getTerms(): + print(f" Terminal: {term}") + for net in design.getNets(): + print(f" Net: {net}") + for bit in net.getBits(): + for component in bit.getComponents(): + print(f" Component: {component}") + print_design_tree(model) + +def edit(): + universe = snl.SNLUniverse.get() + top = universe.getTopDesign() + + print_design_tree(top) \ No newline at end of file diff --git a/src/apps/naja_edit/examples/scripts/remove_iobufs.py b/src/apps/naja_edit/examples/scripts/remove_iobufs.py new file mode 100644 index 00000000..7a160e3f --- /dev/null +++ b/src/apps/naja_edit/examples/scripts/remove_iobufs.py @@ -0,0 +1,27 @@ +from naja import snl + +def remove_io_bufs(design): + for ins in design.getInstances(): + model = ins.getModel() + if model.isPrimitive(): + model_name = model.getName() + if model_name in ['IBUF', 'BUFG']: + input_net = ins.getInstTerm(model.getScalarTerm('I')).getNet() + output_net = ins.getInstTerm(model.getScalarTerm('O')).getNet() + for component in output_net.getComponents(): + component.setNet(input_net) + output_net.destroy() + ins.destroy() + elif model_name == 'OBUF': + input_net = ins.getInstTerm(model.getScalarTerm('I')).getNet() + output_net = ins.getInstTerm(model.getScalarTerm('O')).getNet() + for component in input_net.getComponents(): + component.setNet(output_net) + input_net.destroy() + ins.destroy() + +def edit(): + universe = snl.SNLUniverse.get() + top = universe.getTopDesign() + + remove_io_bufs(top) \ No newline at end of file diff --git a/regress/test_args/Makefile b/src/apps/naja_edit/examples/test_args/Makefile similarity index 100% rename from regress/test_args/Makefile rename to src/apps/naja_edit/examples/test_args/Makefile diff --git a/regress/test_args/module1.v b/src/apps/naja_edit/examples/test_args/module1.v similarity index 100% rename from regress/test_args/module1.v rename to src/apps/naja_edit/examples/test_args/module1.v diff --git a/regress/test_args/module2.v b/src/apps/naja_edit/examples/test_args/module2.v similarity index 100% rename from regress/test_args/module2.v rename to src/apps/naja_edit/examples/test_args/module2.v diff --git a/regress/test_args/top.v b/src/apps/naja_edit/examples/test_args/top.v similarity index 100% rename from regress/test_args/top.v rename to src/apps/naja_edit/examples/test_args/top.v diff --git a/regress/vexriscv/Makefile b/src/apps/naja_edit/examples/vexriscv/Makefile similarity index 100% rename from regress/vexriscv/Makefile rename to src/apps/naja_edit/examples/vexriscv/Makefile diff --git a/regress/vexriscv/src/comp.ys b/src/apps/naja_edit/examples/vexriscv/src/comp.ys similarity index 100% rename from regress/vexriscv/src/comp.ys rename to src/apps/naja_edit/examples/vexriscv/src/comp.ys diff --git a/regress/vexriscv/src/synth.ys b/src/apps/naja_edit/examples/vexriscv/src/synth.ys similarity index 100% rename from regress/vexriscv/src/synth.ys rename to src/apps/naja_edit/examples/vexriscv/src/synth.ys diff --git a/regress/vexriscv/src/vexriscv.demo.GenFull.v b/src/apps/naja_edit/examples/vexriscv/src/vexriscv.demo.GenFull.v similarity index 100% rename from regress/vexriscv/src/vexriscv.demo.GenFull.v rename to src/apps/naja_edit/examples/vexriscv/src/vexriscv.demo.GenFull.v