3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-16 19:06:18 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Jim Lawson 2019-07-24 10:20:46 -07:00
commit c66b7402c0
199 changed files with 9380 additions and 1171 deletions

13
.dockerignore Normal file
View file

@ -0,0 +1,13 @@
.editorconfig
.gitignore
.gitmodules
.github
.git
Dockerfile
README.md
manual
CodingReadme
CodeOfConduct
.travis
.travis.yml

View file

@ -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 Yosys 0.8 .. Yosys 0.8-dev
-------------------------- --------------------------
@ -16,7 +29,18 @@ Yosys 0.8 .. Yosys 0.8-dev
- Added "gate2lut.v" techmap rule - Added "gate2lut.v" techmap rule
- Added "rename -src" - Added "rename -src"
- Added "equiv_opt" pass - 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 Yosys 0.7 .. Yosys 0.8
@ -30,7 +54,7 @@ Yosys 0.7 .. Yosys 0.8
- Added "write_verilog -decimal" - Added "write_verilog -decimal"
- Added "scc -set_attr" - Added "scc -set_attr"
- Added "verilog_defines" command - 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 support for hierarchical defparam
- Added FIRRTL back-end - Added FIRRTL back-end
- Improved ABC default scripts - Improved ABC default scripts
@ -39,7 +63,7 @@ Yosys 0.7 .. Yosys 0.8
- Added Verilog $rtoi and $itor support - Added Verilog $rtoi and $itor support
- Added "check -initdrv" - Added "check -initdrv"
- Added "read_blif -wideports" - 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 support for SystemVerilog unique, unique0, and priority case
- Added "write_edif" options for edif "flavors" - Added "write_edif" options for edif "flavors"
- Added support for resetall compiler directive - Added support for resetall compiler directive

33
Dockerfile Normal file
View file

@ -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"]

View file

@ -46,6 +46,10 @@ OS := $(shell uname -s)
PREFIX ?= /usr/local PREFIX ?= /usr/local
INSTALL_SUDO := INSTALL_SUDO :=
ifneq ($(wildcard Makefile.conf),)
include Makefile.conf
endif
BINDIR := $(PREFIX)/bin BINDIR := $(PREFIX)/bin
LIBDIR := $(PREFIX)/lib LIBDIR := $(PREFIX)/lib
DATDIR := $(PREFIX)/share/yosys 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' # 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 # will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC.. # delete your work on ABC..
ABCREV = 3709744 ABCREV = 62487de
ABCPULL = 1 ABCPULL = 1
ABCURL ?= https://github.com/berkeley-abc/abc ABCURL ?= https://github.com/berkeley-abc/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
@ -662,6 +666,12 @@ else
SEEDOPT="" SEEDOPT=""
endif endif
ifneq ($(ABCEXTERNAL),)
ABCOPT="-A $(ABCEXTERNAL)"
else
ABCOPT=""
endif
test: $(TARGETS) $(EXTRA_TARGETS) test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/simple && bash run-test.sh $(SEEDOPT)
+cd tests/hana && 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/share && bash run-test.sh $(SEEDOPT)
+cd tests/fsm && bash run-test.sh $(SEEDOPT) +cd tests/fsm && bash run-test.sh $(SEEDOPT)
+cd tests/techmap && bash run-test.sh +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/bram && bash run-test.sh $(SEEDOPT)
+cd tests/various && bash run-test.sh +cd tests/various && bash run-test.sh
+cd tests/sat && bash run-test.sh +cd tests/sat && bash run-test.sh
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+cd tests/opt && bash run-test.sh +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 ""
@echo " Passed \"make test\"." @echo " Passed \"make test\"."
@echo "" @echo ""

View file

@ -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: On FreeBSD use the following command to install all prerequisites:
# pkg install bison flex readline gawk libffi\ # 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: On FreeBSD system use gmake instead of make. To run tests use:
% MAKE=gmake CC=cc gmake test % 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 through the synthesis. When entities are combined, a new |-separated
string is created that contains all the string from the original entities. 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 - In addition to the ``(* ... *)`` attribute syntax, Yosys supports
the non-standard ``{* ... *}`` attribute syntax to set default attributes the non-standard ``{* ... *}`` attribute syntax to set default attributes
for everything that comes after the ``{* ... *}`` statement. (Reset 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' $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v'
- Sized constants (the syntax ``<size>'s?[bodh]<value>``) support constant - 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`` must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010``
- The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in - The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in

View file

@ -1,3 +1,4 @@
OBJS += backends/aiger/aiger.o OBJS += backends/aiger/aiger.o
OBJS += backends/aiger/xaiger.o

View file

@ -70,34 +70,35 @@ struct AigerWriter
int bit2aig(SigBit bit) int bit2aig(SigBit bit)
{ {
if (aig_map.count(bit) == 0) auto it = aig_map.find(bit);
{ if (it != aig_map.end()) {
aig_map[bit] = -1; log_assert(it->second >= 0);
return it->second;
}
if (initstate_bits.count(bit)) { // NB: Cannot use iterator returned from aig_map.insert()
log_assert(initstate_ff > 0); // since this function is called recursively
aig_map[bit] = initstate_ff;
} else int a = -1;
if (not_map.count(bit)) { if (not_map.count(bit)) {
int a = bit2aig(not_map.at(bit)) ^ 1; a = bit2aig(not_map.at(bit)) ^ 1;
aig_map[bit] = a;
} else } else
if (and_map.count(bit)) { if (and_map.count(bit)) {
auto args = and_map.at(bit); auto args = and_map.at(bit);
int a0 = bit2aig(args.first); int a0 = bit2aig(args.first);
int a1 = bit2aig(args.second); int a1 = bit2aig(args.second);
aig_map[bit] = mkgate(a0, a1); a = mkgate(a0, a1);
} else } else
if (alias_map.count(bit)) { if (alias_map.count(bit)) {
aig_map[bit] = bit2aig(alias_map.at(bit)); a = bit2aig(alias_map.at(bit));
} }
if (bit == State::Sx || bit == State::Sz) if (bit == State::Sx || bit == State::Sz)
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
}
log_assert(aig_map.at(bit) >= 0); log_assert(a >= 0);
return aig_map.at(bit); 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) 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("invariant constraints.\n");
log("\n"); log("\n");
log(" -ascii\n"); log(" -ascii\n");
log(" write ASCII version of AGIER format\n"); log(" write ASCII version of AIGER format\n");
log("\n"); log("\n");
log(" -zinit\n"); log(" -zinit\n");
log(" convert FFs to zero-initialized FFs, adding additional inputs for\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); writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
if (!map_filename.empty()) { if (!map_filename.empty()) {
rewrite_filename(filename);
std::ofstream mapf; std::ofstream mapf;
mapf.open(map_filename.c_str(), std::ofstream::trunc); mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail()) if (mapf.fail())

878
backends/aiger/xaiger.cc Normal file
View file

@ -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

View file

@ -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/rtlil.h"
#include "kernel/register.h" #include "kernel/register.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
@ -875,11 +880,30 @@ struct BtorWorker
else else
{ {
if (bit_cell.count(bit) == 0) if (bit_cell.count(bit) == 0)
log_error("No driver for signal bit %s.\n", log_signal(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)); export_cell(bit_cell.at(bit));
log_assert(bit_nid.count(bit)); log_assert(bit_nid.count(bit));
} }
} }
}
nidbits.push_back(bit_nid.at(bit)); nidbits.push_back(bit_nid.at(bit));
} }

View file

@ -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 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()); f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) { for (size_t i = 0; i < (*it)->compare.size(); i++) {
if (i > 0) if (i > 0)
@ -483,6 +488,7 @@ struct DumpPass : public Pass {
std::stringstream buf; std::stringstream buf;
if (!filename.empty()) { if (!filename.empty()) {
rewrite_filename(filename);
std::ofstream *ff = new std::ofstream; std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc); ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc);
if (ff->fail()) { if (ff->fail()) {

View file

@ -126,6 +126,10 @@ struct JsonWriter
f << stringf("%s\n", first ? "" : ","); f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(n).c_str()); f << stringf(" %s: {\n", get_name(n).c_str());
f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); 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(" \"bits\": %s\n", get_bits(w).c_str());
f << stringf(" }"); f << stringf(" }");
first = false; first = false;
@ -189,6 +193,10 @@ struct JsonWriter
f << stringf(" %s: {\n", get_name(w->name).c_str()); f << stringf(" %s: {\n", get_name(w->name).c_str());
f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0");
f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); 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\": {"); f << stringf(" \"attributes\": {");
write_parameters(w->attributes); write_parameters(w->attributes);
f << stringf("\n }\n"); f << stringf("\n }\n");
@ -525,6 +533,7 @@ struct JsonPass : public Pass {
std::stringstream buf; std::stringstream buf;
if (!filename.empty()) { if (!filename.empty()) {
rewrite_filename(filename);
std::ofstream *ff = new std::ofstream; std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc); ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) { if (ff->fail()) {

View file

@ -336,6 +336,7 @@ struct ProtobufPass : public Pass {
std::stringstream buf; std::stringstream buf;
if (!filename.empty()) { if (!filename.empty()) {
rewrite_filename(filename);
std::ofstream *ff = new std::ofstream; std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc); ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) { if (ff->fail()) {

View file

@ -43,7 +43,11 @@ if os.name == "posix":
if current_rlimit_stack[1] != resource.RLIM_INFINITY: if current_rlimit_stack[1] != resource.RLIM_INFINITY:
smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1]) smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1])
if current_rlimit_stack[0] < smtio_stacksize: if current_rlimit_stack[0] < smtio_stacksize:
try:
resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1])) 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) # currently running solvers (so we can kill them)
@ -1023,6 +1027,8 @@ class MkVcd:
assert t >= self.t assert t >= self.t
if t != self.t: if t != self.t:
if self.t == -1: 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 integer 32 t smt_step $end", file=self.f)
print("$var event 1 ! smt_clock $end", file=self.f) print("$var event 1 ! smt_clock $end", file=self.f)
@ -1041,7 +1047,10 @@ class MkVcd:
scope = scope[:-1] scope = scope[:-1]
while uipath[:-1] != scope: 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)]) scope.append(uipath[len(scope)])
if path in self.clocks and self.clocks[path][1] == "event": if path in self.clocks and self.clocks[path][1] == "event":

View file

@ -189,7 +189,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
if (width < 0) if (width < 0)
width = data.bits.size() - offset; width = data.bits.size() - offset;
if (width == 0) { if (width == 0) {
f << "\"\""; // See IEEE 1364-2005 Clause 5.1.14.
f << "{0{1'b0}}";
return; return;
} }
if (nostr) 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::S1: bin_digits.push_back('1'); break;
case RTLIL::Sx: bin_digits.push_back('x'); break; case RTLIL::Sx: bin_digits.push_back('x'); break;
case RTLIL::Sz: bin_digits.push_back('z'); 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."); 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'); hex_digits.push_back('z');
continue; 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'); 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); 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::S1: f << stringf("1"); break;
case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sx: f << stringf("x"); break;
case RTLIL::Sz: f << stringf("z"); 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."); 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) if (noattr)
return; return;
if (attr2comment)
as_comment = true;
for (auto it = attributes.begin(); it != attributes.end(); ++it) { 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(" = "); f << stringf(" = ");
if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
f << stringf(" 0 "); f << stringf(" 0 ");
else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
f << stringf(" 1 "); f << stringf(" 1 ");
else else
dump_const(f, it->second, -1, 0, false, attr2comment); dump_const(f, it->second, -1, 0, false, as_comment);
f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); 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; return true;
} }
if (cell->type == "$pmux" || cell->type == "$pmux_safe") if (cell->type == "$pmux")
{ {
int width = cell->parameters["\\WIDTH"].as_int(); int width = cell->parameters["\\WIDTH"].as_int();
int s_width = cell->getPort("\\S").size(); int s_width = cell->getPort("\\S").size();
@ -792,10 +801,9 @@ 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); f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1);
dump_attributes(f, indent + " ", cell->attributes); 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" " (* parallel_case *)\n", indent.c_str());
f << stringf("%s" " casez (s)", 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++) for (int i = 0; i < s_width; i++)
@ -803,7 +811,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf("%s" " %d'b", indent.c_str(), s_width); f << stringf("%s" " %d'b", indent.c_str(), s_width);
for (int j = s_width-1; j >= 0; j--) 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(":\n");
f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); 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; return;
} }
dump_attributes(f, indent, sw->attributes);
f << stringf("%s" "casez (", indent.c_str()); f << stringf("%s" "casez (", indent.c_str());
dump_sigspec(f, sw->signal); dump_sigspec(f, sw->signal);
f << stringf(")\n"); f << stringf(")\n");
bool got_default = false; bool got_default = false;
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { 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 ((*it)->compare.size() == 0) {
if (got_default) if (got_default)
continue; 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()); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
bool keep_running = true; bool keep_running = true;
for (int port_id = 1; keep_running; port_id++) { for (int port_id = 1; keep_running; port_id++) {

View file

@ -2,7 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite * yosys -- Yosys Open SYnthesis Suite
* *
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -22,19 +22,190 @@
// 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. // 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 // http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf
#ifndef _WIN32 // https://stackoverflow.com/a/46137633
#include <libgen.h> #ifdef _MSC_VER
#include <stdlib.h>
#define __builtin_bswap32 _byteswap_ulong
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define __builtin_bswap32 OSSwapInt32
#endif #endif
#include <array> #include <inttypes.h>
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "aigerparse.h" #include "aigerparse.h"
YOSYS_NAMESPACE_BEGIN YOSYS_NAMESPACE_BEGIN
AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name) inline int32_t from_big_endian(int32_t i32) {
: design(design), f(f), clk_name(clk_name) #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 = new RTLIL::Module;
module->name = module_name; module->name = module_name;
@ -73,6 +244,8 @@ end_of_header:
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); 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; line_count = 1;
piNum = 0;
flopNum = 0;
if (header == "aag") if (header == "aag")
parse_aiger_ascii(); parse_aiger_ascii();
@ -81,11 +254,15 @@ end_of_header:
else else
log_abort(); log_abort();
RTLIL::Wire* n0 = module->wire("\\__0__");
if (n0)
module->connect(n0, RTLIL::S0);
// Parse footer (symbol table, comments, etc.) // Parse footer (symbol table, comments, etc.)
unsigned l1; unsigned l1;
std::string s; std::string s;
for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) {
if (c == 'i' || c == 'l' || c == 'o') { if (c == 'i' || c == 'l' || c == 'o' || c == 'b') {
f.ignore(1); f.ignore(1);
if (!(f >> l1 >> s)) if (!(f >> l1 >> s))
log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count);
@ -97,11 +274,12 @@ end_of_header:
if (c == 'i') wire = inputs[l1]; if (c == 'i') wire = inputs[l1];
else if (c == 'l') wire = latches[l1]; else if (c == 'l') wire = latches[l1];
else if (c == 'o') wire = outputs[l1]; else if (c == 'o') wire = outputs[l1];
else if (c == 'b') wire = bad_properties[l1];
else log_abort(); else log_abort();
module->rename(wire, stringf("\\%s", s.c_str())); module->rename(wire, stringf("\\%s", s.c_str()));
} }
else if (c == 'b' || c == 'j' || c == 'f') { else if (c == 'j' || c == 'f') {
// TODO // TODO
} }
else if (c == 'c') { else if (c == 'c') {
@ -115,36 +293,199 @@ end_of_header:
std::getline(f, line); // Ignore up to start of next line std::getline(f, line); // Ignore up to start of next line
} }
module->fixup_ports(); post_process();
design->add(module); }
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) static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal)
{ {
const unsigned variable = literal >> 1; const unsigned variable = literal >> 1;
const bool invert = 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::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : ""));
RTLIL::Wire *wire = module->wire(wire_name); RTLIL::Wire *wire = module->wire(wire_name);
if (wire) return wire; if (wire) return wire;
log_debug("Creating %s\n", wire_name.c_str()); log_debug2("Creating %s\n", wire_name.c_str());
wire = module->addWire(wire_name); wire = module->addWire(wire_name);
wire->port_input = wire->port_output = false;
if (!invert) return wire; if (!invert) return wire;
RTLIL::IdString wire_inv_name(stringf("\\n%d", variable)); RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable));
RTLIL::Wire *wire_inv = module->wire(wire_inv_name); RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
if (wire_inv) { if (wire_inv) {
if (module->cell(wire_inv_name)) return wire; if (module->cell(wire_inv_name)) return wire;
} }
else { else {
log_debug("Creating %s\n", wire_inv_name.c_str()); log_debug2("Creating %s\n", wire_inv_name.c_str());
wire_inv = module->addWire(wire_inv_name); wire_inv = module->addWire(wire_inv_name);
wire_inv->port_input = wire_inv->port_output = false;
} }
log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); log_debug2("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? module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire);
return 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() void AigerReader::parse_aiger_ascii()
{ {
std::string line; std::string line;
@ -153,11 +494,11 @@ void AigerReader::parse_aiger_ascii()
unsigned l1, l2, l3; unsigned l1, l2, l3;
// Parse inputs // Parse inputs
for (unsigned i = 0; i < I; ++i, ++line_count) { for (unsigned i = 1; i <= I; ++i, ++line_count) {
if (!(f >> l1)) if (!(f >> l1))
log_error("Line %u cannot be interpreted as an input!\n", line_count); log_error("Line %u cannot be interpreted as an input!\n", line_count);
log_debug("%d is an input\n", l1); log_debug2("%d is an input\n", l1);
log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted? log_assert(!(l1 & 1)); // Inputs can't be inverted
RTLIL::Wire *wire = createWireIfNotExists(module, l1); RTLIL::Wire *wire = createWireIfNotExists(module, l1);
wire->port_input = true; wire->port_input = true;
inputs.push_back(wire); inputs.push_back(wire);
@ -166,17 +507,19 @@ void AigerReader::parse_aiger_ascii()
// Parse latches // Parse latches
RTLIL::Wire *clk_wire = nullptr; RTLIL::Wire *clk_wire = nullptr;
if (L > 0) { if (L > 0) {
log_assert(clk_name != "");
clk_wire = module->wire(clk_name); clk_wire = module->wire(clk_name);
log_assert(!clk_wire); log_assert(!clk_wire);
log_debug("Creating %s\n", clk_name.c_str()); log_debug2("Creating %s\n", clk_name.c_str());
clk_wire = module->addWire(clk_name); clk_wire = module->addWire(clk_name);
clk_wire->port_input = true; clk_wire->port_input = true;
clk_wire->port_output = false;
} }
for (unsigned i = 0; i < L; ++i, ++line_count) { for (unsigned i = 0; i < L; ++i, ++line_count) {
if (!(f >> l1 >> l2)) if (!(f >> l1 >> l2))
log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_error("Line %u cannot be interpreted as a latch!\n", line_count);
log_debug("%d %d is a latch\n", l1, l2); log_debug2("%d %d is a latch\n", l1, l2);
log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted? log_assert(!(l1 & 1));
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
@ -187,17 +530,19 @@ void AigerReader::parse_aiger_ascii()
if (!(f >> l3)) if (!(f >> l3))
log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_error("Line %u cannot be interpreted as a latch!\n", line_count);
if (l3 == 0 || l3 == 1) if (l3 == 0)
q_wire->attributes["\\init"] = RTLIL::Const(l3); q_wire->attributes["\\init"] = RTLIL::S0;
else if (l3 == 1)
q_wire->attributes["\\init"] = RTLIL::S1;
else if (l3 == l1) { else if (l3 == l1) {
//q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); //q_wire->attributes["\\init"] = RTLIL::Sx;
} }
else else
log_error("Line %u has invalid reset literal for latch!\n", line_count); log_error("Line %u has invalid reset literal for latch!\n", line_count);
} }
else { else {
// AIGER latches are assumed to be initialized to zero // AIGER latches are assumed to be initialized to zero
q_wire->attributes["\\init"] = RTLIL::Const(0); q_wire->attributes["\\init"] = RTLIL::S0;
} }
latches.push_back(q_wire); latches.push_back(q_wire);
} }
@ -207,16 +552,32 @@ void AigerReader::parse_aiger_ascii()
if (!(f >> l1)) if (!(f >> l1))
log_error("Line %u cannot be interpreted as an output!\n", line_count); log_error("Line %u cannot be interpreted as an output!\n", line_count);
log_debug("%d is an output\n", l1); log_debug2("%d is an output\n", l1);
RTLIL::Wire *wire = createWireIfNotExists(module, 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; wire->port_output = true;
outputs.push_back(wire); outputs.push_back(wire);
} }
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse bad state properties // Parse bad properties
for (unsigned i = 0; i < B; ++i, ++line_count) for (unsigned i = 0; i < B; ++i, ++line_count) {
std::getline(f, line); // Ignore up to start of next line 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 // TODO: Parse invariant constraints
for (unsigned i = 0; i < C; ++i, ++line_count) for (unsigned i = 0; i < C; ++i, ++line_count)
@ -235,12 +596,12 @@ void AigerReader::parse_aiger_ascii()
if (!(f >> l1 >> l2 >> l3)) if (!(f >> l1 >> l2 >> l3))
log_error("Line %u cannot be interpreted as an AND!\n", line_count); 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_debug2("%d %d %d is an AND\n", l1, l2, l3);
log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? log_assert(!(l1 & 1));
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire); module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
} }
std::getline(f, line); // Ignore up to start of next line std::getline(f, line); // Ignore up to start of next line
} }
@ -261,19 +622,23 @@ void AigerReader::parse_aiger_binary()
// Parse inputs // Parse inputs
for (unsigned i = 1; i <= I; ++i) { for (unsigned i = 1; i <= I; ++i) {
log_debug2("%d is an input\n", i);
RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
wire->port_input = true; wire->port_input = true;
log_assert(!wire->port_output);
inputs.push_back(wire); inputs.push_back(wire);
} }
// Parse latches // Parse latches
RTLIL::Wire *clk_wire = nullptr; RTLIL::Wire *clk_wire = nullptr;
if (L > 0) { if (L > 0) {
log_assert(clk_name != "");
clk_wire = module->wire(clk_name); clk_wire = module->wire(clk_name);
log_assert(!clk_wire); log_assert(!clk_wire);
log_debug("Creating %s\n", clk_name.c_str()); log_debug2("Creating %s\n", clk_name.c_str());
clk_wire = module->addWire(clk_name); clk_wire = module->addWire(clk_name);
clk_wire->port_input = true; clk_wire->port_input = true;
clk_wire->port_output = false;
} }
l1 = (I+1) * 2; l1 = (I+1) * 2;
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
@ -290,17 +655,19 @@ void AigerReader::parse_aiger_binary()
if (!(f >> l3)) if (!(f >> l3))
log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_error("Line %u cannot be interpreted as a latch!\n", line_count);
if (l3 == 0 || l3 == 1) if (l3 == 0)
q_wire->attributes["\\init"] = RTLIL::Const(l3); q_wire->attributes["\\init"] = RTLIL::S0;
else if (l3 == 1)
q_wire->attributes["\\init"] = RTLIL::S1;
else if (l3 == l1) { else if (l3 == l1) {
//q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); //q_wire->attributes["\\init"] = RTLIL::Sx;
} }
else else
log_error("Line %u has invalid reset literal for latch!\n", line_count); log_error("Line %u has invalid reset literal for latch!\n", line_count);
} }
else { else {
// AIGER latches are assumed to be initialized to zero // AIGER latches are assumed to be initialized to zero
q_wire->attributes["\\init"] = RTLIL::Const(0); q_wire->attributes["\\init"] = RTLIL::S0;
} }
latches.push_back(q_wire); latches.push_back(q_wire);
} }
@ -310,15 +677,34 @@ void AigerReader::parse_aiger_binary()
if (!(f >> l1)) if (!(f >> l1))
log_error("Line %u cannot be interpreted as an output!\n", line_count); log_error("Line %u cannot be interpreted as an output!\n", line_count);
log_debug("%d is an output\n", l1); log_debug2("%d is an output\n", l1);
RTLIL::Wire *wire = createWireIfNotExists(module, 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; wire->port_output = true;
outputs.push_back(wire); outputs.push_back(wire);
} }
std::getline(f, line); // Ignore up to start of next line std::getline(f, line); // Ignore up to start of next line
// TODO: Parse bad state properties // Parse bad properties
for (unsigned i = 0; i < B; ++i, ++line_count) 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 std::getline(f, line); // Ignore up to start of next line
// TODO: Parse invariant constraints // TODO: Parse invariant constraints
@ -339,16 +725,280 @@ void AigerReader::parse_aiger_binary()
l2 = parse_next_delta_literal(f, l1); l2 = parse_next_delta_literal(f, l1);
l3 = parse_next_delta_literal(f, l2); l3 = parse_next_delta_literal(f, l2);
log_debug("%d %d %d is an AND\n", l1, l2, l3); log_debug2("%d %d %d is an AND\n", l1, l2, l3);
log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? log_assert(!(l1 & 1));
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
}
}
RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_"); void AigerReader::post_process()
and_cell->setPort("\\A", i1_wire); {
and_cell->setPort("\\B", i2_wire); pool<IdString> seen_boxes;
and_cell->setPort("\\Y", o_wire); 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));
} }
} }
@ -363,18 +1013,19 @@ struct AigerFrontend : public Frontend {
log("Load module from an AIGER file into the current design.\n"); log("Load module from an AIGER file into the current design.\n");
log("\n"); log("\n");
log(" -module_name <module_name>\n"); log(" -module_name <module_name>\n");
log(" Name of module to be created (default: <filename>)" log(" Name of module to be created (default: <filename>)\n");
#ifdef _WIN32
"top" // FIXME
#else
"<filename>"
#endif
")\n");
log("\n"); log("\n");
log(" -clk_name <wire_name>\n"); log(" -clk_name <wire_name>\n");
log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
log(" this name (default: clk)\n"); log(" this name (default: clk)\n");
log("\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 void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
@ -382,6 +1033,8 @@ struct AigerFrontend : public Frontend {
RTLIL::IdString clk_name = "\\clk"; RTLIL::IdString clk_name = "\\clk";
RTLIL::IdString module_name; RTLIL::IdString module_name;
std::string map_filename;
bool wideports = false;
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) { for (argidx = 1; argidx < args.size(); argidx++) {
@ -394,13 +1047,23 @@ struct AigerFrontend : public Frontend {
clk_name = RTLIL::escape_id(args[++argidx]); clk_name = RTLIL::escape_id(args[++argidx]);
continue; continue;
} }
if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) {
map_filename = args[++argidx];
continue;
}
if (arg == "-wideports") {
wideports = true;
continue;
}
break; break;
} }
extra_args(f, filename, args, argidx); extra_args(f, filename, args, argidx);
if (module_name.empty()) { if (module_name.empty()) {
#ifdef _WIN32 #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 #else
char* bn = strdup(filename.c_str()); char* bn = strdup(filename.c_str());
module_name = RTLIL::escape_id(bn); module_name = RTLIL::escape_id(bn);
@ -408,7 +1071,7 @@ struct AigerFrontend : public Frontend {
#endif #endif
} }
AigerReader reader(design, *f, module_name, clk_name); AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports);
reader.parse_aiger(); reader.parse_aiger();
} }
} AigerFrontend; } AigerFrontend;

View file

@ -2,7 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite * yosys -- Yosys Open SYnthesis Suite
* *
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -31,19 +31,26 @@ struct AigerReader
std::istream &f; std::istream &f;
RTLIL::IdString clk_name; RTLIL::IdString clk_name;
RTLIL::Module *module; RTLIL::Module *module;
std::string map_filename;
bool wideports;
unsigned M, I, L, O, A; unsigned M, I, L, O, A;
unsigned B, C, J, F; // Optional in AIGER 1.9 unsigned B, C, J, F; // Optional in AIGER 1.9
unsigned line_count; unsigned line_count;
uint32_t piNum, flopNum;
std::vector<RTLIL::Wire*> inputs; std::vector<RTLIL::Wire*> inputs;
std::vector<RTLIL::Wire*> latches; std::vector<RTLIL::Wire*> latches;
std::vector<RTLIL::Wire*> outputs; 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_aiger();
void parse_xaiger();
void parse_aiger_ascii(); void parse_aiger_ascii();
void parse_aiger_binary(); void parse_aiger_binary();
void post_process();
}; };
YOSYS_NAMESPACE_END YOSYS_NAMESPACE_END

View file

@ -46,7 +46,7 @@ namespace AST {
// instantiate global variables (private API) // instantiate global variables (private API)
namespace AST_INTERNAL { 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_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; AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope; std::map<std::string, AstNode*> current_scope;
const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; 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_GENIF)
X(AST_GENCASE) X(AST_GENCASE)
X(AST_GENBLOCK) X(AST_GENBLOCK)
X(AST_TECALL)
X(AST_POSEDGE) X(AST_POSEDGE)
X(AST_NEGEDGE) X(AST_NEGEDGE)
X(AST_EDGE) 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->nowb = flag_nowb;
current_module->noopt = flag_noopt; current_module->noopt = flag_noopt;
current_module->icells = flag_icells; current_module->icells = flag_icells;
current_module->pwires = flag_pwires;
current_module->autowire = flag_autowire; current_module->autowire = flag_autowire;
current_module->fixup_ports(); 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' // 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, 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; current_ast = ast;
flag_dump_ast1 = dump_ast1; 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_nowb = nowb;
flag_noopt = noopt; flag_noopt = noopt;
flag_icells = icells; flag_icells = icells;
flag_pwires = pwires;
flag_autowire = autowire; flag_autowire = autowire;
log_assert(current_ast->type == AST_DESIGN); 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_nowb = nowb;
flag_noopt = noopt; flag_noopt = noopt;
flag_icells = icells; flag_icells = icells;
flag_pwires = pwires;
flag_autowire = autowire; flag_autowire = autowire;
use_internal_line_num(); use_internal_line_num();
@ -1547,9 +1551,12 @@ RTLIL::Module *AstModule::clone() const
new_mod->nomeminit = nomeminit; new_mod->nomeminit = nomeminit;
new_mod->nomem2reg = nomem2reg; new_mod->nomem2reg = nomem2reg;
new_mod->mem2reg = mem2reg; new_mod->mem2reg = mem2reg;
new_mod->noblackbox = noblackbox;
new_mod->lib = lib; new_mod->lib = lib;
new_mod->nowb = nowb;
new_mod->noopt = noopt; new_mod->noopt = noopt;
new_mod->icells = icells; new_mod->icells = icells;
new_mod->pwires = pwires;
new_mod->autowire = autowire; new_mod->autowire = autowire;
return new_mod; return new_mod;

View file

@ -137,6 +137,7 @@ namespace AST
AST_GENIF, AST_GENIF,
AST_GENCASE, AST_GENCASE,
AST_GENBLOCK, AST_GENBLOCK,
AST_TECALL,
AST_POSEDGE, AST_POSEDGE,
AST_NEGEDGE, AST_NEGEDGE,
@ -285,13 +286,13 @@ namespace AST
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code // 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, 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 // parametric modules are supported directly by the AST library
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
struct AstModule : RTLIL::Module { struct AstModule : RTLIL::Module {
AstNode *ast; 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; ~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, 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; 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 // 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_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 AST::AstNode *current_ast, *current_ast_mod;
extern std::map<std::string, AST::AstNode*> current_scope; extern std::map<std::string, AST::AstNode*> current_scope;
extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr;

View file

@ -504,6 +504,7 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::CaseRule *backup_case = current_case; RTLIL::CaseRule *backup_case = current_case;
current_case = new RTLIL::CaseRule; current_case = new RTLIL::CaseRule;
current_case->attributes["\\src"] = stringf("%s:%d", child->filename.c_str(), child->linenum);
last_generated_case = current_case; last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
for (auto node : child->children) { for (auto node : child->children) {
@ -853,7 +854,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_FUNCTION: case AST_FUNCTION:
case AST_DPI_FUNCTION: case AST_DPI_FUNCTION:
case AST_AUTOWIRE: case AST_AUTOWIRE:
case AST_LOCALPARAM:
case AST_DEFPARAM: case AST_DEFPARAM:
case AST_GENVAR: case AST_GENVAR:
case AST_GENFOR: 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 // remember the parameter, needed for example in techmap
case AST_PARAMETER: case AST_PARAMETER:
current_module->avail_parameters.insert(str); 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; break;
// create an RTLIL::Wire for an AST_WIRE node // 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; delete always;
} break; } 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: { case AST_FCALL: {
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
{ {

View file

@ -282,14 +282,14 @@ proc_stmt:
} case_body sync_list TOK_END EOL; } case_body sync_list TOK_END EOL;
switch_stmt: switch_stmt:
attr_list TOK_SWITCH sigspec EOL { TOK_SWITCH sigspec EOL {
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
rule->signal = *$3; rule->signal = *$2;
rule->attributes = attrbuf; rule->attributes = attrbuf;
switch_stack.back()->push_back(rule); switch_stack.back()->push_back(rule);
attrbuf.clear(); attrbuf.clear();
delete $3; delete $2;
} switch_body TOK_END EOL; } attr_list switch_body TOK_END EOL;
attr_list: attr_list:
/* empty */ | /* empty */ |
@ -298,9 +298,11 @@ attr_list:
switch_body: switch_body:
switch_body TOK_CASE { switch_body TOK_CASE {
RTLIL::CaseRule *rule = new RTLIL::CaseRule; RTLIL::CaseRule *rule = new RTLIL::CaseRule;
rule->attributes = attrbuf;
switch_stack.back()->back()->cases.push_back(rule); switch_stack.back()->back()->cases.push_back(rule);
switch_stack.push_back(&rule->switches); switch_stack.push_back(&rule->switches);
case_stack.push_back(rule); case_stack.push_back(rule);
attrbuf.clear();
} compare_list EOL case_body { } compare_list EOL case_body {
switch_stack.pop_back(); switch_stack.pop_back();
case_stack.pop_back(); case_stack.pop_back();
@ -319,12 +321,15 @@ compare_list:
/* empty */; /* empty */;
case_body: case_body:
case_body attr_stmt |
case_body switch_stmt | case_body switch_stmt |
case_body assign_stmt | case_body assign_stmt |
/* empty */; /* empty */;
assign_stmt: assign_stmt:
TOK_ASSIGN sigspec sigspec EOL { 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)); case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
delete $2; delete $2;
delete $3; delete $3;

View file

@ -292,6 +292,18 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (port_wire == nullptr) if (port_wire == nullptr)
port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array)); 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") { if (port_direction_node->data_string == "input") {
port_wire->port_input = true; port_wire->port_input = true;
} else } else
@ -372,6 +384,18 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (wire == nullptr) if (wire == nullptr)
wire = module->addWire(net_name, GetSize(bits_node->data_array)); 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++) for (int i = 0; i < GetSize(bits_node->data_array); i++)
{ {
JsonNode *bitval_node = bits_node->data_array.at(i); JsonNode *bitval_node = bits_node->data_array.at(i);

View file

@ -551,7 +551,7 @@ struct LibertyFrontend : public Frontend {
if (design->has(cell_name)) { if (design->has(cell_name)) {
Module *existing_mod = design->module(cell_name); Module *existing_mod = design->module(cell_name);
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { 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) { } else if (flag_nooverwrite) {
log("Ignoring re-definition of module %s.\n", log_id(cell_name)); log("Ignoring re-definition of module %s.\n", log_id(cell_name));
continue; continue;

View file

@ -48,6 +48,14 @@ USING_YOSYS_NAMESPACE
#include "VhdlUnits.h" #include "VhdlUnits.h"
#include "VeriLibrary.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__ #ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif #endif
@ -2016,6 +2024,9 @@ struct VerificPass : public Pass {
// WARNING: instantiating unknown module 'XYZ' (VERI-1063) // WARNING: instantiating unknown module 'XYZ' (VERI-1063)
Message::SetMessageType("VERI-1063", VERIFIC_ERROR); 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 #ifndef DB_PRESERVE_INITIAL_VALUE
# warning Verific was built without DB_PRESERVE_INITIAL_VALUE. # warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
#endif #endif

View file

@ -153,7 +153,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{ {
if (warn_z) { if (warn_z) {
AstNode *ret = const2ast(code, case_type); 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", log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
current_filename.c_str(), get_line_num()); current_filename.c_str(), get_line_num());
return ret; return ret;
@ -204,7 +204,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{ {
std::vector<RTLIL::State> data; std::vector<RTLIL::State> data;
bool is_signed = false; bool is_signed = false;
bool is_unsized = false; bool is_unsized = len_in_bits < 0;
if (*(endptr+1) == 's') { if (*(endptr+1) == 's') {
is_signed = true; is_signed = true;
endptr++; endptr++;
@ -213,25 +213,25 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{ {
case 'b': case 'b':
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; break;
case 'o': case 'o':
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; break;
case 'd': case 'd':
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; break;
case 'h': case 'h':
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; break;
default: default:
char next_char = char(tolower(*(endptr+1))); char next_char = char(tolower(*(endptr+1)));
if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') { 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; is_unsized = true;
my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized);
} else { } else {
return NULL; return NULL;
} }

View file

@ -168,6 +168,9 @@ struct VerilogFrontend : public Frontend {
log(" -icells\n"); log(" -icells\n");
log(" interpret cell types starting with '$' as internal cell types\n"); log(" interpret cell types starting with '$' as internal cell types\n");
log("\n"); log("\n");
log(" -pwires\n");
log(" add a wire for each module parameter\n");
log("\n");
log(" -nooverwrite\n"); log(" -nooverwrite\n");
log(" ignore re-definitions of modules. (the default behavior is to\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"); 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_nodpi = false;
bool flag_noopt = false; bool flag_noopt = false;
bool flag_icells = false; bool flag_icells = false;
bool flag_pwires = false;
bool flag_nooverwrite = false; bool flag_nooverwrite = false;
bool flag_overwrite = false; bool flag_overwrite = false;
bool flag_defer = false; bool flag_defer = false;
@ -368,6 +372,10 @@ struct VerilogFrontend : public Frontend {
flag_icells = true; flag_icells = true;
continue; continue;
} }
if (arg == "-pwires") {
flag_pwires = true;
continue;
}
if (arg == "-ignore_redef" || arg == "-nooverwrite") { if (arg == "-ignore_redef" || arg == "-nooverwrite") {
flag_nooverwrite = true; flag_nooverwrite = true;
flag_overwrite = false; flag_overwrite = false;
@ -458,7 +466,7 @@ struct VerilogFrontend : public Frontend {
error_on_dpi_function(current_ast); 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, 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) if (!flag_nopp)
delete lexin; delete lexin;

View file

@ -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 to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
global state.. its a mess) */ 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_$\.] { [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); frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
return TOK_SVA_LABEL; return TOK_SVA_LABEL;
} }
@ -311,6 +313,11 @@ supply1 { return TOK_SUPPLY1; }
return TOK_ID; return TOK_ID;
} }
"$"(info|warning|error|fatal) {
frontend_verilog_yylval.string = new std::string(yytext);
return TOK_MSG_TASKS;
}
"$signed" { return TOK_TO_SIGNED; } "$signed" { return TOK_TO_SIGNED; }
"$unsigned" { return TOK_TO_UNSIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; }

View file

@ -133,7 +133,7 @@ struct specify_rise_fall {
} }
%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE %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 TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
@ -319,15 +319,17 @@ module_para_list:
single_module_para: single_module_para:
/* empty */ | /* empty */ |
TOK_PARAMETER { attr TOK_PARAMETER {
if (astbuf1) delete astbuf1; if (astbuf1) delete astbuf1;
astbuf1 = new AstNode(AST_PARAMETER); astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true)); astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
} param_signed param_integer param_range single_param_decl | } param_signed param_integer param_range single_param_decl |
TOK_LOCALPARAM { attr TOK_LOCALPARAM {
if (astbuf1) delete astbuf1; if (astbuf1) delete astbuf1;
astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true)); astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
} param_signed param_integer param_range single_param_decl | } param_signed param_integer param_range single_param_decl |
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) { if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) {
AstNode *wire = new AstNode(AST_IDENTIFIER); AstNode *wire = new AstNode(AST_IDENTIFIER);
wire->str = ast_stack.back()->children.back()->str; 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)))); ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2))));
else else
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2));
@ -509,6 +517,7 @@ wire_type_token:
TOK_GENVAR { TOK_GENVAR {
astbuf3->type = AST_GENVAR; astbuf3->type = AST_GENVAR;
astbuf3->is_reg = true; astbuf3->is_reg = true;
astbuf3->is_signed = true;
astbuf3->range_left = 31; astbuf3->range_left = 31;
astbuf3->range_right = 0; astbuf3->range_right = 0;
} | } |
@ -1012,13 +1021,8 @@ list_of_specparam_assignments:
specparam_assignment: specparam_assignment:
ignspec_id '=' constant_mintypmax_expression ; ignspec_id '=' constant_mintypmax_expression ;
/* ignspec_opt_cond:
pulsestyle_declaration : TOK_IF '(' ignspec_expr ')' | /* empty */;
;
showcancelled_declaration :
;
*/
path_declaration : path_declaration :
simple_path_declaration ';' simple_path_declaration ';'
@ -1027,8 +1031,8 @@ path_declaration :
; ;
simple_path_declaration : simple_path_declaration :
parallel_path_description '=' path_delay_value | ignspec_opt_cond parallel_path_description '=' path_delay_value |
full_path_description '=' path_delay_value ignspec_opt_cond full_path_description '=' path_delay_value
; ;
path_delay_value : path_delay_value :
@ -1038,32 +1042,20 @@ path_delay_value :
; ;
list_of_path_delay_extra_expressions : list_of_path_delay_extra_expressions :
/* ',' path_delay_expression | ',' path_delay_expression list_of_path_delay_extra_expressions;
t_path_delay_expression
| trise_path_delay_expression ',' tfall_path_delay_expression specify_edge_identifier :
| trise_path_delay_expression ',' tfall_path_delay_expression ',' tz_path_delay_expression TOK_POSEDGE | TOK_NEGEDGE ;
| 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
;
parallel_path_description : 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 : 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 // This was broken into 2 rules to solve shift/reduce conflicts
list_of_path_inputs : list_of_path_inputs :
@ -1103,56 +1095,6 @@ system_timing_args :
system_timing_arg | system_timing_arg |
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 : path_delay_expression :
ignspec_constant_expression; ignspec_constant_expression;
@ -1211,6 +1153,7 @@ param_decl:
attr TOK_PARAMETER { attr TOK_PARAMETER {
astbuf1 = new AstNode(AST_PARAMETER); astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true)); astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
} param_signed param_integer param_real param_range param_decl_list ';' { } param_signed param_integer param_real param_range param_decl_list ';' {
delete astbuf1; delete astbuf1;
}; };
@ -1219,6 +1162,7 @@ localparam_decl:
attr TOK_LOCALPARAM { attr TOK_LOCALPARAM {
astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true)); astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
} param_signed param_integer param_real param_range param_decl_list ';' { } param_signed param_integer param_real param_range param_decl_list ';' {
delete astbuf1; delete astbuf1;
}; };
@ -1360,7 +1304,12 @@ wire_name_and_opt_assign:
wire_name '=' expr { wire_name '=' expr {
AstNode *wire = new AstNode(AST_IDENTIFIER); AstNode *wire = new AstNode(AST_IDENTIFIER);
wire->str = ast_stack.back()->children.back()->str; 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)))); ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $3))));
else else
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3)); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3));
@ -1385,7 +1334,13 @@ wire_name:
node->children.push_back(rng); node->children.push_back(rng);
} }
node->type = AST_MEMORY; 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 (current_function_or_task == NULL) {
if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { 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 | cell_port_list_rules ',' cell_port;
cell_port: cell_port:
/* empty */ { attr {
AstNode *node = new AstNode(AST_ARGUMENT); AstNode *node = new AstNode(AST_ARGUMENT);
astbuf2->children.push_back(node); astbuf2->children.push_back(node);
free_attr($1);
} | } |
expr { attr expr {
AstNode *node = new AstNode(AST_ARGUMENT); AstNode *node = new AstNode(AST_ARGUMENT);
astbuf2->children.push_back(node); 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); AstNode *node = new AstNode(AST_ARGUMENT);
node->str = *$2; node->str = *$3;
astbuf2->children.push_back(node); astbuf2->children.push_back(node);
node->children.push_back($4); node->children.push_back($5);
delete $2; delete $3;
free_attr($1);
} | } |
'.' TOK_ID '(' ')' { attr '.' TOK_ID '(' ')' {
AstNode *node = new AstNode(AST_ARGUMENT); AstNode *node = new AstNode(AST_ARGUMENT);
node->str = *$2; node->str = *$3;
astbuf2->children.push_back(node); 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: always_stmt:
@ -1868,6 +1836,16 @@ behavioral_stmt:
} opt_arg_list ';'{ } opt_arg_list ';'{
ast_stack.pop_back(); 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 { attr TOK_BEGIN opt_label {
AstNode *node = new AstNode(AST_BLOCK); AstNode *node = new AstNode(AST_BLOCK);
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
@ -2163,6 +2141,15 @@ gen_stmt:
if ($6 != NULL) if ($6 != NULL)
delete $6; delete $6;
ast_stack.pop_back(); 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: gen_stmt_block:

View file

@ -246,24 +246,24 @@ struct CellTypes
cell_types.clear(); cell_types.clear();
} }
bool cell_known(RTLIL::IdString type) bool cell_known(RTLIL::IdString type) const
{ {
return cell_types.count(type) != 0; 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); auto it = cell_types.find(type);
return it != cell_types.end() && it->second.outputs.count(port) != 0; 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); auto it = cell_types.find(type);
return it != cell_types.end() && it->second.inputs.count(port) != 0; 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); auto it = cell_types.find(type);
return it != cell_types.end() && it->second.is_evaluable; return it != cell_types.end() && it->second.is_evaluable;
@ -482,4 +482,3 @@ extern CellTypes yosys_celltypes;
YOSYS_NAMESPACE_END YOSYS_NAMESPACE_END
#endif #endif

View file

@ -61,7 +61,7 @@ int log_force_debug = 0;
int log_debug_suppressed = 0; int log_debug_suppressed = 0;
vector<int> header_count; vector<int> header_count;
pool<RTLIL::IdString> log_id_cache; vector<char*> log_id_cache;
vector<shared_str> string_buf; vector<shared_str> string_buf;
int string_buf_index = -1; int string_buf_index = -1;
@ -69,6 +69,13 @@ static struct timeval initial_tv = { 0, 0 };
static bool next_print_log = false; static bool next_print_log = false;
static int log_newline_count = 0; 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__) #if defined(_WIN32) && !defined(__MINGW32__)
// this will get time information and return it in timeval, simulating gettimeofday() // this will get time information and return it in timeval, simulating gettimeofday()
int gettimeofday(struct timeval *tv, struct timezone *tz) int gettimeofday(struct timeval *tv, struct timezone *tz)
@ -282,6 +289,17 @@ void log_file_warning(const std::string &filename, int lineno,
va_end(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) YS_ATTRIBUTE(noreturn)
static void logv_error_with_prefix(const char *prefix, static void logv_error_with_prefix(const char *prefix,
const char *format, va_list ap) const char *format, va_list ap)
@ -403,7 +421,7 @@ void log_push()
void log_pop() void log_pop()
{ {
header_count.pop_back(); header_count.pop_back();
log_id_cache.clear(); log_id_cache_clear();
string_buf.clear(); string_buf.clear();
string_buf_index = -1; string_buf_index = -1;
log_flush(); log_flush();
@ -510,7 +528,7 @@ void log_reset_stack()
{ {
while (header_count.size() > 1) while (header_count.size() > 1)
header_count.pop_back(); header_count.pop_back();
log_id_cache.clear(); log_id_cache_clear();
string_buf.clear(); string_buf.clear();
string_buf_index = -1; string_buf_index = -1;
log_flush(); log_flush();
@ -569,8 +587,8 @@ const char *log_const(const RTLIL::Const &value, bool autoint)
const char *log_id(RTLIL::IdString str) const char *log_id(RTLIL::IdString str)
{ {
log_id_cache.insert(str); log_id_cache.push_back(strdup(str.c_str()));
const char *p = str.c_str(); const char *p = log_id_cache.back();
if (p[0] != '\\') if (p[0] != '\\')
return p; return p;
if (p[1] == '$' || p[1] == '\\' || p[1] == 0) if (p[1] == '$' || p[1] == '\\' || p[1] == 0)

View file

@ -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. // 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_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)); 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); YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);

View file

@ -545,6 +545,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st
} }
filename = arg; filename = arg;
rewrite_filename(filename);
std::ofstream *ff = new std::ofstream; std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc); ff->open(filename.c_str(), std::ofstream::trunc);
yosys_output_files.insert(filename); yosys_output_files.insert(filename);

View file

@ -1381,7 +1381,34 @@ void RTLIL::Module::check()
for (auto &it : processes) { for (auto &it : processes) {
log_assert(it.first == it.second->name); log_assert(it.first == it.second->name);
log_assert(!it.first.empty()); 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_) { for (auto &it : connections_) {

View file

@ -276,6 +276,18 @@ namespace RTLIL
return std::string(c_str() + pos, len); 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 { size_t size() const {
return str().size(); return str().size();
} }
@ -1315,7 +1327,7 @@ public:
#endif #endif
}; };
struct RTLIL::CaseRule struct RTLIL::CaseRule : public RTLIL::AttrObject
{ {
std::vector<RTLIL::SigSpec> compare; std::vector<RTLIL::SigSpec> compare;
std::vector<RTLIL::SigSig> actions; std::vector<RTLIL::SigSig> actions;

View file

@ -651,6 +651,10 @@ void rewrite_filename(std::string &filename)
filename = filename.substr(1, GetSize(filename)-2); filename = filename.substr(1, GetSize(filename)-2);
if (filename.substr(0, 2) == "+/") if (filename.substr(0, 2) == "+/")
filename = proc_share_dirname() + filename.substr(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 #ifdef YOSYS_ENABLE_TCL
@ -1250,24 +1254,59 @@ struct HistoryPass : public Pass {
#endif #endif
struct ScriptCmdPass : public Pass { 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 { void help() YS_OVERRIDE {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n"); log("\n");
log(" script <filename> [<from_label>:<to_label>]\n"); log(" script <filename> [<from_label>:<to_label>]\n");
log(" script -scriptwire [selection]\n");
log("\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("\n");
log("The 2nd argument can be used to only execute the section of the\n"); log("In the default (file) case, the 2nd argument can be used to only execute the\n");
log("file between the specified labels. An empty from label is synonymous\n"); log("section of the file between the specified labels. An empty from label is\n");
log("for the beginning of the file and an empty to label is synonymous\n"); log("synonymous with the beginning of the file and an empty to label is synonymous\n");
log("for the end of the file.\n"); log("with the end of the file.\n");
log("\n"); log("\n");
log("If only one label is specified (without ':') then only the block\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("marked with that label (until the next label) is executed.\n");
log("\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 { void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
if (args.size() < 2) {
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"); log_cmd_error("Missing script file.\n");
else if (args.size() == 2) else if (args.size() == 2)
run_frontend(args[1], "script", design); run_frontend(args[1], "script", design);

View file

@ -331,8 +331,9 @@ to update {\tt \textbackslash{}q}.
An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and 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}. exactly one RTLIL::CaseRule object, which is called the {\it root case}.
An RTLIL::SyncRule object contains an (optional) synchronization condition An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type) and zero or
(signal and edge-type) and zero or more assignments (RTLIL::SigSig). 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) 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 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 sets {\tt \$0\textbackslash{}q[0:0]} to the value of {\tt \textbackslash{}d} if {\tt
\textbackslash{}enable} is active (lines $6 \dots 11$). \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 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}. a positive clock edge on {\tt \textbackslash{}clock} or {\tt \textbackslash{}reset}.

View file

@ -779,6 +779,9 @@ class WClass:
#if self.link_type != link_types.pointer: #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\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 + "));" text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
if self.link_type == link_types.pointer: if self.link_type == link_types.pointer:
text += "\n\t\t\tret->ref_obj = ref;" 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/wrapper.hpp>
#include <boost/python/call.hpp> #include <boost/python/call.hpp>
#include <boost/python.hpp> #include <boost/python.hpp>
#include <boost/log/exceptions.hpp>
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
@ -2060,7 +2062,6 @@ namespace YOSYS_PYTHON {
Yosys::log_streams.push_back(&std::cout); Yosys::log_streams.push_back(&std::cout);
Yosys::log_error_stderr = true; Yosys::log_error_stderr = true;
Yosys::yosys_setup(); Yosys::yosys_setup();
Yosys::yosys_banner();
} }
} }

View file

@ -23,7 +23,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
struct BlackboxPass : public Pass { 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 void help() YS_OVERRIDE
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|

View file

@ -51,14 +51,14 @@ struct BugpointPass : public Pass {
log(" only consider crashes that place this string in the log file.\n"); log(" only consider crashes that place this string in the log file.\n");
log("\n"); log("\n");
log(" -fast\n"); log(" -fast\n");
log(" run `clean -purge` after each minimization step. converges faster, but\n"); log(" run `proc_clean; clean -purge` after each minimization step. converges\n");
log(" produces larger testcases, and may fail to produce any testcase at all if\n"); log(" faster, but produces larger testcases, and may fail to produce any\n");
log(" the crash is related to dangling wires.\n"); log(" testcase at all if the crash is related to dangling wires.\n");
log("\n"); log("\n");
log(" -clean\n"); log(" -clean\n");
log(" run `clean -purge` before checking testcase and after finishing. produces\n"); log(" run `proc_clean; clean -purge` before checking testcase and after\n");
log(" smaller and more useful testcases, but may fail to produce any testcase\n"); log(" finishing. produces smaller and more useful testcases, but may fail to\n");
log(" at all if the crash is related to dangling wires.\n"); log(" produce any testcase at all if the crash is related to dangling wires.\n");
log("\n"); log("\n");
log(" -modules\n"); log(" -modules\n");
log(" try to remove modules.\n"); log(" try to remove modules.\n");
@ -72,6 +72,12 @@ struct BugpointPass : public Pass {
log(" -connections\n"); log(" -connections\n");
log(" try to reconnect ports to 'x.\n"); log(" try to reconnect ports to 'x.\n");
log("\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) 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; RTLIL::Design *design_copy = new RTLIL::Design;
for (auto &it : design->modules_) for (auto &it : design->modules_)
design_copy->add(it.second->clone()); design_copy->add(it.second->clone());
Pass::call(design_copy, "proc_clean -quiet");
Pass::call(design_copy, "clean -purge"); Pass::call(design_copy, "clean -purge");
if (do_delete) if (do_delete)
@ -117,7 +124,7 @@ struct BugpointPass : public Pass {
return design_copy; 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; RTLIL::Design *design_copy = new RTLIL::Design;
for (auto &it : design->modules_) 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; return NULL;
} }
@ -232,7 +292,7 @@ struct BugpointPass : public Pass {
{ {
string yosys_cmd = "yosys", script, grep; string yosys_cmd = "yosys", script, grep;
bool fast = false, clean = false; 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; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
@ -277,6 +337,16 @@ struct BugpointPass : public Pass {
has_part = true; has_part = true;
continue; continue;
} }
if (args[argidx] == "-assigns") {
assigns = true;
has_part = true;
continue;
}
if (args[argidx] == "-updates") {
updates = true;
has_part = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -290,6 +360,8 @@ struct BugpointPass : public Pass {
ports = true; ports = true;
cells = true; cells = true;
connections = true; connections = true;
assigns = true;
updates = true;
} }
if (!design->full_selection()) if (!design->full_selection())
@ -305,7 +377,7 @@ struct BugpointPass : public Pass {
bool found_something = false, stage2 = false; bool found_something = false, stage2 = false;
while (true) 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); simplified = clean_design(simplified, fast, /*do_delete=*/true);

View file

@ -393,46 +393,114 @@ struct SetundefPass : public Pass {
ffbits.insert(bit); ffbits.insert(bit);
} }
auto process_initwires = [&]()
{
dict<Wire*, int> wire_weights;
for (auto wire : initwires)
{
int weight = 0;
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++)
{
// prioritize wires that already have an init attribute
if (!ffbits.empty())
{
for (auto wire : module->wires()) for (auto wire : module->wires())
{ {
if (wire->name[0] == (wire_types ? '\\' : '$'))
continue;
if (!wire->attributes.count("\\init")) if (!wire->attributes.count("\\init"))
continue; continue;
for (auto bit : sigmap(wire)) Const &initval = wire->attributes["\\init"];
ffbits.erase(bit); initval.bits.resize(GetSize(wire), State::Sx);
if (initval.is_fully_undef()) {
wire->attributes.erase("\\init");
continue;
}
for (int i = 0; i < GetSize(wire); i++)
if (initval[i] != State::Sx)
ffbits.erase(sigmap(SigBit(wire, i)));
initwires.insert(wire); initwires.insert(wire);
} }
for (int wire_types = 0; wire_types < 2; wire_types++) process_initwires();
}
// next consider wires that completely contain bits to be initialized
if (!ffbits.empty())
{
for (auto wire : module->wires()) for (auto wire : module->wires())
{ {
if (wire->name[0] == (wire_types ? '\\' : '$')) if (wire->name[0] == (wire_types ? '\\' : '$'))
next_wire:
continue; continue;
for (auto bit : sigmap(wire)) for (auto bit : sigmap(wire))
if (!ffbits.count(bit)) if (!ffbits.count(bit))
goto next_wire; goto next_wire;
for (auto bit : sigmap(wire)) initwires.insert(wire);
ffbits.erase(bit);
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); initwires.insert(wire);
} }
for (auto wire : initwires) process_initwires();
{
Const &initval = wire->attributes["\\init"];
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();
} }
} }
log_assert(ffbits.empty());
}
module->rewrite_sigspecs(worker); module->rewrite_sigspecs(worker);
if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)

View file

@ -223,6 +223,33 @@ struct statdata_t
log("\n"); log("\n");
log(" Estimated number of LCs: %10d\n", lc_cnt); 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(" use cell area information from the provided liberty file\n");
log("\n"); log("\n");
log(" -tech <technology>\n"); log(" -tech <technology>\n");
log(" print area estemate for the specified technology. Corrently supported\n"); log(" print area estemate for the specified technology. Currently supported\n");
log(" calues for <technology>: xilinx\n"); log(" values for <technology>: xilinx, cmos\n");
log("\n"); log("\n");
log(" -width\n"); log(" -width\n");
log(" annotate internal cell types with their word 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); 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()); log_cmd_error("Unsupported technology: '%s'\n", techname.c_str());
for (auto mod : design->selected_modules()) for (auto mod : design->selected_modules())

View file

@ -52,7 +52,9 @@ struct TeePass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
std::vector<FILE*> backup_log_files, files_to_close; std::vector<FILE*> backup_log_files, files_to_close;
std::vector<std::ostream*> backup_log_streams;
int backup_log_verbose_level = log_verbose_level; int backup_log_verbose_level = log_verbose_level;
backup_log_streams = log_streams;
backup_log_files = log_files; backup_log_files = log_files;
size_t argidx; size_t argidx;
@ -60,6 +62,7 @@ struct TeePass : public Pass {
{ {
if (args[argidx] == "-q" && files_to_close.empty()) { if (args[argidx] == "-q" && files_to_close.empty()) {
log_files.clear(); log_files.clear();
log_streams.clear();
continue; continue;
} }
if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) { 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) for (auto cf : files_to_close)
fclose(cf); fclose(cf);
log_files = backup_log_files; log_files = backup_log_files;
log_streams = backup_log_streams;
throw; throw;
} }
@ -97,6 +101,7 @@ struct TeePass : public Pass {
log_verbose_level = backup_log_verbose_level; log_verbose_level = backup_log_verbose_level;
log_files = backup_log_files; log_files = backup_log_files;
log_streams = backup_log_streams;
} }
} TeePass; } TeePass;

View file

@ -62,7 +62,7 @@ struct WriteFileFrontend : public Frontend {
if (argidx < args.size() && args[argidx].rfind("-", 0) != 0) if (argidx < args.size() && args[argidx].rfind("-", 0) != 0)
output_filename = args[argidx++]; output_filename = args[argidx++];
else else
log_cmd_error("Missing putput filename.\n"); log_cmd_error("Missing output filename.\n");
extra_args(f, filename, args, argidx); extra_args(f, filename, args, argidx);

View file

@ -591,6 +591,9 @@ struct HierarchyPass : public Pass {
log(" module instances when the width does not match the module port. This\n"); log(" module instances when the width does not match the module port. This\n");
log(" option disables this behavior.\n"); log(" option disables this behavior.\n");
log("\n"); log("\n");
log(" -nodefaults\n");
log(" do not resolve input port default values\n");
log("\n");
log(" -nokeep_asserts\n"); log(" -nokeep_asserts\n");
log(" per default this pass sets the \"keep\" attribute on all modules\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"); 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 generate_mode = false;
bool keep_positionals = false; bool keep_positionals = false;
bool keep_portwidths = false; bool keep_portwidths = false;
bool nodefaults = false;
bool nokeep_asserts = false; bool nokeep_asserts = false;
std::vector<std::string> generate_cells; std::vector<std::string> generate_cells;
std::vector<generate_port_decl_t> generate_ports; std::vector<generate_port_decl_t> generate_ports;
@ -712,6 +716,10 @@ struct HierarchyPass : public Pass {
keep_portwidths = true; keep_portwidths = true;
continue; continue;
} }
if (args[argidx] == "-nodefaults") {
nodefaults = true;
continue;
}
if (args[argidx] == "-nokeep_asserts") { if (args[argidx] == "-nokeep_asserts") {
nokeep_asserts = true; nokeep_asserts = true;
continue; 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::set<Module*> blackbox_derivatives;
std::vector<Module*> design_modules = design->modules(); std::vector<Module*> design_modules = design->modules();

View file

@ -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(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(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)); 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 vector<portinfo_t> make_portinfos() const

View file

@ -17,6 +17,7 @@
* *
*/ */
#include <algorithm>
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
@ -182,20 +183,27 @@ struct MemoryDffWorker
if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
{ {
RTLIL::SigSpec en;
std::vector<RTLIL::SigSpec> check_q;
do {
bool enable_invert = mux_cells_a.count(sig_data) != 0; 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); 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")); check_q.push_back(sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")));
sig_data = sigmap(mux->getPort("\\Y")); 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));
for (auto bit : sig_data) for (auto bit : sig_data)
if (sigbit_users_count[bit] > 1) if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging; 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); disconnect_dff(sig_data);
cell->setPort("\\CLK", clk_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->setPort("\\DATA", sig_data);
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);

View file

@ -14,5 +14,6 @@ OBJS += passes/opt/opt_demorgan.o
OBJS += passes/opt/rmports.o OBJS += passes/opt/rmports.o
OBJS += passes/opt/opt_lut.o OBJS += passes/opt/opt_lut.o
OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/pmux2shiftx.o
OBJS += passes/opt/muxpack.o
endif endif

368
passes/opt/muxpack.cc Normal file
View file

@ -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

View file

@ -44,7 +44,7 @@ struct OptPass : public Pass {
log(" opt_muxtree\n"); log(" opt_muxtree\n");
log(" opt_reduce [-fine] [-full]\n"); log(" opt_reduce [-fine] [-full]\n");
log(" opt_merge [-share_all]\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_clean [-purge]\n");
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
log(" while <changed design>\n"); log(" while <changed design>\n");
@ -54,7 +54,7 @@ struct OptPass : public Pass {
log(" do\n"); log(" do\n");
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all]\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_clean [-purge]\n");
log(" while <changed design in opt_rmdff>\n"); log(" while <changed design in opt_rmdff>\n");
log("\n"); log("\n");
@ -112,6 +112,10 @@ struct OptPass : public Pass {
opt_rmdff_args += " -keepdc"; opt_rmdff_args += " -keepdc";
continue; continue;
} }
if (args[argidx] == "-sat") {
opt_rmdff_args += " -sat";
continue;
}
if (args[argidx] == "-share_all") { if (args[argidx] == "-share_all") {
opt_merge_args += " -share_all"; opt_merge_args += " -share_all";
continue; continue;

View file

@ -106,7 +106,7 @@ void rmunused_module_cells(Module *module, bool verbose)
if (raw_bit.wire == nullptr) if (raw_bit.wire == nullptr)
continue; continue;
auto bit = sigmap(raw_bit); 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 " 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.", "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))); 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()) { 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 // do not delete anything with "keep" or module ports or initialized wires
} else } else
if (!purge_mode && check_public_name(wire->name)) { 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 // do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
} else } else
if (!raw_used_signals.check_any(s1)) { if (!raw_used_signals.check_any(s1)) {
// delete wires that aren't used by anything directly // 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; std::vector<RTLIL::Cell*> delcells;
for (auto cell : module->cells()) 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(); bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool();
RTLIL::SigSpec a = cell->getPort("\\A"); RTLIL::SigSpec a = cell->getPort("\\A");
RTLIL::SigSpec y = cell->getPort("\\Y"); RTLIL::SigSpec y = cell->getPort("\\Y");

View file

@ -105,7 +105,7 @@ struct OptLutWorker
SigSpec lut_input = cell->getPort("\\A"); SigSpec lut_input = cell->getPort("\\A");
int lut_arity = 0; 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); luts.insert(cell);
// First, find all dedicated logic we're connected to. This results in an overapproximation // 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) 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_debug(" 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 input A[%d] not present.\n", dlogic_conn.first);
legal = false; legal = false;
break; break;
} }
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second))) 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_debug(" 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 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; legal = false;
break; break;
} }
@ -163,7 +163,7 @@ struct OptLutWorker
if (legal) 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); lut_legal_dlogics.insert(lut_dlogic);
for (auto &dlogic_conn : dlogic_map) for (auto &dlogic_conn : dlogic_map)
lut_dlogic_inputs.insert(dlogic_conn.first); lut_dlogic_inputs.insert(dlogic_conn.first);
@ -179,7 +179,7 @@ struct OptLutWorker
lut_arity++; 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_arity[cell] = lut_arity;
luts_dlogics[cell] = lut_legal_dlogics; luts_dlogics[cell] = lut_legal_dlogics;
luts_dlogic_inputs[cell] = lut_dlogic_inputs; luts_dlogic_inputs[cell] = lut_dlogic_inputs;
@ -239,28 +239,26 @@ struct OptLutWorker
if (const0_match || const1_match || input_match != -1) 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; SigBit value;
if (const0_match) if (const0_match)
{ {
log(" Cell evaluates constant 0.\n"); log_debug(" Cell evaluates constant 0.\n");
value = State::S0; value = State::S0;
} }
if (const1_match) if (const1_match)
{ {
log(" Cell evaluates constant 1.\n"); log_debug(" Cell evaluates constant 1.\n");
value = State::S1; value = State::S1;
} }
if (input_match != -1) { 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]; value = lut_inputs[input_match];
} }
if (lut_dlogic_inputs.size()) if (lut_dlogic_inputs.size())
{ log_debug(" Not eliminating cell (connected to dedicated logic).\n");
log(" Not eliminating cell (connected to dedicated logic).\n");
}
else else
{ {
SigSpec lut_output = lut->getPort("\\Y"); SigSpec lut_output = lut->getPort("\\Y");
@ -323,11 +321,11 @@ struct OptLutWorker
int lutB_arity = luts_arity[lutB]; int lutB_arity = luts_arity[lutB];
pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[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"))) 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; continue;
} }
@ -353,67 +351,51 @@ struct OptLutWorker
int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size(); int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size();
if (lutA_dlogic_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 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()) 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 else
log("Cell B is a %d-LUT.\n", lutB_arity); log_debug("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(" 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; const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B;
int combine_mask = 0; int combine_mask = 0;
if (lutM_arity > lutA_width) if (lutM_arity > lutA_width)
{ log_debug(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
}
else if (lutB_dlogic_inputs.size() > 0) else if (lutB_dlogic_inputs.size() > 0)
{ log_debug(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
}
else if (lutB->get_bool_attribute("\\lut_keep")) else if (lutB->get_bool_attribute("\\lut_keep"))
{ log_debug(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
}
else else
{
combine_mask |= COMBINE_A; combine_mask |= COMBINE_A;
}
if (lutM_arity > lutB_width) if (lutM_arity > lutB_width)
{ log_debug(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
}
else if (lutA_dlogic_inputs.size() > 0) else if (lutA_dlogic_inputs.size() > 0)
{ log_debug(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
}
else if (lutA->get_bool_attribute("\\lut_keep")) else if (lutA->get_bool_attribute("\\lut_keep"))
{ log_debug(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n");
log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n");
}
else else
{
combine_mask |= COMBINE_B; combine_mask |= COMBINE_B;
}
int combine = combine_mask; int combine = combine_mask;
if (combine == COMBINE_EITHER) if (combine == COMBINE_EITHER)
{ {
log(" Can combine into either cell.\n"); log_debug(" Can combine into either cell.\n");
if (lutA_arity == 1) 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; combine = COMBINE_B;
} }
else if (lutB_arity == 1) 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; combine = COMBINE_A;
} }
else else
{ {
log(" Arbitrarily combining into cell A.\n"); log_debug(" Arbitrarily combining into cell A.\n");
combine = COMBINE_A; combine = COMBINE_A;
} }
} }
@ -423,7 +405,7 @@ struct OptLutWorker
pool<int> lutM_dlogic_inputs; pool<int> lutM_dlogic_inputs;
if (combine == COMBINE_A) if (combine == COMBINE_A)
{ {
log(" Combining LUTs into cell A.\n"); log_debug(" Combining LUTs into cell A.\n");
lutM = lutA; lutM = lutA;
lutM_inputs = lutA_inputs; lutM_inputs = lutA_inputs;
lutM_dlogic_inputs = lutA_dlogic_inputs; lutM_dlogic_inputs = lutA_dlogic_inputs;
@ -432,7 +414,7 @@ struct OptLutWorker
} }
else if (combine == COMBINE_B) else if (combine == COMBINE_B)
{ {
log(" Combining LUTs into cell B.\n"); log_debug(" Combining LUTs into cell B.\n");
lutM = lutB; lutM = lutB;
lutM_inputs = lutB_inputs; lutM_inputs = lutB_inputs;
lutM_dlogic_inputs = lutB_dlogic_inputs; lutM_dlogic_inputs = lutB_dlogic_inputs;
@ -441,7 +423,7 @@ struct OptLutWorker
} }
else else
{ {
log(" Cannot combine LUTs.\n"); log_debug(" Cannot combine LUTs.\n");
continue; continue;
} }
@ -466,17 +448,17 @@ struct OptLutWorker
if (input_unused && lutR_unique.size()) if (input_unused && lutR_unique.size())
{ {
SigBit new_input = lutR_unique.pop(); 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); lutM_new_inputs.push_back(new_input);
} }
else if (sigmap(lutM_input[i]) == lutA_output) 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()); lutM_new_inputs.push_back(SigBit());
} }
else 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]); 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); 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_debug(" 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_debug(" 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(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str());
lutM->setParam("\\LUT", lutM_new_table); lutM->setParam("\\LUT", lutM_new_table);
lutM->setPort("\\A", lutM_new_inputs); lutM->setPort("\\A", lutM_new_inputs);

View file

@ -17,19 +17,24 @@
* *
*/ */
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/log.h" #include "kernel/log.h"
#include <stdlib.h> #include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/satgen.h"
#include "kernel/sigtools.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
SigMap assign_map, dff_init_map; SigMap assign_map, dff_init_map;
SigSet<RTLIL::Cell*> mux_drivers; SigSet<RTLIL::Cell*> mux_drivers;
dict<SigBit, RTLIL::Cell*> bit2driver;
dict<SigBit, pool<SigBit>> init_attributes; dict<SigBit, pool<SigBit>> init_attributes;
bool keepdc; bool keepdc;
bool sat;
void remove_init_attr(SigSpec sig) 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_q = dff->getPort("\\Q");
sig_c = dff->getPort("\\C"); sig_c = dff->getPort("\\C");
sig_e = dff->getPort("\\E"); sig_e = dff->getPort("\\E");
val_cp = RTLIL::Const(dff->type[6] == 'P', 1); val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
val_ep = RTLIL::Const(dff->type[7] == 'P', 1); val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
} }
else if (dff->type == "$ff") { else if (dff->type == "$ff") {
sig_d = dff->getPort("\\D"); sig_d = dff->getPort("\\D");
@ -452,12 +457,84 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
dff->unsetPort("\\E"); 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; return false;
delete_dff: delete_dff:
log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod)); log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
remove_init_attr(dff->getPort("\\Q")); remove_init_attr(dff->getPort("\\Q"));
mod->remove(dff); mod->remove(dff);
for (auto &entry : bit2driver)
if (entry.second == dff)
bit2driver.erase(entry.first);
return true; return true;
} }
@ -467,11 +544,15 @@ struct OptRmdffPass : public Pass {
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n"); log("\n");
log(" opt_rmdff [-keepdc] [selection]\n"); log(" opt_rmdff [-keepdc] [-sat] [selection]\n");
log("\n"); log("\n");
log("This pass identifies flip-flops with constant inputs and replaces them with\n"); log("This pass identifies flip-flops with constant inputs and replaces them with\n");
log("a constant driver.\n"); log("a constant driver.\n");
log("\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 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"); log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
keepdc = false; keepdc = false;
sat = false;
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) { for (argidx = 1; argidx < args.size(); argidx++) {
@ -486,18 +568,22 @@ struct OptRmdffPass : public Pass {
keepdc = true; keepdc = true;
continue; continue;
} }
if (args[argidx] == "-sat") {
sat = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
for (auto module : design->selected_modules()) for (auto module : design->selected_modules()) {
{
pool<SigBit> driven_bits; pool<SigBit> driven_bits;
dict<SigBit, State> init_bits; dict<SigBit, State> init_bits;
assign_map.set(module); assign_map.set(module);
dff_init_map.set(module); dff_init_map.set(module);
mux_drivers.clear(); mux_drivers.clear();
bit2driver.clear();
init_attributes.clear(); init_attributes.clear();
for (auto wire : module->wires()) for (auto wire : module->wires())
@ -522,17 +608,21 @@ struct OptRmdffPass : public Pass {
driven_bits.insert(bit); driven_bits.insert(bit);
} }
} }
mux_drivers.clear();
std::vector<RTLIL::IdString> dff_list; std::vector<RTLIL::IdString> dff_list;
std::vector<RTLIL::IdString> dffsr_list; std::vector<RTLIL::IdString> dffsr_list;
std::vector<RTLIL::IdString> dlatch_list; std::vector<RTLIL::IdString> dlatch_list;
for (auto cell : module->cells()) for (auto cell : module->cells())
{ {
for (auto &conn : cell->connections()) for (auto &conn : cell->connections()) {
if (cell->output(conn.first) || !cell->known()) bool is_output = cell->output(conn.first);
for (auto bit : assign_map(conn.second)) if (is_output || !cell->known())
for (auto bit : assign_map(conn.second)) {
if (is_output)
bit2driver[bit] = cell;
driven_bits.insert(bit); driven_bits.insert(bit);
}
}
if (cell->type == "$mux" || cell->type == "$pmux") { if (cell->type == "$mux" || cell->type == "$pmux") {
if (cell->getPort("\\A").size() == cell->getPort("\\B").size()) if (cell->getPort("\\A").size() == cell->getPort("\\B").size())
@ -604,6 +694,7 @@ struct OptRmdffPass : public Pass {
assign_map.clear(); assign_map.clear();
mux_drivers.clear(); mux_drivers.clear();
bit2driver.clear();
init_attributes.clear(); init_attributes.clear();
if (total_count || total_initdrv) if (total_count || total_initdrv)

View file

@ -221,6 +221,9 @@ struct Pmux2ShiftxPass : public Pass {
log(" select strategy for one-hot encoded control signals\n"); log(" select strategy for one-hot encoded control signals\n");
log(" default: pmux\n"); log(" default: pmux\n");
log("\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 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 optimize_onehot = true;
bool verbose = false; bool verbose = false;
bool verbose_onehot = false; bool verbose_onehot = false;
bool norange = false;
log_header(design, "Executing PMUX2SHIFTX pass.\n"); log_header(design, "Executing PMUX2SHIFTX pass.\n");
@ -270,6 +274,10 @@ struct Pmux2ShiftxPass : public Pass {
verbose_onehot = true; verbose_onehot = true;
continue; continue;
} }
if (args[argidx] == "-norange") {
norange = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -559,7 +567,7 @@ struct Pmux2ShiftxPass : public Pass {
int this_inv_delta = this_maxval - this_minval; int this_inv_delta = this_maxval - this_minval;
bool this_inv = false; bool this_inv = false;
if (this_delta != this_inv_delta) if (!norange && this_delta != this_inv_delta)
this_inv = this_inv_delta < this_delta; this_inv = this_inv_delta < this_delta;
else if (this_maxval != this_inv_maxval) else if (this_maxval != this_inv_maxval)
this_inv = this_inv_maxval < this_maxval; this_inv = this_inv_maxval < this_maxval;
@ -574,7 +582,7 @@ struct Pmux2ShiftxPass : public Pass {
if (best_src_col < 0) if (best_src_col < 0)
this_is_better = true; this_is_better = true;
else if (this_delta != best_delta) else if (!norange && this_delta != best_delta)
this_is_better = this_delta < best_delta; this_is_better = this_delta < best_delta;
else if (this_maxval != best_maxval) else if (this_maxval != best_maxval)
this_is_better = this_maxval < best_maxval; this_is_better = this_maxval < best_maxval;
@ -656,7 +664,7 @@ struct Pmux2ShiftxPass : public Pass {
// check density percentages // check density percentages
Const offset(State::S0, GetSize(sig)); 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)); offset = Const(min_choice, GetSize(sig));
log(" offset: %s\n", log_signal(offset)); log(" offset: %s\n", log_signal(offset));

View file

@ -1,5 +1,6 @@
OBJS += passes/proc/proc.o OBJS += passes/proc/proc.o
OBJS += passes/proc/proc_prune.o
OBJS += passes/proc/proc_clean.o OBJS += passes/proc/proc_clean.o
OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_rmdead.o
OBJS += passes/proc/proc_init.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_mux.o
OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dlatch.o
OBJS += passes/proc/proc_dff.o OBJS += passes/proc/proc_dff.o

View file

@ -37,6 +37,7 @@ struct ProcPass : public Pass {
log("\n"); log("\n");
log(" proc_clean\n"); log(" proc_clean\n");
log(" proc_rmdead\n"); log(" proc_rmdead\n");
log(" proc_prune\n");
log(" proc_init\n"); log(" proc_init\n");
log(" proc_arst\n"); log(" proc_arst\n");
log(" proc_mux\n"); log(" proc_mux\n");
@ -83,6 +84,7 @@ struct ProcPass : public Pass {
Pass::call(design, "proc_clean"); Pass::call(design, "proc_clean");
if (!ifxmode) if (!ifxmode)
Pass::call(design, "proc_rmdead"); Pass::call(design, "proc_rmdead");
Pass::call(design, "proc_prune");
Pass::call(design, "proc_init"); Pass::call(design, "proc_init");
if (global_arst.empty()) if (global_arst.empty())
Pass::call(design, "proc_arst"); Pass::call(design, "proc_arst");

View file

@ -172,7 +172,7 @@ restart_proc_arst:
sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0;
} }
for (auto &action : sync->actions) { 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()); RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size());
for (int i = 0; i < GetSize(rspec); i++) for (int i = 0; i < GetSize(rspec); i++)
if (rspec[i].wire == NULL) if (rspec[i].wire == NULL)

View file

@ -143,7 +143,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m
YOSYS_NAMESPACE_END YOSYS_NAMESPACE_END
PRIVATE_NAMESPACE_BEGIN 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; int count = 0;
bool did_something = true; bool did_something = true;
@ -160,7 +160,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
did_something = false; did_something = false;
proc_clean_case(&proc->root_case, did_something, count, -1); 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()); 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; total_count += count;
} }
@ -171,7 +171,10 @@ struct ProcCleanPass : public Pass {
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n"); 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("\n");
log("This pass removes empty parts of processes and ultimately removes a process\n"); log("This pass removes empty parts of processes and ultimately removes a process\n");
log("if it contains only empty structures.\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 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
int total_count = 0; int total_count = 0;
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"); 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()) { for (auto mod : design->modules()) {
std::vector<RTLIL::IdString> delme; std::vector<RTLIL::IdString> delme;
@ -191,9 +205,10 @@ struct ProcCleanPass : public Pass {
for (auto &proc_it : mod->processes) { for (auto &proc_it : mod->processes) {
if (!design->selected(mod, proc_it.second)) if (!design->selected(mod, proc_it.second))
continue; 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 && if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 &&
proc_it.second->root_case.actions.size() == 0) { proc_it.second->root_case.actions.size() == 0) {
if (!quiet)
log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str());
delme.push_back(proc_it.first); delme.push_back(proc_it.first);
} }
@ -204,6 +219,7 @@ struct ProcCleanPass : public Pass {
} }
} }
if (!quiet)
log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
} }
} ProcCleanPass; } ProcCleanPass;

View file

@ -26,21 +26,7 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc)
{
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)
{ {
bool found_init = false; bool found_init = false;
@ -53,9 +39,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
for (auto &action : sync->actions) for (auto &action : sync->actions)
{ {
RTLIL::SigSpec lhs = action.first; RTLIL::SigSpec lhs = action.first;
RTLIL::SigSpec rhs = action.second; RTLIL::SigSpec rhs = sigmap(action.second);
proc_get_const(rhs, proc->root_case);
if (!rhs.is_fully_const()) 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)); 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); extra_args(args, 1, design);
for (auto mod : design->modules()) for (auto mod : design->modules())
if (design->selected(mod)) if (design->selected(mod)) {
SigMap sigmap(mod);
for (auto &proc_it : mod->processes) for (auto &proc_it : mod->processes)
if (design->selected(mod, proc_it.second)) if (design->selected(mod, proc_it.second))
proc_init(mod, proc_it.second); proc_init(mod, sigmap, proc_it.second);
}
} }
} ProcInitPass; } ProcInitPass;

View file

@ -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; std::stringstream sstr;
sstr << "$procmux$" << (autoidx++); sstr << "$procmux$" << (autoidx++);
@ -173,7 +179,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
{ {
// create compare cell // create compare cell
RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); 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["\\A_SIGNED"] = RTLIL::Const(0);
eq_cell->parameters["\\B_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 // reduce cmp vector to one logic signal
RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or"); 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_SIGNED"] = RTLIL::Const(0);
any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width); 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); 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()); 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; return when_signal;
// compare results // 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) if (ctrl_sig.size() == 0)
return when_signal; return when_signal;
log_assert(ctrl_sig.size() == 1); 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 // create the multiplexer itself
RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux"); 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->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size());
mux_cell->setPort("\\A", else_signal); 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); 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(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); 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")) if (when_signal == last_mux_cell->getPort("\\A"))
return; 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); log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = "$pmux"; 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::CaseRule *cs2 = sw->cases[case_idx];
RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); 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]) 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 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);
} }
} }

158
passes/proc/proc_prune.cc Normal file
View file

@ -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

View file

@ -180,7 +180,7 @@ struct AssertpmuxWorker
}; };
struct AssertpmuxPass : public Pass { 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 void help() YS_OVERRIDE
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
@ -195,8 +195,8 @@ struct AssertpmuxPass : public Pass {
log("\n"); log("\n");
log(" -always\n"); log(" -always\n");
log(" usually the $pmux condition is only checked when the $pmux output\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(" is used by the mux tree it drives. this option will deactivate this\n");
log(" additional constrained and check the $pmux condition always.\n"); log(" additional constraint and check the $pmux condition always.\n");
log("\n"); log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE

View file

@ -253,6 +253,13 @@ struct Clk2fflogicPass : public Pass {
SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
Const rstval = cell->parameters["\\ARST_VALUE"]; 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()) if (cell->parameters["\\ARST_POLARITY"].as_bool())
module->addMux(NEW_ID, qval, rstval, arst, sig_q); module->addMux(NEW_ID, qval, rstval, arst, sig_q);
else else

View file

@ -24,7 +24,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
struct CutpointPass : public Pass { 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 void help() YS_OVERRIDE
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|

View file

@ -332,7 +332,7 @@ struct FmcombinePass : public Pass {
gate_cell = module->cell(gate_name); gate_cell = module->cell(gate_name);
if (gate_cell == nullptr) 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 else
{ {
@ -351,7 +351,7 @@ struct FmcombinePass : public Pass {
if (!gold_cell->parameters.empty()) if (!gold_cell->parameters.empty())
log_cmd_error("Gold cell has unresolved instance parameters.\n"); log_cmd_error("Gold cell has unresolved instance parameters.\n");
if (!gate_cell->parameters.empty()) 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); FmcombineWorker worker(design, gold_cell->type, opts);
worker.generate(); worker.generate();

View file

@ -659,6 +659,7 @@ struct SatHelper
void dump_model_to_vcd(std::string vcd_file_name) void dump_model_to_vcd(std::string vcd_file_name)
{ {
rewrite_filename(vcd_file_name);
FILE *f = fopen(vcd_file_name.c_str(), "w"); FILE *f = fopen(vcd_file_name.c_str(), "w");
if (!f) if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno)); 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) void dump_model_to_json(std::string json_file_name)
{ {
rewrite_filename(json_file_name);
FILE *f = fopen(json_file_name.c_str(), "w"); FILE *f = fopen(json_file_name.c_str(), "w");
if (!f) if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno)); 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()) if (!cnf_file_name.empty())
{ {
rewrite_filename(cnf_file_name);
FILE *f = fopen(cnf_file_name.c_str(), "w"); FILE *f = fopen(cnf_file_name.c_str(), "w");
if (!f) if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); 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()) if (!cnf_file_name.empty())
{ {
rewrite_filename(cnf_file_name);
FILE *f = fopen(cnf_file_name.c_str(), "w"); FILE *f = fopen(cnf_file_name.c_str(), "w");
if (!f) if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));

View file

@ -88,6 +88,8 @@ struct SimInstance
SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
shared(shared), module(module), instance(instance), parent(parent), sigmap(module) shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
{ {
log_assert(module);
if (parent) { if (parent) {
log_assert(parent->children.count(instance) == 0); log_assert(parent->children.count(instance) == 0);
parent->children[instance] = this; parent->children[instance] = this;
@ -848,6 +850,9 @@ struct SimPass : public Pass {
if (design->full_selection()) { if (design->full_selection()) {
top_mod = design->top_module(); 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 { } else {
auto mods = design->selected_whole_modules(); auto mods = design->selected_whole_modules();
if (GetSize(mods) != 1) if (GetSize(mods) != 1)

View file

@ -7,8 +7,10 @@ OBJS += passes/techmap/libparse.o
ifeq ($(ENABLE_ABC),1) ifeq ($(ENABLE_ABC),1)
OBJS += passes/techmap/abc.o OBJS += passes/techmap/abc.o
OBJS += passes/techmap/abc9.o
ifneq ($(ABCEXTERNAL),) ifneq ($(ABCEXTERNAL),)
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
endif endif
endif endif

View file

@ -1172,7 +1172,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
continue; continue;
} }
} }
else
cell_stats[RTLIL::unescape_id(c->type)]++; cell_stats[RTLIL::unescape_id(c->type)]++;
if (c->type == "\\_const0_" || c->type == "\\_const1_") { if (c->type == "\\_const0_" || c->type == "\\_const1_") {
@ -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("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("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("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("you want to use ABC to convert your design into another format.\n");
log("\n"); log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");

1258
passes/techmap/abc9.cc Normal file

File diff suppressed because it is too large Load diff

View file

@ -263,6 +263,25 @@ struct AttrmapPass : public Pass {
for (auto cell : module->selected_cells()) for (auto cell : module->selected_cells())
attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes); 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());
}
}
}
} }
} }
} }

View file

@ -174,8 +174,10 @@ struct ExtractFaWorker
SigSpec sig = root; SigSpec sig = root;
if (!ce.eval(sig)) if (!ce.eval(sig)) {
log_abort(); ce.pop();
return;
}
if (sig == State::S1) if (sig == State::S1)
func |= 1 << i; func |= 1 << i;
@ -214,8 +216,10 @@ struct ExtractFaWorker
SigSpec sig = root; SigSpec sig = root;
if (!ce.eval(sig)) if (!ce.eval(sig)) {
log_abort(); ce.pop();
return;
}
if (sig == State::S1) if (sig == State::S1)
func |= 1 << i; func |= 1 << i;

View file

@ -23,6 +23,7 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
#define COST_DMUX 90
#define COST_MUX2 100 #define COST_MUX2 100
#define COST_MUX4 220 #define COST_MUX4 220
#define COST_MUX8 460 #define COST_MUX8 460
@ -57,6 +58,13 @@ struct MuxcoverWorker
bool use_mux8; bool use_mux8;
bool use_mux16; bool use_mux16;
bool nodecode; 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) MuxcoverWorker(Module *module) : module(module), sigmap(module)
{ {
@ -64,9 +72,32 @@ struct MuxcoverWorker
use_mux8 = false; use_mux8 = false;
use_mux16 = false; use_mux16 = false;
nodecode = 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; 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() void treeify()
{ {
pool<SigBit> roots; pool<SigBit> roots;
@ -124,13 +155,22 @@ struct MuxcoverWorker
log(" Finished treeification: Found %d trees.\n", GetSize(tree_list)); 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 (*path) {
if (tree.muxes.count(bit) == 0) if (tree.muxes.count(bit) == 0) {
if (first_layer || nopartial)
return false; 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}; 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 { } else {
ret_bit = bit; ret_bit = bit;
return true; return true;
@ -139,7 +179,7 @@ struct MuxcoverWorker
int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit) int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit)
{ {
if (A == B) if (A == B || sel == State::Sx)
return 0; return 0;
tuple<SigBit, SigBit, SigBit> key(A, B, sel); tuple<SigBit, SigBit, SigBit> key(A, B, sel);
@ -157,7 +197,10 @@ struct MuxcoverWorker
if (std::get<2>(entry)) if (std::get<2>(entry))
return 0; 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) void implement_decode_mux(SigBit ctrl_bit)
@ -174,10 +217,33 @@ struct MuxcoverWorker
implement_decode_mux(std::get<0>(key)); implement_decode_mux(std::get<0>(key));
implement_decode_mux(std::get<1>(key)); implement_decode_mux(std::get<1>(key));
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); module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
std::get<2>(entry) = true;
decode_mux_counter++; decode_mux_counter++;
} }
std::get<2>(entry) = true;
}
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) int find_best_cover(tree_t &tree, SigBit bit)
{ {
@ -209,9 +275,13 @@ struct MuxcoverWorker
mux.inputs.push_back(B); mux.inputs.push_back(B);
mux.selects.push_back(S1); mux.selects.push_back(S1);
mux.cost += COST_MUX2; find_best_covers(tree, mux.inputs);
mux.cost += find_best_cover(tree, A); log_debug(" Decode cost for mux2 at %s: %d\n", log_signal(bit), mux.cost);
mux.cost += find_best_cover(tree, B);
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; best_mux = mux;
} }
@ -229,7 +299,7 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S2, tree, bit, "BS"); ok = ok && follow_muxtree(S2, tree, bit, "BS");
if (nodecode) if (nodecode)
ok = ok && S1 == S2; ok = ok && xcmp({S1, S2});
ok = ok && follow_muxtree(T1, tree, bit, "S"); ok = ok && follow_muxtree(T1, tree, bit, "S");
@ -247,13 +317,15 @@ struct MuxcoverWorker
mux.selects.push_back(S1); mux.selects.push_back(S1);
mux.selects.push_back(T1); mux.selects.push_back(T1);
mux.cost += COST_MUX4; find_best_covers(tree, mux.inputs);
mux.cost += find_best_cover(tree, A); log_debug(" Decode cost for mux4 at %s: %d\n", log_signal(bit), mux.cost);
mux.cost += find_best_cover(tree, B);
mux.cost += find_best_cover(tree, C);
mux.cost += find_best_cover(tree, D);
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; best_mux = mux;
} }
} }
@ -277,13 +349,13 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S4, tree, bit, "BBS"); ok = ok && follow_muxtree(S4, tree, bit, "BBS");
if (nodecode) 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(T1, tree, bit, "AS");
ok = ok && follow_muxtree(T2, tree, bit, "BS"); ok = ok && follow_muxtree(T2, tree, bit, "BS");
if (nodecode) if (nodecode)
ok = ok && T1 == T2; ok = ok && xcmp({T1, T2});
ok = ok && follow_muxtree(U1, tree, bit, "S"); ok = ok && follow_muxtree(U1, tree, bit, "S");
@ -310,17 +382,15 @@ struct MuxcoverWorker
mux.selects.push_back(T1); mux.selects.push_back(T1);
mux.selects.push_back(U1); mux.selects.push_back(U1);
mux.cost += COST_MUX8; find_best_covers(tree, mux.inputs);
mux.cost += find_best_cover(tree, A); log_debug(" Decode cost for mux8 at %s: %d\n", log_signal(bit), mux.cost);
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) 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; best_mux = mux;
} }
} }
@ -356,7 +426,7 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S8, tree, bit, "BBBS"); ok = ok && follow_muxtree(S8, tree, bit, "BBBS");
if (nodecode) 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(T1, tree, bit, "AAS");
ok = ok && follow_muxtree(T2, tree, bit, "ABS"); ok = ok && follow_muxtree(T2, tree, bit, "ABS");
@ -364,13 +434,13 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(T4, tree, bit, "BBS"); ok = ok && follow_muxtree(T4, tree, bit, "BBS");
if (nodecode) 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(U1, tree, bit, "AS");
ok = ok && follow_muxtree(U2, tree, bit, "BS"); ok = ok && follow_muxtree(U2, tree, bit, "BS");
if (nodecode) if (nodecode)
ok = ok && U1 == U2; ok = ok && xcmp({U1, U2});
ok = ok && follow_muxtree(V1, tree, bit, "S"); ok = ok && follow_muxtree(V1, tree, bit, "S");
@ -414,25 +484,15 @@ struct MuxcoverWorker
mux.selects.push_back(U1); mux.selects.push_back(U1);
mux.selects.push_back(V1); mux.selects.push_back(V1);
mux.cost += COST_MUX16; find_best_covers(tree, mux.inputs);
mux.cost += find_best_cover(tree, A); log_debug(" Decode cost for mux16 at %s: %d\n", log_signal(bit), mux.cost);
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) 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; best_mux = mux;
} }
} }
@ -528,6 +588,7 @@ struct MuxcoverWorker
void treecover(tree_t &tree) void treecover(tree_t &tree)
{ {
int count_muxes_by_type[4] = {0, 0, 0, 0}; 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); find_best_cover(tree, tree.root);
implement_best_cover(tree, tree.root, count_muxes_by_type); 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), 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"); 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) { for (auto &tree : tree_list) {
find_best_cover(tree, tree.root); find_best_cover(tree, tree.root);
tree.newmuxes.clear(); tree.newmuxes.clear();
} }
}
for (auto &tree : tree_list) for (auto &tree : tree_list)
treecover(tree); treecover(tree);
@ -569,15 +631,30 @@ struct MuxcoverPass : public Pass {
log("\n"); log("\n");
log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n"); log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n");
log("\n"); log("\n");
log(" -mux4, -mux8, -mux16\n"); log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n");
log(" Use the specified types of MUXes. If none of those options are used,\n"); log(" Cover $_MUX_ trees using the specified types of MUXes (with optional\n");
log(" the effect is the same as if all of them where used.\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("\n");
log(" -nodecode\n"); log(" -nodecode\n");
log(" Do not insert decoder logic. This reduces the number of possible\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(" substitutions, but guarantees that the resulting circuit is not\n");
log(" less efficient than the original circuit.\n"); log(" less efficient than the original circuit.\n");
log("\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 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_mux8 = false;
bool use_mux16 = false; bool use_mux16 = false;
bool nodecode = 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; size_t argidx;
for (argidx = 1; argidx < args.size(); 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; use_mux4 = true;
if (arg.size() > 5) {
if (arg[5] != '=') break;
cost_mux4 = std::stoi(arg.substr(6));
}
continue; continue;
} }
if (args[argidx] == "-mux8") { if (arg.size() >= 5 && arg.substr(0,5) == "-mux8") {
use_mux8 = true; use_mux8 = true;
if (arg.size() > 5) {
if (arg[5] != '=') break;
cost_mux8 = std::stoi(arg.substr(6));
}
continue; continue;
} }
if (args[argidx] == "-mux16") { if (arg.size() >= 6 && arg.substr(0,6) == "-mux16") {
use_mux16 = true; use_mux16 = true;
if (arg.size() > 6) {
if (arg[6] != '=') break;
cost_mux16 = std::stoi(arg.substr(7));
}
continue; continue;
} }
if (args[argidx] == "-nodecode") { if (arg.size() >= 6 && arg.substr(0,6) == "-dmux=") {
cost_dmux = std::stoi(arg.substr(6));
continue;
}
if (arg == "-nodecode") {
nodecode = true; nodecode = true;
continue; continue;
} }
if (arg == "-nopartial") {
nopartial = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -623,7 +731,13 @@ struct MuxcoverPass : public Pass {
worker.use_mux4 = use_mux4; worker.use_mux4 = use_mux4;
worker.use_mux8 = use_mux8; worker.use_mux8 = use_mux8;
worker.use_mux16 = use_mux16; 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.nodecode = nodecode;
worker.nopartial = nopartial;
worker.run(); worker.run();
} }
} }

View file

@ -293,10 +293,22 @@ struct ShregmapWorker
if (opts.init || sigbit_init.count(q_bit) == 0) 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); sigbit_with_non_chain_users.insert(d_bit);
} else // ... and clone d_bit into another wire, and use that
sigbit_chain_next[d_bit] = cell; // 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; sigbit_chain_prev[q_bit] = cell;
continue; continue;
@ -605,6 +617,11 @@ struct ShregmapPass : public Pass {
log("\n"); log("\n");
log(" -tech greenpak4\n"); log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\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"); log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE

View file

@ -649,10 +649,13 @@ struct TechmapWorker
unique_bit_id[bit] = unique_bit_id_counter++; unique_bit_id[bit] = unique_bit_id_counter++;
} }
// Find highest bit set
int bits = 0; int bits = 0;
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
if (((unique_bit_id_counter-1) & (1 << i)) != 0) if (((unique_bit_id_counter-1) & (1 << i)) != 0)
bits = i; bits = i;
// Increment index by one to get number of bits
bits++;
if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_")) if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_"))
parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits; parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits;

View file

@ -27,7 +27,7 @@ parameter _TECHMAP_CONSTVAL_A_ = 0;
parameter _TECHMAP_CONSTMSK_B_ = 0; parameter _TECHMAP_CONSTMSK_B_ = 0;
parameter _TECHMAP_CONSTVAL_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 width;
input integer operation; input integer operation;
input integer swap; input integer swap;

View file

@ -75,13 +75,16 @@ struct SynthPass : public ScriptPass
log(" from label is synonymous to 'begin', and empty to label is\n"); log(" from label is synonymous to 'begin', and empty to label is\n");
log(" synonymous to the end of the command list.\n"); log(" synonymous to the end of the command list.\n");
log("\n"); log("\n");
log(" -abc9\n");
log(" use new ABC9 flow (EXPERIMENTAL)\n");
log("\n");
log("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
help_script(); help_script();
log("\n"); log("\n");
} }
string top_module, fsm_opts, memory_opts; string top_module, fsm_opts, memory_opts, abc;
bool autotop, flatten, noalumacc, nofsm, noabc, noshare; bool autotop, flatten, noalumacc, nofsm, noabc, noshare;
int lut; int lut;
@ -98,6 +101,7 @@ struct SynthPass : public ScriptPass
nofsm = false; nofsm = false;
noabc = false; noabc = false;
noshare = false; noshare = false;
abc = "abc";
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@ -159,6 +163,10 @@ struct SynthPass : public ScriptPass
noshare = true; noshare = true;
continue; continue;
} }
if (args[argidx] == "-abc9") {
abc = "abc9";
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -166,6 +174,9 @@ struct SynthPass : public ScriptPass
if (!design->full_selection()) if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n"); 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_header(design, "Executing SYNTH pass.\n");
log_push(); log_push();
@ -241,15 +252,15 @@ struct SynthPass : public ScriptPass
#ifdef YOSYS_ENABLE_ABC #ifdef YOSYS_ENABLE_ABC
if (help_mode) if (help_mode)
{ {
run("abc -fast", " (unless -noabc, unless -lut)"); run(abc + " -fast", " (unless -noabc, unless -lut)");
run("abc -fast -lut k", "(unless -noabc, if -lut)"); run(abc + " -fast -lut k", "(unless -noabc, if -lut)");
} }
else else
{ {
if (lut) if (lut)
run(stringf("abc -fast -lut %d", lut)); run(stringf("%s -fast -lut %d", abc.c_str(), lut));
else else
run("abc -fast"); run(abc + " -fast");
} }
run("opt -fast", " (unless -noabc)"); run("opt -fast", " (unless -noabc)");
#endif #endif

View file

@ -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_map.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_sim.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/cells_bb.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/drams_map.v)) $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutrams_map.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dram.txt)) $(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/brams_map.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) $(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/arith_map.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_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 EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
.SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk .SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk

43
techlibs/ecp5/abc_5g.box Normal file
View file

@ -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

25
techlibs/ecp5/abc_5g.lut Normal file
View file

@ -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

View file

@ -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

View file

@ -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] AA = A_buf;
wire [Y_WIDTH2-1:0] BB = BI ? ~B_buf : B_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] C = {CO, CI};
wire [Y_WIDTH2-1:0] FCO, Y1; wire [Y_WIDTH2-1:0] FCO, Y1;
genvar i; genvar i;
generate for (i = 0; i < Y_WIDTH2; i = i + 2) begin:slice generate for (i = 0; i < Y_WIDTH2; i = i + 2) begin:slice
CCU2C #( CCU2C #(
.INIT0(16'b0110011010101010), .INIT0(16'b1001011010101010),
.INIT1(16'b0110011010101010), .INIT1(16'b1001011010101010),
.INJECT1_0("NO"), .INJECT1_0("NO"),
.INJECT1_1("NO") .INJECT1_1("NO")
) ccu2c_i ( ) ccu2c_i (
.CIN(C[i]), .CIN(C[i]),
.A0(AA[i]), .B0(BB[i]), .C0(1'b0), .D0(1'b1), .A0(AA[i]), .B0(BX[i]), .C0(BI), .D0(1'b1),
.A1(AA[i+1]), .B1(BB[i+1]), .C1(1'b0), .D1(1'b1), .A1(AA[i+1]), .B1(BX[i+1]), .C1(BI), .D1(1'b1),
.S0(Y[i]), .S1(Y1[i]), .S0(Y[i]), .S1(Y1[i]),
.COUT(FCO[i]) .COUT(FCO[i])
); );

View file

@ -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_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 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 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 `ifndef NO_LUT
module \$lut (A, Y); module \$lut (A, Y);
@ -58,77 +109,102 @@ module \$lut (A, Y);
input [WIDTH-1:0] A; input [WIDTH-1:0] A;
output Y; 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 generate
if (WIDTH == 1) begin if (WIDTH == 1) begin
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
.A(A[0]), .B(1'b0), .C(1'b0), .D(1'b0)); .A(1'b0), .B(1'b0), .C(1'b0), .D(A[0]));
end else end else
if (WIDTH == 2) begin if (WIDTH == 2) begin
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
.A(A[0]), .B(A[1]), .C(1'b0), .D(1'b0)); .A(1'b0), .B(1'b0), .C(A[1]), .D(A[0]));
end else end else
if (WIDTH == 3) begin if (WIDTH == 3) begin
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
.A(A[0]), .B(A[1]), .C(A[2]), .D(1'b0)); .A(1'b0), .B(A[2]), .C(A[1]), .D(A[0]));
end else end else
if (WIDTH == 4) begin if (WIDTH == 4) begin
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[3]), .B(A[2]), .C(A[1]), .D(A[0]));
`ifndef NO_PFUMUX `ifndef NO_PFUMUX
end else end else
if (WIDTH == 5) begin if (WIDTH == 5) begin
wire f0, f1; wire f0, f1;
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y)); PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y));
end else end else
if (WIDTH == 6) begin if (WIDTH == 6) begin
wire f0, f1, f2, f3, g0, g1; wire f0, f1, f2, f3, g0, g1;
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0));
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1));
L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y)); L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y));
end else end else
if (WIDTH == 7) begin if (WIDTH == 7) begin
wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1; wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1;
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4), LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5), LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6), LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7), LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7),
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0));
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1));
PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2)); PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2));
PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3)); PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3));
L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0)); L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0));
L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1)); L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1));
L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y)); L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y));
`endif `endif
end else begin end else begin
wire _TECHMAP_FAIL_ = 1; wire _TECHMAP_FAIL_ = 1;

View file

@ -9,13 +9,13 @@ module LUT4(input A, B, C, D, output Z);
endmodule endmodule
// --------------------------------------- // ---------------------------------------
(* abc_box_id=4, lib_whitebox *)
module L6MUX21 (input D0, D1, SD, output Z); module L6MUX21 (input D0, D1, SD, output Z);
assign Z = SD ? D1 : D0; assign Z = SD ? D1 : D0;
endmodule endmodule
// --------------------------------------- // ---------------------------------------
(* abc_box_id=1, abc_carry="CIN,COUT", lib_whitebox *)
module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1, module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1,
output S0, S1, COUT); output S0, S1, COUT);
@ -26,9 +26,13 @@ module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1,
// First half // First half
wire LUT4_0, LUT2_0; 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)); 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)); 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; wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN;
assign S0 = LUT4_0 ^ gated_cin_0; 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 // Second half
wire LUT4_1, LUT2_1; 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)); 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)); 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; wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0;
assign S1 = LUT4_1 ^ gated_cin_1; assign S1 = LUT4_1 ^ gated_cin_1;
@ -90,13 +98,13 @@ module TRELLIS_RAM16X2 (
endmodule endmodule
// --------------------------------------- // ---------------------------------------
(* abc_box_id=3, lib_whitebox *)
module PFUMX (input ALUT, BLUT, C0, output Z); module PFUMX (input ALUT, BLUT, C0, output Z);
assign Z = C0 ? ALUT : BLUT; assign Z = C0 ? ALUT : BLUT;
endmodule endmodule
// --------------------------------------- // ---------------------------------------
//(* abc_box_id=2, abc_scc_break="DI,WAD,WRE" *)
module TRELLIS_DPR16X4 ( module TRELLIS_DPR16X4 (
input [3:0] DI, input [3:0] DI,
input [3:0] WAD, input [3:0] WAD,
@ -250,18 +258,6 @@ module TRELLIS_FF(input CLK, LSR, CE, DI, M, output reg Q);
endgenerate endgenerate
endmodule 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 *) (* keep *)
module TRELLIS_IO( module TRELLIS_IO(
@ -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); module INV(input A, output Z);
assign Z = !A; assign Z = !A;
endmodule endmodule
@ -558,19 +541,56 @@ module DP16KD(
parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
endmodule endmodule
// For Diamond compatibility, FIXME: add all Diamond flipflop mappings // TODO: Diamond flip-flops
module FD1S3BX(input PD, D, CK, output Q); // module FD1P3AX(); endmodule
TRELLIS_FF #( // module FD1P3AY(); endmodule
.GSR("DISABLED"), // module FD1P3BX(); endmodule
.CEMUX("1"), // module FD1P3DX(); endmodule
.CLKMUX("CLK"), // module FD1P3IX(); endmodule
.LSRMUX("LSR"), // module FD1P3JX(); endmodule
.REGSET("SET"), // module FD1S3AX(); endmodule
.SRMODE("ASYNC") // module FD1S3AY(); endmodule
) tff_i ( 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
.CLK(CK), 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
.LSR(PD), 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
.DI(D), 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
.Q(Q) // module FL1P3AY(); endmodule
); // module FL1P3AZ(); endmodule
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

View file

@ -2,7 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite * yosys -- Yosys Open SYnthesis Suite
* *
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * 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 * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * 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(" do not use flipflops with CE in output netlist\n");
log("\n"); log("\n");
log(" -nobram\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("\n");
log(" -nodram\n"); log(" -nolutram\n");
log(" do not use distributed RAM cells in output netlist\n"); log(" do not use LUT RAM cells in output netlist\n");
log("\n"); log("\n");
log(" -nomux\n"); log(" -nowidelut\n");
log(" do not use PFU muxes to implement LUTs larger than LUT4s\n"); log(" do not use PFU muxes to implement LUTs larger than LUT4s\n");
log("\n"); log("\n");
log(" -abc2\n"); log(" -abc2\n");
log(" run two passes of 'abc' for slightly improved logic density\n"); log(" run two passes of 'abc' for slightly improved logic density\n");
log("\n"); log("\n");
log(" -abc9\n");
log(" use new ABC9 flow (EXPERIMENTAL)\n");
log("\n");
log(" -vpr\n"); log(" -vpr\n");
log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" generate an output netlist (and BLIF file) suitable for VPR\n");
log(" (this feature is experimental and incomplete)\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; 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 void clear_flags() YS_OVERRIDE
{ {
@ -104,12 +107,13 @@ struct SynthEcp5Pass : public ScriptPass
noccu2 = false; noccu2 = false;
nodffe = false; nodffe = false;
nobram = false; nobram = false;
nodram = false; nolutram = false;
nomux = false; nowidelut = false;
flatten = true; flatten = true;
retime = false; retime = false;
abc2 = false; abc2 = false;
vpr = false; vpr = false;
abc9 = false;
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@ -168,12 +172,12 @@ struct SynthEcp5Pass : public ScriptPass
nobram = true; nobram = true;
continue; continue;
} }
if (args[argidx] == "-nodram") { if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") {
nodram = true; nolutram = true;
continue; continue;
} }
if (args[argidx] == "-nomux") { if (args[argidx] == "-nowidelut" || /*deprecated alias*/ args[argidx] == "-nomux") {
nomux = true; nowidelut = true;
continue; continue;
} }
if (args[argidx] == "-abc2") { if (args[argidx] == "-abc2") {
@ -184,6 +188,10 @@ struct SynthEcp5Pass : public ScriptPass
vpr = true; vpr = true;
continue; continue;
} }
if (args[argidx] == "-abc9") {
abc9 = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -191,6 +199,9 @@ struct SynthEcp5Pass : public ScriptPass
if (!design->full_selection()) if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n"); 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_header(design, "Executing SYNTH_ECP5 pass.\n");
log_push(); log_push();
@ -203,7 +214,7 @@ struct SynthEcp5Pass : public ScriptPass
{ {
if (check_label("begin")) 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())); 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"); 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("memory_bram -rules +/ecp5/bram.txt");
run("techmap -map +/ecp5/brams_map.v"); 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("memory_bram -rules +/ecp5/lutram.txt");
run("techmap -map +/ecp5/drams_map.v"); run("techmap -map +/ecp5/lutrams_map.v");
} }
if (check_label("fine")) if (check_label("map_ffram"))
{ {
run("opt -fast -mux_undef -undriven -fine"); run("opt -fast -mux_undef -undriven -fine");
run("memory_map"); run("memory_map");
run("opt -undriven -fine"); run("opt -undriven -fine");
}
if (check_label("map_gates"))
{
if (noccu2) if (noccu2)
run("techmap"); run("techmap");
else else
@ -264,10 +279,17 @@ struct SynthEcp5Pass : public ScriptPass
run("abc", " (only if -abc2)"); run("abc", " (only if -abc2)");
} }
run("techmap -map +/ecp5/latches_map.v"); run("techmap -map +/ecp5/latches_map.v");
if (nomux) 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"); run("abc -lut 4 -dress");
else else
run("abc -lut 4:7 -dress"); run("abc -lut 4:7 -dress");
}
run("clean"); run("clean");
} }

View file

@ -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/latches_map.v))
$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt)) $(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/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_init1.vh))
$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh)) $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh))

13
techlibs/ice40/abc_hx.box Normal file
View file

@ -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

View file

@ -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

13
techlibs/ice40/abc_lp.box Normal file
View file

@ -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

View file

@ -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

13
techlibs/ice40/abc_u.box Normal file
View file

@ -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

6
techlibs/ice40/abc_u.lut Normal file
View file

@ -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

View file

@ -44,6 +44,15 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
genvar i; genvar i;
generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice 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 ( SB_CARRY carry (
.I0(AA[i]), .I0(AA[i]),
.I1(BB[i]), .I1(BB[i]),
@ -63,6 +72,7 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
.I3(C[i]), .I3(C[i]),
.O(Y[i]) .O(Y[i])
); );
`endif
end endgenerate end endgenerate
assign X = AA ^ BB; assign X = AA ^ BB;

View file

@ -37,23 +37,51 @@ module \$lut (A, Y);
generate generate
if (WIDTH == 1) begin if (WIDTH == 1) begin
SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}};
.I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0)); SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0]));
end else end else
if (WIDTH == 2) begin if (WIDTH == 2) begin
SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}};
.I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0)); SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0]));
end else end else
if (WIDTH == 3) begin if (WIDTH == 3) begin
SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), 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]}}};
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0)); SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0]));
end else end else
if (WIDTH == 4) begin if (WIDTH == 4) begin
SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), 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]};
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3])); SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0]));
end else begin end else begin
wire _TECHMAP_FAIL_ = 1; wire _TECHMAP_FAIL_ = 1;
end end
endgenerate endgenerate
endmodule endmodule
`endif `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

View file

@ -127,6 +127,7 @@ endmodule
// SiliconBlue Logic Cells // SiliconBlue Logic Cells
(* lib_whitebox *)
module SB_LUT4 (output O, input I0, I1, I2, I3); module SB_LUT4 (output O, input I0, I1, I2, I3);
parameter [15:0] LUT_INIT = 0; parameter [15:0] LUT_INIT = 0;
wire [7:0] s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7: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]; assign O = I0 ? s1[1] : s1[0];
endmodule endmodule
(* lib_whitebox *)
module SB_CARRY (output CO, input I0, I1, CI); module SB_CARRY (output CO, input I0, I1, CI);
assign CO = (I0 && I1) || ((I0 || I1) && CI); assign CO = (I0 && I1) || ((I0 || I1) && CI);
endmodule 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 // Positive Edge SiliconBlue FF Cells
module SB_DFF (output `SB_DFF_REG, input C, D); module SB_DFF (output `SB_DFF_REG, input C, D);
@ -973,6 +998,30 @@ parameter RGB1_CURRENT = "0b000000";
parameter RGB2_CURRENT = "0b000000"; parameter RGB2_CURRENT = "0b000000";
endmodule 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 *) (* blackbox *)
module SB_I2C( module SB_I2C(
input SBCLKI, input SBCLKI,
@ -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] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl;
wire [15:0] Ah, Al, Bh, Bl; wire [15:0] Ah, Al, Bh, Bl;
assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]}; 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 Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]};
assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; assign Bl = {B_SIGNED && MODE_8x8 ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]};
assign p_Ah_Bh = Ah * Bh; assign p_Ah_Bh = Ah * Bh; // F
assign p_Al_Bh = Al * Bh; assign p_Al_Bh = {8'b0, Al[7:0]} * Bh; // J
assign p_Ah_Bl = Ah * Bl; assign p_Ah_Bl = Ah * {8'b0, Bl[7:0]}; // K
assign p_Al_Bl = Al * Bl; assign p_Al_Bl = Al * Bl; // G
// Regs F and J // Regs F and J
reg [15:0] rF, rJ; reg [15:0] rF, rJ;
@ -1351,7 +1400,9 @@ module SB_MAC16 (
assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl; assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl;
// Adder Stage // 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 H
reg [31:0] rH; reg [31:0] rH;

View file

@ -83,6 +83,51 @@ static void run_ice40_opts(Module *module)
} }
continue; 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) for (auto cell : sb_lut_cells)

View file

@ -56,10 +56,10 @@ static void run_ice40_unlut(Module *module)
cell->unsetParam("\\LUT_INIT"); cell->unsetParam("\\LUT_INIT");
cell->setPort("\\A", SigSpec({ cell->setPort("\\A", SigSpec({
get_bit_or_zero(cell->getPort("\\I3")), get_bit_or_zero(cell->getPort("\\I0")),
get_bit_or_zero(cell->getPort("\\I2")),
get_bit_or_zero(cell->getPort("\\I1")), 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->setPort("\\Y", cell->getPort("\\O")[0]);
cell->unsetPort("\\I0"); cell->unsetPort("\\I0");
@ -74,7 +74,7 @@ static void run_ice40_unlut(Module *module)
} }
struct Ice40UnlutPass : public Pass { 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 void help() YS_OVERRIDE
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|

View file

@ -37,6 +37,10 @@ struct SynthIce40Pass : public ScriptPass
log("\n"); log("\n");
log("This command runs synthesis for iCE40 FPGAs.\n"); log("This command runs synthesis for iCE40 FPGAs.\n");
log("\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(" -top <module>\n");
log(" use the specified module as top module\n"); log(" use the specified module as top module\n");
log("\n"); log("\n");
@ -63,9 +67,6 @@ struct SynthIce40Pass : public ScriptPass
log(" -retime\n"); log(" -retime\n");
log(" run 'abc' with -dff option\n"); log(" run 'abc' with -dff option\n");
log("\n"); log("\n");
log(" -relut\n");
log(" combine LUTs after synthesis\n");
log("\n");
log(" -nocarry\n"); log(" -nocarry\n");
log(" do not use SB_CARRY cells in output netlist\n"); log(" do not use SB_CARRY cells in output netlist\n");
log("\n"); log("\n");
@ -92,14 +93,17 @@ struct SynthIce40Pass : public ScriptPass
log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" generate an output netlist (and BLIF file) suitable for VPR\n");
log(" (this feature is experimental and incomplete)\n"); log(" (this feature is experimental and incomplete)\n");
log("\n"); log("\n");
log(" -abc9\n");
log(" use new ABC9 flow (EXPERIMENTAL)\n");
log("\n");
log("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
help_script(); help_script();
log("\n"); log("\n");
} }
string top_opt, blif_file, edif_file, json_file; string top_opt, blif_file, edif_file, json_file, abc, device_opt;
bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr; bool nocarry, nodffe, nobram, dsp, flatten, retime, noabc, abc2, vpr;
int min_ce_use; int min_ce_use;
void clear_flags() YS_OVERRIDE void clear_flags() YS_OVERRIDE
@ -115,10 +119,11 @@ struct SynthIce40Pass : public ScriptPass
dsp = false; dsp = false;
flatten = true; flatten = true;
retime = false; retime = false;
relut = false;
noabc = false; noabc = false;
abc2 = false; abc2 = false;
vpr = false; vpr = false;
abc = "abc";
device_opt = "hx";
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@ -166,7 +171,7 @@ struct SynthIce40Pass : public ScriptPass
continue; continue;
} }
if (args[argidx] == "-relut") { if (args[argidx] == "-relut") {
relut = true; // removed, opt_lut is always run
continue; continue;
} }
if (args[argidx] == "-nocarry") { if (args[argidx] == "-nocarry") {
@ -201,12 +206,25 @@ struct SynthIce40Pass : public ScriptPass
vpr = true; vpr = true;
continue; continue;
} }
if (args[argidx] == "-abc9") {
abc = "abc9";
continue;
}
if (args[argidx] == "-device" && argidx+1 < args.size()) {
device_opt = args[++argidx];
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
if (!design->full_selection()) if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n"); 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_header(design, "Executing SYNTH_ICE40 pass.\n");
log_push(); log_push();
@ -220,7 +238,7 @@ struct SynthIce40Pass : public ScriptPass
{ {
if (check_label("begin")) 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(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
run("proc"); run("proc");
} }
@ -257,14 +275,14 @@ struct SynthIce40Pass : public ScriptPass
run("opt_clean"); 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("memory_bram -rules +/ice40/brams.txt");
run("techmap -map +/ice40/brams_map.v"); run("techmap -map +/ice40/brams_map.v");
run("ice40_braminit"); run("ice40_braminit");
} }
if (check_label("map")) if (check_label("map_ffram"))
{ {
run("opt -fast -mux_undef -undriven -fine"); run("opt -fast -mux_undef -undriven -fine");
run("memory_map"); run("memory_map");
@ -276,9 +294,9 @@ struct SynthIce40Pass : public ScriptPass
if (nocarry) if (nocarry)
run("techmap"); run("techmap");
else 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) if (retime || help_mode)
run("abc -dff", "(only if -retime)"); run(abc + " -dff", "(only if -retime)");
run("ice40_opt"); run("ice40_opt");
} }
@ -302,7 +320,7 @@ struct SynthIce40Pass : public ScriptPass
if (check_label("map_luts")) if (check_label("map_luts"))
{ {
if (abc2 || help_mode) { if (abc2 || help_mode) {
run("abc", " (only if -abc2)"); run(abc, " (only if -abc2)");
run("ice40_opt", "(only if -abc2)"); run("ice40_opt", "(only if -abc2)");
} }
run("techmap -map +/ice40/latches_map.v"); 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)"); run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)");
} }
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"); run("clean");
if (relut || help_mode) { run("ice40_unlut");
run("ice40_unlut", " (only if -relut)"); run("opt_lut -dlogic SB_CARRY:I0=2:I1=1:CI=0");
run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3", "(only if -relut)");
}
} }
if (check_label("map_cells")) if (check_label("map_cells"))

View file

@ -1,10 +1,15 @@
#!/bin/bash #!/bin/bash
set -ex set -ex
sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.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 cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v
fi
for tb in testbench \ for tb in testbench \
testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \ 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 do
iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v 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 vvp -N ./test_dsp_model

View file

@ -241,6 +241,81 @@ module testbench_comb_8x8_A;
) testbench (); ) testbench ();
endmodule 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; module testbench_comb_8x8_B;
testbench #( testbench #(
.NEG_TRIGGER (0), .NEG_TRIGGER (0),
@ -266,6 +341,81 @@ module testbench_comb_8x8_B;
) testbench (); ) testbench ();
endmodule 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; module testbench_comb_16x16;
testbench #( testbench #(
.NEG_TRIGGER (0), .NEG_TRIGGER (0),
@ -291,6 +441,81 @@ module testbench_comb_16x16;
) testbench (); ) testbench ();
endmodule 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; module testbench_seq_16x16_A;
testbench #( testbench #(
.NEG_TRIGGER (0), .NEG_TRIGGER (0),

Some files were not shown because too many files have changed in this diff Show more