From a5d069ba82c24df86035e4937e498857758a94f5 Mon Sep 17 00:00:00 2001 From: Ang Li Date: Mon, 3 May 2021 18:34:23 -0400 Subject: [PATCH] [Rel] Release 0.3.5 * Added support for frame-based programming protocol * Added support for memory initialization (ROMs!) * Changed API so programming protocol may be selected after VPR/Yosys script generation, decoupling design space exploration with physical implementation * Improved bitstream generation to a more generic FASM format and more flexible assembler * Added "Magic" bitstream checker based on the Magic programming protocol to help debugging the bitstream loading process for other protocols * Example scripts and documentation updated to match the modifications above --- docs/source/_static/images/picosoc.PNG | Bin 0 -> 102108 bytes docs/source/prga.py/prga.app.app.rst | 7 + docs/source/prga.py/prga.app.rst | 16 ++ docs/source/prga.py/prga.app.softregs.rst | 7 + .../prga.py/prga.passes.materialization.rst | 7 + .../prga.py/prga.passes.proginsertion.rst | 7 + docs/source/prga.py/prga.passes.rst | 2 + docs/source/prga.py/prga.prog.frame.lib.rst | 7 + .../prga.py/prga.prog.frame.protocol.rst | 7 + docs/source/prga.py/prga.prog.frame.rst | 16 ++ docs/source/prga.py/prga.prog.rst | 1 + docs/source/prga.py/prga.rst | 2 + docs/source/prga.py/prga.system.rst | 7 + .../prga.py/prga.tools.bitgen.frame.rst | 7 + docs/source/prga.py/prga.tools.bitgen.rst | 2 + .../source/prga.py/prga.tools.bitgen.util.rst | 7 + docs/source/tutorial/bring_your_own_ip.rst | 9 + .../tutorial/build_your_custom_fpga.rst | 117 +++++--- docs/source/workflow.rst | 27 +- examples/app/bcd2bin/frame_k4_N2_8x8/Makefile | 9 + .../frame_k4_N2_8x8/config/config.ivl.yaml | 12 + .../frame_k4_N2_8x8/config/config.vcs.yaml | 12 + .../bcd2bin/frame_k4_N2_8x8/config/io.partial | 1 + .../app/bcd2bin/frame_k4_N2_8x8_b8/Makefile | 9 + .../frame_k4_N2_8x8_b8/config/config.ivl.yaml | 12 + .../frame_k4_N2_8x8_b8/config/config.vcs.yaml | 12 + .../frame_k4_N2_8x8_b8/config/io.partial | 1 + .../pktchain_defaultchain_k4_N2_8x8/Makefile | 9 + .../config/config.ivl.yaml | 12 + .../config/config.vcs.yaml | 12 + .../config/io.partial | 1 + .../picorv32/fpga21/config/config.ivl.yaml | 2 +- .../picorv32/fpga21/config/config.vcs.yaml | 2 +- .../Makefile | 9 + .../config/config.ivl.yaml | 22 ++ .../config/config.vcs.yaml | 22 ++ .../config/io.partial | 1 + .../config/config.ivl.yaml | 2 +- .../config/config.vcs.yaml | 2 +- .../Makefile | 4 +- .../config/config.ivl.yaml | 2 +- .../config/config.vcs.yaml | 2 +- examples/app/romtest/.gitignore | 5 + .../frame_grady18_N4_rom2K_8x8/Makefile | 9 + .../config/config.ivl.yaml | 14 + .../config/config.vcs.yaml | 14 + .../config/io.partial | 1 + examples/app/romtest/src/include/rom.vh | 256 ++++++++++++++++++ examples/app/romtest/src/romtest.v | 17 ++ examples/app/romtest/src/romtest_test.v | 63 +++++ examples/fpga/Makefile.in | 19 +- .../Makefile | 1 + .../build.py | 132 +++++++++ .../fpga/frame/grady18_N4_rom2K_8x8/Makefile | 1 + .../fpga/frame/grady18_N4_rom2K_8x8/build.py | 128 +++++++++ examples/fpga/frame/k4_N2_8x8/Makefile | 1 + examples/fpga/frame/k4_N2_8x8/build.py | 85 ++++++ examples/fpga/frame/k4_N2_8x8_b8/Makefile | 1 + examples/fpga/frame/k4_N2_8x8_b8/build.py | 85 ++++++ .../fle6_N10_mem32Kb_mul24x18_42x34/build.py | 19 +- .../fpga/magic/fle6_N2_mem2K_8x8/build.py | 15 +- examples/fpga/magic/grady18_N2_10x6/build.py | 15 +- .../grady18v2_N10_mem32Kb_42x34/build.py | 28 +- .../fpga/magic/grady18v2_N2_8x8_hier/build.py | 15 +- examples/fpga/magic/hardpico/build.py | 21 +- examples/fpga/magic/k4_N2_8x8/build.py | 13 +- .../pktchain/defaultchain_k4_N2_8x8/Makefile | 1 + .../pktchain/defaultchain_k4_N2_8x8/build.py | 77 ++++++ examples/fpga/pktchain/fpga21/build.py | 170 ++++++------ examples/fpga/pktchain/k4_N2_8x8/build.py | 30 +- .../fpga/scanchain/fle6_N2_mem2K_8x8/build.py | 20 +- .../fpga/scanchain/grady18_N2_10x6/build.py | 15 +- .../scanchain/grady18v2_N2_8x8_hier/build.py | 15 +- examples/fpga/scanchain/k4_N2_8x8/build.py | 13 +- prga.py | 2 +- 75 files changed, 1464 insertions(+), 264 deletions(-) create mode 100755 docs/source/_static/images/picosoc.PNG create mode 100644 docs/source/prga.py/prga.app.app.rst create mode 100644 docs/source/prga.py/prga.app.rst create mode 100644 docs/source/prga.py/prga.app.softregs.rst create mode 100644 docs/source/prga.py/prga.passes.materialization.rst create mode 100644 docs/source/prga.py/prga.passes.proginsertion.rst create mode 100644 docs/source/prga.py/prga.prog.frame.lib.rst create mode 100644 docs/source/prga.py/prga.prog.frame.protocol.rst create mode 100644 docs/source/prga.py/prga.prog.frame.rst create mode 100644 docs/source/prga.py/prga.system.rst create mode 100644 docs/source/prga.py/prga.tools.bitgen.frame.rst create mode 100644 docs/source/prga.py/prga.tools.bitgen.util.rst create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8/Makefile create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8/config/config.ivl.yaml create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8/config/config.vcs.yaml create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8/config/io.partial create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8_b8/Makefile create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.ivl.yaml create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.vcs.yaml create mode 100644 examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/io.partial create mode 100644 examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/Makefile create mode 100644 examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.ivl.yaml create mode 100644 examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.vcs.yaml create mode 100644 examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/io.partial create mode 100644 examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/Makefile create mode 100644 examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml create mode 100644 examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml create mode 100644 examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/io.partial create mode 100644 examples/app/romtest/.gitignore create mode 100644 examples/app/romtest/frame_grady18_N4_rom2K_8x8/Makefile create mode 100644 examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.ivl.yaml create mode 100644 examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.vcs.yaml create mode 100644 examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/io.partial create mode 100644 examples/app/romtest/src/include/rom.vh create mode 100644 examples/app/romtest/src/romtest.v create mode 100644 examples/app/romtest/src/romtest_test.v create mode 100644 examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/Makefile create mode 100644 examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/build.py create mode 100644 examples/fpga/frame/grady18_N4_rom2K_8x8/Makefile create mode 100644 examples/fpga/frame/grady18_N4_rom2K_8x8/build.py create mode 100644 examples/fpga/frame/k4_N2_8x8/Makefile create mode 100644 examples/fpga/frame/k4_N2_8x8/build.py create mode 100644 examples/fpga/frame/k4_N2_8x8_b8/Makefile create mode 100644 examples/fpga/frame/k4_N2_8x8_b8/build.py create mode 100644 examples/fpga/pktchain/defaultchain_k4_N2_8x8/Makefile create mode 100644 examples/fpga/pktchain/defaultchain_k4_N2_8x8/build.py diff --git a/docs/source/_static/images/picosoc.PNG b/docs/source/_static/images/picosoc.PNG new file mode 100755 index 0000000000000000000000000000000000000000..fac8534dc32d6d1175b282566b3ee6c49a3d7c6f GIT binary patch literal 102108 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 J-spV(zW^C}vE%>% literal 0 HcmV?d00001 diff --git a/docs/source/prga.py/prga.app.app.rst b/docs/source/prga.py/prga.app.app.rst new file mode 100644 index 00000000..e0946392 --- /dev/null +++ b/docs/source/prga.py/prga.app.app.rst @@ -0,0 +1,7 @@ +prga.app.app module +=================== + +.. automodule:: prga.app.app + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.app.rst b/docs/source/prga.py/prga.app.rst new file mode 100644 index 00000000..64bcea11 --- /dev/null +++ b/docs/source/prga.py/prga.app.rst @@ -0,0 +1,16 @@ +prga.app package +================ + +.. automodule:: prga.app + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + prga.app.app + prga.app.softregs diff --git a/docs/source/prga.py/prga.app.softregs.rst b/docs/source/prga.py/prga.app.softregs.rst new file mode 100644 index 00000000..63b3f1b5 --- /dev/null +++ b/docs/source/prga.py/prga.app.softregs.rst @@ -0,0 +1,7 @@ +prga.app.softregs module +======================== + +.. automodule:: prga.app.softregs + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.passes.materialization.rst b/docs/source/prga.py/prga.passes.materialization.rst new file mode 100644 index 00000000..6de33563 --- /dev/null +++ b/docs/source/prga.py/prga.passes.materialization.rst @@ -0,0 +1,7 @@ +prga.passes.materialization module +================================== + +.. automodule:: prga.passes.materialization + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.passes.proginsertion.rst b/docs/source/prga.py/prga.passes.proginsertion.rst new file mode 100644 index 00000000..f426c7bf --- /dev/null +++ b/docs/source/prga.py/prga.passes.proginsertion.rst @@ -0,0 +1,7 @@ +prga.passes.proginsertion module +================================ + +.. automodule:: prga.passes.proginsertion + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.passes.rst b/docs/source/prga.py/prga.passes.rst index 2d8173f9..f82f01d0 100644 --- a/docs/source/prga.py/prga.passes.rst +++ b/docs/source/prga.py/prga.passes.rst @@ -23,6 +23,8 @@ Submodules prga.passes.annotation prga.passes.base prga.passes.flow + prga.passes.materialization + prga.passes.proginsertion prga.passes.rtl prga.passes.translation prga.passes.yosys diff --git a/docs/source/prga.py/prga.prog.frame.lib.rst b/docs/source/prga.py/prga.prog.frame.lib.rst new file mode 100644 index 00000000..ae3d64d2 --- /dev/null +++ b/docs/source/prga.py/prga.prog.frame.lib.rst @@ -0,0 +1,7 @@ +prga.prog.frame.lib module +========================== + +.. automodule:: prga.prog.frame.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.prog.frame.protocol.rst b/docs/source/prga.py/prga.prog.frame.protocol.rst new file mode 100644 index 00000000..950c48f5 --- /dev/null +++ b/docs/source/prga.py/prga.prog.frame.protocol.rst @@ -0,0 +1,7 @@ +prga.prog.frame.protocol module +=============================== + +.. automodule:: prga.prog.frame.protocol + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.prog.frame.rst b/docs/source/prga.py/prga.prog.frame.rst new file mode 100644 index 00000000..72d2d36f --- /dev/null +++ b/docs/source/prga.py/prga.prog.frame.rst @@ -0,0 +1,16 @@ +prga.prog.frame package +======================= + +.. automodule:: prga.prog.frame + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + prga.prog.frame.lib + prga.prog.frame.protocol diff --git a/docs/source/prga.py/prga.prog.rst b/docs/source/prga.py/prga.prog.rst index 5537bcf6..67d150dd 100644 --- a/docs/source/prga.py/prga.prog.rst +++ b/docs/source/prga.py/prga.prog.rst @@ -12,6 +12,7 @@ Subpackages .. toctree:: :maxdepth: 4 + prga.prog.frame prga.prog.magic prga.prog.pktchain prga.prog.scanchain diff --git a/docs/source/prga.py/prga.rst b/docs/source/prga.py/prga.rst index 6291f11c..4638e4cd 100644 --- a/docs/source/prga.py/prga.rst +++ b/docs/source/prga.py/prga.rst @@ -13,12 +13,14 @@ Subpackages :maxdepth: 4 prga.algorithm + prga.app prga.core prga.integration prga.netlist prga.passes prga.prog prga.renderer + prga.system prga.tools Submodules diff --git a/docs/source/prga.py/prga.system.rst b/docs/source/prga.py/prga.system.rst new file mode 100644 index 00000000..96be4e17 --- /dev/null +++ b/docs/source/prga.py/prga.system.rst @@ -0,0 +1,7 @@ +prga.system package +=================== + +.. automodule:: prga.system + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.tools.bitgen.frame.rst b/docs/source/prga.py/prga.tools.bitgen.frame.rst new file mode 100644 index 00000000..95d632f6 --- /dev/null +++ b/docs/source/prga.py/prga.tools.bitgen.frame.rst @@ -0,0 +1,7 @@ +prga.tools.bitgen.frame module +============================== + +.. automodule:: prga.tools.bitgen.frame + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/prga.py/prga.tools.bitgen.rst b/docs/source/prga.py/prga.tools.bitgen.rst index d36b39f1..f85707e1 100644 --- a/docs/source/prga.py/prga.tools.bitgen.rst +++ b/docs/source/prga.py/prga.tools.bitgen.rst @@ -13,6 +13,8 @@ Submodules :maxdepth: 4 prga.tools.bitgen.common + prga.tools.bitgen.frame prga.tools.bitgen.magic prga.tools.bitgen.pktchain prga.tools.bitgen.scanchain + prga.tools.bitgen.util diff --git a/docs/source/prga.py/prga.tools.bitgen.util.rst b/docs/source/prga.py/prga.tools.bitgen.util.rst new file mode 100644 index 00000000..c4d0b638 --- /dev/null +++ b/docs/source/prga.py/prga.tools.bitgen.util.rst @@ -0,0 +1,7 @@ +prga.tools.bitgen.util module +============================= + +.. automodule:: prga.tools.bitgen.util + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/tutorial/bring_your_own_ip.rst b/docs/source/tutorial/bring_your_own_ip.rst index 096dcc7a..e729a71d 100644 --- a/docs/source/tutorial/bring_your_own_ip.rst +++ b/docs/source/tutorial/bring_your_own_ip.rst @@ -31,6 +31,15 @@ implementation of `PicoSoC`_. .. _PicoRV32: https://github.com/cliffordwolf/picorv32 .. _PicoSoC: https://github.com/cliffordwolf/picorv32/tree/master/picosoc +.. image:: /_static/images/picosoc.PNG + :width: 600px + :alt: PicoSoC implemented on an FPGA with a hard PicoRV32 core + :align: center + +Figure created by `VPR`_. + +.. _VPR: https://verilogtorouting.org/ + Describe custom primitives -------------------------- diff --git a/docs/source/tutorial/build_your_custom_fpga.rst b/docs/source/tutorial/build_your_custom_fpga.rst index 3beb1677..102d2606 100644 --- a/docs/source/tutorial/build_your_custom_fpga.rst +++ b/docs/source/tutorial/build_your_custom_fpga.rst @@ -1,3 +1,4 @@ + Build Your Custom FPGA ====================== @@ -16,13 +17,6 @@ layout and routing resources. It also stores and manages all created/generated modules and other information about the FPGA, which are later used by the RTL-to-bitstream flow. -To create a `Context` object, call the ``new_context`` class method of a -configuration circuitry class, for example, `Scanchain`. This is because -different configuration circuitry needs to initialize the `Context` object -differently. For example, the configuration cell in a SRAM-based configuration -circuitry are SRAM cells, while the `Scanchain` configuration circuitry simply -uses D-Flipflops. - .. code-block:: python from prga import * @@ -30,8 +24,8 @@ uses D-Flipflops. import sys - # Use single-bit scanchain configuration circuitry - ctx = Scanchain.new_context() + # create a new context + ctx = Context() After creating the `Context` object, we can start to describe our custom FPGA. Here, we first describe the routing resources in the FPGA: the routing wire @@ -66,8 +60,8 @@ a memory module: .. code-block:: python - # create a memory primitive: name, addr width, data width - memory = ctx.create_memory( "dpram_a8d8", 8, 8).commit() + # create a memory primitive: addr width, data width + memory = ctx.create_memory( 8, 8 ) PRGA also provides API for adding and using arbitrary Verilog modules in the FPGA, for example `Context.build_primitive`. Multi-modal primitives are also supported @@ -280,45 +274,27 @@ and switch box slots: # commit the top-level array top = builder.fill( pattern ).auto_connect().commit() -Auto-complete the architecture, generate RTL and other files +Generate Yosys and VPR scripts ------------------------------------------------------------ -PRGA uses `Jinja2`_ for generating most files. `Jinja2`_ is a templating -language/framework for Python. It is fast, lightweight, and also compatible with -plain text. - -To set up a `Jinja2`_ environment, call the ``new_renderer`` method of the same -configuration circuitry class used to create the `Context`. This points the -`Jinja2`_ environment to the correct directories to look for Verilog and other -templates. +After describing the desired FPGA architecture, we can generate the scripts for +our RTL-to-bitstream flow. +Specifically, PRGA generates the `Yosys`_ scripts for synthesizing an +application for the custom FPGA, and the `VPR`_ scripts for placing and routing +the synthesized application. -.. _Jinja2: https://jinja.palletsprojects.com/en/2.11.x/ - -.. code-block:: python - - renderer = Scanchain.new_renderer() +.. _Yosys: http://www.clifford.at/yosys +.. _VPR: https://verilogtorouting.org/ PRGA adopts a pass-based flow to complete, modify, optimize the FPGA architecture as well as generate all files for the architecture. A `Flow` object is used to manage and run all the passes. It also checks and resolves the -dependences between the passes. For example, the `VerilogCollection` pass -requires `Translation` as a dependency. Even if a `Translation` pass is -added after a `VerilogCollection` pass, it will be executed before the -`VerilogCollection` pass. +dependences between the passes. .. code-block:: python flow = Flow( - # This pass converts user-defined modules to Verilog modules - Translation(), - - # Analyze how configurable connections are implemented with switches - SwitchPathAnnotation(), - - # This pass inserts configuration circuitry into the FPGA - Scanchain.InsertProgCircuitry(), - # This pass generates the architecture specification for VPR to place # and route designs onto this FPGA VPRArchGeneration("vpr/arch.xml"), @@ -327,16 +303,73 @@ added after a `VerilogCollection` pass, it will be executed before the # to place and route designs onto this FPGA VPR_RRG_Generation("vpr/rrg.xml"), - # This pass create Verilog rendering tasks in the renderer. - VerilogCollection('rtl'), - # This pass analyzes the primitives in the FPGA and generates synthesis # script for Yosys YosysScriptsCollection(r, "syn"), ) # Run the flow on our context - flow.run(ctx, renderer) + flow.run(ctx) + +After this step, PRGA should generate the following files: + +.. code-block:: bash + + +- syn/ + | +- m_adder.lib.v # behavioral model for logic primitive "adder" + | +- m_adder.techmap.v # technology mapping rules for logic primitve "adder" + | | + | +- m_ram_1r1w.lib.v # behavioral model for the block RAM primitive + | +- memory.techmap.v # technology mapping rules for the block RAM primitive + | +- bram.rule # block RAM inference rules for Yosys + | | + | +- read_lib.tcl # Yosys script for reading in the primitives as lib cells + | +- synth.tcl # Yosys script for synthesizing an application + | + +- vpr/ + +- arch.xml # VPR's architecture description + +- rrg.xml # VPR's routing resource graph + +Auto-complete the architecture, generate RTL, and serialize the context +----------------------------------------------------------------------- + +.. PRGA uses `Jinja2`_ for generating most files. `Jinja2`_ is a templating + language/framework for Python. It is fast, lightweight, and also compatible with + plain text. + +.. _Jinja2: https://jinja.palletsprojects.com/en/2.11.x/ + +We have not yet chosen the programming protocol for the custom FPGA until this +point in our script. +This is intended to facilitate early and fast design-space exploration before +diving into the vast physical optimization space. + +To choose the programming protocol and then implement the abstract FPGA +architecture with synthesizable RTL, run the following pases: + +.. code-block:: python + + flow = Flow( + + # This pass chooses the programming protocol, and adds protocol-specific + # designs into the context + Materialization("scanchain", chain_width = 1), + + # This pass converts user-defined modules to Verilog modules + Translation(), + + # Analyze how configurable connections are implemented with switches + SwitchPathAnnotation(), + + # This pass inserts configuration circuitry into the FPGA + ProgCircuitryInsertion(), + + # This pass create Verilog rendering tasks in the renderer. + VerilogCollection('rtl'), + ) + + # Run the flow on our context + flow.run(ctx) After running the flow, all the models and information about our FPGA are stored in the context, and all the file are generated. As the final step, we make a diff --git a/docs/source/workflow.rst b/docs/source/workflow.rst index 527d7e68..3907b4fe 100644 --- a/docs/source/workflow.rst +++ b/docs/source/workflow.rst @@ -129,27 +129,32 @@ conflict and ordering between ``Pass`` es. Here's a list of the most commonly used ``Pass`` es: +- `VPRArchGeneration` and `VPR_RRG_Generation`: These two passes + generate the VPR architecture specification and routing resource graph + specification, respectively. +- `YosysScriptsCollection`: This pass inspects the `Context` object and + creates `Yosys`_ script generation tasks, including the main synthesis script, + technology mapping script, block RAM inferrence script, and so on. +- `Materialization`: This pass adds the :ref:`design` view of + the :ref:`arch:Logic Primitive` s. + Certain primitives may be implemented differently on different programming + protocols, while some primitives are not supported by all programming + protocols. + For example, initializable memories that may be used as ROMs are only + supported by the `Frame`-based programming protocol. - `Translation`: This pass generates the :ref:`design` view for modules in the :ref:`abstract` view by linking :ref:`arch:Logic Primitive` s and implementing the abstract configuratble connections with switch modules. - `SwitchPathAnnotation`: This pass analyzes the switch modules instantiated in the :ref:`design` view, and annotate the MUX/BUFFER paths back to the :ref:`abstract` view. - This information is used by ``FASM`` metadata generation during `VPR`_ script - generation. -- ``*.InsertProgCircuitry``: This pass inserts configuration memory into the + This information is used later by the bitstream generator. +- `ProgCircuitryInsertion`: This pass inserts configuration memory into the :ref:`design` view. This pass is specific to configuration circuitry types, e.g. - `Scanchain.InsertProgCircuitry` and `Pktchain.InsertProgCircuitry`. -- `VPRArchGeneration` and `VPR_RRG_Generation`: These two passes - generate the VPR architecture specification and routing resource graph - specification, respectively. -- `VerilogCollection`: This pass inspects the `Context` object and creates + `Scanchain`, `Pktchain` and `Frame`, and only available after RTL generation tasks for all the modules in a `FileRenderer` object. RTL Verilog files are generated based on the :ref:`design` views. -- `YosysScriptsCollection`: This pass inspects the `Context` object and - creates `Yosys`_ script generation tasks, including the main synthesis script, - technology mapping script, block RAM inferrence script, and so on. File Rendering ^^^^^^^^^^^^^^ diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8/Makefile b/examples/app/bcd2bin/frame_k4_N2_8x8/Makefile new file mode 100644 index 00000000..1cec6521 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8/Makefile @@ -0,0 +1,9 @@ +COMP ?= vcs +CONFIG := config/config.${COMP}.yaml +PROJECTS := app tests + +$(PROJECTS): $(CONFIG) + python -O -m prga.tools.wizard $< + +clean: + rm -rf $(PROJECTS) diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8/config/config.ivl.yaml b/examples/app/bcd2bin/frame_k4_N2_8x8/config/config.ivl.yaml new file mode 100644 index 00000000..0282a682 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8/config/config.ivl.yaml @@ -0,0 +1,12 @@ +context: ../../../../fpga/frame/k4_N2_8x8/ctx.pkl +compiler: iverilog +app: + name: bcd2bin + sources: + - ../../src/bcd2bin.v +constraints: + io: io.partial +tests: + basic: + sources: + - ../../src/bcd2bin_test_basic.v diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8/config/config.vcs.yaml b/examples/app/bcd2bin/frame_k4_N2_8x8/config/config.vcs.yaml new file mode 100644 index 00000000..68745ee1 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8/config/config.vcs.yaml @@ -0,0 +1,12 @@ +context: ../../../../fpga/frame/k4_N2_8x8/ctx.pkl +compiler: vcs +app: + name: bcd2bin + sources: + - ../../src/bcd2bin.v +constraints: + io: io.partial +tests: + basic: + sources: + - ../../src/bcd2bin_test_basic.v diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8/config/io.partial b/examples/app/bcd2bin/frame_k4_N2_8x8/config/io.partial new file mode 100644 index 00000000..87d21637 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8/config/io.partial @@ -0,0 +1 @@ +clk 0 1 0 diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8_b8/Makefile b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/Makefile new file mode 100644 index 00000000..1cec6521 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/Makefile @@ -0,0 +1,9 @@ +COMP ?= vcs +CONFIG := config/config.${COMP}.yaml +PROJECTS := app tests + +$(PROJECTS): $(CONFIG) + python -O -m prga.tools.wizard $< + +clean: + rm -rf $(PROJECTS) diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.ivl.yaml b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.ivl.yaml new file mode 100644 index 00000000..38f1e357 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.ivl.yaml @@ -0,0 +1,12 @@ +context: ../../../../fpga/frame/k4_N2_8x8_b8/ctx.pkl +compiler: iverilog +app: + name: bcd2bin + sources: + - ../../src/bcd2bin.v +constraints: + io: io.partial +tests: + basic: + sources: + - ../../src/bcd2bin_test_basic.v diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.vcs.yaml b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.vcs.yaml new file mode 100644 index 00000000..bbf6f73b --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/config.vcs.yaml @@ -0,0 +1,12 @@ +context: ../../../../fpga/frame/k4_N2_8x8_b8/ctx.pkl +compiler: vcs +app: + name: bcd2bin + sources: + - ../../src/bcd2bin.v +constraints: + io: io.partial +tests: + basic: + sources: + - ../../src/bcd2bin_test_basic.v diff --git a/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/io.partial b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/io.partial new file mode 100644 index 00000000..87d21637 --- /dev/null +++ b/examples/app/bcd2bin/frame_k4_N2_8x8_b8/config/io.partial @@ -0,0 +1 @@ +clk 0 1 0 diff --git a/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/Makefile b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/Makefile new file mode 100644 index 00000000..fd382863 --- /dev/null +++ b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/Makefile @@ -0,0 +1,9 @@ +COMP ?= ivl +CONFIG := config/config.${COMP}.yaml +PROJECTS := app tests + +$(PROJECTS): $(CONFIG) + python -O -m prga.tools.wizard $< + +clean: + rm -rf $(PROJECTS) diff --git a/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.ivl.yaml b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.ivl.yaml new file mode 100644 index 00000000..423290b7 --- /dev/null +++ b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.ivl.yaml @@ -0,0 +1,12 @@ +context: ../../../../fpga/pktchain/defaultchain_k4_N2_8x8/ctx.pkl +compiler: iverilog +app: + name: bcd2bin + sources: + - ../../src/bcd2bin.v +constraints: + io: io.partial +tests: + basic: + sources: + - ../../src/bcd2bin_test_basic.v diff --git a/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.vcs.yaml b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.vcs.yaml new file mode 100644 index 00000000..a0fa477b --- /dev/null +++ b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/config.vcs.yaml @@ -0,0 +1,12 @@ +context: ../../../../fpga/pktchain/defaultchain_k4_N2_8x8/ctx.pkl +compiler: vcs +app: + name: bcd2bin + sources: + - ../../src/bcd2bin.v +constraints: + io: io.partial +tests: + basic: + sources: + - ../../src/bcd2bin_test_basic.v diff --git a/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/io.partial b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/io.partial new file mode 100644 index 00000000..87d21637 --- /dev/null +++ b/examples/app/bcd2bin/pktchain_defaultchain_k4_N2_8x8/config/io.partial @@ -0,0 +1 @@ +clk 0 1 0 diff --git a/examples/app/picorv32/fpga21/config/config.ivl.yaml b/examples/app/picorv32/fpga21/config/config.ivl.yaml index e9d18ff9..38f8f1c2 100644 --- a/examples/app/picorv32/fpga21/config/config.ivl.yaml +++ b/examples/app/picorv32/fpga21/config/config.ivl.yaml @@ -18,5 +18,5 @@ tests: sources: - ../../src/picorv32_test_basic.v run_flags: - - +firmware=${PRGA_ROOT}/examples/target/picorv32/src/firmware.hex + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex - +max_cycle=1000000 diff --git a/examples/app/picorv32/fpga21/config/config.vcs.yaml b/examples/app/picorv32/fpga21/config/config.vcs.yaml index cfac3d76..d6e12f58 100644 --- a/examples/app/picorv32/fpga21/config/config.vcs.yaml +++ b/examples/app/picorv32/fpga21/config/config.vcs.yaml @@ -18,5 +18,5 @@ tests: sources: - ../../src/picorv32_test_basic.v run_flags: - - +firmware=${PRGA_ROOT}/examples/target/picorv32/src/firmware.hex + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex - +max_cycle=1000000 diff --git a/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/Makefile b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/Makefile new file mode 100644 index 00000000..fd382863 --- /dev/null +++ b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/Makefile @@ -0,0 +1,9 @@ +COMP ?= ivl +CONFIG := config/config.${COMP}.yaml +PROJECTS := app tests + +$(PROJECTS): $(CONFIG) + python -O -m prga.tools.wizard $< + +clean: + rm -rf $(PROJECTS) diff --git a/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml new file mode 100644 index 00000000..4284f622 --- /dev/null +++ b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml @@ -0,0 +1,22 @@ +context: ../../../../fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/ctx.pkl +compiler: iverilog +app: + name: picorv32_axi + sources: + - ../../src/picorv32.v + parameters: + COMPRESSED_ISA: 1 + ENABLE_MUL: 1 + ENABLE_FAST_MUL: 1 + ENABLE_DIV: 1 + ENABLE_IRQ: 1 + ENABLE_TRACE: 1 +constraints: + io: io.partial +tests: + picorv32_test_basic: + sources: + - ../../src/picorv32_test_basic.v + run_flags: + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex + - +max_cycle=1000000 diff --git a/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml new file mode 100644 index 00000000..025491b9 --- /dev/null +++ b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml @@ -0,0 +1,22 @@ +context: ../../../../fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/ctx.pkl +compiler: vcs +app: + name: picorv32_axi + sources: + - ../../src/picorv32.v + parameters: + COMPRESSED_ISA: 1 + ENABLE_MUL: 1 + ENABLE_FAST_MUL: 1 + ENABLE_DIV: 1 + ENABLE_IRQ: 1 + ENABLE_TRACE: 1 +constraints: + io: io.partial +tests: + picorv32_test_basic: + sources: + - ../../src/picorv32_test_basic.v + run_flags: + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex + - +max_cycle=1000000 diff --git a/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/io.partial b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/io.partial new file mode 100644 index 00000000..87d21637 --- /dev/null +++ b/examples/app/picorv32/frame_grady18_N10_mem32Kb_mul24x18_42x34/config/io.partial @@ -0,0 +1 @@ +clk 0 1 0 diff --git a/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml b/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml index df0b2196..71b0aed7 100644 --- a/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml +++ b/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.ivl.yaml @@ -18,5 +18,5 @@ tests: sources: - ../../src/picorv32_test_basic.v run_flags: - - +firmware=${PRGA_ROOT}/examples/target/picorv32/src/firmware.hex + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex - +max_cycle=1000000 diff --git a/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml b/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml index 7dc7bb7d..c4fb7e35 100644 --- a/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml +++ b/examples/app/picorv32/magic_fle6_N10_mem32Kb_mul24x18_42x34/config/config.vcs.yaml @@ -18,5 +18,5 @@ tests: sources: - ../../src/picorv32_test_basic.v run_flags: - - +firmware=${PRGA_ROOT}/examples/target/picorv32/src/firmware.hex + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex - +max_cycle=1000000 diff --git a/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/Makefile b/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/Makefile index 13b3fe77..fd382863 100644 --- a/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/Makefile +++ b/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/Makefile @@ -1,5 +1,5 @@ -COMPILER ?= ivl -CONFIG := config/config.${COMPILER}.yaml +COMP ?= ivl +CONFIG := config/config.${COMP}.yaml PROJECTS := app tests $(PROJECTS): $(CONFIG) diff --git a/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.ivl.yaml b/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.ivl.yaml index 32edd5d5..2905a08e 100644 --- a/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.ivl.yaml +++ b/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.ivl.yaml @@ -18,5 +18,5 @@ tests: sources: - ../../src/picorv32_test_basic.v run_flags: - - +firmware=${PRGA_ROOT}/examples/target/picorv32/src/firmware.hex + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex - +max_cycle=1000000 diff --git a/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.vcs.yaml b/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.vcs.yaml index 43cc2664..79d1ae9e 100644 --- a/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.vcs.yaml +++ b/examples/app/picorv32/magic_grady18v2_N10_mem32Kb_42x34/config/config.vcs.yaml @@ -18,5 +18,5 @@ tests: sources: - ../../src/picorv32_test_basic.v run_flags: - - +firmware=${PRGA_ROOT}/examples/target/picorv32/src/firmware.hex + - +firmware=${PRGA_ROOT}/examples/app/picorv32/src/firmware.hex - +max_cycle=1000000 diff --git a/examples/app/romtest/.gitignore b/examples/app/romtest/.gitignore new file mode 100644 index 00000000..907041c7 --- /dev/null +++ b/examples/app/romtest/.gitignore @@ -0,0 +1,5 @@ +/*/* +!/src/*.v +!/src/include +!/*/Makefile +!/*/config diff --git a/examples/app/romtest/frame_grady18_N4_rom2K_8x8/Makefile b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/Makefile new file mode 100644 index 00000000..1cec6521 --- /dev/null +++ b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/Makefile @@ -0,0 +1,9 @@ +COMP ?= vcs +CONFIG := config/config.${COMP}.yaml +PROJECTS := app tests + +$(PROJECTS): $(CONFIG) + python -O -m prga.tools.wizard $< + +clean: + rm -rf $(PROJECTS) diff --git a/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.ivl.yaml b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.ivl.yaml new file mode 100644 index 00000000..88f2b12d --- /dev/null +++ b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.ivl.yaml @@ -0,0 +1,14 @@ +context: ../../../../fpga/frame/grady18_N4_rom2K_8x8/ctx.pkl +compiler: iverilog +app: + name: romtest + sources: + - ../../src/romtest.v + includes: + - ../../src/include +constraints: + io: io.partial +tests: + romtest_test: + sources: + - ../../src/romtest_test.v diff --git a/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.vcs.yaml b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.vcs.yaml new file mode 100644 index 00000000..5d64151a --- /dev/null +++ b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/config.vcs.yaml @@ -0,0 +1,14 @@ +context: ../../../../fpga/frame/grady18_N4_rom2K_8x8/ctx.pkl +compiler: vcs +app: + name: romtest + sources: + - ../../src/romtest.v + includes: + - ../../src/include +constraints: + io: io.partial +tests: + romtest_test: + sources: + - ../../src/romtest_test.v diff --git a/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/io.partial b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/io.partial new file mode 100644 index 00000000..87d21637 --- /dev/null +++ b/examples/app/romtest/frame_grady18_N4_rom2K_8x8/config/io.partial @@ -0,0 +1 @@ +clk 0 1 0 diff --git a/examples/app/romtest/src/include/rom.vh b/examples/app/romtest/src/include/rom.vh new file mode 100644 index 00000000..21c236c4 --- /dev/null +++ b/examples/app/romtest/src/include/rom.vh @@ -0,0 +1,256 @@ + rom[255] = 8'b01001000; + rom[254] = 8'b11000001; + rom[253] = 8'b00111000; + rom[252] = 8'b00111110; + rom[251] = 8'b10111100; + rom[250] = 8'b01001100; + rom[249] = 8'b01100101; + rom[248] = 8'b00010011; + rom[247] = 8'b01101010; + rom[246] = 8'b11011011; + rom[245] = 8'b00010010; + rom[244] = 8'b11011111; + rom[243] = 8'b11101111; + rom[242] = 8'b11100111; + rom[241] = 8'b10001111; + rom[240] = 8'b00110111; + rom[239] = 8'b11001100; + rom[238] = 8'b01010110; + rom[237] = 8'b10111001; + rom[236] = 8'b00100011; + rom[235] = 8'b11011110; + rom[234] = 8'b10010010; + rom[233] = 8'b10101001; + rom[232] = 8'b11100111; + rom[231] = 8'b00001101; + rom[230] = 8'b10111110; + rom[229] = 8'b10100010; + rom[228] = 8'b11100011; + rom[227] = 8'b01100100; + rom[226] = 8'b01101010; + rom[225] = 8'b00100011; + rom[224] = 8'b11011010; + rom[223] = 8'b01111110; + rom[222] = 8'b00110000; + rom[221] = 8'b01100011; + rom[220] = 8'b11100001; + rom[219] = 8'b10000010; + rom[218] = 8'b10010001; + rom[217] = 8'b00111011; + rom[216] = 8'b01100000; + rom[215] = 8'b11110001; + rom[214] = 8'b01110000; + rom[213] = 8'b01001111; + rom[212] = 8'b00001110; + rom[211] = 8'b00100110; + rom[210] = 8'b11111000; + rom[209] = 8'b00001011; + rom[208] = 8'b00010000; + rom[207] = 8'b01001010; + rom[206] = 8'b10011110; + rom[205] = 8'b11100011; + rom[204] = 8'b10011011; + rom[203] = 8'b11110110; + rom[202] = 8'b00110011; + rom[201] = 8'b11010110; + rom[200] = 8'b01001110; + rom[199] = 8'b10101000; + rom[198] = 8'b01100001; + rom[197] = 8'b11100000; + rom[196] = 8'b00101111; + rom[195] = 8'b11110100; + rom[194] = 8'b00111000; + rom[193] = 8'b11011101; + rom[192] = 8'b11111010; + rom[191] = 8'b00000100; + rom[190] = 8'b01101001; + rom[189] = 8'b00111101; + rom[188] = 8'b10101110; + rom[187] = 8'b11111100; + rom[186] = 8'b10100111; + rom[185] = 8'b11100101; + rom[184] = 8'b10001100; + rom[183] = 8'b11100010; + rom[182] = 8'b00110101; + rom[181] = 8'b11100111; + rom[180] = 8'b11010111; + rom[179] = 8'b11101010; + rom[178] = 8'b00000111; + rom[177] = 8'b10011100; + rom[176] = 8'b01110111; + rom[175] = 8'b11100100; + rom[174] = 8'b01110000; + rom[173] = 8'b00111100; + rom[172] = 8'b11011100; + rom[171] = 8'b00100101; + rom[170] = 8'b01100000; + rom[169] = 8'b10110000; + rom[168] = 8'b01010000; + rom[167] = 8'b01101110; + rom[166] = 8'b10011101; + rom[165] = 8'b00010111; + rom[164] = 8'b01001100; + rom[163] = 8'b11010001; + rom[162] = 8'b10101001; + rom[161] = 8'b00111111; + rom[160] = 8'b11111100; + rom[159] = 8'b11100011; + rom[158] = 8'b10000111; + rom[157] = 8'b11101101; + rom[156] = 8'b10001011; + rom[155] = 8'b01000100; + rom[154] = 8'b00111000; + rom[153] = 8'b11110010; + rom[152] = 8'b00011111; + rom[151] = 8'b10000010; + rom[150] = 8'b00011000; + rom[149] = 8'b10100100; + rom[148] = 8'b01100000; + rom[147] = 8'b01101101; + rom[146] = 8'b11100100; + rom[145] = 8'b10101001; + rom[144] = 8'b01001000; + rom[143] = 8'b01001001; + rom[142] = 8'b10010011; + rom[141] = 8'b10110011; + rom[140] = 8'b01110100; + rom[139] = 8'b10011000; + rom[138] = 8'b00101000; + rom[137] = 8'b11101001; + rom[136] = 8'b10001000; + rom[135] = 8'b01011100; + rom[134] = 8'b01010000; + rom[133] = 8'b01101111; + rom[132] = 8'b11010010; + rom[131] = 8'b01111001; + rom[130] = 8'b11100110; + rom[129] = 8'b10000110; + rom[128] = 8'b11101011; + rom[127] = 8'b10001001; + rom[126] = 8'b00110011; + rom[125] = 8'b01010111; + rom[124] = 8'b11101010; + rom[123] = 8'b10110101; + rom[122] = 8'b01010010; + rom[121] = 8'b10110100; + rom[120] = 8'b00001001; + rom[119] = 8'b00110000; + rom[118] = 8'b10000100; + rom[117] = 8'b00010001; + rom[116] = 8'b10011001; + rom[115] = 8'b11000100; + rom[114] = 8'b11100001; + rom[113] = 8'b01001010; + rom[112] = 8'b00110101; + rom[111] = 8'b10000001; + rom[110] = 8'b10111100; + rom[109] = 8'b11100100; + rom[108] = 8'b01010100; + rom[107] = 8'b00101111; + rom[106] = 8'b10000101; + rom[105] = 8'b10011001; + rom[104] = 8'b00100101; + rom[103] = 8'b10110001; + rom[102] = 8'b01010101; + rom[101] = 8'b00000001; + rom[100] = 8'b11100110; + rom[ 99] = 8'b11001000; + rom[ 98] = 8'b10111011; + rom[ 97] = 8'b00111000; + rom[ 96] = 8'b00000001; + rom[ 95] = 8'b11101011; + rom[ 94] = 8'b00111011; + rom[ 93] = 8'b11011100; + rom[ 92] = 8'b11001100; + rom[ 91] = 8'b11100000; + rom[ 90] = 8'b01111010; + rom[ 89] = 8'b10001001; + rom[ 88] = 8'b00011110; + rom[ 87] = 8'b00110001; + rom[ 86] = 8'b01001110; + rom[ 85] = 8'b10110001; + rom[ 84] = 8'b01011010; + rom[ 83] = 8'b00100001; + rom[ 82] = 8'b11110111; + rom[ 81] = 8'b11101101; + rom[ 80] = 8'b11000001; + rom[ 79] = 8'b11101011; + rom[ 78] = 8'b01000111; + rom[ 77] = 8'b10101001; + rom[ 76] = 8'b11011111; + rom[ 75] = 8'b00000110; + rom[ 74] = 8'b10111011; + rom[ 73] = 8'b01101101; + rom[ 72] = 8'b00110011; + rom[ 71] = 8'b11101011; + rom[ 70] = 8'b10010101; + rom[ 69] = 8'b01001100; + rom[ 68] = 8'b11110001; + rom[ 67] = 8'b11000111; + rom[ 66] = 8'b01000111; + rom[ 65] = 8'b00011110; + rom[ 64] = 8'b11011100; + rom[ 63] = 8'b11010101; + rom[ 62] = 8'b11011111; + rom[ 61] = 8'b01100011; + rom[ 60] = 8'b00001001; + rom[ 59] = 8'b11010110; + rom[ 58] = 8'b00111111; + rom[ 57] = 8'b10101010; + rom[ 56] = 8'b11101111; + rom[ 55] = 8'b11011000; + rom[ 54] = 8'b01000000; + rom[ 53] = 8'b00111010; + rom[ 52] = 8'b00000111; + rom[ 51] = 8'b11101001; + rom[ 50] = 8'b10111001; + rom[ 49] = 8'b01000011; + rom[ 48] = 8'b00101001; + rom[ 47] = 8'b10001010; + rom[ 46] = 8'b01111110; + rom[ 45] = 8'b01110010; + rom[ 44] = 8'b00110010; + rom[ 43] = 8'b11111011; + rom[ 42] = 8'b00111111; + rom[ 41] = 8'b11010000; + rom[ 40] = 8'b00111111; + rom[ 39] = 8'b01011001; + rom[ 38] = 8'b01011001; + rom[ 37] = 8'b10101101; + rom[ 36] = 8'b00010101; + rom[ 35] = 8'b01010000; + rom[ 34] = 8'b01111101; + rom[ 33] = 8'b01001111; + rom[ 32] = 8'b00111100; + rom[ 31] = 8'b10000000; + rom[ 30] = 8'b00100011; + rom[ 29] = 8'b01011101; + rom[ 28] = 8'b00000100; + rom[ 27] = 8'b10001001; + rom[ 26] = 8'b10001001; + rom[ 25] = 8'b01101001; + rom[ 24] = 8'b01011011; + rom[ 23] = 8'b10101001; + rom[ 22] = 8'b01111111; + rom[ 21] = 8'b11100111; + rom[ 20] = 8'b10010110; + rom[ 19] = 8'b10111111; + rom[ 18] = 8'b00111010; + rom[ 17] = 8'b00010010; + rom[ 16] = 8'b00000010; + rom[ 15] = 8'b11011001; + rom[ 14] = 8'b01101111; + rom[ 13] = 8'b11110000; + rom[ 12] = 8'b11101100; + rom[ 11] = 8'b10010010; + rom[ 10] = 8'b10110011; + rom[ 9] = 8'b01001011; + rom[ 8] = 8'b11010100; + rom[ 7] = 8'b00001110; + rom[ 6] = 8'b01000000; + rom[ 5] = 8'b01111111; + rom[ 4] = 8'b11000011; + rom[ 3] = 8'b10100110; + rom[ 2] = 8'b01111000; + rom[ 1] = 8'b01000100; + rom[ 0] = 8'b01001011; diff --git a/examples/app/romtest/src/romtest.v b/examples/app/romtest/src/romtest.v new file mode 100644 index 00000000..7eff7f82 --- /dev/null +++ b/examples/app/romtest/src/romtest.v @@ -0,0 +1,17 @@ +module romtest ( + input wire clk, + input wire [7:0] addr, + output reg [7:0] dout + ); + + reg [7:0] rom [0:255]; + + initial begin + `include "rom.vh" + end + + always @(posedge clk) begin + dout <= rom[addr]; + end + +endmodule diff --git a/examples/app/romtest/src/romtest_test.v b/examples/app/romtest/src/romtest_test.v new file mode 100644 index 00000000..bdc44c69 --- /dev/null +++ b/examples/app/romtest/src/romtest_test.v @@ -0,0 +1,63 @@ +module romtest_test ( + input wire tb_clk, + input wire tb_rst, + output reg tb_pass, + output reg tb_fail, + input wire tb_prog_done, + input wire [31:0] tb_verbosity, + input wire [31:0] tb_cycle_cnt, + + output wire clk, + output reg [7:0] addr, + input wire [7:0] dout + ); + + assign clk = tb_clk; + + reg tb_prog_done_f; + reg [7:0] addr_f; + reg [7:0] rom [0:255]; + + initial begin + addr = 8'h0; + tb_prog_done_f = 1'b0; + tb_pass = 1'b0; + tb_fail = 1'b0; + + `include "rom.vh" + end + + always @(posedge tb_clk) begin + if (tb_rst) begin + addr <= 8'h0; + tb_prog_done_f <= 1'b0; + end else if (tb_prog_done) begin + addr <= addr + 1; + tb_prog_done_f <= 1'b1; + end + end + + always @(posedge tb_clk) begin + addr_f <= addr; + end + + always @(posedge tb_clk) begin + #0; + + if (tb_prog_done_f) begin + if (dout == rom[addr_f]) begin + $display("[Cycle %05d] rom[%03d] => 0x%02x", + tb_cycle_cnt, addr_f, dout); + + if (&addr_f) begin + tb_pass <= 1'b1; + end + end else begin + $display("[Cycle %05d] rom[%03d] => 0x%02x != 0x%02x (expected), fail", + tb_cycle_cnt, addr_f, dout, rom[addr_f]); + tb_fail <= 1'b1; + end + end + end + +endmodule diff --git a/examples/fpga/Makefile.in b/examples/fpga/Makefile.in index b86843e1..e1243c9d 100644 --- a/examples/fpga/Makefile.in +++ b/examples/fpga/Makefile.in @@ -1,5 +1,5 @@ PICKLED_CTX := ctx.pkl -BACKUP ?= $(shell bin/date "+%Y-%m-%d-%H-%M") +BACKUP ?= $(shell date "+%Y-%m-%d-%H-%M") SHELL = /bin/bash .SHELLFLAGS = -o pipefail -c @@ -13,17 +13,18 @@ clean: backup: if [[ -d backup-$(BACKUP) ]]; then echo "Backup $(BACKUP) already exists"; exit 1; fi mkdir backup-$(BACKUP) - cp $(PICKLED_CTX) backup-$(BACKUP) - cp -r rtl backup-$(BACKUP) - cp -r syn backup-$(BACKUP) - cp -r vpr backup-$(BACKUP) + mv $(PICKLED_CTX) backup-$(BACKUP) + mv rtl backup-$(BACKUP) + mv syn backup-$(BACKUP) + mv vpr backup-$(BACKUP) recover: if [[ ! -d backup-$(BACKUP) ]]; then echo "Backup $(BACKUP) does not exist"; exit 1; fi - rm -rf $(PICKLED_CTX) && cp backup-$(BACKUP)/$(PICKLED_CTX) . - rm -rf rtl && cp -r backup-$(BACKUP)/rtl . - rm -rf syn && cp -r backup-$(BACKUP)/syn . - rm -rf vpr && cp -r backup-$(BACKUP)/vpr . + rm -rf $(PICKLED_CTX) && mv backup-$(BACKUP)/$(PICKLED_CTX) . + rm -rf rtl && mv backup-$(BACKUP)/rtl . + rm -rf syn && mv backup-$(BACKUP)/syn . + rm -rf vpr && mv backup-$(BACKUP)/vpr . + rmdir backup-$(BACKUP) clean-backup: rm -rf backup-* diff --git a/examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/Makefile b/examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/Makefile new file mode 100644 index 00000000..f9c03dd5 --- /dev/null +++ b/examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/Makefile @@ -0,0 +1 @@ +include ../../Makefile.in diff --git a/examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/build.py b/examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/build.py new file mode 100644 index 00000000..5d5c7750 --- /dev/null +++ b/examples/fpga/frame/grady18_N10_mem32Kb_mul24x18_42x34/build.py @@ -0,0 +1,132 @@ +from prga import * +from itertools import product + +import logging, sys +logging.getLogger("prga").setLevel(logging.DEBUG) + +# -- Meta Parameters --------------------------------------------------------- +N = 10 # number of BLEs per CLB +IOB_CAP = 8 # number of IOBs per IOB Tile +W, H = 8, 8 # 8x8 subarrays +RAM_COL = 6 # BRAM column +X, Y = 5, 4 # number of subarrays in the top-level array + # top-level size = (X * W + 2) x (Y * H + 2) + +# -- Recover from cache if possible ------------------------------------------ +try: + ctx = Context.unpickle("ctx.tmp.pkl") + +except FileNotFoundError: + ctx = Context() + + # -- Routing Resources --------------------------------------------------- + gbl_clk = ctx.create_global("clk", is_clock = True) + gbl_clk.bind((0, 1), 0) + l1 = ctx.create_segment('L1', 64, 1) + l4 = ctx.create_segment('L4', 24, 4) + # l16 = ctx.create_segment('L16', 2, 16) + + # -- CLB ----------------------------------------------------------------- + builder = ctx.build_logic_block("clb") + ble = ctx.primitives["grady18"] + + lin = len(ble.ports["in"]) + lout = len(ble.ports["out"]) + + clk = builder.create_global(gbl_clk, "south") + ce = builder.create_input ("ce", 1, "west") + in_ = builder.create_input ("in", lin * N, "west") + out = builder.create_output("out", lout * N, "east") + cin = builder.create_input ("cin", 1, "south") + + for i, inst in enumerate(builder.instantiate(ble, "i_ble", N)): + builder.connect(clk, inst.pins["clk"]) + builder.connect(ce, inst.pins["ce"]) + builder.connect(in_[lin * i: lin * (i + 1)], inst.pins["in"]) + builder.connect(inst.pins["out"], out[lout * i: lout * (i + 1)]) + builder.connect(cin, inst.pins["cin"], + vpr_pack_patterns = ["carrychain"]) + cin = inst.pins["cout"] + + cout = builder.create_output("cout", 1, "north") + builder.connect(cin, cout, vpr_pack_patterns = ["carrychain"]) + + clb = builder.commit() + ctx.create_tunnel("carrychain", cout, clb.ports["cin"], (0, -1)) + + # -- IOB ----------------------------------------------------------------- + builder = ctx.build_io_block("iob") + o = builder.create_input("outpad", 1) + i = builder.create_output("inpad", 1) + builder.connect(builder.instances['io'].pins['inpad'], i) + builder.connect(o, builder.instances['io'].pins['outpad']) + iob = builder.commit() + + # -- BRAM ---------------------------------------------------------------- + builder = ctx.build_logic_block("bram", 1, 2) + inst = builder.instantiate( + ctx.create_multimode_memory( + 9, 64, # 512x64 RAM IP + addr_width = 12), # up to 4K x 8b configuration + "i_ram") + builder.connect(builder.create_global(gbl_clk, "south"), inst.pins["clk"]) + builder.connect(builder.create_input("we", 1, "west", (0, 0)), inst.pins["we"]) + builder.connect(builder.create_input("din", len(inst.pins["din"]), "east", (0, 0)), inst.pins["din"]) + builder.connect(builder.create_input("waddr", len(inst.pins["waddr"]), "west", (0, 0)), inst.pins["waddr"]) + builder.connect(builder.create_input("raddr", len(inst.pins["waddr"]), "west", (0, 1)), inst.pins["raddr"]) + builder.connect(inst.pins["dout"], builder.create_output("dout", len(inst.pins["dout"]), "east", (0, 1))) + bram = builder.commit() + + # -- Tiles --------------------------------------------------------------- + iotiles = {ori: ctx.build_tile(iob, IOB_CAP, name = "tile_io_{}".format(ori.name[0]), + edge = OrientationTuple(False, **{ori.name: True})).fill( (0.5, 0.5) ).auto_connect().commit() + for ori in Orientation} + clbtile = ctx.build_tile(clb).fill( (0.15, 0.25) ).auto_connect().commit() + bramtile = ctx.build_tile(bram).fill( (0.15, 0.25) ).auto_connect().commit() + + # -- Sub-Arrays ---------------------------------------------------------- + pattern = SwitchBoxPattern.cycle_free + + builder = ctx.build_array('subarray', W, H, set_as_top = False) + for x, y in product(range(builder.width), range(builder.height)): + if x == RAM_COL: + if y % 2 == 0: + builder.instantiate(bramtile, (x, y)) + else: + builder.instantiate(clbtile, (x, y)) + subarray = builder.fill( pattern ).auto_connect().commit() + + top_width, top_height = X * W + 2, Y * H + 2 + builder = ctx.build_array("top", top_width, top_height, set_as_top = True) + for x, y in product(range(top_width), range(top_height)): + if x in (0, top_width - 1) and y in (0, top_height - 1): + pass + elif (x in (0, top_width - 1) and 0 < y < top_height - 1) or (y in (0, top_height - 1) and 0 < x < top_width - 1): + builder.instantiate( + iotiles[Orientation.west if x == 0 + else Orientation.east if x == top_width - 1 + else Orientation.south if y == 0 + else Orientation.north], + (x, y)) + elif 0 < x < top_width - 1 and 0 < y < top_height - 1 and x % W == 1 and y % H == 1: + builder.instantiate(subarray, (x, y)) + top = builder.fill( pattern ).auto_connect().commit() + + # -- Generate VPR/Yosys scripts ------------------------------------------ + Flow( + VPRArchGeneration('vpr/arch.xml'), + VPR_RRG_Generation('vpr/rrg.xml'), + YosysScriptsCollection('syn'), + ).run(ctx) + ctx.pickle("ctx.tmp.pkl") + +# -- Implement Programming Circuitry ----------------------------------------- +Flow( + Materialization('frame', word_width = 8), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) + +ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/frame/grady18_N4_rom2K_8x8/Makefile b/examples/fpga/frame/grady18_N4_rom2K_8x8/Makefile new file mode 100644 index 00000000..f9c03dd5 --- /dev/null +++ b/examples/fpga/frame/grady18_N4_rom2K_8x8/Makefile @@ -0,0 +1 @@ +include ../../Makefile.in diff --git a/examples/fpga/frame/grady18_N4_rom2K_8x8/build.py b/examples/fpga/frame/grady18_N4_rom2K_8x8/build.py new file mode 100644 index 00000000..ed7d9a34 --- /dev/null +++ b/examples/fpga/frame/grady18_N4_rom2K_8x8/build.py @@ -0,0 +1,128 @@ +from prga import * +from itertools import product +import argparse + +import logging, sys +logging.getLogger("prga").setLevel(logging.DEBUG) + +_parser = argparse.ArgumentParser() +_parser.add_argument("context", type = str, + help = "Name of the pickled context architecture file") +_parser.add_argument("-w", "--data_width", type = int, default = 8, + help = "Bit width of the programming circuitry") + +args = _parser.parse_args() + +# -- Meta Parameters --------------------------------------------------------- +N = 4 # number of BLEs per CLB +IOB_CAP = 4 # number of IOBs per IOB Tile +W, H = 8, 8 # 8x8 subarrays +RAM_COL = 5 # BRAM column + +# -- Recover from cache if possible ------------------------------------------ +try: + ctx = Context.unpickle("ctx.tmp.pkl") + +except FileNotFoundError: + ctx = Context() + + # -- Routing Resources --------------------------------------------------- + gbl_clk = ctx.create_global("clk", is_clock = True) + gbl_clk.bind((0, 1), 0) + l1 = ctx.create_segment('L1', 20, 1) + + # -- CLB ----------------------------------------------------------------- + builder = ctx.build_logic_block("clb") + ble = ctx.primitives["grady18"] + + lin = len(ble.ports["in"]) + lout = len(ble.ports["out"]) + + clk = builder.create_global(gbl_clk, "south") + ce = builder.create_input ("ce", 1, "west") + in_ = builder.create_input ("in", lin * N, "west") + out = builder.create_output("out", lout * N, "east") + cin = builder.create_input ("cin", 1, "south") + + for i, inst in enumerate(builder.instantiate(ble, "i_ble", N)): + builder.connect(clk, inst.pins["clk"]) + builder.connect(ce, inst.pins["ce"]) + builder.connect(in_[lin * i: lin * (i + 1)], inst.pins["in"]) + builder.connect(inst.pins["out"], out[lout * i: lout * (i + 1)]) + builder.connect(cin, inst.pins["cin"], + vpr_pack_patterns = ["carrychain"]) + cin = inst.pins["cout"] + + cout = builder.create_output("cout", 1, "north") + builder.connect(cin, cout, vpr_pack_patterns = ["carrychain"]) + + clb = builder.commit() + ctx.create_tunnel("carrychain", cout, clb.ports["cin"], (0, -1)) + + # -- IOB ----------------------------------------------------------------- + builder = ctx.build_io_block("iob") + o = builder.create_input("outpad", 1) + i = builder.create_output("inpad", 1) + builder.connect(builder.instances['io'].pins['inpad'], i) + builder.connect(o, builder.instances['io'].pins['outpad']) + iob = builder.commit() + + # -- BRAM ---------------------------------------------------------------- + builder = ctx.build_logic_block("bram", 1, 2) + inst = builder.instantiate( + ctx.create_memory( 8, 8, memory_type = "1r1w_init" ), + "i_ram") + builder.connect(builder.create_global(gbl_clk, "south"), inst.pins["clk"]) + builder.connect(builder.create_input("we", 1, "west", (0, 0)), inst.pins["we"]) + builder.connect(builder.create_input("din", len(inst.pins["din"]), "east", (0, 0)), inst.pins["din"]) + builder.connect(builder.create_input("waddr", len(inst.pins["waddr"]), "west", (0, 0)), inst.pins["waddr"]) + builder.connect(builder.create_input("raddr", len(inst.pins["waddr"]), "west", (0, 1)), inst.pins["raddr"]) + builder.connect(inst.pins["dout"], builder.create_output("dout", len(inst.pins["dout"]), "east", (0, 1))) + bram = builder.commit() + + # -- Tiles --------------------------------------------------------------- + iotiles = {ori: ctx.build_tile(iob, IOB_CAP, name = "tile_io_{}".format(ori.name[0]), + edge = OrientationTuple(False, **{ori.name: True})).fill( (0.5, 0.5) ).auto_connect().commit() + for ori in Orientation} + clbtile = ctx.build_tile(clb).fill( (0.15, 0.25) ).auto_connect().commit() + bramtile = ctx.build_tile(bram).fill( (0.15, 0.25) ).auto_connect().commit() + + # -- Arrays -------------------------------------------------------------- + pattern = SwitchBoxPattern.cycle_free + + builder = ctx.build_array("top", W, H, set_as_top = True) + for x, y in product(range(W), range(H)): + if x in (0, W - 1) and y in (0, H - 1): + pass + elif x in (0, W - 1) or y in (0, H - 1): + builder.instantiate( + iotiles[Orientation.west if x == 0 + else Orientation.east if x == W - 1 + else Orientation.south if y == 0 + else Orientation.north], + (x, y)) + elif x == RAM_COL: + if y % 2 == 1: + builder.instantiate( bramtile, (x, y) ) + else: + builder.instantiate( clbtile, (x, y) ) + top = builder.fill( pattern ).auto_connect().commit() + + # -- Generate VPR/Yosys scripts ------------------------------------------ + Flow( + VPRArchGeneration('vpr/arch.xml'), + VPR_RRG_Generation('vpr/rrg.xml'), + YosysScriptsCollection('syn'), + ).run(ctx) + ctx.pickle("ctx.tmp.pkl") + +# -- Implement Programming Circuitry ----------------------------------------- +Flow( + Materialization('frame', word_width = args.data_width), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) + +ctx.pickle(args.context) diff --git a/examples/fpga/frame/k4_N2_8x8/Makefile b/examples/fpga/frame/k4_N2_8x8/Makefile new file mode 100644 index 00000000..f9c03dd5 --- /dev/null +++ b/examples/fpga/frame/k4_N2_8x8/Makefile @@ -0,0 +1 @@ +include ../../Makefile.in diff --git a/examples/fpga/frame/k4_N2_8x8/build.py b/examples/fpga/frame/k4_N2_8x8/build.py new file mode 100644 index 00000000..bae61955 --- /dev/null +++ b/examples/fpga/frame/k4_N2_8x8/build.py @@ -0,0 +1,85 @@ +from prga import * +from itertools import product + +import sys +import logging + +logging.getLogger("prga").setLevel(logging.DEBUG) + +try: + ctx = Context.unpickle("ctx.tmp.pkl") + +except FileNotFoundError: + ctx = Context() + gbl_clk = ctx.create_global("clk", is_clock = True) + gbl_clk.bind((0, 1), 0) + ctx.create_segment('L1', 20, 1) + + builder = ctx.build_slice("slice") + clk = builder.create_clock("clk") + i = builder.create_input("i", 4) + o = builder.create_output("o", 1) + lut = builder.instantiate(ctx.primitives["lut4"], "lut") + ff = builder.instantiate(ctx.primitives["flipflop"], "ff") + builder.connect(clk, ff.pins['clk']) + builder.connect(i, lut.pins['in']) + builder.connect(lut.pins['out'], o) + builder.connect(lut.pins['out'], ff.pins['D'], vpr_pack_patterns = ('lut_dff', )) + builder.connect(ff.pins['Q'], o) + cluster = builder.commit() + + builder = ctx.build_io_block("iob") + o = builder.create_input("outpad", 1) + i = builder.create_output("inpad", 1) + builder.connect(builder.instances['io'].pins['inpad'], i) + builder.connect(o, builder.instances['io'].pins['outpad']) + iob = builder.commit() + + builder = ctx.build_logic_block("clb") + clk = builder.create_global(gbl_clk, Orientation.south) + for i, inst in enumerate(builder.instantiate(cluster, "cluster", 2)): + builder.connect(clk, inst.pins['clk']) + builder.connect(builder.create_input("i{}".format(i), 4, Orientation.west), inst.pins['i']) + builder.connect(inst.pins['o'], builder.create_output("o{}".format(i), 1, Orientation.east)) + clb = builder.commit() + + clbtile = ctx.build_tile(clb).fill( (0.4, 0.25) ).auto_connect().commit() + + iotiles = {} + for ori in Orientation: + builder = ctx.build_tile(iob, 4, name = "t_io_{}".format(ori.name[0]), + edge = OrientationTuple(False, **{ori.name: True})) + iotiles[ori] = builder.fill( (1., 1.) ).auto_connect().commit() + + builder = ctx.build_array('top', 8, 8, set_as_top = True) + for x, y in product(range(builder.width), range(builder.height)): + if x in (0, builder.width - 1) and y in (0, builder.height - 1): + pass + elif x == 0: + builder.instantiate(iotiles[Orientation.west], (x, y)) + elif x == builder.width - 1: + builder.instantiate(iotiles[Orientation.east], (x, y)) + elif y == 0: + builder.instantiate(iotiles[Orientation.south], (x, y)) + elif y == builder.height - 1: + builder.instantiate(iotiles[Orientation.north], (x, y)) + else: + builder.instantiate(clbtile, (x, y)) + top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() + + Flow( + VPRArchGeneration('vpr/arch.xml'), + VPR_RRG_Generation('vpr/rrg.xml'), + YosysScriptsCollection('syn'), + ).run(ctx) + ctx.pickle("ctx.tmp.pkl") + +Flow( + Materialization('frame'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) + +ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/frame/k4_N2_8x8_b8/Makefile b/examples/fpga/frame/k4_N2_8x8_b8/Makefile new file mode 100644 index 00000000..f9c03dd5 --- /dev/null +++ b/examples/fpga/frame/k4_N2_8x8_b8/Makefile @@ -0,0 +1 @@ +include ../../Makefile.in diff --git a/examples/fpga/frame/k4_N2_8x8_b8/build.py b/examples/fpga/frame/k4_N2_8x8_b8/build.py new file mode 100644 index 00000000..ffd785dc --- /dev/null +++ b/examples/fpga/frame/k4_N2_8x8_b8/build.py @@ -0,0 +1,85 @@ +from prga import * +from itertools import product + +import sys +import logging + +logging.getLogger("prga").setLevel(logging.DEBUG) + +try: + ctx = Context.unpickle("ctx.tmp.pkl") + +except FileNotFoundError: + ctx = Context() + gbl_clk = ctx.create_global("clk", is_clock = True) + gbl_clk.bind((0, 1), 0) + ctx.create_segment('L1', 20, 1) + + builder = ctx.build_slice("slice") + clk = builder.create_clock("clk") + i = builder.create_input("i", 4) + o = builder.create_output("o", 1) + lut = builder.instantiate(ctx.primitives["lut4"], "lut") + ff = builder.instantiate(ctx.primitives["flipflop"], "ff") + builder.connect(clk, ff.pins['clk']) + builder.connect(i, lut.pins['in']) + builder.connect(lut.pins['out'], o) + builder.connect(lut.pins['out'], ff.pins['D'], vpr_pack_patterns = ('lut_dff', )) + builder.connect(ff.pins['Q'], o) + cluster = builder.commit() + + builder = ctx.build_io_block("iob") + o = builder.create_input("outpad", 1) + i = builder.create_output("inpad", 1) + builder.connect(builder.instances['io'].pins['inpad'], i) + builder.connect(o, builder.instances['io'].pins['outpad']) + iob = builder.commit() + + builder = ctx.build_logic_block("clb") + clk = builder.create_global(gbl_clk, Orientation.south) + for i, inst in enumerate(builder.instantiate(cluster, "cluster", 2)): + builder.connect(clk, inst.pins['clk']) + builder.connect(builder.create_input("i{}".format(i), 4, Orientation.west), inst.pins['i']) + builder.connect(inst.pins['o'], builder.create_output("o{}".format(i), 1, Orientation.east)) + clb = builder.commit() + + clbtile = ctx.build_tile(clb).fill( (0.4, 0.25) ).auto_connect().commit() + + iotiles = {} + for ori in Orientation: + builder = ctx.build_tile(iob, 4, name = "t_io_{}".format(ori.name[0]), + edge = OrientationTuple(False, **{ori.name: True})) + iotiles[ori] = builder.fill( (1., 1.) ).auto_connect().commit() + + builder = ctx.build_array('top', 8, 8, set_as_top = True) + for x, y in product(range(builder.width), range(builder.height)): + if x in (0, builder.width - 1) and y in (0, builder.height - 1): + pass + elif x == 0: + builder.instantiate(iotiles[Orientation.west], (x, y)) + elif x == builder.width - 1: + builder.instantiate(iotiles[Orientation.east], (x, y)) + elif y == 0: + builder.instantiate(iotiles[Orientation.south], (x, y)) + elif y == builder.height - 1: + builder.instantiate(iotiles[Orientation.north], (x, y)) + else: + builder.instantiate(clbtile, (x, y)) + top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() + + Flow( + VPRArchGeneration('vpr/arch.xml'), + VPR_RRG_Generation('vpr/rrg.xml'), + YosysScriptsCollection('syn'), + ).run(ctx) + ctx.pickle("ctx.tmp.pkl") + +Flow( + Materialization('frame', word_width = 8), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) + +ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/magic/fle6_N10_mem32Kb_mul24x18_42x34/build.py b/examples/fpga/magic/fle6_N10_mem32Kb_mul24x18_42x34/build.py index e8dcc6fd..c64bbf6d 100644 --- a/examples/fpga/magic/fle6_N10_mem32Kb_mul24x18_42x34/build.py +++ b/examples/fpga/magic/fle6_N10_mem32Kb_mul24x18_42x34/build.py @@ -3,8 +3,6 @@ from itertools import product import sys -r = Magic.new_renderer() - try: ctx = Context.unpickle("ctx.tmp.pkl") @@ -20,7 +18,7 @@ mul_a_width = 24 mul_b_width = 18 - ctx = Magic.new_context() + ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) @@ -117,20 +115,19 @@ top = builder.fill( pattern ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration("vpr/arch.xml"), VPR_RRG_Generation("vpr/rrg.xml"), - # VerilogCollection('rtl'), - # YosysScriptsCollection("syn"), - ).run(ctx, r) + YosysScriptsCollection("syn"), + ).run(ctx) ctx.pickle("ctx.tmp.pkl") Flow( + Materialization('magic'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), VerilogCollection('rtl'), - YosysScriptsCollection("syn"), - ).run(ctx, r) + ).run(ctx) ctx.pickle(sys.argv[1] if len(sys.argv) > 1 else "ctx.pkl") diff --git a/examples/fpga/magic/fle6_N2_mem2K_8x8/build.py b/examples/fpga/magic/fle6_N2_mem2K_8x8/build.py index 2e7a386a..0ff8c318 100644 --- a/examples/fpga/magic/fle6_N2_mem2K_8x8/build.py +++ b/examples/fpga/magic/fle6_N2_mem2K_8x8/build.py @@ -3,7 +3,7 @@ import sys -ctx = Magic.new_context() +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L1', 20, 1) @@ -30,7 +30,7 @@ clb = builder.commit() builder = ctx.build_logic_block("bram", 1, 2) -inst = builder.instantiate(ctx.create_memory("ram_a8d8", 8, 8), "i_ram") +inst = builder.instantiate(ctx.create_memory(8, 8), "i_ram") builder.connect(builder.create_global(gbl_clk, Orientation.south), inst.pins["clk"]) builder.connect(builder.create_input("we", 1, Orientation.west, (0, 0)), inst.pins["we"]) builder.connect(builder.create_input("waddr", len(inst.pins["waddr"]), Orientation.west, (0, 0)), inst.pins["waddr"]) @@ -79,13 +79,14 @@ top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Magic.new_renderer()) + Materialization('magic'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/magic/grady18_N2_10x6/build.py b/examples/fpga/magic/grady18_N2_10x6/build.py index cc9c7637..5fdb9d43 100644 --- a/examples/fpga/magic/grady18_N2_10x6/build.py +++ b/examples/fpga/magic/grady18_N2_10x6/build.py @@ -3,7 +3,7 @@ import sys -ctx = Magic.new_context() +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L1', 20, 1) @@ -20,7 +20,7 @@ in_ = builder.create_input("in", 16, Orientation.west) out = builder.create_output("out", 8, Orientation.east) cin = builder.create_input("cin", 1, Orientation.south) -for i, inst in enumerate(builder.instantiate(ctx.primitives["grady18"], "i_grady18", 2)): +for i, inst in enumerate(builder.instantiate(ctx.primitives["grady18:v0"], "i_grady18", 2)): builder.connect(clk, inst.pins['clk']) builder.connect(in_[8 * i: 8 * (i + 1)], inst.pins['in']) builder.connect(inst.pins["cout_fabric"], out[4 * i + 2: 4 * i + 4]) @@ -56,13 +56,14 @@ top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Magic.new_renderer()) + Materialization('magic'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/magic/grady18v2_N10_mem32Kb_42x34/build.py b/examples/fpga/magic/grady18v2_N10_mem32Kb_42x34/build.py index 1081d9f8..7deb0735 100644 --- a/examples/fpga/magic/grady18v2_N10_mem32Kb_42x34/build.py +++ b/examples/fpga/magic/grady18v2_N10_mem32Kb_42x34/build.py @@ -2,8 +2,6 @@ from itertools import product import sys - -r = Magic.new_renderer() try: ctx = Context.unpickle("ctx.tmp.pkl") @@ -16,16 +14,15 @@ mem_data_width = 64 mem_addr_width = 12 # 4K8b - ctx = Magic.new_context() + ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) - l1 = ctx.create_segment('L1', 40, 1) - l4 = ctx.create_segment('L4', 12, 4) - l16 = ctx.create_segment('L16', 2, 16) + l1 = ctx.create_segment('L1', 80, 1) + l4 = ctx.create_segment('L4', 20, 4) builder = ctx.build_logic_block("clb") - grady18v2 = ctx.primitives["grady18v2"] + grady18v2 = ctx.primitives["grady18"] lin = len(grady18v2.ports["in"]) lout = len(grady18v2.ports["out"]) @@ -35,7 +32,7 @@ in_ = builder.create_input("in", lin * N, Orientation.west) out = builder.create_output("out", lout * N, Orientation.east) cin = builder.create_input("cin", 1, Orientation.south) - for i, inst in enumerate(builder.instantiate(grady18v2, "i_grady18", N)): + for i, inst in enumerate(builder.instantiate(grady18v2, "i_ble", N)): builder.connect(clk, inst.pins['clk']) builder.connect(ce, inst.pins['ce']) builder.connect(in_[lin * i: lin * (i + 1)], inst.pins['in']) @@ -100,20 +97,19 @@ top = builder.fill( pattern ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration("vpr/arch.xml"), VPR_RRG_Generation("vpr/rrg.xml"), - # VerilogCollection('rtl'), - # YosysScriptsCollection("syn"), - ).run(ctx, r) + YosysScriptsCollection("syn"), + ).run(ctx) ctx.pickle("ctx.tmp.pkl") Flow( + Materialization('magic'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), VerilogCollection('rtl'), - YosysScriptsCollection("syn"), - ).run(ctx, r) + ).run(ctx) ctx.pickle(sys.argv[1] if len(sys.argv) > 1 else "ctx.pkl") diff --git a/examples/fpga/magic/grady18v2_N2_8x8_hier/build.py b/examples/fpga/magic/grady18v2_N2_8x8_hier/build.py index e5e3f016..3c1f0a4c 100644 --- a/examples/fpga/magic/grady18v2_N2_8x8_hier/build.py +++ b/examples/fpga/magic/grady18v2_N2_8x8_hier/build.py @@ -3,7 +3,7 @@ import sys -ctx = Magic.new_context() +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L2', 20, 2) @@ -16,7 +16,7 @@ iob = builder.commit() builder = ctx.build_logic_block("clb") -grady18v2 = ctx.primitives["grady18v2"] +grady18v2 = ctx.primitives["grady18"] N = 2 lin = len(grady18v2.ports["in"]) @@ -69,13 +69,14 @@ top = builder.fill( pattern ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Magic.new_renderer()) + Materialization('magic'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/magic/hardpico/build.py b/examples/fpga/magic/hardpico/build.py index c528bec4..19191a99 100644 --- a/examples/fpga/magic/hardpico/build.py +++ b/examples/fpga/magic/hardpico/build.py @@ -7,7 +7,7 @@ # ============================================================================ # -- Create Context ---------------------------------------------------------- # ============================================================================ -ctx = Magic.new_context() +ctx = Context(['src']) # ============================================================================ # -- Routing Resources ------------------------------------------------------- @@ -93,11 +93,11 @@ # -- CLB --------------------------------------------------------------------- builder = ctx.build_logic_block("clb") -grady18v2 = ctx.primitives["grady18v2"] +grady18 = ctx.primitives["grady18"] N = 4 -lin = len(grady18v2.ports["in"]) -lout = len(grady18v2.ports["out"]) +lin = len(grady18.ports["in"]) +lout = len(grady18.ports["out"]) clk = builder.create_global(gbl_clk, Orientation.south) ce = builder.create_input("ce", 1, Orientation.west) @@ -108,7 +108,7 @@ xin, xout = [], [] xin.extend(in_) -for i, inst in enumerate(builder.instantiate(grady18v2, "i_grady18", N)): +for i, inst in enumerate(builder.instantiate(grady18, "i_grady18", N)): builder.connect(clk, inst.pins['clk']) builder.connect(ce, inst.pins['ce']) builder.connect(cin, inst.pins["cin"], vpr_pack_patterns = ["carrychain"]) @@ -293,13 +293,14 @@ # -- Workflow ---------------------------------------------------------------- # ============================================================================ Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Magic.new_renderer(["src"])) # add `src` into file rendering template search path + Materialization("magic"), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx, ) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/magic/k4_N2_8x8/build.py b/examples/fpga/magic/k4_N2_8x8/build.py index 06e0b3ef..c65eed06 100644 --- a/examples/fpga/magic/k4_N2_8x8/build.py +++ b/examples/fpga/magic/k4_N2_8x8/build.py @@ -3,7 +3,7 @@ import sys -ctx = Magic.new_context() +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L1', 20, 1) @@ -61,13 +61,14 @@ top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Magic.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Magic.new_renderer()) + Materialization('magic'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/pktchain/defaultchain_k4_N2_8x8/Makefile b/examples/fpga/pktchain/defaultchain_k4_N2_8x8/Makefile new file mode 100644 index 00000000..f9c03dd5 --- /dev/null +++ b/examples/fpga/pktchain/defaultchain_k4_N2_8x8/Makefile @@ -0,0 +1 @@ +include ../../Makefile.in diff --git a/examples/fpga/pktchain/defaultchain_k4_N2_8x8/build.py b/examples/fpga/pktchain/defaultchain_k4_N2_8x8/build.py new file mode 100644 index 00000000..1bcfa525 --- /dev/null +++ b/examples/fpga/pktchain/defaultchain_k4_N2_8x8/build.py @@ -0,0 +1,77 @@ +from prga import * +from itertools import product + +import sys + +import logging +logging.getLogger("prga").setLevel(logging.DEBUG) + +ctx = Context() +gbl_clk = ctx.create_global("clk", is_clock = True) +gbl_clk.bind((0, 1), 0) +ctx.create_segment('L1', 20, 1) + +builder = ctx.build_slice("slice") +clk = builder.create_clock("clk") +i = builder.create_input("i", 4) +o = builder.create_output("o", 1) +lut = builder.instantiate(ctx.primitives["lut4"], "lut") +ff = builder.instantiate(ctx.primitives["flipflop"], "ff") +builder.connect(clk, ff.pins['clk']) +builder.connect(i, lut.pins['in']) +builder.connect(lut.pins['out'], o) +builder.connect(lut.pins['out'], ff.pins['D'], vpr_pack_patterns = ('lut_dff', )) +builder.connect(ff.pins['Q'], o) +cluster = builder.commit() + +builder = ctx.build_io_block("iob") +o = builder.create_input("outpad", 1) +i = builder.create_output("inpad", 1) +builder.connect(builder.instances['io'].pins['inpad'], i) +builder.connect(o, builder.instances['io'].pins['outpad']) +iob = builder.commit() + +builder = ctx.build_logic_block("clb") +clk = builder.create_global(gbl_clk, Orientation.south) +for i, inst in enumerate(builder.instantiate(cluster, "cluster", 2)): + builder.connect(clk, inst.pins['clk']) + builder.connect(builder.create_input("i{}".format(i), 4, Orientation.west), inst.pins['i']) + builder.connect(inst.pins['o'], builder.create_output("o{}".format(i), 1, Orientation.east)) +clb = builder.commit() + +clbtile = ctx.build_tile(clb).fill( (0.4, 0.25) ).auto_connect().commit() + +iotiles = {} +for ori in Orientation: + builder = ctx.build_tile(iob, 4, name = "t_io_{}".format(ori.name[0]), + edge = OrientationTuple(False, **{ori.name: True})) + iotiles[ori] = builder.fill( (1., 1.) ).auto_connect().commit() + +builder = ctx.build_array('top', 8, 8, set_as_top = True) +for x, y in product(range(builder.width), range(builder.height)): + if x in (0, builder.width - 1) and y in (0, builder.height - 1): + pass + elif x == 0: + builder.instantiate(iotiles[Orientation.west], (x, y)) + elif x == builder.width - 1: + builder.instantiate(iotiles[Orientation.east], (x, y)) + elif y == 0: + builder.instantiate(iotiles[Orientation.south], (x, y)) + elif y == builder.height - 1: + builder.instantiate(iotiles[Orientation.north], (x, y)) + else: + builder.instantiate(clbtile, (x, y)) +top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() + +Flow( + VPRArchGeneration('vpr/arch.xml'), + VPR_RRG_Generation('vpr/rrg.xml'), + YosysScriptsCollection('syn'), + Materialization('pktchain', chain_width = 1, phit_width = 4), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) + +ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/pktchain/fpga21/build.py b/examples/fpga/pktchain/fpga21/build.py index 4acd979e..791ac919 100644 --- a/examples/fpga/pktchain/fpga21/build.py +++ b/examples/fpga/pktchain/fpga21/build.py @@ -18,10 +18,7 @@ ctx = Context.unpickle("ctx.tmp.pkl") except FileNotFoundError: - ctx = Pktchain.new_context( - phit_width = PHIT_WIDTH, - chain_width = CHAIN_WIDTH, - ) + ctx = Context() # ============================================================================ # -- Routing Resources ------------------------------------------------------- @@ -47,7 +44,7 @@ clk = builder.create_global(glb_clk, Orientation.south) in_ = builder.create_input ("in", - N * len(ctx.primitives["grady18v2"].ports["in"]) // 2, Orientation.east) + N * len(ctx.primitives["grady18"].ports["in"]) // 2, Orientation.east) ce = builder.create_input ("ce", 1, Orientation.east) ci = builder.create_input ("ci", 1, Orientation.south) out = builder.create_output("out", N * 2, Orientation.east) @@ -56,7 +53,7 @@ xbar_i, xbar_o = [], [] xbar_i.extend(in_) - for i, inst in enumerate(builder.instantiate(ctx.primitives["grady18v2"], "i_cluster", N)): + for i, inst in enumerate(builder.instantiate(ctx.primitives["grady18"], "i_cluster", N)): builder.connect(clk, inst.pins['clk']) builder.connect(ce, inst.pins["ce"]) builder.connect(inst.pins['out'], out[(l := len(inst.pins["out"])) * i: l * (i + 1)]) @@ -155,93 +152,96 @@ builder.instantiate(iotiles[Orientation.east], (M_LOGIC * W_LOGIC + 1, y + 1)) top = builder.fill( pattern ).auto_connect().commit() - # ============================================================================ - # -- Configuration Chain Injection ------------------------------------------- - # ============================================================================ - def iter_instances(module): - if module.name == "tile_clb": - yield module.instances[0] - yield module.instances[Orientation.east, 0] - elif module.name.startswith("tile_iob_"): - for i in range(NUM_IOB_PER_TILE): - yield module.instances[i] - for ori in Orientation: - if i := module.instances.get( (ori, 0) ): - yield i - elif module.name == "tile_bram": - yield module.instances[Orientation.east, 0] - yield module.instances[Orientation.east, 1] - yield module.instances[0] - elif module.name == "subarray": - for y in range(module.height): - for x in range(module.width): - if x > 0 and (i := module.instances.get( ((x, y), Corner.southwest) )): yield i - if i := module.instances.get( (x, y) ): yield i - if i := module.instances.get( ((x, y), Corner.southeast) ): yield i - for x in reversed(range(module.width)): - if i := module.instances.get( ((x, y), Corner.northeast) ): yield i - if x > 0 and (i := module.instances.get( ((x, y), Corner.northwest) )): yield i - for y in reversed(range(module.height)): - if (i := module.instances.get( ((0, y), Corner.northwest) )): yield i - if (i := module.instances.get( ((0, y), Corner.southwest) )): yield i - yield None - elif module.name == "top": - # go in a circle to program all IOBs and their neighbouring switch boxes - if i:= module.instances.get( ((0, 0), Corner.northeast) ): yield i - for y in range(1, N_LOGIC * H_LOGIC + 1): - if i := module.instances.get( ((0, y), Corner.southeast) ): yield i - if i := module.instances.get( (0, y) ): yield i - if i := module.instances.get( ((0, y), Corner.northeast) ): yield i - # wrap up and insert leaf router - yield None - if i:= module.instances.get( ((0, N_LOGIC * H_LOGIC + 1), Corner.southeast) ): yield i - for x in range(1, M_LOGIC * W_LOGIC + 1): - if i := module.instances.get( ((x, N_LOGIC * H_LOGIC + 1), Corner.southwest) ): yield i - if i := module.instances.get( (x, N_LOGIC * H_LOGIC + 1) ): yield i - if i := module.instances.get( ((x, N_LOGIC * H_LOGIC + 1), Corner.southeast) ): yield i - # wrap up and insert leaf router - yield None - if i:= module.instances.get( ((M_LOGIC * W_LOGIC + 1, N_LOGIC * H_LOGIC + 1), Corner.southwest) ): yield i - for y in reversed(range(1, N_LOGIC * H_LOGIC + 1)): - if i := module.instances.get( ((M_LOGIC * W_LOGIC + 1, y), Corner.northwest) ): yield i - if i := module.instances.get( (M_LOGIC * W_LOGIC + 1, y) ): yield i - if i := module.instances.get( ((M_LOGIC * W_LOGIC + 1, y), Corner.southwest) ): yield i - # wrap up and insert leaf router - yield None - if i:= module.instances.get( ((M_LOGIC * W_LOGIC + 1, 0), Corner.northwest) ): yield i - for x in reversed(range(1, M_LOGIC * W_LOGIC + 1)): - if i := module.instances.get( ((x, 0), Corner.northeast) ): yield i - if i := module.instances.get( (x, 0) ): yield i - if i := module.instances.get( ((x, 0), Corner.northwest) ): yield i - # wrap up and insert leaf router - yield None - # wrap up branches and attach to the main chunk - yield None - - for x in range(M_LOGIC): - yield module.instances[ x * W_LOGIC + 1, 0 * H_LOGIC + 1 ] - yield module.instances[ x * W_LOGIC + 1, 2 * H_LOGIC + 1 ] - yield module.instances[ x * W_LOGIC + 1, 3 * H_LOGIC + 1 ] - yield module.instances[ x * W_LOGIC + 1, 1 * H_LOGIC + 1 ] - yield None - yield None - else: - for i in module.instances.values(): - yield i - Flow( - Translation(), - SwitchPathAnnotation(), - Pktchain.InsertProgCircuitry(iter_instances = iter_instances), VPRArchGeneration("vpr/arch.xml"), VPR_RRG_Generation("vpr/rrg.xml"), YosysScriptsCollection("syn"), - ).run(ctx, Pktchain.new_renderer()) + ).run(ctx) ctx.pickle("ctx.tmp.pkl") +# ============================================================================ +# -- Configuration Chain Injection ------------------------------------------- +# ============================================================================ +def iter_instances(module): + if module.name == "tile_clb": + yield module.instances[0] + yield module.instances[Orientation.east, 0] + elif module.name.startswith("tile_iob_"): + for i in range(NUM_IOB_PER_TILE): + yield module.instances[i] + for ori in Orientation: + if i := module.instances.get( (ori, 0) ): + yield i + elif module.name == "tile_bram": + yield module.instances[Orientation.east, 0] + yield module.instances[Orientation.east, 1] + yield module.instances[0] + elif module.name == "subarray": + for y in range(module.height): + for x in range(module.width): + if x > 0 and (i := module.instances.get( ((x, y), Corner.southwest) )): yield i + if i := module.instances.get( (x, y) ): yield i + if i := module.instances.get( ((x, y), Corner.southeast) ): yield i + for x in reversed(range(module.width)): + if i := module.instances.get( ((x, y), Corner.northeast) ): yield i + if x > 0 and (i := module.instances.get( ((x, y), Corner.northwest) )): yield i + for y in reversed(range(module.height)): + if (i := module.instances.get( ((0, y), Corner.northwest) )): yield i + if (i := module.instances.get( ((0, y), Corner.southwest) )): yield i + yield Pktchain.TERMINATE_LEAF + elif module.name == "top": + # go in a circle to program all IOBs and their neighbouring switch boxes + if i:= module.instances.get( ((0, 0), Corner.northeast) ): yield i + for y in range(1, N_LOGIC * H_LOGIC + 1): + if i := module.instances.get( ((0, y), Corner.southeast) ): yield i + if i := module.instances.get( (0, y) ): yield i + if i := module.instances.get( ((0, y), Corner.northeast) ): yield i + # wrap up and insert leaf router + yield Pktchain.TERMINATE_LEAF + if i:= module.instances.get( ((0, N_LOGIC * H_LOGIC + 1), Corner.southeast) ): yield i + for x in range(1, M_LOGIC * W_LOGIC + 1): + if i := module.instances.get( ((x, N_LOGIC * H_LOGIC + 1), Corner.southwest) ): yield i + if i := module.instances.get( (x, N_LOGIC * H_LOGIC + 1) ): yield i + if i := module.instances.get( ((x, N_LOGIC * H_LOGIC + 1), Corner.southeast) ): yield i + # wrap up and insert leaf router + yield Pktchain.TERMINATE_LEAF + if i:= module.instances.get( ((M_LOGIC * W_LOGIC + 1, N_LOGIC * H_LOGIC + 1), Corner.southwest) ): yield i + for y in reversed(range(1, N_LOGIC * H_LOGIC + 1)): + if i := module.instances.get( ((M_LOGIC * W_LOGIC + 1, y), Corner.northwest) ): yield i + if i := module.instances.get( (M_LOGIC * W_LOGIC + 1, y) ): yield i + if i := module.instances.get( ((M_LOGIC * W_LOGIC + 1, y), Corner.southwest) ): yield i + # wrap up and insert leaf router + yield Pktchain.TERMINATE_LEAF + if i:= module.instances.get( ((M_LOGIC * W_LOGIC + 1, 0), Corner.northwest) ): yield i + for x in reversed(range(1, M_LOGIC * W_LOGIC + 1)): + if i := module.instances.get( ((x, 0), Corner.northeast) ): yield i + if i := module.instances.get( (x, 0) ): yield i + if i := module.instances.get( ((x, 0), Corner.northwest) ): yield i + # wrap up and insert leaf router + yield Pktchain.TERMINATE_LEAF + # wrap up branches and attach to the main chunk + yield Pktchain.TERMINATE_BRANCH + + for x in range(M_LOGIC): + yield module.instances[ x * W_LOGIC + 1, 0 * H_LOGIC + 1 ] + yield module.instances[ x * W_LOGIC + 1, 2 * H_LOGIC + 1 ] + yield module.instances[ x * W_LOGIC + 1, 3 * H_LOGIC + 1 ] + yield module.instances[ x * W_LOGIC + 1, 1 * H_LOGIC + 1 ] + yield Pktchain.TERMINATE_BRANCH + else: + for i in module.instances.values(): + yield i + Flow( + Materialization('pktchain', + chain_width = CHAIN_WIDTH, + phit_width = PHIT_WIDTH, + router_fifo_depth_log2 = 8), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(iter_instances = iter_instances), VerilogCollection("rtl", "include"), - ).run(ctx, Pktchain.new_renderer()) + ).run(ctx) ctx.pickle("ctx.pkl") diff --git a/examples/fpga/pktchain/k4_N2_8x8/build.py b/examples/fpga/pktchain/k4_N2_8x8/build.py index a1f51252..bee11153 100644 --- a/examples/fpga/pktchain/k4_N2_8x8/build.py +++ b/examples/fpga/pktchain/k4_N2_8x8/build.py @@ -3,7 +3,7 @@ import sys -ctx = Pktchain.new_context( chain_width = 1, phit_width = 4 ) +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L1', 20, 1) @@ -60,6 +60,12 @@ builder.instantiate(clbtile, (x, y)) top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() +Flow( + VPRArchGeneration('vpr/arch.xml'), + VPR_RRG_Generation('vpr/rrg.xml'), + YosysScriptsCollection('syn'), + ).run(ctx) + def iter_instances(module): if module.name == "top": for x in range(4): @@ -69,55 +75,53 @@ def iter_instances(module): for corner in Corner: if (box := module.instances.get( ((x * 2 + xx, 7), corner) )) is not None: yield box - yield None + yield Pktchain.TERMINATE_LEAF for yy in range(3): if (t := module.instances.get( (x * 2, 6 - yy) )) is not None: yield t for corner in Corner: if (box := module.instances.get( ((x * 2, 6 - yy), corner) )) is not None: yield box - yield None + yield Pktchain.TERMINATE_LEAF for yy in range(3): if (t := module.instances.get( (x * 2, 3 - yy) )) is not None: yield t for corner in Corner: if (box := module.instances.get( ((x * 2, 3 - yy), corner) )) is not None: yield box - yield None + yield Pktchain.TERMINATE_LEAF for xx in range(2): if (t := module.instances.get( (x * 2 + xx, 0) )) is not None: yield t for corner in Corner: if (box := module.instances.get( ((x * 2 + xx, 0), corner) )) is not None: yield box - yield None + yield Pktchain.TERMINATE_LEAF for yy in range(3): if (t := module.instances.get( (x * 2 + 1, 1 + yy) )) is not None: yield t for corner in Corner: if (box := module.instances.get( ((x * 2 + 1, 1 + yy), corner) )) is not None: yield box - yield None + yield Pktchain.TERMINATE_LEAF for yy in range(3): if (t := module.instances.get( (x * 2 + 1, 4 + yy) )) is not None: yield t for corner in Corner: if (box := module.instances.get( ((x * 2 + 1, 4 + yy), corner) )) is not None: yield box - yield None - yield None + yield Pktchain.TERMINATE_LEAF + yield Pktchain.TERMINATE_BRANCH else: for i in module.instances.values(): yield i Flow( + Materialization('pktchain', chain_width = 1, phit_width = 4), Translation(), SwitchPathAnnotation(), - Pktchain.InsertProgCircuitry(iter_instances = iter_instances), - VPRArchGeneration('vpr/arch.xml'), - VPR_RRG_Generation('vpr/rrg.xml'), + ProgCircuitryInsertion(iter_instances = iter_instances), VerilogCollection('rtl'), - YosysScriptsCollection('syn'), - ).run(ctx, Pktchain.new_renderer()) + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/scanchain/fle6_N2_mem2K_8x8/build.py b/examples/fpga/scanchain/fle6_N2_mem2K_8x8/build.py index 0005f507..998d04f3 100644 --- a/examples/fpga/scanchain/fle6_N2_mem2K_8x8/build.py +++ b/examples/fpga/scanchain/fle6_N2_mem2K_8x8/build.py @@ -3,12 +3,12 @@ import sys -ctx = Scanchain.new_context() +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L2', 20, 2) -memory = ctx.create_memory("ram_a8d8", 8, 8) +memory = ctx.create_memory(8, 8) builder = ctx.build_logic_block("clb") clk = builder.create_global(gbl_clk, Orientation.south) @@ -85,17 +85,15 @@ builder.instantiate(subarray, (x, y)) top = builder.fill( pattern ).auto_connect().commit() -renderer = Scanchain.new_renderer() - -flow = Flow( - Translation(), - SwitchPathAnnotation(), - Scanchain.InsertProgCircuitry(), +Flow( VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ) -flow.run(ctx, renderer) + Materialization('scanchain', chain_width = 1), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/scanchain/grady18_N2_10x6/build.py b/examples/fpga/scanchain/grady18_N2_10x6/build.py index daa9bf78..a5041f91 100644 --- a/examples/fpga/scanchain/grady18_N2_10x6/build.py +++ b/examples/fpga/scanchain/grady18_N2_10x6/build.py @@ -3,7 +3,7 @@ import sys -ctx = Scanchain.new_context(2) +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L1', 20, 1) @@ -20,7 +20,7 @@ in_ = builder.create_input("in", 16, Orientation.west) out = builder.create_output("out", 8, Orientation.east) cin = builder.create_input("cin", 1, Orientation.south) -for i, inst in enumerate(builder.instantiate(ctx.primitives["grady18"], "i_grady18", 2)): +for i, inst in enumerate(builder.instantiate(ctx.primitives["grady18:v0"], "i_grady18", 2)): builder.connect(clk, inst.pins['clk']) builder.connect(in_[8 * i: 8 * (i + 1)], inst.pins['in']) builder.connect(inst.pins["cout_fabric"], out[4 * i + 2: 4 * i + 4]) @@ -56,13 +56,14 @@ top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Scanchain.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Scanchain.new_renderer()) + Materialization('scanchain', chain_width = 2), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/scanchain/grady18v2_N2_8x8_hier/build.py b/examples/fpga/scanchain/grady18v2_N2_8x8_hier/build.py index 9c82b513..67994fcb 100644 --- a/examples/fpga/scanchain/grady18v2_N2_8x8_hier/build.py +++ b/examples/fpga/scanchain/grady18v2_N2_8x8_hier/build.py @@ -3,7 +3,7 @@ import sys -ctx = Scanchain.new_context(4) +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L2', 20, 2) @@ -16,7 +16,7 @@ iob = builder.commit() builder = ctx.build_logic_block("clb") -grady18v2 = ctx.primitives["grady18v2"] +grady18v2 = ctx.primitives["grady18"] N = 2 lin = len(grady18v2.ports["in"]) @@ -69,13 +69,14 @@ top = builder.fill( pattern ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Scanchain.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Scanchain.new_renderer()) + Materialization('scanchain', chain_width = 4), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/examples/fpga/scanchain/k4_N2_8x8/build.py b/examples/fpga/scanchain/k4_N2_8x8/build.py index b08a2bb1..582d214b 100644 --- a/examples/fpga/scanchain/k4_N2_8x8/build.py +++ b/examples/fpga/scanchain/k4_N2_8x8/build.py @@ -3,7 +3,7 @@ import sys -ctx = Scanchain.new_context() +ctx = Context() gbl_clk = ctx.create_global("clk", is_clock = True) gbl_clk.bind((0, 1), 0) ctx.create_segment('L1', 20, 1) @@ -61,13 +61,14 @@ top = builder.fill( SwitchBoxPattern.cycle_free ).auto_connect().commit() Flow( - Translation(), - SwitchPathAnnotation(), - Scanchain.InsertProgCircuitry(), VPRArchGeneration('vpr/arch.xml'), VPR_RRG_Generation('vpr/rrg.xml'), - VerilogCollection('rtl'), YosysScriptsCollection('syn'), - ).run(ctx, Scanchain.new_renderer()) + Materialization('scanchain'), + Translation(), + SwitchPathAnnotation(), + ProgCircuitryInsertion(), + VerilogCollection('rtl'), + ).run(ctx) ctx.pickle("ctx.pkl" if len(sys.argv) < 2 else sys.argv[1]) diff --git a/prga.py b/prga.py index 07611b87..c13fb90c 160000 --- a/prga.py +++ b/prga.py @@ -1 +1 @@ -Subproject commit 07611b878cdc8f89e9c3c2209a3d0946d596734b +Subproject commit c13fb90c5d32e54eb5b8878f3c0adb2da7ac13c5