diff options
author | Jim Lawson <ucbjrl@berkeley.edu> | 2019-07-24 10:20:46 -0700 |
---|---|---|
committer | Jim Lawson <ucbjrl@berkeley.edu> | 2019-07-24 10:20:46 -0700 |
commit | c66b7402c06455535bb43ee65fe20515b5b9c0ee (patch) | |
tree | ad135d83bf75e72b65e3136b4f6746c1f9cafab3 | |
parent | 349c47250a9779bc58634870d2e3facfe95fbff8 (diff) | |
parent | a66f17b6a78af8f6989235f0c72d5548b0560a58 (diff) | |
download | yosys-c66b7402c06455535bb43ee65fe20515b5b9c0ee.tar.gz yosys-c66b7402c06455535bb43ee65fe20515b5b9c0ee.tar.bz2 yosys-c66b7402c06455535bb43ee65fe20515b5b9c0ee.zip |
Merge remote-tracking branch 'upstream/master'
199 files changed, 9423 insertions, 1214 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..9910e9954 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.editorconfig
+.gitignore
+.gitmodules
+.github
+.git
+Dockerfile
+README.md
+manual
+CodingReadme
+CodeOfConduct
+.travis
+.travis.yml
+
@@ -3,6 +3,19 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.9 .. Yosys 0.9-dev +-------------------------- + + * Various + - Added "write_xaiger" backend + - Added "abc9" pass for timing-aware techmapping (experimental, FPGA only, no FFs) + - Added "synth_xilinx -abc9" (experimental) + - Added "synth_ice40 -abc9" (experimental) + - Added "synth -abc9" (experimental) + - Added "script -scriptwire + - "synth_xilinx" to now infer wide multiplexers (-widemux <min> to enable) + + Yosys 0.8 .. Yosys 0.8-dev -------------------------- @@ -16,7 +29,18 @@ Yosys 0.8 .. Yosys 0.8-dev - Added "gate2lut.v" techmap rule - Added "rename -src" - Added "equiv_opt" pass - - "synth_xilinx" to now infer hard shift registers, using new "shregmap -tech xilinx" + - Added "shregmap -tech xilinx" + - Added "read_aiger" frontend + - Added "muxcover -mux{4,8,16}=<cost>" + - Added "muxcover -dmux=<cost>" + - Added "muxcover -nopartial" + - Added "muxpack" pass + - Added "pmux2shiftx -norange" + - Added "synth_xilinx -nocarry" + - Added "synth_xilinx -nowidelut" + - Added "synth_ecp5 -nowidelut" + - "synth_xilinx" to now infer hard shift registers (-nosrl to disable) + - Fixed sign extension of unsized constants with 'bx and 'bz MSB Yosys 0.7 .. Yosys 0.8 @@ -30,7 +54,7 @@ Yosys 0.7 .. Yosys 0.8 - Added "write_verilog -decimal" - Added "scc -set_attr" - Added "verilog_defines" command - - Remeber defines from one read_verilog to next + - Remember defines from one read_verilog to next - Added support for hierarchical defparam - Added FIRRTL back-end - Improved ABC default scripts @@ -39,7 +63,7 @@ Yosys 0.7 .. Yosys 0.8 - Added Verilog $rtoi and $itor support - Added "check -initdrv" - Added "read_blif -wideports" - - Added support for systemVerilog "++" and "--" operators + - Added support for SystemVerilog "++" and "--" operators - Added support for SystemVerilog unique, unique0, and priority case - Added "write_edif" options for edif "flavors" - Added support for resetall compiler directive diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..3c7188d82 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:18.04 as builder +LABEL author="Abdelrahman Hosny <abdelrahman.hosny@hotmail.com>" +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y build-essential \ + clang \ + bison \ + flex \ + libreadline-dev \ + gawk \ + tcl-dev \ + libffi-dev \ + git \ + pkg-config \ + python3 && \ + rm -rf /var/lib/apt/lists +COPY . / +RUN make && \ + make install + +FROM ubuntu:18.04 +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y libreadline-dev tcl-dev + +COPY --from=builder /yosys /build/yosys +COPY --from=builder /yosys-abc /build/yosys-abc +COPY --from=builder /yosys-config /build/yosys-config +COPY --from=builder /yosys-filterlib /build/yosys-filterlib +COPY --from=builder /yosys-smtbmc /build/yosys-smtbmc + +ENV PATH /build:$PATH +RUN useradd -m yosys +USER yosys +ENTRYPOINT ["yosys"] @@ -46,6 +46,10 @@ OS := $(shell uname -s) PREFIX ?= /usr/local INSTALL_SUDO := +ifneq ($(wildcard Makefile.conf),) +include Makefile.conf +endif + BINDIR := $(PREFIX)/bin LIBDIR := $(PREFIX)/lib DATDIR := $(PREFIX)/share/yosys @@ -118,7 +122,7 @@ OBJS = kernel/version_$(GIT_REV).o # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = 3709744 +ABCREV = 62487de ABCPULL = 1 ABCURL ?= https://github.com/berkeley-abc/abc ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 @@ -662,6 +666,12 @@ else SEEDOPT="" endif +ifneq ($(ABCEXTERNAL),) +ABCOPT="-A $(ABCEXTERNAL)" +else +ABCOPT="" +endif + test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/hana && bash run-test.sh $(SEEDOPT) @@ -670,13 +680,15 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/share && bash run-test.sh $(SEEDOPT) +cd tests/fsm && bash run-test.sh $(SEEDOPT) +cd tests/techmap && bash run-test.sh - +cd tests/memories && bash run-test.sh $(SEEDOPT) + +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT) +cd tests/bram && bash run-test.sh $(SEEDOPT) +cd tests/various && bash run-test.sh +cd tests/sat && bash run-test.sh +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) +cd tests/opt && bash run-test.sh - +cd tests/aiger && bash run-test.sh + +cd tests/aiger && bash run-test.sh $(ABCOPT) + +cd tests/arch && bash run-test.sh + +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) @echo "" @echo " Passed \"make test\"." @echo "" @@ -78,7 +78,7 @@ Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies On FreeBSD use the following command to install all prerequisites: # pkg install bison flex readline gawk libffi\ - git graphviz pkgconfig python3 python36 tcl-wrapper boost-libs + git graphviz pkgconf python3 python36 tcl-wrapper boost-libs On FreeBSD system use gmake instead of make. To run tests use: % MAKE=gmake CC=cc gmake test @@ -350,6 +350,14 @@ Verilog Attributes and non-standard features through the synthesis. When entities are combined, a new |-separated string is created that contains all the string from the original entities. +- The ``defaultvalue`` attribute is used to store default values for + module inputs. The attribute is attached to the input wire by the HDL + front-end when the input is declared with a default value. + +- The ``parameter`` and ``localparam`` attributes are used to mark wires + that represent module parameters or localparams (when the HDL front-end + is run in -pwires mode). + - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset @@ -413,7 +421,7 @@ Verilog Attributes and non-standard features $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v' - Sized constants (the syntax ``<size>'s?[bodh]<value>``) support constant - expressions as <size>. If the expression is not a simple identifier, it + expressions as ``<size>``. If the expression is not a simple identifier, it must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010`` - The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in diff --git a/backends/aiger/Makefile.inc b/backends/aiger/Makefile.inc index 0fc37e95c..4a4cf30bd 100644 --- a/backends/aiger/Makefile.inc +++ b/backends/aiger/Makefile.inc @@ -1,3 +1,4 @@ OBJS += backends/aiger/aiger.o +OBJS += backends/aiger/xaiger.o diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index dfe506c66..7c851bb91 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -70,34 +70,35 @@ struct AigerWriter int bit2aig(SigBit bit) { - if (aig_map.count(bit) == 0) - { - aig_map[bit] = -1; - - if (initstate_bits.count(bit)) { - log_assert(initstate_ff > 0); - aig_map[bit] = initstate_ff; - } else - if (not_map.count(bit)) { - int a = bit2aig(not_map.at(bit)) ^ 1; - aig_map[bit] = a; - } else - if (and_map.count(bit)) { - auto args = and_map.at(bit); - int a0 = bit2aig(args.first); - int a1 = bit2aig(args.second); - aig_map[bit] = mkgate(a0, a1); - } else - if (alias_map.count(bit)) { - aig_map[bit] = bit2aig(alias_map.at(bit)); - } + auto it = aig_map.find(bit); + if (it != aig_map.end()) { + log_assert(it->second >= 0); + return it->second; + } - if (bit == State::Sx || bit == State::Sz) - log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); + // NB: Cannot use iterator returned from aig_map.insert() + // since this function is called recursively + + int a = -1; + if (not_map.count(bit)) { + a = bit2aig(not_map.at(bit)) ^ 1; + } else + if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + a = mkgate(a0, a1); + } else + if (alias_map.count(bit)) { + a = bit2aig(alias_map.at(bit)); } - log_assert(aig_map.at(bit) >= 0); - return aig_map.at(bit); + if (bit == State::Sx || bit == State::Sz) + log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); + + log_assert(a >= 0); + aig_map[bit] = a; + return a; } AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module) @@ -685,7 +686,7 @@ struct AigerBackend : public Backend { log("invariant constraints.\n"); log("\n"); log(" -ascii\n"); - log(" write ASCII version of AGIER format\n"); + log(" write ASCII version of AIGER format\n"); log("\n"); log(" -zinit\n"); log(" convert FFs to zero-initialized FFs, adding additional inputs for\n"); @@ -776,6 +777,7 @@ struct AigerBackend : public Backend { writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); if (!map_filename.empty()) { + rewrite_filename(filename); std::ofstream mapf; mapf.open(map_filename.c_str(), std::ofstream::trunc); if (mapf.fail()) diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc new file mode 100644 index 000000000..69f63486c --- /dev/null +++ b/backends/aiger/xaiger.cc @@ -0,0 +1,878 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// https://stackoverflow.com/a/46137633 +#ifdef _MSC_VER +#include <stdlib.h> +#define bswap32 _byteswap_ulong +#elif defined(__APPLE__) +#include <libkern/OSByteOrder.h> +#define bswap32 OSSwapInt32 +#elif defined(__GNUC__) +#define bswap32 __builtin_bswap32 +#else +#include <cstdint> +inline static uint32_t bswap32(uint32_t x) +{ + // https://stackoverflow.com/a/27796212 + register uint32_t value = number_to_be_reversed; + uint8_t lolo = (value >> 0) & 0xFF; + uint8_t lohi = (value >> 8) & 0xFF; + uint8_t hilo = (value >> 16) & 0xFF; + uint8_t hihi = (value >> 24) & 0xFF; + return (hihi << 24) + | (hilo << 16) + | (lohi << 8) + | (lolo << 0); +} +#endif + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +inline int32_t to_big_endian(int32_t i32) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return __builtin_bswap32(i32); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return i32; +#else +#error "Unknown endianness" +#endif +} + +void aiger_encode(std::ostream &f, int x) +{ + log_assert(x >= 0); + + while (x & ~0x7f) { + f.put((x & 0x7f) | 0x80); + x = x >> 7; + } + + f.put(x); +} + +struct XAigerWriter +{ + Module *module; + SigMap sigmap; + + pool<SigBit> input_bits, output_bits; + dict<SigBit, SigBit> not_map, alias_map; + dict<SigBit, pair<SigBit, SigBit>> and_map; + vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; + vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; + + vector<pair<int, int>> aig_gates; + vector<int> aig_outputs; + int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; + + dict<SigBit, int> aig_map; + dict<SigBit, int> ordered_outputs; + + vector<Cell*> box_list; + bool omode = false; + + int mkgate(int a0, int a1) + { + aig_m++, aig_a++; + aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); + return 2*aig_m; + } + + int bit2aig(SigBit bit) + { + auto it = aig_map.find(bit); + if (it != aig_map.end()) { + log_assert(it->second >= 0); + return it->second; + } + + // NB: Cannot use iterator returned from aig_map.insert() + // since this function is called recursively + + int a = -1; + if (not_map.count(bit)) { + a = bit2aig(not_map.at(bit)) ^ 1; + } else + if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + a = mkgate(a0, a1); + } else + if (alias_map.count(bit)) { + a = bit2aig(alias_map.at(bit)); + } + + if (bit == State::Sx || bit == State::Sz) { + log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n"); + a = aig_map.at(State::S0); + } + + log_assert(a >= 0); + aig_map[bit] = a; + return a; + } + + XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module) + { + pool<SigBit> undriven_bits; + pool<SigBit> unused_bits; + pool<SigBit> keep_bits; + + // promote public wires + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + sigmap.add(wire); + + // promote input wires + for (auto wire : module->wires()) + if (wire->port_input) + sigmap.add(wire); + + // promote output wires + for (auto wire : module->wires()) + if (wire->port_output) + sigmap.add(wire); + + for (auto wire : module->wires()) + { + bool keep = wire->attributes.count("\\keep"); + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wirebit(wire, i); + SigBit bit = sigmap(wirebit); + + if (bit.wire) { + undriven_bits.insert(bit); + unused_bits.insert(bit); + } + + if (keep) + keep_bits.insert(bit); + + if (wire->port_input || keep) { + if (bit != wirebit) + alias_map[bit] = wirebit; + input_bits.insert(wirebit); + } + + if (wire->port_output || keep) { + if (bit != RTLIL::Sx) { + if (bit != wirebit) + alias_map[wirebit] = bit; + output_bits.insert(wirebit); + } + else + log_debug("Skipping PO '%s' driven by 1'bx\n", log_signal(wirebit)); + } + } + } + + for (auto bit : input_bits) + undriven_bits.erase(sigmap(bit)); + for (auto bit : output_bits) + if (!bit.wire->port_input) + unused_bits.erase(bit); + + // TODO: Speed up toposort -- ultimately we care about + // box ordering, but not individual AIG cells + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + bool abc_box_seen = false; + + for (auto cell : module->selected_cells()) { + if (cell->type == "$_NOT_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + undriven_bits.erase(Y); + not_map[Y] = A; + if (!holes_mode) { + toposort.node(cell->name); + bit_users[A].insert(cell->name); + bit_drivers[Y].insert(cell->name); + } + continue; + } + + if (cell->type == "$_AND_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit B = sigmap(cell->getPort("\\B").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + unused_bits.erase(B); + undriven_bits.erase(Y); + and_map[Y] = make_pair(A, B); + if (!holes_mode) { + toposort.node(cell->name); + bit_users[A].insert(cell->name); + bit_users[B].insert(cell->name); + bit_drivers[Y].insert(cell->name); + } + continue; + } + + log_assert(!holes_mode); + + RTLIL::Module* inst_module = module->design->module(cell->type); + if (inst_module && inst_module->attributes.count("\\abc_box_id")) { + abc_box_seen = true; + + if (!holes_mode) { + toposort.node(cell->name); + for (const auto &conn : cell->connections()) { + if (cell->input(conn.first)) { + // Ignore inout for the sake of topographical ordering + if (cell->output(conn.first)) continue; + for (auto bit : sigmap(conn.second)) + bit_users[bit].insert(cell->name); + } + + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_drivers[bit].insert(cell->name); + } + } + } + else { + bool cell_known = cell->known(); + for (const auto &c : cell->connections()) { + if (c.second.is_fully_const()) continue; + auto is_input = !cell_known || cell->input(c.first); + auto is_output = !cell_known || cell->output(c.first); + if (!is_input && !is_output) + log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); + + if (is_input) { + for (auto b : c.second.bits()) { + Wire *w = b.wire; + if (!w) continue; + if (!w->port_output || !cell_known) { + SigBit I = sigmap(b); + if (I != b) + alias_map[b] = I; + output_bits.insert(b); + unused_bits.erase(b); + + if (!cell_known) + keep_bits.insert(b); + } + } + } + if (is_output) { + for (auto b : c.second.bits()) { + Wire *w = b.wire; + if (!w) continue; + input_bits.insert(b); + SigBit O = sigmap(b); + if (O != b) + alias_map[O] = b; + undriven_bits.erase(O); + } + } + } + } + + //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + } + + if (abc_box_seen) { + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + +#if 0 + toposort.analyze_loops = true; +#endif + bool no_loops = toposort.sort(); +#if 0 + unsigned i = 0; + for (auto &it : toposort.loops) { + log(" loop %d\n", i++); + for (auto cell_name : it) { + auto cell = module->cell(cell_name); + log_assert(cell); + log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); + } + } +#endif + log_assert(no_loops); + + pool<IdString> seen_boxes; + for (auto cell_name : toposort.sorted) { + RTLIL::Cell *cell = module->cell(cell_name); + log_assert(cell); + + RTLIL::Module* box_module = module->design->module(cell->type); + if (!box_module || !box_module->attributes.count("\\abc_box_id")) + continue; + + if (seen_boxes.insert(cell->type).second) { + auto it = box_module->attributes.find("\\abc_carry"); + if (it != box_module->attributes.end()) { + RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; + auto carry_in_out = it->second.decode_string(); + auto pos = carry_in_out.find(','); + if (pos == std::string::npos) + log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type)); + auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos)); + carry_in = box_module->wire(carry_in_name); + if (!carry_in || !carry_in->port_input) + log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str()); + + auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1)); + carry_out = box_module->wire(carry_out_name); + if (!carry_out || !carry_out->port_output) + log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str()); + + auto &ports = box_module->ports; + for (auto jt = ports.begin(); jt != ports.end(); ) { + RTLIL::Wire* w = box_module->wire(*jt); + log_assert(w); + if (w == carry_in || w == carry_out) { + jt = ports.erase(jt); + continue; + } + if (w->port_id > carry_in->port_id) + --w->port_id; + if (w->port_id > carry_out->port_id) + --w->port_id; + log_assert(w->port_input || w->port_output); + log_assert(ports[w->port_id-1] == w->name); + ++jt; + } + ports.push_back(carry_in->name); + carry_in->port_id = ports.size(); + ports.push_back(carry_out->name); + carry_out->port_id = ports.size(); + } + } + + // Fully pad all unused input connections of this box cell with S0 + // Fully pad all undriven output connections of this box cell with anonymous wires + // NB: Assume box_module->ports are sorted alphabetically + // (as RTLIL::Module::fixup_ports() would do) + for (const auto &port_name : box_module->ports) { + RTLIL::Wire* w = box_module->wire(port_name); + log_assert(w); + auto it = cell->connections_.find(port_name); + if (w->port_input) { + RTLIL::SigSpec rhs; + if (it != cell->connections_.end()) { + if (GetSize(it->second) < GetSize(w)) + it->second.append(RTLIL::SigSpec(RTLIL::S0, GetSize(w)-GetSize(it->second))); + rhs = it->second; + } + else { + rhs = RTLIL::SigSpec(RTLIL::S0, GetSize(w)); + cell->setPort(port_name, rhs); + } + + int offset = 0; + for (auto b : rhs.bits()) { + SigBit I = sigmap(b); + if (b == RTLIL::Sx) + b = RTLIL::S0; + else if (I != b) { + if (I == RTLIL::Sx) + alias_map[b] = RTLIL::S0; + else + alias_map[b] = I; + } + co_bits.emplace_back(b, cell, port_name, offset++, 0); + unused_bits.erase(b); + } + } + if (w->port_output) { + RTLIL::SigSpec rhs; + auto it = cell->connections_.find(w->name); + if (it != cell->connections_.end()) { + if (GetSize(it->second) < GetSize(w)) + it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second))); + rhs = it->second; + } + else { + rhs = module->addWire(NEW_ID, GetSize(w)); + cell->setPort(port_name, rhs); + } + + int offset = 0; + for (const auto &b : rhs.bits()) { + ci_bits.emplace_back(b, cell, port_name, offset++); + SigBit O = sigmap(b); + if (O != b) + alias_map[O] = b; + undriven_bits.erase(O); + + auto jt = input_bits.find(b); + if (jt != input_bits.end()) { + log_assert(keep_bits.count(O)); + input_bits.erase(b); + } + } + } + } + box_list.emplace_back(cell); + } + + // TODO: Free memory from toposort, bit_drivers, bit_users + } + + for (auto bit : input_bits) { + if (!output_bits.count(bit)) + continue; + RTLIL::Wire *wire = bit.wire; + // If encountering an inout port, or a keep-ed wire, then create a new wire + // with $inout.out suffix, make it a PO driven by the existing inout, and + // inherit existing inout's drivers + if ((wire->port_input && wire->port_output && !undriven_bits.count(bit)) + || keep_bits.count(bit)) { + RTLIL::IdString wire_name = wire->name.str() + "$inout.out"; + RTLIL::Wire *new_wire = module->wire(wire_name); + if (!new_wire) + new_wire = module->addWire(wire_name, GetSize(wire)); + SigBit new_bit(new_wire, bit.offset); + module->connect(new_bit, bit); + if (not_map.count(bit)) { + auto a = not_map.at(bit); + not_map[new_bit] = a; + } + else if (and_map.count(bit)) { + auto a = and_map.at(bit); + and_map[new_bit] = a; + } + else if (alias_map.count(bit)) { + auto a = alias_map.at(bit); + alias_map[new_bit] = a; + } + else + alias_map[new_bit] = bit; + output_bits.erase(bit); + output_bits.insert(new_bit); + } + } + + for (auto bit : unused_bits) + undriven_bits.erase(bit); + + if (!undriven_bits.empty() && !holes_mode) { + undriven_bits.sort(); + for (auto bit : undriven_bits) { + log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); + input_bits.insert(bit); + } + log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); + } + + if (holes_mode) { + struct sort_by_port_id { + bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { + return a.wire->port_id < b.wire->port_id; + } + }; + input_bits.sort(sort_by_port_id()); + output_bits.sort(sort_by_port_id()); + } + else { + input_bits.sort(); + output_bits.sort(); + } + + not_map.sort(); + and_map.sort(); + + aig_map[State::S0] = 0; + aig_map[State::S1] = 1; + + for (auto bit : input_bits) { + aig_m++, aig_i++; + log_assert(!aig_map.count(bit)); + aig_map[bit] = 2*aig_m; + } + + for (auto &c : ci_bits) { + RTLIL::SigBit bit = std::get<0>(c); + aig_m++, aig_i++; + aig_map[bit] = 2*aig_m; + } + + for (auto &c : co_bits) { + RTLIL::SigBit bit = std::get<0>(c); + std::get<4>(c) = ordered_outputs[bit] = aig_o++; + aig_outputs.push_back(bit2aig(bit)); + } + + for (auto bit : output_bits) { + ordered_outputs[bit] = aig_o++; + aig_outputs.push_back(bit2aig(bit)); + } + + if (output_bits.empty()) { + aig_o++; + aig_outputs.push_back(0); + omode = true; + } + } + + void write_aiger(std::ostream &f, bool ascii_mode) + { + int aig_obc = aig_o; + int aig_obcj = aig_obc; + int aig_obcjf = aig_obcj; + + log_assert(aig_m == aig_i + aig_l + aig_a); + log_assert(aig_obcjf == GetSize(aig_outputs)); + + f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); + f << stringf("\n"); + + if (ascii_mode) + { + for (int i = 0; i < aig_i; i++) + f << stringf("%d\n", 2*i+2); + + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) + f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); + } + else + { + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) { + int lhs = 2*(aig_i+aig_l+i)+2; + int rhs0 = aig_gates.at(i).first; + int rhs1 = aig_gates.at(i).second; + int delta0 = lhs - rhs0; + int delta1 = rhs0 - rhs1; + aiger_encode(f, delta0); + aiger_encode(f, delta1); + } + } + + f << "c"; + + if (!box_list.empty()) { + auto write_buffer = [](std::stringstream &buffer, int i32) { + int32_t i32_be = to_big_endian(i32); + buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); + }; + + std::stringstream h_buffer; + auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); + write_h_buffer(1); + log_debug("ciNum = %zu\n", input_bits.size() + ci_bits.size()); + write_h_buffer(input_bits.size() + ci_bits.size()); + log_debug("coNum = %zu\n", output_bits.size() + co_bits.size()); + write_h_buffer(output_bits.size() + co_bits.size()); + log_debug("piNum = %zu\n", input_bits.size()); + write_h_buffer(input_bits.size()); + log_debug("poNum = %zu\n", output_bits.size()); + write_h_buffer(output_bits.size()); + log_debug("boxNum = %zu\n", box_list.size()); + write_h_buffer(box_list.size()); + + RTLIL::Module *holes_module = nullptr; + holes_module = module->design->addModule("$__holes__"); + log_assert(holes_module); + + int port_id = 1; + int box_count = 0; + for (auto cell : box_list) { + RTLIL::Module* box_module = module->design->module(cell->type); + int box_inputs = 0, box_outputs = 0; + Cell *holes_cell = nullptr; + if (box_module->get_bool_attribute("\\whitebox")) { + holes_cell = holes_module->addCell(cell->name, cell->type); + holes_cell->parameters = cell->parameters; + } + + // NB: Assume box_module->ports are sorted alphabetically + // (as RTLIL::Module::fixup_ports() would do) + for (const auto &port_name : box_module->ports) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + RTLIL::Wire *holes_wire; + RTLIL::SigSpec port_wire; + if (w->port_input) { + for (int i = 0; i < GetSize(w); i++) { + box_inputs++; + holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); + if (!holes_wire) { + holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); + holes_wire->port_input = true; + holes_wire->port_id = port_id++; + holes_module->ports.push_back(holes_wire->name); + } + if (holes_cell) + port_wire.append(holes_wire); + } + if (!port_wire.empty()) + holes_cell->setPort(w->name, port_wire); + } + if (w->port_output) { + box_outputs += GetSize(w); + for (int i = 0; i < GetSize(w); i++) { + if (GetSize(w) == 1) + holes_wire = holes_module->addWire(stringf("%s.%s", cell->name.c_str(), w->name.c_str())); + else + holes_wire = holes_module->addWire(stringf("%s.%s[%d]", cell->name.c_str(), w->name.c_str(), i)); + holes_wire->port_output = true; + holes_wire->port_id = port_id++; + holes_module->ports.push_back(holes_wire->name); + if (holes_cell) + port_wire.append(holes_wire); + else + holes_module->connect(holes_wire, RTLIL::S0); + } + if (!port_wire.empty()) + holes_cell->setPort(w->name, port_wire); + } + } + + write_h_buffer(box_inputs); + write_h_buffer(box_outputs); + write_h_buffer(box_module->attributes.at("\\abc_box_id").as_int()); + write_h_buffer(box_count++); + } + + f << "h"; + std::string buffer_str = h_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + + std::stringstream r_buffer; + auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); + write_r_buffer(0); + + f << "r"; + buffer_str = r_buffer.str(); + buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + + if (holes_module) { + log_push(); + + // NB: fixup_ports() will sort ports by name + //holes_module->fixup_ports(); + holes_module->check(); + + holes_module->design->selection_stack.emplace_back(false); + RTLIL::Selection& sel = holes_module->design->selection_stack.back(); + sel.select(holes_module); + + // TODO: Should not need to opt_merge if we only instantiate + // each box type once... + Pass::call(holes_module->design, "opt_merge -share_all"); + + Pass::call(holes_module->design, "flatten -wb"); + + // TODO: Should techmap/aigmap/check all lib_whitebox-es just once, + // instead of per write_xaiger call + Pass::call(holes_module->design, "techmap"); + Pass::call(holes_module->design, "aigmap"); + for (auto cell : holes_module->cells()) + if (!cell->type.in("$_NOT_", "$_AND_")) + log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n"); + + Pass::call(holes_module->design, "clean -purge"); + + std::stringstream a_buffer; + XAigerWriter writer(holes_module, true /* holes_mode */); + writer.write_aiger(a_buffer, false /*ascii_mode*/); + + holes_module->design->selection_stack.pop_back(); + + f << "a"; + std::string buffer_str = a_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + holes_module->design->remove(holes_module); + + log_pop(); + } + } + + f << stringf("Generated by %s\n", yosys_version_str); + } + + void write_map(std::ostream &f, bool verbose_map) + { + dict<int, string> input_lines; + dict<int, string> output_lines; + dict<int, string> wire_lines; + + for (auto wire : module->wires()) + { + //if (!verbose_map && wire->name[0] == '$') + // continue; + + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(wire); i++) + { + RTLIL::SigBit b(wire, i); + if (input_bits.count(b)) { + int a = aig_map.at(b); + log_assert((a & 1) == 0); + input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); + } + + if (output_bits.count(b)) { + int o = ordered_outputs.at(b); + output_lines[o] += stringf("output %lu %d %s\n", o - co_bits.size(), i, log_id(wire)); + continue; + } + + if (verbose_map) { + if (aig_map.count(sig[i]) == 0) + continue; + + int a = aig_map.at(sig[i]); + wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire)); + } + } + } + + input_lines.sort(); + for (auto &it : input_lines) + f << it.second; + log_assert(input_lines.size() == input_bits.size()); + + int box_count = 0; + for (auto cell : box_list) + f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); + + output_lines.sort(); + for (auto &it : output_lines) + f << it.second; + log_assert(output_lines.size() == output_bits.size()); + if (omode && output_bits.empty()) + f << "output " << output_lines.size() << " 0 $__dummy__\n"; + + wire_lines.sort(); + for (auto &it : wire_lines) + f << it.second; + } +}; + +struct XAigerBackend : public Backend { + XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_xaiger [options] [filename]\n"); + log("\n"); + log("Write the current design to an XAIGER file. The design must be flattened and\n"); + log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\n"); + log("\n"); + log(" -ascii\n"); + log(" write ASCII version of AIGER format\n"); + log("\n"); + log(" -map <filename>\n"); + log(" write an extra file with port and latch symbols\n"); + log("\n"); + log(" -vmap <filename>\n"); + log(" like -map, but more verbose\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool ascii_mode = false; + bool verbose_map = false; + std::string map_filename; + + log_header(design, "Executing XAIGER backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ascii") { + ascii_mode = true; + continue; + } + if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + verbose_map = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + Module *top_module = design->top_module(); + + if (top_module == nullptr) + log_error("Can't find top module in current design!\n"); + + XAigerWriter writer(top_module); + writer.write_aiger(*f, ascii_mode); + + if (!map_filename.empty()) { + std::ofstream mapf; + mapf.open(map_filename.c_str(), std::ofstream::trunc); + if (mapf.fail()) + log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); + writer.write_map(mapf, verbose_map); + } + } +} XAigerBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 511a11942..a507b120b 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -17,6 +17,11 @@ * */ +// [[CITE]] Btor2 , BtorMC and Boolector 3.0 +// Aina Niemetz, Mathias Preiner, Clifford Wolf, Armin Biere +// Computer Aided Verification - 30th International Conference, CAV 2018 +// https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/ + #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" @@ -875,9 +880,28 @@ struct BtorWorker else { if (bit_cell.count(bit) == 0) - log_error("No driver for signal bit %s.\n", log_signal(bit)); - export_cell(bit_cell.at(bit)); - log_assert(bit_nid.count(bit)); + { + SigSpec s = bit; + + while (i+GetSize(s) < GetSize(sig) && sig[i+GetSize(s)].wire != nullptr && + bit_cell.count(sig[i+GetSize(s)]) == 0) + s.append(sig[i+GetSize(s)]); + + log_warning("No driver for signal %s.\n", log_signal(s)); + + int sid = get_bv_sid(GetSize(s)); + int nid = next_nid++; + btorf("%d input %d %s\n", nid, sid); + nid_width[nid] = GetSize(s); + + i += GetSize(s)-1; + continue; + } + else + { + export_cell(bit_cell.at(bit)); + log_assert(bit_nid.count(bit)); + } } } diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index 04d1ee311..313af7d5c 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -204,6 +204,11 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) { + f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str()); + dump_const(f, ait->second); + f << stringf("\n"); + } f << stringf("%s case ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) @@ -483,6 +488,7 @@ struct DumpPass : public Pass { std::stringstream buf; if (!filename.empty()) { + rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc); if (ff->fail()) { diff --git a/backends/json/json.cc b/backends/json/json.cc index f5c687981..dda4dfedd 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -126,6 +126,10 @@ struct JsonWriter f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(n).c_str()); f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); + if (w->start_offset) + f << stringf(" \"offset\": %d,\n", w->start_offset); + if (w->upto) + f << stringf(" \"upto\": 1,\n"); f << stringf(" \"bits\": %s\n", get_bits(w).c_str()); f << stringf(" }"); first = false; @@ -189,6 +193,10 @@ struct JsonWriter f << stringf(" %s: {\n", get_name(w->name).c_str()); f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); + if (w->start_offset) + f << stringf(" \"offset\": %d,\n", w->start_offset); + if (w->upto) + f << stringf(" \"upto\": 1,\n"); f << stringf(" \"attributes\": {"); write_parameters(w->attributes); f << stringf("\n }\n"); @@ -525,6 +533,7 @@ struct JsonPass : public Pass { std::stringstream buf; if (!filename.empty()) { + rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); if (ff->fail()) { diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc index 549fc73ae..fff110bb0 100644 --- a/backends/protobuf/protobuf.cc +++ b/backends/protobuf/protobuf.cc @@ -336,6 +336,7 @@ struct ProtobufPass : public Pass { std::stringstream buf; if (!filename.empty()) { + rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); if (ff->fail()) { diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index ab20a4af2..bac68ac70 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -43,7 +43,11 @@ if os.name == "posix": if current_rlimit_stack[1] != resource.RLIM_INFINITY: smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1]) if current_rlimit_stack[0] < smtio_stacksize: - resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1])) + try: + resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1])) + except ValueError: + # couldn't get more stack, just run with what we have + pass # currently running solvers (so we can kill them) @@ -1023,6 +1027,8 @@ class MkVcd: assert t >= self.t if t != self.t: if self.t == -1: + print("$version Generated by Yosys-SMTBMC $end", file=self.f) + print("$timescale 1ns $end", file=self.f) print("$var integer 32 t smt_step $end", file=self.f) print("$var event 1 ! smt_clock $end", file=self.f) @@ -1041,7 +1047,10 @@ class MkVcd: scope = scope[:-1] while uipath[:-1] != scope: - print("$scope module %s $end" % uipath[len(scope)], file=self.f) + scopename = uipath[len(scope)] + if scopename.startswith("$"): + scopename = "\\" + scopename + print("$scope module %s $end" % scopename, file=self.f) scope.append(uipath[len(scope)]) if path in self.clocks and self.clocks[path][1] == "event": diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 827af5d85..e0b3a6f80 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -189,7 +189,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o if (width < 0) width = data.bits.size() - offset; if (width == 0) { - f << "\"\""; + // See IEEE 1364-2005 Clause 5.1.14. + f << "{0{1'b0}}"; return; } if (nostr) @@ -222,7 +223,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o case RTLIL::S1: bin_digits.push_back('1'); break; case RTLIL::Sx: bin_digits.push_back('x'); break; case RTLIL::Sz: bin_digits.push_back('z'); break; - case RTLIL::Sa: bin_digits.push_back('z'); break; + case RTLIL::Sa: bin_digits.push_back('?'); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } @@ -251,6 +252,12 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o hex_digits.push_back('z'); continue; } + if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') { + if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?') + goto dump_bin; + hex_digits.push_back('?'); + continue; + } int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); } @@ -270,7 +277,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o case RTLIL::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sz: f << stringf("z"); break; - case RTLIL::Sa: f << stringf("z"); break; + case RTLIL::Sa: f << stringf("?"); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } @@ -364,20 +371,22 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false) +void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool as_comment = false) { if (noattr) return; + if (attr2comment) + as_comment = true; for (auto it = attributes.begin(); it != attributes.end(); ++it) { - f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); + f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str()); f << stringf(" = "); if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) f << stringf(" 0 "); else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) f << stringf(" 1 "); else - dump_const(f, it->second, -1, 0, false, attr2comment); - f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); + dump_const(f, it->second, -1, 0, false, as_comment); + f << stringf(" %s%c", as_comment ? "*/" : "*)", term); } } @@ -780,7 +789,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type == "$pmux" || cell->type == "$pmux_safe") + if (cell->type == "$pmux") { int width = cell->parameters["\\WIDTH"].as_int(); int s_width = cell->getPort("\\S").size(); @@ -792,18 +801,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); dump_attributes(f, indent + " ", cell->attributes); - if (cell->type != "$pmux_safe" && !noattr) + if (!noattr) f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); f << stringf("%s" " casez (s)", indent.c_str()); - if (cell->type != "$pmux_safe") - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); for (int i = 0; i < s_width; i++) { f << stringf("%s" " %d'b", indent.c_str(), s_width); for (int j = s_width-1; j >= 0; j--) - f << stringf("%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?'); + f << stringf("%c", j == i ? '1' : '?'); f << stringf(":\n"); f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); @@ -1492,12 +1500,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw return; } + dump_attributes(f, indent, sw->attributes); f << stringf("%s" "casez (", indent.c_str()); dump_sigspec(f, sw->signal); f << stringf(")\n"); bool got_default = false; for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*as_comment=*/true); if ((*it)->compare.size() == 0) { if (got_default) continue; @@ -1662,7 +1672,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } - dump_attributes(f, indent, module->attributes, '\n', true); + dump_attributes(f, indent, module->attributes, '\n', /*attr2comment=*/true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; for (int port_id = 1; keep_running; port_id++) { diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 38348cd65..03c541b7c 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -2,7 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * Eddie Hung <eddie@fpgeh.com> + * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,366 +22,1019 @@ // Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria. // http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf -#ifndef _WIN32 -#include <libgen.h> +// https://stackoverflow.com/a/46137633 +#ifdef _MSC_VER +#include <stdlib.h> +#define __builtin_bswap32 _byteswap_ulong +#elif defined(__APPLE__) +#include <libkern/OSByteOrder.h> +#define __builtin_bswap32 OSSwapInt32 #endif -#include <array> +#include <inttypes.h> #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/celltypes.h" #include "aigerparse.h" YOSYS_NAMESPACE_BEGIN -AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name) - : design(design), f(f), clk_name(clk_name) +inline int32_t from_big_endian(int32_t i32) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return __builtin_bswap32(i32); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return i32; +#else +#error "Unknown endianness" +#endif +} + +#define log_debug2(...) ; +//#define log_debug2(...) log_debug(__VA_ARGS__) + +struct ConstEvalAig +{ + RTLIL::Module *module; + dict<RTLIL::SigBit, RTLIL::State> values_map; + dict<RTLIL::SigBit, RTLIL::Cell*> sig2driver; + dict<SigBit, pool<RTLIL::SigBit>> sig2deps; + + ConstEvalAig(RTLIL::Module *module) : module(module) + { + for (auto &it : module->cells_) { + if (!yosys_celltypes.cell_known(it.second->type)) + continue; + for (auto &it2 : it.second->connections()) + if (yosys_celltypes.cell_output(it.second->type, it2.first)) { + auto r = sig2driver.insert(std::make_pair(it2.second, it.second)); + log_assert(r.second); + } + } + } + + void clear() + { + values_map.clear(); + sig2deps.clear(); + } + + void set(RTLIL::SigBit sig, RTLIL::State value) + { + auto it = values_map.find(sig); +#ifndef NDEBUG + if (it != values_map.end()) { + RTLIL::State current_val = it->second; + log_assert(current_val == value); + } +#endif + if (it != values_map.end()) + it->second = value; + else + values_map[sig] = value; + } + + void set_incremental(RTLIL::SigSpec sig, RTLIL::Const value) + { + log_assert(GetSize(sig) == GetSize(value)); + + for (int i = 0; i < GetSize(sig); i++) { + auto it = values_map.find(sig[i]); + if (it != values_map.end()) { + RTLIL::State current_val = it->second; + if (current_val != value[i]) + for (auto dep : sig2deps[sig[i]]) + values_map.erase(dep); + it->second = value[i]; + } + else + values_map[sig[i]] = value[i]; + } + } + + void compute_deps(RTLIL::SigBit output, const pool<RTLIL::SigBit> &inputs) + { + sig2deps[output].insert(output); + + RTLIL::Cell *cell = sig2driver.at(output); + RTLIL::SigBit sig_a = cell->getPort("\\A"); + sig2deps[sig_a].reserve(sig2deps[sig_a].size() + sig2deps[output].size()); // Reserve so that any invalidation + // that may occur does so here, and + // not mid insertion (below) + sig2deps[sig_a].insert(sig2deps[output].begin(), sig2deps[output].end()); + if (!inputs.count(sig_a)) + compute_deps(sig_a, inputs); + + if (cell->type == "$_AND_") { + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + sig2deps[sig_b].reserve(sig2deps[sig_b].size() + sig2deps[output].size()); // Reserve so that any invalidation + // that may occur does so here, and + // not mid insertion (below) + sig2deps[sig_b].insert(sig2deps[output].begin(), sig2deps[output].end()); + + if (!inputs.count(sig_b)) + compute_deps(sig_b, inputs); + } + else if (cell->type == "$_NOT_") { + } + else log_abort(); + } + + bool eval(RTLIL::Cell *cell) + { + RTLIL::SigBit sig_y = cell->getPort("\\Y"); + if (values_map.count(sig_y)) + return true; + + RTLIL::SigBit sig_a = cell->getPort("\\A"); + if (!eval(sig_a)) + return false; + + RTLIL::State eval_ret = RTLIL::Sx; + if (cell->type == "$_NOT_") { + if (sig_a == RTLIL::S0) eval_ret = RTLIL::S1; + else if (sig_a == RTLIL::S1) eval_ret = RTLIL::S0; + } + else if (cell->type == "$_AND_") { + if (sig_a == RTLIL::S0) { + eval_ret = RTLIL::S0; + goto eval_end; + } + + { + RTLIL::SigBit sig_b = cell->getPort("\\B"); + if (!eval(sig_b)) + return false; + if (sig_b == RTLIL::S0) { + eval_ret = RTLIL::S0; + goto eval_end; + } + + if (sig_a != RTLIL::S1 || sig_b != RTLIL::S1) + goto eval_end; + + eval_ret = RTLIL::S1; + } + } + else log_abort(); + +eval_end: + set(sig_y, eval_ret); + return true; + } + + bool eval(RTLIL::SigBit &sig) + { + auto it = values_map.find(sig); + if (it != values_map.end()) { + sig = it->second; + return true; + } + + RTLIL::Cell *cell = sig2driver.at(sig); + if (!eval(cell)) + return false; + + it = values_map.find(sig); + if (it != values_map.end()) { + sig = it->second; + return true; + } + + return false; + } +}; + +AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports) + : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports) { - module = new RTLIL::Module; - module->name = module_name; - if (design->module(module->name)) - log_error("Duplicate definition of module %s!\n", log_id(module->name)); + module = new RTLIL::Module; + module->name = module_name; + if (design->module(module->name)) + log_error("Duplicate definition of module %s!\n", log_id(module->name)); } void AigerReader::parse_aiger() { - std::string header; - f >> header; - if (header != "aag" && header != "aig") - log_error("Unsupported AIGER file!\n"); - - // Parse rest of header - if (!(f >> M >> I >> L >> O >> A)) - log_error("Invalid AIGER header\n"); - - // Optional values - B = C = J = F = 0; - if (f.peek() != ' ') goto end_of_header; - if (!(f >> B)) log_error("Invalid AIGER header\n"); - if (f.peek() != ' ') goto end_of_header; - if (!(f >> C)) log_error("Invalid AIGER header\n"); - if (f.peek() != ' ') goto end_of_header; - if (!(f >> J)) log_error("Invalid AIGER header\n"); - if (f.peek() != ' ') goto end_of_header; - if (!(f >> F)) log_error("Invalid AIGER header\n"); + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + if (f.peek() != ' ') goto end_of_header; + if (!(f >> B)) log_error("Invalid AIGER header\n"); + if (f.peek() != ' ') goto end_of_header; + if (!(f >> C)) log_error("Invalid AIGER header\n"); + if (f.peek() != ' ') goto end_of_header; + if (!(f >> J)) log_error("Invalid AIGER header\n"); + if (f.peek() != ' ') goto end_of_header; + if (!(f >> F)) log_error("Invalid AIGER header\n"); end_of_header: - std::string line; - std::getline(f, line); // Ignore up to start of next line, as standard - // says anything that follows could be used for - // optional sections - - log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); - - line_count = 1; - - if (header == "aag") - parse_aiger_ascii(); - else if (header == "aig") - parse_aiger_binary(); - else - log_abort(); - - // Parse footer (symbol table, comments, etc.) - unsigned l1; - std::string s; - for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { - if (c == 'i' || c == 'l' || c == 'o') { - f.ignore(1); - if (!(f >> l1 >> s)) - log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); - - if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) - log_error("Line %u has invalid symbol position!\n", line_count); - - RTLIL::Wire* wire; - if (c == 'i') wire = inputs[l1]; - else if (c == 'l') wire = latches[l1]; - else if (c == 'o') wire = outputs[l1]; - else log_abort(); - - module->rename(wire, stringf("\\%s", s.c_str())); - } - else if (c == 'b' || c == 'j' || c == 'f') { - // TODO - } - else if (c == 'c') { - f.ignore(1); - if (f.peek() == '\n') - break; - // Else constraint (TODO) - } - else - log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); - std::getline(f, line); // Ignore up to start of next line - } - - module->fixup_ports(); - design->add(module); + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); + + line_count = 1; + piNum = 0; + flopNum = 0; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + RTLIL::Wire* n0 = module->wire("\\__0__"); + if (n0) + module->connect(n0, RTLIL::S0); + + // Parse footer (symbol table, comments, etc.) + unsigned l1; + std::string s; + for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { + if (c == 'i' || c == 'l' || c == 'o' || c == 'b') { + f.ignore(1); + if (!(f >> l1 >> s)) + log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); + + if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) + log_error("Line %u has invalid symbol position!\n", line_count); + + RTLIL::Wire* wire; + if (c == 'i') wire = inputs[l1]; + else if (c == 'l') wire = latches[l1]; + else if (c == 'o') wire = outputs[l1]; + else if (c == 'b') wire = bad_properties[l1]; + else log_abort(); + + module->rename(wire, stringf("\\%s", s.c_str())); + } + else if (c == 'j' || c == 'f') { + // TODO + } + else if (c == 'c') { + f.ignore(1); + if (f.peek() == '\n') + break; + // Else constraint (TODO) + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + std::getline(f, line); // Ignore up to start of next line + } + + post_process(); +} + +static uint32_t parse_xaiger_literal(std::istream &f) +{ + uint32_t l; + f.read(reinterpret_cast<char*>(&l), sizeof(l)); + if (f.gcount() != sizeof(l)) + log_error("Offset %" PRId64 ": unable to read literal!\n", static_cast<int64_t>(f.tellg())); + return from_big_endian(l); } static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal) { - const unsigned variable = literal >> 1; - const bool invert = literal & 1; - RTLIL::IdString wire_name(stringf("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix? - RTLIL::Wire *wire = module->wire(wire_name); - if (wire) return wire; - log_debug("Creating %s\n", wire_name.c_str()); - wire = module->addWire(wire_name); - if (!invert) return wire; - RTLIL::IdString wire_inv_name(stringf("\\n%d", variable)); - RTLIL::Wire *wire_inv = module->wire(wire_inv_name); - if (wire_inv) { - if (module->cell(wire_inv_name)) return wire; - } - else { - log_debug("Creating %s\n", wire_inv_name.c_str()); - wire_inv = module->addWire(wire_inv_name); - } - - log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); - module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix? - - return wire; + const unsigned variable = literal >> 1; + const bool invert = literal & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); + RTLIL::Wire *wire = module->wire(wire_name); + if (wire) return wire; + log_debug2("Creating %s\n", wire_name.c_str()); + wire = module->addWire(wire_name); + wire->port_input = wire->port_output = false; + if (!invert) return wire; + RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable)); + RTLIL::Wire *wire_inv = module->wire(wire_inv_name); + if (wire_inv) { + if (module->cell(wire_inv_name)) return wire; + } + else { + log_debug2("Creating %s\n", wire_inv_name.c_str()); + wire_inv = module->addWire(wire_inv_name); + wire_inv->port_input = wire_inv->port_output = false; + } + + log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); + module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire); + + return wire; +} + +void AigerReader::parse_xaiger() +{ + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); + + line_count = 1; + piNum = 0; + flopNum = 0; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + RTLIL::Wire* n0 = module->wire("\\__0__"); + if (n0) + module->connect(n0, RTLIL::S0); + + dict<int,IdString> box_lookup; + for (auto m : design->modules()) { + auto it = m->attributes.find("\\abc_box_id"); + if (it == m->attributes.end()) + continue; + if (m->name.begins_with("$paramod")) + continue; + auto id = it->second.as_int(); + auto r = box_lookup.insert(std::make_pair(id, m->name)); + if (!r.second) + log_error("Module '%s' has the same abc_box_id = %d value as '%s'.\n", + log_id(m), id, log_id(r.first->second)); + log_assert(r.second); + } + + // Parse footer (symbol table, comments, etc.) + std::string s; + bool comment_seen = false; + for (int c = f.peek(); c != EOF; c = f.peek()) { + if (comment_seen || c == 'c') { + if (!comment_seen) { + f.ignore(1); + c = f.peek(); + comment_seen = true; + } + if (c == '\n') + break; + f.ignore(1); + // XAIGER extensions + if (c == 'm') { + uint32_t dataSize = parse_xaiger_literal(f); + uint32_t lutNum = parse_xaiger_literal(f); + uint32_t lutSize = parse_xaiger_literal(f); + log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); + ConstEvalAig ce(module); + for (unsigned i = 0; i < lutNum; ++i) { + uint32_t rootNodeID = parse_xaiger_literal(f); + uint32_t cutLeavesM = parse_xaiger_literal(f); + log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); + RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID)); + uint32_t nodeID; + RTLIL::SigSpec input_sig; + for (unsigned j = 0; j < cutLeavesM; ++j) { + nodeID = parse_xaiger_literal(f); + log_debug2("\t%u\n", nodeID); + RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID)); + log_assert(wire); + input_sig.append(wire); + } + // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size()) + ce.clear(); + ce.compute_deps(output_sig, input_sig.to_sigbit_pool()); + RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); + for (int j = 0; j < (1 << cutLeavesM); ++j) { + int gray = j ^ (j >> 1); + ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)}); + RTLIL::SigBit o(output_sig); + bool success = ce.eval(o); + log_assert(success); + log_assert(o.wire == nullptr); + lut_mask[gray] = o.data; + } + RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID)); + log_assert(output_cell); + module->remove(output_cell); + module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask)); + } + } + else if (c == 'r') { + uint32_t dataSize = parse_xaiger_literal(f); + flopNum = parse_xaiger_literal(f); + log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); + f.ignore(flopNum * sizeof(uint32_t)); + } + else if (c == 'n') { + parse_xaiger_literal(f); + f >> s; + log_debug("n: '%s'\n", s.c_str()); + } + else if (c == 'h') { + f.ignore(sizeof(uint32_t)); + uint32_t version = parse_xaiger_literal(f); + log_assert(version == 1); + uint32_t ciNum = parse_xaiger_literal(f); + log_debug("ciNum = %u\n", ciNum); + uint32_t coNum = parse_xaiger_literal(f); + log_debug("coNum = %u\n", coNum); + piNum = parse_xaiger_literal(f); + log_debug("piNum = %u\n", piNum); + uint32_t poNum = parse_xaiger_literal(f); + log_debug("poNum = %u\n", poNum); + uint32_t boxNum = parse_xaiger_literal(f); + log_debug("boxNum = %u\n", poNum); + for (unsigned i = 0; i < boxNum; i++) { + f.ignore(2*sizeof(uint32_t)); + uint32_t boxUniqueId = parse_xaiger_literal(f); + log_assert(boxUniqueId > 0); + uint32_t oldBoxNum = parse_xaiger_literal(f); + RTLIL::Cell* cell = module->addCell(stringf("$__box%u__", oldBoxNum), box_lookup.at(boxUniqueId)); + boxes.emplace_back(cell); + } + } + else if (c == 'a' || c == 'i' || c == 'o') { + uint32_t dataSize = parse_xaiger_literal(f); + f.ignore(dataSize); + } + else { + break; + } + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + } + + post_process(); } void AigerReader::parse_aiger_ascii() { - std::string line; - std::stringstream ss; - - unsigned l1, l2, l3; - - // Parse inputs - for (unsigned i = 0; i < I; ++i, ++line_count) { - if (!(f >> l1)) - log_error("Line %u cannot be interpreted as an input!\n", line_count); - log_debug("%d is an input\n", l1); - log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted? - RTLIL::Wire *wire = createWireIfNotExists(module, l1); - wire->port_input = true; - inputs.push_back(wire); - } - - // Parse latches - RTLIL::Wire *clk_wire = nullptr; - if (L > 0) { - clk_wire = module->wire(clk_name); - log_assert(!clk_wire); - log_debug("Creating %s\n", clk_name.c_str()); - clk_wire = module->addWire(clk_name); - clk_wire->port_input = true; - } - for (unsigned i = 0; i < L; ++i, ++line_count) { - if (!(f >> l1 >> l2)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - log_debug("%d %d is a latch\n", l1, l2); - log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted? - RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); - - module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); - - // Reset logic is optional in AIGER 1.9 - if (f.peek() == ' ') { - if (!(f >> l3)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - - if (l3 == 0 || l3 == 1) - q_wire->attributes["\\init"] = RTLIL::Const(l3); - else if (l3 == l1) { - //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); - } - else - log_error("Line %u has invalid reset literal for latch!\n", line_count); - } - else { - // AIGER latches are assumed to be initialized to zero - q_wire->attributes["\\init"] = RTLIL::Const(0); - } - latches.push_back(q_wire); - } - - // Parse outputs - for (unsigned i = 0; i < O; ++i, ++line_count) { - if (!(f >> l1)) - log_error("Line %u cannot be interpreted as an output!\n", line_count); - - log_debug("%d is an output\n", l1); - RTLIL::Wire *wire = createWireIfNotExists(module, l1); - wire->port_output = true; - outputs.push_back(wire); - } - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse bad state properties - for (unsigned i = 0; i < B; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse invariant constraints - for (unsigned i = 0; i < C; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse justice properties - for (unsigned i = 0; i < J; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse fairness constraints - for (unsigned i = 0; i < F; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // Parse AND - for (unsigned i = 0; i < A; ++i) { - if (!(f >> l1 >> l2 >> l3)) - log_error("Line %u cannot be interpreted as an AND!\n", line_count); - - log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? - RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); - RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); - module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire); - } - std::getline(f, line); // Ignore up to start of next line + std::string line; + std::stringstream ss; + + unsigned l1, l2, l3; + + // Parse inputs + for (unsigned i = 1; i <= I; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an input!\n", line_count); + log_debug2("%d is an input\n", l1); + log_assert(!(l1 & 1)); // Inputs can't be inverted + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_input = true; + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + log_assert(clk_name != ""); + clk_wire = module->wire(clk_name); + log_assert(!clk_wire); + log_debug2("Creating %s\n", clk_name.c_str()); + clk_wire = module->addWire(clk_name); + clk_wire->port_input = true; + clk_wire->port_output = false; + } + for (unsigned i = 0; i < L; ++i, ++line_count) { + if (!(f >> l1 >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug2("%d %d is a latch\n", l1, l2); + log_assert(!(l1 & 1)); + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0) + q_wire->attributes["\\init"] = RTLIL::S0; + else if (l3 == 1) + q_wire->attributes["\\init"] = RTLIL::S1; + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Sx; + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::S0; + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an output!\n", line_count); + + log_debug2("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? + RTLIL::Wire *wire = module->wire(wire_name); + if (!wire) + wire = createWireIfNotExists(module, l1); + else if (wire->port_input || wire->port_output) { + RTLIL::Wire *new_wire = module->addWire(NEW_ID); + module->connect(new_wire, wire); + wire = new_wire; + } + wire->port_output = true; + outputs.push_back(wire); + } + + // Parse bad properties + for (unsigned i = 0; i < B; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as a bad state property!\n", line_count); + + log_debug2("%d is a bad state property\n", l1); + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_output = true; + bad_properties.push_back(wire); + } + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + for (unsigned i = 0; i < A; ++i) { + if (!(f >> l1 >> l2 >> l3)) + log_error("Line %u cannot be interpreted as an AND!\n", line_count); + + log_debug2("%d %d %d is an AND\n", l1, l2, l3); + log_assert(!(l1 & 1)); + RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); + RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } + std::getline(f, line); // Ignore up to start of next line } static unsigned parse_next_delta_literal(std::istream &f, unsigned ref) { - unsigned x = 0, i = 0; - unsigned char ch; - while ((ch = f.get()) & 0x80) - x |= (ch & 0x7f) << (7 * i++); - return ref - (x | (ch << (7 * i))); + unsigned x = 0, i = 0; + unsigned char ch; + while ((ch = f.get()) & 0x80) + x |= (ch & 0x7f) << (7 * i++); + return ref - (x | (ch << (7 * i))); } void AigerReader::parse_aiger_binary() { - unsigned l1, l2, l3; - std::string line; - - // Parse inputs - for (unsigned i = 1; i <= I; ++i) { - RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); - wire->port_input = true; - inputs.push_back(wire); - } - - // Parse latches - RTLIL::Wire *clk_wire = nullptr; - if (L > 0) { - clk_wire = module->wire(clk_name); - log_assert(!clk_wire); - log_debug("Creating %s\n", clk_name.c_str()); - clk_wire = module->addWire(clk_name); - clk_wire->port_input = true; - } - l1 = (I+1) * 2; - for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { - if (!(f >> l2)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - log_debug("%d %d is a latch\n", l1, l2); - RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); - - module->addDff(NEW_ID, clk_wire, d_wire, q_wire); - - // Reset logic is optional in AIGER 1.9 - if (f.peek() == ' ') { - if (!(f >> l3)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - - if (l3 == 0 || l3 == 1) - q_wire->attributes["\\init"] = RTLIL::Const(l3); - else if (l3 == l1) { - //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); - } - else - log_error("Line %u has invalid reset literal for latch!\n", line_count); - } - else { - // AIGER latches are assumed to be initialized to zero - q_wire->attributes["\\init"] = RTLIL::Const(0); - } - latches.push_back(q_wire); - } - - // Parse outputs - for (unsigned i = 0; i < O; ++i, ++line_count) { - if (!(f >> l1)) - log_error("Line %u cannot be interpreted as an output!\n", line_count); - - log_debug("%d is an output\n", l1); - RTLIL::Wire *wire = createWireIfNotExists(module, l1); - wire->port_output = true; - outputs.push_back(wire); - } - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse bad state properties - for (unsigned i = 0; i < B; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse invariant constraints - for (unsigned i = 0; i < C; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse justice properties - for (unsigned i = 0; i < J; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse fairness constraints - for (unsigned i = 0; i < F; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // Parse AND - l1 = (I+L+1) << 1; - for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { - l2 = parse_next_delta_literal(f, l1); - l3 = parse_next_delta_literal(f, l2); - - log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? - RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); - RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); - - RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_"); - and_cell->setPort("\\A", i1_wire); - and_cell->setPort("\\B", i2_wire); - and_cell->setPort("\\Y", o_wire); - } + unsigned l1, l2, l3; + std::string line; + + // Parse inputs + for (unsigned i = 1; i <= I; ++i) { + log_debug2("%d is an input\n", i); + RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); + wire->port_input = true; + log_assert(!wire->port_output); + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + log_assert(clk_name != ""); + clk_wire = module->wire(clk_name); + log_assert(!clk_wire); + log_debug2("Creating %s\n", clk_name.c_str()); + clk_wire = module->addWire(clk_name); + clk_wire->port_input = true; + clk_wire->port_output = false; + } + l1 = (I+1) * 2; + for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { + if (!(f >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug("%d %d is a latch\n", l1, l2); + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDff(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0) + q_wire->attributes["\\init"] = RTLIL::S0; + else if (l3 == 1) + q_wire->attributes["\\init"] = RTLIL::S1; + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Sx; + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::S0; + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an output!\n", line_count); + + log_debug2("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_b" the right suffix? + RTLIL::Wire *wire = module->wire(wire_name); + if (!wire) + wire = createWireIfNotExists(module, l1); + else if (wire->port_input || wire->port_output) { + RTLIL::Wire *new_wire = module->addWire(NEW_ID); + module->connect(new_wire, wire); + wire = new_wire; + } + wire->port_output = true; + outputs.push_back(wire); + } + std::getline(f, line); // Ignore up to start of next line + + // Parse bad properties + for (unsigned i = 0; i < B; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as a bad state property!\n", line_count); + + log_debug2("%d is a bad state property\n", l1); + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_output = true; + bad_properties.push_back(wire); + } + if (B > 0) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + l1 = (I+L+1) << 1; + for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { + l2 = parse_next_delta_literal(f, l1); + l3 = parse_next_delta_literal(f, l2); + + log_debug2("%d %d %d is an AND\n", l1, l2, l3); + log_assert(!(l1 & 1)); + RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); + RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } +} + +void AigerReader::post_process() +{ + pool<IdString> seen_boxes; + unsigned ci_count = 0, co_count = 0; + for (auto cell : boxes) { + RTLIL::Module* box_module = design->module(cell->type); + log_assert(box_module); + + if (seen_boxes.insert(cell->type).second) { + auto it = box_module->attributes.find("\\abc_carry"); + if (it != box_module->attributes.end()) { + RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; + auto carry_in_out = it->second.decode_string(); + auto pos = carry_in_out.find(','); + if (pos == std::string::npos) + log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type)); + auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos)); + carry_in = box_module->wire(carry_in_name); + if (!carry_in || !carry_in->port_input) + log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str()); + + auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1)); + carry_out = box_module->wire(carry_out_name); + if (!carry_out || !carry_out->port_output) + log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str()); + + auto &ports = box_module->ports; + for (auto jt = ports.begin(); jt != ports.end(); ) { + RTLIL::Wire* w = box_module->wire(*jt); + log_assert(w); + if (w == carry_in || w == carry_out) { + jt = ports.erase(jt); + continue; + } + if (w->port_id > carry_in->port_id) + --w->port_id; + if (w->port_id > carry_out->port_id) + --w->port_id; + log_assert(w->port_input || w->port_output); + log_assert(ports[w->port_id-1] == w->name); + ++jt; + } + ports.push_back(carry_in->name); + carry_in->port_id = ports.size(); + ports.push_back(carry_out->name); + carry_out->port_id = ports.size(); + } + } + + // NB: Assume box_module->ports are sorted alphabetically + // (as RTLIL::Module::fixup_ports() would do) + for (auto port_name : box_module->ports) { + RTLIL::Wire* w = box_module->wire(port_name); + log_assert(w); + RTLIL::SigSpec rhs; + RTLIL::Wire* wire = nullptr; + for (int i = 0; i < GetSize(w); i++) { + if (w->port_input) { + log_assert(co_count < outputs.size()); + wire = outputs[co_count++]; + log_assert(wire); + log_assert(wire->port_output); + wire->port_output = false; + } + if (w->port_output) { + log_assert((piNum + ci_count) < inputs.size()); + wire = inputs[piNum + ci_count++]; + log_assert(wire); + log_assert(wire->port_input); + wire->port_input = false; + } + rhs.append(wire); + } + cell->setPort(port_name, rhs); + } + } + + dict<RTLIL::IdString, int> wideports_cache; + + if (!map_filename.empty()) { + std::ifstream mf(map_filename); + std::string type, symbol; + int variable, index; + while (mf >> type >> variable >> index >> symbol) { + RTLIL::IdString escaped_s = RTLIL::escape_id(symbol); + if (type == "input") { + log_assert(static_cast<unsigned>(variable) < inputs.size()); + RTLIL::Wire* wire = inputs[variable]; + log_assert(wire); + log_assert(wire->port_input); + + if (index == 0) { + // Cope with the fact that a CI might be identical + // to a PI (necessary due to ABC); in those cases + // simply connect the latter to the former + RTLIL::Wire* existing = module->wire(escaped_s); + if (!existing) + module->rename(wire, escaped_s); + else { + wire->port_input = false; + module->connect(wire, existing); + } + } + else if (index > 0) { + std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::Wire* existing = module->wire(indexed_name); + if (!existing) { + module->rename(wire, indexed_name); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + else { + module->connect(wire, existing); + wire->port_input = false; + } + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); + RTLIL::Wire* wire = outputs[variable + co_count]; + log_assert(wire); + log_assert(wire->port_output); + if (escaped_s == "$__dummy__") { + wire->port_output = false; + continue; + } + + if (index == 0) { + // Cope with the fact that a CO might be identical + // to a PO (necessary due to ABC); in those cases + // simply connect the latter to the former + RTLIL::Wire* existing = module->wire(escaped_s); + if (!existing) { + if (escaped_s.ends_with("$inout.out")) { + wire->port_output = false; + RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10)); + log_assert(in_wire); + log_assert(in_wire->port_input && !in_wire->port_output); + in_wire->port_output = true; + module->connect(in_wire, wire); + } + else + module->rename(wire, escaped_s); + } + else { + wire->port_output = false; + module->connect(wire, existing); + } + } + else if (index > 0) { + std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::Wire* existing = module->wire(indexed_name); + if (!existing) { + if (escaped_s.ends_with("$inout.out")) { + wire->port_output = false; + RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index)); + log_assert(in_wire); + log_assert(in_wire->port_input && !in_wire->port_output); + in_wire->port_output = true; + module->connect(in_wire, wire); + } + else { + module->rename(wire, indexed_name); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + } + else { + module->connect(wire, existing); + wire->port_output = false; + } + } + } + else if (type == "box") { + RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); + if (cell) { // ABC could have optimised this box away + module->rename(cell, escaped_s); + RTLIL::Module* box_module = design->module(cell->type); + log_assert(box_module); + + for (const auto &i : cell->connections()) { + RTLIL::IdString port_name = i.first; + RTLIL::SigSpec rhs = i.second; + int index = 0; + for (auto bit : rhs.bits()) { + RTLIL::Wire* wire = bit.wire; + RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name))); + if (index == 0) + module->rename(wire, escaped_s); + else if (index > 0) { + module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index)); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + index++; + } + } + } + } + else + log_error("Symbol type '%s' not recognised.\n", type.c_str()); + } + } + + for (auto &wp : wideports_cache) { + auto name = wp.first; + int width = wp.second + 1; + + RTLIL::Wire *wire = module->wire(name); + if (wire) + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + // Do not make ports with a mix of input/output into + // wide ports + bool port_input = false, port_output = false; + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + port_input = port_input || other_wire->port_input; + port_output = port_output || other_wire->port_output; + } + } + + wire = module->addWire(name, width); + wire->port_input = port_input; + wire->port_output = port_output; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + } + if (wire->port_input) { + if (other_wire) + module->connect(other_wire, SigSpec(wire, i)); + } + else { + // Since we skip POs that are connected to Sx, + // re-connect them here + module->connect(SigSpec(wire, i), other_wire ? other_wire : SigSpec(RTLIL::Sx)); + } + } + } + + module->fixup_ports(); + design->add(module); + + design->selection_stack.emplace_back(false); + RTLIL::Selection& sel = design->selection_stack.back(); + sel.select(module); + + Pass::call(design, "clean"); + + design->selection_stack.pop_back(); + + for (auto cell : module->cells().to_vector()) { + if (cell->type != "$lut") continue; + auto y_port = cell->getPort("\\Y").as_bit(); + if (y_port.wire->width == 1) + module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str())); + else + module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset)); + } } struct AigerFrontend : public Frontend { - AigerFrontend() : Frontend("aiger", "read AIGER file") { } - void help() YS_OVERRIDE - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" read_aiger [options] [filename]\n"); - log("\n"); - log("Load module from an AIGER file into the current design.\n"); - log("\n"); - log(" -module_name <module_name>\n"); - log(" Name of module to be created (default: <filename>)" -#ifdef _WIN32 - "top" // FIXME -#else - "<filename>" -#endif - ")\n"); - log("\n"); - log(" -clk_name <wire_name>\n"); - log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); - log(" this name (default: clk)\n"); - log("\n"); - } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE - { - log_header(design, "Executing AIGER frontend.\n"); - - RTLIL::IdString clk_name = "\\clk"; - RTLIL::IdString module_name; + AigerFrontend() : Frontend("aiger", "read AIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_aiger [options] [filename]\n"); + log("\n"); + log("Load module from an AIGER file into the current design.\n"); + log("\n"); + log(" -module_name <module_name>\n"); + log(" Name of module to be created (default: <filename>)\n"); + log("\n"); + log(" -clk_name <wire_name>\n"); + log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); + log(" this name (default: clk)\n"); + log("\n"); + log(" -map <filename>\n"); + log(" read file with port and latch symbols\n"); + log("\n"); + log(" -wideports\n"); + log(" Merge ports that match the pattern 'name[int]' into a single\n"); + log(" multi-bit port 'name'.\n"); + log("\n"); + } + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing AIGER frontend.\n"); + + RTLIL::IdString clk_name = "\\clk"; + RTLIL::IdString module_name; + std::string map_filename; + bool wideports = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -394,23 +1047,33 @@ struct AigerFrontend : public Frontend { clk_name = RTLIL::escape_id(args[++argidx]); continue; } + if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (arg == "-wideports") { + wideports = true; + continue; + } break; } extra_args(f, filename, args, argidx); - if (module_name.empty()) { + if (module_name.empty()) { #ifdef _WIN32 - module_name = "top"; // FIXME: basename equivalent on Win32? + char fname[_MAX_FNAME]; + _splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */); + module_name = fname; #else - char* bn = strdup(filename.c_str()); - module_name = RTLIL::escape_id(bn); - free(bn); + char* bn = strdup(filename.c_str()); + module_name = RTLIL::escape_id(bn); + free(bn); #endif - } + } - AigerReader reader(design, *f, module_name, clk_name); + AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); reader.parse_aiger(); - } + } } AigerFrontend; YOSYS_NAMESPACE_END diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index c49cd152d..de3c3efbc 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -2,7 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * Eddie Hung <eddie@fpgeh.com> + * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -31,19 +31,26 @@ struct AigerReader std::istream &f; RTLIL::IdString clk_name; RTLIL::Module *module; + std::string map_filename; + bool wideports; unsigned M, I, L, O, A; unsigned B, C, J, F; // Optional in AIGER 1.9 unsigned line_count; + uint32_t piNum, flopNum; std::vector<RTLIL::Wire*> inputs; std::vector<RTLIL::Wire*> latches; std::vector<RTLIL::Wire*> outputs; + std::vector<RTLIL::Wire*> bad_properties; + std::vector<RTLIL::Cell*> boxes; - AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name); + AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); + void parse_xaiger(); void parse_aiger_ascii(); void parse_aiger_binary(); + void post_process(); }; YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 83993eea9..943466ee3 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -46,7 +46,7 @@ namespace AST { // instantiate global variables (private API) namespace AST_INTERNAL { bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; - bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_autowire; + bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; @@ -154,6 +154,7 @@ std::string AST::type2str(AstNodeType type) X(AST_GENIF) X(AST_GENCASE) X(AST_GENBLOCK) + X(AST_TECALL) X(AST_POSEDGE) X(AST_NEGEDGE) X(AST_EDGE) @@ -1111,6 +1112,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast current_module->nowb = flag_nowb; current_module->noopt = flag_noopt; current_module->icells = flag_icells; + current_module->pwires = flag_pwires; current_module->autowire = flag_autowire; current_module->fixup_ports(); @@ -1125,7 +1127,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast // create AstModule instances for all modules in the AST tree and add them to 'design' void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, - bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) + bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; @@ -1143,6 +1145,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; + flag_pwires = pwires; flag_autowire = autowire; log_assert(current_ast->type == AST_DESIGN); @@ -1479,6 +1482,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; + flag_pwires = pwires; flag_autowire = autowire; use_internal_line_num(); @@ -1547,9 +1551,12 @@ RTLIL::Module *AstModule::clone() const new_mod->nomeminit = nomeminit; new_mod->nomem2reg = nomem2reg; new_mod->mem2reg = mem2reg; + new_mod->noblackbox = noblackbox; new_mod->lib = lib; + new_mod->nowb = nowb; new_mod->noopt = noopt; new_mod->icells = icells; + new_mod->pwires = pwires; new_mod->autowire = autowire; return new_mod; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 46d482f1a..54b2fb319 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -137,7 +137,8 @@ namespace AST AST_GENIF, AST_GENCASE, AST_GENBLOCK, - + AST_TECALL, + AST_POSEDGE, AST_NEGEDGE, AST_EDGE, @@ -285,13 +286,13 @@ namespace AST // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, - bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); + bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, autowire; + bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire; ~AstModule() YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; @@ -324,7 +325,7 @@ namespace AST_INTERNAL { // internal state variables extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; - extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map<std::string, AST::AstNode*> current_scope; extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d2651c9aa..571ddd988 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -504,6 +504,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; + current_case->attributes["\\src"] = stringf("%s:%d", child->filename.c_str(), child->linenum); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); for (auto node : child->children) { @@ -853,7 +854,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_FUNCTION: case AST_DPI_FUNCTION: case AST_AUTOWIRE: - case AST_LOCALPARAM: case AST_DEFPARAM: case AST_GENVAR: case AST_GENFOR: @@ -895,6 +895,26 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // remember the parameter, needed for example in techmap case AST_PARAMETER: current_module->avail_parameters.insert(str); + /* fall through */ + case AST_LOCALPARAM: + if (flag_pwires) + { + if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT) + log_file_error(filename, linenum, "Parameter `%s' with non-constant value!\n", str.c_str()); + + RTLIL::Const val = children[0]->bitsAsConst(); + RTLIL::Wire *wire = current_module->addWire(str, GetSize(val)); + current_module->connect(wire, val); + + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->attributes[type == AST_PARAMETER ? "\\parameter" : "\\localparam"] = 1; + + for (auto &attr : attributes) { + if (attr.second->type != AST_CONSTANT) + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + wire->attributes[attr.first] = attr.second->asAttrConst(); + } + } break; // create an RTLIL::Wire for an AST_WIRE node @@ -1575,6 +1595,37 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) delete always; } break; + case AST_TECALL: { + int sz = children.size(); + if (str == "$info") { + if (sz > 0) + log_file_info(filename, linenum, "%s.\n", children[0]->str.c_str()); + else + log_file_info(filename, linenum, "\n"); + } else if (str == "$warning") { + if (sz > 0) + log_file_warning(filename, linenum, "%s.\n", children[0]->str.c_str()); + else + log_file_warning(filename, linenum, "\n"); + } else if (str == "$error") { + if (sz > 0) + log_file_error(filename, linenum, "%s.\n", children[0]->str.c_str()); + else + log_file_error(filename, linenum, "\n"); + } else if (str == "$fatal") { + // TODO: 1st parameter, if exists, is 0,1 or 2, and passed to $finish() + // if no parameter is given, default value is 1 + // dollar_finish(sz ? children[0] : 1); + // perhaps create & use log_file_fatal() + if (sz > 0) + log_file_error(filename, linenum, "FATAL: %s.\n", children[0]->str.c_str()); + else + log_file_error(filename, linenum, "FATAL.\n"); + } else { + log_file_error(filename, linenum, "Unknown elabortoon system task '%s'.\n", str.c_str()); + } + } break; + case AST_FCALL: { if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 44c99906a..b4b9693da 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -282,14 +282,14 @@ proc_stmt: } case_body sync_list TOK_END EOL; switch_stmt: - attr_list TOK_SWITCH sigspec EOL { + TOK_SWITCH sigspec EOL { RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; - rule->signal = *$3; + rule->signal = *$2; rule->attributes = attrbuf; switch_stack.back()->push_back(rule); attrbuf.clear(); - delete $3; - } switch_body TOK_END EOL; + delete $2; + } attr_list switch_body TOK_END EOL; attr_list: /* empty */ | @@ -298,9 +298,11 @@ attr_list: switch_body: switch_body TOK_CASE { RTLIL::CaseRule *rule = new RTLIL::CaseRule; + rule->attributes = attrbuf; switch_stack.back()->back()->cases.push_back(rule); switch_stack.push_back(&rule->switches); case_stack.push_back(rule); + attrbuf.clear(); } compare_list EOL case_body { switch_stack.pop_back(); case_stack.pop_back(); @@ -319,12 +321,15 @@ compare_list: /* empty */; case_body: + case_body attr_stmt | case_body switch_stmt | case_body assign_stmt | /* empty */; assign_stmt: TOK_ASSIGN sigspec sigspec EOL { + if (attrbuf.size() != 0) + rtlil_frontend_ilang_yyerror("dangling attribute"); case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); delete $2; delete $3; diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 82361ea9b..f5ae8eb72 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -292,6 +292,18 @@ void json_import(Design *design, string &modname, JsonNode *node) if (port_wire == nullptr) port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array)); + if (port_node->data_dict.count("upto") != 0) { + JsonNode *val = port_node->data_dict.at("upto"); + if (val->type == 'N') + port_wire->upto = val->data_number != 0; + } + + if (port_node->data_dict.count("offset") != 0) { + JsonNode *val = port_node->data_dict.at("offset"); + if (val->type == 'N') + port_wire->start_offset = val->data_number; + } + if (port_direction_node->data_string == "input") { port_wire->port_input = true; } else @@ -372,6 +384,18 @@ void json_import(Design *design, string &modname, JsonNode *node) if (wire == nullptr) wire = module->addWire(net_name, GetSize(bits_node->data_array)); + if (net_node->data_dict.count("upto") != 0) { + JsonNode *val = net_node->data_dict.at("upto"); + if (val->type == 'N') + wire->upto = val->data_number != 0; + } + + if (net_node->data_dict.count("offset") != 0) { + JsonNode *val = net_node->data_dict.at("offset"); + if (val->type == 'N') + wire->start_offset = val->data_number; + } + for (int i = 0; i < GetSize(bits_node->data_array); i++) { JsonNode *bitval_node = bits_node->data_array.at(i); diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 6e3cffaca..14de95e07 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -551,7 +551,7 @@ struct LibertyFrontend : public Frontend { if (design->has(cell_name)) { Module *existing_mod = design->module(cell_name); if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { - log_error("Re-definition of of cell/module %s!\n", log_id(cell_name)); + log_error("Re-definition of cell/module %s!\n", log_id(cell_name)); } else if (flag_nooverwrite) { log("Ignoring re-definition of module %s.\n", log_id(cell_name)); continue; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 21a1bbbbe..2bf99e58e 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -48,6 +48,14 @@ USING_YOSYS_NAMESPACE #include "VhdlUnits.h" #include "VeriLibrary.h" +#ifndef SYMBIOTIC_VERIFIC_API_VERSION +# error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." +#endif + +#if SYMBIOTIC_VERIFIC_API_VERSION < 1 +# error "Please update your version of Symbiotic EDA flavored Verific." +#endif + #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -2016,6 +2024,9 @@ struct VerificPass : public Pass { // WARNING: instantiating unknown module 'XYZ' (VERI-1063) Message::SetMessageType("VERI-1063", VERIFIC_ERROR); + // https://github.com/YosysHQ/yosys/issues/1055 + RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ; + #ifndef DB_PRESERVE_INITIAL_VALUE # warning Verific was built without DB_PRESERVE_INITIAL_VALUE. #endif diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index 57d366dbf..f6a17b242 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -153,7 +153,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { if (warn_z) { AstNode *ret = const2ast(code, case_type); - if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) + if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n", current_filename.c_str(), get_line_num()); return ret; @@ -204,7 +204,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { std::vector<RTLIL::State> data; bool is_signed = false; - bool is_unsized = false; + bool is_unsized = len_in_bits < 0; if (*(endptr+1) == 's') { is_signed = true; endptr++; @@ -213,25 +213,25 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { case 'b': case 'B': - my_strtobin(data, endptr+2, len_in_bits, 2, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 2, case_type, is_unsized); break; case 'o': case 'O': - my_strtobin(data, endptr+2, len_in_bits, 8, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 8, case_type, is_unsized); break; case 'd': case 'D': - my_strtobin(data, endptr+2, len_in_bits, 10, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 10, case_type, is_unsized); break; case 'h': case 'H': - my_strtobin(data, endptr+2, len_in_bits, 16, case_type, false); + my_strtobin(data, endptr+2, len_in_bits, 16, case_type, is_unsized); break; default: char next_char = char(tolower(*(endptr+1))); if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') { - my_strtobin(data, endptr+1, 1, 2, case_type, true); is_unsized = true; + my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized); } else { return NULL; } diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 01e589efb..0e2bead6f 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -168,6 +168,9 @@ struct VerilogFrontend : public Frontend { log(" -icells\n"); log(" interpret cell types starting with '$' as internal cell types\n"); log("\n"); + log(" -pwires\n"); + log(" add a wire for each module parameter\n"); + log("\n"); log(" -nooverwrite\n"); log(" ignore re-definitions of modules. (the default behavior is to\n"); log(" create an error message if the existing module is not a black box\n"); @@ -228,6 +231,7 @@ struct VerilogFrontend : public Frontend { bool flag_nodpi = false; bool flag_noopt = false; bool flag_icells = false; + bool flag_pwires = false; bool flag_nooverwrite = false; bool flag_overwrite = false; bool flag_defer = false; @@ -368,6 +372,10 @@ struct VerilogFrontend : public Frontend { flag_icells = true; continue; } + if (arg == "-pwires") { + flag_pwires = true; + continue; + } if (arg == "-ignore_redef" || arg == "-nooverwrite") { flag_nooverwrite = true; flag_overwrite = false; @@ -458,7 +466,7 @@ struct VerilogFrontend : public Frontend { error_on_dpi_function(current_ast); AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, - flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 9558bbfb9..951d9c66f 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -193,6 +193,8 @@ YOSYS_NAMESPACE_END to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some global state.. its a mess) */ [a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] { + if (!strcmp(yytext, "default")) + return TOK_DEFAULT; frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); return TOK_SVA_LABEL; } @@ -311,6 +313,11 @@ supply1 { return TOK_SUPPLY1; } return TOK_ID; } +"$"(info|warning|error|fatal) { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_MSG_TASKS; +} + "$signed" { return TOK_TO_SIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 8244a8f44..0fec445fa 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -133,7 +133,7 @@ struct specify_rise_fall { } %token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE -%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER +%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM @@ -319,15 +319,17 @@ module_para_list: single_module_para: /* empty */ | - TOK_PARAMETER { + attr TOK_PARAMETER { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_range single_param_decl | - TOK_LOCALPARAM { + attr TOK_LOCALPARAM { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_range single_param_decl | single_param_decl; @@ -345,7 +347,13 @@ module_arg_opt_assignment: if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { AstNode *wire = new AstNode(AST_IDENTIFIER); wire->str = ast_stack.back()->children.back()->str; - if (ast_stack.back()->children.back()->is_reg) + if (ast_stack.back()->children.back()->is_input) { + AstNode *n = ast_stack.back()->children.back(); + if (n->attributes.count("\\defaultvalue")) + delete n->attributes.at("\\defaultvalue"); + n->attributes["\\defaultvalue"] = $2; + } else + if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); else ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); @@ -509,6 +517,7 @@ wire_type_token: TOK_GENVAR { astbuf3->type = AST_GENVAR; astbuf3->is_reg = true; + astbuf3->is_signed = true; astbuf3->range_left = 31; astbuf3->range_right = 0; } | @@ -1012,13 +1021,8 @@ list_of_specparam_assignments: specparam_assignment: ignspec_id '=' constant_mintypmax_expression ; -/* -pulsestyle_declaration : - ; - -showcancelled_declaration : - ; -*/ +ignspec_opt_cond: + TOK_IF '(' ignspec_expr ')' | /* empty */; path_declaration : simple_path_declaration ';' @@ -1027,8 +1031,8 @@ path_declaration : ; simple_path_declaration : - parallel_path_description '=' path_delay_value | - full_path_description '=' path_delay_value + ignspec_opt_cond parallel_path_description '=' path_delay_value | + ignspec_opt_cond full_path_description '=' path_delay_value ; path_delay_value : @@ -1038,32 +1042,20 @@ path_delay_value : ; list_of_path_delay_extra_expressions : -/* - t_path_delay_expression - | trise_path_delay_expression ',' tfall_path_delay_expression - | trise_path_delay_expression ',' tfall_path_delay_expression ',' tz_path_delay_expression - | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ',' - tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression - | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ',' - tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression ',' - t0x_path_delay_expression ',' tx1_path_delay_expression ',' t1x_path_delay_expression ',' - tx0_path_delay_expression ',' txz_path_delay_expression ',' tzx_path_delay_expression -*/ - ',' path_delay_expression - | ',' path_delay_expression ',' path_delay_expression - | ',' path_delay_expression ',' path_delay_expression ',' - path_delay_expression ',' path_delay_expression ',' path_delay_expression - | ',' path_delay_expression ',' path_delay_expression ',' - path_delay_expression ',' path_delay_expression ',' path_delay_expression ',' - path_delay_expression ',' path_delay_expression ',' path_delay_expression ',' - path_delay_expression ',' path_delay_expression ',' path_delay_expression - ; + ',' path_delay_expression | ',' path_delay_expression list_of_path_delay_extra_expressions; + +specify_edge_identifier : + TOK_POSEDGE | TOK_NEGEDGE ; parallel_path_description : - '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' ; + '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' | + '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor opt_polarity_operator ':' ignspec_expr ')' ')' | + '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr ')' ')' ; full_path_description : - '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' ; + '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' | + '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs opt_polarity_operator ':' ignspec_expr ')' ')' | + '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs TOK_POS_INDEXED ignspec_expr ')' ')' ; // This was broken into 2 rules to solve shift/reduce conflicts list_of_path_inputs : @@ -1103,56 +1095,6 @@ system_timing_args : system_timing_arg | system_timing_args ',' system_timing_arg ; -/* -t_path_delay_expression : - path_delay_expression; - -trise_path_delay_expression : - path_delay_expression; - -tfall_path_delay_expression : - path_delay_expression; - -tz_path_delay_expression : - path_delay_expression; - -t01_path_delay_expression : - path_delay_expression; - -t10_path_delay_expression : - path_delay_expression; - -t0z_path_delay_expression : - path_delay_expression; - -tz1_path_delay_expression : - path_delay_expression; - -t1z_path_delay_expression : - path_delay_expression; - -tz0_path_delay_expression : - path_delay_expression; - -t0x_path_delay_expression : - path_delay_expression; - -tx1_path_delay_expression : - path_delay_expression; - -t1x_path_delay_expression : - path_delay_expression; - -tx0_path_delay_expression : - path_delay_expression; - -txz_path_delay_expression : - path_delay_expression; - -tzx_path_delay_expression : - path_delay_expression; -*/ - path_delay_expression : ignspec_constant_expression; @@ -1211,6 +1153,7 @@ param_decl: attr TOK_PARAMETER { astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -1219,6 +1162,7 @@ localparam_decl: attr TOK_LOCALPARAM { astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); + append_attr(astbuf1, $1); } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -1360,7 +1304,12 @@ wire_name_and_opt_assign: wire_name '=' expr { AstNode *wire = new AstNode(AST_IDENTIFIER); wire->str = ast_stack.back()->children.back()->str; - if (astbuf1->is_reg) + if (astbuf1->is_input) { + if (astbuf1->attributes.count("\\defaultvalue")) + delete astbuf1->attributes.at("\\defaultvalue"); + astbuf1->attributes["\\defaultvalue"] = $3; + } else + if (astbuf1->is_reg || astbuf1->is_logic) ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $3)))); else ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3)); @@ -1385,7 +1334,13 @@ wire_name: node->children.push_back(rng); } node->type = AST_MEMORY; - node->children.push_back($2); + auto *rangeNode = $2; + if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { + // SV array size [n], rewrite as [n-1:0] + rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); + rangeNode->children.push_back(AstNode::mkconst_int(0, false)); + } + node->children.push_back(rangeNode); } if (current_function_or_task == NULL) { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { @@ -1532,27 +1487,40 @@ cell_port_list_rules: cell_port | cell_port_list_rules ',' cell_port; cell_port: - /* empty */ { + attr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); + free_attr($1); } | - expr { + attr expr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); - node->children.push_back($1); + node->children.push_back($2); + free_attr($1); } | - '.' TOK_ID '(' expr ')' { + attr '.' TOK_ID '(' expr ')' { AstNode *node = new AstNode(AST_ARGUMENT); - node->str = *$2; + node->str = *$3; astbuf2->children.push_back(node); - node->children.push_back($4); - delete $2; + node->children.push_back($5); + delete $3; + free_attr($1); } | - '.' TOK_ID '(' ')' { + attr '.' TOK_ID '(' ')' { AstNode *node = new AstNode(AST_ARGUMENT); - node->str = *$2; + node->str = *$3; astbuf2->children.push_back(node); - delete $2; + delete $3; + free_attr($1); + } | + attr '.' TOK_ID { + AstNode *node = new AstNode(AST_ARGUMENT); + node->str = *$3; + astbuf2->children.push_back(node); + node->children.push_back(new AstNode(AST_IDENTIFIER)); + node->children.back()->str = *$3; + delete $3; + free_attr($1); }; always_stmt: @@ -1868,6 +1836,16 @@ behavioral_stmt: } opt_arg_list ';'{ ast_stack.pop_back(); } | + TOK_MSG_TASKS attr { + AstNode *node = new AstNode(AST_TCALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $2); + } opt_arg_list ';'{ + ast_stack.pop_back(); + } | attr TOK_BEGIN opt_label { AstNode *node = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(node); @@ -2163,6 +2141,15 @@ gen_stmt: if ($6 != NULL) delete $6; ast_stack.pop_back(); + } | + TOK_MSG_TASKS { + AstNode *node = new AstNode(AST_TECALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } opt_arg_list ';'{ + ast_stack.pop_back(); }; gen_stmt_block: diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 4e91eddda..758661c02 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -246,24 +246,24 @@ struct CellTypes cell_types.clear(); } - bool cell_known(RTLIL::IdString type) + bool cell_known(RTLIL::IdString type) const { return cell_types.count(type) != 0; } - bool cell_output(RTLIL::IdString type, RTLIL::IdString port) + bool cell_output(RTLIL::IdString type, RTLIL::IdString port) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.outputs.count(port) != 0; } - bool cell_input(RTLIL::IdString type, RTLIL::IdString port) + bool cell_input(RTLIL::IdString type, RTLIL::IdString port) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.inputs.count(port) != 0; } - bool cell_evaluable(RTLIL::IdString type) + bool cell_evaluable(RTLIL::IdString type) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.is_evaluable; @@ -482,4 +482,3 @@ extern CellTypes yosys_celltypes; YOSYS_NAMESPACE_END #endif - diff --git a/kernel/log.cc b/kernel/log.cc index fa74a6a3c..e0a60ca12 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -61,7 +61,7 @@ int log_force_debug = 0; int log_debug_suppressed = 0; vector<int> header_count; -pool<RTLIL::IdString> log_id_cache; +vector<char*> log_id_cache; vector<shared_str> string_buf; int string_buf_index = -1; @@ -69,6 +69,13 @@ static struct timeval initial_tv = { 0, 0 }; static bool next_print_log = false; static int log_newline_count = 0; +static void log_id_cache_clear() +{ + for (auto p : log_id_cache) + free(p); + log_id_cache.clear(); +} + #if defined(_WIN32) && !defined(__MINGW32__) // this will get time information and return it in timeval, simulating gettimeofday() int gettimeofday(struct timeval *tv, struct timezone *tz) @@ -277,11 +284,22 @@ void log_file_warning(const std::string &filename, int lineno, va_list ap; va_start(ap, format); std::string prefix = stringf("%s:%d: Warning: ", - filename.c_str(), lineno); + filename.c_str(), lineno); logv_warning_with_prefix(prefix.c_str(), format, ap); va_end(ap); } +void log_file_info(const std::string &filename, int lineno, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + std::string fmt = stringf("%s:%d: Info: %s", + filename.c_str(), lineno, format); + logv(fmt.c_str(), ap); + va_end(ap); +} + YS_ATTRIBUTE(noreturn) static void logv_error_with_prefix(const char *prefix, const char *format, va_list ap) @@ -403,7 +421,7 @@ void log_push() void log_pop() { header_count.pop_back(); - log_id_cache.clear(); + log_id_cache_clear(); string_buf.clear(); string_buf_index = -1; log_flush(); @@ -510,7 +528,7 @@ void log_reset_stack() { while (header_count.size() > 1) header_count.pop_back(); - log_id_cache.clear(); + log_id_cache_clear(); string_buf.clear(); string_buf_index = -1; log_flush(); @@ -569,8 +587,8 @@ const char *log_const(const RTLIL::Const &value, bool autoint) const char *log_id(RTLIL::IdString str) { - log_id_cache.insert(str); - const char *p = str.c_str(); + log_id_cache.push_back(strdup(str.c_str())); + const char *p = log_id_cache.back(); if (p[0] != '\\') return p; if (p[1] == '$' || p[1] == '\\' || p[1] == 0) diff --git a/kernel/log.h b/kernel/log.h index e6afae716..3e1facae8 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -80,6 +80,7 @@ void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); // Log with filename to report a problem in a source file. void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); +void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); void log_warning_noprefix(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn); diff --git a/kernel/register.cc b/kernel/register.cc index 71eb6b187..26da96b95 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -545,6 +545,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st } filename = arg; + rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); yosys_output_files.insert(filename); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 790ba52a3..a09f4a0d1 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1381,7 +1381,34 @@ void RTLIL::Module::check() for (auto &it : processes) { log_assert(it.first == it.second->name); log_assert(!it.first.empty()); - // FIXME: More checks here.. + log_assert(it.second->root_case.compare.empty()); + std::vector<CaseRule*> all_cases = {&it.second->root_case}; + for (size_t i = 0; i < all_cases.size(); i++) { + for (auto &switch_it : all_cases[i]->switches) { + for (auto &case_it : switch_it->cases) { + for (auto &compare_it : case_it->compare) { + log_assert(switch_it->signal.size() == compare_it.size()); + } + all_cases.push_back(case_it); + } + } + } + for (auto &sync_it : it.second->syncs) { + switch (sync_it->type) { + case SyncType::ST0: + case SyncType::ST1: + case SyncType::STp: + case SyncType::STn: + case SyncType::STe: + log_assert(!sync_it->signal.empty()); + break; + case SyncType::STa: + case SyncType::STg: + case SyncType::STi: + log_assert(sync_it->signal.empty()); + break; + } + } } for (auto &it : connections_) { diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 8509670ff..82cbfaf28 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -276,6 +276,18 @@ namespace RTLIL return std::string(c_str() + pos, len); } + bool begins_with(const char* prefix) const { + size_t len = strlen(prefix); + if (size() < len) return false; + return substr(0, len) == prefix; + } + + bool ends_with(const char* suffix) const { + size_t len = strlen(suffix); + if (size() < len) return false; + return substr(size()-len) == suffix; + } + size_t size() const { return str().size(); } @@ -1315,7 +1327,7 @@ public: #endif }; -struct RTLIL::CaseRule +struct RTLIL::CaseRule : public RTLIL::AttrObject { std::vector<RTLIL::SigSpec> compare; std::vector<RTLIL::SigSig> actions; diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 377572fc2..a42a7c0b8 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -651,6 +651,10 @@ void rewrite_filename(std::string &filename) filename = filename.substr(1, GetSize(filename)-2); if (filename.substr(0, 2) == "+/") filename = proc_share_dirname() + filename.substr(2); +#ifndef _WIN32 + if (filename.substr(0, 2) == "~/") + filename = filename.replace(0, 1, getenv("HOME")); +#endif } #ifdef YOSYS_ENABLE_TCL @@ -1250,24 +1254,59 @@ struct HistoryPass : public Pass { #endif struct ScriptCmdPass : public Pass { - ScriptCmdPass() : Pass("script", "execute commands from script file") { } + ScriptCmdPass() : Pass("script", "execute commands from file or wire") { } void help() YS_OVERRIDE { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" script <filename> [<from_label>:<to_label>]\n"); + log(" script -scriptwire [selection]\n"); log("\n"); - log("This command executes the yosys commands in the specified file.\n"); + log("This command executes the yosys commands in the specified file (default\n"); + log("behaviour), or commands embedded in the constant text value connected to the\n"); + log("selected wires.\n"); log("\n"); - log("The 2nd argument can be used to only execute the section of the\n"); - log("file between the specified labels. An empty from label is synonymous\n"); - log("for the beginning of the file and an empty to label is synonymous\n"); - log("for the end of the file.\n"); + log("In the default (file) case, the 2nd argument can be used to only execute the\n"); + log("section of the file between the specified labels. An empty from label is\n"); + log("synonymous with the beginning of the file and an empty to label is synonymous\n"); + log("with the end of the file.\n"); log("\n"); log("If only one label is specified (without ':') then only the block\n"); log("marked with that label (until the next label) is executed.\n"); log("\n"); + log("In \"-scriptwire\" mode, the commands on the selected wire(s) will be executed\n"); + log("in the scope of (and thus, relative to) the wires' owning module(s). This\n"); + log("'-module' mode can be exited by using the 'cd' command.\n"); + log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - if (args.size() < 2) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool scriptwire = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-scriptwire") { + scriptwire = true; + continue; + } + break; + } + if (scriptwire) { + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + for (auto &c : mod->connections()) { + if (!c.first.is_wire()) + continue; + auto w = c.first.as_wire(); + if (!mod->selected(w)) + continue; + if (!c.second.is_fully_const()) + log_error("RHS of selected wire %s.%s is not constant.\n", log_id(mod), log_id(w)); + auto v = c.second.as_const(); + Pass::call_on_module(design, mod, v.decode_string()); + } + } + else if (args.size() < 2) log_cmd_error("Missing script file.\n"); else if (args.size() == 2) run_frontend(args[1], "script", design); diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex index 2feb0f1cb..3009bf2c0 100644 --- a/manual/CHAPTER_Overview.tex +++ b/manual/CHAPTER_Overview.tex @@ -331,8 +331,9 @@ to update {\tt \textbackslash{}q}. An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and exactly one RTLIL::CaseRule object, which is called the {\it root case}. -An RTLIL::SyncRule object contains an (optional) synchronization condition -(signal and edge-type) and zero or more assignments (RTLIL::SigSig). +An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type) and zero or +more assignments (RTLIL::SigSig). The {\tt always} synchronization condition is used to break combinatorial +loops when a latch should be inferred instead. An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig) and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a @@ -350,6 +351,18 @@ and this bit is a one (the second ``1'').} for {\tt \textbackslash{}reset == 1} sets {\tt \$0\textbackslash{}q[0:0]} to the value of {\tt \textbackslash{}d} if {\tt \textbackslash{}enable} is active (lines $6 \dots 11$). +A case can specify zero or more compare values that will determine whether it matches. Each of the compare values +must be the exact same width as the control signal. When more than one compare value is specified, the case matches +if any of them matches the control signal; when zero compare values are specified, the case always matches (i.e. +it is the default case). + +A switch prioritizes cases from first to last: multiple cases can match, but only the first matched case becomes +active. This normally synthesizes to a priority encoder. The {\tt parallel\_case} attribute allows passes to assume +that no more than one case will match, and {\tt full\_case} attribute allows passes to assume that exactly one +case will match; if these invariants are ever dynamically violated, the behavior is undefined. These attributes +are useful when an invariant invisible to the synthesizer causes the control signal to never take certain +bit patterns. + The lines $13 \dots 16$ then cause {\tt \textbackslash{}q} to be updated whenever there is a positive clock edge on {\tt \textbackslash{}clock} or {\tt \textbackslash{}reset}. diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 09f934040..66d661fa1 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -779,6 +779,9 @@ class WClass: #if self.link_type != link_types.pointer: text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{" + text += "\n\t\t\tif(ref == nullptr){" + text += "\n\t\t\t\tthrow std::runtime_error(\"" + self.name + " does not exist.\");" + text += "\n\t\t\t}" text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));" if self.link_type == link_types.pointer: text += "\n\t\t\tret->ref_obj = ref;" @@ -2026,7 +2029,6 @@ def gen_wrappers(filename, debug_level_ = 0): #include <boost/python/wrapper.hpp> #include <boost/python/call.hpp> #include <boost/python.hpp> -#include <boost/log/exceptions.hpp> USING_YOSYS_NAMESPACE @@ -2060,7 +2062,6 @@ namespace YOSYS_PYTHON { Yosys::log_streams.push_back(&std::cout); Yosys::log_error_stderr = true; Yosys::yosys_setup(); - Yosys::yosys_banner(); } } diff --git a/passes/cmds/blackbox.cc b/passes/cmds/blackbox.cc index 6094f8f16..d09ed872e 100644 --- a/passes/cmds/blackbox.cc +++ b/passes/cmds/blackbox.cc @@ -23,7 +23,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct BlackboxPass : public Pass { - BlackboxPass() : Pass("blackbox", "change type of cells in the design") { } + BlackboxPass() : Pass("blackbox", "convert modules into blackbox modules") { } void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 038ab7c7c..5a47988ec 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -51,14 +51,14 @@ struct BugpointPass : public Pass { log(" only consider crashes that place this string in the log file.\n"); log("\n"); log(" -fast\n"); - log(" run `clean -purge` after each minimization step. converges faster, but\n"); - log(" produces larger testcases, and may fail to produce any testcase at all if\n"); - log(" the crash is related to dangling wires.\n"); + log(" run `proc_clean; clean -purge` after each minimization step. converges\n"); + log(" faster, but produces larger testcases, and may fail to produce any\n"); + log(" testcase at all if the crash is related to dangling wires.\n"); log("\n"); log(" -clean\n"); - log(" run `clean -purge` before checking testcase and after finishing. produces\n"); - log(" smaller and more useful testcases, but may fail to produce any testcase\n"); - log(" at all if the crash is related to dangling wires.\n"); + log(" run `proc_clean; clean -purge` before checking testcase and after\n"); + log(" finishing. produces smaller and more useful testcases, but may fail to\n"); + log(" produce any testcase at all if the crash is related to dangling wires.\n"); log("\n"); log(" -modules\n"); log(" try to remove modules.\n"); @@ -72,6 +72,12 @@ struct BugpointPass : public Pass { log(" -connections\n"); log(" try to reconnect ports to 'x.\n"); log("\n"); + log(" -assigns\n"); + log(" try to remove process assigns from cases.\n"); + log("\n"); + log(" -updates\n"); + log(" try to remove process updates from syncs.\n"); + log("\n"); } bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script) @@ -110,6 +116,7 @@ struct BugpointPass : public Pass { RTLIL::Design *design_copy = new RTLIL::Design; for (auto &it : design->modules_) design_copy->add(it.second->clone()); + Pass::call(design_copy, "proc_clean -quiet"); Pass::call(design_copy, "clean -purge"); if (do_delete) @@ -117,7 +124,7 @@ struct BugpointPass : public Pass { return design_copy; } - RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections) + RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections, bool assigns, bool updates) { RTLIL::Design *design_copy = new RTLIL::Design; for (auto &it : design->modules_) @@ -225,6 +232,59 @@ struct BugpointPass : public Pass { } } } + if (assigns) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_blackbox_attribute()) + continue; + + for (auto &pr : mod->processes) + { + vector<RTLIL::CaseRule*> cases = {&pr.second->root_case}; + while (!cases.empty()) + { + RTLIL::CaseRule *cs = cases[0]; + cases.erase(cases.begin()); + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) + { + if (index++ == seed) + { + log("Trying to remove assign %s %s in %s.%s.\n", log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str()); + cs->actions.erase(it); + return design_copy; + } + } + for (auto &sw : cs->switches) + cases.insert(cases.end(), sw->cases.begin(), sw->cases.end()); + } + } + } + } + if (updates) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_blackbox_attribute()) + continue; + + for (auto &pr : mod->processes) + { + for (auto &sy : pr.second->syncs) + { + for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) + { + if (index++ == seed) + { + log("Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str()); + sy->actions.erase(it); + return design_copy; + } + } + } + } + } + } return NULL; } @@ -232,7 +292,7 @@ struct BugpointPass : public Pass { { string yosys_cmd = "yosys", script, grep; bool fast = false, clean = false; - bool modules = false, ports = false, cells = false, connections = false, has_part = false; + bool modules = false, ports = false, cells = false, connections = false, assigns = false, updates = false, has_part = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -277,6 +337,16 @@ struct BugpointPass : public Pass { has_part = true; continue; } + if (args[argidx] == "-assigns") { + assigns = true; + has_part = true; + continue; + } + if (args[argidx] == "-updates") { + updates = true; + has_part = true; + continue; + } break; } extra_args(args, argidx, design); @@ -290,6 +360,8 @@ struct BugpointPass : public Pass { ports = true; cells = true; connections = true; + assigns = true; + updates = true; } if (!design->full_selection()) @@ -305,7 +377,7 @@ struct BugpointPass : public Pass { bool found_something = false, stage2 = false; while (true) { - if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections)) + if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections, assigns, updates)) { simplified = clean_design(simplified, fast, /*do_delete=*/true); diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index f6949c820..3eedc86b8 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -393,44 +393,112 @@ struct SetundefPass : public Pass { ffbits.insert(bit); } - for (auto wire : module->wires()) + auto process_initwires = [&]() { - if (!wire->attributes.count("\\init")) - continue; + dict<Wire*, int> wire_weights; - for (auto bit : sigmap(wire)) - ffbits.erase(bit); + for (auto wire : initwires) + { + int weight = 0; - initwires.insert(wire); - } + for (auto bit : sigmap(wire)) + weight += ffbits.count(bit) ? +1 : -1; + + wire_weights[wire] = weight; + } + + initwires.sort([&](Wire *a, Wire *b) { return wire_weights.at(a) > wire_weights.at(b); }); + + for (auto wire : initwires) + { + Const &initval = wire->attributes["\\init"]; + initval.bits.resize(GetSize(wire), State::Sx); + + for (int i = 0; i < GetSize(wire); i++) { + SigBit bit = sigmap(SigBit(wire, i)); + if (initval[i] == State::Sx && ffbits.count(bit)) { + initval[i] = worker.next_bit(); + ffbits.erase(bit); + } + } + + if (initval.is_fully_undef()) + wire->attributes.erase("\\init"); + } + + initwires.clear(); + }; for (int wire_types = 0; wire_types < 2; wire_types++) - for (auto wire : module->wires()) + { + // prioritize wires that already have an init attribute + if (!ffbits.empty()) { - if (wire->name[0] == (wire_types ? '\\' : '$')) - next_wire: - continue; + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + continue; - for (auto bit : sigmap(wire)) - if (!ffbits.count(bit)) - goto next_wire; + if (!wire->attributes.count("\\init")) + continue; - for (auto bit : sigmap(wire)) - ffbits.erase(bit); + Const &initval = wire->attributes["\\init"]; + initval.bits.resize(GetSize(wire), State::Sx); + + if (initval.is_fully_undef()) { + wire->attributes.erase("\\init"); + continue; + } - initwires.insert(wire); + for (int i = 0; i < GetSize(wire); i++) + if (initval[i] != State::Sx) + ffbits.erase(sigmap(SigBit(wire, i))); + + initwires.insert(wire); + } + + process_initwires(); } - for (auto wire : initwires) - { - Const &initval = wire->attributes["\\init"]; + // next consider wires that completely contain bits to be initialized + if (!ffbits.empty()) + { + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + continue; - for (int i = 0; i < GetSize(wire); i++) - if (GetSize(initval) <= i) - initval.bits.push_back(worker.next_bit()); - else if (initval.bits[i] == State::Sx) - initval.bits[i] = worker.next_bit(); + for (auto bit : sigmap(wire)) + if (!ffbits.count(bit)) + goto next_wire; + + initwires.insert(wire); + + next_wire: + continue; + } + + process_initwires(); + } + + // finally use whatever wire we can find. + if (!ffbits.empty()) + { + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + continue; + + for (auto bit : sigmap(wire)) + if (ffbits.count(bit)) + initwires.insert(wire); + } + + process_initwires(); + } } + + log_assert(ffbits.empty()); } module->rewrite_sigspecs(worker); diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index d22685b62..80b400e0c 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -223,6 +223,33 @@ struct statdata_t log("\n"); log(" Estimated number of LCs: %10d\n", lc_cnt); } + + if (tech == "cmos") + { + int tran_cnt = 0; + bool tran_cnt_exact = true; + + for (auto it : num_cells_by_type) { + auto ctype = it.first; + auto cnum = it.second; + + if (ctype == "$_NOT_") + tran_cnt += 2*cnum; + else if (ctype.in("$_NAND_", "$_NOR_")) + tran_cnt += 4*cnum; + else if (ctype.in("$_AOI3_", "$_OAI3_")) + tran_cnt += 6*cnum; + else if (ctype.in("$_AOI4_", "$_OAI4_")) + tran_cnt += 8*cnum; + else if (ctype.in("$_DFF_P_", "$_DFF_N_")) + tran_cnt += 16*cnum; + else + tran_cnt_exact = false; + } + + log("\n"); + log(" Estimated number of transistors: %10d%s\n", tran_cnt, tran_cnt_exact ? "" : "+"); + } } }; @@ -285,8 +312,8 @@ struct StatPass : public Pass { log(" use cell area information from the provided liberty file\n"); log("\n"); log(" -tech <technology>\n"); - log(" print area estemate for the specified technology. Corrently supported\n"); - log(" calues for <technology>: xilinx\n"); + log(" print area estemate for the specified technology. Currently supported\n"); + log(" values for <technology>: xilinx, cmos\n"); log("\n"); log(" -width\n"); log(" annotate internal cell types with their word width.\n"); @@ -330,7 +357,7 @@ struct StatPass : public Pass { } extra_args(args, argidx, design); - if (techname != "" && techname != "xilinx") + if (techname != "" && techname != "xilinx" && techname != "cmos") log_cmd_error("Unsupported technology: '%s'\n", techname.c_str()); for (auto mod : design->selected_modules()) diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index ee96ace86..1a44bdaec 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -52,7 +52,9 @@ struct TeePass : public Pass { void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<FILE*> backup_log_files, files_to_close; + std::vector<std::ostream*> backup_log_streams; int backup_log_verbose_level = log_verbose_level; + backup_log_streams = log_streams; backup_log_files = log_files; size_t argidx; @@ -60,6 +62,7 @@ struct TeePass : public Pass { { if (args[argidx] == "-q" && files_to_close.empty()) { log_files.clear(); + log_streams.clear(); continue; } if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) { @@ -89,6 +92,7 @@ struct TeePass : public Pass { for (auto cf : files_to_close) fclose(cf); log_files = backup_log_files; + log_streams = backup_log_streams; throw; } @@ -97,6 +101,7 @@ struct TeePass : public Pass { log_verbose_level = backup_log_verbose_level; log_files = backup_log_files; + log_streams = backup_log_streams; } } TeePass; diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index 9613b462b..64a762d7c 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -62,7 +62,7 @@ struct WriteFileFrontend : public Frontend { if (argidx < args.size() && args[argidx].rfind("-", 0) != 0) output_filename = args[argidx++]; else - log_cmd_error("Missing putput filename.\n"); + log_cmd_error("Missing output filename.\n"); extra_args(f, filename, args, argidx); diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 24e64a9b2..213437c01 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -591,6 +591,9 @@ struct HierarchyPass : public Pass { log(" module instances when the width does not match the module port. This\n"); log(" option disables this behavior.\n"); log("\n"); + log(" -nodefaults\n"); + log(" do not resolve input port default values\n"); + log("\n"); log(" -nokeep_asserts\n"); log(" per default this pass sets the \"keep\" attribute on all modules\n"); log(" that directly or indirectly contain one or more formal properties.\n"); @@ -645,6 +648,7 @@ struct HierarchyPass : public Pass { bool generate_mode = false; bool keep_positionals = false; bool keep_portwidths = false; + bool nodefaults = false; bool nokeep_asserts = false; std::vector<std::string> generate_cells; std::vector<generate_port_decl_t> generate_ports; @@ -712,6 +716,10 @@ struct HierarchyPass : public Pass { keep_portwidths = true; continue; } + if (args[argidx] == "-nodefaults") { + nodefaults = true; + continue; + } if (args[argidx] == "-nokeep_asserts") { nokeep_asserts = true; continue; @@ -940,6 +948,36 @@ struct HierarchyPass : public Pass { } } + if (!nodefaults) + { + dict<IdString, dict<IdString, Const>> defaults_db; + + for (auto module : design->modules()) + for (auto wire : module->wires()) + if (wire->port_input && wire->attributes.count("\\defaultvalue")) + defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue"); + + for (auto module : design->modules()) + for (auto cell : module->cells()) + { + if (defaults_db.count(cell->type) == 0) + continue; + + if (keep_positionals) { + bool found_positionals = false; + for (auto &conn : cell->connections()) + if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') + found_positionals = true; + if (found_positionals) + continue; + } + + for (auto &it : defaults_db.at(cell->type)) + if (!cell->hasPort(it.first)) + cell->setPort(it.first, it.second); + } + } + std::set<Module*> blackbox_derivatives; std::vector<Module*> design_modules = design->modules(); diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index ddc56d9b5..aa8f94149 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -68,6 +68,10 @@ struct rules_t if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp)); if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks)); if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol)); + + int group = 0; + for (auto e : enable) + if (e > dbits) log_error("Bram %s variant %d group %d has %d enable bits but only %d dbits.\n", log_id(name), variant, group, e, dbits); } vector<portinfo_t> make_portinfos() const diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 220d29295..32b97f27a 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -17,6 +17,7 @@ * */ +#include <algorithm> #include "kernel/yosys.h" #include "kernel/sigtools.h" @@ -182,20 +183,27 @@ struct MemoryDffWorker if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) { - bool enable_invert = mux_cells_a.count(sig_data) != 0; - Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); - SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")); + RTLIL::SigSpec en; + std::vector<RTLIL::SigSpec> check_q; + + do { + bool enable_invert = mux_cells_a.count(sig_data) != 0; + Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); + check_q.push_back(sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"))); + sig_data = sigmap(mux->getPort("\\Y")); + en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)); - sig_data = sigmap(mux->getPort("\\Y")); for (auto bit : sig_data) if (sigbit_users_count[bit] > 1) goto skip_ff_after_read_merging; - if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q) + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && + std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; })) { disconnect_dff(sig_data); cell->setPort("\\CLK", clk_data); - cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + cell->setPort("\\EN", en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en); cell->setPort("\\DATA", sig_data); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 337fee9e4..ea3646330 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -14,5 +14,6 @@ OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o OBJS += passes/opt/pmux2shiftx.o +OBJS += passes/opt/muxpack.o endif diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc new file mode 100644 index 000000000..6697d6ca1 --- /dev/null +++ b/passes/opt/muxpack.cc @@ -0,0 +1,368 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ExclusiveDatabase +{ + Module *module; + const SigMap &sigmap; + + dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev; + + ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap) + { + SigSpec const_sig, nonconst_sig; + SigBit y_port; + pool<Cell*> reduce_or; + for (auto cell : module->cells()) { + if (cell->type == "$eq") { + nonconst_sig = sigmap(cell->getPort("\\A")); + const_sig = sigmap(cell->getPort("\\B")); + if (!const_sig.is_fully_const()) { + if (!nonconst_sig.is_fully_const()) + continue; + std::swap(nonconst_sig, const_sig); + } + y_port = sigmap(cell->getPort("\\Y")); + } + else if (cell->type == "$logic_not") { + nonconst_sig = sigmap(cell->getPort("\\A")); + const_sig = Const(RTLIL::S0, GetSize(nonconst_sig)); + y_port = sigmap(cell->getPort("\\Y")); + } + else if (cell->type == "$reduce_or") { + reduce_or.insert(cell); + continue; + } + else continue; + + log_assert(!nonconst_sig.empty()); + log_assert(!const_sig.empty()); + sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::vector<Const>{const_sig.as_const()}); + } + + for (auto cell : reduce_or) { + nonconst_sig = SigSpec(); + std::vector<Const> values; + SigSpec a_port = sigmap(cell->getPort("\\A")); + for (auto bit : a_port) { + auto it = sig_cmp_prev.find(bit); + if (it == sig_cmp_prev.end()) { + nonconst_sig = SigSpec(); + break; + } + if (nonconst_sig.empty()) + nonconst_sig = it->second.first; + else if (nonconst_sig != it->second.first) { + nonconst_sig = SigSpec(); + break; + } + for (auto value : it->second.second) + values.push_back(value); + } + if (nonconst_sig.empty()) + continue; + y_port = sigmap(cell->getPort("\\Y")); + sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values)); + } + } + + bool query(const SigSpec &sig) const + { + SigSpec nonconst_sig; + pool<Const> const_values; + + for (auto bit : sig.bits()) { + auto it = sig_cmp_prev.find(bit); + if (it == sig_cmp_prev.end()) + return false; + + if (nonconst_sig.empty()) + nonconst_sig = it->second.first; + else if (nonconst_sig != it->second.first) + return false; + + for (auto value : it->second.second) + if (!const_values.insert(value).second) + return false; + } + + return true; + } +}; + + +struct MuxpackWorker +{ + Module *module; + SigMap sigmap; + + int mux_count, pmux_count; + + pool<Cell*> remove_cells; + + dict<SigSpec, Cell*> sig_chain_next; + dict<SigSpec, Cell*> sig_chain_prev; + pool<SigBit> sigbit_with_non_chain_users; + pool<Cell*> chain_start_cells; + pool<Cell*> candidate_cells; + + ExclusiveDatabase excl_db; + + void make_sig_chain_next_prev() + { + for (auto wire : module->wires()) + { + if (wire->port_output || wire->get_bool_attribute("\\keep")) { + for (auto bit : sigmap(wire)) + sigbit_with_non_chain_users.insert(bit); + } + } + + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux") && !cell->get_bool_attribute("\\keep")) + { + SigSpec a_sig = sigmap(cell->getPort("\\A")); + SigSpec b_sig; + if (cell->type == "$mux") + b_sig = sigmap(cell->getPort("\\B")); + SigSpec y_sig = sigmap(cell->getPort("\\Y")); + + if (sig_chain_next.count(a_sig)) + for (auto a_bit : a_sig.bits()) + sigbit_with_non_chain_users.insert(a_bit); + else { + sig_chain_next[a_sig] = cell; + candidate_cells.insert(cell); + } + + if (!b_sig.empty()) { + if (sig_chain_next.count(b_sig)) + for (auto b_bit : b_sig.bits()) + sigbit_with_non_chain_users.insert(b_bit); + else { + sig_chain_next[b_sig] = cell; + candidate_cells.insert(cell); + } + } + + sig_chain_prev[y_sig] = cell; + continue; + } + + for (auto conn : cell->connections()) + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_with_non_chain_users.insert(bit); + } + } + + void find_chain_start_cells() + { + for (auto cell : candidate_cells) + { + log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type)); + + SigSpec a_sig = sigmap(cell->getPort("\\A")); + if (cell->type == "$mux") { + SigSpec b_sig = sigmap(cell->getPort("\\B")); + if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1) + goto start_cell; + + if (!sig_chain_prev.count(a_sig)) + a_sig = b_sig; + } + else if (cell->type == "$pmux") { + if (!sig_chain_prev.count(a_sig)) + goto start_cell; + } + else log_abort(); + + for (auto bit : a_sig.bits()) + if (sigbit_with_non_chain_users.count(bit)) + goto start_cell; + + { + Cell *prev_cell = sig_chain_prev.at(a_sig); + log_assert(prev_cell); + SigSpec s_sig = sigmap(cell->getPort("\\S")); + s_sig.append(sigmap(prev_cell->getPort("\\S"))); + if (!excl_db.query(s_sig)) + goto start_cell; + } + + continue; + + start_cell: + chain_start_cells.insert(cell); + } + } + + vector<Cell*> create_chain(Cell *start_cell) + { + vector<Cell*> chain; + + Cell *c = start_cell; + while (c != nullptr) + { + chain.push_back(c); + + SigSpec y_sig = sigmap(c->getPort("\\Y")); + + if (sig_chain_next.count(y_sig) == 0) + break; + + c = sig_chain_next.at(y_sig); + if (chain_start_cells.count(c) != 0) + break; + } + + return chain; + } + + void process_chain(vector<Cell*> &chain) + { + if (GetSize(chain) < 2) + return; + + int cursor = 0; + while (cursor < GetSize(chain)) + { + int cases = GetSize(chain) - cursor; + + Cell *first_cell = chain[cursor]; + dict<int, SigBit> taps_dict; + + if (cases < 2) { + cursor++; + continue; + } + + Cell *last_cell = chain[cursor+cases-1]; + + log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n", + log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases); + + mux_count += cases; + pmux_count += 1; + + first_cell->type = "$pmux"; + SigSpec b_sig = first_cell->getPort("\\B"); + SigSpec s_sig = first_cell->getPort("\\S"); + + for (int i = 1; i < cases; i++) { + Cell* prev_cell = chain[cursor+i-1]; + Cell* cursor_cell = chain[cursor+i]; + if (sigmap(prev_cell->getPort("\\Y")) == sigmap(cursor_cell->getPort("\\A"))) { + b_sig.append(cursor_cell->getPort("\\B")); + s_sig.append(cursor_cell->getPort("\\S")); + } + else { + log_assert(cursor_cell->type == "$mux"); + b_sig.append(cursor_cell->getPort("\\A")); + s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort("\\S"))); + } + remove_cells.insert(cursor_cell); + } + + first_cell->setPort("\\B", b_sig); + first_cell->setPort("\\S", s_sig); + first_cell->setParam("\\S_WIDTH", GetSize(s_sig)); + first_cell->setPort("\\Y", last_cell->getPort("\\Y")); + + cursor += cases; + } + } + + void cleanup() + { + for (auto cell : remove_cells) + module->remove(cell); + + remove_cells.clear(); + sig_chain_next.clear(); + sig_chain_prev.clear(); + chain_start_cells.clear(); + candidate_cells.clear(); + } + + MuxpackWorker(Module *module) : + module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap) + { + make_sig_chain_next_prev(); + find_chain_start_cells(); + + for (auto c : chain_start_cells) { + vector<Cell*> chain = create_chain(c); + process_chain(chain); + } + + cleanup(); + } +}; + +struct MuxpackPass : public Pass { + MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" muxpack [selection]\n"); + log("\n"); + log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n"); + log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n"); + log("$pmux cells.\n"); + log("\n"); + log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n"); + log("whose select lines are driven by '$eq' cells with other such cells if it can be\n"); + log("certain that their select inputs are mutually exclusive.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + break; + } + extra_args(args, argidx, design); + + int mux_count = 0; + int pmux_count = 0; + + for (auto module : design->selected_modules()) { + MuxpackWorker worker(module); + mux_count += worker.mux_count; + pmux_count += worker.pmux_count; + } + + log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count); + } +} MuxpackPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index a4aca2fee..e9a43e0f3 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -44,7 +44,7 @@ struct OptPass : public Pass { log(" opt_muxtree\n"); log(" opt_reduce [-fine] [-full]\n"); log(" opt_merge [-share_all]\n"); - log(" opt_rmdff [-keepdc]\n"); + log(" opt_rmdff [-keepdc] [-sat]\n"); log(" opt_clean [-purge]\n"); log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" while <changed design>\n"); @@ -54,7 +54,7 @@ struct OptPass : public Pass { log(" do\n"); log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" opt_merge [-share_all]\n"); - log(" opt_rmdff [-keepdc]\n"); + log(" opt_rmdff [-keepdc] [-sat]\n"); log(" opt_clean [-purge]\n"); log(" while <changed design in opt_rmdff>\n"); log("\n"); @@ -112,6 +112,10 @@ struct OptPass : public Pass { opt_rmdff_args += " -keepdc"; continue; } + if (args[argidx] == "-sat") { + opt_rmdff_args += " -sat"; + continue; + } if (args[argidx] == "-share_all") { opt_merge_args += " -share_all"; continue; diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 7011d4602..a8a8e0bc7 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -106,7 +106,7 @@ void rmunused_module_cells(Module *module, bool verbose) if (raw_bit.wire == nullptr) continue; auto bit = sigmap(raw_bit); - if (bit.wire == nullptr) + if (bit.wire == nullptr && ct_all.cell_known(cell->type)) driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict " "for %s between cell %s.%s and constant %s in %s: Resolved using constant.", log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module))); @@ -326,8 +326,8 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) { // do not delete anything with "keep" or module ports or initialized wires } else - if (!purge_mode && check_public_name(wire->name)) { - // do not get rid of public names unless in purge mode + if (!purge_mode && check_public_name(wire->name) && (raw_used_signals.check_any(s1) || used_signals.check_any(s2) || s1 != s2)) { + // do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased } else if (!raw_used_signals.check_any(s1)) { // delete wires that aren't used by anything directly @@ -480,7 +480,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool std::vector<RTLIL::Cell*> delcells; for (auto cell : module->cells()) - if (cell->type.in("$pos", "$_BUF_")) { + if (cell->type.in("$pos", "$_BUF_") && !cell->has_keep_attr()) { bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool(); RTLIL::SigSpec a = cell->getPort("\\A"); RTLIL::SigSpec y = cell->getPort("\\Y"); diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 26855fd70..182f63d99 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -105,7 +105,7 @@ struct OptLutWorker SigSpec lut_input = cell->getPort("\\A"); int lut_arity = 0; - log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell)); + log_debug("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell)); luts.insert(cell); // First, find all dedicated logic we're connected to. This results in an overapproximation @@ -147,15 +147,15 @@ struct OptLutWorker { if (lut_width <= dlogic_conn.first) { - log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); - log(" LUT input A[%d] not present.\n", dlogic_conn.first); + log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT input A[%d] not present.\n", dlogic_conn.first); legal = false; break; } if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second))) { - log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); - log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second))); + log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second))); legal = false; break; } @@ -163,7 +163,7 @@ struct OptLutWorker if (legal) { - log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); lut_legal_dlogics.insert(lut_dlogic); for (auto &dlogic_conn : dlogic_map) lut_dlogic_inputs.insert(dlogic_conn.first); @@ -179,7 +179,7 @@ struct OptLutWorker lut_arity++; } - log(" Cell implements a %d-LUT.\n", lut_arity); + log_debug(" Cell implements a %d-LUT.\n", lut_arity); luts_arity[cell] = lut_arity; luts_dlogics[cell] = lut_legal_dlogics; luts_dlogic_inputs[cell] = lut_dlogic_inputs; @@ -239,28 +239,26 @@ struct OptLutWorker if (const0_match || const1_match || input_match != -1) { - log("Found redundant cell %s.%s.\n", log_id(module), log_id(lut)); + log_debug("Found redundant cell %s.%s.\n", log_id(module), log_id(lut)); SigBit value; if (const0_match) { - log(" Cell evaluates constant 0.\n"); + log_debug(" Cell evaluates constant 0.\n"); value = State::S0; } if (const1_match) { - log(" Cell evaluates constant 1.\n"); + log_debug(" Cell evaluates constant 1.\n"); value = State::S1; } if (input_match != -1) { - log(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match])); + log_debug(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match])); value = lut_inputs[input_match]; } if (lut_dlogic_inputs.size()) - { - log(" Not eliminating cell (connected to dedicated logic).\n"); - } + log_debug(" Not eliminating cell (connected to dedicated logic).\n"); else { SigSpec lut_output = lut->getPort("\\Y"); @@ -323,11 +321,11 @@ struct OptLutWorker int lutB_arity = luts_arity[lutB]; pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB]; - log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB)); + log_debug("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB)); if (index.query_is_output(lutA->getPort("\\Y"))) { - log(" Not combining LUTs (cascade connection feeds module output).\n"); + log_debug(" Not combining LUTs (cascade connection feeds module output).\n"); continue; } @@ -353,67 +351,51 @@ struct OptLutWorker int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size(); if (lutA_dlogic_inputs.size()) - log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size()); + log_debug(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size()); else - log(" Cell A is a %d-LUT. ", lutA_arity); + log_debug(" Cell A is a %d-LUT. ", lutA_arity); if (lutB_dlogic_inputs.size()) - log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size()); + log_debug("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size()); else - log("Cell B is a %d-LUT.\n", lutB_arity); - log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity); + log_debug("Cell B is a %d-LUT.\n", lutB_arity); + log_debug(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity); const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B; int combine_mask = 0; if (lutM_arity > lutA_width) - { - log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n"); - } + log_debug(" Not combining LUTs into cell A (combined LUT wider than cell A).\n"); else if (lutB_dlogic_inputs.size() > 0) - { - log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n"); - } + log_debug(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n"); else if (lutB->get_bool_attribute("\\lut_keep")) - { - log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n"); - } + log_debug(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n"); else - { combine_mask |= COMBINE_A; - } if (lutM_arity > lutB_width) - { - log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n"); - } + log_debug(" Not combining LUTs into cell B (combined LUT wider than cell B).\n"); else if (lutA_dlogic_inputs.size() > 0) - { - log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n"); - } + log_debug(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n"); else if (lutA->get_bool_attribute("\\lut_keep")) - { - log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n"); - } + log_debug(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n"); else - { combine_mask |= COMBINE_B; - } int combine = combine_mask; if (combine == COMBINE_EITHER) { - log(" Can combine into either cell.\n"); + log_debug(" Can combine into either cell.\n"); if (lutA_arity == 1) { - log(" Cell A is a buffer or inverter, combining into cell B.\n"); + log_debug(" Cell A is a buffer or inverter, combining into cell B.\n"); combine = COMBINE_B; } else if (lutB_arity == 1) { - log(" Cell B is a buffer or inverter, combining into cell A.\n"); + log_debug(" Cell B is a buffer or inverter, combining into cell A.\n"); combine = COMBINE_A; } else { - log(" Arbitrarily combining into cell A.\n"); + log_debug(" Arbitrarily combining into cell A.\n"); combine = COMBINE_A; } } @@ -423,7 +405,7 @@ struct OptLutWorker pool<int> lutM_dlogic_inputs; if (combine == COMBINE_A) { - log(" Combining LUTs into cell A.\n"); + log_debug(" Combining LUTs into cell A.\n"); lutM = lutA; lutM_inputs = lutA_inputs; lutM_dlogic_inputs = lutA_dlogic_inputs; @@ -432,7 +414,7 @@ struct OptLutWorker } else if (combine == COMBINE_B) { - log(" Combining LUTs into cell B.\n"); + log_debug(" Combining LUTs into cell B.\n"); lutM = lutB; lutM_inputs = lutB_inputs; lutM_dlogic_inputs = lutB_dlogic_inputs; @@ -441,7 +423,7 @@ struct OptLutWorker } else { - log(" Cannot combine LUTs.\n"); + log_debug(" Cannot combine LUTs.\n"); continue; } @@ -466,17 +448,17 @@ struct OptLutWorker if (input_unused && lutR_unique.size()) { SigBit new_input = lutR_unique.pop(); - log(" Connecting input %d as %s.\n", i, log_signal(new_input)); + log_debug(" Connecting input %d as %s.\n", i, log_signal(new_input)); lutM_new_inputs.push_back(new_input); } else if (sigmap(lutM_input[i]) == lutA_output) { - log(" Disconnecting cascade input %d.\n", i); + log_debug(" Disconnecting cascade input %d.\n", i); lutM_new_inputs.push_back(SigBit()); } else { - log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i])); + log_debug(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i])); lutM_new_inputs.push_back(lutM_input[i]); } } @@ -494,9 +476,9 @@ struct OptLutWorker lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs); } - log(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str()); - log(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str()); - log(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str()); + log_debug(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str()); + log_debug(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str()); + log_debug(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str()); lutM->setParam("\\LUT", lutM_new_table); lutM->setPort("\\A", lutM_new_inputs); diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 2abffa2a9..be6ac2d30 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -17,19 +17,24 @@ * */ +#include "kernel/log.h" #include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/satgen.h" #include "kernel/sigtools.h" -#include "kernel/log.h" -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN SigMap assign_map, dff_init_map; SigSet<RTLIL::Cell*> mux_drivers; +dict<SigBit, RTLIL::Cell*> bit2driver; dict<SigBit, pool<SigBit>> init_attributes; + bool keepdc; +bool sat; void remove_init_attr(SigSpec sig) { @@ -292,8 +297,8 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) sig_q = dff->getPort("\\Q"); sig_c = dff->getPort("\\C"); sig_e = dff->getPort("\\E"); - val_cp = RTLIL::Const(dff->type[6] == 'P', 1); - val_ep = RTLIL::Const(dff->type[7] == 'P', 1); + val_cp = RTLIL::Const(dff->type[7] == 'P', 1); + val_ep = RTLIL::Const(dff->type[8] == 'P', 1); } else if (dff->type == "$ff") { sig_d = dff->getPort("\\D"); @@ -452,12 +457,84 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) dff->unsetPort("\\E"); } + if (sat && has_init && (!sig_r.size() || val_init == val_rv)) + { + bool removed_sigbits = false; + + ezSatPtr ez; + SatGen satgen(ez.get(), &assign_map); + pool<Cell*> sat_cells; + + std::function<void(Cell*)> sat_import_cell = [&](Cell *c) { + if (!sat_cells.insert(c).second) + return; + if (!satgen.importCell(c)) + return; + for (auto &conn : c->connections()) { + if (!c->input(conn.first)) + continue; + for (auto bit : assign_map(conn.second)) + if (bit2driver.count(bit)) + sat_import_cell(bit2driver.at(bit)); + } + }; + + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + for (int position = 0; position < GetSize(sig_d); position += 1) { + RTLIL::SigBit q_sigbit = sig_q[position]; + RTLIL::SigBit d_sigbit = sig_d[position]; + + if ((!q_sigbit.wire) || (!d_sigbit.wire)) + continue; + + if (!bit2driver.count(d_sigbit)) + continue; + + sat_import_cell(bit2driver.at(d_sigbit)); + + RTLIL::State sigbit_init_val = val_init[position]; + if (sigbit_init_val != State::S0 && sigbit_init_val != State::S1) + continue; + + int init_sat_pi = satgen.importSigSpec(sigbit_init_val).front(); + int q_sat_pi = satgen.importSigBit(q_sigbit); + int d_sat_pi = satgen.importSigBit(d_sigbit); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (!counter_example_found) + { + log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", sigbit_init_val ? 1 : 0, + position, log_id(dff), log_id(dff->type), log_id(mod)); + + SigSpec tmp = dff->getPort("\\D"); + tmp[position] = sigbit_init_val; + dff->setPort("\\D", tmp); + + removed_sigbits = true; + } + } + + if (removed_sigbits) { + handle_dff(mod, dff); + return true; + } + } + + return false; delete_dff: log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod)); remove_init_attr(dff->getPort("\\Q")); mod->remove(dff); + + for (auto &entry : bit2driver) + if (entry.second == dff) + bit2driver.erase(entry.first); + return true; } @@ -467,11 +544,15 @@ struct OptRmdffPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_rmdff [-keepdc] [selection]\n"); + log(" opt_rmdff [-keepdc] [-sat] [selection]\n"); log("\n"); log("This pass identifies flip-flops with constant inputs and replaces them with\n"); log("a constant driver.\n"); log("\n"); + log(" -sat\n"); + log(" additionally invoke SAT solver to detect and remove flip-flops (with \n"); + log(" non-constant inputs) that can also be replaced with a constant driver\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -479,6 +560,7 @@ struct OptRmdffPass : public Pass { log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); keepdc = false; + sat = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -486,18 +568,22 @@ struct OptRmdffPass : public Pass { keepdc = true; continue; } + if (args[argidx] == "-sat") { + sat = true; + continue; + } break; } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - { + for (auto module : design->selected_modules()) { pool<SigBit> driven_bits; dict<SigBit, State> init_bits; assign_map.set(module); dff_init_map.set(module); mux_drivers.clear(); + bit2driver.clear(); init_attributes.clear(); for (auto wire : module->wires()) @@ -522,17 +608,21 @@ struct OptRmdffPass : public Pass { driven_bits.insert(bit); } } - mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; std::vector<RTLIL::IdString> dffsr_list; std::vector<RTLIL::IdString> dlatch_list; for (auto cell : module->cells()) { - for (auto &conn : cell->connections()) - if (cell->output(conn.first) || !cell->known()) - for (auto bit : assign_map(conn.second)) + for (auto &conn : cell->connections()) { + bool is_output = cell->output(conn.first); + if (is_output || !cell->known()) + for (auto bit : assign_map(conn.second)) { + if (is_output) + bit2driver[bit] = cell; driven_bits.insert(bit); + } + } if (cell->type == "$mux" || cell->type == "$pmux") { if (cell->getPort("\\A").size() == cell->getPort("\\B").size()) @@ -604,6 +694,7 @@ struct OptRmdffPass : public Pass { assign_map.clear(); mux_drivers.clear(); + bit2driver.clear(); init_attributes.clear(); if (total_count || total_initdrv) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 29870f510..65d8b8f32 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -221,6 +221,9 @@ struct Pmux2ShiftxPass : public Pass { log(" select strategy for one-hot encoded control signals\n"); log(" default: pmux\n"); log("\n"); + log(" -norange\n"); + log(" disable $sub inference for \"range decoders\"\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -230,6 +233,7 @@ struct Pmux2ShiftxPass : public Pass { bool optimize_onehot = true; bool verbose = false; bool verbose_onehot = false; + bool norange = false; log_header(design, "Executing PMUX2SHIFTX pass.\n"); @@ -270,6 +274,10 @@ struct Pmux2ShiftxPass : public Pass { verbose_onehot = true; continue; } + if (args[argidx] == "-norange") { + norange = true; + continue; + } break; } extra_args(args, argidx, design); @@ -559,7 +567,7 @@ struct Pmux2ShiftxPass : public Pass { int this_inv_delta = this_maxval - this_minval; bool this_inv = false; - if (this_delta != this_inv_delta) + if (!norange && this_delta != this_inv_delta) this_inv = this_inv_delta < this_delta; else if (this_maxval != this_inv_maxval) this_inv = this_inv_maxval < this_maxval; @@ -574,7 +582,7 @@ struct Pmux2ShiftxPass : public Pass { if (best_src_col < 0) this_is_better = true; - else if (this_delta != best_delta) + else if (!norange && this_delta != best_delta) this_is_better = this_delta < best_delta; else if (this_maxval != best_maxval) this_is_better = this_maxval < best_maxval; @@ -656,7 +664,7 @@ struct Pmux2ShiftxPass : public Pass { // check density percentages Const offset(State::S0, GetSize(sig)); - if (absolute_density < min_density && range_density >= min_density) + if (!norange && absolute_density < min_density && range_density >= min_density) { offset = Const(min_choice, GetSize(sig)); log(" offset: %s\n", log_signal(offset)); diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index 397fe46a1..4b56979f8 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -1,5 +1,6 @@ OBJS += passes/proc/proc.o +OBJS += passes/proc/proc_prune.o OBJS += passes/proc/proc_clean.o OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_init.o @@ -7,4 +8,3 @@ OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o - diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index ef7cb0f71..a5b4a3112 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -37,6 +37,7 @@ struct ProcPass : public Pass { log("\n"); log(" proc_clean\n"); log(" proc_rmdead\n"); + log(" proc_prune\n"); log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); @@ -83,6 +84,7 @@ struct ProcPass : public Pass { Pass::call(design, "proc_clean"); if (!ifxmode) Pass::call(design, "proc_rmdead"); + Pass::call(design, "proc_prune"); Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index b69eba3f9..d069f152a 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -172,7 +172,7 @@ restart_proc_arst: sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; } for (auto &action : sync->actions) { - RTLIL::SigSpec rspec = action.second; + RTLIL::SigSpec rspec = assign_map(action.second); RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); for (int i = 0; i < GetSize(rspec); i++) if (rspec[i].wire == NULL) diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 52141a8ec..97f4c6573 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -143,7 +143,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m YOSYS_NAMESPACE_END PRIVATE_NAMESPACE_BEGIN -void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) +void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool quiet) { int count = 0; bool did_something = true; @@ -160,7 +160,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) did_something = false; proc_clean_case(&proc->root_case, did_something, count, -1); } - if (count > 0) + if (count > 0 && !quiet) log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str()); total_count += count; } @@ -171,7 +171,10 @@ struct ProcCleanPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" proc_clean [selection]\n"); + log(" proc_clean [options] [selection]\n"); + log("\n"); + log(" -quiet\n"); + log(" do not print any messages.\n"); log("\n"); log("This pass removes empty parts of processes and ultimately removes a process\n"); log("if it contains only empty structures.\n"); @@ -180,9 +183,20 @@ struct ProcCleanPass : public Pass { void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { int total_count = 0; - log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + bool quiet = false; + + if (find(args.begin(), args.end(), "-quiet") == args.end()) + log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); - extra_args(args, 1, design); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-quiet") { + quiet = true; + continue; + } + } + extra_args(args, argidx, design); for (auto mod : design->modules()) { std::vector<RTLIL::IdString> delme; @@ -191,10 +205,11 @@ struct ProcCleanPass : public Pass { for (auto &proc_it : mod->processes) { if (!design->selected(mod, proc_it.second)) continue; - proc_clean(mod, proc_it.second, total_count); + proc_clean(mod, proc_it.second, total_count, quiet); if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 && proc_it.second->root_case.actions.size() == 0) { - log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); + if (!quiet) + log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); delme.push_back(proc_it.first); } } @@ -204,7 +219,8 @@ struct ProcCleanPass : public Pass { } } - log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); + if (!quiet) + log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); } } ProcCleanPass; diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index e2dc07e53..462a384b7 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -26,21 +26,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) -{ - log_assert(rule.compare.size() == 0); - - while (1) { - RTLIL::SigSpec tmp = sig; - for (auto &it : rule.actions) - tmp.replace(it.first, it.second); - if (tmp == sig) - break; - sig = tmp; - } -} - -void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc) { bool found_init = false; @@ -53,9 +39,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) for (auto &action : sync->actions) { RTLIL::SigSpec lhs = action.first; - RTLIL::SigSpec rhs = action.second; - - proc_get_const(rhs, proc->root_case); + RTLIL::SigSpec rhs = sigmap(action.second); if (!rhs.is_fully_const()) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); @@ -120,10 +104,12 @@ struct ProcInitPass : public Pass { extra_args(args, 1, design); for (auto mod : design->modules()) - if (design->selected(mod)) + if (design->selected(mod)) { + SigMap sigmap(mod); for (auto &proc_it : mod->processes) if (design->selected(mod, proc_it.second)) - proc_init(mod, proc_it.second); + proc_init(mod, sigmap, proc_it.second); + } } } ProcInitPass; diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index aac0b121c..d029282fd 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -144,7 +144,13 @@ struct SnippetSwCache } }; -RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode) +void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs) +{ + cell->attributes = sw->attributes; + cell->add_strpool_attribute("\\src", cs->get_strpool_attribute("\\src")); +} + +RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode) { std::stringstream sstr; sstr << "$procmux$" << (autoidx++); @@ -173,7 +179,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s { // create compare cell RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); - eq_cell->attributes = sw->attributes; + apply_attrs(eq_cell, sw, cs); eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0); @@ -199,7 +205,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s // reduce cmp vector to one logic signal RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or"); - any_cell->attributes = sw->attributes; + apply_attrs(any_cell, sw, cs); any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width); @@ -212,7 +218,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(ctrl_wire); } -RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) +RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode) { log_assert(when_signal.size() == else_signal.size()); @@ -224,7 +230,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return when_signal; // compare results - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode); if (ctrl_sig.size() == 0) return when_signal; log_assert(ctrl_sig.size() == 1); @@ -234,7 +240,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s // create the multiplexer itself RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux"); - mux_cell->attributes = sw->attributes; + apply_attrs(mux_cell, sw, cs); mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size()); mux_cell->setPort("\\A", else_signal); @@ -246,7 +252,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(result_wire); } -void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) +void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); @@ -254,7 +260,7 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve if (when_signal == last_mux_cell->getPort("\\A")) return; - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode); log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; @@ -395,9 +401,9 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d RTLIL::CaseRule *cs2 = sw->cases[case_idx]; RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) - append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, cs2, ifxmode); else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode); } } diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc new file mode 100644 index 000000000..9e00b0a8a --- /dev/null +++ b/passes/proc/proc_prune.cc @@ -0,0 +1,158 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2019 whitequark <whitequark@whitequark.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct PruneWorker +{ + RTLIL::Module *module; + SigMap sigmap; + + int removed_count = 0, promoted_count = 0; + + PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {} + + pool<RTLIL::SigBit> do_switch(RTLIL::SwitchRule *sw, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected) + { + pool<RTLIL::SigBit> all_assigned; + bool full_case = sw->get_bool_attribute("\\full_case"); + bool first = true; + for (auto it : sw->cases) { + if (it->compare.empty()) + full_case = true; + pool<RTLIL::SigBit> case_assigned = do_case(it, assigned, affected); + if (first) { + first = false; + all_assigned = case_assigned; + } else { + for (auto &bit : all_assigned) + if (!case_assigned[bit]) + all_assigned.erase(bit); + } + } + if (full_case) + assigned.insert(all_assigned.begin(), all_assigned.end()); + return assigned; + } + + pool<RTLIL::SigBit> do_case(RTLIL::CaseRule *cs, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected, + bool root = false) + { + for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) { + pool<RTLIL::SigBit> sw_assigned = do_switch((*it), assigned, affected); + assigned.insert(sw_assigned.begin(), sw_assigned.end()); + } + pool<RTLIL::SigSig> remove; + for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) { + RTLIL::SigSpec lhs = sigmap(it->first); + bool redundant = true; + for (auto &bit : lhs) { + if (bit.wire && !assigned[bit]) { + redundant = false; + break; + } + } + if (redundant) { + removed_count++; + remove.insert(*it); + } else { + if (root) { + bool promotable = true; + for (auto &bit : lhs) { + if (bit.wire && affected[bit]) { + promotable = false; + break; + } + } + if (promotable) { + promoted_count++; + module->connect(*it); + remove.insert(*it); + } + } + for (auto &bit : lhs) + if (bit.wire) + assigned.insert(bit); + for (auto &bit : lhs) + if (bit.wire) + affected.insert(bit); + } + } + for (auto it = cs->actions.begin(); it != cs->actions.end(); ) { + if (remove[*it]) { + it = cs->actions.erase(it); + } else it++; + } + return assigned; + } + + void do_process(RTLIL::Process *pr) + { + pool<RTLIL::SigBit> affected; + do_case(&pr->root_case, {}, affected, /*root=*/true); + } +}; + +struct ProcPrunePass : public Pass { + ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_prune [selection]\n"); + log("\n"); + log("This pass identifies assignments in processes that are always overwritten by\n"); + log("a later assignment to the same signal and removes them.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + int total_removed_count = 0, total_promoted_count = 0; + log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n"); + + extra_args(args, 1, design); + + for (auto mod : design->modules()) { + if (!design->selected(mod)) + continue; + PruneWorker worker(mod); + for (auto &proc_it : mod->processes) { + if (!design->selected(mod, proc_it.second)) + continue; + worker.do_process(proc_it.second); + } + total_removed_count += worker.removed_count; + total_promoted_count += worker.promoted_count; + } + + log("Removed %d redundant assignment%s.\n", + total_removed_count, total_removed_count == 1 ? "" : "s"); + log("Promoted %d assignment%s to connection%s.\n", + total_promoted_count, total_promoted_count == 1 ? "" : "s", total_promoted_count == 1 ? "" : "s"); + } +} ProcPrunePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index 509cb0ba9..3b432c461 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -180,7 +180,7 @@ struct AssertpmuxWorker }; struct AssertpmuxPass : public Pass { - AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { } + AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { } void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -195,8 +195,8 @@ struct AssertpmuxPass : public Pass { log("\n"); log(" -always\n"); log(" usually the $pmux condition is only checked when the $pmux output\n"); - log(" is used be the mux tree it drives. this option will deactivate this\n"); - log(" additional constrained and check the $pmux condition always.\n"); + log(" is used by the mux tree it drives. this option will deactivate this\n"); + log(" additional constraint and check the $pmux condition always.\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 49ec795d3..4bb4aa047 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -253,6 +253,13 @@ struct Clk2fflogicPass : public Pass { SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); Const rstval = cell->parameters["\\ARST_VALUE"]; + Wire *past_arst = module->addWire(NEW_ID); + module->addFf(NEW_ID, arst, past_arst); + if (cell->parameters["\\ARST_POLARITY"].as_bool()) + arst = module->LogicOr(NEW_ID, arst, past_arst); + else + arst = module->LogicAnd(NEW_ID, arst, past_arst); + if (cell->parameters["\\ARST_POLARITY"].as_bool()) module->addMux(NEW_ID, qval, rstval, arst, sig_q); else diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 048aec7f3..b4549bc39 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -24,7 +24,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CutpointPass : public Pass { - CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { } + CutpointPass() : Pass("cutpoint", "adds formal cut points to the design") { } void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index f64d99dc2..00c098542 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -332,7 +332,7 @@ struct FmcombinePass : public Pass { gate_cell = module->cell(gate_name); if (gate_cell == nullptr) - log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module)); + log_cmd_error("Gate cell %s not found in module %s.\n", log_id(gate_name), log_id(module)); } else { @@ -351,7 +351,7 @@ struct FmcombinePass : public Pass { if (!gold_cell->parameters.empty()) log_cmd_error("Gold cell has unresolved instance parameters.\n"); if (!gate_cell->parameters.empty()) - log_cmd_error("Gold cell has unresolved instance parameters.\n"); + log_cmd_error("Gate cell has unresolved instance parameters.\n"); FmcombineWorker worker(design, gold_cell->type, opts); worker.generate(); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index cbba738f0..e4654d835 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -659,6 +659,7 @@ struct SatHelper void dump_model_to_vcd(std::string vcd_file_name) { + rewrite_filename(vcd_file_name); FILE *f = fopen(vcd_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno)); @@ -761,6 +762,7 @@ struct SatHelper void dump_model_to_json(std::string json_file_name) { + rewrite_filename(json_file_name); FILE *f = fopen(json_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno)); @@ -1505,6 +1507,7 @@ struct SatPass : public Pass { { if (!cnf_file_name.empty()) { + rewrite_filename(cnf_file_name); FILE *f = fopen(cnf_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); @@ -1608,6 +1611,7 @@ struct SatPass : public Pass { if (!cnf_file_name.empty()) { + rewrite_filename(cnf_file_name); FILE *f = fopen(cnf_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 53e248adf..4c3022c70 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -88,6 +88,8 @@ struct SimInstance SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : shared(shared), module(module), instance(instance), parent(parent), sigmap(module) { + log_assert(module); + if (parent) { log_assert(parent->children.count(instance) == 0); parent->children[instance] = this; @@ -848,6 +850,9 @@ struct SimPass : public Pass { if (design->full_selection()) { top_mod = design->top_module(); + + if (!top_mod) + log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n"); } else { auto mods = design->selected_whole_modules(); if (GetSize(mods) != 1) diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cf9e198ad..56f05eca4 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -7,8 +7,10 @@ OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) OBJS += passes/techmap/abc.o +OBJS += passes/techmap/abc9.o ifneq ($(ABCEXTERNAL),) passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif endif diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 5b19d84fb..65c7d1bb8 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1172,8 +1172,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin continue; } } - - cell_stats[RTLIL::unescape_id(c->type)]++; + else + cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type == "\\_const0_" || c->type == "\\_const1_") { RTLIL::SigSig conn; @@ -1453,7 +1453,7 @@ struct AbcPass : public Pass { log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); log("ABC on logic snippets extracted from your design. You will not get any useful\n"); log("output when passing an ABC script that writes a file. Instead write your full\n"); - log("design as BLIF file with write_blif and the load that into ABC externally if\n"); + log("design as BLIF file with write_blif and then load that into ABC externally if\n"); log("you want to use ABC to convert your design into another format.\n"); log("\n"); log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc new file mode 100644 index 000000000..658bb1225 --- /dev/null +++ b/passes/techmap/abc9.cc @@ -0,0 +1,1258 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] ABC +// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification +// http://www.eecs.berkeley.edu/~alanmi/abc/ + +#if 0 +// Based on &flow3 - better QoR but more experimental +#define ABC_COMMAND_LUT "&st; &ps -l; &sweep -v; &scorr; " \ + "&st; &if {W}; &save; &st; &syn2; &if {W} -v; &save; &load; "\ + "&st; &if -g -K 6; &dch -f; &if {W} -v; &save; &load; "\ + "&st; &if -g -K 6; &synch2; &if {W} -v; &save; &load; "\ + "&mfs; &ps -l" +#else +#define ABC_COMMAND_LUT "&st; &scorr; &sweep; &dc2; &st; &dch -f; &ps; &if {W} {D} -v; &mfs; &ps -l" +#endif + + +#define ABC_FAST_COMMAND_LUT "&st; &if {W} {D}" + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cost.h" +#include "kernel/log.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <cerrno> +#include <sstream> +#include <climits> + +#ifndef _WIN32 +# include <unistd.h> +# include <dirent.h> +#endif + +#include "frontends/aiger/aigerparse.h" +#include "kernel/utils.h" + +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool markgroups; +int map_autoidx; +SigMap assign_map; +RTLIL::Module *module; + +bool clk_polarity, en_polarity; +RTLIL::SigSpec clk_sig, en_sig; + +std::string remap_name(RTLIL::IdString abc_name) +{ + std::stringstream sstr; + sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); + return sstr.str(); +} + +void handle_loops(RTLIL::Design *design) +{ + Pass::call(design, "scc -set_attr abc_scc_id {}"); + + dict<IdString, vector<IdString>> abc_scc_break; + + // For every unique SCC found, (arbitrarily) find the first + // cell in the component, and select (and mark) all its output + // wires + pool<RTLIL::Const> ids_seen; + for (auto cell : module->cells()) { + auto it = cell->attributes.find("\\abc_scc_id"); + if (it != cell->attributes.end()) { + auto r = ids_seen.insert(it->second); + if (r.second) { + for (auto &c : cell->connections_) { + if (c.second.is_fully_const()) continue; + if (cell->output(c.first)) { + SigBit b = c.second.as_bit(); + Wire *w = b.wire; + log_assert(!w->port_input); + w->port_input = true; + w = module->wire(stringf("%s.abci", w->name.c_str())); + if (!w) { + w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire)); + w->port_output = true; + } + else { + log_assert(w->port_input); + log_assert(b.offset < GetSize(w)); + } + w->set_bool_attribute("\\abc_scc_break"); + module->swap_names(b.wire, w); + c.second = RTLIL::SigBit(w, b.offset); + } + } + } + cell->attributes.erase(it); + } + + auto jt = abc_scc_break.find(cell->type); + if (jt == abc_scc_break.end()) { + std::vector<IdString> ports; + RTLIL::Module* box_module = design->module(cell->type); + if (box_module) { + auto ports_csv = box_module->attributes.at("\\abc_scc_break", RTLIL::Const::from_string("")).decode_string(); + for (const auto &port_name : split_tokens(ports_csv, ",")) { + auto port_id = RTLIL::escape_id(port_name); + auto kt = cell->connections_.find(port_id); + if (kt == cell->connections_.end()) + log_error("abc_scc_break attribute value '%s' does not exist as port on module '%s'\n", port_name.c_str(), log_id(box_module)); + ports.push_back(port_id); + } + } + jt = abc_scc_break.insert(std::make_pair(cell->type, std::move(ports))).first; + } + + for (auto port_name : jt->second) { + RTLIL::SigSpec sig; + auto &rhs = cell->connections_.at(port_name); + for (auto b : rhs) { + Wire *w = b.wire; + if (!w) continue; + w->port_output = true; + w->set_bool_attribute("\\abc_scc_break"); + w = module->wire(stringf("%s.abci", w->name.c_str())); + if (!w) { + w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire)); + w->port_input = true; + } + else { + log_assert(b.offset < GetSize(w)); + log_assert(w->port_input); + } + sig.append(RTLIL::SigBit(w, b.offset)); + } + rhs = sig; + } + } + + module->fixup_ports(); +} + +std::string add_echos_to_abc_cmd(std::string str) +{ + std::string new_str, token; + for (size_t i = 0; i < str.size(); i++) { + token += str[i]; + if (str[i] == ';') { + while (i+1 < str.size() && str[i+1] == ' ') + i++; + new_str += "echo + " + token + " " + token + " "; + token.clear(); + } + } + + if (!token.empty()) { + if (!new_str.empty()) + new_str += "echo + " + token + "; "; + new_str += token; + } + + return new_str; +} + +std::string fold_abc_cmd(std::string str) +{ + std::string token, new_str = " "; + int char_counter = 10; + + for (size_t i = 0; i <= str.size(); i++) { + if (i < str.size()) + token += str[i]; + if (i == str.size() || str[i] == ';') { + if (char_counter + token.size() > 75) + new_str += "\n ", char_counter = 14; + new_str += token, char_counter += token.size(); + token.clear(); + } + } + + return new_str; +} + +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ + if (show_tempdir) + return text; + + while (1) { + size_t pos = text.find(tempdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); + } + + std::string selfdir_name = proc_self_dirname(); + if (selfdir_name != "/") { + while (1) { + size_t pos = text.find(selfdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); + } + } + + return text; +} + +struct abc_output_filter +{ + bool got_cr; + int escape_seq_state; + std::string linebuf; + std::string tempdir_name; + bool show_tempdir; + + abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) + { + got_cr = false; + escape_seq_state = 0; + } + + void next_char(char ch) + { + if (escape_seq_state == 0 && ch == '\033') { + escape_seq_state = 1; + return; + } + if (escape_seq_state == 1) { + escape_seq_state = ch == '[' ? 2 : 0; + return; + } + if (escape_seq_state == 2) { + if ((ch < '0' || '9' < ch) && ch != ';') + escape_seq_state = 0; + return; + } + escape_seq_state = 0; + if (ch == '\r') { + got_cr = true; + return; + } + if (ch == '\n') { + log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); + got_cr = false, linebuf.clear(); + return; + } + if (got_cr) + got_cr = false, linebuf.clear(); + linebuf += ch; + } + + void next_line(const std::string &line) + { + //int pi, po; + //if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { + // log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", + // pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", + // po, po_map.count(po) ? po_map.at(po).c_str() : "???"); + // return; + //} + + for (char ch : line) + next_char(ch); + } +}; + +void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, + bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, + bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, + bool show_tempdir, std::string box_file, std::string lut_file, + std::string wire_delay) +{ + module = current_module; + map_autoidx = autoidx++; + + if (clk_str != "$") + { + clk_polarity = true; + clk_sig = RTLIL::SigSpec(); + + en_polarity = true; + en_sig = RTLIL::SigSpec(); + } + + if (!clk_str.empty() && clk_str != "$") + { + if (clk_str.find(',') != std::string::npos) { + int pos = clk_str.find(','); + std::string en_str = clk_str.substr(pos+1); + clk_str = clk_str.substr(0, pos); + if (en_str[0] == '!') { + en_polarity = false; + en_str = en_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) + en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); + } + if (clk_str[0] == '!') { + clk_polarity = false; + clk_str = clk_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) + clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); + } + + if (dff_mode && clk_sig.empty()) + log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); + + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; + if (!cleanup) + tempdir_name[0] = tempdir_name[4] = '_'; + tempdir_name = make_temp_dir(tempdir_name); + log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", + module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); + + std::string abc_script; + + if (!lut_costs.empty()) { + abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); + if (!box_file.empty()) + abc_script += stringf("read_box -v %s; ", box_file.c_str()); + } + else + if (!lut_file.empty()) { + abc_script += stringf("read_lut %s; ", lut_file.c_str()); + if (!box_file.empty()) + abc_script += stringf("read_box -v %s; ", box_file.c_str()); + } + else + log_abort(); + + abc_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); + + if (!script_file.empty()) { + if (script_file[0] == '+') { + for (size_t i = 1; i < script_file.size(); i++) + if (script_file[i] == '\'') + abc_script += "'\\''"; + else if (script_file[i] == ',') + abc_script += " "; + else + abc_script += script_file[i]; + } else + abc_script += stringf("source %s", script_file.c_str()); + } else if (!lut_costs.empty() || !lut_file.empty()) { + //bool all_luts_cost_same = true; + //for (int this_cost : lut_costs) + // if (this_cost != lut_costs.front()) + // all_luts_cost_same = false; + abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; + //if (all_luts_cost_same && !fast_mode) + // abc_script += "; lutpack {S}"; + } else + log_abort(); + + //if (script_file.empty() && !delay_target.empty()) + // for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) + // abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); + + for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + + //for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) + // abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); + + for (size_t pos = abc_script.find("{W}"); pos != std::string::npos; pos = abc_script.find("{W}", pos)) + abc_script = abc_script.substr(0, pos) + wire_delay + abc_script.substr(pos+3); + + abc_script += stringf("; &write %s/output.aig", tempdir_name.c_str()); + abc_script = add_echos_to_abc_cmd(abc_script); + + for (size_t i = 0; i+1 < abc_script.size(); i++) + if (abc_script[i] == ';' && abc_script[i+1] == ' ') + abc_script[i+1] = '\n'; + + FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); + fprintf(f, "%s\n", abc_script.c_str()); + fclose(f); + + if (dff_mode || !clk_str.empty()) + { + if (clk_sig.size() == 0) + log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching"); + else { + log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig)); + if (en_sig.size() != 0) + log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig)); + log("\n"); + } + } + + bool count_output = false; + for (auto port_name : module->ports) { + RTLIL::Wire *port_wire = module->wire(port_name); + log_assert(port_wire); + if (port_wire->port_output) { + count_output = true; + break; + } + } + + log_push(); + + if (count_output) + { + design->selection_stack.emplace_back(false); + RTLIL::Selection& sel = design->selection_stack.back(); + sel.select(module); + + Pass::call(design, "aigmap"); + + handle_loops(design); + + //log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", + // count_gates, GetSize(signal_list), count_input, count_output); + + Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); + + std::string buffer; + std::ifstream ifs; +#if 0 + buffer = stringf("%s/%s", tempdir_name.c_str(), "input.xaig"); + ifs.open(buffer); + if (ifs.fail()) + log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); + buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); + log_assert(!design->module("$__abc9__")); + { + AigerReader reader(design, ifs, "$__abc9__", "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); + reader.parse_xaiger(); + } + ifs.close(); + Pass::call(design, stringf("write_verilog -noexpr -norename")); + design->remove(design->module("$__abc9__")); +#endif + + design->selection_stack.pop_back(); + + // Now 'unexpose' those wires by undoing + // the expose operation -- remove them from PO/PI + // and re-connecting them back together + for (auto wire : module->wires()) { + auto it = wire->attributes.find("\\abc_scc_break"); + if (it != wire->attributes.end()) { + wire->attributes.erase(it); + log_assert(wire->port_output); + wire->port_output = false; + RTLIL::Wire *i_wire = module->wire(wire->name.str() + ".abci"); + log_assert(i_wire); + log_assert(i_wire->port_input); + i_wire->port_input = false; + module->connect(i_wire, wire); + } + } + module->fixup_ports(); + + + log_header(design, "Executing ABC9.\n"); + + if (!lut_costs.empty()) { + buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); + f = fopen(buffer.c_str(), "wt"); + if (f == NULL) + log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); + for (int i = 0; i < GetSize(lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); + fclose(f); + } + + buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); + log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + +#ifndef YOSYS_LINK_ABC + abc_output_filter filt(tempdir_name, show_tempdir); + int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); +#else + // These needs to be mutable, supposedly due to getopt + char *abc_argv[5]; + string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); + abc_argv[0] = strdup(exe_file.c_str()); + abc_argv[1] = strdup("-s"); + abc_argv[2] = strdup("-f"); + abc_argv[3] = strdup(tmp_script_name.c_str()); + abc_argv[4] = 0; + int ret = Abc_RealMain(4, abc_argv); + free(abc_argv[0]); + free(abc_argv[1]); + free(abc_argv[2]); + free(abc_argv[3]); +#endif + if (ret != 0) + log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); + + buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig"); + ifs.open(buffer); + if (ifs.fail()) + log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); + + buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); + log_assert(!design->module("$__abc9__")); + AigerReader reader(design, ifs, "$__abc9__", "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); + reader.parse_xaiger(); + ifs.close(); + +#if 0 + Pass::call(design, stringf("write_verilog -noexpr -norename")); +#endif + + log_header(design, "Re-integrating ABC9 results.\n"); + RTLIL::Module *mapped_mod = design->module("$__abc9__"); + if (mapped_mod == NULL) + log_error("ABC output file does not contain a module `$__abc9__'.\n"); + + pool<RTLIL::SigBit> output_bits; + for (auto &it : mapped_mod->wires_) { + RTLIL::Wire *w = it.second; + RTLIL::Wire *remap_wire = module->addWire(remap_name(w->name), GetSize(w)); + if (markgroups) remap_wire->attributes["\\abcgroup"] = map_autoidx; + if (w->port_output) { + RTLIL::Wire *wire = module->wire(w->name); + log_assert(wire); + for (int i = 0; i < GetSize(w); i++) + output_bits.insert({wire, i}); + } + } + + for (auto &it : module->connections_) { + auto &signal = it.first; + auto bits = signal.bits(); + for (auto &b : bits) + if (output_bits.count(b)) + b = module->addWire(NEW_ID); + signal = std::move(bits); + } + + dict<IdString, bool> abc_box; + vector<RTLIL::Cell*> boxes; + for (const auto &it : module->cells_) { + auto cell = it.second; + if (cell->type.in("$_AND_", "$_NOT_")) { + module->remove(cell); + continue; + } + auto jt = abc_box.find(cell->type); + if (jt == abc_box.end()) { + RTLIL::Module* box_module = design->module(cell->type); + jt = abc_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count("\\abc_box_id"))).first; + } + if (jt->second) + boxes.emplace_back(cell); + } + + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; + dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; + + std::map<std::string, int> cell_stats; + for (auto c : mapped_mod->cells()) + { + toposort.node(c->name); + + RTLIL::Cell *cell = nullptr; + if (c->type == "$_NOT_") { + RTLIL::SigBit a_bit = c->getPort("\\A"); + RTLIL::SigBit y_bit = c->getPort("\\Y"); + bit_users[a_bit].insert(c->name); + bit_drivers[y_bit].insert(c->name); + + if (!a_bit.wire) { + c->setPort("\\Y", module->addWire(NEW_ID)); + RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); + log_assert(wire); + module->connect(RTLIL::SigBit(wire, y_bit.offset), RTLIL::S1); + } + else if (!lut_costs.empty() || !lut_file.empty()) { + RTLIL::Cell* driver_lut = nullptr; + // ABC can return NOT gates that drive POs + if (!a_bit.wire->port_input) { + // If it's not a NOT gate that that comes from a PI directly, + // find the driver LUT and clone that to guarantee that we won't + // increase the max logic depth + // (TODO: Optimise by not cloning unless will increase depth) + RTLIL::IdString driver_name; + if (GetSize(a_bit.wire) == 1) + driver_name = stringf("%s$lut", a_bit.wire->name.c_str()); + else + driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset); + driver_lut = mapped_mod->cell(driver_name); + } + + if (!driver_lut) { + // If a driver couldn't be found (could be from PI or box CI) + // then implement using a LUT + cell = module->addLut(remap_name(stringf("%s$lut", c->name.c_str())), + RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), + RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), + RTLIL::Const::from_string("01")); + bit2sinks[cell->getPort("\\A")].push_back(cell); + cell_stats["$lut"]++; + } + else + not2drivers[c] = driver_lut; + continue; + } + else + log_abort(); + if (cell && markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + continue; + } + cell_stats[RTLIL::unescape_id(c->type)]++; + + RTLIL::Cell *existing_cell = nullptr; + if (c->type == "$lut") { + if (GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT") == RTLIL::Const::from_string("01")) { + SigSpec my_a = module->wires_.at(remap_name(c->getPort("\\A").as_wire()->name)); + SigSpec my_y = module->wires_.at(remap_name(c->getPort("\\Y").as_wire()->name)); + module->connect(my_y, my_a); + if (markgroups) c->attributes["\\abcgroup"] = map_autoidx; + log_abort(); + continue; + } + cell = module->addCell(remap_name(c->name), c->type); + } + else { + existing_cell = module->cell(c->name); + cell = module->addCell(remap_name(c->name), c->type); + module->swap_names(cell, existing_cell); + } + + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + if (existing_cell) { + cell->parameters = existing_cell->parameters; + cell->attributes = existing_cell->attributes; + } + else { + cell->parameters = c->parameters; + cell->attributes = c->attributes; + } + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto c : conn.second.chunks()) { + if (c.width == 0) + continue; + //log_assert(c.width == 1); + if (c.wire) + c.wire = module->wires_.at(remap_name(c.wire->name)); + newsig.append(c); + } + cell->setPort(conn.first, newsig); + + if (cell->input(conn.first)) { + for (auto i : newsig) + bit2sinks[i].push_back(cell); + for (auto i : conn.second) + bit_users[i].insert(c->name); + } + if (cell->output(conn.first)) + for (auto i : conn.second) + bit_drivers[i].insert(c->name); + } + } + + for (auto cell : boxes) + module->remove(cell); + + // Copy connections (and rename) from mapped_mod to module + for (auto conn : mapped_mod->connections()) { + if (!conn.first.is_fully_const()) { + auto chunks = conn.first.chunks(); + for (auto &c : chunks) + c.wire = module->wires_.at(remap_name(c.wire->name)); + conn.first = std::move(chunks); + } + if (!conn.second.is_fully_const()) { + auto chunks = conn.second.chunks(); + for (auto &c : chunks) + if (c.wire) + c.wire = module->wires_.at(remap_name(c.wire->name)); + conn.second = std::move(chunks); + } + module->connect(conn); + } + + for (auto &it : cell_stats) + log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); + int in_wires = 0, out_wires = 0; + + // Stitch in mapped_mod's inputs/outputs into module + for (auto &it : mapped_mod->wires_) { + RTLIL::Wire *w = it.second; + if (!w->port_input && !w->port_output) + continue; + RTLIL::Wire *wire = module->wire(w->name); + log_assert(wire); + RTLIL::Wire *remap_wire = module->wire(remap_name(w->name)); + RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire)); + log_assert(GetSize(signal) >= GetSize(remap_wire)); + + log_assert(w->port_input || w->port_output); + RTLIL::SigSig conn; + if (w->port_input) { + conn.first = remap_wire; + conn.second = signal; + in_wires++; + module->connect(conn); + } + if (w->port_output) { + conn.first = signal; + conn.second = remap_wire; + out_wires++; + module->connect(conn); + } + } + + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + bool no_loops = toposort.sort(); + log_assert(no_loops); + + for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { + RTLIL::Cell *not_cell = mapped_mod->cell(*ii); + log_assert(not_cell); + if (not_cell->type != "$_NOT_") + continue; + auto it = not2drivers.find(not_cell); + if (it == not2drivers.end()) + continue; + RTLIL::Cell *driver_lut = it->second; + RTLIL::SigBit a_bit = not_cell->getPort("\\A"); + RTLIL::SigBit y_bit = not_cell->getPort("\\Y"); + RTLIL::Const driver_mask; + + a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); + y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); + + auto jt = bit2sinks.find(a_bit); + if (jt == bit2sinks.end()) + goto clone_lut; + + for (auto sink_cell : jt->second) + if (sink_cell->type != "$lut") + goto clone_lut; + + // Push downstream LUTs past inverter + for (auto sink_cell : jt->second) { + SigSpec A = sink_cell->getPort("\\A"); + RTLIL::Const mask = sink_cell->getParam("\\LUT"); + int index = 0; + for (; index < GetSize(A); index++) + if (A[index] == a_bit) + break; + log_assert(index < GetSize(A)); + int i = 0; + while (i < GetSize(mask)) { + for (int j = 0; j < (1 << index); j++) + std::swap(mask[i+j], mask[i+j+(1 << index)]); + i += 1 << (index+1); + } + A[index] = y_bit; + sink_cell->setPort("\\A", A); + sink_cell->setParam("\\LUT", mask); + } + + // Since we have rewritten all sinks (which we know + // to be only LUTs) to be after the inverter, we can + // go ahead and clone the LUT with the expectation + // that the original driving LUT will become dangling + // and get cleaned away +clone_lut: + driver_mask = driver_lut->getParam("\\LUT"); + for (auto &b : driver_mask.bits) { + if (b == RTLIL::State::S0) b = RTLIL::State::S1; + else if (b == RTLIL::State::S1) b = RTLIL::State::S0; + } + auto cell = module->addLut(NEW_ID, + driver_lut->getPort("\\A"), + y_bit, + driver_mask); + for (auto &bit : cell->connections_.at("\\A")) { + bit.wire = module->wires_.at(remap_name(bit.wire->name)); + bit2sinks[bit].push_back(cell); + } + } + + //log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); + log("ABC RESULTS: input signals: %8d\n", in_wires); + log("ABC RESULTS: output signals: %8d\n", out_wires); + + design->remove(mapped_mod); + } + else + { + log("Don't call ABC as there is nothing to map.\n"); + } + + if (cleanup) + { + log("Removing temp directory.\n"); + remove_directory(tempdir_name); + } + + log_pop(); +} + +struct Abc9Pass : public Pass { + Abc9Pass() : Pass("abc9", "use ABC9 for technology mapping") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" abc9 [options] [selection]\n"); + log("\n"); + log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); + log("library to a target architecture.\n"); + log("\n"); + log(" -exe <command>\n"); +#ifdef ABCEXTERNAL + log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else + log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif + log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n"); + log("\n"); + log(" -script <file>\n"); + log(" use the specified ABC script file instead of the default script.\n"); + log("\n"); + log(" if <file> starts with a plus sign (+), then the rest of the filename\n"); + log(" string is interpreted as the command string to be passed to ABC. The\n"); + log(" leading plus sign is removed and all commas (,) in the string are\n"); + log(" replaced with blanks before the string is passed to ABC.\n"); + log("\n"); + log(" if no -script parameter is given, the following scripts are used:\n"); + log("\n"); + log(" for -lut/-luts (only one LUT size):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str()); + log("\n"); + log(" for -lut/-luts (different LUT sizes):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); + log("\n"); + log(" -fast\n"); + log(" use different default scripts that are slightly faster (at the cost\n"); + log(" of output quality):\n"); + log("\n"); + log(" for -lut/-luts:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); + log("\n"); + log(" -D <picoseconds>\n"); + log(" set delay target. the string {D} in the default scripts above is\n"); + log(" replaced by this option when used, and an empty string otherwise\n"); + log(" (indicating best possible delay).\n"); +// log(" This also replaces 'dretime' with 'dretime; retime -o {D}' in the\n"); +// log(" default scripts above.\n"); + log("\n"); +// log(" -S <num>\n"); +// log(" maximum number of LUT inputs shared.\n"); +// log(" (replaces {S} in the default scripts above, default: -S 1)\n"); +// log("\n"); + log(" -lut <width>\n"); + log(" generate netlist using luts of (max) the specified width.\n"); + log("\n"); + log(" -lut <w1>:<w2>\n"); + log(" generate netlist using luts of (max) the specified width <w2>. All\n"); + log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); + log(" the area cost doubles with each additional input bit. the delay cost\n"); + log(" is still constant for all lut widths.\n"); + log("\n"); + log(" -lut <file>\n"); + log(" pass this file with lut library to ABC.\n"); + log("\n"); + log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); + log(" generate netlist using luts. Use the specified costs for luts with 1,\n"); + log(" 2, 3, .. inputs.\n"); + log("\n"); +// log(" -dff\n"); +// log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); +// log(" clock domains are automatically partitioned in clock domains and each\n"); +// log(" domain is passed through ABC independently.\n"); +// log("\n"); +// log(" -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n"); +// log(" use only the specified clock domain. this is like -dff, but only FF\n"); +// log(" cells that belong to the specified clock domain are used.\n"); +// log("\n"); +// log(" -keepff\n"); +// log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); +// log(" them, for example for equivalence checking.)\n"); +// log("\n"); + log(" -nocleanup\n"); + log(" when this option is used, the temporary files created by this pass\n"); + log(" are not removed. this is useful for debugging.\n"); + log("\n"); + log(" -showtmp\n"); + log(" print the temp dir name in log. usually this is suppressed so that the\n"); + log(" command output is identical across runs.\n"); + log("\n"); + log(" -markgroups\n"); + log(" set a 'abcgroup' attribute on all objects created by ABC. The value of\n"); + log(" this attribute is a unique integer for each ABC process started. This\n"); + log(" is useful for debugging the partitioning of clock domains.\n"); + log("\n"); + log(" -box <file>\n"); + log(" pass this file with box library to ABC. Use with -lut.\n"); + log("\n"); + log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); + log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); + log("ABC on logic snippets extracted from your design. You will not get any useful\n"); + log("output when passing an ABC script that writes a file. Instead write your full\n"); + log("design as BLIF file with write_blif and then load that into ABC externally if\n"); + log("you want to use ABC to convert your design into another format.\n"); + log("\n"); + log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\n"); + log_push(); + + assign_map.clear(); + +#ifdef ABCEXTERNAL + std::string exe_file = ABCEXTERNAL; +#else + std::string exe_file = proc_self_dirname() + "yosys-abc"; +#endif + std::string script_file, clk_str, box_file, lut_file; + std::string delay_target, lutin_shared = "-S 1", wire_delay; + bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; + bool show_tempdir = false; + vector<int> lut_costs; + markgroups = false; + +#if 0 + cleanup = false; + show_tempdir = true; +#endif + +#ifdef _WIN32 +#ifndef ABCEXTERNAL + if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) + exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif +#endif + + size_t argidx; + char pwd [PATH_MAX]; + if (!getcwd(pwd, sizeof(pwd))) { + log_cmd_error("getcwd failed: %s\n", strerror(errno)); + log_abort(); + } + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-exe" && argidx+1 < args.size()) { + exe_file = args[++argidx]; + continue; + } + if (arg == "-script" && argidx+1 < args.size()) { + script_file = args[++argidx]; + rewrite_filename(script_file); + if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') + script_file = std::string(pwd) + "/" + script_file; + continue; + } + if (arg == "-D" && argidx+1 < args.size()) { + delay_target = "-D " + args[++argidx]; + continue; + } + //if (arg == "-S" && argidx+1 < args.size()) { + // lutin_shared = "-S " + args[++argidx]; + // continue; + //} + if (arg == "-lut" && argidx+1 < args.size()) { + string arg = args[++argidx]; + size_t pos = arg.find_first_of(':'); + int lut_mode = 0, lut_mode2 = 0; + if (pos != string::npos) { + lut_mode = atoi(arg.substr(0, pos).c_str()); + lut_mode2 = atoi(arg.substr(pos+1).c_str()); + } else { + pos = arg.find_first_of('.'); + if (pos != string::npos) { + lut_file = arg; + rewrite_filename(lut_file); + if (!lut_file.empty() && !is_absolute_path(lut_file)) + lut_file = std::string(pwd) + "/" + lut_file; + } + else { + lut_mode = atoi(arg.c_str()); + lut_mode2 = lut_mode; + } + } + lut_costs.clear(); + for (int i = 0; i < lut_mode; i++) + lut_costs.push_back(1); + for (int i = lut_mode; i < lut_mode2; i++) + lut_costs.push_back(2 << (i - lut_mode)); + continue; + } + if (arg == "-luts" && argidx+1 < args.size()) { + lut_costs.clear(); + for (auto &tok : split_tokens(args[++argidx], ",")) { + auto parts = split_tokens(tok, ":"); + if (GetSize(parts) == 0 && !lut_costs.empty()) + lut_costs.push_back(lut_costs.back()); + else if (GetSize(parts) == 1) + lut_costs.push_back(atoi(parts.at(0).c_str())); + else if (GetSize(parts) == 2) + while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) + lut_costs.push_back(atoi(parts.at(1).c_str())); + else + log_cmd_error("Invalid -luts syntax.\n"); + } + continue; + } + if (arg == "-fast") { + fast_mode = true; + continue; + } + //if (arg == "-dff") { + // dff_mode = true; + // continue; + //} + //if (arg == "-clk" && argidx+1 < args.size()) { + // clk_str = args[++argidx]; + // dff_mode = true; + // continue; + //} + //if (arg == "-keepff") { + // keepff = true; + // continue; + //} + if (arg == "-nocleanup") { + cleanup = false; + continue; + } + if (arg == "-showtmp") { + show_tempdir = true; + continue; + } + if (arg == "-markgroups") { + markgroups = true; + continue; + } + if (arg == "-box" && argidx+1 < args.size()) { + box_file = args[++argidx]; + rewrite_filename(box_file); + if (!box_file.empty() && !is_absolute_path(box_file)) + box_file = std::string(pwd) + "/" + box_file; + continue; + } + if (arg == "-W" && argidx+1 < args.size()) { + wire_delay = "-W " + args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto mod : design->selected_modules()) + { + if (mod->attributes.count("\\abc_box_id")) + continue; + + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); + continue; + } + + assign_map.set(mod); + + if (!dff_mode || !clk_str.empty()) { + abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + delay_target, lutin_shared, fast_mode, show_tempdir, + box_file, lut_file, wire_delay); + continue; + } + + CellTypes ct(design); + + std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set<RTLIL::Cell*> expand_queue, next_expand_queue; + std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; + std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; + + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; + std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + + for (auto cell : all_cells) + { + clkdomain_t key; + + for (auto &conn : cell->connections()) + for (auto bit : conn.second) { + bit = assign_map(bit); + if (bit.wire != nullptr) { + cell_to_bit[cell].insert(bit); + bit_to_cell[bit].insert(cell); + if (ct.cell_input(cell->type, conn.first)) { + cell_to_bit_up[cell].insert(bit); + bit_to_cell_down[bit].insert(cell); + } + if (ct.cell_output(cell->type, conn.first)) { + cell_to_bit_down[cell].insert(bit); + bit_to_cell_up[bit].insert(cell); + } + } + } + + if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") + { + key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); + } + else + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"; + bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"; + key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E"))); + } + else + continue; + + unassigned_cells.erase(cell); + expand_queue.insert(cell); + expand_queue_up.insert(cell); + expand_queue_down.insert(cell); + + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + while (!expand_queue_up.empty() || !expand_queue_down.empty()) + { + if (!expand_queue_up.empty()) + { + RTLIL::Cell *cell = *expand_queue_up.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_up.erase(cell); + + for (auto bit : cell_to_bit_up[cell]) + for (auto c : bit_to_cell_up[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (!expand_queue_down.empty()) + { + RTLIL::Cell *cell = *expand_queue_down.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_down.erase(cell); + + for (auto bit : cell_to_bit_down[cell]) + for (auto c : bit_to_cell_down[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (expand_queue_up.empty() && expand_queue_down.empty()) { + expand_queue_up.swap(next_expand_queue_up); + expand_queue_down.swap(next_expand_queue_down); + } + } + + while (!expand_queue.empty()) + { + RTLIL::Cell *cell = *expand_queue.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue.erase(cell); + + for (auto bit : cell_to_bit.at(cell)) { + for (auto c : bit_to_cell[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + } + bit_to_cell[bit].clear(); + } + + if (expand_queue.empty()) + expand_queue.swap(next_expand_queue); + } + + clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); + for (auto cell : unassigned_cells) { + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + log_header(design, "Summary of detected clock domains:\n"); + for (auto &it : assigned_cells) + log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), + std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), + std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); + + for (auto &it : assigned_cells) { + clk_polarity = std::get<0>(it.first); + clk_sig = assign_map(std::get<1>(it.first)); + en_polarity = std::get<2>(it.first); + en_sig = assign_map(std::get<3>(it.first)); + abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, lutin_shared, fast_mode, show_tempdir, + box_file, lut_file, wire_delay); + assign_map.set(mod); + } + } + + Pass::call(design, "clean"); + + assign_map.clear(); + + log_pop(); + } +} Abc9Pass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc index aa48e1125..a38638e0b 100644 --- a/passes/techmap/attrmap.cc +++ b/passes/techmap/attrmap.cc @@ -263,6 +263,25 @@ struct AttrmapPass : public Pass { for (auto cell : module->selected_cells()) attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes); + + for (auto proc : module->processes) + { + if (!design->selected(module, proc.second)) + continue; + attrmap_apply(stringf("%s.%s", log_id(module), log_id(proc.first)), actions, proc.second->attributes); + + std::vector<RTLIL::CaseRule*> all_cases = {&proc.second->root_case}; + while (!all_cases.empty()) { + RTLIL::CaseRule *cs = all_cases.back(); + all_cases.pop_back(); + attrmap_apply(stringf("%s.%s (case)", log_id(module), log_id(proc.first)), actions, cs->attributes); + + for (auto &sw : cs->switches) { + attrmap_apply(stringf("%s.%s (switch)", log_id(module), log_id(proc.first)), actions, sw->attributes); + all_cases.insert(all_cases.end(), sw->cases.begin(), sw->cases.end()); + } + } + } } } } diff --git a/passes/techmap/extract_fa.cc b/passes/techmap/extract_fa.cc index 9e6dc0d24..591bc43dd 100644 --- a/passes/techmap/extract_fa.cc +++ b/passes/techmap/extract_fa.cc @@ -174,8 +174,10 @@ struct ExtractFaWorker SigSpec sig = root; - if (!ce.eval(sig)) - log_abort(); + if (!ce.eval(sig)) { + ce.pop(); + return; + } if (sig == State::S1) func |= 1 << i; @@ -214,8 +216,10 @@ struct ExtractFaWorker SigSpec sig = root; - if (!ce.eval(sig)) - log_abort(); + if (!ce.eval(sig)) { + ce.pop(); + return; + } if (sig == State::S1) func |= 1 << i; diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 12da9ed0c..d53378a29 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -23,6 +23,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#define COST_DMUX 90 #define COST_MUX2 100 #define COST_MUX4 220 #define COST_MUX8 460 @@ -57,6 +58,13 @@ struct MuxcoverWorker bool use_mux8; bool use_mux16; bool nodecode; + bool nopartial; + + int cost_dmux; + int cost_mux2; + int cost_mux4; + int cost_mux8; + int cost_mux16; MuxcoverWorker(Module *module) : module(module), sigmap(module) { @@ -64,9 +72,32 @@ struct MuxcoverWorker use_mux8 = false; use_mux16 = false; nodecode = false; + nopartial = false; + cost_dmux = COST_DMUX; + cost_mux2 = COST_MUX2; + cost_mux4 = COST_MUX4; + cost_mux8 = COST_MUX8; + cost_mux16 = COST_MUX16; decode_mux_counter = 0; } + bool xcmp(std::initializer_list<SigBit> list) + { + auto cursor = list.begin(), end = list.end(); + log_assert(cursor != end); + SigBit tmp = *(cursor++); + while (cursor != end) { + SigBit bit = *(cursor++); + if (bit == State::Sx) + continue; + if (tmp == State::Sx) + tmp = bit; + if (bit != tmp) + return false; + } + return true; + } + void treeify() { pool<SigBit> roots; @@ -124,13 +155,22 @@ struct MuxcoverWorker log(" Finished treeification: Found %d trees.\n", GetSize(tree_list)); } - bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path) + bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path, bool first_layer = true) { if (*path) { - if (tree.muxes.count(bit) == 0) - return false; + if (tree.muxes.count(bit) == 0) { + if (first_layer || nopartial) + return false; + while (path[0] && path[1]) + path++; + if (path[0] == 'S') + ret_bit = State::Sx; + else + ret_bit = bit; + return true; + } char port_name[3] = {'\\', *path, 0}; - return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1); + return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1, false); } else { ret_bit = bit; return true; @@ -139,7 +179,7 @@ struct MuxcoverWorker int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit) { - if (A == B) + if (A == B || sel == State::Sx) return 0; tuple<SigBit, SigBit, SigBit> key(A, B, sel); @@ -157,7 +197,10 @@ struct MuxcoverWorker if (std::get<2>(entry)) return 0; - return COST_MUX2 / GetSize(std::get<1>(entry)); + if (A == State::Sx || B == State::Sx) + return 0; + + return cost_dmux / GetSize(std::get<1>(entry)); } void implement_decode_mux(SigBit ctrl_bit) @@ -174,9 +217,32 @@ struct MuxcoverWorker implement_decode_mux(std::get<0>(key)); implement_decode_mux(std::get<1>(key)); - module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit); + if (std::get<0>(key) == State::Sx) { + module->addBufGate(NEW_ID, std::get<1>(key), ctrl_bit); + } else if (std::get<1>(key) == State::Sx) { + module->addBufGate(NEW_ID, std::get<0>(key), ctrl_bit); + } else { + module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit); + decode_mux_counter++; + } std::get<2>(entry) = true; - decode_mux_counter++; + } + + void find_best_covers(tree_t &tree, const vector<SigBit> &bits) + { + for (auto bit : bits) + find_best_cover(tree, bit); + } + + int sum_best_covers(tree_t &tree, const vector<SigBit> &bits) + { + int sum = 0; + for (auto bit : pool<SigBit>(bits.begin(), bits.end())) { + int cost = tree.newmuxes.at(bit).cost; + log_debug(" Best cost for %s: %d\n", log_signal(bit), cost); + sum += cost; + } + return sum; } int find_best_cover(tree_t &tree, SigBit bit) @@ -209,9 +275,13 @@ struct MuxcoverWorker mux.inputs.push_back(B); mux.selects.push_back(S1); - mux.cost += COST_MUX2; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux2 at %s: %d\n", log_signal(bit), mux.cost); + + mux.cost += cost_mux2; + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux2 at %s: %d\n", log_signal(bit), mux.cost); best_mux = mux; } @@ -229,7 +299,7 @@ struct MuxcoverWorker ok = ok && follow_muxtree(S2, tree, bit, "BS"); if (nodecode) - ok = ok && S1 == S2; + ok = ok && xcmp({S1, S2}); ok = ok && follow_muxtree(T1, tree, bit, "S"); @@ -247,13 +317,15 @@ struct MuxcoverWorker mux.selects.push_back(S1); mux.selects.push_back(T1); - mux.cost += COST_MUX4; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); - mux.cost += find_best_cover(tree, C); - mux.cost += find_best_cover(tree, D); + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux4 at %s: %d\n", log_signal(bit), mux.cost); - if (best_mux.cost > mux.cost) + mux.cost += cost_mux4; + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux4 at %s: %d\n", log_signal(bit), mux.cost); + + if (best_mux.cost >= mux.cost) best_mux = mux; } } @@ -277,13 +349,13 @@ struct MuxcoverWorker ok = ok && follow_muxtree(S4, tree, bit, "BBS"); if (nodecode) - ok = ok && S1 == S2 && S2 == S3 && S3 == S4; + ok = ok && xcmp({S1, S2, S3, S4}); ok = ok && follow_muxtree(T1, tree, bit, "AS"); ok = ok && follow_muxtree(T2, tree, bit, "BS"); if (nodecode) - ok = ok && T1 == T2; + ok = ok && xcmp({T1, T2}); ok = ok && follow_muxtree(U1, tree, bit, "S"); @@ -310,17 +382,15 @@ struct MuxcoverWorker mux.selects.push_back(T1); mux.selects.push_back(U1); - mux.cost += COST_MUX8; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); - mux.cost += find_best_cover(tree, C); - mux.cost += find_best_cover(tree, D); - mux.cost += find_best_cover(tree, E); - mux.cost += find_best_cover(tree, F); - mux.cost += find_best_cover(tree, G); - mux.cost += find_best_cover(tree, H); - - if (best_mux.cost > mux.cost) + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux8 at %s: %d\n", log_signal(bit), mux.cost); + + mux.cost += cost_mux8; + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux8 at %s: %d\n", log_signal(bit), mux.cost); + + if (best_mux.cost >= mux.cost) best_mux = mux; } } @@ -356,7 +426,7 @@ struct MuxcoverWorker ok = ok && follow_muxtree(S8, tree, bit, "BBBS"); if (nodecode) - ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8; + ok = ok && xcmp({S1, S2, S3, S4, S5, S6, S7, S8}); ok = ok && follow_muxtree(T1, tree, bit, "AAS"); ok = ok && follow_muxtree(T2, tree, bit, "ABS"); @@ -364,13 +434,13 @@ struct MuxcoverWorker ok = ok && follow_muxtree(T4, tree, bit, "BBS"); if (nodecode) - ok = ok && T1 == T2 && T2 == T3 && T3 == T4; + ok = ok && xcmp({T1, T2, T3, T4}); ok = ok && follow_muxtree(U1, tree, bit, "AS"); ok = ok && follow_muxtree(U2, tree, bit, "BS"); if (nodecode) - ok = ok && U1 == U2; + ok = ok && xcmp({U1, U2}); ok = ok && follow_muxtree(V1, tree, bit, "S"); @@ -414,25 +484,15 @@ struct MuxcoverWorker mux.selects.push_back(U1); mux.selects.push_back(V1); - mux.cost += COST_MUX16; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); - mux.cost += find_best_cover(tree, C); - mux.cost += find_best_cover(tree, D); - mux.cost += find_best_cover(tree, E); - mux.cost += find_best_cover(tree, F); - mux.cost += find_best_cover(tree, G); - mux.cost += find_best_cover(tree, H); - mux.cost += find_best_cover(tree, I); - mux.cost += find_best_cover(tree, J); - mux.cost += find_best_cover(tree, K); - mux.cost += find_best_cover(tree, L); - mux.cost += find_best_cover(tree, M); - mux.cost += find_best_cover(tree, N); - mux.cost += find_best_cover(tree, O); - mux.cost += find_best_cover(tree, P); - - if (best_mux.cost > mux.cost) + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux16 at %s: %d\n", log_signal(bit), mux.cost); + + mux.cost += cost_mux16; + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux16 at %s: %d\n", log_signal(bit), mux.cost); + + if (best_mux.cost >= mux.cost) best_mux = mux; } } @@ -528,6 +588,7 @@ struct MuxcoverWorker void treecover(tree_t &tree) { int count_muxes_by_type[4] = {0, 0, 0, 0}; + log_debug(" Searching for best cover for tree at %s.\n", log_signal(tree.root)); find_best_cover(tree, tree.root); implement_best_cover(tree, tree.root, count_muxes_by_type); log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root), @@ -544,12 +605,13 @@ struct MuxcoverWorker log(" Covering trees:\n"); - // pre-fill cache of decoder muxes - if (!nodecode) + if (!nodecode) { + log_debug(" Populating cache of decoder muxes.\n"); for (auto &tree : tree_list) { find_best_cover(tree, tree.root); tree.newmuxes.clear(); } + } for (auto &tree : tree_list) treecover(tree); @@ -569,15 +631,30 @@ struct MuxcoverPass : public Pass { log("\n"); log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n"); log("\n"); - log(" -mux4, -mux8, -mux16\n"); - log(" Use the specified types of MUXes. If none of those options are used,\n"); - log(" the effect is the same as if all of them where used.\n"); + log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n"); + log(" Cover $_MUX_ trees using the specified types of MUXes (with optional\n"); + log(" integer costs). If none of these options are given, the effect is the\n"); + log(" same as if all of them are.\n"); + log(" Default costs: $_MUX4_ = %d, $_MUX8_ = %d, \n", COST_MUX4, COST_MUX8); + log(" $_MUX16_ = %d\n", COST_MUX16); + log("\n"); + log(" -mux2=cost\n"); + log(" Use the specified cost for $_MUX_ cells when making covering decisions.\n"); + log(" Default cost: $_MUX_ = %d\n", COST_MUX2); + log("\n"); + log(" -dmux=cost\n"); + log(" Use the specified cost for $_MUX_ cells used in decoders.\n"); + log(" Default cost: %d\n", COST_DMUX); log("\n"); log(" -nodecode\n"); log(" Do not insert decoder logic. This reduces the number of possible\n"); log(" substitutions, but guarantees that the resulting circuit is not\n"); log(" less efficient than the original circuit.\n"); log("\n"); + log(" -nopartial\n"); + log(" Do not consider mappings that use $_MUX<N>_ to select from less\n"); + log(" than <N> different signals.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -587,26 +664,57 @@ struct MuxcoverPass : public Pass { bool use_mux8 = false; bool use_mux16 = false; bool nodecode = false; + bool nopartial = false; + int cost_dmux = COST_DMUX; + int cost_mux2 = COST_MUX2; + int cost_mux4 = COST_MUX4; + int cost_mux8 = COST_MUX8; + int cost_mux16 = COST_MUX16; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-mux4") { + const auto &arg = args[argidx]; + if (arg.size() >= 6 && arg.substr(0,6) == "-mux2=") { + cost_mux2 = std::stoi(arg.substr(6)); + continue; + } + if (arg.size() >= 5 && arg.substr(0,5) == "-mux4") { use_mux4 = true; + if (arg.size() > 5) { + if (arg[5] != '=') break; + cost_mux4 = std::stoi(arg.substr(6)); + } continue; } - if (args[argidx] == "-mux8") { + if (arg.size() >= 5 && arg.substr(0,5) == "-mux8") { use_mux8 = true; + if (arg.size() > 5) { + if (arg[5] != '=') break; + cost_mux8 = std::stoi(arg.substr(6)); + } continue; } - if (args[argidx] == "-mux16") { + if (arg.size() >= 6 && arg.substr(0,6) == "-mux16") { use_mux16 = true; + if (arg.size() > 6) { + if (arg[6] != '=') break; + cost_mux16 = std::stoi(arg.substr(7)); + } + continue; + } + if (arg.size() >= 6 && arg.substr(0,6) == "-dmux=") { + cost_dmux = std::stoi(arg.substr(6)); continue; } - if (args[argidx] == "-nodecode") { + if (arg == "-nodecode") { nodecode = true; continue; } + if (arg == "-nopartial") { + nopartial = true; + continue; + } break; } extra_args(args, argidx, design); @@ -623,7 +731,13 @@ struct MuxcoverPass : public Pass { worker.use_mux4 = use_mux4; worker.use_mux8 = use_mux8; worker.use_mux16 = use_mux16; + worker.cost_dmux = cost_dmux; + worker.cost_mux2 = cost_mux2; + worker.cost_mux4 = cost_mux4; + worker.cost_mux8 = cost_mux8; + worker.cost_mux16 = cost_mux16; worker.nodecode = nodecode; + worker.nopartial = nopartial; worker.run(); } } diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index 75eedfbcc..004ab1eb9 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -293,10 +293,22 @@ struct ShregmapWorker if (opts.init || sigbit_init.count(q_bit) == 0) { - if (sigbit_chain_next.count(d_bit)) { + auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell)); + if (!r.second) { + // Insertion not successful means that d_bit is already + // connected to another register, thus mark it as a + // non chain user ... sigbit_with_non_chain_users.insert(d_bit); - } else - sigbit_chain_next[d_bit] = cell; + // ... and clone d_bit into another wire, and use that + // wire as a different key in the d_bit-to-cell dictionary + // so that it can be identified as another chain + // (omitting this common flop) + // Link: https://github.com/YosysHQ/yosys/pull/1085 + Wire *wire = module->addWire(NEW_ID); + module->connect(wire, d_bit); + sigmap.add(wire, d_bit); + sigbit_chain_next.insert(std::make_pair(wire, cell)); + } sigbit_chain_prev[q_bit] = cell; continue; @@ -605,6 +617,11 @@ struct ShregmapPass : public Pass { log("\n"); log(" -tech greenpak4\n"); log(" map to greenpak4 shift registers.\n"); + log(" this option also implies -clkpol pos -zinit\n"); + log("\n"); + log(" -tech xilinx\n"); + log(" map to xilinx dynamic-length shift registers.\n"); + log(" this option also implies -params -init\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index ab0bd3b54..ceb053825 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -649,10 +649,13 @@ struct TechmapWorker unique_bit_id[bit] = unique_bit_id_counter++; } + // Find highest bit set int bits = 0; for (int i = 0; i < 32; i++) if (((unique_bit_id_counter-1) & (1 << i)) != 0) bits = i; + // Increment index by one to get number of bits + bits++; if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_")) parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits; diff --git a/techlibs/common/cmp2lut.v b/techlibs/common/cmp2lut.v index 8aa1eb957..0d0757767 100644 --- a/techlibs/common/cmp2lut.v +++ b/techlibs/common/cmp2lut.v @@ -27,7 +27,7 @@ parameter _TECHMAP_CONSTVAL_A_ = 0; parameter _TECHMAP_CONSTMSK_B_ = 0; parameter _TECHMAP_CONSTVAL_B_ = 0; -function automatic integer gen_lut; +function automatic [(1 << `LUT_WIDTH)-1:0] gen_lut; input integer width; input integer operation; input integer swap; diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index e41c0fe97..555de9fba 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -75,13 +75,16 @@ struct SynthPass : public ScriptPass log(" from label is synonymous to 'begin', and empty to label is\n"); log(" synonymous to the end of the command list.\n"); log("\n"); + log(" -abc9\n"); + log(" use new ABC9 flow (EXPERIMENTAL)\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - string top_module, fsm_opts, memory_opts; + string top_module, fsm_opts, memory_opts, abc; bool autotop, flatten, noalumacc, nofsm, noabc, noshare; int lut; @@ -98,6 +101,7 @@ struct SynthPass : public ScriptPass nofsm = false; noabc = false; noshare = false; + abc = "abc"; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -159,6 +163,10 @@ struct SynthPass : public ScriptPass noshare = true; continue; } + if (args[argidx] == "-abc9") { + abc = "abc9"; + continue; + } break; } extra_args(args, argidx, design); @@ -166,6 +174,9 @@ struct SynthPass : public ScriptPass if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); + if (abc == "abc9" && !lut) + log_cmd_error("ABC9 flow only supported for FPGA synthesis (using '-lut' option)"); + log_header(design, "Executing SYNTH pass.\n"); log_push(); @@ -241,15 +252,15 @@ struct SynthPass : public ScriptPass #ifdef YOSYS_ENABLE_ABC if (help_mode) { - run("abc -fast", " (unless -noabc, unless -lut)"); - run("abc -fast -lut k", "(unless -noabc, if -lut)"); + run(abc + " -fast", " (unless -noabc, unless -lut)"); + run(abc + " -fast -lut k", "(unless -noabc, if -lut)"); } else { if (lut) - run(stringf("abc -fast -lut %d", lut)); + run(stringf("%s -fast -lut %d", abc.c_str(), lut)); else - run("abc -fast"); + run(abc + " -fast"); } run("opt -fast", " (unless -noabc)"); #endif diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc index 4db087e87..73e18112f 100644 --- a/techlibs/ecp5/Makefile.inc +++ b/techlibs/ecp5/Makefile.inc @@ -4,13 +4,17 @@ OBJS += techlibs/ecp5/synth_ecp5.o techlibs/ecp5/ecp5_ffinit.o $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_sim.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_bb.v)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/drams_map.v)) -$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dram.txt)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutrams_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutram.txt)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut)) + EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk .SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk diff --git a/techlibs/ecp5/abc_5g.box b/techlibs/ecp5/abc_5g.box new file mode 100644 index 000000000..c757d137d --- /dev/null +++ b/techlibs/ecp5/abc_5g.box @@ -0,0 +1,43 @@ +# NB: Inputs/Outputs must be ordered alphabetically +# (with exceptions for carry in/out) + +# Box 1 : CCU2C (2xCARRY + 2xLUT4) +# Outputs: S0, S1, COUT +# (NB: carry chain input/output must be last +# input/output and bus has been moved +# there overriding the otherwise +# alphabetical ordering) +# name ID w/b ins outs +CCU2C 1 1 9 3 + +#A0 A1 B0 B1 C0 C1 D0 D1 CIN +379 - 379 - 275 - 141 - 257 +630 379 630 379 526 275 392 141 273 +516 516 516 516 412 412 278 278 43 + +# Box 2 : TRELLIS_DPR16X4 (16x4 dist ram) +# Outputs: DO0, DO1, DO2, DO3 +# name ID w/b ins outs +TRELLIS_DPR16X4 2 0 14 4 + +#DI0 DI1 DI2 DI3 RAD0 RAD1 RAD2 RAD3 WAD0 WAD1 WAD2 WAD3 WCK WRE +- - - - 141 379 275 379 - - - - - - +- - - - 141 379 275 379 - - - - - - +- - - - 141 379 275 379 - - - - - - +- - - - 141 379 275 379 - - - - - - + +# Box 3 : PFUMX (MUX2) +# Outputs: Z +# name ID w/b ins outs +PFUMX 3 1 3 1 + +#ALUT BLUT C0 +98 98 151 + +# Box 4 : L6MUX21 (MUX2) +# Outputs: Z +# name ID w/b ins outs +L6MUX21 4 1 3 1 + +#D0 D1 SD +140 141 148 diff --git a/techlibs/ecp5/abc_5g.lut b/techlibs/ecp5/abc_5g.lut new file mode 100644 index 000000000..e8aa9b35d --- /dev/null +++ b/techlibs/ecp5/abc_5g.lut @@ -0,0 +1,25 @@ +# ECP5-5G LUT library for ABC +# Note that ECP5 architecture assigns difference +# in LUT input delay to interconnect, so this is +# considered too + + +# Simple LUTs +# area D C B A +1 1 141 +2 1 141 275 +3 1 141 275 379 +4 1 141 275 379 379 + +# LUT5 = 2x LUT4 + PFUMX +# area M0 D C B A +5 2 151 239 373 477 477 + +# LUT6 = 2x LUT5 + MUX2 +# area M1 M0 D C B A +6 4 148 292 380 514 618 618 + +# LUT7 = 2x LUT6 + MUX2 +# area M2 M1 M0 D C B A +7 8 148 289 433 521 655 759 759 + diff --git a/techlibs/ecp5/abc_5g_nowide.lut b/techlibs/ecp5/abc_5g_nowide.lut new file mode 100644 index 000000000..60352d892 --- /dev/null +++ b/techlibs/ecp5/abc_5g_nowide.lut @@ -0,0 +1,12 @@ +# ECP5-5G LUT library for ABC +# Note that ECP5 architecture assigns difference +# in LUT input delay to interconnect, so this is +# considered too + + +# Simple LUTs +# area D C B A +1 1 141 +2 1 141 275 +3 1 141 275 379 +4 1 141 275 379 379 diff --git a/techlibs/ecp5/arith_map.v b/techlibs/ecp5/arith_map.v index eb7947601..17bde0497 100644 --- a/techlibs/ecp5/arith_map.v +++ b/techlibs/ecp5/arith_map.v @@ -50,20 +50,21 @@ module _80_ecp5_alu (A, B, CI, BI, X, Y, CO); wire [Y_WIDTH2-1:0] AA = A_buf; wire [Y_WIDTH2-1:0] BB = BI ? ~B_buf : B_buf; + wire [Y_WIDTH2-1:0] BX = B_buf; wire [Y_WIDTH2-1:0] C = {CO, CI}; wire [Y_WIDTH2-1:0] FCO, Y1; genvar i; generate for (i = 0; i < Y_WIDTH2; i = i + 2) begin:slice CCU2C #( - .INIT0(16'b0110011010101010), - .INIT1(16'b0110011010101010), + .INIT0(16'b1001011010101010), + .INIT1(16'b1001011010101010), .INJECT1_0("NO"), .INJECT1_1("NO") ) ccu2c_i ( .CIN(C[i]), - .A0(AA[i]), .B0(BB[i]), .C0(1'b0), .D0(1'b1), - .A1(AA[i+1]), .B1(BB[i+1]), .C1(1'b0), .D1(1'b1), + .A0(AA[i]), .B0(BX[i]), .C0(BI), .D0(1'b1), + .A1(AA[i+1]), .B1(BX[i+1]), .C1(BI), .D1(1'b1), .S0(Y[i]), .S1(Y1[i]), .COUT(FCO[i]) ); diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v index 6ab4b69f2..6985fbbc8 100644 --- a/techlibs/ecp5/cells_map.v +++ b/techlibs/ecp5/cells_map.v @@ -47,8 +47,59 @@ module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED" module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule -// For Diamond compatibility, FIXME: add all Diamond flipflop mappings +// TODO: Diamond flip-flops +// module FD1P3AX(); endmodule +// module FD1P3AY(); endmodule +// module FD1P3BX(); endmodule +// module FD1P3DX(); endmodule +// module FD1P3IX(); endmodule +// module FD1P3JX(); endmodule +// module FD1S3AX(); endmodule +// module FD1S3AY(); endmodule module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule +module FD1S3DX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule +module FD1S3IX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule +module FD1S3JX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule +// module FL1P3AY(); endmodule +// module FL1P3AZ(); endmodule +// module FL1P3BX(); endmodule +// module FL1P3DX(); endmodule +// module FL1P3IY(); endmodule +// module FL1P3JY(); endmodule +// module FL1S3AX(); endmodule +// module FL1S3AY(); endmodule + +// Diamond I/O buffers +module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule +module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule +module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule +module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I)); endmodule +module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule +module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule +module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule +module OBCO (input I, output OT, OC); OLVDS _TECHMAP_REPLACE_ (.A(I), .Z(OT), .ZN(OC)); endmodule +module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule +module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule +module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule +module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(A), .O(Z)); endmodule +module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(Z), .I(A)); endmodule + +// Diamond I/O registers +module IFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule +module IFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module IFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module IFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule + +module OFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule +module OFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module OFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module OFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule + +// TODO: Diamond I/O latches +// module IFS1S1B(input PD, D, SCLK, output Q); endmodule +// module IFS1S1D(input CD, D, SCLK, output Q); endmodule +// module IFS1S1I(input PD, D, SCLK, output Q); endmodule +// module IFS1S1J(input CD, D, SCLK, output Q); endmodule `ifndef NO_LUT module \$lut (A, Y); @@ -58,77 +109,102 @@ module \$lut (A, Y); input [WIDTH-1:0] A; output Y; + // Need to swap input ordering, and fix init accordingly, + // to match ABC's expectation of LUT inputs in non-decreasing + // delay order + localparam P_WIDTH = WIDTH < 4 ? 4 : WIDTH; + function [P_WIDTH-1:0] permute_index; + input [P_WIDTH-1:0] i; + integer j; + begin + permute_index = 0; + for (j = 0; j < P_WIDTH; j = j + 1) + permute_index[P_WIDTH-1 - j] = i[j]; + end + endfunction + + function [2**P_WIDTH-1:0] permute_init; + integer i; + begin + permute_init = 0; + for (i = 0; i < 2**P_WIDTH; i = i + 1) + permute_init[i] = LUT[permute_index(i)]; + end + endfunction + + parameter [2**P_WIDTH-1:0] P_LUT = permute_init(); + generate if (WIDTH == 1) begin - LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(A[0]), .B(1'b0), .C(1'b0), .D(1'b0)); + LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(1'b0), .C(1'b0), .D(A[0])); end else if (WIDTH == 2) begin - LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(A[0]), .B(A[1]), .C(1'b0), .D(1'b0)); + LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(1'b0), .C(A[1]), .D(A[0])); end else if (WIDTH == 3) begin - LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(A[0]), .B(A[1]), .C(A[2]), .D(1'b0)); + LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(A[2]), .C(A[1]), .D(A[0])); end else if (WIDTH == 4) begin - LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[3]), .B(A[2]), .C(A[1]), .D(A[0])); `ifndef NO_PFUMUX end else if (WIDTH == 5) begin wire f0, f1; - LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y)); + LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), + .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1])); + LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), + .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1])); + PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y)); end else if (WIDTH == 6) begin wire f0, f1, f2, f3, g0, g1; - LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - - LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - - PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); - PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); - L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y)); + LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), + .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); + LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), + .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); + + LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2), + .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); + LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3), + .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1)); + L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y)); end else if (WIDTH == 7) begin wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1; - LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - - LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - - LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - - LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7), - .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); - - PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); - PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); - PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2)); - PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3)); - L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0)); - L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1)); - L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y)); + LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + + LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + + LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + + LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7), + .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1)); + PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2)); + PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3)); + L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0)); + L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1)); + L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y)); `endif end else begin wire _TECHMAP_FAIL_ = 1; diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v index 1e4002ee0..ca88d0a5b 100644 --- a/techlibs/ecp5/cells_sim.v +++ b/techlibs/ecp5/cells_sim.v @@ -9,13 +9,13 @@ module LUT4(input A, B, C, D, output Z); endmodule // --------------------------------------- - +(* abc_box_id=4, lib_whitebox *) module L6MUX21 (input D0, D1, SD, output Z); assign Z = SD ? D1 : D0; endmodule // --------------------------------------- - +(* abc_box_id=1, abc_carry="CIN,COUT", lib_whitebox *) module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1, output S0, S1, COUT); @@ -26,9 +26,13 @@ module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1, // First half wire LUT4_0, LUT2_0; +`ifdef _ABC + assign LUT4_0 = INIT0[{D0, C0, B0, A0}]; + assign LUT2_0 = INIT0[{2'b00, B0, A0}]; +`else LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0)); LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0)); - +`endif wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN; assign S0 = LUT4_0 ^ gated_cin_0; @@ -37,9 +41,13 @@ module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1, // Second half wire LUT4_1, LUT2_1; +`ifdef _ABC + assign LUT4_1 = INIT1[{D1, C1, B1, A1}]; + assign LUT2_1 = INIT1[{2'b00, B1, A1}]; +`else LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1)); LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1)); - +`endif wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0; assign S1 = LUT4_1 ^ gated_cin_1; @@ -90,13 +98,13 @@ module TRELLIS_RAM16X2 ( endmodule // --------------------------------------- - +(* abc_box_id=3, lib_whitebox *) module PFUMX (input ALUT, BLUT, C0, output Z); assign Z = C0 ? ALUT : BLUT; endmodule // --------------------------------------- - +//(* abc_box_id=2, abc_scc_break="DI,WAD,WRE" *) module TRELLIS_DPR16X4 ( input [3:0] DI, input [3:0] WAD, @@ -251,18 +259,6 @@ module TRELLIS_FF(input CLK, LSR, CE, DI, M, output reg Q); endmodule // --------------------------------------- - -module OBZ(input I, T, output O); -assign O = T ? 1'bz : I; -endmodule - -// --------------------------------------- - -module IB(input I, output O); -assign O = I; -endmodule - -// --------------------------------------- (* keep *) module TRELLIS_IO( inout B, @@ -293,19 +289,6 @@ endmodule // --------------------------------------- -module OB(input I, output O); -assign O = I; -endmodule - -// --------------------------------------- - -module BB(input I, T, output O, inout B); -assign B = T ? 1'bz : I; -assign O = B; -endmodule - -// --------------------------------------- - module INV(input A, output Z); assign Z = !A; endmodule @@ -558,19 +541,56 @@ module DP16KD( parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; endmodule -// For Diamond compatibility, FIXME: add all Diamond flipflop mappings -module FD1S3BX(input PD, D, CK, output Q); - TRELLIS_FF #( - .GSR("DISABLED"), - .CEMUX("1"), - .CLKMUX("CLK"), - .LSRMUX("LSR"), - .REGSET("SET"), - .SRMODE("ASYNC") - ) tff_i ( - .CLK(CK), - .LSR(PD), - .DI(D), - .Q(Q) - ); -endmodule +// TODO: Diamond flip-flops +// module FD1P3AX(); endmodule +// module FD1P3AY(); endmodule +// module FD1P3BX(); endmodule +// module FD1P3DX(); endmodule +// module FD1P3IX(); endmodule +// module FD1P3JX(); endmodule +// module FD1S3AX(); endmodule +// module FD1S3AY(); endmodule +module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule +module FD1S3DX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule +module FD1S3IX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule +module FD1S3JX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule +// module FL1P3AY(); endmodule +// module FL1P3AZ(); endmodule +// module FL1P3BX(); endmodule +// module FL1P3DX(); endmodule +// module FL1P3IY(); endmodule +// module FL1P3JY(); endmodule +// module FL1S3AX(); endmodule +// module FL1S3AY(); endmodule + +// Diamond I/O buffers +module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule +module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule +module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule +module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I)); endmodule +module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule +module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule +module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule +module OBCO (input I, output OT, OC); OLVDS olvds (.A(I), .Z(OT), .ZN(OC)); endmodule +module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule +module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule +module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule +module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) tio (.B(A), .O(Z)); endmodule +module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(Z), .I(A)); endmodule + +// Diamond I/O registers +module IFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule +module IFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module IFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module IFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule + +module OFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule +module OFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module OFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule +module OFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule + +// TODO: Diamond I/O latches +// module IFS1S1B(input PD, D, SCLK, output Q); endmodule +// module IFS1S1D(input CD, D, SCLK, output Q); endmodule +// module IFS1S1I(input PD, D, SCLK, output Q); endmodule +// module IFS1S1J(input CD, D, SCLK, output Q); endmodule diff --git a/techlibs/ecp5/dram.txt b/techlibs/ecp5/lutram.txt index b94357429..b94357429 100644 --- a/techlibs/ecp5/dram.txt +++ b/techlibs/ecp5/lutram.txt diff --git a/techlibs/ecp5/drams_map.v b/techlibs/ecp5/lutrams_map.v index 3b3de831f..3b3de831f 100644 --- a/techlibs/ecp5/drams_map.v +++ b/techlibs/ecp5/lutrams_map.v diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index c6e12248e..143d1f95c 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -2,7 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * Copyright (C) 2018 Clifford Wolf <dave@ds0.me> + * Copyright (C) 2018 David Shah <dave@ds0.me> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -71,17 +71,20 @@ struct SynthEcp5Pass : public ScriptPass log(" do not use flipflops with CE in output netlist\n"); log("\n"); log(" -nobram\n"); - log(" do not use BRAM cells in output netlist\n"); + log(" do not use block RAM cells in output netlist\n"); log("\n"); - log(" -nodram\n"); - log(" do not use distributed RAM cells in output netlist\n"); + log(" -nolutram\n"); + log(" do not use LUT RAM cells in output netlist\n"); log("\n"); - log(" -nomux\n"); + log(" -nowidelut\n"); log(" do not use PFU muxes to implement LUTs larger than LUT4s\n"); log("\n"); log(" -abc2\n"); log(" run two passes of 'abc' for slightly improved logic density\n"); log("\n"); + log(" -abc9\n"); + log(" use new ABC9 flow (EXPERIMENTAL)\n"); + log("\n"); log(" -vpr\n"); log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); @@ -93,7 +96,7 @@ struct SynthEcp5Pass : public ScriptPass } string top_opt, blif_file, edif_file, json_file; - bool noccu2, nodffe, nobram, nodram, nomux, flatten, retime, abc2, vpr; + bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr; void clear_flags() YS_OVERRIDE { @@ -104,12 +107,13 @@ struct SynthEcp5Pass : public ScriptPass noccu2 = false; nodffe = false; nobram = false; - nodram = false; - nomux = false; + nolutram = false; + nowidelut = false; flatten = true; retime = false; abc2 = false; vpr = false; + abc9 = false; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -168,12 +172,12 @@ struct SynthEcp5Pass : public ScriptPass nobram = true; continue; } - if (args[argidx] == "-nodram") { - nodram = true; + if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") { + nolutram = true; continue; } - if (args[argidx] == "-nomux") { - nomux = true; + if (args[argidx] == "-nowidelut" || /*deprecated alias*/ args[argidx] == "-nomux") { + nowidelut = true; continue; } if (args[argidx] == "-abc2") { @@ -184,6 +188,10 @@ struct SynthEcp5Pass : public ScriptPass vpr = true; continue; } + if (args[argidx] == "-abc9") { + abc9 = true; + continue; + } break; } extra_args(args, argidx, design); @@ -191,6 +199,9 @@ struct SynthEcp5Pass : public ScriptPass if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); + if (abc9 && retime) + log_cmd_error("-retime option not currently compatible with -abc9!\n"); + log_header(design, "Executing SYNTH_ECP5 pass.\n"); log_push(); @@ -203,7 +214,7 @@ struct SynthEcp5Pass : public ScriptPass { if (check_label("begin")) { - run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v"); + run("read_verilog -D_ABC -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v"); run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); } @@ -220,23 +231,27 @@ struct SynthEcp5Pass : public ScriptPass run("synth -run coarse"); } - if (!nobram && check_label("bram", "(skip if -nobram)")) + if (!nobram && check_label("map_bram", "(skip if -nobram)")) { run("memory_bram -rules +/ecp5/bram.txt"); run("techmap -map +/ecp5/brams_map.v"); } - if (!nodram && check_label("dram", "(skip if -nodram)")) + if (!nolutram && check_label("map_lutram", "(skip if -nolutram)")) { - run("memory_bram -rules +/ecp5/dram.txt"); - run("techmap -map +/ecp5/drams_map.v"); + run("memory_bram -rules +/ecp5/lutram.txt"); + run("techmap -map +/ecp5/lutrams_map.v"); } - if (check_label("fine")) + if (check_label("map_ffram")) { run("opt -fast -mux_undef -undriven -fine"); run("memory_map"); run("opt -undriven -fine"); + } + + if (check_label("map_gates")) + { if (noccu2) run("techmap"); else @@ -264,10 +279,17 @@ struct SynthEcp5Pass : public ScriptPass run("abc", " (only if -abc2)"); } run("techmap -map +/ecp5/latches_map.v"); - if (nomux) - run("abc -lut 4 -dress"); - else - run("abc -lut 4:7 -dress"); + if (abc9) { + if (nowidelut) + run("abc9 -lut +/ecp5/abc_5g_nowide.lut -box +/ecp5/abc_5g.box -W 200"); + else + run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200"); + } else { + if (nowidelut) + run("abc -lut 4 -dress"); + else + run("abc -lut 4:7 -dress"); + } run("clean"); } diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc index 723b59d6f..d258d5a5d 100644 --- a/techlibs/ice40/Makefile.inc +++ b/techlibs/ice40/Makefile.inc @@ -28,6 +28,12 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.box)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.lut)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.lut)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.box)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.lut)) $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init1.vh)) $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh)) diff --git a/techlibs/ice40/abc_hx.box b/techlibs/ice40/abc_hx.box new file mode 100644 index 000000000..c0ea742e2 --- /dev/null +++ b/techlibs/ice40/abc_hx.box @@ -0,0 +1,13 @@ +# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt + +# NB: Inputs/Outputs must be ordered alphabetically +# (with exceptions for carry in/out) + +# Inputs: A B CI +# Outputs: O CO +# (NB: carry chain input/output must be last +# input/output and have been moved there +# overriding the alphabetical ordering) +$__ICE40_FULL_ADDER 1 1 3 2 +400 379 316 +259 231 126 diff --git a/techlibs/ice40/abc_hx.lut b/techlibs/ice40/abc_hx.lut new file mode 100644 index 000000000..3b3bb11e2 --- /dev/null +++ b/techlibs/ice40/abc_hx.lut @@ -0,0 +1,6 @@ +# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt +# I3 I2 I1 I0 +1 1 316 +2 1 316 379 +3 1 316 379 400 +4 1 316 379 400 449 diff --git a/techlibs/ice40/abc_lp.box b/techlibs/ice40/abc_lp.box new file mode 100644 index 000000000..d73b6d649 --- /dev/null +++ b/techlibs/ice40/abc_lp.box @@ -0,0 +1,13 @@ +# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt + +# NB: Inputs/Outputs must be ordered alphabetically +# (with exceptions for carry in/out) + +# Inputs: A B CI +# Outputs: O CO +# (NB: carry chain input/output must be last +# input/output and have been moved there +# overriding the alphabetical ordering) +$__ICE40_FULL_ADDER 1 1 3 2 +589 558 465 +675 609 186 diff --git a/techlibs/ice40/abc_lp.lut b/techlibs/ice40/abc_lp.lut new file mode 100644 index 000000000..e72f760a2 --- /dev/null +++ b/techlibs/ice40/abc_lp.lut @@ -0,0 +1,6 @@ +# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt +# I3 I2 I1 I0 +1 1 465 +2 1 465 558 +3 1 465 558 589 +4 1 465 558 589 661 diff --git a/techlibs/ice40/abc_u.box b/techlibs/ice40/abc_u.box new file mode 100644 index 000000000..42d666051 --- /dev/null +++ b/techlibs/ice40/abc_u.box @@ -0,0 +1,13 @@ +# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt + +# NB: Inputs/Outputs must be ordered alphabetically +# (with exceptions for carry in/out) + +# Inputs: A B CI +# Outputs: O CO +# (NB: carry chain input/output must be last +# input/output and have been moved there +# overriding the alphabetical ordering) +$__ICE40_FULL_ADDER 1 1 3 2 +1231 1205 874 +675 609 278 diff --git a/techlibs/ice40/abc_u.lut b/techlibs/ice40/abc_u.lut new file mode 100644 index 000000000..1e4fcadb6 --- /dev/null +++ b/techlibs/ice40/abc_u.lut @@ -0,0 +1,6 @@ +# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt +# I3 I2 I1 I0 +1 1 874 +2 1 874 1205 +3 1 874 1205 1231 +4 1 874 1205 1231 1285 diff --git a/techlibs/ice40/arith_map.v b/techlibs/ice40/arith_map.v index 4449fdc1b..fe83a8e38 100644 --- a/techlibs/ice40/arith_map.v +++ b/techlibs/ice40/arith_map.v @@ -44,6 +44,15 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO); genvar i; generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice +`ifdef _ABC + \$__ICE40_FULL_ADDER carry ( + .A(AA[i]), + .B(BB[i]), + .CI(C[i]), + .CO(CO[i]), + .O(Y[i]) + ); +`else SB_CARRY carry ( .I0(AA[i]), .I1(BB[i]), @@ -63,6 +72,7 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO); .I3(C[i]), .O(Y[i]) ); +`endif end endgenerate assign X = AA ^ BB; diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v index d0ddfd02e..b4b831165 100644 --- a/techlibs/ice40/cells_map.v +++ b/techlibs/ice40/cells_map.v @@ -37,23 +37,51 @@ module \$lut (A, Y); generate if (WIDTH == 1) begin - SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0)); + localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}}; + SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0])); end else if (WIDTH == 2) begin - SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0)); + localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}}; + SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0])); end else if (WIDTH == 3) begin - SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0)); + localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[3]}}, {2{LUT[5]}}, {2{LUT[1]}}, {2{LUT[6]}}, {2{LUT[2]}}, {2{LUT[4]}}, {2{LUT[0]}}}; + SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0])); end else if (WIDTH == 4) begin - SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3])); + localparam [15:0] INIT = {LUT[15], LUT[7], LUT[11], LUT[3], LUT[13], LUT[5], LUT[9], LUT[1], LUT[14], LUT[6], LUT[10], LUT[2], LUT[12], LUT[4], LUT[8], LUT[0]}; + SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0])); end else begin wire _TECHMAP_FAIL_ = 1; end endgenerate endmodule `endif + +`ifdef _ABC +module \$__ICE40_FULL_ADDER (output CO, O, input A, B, CI); + SB_CARRY carry ( + .I0(A), + .I1(B), + .CI(CI), + .CO(CO) + ); + SB_LUT4 #( + // I0: 1010 1010 1010 1010 + // I1: 1100 1100 1100 1100 + // I2: 1111 0000 1111 0000 + // I3: 1111 1111 0000 0000 + .LUT_INIT(16'b 0110_1001_1001_0110) + ) adder ( + .I0(1'b0), + .I1(A), + .I2(B), + .I3(CI), + .O(O) + ); +endmodule +`endif diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index e89405b22..2205be27d 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -127,6 +127,7 @@ endmodule // SiliconBlue Logic Cells +(* lib_whitebox *) module SB_LUT4 (output O, input I0, I1, I2, I3); parameter [15:0] LUT_INIT = 0; wire [7:0] s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7:0]; @@ -135,10 +136,34 @@ module SB_LUT4 (output O, input I0, I1, I2, I3); assign O = I0 ? s1[1] : s1[0]; endmodule +(* lib_whitebox *) module SB_CARRY (output CO, input I0, I1, CI); assign CO = (I0 && I1) || ((I0 || I1) && CI); endmodule +(* abc_box_id = 1, abc_carry="CI,CO", lib_whitebox *) +module \$__ICE40_FULL_ADDER (output CO, O, input A, B, CI); + SB_CARRY carry ( + .I0(A), + .I1(B), + .CI(CI), + .CO(CO) + ); + SB_LUT4 #( + // I0: 1010 1010 1010 1010 + // I1: 1100 1100 1100 1100 + // I2: 1111 0000 1111 0000 + // I3: 1111 1111 0000 0000 + .LUT_INIT(16'b 0110_1001_1001_0110) + ) adder ( + .I0(1'b0), + .I1(A), + .I2(B), + .I3(CI), + .O(O) + ); +endmodule + // Positive Edge SiliconBlue FF Cells module SB_DFF (output `SB_DFF_REG, input C, D); @@ -974,6 +999,30 @@ parameter RGB2_CURRENT = "0b000000"; endmodule (* blackbox *) +module SB_LED_DRV_CUR( + input EN, + output LEDPU +); +endmodule + +(* blackbox *) +module SB_RGB_DRV( + input RGBLEDEN, + input RGB0PWM, + input RGB1PWM, + input RGB2PWM, + input RGBPU, + output RGB0, + output RGB1, + output RGB2 +); +parameter CURRENT_MODE = "0b0"; +parameter RGB0_CURRENT = "0b000000"; +parameter RGB1_CURRENT = "0b000000"; +parameter RGB2_CURRENT = "0b000000"; +endmodule + +(* blackbox *) module SB_I2C( input SBCLKI, input SBRWI, @@ -1314,13 +1363,13 @@ module SB_MAC16 ( wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl; wire [15:0] Ah, Al, Bh, Bl; assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]}; - assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]}; + assign Al = {A_SIGNED && MODE_8x8 ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]}; assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]}; - assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; - assign p_Ah_Bh = Ah * Bh; - assign p_Al_Bh = Al * Bh; - assign p_Ah_Bl = Ah * Bl; - assign p_Al_Bl = Al * Bl; + assign Bl = {B_SIGNED && MODE_8x8 ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; + assign p_Ah_Bh = Ah * Bh; // F + assign p_Al_Bh = {8'b0, Al[7:0]} * Bh; // J + assign p_Ah_Bl = Ah * {8'b0, Bl[7:0]}; // K + assign p_Al_Bl = Al * Bl; // G // Regs F and J reg [15:0] rF, rJ; @@ -1351,7 +1400,9 @@ module SB_MAC16 ( assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl; // Adder Stage - assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16); + wire [23:0] iK_e = {A_SIGNED ? {8{iK[15]}} : 8'b0, iK}; + wire [23:0] iJ_e = {B_SIGNED ? {8{iJ[15]}} : 8'b0, iJ}; + assign iL = iG + (iK_e << 8) + (iJ_e << 8) + (iF << 16); // Reg H reg [31:0] rH; diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc index f528607d6..e492454fb 100644 --- a/techlibs/ice40/ice40_opt.cc +++ b/techlibs/ice40/ice40_opt.cc @@ -83,6 +83,51 @@ static void run_ice40_opts(Module *module) } continue; } + + if (cell->type == "$__ICE40_FULL_ADDER") + { + SigSpec non_const_inputs, replacement_output; + int count_zeros = 0, count_ones = 0; + + SigBit inbit[3] = { + cell->getPort("\\A"), + cell->getPort("\\B"), + cell->getPort("\\CI") + }; + for (int i = 0; i < 3; i++) + if (inbit[i].wire == nullptr) { + if (inbit[i] == State::S1) + count_ones++; + else + count_zeros++; + } else + non_const_inputs.append(inbit[i]); + + if (count_zeros >= 2) + replacement_output = State::S0; + else if (count_ones >= 2) + replacement_output = State::S1; + else if (GetSize(non_const_inputs) == 1) + replacement_output = non_const_inputs; + + if (GetSize(replacement_output)) { + optimized_co.insert(sigmap(cell->getPort("\\CO")[0])); + module->connect(cell->getPort("\\CO")[0], replacement_output); + module->design->scratchpad_set_bool("opt.did_something", true); + log("Optimized $__ICE40_FULL_ADDER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n", + log_id(module), log_id(cell), log_signal(replacement_output)); + cell->type = "$lut"; + cell->setPort("\\A", { RTLIL::S0, inbit[0], inbit[1], inbit[2] }); + cell->setPort("\\Y", cell->getPort("\\O")); + cell->unsetPort("\\B"); + cell->unsetPort("\\CI"); + cell->unsetPort("\\CO"); + cell->unsetPort("\\O"); + cell->setParam("\\LUT", RTLIL::Const::from_string("0110100110010110")); + cell->setParam("\\WIDTH", 4); + } + continue; + } } for (auto cell : sb_lut_cells) diff --git a/techlibs/ice40/ice40_unlut.cc b/techlibs/ice40/ice40_unlut.cc index 2428a8e78..f3f70ac1f 100644 --- a/techlibs/ice40/ice40_unlut.cc +++ b/techlibs/ice40/ice40_unlut.cc @@ -56,10 +56,10 @@ static void run_ice40_unlut(Module *module) cell->unsetParam("\\LUT_INIT"); cell->setPort("\\A", SigSpec({ - get_bit_or_zero(cell->getPort("\\I3")), - get_bit_or_zero(cell->getPort("\\I2")), + get_bit_or_zero(cell->getPort("\\I0")), get_bit_or_zero(cell->getPort("\\I1")), - get_bit_or_zero(cell->getPort("\\I0")) + get_bit_or_zero(cell->getPort("\\I2")), + get_bit_or_zero(cell->getPort("\\I3")) })); cell->setPort("\\Y", cell->getPort("\\O")[0]); cell->unsetPort("\\I0"); @@ -74,7 +74,7 @@ static void run_ice40_unlut(Module *module) } struct Ice40UnlutPass : public Pass { - Ice40UnlutPass() : Pass("ice40_unlut", "iCE40: perform simple optimizations") { } + Ice40UnlutPass() : Pass("ice40_unlut", "iCE40: transform SB_LUT4 cells to $lut cells") { } void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index bb96d66d1..be60a0071 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -37,6 +37,10 @@ struct SynthIce40Pass : public ScriptPass log("\n"); log("This command runs synthesis for iCE40 FPGAs.\n"); log("\n"); + log(" -device < hx | lp | u >\n"); + log(" relevant only for '-abc9' flow, optimise timing for the specified device.\n"); + log(" default: hx\n"); + log("\n"); log(" -top <module>\n"); log(" use the specified module as top module\n"); log("\n"); @@ -63,9 +67,6 @@ struct SynthIce40Pass : public ScriptPass log(" -retime\n"); log(" run 'abc' with -dff option\n"); log("\n"); - log(" -relut\n"); - log(" combine LUTs after synthesis\n"); - log("\n"); log(" -nocarry\n"); log(" do not use SB_CARRY cells in output netlist\n"); log("\n"); @@ -74,7 +75,7 @@ struct SynthIce40Pass : public ScriptPass log("\n"); log(" -dffe_min_ce_use <min_ce_use>\n"); log(" do not use SB_DFFE* cells if the resulting CE line would go to less\n"); - log(" than min_ce_use SB_DFFE*in output netlist\n"); + log(" than min_ce_use SB_DFFE* in output netlist\n"); log("\n"); log(" -nobram\n"); log(" do not use SB_RAM40_4K* cells in output netlist\n"); @@ -92,14 +93,17 @@ struct SynthIce40Pass : public ScriptPass log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); log("\n"); + log(" -abc9\n"); + log(" use new ABC9 flow (EXPERIMENTAL)\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - string top_opt, blif_file, edif_file, json_file; - bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr; + string top_opt, blif_file, edif_file, json_file, abc, device_opt; + bool nocarry, nodffe, nobram, dsp, flatten, retime, noabc, abc2, vpr; int min_ce_use; void clear_flags() YS_OVERRIDE @@ -115,10 +119,11 @@ struct SynthIce40Pass : public ScriptPass dsp = false; flatten = true; retime = false; - relut = false; noabc = false; abc2 = false; vpr = false; + abc = "abc"; + device_opt = "hx"; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -166,7 +171,7 @@ struct SynthIce40Pass : public ScriptPass continue; } if (args[argidx] == "-relut") { - relut = true; + // removed, opt_lut is always run continue; } if (args[argidx] == "-nocarry") { @@ -201,12 +206,25 @@ struct SynthIce40Pass : public ScriptPass vpr = true; continue; } + if (args[argidx] == "-abc9") { + abc = "abc9"; + continue; + } + if (args[argidx] == "-device" && argidx+1 < args.size()) { + device_opt = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); + if (device_opt != "hx" && device_opt != "lp" && device_opt !="u") + log_cmd_error("Invalid or no device specified: '%s'\n", device_opt.c_str()); + + if (abc == "abc9" && retime) + log_cmd_error("-retime option not currently compatible with -abc9!\n"); log_header(design, "Executing SYNTH_ICE40 pass.\n"); log_push(); @@ -220,7 +238,7 @@ struct SynthIce40Pass : public ScriptPass { if (check_label("begin")) { - run("read_verilog -lib +/ice40/cells_sim.v"); + run("read_verilog -icells -lib -D_ABC +/ice40/cells_sim.v"); run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); run("proc"); } @@ -257,14 +275,14 @@ struct SynthIce40Pass : public ScriptPass run("opt_clean"); } - if (!nobram && check_label("bram", "(skip if -nobram)")) + if (!nobram && check_label("map_bram", "(skip if -nobram)")) { run("memory_bram -rules +/ice40/brams.txt"); run("techmap -map +/ice40/brams_map.v"); run("ice40_braminit"); } - if (check_label("map")) + if (check_label("map_ffram")) { run("opt -fast -mux_undef -undriven -fine"); run("memory_map"); @@ -276,9 +294,9 @@ struct SynthIce40Pass : public ScriptPass if (nocarry) run("techmap"); else - run("techmap -map +/techmap.v -map +/ice40/arith_map.v"); + run("techmap -map +/techmap.v -map +/ice40/arith_map.v" + std::string(abc == "abc9" ? " -D _ABC" : "")); if (retime || help_mode) - run("abc -dff", "(only if -retime)"); + run(abc + " -dff", "(only if -retime)"); run("ice40_opt"); } @@ -302,7 +320,7 @@ struct SynthIce40Pass : public ScriptPass if (check_label("map_luts")) { if (abc2 || help_mode) { - run("abc", " (only if -abc2)"); + run(abc, " (only if -abc2)"); run("ice40_opt", "(only if -abc2)"); } run("techmap -map +/ice40/latches_map.v"); @@ -311,13 +329,23 @@ struct SynthIce40Pass : public ScriptPass run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)"); } if (!noabc) { - run("abc -dress -lut 4", "(skip if -noabc)"); + if (abc == "abc9") { + int wire_delay; + if (device_opt == "lp") + wire_delay = 400; + else if (device_opt == "u") + wire_delay = 750; + else + wire_delay = 250; + run(abc + stringf(" -W %d -lut +/ice40/abc_%s.lut -box +/ice40/abc_%s.box", wire_delay, device_opt.c_str(), device_opt.c_str()), "(skip if -noabc)"); + run("techmap -D NO_LUT -D _ABC -map +/ice40/cells_map.v"); + } + else + run(abc + " -dress -lut 4", "(skip if -noabc)"); } run("clean"); - if (relut || help_mode) { - run("ice40_unlut", " (only if -relut)"); - run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3", "(only if -relut)"); - } + run("ice40_unlut"); + run("opt_lut -dlogic SB_CARRY:I0=2:I1=1:CI=0"); } if (check_label("map_cells")) diff --git a/techlibs/ice40/tests/test_dsp_model.sh b/techlibs/ice40/tests/test_dsp_model.sh index 1bc0cc688..1e564d1b2 100644 --- a/techlibs/ice40/tests/test_dsp_model.sh +++ b/techlibs/ice40/tests/test_dsp_model.sh @@ -1,10 +1,15 @@ #!/bin/bash set -ex sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v -cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v +if [ ! -f "test_dsp_model_ref.v" ]; then + cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v +fi for tb in testbench \ testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \ - testbench_seq_16x16_A testbench_seq_16x16_B + testbench_seq_16x16_A testbench_seq_16x16_B \ + testbench_comb_8x8_A_signedA testbench_comb_8x8_A_signedB testbench_comb_8x8_A_signedAB \ + testbench_comb_8x8_B_signedA testbench_comb_8x8_B_signedB testbench_comb_8x8_B_signedAB \ + testbench_comb_16x16_signedA testbench_comb_16x16_signedB testbench_comb_16x16_signedAB do iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v vvp -N ./test_dsp_model diff --git a/techlibs/ice40/tests/test_dsp_model.v b/techlibs/ice40/tests/test_dsp_model.v index 594bd4ad3..f4f6858f0 100644 --- a/techlibs/ice40/tests/test_dsp_model.v +++ b/techlibs/ice40/tests/test_dsp_model.v @@ -241,6 +241,81 @@ module testbench_comb_8x8_A; ) testbench (); endmodule +module testbench_comb_8x8_A_signedA; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_8x8_A_signedB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (1) + ) testbench (); +endmodule + +module testbench_comb_8x8_A_signedAB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (1) + ) testbench (); +endmodule + module testbench_comb_8x8_B; testbench #( .NEG_TRIGGER (0), @@ -266,6 +341,81 @@ module testbench_comb_8x8_B; ) testbench (); endmodule +module testbench_comb_8x8_B_signedA; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_8x8_B_signedB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (1) + ) testbench (); +endmodule + +module testbench_comb_8x8_B_signedAB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (1) + ) testbench (); +endmodule + module testbench_comb_16x16; testbench #( .NEG_TRIGGER (0), @@ -291,6 +441,81 @@ module testbench_comb_16x16; ) testbench (); endmodule +module testbench_comb_16x16_signedA; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_16x16_signedB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (1) + ) testbench (); +endmodule + +module testbench_comb_16x16_signedAB; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (1), + .B_SIGNED (1) + ) testbench (); +endmodule + module testbench_seq_16x16_A; testbench #( .NEG_TRIGGER (0), diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index 639cba2c2..d7b089503 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -38,9 +38,9 @@ struct SynthIntelPass : public ScriptPass { log("\n"); log(" -family < max10 | a10gx | cyclone10 | cyclonev | cycloneiv | cycloneive>\n"); log(" generate the synthesis netlist for the specified family.\n"); - log(" MAX10 is the default target if not family argument specified.\n"); + log(" MAX10 is the default target if no family argument specified.\n"); log(" For Cyclone GX devices, use cycloneiv argument; For Cyclone E, use cycloneive.\n"); - log(" Cyclone V and Arria 10 GX devices are experimental, use it with a10gx argument.\n"); + log(" Cyclone V and Arria 10 GX devices are experimental.\n"); log("\n"); log(" -top <module>\n"); log(" use the specified module as top module (default='top')\n"); @@ -48,6 +48,8 @@ struct SynthIntelPass : public ScriptPass { log(" -vqm <file>\n"); log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n"); log(" output file is omitted if this parameter is not specified.\n"); + log(" Note that this backend has not been tested and is likely incompatible\n"); + log(" with recent versions of Quartus.\n"); log("\n"); log(" -vpr <file>\n"); log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n"); @@ -60,10 +62,10 @@ struct SynthIntelPass : public ScriptPass { log(" synonymous to the end of the command list.\n"); log("\n"); log(" -noiopads\n"); - log(" do not use altsyncram cells in output netlist\n"); + log(" do not use IO pad cells in output netlist\n"); log("\n"); log(" -nobram\n"); - log(" do not use altsyncram cells in output netlist\n"); + log(" do not use block RAM cells in output netlist\n"); log("\n"); log(" -noflatten\n"); log(" do not flatten design before synthesis\n"); @@ -108,6 +110,7 @@ struct SynthIntelPass : public ScriptPass { } if (args[argidx] == "-vqm" && argidx + 1 < args.size()) { vout_file = args[++argidx]; + log_warning("The Quartus backend has not been tested recently and is likely incompatible with modern versions of Quartus.\n"); continue; } if (args[argidx] == "-vpr" && argidx + 1 < args.size()) { @@ -144,9 +147,13 @@ struct SynthIntelPass : public ScriptPass { if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); - if (family_opt != "max10" && family_opt != "a10gx" && family_opt != "cyclonev" && family_opt != "cycloneiv" && - family_opt != "cycloneive" && family_opt != "cyclone10") - log_cmd_error("Invalid or not family specified: '%s'\n", family_opt.c_str()); + if (family_opt != "max10" && + family_opt != "a10gx" && + family_opt != "cyclonev" && + family_opt != "cycloneiv" && + family_opt != "cycloneive" && + family_opt != "cyclone10") + log_cmd_error("Invalid or no family specified: '%s'\n", family_opt.c_str()); log_header(design, "Executing SYNTH_INTEL pass.\n"); log_push(); @@ -159,18 +166,9 @@ struct SynthIntelPass : public ScriptPass { void script() YS_OVERRIDE { if (check_label("begin")) { - if (check_label("family") && family_opt == "max10") - run("read_verilog -sv -lib +/intel/max10/cells_sim.v"); - else if (check_label("family") && family_opt == "a10gx") - run("read_verilog -sv -lib +/intel/a10gx/cells_sim.v"); - else if (check_label("family") && family_opt == "cyclonev") - run("read_verilog -sv -lib +/intel/cyclonev/cells_sim.v"); - else if (check_label("family") && family_opt == "cyclone10") - run("read_verilog -sv -lib +/intel/cyclone10/cells_sim.v"); - else if (check_label("family") && family_opt == "cycloneiv") - run("read_verilog -sv -lib +/intel/cycloneiv/cells_sim.v"); - else - run("read_verilog -sv -lib +/intel/cycloneive/cells_sim.v"); + if (check_label("family")) + run(stringf("read_verilog -sv -lib +/intel/%s/cells_sim.v", family_opt.c_str())); + // Misc and common cells run("read_verilog -sv -lib +/intel/common/m9k_bb.v"); run("read_verilog -sv -lib +/intel/common/altpll_bb.v"); @@ -188,12 +186,12 @@ struct SynthIntelPass : public ScriptPass { run("synth -run coarse"); } - if (!nobram && check_label("bram", "(skip if -nobram)")) { + if (!nobram && check_label("map_bram", "(skip if -nobram)")) { run("memory_bram -rules +/intel/common/brams.txt"); run("techmap -map +/intel/common/brams_map.v"); } - if (check_label("fine")) { + if (check_label("map_ffram")) { run("opt -fast -mux_undef -undriven -fine -full"); run("memory_map"); run("opt -undriven -fine"); @@ -219,18 +217,8 @@ struct SynthIntelPass : public ScriptPass { if (check_label("map_cells")) { if (!noiopads) run("iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I", "(unless -noiopads)"); - if (family_opt == "max10") - run("techmap -map +/intel/max10/cells_map.v"); - else if (family_opt == "a10gx") - run("techmap -map +/intel/a10gx/cells_map.v"); - else if (family_opt == "cyclonev") - run("techmap -map +/intel/cyclonev/cells_map.v"); - else if (family_opt == "cyclone10") - run("techmap -map +/intel/cyclone10/cells_map.v"); - else if (family_opt == "cycloneiv") - run("techmap -map +/intel/cycloneiv/cells_map.v"); - else - run("techmap -map +/intel/cycloneive/cells_map.v"); + run(stringf("techmap -map +/intel/%s/cells_map.v", family_opt.c_str())); + run("dffinit -highlow -ff dffeas q power_up"); run("clean -purge"); } diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index d68f03bb4..2c6e7432e 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -5,6 +5,8 @@ GENFILES += techlibs/xilinx/brams_init_36.vh GENFILES += techlibs/xilinx/brams_init_32.vh GENFILES += techlibs/xilinx/brams_init_18.vh GENFILES += techlibs/xilinx/brams_init_16.vh +GENFILES += techlibs/xilinx/brams_init_9.vh +GENFILES += techlibs/xilinx/brams_init_8.vh EXTRA_OBJS += techlibs/xilinx/brams_init.mk .SECONDARY: techlibs/xilinx/brams_init.mk @@ -18,21 +20,33 @@ techlibs/xilinx/brams_init_36.vh: techlibs/xilinx/brams_init.mk techlibs/xilinx/brams_init_32.vh: techlibs/xilinx/brams_init.mk techlibs/xilinx/brams_init_18.vh: techlibs/xilinx/brams_init.mk techlibs/xilinx/brams_init_16.vh: techlibs/xilinx/brams_init.mk +techlibs/xilinx/brams_init_9.vh: techlibs/xilinx/brams_init.mk +techlibs/xilinx/brams_init_8.vh: techlibs/xilinx/brams_init.mk $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v)) -$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams.txt)) -$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams_map.v)) -$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams_bb.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_bb.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_bb.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v)) + +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7_nowide.lut)) $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh)) $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh)) $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_18.vh)) $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_16.vh)) +$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_9.vh)) +$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_8.vh)) diff --git a/techlibs/xilinx/abc_xc7.box b/techlibs/xilinx/abc_xc7.box new file mode 100644 index 000000000..3789ff350 --- /dev/null +++ b/techlibs/xilinx/abc_xc7.box @@ -0,0 +1,58 @@ +# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf + +# NB: Inputs/Outputs must be ordered alphabetically +# (with exceptions for carry in/out) + +# Average across F7[AB]MUX +# Inputs: I0 I1 S0 +# Outputs: O +F7MUX 1 1 3 1 +204 208 286 + +# Inputs: I0 I1 S0 +# Outputs: O +MUXF8 2 1 3 1 +104 94 273 + +# Inputs: I0 I1 I2 I3 S0 S1 +# Outputs: O +$__MUXF78 3 1 6 1 +294 297 311 317 390 273 + +# CARRY4 + CARRY4_[ABCD]X +# Inputs: CYINIT DI0 DI1 DI2 DI3 S0 S1 S2 S3 CI +# Outputs: O0 O1 O2 O3 CO0 CO1 CO2 CO3 +# (NB: carry chain input/output must be last +# input/output and the entire bus has been +# moved there overriding the otherwise +# alphabetical ordering) +CARRY4 4 1 10 8 +482 - - - - 223 - - - 222 +598 407 - - - 400 205 - - 334 +584 556 537 - - 523 558 226 - 239 +642 615 596 438 - 582 618 330 227 313 +536 379 - - - 340 - - - 271 +494 465 445 - - 433 469 - - 157 +592 540 520 356 - 512 548 292 - 228 +580 526 507 398 385 508 528 378 380 114 + +# SLICEM/A6LUT +# Inputs: A0 A1 A2 A3 A4 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 WCLK WE +# Outputs: DPO SPO +RAM32X1D 5 0 13 2 +- - - - - - 631 472 407 238 127 - - +631 472 407 238 127 - - - - - - - - + +# SLICEM/A6LUT +# Inputs: A0 A1 A2 A3 A4 A5 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 WCLK WE +# Outputs: DPO SPO +RAM64X1D 6 0 15 2 +- - - - - - - 642 631 472 407 238 127 - - +642 631 472 407 238 127 - - - - - - - - - + +# SLICEM/A6LUT + F7[AB]MUX +# Inputs: A0 A1 A2 A3 A4 A5 A6 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 DPRA6 WCLK WE +# Outputs: DPO SPO +RAM128X1D 7 0 17 2 +- - - - - - - - 1009 998 839 774 605 494 450 - - +1047 1036 877 812 643 532 478 - - - - - - - - - - diff --git a/techlibs/xilinx/abc_xc7.lut b/techlibs/xilinx/abc_xc7.lut new file mode 100644 index 000000000..bcbdec127 --- /dev/null +++ b/techlibs/xilinx/abc_xc7.lut @@ -0,0 +1,15 @@ +# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/82bf5f158cd8e9a11ac4d04f1aeef48ed1a528a5/artix7/timings/CLBLL_L.sdf +# and https://github.com/SymbiFlow/prjxray-db/blob/82bf5f158cd8e9a11ac4d04f1aeef48ed1a528a5/artix7/tile_type_CLBLL_L.json + +# K area delay +1 1 127 +2 2 127 238 +3 3 127 238 407 +4 3 127 238 407 472 +5 3 127 238 407 472 631 +6 5 127 238 407 472 631 642 + # (F7[AB]MUX.S + [AC]OUTMUX) / 2 +7 10 464 513 624 793 858 1017 1028 + # F8MUX.S+BOUTMUX + # F8MUX.I0+F7MUX.S+BOUTMUX +8 20 468 585 634 745 914 979 1138 1149 diff --git a/techlibs/xilinx/abc_xc7_nowide.lut b/techlibs/xilinx/abc_xc7_nowide.lut new file mode 100644 index 000000000..fab48c879 --- /dev/null +++ b/techlibs/xilinx/abc_xc7_nowide.lut @@ -0,0 +1,10 @@ +# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/82bf5f158cd8e9a11ac4d04f1aeef48ed1a528a5/artix7/timings/CLBLL_L.sdf +# and https://github.com/SymbiFlow/prjxray-db/blob/82bf5f158cd8e9a11ac4d04f1aeef48ed1a528a5/artix7/tile_type_CLBLL_L.json + +# K area delay +1 1 127 +2 2 127 238 +3 3 127 238 407 +4 3 127 238 407 472 +5 3 127 238 407 472 631 +6 5 127 238 407 472 631 642 diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index 09a5f07e8..5c848d4e6 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -180,7 +180,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); // First one if (i == 0) begin - CARRY4 #(.IS_INITIALIZED(1'd1)) carry4_1st_part + CARRY4 carry4_1st_part ( .CYINIT(CI), .CI (1'd0), @@ -207,7 +207,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); // First one if (i == 0) begin - CARRY4 #(.IS_INITIALIZED(1'd1)) carry4_1st_full + CARRY4 carry4_1st_full ( .CYINIT(CI), .CI (1'd0), diff --git a/techlibs/xilinx/brams_init.py b/techlibs/xilinx/brams_init.py index d46a2b4f7..10057a0cb 100644 --- a/techlibs/xilinx/brams_init.py +++ b/techlibs/xilinx/brams_init.py @@ -1,5 +1,17 @@ #!/usr/bin/env python3 +with open("techlibs/xilinx/brams_init_9.vh", "w") as f: + for i in range(4): + init_snippets = [" INIT[%3d*9+8]" % (k+256*i,) for k in range(255, -1, -1)] + for k in range(4, 256, 4): + init_snippets[k] = "\n " + init_snippets[k] + print(".INITP_%02X({%s})," % (i, ",".join(init_snippets)), file=f) + for i in range(32): + init_snippets = [" INIT[%3d*9 +: 8]" % (k+32*i,) for k in range(31, -1, -1)] + for k in range(4, 32, 4): + init_snippets[k] = "\n " + init_snippets[k] + print(".INIT_%02X({%s})," % (i, ",".join(init_snippets)), file=f) + with open("techlibs/xilinx/brams_init_18.vh", "w") as f: for i in range(8): init_snippets = [" INIT[%3d*9+8]" % (k+256*i,) for k in range(255, -1, -1)] @@ -24,6 +36,10 @@ with open("techlibs/xilinx/brams_init_36.vh", "w") as f: init_snippets[k] = "\n " + init_snippets[k] print(".INIT_%02X({%s})," % (i, ",".join(init_snippets)), file=f) +with open("techlibs/xilinx/brams_init_8.vh", "w") as f: + for i in range(32): + print(".INIT_%02X(INIT[%3d*256 +: 256])," % (i, i), file=f) + with open("techlibs/xilinx/brams_init_16.vh", "w") as f: for i in range(64): print(".INIT_%02X(INIT[%3d*256 +: 256])," % (i, i), file=f) diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v index 40789ddbe..2eb9fa2c1 100644 --- a/techlibs/xilinx/cells_map.v +++ b/techlibs/xilinx/cells_map.v @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -55,7 +56,6 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o localparam [DEPTH-1:0] INIT_R = brev(INIT); parameter _TECHMAP_CONSTMSK_L_ = 0; - parameter _TECHMAP_CONSTVAL_L_ = 0; wire CE; generate @@ -88,69 +88,280 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o end else if (DEPTH > 65 && DEPTH <= 96) begin wire T0, T1, T2, T3, T4, T5, T6; - SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); + SRLC32E #(.INIT(INIT_R[32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1)); SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); \$__XILINX_SHREG_ #(.DEPTH(DEPTH-64), .INIT(INIT[DEPTH-64-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_2 (.C(C), .D(T3), .L(L[4:0]), .E(E), .Q(T4)); if (&_TECHMAP_CONSTMSK_L_) assign Q = T4; - else begin - MUXF7 fpga_mux_0 (.O(T5), .I0(T0), .I1(T2), .S(L[5])); - MUXF7 fpga_mux_1 (.O(T6), .I0(T4), .I1(1'b0 /* unused */), .S(L[5])); - MUXF8 fpga_mux_2 (.O(Q), .I0(T5), .I1(T6), .S(L[6])); - end + else + \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(1'bx), .S0(L[5]), .S1(L[6]), .O(Q)); end else if (DEPTH > 97 && DEPTH < 128) begin wire T0, T1, T2, T3, T4, T5, T6, T7, T8; - SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); + SRLC32E #(.INIT(INIT_R[32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1)); SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5)); \$__XILINX_SHREG_ #(.DEPTH(DEPTH-96), .INIT(INIT[DEPTH-96-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_3 (.C(C), .D(T5), .L(L[4:0]), .E(E), .Q(T6)); if (&_TECHMAP_CONSTMSK_L_) assign Q = T6; - else begin - MUXF7 fpga_mux_0 (.O(T7), .I0(T0), .I1(T2), .S(L[5])); - MUXF7 fpga_mux_1 (.O(T8), .I0(T4), .I1(T6), .S(L[5])); - MUXF8 fpga_mux_2 (.O(Q), .I0(T7), .I1(T8), .S(L[6])); - end + else + \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q)); end else if (DEPTH == 128) begin wire T0, T1, T2, T3, T4, T5, T6; - SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1)); - SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); - SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5)); + SRLC32E #(.INIT(INIT_R[ 32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1)); + SRLC32E #(.INIT(INIT_R[ 64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3)); + SRLC32E #(.INIT(INIT_R[ 96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5)); SRLC32E #(.INIT(INIT_R[128-1:96]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_3 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T5), .Q(T6), .Q31(SO)); if (&_TECHMAP_CONSTMSK_L_) assign Q = T6; - else begin - wire T7, T8; - MUXF7 fpga_mux_0 (.O(T7), .I0(T0), .I1(T2), .S(L[5])); - MUXF7 fpga_mux_1 (.O(T8), .I0(T4), .I1(T6), .S(L[5])); - MUXF8 fpga_mux_2 (.O(Q), .I0(T7), .I1(T8), .S(L[6])); - end + else + \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T4), .I3(T6), .S0(L[5]), .S1(L[6]), .O(Q)); + end + // For fixed length, if just 1 over a convenient value, decompose + else if (DEPTH <= 129 && &_TECHMAP_CONSTMSK_L_) begin + wire T; + \$__XILINX_SHREG_ #(.DEPTH(DEPTH-1), .INIT(INIT[DEPTH-1:1]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(D), .L({32{1'b1}}), .E(E), .Q(T)); + \$__XILINX_SHREG_ #(.DEPTH(1), .INIT(INIT[0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(T), .L(L), .E(E), .Q(Q)); end - else if (DEPTH <= 129 && ~&_TECHMAP_CONSTMSK_L_) begin - // Handle cases where fixed-length depth is - // just 1 over a convenient value + // For variable length, if just 1 over a convenient value, then bump up one more + else if (DEPTH < 129 && ~&_TECHMAP_CONSTMSK_L_) \$__XILINX_SHREG_ #(.DEPTH(DEPTH+1), .INIT({INIT,1'b0}), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(L), .E(E), .Q(Q)); + else begin + localparam depth0 = 128; + localparam num_srl128 = DEPTH / depth0; + localparam depthN = DEPTH % depth0; + wire [num_srl128 + (depthN > 0 ? 1 : 0) - 1:0] T; + wire [num_srl128 + (depthN > 0 ? 1 : 0) :0] S; + assign S[0] = D; + genvar i; + for (i = 0; i < num_srl128; i++) + \$__XILINX_SHREG_ #(.DEPTH(depth0), .INIT(INIT[DEPTH-1-i*depth0-:depth0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl (.C(C), .D(S[i]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[i]), .SO(S[i+1])); + + if (depthN > 0) + \$__XILINX_SHREG_ #(.DEPTH(depthN), .INIT(INIT[depthN-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_last (.C(C), .D(S[num_srl128]), .L(L[$clog2(depth0)-1:0]), .E(E), .Q(T[num_srl128])); + + if (&_TECHMAP_CONSTMSK_L_) + assign Q = T[num_srl128 + (depthN > 0 ? 1 : 0) - 1]; + else + assign Q = T[L[DEPTH-1:$clog2(depth0)]]; + end + endgenerate +endmodule + +`ifdef MIN_MUX_INPUTS +module \$__XILINX_SHIFTX (A, B, Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + input [A_WIDTH-1:0] A; + input [B_WIDTH-1:0] B; + output [Y_WIDTH-1:0] Y; + + parameter [A_WIDTH-1:0] _TECHMAP_CONSTMSK_A_ = 0; + parameter [A_WIDTH-1:0] _TECHMAP_CONSTVAL_A_ = 0; + parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0; + parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0; + + function integer A_WIDTH_trimmed; + input integer start; + begin + A_WIDTH_trimmed = start; + while (A_WIDTH_trimmed > 0 && _TECHMAP_CONSTMSK_A_[A_WIDTH_trimmed-1] && _TECHMAP_CONSTVAL_A_[A_WIDTH_trimmed-1] === 1'bx) + A_WIDTH_trimmed = A_WIDTH_trimmed - 1; + end + endfunction + + generate + genvar i, j; + // Bit-blast + if (Y_WIDTH > 1) begin + for (i = 0; i < Y_WIDTH; i++) + \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH-Y_WIDTH+1), .B_WIDTH(B_WIDTH), .Y_WIDTH(1'd1)) bitblast (.A(A[A_WIDTH-Y_WIDTH+i:i]), .B(B), .Y(Y[i])); + end + // If the LSB of B is constant zero (and Y_WIDTH is 1) then + // we can optimise by removing every other entry from A + // and popping the constant zero from B + else if (_TECHMAP_CONSTMSK_B_[0] && !_TECHMAP_CONSTVAL_B_[0]) begin + wire [(A_WIDTH+1)/2-1:0] A_i; + for (i = 0; i < (A_WIDTH+1)/2; i++) + assign A_i[i] = A[i*2]; + \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH((A_WIDTH+1'd1)/2'd2), .B_WIDTH(B_WIDTH-1'd1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A_i), .B(B[B_WIDTH-1:1]), .Y(Y)); + end + // Trim off any leading 1'bx -es in A + else if (_TECHMAP_CONSTMSK_A_[A_WIDTH-1] && _TECHMAP_CONSTVAL_A_[A_WIDTH-1] === 1'bx) begin + localparam A_WIDTH_new = A_WIDTH_trimmed(A_WIDTH-1); + \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH_new), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A[A_WIDTH_new-1:0]), .B(B), .Y(Y)); + end + else if (A_WIDTH < `MIN_MUX_INPUTS) begin + wire _TECHMAP_FAIL_ = 1; + end + else if (A_WIDTH == 2) begin + MUXF7 fpga_hard_mux (.I0(A[0]), .I1(A[1]), .S(B[0]), .O(Y)); + end + else if (A_WIDTH <= 4) begin + wire [4-1:0] Ax; + if (A_WIDTH == 4) + assign Ax = A; + else + // Rather than extend with 1'bx which gets flattened to 1'b0 + // causing the "don't care" status to get lost, extend with + // the same driver of F7B.I0 so that we can optimise F7B away + // later + assign Ax = {A[1], A}; + \$__XILINX_MUXF78 fpga_hard_mux (.I0(Ax[0]), .I1(Ax[2]), .I2(Ax[1]), .I3(Ax[3]), .S0(B[1]), .S1(B[0]), .O(Y)); + end + // Note that the following decompositions are 'backwards' in that + // the LSBs are placed on the hard resources, and the soft resources + // are used for MSBs. + // This has the effect of more effectively utilising the hard mux; + // take for example a 5:1 multiplexer, currently this would map as: + // + // A[0] \___ __ A[0] \__ __ + // A[4] / \| \ whereas the more A[1] / \| \ + // A[1] _____| | obvious mapping A[2] \___| | + // A[2] _____| |-- of MSBs to hard A[3] / | |__ + // A[3]______| | resources would A[4] ____| | + // |__/ lead to: 1'bx ____| | + // || |__/ + // || || + // B[1:0] B[1:2] + // + // Expectation would be that the 'forward' mapping (right) is more + // area efficient (consider a 9:1 multiplexer using 2x4:1 multiplexers + // on its I0 and I1 inputs, and A[8] and 1'bx on its I2 and I3 inputs) + // but that the 'backwards' mapping (left) is more delay efficient + // since smaller LUTs are faster than wider ones. + else if (A_WIDTH <= 8) begin + wire [8-1:0] Ax = {{{8-A_WIDTH}{1'bx}}, A}; + wire T0 = B[2] ? Ax[4] : Ax[0]; + wire T1 = B[2] ? Ax[5] : Ax[1]; + wire T2 = B[2] ? Ax[6] : Ax[2]; + wire T3 = B[2] ? Ax[7] : Ax[3]; + \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T1), .I3(T3), .S0(B[1]), .S1(B[0]), .O(Y)); + end + else if (A_WIDTH <= 16) begin + wire [16-1:0] Ax = {{{16-A_WIDTH}{1'bx}}, A}; + wire T0 = B[2] ? B[3] ? Ax[12] : Ax[4] + : B[3] ? Ax[ 8] : Ax[0]; + wire T1 = B[2] ? B[3] ? Ax[13] : Ax[5] + : B[3] ? Ax[ 9] : Ax[1]; + wire T2 = B[2] ? B[3] ? Ax[14] : Ax[6] + : B[3] ? Ax[10] : Ax[2]; + wire T3 = B[2] ? B[3] ? Ax[15] : Ax[7] + : B[3] ? Ax[11] : Ax[3]; + \$__XILINX_MUXF78 fpga_hard_mux (.I0(T0), .I1(T2), .I2(T1), .I3(T3), .S0(B[1]), .S1(B[0]), .O(Y)); end else begin - localparam lower_clog2 = $clog2((DEPTH+1)/2); - localparam lower_depth = 2 ** lower_clog2; - wire T0, T1, T2, T3; - if (&_TECHMAP_CONSTMSK_L_) begin - \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(lower_depth-1), .E(E), .Q(T0)); - \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T0), .L(DEPTH-lower_depth-1), .E(E), .Q(Q), .SO(T3)); - end - else begin - \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(L[lower_clog2-1:0]), .E(E), .Q(T0), .SO(T1)); - \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L[lower_clog2-1:0]), .E(E), .Q(T2), .SO(T3)); - assign Q = L[lower_clog2] ? T2 : T0; - end - if (DEPTH == 2 * lower_depth) - assign SO = T3; + localparam num_mux16 = (A_WIDTH+15) / 16; + localparam clog2_num_mux16 = $clog2(num_mux16); + wire [num_mux16-1:0] T; + wire [num_mux16*16-1:0] Ax = {{(num_mux16*16-A_WIDTH){1'bx}}, A}; + for (i = 0; i < num_mux16; i++) + \$__XILINX_SHIFTX #( + .A_SIGNED(A_SIGNED), + .B_SIGNED(B_SIGNED), + .A_WIDTH(16), + .B_WIDTH(4), + .Y_WIDTH(Y_WIDTH) + ) fpga_mux ( + .A(Ax[i*16+:16]), + .B(B[3:0]), + .Y(T[i]) + ); + \$__XILINX_SHIFTX #( + .A_SIGNED(A_SIGNED), + .B_SIGNED(B_SIGNED), + .A_WIDTH(num_mux16), + .B_WIDTH(clog2_num_mux16), + .Y_WIDTH(Y_WIDTH) + ) _TECHMAP_REPLACE_ ( + .A(T), + .B(B[B_WIDTH-1-:clog2_num_mux16]), + .Y(Y)); end endgenerate endmodule -`ifndef SRL_ONLY +(* techmap_celltype = "$__XILINX_SHIFTX" *) +module _90__XILINX_SHIFTX (A, B, Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + input [A_WIDTH-1:0] A; + input [B_WIDTH-1:0] B; + output [Y_WIDTH-1:0] Y; + + \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A), .B(B), .Y(Y)); +endmodule + +module \$_MUX_ (A, B, S, Y); + input A, B, S; + output Y; + generate + if (`MIN_MUX_INPUTS == 2) + \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(2), .B_WIDTH(1), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({B,A}), .B(S), .Y(Y)); + else + wire _TECHMAP_FAIL_ = 1; + endgenerate +endmodule + +module \$_MUX4_ (A, B, C, D, S, T, Y); + input A, B, C, D, S, T; + output Y; + \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(4), .B_WIDTH(2), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({D,C,B,A}), .B({T,S}), .Y(Y)); +endmodule + +module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y); + input A, B, C, D, E, F, G, H, S, T, U; + output Y; + \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(8), .B_WIDTH(3), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({H,G,F,E,D,C,B,A}), .B({U,T,S}), .Y(Y)); +endmodule + +module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y); + input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V; + output Y; + \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(16), .B_WIDTH(4), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A}), .B({V,U,T,S}), .Y(Y)); +endmodule +`endif + +`ifndef _ABC +module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1); + output O; + input I0, I1, I2, I3, S0, S1; + wire T0, T1; + parameter _TECHMAP_BITS_CONNMAP_ = 0; + parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I0_ = 0; + parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I1_ = 0; + parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I2_ = 0; + parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I3_ = 0; + parameter _TECHMAP_CONSTMSK_S0_ = 0; + parameter _TECHMAP_CONSTVAL_S0_ = 0; + parameter _TECHMAP_CONSTMSK_S1_ = 0; + parameter _TECHMAP_CONSTVAL_S1_ = 0; + if (_TECHMAP_CONSTMSK_S0_ && _TECHMAP_CONSTVAL_S0_ === 1'b1) + assign T0 = I1; + else if (_TECHMAP_CONSTMSK_S0_ || _TECHMAP_CONNMAP_I0_ === _TECHMAP_CONNMAP_I1_) + assign T0 = I0; + else + MUXF7 mux7a (.I0(I0), .I1(I1), .S(S0), .O(T0)); + if (_TECHMAP_CONSTMSK_S0_ && _TECHMAP_CONSTVAL_S0_ === 1'b1) + assign T1 = I3; + else if (_TECHMAP_CONSTMSK_S0_ || _TECHMAP_CONNMAP_I2_ === _TECHMAP_CONNMAP_I3_) + assign T1 = I2; + else + MUXF7 mux7b (.I0(I2), .I1(I3), .S(S0), .O(T1)); + if (_TECHMAP_CONSTMSK_S1_ && _TECHMAP_CONSTVAL_S1_ === 1'b1) + assign O = T1; + else if (_TECHMAP_CONSTMSK_S1_ || (_TECHMAP_CONNMAP_I0_ === _TECHMAP_CONNMAP_I1_ && _TECHMAP_CONNMAP_I1_ === _TECHMAP_CONNMAP_I2_ && _TECHMAP_CONNMAP_I2_ === _TECHMAP_CONNMAP_I3_)) + assign O = T0; + else + MUXF8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O)); +endmodule `endif diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 3a4540b83..05e46b4e7 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -159,18 +159,29 @@ module MUXCY(output O, input CI, DI, S); assign O = S ? CI : DI; endmodule +(* abc_box_id = 1, lib_whitebox *) module MUXF7(output O, input I0, I1, S); assign O = S ? I1 : I0; endmodule +(* abc_box_id = 2, lib_whitebox *) module MUXF8(output O, input I0, I1, S); assign O = S ? I1 : I0; endmodule +`ifdef _ABC +(* abc_box_id = 3, lib_whitebox *) +module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1); + assign O = S1 ? (S0 ? I3 : I2) + : (S0 ? I1 : I0); +endmodule +`endif + module XORCY(output O, input CI, LI); assign O = CI ^ LI; endmodule +(* abc_box_id = 4, abc_carry="CI,CO", lib_whitebox *) module CARRY4(output [3:0] CO, O, input CI, CYINIT, input [3:0] DI, S); assign O = S ^ {CO[2:0], CI | CYINIT}; assign CO[0] = S[0] ? CI | CYINIT : DI[0]; @@ -215,7 +226,7 @@ module FDRE (output reg Q, input C, CE, D, R); endmodule module FDSE (output reg Q, input C, CE, D, S); - parameter [0:0] INIT = 1'b0; + parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; parameter [0:0] IS_S_INVERTED = 1'b0; @@ -241,7 +252,7 @@ module FDCE (output reg Q, input C, CE, D, CLR); endmodule module FDPE (output reg Q, input C, CE, D, PRE); - parameter [0:0] INIT = 1'b0; + parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; parameter [0:0] IS_PRE_INVERTED = 1'b0; @@ -278,6 +289,25 @@ module FDPE_1 (output reg Q, input C, CE, D, PRE); always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D; endmodule +(* abc_box_id = 5, abc_scc_break="D,WE" *) +module RAM32X1D ( + output DPO, SPO, + input D, WCLK, WE, + input A0, A1, A2, A3, A4, + input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4 +); + parameter INIT = 32'h0; + parameter IS_WCLK_INVERTED = 1'b0; + wire [4:0] a = {A4, A3, A2, A1, A0}; + wire [4:0] dpra = {DPRA4, DPRA3, DPRA2, DPRA1, DPRA0}; + reg [31:0] mem = INIT; + assign SPO = mem[a]; + assign DPO = mem[dpra]; + wire clk = WCLK ^ IS_WCLK_INVERTED; + always @(posedge clk) if (WE) mem[a] <= D; +endmodule + +(* abc_box_id = 6, abc_scc_break="D,WE" *) module RAM64X1D ( output DPO, SPO, input D, WCLK, WE, @@ -295,6 +325,7 @@ module RAM64X1D ( always @(posedge clk) if (WE) mem[a] <= D; endmodule +(* abc_box_id = 7, abc_scc_break="D,WE" *) module RAM128X1D ( output DPO, SPO, input D, WCLK, WE, diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh index 8e39b440d..53b528820 100644 --- a/techlibs/xilinx/cells_xtra.sh +++ b/techlibs/xilinx/cells_xtra.sh @@ -116,16 +116,16 @@ function xtract_cell_decl() xtract_cell_decl PS7 "(* keep *)" xtract_cell_decl PULLDOWN xtract_cell_decl PULLUP - xtract_cell_decl RAM128X1D + #xtract_cell_decl RAM128X1D xtract_cell_decl RAM128X1S xtract_cell_decl RAM256X1S xtract_cell_decl RAM32M - xtract_cell_decl RAM32X1D + #xtract_cell_decl RAM32X1D xtract_cell_decl RAM32X1S xtract_cell_decl RAM32X1S_1 xtract_cell_decl RAM32X2S xtract_cell_decl RAM64M - xtract_cell_decl RAM64X1D + #xtract_cell_decl RAM64X1D xtract_cell_decl RAM64X1S xtract_cell_decl RAM64X1S_1 xtract_cell_decl RAM64X2S diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index fbcc74682..15fa1b63a 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -3655,17 +3655,6 @@ module PULLUP (...); output O; endmodule -module RAM128X1D (...); - parameter [127:0] INIT = 128'h00000000000000000000000000000000; - parameter [0:0] IS_WCLK_INVERTED = 1'b0; - output DPO, SPO; - input [6:0] A; - input [6:0] DPRA; - input D; - input WCLK; - input WE; -endmodule - module RAM128X1S (...); parameter [127:0] INIT = 128'h00000000000000000000000000000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; @@ -3705,13 +3694,6 @@ module RAM32M (...); input WE; endmodule -module RAM32X1D (...); - parameter [31:0] INIT = 32'h00000000; - parameter [0:0] IS_WCLK_INVERTED = 1'b0; - output DPO, SPO; - input A0, A1, A2, A3, A4, D, DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, WCLK, WE; -endmodule - module RAM32X1S (...); parameter [31:0] INIT = 32'h00000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; @@ -3756,13 +3738,6 @@ module RAM64M (...); input WE; endmodule -module RAM64X1D (...); - parameter [63:0] INIT = 64'h0000000000000000; - parameter [0:0] IS_WCLK_INVERTED = 1'b0; - output DPO, SPO; - input A0, A1, A2, A3, A4, A5, D, DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5, WCLK, WE; -endmodule - module RAM64X1S (...); parameter [63:0] INIT = 64'h0000000000000000; parameter [0:0] IS_WCLK_INVERTED = 1'b0; diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/drams.txt index 91632bcee..2613c206c 100644 --- a/techlibs/xilinx/drams.txt +++ b/techlibs/xilinx/drams.txt @@ -1,4 +1,17 @@ +bram $__XILINX_RAM32X1D + init 1 + abits 5 + dbits 1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 0 1 + clkpol 0 2 +endbram + bram $__XILINX_RAM64X1D init 1 abits 6 @@ -25,6 +38,13 @@ bram $__XILINX_RAM128X1D clkpol 0 2 endbram +match $__XILINX_RAM32X1D + min bits 3 + min wports 1 + make_outreg + or_next_if_better +endmatch + match $__XILINX_RAM64X1D min bits 5 min wports 1 diff --git a/techlibs/xilinx/drams_map.v b/techlibs/xilinx/drams_map.v index 47476b592..77041ca86 100644 --- a/techlibs/xilinx/drams_map.v +++ b/techlibs/xilinx/drams_map.v @@ -1,4 +1,38 @@ +module \$__XILINX_RAM32X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); + parameter [31:0] INIT = 32'bx; + parameter CLKPOL2 = 1; + input CLK1; + + input [4:0] A1ADDR; + output A1DATA; + + input [4:0] B1ADDR; + input B1DATA; + input B1EN; + + RAM32X1D #( + .INIT(INIT), + .IS_WCLK_INVERTED(!CLKPOL2) + ) _TECHMAP_REPLACE_ ( + .DPRA0(A1ADDR[0]), + .DPRA1(A1ADDR[1]), + .DPRA2(A1ADDR[2]), + .DPRA3(A1ADDR[3]), + .DPRA4(A1ADDR[4]), + .DPO(A1DATA), + + .A0(B1ADDR[0]), + .A1(B1ADDR[1]), + .A2(B1ADDR[2]), + .A3(B1ADDR[3]), + .A4(B1ADDR[4]), + .D(B1DATA), + .WCLK(CLK1), + .WE(B1EN) + ); +endmodule + module \$__XILINX_RAM64X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); parameter [63:0] INIT = 64'bx; parameter CLKPOL2 = 1; diff --git a/techlibs/xilinx/ff_map.v b/techlibs/xilinx/ff_map.v index 13beaa6ae..4571f6d5c 100644 --- a/techlibs/xilinx/ff_map.v +++ b/techlibs/xilinx/ff_map.v @@ -33,10 +33,10 @@ module \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPL module \$_DFF_PN0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule -module \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule -module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule -module \$_DFF_PN1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule -module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); FDPE #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule `endif diff --git a/techlibs/xilinx/lut_map.v b/techlibs/xilinx/lut_map.v index d07c59dee..13d3c3268 100644 --- a/techlibs/xilinx/lut_map.v +++ b/techlibs/xilinx/lut_map.v @@ -29,61 +29,86 @@ module \$lut (A, Y); input [WIDTH-1:0] A; output Y; + // Need to swap input ordering, and fix init accordingly, + // to match ABC's expectation of LUT inputs in non-decreasing + // delay order + function [WIDTH-1:0] permute_index; + input [WIDTH-1:0] i; + integer j; + begin + permute_index = 0; + for (j = 0; j < WIDTH; j = j + 1) + permute_index[WIDTH-1 - j] = i[j]; + end + endfunction + + function [2**WIDTH-1:0] permute_init; + input [2**WIDTH-1:0] orig; + integer i; + begin + permute_init = 0; + for (i = 0; i < 2**WIDTH; i = i + 1) + permute_init[i] = orig[permute_index(i)]; + end + endfunction + + parameter [2**WIDTH-1:0] P_LUT = permute_init(LUT); + generate if (WIDTH == 1) begin - LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + LUT1 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0])); end else if (WIDTH == 2) begin - LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1])); + LUT2 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[1]), .I1(A[0])); end else if (WIDTH == 3) begin - LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(A[2])); + LUT3 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[2]), .I1(A[1]), .I2(A[0])); end else if (WIDTH == 4) begin - LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3])); + LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[3]), .I1(A[2]), .I2(A[1]), + .I3(A[0])); end else if (WIDTH == 5) begin - LUT5 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4])); + LUT5 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[4]), .I1(A[3]), .I2(A[2]), + .I3(A[1]), .I4(A[0])); end else if (WIDTH == 6) begin - LUT6 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[5]), .I1(A[4]), .I2(A[3]), + .I3(A[2]), .I4(A[1]), .I5(A[0])); end else if (WIDTH == 7) begin wire T0, T1; - LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); - LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); - MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[6])); + LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0), + .I0(A[6]), .I1(A[5]), .I2(A[4]), + .I3(A[3]), .I4(A[2]), .I5(A[1])); + LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1), + .I0(A[6]), .I1(A[5]), .I2(A[4]), + .I3(A[3]), .I4(A[2]), .I5(A[1])); + MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[0])); end else if (WIDTH == 8) begin wire T0, T1, T2, T3, T4, T5; - LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); - LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); - LUT6 #(.INIT(LUT[191:128])) fpga_lut_2 (.O(T2), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); - LUT6 #(.INIT(LUT[255:192])) fpga_lut_3 (.O(T3), - .I0(A[0]), .I1(A[1]), .I2(A[2]), - .I3(A[3]), .I4(A[4]), .I5(A[5])); - MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[6])); - MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[6])); - MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[7])); + LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0), + .I0(A[7]), .I1(A[6]), .I2(A[5]), + .I3(A[4]), .I4(A[3]), .I5(A[2])); + LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1), + .I0(A[7]), .I1(A[6]), .I2(A[5]), + .I3(A[4]), .I4(A[3]), .I5(A[2])); + LUT6 #(.INIT(P_LUT[191:128])) fpga_lut_2 (.O(T2), + .I0(A[7]), .I1(A[6]), .I2(A[5]), + .I3(A[4]), .I4(A[3]), .I5(A[2])); + LUT6 #(.INIT(P_LUT[255:192])) fpga_lut_3 (.O(T3), + .I0(A[7]), .I1(A[6]), .I2(A[5]), + .I3(A[4]), .I4(A[3]), .I5(A[2])); + MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[1])); + MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[1])); + MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[0])); end else begin wire _TECHMAP_FAIL_ = 1; end diff --git a/techlibs/xilinx/mux_map.v b/techlibs/xilinx/mux_map.v new file mode 100644 index 000000000..91aaf2118 --- /dev/null +++ b/techlibs/xilinx/mux_map.v @@ -0,0 +1,71 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// The purpose of these mapping rules is to allow preserve all (sufficiently +// wide) $shiftx cells during 'techmap' so that they can be mapped to hard +// resources, rather than being bit-blasted to gates during 'techmap' +// execution + +module \$shiftx (A, B, Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + input [A_WIDTH-1:0] A; + input [B_WIDTH-1:0] B; + output [Y_WIDTH-1:0] Y; + + parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0; + parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0; + + generate + if (B_SIGNED) begin + if (_TECHMAP_CONSTMSK_B_[B_WIDTH-1] && (_TECHMAP_CONSTVAL_B_[B_WIDTH-1] == 1'b0 || _TECHMAP_CONSTVAL_B_[B_WIDTH-1] === 1'bx)) + // Optimisation to remove B_SIGNED if sign bit of B is constant-0 + \$shiftx #( + .A_SIGNED(A_SIGNED), + .B_SIGNED(0), + .A_WIDTH(A_WIDTH), + .B_WIDTH(B_WIDTH-1'd1), + .Y_WIDTH(Y_WIDTH) + ) _TECHMAP_REPLACE_ ( + .A(A), .B(B[B_WIDTH-2:0]), .Y(Y) + ); + else + wire _TECHMAP_FAIL_ = 1; + end + else begin + if (((A_WIDTH + Y_WIDTH - 1) / Y_WIDTH) < `MIN_MUX_INPUTS) + wire _TECHMAP_FAIL_ = 1; + else + \$__XILINX_SHIFTX #( + .A_SIGNED(A_SIGNED), + .B_SIGNED(B_SIGNED), + .A_WIDTH(A_WIDTH), + .B_WIDTH(B_WIDTH), + .Y_WIDTH(Y_WIDTH) + ) _TECHMAP_REPLACE_ ( + .A(A), .B(B), .Y(Y) + ); + end + endgenerate +endmodule diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index c20cac09b..b672a0d4f 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * (C) 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,6 +26,9 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#define XC7_WIRE_DELAY 300 // Number with which ABC will map a 6-input gate + // to one LUT6 (instead of a LUT5 + LUT2) + struct SynthXilinxPass : public ScriptPass { SynthXilinxPass() : ScriptPass("synth_xilinx", "synthesis for Xilinx FPGAs") { } @@ -42,8 +46,9 @@ struct SynthXilinxPass : public ScriptPass log(" -top <module>\n"); log(" use the specified module as top module\n"); log("\n"); - log(" -arch {xcup|xcu|xc7|xc6s}\n"); + log(" -family {xcup|xcu|xc7|xc6s}\n"); log(" run synthesis for the specified Xilinx architecture\n"); + log(" generate the synthesis netlist for the specified family.\n"); log(" default: xc7\n"); log("\n"); log(" -edif <file>\n"); @@ -67,6 +72,17 @@ struct SynthXilinxPass : public ScriptPass log(" -nosrl\n"); log(" disable inference of shift registers\n"); log("\n"); + log(" -nocarry\n"); + log(" do not use XORCY/MUXCY/CARRY4 cells in output netlist\n"); + log("\n"); + log(" -nowidelut\n"); + log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n"); + log("\n"); + log(" -widemux <int>\n"); + log(" enable inference of hard multiplexer resources (MUXF[78]) for muxes at or\n"); + log(" above this number of inputs (minimum value 2, recommended value >= 5).\n"); + log(" default: 0 (no inference)\n"); + log("\n"); log(" -run <from_label>:<to_label>\n"); log(" only run the commands between the labels (see below). an empty\n"); log(" from label is synonymous to 'begin', and empty to label is\n"); @@ -78,27 +94,36 @@ struct SynthXilinxPass : public ScriptPass log(" -retime\n"); log(" run 'abc' with -dff option\n"); log("\n"); + log(" -abc9\n"); + log(" use new ABC9 flow (EXPERIMENTAL)\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - std::string top_opt, edif_file, blif_file, arch; - bool flatten, retime, vpr, nobram, nodram, nosrl; + std::string top_opt, edif_file, blif_file, family; + bool flatten, retime, vpr, nobram, nodram, nosrl, nocarry, nowidelut, abc9; + int widemux; void clear_flags() YS_OVERRIDE { top_opt = "-auto-top"; edif_file.clear(); blif_file.clear(); + family = "xc7"; flatten = false; retime = false; vpr = false; + nocarry = false; nobram = false; nodram = false; nosrl = false; - arch = "xc7"; + nocarry = false; + nowidelut = false; + abc9 = false; + widemux = 0; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -113,8 +138,8 @@ struct SynthXilinxPass : public ScriptPass top_opt = "-top " + args[++argidx]; continue; } - if (args[argidx] == "-arch" && argidx+1 < args.size()) { - arch = args[++argidx]; + if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) { + family = args[++argidx]; continue; } if (args[argidx] == "-edif" && argidx+1 < args.size()) { @@ -141,10 +166,22 @@ struct SynthXilinxPass : public ScriptPass retime = true; continue; } + if (args[argidx] == "-nocarry") { + nocarry = true; + continue; + } + if (args[argidx] == "-nowidelut") { + nowidelut = true; + continue; + } if (args[argidx] == "-vpr") { vpr = true; continue; } + if (args[argidx] == "-nocarry") { + nocarry = true; + continue; + } if (args[argidx] == "-nobram") { nobram = true; continue; @@ -157,16 +194,30 @@ struct SynthXilinxPass : public ScriptPass nosrl = true; continue; } + if (args[argidx] == "-widemux" && argidx+1 < args.size()) { + widemux = std::stoi(args[++argidx]); + continue; + } + if (args[argidx] == "-abc9") { + abc9 = true; + continue; + } break; } extra_args(args, argidx, design); - if (arch != "xcup" && arch != "xcu" && arch != "xc7" && arch != "xc6s") - log_cmd_error("Invalid Xilinx -arch setting: %s\n", arch.c_str()); + if (family != "xcup" && family != "xcu" && family != "xc7" && family != "xc6s") + log_cmd_error("Invalid Xilinx -family setting: '%s'.\n", family.c_str()); + + if (widemux != 0 && widemux < 2) + log_cmd_error("-widemux value must be 0 or >= 2.\n"); if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); + if (abc9 && retime) + log_cmd_error("-retime option not currently compatible with -abc9!\n"); + log_header(design, "Executing SYNTH_XILINX pass.\n"); log_push(); @@ -179,33 +230,74 @@ struct SynthXilinxPass : public ScriptPass { if (check_label("begin")) { if (vpr) - run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); + run("read_verilog -lib -icells -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); else - run("read_verilog -lib +/xilinx/cells_sim.v"); + run("read_verilog -lib -icells -D _ABC +/xilinx/cells_sim.v"); run("read_verilog -lib +/xilinx/cells_xtra.v"); - if (!nobram || help_mode) - run("read_verilog -lib +/xilinx/brams_bb.v", "(skip if '-nobram')"); + if (help_mode) { + run("read_verilog -lib +/xilinx/{family}_brams_bb.v"); + } else if (family == "xc6s") { + run("read_verilog -lib +/xilinx/xc6s_brams_bb.v"); + } else if (family == "xc7") { + run("read_verilog -lib +/xilinx/xc7_brams_bb.v"); + } run(stringf("hierarchy -check %s", top_opt.c_str())); } - if (check_label("flatten", "(with '-flatten' only)")) { - if (flatten || help_mode) { - run("proc"); - run("flatten"); + if (check_label("coarse")) { + run("proc"); + if (help_mode || flatten) + run("flatten", "(if -flatten)"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt"); + if (help_mode) + run("wreduce [-keepdc]", "(option for '-widemux')"); + else + run("wreduce" + std::string(widemux > 0 ? " -keepdc" : "")); + run("peepopt"); + run("opt_clean"); + + if (widemux > 0 || help_mode) + run("muxpack", " ('-widemux' only)"); + + // shregmap -tech xilinx can cope with $shiftx and $mux + // cells for identifying variable-length shift registers, + // so attempt to convert $pmux-es to the former + // Also: wide multiplexer inference benefits from this too + if (!(nosrl && widemux == 0) || help_mode) { + run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')"); + run("clean", " (skip if '-nosrl' and '-widemux=0')"); } - } - if (check_label("coarse")) { - run("synth -run coarse"); + run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6"); + run("alumacc"); + run("share"); + run("opt"); + run("fsm"); + run("opt -fast"); + run("memory -nomap"); + run("opt_clean"); } if (check_label("bram", "(skip if '-nobram')")) { - if (!nobram || help_mode) { - run("memory_bram -rules +/xilinx/brams.txt"); - run("techmap -map +/xilinx/brams_map.v"); + if (help_mode) { + run("memory_bram -rules +/xilinx/{family}_brams.txt"); + run("techmap -map +/xilinx/{family}_brams_map.v"); + } else if (!nobram) { + if (family == "xc6s") { + run("memory_bram -rules +/xilinx/xc6s_brams.txt"); + run("techmap -map +/xilinx/xc6s_brams_map.v"); + } else if (family == "xc7") { + run("memory_bram -rules +/xilinx/xc7_brams.txt"); + run("techmap -map +/xilinx/xc7_brams_map.v"); + } else { + log_warning("Block RAM inference not yet supported for family %s.\n", family.c_str()); + } } } @@ -217,46 +309,97 @@ struct SynthXilinxPass : public ScriptPass } if (check_label("fine")) { - // shregmap -tech xilinx can cope with $shiftx and $mux - // cells for identifiying variable-length shift registers, - // so attempt to convert $pmux-es to the former - if (!nosrl || help_mode) - run("pmux2shiftx", "(skip if '-nosrl')"); - - run("opt -fast -full"); + if (widemux > 0) + run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover + // performs less efficiently + else + run("opt -fast -full"); run("memory_map"); run("dffsr2dff"); run("dff2dffe"); + if (help_mode) { + run("simplemap t:$mux", " ('-widemux' only)"); + run("muxcover <internal options>, ('-widemux' only)"); + } + else if (widemux > 0) { + run("simplemap t:$mux"); + constexpr int cost_mux2 = 100; + std::string muxcover_args = stringf(" -nodecode -mux2=%d", cost_mux2); + switch (widemux) { + case 2: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2+1, cost_mux2+2, cost_mux2+3); break; + case 3: + case 4: muxcover_args += stringf(" -mux4=%d -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-2, cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break; + case 5: + case 6: + case 7: + case 8: muxcover_args += stringf(" -mux8=%d -mux16=%d", cost_mux2*(widemux-1)-1, cost_mux2*(widemux-1)); break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + default: muxcover_args += stringf(" -mux16=%d", cost_mux2*(widemux-1)-1); break; + } + run("muxcover " + muxcover_args); + } run("opt -full"); - if (!vpr || help_mode) - run("techmap -map +/xilinx/arith_map.v"); - else - run("techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY"); - if (!nosrl || help_mode) { // shregmap operates on bit-level flops, not word-level, // so break those down here - run("simplemap t:$dff t:$dffe", "(skip if '-nosrl')"); + run("simplemap t:$dff t:$dffe", " (skip if '-nosrl')"); // shregmap with '-tech xilinx' infers variable length shift regs run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')"); } - run("techmap"); + std::string techmap_args = " -map +/techmap.v"; + if (help_mode) + techmap_args += " [-map +/xilinx/mux_map.v]"; + else if (widemux > 0) + techmap_args += stringf(" -D MIN_MUX_INPUTS=%d -map +/xilinx/mux_map.v", widemux); + if (help_mode) + techmap_args += " [-map +/xilinx/arith_map.v]"; + else if (!nocarry) { + techmap_args += " -map +/xilinx/arith_map.v"; + if (vpr) + techmap_args += " -D _EXPLICIT_CARRY"; + else if (abc9) + techmap_args += " -D _CLB_CARRY"; + } + run("techmap " + techmap_args); run("opt -fast"); } if (check_label("map_cells")) { - run("techmap -map +/techmap.v -map +/xilinx/cells_map.v"); + std::string techmap_args = "-map +/techmap.v -D _ABC -map +/xilinx/cells_map.v"; + if (widemux > 0) + techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); + run("techmap " + techmap_args); run("clean"); } if (check_label("map_luts")) { + run("opt_expr -mux_undef"); if (help_mode) - run("abc -luts 2:2,3,6:5,10,20 [-dff]"); - else - run("abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); + run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')"); + else if (abc9) { + if (family != "xc7") + log_warning("'synth_xilinx -abc9' currently supports '-family xc7' only.\n"); + if (nowidelut) + run("abc9 -lut +/xilinx/abc_xc7_nowide.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY)); + else + run("abc9 -lut +/xilinx/abc_xc7.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY)); + } + else { + if (nowidelut) + run("abc -luts 2:2,3,6:5" + string(retime ? " -dff" : "")); + else + run("abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); + } run("clean"); + // This shregmap call infers fixed length shift registers after abc // has performed any necessary retiming if (!nosrl || help_mode) diff --git a/techlibs/xilinx/xc6s_brams.txt b/techlibs/xilinx/xc6s_brams.txt new file mode 100644 index 000000000..17cd8e355 --- /dev/null +++ b/techlibs/xilinx/xc6s_brams.txt @@ -0,0 +1,84 @@ + +bram $__XILINX_RAMB8BWER_SDP + init 1 + abits 8 + dbits 36 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 4 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +bram $__XILINX_RAMB16BWER_TDP + init 1 + abits 9 @a9d36 + dbits 36 @a9d36 + abits 10 @a10d18 + dbits 18 @a10d18 + abits 11 @a11d9 + dbits 9 @a11d9 + abits 12 @a12d4 + dbits 4 @a12d4 + abits 13 @a13d2 + dbits 2 @a13d2 + abits 14 @a14d1 + dbits 1 @a14d1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 4 @a9d36 + enable 1 2 @a10d18 + enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +bram $__XILINX_RAMB8BWER_TDP + init 1 + abits 9 @a9d18 + dbits 18 @a9d18 + abits 10 @a10d9 + dbits 9 @a10d9 + abits 11 @a11d4 + dbits 4 @a11d4 + abits 12 @a12d2 + dbits 2 @a12d2 + abits 13 @a13d1 + dbits 1 @a13d1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 2 @a9d18 + enable 1 1 @a10d9 @a11d4 @a12d2 @a13d1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +match $__XILINX_RAMB8BWER_SDP + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp + or_next_if_better +endmatch + +match $__XILINX_RAMB16BWER_TDP + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp + or_next_if_better +endmatch + +match $__XILINX_RAMB8BWER_TDP + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp +endmatch + diff --git a/techlibs/xilinx/xc6s_brams_bb.v b/techlibs/xilinx/xc6s_brams_bb.v new file mode 100644 index 000000000..eb1a29579 --- /dev/null +++ b/techlibs/xilinx/xc6s_brams_bb.v @@ -0,0 +1,211 @@ +module RAMB8BWER ( + input CLKAWRCLK, + input CLKBRDCLK, + input ENAWREN, + input ENBRDEN, + input REGCEA, + input REGCEBREGCE, + input RSTA, + input RSTBRST, + + input [12:0] ADDRAWRADDR, + input [12:0] ADDRBRDADDR, + input [15:0] DIADI, + input [15:0] DIBDI, + input [1:0] DIPADIP, + input [1:0] DIPBDIP, + input [1:0] WEAWEL, + input [1:0] WEBWEU, + + output [15:0] DOADO, + output [15:0] DOBDO, + output [1:0] DOPADOP, + output [1:0] DOPBDOP +); + parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter INIT_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter RAM_MODE = "TDP"; + parameter integer DOA_REG = 0; + parameter integer DOB_REG = 0; + + parameter integer DATA_WIDTH_A = 0; + parameter integer DATA_WIDTH_B = 0; + + parameter WRITE_MODE_A = "WRITE_FIRST"; + parameter WRITE_MODE_B = "WRITE_FIRST"; + + parameter EN_RSTRAM_A = "TRUE"; + parameter EN_RSTRAM_B = "TRUE"; + + parameter INIT_A = 18'h000000000; + parameter INIT_B = 18'h000000000; + parameter SRVAL_A = 18'h000000000; + parameter SRVAL_B = 18'h000000000; + + parameter RST_PRIORITY_A = "CE"; + parameter RST_PRIORITY_B = "CE"; + + parameter RSTTYPE = "SYNC"; + + parameter SIM_COLLISION_CHECK = "ALL"; +endmodule + +module RAMB16BWER ( + input CLKA, + input CLKB, + input ENA, + input ENB, + input REGCEA, + input REGCEB, + input RSTA, + input RSTB, + + input [13:0] ADDRA, + input [13:0] ADDRB, + input [31:0] DIA, + input [31:0] DIB, + input [3:0] DIPA, + input [3:0] DIPB, + input [3:0] WEA, + input [3:0] WEB, + + output [31:0] DOA, + output [31:0] DOB, + output [3:0] DOPA, + output [3:0] DOPB +); + parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter INIT_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter integer DOA_REG = 0; + parameter integer DOB_REG = 0; + + parameter integer DATA_WIDTH_A = 0; + parameter integer DATA_WIDTH_B = 0; + + parameter WRITE_MODE_A = "WRITE_FIRST"; + parameter WRITE_MODE_B = "WRITE_FIRST"; + + parameter EN_RSTRAM_A = "TRUE"; + parameter EN_RSTRAM_B = "TRUE"; + + parameter INIT_A = 36'h000000000; + parameter INIT_B = 36'h000000000; + parameter SRVAL_A = 36'h000000000; + parameter SRVAL_B = 36'h000000000; + + parameter RST_PRIORITY_A = "CE"; + parameter RST_PRIORITY_B = "CE"; + + parameter RSTTYPE = "SYNC"; + + parameter SIM_COLLISION_CHECK = "ALL"; +endmodule + diff --git a/techlibs/xilinx/xc6s_brams_map.v b/techlibs/xilinx/xc6s_brams_map.v new file mode 100644 index 000000000..c9b33af42 --- /dev/null +++ b/techlibs/xilinx/xc6s_brams_map.v @@ -0,0 +1,255 @@ +module \$__XILINX_RAMB8BWER_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [9215:0] INIT = 9216'bx; + + input CLK2; + input CLK3; + + input [7:0] A1ADDR; + output [35:0] A1DATA; + input A1EN; + + input [7:0] B1ADDR; + input [35:0] B1DATA; + input [3:0] B1EN; + + wire [12:0] A1ADDR_13 = {A1ADDR, 5'b0}; + wire [12:0] B1ADDR_13 = {B1ADDR, 5'b0}; + + wire [3:0] DIP, DOP; + wire [31:0] DI, DO; + + assign A1DATA = { DOP[3], DO[31:24], DOP[2], DO[23:16], DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] }; + assign { DIP[3], DI[31:24], DIP[2], DI[23:16], DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA; + + RAMB8BWER #( + .RAM_MODE("SDP"), + .DATA_WIDTH_A(36), + .DATA_WIDTH_B(36), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + `include "brams_init_9.vh" + ) _TECHMAP_REPLACE_ ( + .DOBDO(DO[31:16]), + .DOADO(DO[15:0]), + .DOPBDOP(DOP[3:2]), + .DOPADOP(DOP[1:0]), + .DIBDI(DI[31:16]), + .DIADI(DI[15:0]), + .DIPBDIP(DIP[3:2]), + .DIPADIP(DIP[1:0]), + .WEBWEU(B1EN[3:2]), + .WEAWEL(B1EN[1:0]), + + .ADDRAWRADDR(B1ADDR_13), + .CLKAWRCLK(CLK3 ^ !CLKPOL3), + .ENAWREN(|1), + .REGCEA(|0), + .RSTA(|0), + + .ADDRBRDADDR(A1ADDR_13), + .CLKBRDCLK(CLK2 ^ !CLKPOL2), + .ENBRDEN(A1EN), + .REGCEBREGCE(|1), + .RSTB(|0) + ); +endmodule + +// ------------------------------------------------------------------------ + +module \$__XILINX_RAMB16BWER_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 9; + parameter CFG_DBITS = 36; + parameter CFG_ENABLE_B = 4; + + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [18431:0] INIT = 18432'bx; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + output [CFG_DBITS-1:0] A1DATA; + input A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + input [CFG_DBITS-1:0] B1DATA; + input [CFG_ENABLE_B-1:0] B1EN; + + wire [13:0] A1ADDR_14 = A1ADDR << (14 - CFG_ABITS); + wire [13:0] B1ADDR_14 = B1ADDR << (14 - CFG_ABITS); + wire [3:0] B1EN_4 = {4{B1EN}}; + + wire [3:0] DIP, DOP; + wire [31:0] DI, DO; + + wire [31:0] DOB; + wire [3:0] DOPB; + + assign A1DATA = { DOP[3], DO[31:24], DOP[2], DO[23:16], DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] }; + assign { DIP[3], DI[31:24], DIP[2], DI[23:16], DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA; + + generate if (CFG_DBITS > 8) begin + RAMB16BWER #( + .DATA_WIDTH_A(CFG_DBITS), + .DATA_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + `include "brams_init_18.vh" + ) _TECHMAP_REPLACE_ ( + .DIA(32'd0), + .DIPA(4'd0), + .DOA(DO[31:0]), + .DOPA(DOP[3:0]), + .ADDRA(A1ADDR_14), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .REGCEA(|1), + .RSTA(|0), + .WEA(4'b0), + + .DIB(DI), + .DIPB(DIP), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR_14), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .REGCEB(|0), + .RSTB(|0), + .WEB(B1EN_4) + ); + end else begin + RAMB16BWER #( + .DATA_WIDTH_A(CFG_DBITS), + .DATA_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + `include "brams_init_16.vh" + ) _TECHMAP_REPLACE_ ( + .DIA(32'd0), + .DIPA(4'd0), + .DOA(DO[31:0]), + .DOPA(DOP[3:0]), + .ADDRA(A1ADDR_14), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .REGCEA(|1), + .RSTA(|0), + .WEA(4'b0), + + .DIB(DI), + .DIPB(DIP), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR_14), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .REGCEB(|0), + .RSTB(|0), + .WEB(B1EN_4) + ); + end endgenerate +endmodule + +// ------------------------------------------------------------------------ + +module \$__XILINX_RAMB8BWER_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 9; + parameter CFG_DBITS = 18; + parameter CFG_ENABLE_B = 2; + + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [9215:0] INIT = 9216'bx; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + output [CFG_DBITS-1:0] A1DATA; + input A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + input [CFG_DBITS-1:0] B1DATA; + input [CFG_ENABLE_B-1:0] B1EN; + + wire [12:0] A1ADDR_13 = A1ADDR << (13 - CFG_ABITS); + wire [12:0] B1ADDR_13 = B1ADDR << (13 - CFG_ABITS); + wire [1:0] B1EN_2 = {2{B1EN}}; + + wire [1:0] DIP, DOP; + wire [15:0] DI, DO; + + wire [15:0] DOBDO; + wire [1:0] DOPBDOP; + + assign A1DATA = { DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] }; + assign { DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA; + + generate if (CFG_DBITS > 8) begin + RAMB8BWER #( + .RAM_MODE("TDP"), + .DATA_WIDTH_A(CFG_DBITS), + .DATA_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + `include "brams_init_9.vh" + ) _TECHMAP_REPLACE_ ( + .DIADI(16'b0), + .DIPADIP(2'b0), + .DOADO(DO), + .DOPADOP(DOP), + .ADDRAWRADDR(A1ADDR_13), + .CLKAWRCLK(CLK2 ^ !CLKPOL2), + .ENAWREN(A1EN), + .REGCEA(|1), + .RSTA(|0), + .WEAWEL(2'b0), + + .DIBDI(DI), + .DIPBDIP(DIP), + .DOBDO(DOBDO), + .DOPBDOP(DOPBDOP), + .ADDRBRDADDR(B1ADDR_13), + .CLKBRDCLK(CLK3 ^ !CLKPOL3), + .ENBRDEN(|1), + .REGCEBREGCE(|0), + .RSTB(|0), + .WEBWEU(B1EN_2) + ); + end else begin + RAMB8BWER #( + .RAM_MODE("TDP"), + .DATA_WIDTH_A(CFG_DBITS), + .DATA_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + `include "brams_init_8.vh" + ) _TECHMAP_REPLACE_ ( + .DIADI(16'b0), + .DIPADIP(2'b0), + .DOADO(DO), + .DOPADOP(DOP), + .ADDRAWRADDR(A1ADDR_13), + .CLKAWRCLK(CLK2 ^ !CLKPOL2), + .ENAWREN(A1EN), + .REGCEA(|1), + .RSTA(|0), + .WEAWEL(2'b0), + + .DIBDI(DI), + .DIPBDIP(DIP), + .DOBDO(DOBDO), + .DOPBDOP(DOPBDOP), + .ADDRBRDADDR(B1ADDR_13), + .CLKBRDCLK(CLK3 ^ !CLKPOL3), + .ENBRDEN(|1), + .REGCEBREGCE(|0), + .RSTB(|0), + .WEBWEU(B1EN_2) + ); + end endgenerate +endmodule diff --git a/techlibs/xilinx/brams.txt b/techlibs/xilinx/xc7_brams.txt index f1161114e..f1161114e 100644 --- a/techlibs/xilinx/brams.txt +++ b/techlibs/xilinx/xc7_brams.txt diff --git a/techlibs/xilinx/brams_bb.v b/techlibs/xilinx/xc7_brams_bb.v index a682ba4a7..a682ba4a7 100644 --- a/techlibs/xilinx/brams_bb.v +++ b/techlibs/xilinx/xc7_brams_bb.v diff --git a/techlibs/xilinx/brams_map.v b/techlibs/xilinx/xc7_brams_map.v index 7ea49158d..7ea49158d 100644 --- a/techlibs/xilinx/brams_map.v +++ b/techlibs/xilinx/xc7_brams_map.v diff --git a/tests/aiger/.gitignore b/tests/aiger/.gitignore index 073f46157..9a26bb8f4 100644 --- a/tests/aiger/.gitignore +++ b/tests/aiger/.gitignore @@ -1,2 +1 @@ -*.log -*.out +/*_ref.v diff --git a/tests/aiger/and.aig b/tests/aiger/and.aig deleted file mode 100644 index da0fa0719..000000000 --- a/tests/aiger/and.aig +++ /dev/null @@ -1,3 +0,0 @@ -aig 3 2 0 1 1 -6 -
\ No newline at end of file diff --git a/tests/aiger/and.aag b/tests/aiger/and_.aag index d1ef2c5a5..cadd505f0 100644 --- a/tests/aiger/and.aag +++ b/tests/aiger/and_.aag @@ -3,3 +3,6 @@ aag 3 2 0 1 1 4 6 6 2 4 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/and_.aig b/tests/aiger/and_.aig new file mode 100644 index 000000000..13c7a0c17 --- /dev/null +++ b/tests/aiger/and_.aig @@ -0,0 +1,5 @@ +aig 3 2 0 1 1 +6 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/buffer.aag b/tests/aiger/buffer.aag index 94a6fb1ed..211106ed6 100644 --- a/tests/aiger/buffer.aag +++ b/tests/aiger/buffer.aag @@ -1,3 +1,5 @@ aag 1 1 0 1 0 2 2 +i0 pi0 +o0 po0 diff --git a/tests/aiger/buffer.aig b/tests/aiger/buffer.aig index 0c715fdeb..01df6f1cf 100644 --- a/tests/aiger/buffer.aig +++ b/tests/aiger/buffer.aig @@ -1,2 +1,4 @@ aig 1 1 0 1 0 2 +i0 pi0 +o0 po0 diff --git a/tests/aiger/cnt1.aag b/tests/aiger/cnt1.aag index ce4f28fcb..75598862c 100644 --- a/tests/aiger/cnt1.aag +++ b/tests/aiger/cnt1.aag @@ -1,3 +1,4 @@ aag 1 0 1 0 0 1 2 3 2 +b0 po0 diff --git a/tests/aiger/cnt1.aig b/tests/aiger/cnt1.aig index 8d0ba13b1..6fcf62522 100644 --- a/tests/aiger/cnt1.aig +++ b/tests/aiger/cnt1.aig @@ -1,3 +1,4 @@ aig 1 0 1 0 0 1 3 2 +b0 po0 diff --git a/tests/aiger/cnt1e.aag b/tests/aiger/cnt1e.aag index 6db3f0ffd..35cd5a482 100644 --- a/tests/aiger/cnt1e.aag +++ b/tests/aiger/cnt1e.aag @@ -6,3 +6,4 @@ aag 5 1 1 0 3 1 8 4 2 10 9 7 b0 AIGER_NEVER +i0 po0 diff --git a/tests/aiger/cnt1e.aig b/tests/aiger/cnt1e.aig index d8d159f11..7284dd42a 100644 --- a/tests/aiger/cnt1e.aig +++ b/tests/aiger/cnt1e.aig @@ -1,4 +1,5 @@ aig 5 1 1 0 3 1 10 4 -b0 AIGER_NEVER +i0 po0 +b0 AIGER_NEVER diff --git a/tests/aiger/false.aag b/tests/aiger/false.aag index 421e64a91..bab4a06a6 100644 --- a/tests/aiger/false.aag +++ b/tests/aiger/false.aag @@ -1,2 +1,3 @@ aag 0 0 0 1 0 0 +o0 po0 diff --git a/tests/aiger/false.aig b/tests/aiger/false.aig index ad7d039fa..4dc442d7b 100644 --- a/tests/aiger/false.aig +++ b/tests/aiger/false.aig @@ -1,2 +1,3 @@ aig 0 0 0 1 0 0 +o0 po0 diff --git a/tests/aiger/inverter.aag b/tests/aiger/inverter.aag index ff7c28542..428bad9e4 100644 --- a/tests/aiger/inverter.aag +++ b/tests/aiger/inverter.aag @@ -1,3 +1,5 @@ aag 1 1 0 1 0 2 3 +i0 pi0 +o0 po0 diff --git a/tests/aiger/inverter.aig b/tests/aiger/inverter.aig index 525d82392..5bec90ae3 100644 --- a/tests/aiger/inverter.aig +++ b/tests/aiger/inverter.aig @@ -1,2 +1,4 @@ aig 1 1 0 1 0 3 +i0 pi0 +o0 po0 diff --git a/tests/aiger/notcnt1e.aag b/tests/aiger/notcnt1e.aag index 141c864f7..2ed645d84 100644 --- a/tests/aiger/notcnt1e.aag +++ b/tests/aiger/notcnt1e.aag @@ -6,3 +6,4 @@ aag 5 1 1 0 3 1 8 4 2 10 9 7 b0 AIGER_NEVER +i0 pi0 diff --git a/tests/aiger/notcnt1e.aig b/tests/aiger/notcnt1e.aig index 7c85a7290..fd7e94508 100644 --- a/tests/aiger/notcnt1e.aig +++ b/tests/aiger/notcnt1e.aig @@ -1,4 +1,5 @@ aig 5 1 1 0 3 1 10 5 -b0 AIGER_NEVER +i0 pi0 +b0 AIGER_NEVER diff --git a/tests/aiger/or.aig b/tests/aiger/or.aig deleted file mode 100644 index 75c9e4480..000000000 --- a/tests/aiger/or.aig +++ /dev/null @@ -1,3 +0,0 @@ -aig 3 2 0 1 1 -7 -
\ No newline at end of file diff --git a/tests/aiger/or.aag b/tests/aiger/or_.aag index f780e339f..0f619dba3 100644 --- a/tests/aiger/or.aag +++ b/tests/aiger/or_.aag @@ -3,3 +3,6 @@ aag 3 2 0 1 1 4 7 6 3 5 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/or_.aig b/tests/aiger/or_.aig new file mode 100644 index 000000000..051687512 --- /dev/null +++ b/tests/aiger/or_.aig @@ -0,0 +1,5 @@ +aig 3 2 0 1 1 +7 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/run-test.sh b/tests/aiger/run-test.sh index e0a34f023..deaf48a3d 100755 --- a/tests/aiger/run-test.sh +++ b/tests/aiger/run-test.sh @@ -1,24 +1,54 @@ #!/bin/bash +set -e + OPTIND=1 -seed="" # default to no seed specified -while getopts "S:" opt +abcprog="../../yosys-abc" # default to built-in version of abc +while getopts "A:" opt do case "$opt" in - S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space - seed="SEED=$arg" ;; + A) abcprog="$OPTARG" ;; esac done shift "$((OPTIND-1))" -# check for Icarus Verilog -if ! which iverilog > /dev/null ; then - echo "$0: Error: Icarus Verilog 'iverilog' not found." - exit 1 -fi +# NB: *.aag and *.aig must contain a symbol table naming the primary +# inputs and outputs, otherwise ABC and Yosys will name them +# arbitrarily (and inconsistently with each other). -echo "===== AAG ======" -${MAKE:-make} -f ../tools/autotest.mk $seed *.aag EXTRA_FLAGS="-f aiger" +for aag in *.aag; do + # Since ABC cannot read *.aag, read the *.aig instead + # (which would have been created by the reference aig2aig utility, + # available from http://fmv.jku.at/aiger/) + echo "Checking $aag." + $abcprog -q "read -c ${aag%.*}.aig; write ${aag%.*}_ref.v" + ../../yosys -qp " +read_verilog ${aag%.*}_ref.v +prep +design -stash gold +read_aiger -clk_name clock $aag +prep +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -seq 16 miter +" +done -echo "===== AIG ======" -exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.aig EXTRA_FLAGS="-f aiger" +for aig in *.aig; do + echo "Checking $aig." + $abcprog -q "read -c $aig; write ${aig%.*}_ref.v" + ../../yosys -qp " +read_verilog ${aig%.*}_ref.v +prep +design -stash gold +read_aiger -clk_name clock $aig +prep +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -seq 16 miter +" +done diff --git a/tests/aiger/toggle.aag b/tests/aiger/toggle.aag index 09651012d..b1a1582d7 100644 --- a/tests/aiger/toggle.aag +++ b/tests/aiger/toggle.aag @@ -2,3 +2,5 @@ aag 1 0 1 2 0 2 3 2 3 +o0 po0 +o1 po1 diff --git a/tests/aiger/toggle.aig b/tests/aiger/toggle.aig index b69e21aaf..68b41763f 100644 --- a/tests/aiger/toggle.aig +++ b/tests/aiger/toggle.aig @@ -2,3 +2,5 @@ aig 1 0 1 2 0 3 2 3 +o0 po0 +o1 po1 diff --git a/tests/aiger/true.aag b/tests/aiger/true.aag index 366893648..66a9eab46 100644 --- a/tests/aiger/true.aag +++ b/tests/aiger/true.aag @@ -1,2 +1,3 @@ aag 0 0 0 1 0 1 +o0 po0 diff --git a/tests/aiger/true.aig b/tests/aiger/true.aig index 10086f389..f9bad6000 100644 --- a/tests/aiger/true.aig +++ b/tests/aiger/true.aig @@ -1,2 +1,3 @@ aig 0 0 0 1 0 1 +o0 po0 diff --git a/tests/arch/run-test.sh b/tests/arch/run-test.sh new file mode 100755 index 000000000..5292d1615 --- /dev/null +++ b/tests/arch/run-test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +echo "Running syntax check on arch sim models" +for arch in ../../techlibs/*; do + find $arch -name cells_sim.v | while read path; do + echo -n "Test $path ->" + iverilog -t null -I$arch $path + echo " ok" + done +done + +for path in "../../techlibs/common/simcells.v" "../../techlibs/common/simlib.v"; do + echo -n "Test $path ->" + iverilog -t null $path + echo " ok" +done diff --git a/tests/lut/check_map_lut6.ys b/tests/lut/check_map_lut6.ys new file mode 100644 index 000000000..8a32e4d10 --- /dev/null +++ b/tests/lut/check_map_lut6.ys @@ -0,0 +1,7 @@ +chparam -set LUT_WIDTH 6 top +simplemap +equiv_opt -assert techmap -D LUT_WIDTH=6 -map +/cmp2lut.v +design -load postopt +equiv_opt -assert techmap -D LUT_WIDTH=6 -map +/gate2lut.v +design -load postopt +select -assert-count 0 t:* t:$lut %d diff --git a/tests/lut/map_cmp.v b/tests/lut/map_cmp.v index 5e413f894..0014eb9ac 100644 --- a/tests/lut/map_cmp.v +++ b/tests/lut/map_cmp.v @@ -1,29 +1,30 @@ module top(...); - input [3:0] a; + parameter LUT_WIDTH = 4; // Multiples of 2 only + input [LUT_WIDTH-1:0] a; - output o1_1 = 4'b1010 <= a; - output o1_2 = 4'b1010 < a; - output o1_3 = 4'b1010 >= a; - output o1_4 = 4'b1010 > a; - output o1_5 = 4'b1010 == a; - output o1_6 = 4'b1010 != a; + output o1_1 = {(LUT_WIDTH/2){2'b10}} <= a; + output o1_2 = {(LUT_WIDTH/2){2'b10}} < a; + output o1_3 = {(LUT_WIDTH/2){2'b10}} >= a; + output o1_4 = {(LUT_WIDTH/2){2'b10}} > a; + output o1_5 = {(LUT_WIDTH/2){2'b10}} == a; + output o1_6 = {(LUT_WIDTH/2){2'b10}} != a; - output o2_1 = a <= 4'b1010; - output o2_2 = a < 4'b1010; - output o2_3 = a >= 4'b1010; - output o2_4 = a > 4'b1010; - output o2_5 = a == 4'b1010; - output o2_6 = a != 4'b1010; + output o2_1 = a <= {(LUT_WIDTH/2){2'b10}}; + output o2_2 = a < {(LUT_WIDTH/2){2'b10}}; + output o2_3 = a >= {(LUT_WIDTH/2){2'b10}}; + output o2_4 = a > {(LUT_WIDTH/2){2'b10}}; + output o2_5 = a == {(LUT_WIDTH/2){2'b10}}; + output o2_6 = a != {(LUT_WIDTH/2){2'b10}}; - output o3_1 = 4'sb0101 <= $signed(a); - output o3_2 = 4'sb0101 < $signed(a); - output o3_3 = 4'sb0101 >= $signed(a); - output o3_4 = 4'sb0101 > $signed(a); - output o3_5 = 4'sb0101 == $signed(a); - output o3_6 = 4'sb0101 != $signed(a); + output o3_1 = {(LUT_WIDTH/2){2'sb01}} <= $signed(a); + output o3_2 = {(LUT_WIDTH/2){2'sb01}} < $signed(a); + output o3_3 = {(LUT_WIDTH/2){2'sb01}} >= $signed(a); + output o3_4 = {(LUT_WIDTH/2){2'sb01}} > $signed(a); + output o3_5 = {(LUT_WIDTH/2){2'sb01}} == $signed(a); + output o3_6 = {(LUT_WIDTH/2){2'sb01}} != $signed(a); - output o4_1 = $signed(a) <= 4'sb0000; - output o4_2 = $signed(a) < 4'sb0000; - output o4_3 = $signed(a) >= 4'sb0000; - output o4_4 = $signed(a) > 4'sb0000; + output o4_1 = $signed(a) <= {LUT_WIDTH{1'sb0}}; + output o4_2 = $signed(a) < {LUT_WIDTH{1'sb0}}; + output o4_3 = $signed(a) >= {LUT_WIDTH{1'sb0}}; + output o4_4 = $signed(a) > {LUT_WIDTH{1'sb0}}; endmodule diff --git a/tests/lut/run-test.sh b/tests/lut/run-test.sh index 207417fa6..f8964f146 100755 --- a/tests/lut/run-test.sh +++ b/tests/lut/run-test.sh @@ -4,3 +4,8 @@ for x in *.v; do echo "Running $x.." ../../yosys -q -s check_map.ys -l ${x%.v}.log $x done + +for x in map_cmp.v; do + echo "Running $x.." + ../../yosys -q -s check_map_lut6.ys -l ${x%.v}_lut6.log $x +done diff --git a/tests/memories/issue00335.v b/tests/memories/issue00335.v new file mode 100644 index 000000000..f3b6e5dfe --- /dev/null +++ b/tests/memories/issue00335.v @@ -0,0 +1,28 @@ +// expect-wr-ports 1 +// expect-rd-ports 1 +// expect-rd-clk \clk + +module ram2 (input clk, + input sel, + input we, + input [SIZE-1:0] adr, + input [63:0] dat_i, + output reg [63:0] dat_o); + parameter SIZE = 5; // Address size + + reg [63:0] mem [0:(1 << SIZE)-1]; + integer i; + + initial begin + for (i = 0; i < (1<<SIZE) - 1; i = i + 1) + mem[i] <= 0; + end + + always @(posedge clk) + if (sel) begin + if (~we) + dat_o <= mem[adr]; + else + mem[adr] <= dat_i; + end +endmodule diff --git a/tests/memories/issue00710.v b/tests/memories/issue00710.v new file mode 100644 index 000000000..7a5fad1c2 --- /dev/null +++ b/tests/memories/issue00710.v @@ -0,0 +1,17 @@ +// expect-wr-ports 1 +// expect-rd-ports 1 +// expect-rd-clk \clk + +module top(input clk, input we, re, reset, input [7:0] addr, wdata, output reg [7:0] rdata); + +reg [7:0] bram[0:255]; +(* keep *) reg dummy; + +always @(posedge clk) + if (reset) + dummy <= 1'b0; + else if (re) + rdata <= bram[addr]; + else if (we) + bram[addr] <= wdata; +endmodule diff --git a/tests/memories/read_two_mux.v b/tests/memories/read_two_mux.v new file mode 100644 index 000000000..4f2e7e1cd --- /dev/null +++ b/tests/memories/read_two_mux.v @@ -0,0 +1,16 @@ +// expect-wr-ports 1 +// expect-rd-ports 1 +// expect-no-rd-clk + +module top(input clk, input we, re, reset, input [7:0] addr, wdata, output reg [7:0] rdata); + +reg [7:0] bram[0:255]; +(* keep *) reg dummy; + +always @(posedge clk) begin + rdata <= re ? (reset ? 8'b0 : bram[addr]) : rdata; + if (we) + bram[addr] <= wdata; +end + +endmodule diff --git a/tests/memories/run-test.sh b/tests/memories/run-test.sh index 734a96682..8d1a8b413 100755 --- a/tests/memories/run-test.sh +++ b/tests/memories/run-test.sh @@ -4,17 +4,19 @@ set -e OPTIND=1 seed="" # default to no seed specified -while getopts "S:" opt +abcopt="" +while getopts "A:S:" opt do case "$opt" in + A) abcopt="-A $OPTARG" ;; S) seed="-S $OPTARG" ;; esac done shift "$((OPTIND-1))" -bash ../tools/autotest.sh $seed -G *.v +bash ../tools/autotest.sh $abcopt $seed -G *.v -for f in `egrep -l 'expect-(wr|rd)-ports' *.v`; do +for f in `egrep -l 'expect-(wr-ports|rd-ports|rd-clk)' *.v`; do echo -n "Testing expectations for $f .." ../../yosys -qp "proc; opt; memory -nomap;; dump -outfile ${f%.v}.dmp t:\$mem" $f if grep -q expect-wr-ports $f; then @@ -25,6 +27,14 @@ for f in `egrep -l 'expect-(wr|rd)-ports' *.v`; do grep -q "parameter \\\\RD_PORTS $(gawk '/expect-rd-ports/ { print $3; }' $f)\$" ${f%.v}.dmp || { echo " ERROR: Unexpected number of read ports."; false; } fi + if grep -q expect-rd-clk $f; then + grep -q "connect \\\\RD_CLK \\$(gawk '/expect-rd-clk/ { print $3; }' $f)\$" ${f%.v}.dmp || + { echo " ERROR: Unexpected read clock."; false; } + fi + if grep -q expect-no-rd-clk $f; then + grep -q "connect \\\\RD_CLK 1'x\$" ${f%.v}.dmp || + { echo " ERROR: Expected no read clock."; false; } + fi echo " ok." done diff --git a/tests/opt/opt_ff_sat.v b/tests/opt/opt_ff_sat.v new file mode 100644 index 000000000..5a0a6fe37 --- /dev/null +++ b/tests/opt/opt_ff_sat.v @@ -0,0 +1,12 @@ +module top ( + input clk, + output reg [7:0] cnt +); + initial cnt = 0; + always @(posedge clk) begin + if (cnt < 20) + cnt <= cnt + 1; + else + cnt <= 0; + end +endmodule diff --git a/tests/opt/opt_ff_sat.ys b/tests/opt/opt_ff_sat.ys new file mode 100644 index 000000000..4e7cc6ca4 --- /dev/null +++ b/tests/opt/opt_ff_sat.ys @@ -0,0 +1,5 @@ +read_verilog opt_ff_sat.v +prep -flatten +opt_rmdff -sat +synth +select -assert-count 5 t:$_DFF_P_ diff --git a/tests/simple/arrays02.sv b/tests/simple/arrays02.sv new file mode 100644 index 000000000..76c2a7388 --- /dev/null +++ b/tests/simple/arrays02.sv @@ -0,0 +1,16 @@ +module uut_arrays02(clock, we, addr, wr_data, rd_data); + +input clock, we; +input [3:0] addr, wr_data; +output [3:0] rd_data; +reg [3:0] rd_data; + +reg [3:0] memory [16]; + +always @(posedge clock) begin + if (we) + memory[addr] <= wr_data; + rd_data <= memory[addr]; +end + +endmodule diff --git a/tests/simple/attrib01_module.v b/tests/simple/attrib01_module.v new file mode 100644 index 000000000..adef34f5b --- /dev/null +++ b/tests/simple/attrib01_module.v @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib02_port_decl.v b/tests/simple/attrib02_port_decl.v new file mode 100644 index 000000000..3505e7265 --- /dev/null +++ b/tests/simple/attrib02_port_decl.v @@ -0,0 +1,25 @@ +module bar(clk, rst, inp, out); + (* this_is_clock = 1 *) + input wire clk; + (* this_is_reset = 1 *) + input wire rst; + input wire inp; + (* an_output_register = 1*) + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + (* this_is_the_master_clock *) + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib03_parameter.v b/tests/simple/attrib03_parameter.v new file mode 100644 index 000000000..562d225cd --- /dev/null +++ b/tests/simple/attrib03_parameter.v @@ -0,0 +1,28 @@ +module bar(clk, rst, inp, out); + + (* bus_width *) + parameter WIDTH = 2; + + (* an_attribute_on_localparam = 55 *) + localparam INCREMENT = 5; + + input wire clk; + input wire rst; + input wire [WIDTH-1:0] inp; + output reg [WIDTH-1:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= inp + INCREMENT; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [7:0] inp; + output wire [7:0] out; + + bar # (.WIDTH(8)) bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib04_net_var.v b/tests/simple/attrib04_net_var.v new file mode 100644 index 000000000..8b5523406 --- /dev/null +++ b/tests/simple/attrib04_net_var.v @@ -0,0 +1,32 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + (* this_is_a_prescaler *) + reg [7:0] counter; + + (* temp_wire *) + wire out_val; + + always @(posedge clk) + counter <= counter + 1; + + assign out_val = inp ^ counter[4]; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= out_val; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib05_port_conn.v.DISABLED b/tests/simple/attrib05_port_conn.v.DISABLED new file mode 100644 index 000000000..e20e66319 --- /dev/null +++ b/tests/simple/attrib05_port_conn.v.DISABLED @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance ( (* clock_connected *) clk, rst, (* this_is_the_input *) inp, out); +endmodule + diff --git a/tests/simple/attrib06_operator_suffix.v b/tests/simple/attrib06_operator_suffix.v new file mode 100644 index 000000000..e21173c58 --- /dev/null +++ b/tests/simple/attrib06_operator_suffix.v @@ -0,0 +1,23 @@ +module bar(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output reg [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= inp_a + (* ripple_adder *) inp_b; + +endmodule + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + bar bar_instance (clk, rst, inp_a, inp_b, out); +endmodule + diff --git a/tests/simple/attrib07_func_call.v.DISABLED b/tests/simple/attrib07_func_call.v.DISABLED new file mode 100644 index 000000000..f55ef2316 --- /dev/null +++ b/tests/simple/attrib07_func_call.v.DISABLED @@ -0,0 +1,21 @@ +function [7:0] do_add; + input [7:0] inp_a; + input [7:0] inp_b; + + do_add = inp_a + inp_b; + +endfunction + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= do_add (* combinational_adder *) (inp_a, inp_b); + +endmodule + diff --git a/tests/simple/attrib08_mod_inst.v b/tests/simple/attrib08_mod_inst.v new file mode 100644 index 000000000..c5a32234e --- /dev/null +++ b/tests/simple/attrib08_mod_inst.v @@ -0,0 +1,22 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + (* my_module_instance = 99 *) + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib09_case.v b/tests/simple/attrib09_case.v new file mode 100644 index 000000000..8551bf9d0 --- /dev/null +++ b/tests/simple/attrib09_case.v @@ -0,0 +1,26 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [1:0] inp; + output reg [1:0] out; + + always @(inp) + (* full_case, parallel_case *) + case(inp) + 2'd0: out <= 2'd3; + 2'd1: out <= 2'd2; + 2'd2: out <= 2'd1; + 2'd3: out <= 2'd0; + endcase + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [1:0] inp; + output wire [1:0] out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/defvalue.sv b/tests/simple/defvalue.sv new file mode 100644 index 000000000..b0a087ecb --- /dev/null +++ b/tests/simple/defvalue.sv @@ -0,0 +1,22 @@ +module top(input clock, input [3:0] delta, output [3:0] cnt1, cnt2); + cnt #(1) foo (.clock, .cnt(cnt1), .delta); + cnt #(2) bar (.clock, .cnt(cnt2)); +endmodule + +module cnt #( + parameter integer initval = 0 +) ( + input clock, + output logic [3:0] cnt = initval, +`ifdef __ICARUS__ + input [3:0] delta +`else + input [3:0] delta = 10 +`endif +); +`ifdef __ICARUS__ + assign (weak0, weak1) delta = 10; +`endif + always @(posedge clock) + cnt <= cnt + delta; +endmodule diff --git a/tests/simple/generate.v b/tests/simple/generate.v index 3c55682cb..0e353ad9b 100644 --- a/tests/simple/generate.v +++ b/tests/simple/generate.v @@ -148,3 +148,14 @@ generate endgenerate assign out = steps[WIDTH].outer[0].val; endmodule + +// ------------------------------------------ + +module gen_test6(output [3:0] o); +generate + genvar i; + for (i = 3; i >= 0; i = i-1) begin + assign o[i] = 1'b0; + end +endgenerate +endmodule diff --git a/tests/simple/implicit_ports.v b/tests/simple/implicit_ports.v new file mode 100644 index 000000000..8b0a6f386 --- /dev/null +++ b/tests/simple/implicit_ports.v @@ -0,0 +1,16 @@ +// Test implicit port connections +module alu (input [2:0] a, input [2:0] b, input cin, output cout, output [2:0] result); + assign cout = cin; + assign result = a + b; +endmodule + +module named_ports(input [2:0] a, b, output [2:0] alu_result, output cout); + wire cin = 1; + alu alu ( + .a(a), + .b, // Implicit connection is equivalent to .b(b) + .cin(), // Explicitely unconnected + .cout(cout), + .result(alu_result) + ); +endmodule diff --git a/tests/simple/run-test.sh b/tests/simple/run-test.sh index aaa1cf940..967ac49f2 100755 --- a/tests/simple/run-test.sh +++ b/tests/simple/run-test.sh @@ -17,4 +17,5 @@ if ! which iverilog > /dev/null ; then exit 1 fi -exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v +shopt -s nullglob +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.{sv,v} diff --git a/tests/simple_abc9/.gitignore b/tests/simple_abc9/.gitignore new file mode 100644 index 000000000..598951333 --- /dev/null +++ b/tests/simple_abc9/.gitignore @@ -0,0 +1,3 @@ +*.v +*.log +*.out diff --git a/tests/simple_abc9/abc.box b/tests/simple_abc9/abc.box new file mode 100644 index 000000000..a8801d807 --- /dev/null +++ b/tests/simple_abc9/abc.box @@ -0,0 +1,2 @@ +MUXF8 1 0 3 1 +1 1 1 diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v new file mode 100644 index 000000000..64b625efe --- /dev/null +++ b/tests/simple_abc9/abc9.v @@ -0,0 +1,269 @@ +module abc9_test001(input a, output o); +assign o = a; +endmodule + +module abc9_test002(input [1:0] a, output o); +assign o = a[1]; +endmodule + +module abc9_test003(input [1:0] a, output [1:0] o); +assign o = a; +endmodule + +module abc9_test004(input [1:0] a, output o); +assign o = ^a; +endmodule + +module abc9_test005(input [1:0] a, output o, output p); +assign o = ^a; +assign p = ~o; +endmodule + +module abc9_test006(input [1:0] a, output [2:0] o); +assign o[0] = ^a; +assign o[1] = ~o[0]; +assign o[2] = o[1]; +endmodule + +module abc9_test007(input a, output o); +wire b, c; +assign c = ~a; +assign b = c; +abc9_test007_sub s(b, o); +endmodule + +module abc9_test007_sub(input a, output b); +assign b = a; +endmodule + +module abc9_test008(input a, output o); +wire b, c; +assign b = ~a; +assign c = b; +abc9_test008_sub s(b, o); +endmodule + +module abc9_test008_sub(input a, output b); +assign b = ~a; +endmodule + +module abc9_test009(inout io, input oe); +reg latch; +always @(io or oe) + if (!oe) + latch <= io; +assign io = oe ? ~latch : 1'bz; +endmodule + +module abc9_test010(inout [7:0] io, input oe); +reg [7:0] latch; +always @(io or oe) + if (!oe) + latch <= io; +assign io = oe ? ~latch : 8'bz; +endmodule + +module abc9_test011(inout io, input oe); +reg latch; +always @(io or oe) + if (!oe) + latch <= io; +//assign io = oe ? ~latch : 8'bz; +endmodule + +module abc9_test012(inout io, input oe); +reg latch; +//always @(io or oe) +// if (!oe) +// latch <= io; +assign io = oe ? ~latch : 8'bz; +endmodule + +module abc9_test013(inout [3:0] io, input oe); +reg [3:0] latch; +always @(io or oe) + if (!oe) + latch[3:0] <= io[3:0]; + else + latch[7:4] <= io; +assign io[3:0] = oe ? ~latch[3:0] : 4'bz; +assign io[7:4] = !oe ? {latch[4], latch[7:3]} : 4'bz; +endmodule + +module abc9_test014(inout [7:0] io, input oe); +abc9_test012_sub sub(io, oe); +endmodule + +module abc9_test012_sub(inout [7:0] io, input oe); +reg [7:0] latch; +always @(io or oe) + if (!oe) + latch[3:0] <= io; + else + latch[7:4] <= io; +assign io[3:0] = oe ? ~latch[3:0] : 4'bz; +assign io[7:4] = !oe ? {latch[4], latch[7:3]} : 4'bz; +endmodule + +module abc9_test015(input a, output b, input c); +assign b = ~a; +(* keep *) wire d; +assign d = ~c; +endmodule + +module abc9_test016(input a, output b); +assign b = ~a; +(* keep *) reg c; +always @* c <= ~a; +endmodule + +module abc9_test017(input a, output b); +assign b = ~a; +(* keep *) reg c; +always @* c = b; +endmodule + +module abc9_test018(input a, output b, output c); +assign b = ~a; +(* keep *) wire [1:0] d; +assign c = &d; +endmodule + +module abc9_test019(input a, output b); +assign b = ~a; +(* keep *) reg [1:0] c; +reg d; +always @* d <= &c; +endmodule + +module abc9_test020(input a, output b); +assign b = ~a; +(* keep *) reg [1:0] c; +(* keep *) reg d; +always @* d <= &c; +endmodule + +// Citation: https://github.com/alexforencich/verilog-ethernet +module abc9_test021(clk, rst, s_eth_hdr_valid, s_eth_hdr_ready, s_eth_dest_mac, s_eth_src_mac, s_eth_type, s_eth_payload_axis_tdata, s_eth_payload_axis_tkeep, s_eth_payload_axis_tvalid, s_eth_payload_axis_tready, s_eth_payload_axis_tlast, s_eth_payload_axis_tid, s_eth_payload_axis_tdest, s_eth_payload_axis_tuser, m_eth_hdr_valid, m_eth_hdr_ready, m_eth_dest_mac, m_eth_src_mac, m_eth_type, m_eth_payload_axis_tdata, m_eth_payload_axis_tkeep, m_eth_payload_axis_tvalid, m_eth_payload_axis_tready, m_eth_payload_axis_tlast, m_eth_payload_axis_tid, m_eth_payload_axis_tdest, m_eth_payload_axis_tuser); + input clk; + output [47:0] m_eth_dest_mac; + input m_eth_hdr_ready; + output m_eth_hdr_valid; + output [7:0] m_eth_payload_axis_tdata; + output [7:0] m_eth_payload_axis_tdest; + output [7:0] m_eth_payload_axis_tid; + output m_eth_payload_axis_tkeep; + output m_eth_payload_axis_tlast; + input m_eth_payload_axis_tready; + output m_eth_payload_axis_tuser; + output m_eth_payload_axis_tvalid; + output [47:0] m_eth_src_mac; + output [15:0] m_eth_type; + input rst; + input [191:0] s_eth_dest_mac; + output [3:0] s_eth_hdr_ready; + input [3:0] s_eth_hdr_valid; + input [31:0] s_eth_payload_axis_tdata; + input [31:0] s_eth_payload_axis_tdest; + input [31:0] s_eth_payload_axis_tid; + input [3:0] s_eth_payload_axis_tkeep; + input [3:0] s_eth_payload_axis_tlast; + output [3:0] s_eth_payload_axis_tready; + input [3:0] s_eth_payload_axis_tuser; + input [3:0] s_eth_payload_axis_tvalid; + input [191:0] s_eth_src_mac; + input [63:0] s_eth_type; + (* keep *) + wire [0:0] grant, request; + wire a; + not u0 ( + a, + grant[0] + ); + and u1 ( + request[0], + s_eth_hdr_valid[0], + a + ); + (* keep *) + MUXF8 u2 ( + .I0(1'bx), + .I1(1'bx), + .O(o), + .S(1'bx) + ); + arbiter arb_inst ( + .acknowledge(acknowledge), + .clk(clk), + .grant(grant), + .grant_encoded(grant_encoded), + .grant_valid(grant_valid), + .request(request), + .rst(rst) + ); +endmodule + +module arbiter (clk, rst, request, acknowledge, grant, grant_valid, grant_encoded); + input [3:0] acknowledge; + input clk; + output [3:0] grant; + output [1:0] grant_encoded; + output grant_valid; + input [3:0] request; + input rst; +endmodule + +(* abc_box_id=1 *) +module MUXF8(input I0, I1, S, output O); +endmodule + +// Citation: https://github.com/alexforencich/verilog-ethernet +// TODO: yosys -p "synth_xilinx -abc9 -top abc9_test022" abc9.v -q +// returns before b4321a31 +// Warning: Wire abc9_test022.\m_eth_payload_axis_tkeep [7] is used but has no +// driver. +// Warning: Wire abc9_test022.\m_eth_payload_axis_tkeep [3] is used but has no +// driver. +module abc9_test022 +( + input wire clk, + input wire i, + output wire [7:0] m_eth_payload_axis_tkeep +); + reg [7:0] m_eth_payload_axis_tkeep_reg = 8'd0; + assign m_eth_payload_axis_tkeep = m_eth_payload_axis_tkeep_reg; + always @(posedge clk) + m_eth_payload_axis_tkeep_reg <= i ? 8'hff : 8'h0f; +endmodule + +// Citation: https://github.com/riscv/riscv-bitmanip +// TODO: yosys -p "synth_xilinx -abc9 -top abc9_test023" abc9.v -q +// returns before 14233843 +// Warning: Wire abc9_test023.\dout [1] is used but has no driver. +module abc9_test023 #( + parameter integer N = 2, + parameter integer M = 2 +) ( + input [7:0] din, + output [M-1:0] dout +); + wire [2*M-1:0] mask = {M{1'b1}}; + assign dout = (mask << din[N-1:0]) >> M; +endmodule + +module abc9_test024(input [3:0] i, output [3:0] o); +abc9_test024_sub a(i[1:0], o[1:0]); +endmodule + +module abc9_test024_sub(input [1:0] i, output [1:0] o); +assign o = i; +endmodule + +module abc9_test025(input [3:0] i, output [3:0] o); +abc9_test024_sub a(i[2:1], o[2:1]); +endmodule + +module abc9_test026(output [3:0] o, p); +assign o = { 1'b1, 1'bx }; +assign p = { 1'b1, 1'bx, 1'b0 }; +endmodule diff --git a/tests/simple_abc9/run-test.sh b/tests/simple_abc9/run-test.sh new file mode 100755 index 000000000..4935d41ad --- /dev/null +++ b/tests/simple_abc9/run-test.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +OPTIND=1 +seed="" # default to no seed specified +while getopts "S:" opt +do + case "$opt" in + S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space + seed="SEED=$arg" ;; + esac +done +shift "$((OPTIND-1))" + +# check for Icarus Verilog +if ! which iverilog > /dev/null ; then + echo "$0: Error: Icarus Verilog 'iverilog' not found." + exit 1 +fi + +cp ../simple/*.v . +DOLLAR='?' +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-p 'hierarchy; synth -run coarse; opt -full; techmap; abc9 -lut 4 -box ../abc.box; stat; check -assert; select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'" diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index 920474a84..4d3478628 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -23,12 +23,13 @@ warn_iverilog_git=false # The tests are skipped if firrtl2verilog is the empty string (the default). firrtl2verilog="" xfirrtl="../xfirrtl" +abcprog="$toolsdir/../../yosys-abc" if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then ( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 fi -while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do +while getopts xmGl:wkjvref:s:p:n:S:I:A:-: opt; do case "$opt" in x) use_xsim=true ;; @@ -65,6 +66,8 @@ while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do include_opts="$include_opts -I $OPTARG" xinclude_opts="$xinclude_opts -i $OPTARG" minclude_opts="$minclude_opts +incdir+$OPTARG" ;; + A) + abcprog="$OPTARG" ;; -) case "${OPTARG}" in xfirrtl) @@ -89,6 +92,12 @@ done compile_and_run() { exe="$1"; output="$2"; shift 2 + if [ "${2##*.}" == "sv" ]; then + language_gen="-g2012" + else + language_gen="-g2005" + fi + if $use_modelsim; then altver=$( ls -v /opt/altera/ | grep '^[0-9]' | tail -n1; ) /opt/altera/$altver/modelsim_ase/bin/vlib work @@ -99,7 +108,7 @@ compile_and_run() { /opt/Xilinx/Vivado/$xilver/bin/xvlog $xinclude_opts -d outfile=\"$output\" "$@" /opt/Xilinx/Vivado/$xilver/bin/xelab -R work.testbench else - iverilog $include_opts -Doutfile=\"$output\" -s testbench -o "$exe" "$@" + iverilog $language_gen $include_opts -Doutfile=\"$output\" -s testbench -o "$exe" "$@" vvp -n "$exe" fi } @@ -110,7 +119,7 @@ for fn do bn=${fn%.*} ext=${fn##*.} - if [[ "$ext" != "v" ]] && [[ "$ext" != "aag" ]] && [[ "$ext" != "aig" ]]; then + if [[ "$ext" != "v" ]] && [[ "$ext" != "sv" ]] && [[ "$ext" != "aag" ]] && [[ "$ext" != "aig" ]]; then echo "Invalid argument: $fn" >&2 exit 1 fi @@ -123,6 +132,10 @@ do echo -n "Test: $bn " fi + if [ "$ext" == sv ]; then + frontend="$frontend -sv" + fi + rm -f ${bn}.{err,log,skip} mkdir -p ${bn}.out rm -rf ${bn}.out/* @@ -131,22 +144,25 @@ do cd ${bn}.out fn=$(basename $fn) bn=$(basename $bn) + refext=v rm -f ${bn}_ref.fir if [[ "$ext" == "v" ]]; then egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext} + elif [[ "$ext" == "aig" ]] || [[ "$ext" == "aag" ]]; then + $abcprog -c "read_aiger ../${fn}; write ${bn}_ref.${refext}" else - "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn} - frontend="verilog -noblackbox" + refext=$ext + cp ../${fn} ${bn}_ref.${refext} fi if [ ! -f ../${bn}_tb.v ]; then - "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "test_autotb $autotb_opts" -o ${bn}_tb.v ${bn}_ref.v + "$toolsdir"/../../yosys -f "$frontend $include_opts -D_AUTOTB" -b "test_autotb $autotb_opts" -o ${bn}_tb.v ${bn}_ref.${refext} else cp ../${bn}_tb.v ${bn}_tb.v fi if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi - compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \ + compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.${refext} $libs \ "$toolsdir"/../../techlibs/common/simlib.v \ "$toolsdir"/../../techlibs/common/simcells.v if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi @@ -163,25 +179,25 @@ do test_count=$(( test_count + 1 )) } - if [ "$frontend" = "verific" -o "$frontend" = "verific_gates" ] && grep -q VERIFIC-SKIP ${bn}_ref.v; then + if [ "$frontend" = "verific" -o "$frontend" = "verific_gates" ] && grep -q VERIFIC-SKIP ${bn}_ref.${refext}; then touch ../${bn}.skip return fi if [ -n "$scriptfiles" ]; then - test_passes -f "$frontend $include_opts" ${bn}_ref.v $scriptfiles + test_passes -f "$frontend $include_opts" ${bn}_ref.${refext} $scriptfiles elif [ -n "$scriptopt" ]; then - test_passes -f "$frontend $include_opts" -p "$scriptopt" ${bn}_ref.v + test_passes -f "$frontend $include_opts" -p "$scriptopt" ${bn}_ref.${refext} elif [ "$frontend" = "verific" ]; then - test_passes -p "verific -vlog2k ${bn}_ref.v; verific -import -all; opt; memory;;" + test_passes -p "verific -vlog2k ${bn}_ref.${refext}; verific -import -all; opt; memory;;" elif [ "$frontend" = "verific_gates" ]; then - test_passes -p "verific -vlog2k ${bn}_ref.v; verific -import -gates -all; opt; memory;;" + test_passes -p "verific -vlog2k ${bn}_ref.${refext}; verific -import -gates -all; opt; memory;;" else - test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.v - test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.v + test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.${refext} + test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.${refext} if [ -n "$firrtl2verilog" ]; then if test -z "$xfirrtl" || ! grep "$fn" "$xfirrtl" ; then - "$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt; memory; opt; fsm; opt -full -fine; pmuxtree" ${bn}_ref.v + "$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt; memory; opt; fsm; opt -full -fine; pmuxtree" ${bn}_ref.${refext} $firrtl2verilog -i ${bn}_ref.fir -o ${bn}_ref.fir.v test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.fir.v fi diff --git a/tests/various/.gitignore b/tests/various/.gitignore index 397b4a762..7b3e8c68e 100644 --- a/tests/various/.gitignore +++ b/tests/various/.gitignore @@ -1 +1,2 @@ -*.log +/*.log +/*.out diff --git a/tests/various/abc9.v b/tests/various/abc9.v new file mode 100644 index 000000000..a08b613a8 --- /dev/null +++ b/tests/various/abc9.v @@ -0,0 +1,9 @@ +module abc9_test027(output reg o); +initial o = 1'b0; +always @* + o <= ~o; +endmodule + +module abc9_test028(input i, output o); +unknown u(~i, o); +endmodule diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys new file mode 100644 index 000000000..5c9a4075d --- /dev/null +++ b/tests/various/abc9.ys @@ -0,0 +1,24 @@ +read_verilog abc9.v +design -save read +hierarchy -top abc9_test027 +proc +design -save gold + +abc9 -lut 4 +check +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top abc9_test028 +proc + +abc9 -lut 4 +select -assert-count 1 t:$lut r:LUT=2'b01 r:WIDTH=1 %i %i +select -assert-count 1 t:unknown +select -assert-none t:$lut t:unknown %% t: %D diff --git a/tests/various/async.sh b/tests/various/async.sh new file mode 100644 index 000000000..7c41d6d94 --- /dev/null +++ b/tests/various/async.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -ex +../../yosys -q -o async_syn.v -p 'synth; rename uut syn' async.v +../../yosys -q -o async_prp.v -p 'prep; rename uut prp' async.v +../../yosys -q -o async_a2s.v -p 'prep; async2sync; rename uut a2s' async.v +../../yosys -q -o async_ffl.v -p 'prep; clk2fflogic; rename uut ffl' async.v +iverilog -o async_sim -DTESTBENCH async.v async_???.v +vvp -N async_sim > async.out +tail async.out +grep PASS async.out +rm -f async_???.v async_sim async.out async.vcd diff --git a/tests/various/async.v b/tests/various/async.v new file mode 100644 index 000000000..c27e30c4b --- /dev/null +++ b/tests/various/async.v @@ -0,0 +1,108 @@ +`define MAXQ 2 +module uut ( + input clk, + input d, r, e, + output [`MAXQ:0] q +); + reg q0; + always @(posedge clk) begin + if (r) + q0 <= 0; + else if (e) + q0 <= d; + end + + reg q1; + always @(posedge clk, posedge r) begin + if (r) + q1 <= 0; + else if (e) + q1 <= d; + end + + reg q2; + always @(posedge clk, negedge r) begin + if (!r) + q2 <= 0; + else if (!e) + q2 <= d; + end + + assign q = {q2, q1, q0}; +endmodule + +`ifdef TESTBENCH +module \$ff #( + parameter integer WIDTH = 1 +) ( + input [WIDTH-1:0] D, + output reg [WIDTH-1:0] Q +); + wire sysclk = testbench.sysclk; + always @(posedge sysclk) + Q <= D; +endmodule + +module testbench; + reg sysclk; + always #5 sysclk = (sysclk === 1'b0); + + reg clk; + always @(posedge sysclk) clk = (clk === 1'b0); + + reg d, r, e; + + wire [`MAXQ:0] q_uut; + uut uut (.clk(clk), .d(d), .r(r), .e(e), .q(q_uut)); + + wire [`MAXQ:0] q_syn; + syn syn (.clk(clk), .d(d), .r(r), .e(e), .q(q_syn)); + + wire [`MAXQ:0] q_prp; + prp prp (.clk(clk), .d(d), .r(r), .e(e), .q(q_prp)); + + wire [`MAXQ:0] q_a2s; + a2s a2s (.clk(clk), .d(d), .r(r), .e(e), .q(q_a2s)); + + wire [`MAXQ:0] q_ffl; + ffl ffl (.clk(clk), .d(d), .r(r), .e(e), .q(q_ffl)); + + task printq; + reg [5*8-1:0] msg; + begin + msg = "OK"; + if (q_uut !== q_syn) msg = "SYN"; + if (q_uut !== q_prp) msg = "PRP"; + if (q_uut !== q_a2s) msg = "A2S"; + if (q_uut !== q_ffl) msg = "FFL"; + $display("%6t %b %b %b %b %b %s", $time, q_uut, q_syn, q_prp, q_a2s, q_ffl, msg); + if (msg != "OK") $finish; + end + endtask + + initial if(0) begin + $dumpfile("async.vcd"); + $dumpvars(0, testbench); + end + + initial begin + @(posedge clk); + d <= 0; + r <= 0; + e <= 0; + @(posedge clk); + e <= 1; + @(posedge clk); + e <= 0; + repeat (10000) begin + @(posedge clk); + printq; + d <= $random; + r <= $random; + e <= $random; + end + $display("PASS"); + $finish; + end +endmodule +`endif diff --git a/tests/various/attrib05_port_conn.v b/tests/various/attrib05_port_conn.v new file mode 100644 index 000000000..e20e66319 --- /dev/null +++ b/tests/various/attrib05_port_conn.v @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance ( (* clock_connected *) clk, rst, (* this_is_the_input *) inp, out); +endmodule + diff --git a/tests/various/attrib05_port_conn.ys b/tests/various/attrib05_port_conn.ys new file mode 100644 index 000000000..27a016733 --- /dev/null +++ b/tests/various/attrib05_port_conn.ys @@ -0,0 +1,2 @@ +# Read and parse Verilog file +read_verilog attrib05_port_conn.v diff --git a/tests/various/attrib07_func_call.v b/tests/various/attrib07_func_call.v new file mode 100644 index 000000000..f55ef2316 --- /dev/null +++ b/tests/various/attrib07_func_call.v @@ -0,0 +1,21 @@ +function [7:0] do_add; + input [7:0] inp_a; + input [7:0] inp_b; + + do_add = inp_a + inp_b; + +endfunction + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= do_add (* combinational_adder *) (inp_a, inp_b); + +endmodule + diff --git a/tests/various/attrib07_func_call.ys b/tests/various/attrib07_func_call.ys new file mode 100644 index 000000000..774827651 --- /dev/null +++ b/tests/various/attrib07_func_call.ys @@ -0,0 +1,2 @@ +# Read and parse Verilog file +read_verilog attrib07_func_call.v diff --git a/tests/various/elab_sys_tasks.sv b/tests/various/elab_sys_tasks.sv new file mode 100644 index 000000000..774d85b32 --- /dev/null +++ b/tests/various/elab_sys_tasks.sv @@ -0,0 +1,30 @@ +module test; +localparam X=1; +genvar i; +generate +if (X == 1) + $info("X is 1"); +if (X == 1) + $warning("X is 1"); +else + $error("X is not 1"); +case (X) + 1: $info("X is 1 in a case statement"); +endcase +//case (X-1) +// 1: $warn("X is 2"); +// default: $warn("X might be anything in a case statement"); +//endcase +for (i = 0; i < 3; i = i + 1) +begin + case(i) + 0: $info; + 1: $warning; + default: $info("default case statemnent"); + endcase +end + +$info("This is a standalone $info(). Next $info has no parameters"); +$info; +endgenerate +endmodule diff --git a/tests/various/elab_sys_tasks.ys b/tests/various/elab_sys_tasks.ys new file mode 100644 index 000000000..45bee3a60 --- /dev/null +++ b/tests/various/elab_sys_tasks.ys @@ -0,0 +1 @@ +read_verilog -sv elab_sys_tasks.sv diff --git a/tests/various/muxcover.ys b/tests/various/muxcover.ys index 7ac460f13..67e9625e6 100644 --- a/tests/various/muxcover.ys +++ b/tests/various/muxcover.ys @@ -13,7 +13,7 @@ read_verilog -formal <<EOT EOT -## Examle usage for "pmuxtree" and "muxcover" +## Example usage for "pmuxtree" and "muxcover" proc pmuxtree @@ -49,3 +49,462 @@ hierarchy -top equiv equiv_simple -undef equiv_status -assert +## Partial matching MUX4 + +design -reset +read_verilog -formal <<EOT +module mux_if_bal_3_1 #(parameter N=3, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {{W{{1'bx}}}}; + if (s[0] == 1'b0) + if (s[1] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[1] == 1'b0) + o <= i[2*W+:W]; +end +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux4=150 +select -assert-count 0 t:$_MUX_ +select -assert-count 1 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX4_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## Partial matching MUX8 + +design -reset +read_verilog -formal <<EOT +module mux_if_bal_5_1 #(parameter N=5, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {{W{{1'bx}}}}; + if (s[0] == 1'b0) + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[2] == 1'b0) + o <= i[2*W+:W]; + else + o <= i[3*W+:W]; + else + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[4*W+:W]; +end +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux4=150 -mux8=200 +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 1 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX8_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## Partial matching MUX16 + +design -reset +read_verilog -formal <<EOT +module mux_if_bal_9_1 #(parameter N=9, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {{W{{1'bx}}}}; + if (s[0] == 1'b0) + if (s[1] == 1'b0) + if (s[2] == 1'b0) + if (s[3] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[3] == 1'b0) + o <= i[2*W+:W]; + else + o <= i[3*W+:W]; + else + if (s[2] == 1'b0) + if (s[3] == 1'b0) + o <= i[4*W+:W]; + else + o <= i[5*W+:W]; + else + if (s[3] == 1'b0) + o <= i[6*W+:W]; + else + o <= i[7*W+:W]; + else + if (s[1] == 1'b0) + if (s[2] == 1'b0) + if (s[3] == 1'b0) + o <= i[8*W+:W]; +end +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux4=150 -mux8=200 -mux16=250 +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 1 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX16_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## MUX2 in MUX4 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux2in4(input [1:0] i, input s, output o); + assign o = s ? i[1] : i[0]; +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux4=99 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 1 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX4_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## MUX2 in MUX8 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux2in8(input [1:0] i, input s, output o); + assign o = s ? i[1] : i[0]; +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux8=99 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 1 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX8_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## MUX4 in MUX8 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux4in8(input [3:0] i, input [1:0] s, output o); + assign o = s[1] ? (s[0] ? i[3] : i[2]) : (s[0] ? i[1] : i[0]); +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux8=299 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 1 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX8_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## MUX2 in MUX16 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux2in16(input [1:0] i, input s, output o); + assign o = s ? i[1] : i[0]; +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux16=99 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 1 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX16_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## MUX4 in MUX16 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux4in16(input [3:0] i, input [1:0] s, output o); + assign o = s[1] ? (s[0] ? i[3] : i[2]) : (s[0] ? i[1] : i[0]); +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux16=299 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 1 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX16_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## MUX8 in MUX16 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux4in16(input [7:0] i, input [2:0] s, output o); + assign o = s[2] ? s[1] ? (s[0] ? i[3] : i[2]) : (s[0] ? i[1] : i[0]) + : s[1] ? (s[0] ? i[7] : i[6]) : (s[0] ? i[5] : i[4]); +endmodule +EOT +prep +design -save gold + +techmap +muxcover -mux16=699 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 1 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX16_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +## mux_if_bal_5_1 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux_if_bal_5_1 #(parameter N=5, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {{W{{1'bx}}}}; + if (s[0] == 1'b0) + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[2] == 1'b0) + o <= i[2*W+:W]; + else + o <= i[3*W+:W]; + else + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[4*W+:W]; +end +endmodule +EOT +prep +design -save gold + +wreduce +opt -full +techmap +muxcover -mux8=350 +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 1 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX8_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs -ignore_gold_x gold gate miter +sat -verify -prove-asserts -show-ports miter + +## mux_if_bal_5_1 (nodecode) :: https://github.com/YosysHQ/yosys/issues/1132 +design -load gold + +wreduce +opt -full +techmap +muxcover -mux8=350 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 1 t:$_MUX8_ +select -assert-count 0 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX8_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs -ignore_gold_x gold gate miter +sat -verify -prove-asserts -show-ports miter + +## mux_if_bal_9_1 :: https://github.com/YosysHQ/yosys/issues/1132 + +design -reset +read_verilog -formal <<EOT +module mux_if_bal_9_1 #(parameter N=9, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {{W{{1'bx}}}}; + if (s[3] == 1'b0) + if (s[2] == 1'b0) + if (s[1] == 1'b0) + if (s[0] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[0] == 1'b0) + o <= i[2*W+:W]; + else + o <= i[3*W+:W]; + else + if (s[1] == 1'b0) + if (s[0] == 1'b0) + o <= i[4*W+:W]; + else + o <= i[5*W+:W]; + else + if (s[0] == 1'b0) + o <= i[6*W+:W]; + else + o <= i[7*W+:W]; + else + if (s[2] == 1'b0) + if (s[1] == 1'b0) + if (s[0] == 1'b0) + o <= i[8*W+:W]; +end +endmodule +EOT +prep +design -save gold + +wreduce +opt -full +techmap +muxcover -mux16=750 +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 1 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX16_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs -ignore_gold_x gold gate miter +sat -verify -prove-asserts -show-ports miter + +## mux_if_bal_9_1 (nodecode) :: https://github.com/YosysHQ/yosys/issues/1132 + +design -load gold + +wreduce +opt -full +techmap +muxcover -mux16=750 -nodecode +clean +opt_expr -mux_bool +select -assert-count 0 t:$_MUX_ +select -assert-count 0 t:$_MUX4_ +select -assert-count 0 t:$_MUX8_ +select -assert-count 1 t:$_MUX16_ +techmap -map +/simcells.v t:$_MUX16_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs -ignore_gold_x gold gate miter +sat -verify -prove-asserts -show-ports miter diff --git a/tests/various/muxpack.v b/tests/various/muxpack.v new file mode 100644 index 000000000..33ece1f16 --- /dev/null +++ b/tests/various/muxpack.v @@ -0,0 +1,259 @@ +module mux_if_unbal_4_1 #(parameter N=4, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* + if (s == 0) o <= i[0*W+:W]; + else if (s == 1) o <= i[1*W+:W]; + else if (s == 2) o <= i[2*W+:W]; + else if (s == 3) o <= i[3*W+:W]; + else o <= {W{1'bx}}; +endmodule + +module mux_if_unbal_5_3 #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {W{1'bx}}; + if (s == 0) o <= i[0*W+:W]; + if (s == 1) o <= i[1*W+:W]; + if (s == 2) o <= i[2*W+:W]; + if (s == 3) o <= i[3*W+:W]; + if (s == 4) o <= i[4*W+:W]; +end +endmodule + +module mux_if_unbal_5_3_invert #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* + if (s != 0) + if (s != 1) + if (s != 2) + if (s != 3) + if (s != 4) o <= i[4*W+:W]; + else o <= i[0*W+:W]; + else o <= i[3*W+:W]; + else o <= i[2*W+:W]; + else o <= i[1*W+:W]; + else o <= {W{1'bx}}; +endmodule + +module mux_if_unbal_5_3_width_mismatch #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {W{1'bx}}; + if (s == 0) o <= i[0*W+:W]; + if (s == 1) o <= i[1*W+:W]; + if (s == 2) o[W-2:0] <= i[2*W+:W-1]; + if (s == 3) o <= i[3*W+:W]; + if (s == 4) o <= i[4*W+:W]; +end +endmodule + +module mux_if_unbal_4_1_missing #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + if (s == 0) o <= i[0*W+:W]; +// else if (s == 1) o <= i[1*W+:W]; +// else if (s == 2) o <= i[2*W+:W]; + else if (s == 3) o <= i[3*W+:W]; + else o <= {W{1'bx}}; +end +endmodule + +module mux_if_unbal_5_3_order #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {W{1'bx}}; + if (s == 3) o <= i[3*W+:W]; + if (s == 2) o <= i[2*W+:W]; + if (s == 1) o <= i[1*W+:W]; + if (s == 4) o <= i[4*W+:W]; + if (s == 0) o <= i[0*W+:W]; +end +endmodule + +module mux_if_unbal_4_1_nonexcl #(parameter N=4, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* + if (s == 0) o <= i[0*W+:W]; + else if (s == 1) o <= i[1*W+:W]; + else if (s == 2) o <= i[2*W+:W]; + else if (s == 3) o <= i[3*W+:W]; + else if (s == 0) o <= {W{1'b0}}; + else o <= {W{1'bx}}; +endmodule + +module mux_if_unbal_5_3_nonexcl #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {W{1'bx}}; + if (s == 0) o <= i[0*W+:W]; + if (s == 1) o <= i[1*W+:W]; + if (s == 2) o <= i[2*W+:W]; + if (s == 3) o <= i[3*W+:W]; + if (s == 4) o <= i[4*W+:W]; + if (s == 0) o <= i[2*W+:W]; +end +endmodule + +module mux_case_unbal_8_7#(parameter N=8, parameter W=7) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* begin + o <= {W{1'bx}}; + case (s) + 0: o <= i[0*W+:W]; + default: + case (s) + 1: o <= i[1*W+:W]; + 2: o <= i[2*W+:W]; + default: + case (s) + 3: o <= i[3*W+:W]; + 4: o <= i[4*W+:W]; + 5: o <= i[5*W+:W]; + default: + case (s) + 6: o <= i[6*W+:W]; + default: o <= i[7*W+:W]; + endcase + endcase + endcase + endcase +end +endmodule + +module mux_if_bal_8_2 #(parameter N=8, parameter W=2) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* + if (s[0] == 1'b0) + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[2] == 1'b0) + o <= i[2*W+:W]; + else + o <= i[3*W+:W]; + else + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[4*W+:W]; + else + o <= i[5*W+:W]; + else + if (s[2] == 1'b0) + o <= i[6*W+:W]; + else + o <= i[7*W+:W]; +endmodule + +module mux_if_bal_5_1 #(parameter N=5, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o); +always @* + if (s[0] == 1'b0) + if (s[1] == 1'b0) + if (s[2] == 1'b0) + o <= i[0*W+:W]; + else + o <= i[1*W+:W]; + else + if (s[2] == 1'b0) + o <= i[2*W+:W]; + else + o <= i[3*W+:W]; + else + o <= i[4*W+:W]; +endmodule + +module cliffordwolf_nonexclusive_select ( + input wire x, y, z, + input wire a, b, c, d, + output reg o +); + always @* begin + o = a; + if (x) o = b; + if (y) o = c; + if (z) o = d; + end +endmodule + +module cliffordwolf_freduce ( + input wire [1:0] s, + input wire a, b, c, d, + output reg [3:0] o +); + always @* begin + o = {4{a}}; + if (s == 0) o = {3{b}}; + if (s == 1) o = {2{c}}; + if (s == 2) o = d; + end +endmodule + +module case_nonexclusive_select ( + input wire [1:0] x, y, + input wire a, b, c, d, e, + output reg o +); + always @* begin + case (x) + 0: o = b; + 2: o = b; + 1: o = c; + default: begin + o = a; + if (y == 0) o = d; + if (y == 1) o = e; + end + endcase + end +endmodule + +module case_nonoverlap ( + input wire [2:0] x, + input wire a, b, c, d, e, + output reg o +); + always @* begin + case (x) + 0, 2: o = b; // Creates $reduce_or + 1: o = c; + default: + case (x) + 3: o = d; 4: o = d; // Creates $reduce_or + 5: o = e; + default: o = 1'b0; + endcase + endcase + end +endmodule + +module case_overlap ( + input wire [2:0] x, + input wire a, b, c, d, e, + output reg o +); + always @* begin + case (x) + 0, 2: o = b; // Creates $reduce_or + 1: o = c; + default: + case (x) + 0: o = 1'b1; // OVERLAP! + 3, 4: o = d; // Creates $reduce_or + 5: o = e; + default: o = 1'b0; + endcase + endcase + end +endmodule + +module case_overlap2 ( + input wire [2:0] x, + input wire a, b, c, d, e, + output reg o +); + always @* begin + case (x) + 0: o = b; 2: o = b; // Creates $reduce_or + 1: o = c; + default: + case (x) + 0: o = d; 2: o = d; // Creates $reduce_or + 3: o = d; 4: o = d; // Creates $reduce_or + 5: o = e; + default: o = 1'b0; + endcase + endcase + end +endmodule diff --git a/tests/various/muxpack.ys b/tests/various/muxpack.ys new file mode 100644 index 000000000..af23fcec8 --- /dev/null +++ b/tests/various/muxpack.ys @@ -0,0 +1,268 @@ +read_verilog muxpack.v +design -save read + +hierarchy -top mux_if_unbal_4_1 +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_unbal_5_3 +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +# TODO: Currently ExclusiveDatabase only analyses $eq cells +#design -load read +#hierarchy -top mux_if_unbal_5_3_invert +#prep +#design -save gold +#muxpack +#opt +#stat +#select -assert-count 0 t:$mux +#select -assert-count 1 t:$pmux +#design -stash gate +#design -import gold -as gold +#design -import gate -as gate +#miter -equiv -flatten -make_assert -make_outputs gold gate miter +#sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_unbal_5_3_width_mismatch +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 2 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_unbal_4_1_missing +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_unbal_5_3_order +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_unbal_4_1_nonexcl +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_unbal_5_3_nonexcl +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_case_unbal_8_7 +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_bal_8_2 +prep +design -save gold +muxpack +opt +stat +select -assert-count 7 t:$mux +select -assert-count 0 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top mux_if_bal_5_1 +prep +design -save gold +muxpack +opt +stat +select -assert-count 4 t:$mux +select -assert-count 0 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top cliffordwolf_nonexclusive_select +prep +design -save gold +muxpack +opt +stat +select -assert-count 3 t:$mux +select -assert-count 0 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +#design -load read +#hierarchy -top cliffordwolf_freduce +#prep +#design -save gold +#proc; opt; freduce; opt +#show +#muxpack +#opt +#stat +#select -assert-count 0 t:$mux +#select -assert-count 1 t:$pmux +#design -stash gate +#design -import gold -as gold +#design -import gate -as gate +#miter -equiv -flatten -make_assert -make_outputs gold gate miter +#sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top case_nonexclusive_select +prep +design -save gold +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 2 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top case_nonoverlap +#prep # Do not prep otherwise $pmux's overlapping entry will get removed +proc +design -save gold +opt -fast -mux_undef +select -assert-count 2 t:$pmux +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 1 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top case_overlap +#prep # Do not prep otherwise $pmux's overlapping entry will get removed +proc +design -save gold +opt -fast -mux_undef +select -assert-count 2 t:$pmux +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 2 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load read +hierarchy -top case_overlap2 +#prep # Do not prep otherwise $pmux's overlapping entry will get removed +proc +design -save gold +opt -fast -mux_undef +select -assert-count 2 t:$pmux +muxpack +opt +stat +select -assert-count 0 t:$mux +select -assert-count 2 t:$pmux +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter diff --git a/tests/various/pmux2shiftx.v b/tests/various/pmux2shiftx.v index fec84187b..563394080 100644 --- a/tests/various/pmux2shiftx.v +++ b/tests/various/pmux2shiftx.v @@ -32,3 +32,13 @@ module pmux2shiftx_test ( endcase end endmodule + +module issue01135(input [7:0] i, output o); +always @* +case (i[6:3]) + 4: o <= i[0]; + 3: o <= i[2]; + 7: o <= i[3]; + default: o <= 1'b0; +endcase +endmodule diff --git a/tests/various/pmux2shiftx.ys b/tests/various/pmux2shiftx.ys index deb134083..51ee2f7be 100644 --- a/tests/various/pmux2shiftx.ys +++ b/tests/various/pmux2shiftx.ys @@ -1,4 +1,7 @@ read_verilog pmux2shiftx.v +design -save read + +hierarchy -top pmux2shiftx_test prep design -save gold @@ -21,8 +24,16 @@ design -import gate -as gate miter -equiv -flatten -make_assert -make_outputs gold gate miter sat -verify -prove-asserts -show-ports miter -design -load gold -stat - -design -load gate -stat +#design -load gold +#stat +# +#design -load gate +#stat + +design -load read +hierarchy -top issue01135 +proc +pmux2shiftx -norange +opt -full +select -assert-count 0 t:$shift* +select -assert-count 1 t:$pmux diff --git a/tests/various/run-test.sh b/tests/various/run-test.sh index d49553ede..92b905765 100755 --- a/tests/various/run-test.sh +++ b/tests/various/run-test.sh @@ -4,11 +4,9 @@ for x in *.ys; do echo "Running $x.." ../../yosys -ql ${x%.ys}.log $x done -# Run any .sh files in this directory (with the exception of the file - run-test.sh -shell_tests=$(echo *.sh | sed -e 's/run-test.sh//') -if [ "$shell_tests" ]; then - for s in $shell_tests; do - echo "Running $s.." - bash $s - done -fi +for s in *.sh; do + if [ "$s" != "run-test.sh" ]; then + echo "Running $s.." + bash $s + fi +done diff --git a/tests/various/script.ys b/tests/various/script.ys new file mode 100644 index 000000000..66b7b5caa --- /dev/null +++ b/tests/various/script.ys @@ -0,0 +1,20 @@ +read_verilog -formal <<EOT + module top; + foo bar(); + foo asdf(); + winnie the_pooh(); + + wire [1023:0] _RUNME0 = "select -assert-count 2 t:foo"; + wire [1023:0] _RUNME1 = "select -assert-count 1 t:winnie"; + endmodule + + module other; + wire [1023:0] _DELETE = "cd; delete c:bar"; + endmodule +EOT + +script -scriptwire w:_RUNME* + +select w:_DELETE +script -scriptwire +select -assert-count 1 t:foo diff --git a/tests/various/shregmap.v b/tests/various/shregmap.v new file mode 100644 index 000000000..604c2c976 --- /dev/null +++ b/tests/various/shregmap.v @@ -0,0 +1,48 @@ +module shregmap_static_test(input i, clk, output [1:0] q); +reg head = 1'b0; +reg [3:0] shift1 = 4'b0000; +reg [3:0] shift2 = 4'b0000; + +always @(posedge clk) begin + head <= i; + shift1 <= {shift1[2:0], head}; + shift2 <= {shift2[2:0], head}; +end + +assign q = {shift2[3], shift1[3]}; +endmodule + +module $__SHREG_DFF_P_(input C, D, output Q); +parameter DEPTH = 1; +parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}}; +reg [DEPTH-1:0] r = INIT; +always @(posedge C) + r <= { r[DEPTH-2:0], D }; +assign Q = r[DEPTH-1]; +endmodule + +module shregmap_variable_test(input i, clk, input [1:0] l1, l2, output [1:0] q); +reg head = 1'b0; +reg [3:0] shift1 = 4'b0000; +reg [3:0] shift2 = 4'b0000; + +always @(posedge clk) begin + head <= i; + shift1 <= {shift1[2:0], head}; + shift2 <= {shift2[2:0], head}; +end + +assign q = {shift2[l2], shift1[l1]}; +endmodule + +module $__XILINX_SHREG_(input C, D, input [1:0] L, output Q); +parameter CLKPOL = 1; +parameter ENPOL = 1; +parameter DEPTH = 1; +parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}}; +reg [DEPTH-1:0] r = INIT; +wire clk = C ^ CLKPOL; +always @(posedge C) + r <= { r[DEPTH-2:0], D }; +assign Q = r[L]; +endmodule diff --git a/tests/various/shregmap.ys b/tests/various/shregmap.ys new file mode 100644 index 000000000..d644a88aa --- /dev/null +++ b/tests/various/shregmap.ys @@ -0,0 +1,66 @@ +read_verilog shregmap.v +design -save read + +design -copy-to model $__SHREG_DFF_P_ +hierarchy -top shregmap_static_test +prep +design -save gold + +techmap +shregmap -init + +opt + +stat +# show -width +select -assert-count 1 t:$_DFF_P_ +select -assert-count 2 t:$__SHREG_DFF_P_ + +design -stash gate + +design -import gold -as gold +design -import gate -as gate +design -copy-from model -as $__SHREG_DFF_P_ \$__SHREG_DFF_P_ +prep + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -seq 5 miter + +design -load gold +stat + +design -load gate +stat + +########## + +design -load read +design -copy-to model $__XILINX_SHREG_ +hierarchy -top shregmap_variable_test +prep +design -save gold + +simplemap t:$dff t:$dffe +shregmap -tech xilinx + +stat +# show -width +write_verilog -noexpr -norename +select -assert-count 1 t:$_DFF_P_ +select -assert-count 2 t:$__XILINX_SHREG_ + +design -stash gate + +design -import gold -as gold +design -import gate -as gate +design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_ +prep + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -seq 5 miter + +design -load gold +stat + +design -load gate +stat diff --git a/tests/various/signext.ys b/tests/various/signext.ys new file mode 100644 index 000000000..0c8d671e7 --- /dev/null +++ b/tests/various/signext.ys @@ -0,0 +1,33 @@ + +read_verilog -formal <<EOT +module gate(input clk, output [32:0] o, p, q, r, s, t, u); +assign o = 'bx; +assign p = 1'bx; +assign q = 'bz; +assign r = 1'bz; +assign s = 1'b0; +assign t = 'b1; +assign u = -'sb1; +endmodule +EOT + +proc + +## Equivalence checking + +read_verilog -formal <<EOT +module gold(input clk, output [32:0] o, p, q, r, s, t, u); +assign o = {33{1'bx}}; +assign p = {{32{1'b0}}, 1'bx}; +assign q = {33{1'bz}}; +assign r = {{32{1'b0}}, 1'bz}; +assign s = {33{1'b0}}; +assign t = {{32{1'b0}}, 1'b1}; +assign u = {33{1'b1}}; +endmodule +EOT + +proc + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -enable_undef miter diff --git a/tests/various/specify.v b/tests/various/specify.v index afc421da8..5d44d78f7 100644 --- a/tests/various/specify.v +++ b/tests/various/specify.v @@ -7,9 +7,11 @@ module test ( if (EN) Q <= D; specify - if (EN) (CLK *> (Q : D)) = (1, 2:3:4); +`ifndef SKIP_UNSUPPORTED_IGN_PARSER_CONSTRUCTS + if (EN) (posedge CLK *> (Q : D)) = (1, 2:3:4); $setup(D, posedge CLK &&& EN, 5); $hold(posedge CLK, D &&& EN, 6); +`endif endspecify endmodule @@ -28,3 +30,10 @@ module test2 ( (B => Q) = 1.5; endspecify endmodule + +module issue01144(input clk, d, output q); +specify + (posedge clk => (q +: d)) = (3,1); + (posedge clk *> (q +: d)) = (3,1); +endspecify +endmodule diff --git a/tests/various/specify.ys b/tests/various/specify.ys index a5ca07219..00597e1e2 100644 --- a/tests/various/specify.ys +++ b/tests/various/specify.ys @@ -54,3 +54,5 @@ equiv_struct equiv_induct -seq 5 equiv_status -assert design -reset + +read_verilog -DSKIP_UNSUPPORTED_IGN_PARSER_CONSTRUCTS specify.v |