From 23c78c2f910d0abe96fe11a62c1a374c690098be Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 2 May 2019 14:50:06 +0200 Subject: [PATCH] Fiction is art and art is the triumph over chaos --- .gitattributes | 1 + CHANGELOG.md | 34 ++++- CMakeLists.txt | 20 ++- README.md | 30 ++-- benchmarks/MAJ/1bitAdderAOIG.v | 14 ++ benchmarks/MAJ/1bitAdderMaj.v | 7 + benchmarks/MAJ/RCA2.v | 19 +++ benchmarks/MAJ/clpl.v | 20 +++ benchmarks/MAJ/mux21.v | 9 ++ benchmarks/MAJ/mux41.v | 13 ++ benchmarks/MAJ/newtag.v | 12 ++ benchmarks/MAJ/par_check.v | 11 ++ benchmarks/MAJ/par_gen.v | 9 ++ benchmarks/MAJ/t.v | 12 ++ benchmarks/MAJ/xnor2.v | 9 ++ benchmarks/MAJ/xor2.v | 9 ++ benchmarks/MAJ/xor5R.v | 12 ++ benchmarks/MAJ/xor5_r1.v | 12 ++ shortcuts.fs | 10 ++ src/algo/exact_pr.cpp | 150 +++++++++--------- src/algo/exact_pr.h | 4 +- src/algo/exact_pr_config.h | 2 +- src/algo/orthogonal_pr.cpp | 53 ++++--- src/algo/orthogonal_pr.h | 7 +- src/io/commands.h | 197 +++++++++++++++--------- src/io/stores.h | 3 +- src/io/svg_writer.cpp | 6 +- src/io/svg_writer.h | 11 +- src/tech/fcn_cell_layout.cpp | 10 +- src/tech/fcn_cell_layout.h | 5 +- src/tech/qca_one_library.cpp | 235 ++++++++++++++-------------- src/tech/qca_one_library.h | 4 +- src/topo/bidirectional_graph.h | 32 ---- src/topo/fcn_clocking_scheme.cpp | 39 ++++- src/topo/fcn_clocking_scheme.h | 88 +++++++---- src/topo/fcn_gate_layout.cpp | 255 +++++++++++++++++-------------- src/topo/fcn_gate_layout.h | 88 +++++++++-- src/topo/fcn_layout.h | 2 +- src/topo/logic_network.cpp | 5 - src/topo/logic_network.h | 6 - src/util/version.h.in | 21 +++ 41 files changed, 945 insertions(+), 541 deletions(-) create mode 100644 benchmarks/MAJ/1bitAdderAOIG.v create mode 100644 benchmarks/MAJ/1bitAdderMaj.v create mode 100644 benchmarks/MAJ/RCA2.v create mode 100644 benchmarks/MAJ/clpl.v create mode 100644 benchmarks/MAJ/mux21.v create mode 100644 benchmarks/MAJ/mux41.v create mode 100644 benchmarks/MAJ/newtag.v create mode 100644 benchmarks/MAJ/par_check.v create mode 100644 benchmarks/MAJ/par_gen.v create mode 100644 benchmarks/MAJ/t.v create mode 100644 benchmarks/MAJ/xnor2.v create mode 100644 benchmarks/MAJ/xor2.v create mode 100644 benchmarks/MAJ/xor5R.v create mode 100644 benchmarks/MAJ/xor5_r1.v create mode 100644 shortcuts.fs create mode 100644 src/util/version.h.in diff --git a/.gitattributes b/.gitattributes index 046098eff..b6e3bf955 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,4 @@ benchmarks/ISCAS89/* linguist-vendored benchmarks/EPFL/* linguist-vendored benchmarks/ITC99/* linguist-vendored *.v linguist-detectable=false +*.fs linguist-detectable=false diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1dbf43c..531536607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,36 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## v0.2.1 - 2019-05-02 +*Fiction is art and art is the triumph over chaos.* — John Cheever + +### Added +- Support for BANCS clocking and integration in `exact` +- Name strings for `fcn_clocking_scheme` objects and corresponding name-based look-up +- Version and build information accessible within the code by including `version.h` +- Parameter `-i` for command `ortho` +- `shortcuts.fs` with predefined flows +- `benchmarks/MAJ/` folder with some TOY benchmarks using MAJ gates + +### Changed +- Calls to `exact -s` now need to name the desired clocking, e.g. `exact -s use` (case insensitive) +- `incoming`/`outgoing_information_flow_tiles` have been renamed to `incoming`/`outgoing_data_flow` and handle multi wires now +- Renamed diagonal clocking schemes to 2DDWAVE and gave proper credit. +- More verbose error messages + +### Fixed +- TP calculation for layouts without designated I/O pins (thanks to Mario Kneidinger!) +- I/O port orientation of PI/PO gates using QCA-ONE library +- Usage of non-PI/PO MAJ gates in QCA-ONE library +- Visualization of clock latches in `show -c` (thanks to Gregor Kuhn!) +- Multi direction assignment to wires and gates in `exact` leading to physically impossible layouts +- `shrink_to_fit` in `fcn_gate_layout` incorporates the BGL bug now. Minimum size in each dimension is 2. +For more information, see +- Parameters for `exact` no longer get stuck once set + +### Removed +- Parameter `-n` for `exact` + ## v0.2.0 - 2019-03-21 *Fiction reveals the truth that reality obscures.* — Jessamyn West ### Added @@ -22,8 +52,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `write_qca` is now called `qca` and handles file names automatically if necessary - `-u` is not a required parameter for `exact` anymore - Richer output for `print -w` -- Included latest bugfixes for [Alice](https://github.com/msoeken/alice) -- Included latest update for [Lorina](https://github.com/hriener/lorina) +- Included latest bugfixes for [alice](https://github.com/msoeken/alice) +- Included latest update for [lorina](https://github.com/hriener/lorina) ### Fixed - Starting layout size for calls to `exact -i` was too low and has been corrected diff --git a/CMakeLists.txt b/CMakeLists.txt index 4424fb445..d182c8a75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.2) project(fiction LANGUAGES CXX - VERSION 0.2.0) + VERSION 0.2.1) # C++14 set(CMAKE_CXX_STANDARD 14) @@ -22,6 +22,10 @@ include_directories(src/algo/ src/io/ src/tech/ src/topo/ src/util/) # Find source files file(GLOB_RECURSE SOURCES src/*.cpp src/*.h) +# Add configuration file +configure_file(src/util/version.h.in util/version.h) +# Include configuration file +include_directories(${PROJECT_BINARY_DIR}/util/) # Require Boost libraries find_package(Boost COMPONENTS system filesystem REQUIRED) @@ -30,7 +34,7 @@ if(Boost_FOUND) link_directories(${Boost_LIBRARY_DIRS}) endif() -# custom install prefix for libraries +# Custom install prefix for libraries set(LIB_PREFIX ${CMAKE_SOURCE_DIR}/libs) # Clone, build and locally install Z3 @@ -49,21 +53,21 @@ ExternalProject_Add(z3 LOG_INSTALL 1 LOG_BUILD 1) -# include Z3 +# Include Z3 include_directories(${Z3_DIR}/include/) -# include cppitertools +# Include cppitertools include_directories(${LIB_PREFIX}/cppitertools/) -# include alice +# Include alice add_subdirectory(${LIB_PREFIX}/alice/) -# include lorina +# Include lorina add_subdirectory(${LIB_PREFIX}/lorina/) -# build executable +# Build executable add_executable(fiction ${SOURCES}) add_dependencies(fiction z3) -# link against Boost, Z3, alice, and lorina +# Link against Boost, Z3, alice, and lorina target_link_libraries(fiction ${Boost_LIBRARIES} ${Z3_DIR}/lib/libz3.so alice lorina) diff --git a/README.md b/README.md index 2347a4546..0887b20bc 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ More technologies and libraries are to come! An [exact approach](http://www.informatik.uni-bremen.de/agra/doc/konf/2018DATE_ExactMethodforDesignExplorationOfQCA.pdf) in terms of circuit area is provided which is the first one ever in the field of FCN circuits. It is based on Satisfiability Modulo Theories (SMT) and utilizes the solver engine [Z3](https://github.com/Z3Prover/z3) -by Microsoft Research. It is only feasable for small circuits but provides the best results. +by Microsoft Research. It is only feasible for small circuits but provides the best results. Additionally, a [scalable method](http://www.informatik.uni-bremen.de/agra/doc/konf/2019_ASP-DAC_Scalable_Design_for_Field-coupled_Nanocomputing_Circuits.pdf) is implemented which is based on Orthogonal Graph Drawing (OGD). It has a huge runtime advantage compared to the SMT technique. -Though, its results are not optimal in terms of area. It is feasable even for larger circuits and provides results real quick. +Though, its results are not optimal in terms of area. It is feasible even for larger circuits and provides results real quick. This is ongoing research but more algorithms are to come! @@ -49,7 +49,7 @@ CMake will inform you about missing dependencies during the build process. Check out the git project (and all of its submodules) using the following command: ```sh -git clone https://github.com/marcelwa/fiction.git --recursive +git clone https://gitlab.informatik.uni-bremen.de/m_walter/fiction.git --recursive ``` Several third-party libraries will be cloned within the `libs/` folder. The `cmake` build process will take care of @@ -150,7 +150,7 @@ parse all parsable files within that given directory powered by the [lorina](htt by Heinz Riener. The flag `-s` allows prior sorting. The content of the logic network store can be briefly viewed by entering `store -w` whereas `print -w` writes a [Graphviz](https://www.graphviz.org/) dot file of the current network to the standard output. Arbitrarily many logic networks can be held in store from which the latest is always the -active one. Change active network with `set -w ` where you replace `` by the number of the store element you +active one. Change active network with `current -w ` where you replace `` by the number of the store element you want to activate. ### Placement & Routing @@ -163,19 +163,19 @@ and route the current network on 25 FCN tiles, utilizing crossings (`-x`) and ar balancing I/O paths to internally synchronize the circuit using designated pins (`-i`) plus routing all I/Os to the grid borders for easier access (`-b`). -Additionally, you can provide a pre-defined clocking scheme to prune the search space. Doing so by option `-s` expects a -number where `0` represents default open clocking, `1` is -[USE](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7219390), `2` is a simple diagonal scheme, and `3` is the -[RES](https://www.tandfonline.com/doi/pdf/10.1080/21681724.2019.1570551?needAccess=true) clocking. Additionally, -a number of clock phases to use can be specified by utilizing the `-n` flag. Currently, only 3- and 4-phase clockings -are supported. Additional clocking schemes can be implemented in the header file `fcn_clocking_scheme.h`. +Additionally, you can provide a pre-defined clocking scheme to prune the search space with option `-s`. Possible schemes +besides the default open clocking, are [2DDWave](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=1717097), +[USE](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7219390), +[RES](https://www.tandfonline.com/doi/pdf/10.1080/21681724.2019.1570551?needAccess=true), and +[BANCS](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8533251&tag=1). For detailed information on usage see +`exact -h`. Additional clocking schemes can be implemented in the header file `fcn_clocking_scheme.h`. Calls to `ortho` are not expecting flags. This algorithm based on orthogonal graph drawing produces layouts in much shorter time. Though their quality in terms of area is far from optimum. Plus, for physical implementation, it might be necessary to route primary inputs/outputs to the grid borders by hand. Generated FCN gate layouts are also saved in stores. Entering `store -g` shows a list of all gate layouts available. -Statistical informations about store elements can be printed using command `ps`. For a gate layout, a call to `ps -g` +Statistical information about store elements can be printed using command `ps`. For a gate layout, a call to `ps -g` could for instance output the following. ``` c17 - 5 × 7, #G: 18, #W: 18, #C: 3, #L: 0, CP: 11, TP: 1/1 @@ -205,7 +205,7 @@ in store. If you want to just generate the SVG file without opening it in your s Alternatively, open the exported file with a different program by using `show -c --program "\"google-chrome\" {}"` for instance to open it with your Chrome browser. Note, that this behavior is platform dependent and might not work properly under your system. For more information see `show --help` or the full -[Alice documentation](https://libalice.readthedocs.io/en/latest/index.html). +[alice documentation](https://libalice.readthedocs.io/en/latest/index.html). The used color scheme is based on the one known from [QCADesigner](https://waluslab.ece.ubc.ca/qcadesigner/). @@ -232,8 +232,12 @@ which can be executed by `./fiction -ef c17_synth.fs -l c17_log.json` where stat called `c17_log.json`. These scripts can also be nested. Use `< script.fs` within a *fiction script* to load `script.fs` in that very position. +A script called `shortcuts.fs` has been placed in the top level folder. It can be loaded on start-up by calling +`./fiction -if ../shortcuts.fs` in the build folder. This makes predefined commands and flows available as shortcuts. +Try `synth xibs use` for instance to perform the whole flow of layouting (utilizing `USE` clocking) and physical +synthesis down to cell level including visual representation. -Additionally, *fiction* can also be part of a bash script. Consider the following snippet +Additionally, *fiction* itself can be part of a bash script. Consider the following snippet ```sh for filepath in ../benchmarks/TOY/*.v; do diff --git a/benchmarks/MAJ/1bitAdderAOIG.v b/benchmarks/MAJ/1bitAdderAOIG.v new file mode 100644 index 000000000..7f415f82f --- /dev/null +++ b/benchmarks/MAJ/1bitAdderAOIG.v @@ -0,0 +1,14 @@ +module top(x0, x1, x2, y0, y1); + input x0, x1, x2; + output y0, y1; + wire n4, n5, n6, n7, n8, n9, n10; + assign n4 = x1 & x2; + assign n5 = (x1 & x2) | (x1 & ~n4) | (x2 & ~n4); + assign n6 = ~n4 & n5; + assign n7 = x0 & n6; + assign n8 = x0 | n6; + assign n9 = ~n7 & n8; + assign n10 = n4 | n7; + assign y0 = n9; + assign y1 = n10; +endmodule diff --git a/benchmarks/MAJ/1bitAdderMaj.v b/benchmarks/MAJ/1bitAdderMaj.v new file mode 100644 index 000000000..a57f53ef4 --- /dev/null +++ b/benchmarks/MAJ/1bitAdderMaj.v @@ -0,0 +1,7 @@ +module top(x0, x1, x2, y0); + input x0, x1, x2; + output y0; + wire n4; + assign n4 = (x0 & x1) | (x0 & ~x2) | (x1 & ~x2); + assign y0 = n4; +endmodule diff --git a/benchmarks/MAJ/RCA2.v b/benchmarks/MAJ/RCA2.v new file mode 100644 index 000000000..08393bae9 --- /dev/null +++ b/benchmarks/MAJ/RCA2.v @@ -0,0 +1,19 @@ +module top(x0, x1, x2, x3, x4, y0, y1, y2); + input x0, x1, x2, x3, x4; + output y0, y1, y2; + wire n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16; + assign n6 = x0 | x2; + assign n7 = (x0 & x2) | (x0 & ~n6) | (x2 & ~n6); + assign n8 = n6 & ~n7; + assign n9 = x4 & n8; + assign n10 = (x4 & n8) | (x4 & ~n9) | (n8 & ~n9); + assign n11 = ~n9 & n10; + assign n12 = x1 & n9; + assign n13 = x1 & x3; + assign n14 = (~x1 & n9) | (~x1 & n13) | (n9 & n13); + assign n15 = (x1 & ~n12) | (x1 & n14) | (~n12 & n14); + assign n16 = (x1 & x4) | (x1 & n13) | (x4 & n13); + assign y0 = n11; + assign y1 = n15; + assign y2 = n16; +endmodule diff --git a/benchmarks/MAJ/clpl.v b/benchmarks/MAJ/clpl.v new file mode 100644 index 000000000..7fefdfe75 --- /dev/null +++ b/benchmarks/MAJ/clpl.v @@ -0,0 +1,20 @@ +module top(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, y0, y1, y2, y3, y4); + input x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10; + output y0, y1, y2, y3, y4; + wire n12, n13, n14, n15, n16, n17, n18, n19, n20, n21; + assign n12 = x1 | x6; + assign n13 = x0 & x4; + assign n14 = x2 | n13; + assign n15 = x3 | x5; + assign n16 = (x3 & n14) | (x3 & n15) | (n14 & n15); + assign n17 = (x1 & n12) | (x1 & n16) | (n12 & n16); + assign n18 = x9 | x10; + assign n19 = x7 | x8; + assign n20 = (x7 & n17) | (x7 & n19) | (n17 & n19); + assign n21 = (x10 & n18) | (x10 & n20) | (n18 & n20); + assign y0 = n17; + assign y1 = n16; + assign y2 = n14; + assign y3 = n21; + assign y4 = n20; +endmodule diff --git a/benchmarks/MAJ/mux21.v b/benchmarks/MAJ/mux21.v new file mode 100644 index 000000000..0c23d0b03 --- /dev/null +++ b/benchmarks/MAJ/mux21.v @@ -0,0 +1,9 @@ +module top(x0, x1, x2, y0); + input x0, x1, x2; + output y0; + wire n4, n5, n6; + assign n4 = x0 | x2; + assign n5 = ~x1 & x2; + assign n6 = n4 & ~n5; + assign y0 = n6; +endmodule diff --git a/benchmarks/MAJ/mux41.v b/benchmarks/MAJ/mux41.v new file mode 100644 index 000000000..2197b752f --- /dev/null +++ b/benchmarks/MAJ/mux41.v @@ -0,0 +1,13 @@ +module top(x0, x1, x2, x3, x4, x5, y0); + input x0, x1, x2, x3, x4, x5; + output y0; + wire n7, n8, n9, n10, n11, n12, n13; + assign n7 = (x0 & x4) | (x0 & ~x5) | (x4 & ~x5); + assign n8 = (~x1 & x4) | (~x1 & x5) | (x4 & x5); + assign n9 = n7 & ~n8; + assign n10 = (x3 & ~x4) | (x3 & x5) | (~x4 & x5); + assign n11 = (x2 & x4) | (x2 & x5) | (x4 & x5); + assign n12 = n10 & ~n11; + assign n13 = (n9 & n10) | (n9 & ~n12) | (n10 & ~n12); + assign y0 = n13; +endmodule diff --git a/benchmarks/MAJ/newtag.v b/benchmarks/MAJ/newtag.v new file mode 100644 index 000000000..fc46b73ac --- /dev/null +++ b/benchmarks/MAJ/newtag.v @@ -0,0 +1,12 @@ +module top(x0, x1, x2, x3, x4, x5, x6, x7, y0); + input x0, x1, x2, x3, x4, x5, x6, x7; + output y0; + wire n9, n10, n11, n12, n13, n14; + assign n9 = (~x3 & x6) | (~x3 & x7) | (x6 & x7); + assign n10 = (x4 & x5) | (x4 & n9) | (x5 & n9); + assign n11 = x3 & ~n10; + assign n12 = x1 & ~x2; + assign n13 = ~x0 & n12; + assign n14 = ~n11 & n13; + assign y0 = ~n14; +endmodule diff --git a/benchmarks/MAJ/par_check.v b/benchmarks/MAJ/par_check.v new file mode 100644 index 000000000..9e2ba5722 --- /dev/null +++ b/benchmarks/MAJ/par_check.v @@ -0,0 +1,11 @@ +module top(x0, x1, x2, x3, y0); + input x0, x1, x2, x3; + output y0; + wire n5, n6, n7, n8, n9; + assign n5 = (x0 & x1) | (x0 & ~x2) | (x1 & ~x2); + assign n6 = x3 | n5; + assign n7 = (~x0 & x2) | (~x0 & n5) | (x2 & n5); + assign n8 = ~x3 & n7; + assign n9 = (~x1 & n6) | (~x1 & n8) | (n6 & n8); + assign y0 = n9; +endmodule diff --git a/benchmarks/MAJ/par_gen.v b/benchmarks/MAJ/par_gen.v new file mode 100644 index 000000000..4b31a4104 --- /dev/null +++ b/benchmarks/MAJ/par_gen.v @@ -0,0 +1,9 @@ +module top(x0, x1, x2, y0); + input x0, x1, x2; + output y0; + wire n4, n5, n6; + assign n4 = (~x0 & x1) | (~x0 & x2) | (x1 & x2); + assign n5 = (x1 & x2) | (x1 & ~n4) | (x2 & ~n4); + assign n6 = (x0 & n4) | (x0 & ~n5) | (n4 & ~n5); + assign y0 = n6; +endmodule diff --git a/benchmarks/MAJ/t.v b/benchmarks/MAJ/t.v new file mode 100644 index 000000000..bf4445770 --- /dev/null +++ b/benchmarks/MAJ/t.v @@ -0,0 +1,12 @@ +module top(x0, x1, x2, x3, x4, y0, y1); + input x0, x1, x2, x3, x4; + output y0, y1; + wire n6, n7, n8, n9, n10; + assign n6 = x2 & x3; + assign n7 = x1 & ~n6; + assign n8 = x0 & x2; + assign n9 = n7 | n8; + assign n10 = (x4 & ~n6) | (x4 & n7) | (~n6 & n7); + assign y0 = n9; + assign y1 = n10; +endmodule diff --git a/benchmarks/MAJ/xnor2.v b/benchmarks/MAJ/xnor2.v new file mode 100644 index 000000000..c29137379 --- /dev/null +++ b/benchmarks/MAJ/xnor2.v @@ -0,0 +1,9 @@ +module top(x0, x1, y0); + input x0, x1; + output y0; + wire n3, n4, n5; + assign n3 = x0 | x1; + assign n4 = (x0 & x1) | (x0 & ~n3) | (x1 & ~n3); + assign n5 = n3 & ~n4; + assign y0 = ~n5; +endmodule diff --git a/benchmarks/MAJ/xor2.v b/benchmarks/MAJ/xor2.v new file mode 100644 index 000000000..5d75f1ac1 --- /dev/null +++ b/benchmarks/MAJ/xor2.v @@ -0,0 +1,9 @@ +module top(x0, x1, y0); + input x0, x1; + output y0; + wire n3, n4, n5; + assign n3 = x0 | x1; + assign n4 = (x0 & x1) | (x0 & ~n3) | (x1 & ~n3); + assign n5 = n3 & ~n4; + assign y0 = n5; +endmodule diff --git a/benchmarks/MAJ/xor5R.v b/benchmarks/MAJ/xor5R.v new file mode 100644 index 000000000..5ab5beab0 --- /dev/null +++ b/benchmarks/MAJ/xor5R.v @@ -0,0 +1,12 @@ +module top(x0, x1, x2, x3, x4, y0); + input x0, x1, x2, x3, x4; + output y0; + wire n6, n7, n8, n9, n10, n11; + assign n6 = (~x0 & x1) | (~x0 & x2) | (x1 & x2); + assign n7 = (x1 & x2) | (x1 & ~n6) | (x2 & ~n6); + assign n8 = (x0 & n6) | (x0 & ~n7) | (n6 & ~n7); + assign n9 = (~x3 & x4) | (~x3 & n8) | (x4 & n8); + assign n10 = (x4 & n8) | (x4 & ~n9) | (n8 & ~n9); + assign n11 = (x3 & n9) | (x3 & ~n10) | (n9 & ~n10); + assign y0 = n11; +endmodule diff --git a/benchmarks/MAJ/xor5_r1.v b/benchmarks/MAJ/xor5_r1.v new file mode 100644 index 000000000..5ab5beab0 --- /dev/null +++ b/benchmarks/MAJ/xor5_r1.v @@ -0,0 +1,12 @@ +module top(x0, x1, x2, x3, x4, y0); + input x0, x1, x2, x3, x4; + output y0; + wire n6, n7, n8, n9, n10, n11; + assign n6 = (~x0 & x1) | (~x0 & x2) | (x1 & x2); + assign n7 = (x1 & x2) | (x1 & ~n6) | (x2 & ~n6); + assign n8 = (x0 & n6) | (x0 & ~n7) | (n6 & ~n7); + assign n9 = (~x3 & x4) | (~x3 & n8) | (x4 & n8); + assign n10 = (x4 & n8) | (x4 & ~n9) | (n8 & ~n9); + assign n11 = (x3 & n9) | (x3 & ~n10) | (n9 & ~n10); + assign y0 = n11; +endmodule diff --git a/shortcuts.fs b/shortcuts.fs new file mode 100644 index 000000000..d59a44e09 --- /dev/null +++ b/shortcuts.fs @@ -0,0 +1,10 @@ +# commands +alias "xibs(.*)" "exact -x -i -b -s{}" + +# flows +alias "synth(.*)" "{}; cell; show" +alias "phys(.*)" "{}; cell; qca" + +# load +alias "mux" "read ../benchmarks/TOY/mux21.v" +alias "rca" "read ../benchmarks/TOY/RCA2.v" diff --git a/src/algo/exact_pr.cpp b/src/algo/exact_pr.cpp index 744a84c0a..d52340179 100644 --- a/src/algo/exact_pr.cpp +++ b/src/algo/exact_pr.cpp @@ -5,14 +5,13 @@ #include "exact_pr.h" -exact_pr::exact_pr(std::shared_ptr&& ln, exact_pr_config&& config) +exact_pr::exact_pr(logic_network_ptr ln, exact_pr_config&& config) : place_route(std::move(ln)), config{config}, - lower_bound{static_cast(network->vertex_count(config.io_wires))}, + lower_bound{static_cast(network->vertex_count(config.io_ports))}, solver{ctx} { - network->update_index_maps(); layout = std::make_shared(std::move(*config.scheme), network); initialize_vcl_map(); set_timeout(config.timeout); @@ -91,7 +90,7 @@ void exact_pr::initialize_tv_map() for (auto&& t : layout->ground_layer()) { const auto t_i = layout->index(t); - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { const auto v_i = network->index(v); z3_expr_proxy ep{ctx.bool_const(boost::str(boost::format("tv_%d_%d") % t_i % v_i).c_str())}; @@ -105,11 +104,11 @@ void exact_pr::initialize_te_map() for (auto&& t : layout->ground_layer()) { const auto t_i = layout->index(t); - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { - const auto e_i = network->index(e); - z3_expr_proxy ep{ctx.bool_const(boost::str(boost::format("te_%d_%d") % t_i % e_i).c_str())}; - te_map.emplace(std::make_pair(t_i, e_i), ep); + const auto src = network->index(network->source(e)), tgt = network->index(network->target(e)); + z3_expr_proxy ep{ctx.bool_const(boost::str(boost::format("te_%d_(%d,%d)") % t_i % src % tgt).c_str())}; + te_map.emplace(std::make_pair(t_i, std::make_pair(src, tgt)), ep); } } } @@ -163,7 +162,7 @@ void exact_pr::initialize_vcl_map() vcl_map.emplace(v_i, ep); }; - if (config.io_wires) + if (config.io_ports) { for (auto&& v : network->get_pis()) initialize(v); @@ -205,7 +204,8 @@ z3::expr exact_pr::get_tv(const layout_tile& t, const logic_vertex v) z3::expr exact_pr::get_te(const layout_tile& t, const logic_edge& e) { - return te_map.at(std::make_pair(layout->index(t), network->index(e)))[0u]; + return te_map.at(std::make_pair(layout->index(t), std::make_pair(network->index(network->source(e)), + network->index(network->target(e)))))[0u]; } z3::expr exact_pr::get_tc(const layout_tile& t1, const layout_tile& t2) @@ -266,13 +266,13 @@ void exact_pr::restrict_tile_elements() if (config.crossings) { z3::expr_vector tv{ctx}; - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) tv.push_back(get_tv(t, v)); solver.add(z3::atmost(tv, 1u)); z3::expr_vector te{ctx}; - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) te.push_back(get_te(t, e)); solver.add(z3::atmost(te, 2u)); @@ -280,10 +280,10 @@ void exact_pr::restrict_tile_elements() else { z3::expr_vector ve{ctx}; - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) ve.push_back(get_tv(t, v)); - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) ve.push_back(get_te(t, e)); solver.add(z3::atmost(ve, 1u)); @@ -293,7 +293,7 @@ void exact_pr::restrict_tile_elements() void exact_pr::restrict_vertices() { - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { z3::expr_vector ve{ctx}; for (auto&& t : layout->ground_layer()) @@ -333,7 +333,7 @@ void exact_pr::restrict_latches() // tiles without wires cannot have latches z3::expr_vector te{ctx}; - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) te.push_back(get_te(t, e)); solver.add(z3::implies(z3::atmost(te, 0u), l == zero)); } @@ -346,11 +346,11 @@ void exact_pr::define_adjacent_vertex_tiles() { for (auto&& t : layout->ground_layer()) { - for (auto&& v : (network->vertices(config.io_wires))) + for (auto&& v : (network->vertices(config.io_ports))) { auto co = get_tv(t, v); z3::expr_vector conj{ctx}; - for (auto&& av : network->adjacent_vertices(v, config.io_wires)) + for (auto&& av : network->adjacent_vertices(v, config.io_ports)) { auto ev = network->get_edge(v, av).get(); z3::expr_vector disj{ctx}; @@ -383,11 +383,11 @@ void exact_pr::define_inv_adjacent_vertex_tiles() { for (auto&& t : layout->ground_layer()) { - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { auto co = get_tv(t, v); z3::expr_vector conj{ctx}; - for (auto&& iav : network->inv_adjacent_vertices(v, config.io_wires)) + for (auto&& iav : network->inv_adjacent_vertices(v, config.io_ports)) { auto iev = network->get_edge(iav, v).get(); z3::expr_vector disj{ctx}; @@ -420,7 +420,7 @@ void exact_pr::define_adjacent_edge_tiles() { for (auto&& t : layout->ground_layer()) { - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { auto te = network->target(e); z3::expr_vector disj{ctx}; @@ -450,7 +450,7 @@ void exact_pr::define_inv_adjacent_edge_tiles() { for (auto&& t : layout->ground_layer()) { - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { auto se = network->source(e); z3::expr_vector disj{ctx}; @@ -533,7 +533,7 @@ void exact_pr::assign_pi_clockings() } }; - if (config.io_wires) + if (config.io_ports) { for (auto&& v : network->get_pis()) assign(v); @@ -553,7 +553,7 @@ void exact_pr::fanin_length() using logic_edge_path = logic_network::edge_path; auto define_length = [this](const logic_vertex _v) -> void { - auto paths = network->get_all_paths(_v, config.io_wires); + auto paths = network->get_all_paths(_v, config.io_ports); if (paths.empty()) return; @@ -576,9 +576,9 @@ void exact_pr::fanin_length() { // respect clock zone of PI if one is involved auto s = network->source(e); - if (config.io_wires && network->is_pi(s)) + if (config.io_ports && network->is_pi(s)) path_length.push_back(get_vcl(s)); - else if (!config.io_wires) + else if (!config.io_ports) { if (network->pre_pi(s)) path_length.push_back(get_vcl(s)); @@ -591,7 +591,7 @@ void exact_pr::fanin_length() solver.add(mk_eq(all_path_lengths)); }; - if (config.io_wires) + if (config.io_ports) { for (auto&& po : network->get_pos()) define_length(po); @@ -612,15 +612,15 @@ void exact_pr::prevent_insufficiencies() { if (layout->is_regularly_clocked()) { - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { // if vertex v has more adjacent or inversely adjacent elements than tile t - if (layout->out_degree(t) < static_cast(network->out_degree(v, config.io_wires)) || - layout->in_degree(t) < static_cast(network->in_degree(v, config.io_wires))) + if (layout->out_degree(t) < static_cast(network->out_degree(v, config.io_ports)) || + layout->in_degree(t) < static_cast(network->in_degree(v, config.io_ports))) solver.add(not get_tv(t, v)); } - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { // if tile t has no adjacent or inversely adjacent tiles if (layout->out_degree(t) == 0 || layout->in_degree(t) == 0) @@ -632,11 +632,11 @@ void exact_pr::prevent_insufficiencies() auto surrounding_tiles = layout->surrounding_2d(t); auto tile_degree = std::distance(surrounding_tiles.begin(), surrounding_tiles.end()); - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { // in an irregular clocking scheme, not so strict restrictions can be made - if (tile_degree < static_cast(network->out_degree(v, config.io_wires) + - network->in_degree(v, config.io_wires))) + if (tile_degree < static_cast(network->out_degree(v, config.io_ports) + + network->in_degree(v, config.io_ports))) solver.add(not get_tv(t, v)); } } @@ -693,11 +693,11 @@ void exact_pr::define_number_of_connections() z3::expr_vector ow{ctx}; - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { auto tv = get_tv(t1, v); - auto aon = static_cast(network->out_degree(v, config.io_wires)); - auto iaon = static_cast(network->in_degree(v, config.io_wires)); + auto aon = static_cast(network->out_degree(v, config.io_ports)); + auto iaon = static_cast(network->in_degree(v, config.io_ports)); ow.push_back(tv); @@ -709,7 +709,7 @@ void exact_pr::define_number_of_connections() } z3::expr_vector wv{ctx}; - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { auto te = get_te(t1, e); @@ -728,9 +728,11 @@ void exact_pr::define_number_of_connections() if (config.crossings) { if (!acc.empty()) - solver.add(z3::implies(z3::atleast(wv, 2u), z3::atleast(acc, 2u) and z3::atmost(acc, 2u))); + solver.add(z3::implies(z3::atleast(wv, 2u) and z3::atmost(wv, 2u), + z3::atleast(acc, 2u) and z3::atmost(acc, 2u))); if (!iacc.empty()) - solver.add(z3::implies(z3::atleast(wv, 2u), z3::atleast(iacc, 2u) and z3::atmost(iacc, 2u))); + solver.add(z3::implies(z3::atleast(wv, 2u) and z3::atmost(wv, 2u), + z3::atleast(iacc, 2u) and z3::atmost(iacc, 2u))); } // collect all variables representing paths t -> t' and t' -> t @@ -744,7 +746,7 @@ void exact_pr::define_number_of_connections() // if tile t is empty, there must not be any connection or path from or to tile t established if (!ow.empty() && !ccp.empty()) - solver.add(z3::implies(z3::atmost(ow, 0u), z3::atmost(ccp, 0u))); + solver.add(z3::atmost(ow, 0u) == z3::atmost(ccp, 0u)); } } @@ -761,7 +763,7 @@ void exact_pr::enforce_border_io() } }; - if (config.io_wires) + if (config.io_ports) { for (auto&& pi : network->get_pis()) assign_border(pi); @@ -785,7 +787,7 @@ void exact_pr::enforce_border_io() void exact_pr::limit_wire_length() { - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { z3::expr_vector ve{ctx}; for (auto&& t : layout->ground_layer()) @@ -801,7 +803,7 @@ void exact_pr::limit_crossings() for (auto&& t : layout->ground_layer()) { z3::expr_vector wv{ctx}; - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) wv.push_back(get_te(t, e)); crossings_counter.push_back(z3::ite(z3::atleast(wv, 2u), ctx.real_val(1), ctx.real_val(0))); @@ -853,7 +855,7 @@ void exact_pr::generate_smt_instance() if (config.crossings && config.crossings_limit) limit_crossings(); -// std::cout << solver.assertions() << std::endl; +// std::cout << solver.assertions() << std::endl; } @@ -865,14 +867,14 @@ void exact_pr::assign_layout() for (auto&& t : layout->ground_layer()) { auto element_placed = false; - for (auto&& v : network->vertices(config.io_wires)) + for (auto&& v : network->vertices(config.io_ports)) { // if vertex v is set to tile t if (mdl.eval(get_tv(t, v)).bool_value() == Z3_L_TRUE) { // check for I/Os bool pi, po; - if (config.io_wires) + if (config.io_ports) { pi = network->is_pi(v); po = network->is_po(v); @@ -895,7 +897,7 @@ void exact_pr::assign_layout() continue; // assign edges to tiles - for (auto&& e : network->edges(config.io_wires)) + for (auto&& e : network->edges(config.io_ports)) { // if edge e is set to tile t if (mdl.eval(get_te(t, e)).bool_value() == Z3_L_TRUE) @@ -928,43 +930,29 @@ void exact_pr::assign_layout() // if connection t1 -> t2 is established if (mdl.eval(get_tc(t1, t2)).bool_value() == Z3_L_TRUE) { - if (!layout->is_wire_tile(layout->above(t1))) - { - for (auto& e : layout->get_logic_edges(t1)) - layout->assign_wire_out_dir(t1, e, layout->get_bearing(t1, t2)); - layout->assign_tile_out_dir(t1, layout->get_bearing(t1, t2)); - } - if (!layout->is_wire_tile(layout->above(t2))) - { - for (auto& e : layout->get_logic_edges(t2)) - layout->assign_wire_inp_dir(t2, e, layout->get_bearing(t2, t1)); - layout->assign_tile_inp_dir(t2, layout->get_bearing(t2, t1)); - } + layout_tile _t1, _t2; + if (layout->is_data_flow(t1, t2)) + { _t1 = t1; _t2 = t2; } + else if (layout->is_data_flow(t1, layout->above(t2))) + { _t1 = t1; _t2 = layout->above(t2); } + else if (layout->is_data_flow(layout->above(t1), t2)) + { _t1 = layout->above(t1); _t2 = t2; } + else if (layout->is_data_flow(layout->above(t1), layout->above(t2))) + { _t1 = layout->above(t1); _t2 = layout->above(t2); } + + // assign outgoing directions + for (auto& e : layout->get_logic_edges(_t1)) + layout->assign_wire_out_dir(_t1, e, layout->get_bearing(_t1, _t2)); + layout->assign_tile_out_dir(_t1, layout->get_bearing(_t1, _t2)); + + // assign incoming directions + for (auto& e : layout->get_logic_edges(_t2)) + layout->assign_wire_inp_dir(_t2, e, layout->get_bearing(_t2, _t1)); + layout->assign_tile_inp_dir(_t2, layout->get_bearing(_t2, _t1)); } } } - // using tiles() here because now really all tiles will be assigned - for (auto&& t1 : layout->tiles()) - { - // iterate over all adjacent tiles to which a subsequent vertex or edge was assigned - for (auto&& t2 : layout->outgoing_information_flow_tiles(t1)) - { - // if connection has been established, assign direction - for (auto& e : layout->get_logic_edges(t1)) - layout->assign_wire_out_dir(t1, e, layout->get_bearing(t1, t2)); - layout->assign_tile_out_dir(t1, layout->get_bearing(t1, t2)); - } - // iterate over all adjacent tiles to which a preceding vertex or edge was assigned - for (auto&& t2 : layout->incoming_information_flow_tiles(t1)) - { - // if connection has been established, assign direction - for (auto& e : layout->get_logic_edges(t1)) - layout->assign_wire_inp_dir(t1, e, layout->get_bearing(t1, t2)); - layout->assign_tile_inp_dir(t1, layout->get_bearing(t1, t2)); - } - } - // assign artificial latches if there were any in use if (config.artificial_latch) { diff --git a/src/algo/exact_pr.h b/src/algo/exact_pr.h index bdeb8ae05..c1699b812 100644 --- a/src/algo/exact_pr.h +++ b/src/algo/exact_pr.h @@ -29,7 +29,7 @@ class exact_pr : public place_route * @param ln Logic network. * @param config Configuration object storing all the bounds, flags, and so on. */ - exact_pr(std::shared_ptr&& ln, exact_pr_config&& config); + exact_pr(logic_network_ptr ln, exact_pr_config&& config); /** * Default Destructor. */ @@ -72,7 +72,7 @@ class exact_pr : public place_route */ using layout_tile_index = fcn_gate_layout::tile_index; using logic_vertex_index = logic_network::vertex_index; - using logic_edge_index = logic_network::edge_index; + using logic_edge_index = std::pair; /** * Shortcuts for hashes. */ diff --git a/src/algo/exact_pr_config.h b/src/algo/exact_pr_config.h index d2007fadb..54f644033 100644 --- a/src/algo/exact_pr_config.h +++ b/src/algo/exact_pr_config.h @@ -43,7 +43,7 @@ struct exact_pr_config /** * Flag to indicate that designated wires should be routed to balance I/O port paths. */ - bool io_wires = false; + bool io_ports = false; /** * Flag to indicate that I/Os should be placed at the grid's border. */ diff --git a/src/algo/orthogonal_pr.cpp b/src/algo/orthogonal_pr.cpp index 253e95001..d44ac057b 100644 --- a/src/algo/orthogonal_pr.cpp +++ b/src/algo/orthogonal_pr.cpp @@ -4,10 +4,11 @@ #include "orthogonal_pr.h" -orthogonal_pr::orthogonal_pr(std::shared_ptr&& ln, const unsigned n) +orthogonal_pr::orthogonal_pr(logic_network_ptr ln, const unsigned n, const bool io) : place_route(std::move(ln)), - phases{n} + phases{n}, + io_ports{io} {} orthogonal_pr::pr_result orthogonal_pr::perform_place_and_route() @@ -22,7 +23,7 @@ orthogonal_pr::pr_result orthogonal_pr::perform_place_and_route() // get joint DFS ordering auto jDFS = jdfs_order(); - // compute a red-blue-coloring for the netlist + // compute a red-blue-coloring for the network auto rbColoring = find_rb_coloring(jDFS); try @@ -45,7 +46,7 @@ orthogonal_pr::jdfs_ordering orthogonal_pr::jdfs_order() const // store discovery of nodes std::unordered_map discovered{}; // range of logic vertices - auto vertices = network->vertices(); + auto vertices = network->vertices(io_ports); // initialize all vertices undiscovered std::for_each(vertices.begin(), vertices.end(), [&discovered](const logic_network::vertex _v){discovered.emplace(_v, false);}); @@ -56,21 +57,21 @@ orthogonal_pr::jdfs_ordering orthogonal_pr::jdfs_order() const const std::function jdfs = [&](const logic_network::vertex _v) { - auto iav = network->inv_adjacent_vertices(_v); + auto iav = network->inv_adjacent_vertices(_v, io_ports); // if all predecessors are yet discovered if (std::all_of(iav.begin(), iav.end(), is_discovered)) { discovered[_v] = true; ordering.push_back(_v); - for (auto&& av : network->adjacent_vertices(_v) | iter::filterfalse(is_discovered)) + for (auto&& av : network->adjacent_vertices(_v, io_ports) | iter::filterfalse(is_discovered)) jdfs(av); } }; // call joint dfs for each vertex without predecessors - for (auto&& root : network->vertices() | iter::filter([this](const logic_network::vertex _v) - {return network->in_degree(_v) == 0u;})) + for (auto&& root : network->vertices(io_ports) | iter::filter([this](const logic_network::vertex _v) + {return network->in_degree(_v, io_ports) == 0u;})) jdfs(root); return ordering; @@ -83,7 +84,7 @@ orthogonal_pr::red_blue_coloring orthogonal_pr::find_rb_coloring(const jdfs_orde const auto contrary = [](const rb_color _c){return _c == rb_color::RED ? rb_color::BLUE : rb_color::RED;}; // range of logic edges - auto edges = network->edges(); + auto edges = network->edges(io_ports); // color all edges white initially std::for_each(edges.begin(), edges.end(), [&rb_coloring](const logic_network::edge& _e){rb_coloring.emplace(_e, rb_color::WHITE);}); @@ -96,13 +97,13 @@ orthogonal_pr::red_blue_coloring orthogonal_pr::find_rb_coloring(const jdfs_orde rb_coloring[_e] = _c; - for (auto&& oe : network->out_edges(network->source(_e))) + for (auto&& oe : network->out_edges(network->source(_e), io_ports)) { if (oe != _e) apply(oe, contrary(_c)); } - for (auto&& ie : network->in_edges(network->target(_e))) + for (auto&& ie : network->in_edges(network->target(_e), io_ports)) { if (ie != _e) apply(ie, _c); @@ -111,7 +112,7 @@ orthogonal_pr::red_blue_coloring orthogonal_pr::find_rb_coloring(const jdfs_orde for (auto&& v : jdfs | iter::reversed) { - auto ie = network->in_edges(v); + auto ie = network->in_edges(v, io_ports); // if any ingoing edge is BLUE, color them all in BLUE, and RED otherwise auto color = std::any_of(ie.begin(), ie.end(), [&rb_coloring](const logic_network::edge& _e) @@ -129,10 +130,10 @@ orthogonal_pr::red_blue_coloring orthogonal_pr::find_rb_coloring(const jdfs_orde void orthogonal_pr::orthogonal_embedding(red_blue_coloring& rb_coloring, const jdfs_ordering& jdfs) { // create layout with x = v, y = v, where v is the number of vertices in the network - // therefore, the layout needs to be shrinked to fit in the end - layout = std::make_shared(fcn_dimension_xy{network->vertex_count(), network->vertex_count()}, - phases == 3 ? std::move(diagonal_3_clocking) : - std::move(diagonal_4_clocking), network); + // therefore, the layout needs to be shrunk to fit in the end + layout = std::make_shared(fcn_dimension_xy{network->vertex_count(io_ports), network->vertex_count(io_ports)}, + phases == 3 ? std::move(twoddwave_3_clocking) : + std::move(twoddwave_4_clocking), network); // cache map storing information about where vertices were placed on the layout std::unordered_map pos; @@ -198,19 +199,20 @@ void orthogonal_pr::orthogonal_embedding(red_blue_coloring& rb_coloring, const j for (auto& v : jdfs) { // if operation has no predecessors, add 1 row and 1 column to the grid - if (network->in_degree(v) == 0u) + if (network->in_degree(v, io_ports) == 0u) { fcn_gate_layout::tile t{x_helper, y_helper, GROUND}; - layout->assign_logic_vertex(t, v, network->pre_pi(v), network->post_po(v)); + layout->assign_logic_vertex(t, v, io_ports ? network->is_pi(v) : network->pre_pi(v), + io_ports ? network->is_po(v) : network->post_po(v)); pos.emplace(v, t); ++x_helper; ++y_helper; } // if operation has one predecessor, add either 1 row or 1 column - else if (network->in_degree(v) == 1u) + else if (network->in_degree(v, io_ports) == 1u) { // incoming edge - auto in_e = *network->in_edges(v).begin(); + auto in_e = *network->in_edges(v, io_ports).begin(); // predecessor tile const auto pre_t = pos[network->source(in_e)]; @@ -221,7 +223,8 @@ void orthogonal_pr::orthogonal_embedding(red_blue_coloring& rb_coloring, const j const auto y_pos = pre_t[Y]; fcn_gate_layout::tile t{x_helper, y_pos, GROUND}; - layout->assign_logic_vertex(t, v, network->pre_pi(v), network->post_po(v)); + layout->assign_logic_vertex(t, v, io_ports ? network->is_pi(v) : network->pre_pi(v), + io_ports ? network->is_po(v) : network->post_po(v)); pos.emplace(v, t); wire_east(pre_t, t, in_e); @@ -239,7 +242,8 @@ void orthogonal_pr::orthogonal_embedding(red_blue_coloring& rb_coloring, const j const auto x_pos = pre_t[X]; fcn_gate_layout::tile t{x_pos, y_helper, GROUND}; - layout->assign_logic_vertex(t, v, network->pre_pi(v), network->post_po(v)); + layout->assign_logic_vertex(t, v, io_ports ? network->is_pi(v) : network->pre_pi(v), + io_ports ? network->is_po(v) : network->post_po(v)); pos.emplace(v, t); wire_south(pre_t, t, in_e); @@ -254,7 +258,7 @@ void orthogonal_pr::orthogonal_embedding(red_blue_coloring& rb_coloring, const j // operation has two predecessors else { - auto ep = network->in_edges(v).begin(); + auto ep = network->in_edges(v, io_ports).begin(); // incoming edge 1 const auto e1 = *ep; ++ep; @@ -306,7 +310,8 @@ void orthogonal_pr::orthogonal_embedding(red_blue_coloring& rb_coloring, const j } // place operation - layout->assign_logic_vertex(t, v, network->pre_pi(v), network->post_po(v)); + layout->assign_logic_vertex(t, v, io_ports ? network->is_pi(v) : network->pre_pi(v), + io_ports ? network->is_po(v) : network->post_po(v)); pos.emplace(v, t); // do routing dependent on colors diff --git a/src/algo/orthogonal_pr.h b/src/algo/orthogonal_pr.h index 34feacd2b..ce936d981 100644 --- a/src/algo/orthogonal_pr.h +++ b/src/algo/orthogonal_pr.h @@ -26,8 +26,9 @@ class orthogonal_pr : public place_route * * @param ln Logic network. * @param n Number of clock phases. + * @param io Flag to indicate use of I/O ports. */ - orthogonal_pr(std::shared_ptr&& ln, const unsigned n); + orthogonal_pr(logic_network_ptr ln, const unsigned n, const bool io = false); /** * Default Destructor. */ @@ -67,6 +68,10 @@ class orthogonal_pr : public place_route * Number of clock phases to use in the diagonal clocking scheme. */ const unsigned phases; + /** + * Flag to indicate that designated I/O ports should be routed too. + */ + const bool io_ports; /** * Colors used for a red-blue-coloring of 3-graphs. */ diff --git a/src/io/commands.h b/src/io/commands.h index be052b22c..25fd1855c 100644 --- a/src/io/commands.h +++ b/src/io/commands.h @@ -5,6 +5,7 @@ #ifndef FICTION_COMMANDS_H #define FICTION_COMMANDS_H +#include "version.h" #include "verilog_parser.h" #include "exact_pr.h" #include "orthogonal_pr.h" @@ -18,6 +19,35 @@ namespace alice { + /** + * Outputs version and build information. + */ + class version_command : public command + { + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param env alice::environment that specifies stores etc. + */ + explicit version_command(const environment::ptr& env) + : + command(env, "Outputs the version string as well as build time and date.") + {} + + protected: + /** + * Function to perform the version print call. + */ + void execute() override + { + env->out() << VERSION << " - compiled on " << COMPILED_DATE << " at " << COMPILED_TIME << std::endl; + } + }; + + ALICE_ADD_COMMAND(version, "General") + + /** * Shorthand for all read commands. Chooses the proper function by the file ending. * @@ -56,11 +86,23 @@ namespace alice */ void execute() override { + // checks for extension validity + auto is_valid_extension = [](const auto& _f) -> bool + { + const std::vector extensions{{".v"}}; + return std::any_of(extensions.cbegin(), extensions.cend(), + [&_f](const auto& _e) { return boost::filesystem::extension(_f) == _e; }); + }; + // check for for given file's properties if (boost::filesystem::exists(filename)) { if (boost::filesystem::is_regular_file(filename)) - paths.push_back(filename); + { + // collect valid files only + if (is_valid_extension(filename)) + paths.push_back(filename); + } else if (boost::filesystem::is_directory(filename)) { @@ -69,8 +111,8 @@ namespace alice { if (boost::filesystem::is_regular_file(file)) { - // parse Verilog files only - if (boost::filesystem::extension(file) == ".v") + // collect valid files only + if (is_valid_extension(file)) paths.push_back(file.path().string()); } } @@ -88,21 +130,27 @@ namespace alice { return boost::filesystem::file_size(f1) >= boost::filesystem::file_size(f2); }); } - // read all Verilog files + // handle collected files for (const auto& f : paths) { - auto name = boost::filesystem::path{f}.stem().string(); - auto ln = std::make_shared(std::move(name)); - - lorina::diagnostic_engine diag{}; - if (lorina::read_verilog(f, verilog_parser{ln}, &diag) == lorina::return_code::success) + // parse Verilog + if (boost::filesystem::extension(f) == ".v") { - store().extend() = std::move(ln); - } - else - { - env->out() << "[e] parsing error in " << f << std::endl; + auto name = boost::filesystem::path{f}.stem().string(); + auto ln = std::make_shared(std::move(name)); + + lorina::diagnostic_engine diag{}; + if (lorina::read_verilog(f, verilog_parser{ln}, &diag) == lorina::return_code::success) + { + store().extend() = std::move(ln); + } + else + { + env->out() << "[e] parsing error in " << f << std::endl; + } } + // parse ... + // else if (boost::filesystem::extension(f) == ...) } // reset flags, necessary for some reason... alice bug? @@ -174,6 +222,7 @@ namespace alice // reset flags, necessary for some reason... alice bug? cell = false; gate = false; network = false; } + private: /** * Stores to clear. @@ -201,12 +250,10 @@ namespace alice command(env, "Performs exact placement and routing of the current logic network in store. " "A minimum FCN layout will be found that meets all given constraints.") { + add_option("--clocking_scheme,-s", clocking, + "Clocking scheme to be used {OPEN3, OPEN4, 2DDWAVE3, 2DDWAVE4, USE, RES, BANCS}", true); add_option("--upper_bound,-u", config.upper_bound, "Number of FCN gate tiles to use at maximum"); - add_option("--clocking_scheme,-s", clocking, - "Clocking scheme to be used {OPEN=0, USE=1, DIAGONAL=2, RES=3}", true); - add_option("--clock_numbers,-n", phases, - "Number of clock phases to be used {3 or 4}", true); add_option("--limit_crossings,-c", config.crossings_limit, "Maximum number of tiles to use for crossings"); add_option("--limit_wires,-w", config.wire_limit, @@ -216,14 +263,14 @@ namespace alice add_flag("--crossings,-x", config.crossings, "Enable second layer for wire crossings"); + add_flag("--io_ports,-i", config.io_ports, + "Route extra wires to balance I/O port paths"); + add_flag("--border_io,-b", config.border_io, + "Enforce primary I/O to be placed at the layout's borders"); add_flag("--path_discrepancy,-p", config.path_discrepancy, "Allow a discrepancy in fan-in paths (area vs. throughput)"); add_flag("--artificial_latch,-a", config.artificial_latch, "Allow clocked latch delays to balance fan-in paths"); - add_flag("--io_wires,-i", config.io_wires, - "Route extra wires to balance I/O port paths"); - add_flag("--border_io,-b", config.border_io, - "Enforce primary I/O to be placed at the layout's borders"); add_flag("--fixed_size,-f", config.fixed_size, "Execute only one run with upper_bound given as a fixed size"); } @@ -234,55 +281,38 @@ namespace alice */ void execute() override { - auto s = store(); + auto& s = store(); // error case: empty logic network store if (s.empty()) { - env->out() << "[e] no logic network in store" << std::endl; - return; - } - - if (phases == 3 && clocking == 1) - { - env->out() << "[e] 3-phase USE clocking aka BANCS is not supported" << std::endl; + env->out() << "[w] no logic network in store" << std::endl; + reset_flags(); return; } - if (phases == 3 && clocking == 4) + // error case: -f is set but -u is not + if (this->is_set("fixed_size") && !this->is_set("upper_bound")) { - env->out() << "[e] 3-phase RES clocking is not supported" << std::endl; + env->out() << "[e] -u must be defined as well when -f is used" << std::endl; + reset_flags(); return; } // choose clocking - if (phases == 3) + if (auto clk = get_clocking_scheme(clocking)) { - config.scheme = std::make_shared - (std::vector{{open_3_clocking, - fcn_clocking_scheme{fcn_clock::cutout{}, 3u, false}, - diagonal_3_clocking, - fcn_clocking_scheme{fcn_clock::cutout{}, 3u, false} - }}[clocking]); + config.scheme = std::make_shared(*clk); } - else if (phases == 4) - { - config.scheme = std::make_shared - (std::vector{{open_4_clocking, - use_4_clocking, - diagonal_4_clocking, - res_4_clocking - }}[clocking]); - } - // error case: phases out of range else { - env->out() << "[e] only 3- and 4-phase clocking schemes are supported" << std::endl; + env->out() << "[e] \"" << clocking << "\" does not refer to a supported clocking scheme" << std::endl; + reset_flags(); return; } // perform exact P&R - exact_pr pr{std::move(s.current()), std::move(config)}; + exact_pr pr{s.current(), std::move(config)}; auto result = pr.perform_place_and_route(); if (result.success) @@ -291,11 +321,11 @@ namespace alice pr_result = result.json; } else - env->out() << "[e] impossible to place and route the specified network within the given " + env->out() << "[e] impossible to place and route " << s.current()->get_name() << " within the given " "parameters" << std::endl; - // reset flags, necessary for some reason... alice bug? - config = exact_pr_config{}; + + reset_flags(); } /** * Logs the resulting information in a log file. @@ -306,6 +336,14 @@ namespace alice { return pr_result; } + /** + * Reset all flags. Necessary for some reason... alice bug? + */ + void reset_flags() + { + config = exact_pr_config{}; + clocking = "OPEN4"; + } private: /** @@ -313,13 +351,9 @@ namespace alice */ exact_pr_config config{}; /** - * Identifier of clocking scheme to use (0=OPEN, 1=USE, 2=DIAGONAL). - */ - unsigned clocking = 0u; - /** - * Number of clock phases to use. 3 and 4 are supported. + * Identifier of clocking scheme to use. */ - unsigned phases = 4u; + std::string clocking = "OPEN4"; /** * Resulting logging information. */ @@ -349,6 +383,8 @@ namespace alice { add_option("--clock_numbers,-n", phases, "Number of clock phases to be used {3 or 4}", true); + add_flag("--io_ports,-i", io_ports, + "Place designated I/O ports too"); } protected: @@ -357,23 +393,25 @@ namespace alice */ void execute() override { - auto s = store(); + auto& s = store(); // error case: empty logic network store if (s.empty()) { - env->out() << "[e] no logic network in store" << std::endl; + env->out() << "[w] no logic network in store" << std::endl; + reset_flags(); return; } // error case: phases out of range if (phases != 3u && phases != 4u) { env->out() << "[e] only 3- and 4-phase clocking schemes are supported" << std::endl; + reset_flags(); return; } // perform heuristic P&R - orthogonal_pr pr{std::move(s.current()), phases}; + orthogonal_pr pr{s.current(), phases, io_ports}; auto result = pr.perform_place_and_route(); if (result.success) @@ -382,10 +420,9 @@ namespace alice pr_result = result.json; } else - env->out() << "[e] impossible to place and route the specified network" << std::endl; + env->out() << "[e] impossible to place and route " << s.current()->get_name() << std::endl; - // reset flags, necessary for some reason... alice bug? - phases = 4u; + reset_flags(); } /** * Logs the resulting information in a log file. @@ -396,12 +433,24 @@ namespace alice { return pr_result; } + /** + * Reset all flags. Necessary for some reason... alice bug? + */ + void reset_flags() + { + phases = 4u; + io_ports = false; + } private: /** * Number of clock phases to use. 3 and 4 are supported. */ unsigned phases = 4u; + /** + * Flag to indicate that designated I/O ports should be placed. + */ + bool io_ports = false; /** * Resulting logging information. */ @@ -434,11 +483,11 @@ namespace alice protected: /** - * Function to perform the P&R call. Generates an FCN gate layout. + * Function to perform the conversion call. Generates an fcn_cell_layout. */ void execute() override { - auto s = store(); + auto& s = store(); // error case: empty gate layout store if (s.empty()) @@ -448,10 +497,11 @@ namespace alice } fcn_gate_library_ptr lib = nullptr; + std::string lib_name{}; try { if (library == 0u) - lib = std::make_shared(std::move(s.current())); + lib = std::make_shared(s.current()); // else if (library == 1u) // more libraries go here else @@ -459,10 +509,12 @@ namespace alice env->out() << "[e] identifier " << library << " does not refer to a supported gate library" << std::endl; return; } + + lib_name = lib->get_name(); } catch (...) { - env->out() << "[e] could not assign directions in gate layout to cell ports" << std::endl; + env->out() << "[e] could not assign directions in " << s.current()->get_name() << " to cell ports" << std::endl; return; } @@ -473,7 +525,8 @@ namespace alice } catch (...) { - env->out() << "[e] mapping to cell layout with the given library was not successful" << std::endl; + env->out() << "[e] mapping " << s.current()->get_name() << " to a cell layout using the " + << lib_name << " library was not successful" << std::endl; return; } @@ -514,7 +567,7 @@ namespace alice protected: /** - * Function to perform the P&R call. Generates an FCN gate layout. + * Function to perform the output print call. Generates a QCADesigner file. */ void execute() override { diff --git a/src/io/stores.h b/src/io/stores.h index 0b5d35c21..fa36375c6 100644 --- a/src/io/stores.h +++ b/src/io/stores.h @@ -174,8 +174,7 @@ namespace alice { try { - const auto simple = cmd.is_set("simple"); - os << svg::generate_svg_string(element, simple) << std::endl; + os << svg::generate_svg_string(element, cmd.is_set("simple")) << std::endl; } catch (const std::invalid_argument& e) { diff --git a/src/io/svg_writer.cpp b/src/io/svg_writer.cpp index 76958ec49..70eca5c23 100644 --- a/src/io/svg_writer.cpp +++ b/src/io/svg_writer.cpp @@ -46,7 +46,7 @@ namespace svg { // If this is called, then there is no tile for the current cell yet // It also makes sure that all required tiles are created - coord_to_latch_tile[tile_coords] = std::make_tuple(tile, clockzone, latch_delay); + coord_to_latch_tile[tile_coords] = std::make_tuple(latch, clockzone, latch_delay); } } else @@ -182,10 +182,10 @@ namespace svg double x_pos = starting_offset_latch_x + coord.first * tile_distance; double y_pos = starting_offset_latch_y + coord.second * tile_distance; - descr = fmt::format(descr, x_pos, y_pos, tile_colors[czone_up], tile_colors[czone_lo], cell_descriptions, + descr = fmt::format(descr, x_pos, y_pos, tile_colors[czone_lo], tile_colors[czone_up], cell_descriptions, text_colors[czone_up], simple ? "" : std::to_string(czone_up + 1), text_colors[czone_lo], - simple ? "" : fmt::format("{} (+{})", czone_lo + 1, latch_delay)); + simple ? "" : std::to_string(czone_lo + 1)); tile_descriptions << descr; } diff --git a/src/io/svg_writer.h b/src/io/svg_writer.h index 2a01e9f13..86f08d1e1 100644 --- a/src/io/svg_writer.h +++ b/src/io/svg_writer.h @@ -100,13 +100,13 @@ namespace svg "\n" "\n" + "style=\"color:#000000;solid-color:#000000;solid-opacity:1;fill:#{};fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19643486;stroke-opacity:1;enable-background:accumulate\" />\n" "\n" + "style=\"color:#000000;solid-color:#000000;solid-opacity:1;fill:#{};fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19643486;stroke-opacity:1;enable-background:accumulate\" />\n" "\n" "{}\n" "{}\n" ""; + constexpr const char *cell = "\n" diff --git a/src/tech/fcn_cell_layout.cpp b/src/tech/fcn_cell_layout.cpp index 797cf7f13..8ec633fd1 100644 --- a/src/tech/fcn_cell_layout.cpp +++ b/src/tech/fcn_cell_layout.cpp @@ -133,8 +133,8 @@ boost::optional fcn_cell_layout::cell_clocking(const cell& c) c } } -void fcn_cell_layout::assign_gate(const cell& c, const fcn_gate& g, const std::vector& inp_names, - const std::vector& out_names) +void fcn_cell_layout::assign_gate(const cell& c, const fcn_gate& g, const latch_delay l, + const std::vector& inp_names, const std::vector& out_names) { auto start_x = c[X]; auto start_y = c[Y]; @@ -150,6 +150,7 @@ void fcn_cell_layout::assign_gate(const cell& c, const fcn_gate& g, const std::v const auto type = g[y][x]; assign_cell_type(pos, type); + assign_latch(pos, l); if (type == fcn::INPUT_CELL) { @@ -265,9 +266,8 @@ void fcn_cell_layout::map_layout() if (layout->is_free_tile(t)) continue; - auto gate = library->set_up_gate(t); - assign_gate(cell{t[X] * library->gate_x_size(), t[Y] * library->gate_y_size(), t[Z]}, gate, - layout->get_inp_names(t), layout->get_out_names(t)); + assign_gate(cell{t[X] * library->gate_x_size(), t[Y] * library->gate_y_size(), t[Z]}, library->set_up_gate(t), + layout->get_latch(t), layout->get_inp_names(t), layout->get_out_names(t)); } assign_vias(); diff --git a/src/tech/fcn_cell_layout.h b/src/tech/fcn_cell_layout.h index 390efb485..47ee851a3 100644 --- a/src/tech/fcn_cell_layout.h +++ b/src/tech/fcn_cell_layout.h @@ -199,11 +199,12 @@ class fcn_cell_layout : public fcn_layout * * @param c Location of top left corner cell of gate g. * @param g FCN gate to be assigned to a region starting in the top left corner at cell c. + * @param l A latch delay in phases. * @param inp_names Names for PI ports. Each input cell occurring in g will take the next name from this vector. * @param out_names Names for PO ports. Each output cell occurring in g will take the next name from this vector. */ - void assign_gate(const cell& c, const fcn_gate& g, const std::vector& inp_names = {}, - const std::vector& out_names = {}); + void assign_gate(const cell& c, const fcn_gate& g, const latch_delay l = 0, + const std::vector& inp_names = {}, const std::vector& out_names = {}); /** * Returns library pointer. * diff --git a/src/tech/qca_one_library.cpp b/src/tech/qca_one_library.cpp index fb5a377b6..1a808630a 100644 --- a/src/tech/qca_one_library.cpp +++ b/src/tech/qca_one_library.cpp @@ -4,42 +4,25 @@ #include "qca_one_library.h" -qca_one_library::qca_one_library(fcn_gate_layout_ptr&& fgl) +qca_one_library::qca_one_library(fcn_gate_layout_ptr fgl) : fcn_gate_library - ( - "QCA-ONE", - std::move(fgl), - fcn::technology::QCA, - fcn::tile_size::FIVE_X_FIVE - ) + ( + "QCA-ONE", + std::move(fgl), + fcn::technology::QCA, + fcn::tile_size::FIVE_X_FIVE + ) { p_router->compute_ports(); } fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) { - /** - * Determines rotation of given gate g with respect to its unused given directions. - * Works for gates with a combined amount of 3 inputs/outputs only (AND, OR, F1O2). - */ - auto pick_3_io_rotation = [this](const fcn_gate& g, layout::directions unused) -> fcn_gate - { - if (unused == layout::DIR_N || unused == layout::DIR_NE || unused == layout::DIR_NW || unused == layout::DIR_NS) - return g; - else if (unused == layout::DIR_E || unused == layout::DIR_EW || unused == layout::DIR_ES) - return rotate_90(g); - else if (unused == layout::DIR_S || unused == layout::DIR_SW) - return rotate_180(g); - else if (unused == layout::DIR_W) - return rotate_270(g); - else - return g; - }; /** * Marks PI/PO cell for single PI/PO gates. */ - auto mark_1_io = [this, &t](fcn_gate& g, layout::directions d) -> fcn_gate + auto mark_1_io = [this, &t](fcn_gate g, layout::directions d) -> fcn_gate { if (layout->is_pi(t)) g = mark_cell(g, dir_to_port(d), fcn::cell_mark::INPUT); @@ -51,7 +34,7 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) /** * Marks PI/PO cells for double PI/PO gates. */ - auto mark_2_io = [this, &t](fcn_gate& g, layout::directions d1, layout::directions d2) -> fcn_gate + auto mark_2_io = [this, &t](fcn_gate g, layout::directions d1, layout::directions d2) -> fcn_gate { if (layout->is_pi(t) && layout->is_po(t)) { @@ -74,7 +57,7 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) /** * Marks PI/PO cells for triple PI/PO gates. */ - auto mark_3_io_gate = [this, &t](fcn_gate& g, layout::directions d1, layout::directions d2, + auto mark_3_io_gate = [this, &t](fcn_gate g, layout::directions d1, layout::directions d2, layout::directions d3) -> fcn_gate { if (layout->is_pi(t) && layout->is_po(t)) @@ -95,7 +78,7 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) /** * Marks PI/PO cells for quadruple PI/PO gates. */ - auto mark_4_io_gate = [this, &t](fcn_gate& g) -> fcn_gate + auto mark_4_io_gate = [this](fcn_gate g) -> fcn_gate { g = mark_cell(g, dir_to_port(layout::DIR_N), fcn::cell_mark::INPUT); g = mark_cell(g, dir_to_port(layout::DIR_W), fcn::cell_mark::INPUT); @@ -108,7 +91,7 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) /** * Marks PI/PO cells for triple PI/PO fan-outs. */ - auto mark_3_io_fan_out = [this, &t](fcn_gate& g, layout::directions d1, layout::directions d2, + auto mark_3_io_fan_out = [this, &t](fcn_gate g, layout::directions d1, layout::directions d2, layout::directions d3) -> fcn_gate { if (layout->is_pi(t) && layout->is_po(t)) @@ -129,7 +112,7 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) /** * Marks PI/PO cells for quadruple PI/PO fan-outs. */ - auto mark_4_io_fan_out = [this, &t](fcn_gate& g) -> fcn_gate + auto mark_4_io_fan_out = [this](fcn_gate g) -> fcn_gate { g = mark_cell(g, dir_to_port(layout::DIR_N), fcn::cell_mark::INPUT); g = mark_cell(g, dir_to_port(layout::DIR_W), fcn::cell_mark::OUTPUT); @@ -138,6 +121,97 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) return g; }; + /** + * Determines rotation of given gate g with respect to its unused given directions. + * Works for gates with a combined amount of 3 inputs/outputs only (AND, OR, F1O2). + */ + auto pick_3_io_rotation = [&](const fcn_gate& g, layout::directions unused) -> fcn_gate + { + if (unused.count() == 1) + { + if (unused == layout::DIR_N) + return g; + else if (unused == layout::DIR_E) + return rotate_90(g); + else if (unused == layout::DIR_S) + return rotate_180(g); + else if (unused == layout::DIR_W) + return rotate_270(g); + } + else if (unused.count() == 2) + { + if (unused == layout::DIR_NE) + { + if (layout->is_free_tile(layout->north(t)) || layout->closest_border(t) == layout::DIR_N) + return mark_1_io(rotate_90(g), layout::DIR_N); + else + return mark_1_io(g, layout::DIR_E); + } + else if (unused == layout::DIR_NS) + { + if (layout->is_free_tile(layout->north(t)) || layout->closest_border(t) == layout::DIR_N) + return mark_1_io(rotate_180(g), layout::DIR_N); + else + return mark_1_io(g, layout::DIR_S); + } + else if (unused == layout::DIR_NW) + { + if (layout->is_free_tile(layout->north(t)) || layout->closest_border(t) == layout::DIR_N) + return mark_1_io(rotate_270(g), layout::DIR_N); + else + return mark_1_io(g, layout::DIR_W); + } + else if (unused == layout::DIR_EW) + { + if (layout->is_free_tile(layout->east(t)) || layout->closest_border(t) == layout::DIR_E) + return mark_1_io(rotate_270(g), layout::DIR_E); + else + return mark_1_io(rotate_90(g), layout::DIR_W); + } + else if (unused == layout::DIR_ES) + { + if (layout->is_free_tile(layout->east(t)) || layout->closest_border(t) == layout::DIR_E) + return mark_1_io(rotate_180(g), layout::DIR_E); + else + return mark_1_io(rotate_90(g), layout::DIR_S); + } + else if (unused == layout::DIR_SW) + { + if (layout->is_free_tile(layout->south(t)) || layout->closest_border(t) == layout::DIR_S) + return mark_1_io(rotate_270(g), layout::DIR_S); + else + return mark_1_io(rotate_180(g), layout::DIR_W); + } + } + else if (unused.count() == 3) + { + if (layout->get_op(t) == operation::F1O3) + { + if (unused == layout::DIR_NES) + return mark_3_io_fan_out(g, layout::DIR_N, layout::DIR_E, layout::DIR_S); + else if (unused == layout::DIR_NEW) + return mark_3_io_fan_out(g, layout::DIR_N, layout::DIR_E, layout::DIR_W); + else if (unused == layout::DIR_NSW) + return mark_3_io_fan_out(g, layout::DIR_N, layout::DIR_S, layout::DIR_W); + else if (unused == layout::DIR_ESW) + return mark_3_io_fan_out(g, layout::DIR_E, layout::DIR_S, layout::DIR_W); + } + else + { + if (unused == layout::DIR_NEW) + return mark_2_io(g, layout::DIR_E, layout::DIR_W); + else if (unused == layout::DIR_NES) + return mark_2_io(g, layout::DIR_E, layout::DIR_S); + else if (unused == layout::DIR_NSW) + return mark_2_io(g, layout::DIR_S, layout::DIR_W); + else if (unused == layout::DIR_ESW) + return mark_2_io(rotate_90(g), layout::DIR_S, layout::DIR_W); + } + } + + // unused.count() == 4 + return g; + }; switch (layout->get_op(t)) { @@ -160,25 +234,10 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) // choose correct orientation first auto conj = pick_3_io_rotation(conjunction, dirs); // mark PI/POs then - // if there is only one unused direction, there can't be PI/POs - if (dirs.count() == 1) - return conj; - else if (dirs == layout::DIR_NW || dirs == layout::DIR_EW || dirs == layout::DIR_SW) - return mark_1_io(conj, layout::DIR_W); - else if (dirs == layout::DIR_NS || dirs == layout::DIR_ES) - return mark_1_io(conj, layout::DIR_S); - else if (dirs == layout::DIR_NE) - return mark_1_io(conj, layout::DIR_E); - else if (dirs == layout::DIR_NEW) - return mark_2_io(conj, layout::DIR_E, layout::DIR_W); - else if (dirs == layout::DIR_NES) - return mark_2_io(conj, layout::DIR_E, layout::DIR_S); - else if (dirs == layout::DIR_NSW) - return mark_2_io(conj, layout::DIR_S, layout::DIR_W); - else if (dirs == layout::DIR_NESW) + if (dirs == layout::DIR_NESW) return mark_3_io_gate(conj, layout::DIR_W, layout::DIR_S, layout::DIR_E); - - throw std::invalid_argument("AND gate has unsupported directions."); + else + return conj; } case operation::OR: { @@ -187,25 +246,10 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) // choose correct orientation first auto disj = pick_3_io_rotation(disjunction, dirs); // mark PI/POs then - // if there is only one unused direction, there can't be PI/POs - if (dirs.count() == 1) - return disj; - else if (dirs == layout::DIR_NW || dirs == layout::DIR_EW || dirs == layout::DIR_SW) - return mark_1_io(disj, layout::DIR_W); - else if (dirs == layout::DIR_NS || dirs == layout::DIR_ES) - return mark_1_io(disj, layout::DIR_S); - else if (dirs == layout::DIR_NE) - return mark_1_io(disj, layout::DIR_E); - else if (dirs == layout::DIR_NEW) - return mark_2_io(disj, layout::DIR_E, layout::DIR_W); - else if (dirs == layout::DIR_NES) - return mark_2_io(disj, layout::DIR_E, layout::DIR_S); - else if (dirs == layout::DIR_NSW) - return mark_2_io(disj, layout::DIR_S, layout::DIR_W); - else if (dirs == layout::DIR_NESW) + if (dirs == layout::DIR_NESW) return mark_3_io_gate(disj, layout::DIR_W, layout::DIR_S, layout::DIR_E); - - throw std::invalid_argument("OR gate has unsupported directions."); + else + return disj; } case operation::MAJ: { @@ -213,7 +257,9 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) // no need to rotate majority auto maj = majority; - if (dirs.count() == 1) + if (dirs.count() == 0) + return maj; + else if (dirs.count() == 1) return mark_1_io(maj, dirs); else if (dirs.count() == 2) { @@ -253,25 +299,10 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) // choose correct orientation first auto fan_out = pick_3_io_rotation(fan_out_1_2, dirs); // mark PI/POs then - // if there is only one unused direction, there can't be PI/POs - if (dirs.count() == 1) - return fan_out; - else if (dirs == layout::DIR_NW || dirs == layout::DIR_EW || dirs == layout::DIR_SW) - return mark_1_io(fan_out, layout::DIR_W); - else if (dirs == layout::DIR_NS || dirs == layout::DIR_ES) - return mark_1_io(fan_out, layout::DIR_S); - else if (dirs == layout::DIR_NE) - return mark_1_io(fan_out, layout::DIR_E); - else if (dirs == layout::DIR_NEW) - return mark_2_io(fan_out, layout::DIR_E, layout::DIR_W); - else if (dirs == layout::DIR_NES) - return mark_2_io(fan_out, layout::DIR_E, layout::DIR_S); - else if (dirs == layout::DIR_NSW) - return mark_2_io(fan_out, layout::DIR_S, layout::DIR_W); - else if (dirs == layout::DIR_NESW) + if (dirs == layout::DIR_NESW) return mark_3_io_fan_out(fan_out, layout::DIR_W, layout::DIR_S, layout::DIR_E); - - throw std::invalid_argument("2-fan-out has unsupported directions."); + else + return fan_out; } case operation::F1O3: { @@ -279,38 +310,10 @@ fcn_gate qca_one_library::set_up_gate(const fcn_gate_layout::tile& t) // no need to rotate F1O3 auto fan_out = fan_out_1_3; - if (dirs.count() == 1) - return mark_1_io(fan_out, dirs); - else if (dirs.count() == 2) - { - if (dirs == layout::DIR_NE) - return mark_2_io(fan_out, layout::DIR_N, layout::DIR_E); - else if (dirs == layout::DIR_NS) - return mark_2_io(fan_out, layout::DIR_N, layout::DIR_S); - else if (dirs == layout::DIR_NW) - return mark_2_io(fan_out, layout::DIR_N, layout::DIR_W); - else if (dirs == layout::DIR_ES) - return mark_2_io(fan_out, layout::DIR_E, layout::DIR_S); - else if (dirs == layout::DIR_EW) - return mark_2_io(fan_out, layout::DIR_E, layout::DIR_W); - else if (dirs == layout::DIR_SW) - return mark_2_io(fan_out, layout::DIR_S, layout::DIR_W); - } - else if (dirs.count() == 3) - { - if (dirs == layout::DIR_NES) - return mark_3_io_fan_out(fan_out, layout::DIR_N, layout::DIR_E, layout::DIR_S); - else if (dirs == layout::DIR_NEW) - return mark_3_io_fan_out(fan_out, layout::DIR_N, layout::DIR_E, layout::DIR_W); - else if (dirs == layout::DIR_NSW) - return mark_3_io_fan_out(fan_out, layout::DIR_N, layout::DIR_S, layout::DIR_W); - else if (dirs == layout::DIR_ESW) - return mark_3_io_fan_out(fan_out, layout::DIR_E, layout::DIR_S, layout::DIR_W); - } - else if (dirs.count() == 4) + if (dirs == layout::DIR_NESW) return mark_4_io_fan_out(fan_out); - - break; + else + return fan_out; } case operation::PI: { diff --git a/src/tech/qca_one_library.h b/src/tech/qca_one_library.h index 4f41b426b..bec7821fa 100644 --- a/src/tech/qca_one_library.h +++ b/src/tech/qca_one_library.h @@ -24,7 +24,7 @@ class qca_one_library : public fcn_gate_library * doing so. That means, construction of a qca_one_library object can fail with an exception. This is intended! * See commands.h for details on usage. */ - explicit qca_one_library(fcn_gate_layout_ptr&& fgl); + explicit qca_one_library(fcn_gate_layout_ptr fgl); /** * Copy constructor is not available. */ @@ -47,7 +47,7 @@ class qca_one_library : public fcn_gate_library qca_one_library& operator=(qca_one_library&& rhs) = delete; /** * Overrides the corresponding function in fcn_gate_library. Given a tile t, this function takes all necessary - * information from the stored grid into accout to choose the correct fcn_gate representation for that tile. May it + * information from the stored grid into account to choose the correct fcn_gate representation for that tile. May it * be a gate or wires. Rotation and special marks like input and output, const cells etc. are computed additionally. * * @param t Tile to be realized in QCA-ONE. diff --git a/src/topo/bidirectional_graph.h b/src/topo/bidirectional_graph.h index 701845cd2..a9c5730cf 100644 --- a/src/topo/bidirectional_graph.h +++ b/src/topo/bidirectional_graph.h @@ -94,16 +94,6 @@ class bidirectional_graph using edge_index_t = std::size_t; using vertex_index_t = std::size_t; - - // ************************************************************ - // ************************ Attributes ************************ - // ************************************************************ - - using edge_index_map = std::unordered_map>; - /** - * Associates edges with indices. - */ - edge_index_map ei_map; public: // ************************************************************ @@ -403,17 +393,6 @@ class bidirectional_graph { return boost::in_degree(v, graph); } - /** - * Updates the indices stored for each edge. This function only needs to be called when the graph was manipulated. - */ - void update_edge_index_map() noexcept - { - ei_map.clear(); - - auto i = 0u; - for (auto&& e : get_edges()) - ei_map[e] = i++; - } /** * Returns a stored vertex_index for the given vertex_t. May lead to unexpected behavior when * property_vertex_index_map and the vertices are out of sync. @@ -426,17 +405,6 @@ class bidirectional_graph auto param = get(boost::vertex_index, graph); return param[v]; } - /** - * Returns a stored edge_index for the given edge_t. May lead to unexpected behavior when property_edge_index_map - * and the edges are out of sync. - * - * @param e Edge whose stored index is desired. - * @return Stored index of edge e. - */ - edge_index_t get_index(const edge_t& e) const - { - return ei_map.at(e); - } /** * Sorts the graph topologically. Throws a not_a_dag exception if * the graph is not directed and acyclic. diff --git a/src/topo/fcn_clocking_scheme.cpp b/src/topo/fcn_clocking_scheme.cpp index 54b7a9a05..5630d9132 100644 --- a/src/topo/fcn_clocking_scheme.cpp +++ b/src/topo/fcn_clocking_scheme.cpp @@ -5,16 +5,47 @@ #include "fcn_clocking_scheme.h" -fcn_clocking_scheme::fcn_clocking_scheme(const fcn_clock::cutout& c, const fcn_clock::number n, const bool r) +fcn_clocking_scheme::fcn_clocking_scheme(const std::string& name, const fcn_clock::cutout& c, const fcn_clock::number n, const bool r) : + name(name), scheme(c), num_clocks(n), - regular(r) + regular(r), + cutout_y(scheme.size()), + cutout_x(cutout_y ? scheme[0].size() : 0) {} -fcn_clocking_scheme::fcn_clocking_scheme(fcn_clock::cutout&& c, fcn_clock::number&& n, bool&& r) +fcn_clocking_scheme::fcn_clocking_scheme(std::string&& name, fcn_clock::cutout&& c, fcn_clock::number&& n, bool&& r) : + name(std::move(name)), scheme(std::move(c)), num_clocks(std::move(n)), - regular(std::move(r)) + regular(std::move(r)), + cutout_y(scheme.size()), + cutout_x(cutout_y ? scheme[0].size() : 0) {} + +boost::optional get_clocking_scheme(const std::string& name) +{ + static const std::unordered_map scheme_lookup + {{ + { "OPEN3", open_3_clocking }, + { "OPEN4", open_4_clocking }, + { "2DDWAVE3", twoddwave_3_clocking }, + { "DIAG3", twoddwave_3_clocking }, + { "2DDWAVE4", twoddwave_4_clocking }, + { "DIAG4", twoddwave_4_clocking }, + { "USE", use_4_clocking }, + { "RES", res_4_clocking }, + { "BANCS", bancs_3_clocking }, + }}; + + try + { + return scheme_lookup.at(boost::to_upper_copy(name)); + } + catch (...) + { + return boost::none; + } +} \ No newline at end of file diff --git a/src/topo/fcn_clocking_scheme.h b/src/topo/fcn_clocking_scheme.h index 8fcb30dc3..f91bde2a4 100644 --- a/src/topo/fcn_clocking_scheme.h +++ b/src/topo/fcn_clocking_scheme.h @@ -7,13 +7,17 @@ #include #include +#include +#include +#include +#include namespace fcn_clock { /** * Alias for clock zones used for layout tiles. */ - using zone = unsigned; + using zone = std::size_t; /** * Alias for the number of different clocks in a scheme. */ @@ -26,15 +30,15 @@ namespace fcn_clock * Representing the BANCS clocking as defined in "BANCS: Bidirectional Alternating Nanomagnetic Clocking Scheme" by * Ruan Evangelista Formigoni, Omar P. Vilela Neto, and Jose Augusto M. Nacif in SBCCI 2018. */ -// const cutout bancs_3 = -// {{ -// {{0, 1, 2}}, -// {{2, 1, 0}}, -// {{2, 0, 1}}, -// {{1, 0, 2}}, -// {{1, 2, 0}}, -// {{0, 2, 1}} -// }}; + const cutout bancs_3 = + {{ + {{0, 1, 2}}, + {{2, 1, 0}}, + {{2, 0, 1}}, + {{1, 0, 2}}, + {{1, 2, 0}}, + {{0, 2, 1}} + }}; /** * Representing the USE clocking as defined in "USE: A Universal, Scalable, and Efficient Clocking Scheme for QCA" * by Caio Araujo T. Campos, Abner L. Marciano, Omar P. Vilela Neto, and Frank Sill Torres in TCAD 2015. @@ -59,18 +63,20 @@ namespace fcn_clock {{0, 3, 2, 1}} }}; /** - * Representing a simple diagonal 3-phase clocking which can be easily adopted for combinatorial circuits. + * Representing a 3-phase adoption of the 2DDWave clocking as defined in "Clocking and Cell Placement for QCA" by + * V. Vankamamidi, M. Ottavi, and F. Lombardi in IEEE Conference on Nanotechnology 2006. */ - const cutout diagonal_3 = + const cutout twoddwave_3 = {{ {{0, 1, 2}}, {{1, 2, 0}}, {{2, 0, 1}} }}; /** - * Representing a simple diagonal 4-phase clocking which can be easily adopted for combinatorial circuits. + * Representing the original 2DDWave clocking as defined in "Clocking and Cell Placement for QCA" by V. Vankamamidi, + * M. Ottavi, and F. Lombardi in IEEE Conference on Nanotechnology 2006. */ - const cutout diagonal_4 = + const cutout twoddwave_4 = {{ {{0, 1, 2, 3}}, {{1, 2, 3, 0}}, @@ -86,6 +92,10 @@ namespace fcn_clock class fcn_clocking_scheme { public: + /** + * Name of the clocking scheme. + */ + const std::string name; /** * A single CLOCKS x CLOCKS tiles clocking scheme cutout. If scheme is regular, all other * positions can be determined easily. @@ -99,24 +109,35 @@ class fcn_clocking_scheme * Defines the clocking as regular and well-defined by the scheme. */ const bool regular; + /** + * Number of zones in y-direction of the stored cutout. + */ + const fcn_clock::zone cutout_y; + /** + * Number of zones in x-direction of the stored cutout. + */ + const fcn_clock::zone cutout_x; /** * Standard constructor. * Initializes a clocking scheme with the given cutout. * + * @param name Name of the clocking scheme. * @param c Cutout of tiles representing a regular scheme. * @param n Number of different clocks. * @param r Flag to indicate clocking as regular. */ - fcn_clocking_scheme(const fcn_clock::cutout& c, const fcn_clock::number n, const bool r); + fcn_clocking_scheme(const std::string& name, const fcn_clock::cutout& c, const fcn_clock::number n, const bool r); + /** * Move constructor. * Initializes a clocking scheme with the given cutout. * + * @param name Name of the clocking scheme. * @param c Cutout of tiles representing a regular scheme. * @param n Number of different clocks. * @param r Flag to indicate clocking as regular. */ - fcn_clocking_scheme(fcn_clock::cutout&& c, fcn_clock::number&& n, bool&& r); + fcn_clocking_scheme(std::string&& name, fcn_clock::cutout&& c, fcn_clock::number&& n, bool&& r); /** * Default copy constructor. */ @@ -140,33 +161,40 @@ class fcn_clocking_scheme }; /** - * The BANCS clocking is not supported yet. + * Pre-defined open clocking with 3 clock phases. */ -// static fcn_clocking_scheme bancs_3_clocking{fcn_clock::bancs_3, 3u, true}; +static fcn_clocking_scheme open_3_clocking{"OPEN3", fcn_clock::cutout{}, 3u, false}; /** - * Pre-defined 4 x 4 USE clocking instance. + * Pre-defined open clocking with 4 clock phases. */ -static fcn_clocking_scheme use_4_clocking{fcn_clock::use_4, 4u, true}; +static fcn_clocking_scheme open_4_clocking{"OPEN4", fcn_clock::cutout{}, 4u, false}; /** - * Pre-defined 4 x 4 RES clocking instance. + * Pre-defined 3-phase 2DDWave clocking instance. */ -static fcn_clocking_scheme res_4_clocking{fcn_clock::res_4, 4u, true}; +static fcn_clocking_scheme twoddwave_3_clocking{"2DDWAVE3", fcn_clock::twoddwave_3, 3u, true}; /** - * Pre-defined 3 x 3 diagonal clocking instance. + * Pre-defined 4-phase 2DDWave clocking instance. */ -static fcn_clocking_scheme diagonal_3_clocking{fcn_clock::diagonal_3, 3u, true}; +static fcn_clocking_scheme twoddwave_4_clocking{"2DDWAVE4", fcn_clock::twoddwave_4, 4u, true}; /** - * Pre-defined 4 x 4 diagonal clocking instance. + * Pre-defined 4 x 4 USE clocking instance. + */ +static fcn_clocking_scheme use_4_clocking{"USE", fcn_clock::use_4, 4u, true}; +/** + * Pre-defined 4 x 4 RES clocking instance. */ -static fcn_clocking_scheme diagonal_4_clocking{fcn_clock::diagonal_4, 4u, true}; +static fcn_clocking_scheme res_4_clocking{"RES", fcn_clock::res_4, 4u, true}; /** - * Pre-defined open clocking with 3 clock numbers. + * Pre-defined 3 x 6 BANCS clocking instance. */ -static fcn_clocking_scheme open_3_clocking{fcn_clock::cutout{}, 3u, false}; +static fcn_clocking_scheme bancs_3_clocking{"BANCS", fcn_clock::bancs_3, 3u, true}; /** - * Pre-defined open clocking with 4 clock numbers. + * Looks up a clocking scheme by its name. String comparison happens case-insensitive. + * + * @param name Name of desired clocking scheme. + * @return Clocking scheme called name or boost::none if no string matches name. */ -static fcn_clocking_scheme open_4_clocking{fcn_clock::cutout{}, 4u, false}; +boost::optional get_clocking_scheme(const std::string& name); #endif //FICTION_FCN_CLOCKING_SCHEME_H diff --git a/src/topo/fcn_gate_layout.cpp b/src/topo/fcn_gate_layout.cpp index 2d1e5b1ce..b9bd6c474 100644 --- a/src/topo/fcn_gate_layout.cpp +++ b/src/topo/fcn_gate_layout.cpp @@ -44,31 +44,31 @@ bool fcn_gate_layout::is_border_tile(const tile& t) const noexcept layout::directions fcn_gate_layout::closest_border(const tile& t) const noexcept { // tile is on the left side of the layout - if (t[X] <= x() / 2) + if (t[X] < x() / 2) { // tile is on the upper side of the layout - if (t[Y] <= y() / 2) + if (t[Y] < y() / 2) { - return t[X] <= t[Y] ? layout::DIR_W : layout::DIR_N; + return t[X] < t[Y] ? layout::DIR_W : layout::DIR_N; } // tile is on the lower side of the layout else { - return t[X] <= y() - t[Y] ? layout::DIR_W : layout::DIR_S; + return t[X] < y() - t[Y] ? layout::DIR_W : layout::DIR_S; } } // tile is on the right side of the layout else { // tile is on the upper side of the layout - if (t[Y] <= y() / 2) + if (t[Y] < y() / 2) { - return x() - t[X] <= t[Y] ? layout::DIR_E : layout::DIR_N; + return x() - t[X] < t[Y] ? layout::DIR_E : layout::DIR_N; } // tile is on the lower side of the layout else { - return x() - t[X] <= y() - t[Y] ? layout::DIR_E : layout::DIR_S; + return x() - t[X] < y() - t[Y] ? layout::DIR_E : layout::DIR_S; } } } @@ -78,7 +78,7 @@ boost::optional fcn_gate_layout::tile_clocking(const tile& t) c if (clocking.regular) { auto x = t[X], y = t[Y]; - return clocking.scheme[y % clocking.num_clocks][x % clocking.num_clocks]; + return clocking.scheme[y % clocking.cutout_y][x % clocking.cutout_x]; } else // irregular clocking accesses clocking map { @@ -269,125 +269,154 @@ void fcn_gate_layout::clear_tile(const tile& t) noexcept bool fcn_gate_layout::is_free_tile(const tile& t) const noexcept { - return !get_logic_vertex(t) && !is_wire_tile(t); + return !is_gate_tile(t) && !is_wire_tile(t); } -std::vector fcn_gate_layout::outgoing_information_flow_tiles(const tile& t) const noexcept +boost::optional fcn_gate_layout::is_data_flow(const gate_or_wire& gw, const tile& at, const bool out) const noexcept { - std::vector oift{}; // outgoing information flow tiles + // if a gate was passed as gw + if (auto v1 = boost::get(&gw)) + { + // if at is a gate as well + if (auto v2 = get_logic_vertex(at)) + { + // if connection v1 -> v2 or v2 -> v1 exists respectively + if (out ? network->get_edge(*v1, *v2) : network->get_edge(*v2, *v1)) + return gate_or_wire{*v2}; + } + // if at is a wire tile instead + for (auto& e2 : get_logic_edges(at)) + { + // if v1 is the source/target of e2 + if (out ? (network->source(e2) == *v1) : (network->target(e2) == *v1)) + return gate_or_wire{e2}; + } + } + // if a wire was passed as gw + if (auto e1 = boost::get(&gw)) + { + // if v2 is a gate tile + if (auto v2 = get_logic_vertex(at)) + { + // if v2 is the target/source of e1 + if (out ? (network->target(*e1) == *v2) : (network->source(*e1) == *v2)) + return gate_or_wire{*v2}; + } + // if at is a wire tile instead + if (has_logic_edge(at, *e1)) + return gate_or_wire{*e1}; + } - auto v = get_logic_vertex(t); - auto edges = get_logic_edges(t); + // no information flow from t1 to at or non-matching arguments + return boost::none; +} - // for all outgoing clocked tiles - for (auto&& _t : outgoing_clocked_tiles(t)) +boost::optional fcn_gate_layout::is_out_data_flow(const gate_or_wire& gw, const tile& at) const noexcept +{ + return is_data_flow(gw, at, true); +} + +boost::optional fcn_gate_layout::is_in_data_flow(const gate_or_wire& gw, const tile& at) const noexcept +{ + return is_data_flow(gw, at, false); +} + +bool fcn_gate_layout::is_data_flow(const tile& t1, const tile& t2) const noexcept +{ + if (auto v = get_logic_vertex(t1)) { - // if t got assigned a vertex - if (v) + return static_cast(is_out_data_flow({*v}, t2)); + } + else if (is_wire_tile(t1)) + { + for (const auto& e : get_logic_edges(t1)) { - // for all adjacent logic vertices - for (auto&& av : network->adjacent_vertices(*v, true)) - { - // check the tile plus above and below for matches - if (has_logic_vertex(_t, av)) - oift.push_back(_t); - if (has_logic_vertex(above(_t), av)) - oift.push_back(above(_t)); - if (has_logic_vertex(below(_t), av)) - oift.push_back(below(_t)); - } - // for all adjacent logic edges - for (auto&& ae : network->out_edges(*v, true)) - { - // check the tile plus above and below for matches - if (has_logic_edge(_t, ae)) - oift.push_back(_t); - if (has_logic_edge(above(_t), ae)) - oift.push_back(above(_t)); - if (has_logic_edge(below(_t), ae)) - oift.push_back(below(_t)); - } + if (is_out_data_flow({e}, t2)) + return true; } - // if t got assigned edges instead - else + } + + return false; +} + +std::vector> fcn_gate_layout::outgoing_data_flow(const tile& t, const gate_or_wire& gw) const noexcept +{ + std::vector> odf{}; // outgoing data flow + + // for all outgoing clocked tiles + for (auto&& oct : outgoing_clocked_tiles(t)) + { + for (const auto& at : std::vector{{oct, above(oct), below(oct)}}) { - for (auto& e : edges) + if (is_tile_out_dir(t, get_bearing(t, at))) { - // check if there is edge's target - const auto tgt = network->target(e); - if (has_logic_vertex(_t, tgt) || has_logic_edge(_t, e)) - oift.push_back(_t); - if (has_logic_vertex(above(_t), tgt) || has_logic_edge(above(_t), e)) - oift.push_back(above(_t)); - if (has_logic_vertex(below(_t), tgt) || has_logic_edge(below(_t), e)) - oift.push_back(below(_t)); + if (auto target = is_out_data_flow(gw, at)) + odf.push_back(std::make_pair(at, *target)); } } } - // remove duplicates. vector is sorted due to order of pushing - oift.erase(std::unique(oift.begin(), oift.end()), oift.end()); + // sort and remove duplicates + std::sort(odf.begin(), odf.end()); + odf.erase(std::unique(odf.begin(), odf.end()), odf.end()); - return oift; + return odf; } -std::vector fcn_gate_layout::incoming_information_flow_tiles(const tile& t) const noexcept +std::vector fcn_gate_layout::outgoing_data_flow(const tile& t) const noexcept { - std::vector iift{}; // incoming information flow tiles + std::vector odf{}; // outgoing data flow - auto v = get_logic_vertex(t); - auto edges = get_logic_edges(t); + for (auto&& oct : outgoing_clocked_tiles(t)) + { + for (const auto& at : std::vector{{oct, above(oct), below(oct)}}) + { + if (is_data_flow(t, at) && is_tile_out_dir(t, get_bearing(t, at))) + odf.push_back(at); + } + } + + return odf; +} + +std::vector> fcn_gate_layout::incoming_data_flow(const tile& t, const gate_or_wire& gw) const noexcept +{ + std::vector> idf{}; // incoming data flow // for all incoming clocked tiles - for (auto&& _t : incoming_clocked_tiles(t)) + for (auto&& ict : incoming_clocked_tiles(t)) { - // if t got assigned a vertex - if (v) + for (const auto& iat : std::vector{{ict, above(ict), below(ict)}}) { - // for all inversely adjacent logic vertices - for (auto&& iav : network->inv_adjacent_vertices(*v, true)) + if (is_tile_inp_dir(t, get_bearing(t, iat))) { - // check the tile plus above and below for matches - if (has_logic_vertex(_t, iav)) - iift.push_back(_t); - if (has_logic_vertex(above(_t), iav)) - iift.push_back(above(_t)); - if (has_logic_vertex(below(_t), iav)) - iift.push_back(below(_t)); - } - // for all inversely adjacent logic edges - for (auto&& iae : network->in_edges(*v, true)) - { - // check the tile plus above and below for matches - if (has_logic_edge(_t, iae)) - iift.push_back(_t); - if (has_logic_edge(above(_t), iae)) - iift.push_back(above(_t)); - if (has_logic_edge(below(_t), iae)) - iift.push_back(below(_t)); + if (auto source = is_in_data_flow(gw, iat)) + idf.push_back(std::make_pair(iat, *source)); } } - // if t got assigned edges instead - else + } + + // sort and remove duplicates + std::sort(idf.begin(), idf.end()); + idf.erase(std::unique(idf.begin(), idf.end()), idf.end()); + + return idf; +} + +std::vector fcn_gate_layout::incoming_data_flow(const tile& t) const noexcept +{ + std::vector idf{}; // incoming data flow + + for (auto&& ict : incoming_clocked_tiles(t)) + { + for (const auto& iat : std::vector{{ict, above(ict), below(ict)}}) { - for (auto& e : edges) - { - // check if there is edge's source - const auto src = network->source(e); - if (has_logic_vertex(_t, src) || has_logic_edge(_t, e)) - iift.push_back(_t); - if (has_logic_vertex(above(_t), src) || has_logic_edge(above(_t), e)) - iift.push_back(above(_t)); - if (has_logic_vertex(below(_t), src) || has_logic_edge(below(_t), e)) - iift.push_back(below(_t)); - } + if (is_data_flow(iat, t) && is_tile_inp_dir(t, get_bearing(t, iat))) + idf.push_back(iat); } } - // remove duplicates. vector is sorted due to order of pushing - iift.erase(std::unique(iift.begin(), iift.end()), iift.end()); - - return iift; + return idf; } operation fcn_gate_layout::get_op(const tile& t) const noexcept @@ -409,34 +438,34 @@ operation fcn_gate_layout::get_op(const tile& t) const noexcept } } -fcn_gate_layout::path_info fcn_gate_layout::signal_delay(const tile& t, delay_cache& dc) const noexcept +fcn_gate_layout::path_info fcn_gate_layout::signal_delay(const tile& t, const gate_or_wire& gw, delay_cache& dc) const noexcept { if (is_free_tile(t)) return {}; - auto iift = incoming_information_flow_tiles(t); - if (iift.empty()) + auto idf = incoming_data_flow(t, gw); + if (idf.empty()) return {1, *tile_clocking(t), 0}; - else if (dc.count(t)) // cache hit + else if (dc.count(t)) // cache hit return dc.at(t); - else // cache miss + else // cache miss { // fetch information about all incoming paths std::vector infos{}; - for (const auto& cur_t : iift) - infos.push_back(signal_delay(cur_t, dc)); + for (const auto& cur_t : idf) + infos.push_back(signal_delay(cur_t.first, cur_t.second, dc)); path_info dominant_path{}; - if (is_pi(t)) // primary input in the circuit - infos.push_back({ 1, (*tile_clocking(t) + (num_clocks() - 1)) % num_clocks(), 0 }); + if (is_pi(t)) // primary input in the circuit + infos.push_back({1, (*tile_clocking(t) + (num_clocks() - 1)) % num_clocks(), 0}); - if (infos.size() == 1) // size cannot be 0 + if (infos.size() == 1) // size cannot be 0 dominant_path = infos.front(); else // fetch highest delay and difference { // sort by path length - std::sort(infos.begin(), infos.end(), [](const auto& i1, const auto& i2){return i1.length < i2.length;}); + std::sort(infos.begin(), infos.end(), [](const auto& i1, const auto& i2) { return i1.length < i2.length; }); dominant_path.length = infos.back().length; dominant_path.delay = infos.back().delay; @@ -460,18 +489,18 @@ std::pair fcn_gate_layout::critical_path_length_and_th delay_cache dc{}; std::size_t critical_path = 0; for (auto&& po : get_pos()) - critical_path = std::max(signal_delay(po, dc).length, critical_path); + critical_path = std::max(signal_delay(po, gate_or_wire{*get_logic_vertex(po)}, dc).length, critical_path); std::size_t throughput = 0; if (!dc.empty()) throughput = std::max_element(dc.begin(), dc.end(), - [](const auto& i1, const auto& i2){return i1.second.diff < i2.second.diff;})->second.diff; + [](const auto& i1, const auto& i2) { return i1.second.diff < i2.second.diff; })->second.diff; // give throughput in cycles, not in phases throughput /= num_clocks(); - // get from cycle difference to throughput - ++throughput; + // convert cycle difference to throughput + ++throughput; return std::make_pair(critical_path, throughput); } @@ -819,7 +848,7 @@ fcn_gate_layout::bounding_box fcn_gate_layout::determine_bounding_box() const no void fcn_gate_layout::shrink_to_fit() noexcept { auto bb = determine_bounding_box(); - resize(fcn_dimension_xyz{bb.max_x + 1, bb.max_y + 1, z()}); + resize(fcn_dimension_xyz{std::max(bb.max_x + 1, 2ul), std::max(bb.max_y + 1, 2ul), z()}); // incorporate BGL bug } fcn_gate_layout::energy_info fcn_gate_layout::calculate_energy() const noexcept diff --git a/src/topo/fcn_gate_layout.h b/src/topo/fcn_gate_layout.h index df5be65da..4e449f4e7 100644 --- a/src/topo/fcn_gate_layout.h +++ b/src/topo/fcn_gate_layout.h @@ -10,11 +10,12 @@ #include "directions.h" #include "energy_model.h" #include +#include #include #include /** - * Represents layouts of field coupled nanocomputing (FCN) devices on a gate level abstraction. Inherits from fcn_layout + * Represents layouts of field-coupled nanocomputing (FCN) devices on a gate level abstraction. Inherits from fcn_layout * so it is a 3-dimensional grid-like structure as well. Faces are called tiles in a gate layout. Tiles can be occupied * by either an operation like AND, OR, NOT, fan-outs, special PI/PO ports or buffers (which make the tile a gate tile), * or multiple edges (which makes the tile a wire tile). All operations and edges are associated with a logic_network @@ -355,22 +356,86 @@ class fcn_gate_layout : public fcn_layout * @return True, iff tile t has no logic vertex or logic edge assigned to it. */ bool is_free_tile(const tile& t) const noexcept; + /** + * A variant of either a gate or a wire necessary for data flow. + */ + using gate_or_wire = boost::variant; +private: + /** + * Checks if there is a valid data flow from given gate_or_wire to given tile at or vice versa. A data flow is + * characterized by a vertex to edge, edge to edge, or edge to vertex connection according to the stored + * logic_network. The respective target element is returned if there is one. boost::none otherwise. + * + * @param gw Gate or wire as starting/ending point of data flow. + * @param at Adjacent tile of data flow to check. + * @param out Flag to determine direction of data flow to check. True: gw -> at. False: at -> gw. + * @return Target gate_or_wire element iff data flows from gw to at or at to gw according to network. + * boost::none otherwise. + */ + boost::optional is_data_flow(const gate_or_wire& gw, const tile& at, const bool out) const noexcept; +public: + /** + * Public proxy for outgoing is_data_flow without the weird bool flag. For information see is_data_flow. + */ + boost::optional is_out_data_flow(const gate_or_wire& gw, const tile& at) const noexcept; + /** + * Public proxy for incoming is_data_flow without the weird bool flag. For information see is_data_flow. + */ + boost::optional is_in_data_flow(const gate_or_wire& gw, const tile& at) const noexcept; + /** + * Generic version of the proxies above that only returns true iff there is any data flow from given tile t1 to + * given tile t2. + * + * This function is recommended only if no concrete data path should be followed but rather one is interested in all + * data flows from t1 to t2. Otherwise, is_out/in_data_flow should be used. + * + * @param t1 Source tile of data flow. + * @param t2 Target tile of data flow. + * @return True iff there is any data flow from t1 to t2. + */ + bool is_data_flow(const tile& t1, const tile& t2) const noexcept; + /** + * Returns a vector of pairs of tiles and gate_or_wire elements containing the ones with outgoing data flow with + * respect to the given tile t which have a subsequent logic vertex or logic edge assigned with respect to gw and + * the network. + * + * @param t Tile whose outgoing data flow tiles are desired. + * @param gw Gate or wire as a starting point of data flow. + * @return A vector of all outgoing data flow tiles and the gate or wire targets to t. + */ + std::vector> outgoing_data_flow(const tile& t, const gate_or_wire& gw) const noexcept; /** * Returns a vector of tiles containing the ones with outgoing data flow with respect to the given tile t which have - * a subsequent logic vertex or logic edge assigned with respect to the network. + * a subsequent logic vertex or logic edge assigned. * - * @param t Tile whose outgoing information flow tiles are desired. - * @return A vector of all outgoing information flow tiles to t. + * This function is recommended only if no concrete data path should be followed but rather one is interested in all + * data flows from a tile. Otherwise, the other overload should be used. + * + * @param t Tile whose outgoing data flow tiles are desired. + * @return A vector of all outgoing data flow tiles to t. */ - std::vector outgoing_information_flow_tiles(const tile& t) const noexcept; + std::vector outgoing_data_flow(const tile& t) const noexcept; + /** + * Returns a vector of pairs of tiles and gate_or_wire elements containing the ones with incoming data flow with + * respect to the given tile t which have a preceeding logic vertex or logic edge assigned with respect to gw and + * the network. + * + * @param t Tile whose incoming data flow tiles are desired. + * @param gw Gate or wire as an end point of data flow. + * @return A vector of all incoming data flow tiles and the gate or wire targets to t. + */ + std::vector> incoming_data_flow(const tile& t, const gate_or_wire& gw) const noexcept; /** * Returns a vector of tiles containing the ones with incoming data flow with respect to the given tile t which have - * a preceeding logic vertex or logic edge assigned with respect to the network. + * a preceeding logic vertex or logic edge assigned. + * + * This function is recommended only if no concrete data path should be followed but rather one is interested in all + * data flows to a tile. Otherwise, the other overload should be used. * - * @param t Tile whose incoming information flow tiles are desired. - * @return A vector of all incoming information flow tiles to t. + * @param t Tile whose incoming data flow tiles are desired. + * @return A vector of all incoming data flow tiles to t. */ - std::vector incoming_information_flow_tiles(const tile& t) const noexcept; + std::vector incoming_data_flow(const tile& t) const noexcept; /** * Returns the operation assigned to tile t. If no operation has been set, op::NONE is returned. * Note that op::W is returned if a logic edge has been assigned to t. @@ -413,7 +478,7 @@ class fcn_gate_layout : public fcn_layout struct path_info { /** - * Captures absolute path length, signal delay (first clocknumber plays a role), and delay differences. + * Captures absolute path length, signal delay (first clock number plays a role), and delay differences. */ std::size_t length = 0, delay = 0, diff = 0; }; @@ -430,10 +495,11 @@ class fcn_gate_layout : public fcn_layout * anyways. This way, the cache is just a slight memory overhead but significantly reduces runtime. * * @param t Tile whose number of tiles on the longest path from any PI is desired. + * @param gw Gate or wire assigned to tile t to trace the signal path. * @param dc A cache structure to avoid re-computations of paths. * @return Number of tiles in the longest path from a PI to t in several configurations. */ - path_info signal_delay(const tile& t, delay_cache& dc) const noexcept; + path_info signal_delay(const tile& t, const gate_or_wire& gw, delay_cache& dc) const noexcept; /** * Returns the length of the longest path from any PI to any PO, i.e. the highest signal delay in tiles as the first * return value. diff --git a/src/topo/fcn_layout.h b/src/topo/fcn_layout.h index aed5a1f50..687da81c8 100644 --- a/src/topo/fcn_layout.h +++ b/src/topo/fcn_layout.h @@ -75,7 +75,7 @@ class fcn_layout : protected grid_graph<3> /** * Alias for a number that represents delay of a latch face in clock phases (1/n clock cycles). */ - using latch_delay = unsigned; + using latch_delay = std::size_t; /** * Standard constructor. Creates an FCN layout by the means of an array determining its size * as well as a clocking scheme defining its data flow possibilities. diff --git a/src/topo/logic_network.cpp b/src/topo/logic_network.cpp index ec7cf7200..a7ef16672 100644 --- a/src/topo/logic_network.cpp +++ b/src/topo/logic_network.cpp @@ -511,11 +511,6 @@ void logic_network::write_network(std::ostream& os) noexcept os << graph.str() << std::endl; } -void logic_network::update_index_maps() noexcept -{ - update_edge_index_map(); -} - void logic_network::increment_op_counter(const operation o) noexcept { ++operation_counter[o]; diff --git a/src/topo/logic_network.h b/src/topo/logic_network.h index 16815dbd9..bd66e15d4 100644 --- a/src/topo/logic_network.h +++ b/src/topo/logic_network.h @@ -623,12 +623,6 @@ class logic_network : protected bidirectional_graph