3
0
Fork 0
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:
Muthiah Annamalai (முத்து அண்ணாமலை) 2023-07-01 11:10:27 -07:00 committed by GitHub
commit f9f7a4e9db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1399 additions and 203 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View 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];

View file

@ -42,6 +42,7 @@ Yosys manual
CHAPTER_Verilog.rst
CHAPTER_Optimize.rst
CHAPTER_Techmap.rst
CHAPTER_Memorymap.rst
CHAPTER_Eval.rst
.. raw:: latex

View file

@ -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);
}
}

View file

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

View file

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

View file

@ -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 = []

View file

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

View file

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

View file

@ -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 += "&#9586;";
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

View file

@ -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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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