mirror of
https://github.com/YosysHQ/yosys
synced 2025-07-03 11:25:41 +00:00
Merge branch 'YosysHQ:master' into issue/2616
This commit is contained in:
commit
f9f7a4e9db
35 changed files with 1399 additions and 203 deletions
15
CHANGELOG
15
CHANGELOG
|
@ -2,9 +2,22 @@
|
||||||
List of major changes and improvements between releases
|
List of major changes and improvements between releases
|
||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
Yosys 0.29 .. Yosys 0.29-dev
|
Yosys 0.30 .. Yosys 0.31-dev
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
Yosys 0.29 .. Yosys 0.30
|
||||||
|
--------------------------
|
||||||
|
* New commands and options
|
||||||
|
- Added "recover_names" pass to recover names post-mapping.
|
||||||
|
|
||||||
|
* Gowin support
|
||||||
|
- Added remaining primitives blackboxes.
|
||||||
|
|
||||||
|
* Various
|
||||||
|
- "show -colorattr" will now color the cells, wires, and
|
||||||
|
connection arrows.
|
||||||
|
- "show -viewer none" will not execute viewer.
|
||||||
|
|
||||||
Yosys 0.28 .. Yosys 0.29
|
Yosys 0.28 .. Yosys 0.29
|
||||||
--------------------------
|
--------------------------
|
||||||
* New commands and options
|
* New commands and options
|
||||||
|
|
23
Makefile
23
Makefile
|
@ -141,7 +141,7 @@ LDLIBS += -lrt
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.29+44
|
YOSYS_VER := 0.30+48
|
||||||
|
|
||||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||||
|
@ -157,7 +157,7 @@ endif
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
OBJS = kernel/version_$(GIT_REV).o
|
||||||
|
|
||||||
bumpversion:
|
bumpversion:
|
||||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c5a60e.. | wc -l`/;" Makefile
|
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline f7a8284.. | wc -l`/;" Makefile
|
||||||
|
|
||||||
# set 'ABCREV = default' to use abc/ as it is
|
# set 'ABCREV = default' to use abc/ as it is
|
||||||
#
|
#
|
||||||
|
@ -165,7 +165,7 @@ bumpversion:
|
||||||
# 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 = 2c1c83f
|
ABCREV = bb64142
|
||||||
ABCPULL = 1
|
ABCPULL = 1
|
||||||
ABCURL ?= https://github.com/YosysHQ/abc
|
ABCURL ?= https://github.com/YosysHQ/abc
|
||||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
|
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
|
||||||
|
@ -523,6 +523,7 @@ CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL
|
||||||
LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link)
|
LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
LDLIBS_VERIFIC =
|
||||||
ifeq ($(ENABLE_VERIFIC),1)
|
ifeq ($(ENABLE_VERIFIC),1)
|
||||||
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
||||||
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
|
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
|
||||||
|
@ -548,9 +549,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS
|
||||||
endif
|
endif
|
||||||
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz
|
LDLIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz
|
||||||
else
|
else
|
||||||
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz
|
LDLIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -740,13 +741,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(PROGRAM_PREFIX)yosys$(EXE): $(OBJS)
|
$(PROGRAM_PREFIX)yosys$(EXE): $(OBJS)
|
||||||
$(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS)
|
$(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) $(LDLIBS_VERIFIC)
|
||||||
|
|
||||||
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS)
|
$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC)
|
||||||
else
|
else
|
||||||
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS)
|
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
%.o: %.cc
|
%.o: %.cc
|
||||||
|
@ -797,13 +798,13 @@ ifneq ($(ABCREV),default)
|
||||||
$(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \
|
$(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \
|
||||||
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
||||||
fi
|
fi
|
||||||
$(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \
|
$(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \
|
||||||
echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \
|
echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \
|
||||||
fi
|
fi
|
||||||
# set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string
|
# set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string
|
||||||
$(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \
|
$(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \
|
||||||
echo "Compiling local copy of ABC"; \
|
echo "Compiling local copy of ABC"; \
|
||||||
elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \
|
elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" = "$$rev"); then \
|
||||||
test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \
|
test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \
|
||||||
echo "Pulling ABC from $(ABCURL):"; set -x; \
|
echo "Pulling ABC from $(ABCURL):"; set -x; \
|
||||||
test -d abc || git clone $(ABCURL) abc; \
|
test -d abc || git clone $(ABCURL) abc; \
|
||||||
|
|
|
@ -736,6 +736,9 @@ struct AigerWriter
|
||||||
auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q);
|
auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q);
|
||||||
SigSpec sig = sigmap(sig_qy);
|
SigSpec sig = sigmap(sig_qy);
|
||||||
|
|
||||||
|
if (cell->get_bool_attribute(ID(clk2fflogic)))
|
||||||
|
sig_qy = cell->getPort(ID::D); // For a clk2fflogic $_FF_ the named signal is the D input not the Q output
|
||||||
|
|
||||||
for (int i = 0; i < GetSize(sig_qy); i++) {
|
for (int i = 0; i < GetSize(sig_qy); i++) {
|
||||||
if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr)
|
if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -728,6 +728,9 @@ struct BtorWorker
|
||||||
else
|
else
|
||||||
btorf("%d state %d %s\n", nid, sid, log_id(symbol));
|
btorf("%d state %d %s\n", nid, sid, log_id(symbol));
|
||||||
|
|
||||||
|
if (cell->get_bool_attribute(ID(clk2fflogic)))
|
||||||
|
ywmap_state(cell->getPort(ID::D)); // For a clk2fflogic FF the named signal is the D input not the Q output
|
||||||
|
else
|
||||||
ywmap_state(sig_q);
|
ywmap_state(sig_q);
|
||||||
|
|
||||||
if (nid_init_val >= 0) {
|
if (nid_init_val >= 0) {
|
||||||
|
|
|
@ -1595,6 +1595,25 @@ value<BitsY> modfloor_ss(const value<BitsA> &a, const value<BitsB> &b) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<size_t BitsY, size_t BitsA, size_t BitsB>
|
||||||
|
CXXRTL_ALWAYS_INLINE
|
||||||
|
value<BitsY> divfloor_uu(const value<BitsA> &a, const value<BitsB> &b) {
|
||||||
|
return divmod_uu<BitsY>(a, b).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divfloor. Similar to above: returns q=a//b, where q has the sign of a*b and a=b*q+N.
|
||||||
|
// In other words, returns (truncating) a/b, except if a and b have different signs
|
||||||
|
// and there's non-zero remainder, subtract one more towards floor.
|
||||||
|
template<size_t BitsY, size_t BitsA, size_t BitsB>
|
||||||
|
CXXRTL_ALWAYS_INLINE
|
||||||
|
value<BitsY> divfloor_ss(const value<BitsA> &a, const value<BitsB> &b) {
|
||||||
|
value<BitsY> q, r;
|
||||||
|
std::tie(q, r) = divmod_ss<BitsY>(a, b);
|
||||||
|
if ((b.is_neg() != a.is_neg()) && !r.is_zero())
|
||||||
|
return sub_uu<BitsY>(q, value<1> { 1u });
|
||||||
|
return q;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Memory helper
|
// Memory helper
|
||||||
struct memory_index {
|
struct memory_index {
|
||||||
|
|
|
@ -185,7 +185,7 @@ bool is_binary_cell(RTLIL::IdString type)
|
||||||
ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),
|
ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),
|
||||||
ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),
|
ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),
|
||||||
ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le),
|
ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le),
|
||||||
ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor));
|
ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($divfloor));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_extending_cell(RTLIL::IdString type)
|
bool is_extending_cell(RTLIL::IdString type)
|
||||||
|
|
|
@ -120,6 +120,9 @@ struct EdifBackend : public Backend {
|
||||||
log(" sets the delimiting character for module port rename clauses to\n");
|
log(" sets the delimiting character for module port rename clauses to\n");
|
||||||
log(" parentheses, square brackets, or angle brackets.\n");
|
log(" parentheses, square brackets, or angle brackets.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -lsbidx\n");
|
||||||
|
log(" use index 0 for the LSB bit of a net or port instead of MSB.\n");
|
||||||
|
log("\n");
|
||||||
log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n");
|
log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n");
|
||||||
log("command generates EDIF files for the Xilinx place&route tools. It might be\n");
|
log("command generates EDIF files for the Xilinx place&route tools. It might be\n");
|
||||||
log("necessary to make small modifications to this command when a different tool\n");
|
log("necessary to make small modifications to this command when a different tool\n");
|
||||||
|
@ -132,6 +135,7 @@ struct EdifBackend : public Backend {
|
||||||
std::string top_module_name;
|
std::string top_module_name;
|
||||||
bool port_rename = false;
|
bool port_rename = false;
|
||||||
bool attr_properties = false;
|
bool attr_properties = false;
|
||||||
|
bool lsbidx = false;
|
||||||
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
||||||
bool nogndvcc = false, gndvccy = false, keepmode = false;
|
bool nogndvcc = false, gndvccy = false, keepmode = false;
|
||||||
CellTypes ct(design);
|
CellTypes ct(design);
|
||||||
|
@ -173,6 +177,10 @@ struct EdifBackend : public Backend {
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-lsbidx") {
|
||||||
|
lsbidx = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx);
|
||||||
|
@ -184,6 +192,14 @@ struct EdifBackend : public Backend {
|
||||||
|
|
||||||
for (auto module : design->modules())
|
for (auto module : design->modules())
|
||||||
{
|
{
|
||||||
|
lib_cell_ports[module->name];
|
||||||
|
|
||||||
|
for (auto port : module->ports)
|
||||||
|
{
|
||||||
|
Wire *wire = module->wire(port);
|
||||||
|
lib_cell_ports[module->name][port] = std::max(lib_cell_ports[module->name][port], GetSize(wire));
|
||||||
|
}
|
||||||
|
|
||||||
if (module->get_blackbox_attribute())
|
if (module->get_blackbox_attribute())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -200,7 +216,7 @@ struct EdifBackend : public Backend {
|
||||||
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
||||||
lib_cell_ports[cell->type];
|
lib_cell_ports[cell->type];
|
||||||
for (auto p : cell->connections())
|
for (auto p : cell->connections())
|
||||||
lib_cell_ports[cell->type][p.first] = GetSize(p.second);
|
lib_cell_ports[cell->type][p.first] = std::max(lib_cell_ports[cell->type][p.first], GetSize(p.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,7 +453,7 @@ struct EdifBackend : public Backend {
|
||||||
*f << ")\n";
|
*f << ")\n";
|
||||||
for (int i = 0; i < wire->width; i++) {
|
for (int i = 0; i < wire->width; i++) {
|
||||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
|
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
|
||||||
net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input));
|
net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), lsbidx ? i : GetSize(wire)-i-1), wire->port_input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,13 +484,13 @@ struct EdifBackend : public Backend {
|
||||||
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
|
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
|
||||||
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
||||||
else {
|
else {
|
||||||
int member_idx = GetSize(sig)-i-1;
|
int member_idx = lsbidx ? i : GetSize(sig)-i-1;
|
||||||
auto m = design->module(cell->type);
|
auto m = design->module(cell->type);
|
||||||
int width = sig.size();
|
int width = sig.size();
|
||||||
if (m) {
|
if (m) {
|
||||||
auto w = m->wire(p.first);
|
auto w = m->wire(p.first);
|
||||||
if (w) {
|
if (w) {
|
||||||
member_idx = GetSize(w)-i-1;
|
member_idx = lsbidx ? i : GetSize(w)-i-1;
|
||||||
width = GetSize(w);
|
width = GetSize(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,8 +626,9 @@ struct Smt2Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst));
|
bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst));
|
||||||
|
bool clk2fflogic = cell->type == ID($anyinit) && cell->get_bool_attribute(ID(clk2fflogic));
|
||||||
int smtoffset = 0;
|
int smtoffset = 0;
|
||||||
for (auto chunk : cell->getPort(QY).chunks()) {
|
for (auto chunk : cell->getPort(clk2fflogic ? ID::D : QY).chunks()) {
|
||||||
if (chunk.is_wire())
|
if (chunk.is_wire())
|
||||||
decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset));
|
decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset));
|
||||||
smtoffset += chunk.width;
|
smtoffset += chunk.width;
|
||||||
|
@ -772,7 +773,7 @@ struct Smt2Worker
|
||||||
int arrayid = idcounter++;
|
int arrayid = idcounter++;
|
||||||
memarrays[mem] = arrayid;
|
memarrays[mem] = arrayid;
|
||||||
|
|
||||||
int abits = ceil_log2(mem->size);
|
int abits = max(1, ceil_log2(mem->size));
|
||||||
|
|
||||||
bool has_sync_wr = false;
|
bool has_sync_wr = false;
|
||||||
bool has_async_wr = false;
|
bool has_async_wr = false;
|
||||||
|
@ -1219,7 +1220,7 @@ struct Smt2Worker
|
||||||
{
|
{
|
||||||
int arrayid = memarrays.at(mem);
|
int arrayid = memarrays.at(mem);
|
||||||
|
|
||||||
int abits = ceil_log2(mem->size);;
|
int abits = max(1, ceil_log2(mem->size));
|
||||||
|
|
||||||
bool has_sync_wr = false;
|
bool has_sync_wr = false;
|
||||||
bool has_async_wr = false;
|
bool has_async_wr = false;
|
||||||
|
|
|
@ -174,6 +174,8 @@ def help():
|
||||||
further failed assertions. To output multiple traces
|
further failed assertions. To output multiple traces
|
||||||
covering all found failed assertions, the character '%' is
|
covering all found failed assertions, the character '%' is
|
||||||
replaced in all dump filenames with an increasing number.
|
replaced in all dump filenames with an increasing number.
|
||||||
|
In cover mode, don't stop when a cover trace contains a failed
|
||||||
|
assertion.
|
||||||
|
|
||||||
--check-witness
|
--check-witness
|
||||||
check that the used witness file contains sufficient
|
check that the used witness file contains sufficient
|
||||||
|
@ -1739,7 +1741,7 @@ elif covermode:
|
||||||
smt_pop()
|
smt_pop()
|
||||||
smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask))
|
smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask))
|
||||||
|
|
||||||
if found_failed_assert:
|
if found_failed_assert and not keep_going:
|
||||||
break
|
break
|
||||||
|
|
||||||
if "1" not in cover_mask:
|
if "1" not in cover_mask:
|
||||||
|
|
|
@ -768,7 +768,7 @@ class SmtIo:
|
||||||
|
|
||||||
if self.timeinfo:
|
if self.timeinfo:
|
||||||
i = 0
|
i = 0
|
||||||
s = "/-\|"
|
s = r"/-\|"
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
num_bs = 0
|
num_bs = 0
|
||||||
|
@ -1171,7 +1171,7 @@ class MkVcd:
|
||||||
|
|
||||||
def escape_name(self, name):
|
def escape_name(self, name):
|
||||||
name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name)
|
name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name)
|
||||||
if re.match("[\[\]]", name) and name[0] != "\\":
|
if re.match(r"[\[\]]", name) and name[0] != "\\":
|
||||||
name = "\\" + name
|
name = "\\" + name
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ def aiw2yw(input, mapfile, output):
|
||||||
|
|
||||||
values = WitnessValues()
|
values = WitnessValues()
|
||||||
for i, v in enumerate(inline):
|
for i, v in enumerate(inline):
|
||||||
if v == "x" or outyw.t > 0 and i in aiger_map.init_inputs:
|
if outyw.t > 0 and i in aiger_map.init_inputs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -571,7 +571,7 @@ The ``$mem_v2`` cell has the following ports:
|
||||||
signals for the read ports.
|
signals for the read ports.
|
||||||
|
|
||||||
``\RD_DATA``
|
``\RD_DATA``
|
||||||
This input is ``\RD_PORTS*\WIDTH`` bits wide, containing all data
|
This output is ``\RD_PORTS*\WIDTH`` bits wide, containing all data
|
||||||
signals for the read ports.
|
signals for the read ports.
|
||||||
|
|
||||||
``\RD_ARST``
|
``\RD_ARST``
|
||||||
|
|
654
docs/source/CHAPTER_Memorymap.rst
Normal file
654
docs/source/CHAPTER_Memorymap.rst
Normal file
|
@ -0,0 +1,654 @@
|
||||||
|
.. _chapter:memorymap:
|
||||||
|
|
||||||
|
Memory mapping
|
||||||
|
==============
|
||||||
|
|
||||||
|
Documentation for the Yosys ``memory_libmap`` memory mapper. Note that not all supported patterns
|
||||||
|
are included in this document, of particular note is that combinations of multiple patterns should
|
||||||
|
generally work. For example, `Write port with byte enables`_ could be used in conjunction with any
|
||||||
|
of the simple dual port (SDP) models. In general if a hardware memory definition does not support a
|
||||||
|
given configuration, additional logic will be instantiated to guarantee behaviour is consistent with
|
||||||
|
simulation.
|
||||||
|
|
||||||
|
See also: `passes/memory/memlib.md <https://github.com/YosysHQ/yosys/blob/master/passes/memory/memlib.md>`_
|
||||||
|
|
||||||
|
Additional notes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Memory kind selection
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The memory inference code will automatically pick target memory primitive based on memory geometry
|
||||||
|
and features used. Depending on the target, there can be up to four memory primitive classes
|
||||||
|
available for selection:
|
||||||
|
|
||||||
|
- FF RAM (aka logic): no hardware primitive used, memory lowered to a bunch of FFs and multiplexers
|
||||||
|
|
||||||
|
- Can handle arbitrary number of write ports, as long as all write ports are in the same clock domain
|
||||||
|
- Can handle arbitrary number and kind of read ports
|
||||||
|
|
||||||
|
- LUT RAM (aka distributed RAM): uses LUT storage as RAM
|
||||||
|
|
||||||
|
- Supported on most FPGAs (with notable exception of ice40)
|
||||||
|
- Usually has one synchronous write port, one or more asynchronous read ports
|
||||||
|
- Small
|
||||||
|
- Will never be used for ROMs (lowering to plain LUTs is always better)
|
||||||
|
|
||||||
|
- Block RAM: dedicated memory tiles
|
||||||
|
|
||||||
|
- Supported on basically all FPGAs
|
||||||
|
- Supports only synchronous reads
|
||||||
|
- Two ports with separate clocks
|
||||||
|
- Usually supports true dual port (with notable exception of ice40 that only supports SDP)
|
||||||
|
- Usually supports asymmetric memories and per-byte write enables
|
||||||
|
- Several kilobits in size
|
||||||
|
|
||||||
|
- Huge RAM:
|
||||||
|
|
||||||
|
- Only supported on several targets:
|
||||||
|
|
||||||
|
- Some Xilinx UltraScale devices (UltraRAM)
|
||||||
|
|
||||||
|
- Two ports, both with mutually exclusive synchronous read and write
|
||||||
|
- Single clock
|
||||||
|
- Initial data must be all-0
|
||||||
|
|
||||||
|
- Some ice40 devices (SPRAM)
|
||||||
|
|
||||||
|
- Single port with mutually exclusive synchronous read and write
|
||||||
|
- Does not support initial data
|
||||||
|
|
||||||
|
- Nexus (large RAM)
|
||||||
|
|
||||||
|
- Two ports, both with mutually exclusive synchronous read and write
|
||||||
|
- Single clock
|
||||||
|
|
||||||
|
- Will not be automatically selected by memory inference code, needs explicit opt-in via
|
||||||
|
ram_style attribute
|
||||||
|
|
||||||
|
In general, you can expect the automatic selection process to work roughly like this:
|
||||||
|
|
||||||
|
- If any read port is asynchronous, only LUT RAM (or FF RAM) can be used.
|
||||||
|
- If there is more than one write port, only block RAM can be used, and this needs to be a
|
||||||
|
hardware-supported true dual port pattern
|
||||||
|
|
||||||
|
- … unless all write ports are in the same clock domain, in which case FF RAM can also be used,
|
||||||
|
but this is generally not what you want for anything but really small memories
|
||||||
|
|
||||||
|
- Otherwise, either FF RAM, LUT RAM, or block RAM will be used, depending on memory size
|
||||||
|
|
||||||
|
This process can be overridden by attaching a ram_style attribute to the memory:
|
||||||
|
|
||||||
|
- `(* ram_style = "logic" *)` selects FF RAM
|
||||||
|
- `(* ram_style = "distributed" *)` selects LUT RAM
|
||||||
|
- `(* ram_style = "block" *)` selects block RAM
|
||||||
|
- `(* ram_style = "huge" *)` selects huge RAM
|
||||||
|
|
||||||
|
It is an error if this override cannot be realized for the given target.
|
||||||
|
|
||||||
|
Many alternate spellings of the attribute are also accepted, for compatibility with other software.
|
||||||
|
|
||||||
|
Initial data
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Most FPGA targets support initializing all kinds of memory to user-provided values. If explicit
|
||||||
|
initialization is not used the initial memory value is undefined. Initial data can be provided by
|
||||||
|
either initial statements writing memory cells one by one of ``$readmemh`` or ``$readmemb`` system
|
||||||
|
tasks. For an example pattern, see `Synchronous read port with initial value`_.
|
||||||
|
|
||||||
|
Write port with byte enables
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Byte enables can be used with any supported pattern
|
||||||
|
- To ensure that multiple writes will be merged into one port, they need to have disjoint bit
|
||||||
|
ranges, have the same address, and the same clock
|
||||||
|
- Any write enable granularity will be accepted (down to per-bit write enables), but using smaller
|
||||||
|
granularity than natively supported by the target is very likely to be inefficient (eg. using
|
||||||
|
4-bit bytes on ECP5 will result in either padding the bytes with 5 dummy bits to native 9-bit
|
||||||
|
units or splitting the RAM into two block RAMs)
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [31 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable[0])
|
||||||
|
mem[write_addr][7:0] <= write_data[7:0];
|
||||||
|
if (write_enable[1])
|
||||||
|
mem[write_addr][15:8] <= write_data[15:8];
|
||||||
|
if (write_enable[2])
|
||||||
|
mem[write_addr][23:16] <= write_data[23:16];
|
||||||
|
if (write_enable[3])
|
||||||
|
mem[write_addr][31:24] <= write_data[31:24];
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Simple dual port (SDP) memory patterns
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Asynchronous-read SDP
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- This will result in LUT RAM on supported targets
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
always @(posedge clk)
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
assign read_data = mem[read_addr];
|
||||||
|
|
||||||
|
Synchronous SDP with clock domain crossing
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Will result in block RAM or LUT RAM depending on size
|
||||||
|
- No behavior guarantees in case of simultaneous read and write to the same address
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge write_clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge read_clk) begin
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous SDP read first
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- The read and write parts can be in the same or different processes.
|
||||||
|
- Will result in block RAM or LUT RAM depending on size
|
||||||
|
- As long as the same clock is used for both, yosys will ensure read-first behavior. This may
|
||||||
|
require extra circuitry on some targets for block RAM. If this is not necessary, use one of the
|
||||||
|
patterns below.
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous SDP with undefined collision behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Like above, but the read value is undefined when read and write ports target the same address in
|
||||||
|
the same cycle
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
|
||||||
|
if (read_enable) begin
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
|
||||||
|
// 👇 this if block 👇
|
||||||
|
if (write_enable && read_addr == write_addr)
|
||||||
|
read_data <= 'x;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
- Or below, using the no_rw_check attribute
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
(* no_rw_check *)
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous SDP with write-first behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Will result in block RAM or LUT RAM depending on size
|
||||||
|
- May use additional circuitry for block RAM if write-first is not natively supported. Will always
|
||||||
|
use additional circuitry for LUT RAM.
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
|
||||||
|
if (read_enable) begin
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
if (write_enable && read_addr == write_addr)
|
||||||
|
read_data <= write_data;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous SDP with write-first behavior (alternate pattern)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- This pattern is supported for compatibility, but is much less flexible than the above
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
read_addr_reg <= read_addr;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign read_data = mem[read_addr_reg];
|
||||||
|
|
||||||
|
Single-port RAM memory patterns
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Asynchronous-read single-port RAM
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Will result in single-port LUT RAM on supported targets
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
always @(posedge clk)
|
||||||
|
if (write_enable)
|
||||||
|
mem[addr] <= write_data;
|
||||||
|
assign read_data = mem[addr];
|
||||||
|
|
||||||
|
Synchronous single-port RAM with mutually exclusive read/write
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Will result in single-port block RAM or LUT RAM depending on size
|
||||||
|
- This is the correct pattern to infer ice40 SPRAM (with manual ram_style selection)
|
||||||
|
- On targets that don't support read/write block RAM ports (eg. ice40), will result in SDP block RAM instead
|
||||||
|
- For block RAM, will use "NO_CHANGE" mode if available
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[addr] <= write_data;
|
||||||
|
else if (read_enable)
|
||||||
|
read_data <= mem[addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous single-port RAM with read-first behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Will only result in single-port block RAM when read-first behavior is natively supported;
|
||||||
|
otherwise, SDP RAM with additional circuitry will be used
|
||||||
|
- Many targets (Xilinx, ECP5, …) can only natively support read-first/write-first single-port RAM
|
||||||
|
(or TDP RAM) where the write_enable signal implies the read_enable signal (ie. can never write
|
||||||
|
without reading). The memory inference code will run a simple SAT solver on the control signals to
|
||||||
|
determine if this is the case, and insert emulation circuitry if it cannot be easily proven.
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[addr] <= write_data;
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous single-port RAM with write-first behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Will result in single-port block RAM or LUT RAM when supported
|
||||||
|
- Block RAMs will require extra circuitry if write-first behavior not natively supported
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[addr] <= write_data;
|
||||||
|
if (read_enable)
|
||||||
|
if (write_enable)
|
||||||
|
read_data <= write_data;
|
||||||
|
else
|
||||||
|
read_data <= mem[addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous read port with initial value
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Initial read port values can be combined with any other supported pattern
|
||||||
|
- If block RAM is used and initial read port values are not natively supported by the target, small
|
||||||
|
emulation circuit will be inserted
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
reg [DATA_WIDTH - 1 : 0] read_data;
|
||||||
|
initial read_data = 'h1234;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Read register reset patterns
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Resets can be combined with any other supported pattern (except that synchronous reset and
|
||||||
|
asynchronous reset cannot both be used on a single read port). If block RAM is used and the
|
||||||
|
selected reset (synchronous or asynchronous) is used but not natively supported by the target, small
|
||||||
|
emulation circuitry will be inserted.
|
||||||
|
|
||||||
|
Synchronous reset, reset priority over enable
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
|
||||||
|
if (read_reset)
|
||||||
|
read_data <= {sval};
|
||||||
|
else if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous reset, enable priority over reset
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
if (read_enable)
|
||||||
|
if (read_reset)
|
||||||
|
read_data <= 'h1234;
|
||||||
|
else
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Synchronous read port with asynchronous reset
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk, posedge reset_read) begin
|
||||||
|
if (reset_read)
|
||||||
|
read_data <= 'h1234;
|
||||||
|
else if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Asymmetric memory patterns
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
To construct an asymmetric memory (memory with read/write ports of differing widths):
|
||||||
|
|
||||||
|
- Declare the memory with the width of the narrowest intended port
|
||||||
|
- Split all wide ports into multiple narrow ports
|
||||||
|
- To ensure the wide ports will be correctly merged:
|
||||||
|
|
||||||
|
- For the address, use a concatenation of actual address in the high bits and a constant in the
|
||||||
|
low bits
|
||||||
|
- Ensure the actual address is identical for all ports belonging to the wide port
|
||||||
|
- Ensure that clock is identical
|
||||||
|
- For read ports, ensure that enable/reset signals are identical (for write ports, the enable
|
||||||
|
signal may vary — this will result in using the byte enable functionality)
|
||||||
|
|
||||||
|
Asymmetric memory is supported on all targets, but may require emulation circuitry where not
|
||||||
|
natively supported. Note that when the memory is larger than the underlying block RAM primitive,
|
||||||
|
hardware asymmetric memory support is likely not to be used even if present as it is more expensive.
|
||||||
|
|
||||||
|
Wide synchronous read port
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [7:0] mem [0:255];
|
||||||
|
wire [7:0] write_addr;
|
||||||
|
wire [5:0] read_addr;
|
||||||
|
wire [7:0] write_data;
|
||||||
|
reg [31:0] read_data;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
if (read_enable) begin
|
||||||
|
read_data[7:0] <= mem[{read_addr, 2'b00}];
|
||||||
|
read_data[15:8] <= mem[{read_addr, 2'b01}];
|
||||||
|
read_data[23:16] <= mem[{read_addr, 2'b10}];
|
||||||
|
read_data[31:24] <= mem[{read_addr, 2'b11}];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Wide asynchronous read port
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Note: the only target natively supporting this pattern is Xilinx UltraScale
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [7:0] mem [0:511];
|
||||||
|
wire [8:0] write_addr;
|
||||||
|
wire [5:0] read_addr;
|
||||||
|
wire [7:0] write_data;
|
||||||
|
wire [63:0] read_data;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign read_data[7:0] = mem[{read_addr, 3'b000}];
|
||||||
|
assign read_data[15:8] = mem[{read_addr, 3'b001}];
|
||||||
|
assign read_data[23:16] = mem[{read_addr, 3'b010}];
|
||||||
|
assign read_data[31:24] = mem[{read_addr, 3'b011}];
|
||||||
|
assign read_data[39:32] = mem[{read_addr, 3'b100}];
|
||||||
|
assign read_data[47:40] = mem[{read_addr, 3'b101}];
|
||||||
|
assign read_data[55:48] = mem[{read_addr, 3'b110}];
|
||||||
|
assign read_data[63:56] = mem[{read_addr, 3'b111}];
|
||||||
|
|
||||||
|
Wide write port
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [7:0] mem [0:255];
|
||||||
|
wire [5:0] write_addr;
|
||||||
|
wire [7:0] read_addr;
|
||||||
|
wire [31:0] write_data;
|
||||||
|
reg [7:0] read_data;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable[0])
|
||||||
|
mem[{write_addr, 2'b00}] <= write_data[7:0];
|
||||||
|
if (write_enable[1])
|
||||||
|
mem[{write_addr, 2'b01}] <= write_data[15:8];
|
||||||
|
if (write_enable[2])
|
||||||
|
mem[{write_addr, 2'b10}] <= write_data[23:16];
|
||||||
|
if (write_enable[3])
|
||||||
|
mem[{write_addr, 2'b11}] <= write_data[31:24];
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
True dual port (TDP) patterns
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Many different variations of true dual port memory can be created by combining two single-port RAM
|
||||||
|
patterns on the same memory
|
||||||
|
- When TDP memory is used, memory inference code has much less maneuver room to create requested
|
||||||
|
semantics compared to individual single-port patterns (which can end up lowered to SDP memory
|
||||||
|
where necessary) — supported patterns depend strongly on the target
|
||||||
|
- In particular, when both ports have the same clock, it's likely that "undefined collision" mode
|
||||||
|
needs to be manually selected to enable TDP memory inference
|
||||||
|
- The examples below are non-exhaustive — many more combinations of port types are possible
|
||||||
|
- Note: if two write ports are in the same process, this defines a priority relation between them
|
||||||
|
(if both ports are active in the same clock, the later one wins). On almost all targets, this will
|
||||||
|
result in a bit of extra circuitry to ensure the priority semantics. If this is not what you want,
|
||||||
|
put them in separate processes.
|
||||||
|
|
||||||
|
- Priority is not supported when using the verific front end and any priority semantics are ignored.
|
||||||
|
|
||||||
|
TDP with different clocks, exclusive read/write
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk_a) begin
|
||||||
|
if (write_enable_a)
|
||||||
|
mem[addr_a] <= write_data_a;
|
||||||
|
else if (read_enable_a)
|
||||||
|
read_data_a <= mem[addr_a];
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk_b) begin
|
||||||
|
if (write_enable_b)
|
||||||
|
mem[addr_b] <= write_data_b;
|
||||||
|
else if (read_enable_b)
|
||||||
|
read_data_b <= mem[addr_b];
|
||||||
|
end
|
||||||
|
|
||||||
|
TDP with same clock, read-first behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- This requires hardware inter-port read-first behavior, and will only work on some targets (Xilinx, Nexus)
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable_a)
|
||||||
|
mem[addr_a] <= write_data_a;
|
||||||
|
if (read_enable_a)
|
||||||
|
read_data_a <= mem[addr_a];
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable_b)
|
||||||
|
mem[addr_b] <= write_data_b;
|
||||||
|
if (read_enable_b)
|
||||||
|
read_data_b <= mem[addr_b];
|
||||||
|
end
|
||||||
|
|
||||||
|
TDP with multiple read ports
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- The combination of a single write port with an arbitrary amount of read ports is supported on all
|
||||||
|
targets — if a multi-read port primitive is available (like Xilinx RAM64M), it'll be used as
|
||||||
|
appropriate. Otherwise, the memory will be automatically split into multiple primitives.
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [31:0] mem [0:31];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] <= write_data;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign read_data_a = mem[read_addr_a];
|
||||||
|
assign read_data_b = mem[read_addr_b];
|
||||||
|
assign read_data_c = mem[read_addr_c];
|
||||||
|
|
||||||
|
Not yet supported patterns
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Synchronous SDP with write-first behavior via blocking assignments
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Would require modifications to the Yosys Verilog frontend.
|
||||||
|
- Use `Synchronous SDP with write-first behavior`_ instead
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] = write_data;
|
||||||
|
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
Asymmetric memories via part selection
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Would require major changes to the Verilog frontend.
|
||||||
|
- Build wide ports out of narrow ports instead (see `Wide synchronous read port`_)
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [31:0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
wire [1:0] byte_lane;
|
||||||
|
wire [7:0] write_data;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr][byte_lane * 8 +: 8] <= write_data;
|
||||||
|
|
||||||
|
if (read_enable)
|
||||||
|
read_data <= mem[read_addr];
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Undesired patterns
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Asynchronous writes
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Not supported in modern FPGAs
|
||||||
|
- Not supported in yosys code anyhow
|
||||||
|
|
||||||
|
.. code:: verilog
|
||||||
|
|
||||||
|
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
|
||||||
|
|
||||||
|
always @* begin
|
||||||
|
if (write_enable)
|
||||||
|
mem[write_addr] = write_data;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign read_data = mem[read_addr];
|
||||||
|
|
|
@ -42,6 +42,7 @@ Yosys manual
|
||||||
CHAPTER_Verilog.rst
|
CHAPTER_Verilog.rst
|
||||||
CHAPTER_Optimize.rst
|
CHAPTER_Optimize.rst
|
||||||
CHAPTER_Techmap.rst
|
CHAPTER_Techmap.rst
|
||||||
|
CHAPTER_Memorymap.rst
|
||||||
CHAPTER_Eval.rst
|
CHAPTER_Eval.rst
|
||||||
|
|
||||||
.. raw:: latex
|
.. raw:: latex
|
||||||
|
|
|
@ -112,15 +112,26 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil
|
||||||
string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : "";
|
string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : "";
|
||||||
message += vstringf(msg, args);
|
message += vstringf(msg, args);
|
||||||
|
|
||||||
|
if (log_verific_callback) {
|
||||||
|
string full_message = stringf("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||||
|
log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile), full_message.c_str());
|
||||||
|
} else {
|
||||||
if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR)
|
if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR)
|
||||||
log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str());
|
log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||||
else
|
else
|
||||||
log("%s%s\n", message_prefix.c_str(), message.c_str());
|
log("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||||
|
}
|
||||||
if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR))
|
if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR))
|
||||||
verific_error_msg = message;
|
verific_error_msg = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg))
|
||||||
|
{
|
||||||
|
Message::SetConsoleOutput(0);
|
||||||
|
Message::RegisterCallBackMsg(msg_func);
|
||||||
|
log_verific_callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
string get_full_netlist_name(Netlist *nl)
|
string get_full_netlist_name(Netlist *nl)
|
||||||
{
|
{
|
||||||
if (nl->NumOfRefs() == 1) {
|
if (nl->NumOfRefs() == 1) {
|
||||||
|
@ -1638,6 +1649,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
||||||
cell->parameters[ID::TRANSPARENT] = false;
|
cell->parameters[ID::TRANSPARENT] = false;
|
||||||
cell->parameters[ID::ABITS] = GetSize(addr);
|
cell->parameters[ID::ABITS] = GetSize(addr);
|
||||||
cell->parameters[ID::WIDTH] = GetSize(data);
|
cell->parameters[ID::WIDTH] = GetSize(data);
|
||||||
|
import_attributes(cell->attributes, inst);
|
||||||
cell->setPort(ID::CLK, RTLIL::State::Sx);
|
cell->setPort(ID::CLK, RTLIL::State::Sx);
|
||||||
cell->setPort(ID::EN, RTLIL::State::Sx);
|
cell->setPort(ID::EN, RTLIL::State::Sx);
|
||||||
cell->setPort(ID::ADDR, addr);
|
cell->setPort(ID::ADDR, addr);
|
||||||
|
@ -1667,6 +1679,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
||||||
cell->parameters[ID::PRIORITY] = 0;
|
cell->parameters[ID::PRIORITY] = 0;
|
||||||
cell->parameters[ID::ABITS] = GetSize(addr);
|
cell->parameters[ID::ABITS] = GetSize(addr);
|
||||||
cell->parameters[ID::WIDTH] = GetSize(data);
|
cell->parameters[ID::WIDTH] = GetSize(data);
|
||||||
|
import_attributes(cell->attributes, inst);
|
||||||
cell->setPort(ID::EN, RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data)));
|
cell->setPort(ID::EN, RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data)));
|
||||||
cell->setPort(ID::CLK, RTLIL::State::S0);
|
cell->setPort(ID::CLK, RTLIL::State::S0);
|
||||||
cell->setPort(ID::ADDR, addr);
|
cell->setPort(ID::ADDR, addr);
|
||||||
|
@ -1994,7 +2007,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
||||||
initval[i] = State::Sx;
|
initval[i] = State::Sx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initval.is_fully_undef())
|
if (wire->port_input) {
|
||||||
|
wire->attributes[ID::defaultvalue] = Const(initval);
|
||||||
|
wire->attributes.erase(ID::init);
|
||||||
|
} else if (initval.is_fully_undef())
|
||||||
wire->attributes.erase(ID::init);
|
wire->attributes.erase(ID::init);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ bool log_quiet_warnings = false;
|
||||||
int log_verbose_level;
|
int log_verbose_level;
|
||||||
string log_last_error;
|
string log_last_error;
|
||||||
void (*log_error_atexit)() = NULL;
|
void (*log_error_atexit)() = NULL;
|
||||||
|
void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg) = NULL;
|
||||||
|
|
||||||
int log_make_debug = 0;
|
int log_make_debug = 0;
|
||||||
int log_force_debug = 0;
|
int log_force_debug = 0;
|
||||||
|
|
|
@ -130,6 +130,9 @@ void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(for
|
||||||
void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||||
void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||||
|
|
||||||
|
void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg));
|
||||||
|
extern void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg);
|
||||||
|
|
||||||
// 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_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
|
||||||
|
|
|
@ -178,6 +178,8 @@ class WType:
|
||||||
t.cont = None
|
t.cont = None
|
||||||
t.attr_type = attr_types.default
|
t.attr_type = attr_types.default
|
||||||
if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "):
|
if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "):
|
||||||
|
str_def = str_def.replace("const ", "")
|
||||||
|
|
||||||
candidate = WContainer.from_string(str_def, containing_file, line_number)
|
candidate = WContainer.from_string(str_def, containing_file, line_number)
|
||||||
if candidate == None:
|
if candidate == None:
|
||||||
return None
|
return None
|
||||||
|
@ -203,8 +205,12 @@ class WType:
|
||||||
|
|
||||||
prefix = ""
|
prefix = ""
|
||||||
|
|
||||||
|
if str.startswith(str_def, "const "):
|
||||||
|
if "char_p" in str_def:
|
||||||
|
prefix = "const "
|
||||||
|
str_def = str_def[6:]
|
||||||
if str.startswith(str_def, "unsigned "):
|
if str.startswith(str_def, "unsigned "):
|
||||||
prefix = "unsigned "
|
prefix = "unsigned " + prefix
|
||||||
str_def = str_def[9:]
|
str_def = str_def[9:]
|
||||||
while str.startswith(str_def, "long "):
|
while str.startswith(str_def, "long "):
|
||||||
prefix= "long " + prefix
|
prefix= "long " + prefix
|
||||||
|
@ -1285,7 +1291,7 @@ class WFunction:
|
||||||
prefix = ""
|
prefix = ""
|
||||||
i = 0
|
i = 0
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if part in ["unsigned", "long", "short"]:
|
if part in ["unsigned", "long", "short", "const"]:
|
||||||
prefix += part + " "
|
prefix += part + " "
|
||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
|
@ -1361,10 +1367,17 @@ class WFunction:
|
||||||
func.args.append(parsed)
|
func.args.append(parsed)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mangled_name(self):
|
||||||
|
mangled_typename = lambda code: code.replace("::", "_").replace("<","_").replace(">","_") \
|
||||||
|
.replace(" ","").replace("*","").replace(",","")
|
||||||
|
|
||||||
|
return self.name + "".join(
|
||||||
|
f"__{mangled_typename(arg.wtype.gen_text_cpp())}" for arg in self.args
|
||||||
|
)
|
||||||
|
|
||||||
def gen_alias(self):
|
def gen_alias(self):
|
||||||
self.alias = self.name
|
self.alias = self.mangled_name
|
||||||
for arg in self.args:
|
|
||||||
self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","")
|
|
||||||
|
|
||||||
def gen_decl(self):
|
def gen_decl(self):
|
||||||
if self.duplicate:
|
if self.duplicate:
|
||||||
|
@ -2190,12 +2203,15 @@ def clean_duplicates():
|
||||||
for fun in class_.found_funs:
|
for fun in class_.found_funs:
|
||||||
if fun.gen_decl_hash_py() in known_decls:
|
if fun.gen_decl_hash_py() in known_decls:
|
||||||
debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
|
debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
|
||||||
|
|
||||||
other = known_decls[fun.gen_decl_hash_py()]
|
other = known_decls[fun.gen_decl_hash_py()]
|
||||||
other.gen_alias()
|
if fun.mangled_name == other.mangled_name:
|
||||||
fun.gen_alias()
|
|
||||||
if fun.gen_decl_hash_py() == other.gen_decl_hash_py():
|
|
||||||
fun.duplicate = True
|
fun.duplicate = True
|
||||||
debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
|
debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
|
||||||
|
continue
|
||||||
|
|
||||||
|
other.gen_alias()
|
||||||
|
fun.gen_alias()
|
||||||
else:
|
else:
|
||||||
known_decls[fun.gen_decl_hash_py()] = fun
|
known_decls[fun.gen_decl_hash_py()] = fun
|
||||||
known_decls = []
|
known_decls = []
|
||||||
|
|
|
@ -112,11 +112,10 @@ struct CheckPass : public Pass {
|
||||||
for (size_t i = 0; i < all_cases.size(); i++) {
|
for (size_t i = 0; i < all_cases.size(); i++) {
|
||||||
for (auto action : all_cases[i]->actions) {
|
for (auto action : all_cases[i]->actions) {
|
||||||
for (auto bit : sigmap(action.first))
|
for (auto bit : sigmap(action.first))
|
||||||
if (bit.wire) {
|
|
||||||
wire_drivers[bit].push_back(
|
wire_drivers[bit].push_back(
|
||||||
stringf("action %s <= %s (case rule) in process %s",
|
stringf("action %s <= %s (case rule) in process %s",
|
||||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||||
}
|
|
||||||
for (auto bit : sigmap(action.second))
|
for (auto bit : sigmap(action.second))
|
||||||
if (bit.wire) used_wires.insert(bit);
|
if (bit.wire) used_wires.insert(bit);
|
||||||
}
|
}
|
||||||
|
@ -134,7 +133,6 @@ struct CheckPass : public Pass {
|
||||||
if (bit.wire) used_wires.insert(bit);
|
if (bit.wire) used_wires.insert(bit);
|
||||||
for (auto action : sync->actions) {
|
for (auto action : sync->actions) {
|
||||||
for (auto bit : sigmap(action.first))
|
for (auto bit : sigmap(action.first))
|
||||||
if (bit.wire)
|
|
||||||
wire_drivers[bit].push_back(
|
wire_drivers[bit].push_back(
|
||||||
stringf("action %s <= %s (sync rule) in process %s",
|
stringf("action %s <= %s (sync rule) in process %s",
|
||||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||||
|
@ -176,7 +174,8 @@ struct CheckPass : public Pass {
|
||||||
if (logic_cell)
|
if (logic_cell)
|
||||||
topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
|
topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
|
||||||
stringf("wire %s", log_signal(sig[i])));
|
stringf("wire %s", log_signal(sig[i])));
|
||||||
if (sig[i].wire)
|
|
||||||
|
if (sig[i].wire || !cell->input(conn.first))
|
||||||
wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)",
|
wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)",
|
||||||
log_id(conn.first), i, log_id(cell), log_id(cell->type)));
|
log_id(conn.first), i, log_id(cell), log_id(cell->type)));
|
||||||
}
|
}
|
||||||
|
@ -192,6 +191,7 @@ struct CheckPass : public Pass {
|
||||||
if (wire->port_input) {
|
if (wire->port_input) {
|
||||||
SigSpec sig = sigmap(wire);
|
SigSpec sig = sigmap(wire);
|
||||||
for (int i = 0; i < GetSize(sig); i++)
|
for (int i = 0; i < GetSize(sig); i++)
|
||||||
|
if (sig[i].wire || !wire->port_output)
|
||||||
wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
|
wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
|
||||||
}
|
}
|
||||||
if (wire->port_output)
|
if (wire->port_output)
|
||||||
|
@ -212,6 +212,15 @@ struct CheckPass : public Pass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto state : {State::S0, State::S1, State::Sx})
|
||||||
|
if (wire_drivers.count(state)) {
|
||||||
|
string message = stringf("Drivers conflicting with a constant %s driver:\n", log_signal(state));
|
||||||
|
for (auto str : wire_drivers[state])
|
||||||
|
message += stringf(" %s\n", str.c_str());
|
||||||
|
log_warning("%s", message.c_str());
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto it : wire_drivers)
|
for (auto it : wire_drivers)
|
||||||
if (wire_drivers_count[it.first] > 1) {
|
if (wire_drivers_count[it.first] > 1) {
|
||||||
string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first));
|
string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first));
|
||||||
|
|
|
@ -139,7 +139,12 @@ static bool rename_witness(RTLIL::Design *design, dict<RTLIL::Module *, int> &ca
|
||||||
|
|
||||||
if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) {
|
if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) {
|
||||||
has_witness_signals = true;
|
has_witness_signals = true;
|
||||||
auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y;
|
IdString QY;
|
||||||
|
bool clk2fflogic = false;
|
||||||
|
if (cell->type == ID($anyinit))
|
||||||
|
QY = (clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic))) ? ID::D : ID::Q;
|
||||||
|
else
|
||||||
|
QY = ID::Y;
|
||||||
auto sig_out = cell->getPort(QY);
|
auto sig_out = cell->getPort(QY);
|
||||||
|
|
||||||
for (auto chunk : sig_out.chunks()) {
|
for (auto chunk : sig_out.chunks()) {
|
||||||
|
@ -151,6 +156,9 @@ static bool rename_witness(RTLIL::Design *design, dict<RTLIL::Module *, int> &ca
|
||||||
auto new_id = module->uniquify("\\_witness_." + name);
|
auto new_id = module->uniquify("\\_witness_." + name);
|
||||||
auto new_wire = module->addWire(new_id, GetSize(sig_out));
|
auto new_wire = module->addWire(new_id, GetSize(sig_out));
|
||||||
new_wire->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 });
|
new_wire->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 });
|
||||||
|
if (clk2fflogic)
|
||||||
|
module->connect({new_wire, sig_out});
|
||||||
|
else
|
||||||
module->connect({sig_out, new_wire});
|
module->connect({sig_out, new_wire});
|
||||||
cell->setPort(QY, new_wire);
|
cell->setPort(QY, new_wire);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -84,7 +84,7 @@ struct ShowWorker
|
||||||
std::string nextColor()
|
std::string nextColor()
|
||||||
{
|
{
|
||||||
if (currentColor == 0)
|
if (currentColor == 0)
|
||||||
return "color=\"black\"";
|
return "color=\"black\", fontcolor=\"black\"";
|
||||||
return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1, currentColor%8+1);
|
return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1, currentColor%8+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,19 +97,16 @@ struct ShowWorker
|
||||||
|
|
||||||
std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor)
|
std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor)
|
||||||
{
|
{
|
||||||
sig.sort_and_unify();
|
std::string color = findColor(sig);
|
||||||
for (auto &c : sig.chunks()) {
|
if (!color.empty()) return color;
|
||||||
if (c.wire != nullptr)
|
|
||||||
for (auto &s : color_selections)
|
|
||||||
if (s.second.selected_members.count(module->name) > 0 && s.second.selected_members.at(module->name).count(c.wire->name) > 0)
|
|
||||||
return stringf("color=\"%s\"", s.first.c_str());
|
|
||||||
}
|
|
||||||
return defaultColor;
|
return defaultColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string nextColor(const RTLIL::SigSig &conn, std::string defaultColor)
|
std::string nextColor(const RTLIL::SigSig &conn, std::string defaultColor)
|
||||||
{
|
{
|
||||||
return nextColor(conn.first, nextColor(conn.second, defaultColor));
|
std::string color = findColor(conn);
|
||||||
|
if (!color.empty()) return color;
|
||||||
|
return defaultColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string nextColor(const RTLIL::SigSpec &sig)
|
std::string nextColor(const RTLIL::SigSpec &sig)
|
||||||
|
@ -131,12 +128,28 @@ struct ShowWorker
|
||||||
return stringf("style=\"setlinewidth(3)\", label=\"<%d>\"", bits);
|
return stringf("style=\"setlinewidth(3)\", label=\"<%d>\"", bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *findColor(std::string member_name)
|
std::string findColor(RTLIL::SigSpec sig)
|
||||||
|
{
|
||||||
|
sig.sort_and_unify();
|
||||||
|
for (auto &c : sig.chunks()) {
|
||||||
|
if (c.wire != nullptr)
|
||||||
|
return findColor(c.wire->name);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string findColor(const RTLIL::SigSig &conn)
|
||||||
|
{
|
||||||
|
std::string firstColor = findColor(conn.first);
|
||||||
|
if (findColor(conn.second) == firstColor) return firstColor;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string findColor(IdString member_name)
|
||||||
{
|
{
|
||||||
for (auto &s : color_selections)
|
for (auto &s : color_selections)
|
||||||
if (s.second.selected_member(module->name, member_name)) {
|
if (s.second.selected_member(module->name, member_name)) {
|
||||||
dot_escape_store.push_back(stringf(", color=\"%s\"", s.first.c_str()));
|
return stringf("color=\"%s\", fontcolor=\"%s\"", s.first.c_str(), s.first.c_str());
|
||||||
return dot_escape_store.back().c_str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLIL::Const colorattr_value;
|
RTLIL::Const colorattr_value;
|
||||||
|
@ -155,8 +168,7 @@ struct ShowWorker
|
||||||
colorattr_cache[colorattr_value] = (next_id % 8) + 1;
|
colorattr_cache[colorattr_value] = (next_id % 8) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dot_escape_store.push_back(stringf(", colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value)));
|
return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value));
|
||||||
return dot_escape_store.back().c_str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *findLabel(std::string member_name)
|
const char *findLabel(std::string member_name)
|
||||||
|
@ -189,6 +201,12 @@ struct ShowWorker
|
||||||
if (id[0] == '\\')
|
if (id[0] == '\\')
|
||||||
id = id.substr(1);
|
id = id.substr(1);
|
||||||
|
|
||||||
|
// TODO: optionally include autoname + print correspondence in case of ambiguity
|
||||||
|
size_t max_label_len = abbreviateIds ? 256 : 16384;
|
||||||
|
if (id.size() > max_label_len) {
|
||||||
|
id = id.substr(0,max_label_len-3) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
std::string str;
|
std::string str;
|
||||||
for (char ch : id) {
|
for (char ch : id) {
|
||||||
if (ch == '\\') {
|
if (ch == '\\') {
|
||||||
|
@ -196,7 +214,7 @@ struct ShowWorker
|
||||||
str += "╲";
|
str += "╲";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ch == '"')
|
if (ch == '"' || ch == '<' || ch == '>')
|
||||||
str += "\\";
|
str += "\\";
|
||||||
str += ch;
|
str += ch;
|
||||||
}
|
}
|
||||||
|
@ -317,7 +335,7 @@ struct ShowWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
code += stringf("x%d [ shape=record, style=rounded, label=\"", dot_idx) \
|
code += stringf("x%d [ shape=record, style=rounded, label=\"", dot_idx) \
|
||||||
+ join_label_pieces(label_pieces) + "\" ];\n";
|
+ join_label_pieces(label_pieces) + stringf("\", %s ];\n", nextColor(sig).c_str());
|
||||||
|
|
||||||
if (!port.empty()) {
|
if (!port.empty()) {
|
||||||
currentColor = xorshift32(currentColor);
|
currentColor = xorshift32(currentColor);
|
||||||
|
@ -414,9 +432,9 @@ struct ShowWorker
|
||||||
if (wire->port_input || wire->port_output)
|
if (wire->port_input || wire->port_output)
|
||||||
shape = "octagon";
|
shape = "octagon";
|
||||||
if (wire->name.isPublic()) {
|
if (wire->name.isPublic()) {
|
||||||
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n",
|
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s ];\n",
|
||||||
id2num(wire->name), shape, findLabel(wire->name.str()),
|
id2num(wire->name), shape, findLabel(wire->name.str()),
|
||||||
nextColor(RTLIL::SigSpec(wire), "color=\"black\"").c_str());
|
nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str());
|
||||||
if (wire->port_input)
|
if (wire->port_input)
|
||||||
all_sources.insert(stringf("n%d", id2num(wire->name)));
|
all_sources.insert(stringf("n%d", id2num(wire->name)));
|
||||||
else if (wire->port_output)
|
else if (wire->port_output)
|
||||||
|
@ -481,11 +499,11 @@ struct ShowWorker
|
||||||
#ifdef CLUSTER_CELLS_AND_PORTBOXES
|
#ifdef CLUSTER_CELLS_AND_PORTBOXES
|
||||||
if (!code.empty())
|
if (!code.empty())
|
||||||
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n",
|
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n",
|
||||||
id2num(cell->name), id2num(cell->name), label_string.c_str(), findColor(cell->name), code.c_str());
|
id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str());
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
fprintf(f, "c%d [ shape=record, label=\"%s\"%s ];\n%s",
|
fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s",
|
||||||
id2num(cell->name), label_string.c_str(), findColor(cell->name.str()), code.c_str());
|
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &it : module->processes)
|
for (auto &it : module->processes)
|
||||||
|
@ -555,9 +573,9 @@ struct ShowWorker
|
||||||
} else if (right_node[0] == 'x') {
|
} else if (right_node[0] == 'x') {
|
||||||
net_conn_map[left_node].out.insert({right_node, GetSize(conn.first)});
|
net_conn_map[left_node].out.insert({right_node, GetSize(conn.first)});
|
||||||
} else {
|
} else {
|
||||||
net_conn_map[right_node].in.insert({stringf("x%d:e", single_idx_count), GetSize(conn.first)});
|
net_conn_map[right_node].in.insert({stringf("x%d", single_idx_count), GetSize(conn.first)});
|
||||||
net_conn_map[left_node].out.insert({stringf("x%d:w", single_idx_count), GetSize(conn.first)});
|
net_conn_map[left_node].out.insert({stringf("x%d", single_idx_count), GetSize(conn.first)});
|
||||||
fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++);
|
fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\", %s];\n", single_idx_count++, findColor(conn).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,6 +661,7 @@ struct ShowPass : public Pass {
|
||||||
log(" -viewer <viewer>\n");
|
log(" -viewer <viewer>\n");
|
||||||
log(" Run the specified command with the graphics file as parameter.\n");
|
log(" Run the specified command with the graphics file as parameter.\n");
|
||||||
log(" On Windows, this pauses yosys until the viewer exits.\n");
|
log(" On Windows, this pauses yosys until the viewer exits.\n");
|
||||||
|
log(" Use \"-viewer none\" to not run any command.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -format <format>\n");
|
log(" -format <format>\n");
|
||||||
log(" Generate a graphics file in the specified format. Use 'dot' to just\n");
|
log(" Generate a graphics file in the specified format. Use 'dot' to just\n");
|
||||||
|
@ -903,6 +922,7 @@ struct ShowPass : public Pass {
|
||||||
#if defined(YOSYS_DISABLE_SPAWN)
|
#if defined(YOSYS_DISABLE_SPAWN)
|
||||||
log_assert(viewer_exe.empty() && !format.empty());
|
log_assert(viewer_exe.empty() && !format.empty());
|
||||||
#else
|
#else
|
||||||
|
if (viewer_exe != "none") {
|
||||||
if (!viewer_exe.empty()) {
|
if (!viewer_exe.empty()) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// system()/cmd.exe does not understand single quotes nor
|
// system()/cmd.exe does not understand single quotes nor
|
||||||
|
@ -926,6 +946,7 @@ struct ShowPass : public Pass {
|
||||||
if (run_command(cmd) != 0)
|
if (run_command(cmd) != 0)
|
||||||
log_cmd_error("Shell command failed!\n");
|
log_cmd_error("Shell command failed!\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (flag_pause) {
|
if (flag_pause) {
|
||||||
|
|
|
@ -292,10 +292,12 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
||||||
if (!purge_mode)
|
if (!purge_mode)
|
||||||
for (auto &it : module->cells_) {
|
for (auto &it : module->cells_) {
|
||||||
RTLIL::Cell *cell = it.second;
|
RTLIL::Cell *cell = it.second;
|
||||||
if (ct_reg.cell_known(cell->type))
|
if (ct_reg.cell_known(cell->type)) {
|
||||||
|
bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic));
|
||||||
for (auto &it2 : cell->connections())
|
for (auto &it2 : cell->connections())
|
||||||
if (ct_reg.cell_output(cell->type, it2.first))
|
if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first))
|
||||||
register_signals.add(it2.second);
|
register_signals.add(it2.second);
|
||||||
|
}
|
||||||
for (auto &it2 : cell->connections())
|
for (auto &it2 : cell->connections())
|
||||||
connected_signals.add(it2.second);
|
connected_signals.add(it2.second);
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ struct PruneWorker
|
||||||
if (GetSize(new_lhs) == 0) {
|
if (GetSize(new_lhs) == 0) {
|
||||||
if (GetSize(conn_lhs) == 0)
|
if (GetSize(conn_lhs) == 0)
|
||||||
removed_count++;
|
removed_count++;
|
||||||
cs->actions.erase((it++).base() - 1);
|
it = decltype(cs->actions)::reverse_iterator(cs->actions.erase(it.base() - 1));
|
||||||
} else {
|
} else {
|
||||||
it->first = new_lhs;
|
it->first = new_lhs;
|
||||||
it->second = new_rhs;
|
it->second = new_rhs;
|
||||||
|
|
|
@ -80,15 +80,27 @@ struct Clk2fflogicPass : public Pass {
|
||||||
return module->Eqx(NEW_ID, {sampled_sig, sig}, polarity ? SigSpec {State::S0, State::S1} : SigSpec {State::S1, State::S0});
|
return module->Eqx(NEW_ID, {sampled_sig, sig}, polarity ? SigSpec {State::S0, State::S1} : SigSpec {State::S1, State::S0});
|
||||||
}
|
}
|
||||||
// Sampled and current value of a data signal.
|
// Sampled and current value of a data signal.
|
||||||
SampledSig sample_data(Module *module, SigSpec sig, RTLIL::Const init, bool is_fine) {
|
SampledSig sample_data(Module *module, SigSpec sig, RTLIL::Const init, bool is_fine, bool set_attribute = false) {
|
||||||
std::string sig_str = log_signal(sig);
|
std::string sig_str = log_signal(sig);
|
||||||
sig_str.erase(std::remove(sig_str.begin(), sig_str.end(), ' '), sig_str.end());
|
sig_str.erase(std::remove(sig_str.begin(), sig_str.end(), ' '), sig_str.end());
|
||||||
|
|
||||||
|
|
||||||
Wire *sampled_sig = module->addWire(NEW_ID_SUFFIX(stringf("%s#sampled", sig_str.c_str())), GetSize(sig));
|
Wire *sampled_sig = module->addWire(NEW_ID_SUFFIX(stringf("%s#sampled", sig_str.c_str())), GetSize(sig));
|
||||||
sampled_sig->attributes[ID::init] = init;
|
sampled_sig->attributes[ID::init] = init;
|
||||||
|
|
||||||
|
Cell *cell;
|
||||||
if (is_fine)
|
if (is_fine)
|
||||||
module->addFfGate(NEW_ID, sig, sampled_sig);
|
cell = module->addFfGate(NEW_ID, sig, sampled_sig);
|
||||||
else
|
else
|
||||||
module->addFf(NEW_ID, sig, sampled_sig);
|
cell = module->addFf(NEW_ID, sig, sampled_sig);
|
||||||
|
|
||||||
|
if (set_attribute) {
|
||||||
|
for (auto &chunk : sig.chunks())
|
||||||
|
if (chunk.wire != nullptr)
|
||||||
|
chunk.wire->set_bool_attribute(ID::keep);
|
||||||
|
cell->set_bool_attribute(ID(clk2fflogic));
|
||||||
|
}
|
||||||
|
|
||||||
return {sampled_sig, sig};
|
return {sampled_sig, sig};
|
||||||
}
|
}
|
||||||
SigSpec mux(Module *module, SigSpec a, SigSpec b, SigSpec s, bool is_fine) {
|
SigSpec mux(Module *module, SigSpec a, SigSpec b, SigSpec s, bool is_fine) {
|
||||||
|
@ -213,7 +225,7 @@ struct Clk2fflogicPass : public Pass {
|
||||||
if (ff.has_clk)
|
if (ff.has_clk)
|
||||||
ff.unmap_ce_srst();
|
ff.unmap_ce_srst();
|
||||||
|
|
||||||
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine).sampled;
|
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled;
|
||||||
|
|
||||||
if (ff.has_clk) {
|
if (ff.has_clk) {
|
||||||
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||||
|
|
|
@ -140,6 +140,7 @@ struct SimInstance
|
||||||
dict<SigBit, pool<Wire*>> upd_outports;
|
dict<SigBit, pool<Wire*>> upd_outports;
|
||||||
|
|
||||||
dict<SigBit, SigBit> in_parent_drivers;
|
dict<SigBit, SigBit> in_parent_drivers;
|
||||||
|
dict<SigBit, SigBit> clk2fflogic_drivers;
|
||||||
|
|
||||||
pool<SigBit> dirty_bits;
|
pool<SigBit> dirty_bits;
|
||||||
pool<Cell*> dirty_cells;
|
pool<Cell*> dirty_cells;
|
||||||
|
@ -270,6 +271,11 @@ struct SimInstance
|
||||||
ff.past_srst = State::Sx;
|
ff.past_srst = State::Sx;
|
||||||
ff.data = ff_data;
|
ff.data = ff_data;
|
||||||
ff_database[cell] = ff;
|
ff_database[cell] = ff;
|
||||||
|
|
||||||
|
if (cell->get_bool_attribute(ID(clk2fflogic))) {
|
||||||
|
for (int i = 0; i < ff_data.width; i++)
|
||||||
|
clk2fflogic_drivers.emplace(sigmap(ff_data.sig_d[i]), sigmap(ff_data.sig_q[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->is_mem_cell())
|
if (cell->is_mem_cell())
|
||||||
|
@ -389,6 +395,10 @@ struct SimInstance
|
||||||
auto sigbit = sig[i];
|
auto sigbit = sig[i];
|
||||||
auto sigval = value[i];
|
auto sigval = value[i];
|
||||||
|
|
||||||
|
auto clk2fflogic_driver = clk2fflogic_drivers.find(sigbit);
|
||||||
|
if (clk2fflogic_driver != clk2fflogic_drivers.end())
|
||||||
|
sigbit = clk2fflogic_driver->second;
|
||||||
|
|
||||||
auto in_parent_driver = in_parent_drivers.find(sigbit);
|
auto in_parent_driver = in_parent_drivers.find(sigbit);
|
||||||
if (in_parent_driver == in_parent_drivers.end())
|
if (in_parent_driver == in_parent_drivers.end())
|
||||||
set_state(sigbit, sigval);
|
set_state(sigbit, sigval);
|
||||||
|
@ -589,7 +599,7 @@ struct SimInstance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool update_ph2(bool gclk)
|
bool update_ph2(bool gclk, bool stable_past_update = false)
|
||||||
{
|
{
|
||||||
bool did_something = false;
|
bool did_something = false;
|
||||||
|
|
||||||
|
@ -600,7 +610,7 @@ struct SimInstance
|
||||||
|
|
||||||
Const current_q = get_state(ff.data.sig_q);
|
Const current_q = get_state(ff.data.sig_q);
|
||||||
|
|
||||||
if (ff_data.has_clk) {
|
if (ff_data.has_clk && !stable_past_update) {
|
||||||
// flip-flops
|
// flip-flops
|
||||||
State current_clk = get_state(ff_data.sig_clk)[0];
|
State current_clk = get_state(ff_data.sig_clk)[0];
|
||||||
if (ff_data.pol_clk ? (ff.past_clk == State::S0 && current_clk != State::S0) :
|
if (ff_data.pol_clk ? (ff.past_clk == State::S0 && current_clk != State::S0) :
|
||||||
|
@ -621,7 +631,7 @@ struct SimInstance
|
||||||
if (ff_data.has_aload) {
|
if (ff_data.has_aload) {
|
||||||
State current_aload = get_state(ff_data.sig_aload)[0];
|
State current_aload = get_state(ff_data.sig_aload)[0];
|
||||||
if (current_aload == (ff_data.pol_aload ? State::S1 : State::S0)) {
|
if (current_aload == (ff_data.pol_aload ? State::S1 : State::S0)) {
|
||||||
current_q = ff_data.has_clk ? ff.past_ad : get_state(ff.data.sig_ad);
|
current_q = ff_data.has_clk && !stable_past_update ? ff.past_ad : get_state(ff.data.sig_ad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// async reset
|
// async reset
|
||||||
|
@ -672,6 +682,8 @@ struct SimInstance
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (stable_past_update)
|
||||||
|
continue;
|
||||||
if (port.clk_polarity ?
|
if (port.clk_polarity ?
|
||||||
(mdb.past_wr_clk[port_idx] == State::S1 || get_state(port.clk) != State::S1) :
|
(mdb.past_wr_clk[port_idx] == State::S1 || get_state(port.clk) != State::S1) :
|
||||||
(mdb.past_wr_clk[port_idx] == State::S0 || get_state(port.clk) != State::S0))
|
(mdb.past_wr_clk[port_idx] == State::S0 || get_state(port.clk) != State::S0))
|
||||||
|
@ -701,7 +713,7 @@ struct SimInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it : children)
|
for (auto it : children)
|
||||||
if (it.second->update_ph2(gclk)) {
|
if (it.second->update_ph2(gclk, stable_past_update)) {
|
||||||
dirty_children.insert(it.second);
|
dirty_children.insert(it.second);
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
|
@ -1196,10 +1208,22 @@ struct SimWorker : SimShared
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize_stable_past()
|
void initialize_stable_past()
|
||||||
|
{
|
||||||
|
|
||||||
|
while (1)
|
||||||
{
|
{
|
||||||
if (debug)
|
if (debug)
|
||||||
log("\n-- ph1 (initialize) --\n");
|
log("\n-- ph1 (initialize) --\n");
|
||||||
|
|
||||||
top->update_ph1();
|
top->update_ph1();
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
log("\n-- ph2 (initialize) --\n");
|
||||||
|
|
||||||
|
if (!top->update_ph2(false, true))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
log("\n-- ph3 (initialize) --\n");
|
log("\n-- ph3 (initialize) --\n");
|
||||||
top->update_ph3(true);
|
top->update_ph3(true);
|
||||||
|
|
|
@ -131,70 +131,3 @@ module CC_USR_RSTN (
|
||||||
output USR_RSTN
|
output USR_RSTN
|
||||||
);
|
);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
(* blackbox *)
|
|
||||||
module CC_FIFO_40K (
|
|
||||||
output A_ECC_1B_ERR,
|
|
||||||
output B_ECC_1B_ERR,
|
|
||||||
output A_ECC_2B_ERR,
|
|
||||||
output B_ECC_2B_ERR,
|
|
||||||
// FIFO pop port
|
|
||||||
output [39:0] A_DO,
|
|
||||||
output [39:0] B_DO,
|
|
||||||
(* clkbuf_sink *)
|
|
||||||
input A_CLK,
|
|
||||||
input A_EN,
|
|
||||||
// FIFO push port
|
|
||||||
input [39:0] A_DI,
|
|
||||||
input [39:0] B_DI,
|
|
||||||
input [39:0] A_BM,
|
|
||||||
input [39:0] B_BM,
|
|
||||||
(* clkbuf_sink *)
|
|
||||||
input B_CLK,
|
|
||||||
input B_EN,
|
|
||||||
input B_WE,
|
|
||||||
// FIFO control
|
|
||||||
input F_RST_N,
|
|
||||||
input [12:0] F_ALMOST_FULL_OFFSET,
|
|
||||||
input [12:0] F_ALMOST_EMPTY_OFFSET,
|
|
||||||
// FIFO status signals
|
|
||||||
output F_FULL,
|
|
||||||
output F_EMPTY,
|
|
||||||
output F_ALMOST_FULL,
|
|
||||||
output F_ALMOST_EMPTY,
|
|
||||||
output F_RD_ERROR,
|
|
||||||
output F_WR_ERROR,
|
|
||||||
output [15:0] F_RD_PTR,
|
|
||||||
output [15:0] F_WR_PTR
|
|
||||||
);
|
|
||||||
// Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED
|
|
||||||
parameter LOC = "UNPLACED";
|
|
||||||
|
|
||||||
// Offset configuration
|
|
||||||
parameter [12:0] ALMOST_FULL_OFFSET = 12'b0;
|
|
||||||
parameter [12:0] ALMOST_EMPTY_OFFSET = 12'b0;
|
|
||||||
|
|
||||||
// Port Widths
|
|
||||||
parameter A_WIDTH = 0;
|
|
||||||
parameter B_WIDTH = 0;
|
|
||||||
|
|
||||||
// RAM and Write Modes
|
|
||||||
parameter RAM_MODE = "SDP"; // "TPD" or "SDP"
|
|
||||||
parameter FIFO_MODE = "SYNC"; // "ASYNC" or "SYNC"
|
|
||||||
|
|
||||||
// Inverting Control Pins
|
|
||||||
parameter A_CLK_INV = 1'b0;
|
|
||||||
parameter B_CLK_INV = 1'b0;
|
|
||||||
parameter A_EN_INV = 1'b0;
|
|
||||||
parameter B_EN_INV = 1'b0;
|
|
||||||
parameter A_WE_INV = 1'b0;
|
|
||||||
parameter B_WE_INV = 1'b0;
|
|
||||||
|
|
||||||
// Output Register
|
|
||||||
parameter A_DO_REG = 1'b0;
|
|
||||||
parameter B_DO_REG = 1'b0;
|
|
||||||
|
|
||||||
// Error Checking and Correction
|
|
||||||
parameter A_ECC_EN = 1'b0;
|
|
||||||
parameter B_ECC_EN = 1'b0;
|
|
||||||
endmodule
|
|
||||||
|
|
|
@ -733,13 +733,12 @@ module CC_BRAM_20K (
|
||||||
// SDP read port
|
// SDP read port
|
||||||
always @(posedge clkb)
|
always @(posedge clkb)
|
||||||
begin
|
begin
|
||||||
// "NO_CHANGE" only
|
|
||||||
for (k=0; k < B_RD_WIDTH; k=k+1) begin
|
for (k=0; k < B_RD_WIDTH; k=k+1) begin
|
||||||
if (k < 20) begin
|
if (k < 20) begin
|
||||||
if (enb && !wea) A_DO_out[k] <= memory[addrb+k];
|
if (enb) A_DO_out[k] <= memory[addrb+k];
|
||||||
end
|
end
|
||||||
else begin // use both ports
|
else begin // use both ports
|
||||||
if (enb && !wea) B_DO_out[k-20] <= memory[addrb+k];
|
if (enb) B_DO_out[k-20] <= memory[addrb+k];
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1274,13 +1273,12 @@ module CC_BRAM_40K (
|
||||||
// SDP read port
|
// SDP read port
|
||||||
always @(posedge clkb)
|
always @(posedge clkb)
|
||||||
begin
|
begin
|
||||||
// "NO_CHANGE" only
|
|
||||||
for (k=0; k < B_RD_WIDTH; k=k+1) begin
|
for (k=0; k < B_RD_WIDTH; k=k+1) begin
|
||||||
if (k < 40) begin
|
if (k < 40) begin
|
||||||
if (enb && !wea) A_DO_out[k] <= memory[addrb+k];
|
if (enb) A_DO_out[k] <= memory[addrb+k];
|
||||||
end
|
end
|
||||||
else begin // use both ports
|
else begin // use both ports
|
||||||
if (enb && !wea) B_DO_out[k-40] <= memory[addrb+k];
|
if (enb) B_DO_out[k-40] <= memory[addrb+k];
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1412,6 +1410,393 @@ module CC_BRAM_40K (
|
||||||
endgenerate
|
endgenerate
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
module CC_FIFO_40K (
|
||||||
|
output A_ECC_1B_ERR,
|
||||||
|
output B_ECC_1B_ERR,
|
||||||
|
output A_ECC_2B_ERR,
|
||||||
|
output B_ECC_2B_ERR,
|
||||||
|
// FIFO pop port
|
||||||
|
output [39:0] A_DO,
|
||||||
|
output [39:0] B_DO,
|
||||||
|
(* clkbuf_sink *)
|
||||||
|
input A_CLK,
|
||||||
|
input A_EN,
|
||||||
|
// FIFO push port
|
||||||
|
input [39:0] A_DI,
|
||||||
|
input [39:0] B_DI,
|
||||||
|
input [39:0] A_BM,
|
||||||
|
input [39:0] B_BM,
|
||||||
|
(* clkbuf_sink *)
|
||||||
|
input B_CLK,
|
||||||
|
input B_EN,
|
||||||
|
input B_WE,
|
||||||
|
// FIFO control
|
||||||
|
input F_RST_N,
|
||||||
|
input [14:0] F_ALMOST_FULL_OFFSET,
|
||||||
|
input [14:0] F_ALMOST_EMPTY_OFFSET,
|
||||||
|
// FIFO status signals
|
||||||
|
output F_FULL,
|
||||||
|
output F_EMPTY,
|
||||||
|
output F_ALMOST_FULL,
|
||||||
|
output F_ALMOST_EMPTY,
|
||||||
|
output F_RD_ERROR,
|
||||||
|
output F_WR_ERROR,
|
||||||
|
output [15:0] F_RD_PTR,
|
||||||
|
output [15:0] F_WR_PTR
|
||||||
|
);
|
||||||
|
// Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED
|
||||||
|
parameter LOC = "UNPLACED";
|
||||||
|
|
||||||
|
// Offset configuration
|
||||||
|
parameter DYN_STAT_SELECT = 1'b0;
|
||||||
|
parameter [14:0] ALMOST_FULL_OFFSET = 15'b0;
|
||||||
|
parameter [14:0] ALMOST_EMPTY_OFFSET = 15'b0;
|
||||||
|
|
||||||
|
// Port Widths
|
||||||
|
parameter A_WIDTH = 0;
|
||||||
|
parameter B_WIDTH = 0;
|
||||||
|
|
||||||
|
// RAM and Write Modes
|
||||||
|
parameter RAM_MODE = "TDP"; // "TDP" or "SDP"
|
||||||
|
parameter FIFO_MODE = "SYNC"; // "ASYNC" or "SYNC"
|
||||||
|
|
||||||
|
// Inverting Control Pins
|
||||||
|
parameter A_CLK_INV = 1'b0;
|
||||||
|
parameter B_CLK_INV = 1'b0;
|
||||||
|
parameter A_EN_INV = 1'b0;
|
||||||
|
parameter B_EN_INV = 1'b0;
|
||||||
|
parameter A_WE_INV = 1'b0;
|
||||||
|
parameter B_WE_INV = 1'b0;
|
||||||
|
|
||||||
|
// Output Register
|
||||||
|
parameter A_DO_REG = 1'b0;
|
||||||
|
parameter B_DO_REG = 1'b0;
|
||||||
|
|
||||||
|
// Error Checking and Correction
|
||||||
|
parameter A_ECC_EN = 1'b0;
|
||||||
|
parameter B_ECC_EN = 1'b0;
|
||||||
|
|
||||||
|
integer i, k;
|
||||||
|
|
||||||
|
// 512 x 80 bit
|
||||||
|
reg [40959:0] memory = 40960'b0;
|
||||||
|
|
||||||
|
reg [15:0] counter_max;
|
||||||
|
reg [15:0] sram_depth;
|
||||||
|
localparam tp = (A_WIDTH == 1) ? 15 :
|
||||||
|
(A_WIDTH == 2) ? 14 :
|
||||||
|
(A_WIDTH == 5) ? 13 :
|
||||||
|
(A_WIDTH == 10) ? 12 :
|
||||||
|
(A_WIDTH == 20) ? 11 :
|
||||||
|
(A_WIDTH == 40) ? 10 : 9;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// Check parameters
|
||||||
|
if ((RAM_MODE != "SDP") && (RAM_MODE != "TDP")) begin
|
||||||
|
$display("ERROR: Illegal RAM MODE %d.", RAM_MODE);
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((FIFO_MODE != "ASYNC") && (FIFO_MODE != "SYNC")) begin
|
||||||
|
$display("ERROR: Illegal FIFO MODE %d.", FIFO_MODE);
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((RAM_MODE == "SDP") && (DYN_STAT_SELECT == 1)) begin
|
||||||
|
$display("ERROR: Dynamic offset configuration is not supported in %s mode.", RAM_MODE);
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((RAM_MODE == "SDP") && ((A_WIDTH != 80) || (B_WIDTH != 80))) begin
|
||||||
|
$display("ERROR: SDP is ony supported in 80 bit mode.");
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((A_WIDTH == 80) && (RAM_MODE == "TDP")) begin
|
||||||
|
$display("ERROR: Port A width of 80 bits is only supported in SDP mode.");
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((B_WIDTH == 80) && (RAM_MODE == "TDP")) begin
|
||||||
|
$display("ERROR: Port B width of 80 bits is only supported in SDP mode.");
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((A_WIDTH != 80) && (A_WIDTH != 40) && (A_WIDTH != 20) && (A_WIDTH != 10) &&
|
||||||
|
(A_WIDTH != 5) && (A_WIDTH != 2) && (A_WIDTH != 1) && (A_WIDTH != 0)) begin
|
||||||
|
$display("ERROR: Illegal %s Port A width configuration %d.", RAM_MODE, A_WIDTH);
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if ((B_WIDTH != 80) && (B_WIDTH != 40) && (B_WIDTH != 20) && (B_WIDTH != 10) &&
|
||||||
|
(B_WIDTH != 5) && (B_WIDTH != 2) && (B_WIDTH != 1) && (B_WIDTH != 0)) begin
|
||||||
|
$display("ERROR: Illegal %s Port B width configuration %d.", RAM_MODE, B_WIDTH);
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
if (A_WIDTH != B_WIDTH) begin
|
||||||
|
$display("ERROR: The values of A_WIDTH and B_WIDTH must be equal.");
|
||||||
|
end
|
||||||
|
if ((A_ECC_EN == 1'b1) && (RAM_MODE != "SDP") && (A_WIDTH != 40)) begin
|
||||||
|
$display("ERROR: Illegal ECC Port A configuration. ECC mode requires TDP >=40 bit or SDP 80 bit, but is %s %d.", RAM_MODE, A_WIDTH);
|
||||||
|
$finish();
|
||||||
|
end
|
||||||
|
// Set local parameters
|
||||||
|
if (A_WIDTH == 1) begin // A_WIDTH=B_WIDTH
|
||||||
|
counter_max = 2 * 32*1024 - 1;
|
||||||
|
sram_depth = 32*1024;
|
||||||
|
end
|
||||||
|
else if (A_WIDTH == 2) begin
|
||||||
|
counter_max = 2 * 16*1024 - 1;
|
||||||
|
sram_depth = 16*1024;
|
||||||
|
end
|
||||||
|
else if (A_WIDTH == 5) begin
|
||||||
|
counter_max = 2 * 8*1024 - 1;
|
||||||
|
sram_depth = 8*1024;
|
||||||
|
end
|
||||||
|
else if (A_WIDTH == 10) begin
|
||||||
|
counter_max = 2 * 4*1024 - 1;
|
||||||
|
sram_depth = 4*1024;
|
||||||
|
end
|
||||||
|
else if (A_WIDTH == 20) begin
|
||||||
|
counter_max = 2 * 2*1024 - 1;
|
||||||
|
sram_depth = 2*1024;
|
||||||
|
end
|
||||||
|
else if (A_WIDTH == 40) begin
|
||||||
|
counter_max = 2 * 1*1024 - 1;
|
||||||
|
sram_depth = 1*1024;
|
||||||
|
end
|
||||||
|
else begin // 80 bit SDP
|
||||||
|
counter_max = 2 * 512 - 1;
|
||||||
|
sram_depth = 512;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Internal signals
|
||||||
|
wire fifo_rdclk = A_CLK ^ A_CLK_INV;
|
||||||
|
wire fifo_wrclk = (FIFO_MODE == "ASYNC") ? (B_CLK ^ B_CLK_INV) : (A_CLK ^ A_CLK_INV);
|
||||||
|
wire [15:0] almost_full_offset = DYN_STAT_SELECT ? F_ALMOST_FULL_OFFSET : ALMOST_FULL_OFFSET;
|
||||||
|
wire [15:0] almost_empty_offset = DYN_STAT_SELECT ? F_ALMOST_EMPTY_OFFSET : ALMOST_EMPTY_OFFSET;
|
||||||
|
reg [39:0] A_DO_out = 0, A_DO_reg = 0;
|
||||||
|
reg [39:0] B_DO_out = 0, B_DO_reg = 0;
|
||||||
|
|
||||||
|
// Status signals
|
||||||
|
reg fifo_full;
|
||||||
|
reg fifo_empty;
|
||||||
|
reg fifo_almost_full;
|
||||||
|
reg fifo_almost_empty;
|
||||||
|
assign F_FULL = fifo_full;
|
||||||
|
assign F_EMPTY = fifo_empty;
|
||||||
|
assign F_ALMOST_FULL = fifo_almost_full;
|
||||||
|
assign F_ALMOST_EMPTY = fifo_almost_empty;
|
||||||
|
assign F_WR_ERROR = (F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV));
|
||||||
|
assign F_RD_ERROR = (F_EMPTY && (A_EN ^ A_EN_INV));
|
||||||
|
wire ram_we = (~F_FULL && (B_EN ^ B_EN_INV) && (B_WE ^ B_WE_INV));
|
||||||
|
wire ram_en = (~F_EMPTY && (A_EN ^ A_EN_INV));
|
||||||
|
|
||||||
|
// Reset synchronizers
|
||||||
|
reg [1:0] aclk_reset_q, bclk_reset_q;
|
||||||
|
wire fifo_sync_rstn = aclk_reset_q;
|
||||||
|
wire fifo_async_wrrstn = bclk_reset_q;
|
||||||
|
wire fifo_async_rdrstn = aclk_reset_q;
|
||||||
|
|
||||||
|
always @(posedge fifo_rdclk or negedge F_RST_N)
|
||||||
|
begin
|
||||||
|
if (F_RST_N == 1'b0) begin
|
||||||
|
aclk_reset_q <= 2'b0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
aclk_reset_q[1] <= aclk_reset_q[0];
|
||||||
|
aclk_reset_q[0] <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge fifo_wrclk or negedge F_RST_N)
|
||||||
|
begin
|
||||||
|
if (F_RST_N == 1'b0) begin
|
||||||
|
bclk_reset_q <= 2'b0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
bclk_reset_q[1] <= bclk_reset_q[0];
|
||||||
|
bclk_reset_q[0] <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Push/pop pointers
|
||||||
|
reg [15:0] rd_pointer, rd_pointer_int;
|
||||||
|
reg [15:0] wr_pointer, wr_pointer_int;
|
||||||
|
reg [15:0] rd_pointer_cmp, wr_pointer_cmp;
|
||||||
|
wire [15:0] rd_pointer_nxt;
|
||||||
|
wire [15:0] wr_pointer_nxt;
|
||||||
|
reg [15:0] fifo_rdaddr, rdaddr;
|
||||||
|
reg [15:0] fifo_wraddr, wraddr;
|
||||||
|
assign F_RD_PTR = fifo_rdaddr;
|
||||||
|
assign F_WR_PTR = fifo_wraddr;
|
||||||
|
|
||||||
|
always @(posedge fifo_rdclk or negedge F_RST_N)
|
||||||
|
begin
|
||||||
|
if (F_RST_N == 1'b0) begin
|
||||||
|
rd_pointer <= 0;
|
||||||
|
rd_pointer_int <= 0;
|
||||||
|
end
|
||||||
|
else if (ram_en) begin
|
||||||
|
rd_pointer <= rd_pointer_nxt;
|
||||||
|
rd_pointer_int <= rd_pointer_nxt[15:1] ^ rd_pointer_nxt[14:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign rd_pointer_nxt = (rd_pointer == counter_max) ? (0) : (rd_pointer + 1'b1);
|
||||||
|
|
||||||
|
always @(posedge fifo_wrclk or negedge F_RST_N)
|
||||||
|
begin
|
||||||
|
if (F_RST_N == 1'b0) begin
|
||||||
|
wr_pointer <= 0;
|
||||||
|
wr_pointer_int <= 0;
|
||||||
|
end
|
||||||
|
else if (ram_we) begin
|
||||||
|
wr_pointer <= wr_pointer_nxt;
|
||||||
|
wr_pointer_int <= wr_pointer_nxt[15:1] ^ wr_pointer_nxt[14:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign wr_pointer_nxt = (wr_pointer == counter_max) ? (0) : (wr_pointer + 1'b1);
|
||||||
|
|
||||||
|
// Address synchronizers
|
||||||
|
reg [15:0] rd_pointer_sync, wr_pointer_sync;
|
||||||
|
reg [15:0] rd_pointer_sync_0, rd_pointer_sync_1;
|
||||||
|
reg [15:0] wr_pointer_sync_0, wr_pointer_sync_1;
|
||||||
|
|
||||||
|
always @(posedge fifo_rdclk or negedge F_RST_N)
|
||||||
|
begin
|
||||||
|
if (F_RST_N == 1'b0) begin
|
||||||
|
wr_pointer_sync_0 <= 0;
|
||||||
|
wr_pointer_sync_1 <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
wr_pointer_sync_0 <= wraddr;
|
||||||
|
wr_pointer_sync_1 <= wr_pointer_sync_0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge fifo_wrclk or negedge F_RST_N)
|
||||||
|
begin
|
||||||
|
if (F_RST_N == 1'b0) begin
|
||||||
|
rd_pointer_sync_0 <= 0;
|
||||||
|
rd_pointer_sync_1 <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
rd_pointer_sync_0 <= rdaddr;
|
||||||
|
rd_pointer_sync_1 <= rd_pointer_sync_0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
fifo_wraddr = {wr_pointer[tp-1:0], {(15-tp){1'b0}}};
|
||||||
|
fifo_rdaddr = {rd_pointer[tp-1:0], {(15-tp){1'b0}}};
|
||||||
|
|
||||||
|
rdaddr = {rd_pointer[tp], rd_pointer_int[tp-1:0]};
|
||||||
|
wraddr = {{(15-tp){1'b0}}, wr_pointer[tp], wr_pointer_int[tp:0]};
|
||||||
|
|
||||||
|
if (FIFO_MODE == "ASYNC")
|
||||||
|
fifo_full = (wraddr[tp-2:0] == rd_pointer_sync_1[tp-2:0] ) && (wraddr[tp] != rd_pointer_sync_1[tp] ) && ( wraddr[tp-1] != rd_pointer_sync_1[tp-1] );
|
||||||
|
else
|
||||||
|
fifo_full = (wr_pointer[tp-1:0] == rd_pointer[tp-1:0]) && (wr_pointer[tp] ^ rd_pointer[tp]);
|
||||||
|
|
||||||
|
if (FIFO_MODE == "ASYNC")
|
||||||
|
fifo_empty = (wr_pointer_sync_1[tp:0] == rdaddr[tp:0]);
|
||||||
|
else
|
||||||
|
fifo_empty = (wr_pointer[tp:0] == rd_pointer[tp:0]);
|
||||||
|
|
||||||
|
rd_pointer_cmp = (FIFO_MODE == "ASYNC") ? rd_pointer_sync : rd_pointer;
|
||||||
|
if (wr_pointer[tp] == rd_pointer_cmp[tp])
|
||||||
|
fifo_almost_full = ((wr_pointer[tp-1:0] - rd_pointer_cmp[tp-1:0]) >= (sram_depth - almost_full_offset));
|
||||||
|
else
|
||||||
|
fifo_almost_full = ((rd_pointer_cmp[tp-1:0] - wr_pointer[tp-1:0]) <= almost_full_offset);
|
||||||
|
|
||||||
|
wr_pointer_cmp = (FIFO_MODE == "ASYNC") ? wr_pointer_sync : wr_pointer;
|
||||||
|
if (wr_pointer_cmp[tp] == rd_pointer[tp])
|
||||||
|
fifo_almost_empty = ((wr_pointer_cmp[tp-1:0] - rd_pointer[tp-1:0]) <= almost_empty_offset);
|
||||||
|
else
|
||||||
|
fifo_almost_empty = ((rd_pointer[tp-1:0] - wr_pointer_cmp[tp-1:0]) >= (sram_depth - almost_empty_offset));
|
||||||
|
end
|
||||||
|
|
||||||
|
generate
|
||||||
|
always @(*) begin
|
||||||
|
wr_pointer_sync = 0;
|
||||||
|
rd_pointer_sync = 0;
|
||||||
|
for (i=tp; i >= 0; i=i-1) begin
|
||||||
|
if (i == tp) begin
|
||||||
|
wr_pointer_sync[i] = wr_pointer_sync_1[i];
|
||||||
|
rd_pointer_sync[i] = rd_pointer_sync_1[i];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
wr_pointer_sync[i] = wr_pointer_sync_1[i] ^ wr_pointer_sync[i+1];
|
||||||
|
rd_pointer_sync[i] = rd_pointer_sync_1[i] ^ rd_pointer_sync[i+1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (RAM_MODE == "SDP") begin
|
||||||
|
// SDP push ports A+B
|
||||||
|
always @(posedge fifo_wrclk)
|
||||||
|
begin
|
||||||
|
for (k=0; k < A_WIDTH; k=k+1) begin
|
||||||
|
if (k < 40) begin
|
||||||
|
if (ram_we && A_BM[k]) memory[fifo_wraddr+k] <= A_DI[k];
|
||||||
|
end
|
||||||
|
else begin // use both ports
|
||||||
|
if (ram_we && B_BM[k-40]) memory[fifo_wraddr+k] <= B_DI[k-40];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
// SDP pop ports A+B
|
||||||
|
always @(posedge fifo_rdclk)
|
||||||
|
begin
|
||||||
|
for (k=0; k < B_WIDTH; k=k+1) begin
|
||||||
|
if (k < 40) begin
|
||||||
|
if (ram_en) A_DO_out[k] <= memory[fifo_rdaddr+k];
|
||||||
|
end
|
||||||
|
else begin // use both ports
|
||||||
|
if (ram_en) B_DO_out[k-40] <= memory[fifo_rdaddr+k];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else if (RAM_MODE == "TDP") begin
|
||||||
|
// TDP pop port A
|
||||||
|
always @(posedge fifo_rdclk)
|
||||||
|
begin
|
||||||
|
for (i=0; i < A_WIDTH; i=i+1) begin
|
||||||
|
if (ram_en) begin
|
||||||
|
A_DO_out[i] <= memory[fifo_rdaddr+i];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
// TDP push port B
|
||||||
|
always @(posedge fifo_wrclk)
|
||||||
|
begin
|
||||||
|
for (i=0; i < B_WIDTH; i=i+1) begin
|
||||||
|
if (ram_we && B_BM[i])
|
||||||
|
memory[fifo_wraddr+i] <= B_DI[i];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// Optional output register
|
||||||
|
generate
|
||||||
|
if (A_DO_REG) begin
|
||||||
|
always @(posedge fifo_rdclk) begin
|
||||||
|
A_DO_reg <= A_DO_out;
|
||||||
|
end
|
||||||
|
assign A_DO = A_DO_reg;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
assign A_DO = A_DO_out;
|
||||||
|
end
|
||||||
|
if (B_DO_REG) begin
|
||||||
|
always @(posedge fifo_rdclk) begin
|
||||||
|
B_DO_reg <= B_DO_out;
|
||||||
|
end
|
||||||
|
assign B_DO = B_DO_reg;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
assign B_DO = B_DO_out;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
endmodule
|
||||||
|
|
||||||
// Models of the LUT2 tree primitives
|
// Models of the LUT2 tree primitives
|
||||||
module CC_L2T4(
|
module CC_L2T4(
|
||||||
output O,
|
output O,
|
||||||
|
|
|
@ -26,10 +26,12 @@ proc
|
||||||
equiv_opt -assert -map +/anlogic/cells_sim.v synth_anlogic # equivalency check
|
equiv_opt -assert -map +/anlogic/cells_sim.v synth_anlogic # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd mux8 # Constrain all select calls below inside the top module
|
cd mux8 # Constrain all select calls below inside the top module
|
||||||
select -assert-count 3 t:AL_MAP_LUT4
|
select -assert-max 3 t:AL_MAP_LUT3
|
||||||
select -assert-count 1 t:AL_MAP_LUT6
|
select -assert-max 3 t:AL_MAP_LUT4
|
||||||
|
select -assert-max 1 t:AL_MAP_LUT5
|
||||||
|
select -assert-max 1 t:AL_MAP_LUT6
|
||||||
|
|
||||||
select -assert-none t:AL_MAP_LUT4 t:AL_MAP_LUT6 %% t:* %D
|
select -assert-none t:AL_MAP_LUT3 t:AL_MAP_LUT4 t:AL_MAP_LUT5 t:AL_MAP_LUT6 %% t:* %D
|
||||||
|
|
||||||
design -load read
|
design -load read
|
||||||
hierarchy -top mux16
|
hierarchy -top mux16
|
||||||
|
|
|
@ -28,8 +28,8 @@ equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd mux8 # Constrain all select calls below inside the top module
|
cd mux8 # Constrain all select calls below inside the top module
|
||||||
select -assert-max 1 t:L6MUX21
|
select -assert-max 1 t:L6MUX21
|
||||||
select -assert-max 7 t:LUT4
|
select -assert-max 8 t:LUT4
|
||||||
select -assert-max 2 t:PFUMX
|
select -assert-max 3 t:PFUMX
|
||||||
|
|
||||||
select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D
|
select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,6 @@ cd fsm # Constrain all select calls below inside the top module
|
||||||
select -assert-count 1 t:CC_BUFG
|
select -assert-count 1 t:CC_BUFG
|
||||||
select -assert-count 6 t:CC_DFF
|
select -assert-count 6 t:CC_DFF
|
||||||
select -assert-max 5 t:CC_LUT2
|
select -assert-max 5 t:CC_LUT2
|
||||||
select -assert-max 4 t:CC_LUT3
|
select -assert-max 6 t:CC_LUT3
|
||||||
select -assert-max 9 t:CC_LUT4
|
select -assert-max 9 t:CC_LUT4
|
||||||
select -assert-none t:CC_BUFG t:CC_DFF t:CC_LUT2 t:CC_LUT3 t:CC_LUT4 %% t:* %D
|
select -assert-none t:CC_BUFG t:CC_DFF t:CC_LUT2 t:CC_LUT3 t:CC_LUT4 %% t:* %D
|
||||||
|
|
|
@ -4,5 +4,5 @@ flatten
|
||||||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd top # Constrain all select calls below inside the top module
|
cd top # Constrain all select calls below inside the top module
|
||||||
select -assert-count 5 t:SB_LUT4
|
select -assert-max 6 t:SB_LUT4
|
||||||
select -assert-none t:SB_LUT4 %% t:* %D
|
select -assert-none t:SB_LUT4 %% t:* %D
|
||||||
|
|
|
@ -69,7 +69,7 @@ proc
|
||||||
equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclonev -noiopad -noclkbuf # equivalency check
|
equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclonev -noiopad -noclkbuf # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd mux16 # Constrain all select calls below inside the top module
|
cd mux16 # Constrain all select calls below inside the top module
|
||||||
select -assert-count 1 t:MISTRAL_ALUT3
|
select -assert-max 1 t:MISTRAL_ALUT3
|
||||||
select -assert-max 2 t:MISTRAL_ALUT5
|
select -assert-max 2 t:MISTRAL_ALUT5
|
||||||
select -assert-max 5 t:MISTRAL_ALUT6
|
select -assert-max 5 t:MISTRAL_ALUT6
|
||||||
select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
|
select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
|
||||||
|
@ -81,8 +81,8 @@ proc
|
||||||
equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclone10gx -noiopad -noclkbuf # equivalency check
|
equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cyclone10gx -noiopad -noclkbuf # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd mux16 # Constrain all select calls below inside the top module
|
cd mux16 # Constrain all select calls below inside the top module
|
||||||
select -assert-count 1 t:MISTRAL_ALUT3
|
select -assert-max 1 t:MISTRAL_ALUT3
|
||||||
select -assert-count 2 t:MISTRAL_ALUT5
|
select -assert-max 2 t:MISTRAL_ALUT5
|
||||||
select -assert-count 4 t:MISTRAL_ALUT6
|
select -assert-max 5 t:MISTRAL_ALUT6
|
||||||
|
|
||||||
select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
|
select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
|
||||||
|
|
|
@ -36,8 +36,7 @@ proc
|
||||||
equiv_opt -assert -map +/nexus/cells_sim.v synth_nexus # equivalency check
|
equiv_opt -assert -map +/nexus/cells_sim.v synth_nexus # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd mux16 # Constrain all select calls below inside the top module
|
cd mux16 # Constrain all select calls below inside the top module
|
||||||
select -assert-min 11 t:LUT4
|
|
||||||
select -assert-max 12 t:LUT4
|
select -assert-max 12 t:LUT4
|
||||||
select -assert-count 1 t:WIDEFN9
|
select -assert-max 2 t:WIDEFN9
|
||||||
|
|
||||||
select -assert-none t:IB t:OB t:VLO t:VHI t:LUT4 t:WIDEFN9 %% t:* %D
|
select -assert-none t:IB t:OB t:VLO t:VHI t:LUT4 t:WIDEFN9 %% t:* %D
|
||||||
|
|
|
@ -30,12 +30,13 @@ proc
|
||||||
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check
|
equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check
|
||||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||||
cd mux8 # Constrain all select calls below inside the top module
|
cd mux8 # Constrain all select calls below inside the top module
|
||||||
select -assert-count 4 t:LUT1
|
select -assert-max 5 t:LUT1
|
||||||
select -assert-count 3 t:LUT4
|
select -assert-max 3 t:LUT3
|
||||||
select -assert-count 2 t:MUXF5
|
select -assert-max 3 t:LUT4
|
||||||
|
select -assert-max 3 t:MUXF5
|
||||||
select -assert-count 1 t:MUXF6
|
select -assert-count 1 t:MUXF6
|
||||||
|
|
||||||
select -assert-none t:LUT1 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D
|
select -assert-none t:LUT1 t:LUT3 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D
|
||||||
|
|
||||||
|
|
||||||
design -load read
|
design -load read
|
||||||
|
|
51
tests/various/constant_drive_conflict.ys
Normal file
51
tests/various/constant_drive_conflict.ys
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
read_verilog <<EOT
|
||||||
|
module top(input A, output Y);
|
||||||
|
assign A = 1;
|
||||||
|
|
||||||
|
assign Y = A;
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy -top top; proc
|
||||||
|
|
||||||
|
logger -expect warning "Drivers conflicting with a constant" 1
|
||||||
|
logger -expect log "Found and reported 1 problems." 1
|
||||||
|
check
|
||||||
|
logger -check-expected
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
read_verilog <<EOT
|
||||||
|
module top(input A, output Y);
|
||||||
|
buffer some_buffer(A, Y);
|
||||||
|
assign Y = 1;
|
||||||
|
endmodule
|
||||||
|
module buffer(input A, output Y);
|
||||||
|
assign Y = A;
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy -top top; proc
|
||||||
|
|
||||||
|
logger -expect warning "Drivers conflicting with a constant" 1
|
||||||
|
logger -expect log "Found and reported 1 problems." 1
|
||||||
|
check
|
||||||
|
logger -check-expected
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
read_verilog <<EOT
|
||||||
|
module top(input clk, input A, output Y);
|
||||||
|
reg Q;
|
||||||
|
always @(posedge clk) Q <= A;
|
||||||
|
|
||||||
|
assign Q = 1;
|
||||||
|
|
||||||
|
assign Y = A;
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy -top top
|
||||||
|
|
||||||
|
logger -expect warning "Drivers conflicting with a constant" 1
|
||||||
|
logger -expect log "Found and reported 1 problems." 1
|
||||||
|
check
|
||||||
|
logger -check-expected
|
Loading…
Add table
Add a link
Reference in a new issue