mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-16 13:58:47 +00:00
Merge branch 'YosysHQ:master' into issue/2616
This commit is contained in:
commit
f9f7a4e9db
15
CHANGELOG
15
CHANGELOG
|
@ -2,9 +2,22 @@
|
|||
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
|
||||
--------------------------
|
||||
* New commands and options
|
||||
|
|
23
Makefile
23
Makefile
|
@ -141,7 +141,7 @@ LDLIBS += -lrt
|
|||
endif
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.29+44
|
||||
YOSYS_VER := 0.30+48
|
||||
|
||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||
|
@ -157,7 +157,7 @@ endif
|
|||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
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
|
||||
#
|
||||
|
@ -165,7 +165,7 @@ bumpversion:
|
|||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||
# will remove the 'abc' directory and you do not want to accidentally
|
||||
# delete your work on ABC..
|
||||
ABCREV = 2c1c83f
|
||||
ABCREV = bb64142
|
||||
ABCPULL = 1
|
||||
ABCURL ?= https://github.com/YosysHQ/abc
|
||||
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)
|
||||
endif
|
||||
|
||||
LDLIBS_VERIFIC =
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
||||
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
|
||||
|
@ -548,9 +549,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS
|
|||
endif
|
||||
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -740,13 +741,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
|
|||
endif
|
||||
|
||||
$(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))
|
||||
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
|
||||
$(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
|
||||
|
||||
%.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 \
|
||||
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
||||
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; \
|
||||
fi
|
||||
# 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"; \
|
||||
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; }; \
|
||||
echo "Pulling ABC from $(ABCURL):"; set -x; \
|
||||
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);
|
||||
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++) {
|
||||
if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr)
|
||||
continue;
|
||||
|
|
|
@ -728,7 +728,10 @@ struct BtorWorker
|
|||
else
|
||||
btorf("%d state %d %s\n", nid, sid, log_id(symbol));
|
||||
|
||||
ywmap_state(sig_q);
|
||||
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);
|
||||
|
||||
if (nid_init_val >= 0) {
|
||||
int nid_init = next_nid++;
|
||||
|
|
|
@ -1595,6 +1595,25 @@ value<BitsY> modfloor_ss(const value<BitsA> &a, const value<BitsB> &b) {
|
|||
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
|
||||
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($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($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)
|
||||
|
|
|
@ -120,6 +120,9 @@ struct EdifBackend : public Backend {
|
|||
log(" sets the delimiting character for module port rename clauses to\n");
|
||||
log(" parentheses, square brackets, or angle brackets.\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("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");
|
||||
|
@ -132,6 +135,7 @@ struct EdifBackend : public Backend {
|
|||
std::string top_module_name;
|
||||
bool port_rename = false;
|
||||
bool attr_properties = false;
|
||||
bool lsbidx = false;
|
||||
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
||||
bool nogndvcc = false, gndvccy = false, keepmode = false;
|
||||
CellTypes ct(design);
|
||||
|
@ -173,6 +177,10 @@ struct EdifBackend : public Backend {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-lsbidx") {
|
||||
lsbidx = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
@ -184,6 +192,14 @@ struct EdifBackend : public Backend {
|
|||
|
||||
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())
|
||||
continue;
|
||||
|
||||
|
@ -200,7 +216,7 @@ struct EdifBackend : public Backend {
|
|||
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
||||
lib_cell_ports[cell->type];
|
||||
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";
|
||||
for (int i = 0; i < wire->width; 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",
|
||||
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
||||
else {
|
||||
int member_idx = GetSize(sig)-i-1;
|
||||
int member_idx = lsbidx ? i : GetSize(sig)-i-1;
|
||||
auto m = design->module(cell->type);
|
||||
int width = sig.size();
|
||||
if (m) {
|
||||
auto w = m->wire(p.first);
|
||||
if (w) {
|
||||
member_idx = GetSize(w)-i-1;
|
||||
member_idx = lsbidx ? i : GetSize(w)-i-1;
|
||||
width = GetSize(w);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -626,8 +626,9 @@ struct Smt2Worker
|
|||
}
|
||||
|
||||
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;
|
||||
for (auto chunk : cell->getPort(QY).chunks()) {
|
||||
for (auto chunk : cell->getPort(clk2fflogic ? ID::D : QY).chunks()) {
|
||||
if (chunk.is_wire())
|
||||
decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset));
|
||||
smtoffset += chunk.width;
|
||||
|
@ -772,7 +773,7 @@ struct Smt2Worker
|
|||
int arrayid = idcounter++;
|
||||
memarrays[mem] = arrayid;
|
||||
|
||||
int abits = ceil_log2(mem->size);
|
||||
int abits = max(1, ceil_log2(mem->size));
|
||||
|
||||
bool has_sync_wr = false;
|
||||
bool has_async_wr = false;
|
||||
|
@ -1219,7 +1220,7 @@ struct Smt2Worker
|
|||
{
|
||||
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_async_wr = false;
|
||||
|
|
|
@ -174,6 +174,8 @@ def help():
|
|||
further failed assertions. To output multiple traces
|
||||
covering all found failed assertions, the character '%' is
|
||||
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 that the used witness file contains sufficient
|
||||
|
@ -1739,7 +1741,7 @@ elif covermode:
|
|||
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))
|
||||
|
||||
if found_failed_assert:
|
||||
if found_failed_assert and not keep_going:
|
||||
break
|
||||
|
||||
if "1" not in cover_mask:
|
||||
|
|
|
@ -768,7 +768,7 @@ class SmtIo:
|
|||
|
||||
if self.timeinfo:
|
||||
i = 0
|
||||
s = "/-\|"
|
||||
s = r"/-\|"
|
||||
|
||||
count = 0
|
||||
num_bs = 0
|
||||
|
@ -1171,7 +1171,7 @@ class MkVcd:
|
|||
|
||||
def escape_name(self, 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
|
||||
return name
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ def aiw2yw(input, mapfile, output):
|
|||
|
||||
values = WitnessValues()
|
||||
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
|
||||
|
||||
try:
|
||||
|
|
|
@ -571,7 +571,7 @@ The ``$mem_v2`` cell has the following ports:
|
|||
signals for the read ports.
|
||||
|
||||
``\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.
|
||||
|
||||
``\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_Optimize.rst
|
||||
CHAPTER_Techmap.rst
|
||||
CHAPTER_Memorymap.rst
|
||||
CHAPTER_Eval.rst
|
||||
|
||||
.. 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)) : "";
|
||||
message += vstringf(msg, args);
|
||||
|
||||
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());
|
||||
else
|
||||
log("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||
|
||||
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)
|
||||
log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||
else
|
||||
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))
|
||||
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)
|
||||
{
|
||||
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::ABITS] = GetSize(addr);
|
||||
cell->parameters[ID::WIDTH] = GetSize(data);
|
||||
import_attributes(cell->attributes, inst);
|
||||
cell->setPort(ID::CLK, RTLIL::State::Sx);
|
||||
cell->setPort(ID::EN, RTLIL::State::Sx);
|
||||
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::ABITS] = GetSize(addr);
|
||||
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::CLK, RTLIL::State::S0);
|
||||
cell->setPort(ID::ADDR, addr);
|
||||
|
@ -1994,7 +2007,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ bool log_quiet_warnings = false;
|
|||
int log_verbose_level;
|
||||
string log_last_error;
|
||||
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_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_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.
|
||||
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));
|
||||
|
|
|
@ -178,6 +178,8 @@ class WType:
|
|||
t.cont = None
|
||||
t.attr_type = attr_types.default
|
||||
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)
|
||||
if candidate == None:
|
||||
return None
|
||||
|
@ -203,8 +205,12 @@ class WType:
|
|||
|
||||
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 "):
|
||||
prefix = "unsigned "
|
||||
prefix = "unsigned " + prefix
|
||||
str_def = str_def[9:]
|
||||
while str.startswith(str_def, "long "):
|
||||
prefix= "long " + prefix
|
||||
|
@ -1285,7 +1291,7 @@ class WFunction:
|
|||
prefix = ""
|
||||
i = 0
|
||||
for part in parts:
|
||||
if part in ["unsigned", "long", "short"]:
|
||||
if part in ["unsigned", "long", "short", "const"]:
|
||||
prefix += part + " "
|
||||
i += 1
|
||||
else:
|
||||
|
@ -1361,10 +1367,17 @@ class WFunction:
|
|||
func.args.append(parsed)
|
||||
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):
|
||||
self.alias = self.name
|
||||
for arg in self.args:
|
||||
self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","")
|
||||
self.alias = self.mangled_name
|
||||
|
||||
def gen_decl(self):
|
||||
if self.duplicate:
|
||||
|
@ -2190,12 +2203,15 @@ def clean_duplicates():
|
|||
for fun in class_.found_funs:
|
||||
if fun.gen_decl_hash_py() in known_decls:
|
||||
debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
|
||||
|
||||
other = known_decls[fun.gen_decl_hash_py()]
|
||||
other.gen_alias()
|
||||
fun.gen_alias()
|
||||
if fun.gen_decl_hash_py() == other.gen_decl_hash_py():
|
||||
if fun.mangled_name == other.mangled_name:
|
||||
fun.duplicate = True
|
||||
debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
|
||||
continue
|
||||
|
||||
other.gen_alias()
|
||||
fun.gen_alias()
|
||||
else:
|
||||
known_decls[fun.gen_decl_hash_py()] = fun
|
||||
known_decls = []
|
||||
|
|
|
@ -112,11 +112,10 @@ struct CheckPass : public Pass {
|
|||
for (size_t i = 0; i < all_cases.size(); i++) {
|
||||
for (auto action : all_cases[i]->actions) {
|
||||
for (auto bit : sigmap(action.first))
|
||||
if (bit.wire) {
|
||||
wire_drivers[bit].push_back(
|
||||
stringf("action %s <= %s (case rule) in process %s",
|
||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||
}
|
||||
wire_drivers[bit].push_back(
|
||||
stringf("action %s <= %s (case rule) in process %s",
|
||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||
|
||||
for (auto bit : sigmap(action.second))
|
||||
if (bit.wire) used_wires.insert(bit);
|
||||
}
|
||||
|
@ -134,10 +133,9 @@ struct CheckPass : public Pass {
|
|||
if (bit.wire) used_wires.insert(bit);
|
||||
for (auto action : sync->actions) {
|
||||
for (auto bit : sigmap(action.first))
|
||||
if (bit.wire)
|
||||
wire_drivers[bit].push_back(
|
||||
stringf("action %s <= %s (sync rule) in process %s",
|
||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||
wire_drivers[bit].push_back(
|
||||
stringf("action %s <= %s (sync rule) in process %s",
|
||||
log_signal(action.first), log_signal(action.second), log_id(proc_it.first)));
|
||||
for (auto bit : sigmap(action.second))
|
||||
if (bit.wire) used_wires.insert(bit);
|
||||
}
|
||||
|
@ -176,7 +174,8 @@ struct CheckPass : public Pass {
|
|||
if (logic_cell)
|
||||
topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
|
||||
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)",
|
||||
log_id(conn.first), i, log_id(cell), log_id(cell->type)));
|
||||
}
|
||||
|
@ -192,7 +191,8 @@ struct CheckPass : public Pass {
|
|||
if (wire->port_input) {
|
||||
SigSpec sig = sigmap(wire);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
|
||||
if (sig[i].wire || !wire->port_output)
|
||||
wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
|
||||
}
|
||||
if (wire->port_output)
|
||||
for (auto bit : sigmap(wire))
|
||||
|
@ -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)
|
||||
if (wire_drivers_count[it.first] > 1) {
|
||||
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))) {
|
||||
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);
|
||||
|
||||
for (auto chunk : sig_out.chunks()) {
|
||||
|
@ -151,7 +156,10 @@ static bool rename_witness(RTLIL::Design *design, dict<RTLIL::Module *, int> &ca
|
|||
auto new_id = module->uniquify("\\_witness_." + name);
|
||||
auto new_wire = module->addWire(new_id, GetSize(sig_out));
|
||||
new_wire->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 });
|
||||
module->connect({sig_out, new_wire});
|
||||
if (clk2fflogic)
|
||||
module->connect({new_wire, sig_out});
|
||||
else
|
||||
module->connect({sig_out, new_wire});
|
||||
cell->setPort(QY, new_wire);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ struct ShowWorker
|
|||
std::string nextColor()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -97,19 +97,16 @@ struct ShowWorker
|
|||
|
||||
std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor)
|
||||
{
|
||||
sig.sort_and_unify();
|
||||
for (auto &c : sig.chunks()) {
|
||||
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());
|
||||
}
|
||||
std::string color = findColor(sig);
|
||||
if (!color.empty()) return color;
|
||||
return 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)
|
||||
|
@ -131,12 +128,28 @@ struct ShowWorker
|
|||
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)
|
||||
if (s.second.selected_member(module->name, member_name)) {
|
||||
dot_escape_store.push_back(stringf(", color=\"%s\"", s.first.c_str()));
|
||||
return dot_escape_store.back().c_str();
|
||||
return stringf("color=\"%s\", fontcolor=\"%s\"", s.first.c_str(), s.first.c_str());
|
||||
}
|
||||
|
||||
RTLIL::Const colorattr_value;
|
||||
|
@ -155,8 +168,7 @@ struct ShowWorker
|
|||
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 dot_escape_store.back().c_str();
|
||||
return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", colorattr_cache.at(colorattr_value), colorattr_cache.at(colorattr_value));
|
||||
}
|
||||
|
||||
const char *findLabel(std::string member_name)
|
||||
|
@ -189,6 +201,12 @@ struct ShowWorker
|
|||
if (id[0] == '\\')
|
||||
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;
|
||||
for (char ch : id) {
|
||||
if (ch == '\\') {
|
||||
|
@ -196,7 +214,7 @@ struct ShowWorker
|
|||
str += "╲";
|
||||
continue;
|
||||
}
|
||||
if (ch == '"')
|
||||
if (ch == '"' || ch == '<' || ch == '>')
|
||||
str += "\\";
|
||||
str += ch;
|
||||
}
|
||||
|
@ -317,7 +335,7 @@ struct ShowWorker
|
|||
}
|
||||
|
||||
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()) {
|
||||
currentColor = xorshift32(currentColor);
|
||||
|
@ -414,9 +432,9 @@ struct ShowWorker
|
|||
if (wire->port_input || wire->port_output)
|
||||
shape = "octagon";
|
||||
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()),
|
||||
nextColor(RTLIL::SigSpec(wire), "color=\"black\"").c_str());
|
||||
nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str());
|
||||
if (wire->port_input)
|
||||
all_sources.insert(stringf("n%d", id2num(wire->name)));
|
||||
else if (wire->port_output)
|
||||
|
@ -481,11 +499,11 @@ struct ShowWorker
|
|||
#ifdef CLUSTER_CELLS_AND_PORTBOXES
|
||||
if (!code.empty())
|
||||
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
|
||||
#endif
|
||||
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());
|
||||
fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s",
|
||||
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str());
|
||||
}
|
||||
|
||||
for (auto &it : module->processes)
|
||||
|
@ -555,9 +573,9 @@ struct ShowWorker
|
|||
} else if (right_node[0] == 'x') {
|
||||
net_conn_map[left_node].out.insert({right_node, GetSize(conn.first)});
|
||||
} else {
|
||||
net_conn_map[right_node].in.insert({stringf("x%d:e", single_idx_count), GetSize(conn.first)});
|
||||
net_conn_map[left_node].out.insert({stringf("x%d:w", single_idx_count), GetSize(conn.first)});
|
||||
fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++);
|
||||
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", single_idx_count), GetSize(conn.first)});
|
||||
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(" Run the specified command with the graphics file as parameter.\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(" -format <format>\n");
|
||||
log(" Generate a graphics file in the specified format. Use 'dot' to just\n");
|
||||
|
@ -903,28 +922,30 @@ struct ShowPass : public Pass {
|
|||
#if defined(YOSYS_DISABLE_SPAWN)
|
||||
log_assert(viewer_exe.empty() && !format.empty());
|
||||
#else
|
||||
if (!viewer_exe.empty()) {
|
||||
#ifdef _WIN32
|
||||
// system()/cmd.exe does not understand single quotes nor
|
||||
// background tasks on Windows. So we have to pause yosys
|
||||
// until the viewer exits.
|
||||
std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str());
|
||||
#else
|
||||
std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str());
|
||||
#endif
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
} else
|
||||
if (format.empty()) {
|
||||
#ifdef __APPLE__
|
||||
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.c_str());
|
||||
#else
|
||||
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str());
|
||||
#endif
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
if (viewer_exe != "none") {
|
||||
if (!viewer_exe.empty()) {
|
||||
#ifdef _WIN32
|
||||
// system()/cmd.exe does not understand single quotes nor
|
||||
// background tasks on Windows. So we have to pause yosys
|
||||
// until the viewer exits.
|
||||
std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str());
|
||||
#else
|
||||
std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str());
|
||||
#endif
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
} else
|
||||
if (format.empty()) {
|
||||
#ifdef __APPLE__
|
||||
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.c_str());
|
||||
#else
|
||||
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str());
|
||||
#endif
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -292,10 +292,12 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
if (!purge_mode)
|
||||
for (auto &it : module->cells_) {
|
||||
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())
|
||||
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);
|
||||
}
|
||||
for (auto &it2 : cell->connections())
|
||||
connected_signals.add(it2.second);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ struct PruneWorker
|
|||
if (GetSize(new_lhs) == 0) {
|
||||
if (GetSize(conn_lhs) == 0)
|
||||
removed_count++;
|
||||
cs->actions.erase((it++).base() - 1);
|
||||
it = decltype(cs->actions)::reverse_iterator(cs->actions.erase(it.base() - 1));
|
||||
} else {
|
||||
it->first = new_lhs;
|
||||
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});
|
||||
}
|
||||
// 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);
|
||||
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));
|
||||
sampled_sig->attributes[ID::init] = init;
|
||||
|
||||
Cell *cell;
|
||||
if (is_fine)
|
||||
module->addFfGate(NEW_ID, sig, sampled_sig);
|
||||
cell = module->addFfGate(NEW_ID, sig, sampled_sig);
|
||||
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};
|
||||
}
|
||||
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)
|
||||
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) {
|
||||
// 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, SigBit> in_parent_drivers;
|
||||
dict<SigBit, SigBit> clk2fflogic_drivers;
|
||||
|
||||
pool<SigBit> dirty_bits;
|
||||
pool<Cell*> dirty_cells;
|
||||
|
@ -270,6 +271,11 @@ struct SimInstance
|
|||
ff.past_srst = State::Sx;
|
||||
ff.data = ff_data;
|
||||
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())
|
||||
|
@ -389,6 +395,10 @@ struct SimInstance
|
|||
auto sigbit = sig[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);
|
||||
if (in_parent_driver == in_parent_drivers.end())
|
||||
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;
|
||||
|
||||
|
@ -600,7 +610,7 @@ struct SimInstance
|
|||
|
||||
Const current_q = get_state(ff.data.sig_q);
|
||||
|
||||
if (ff_data.has_clk) {
|
||||
if (ff_data.has_clk && !stable_past_update) {
|
||||
// flip-flops
|
||||
State current_clk = get_state(ff_data.sig_clk)[0];
|
||||
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) {
|
||||
State current_aload = get_state(ff_data.sig_aload)[0];
|
||||
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
|
||||
|
@ -672,6 +682,8 @@ struct SimInstance
|
|||
}
|
||||
else
|
||||
{
|
||||
if (stable_past_update)
|
||||
continue;
|
||||
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::S0 || get_state(port.clk) != State::S0))
|
||||
|
@ -701,7 +713,7 @@ struct SimInstance
|
|||
}
|
||||
|
||||
for (auto it : children)
|
||||
if (it.second->update_ph2(gclk)) {
|
||||
if (it.second->update_ph2(gclk, stable_past_update)) {
|
||||
dirty_children.insert(it.second);
|
||||
did_something = true;
|
||||
}
|
||||
|
@ -1197,9 +1209,21 @@ struct SimWorker : SimShared
|
|||
|
||||
void initialize_stable_past()
|
||||
{
|
||||
if (debug)
|
||||
log("\n-- ph1 (initialize) --\n");
|
||||
top->update_ph1();
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (debug)
|
||||
log("\n-- ph1 (initialize) --\n");
|
||||
|
||||
top->update_ph1();
|
||||
|
||||
if (debug)
|
||||
log("\n-- ph2 (initialize) --\n");
|
||||
|
||||
if (!top->update_ph2(false, true))
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
log("\n-- ph3 (initialize) --\n");
|
||||
top->update_ph3(true);
|
||||
|
|
|
@ -131,70 +131,3 @@ module CC_USR_RSTN (
|
|||
output USR_RSTN
|
||||
);
|
||||
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
|
||||
always @(posedge clkb)
|
||||
begin
|
||||
// "NO_CHANGE" only
|
||||
for (k=0; k < B_RD_WIDTH; k=k+1) 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
|
||||
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
|
||||
|
@ -1274,13 +1273,12 @@ module CC_BRAM_40K (
|
|||
// SDP read port
|
||||
always @(posedge clkb)
|
||||
begin
|
||||
// "NO_CHANGE" only
|
||||
for (k=0; k < B_RD_WIDTH; k=k+1) 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
|
||||
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
|
||||
|
@ -1412,6 +1410,393 @@ module CC_BRAM_40K (
|
|||
endgenerate
|
||||
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
|
||||
module CC_L2T4(
|
||||
output O,
|
||||
|
|
|
@ -26,10 +26,12 @@ proc
|
|||
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)
|
||||
cd mux8 # Constrain all select calls below inside the top module
|
||||
select -assert-count 3 t:AL_MAP_LUT4
|
||||
select -assert-count 1 t:AL_MAP_LUT6
|
||||
select -assert-max 3 t:AL_MAP_LUT3
|
||||
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
|
||||
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)
|
||||
cd mux8 # Constrain all select calls below inside the top module
|
||||
select -assert-max 1 t:L6MUX21
|
||||
select -assert-max 7 t:LUT4
|
||||
select -assert-max 2 t:PFUMX
|
||||
select -assert-max 8 t:LUT4
|
||||
select -assert-max 3 t:PFUMX
|
||||
|
||||
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 6 t:CC_DFF
|
||||
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-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
|
||||
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
|
||||
select -assert-count 5 t:SB_LUT4
|
||||
select -assert-max 6 t:SB_LUT4
|
||||
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
|
||||
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
|
||||
select -assert-count 1 t:MISTRAL_ALUT3
|
||||
select -assert-max 1 t:MISTRAL_ALUT3
|
||||
select -assert-max 2 t:MISTRAL_ALUT5
|
||||
select -assert-max 5 t:MISTRAL_ALUT6
|
||||
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
|
||||
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
|
||||
select -assert-count 1 t:MISTRAL_ALUT3
|
||||
select -assert-count 2 t:MISTRAL_ALUT5
|
||||
select -assert-count 4 t:MISTRAL_ALUT6
|
||||
select -assert-max 1 t:MISTRAL_ALUT3
|
||||
select -assert-max 2 t:MISTRAL_ALUT5
|
||||
select -assert-max 5 t:MISTRAL_ALUT6
|
||||
|
||||
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
|
||||
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
|
||||
select -assert-min 11 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
|
||||
|
|
|
@ -30,12 +30,13 @@ proc
|
|||
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)
|
||||
cd mux8 # Constrain all select calls below inside the top module
|
||||
select -assert-count 4 t:LUT1
|
||||
select -assert-count 3 t:LUT4
|
||||
select -assert-count 2 t:MUXF5
|
||||
select -assert-max 5 t:LUT1
|
||||
select -assert-max 3 t:LUT3
|
||||
select -assert-max 3 t:LUT4
|
||||
select -assert-max 3 t:MUXF5
|
||||
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
|
||||
|
|
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…
Reference in a new issue