3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-25 10:10:33 +00:00

Merge branch 'YosysHQ:main' into master

This commit is contained in:
Eder Monteiro 2025-05-07 11:00:58 -03:00 committed by GitHub
commit bdb811bcaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 1204 additions and 99 deletions

View file

@ -2,9 +2,29 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.52 .. Yosys 0.53-dev
Yosys 0.53 .. Yosys 0.54-dev
--------------------------
Yosys 0.52 .. Yosys 0.53
--------------------------
* New commands and options
- Added "constmap" pass for technology mapping of coarse constant value.
- Added "timeest" pass to estimate the critical path in clock domain.
- Added "-blackbox" option to "cutpoint" pass to cut all instances of
blackboxes.
- Added "-noscopeinfo" option to "cutpoint" pass.
- Added "-nocleanup" option to "flatten" pass to prevent removal of
unused submodules.
- Added "-declockgate" option to "formalff" pass that turns clock
gating into clock enables.
* Various
- Added "$scopeinfo" cells to preserve information during "cutpoint" pass.
- Added dataflow tracking documentation.
- share: Restrict activation patterns to potentially relevant signal.
- liberty: More robust parsing.
- verific: bit blast RAM if using mem2reg attribute.
Yosys 0.51 .. Yosys 0.52
--------------------------
* New commands and options

View file

@ -160,7 +160,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.52+63
YOSYS_VER := 0.53+3
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
@ -183,7 +183,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o
bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline fee39a3.. | wc -l`/;" Makefile
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 53c22ab.. | wc -l`/;" Makefile
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
@ -396,6 +396,10 @@ ifeq ($(DISABLE_ABC_THREADS),1)
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
endif
ifeq ($(LINK_ABC),1)
ABCMKARGS += "ABC_USE_PIC=1"
endif
ifeq ($(DISABLE_SPAWN),1)
CXXFLAGS += -DYOSYS_DISABLE_SPAWN
endif
@ -787,7 +791,7 @@ $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in $(YOSYS_SRC)/Makefile
.PHONY: check-git-abc
check-git-abc:
@if [ ! -d "$(YOSYS_SRC)/abc" ]; then \
@if [ ! -d "$(YOSYS_SRC)/abc" ] && git -C "$(YOSYS_SRC)" status >/dev/null 2>&1; then \
echo "Error: The 'abc' directory does not exist."; \
echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
exit 1; \
@ -813,6 +817,12 @@ check-git-abc:
echo "3. Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
echo "4. Reapply your changes: Move your saved changes back to the 'abc' directory, if necessary."; \
exit 1; \
elif ! git -C "$(YOSYS_SRC)" status >/dev/null 2>&1; then \
echo "$(realpath $(YOSYS_SRC)) is not configured as a git repository, and 'abc' folder is missing."; \
echo "If you already have ABC, set 'ABCEXTERNAL' make variable to point to ABC executable."; \
echo "Otherwise, download release archive 'yosys.tar.gz' from https://github.com/YosysHQ/yosys/releases."; \
echo " ('Source code' archive does not contain submodules.)"; \
exit 1; \
else \
echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
exit 1; \

View file

@ -2410,7 +2410,12 @@ struct CxxrtlWorker {
auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
cell_attrs.erase(ID::module_not_derived);
f << indent << "scopes->add(path, " << escape_cxx_string(get_hdl_name(cell)) << ", ";
f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", ";
if (module_attrs.count(ID(hdlname))) {
f << escape_cxx_string(module_attrs.at(ID(hdlname)).decode_string());
} else {
f << escape_cxx_string(cell->get_string_attribute(ID(module)));
}
f << ", ";
dump_serialized_metadata(module_attrs);
f << ", ";
dump_serialized_metadata(cell_attrs);

View file

@ -1769,7 +1769,7 @@ value<BitsY> shr_uu(const value<BitsA> &a, const value<BitsB> &b) {
template<size_t BitsY, size_t BitsA, size_t BitsB>
CXXRTL_ALWAYS_INLINE
value<BitsY> shr_su(const value<BitsA> &a, const value<BitsB> &b) {
return a.shr(b).template scast<BitsY>();
return a.template scast<BitsY>().shr(b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
@ -2010,7 +2010,7 @@ std::pair<value<BitsY>, value<BitsY>> divmod_uu(const value<BitsA> &a, const val
value<Bits> quotient;
value<Bits> remainder;
value<Bits> dividend = a.template zext<Bits>();
value<Bits> divisor = b.template zext<Bits>();
value<Bits> divisor = b.template trunc<BitsB>().template zext<Bits>();
std::tie(quotient, remainder) = dividend.udivmod(divisor);
return {quotient.template trunc<BitsY>(), remainder.template trunc<BitsY>()};
}

View file

@ -6,7 +6,7 @@ import os
project = 'YosysHQ Yosys'
author = 'YosysHQ GmbH'
copyright ='2025 YosysHQ GmbH'
yosys_ver = "0.52"
yosys_ver = "0.53"
# select HTML theme
html_theme = 'furo-ys'

View file

@ -1919,6 +1919,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
if (!str.empty() && str[0] == '\\' && (template_node->type == AST_STRUCT || template_node->type == AST_UNION)) {
// replace instance with wire representing the packed structure
newNode = make_packed_struct(template_node, str, attributes);
if (newNode->attributes.count(ID::wiretype))
delete newNode->attributes[ID::wiretype];
newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str));
// add original input/output attribute to resolved wire
newNode->is_input = this->is_input;

View file

@ -1446,6 +1446,25 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module_name = "\\" + sha1_if_contain_spaces(module_name);
}
{
Array ram_nets ;
MapIter mem_mi;
Net *mem_net;
FOREACH_NET_OF_NETLIST(nl, mem_mi, mem_net)
{
if (!mem_net->IsRamNet()) continue ;
if (mem_net->GetAtt("mem2reg"))
ram_nets.Insert(mem_net) ;
}
unsigned i ;
FOREACH_ARRAY_ITEM(&ram_nets, i, mem_net) {
log("Bit blasting RAM for identifier '%s'\n", mem_net->Name());
mem_net->BlastNet();
}
nl->RemoveDanglingLogic(0);
}
netlist = nl;
if (design->has(module_name)) {

View file

@ -2249,7 +2249,8 @@ cell_parameter:
node->children.push_back($1);
} |
'.' TOK_ID '(' ')' {
// just ignore empty parameters
// delete unused TOK_ID
delete $2;
} |
'.' TOK_ID '(' expr ')' {
AstNode *node = new AstNode(AST_PARASET);

View file

@ -247,7 +247,7 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
db->add_edge(cell, ID::A, a_width - 1, ID::Y, i, -1);
}
for (int k = 0; k < b_width; k++) {
for (int k = 0; k < b_width_capped; k++) {
// left shifts
if (cell->type.in(ID($shl), ID($sshl))) {
if (a_width == 1 && is_signed) {
@ -268,7 +268,7 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
bool shift_in_bulk = i < a_width - 1;
// can we jump into the zero-padding by toggling B[k]?
bool zpad_jump = (((y_width - i) & ((1 << (k + 1)) - 1)) != 0 \
&& (((y_width - i) & ~(1 << k)) < (1 << b_width)));
&& (((y_width - i) & ~(1 << k)) < (1 << b_width_capped)));
if (shift_in_bulk || (cell->type.in(ID($shr), ID($shift), ID($shiftx)) && zpad_jump))
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
@ -279,7 +279,7 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
// bidirectional shifts (positive B shifts right, negative left)
} else if (cell->type.in(ID($shift), ID($shiftx)) && is_b_signed) {
if (is_signed) {
if (k != b_width - 1) {
if (k != b_width_capped - 1) {
bool r_shift_in_bulk = i < a_width - 1;
// assuming B is positive, can we jump into the upper zero-padding by toggling B[k]?
bool r_zpad_jump = (((y_width - i) & ((1 << (k + 1)) - 1)) != 0 \

View file

@ -314,7 +314,7 @@ int main(int argc, char **argv)
auto result = options.parse(argc, argv);
if (result.count("M")) memhasher_on();
if (result.count("X")) yosys_xtrace++;
if (result.count("X")) yosys_xtrace += result.count("X");
if (result.count("A")) call_abort = true;
if (result.count("Q")) print_banner = false;
if (result.count("T")) print_stats = false;

View file

@ -100,11 +100,12 @@ gzip_istream::ibuf::~ibuf() {
// Takes a successfully opened ifstream. If it's gzipped, returns an istream. Otherwise,
// returns the original ifstream, rewound to the start.
// Never returns nullptr or failed state istream*
std::istream* uncompressed(const std::string filename, std::ios_base::openmode mode) {
std::ifstream* f = new std::ifstream();
f->open(filename, mode);
if (f->fail())
return f;
log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
// Check for gzip magic
unsigned char magic[3];
int n = 0;
@ -124,7 +125,7 @@ std::istream* uncompressed(const std::string filename, std::ios_base::openmode m
filename.c_str(), unsigned(magic[2]));
gzip_istream* s = new gzip_istream();
delete f;
s->open(filename.c_str());
log_assert(s->open(filename.c_str()));
return s;
#else
log_cmd_error("File `%s' is a gzip file, but Yosys is compiled without zlib.\n", filename.c_str());

View file

@ -148,7 +148,7 @@ static inline bool ys_debug(int n = 0) { if (log_force_debug) return true; log_d
#else
static inline bool ys_debug(int = 0) { return false; }
#endif
# define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0)
static inline void log_debug(const char *format, ...) { if (ys_debug(1)) { va_list args; va_start(args, format); logv(format, args); va_end(args); } }
static inline void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {

View file

@ -472,8 +472,6 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s
yosys_input_files.insert(filename);
f = uncompressed(filename, bin_input ? std::ifstream::binary : std::ifstream::in);
}
if (f == NULL)
log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
for (size_t i = argidx+1; i < args.size(); i++)
if (args[i].compare(0, 1, "-") == 0)

View file

@ -788,12 +788,18 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
{
std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
std::vector<int> undef_c;
if (cell->type == ID($macc_v2))
undef_c = importUndefSigSpec(cell->getPort(ID::C), timestep);
int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
int undef_any_c = ez->expression(ezSAT::OpOr, undef_c);
int undef_any = ez->OR(undef_any_a, ez->OR(undef_any_b, undef_any_c));
std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
ez->assume(ez->vec_eq(undef_y, std::vector<int>(GetSize(y), ez->OR(undef_any_a, undef_any_b))));
ez->assume(ez->vec_eq(undef_y, std::vector<int>(GetSize(y), undef_any)));
undefGating(y, tmp, undef_y);
}

View file

@ -55,3 +55,4 @@ OBJS += passes/cmds/wrapcell.o
OBJS += passes/cmds/setenv.o
OBJS += passes/cmds/abstract.o
OBJS += passes/cmds/test_select.o
OBJS += passes/cmds/timeest.o

View file

@ -128,7 +128,7 @@ struct CleanZeroWidthPass : public Pass {
// A and B to 1-bit if their width is 0.
if (cell->getParam(ID::Y_WIDTH).as_int() == 0) {
module->remove(cell);
} else if (cell->type == ID($macc)) {
} else if (cell->type.in(ID($macc), ID($macc_v2))) {
// TODO: fixing zero-width A and B not supported.
} else {
if (cell->getParam(ID::A_WIDTH).as_int() == 0) {

View file

@ -40,7 +40,7 @@ struct statdata_t
X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \
X(num_processes)
#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area)
#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) X(sequential_area)
#define X(_name) unsigned int _name;
STAT_INT_MEMBERS
@ -350,8 +350,6 @@ void read_liberty_cellarea(dict<IdString, cell_area_t> &cell_area, string libert
{
std::istream* f = uncompressed(liberty_file.c_str());
yosys_input_files.insert(liberty_file);
if (f->fail())
log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
LibertyParser libparser(*f, liberty_file);
delete f;

View file

@ -72,7 +72,9 @@ struct TeePass : public Pass {
}
if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) {
const char *open_mode = args[argidx] == "-o" ? "w" : "a+";
FILE *f = fopen(args[++argidx].c_str(), open_mode);
auto path = args[++argidx];
rewrite_filename(path);
FILE *f = fopen(path.c_str(), open_mode);
yosys_input_files.insert(args[argidx]);
if (f == NULL) {
for (auto cf : files_to_close)

418
passes/cmds/timeest.cc Normal file
View file

@ -0,0 +1,418 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Martin Povišer <povik@cutebit.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/sigtools.h"
#include "kernel/register.h"
#include "kernel/cellaigs.h"
#include "kernel/utils.h"
#include "kernel/ff.h"
#include "kernel/mem.h"
#include <assert.h>
#include <limits>
USING_YOSYS_NAMESPACE
template<> struct Yosys::hashlib::hash_ops<AigNode *> : Yosys::hashlib::hash_ptr_ops {};
PRIVATE_NAMESPACE_BEGIN
typedef long int arrivalint;
const arrivalint INF_PAST = std::numeric_limits<arrivalint>::min();
// each clock domain must have its own EstimateSta structure
struct EstimateSta {
SigMap sigmap;
Module *m;
SigBit clk;
dict<std::pair<RTLIL::IdString, dict<RTLIL::IdString, RTLIL::Const>>, Aig> aigs;
dict<Cell *, Aig *> cell_aigs;
std::vector<std::pair<Cell *, SigBit>> launchers;
std::vector<std::pair<Cell *, SigBit>> samplers;
bool all_paths = false;
bool select = false;
void add_seq(Cell *cell, SigSpec launch, SigSpec sample)
{
sigmap.apply(launch);
sigmap.apply(sample);
launch.sort_and_unify();
sample.sort_and_unify();
for (auto bit : launch)
launchers.push_back(std::make_pair(cell, bit));
for (auto bit : sample)
samplers.push_back(std::make_pair(cell, bit));
}
// we include a discount factor for cells that can be implemented using carry chain logic
// and to account for the AIG model not being balanced
int cell_type_factor(IdString type)
{
if (type.in(ID($gt), ID($ge), ID($lt), ID($le), ID($add), ID($sub),
ID($logic_not), ID($reduce_and), ID($reduce_or), ID($eq)))
return 1;
else
return 2;
}
// TODO: ignores clock polarity
EstimateSta(Module *m, SigBit clk)
: sigmap(m), m(m), clk(clk)
{
sigmap.apply(clk);
}
void run()
{
log("Domain %s\n", log_signal(clk));
// first, we collect launch and sample points and convert the combinational logic to AIG
std::vector<Cell *> combinational;
for (auto cell : m->cells()) {
SigSpec launch, sample;
if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
// collect launch and sample points for FF cell
FfData ff(nullptr, cell);
if (!ff.has_clk) {
log_warning("Ignoring unsupported storage element '%s' (%s)\n",
log_id(cell), log_id(cell->type));
continue;
}
if (ff.sig_clk != clk)
continue;
launch.append(ff.sig_q);
sample.append(ff.sig_d);
if (ff.has_ce)
sample.append(ff.sig_ce);
if (ff.has_srst)
sample.append(ff.sig_srst);
add_seq(cell, launch, sample);
} else if (cell->is_mem_cell()) {
// memories handled separately
continue;
} else if (cell->type == ID($scopeinfo)) {
continue;
} else {
// find or build AIG model of combinational cell
auto fingerprint = std::make_pair(cell->type, cell->parameters);
if (!aigs.count(fingerprint)) {
aigs.emplace(fingerprint, Aig(cell));
if (aigs.at(fingerprint).name.empty()) {
log_error("Unsupported cell '%s' in module '%s'",
log_id(cell->type), log_id(m));
}
}
combinational.push_back(cell);
continue;
}
}
// since we're now taking reference into `aigs`, we can no longer modify it
// and thus have to fill `cell_aigs` in a separate loop
for (auto cell : combinational) {
auto fingerprint = std::make_pair(cell->type, cell->parameters);
cell_aigs.emplace(cell, &aigs.at(fingerprint));
}
// collect launch and sample points for memory cells
for (auto &mem : Mem::get_all_memories(m)) {
for (auto &rd : mem.rd_ports) {
if (!rd.clk_enable) {
log_error("Unsupported async memory port '%s'\n", log_id(rd.cell));
continue;
}
if (sigmap(rd.clk) != clk)
continue;
add_seq(rd.cell, rd.data, {rd.addr, rd.srst, rd.en});
}
for (auto &wr : mem.wr_ports) {
if (sigmap(wr.clk) != clk)
continue;
add_seq(wr.cell, {}, {wr.en, wr.addr, wr.data});
}
}
// now we toposort the combinational logic
// each toposort node is either a SigBit or a pair of Cell * / AigNode *
TopoSort<std::tuple<SigBit, Cell *, AigNode *>> topo;
auto desc_aig = [&](Cell *cell, AigNode &node) {
return std::make_tuple(RTLIL::S0, cell, &node);
};
auto desc_sig = [&](SigBit bit) {
return std::make_tuple(sigmap(bit), (Cell *) NULL, (AigNode *) NULL);
};
// collect edges of the AIG graph
for (auto cell : combinational) {
assert(cell_aigs.count(cell));
Aig &aig = *cell_aigs.at(cell);
for (auto &node : aig.nodes) {
if (!node.portname.empty()) {
topo.edge(
desc_sig(cell->getPort(node.portname)[node.portbit]),
desc_aig(cell, node)
);
} else if (node.left_parent < 0 && node.right_parent < 0) {
// constant, nothing to do
} else {
topo.edge(
desc_aig(cell, aig.nodes[node.left_parent]),
desc_aig(cell, node)
);
topo.edge(
desc_aig(cell, aig.nodes[node.right_parent]),
desc_aig(cell, node)
);
}
for (auto &oport : node.outports) {
topo.edge(
desc_aig(cell, node),
desc_sig(cell->getPort(oport.first)[oport.second])
);
}
}
}
if (!topo.sort())
log_error("Module '%s' contains combinational loops", log_id(m));
// now we determine how long it takes for signals to stabilize
// `levels` records the time after a clock edge after which a signal is stable
dict<std::tuple<SigBit, Cell *, AigNode *>, arrivalint> levels;
for (auto node : topo.sorted)
levels[node] = INF_PAST;
// launch points are at 0 by definition
for (auto pair : launchers)
levels[desc_sig(pair.second)] = 0;
for (auto node : topo.sorted) {
AigNode *aig_node = std::get<2>(node);
if (aig_node) {
Cell *cell = std::get<1>(node);
Aig &aig = *cell_aigs.at(cell);
if (!aig_node->portname.empty()) {
// for a cell port, copy `levels` value from port bit
SigBit bit = cell->getPort(aig_node->portname)[aig_node->portbit];
levels[node] = levels[desc_sig(bit)];
} else if (aig_node->left_parent < 0 && aig_node->right_parent < 0) {
// constant, nothing to do
} else {
// for each AIG node, find maximum of parents and add a cell-specific delay
int left = levels[desc_aig(cell, aig.nodes[aig_node->left_parent])];
int right = levels[desc_aig(cell, aig.nodes[aig_node->right_parent])];
levels[node] = (std::max(left, right) + cell_type_factor(cell->type));
}
// copy `levels` value to any output ports
for (auto &oport : aig_node->outports) {
levels[desc_sig(cell->getPort(oport.first)[oport.second])] = levels[node];
}
}
}
// now find the length of the critical path (slowest path in the design)
arrivalint crit = INF_PAST;
for (auto pair : samplers)
if (levels[desc_sig(pair.second)] > crit)
crit = levels[desc_sig(pair.second)];
if (crit < 0) {
log("No paths found\n");
return;
}
log("Critical path is %ld nodes long:\n\n", crit);
// we use dict instead of pool because dict gives us
// some compile-time errors related to hashing
dict<std::tuple<SigBit, Cell *, AigNode *>, bool> critical;
// actually find one critical path, or all such paths if requested
for (auto pair : samplers) {
if (levels[desc_sig(pair.second)] == crit) {
critical[desc_sig(pair.second)] = true;
if (!all_paths)
break;
}
}
// walk backwards through toposorted nodes and set critical flag on nodes in critical path
for (auto it = topo.sorted.rbegin(); it != topo.sorted.rend(); it++) {
auto node = *it;
AigNode *aig_node = std::get<2>(node);
if (aig_node) {
Cell *cell = std::get<1>(node);
Aig &aig = *cell_aigs.at(cell);
for (auto &oport : aig_node->outports) {
if (critical.count(desc_sig(cell->getPort(oport.first)[oport.second])))
critical[node] = true;
}
if (!aig_node->portname.empty()) {
SigBit bit = cell->getPort(aig_node->portname)[aig_node->portbit];
if (critical.count(node))
critical[desc_sig(bit)] = true;
} else if (aig_node->left_parent < 0 && aig_node->right_parent < 0) {
// constant, nothing to do
} else {
// figure out which parent is on the critical path
auto left = desc_aig(cell, aig.nodes[aig_node->left_parent]);
auto right = desc_aig(cell, aig.nodes[aig_node->right_parent]);
int crit_input_lvl = levels[node] - cell_type_factor(cell->type);
if (critical.count(node)) {
bool left_critical = (levels[left] == crit_input_lvl);
bool right_critical = (levels[right] == crit_input_lvl);
if (all_paths) {
if (left_critical)
critical[left] = true;
if (right_critical)
critical[right] = true;
} else {
if (left_critical)
critical[left] = true;
else if (right_critical)
critical[right] = true;
}
}
}
}
}
// finally print the path we found
SigPool bits_to_select;
pool<IdString> to_select;
pool<Cell *> printed;
for (auto node : topo.sorted) {
if (!critical.count(node))
continue;
AigNode *aig_node = std::get<2>(node);
if (aig_node) {
Cell *cell = std::get<1>(node);
if (!printed.count(cell)) {
to_select.insert(cell->name);
std::string cell_src;
if (cell->has_attribute(ID::src)) {
std::string src_attr = cell->get_src_attribute();
cell_src = stringf(" source: %s", src_attr.c_str());
}
log(" cell %s (%s)%s\n", log_id(cell), log_id(cell->type), cell_src.c_str());
printed.insert(cell);
}
} else {
SigBit bit = std::get<0>(node);
bits_to_select.add(bit);
std::string wire_src;
if (bit.wire && bit.wire->has_attribute(ID::src)) {
std::string src_attr = bit.wire->get_src_attribute();
wire_src = stringf(" source: %s", src_attr.c_str());
}
log(" wire %s%s (level %ld)\n", log_signal(bit), wire_src.c_str(), levels[node]);
}
}
for (auto wire : m->wires()) {
if (bits_to_select.check_any(sigmap(wire)))
to_select.insert(wire->name);
}
if (select) {
RTLIL::Selection sel(false);
for (auto member : to_select)
sel.selected_members[m->name].insert(member);
m->design->selection_stack.back() = sel;
m->design->selection_stack.back().optimize(m->design);
}
}
};
struct TimeestPass : Pass {
TimeestPass() : Pass("timeest", "estimate timing") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" timeest [-clk <clk_signal>] [options] [selection]\n");
log("\n");
log("Estimate the critical path in clock domain <clk_signal> by counting AIG nodes.\n");
log("\n");
log(" -all_paths\n");
log(" Print or select nodes from all critical paths instead of focusing on\n");
log(" a single illustratory path.\n");
log("\n");
log(" -select\n");
log(" Select the nodes of a critical path\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *d) override
{
log_header(d, "Executing TIMEEST pass. (estimate timing)\n");
std::string clk;
bool all_paths = false;
bool select = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-all_paths") {
all_paths = true;
continue;
}
if (args[argidx] == "-select") {
select = true;
continue;
}
if (args[argidx] == "-clk" && argidx + 1 < args.size()) {
clk = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, d);
if (clk.empty())
log_cmd_error("No -clk argument provided\n");
if (select && d->selected_modules().size() > 1)
log_cmd_error("The -select option operates on a single selected module\n");
for (auto m : d->selected_modules()) {
if (!m->wire(RTLIL::escape_id(clk))) {
log_warning("No domain '%s' in module %s\n", clk.c_str(), log_id(m));
continue;
}
EstimateSta sta(m, SigBit(m->wire(RTLIL::escape_id(clk)), 0));
sta.all_paths = all_paths;
sta.select = select;
sta.run();
}
}
} TimeestPass;
PRIVATE_NAMESPACE_END

View file

@ -1315,13 +1315,14 @@ skip_fine_alu:
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec sig_y(cell->type == ID($shiftx) ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam(ID::Y_WIDTH).as_int());
// Limit indexing to the size of a, which is behaviourally identical (result is all 0)
// and avoids integer overflow of i + shift_bits when e.g. ID::B == INT_MAX
shift_bits = min(shift_bits, GetSize(sig_a));
if (cell->type != ID($shiftx) && GetSize(sig_a) < GetSize(sig_y))
sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool());
// Limit indexing to the size of a, which is behaviourally identical (result is all 0)
// and avoids integer overflow of i + shift_bits when e.g. ID::B == INT_MAX.
// We do this after sign-extending a so this accounts for the output size
shift_bits = min(shift_bits, GetSize(sig_a));
for (int i = 0; i < GetSize(sig_y); i++) {
int idx = i + shift_bits;
if (0 <= idx && idx < GetSize(sig_a))

View file

@ -22,6 +22,7 @@
#include "kernel/ffinit.h"
#include "kernel/ff.h"
#include "kernel/modtools.h"
#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -537,6 +538,12 @@ struct FormalFfPass : public Pass {
log(" Add assumptions that constrain wires with the 'replaced_by_gclk'\n");
log(" attribute to the value they would have before an active clock edge.\n");
log("\n");
log(" -declockgate\n");
log(" Detect clock-gating patterns and modify any FFs clocked by the gated\n");
log(" clock to use the ungated clock with the gate signal as clock enable.\n");
log(" This doesn't affect the design's behavior during FV but can enable the\n");
log(" use of formal verification methods that only support a single global\n");
log(" clock.\n");
// TODO: An option to check whether all FFs use the same clock before changing it to the global clock
}
@ -549,6 +556,7 @@ struct FormalFfPass : public Pass {
bool flag_setundef = false;
bool flag_hierarchy = false;
bool flag_assume = false;
bool flag_declockgate = false;
log_header(design, "Executing FORMALFF pass.\n");
@ -583,22 +591,237 @@ struct FormalFfPass : public Pass {
flag_assume = true;
continue;
}
if (args[argidx] == "-declockgate") {
flag_declockgate = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (!(flag_clk2ff || flag_ff2anyinit || flag_anyinit2ff || flag_hierarchy || flag_assume))
if (!(flag_clk2ff || flag_ff2anyinit || flag_anyinit2ff || flag_hierarchy || flag_assume || flag_declockgate))
log_cmd_error("One of the options -clk2ff, -ff2anyinit, -anyinit2ff, -hierarchy or -assume must be specified.\n");
if (flag_ff2anyinit && flag_anyinit2ff)
log_cmd_error("The options -ff2anyinit and -anyinit2ff are exclusive.\n");
if (flag_ff2anyinit && flag_declockgate)
log_cmd_error("The options -ff2anyinit and -declockgate are exclusive.\n");
if (flag_fine && !flag_anyinit2ff)
log_cmd_error("The option -fine requries the -anyinit2ff option.\n");
if (flag_fine && flag_clk2ff)
log_cmd_error("The options -fine and -clk2ff are exclusive.\n");
if (flag_declockgate)
{
for (auto module : design->selected_modules())
{
ModWalker modwalker(design);
modwalker.setup(module);
SigMap &sigmap = modwalker.sigmap;
FfInitVals initvals(&modwalker.sigmap, module);
dict<IdString, Mem> memories;
for (auto mem : Mem::get_selected_memories(module)) {
if (!mem.packed)
continue;
memories.emplace(mem.cell->name, std::move(mem));
}
dict<pair<SigBit, bool>, vector<Cell *>> clk_bits;
pool<SigBit> input_bits;
pool<pair<SigBit, bool>> input_clk_bits;
for (auto cell : module->selected_cells()) {
if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
FfData ff(&initvals, cell);
if (!ff.has_clk)
continue;
SigBit clk = sigmap(ff.sig_clk);
clk_bits[{clk, ff.pol_clk}].push_back(cell);
} else if (cell->type == ID($mem_v2)) {
auto const &mem = memories.at(cell->name);
for (auto &rd_port : mem.rd_ports)
if (rd_port.clk_enable)
clk_bits[{rd_port.clk, rd_port.clk_polarity}].push_back(mem.cell);
for (auto &wr_port : mem.wr_ports)
if (wr_port.clk_enable)
clk_bits[{wr_port.clk, wr_port.clk_polarity}].push_back(mem.cell);
}
// XXX $check $print
}
log_debug("%s has %d clk bits\n", log_id(module), GetSize(clk_bits));
for (auto port : module->ports) {
Wire *wire = module->wire(port);
if (!wire->port_input)
continue;
for (auto bit : SigSpec(wire)) {
input_bits.insert(bit);
for (bool pol : {false, true}) {
if (clk_bits.count({bit, pol})) {
input_clk_bits.insert({bit, pol});
clk_bits.erase({bit, pol});
}
}
}
}
log_debug("%s has %d non-input clk bits\n", log_id(module), GetSize(clk_bits));
if (clk_bits.empty())
continue;
for (auto &clk_bit : clk_bits)
{
SigBit clk = clk_bit.first.first;
bool pol_clk = clk_bit.first.second;
vector<Cell *> &clocked_cells = clk_bit.second;
if (!clk.is_wire()) {
log_debug("constant clk bit %s.%s\n", log_id(module), log_signal(SigSpec(clk)));
continue;
}
if (input_bits.count(clk)) {
log_debug("input clk bit %s.%s\n", log_id(module), log_signal(SigSpec(clk)));
continue;
}
auto found = modwalker.signal_drivers.find(clk);
if (found == modwalker.signal_drivers.end() || found->second.empty()) {
log_debug("undriven clk bit %s.%s\n", log_id(module), log_signal(SigSpec(clk)));
continue;
}
if (found->second.size() > 1) {
log_debug("multiple drivers for clk bit %s.%s\n", log_id(module), log_signal(SigSpec(clk)));
continue;
}
auto driver = *found->second.begin();
bool is_gate =
pol_clk ? driver.cell->type.in(ID($and), ID($_AND_)) : driver.cell->type.in(ID($or), ID($_OR_));
if (!is_gate) {
log_debug("unsupported gating logic %s.%s (%s) for clock %s %s.%s\n", log_id(module),
log_id(driver.cell), log_id(driver.cell->type), pol_clk ? "posedge" : "negedge",
log_id(module), log_signal(SigSpec(clk)));
continue;
}
SigBit gate_clock = sigmap(driver.cell->getPort(ID::A)[driver.offset]);
SigBit gate_enable = sigmap(driver.cell->getPort(ID::B)[driver.offset]);
std::swap(gate_clock, gate_enable);
for (int i = 0; i < 2; i++) {
std::swap(gate_clock, gate_enable);
log_debug("clock %s.%s for gated clk bit %s.%s\n", log_id(module), log_signal(SigSpec(gate_clock)),
log_id(module), log_signal(SigSpec(clk)));
log_debug("enable %s.%s for gated clk bit %s.%s\n", log_id(module), log_signal(SigSpec(gate_enable)),
log_id(module), log_signal(SigSpec(clk)));
found = modwalker.signal_drivers.find(gate_enable);
if (found == modwalker.signal_drivers.end() || found->second.empty()) {
log_debug("undriven gate enable %s.%s of gated clk bit %s.%s\n", log_id(module),
log_signal(SigSpec(gate_enable)), log_id(module), log_signal(SigSpec(clk)));
continue;
}
if (found->second.size() > 1) {
log_debug("multiple drivers for gate enable %s.%s of gated clk bit %s.%s\n", log_id(module),
log_signal(SigSpec(gate_enable)), log_id(module), log_signal(SigSpec(clk)));
continue;
}
auto gate_driver = *found->second.begin();
if (!RTLIL::builtin_ff_cell_types().count(gate_driver.cell->type)) {
log_debug("non FF driver for gate enable %s.%s of gated clk bit %s.%s\n", log_id(module),
log_signal(SigSpec(gate_enable)), log_id(module), log_signal(SigSpec(clk)));
continue;
}
FfData ff(&initvals, gate_driver.cell);
if (ff.has_gclk || ff.has_ce || ff.has_sr || ff.has_srst || ff.has_arst || (ff.has_aload && ff.has_clk)) {
log_debug(
"FF driver for gate enable %s.%s of gated clk bit %s.%s has incompatible type: %s\n",
log_id(module), log_signal(SigSpec(gate_enable)), log_id(module), log_signal(SigSpec(clk)),
log_id(gate_driver.cell->type));
continue;
}
if (ff.has_aload) {
// this ff is intentionally not emitted!
ff.has_aload = false;
ff.has_clk = true;
ff.pol_clk = !ff.pol_arst;
ff.sig_clk = ff.sig_aload;
ff.sig_d = ff.sig_ad;
}
if (!ff.has_clk || sigmap(ff.sig_clk) != gate_clock || ff.pol_clk != pol_clk) {
log_debug("FF driver for gate enable %s.%s of gated clk bit %s.%s has incompatible clocking: "
"%s %s.%s\n",
log_id(module), log_signal(SigSpec(gate_enable)), log_id(module),
log_signal(SigSpec(clk)), ff.pol_clk ? "posedge" : "negedge", log_id(module),
log_signal(SigSpec(ff.sig_clk)));
continue;
}
SigBit sig_gate = ff.sig_d[gate_driver.offset];
log_debug("found clock gate, rewriting %d cells\n", GetSize(clocked_cells));
for (auto clocked_cell : clocked_cells) {
log_debug("rewriting cell %s.%s (%s)\n", log_id(module), log_id(clocked_cell),
log_id(clocked_cell->type));
if (RTLIL::builtin_ff_cell_types().count(clocked_cell->type)) {
FfData ff(&initvals, clocked_cell);
log_assert(ff.has_clk);
ff.unmap_ce();
ff.pol_ce = pol_clk;
ff.sig_ce = sig_gate;
ff.has_ce = true;
ff.sig_clk = gate_clock;
ff.emit();
} else if (clocked_cell->type == ID($mem_v2)) {
auto &mem = memories.at(clocked_cell->name);
bool changed = false;
for (auto &rd_port : mem.rd_ports) {
if (rd_port.clk_enable && rd_port.clk == clk && rd_port.clk_polarity == pol_clk) {
log_debug("patching rd port\n");
changed = true;
rd_port.clk = gate_clock;
SigBit en_bit = pol_clk ? sig_gate : SigBit(module->Not(NEW_ID, sig_gate));
SigSpec en_mask = SigSpec(en_bit, GetSize(rd_port.en));
rd_port.en = module->And(NEW_ID, rd_port.en, en_mask);
}
}
for (auto &wr_port : mem.wr_ports) {
if (wr_port.clk_enable && wr_port.clk == clk && wr_port.clk_polarity == pol_clk) {
log_debug("patching wr port\n");
changed = true;
wr_port.clk = gate_clock;
SigBit en_bit = pol_clk ? sig_gate : SigBit(module->Not(NEW_ID, sig_gate));
SigSpec en_mask = SigSpec(en_bit, GetSize(wr_port.en));
wr_port.en = module->And(NEW_ID, wr_port.en, en_mask);
}
}
if (changed)
mem.emit();
}
}
break;
}
}
}
}
for (auto module : design->selected_modules())
{
if (flag_setundef)

View file

@ -310,8 +310,6 @@ struct ClockgatePass : public Pass {
LibertyMergedCells merged;
for (auto path : liberty_files) {
std::istream* f = uncompressed(path);
if (f->fail())
log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno));
LibertyParser p(*f, path);
merged.merge(p);
delete f;

View file

@ -102,6 +102,9 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
} else if (expr[0] == '!') {
data_name = expr.substr(1, expr.size()-1);
data_not_inverted = false;
} else if (expr[0] == '(' && expr[expr.size() - 1] == ')') {
data_name = expr.substr(1, expr.size() - 2);
data_not_inverted = true;
} else {
data_name = expr;
data_not_inverted = true;
@ -632,8 +635,6 @@ struct DfflibmapPass : public Pass {
LibertyMergedCells merged;
for (auto path : liberty_files) {
std::istream* f = uncompressed(path);
if (f->fail())
log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno));
LibertyParser p(*f, path);
merged.merge(p);
delete f;

View file

@ -349,6 +349,10 @@ struct FlattenPass : public Pass {
log(" -separator <char>\n");
log(" Use this separator char instead of '.' when concatenating design levels.\n");
log("\n");
log(" -nocleanup\n");
log(" Don't remove unused submodules, leave a flattened version of each\n");
log(" submodule in the design.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
@ -360,6 +364,8 @@ struct FlattenPass : public Pass {
if (design->scratchpad.count("flatten.separator"))
worker.separator = design->scratchpad_get_string("flatten.separator");
bool cleanup = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-wb") {
@ -378,6 +384,10 @@ struct FlattenPass : public Pass {
worker.separator = args[++argidx];
continue;
}
if (args[argidx] == "-nocleanup") {
cleanup = false;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -414,7 +424,7 @@ struct FlattenPass : public Pass {
for (auto module : topo_modules.sorted)
worker.flatten_module(design, module, used_modules, worker.separator);
if (top != nullptr)
if (cleanup && top != nullptr)
for (auto module : design->modules().to_vector())
if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) {
log("Deleting now unused module %s.\n", log_id(module));

View file

@ -386,7 +386,7 @@ module TRELLIS_IO(
);
parameter DIR = "INPUT";
reg T_pd;
always @(*) if (T === 1'bz) T_pd <= 1'b0; else T_pd <= T;
always @(*) if (T === 1'bz) T_pd = 1'b0; else T_pd = T;
generate
if (DIR == "INPUT") begin

View file

@ -34,7 +34,6 @@ ram block $__CC_BRAM_TDP_ {
}
portoption "WR_MODE" "WRITE_THROUGH" {
rdwr new;
wrtrans all new;
}
wrbe_separate;
optional_rw;

View file

@ -115,15 +115,15 @@ generate
.A_CLK(PORT_A_CLK),
.A_EN(PORT_A_CLK_EN),
.A_WE(PORT_A_WR_EN),
.A_BM(PORT_A_WR_BE),
.A_DI(PORT_A_WR_DATA),
.A_BM({{(20-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
.A_DI({{(20-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
.A_ADDR({PORT_A_ADDR[13:5], 1'b0, PORT_A_ADDR[4:0], 1'b0}),
.A_DO(PORT_A_RD_DATA),
.B_CLK(PORT_B_CLK),
.B_EN(PORT_B_CLK_EN),
.B_WE(PORT_B_WR_EN),
.B_BM(PORT_B_WR_BE),
.B_DI(PORT_B_WR_DATA),
.B_BM({{(20-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
.B_DI({{(20-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
.B_ADDR({PORT_B_ADDR[13:5], 1'b0, PORT_B_ADDR[4:0], 1'b0}),
.B_DO(PORT_B_RD_DATA),
);
@ -270,15 +270,15 @@ generate
.A_CLK(PORT_A_CLK),
.A_EN(PORT_A_CLK_EN),
.A_WE(PORT_A_WR_EN),
.A_BM(PORT_A_WR_BE),
.A_DI(PORT_A_WR_DATA),
.A_BM({{(40-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
.A_DI({{(40-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
.A_ADDR({PORT_A_ADDR[14:0], 1'b0}),
.A_DO(PORT_A_RD_DATA),
.B_CLK(PORT_B_CLK),
.B_EN(PORT_B_CLK_EN),
.B_WE(PORT_B_WR_EN),
.B_BM(PORT_B_WR_BE),
.B_DI(PORT_B_WR_DATA),
.B_BM({{(40-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
.B_DI({{(40-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
.B_ADDR({PORT_B_ADDR[14:0], 1'b0}),
.B_DO(PORT_B_RD_DATA),
);
@ -429,14 +429,14 @@ generate
.A_CLK(PORT_A_CLK),
.A_EN(PORT_A_CLK_EN),
.A_WE(PORT_A_WR_EN),
.A_BM(PORT_A_WR_BE),
.A_DI(PORT_A_WR_DATA),
.A_BM({{(40-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
.A_DI({{(40-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
.A_ADDR({PORT_A_ADDR[14:0], PORT_A_ADDR[15]}),
.B_CLK(PORT_B_CLK),
.B_EN(PORT_B_CLK_EN),
.B_WE(PORT_B_WR_EN),
.B_BM(PORT_B_WR_BE),
.B_DI(PORT_B_WR_DATA),
.B_BM({{(40-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
.B_DI({{(40-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
.B_ADDR({PORT_B_ADDR[14:0], PORT_B_ADDR[15]}),
);
CC_BRAM_40K #(
@ -584,15 +584,15 @@ generate
.A_CLK(PORT_A_CLK),
.A_EN(PORT_A_CLK_EN),
.A_WE(PORT_A_WR_EN),
.A_BM(PORT_A_WR_BE),
.A_DI(PORT_A_WR_DATA),
.A_BM({{(40-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
.A_DI({{(40-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
.A_DO(PORT_A_RD_DATA),
.A_ADDR({PORT_A_ADDR[14:0], PORT_A_ADDR[15]}),
.B_CLK(PORT_B_CLK),
.B_EN(PORT_B_CLK_EN),
.B_WE(PORT_B_WR_EN),
.B_BM(PORT_B_WR_BE),
.B_DI(PORT_B_WR_DATA),
.B_BM({{(40-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
.B_DI({{(40-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
.B_DO(PORT_B_RD_DATA),
.B_ADDR({PORT_B_ADDR[14:0], PORT_B_ADDR[15]}),
);
@ -710,9 +710,9 @@ generate
.A_EN(PORT_W_CLK_EN),
.A_WE(PORT_W_WR_EN),
.A_BM(PORT_W_WR_BE[19:0]),
.B_BM(PORT_W_WR_BE[39:20]),
.B_BM({{(40-PORT_W_WIDTH){1'bx}}, PORT_W_WR_BE[39:20]}),
.A_DI(PORT_W_WR_DATA[19:0]),
.B_DI(PORT_W_WR_DATA[39:20]),
.B_DI({{(40-PORT_W_WIDTH){1'bx}}, PORT_W_WR_DATA[39:20]}),
.A_ADDR({PORT_W_ADDR[13:5], 1'b0, PORT_W_ADDR[4:0], 1'b0}),
.B_CLK(PORT_R_CLK),
.B_EN(PORT_R_CLK_EN),
@ -865,9 +865,9 @@ generate
.A_EN(PORT_W_CLK_EN),
.A_WE(PORT_W_WR_EN),
.A_BM(PORT_W_WR_BE[39:0]),
.B_BM(PORT_W_WR_BE[79:40]),
.B_BM({{(80-PORT_W_WIDTH){1'bx}}, PORT_W_WR_BE[79:40]}),
.A_DI(PORT_W_WR_DATA[39:0]),
.B_DI(PORT_W_WR_DATA[79:40]),
.B_DI({{(80-PORT_W_WIDTH){1'bx}}, PORT_W_WR_DATA[79:40]}),
.A_ADDR({PORT_W_ADDR[14:0], 1'b0}),
.B_CLK(PORT_R_CLK),
.B_EN(PORT_R_CLK_EN),

View file

@ -292,10 +292,10 @@ module CC_DLT #(
always @(*)
begin
if (sr) begin
Q <= SR_VAL;
Q = SR_VAL;
end
else if (en) begin
Q <= D;
Q = D;
end
end
@ -407,7 +407,7 @@ module CC_MULT #(
);
always @(*)
begin
P <= A * B;
P = A * B;
end
endmodule

View file

@ -48,7 +48,7 @@ module GP_COUNT14(input CLK, input wire RST, output reg OUT);
//Combinatorially output underflow flag whenever we wrap low
always @(*) begin
OUT <= (count == 14'h0);
OUT = (count == 14'h0);
end
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
@ -133,10 +133,10 @@ module GP_COUNT14_ADV(input CLK, input RST, output reg OUT,
//Combinatorially output underflow flag whenever we wrap low
always @(*) begin
if(UP)
OUT <= (count == 14'h3fff);
OUT = (count == 14'h3fff);
else
OUT <= (count == 14'h0);
POUT <= count[7:0];
OUT = (count == 14'h0);
POUT = count[7:0];
end
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
@ -272,10 +272,10 @@ module GP_COUNT8_ADV(input CLK, input RST, output reg OUT,
//Combinatorially output underflow flag whenever we wrap low
always @(*) begin
if(UP)
OUT <= (count == 8'hff);
OUT = (count == 8'hff);
else
OUT <= (count == 8'h0);
POUT <= count;
OUT = (count == 8'h0);
POUT = count;
end
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
@ -413,8 +413,8 @@ module GP_COUNT8(
//Combinatorially output underflow flag whenever we wrap low
always @(*) begin
OUT <= (count == 8'h0);
POUT <= count;
OUT = (count == 8'h0);
POUT = count;
end
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
@ -488,23 +488,23 @@ module GP_DCMPMUX(input[1:0] SEL, input[7:0] IN0, input[7:0] IN1, input[7:0] IN2
always @(*) begin
case(SEL)
2'd00: begin
OUTA <= IN0;
OUTB <= IN3;
OUTA = IN0;
OUTB = IN3;
end
2'd01: begin
OUTA <= IN1;
OUTB <= IN2;
OUTA = IN1;
OUTB = IN2;
end
2'd02: begin
OUTA <= IN2;
OUTB <= IN1;
OUTA = IN2;
OUTB = IN1;
end
2'd03: begin
OUTA <= IN3;
OUTB <= IN0;
OUTA = IN3;
OUTB = IN0;
end
endcase
@ -635,7 +635,7 @@ module GP_DLATCH(input D, input nCLK, output reg Q);
initial Q = INIT;
always @(*) begin
if(!nCLK)
Q <= D;
Q = D;
end
endmodule
@ -644,7 +644,7 @@ module GP_DLATCHI(input D, input nCLK, output reg nQ);
initial nQ = INIT;
always @(*) begin
if(!nCLK)
nQ <= ~D;
nQ = ~D;
end
endmodule
@ -653,9 +653,9 @@ module GP_DLATCHR(input D, input nCLK, input nRST, output reg Q);
initial Q = INIT;
always @(*) begin
if(!nRST)
Q <= 1'b0;
Q = 1'b0;
else if(!nCLK)
Q <= D;
Q = D;
end
endmodule
@ -664,9 +664,9 @@ module GP_DLATCHRI(input D, input nCLK, input nRST, output reg nQ);
initial nQ = INIT;
always @(*) begin
if(!nRST)
nQ <= 1'b1;
nQ = 1'b1;
else if(!nCLK)
nQ <= ~D;
nQ = ~D;
end
endmodule
@ -675,9 +675,9 @@ module GP_DLATCHS(input D, input nCLK, input nSET, output reg Q);
initial Q = INIT;
always @(*) begin
if(!nSET)
Q <= 1'b1;
Q = 1'b1;
else if(!nCLK)
Q <= D;
Q = D;
end
endmodule
@ -686,9 +686,9 @@ module GP_DLATCHSI(input D, input nCLK, input nSET, output reg nQ);
initial nQ = INIT;
always @(*) begin
if(!nSET)
nQ <= 1'b0;
nQ = 1'b0;
else if(!nCLK)
nQ <= ~D;
nQ = ~D;
end
endmodule
@ -698,9 +698,9 @@ module GP_DLATCHSR(input D, input nCLK, input nSR, output reg Q);
initial Q = INIT;
always @(*) begin
if(!nSR)
Q <= SRMODE;
Q = SRMODE;
else if(!nCLK)
Q <= D;
Q = D;
end
endmodule
@ -710,9 +710,9 @@ module GP_DLATCHSRI(input D, input nCLK, input nSR, output reg nQ);
initial nQ = INIT;
always @(*) begin
if(!nSR)
nQ <= ~SRMODE;
nQ = ~SRMODE;
else if(!nCLK)
nQ <= ~D;
nQ = ~D;
end
endmodule

View file

@ -23,7 +23,7 @@ match mul
endmatch
code sigA sigB sigH
auto unextend = [](const SigSpec &sig) {
auto unextend_signed = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
@ -32,8 +32,16 @@ code sigA sigB sigH
++i;
return sig.extract(0, i);
};
sigA = unextend(port(mul, \A));
sigB = unextend(port(mul, \B));
auto unextend_unsigned = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != SigBit(State::S0))
break;
++i;
return sig.extract(0, i);
};
sigA = param(mul, \A_SIGNED).as_bool() ? unextend_signed(port(mul, \A)) : unextend_unsigned(port(mul, \A));
sigB = param(mul, \B_SIGNED).as_bool() ? unextend_signed(port(mul, \B)) : unextend_unsigned(port(mul, \B));
SigSpec O;
if (mul->type == $mul)

View file

@ -0,0 +1,80 @@
read_verilog << EOT
module top(input wire [14:0] a, output wire [18:0] b);
assign b = a*$unsigned(5'b01111);
endmodule
EOT
prep
ice40_dsp
read_verilog << EOT
module ref(a, b);
wire _0_;
wire _1_;
wire _2_;
wire [12:0] _3_;
(* src = "<<EOT:1.30-1.31" *)
input [14:0] a;
wire [14:0] a;
(* src = "<<EOT:1.52-1.53" *)
output [18:0] b;
wire [18:0] b;
SB_MAC16 #(
.A_REG(1'h0),
.A_SIGNED(32'd0),
.BOTADDSUB_CARRYSELECT(2'h0),
.BOTADDSUB_LOWERINPUT(2'h2),
.BOTADDSUB_UPPERINPUT(1'h1),
.BOTOUTPUT_SELECT(2'h3),
.BOT_8x8_MULT_REG(1'h0),
.B_REG(1'h0),
.B_SIGNED(32'd0),
.C_REG(1'h0),
.D_REG(1'h0),
.MODE_8x8(1'h0),
.NEG_TRIGGER(1'h0),
.PIPELINE_16x16_MULT_REG1(1'h0),
.PIPELINE_16x16_MULT_REG2(1'h0),
.TOPADDSUB_CARRYSELECT(2'h3),
.TOPADDSUB_LOWERINPUT(2'h2),
.TOPADDSUB_UPPERINPUT(1'h1),
.TOPOUTPUT_SELECT(2'h3),
.TOP_8x8_MULT_REG(1'h0)
) _4_ (
.A({ 1'h0, a }),
.ACCUMCI(1'hx),
.ACCUMCO(_1_),
.ADDSUBBOT(1'h0),
.ADDSUBTOP(1'h0),
.AHOLD(1'h0),
.B(16'b1111),
.BHOLD(1'h0),
.C(16'h0000),
.CE(1'h0),
.CHOLD(1'h0),
.CI(1'hx),
.CLK(1'h0),
.CO(_2_),
.D(16'h0000),
.DHOLD(1'h0),
.IRSTBOT(1'h0),
.IRSTTOP(1'h0),
.O({ _3_, b }),
.OHOLDBOT(1'h0),
.OHOLDTOP(1'h0),
.OLOADBOT(1'h0),
.OLOADTOP(1'h0),
.ORSTBOT(1'h0),
.ORSTTOP(1'h0),
.SIGNEXTIN(1'hx),
.SIGNEXTOUT(_0_)
);
endmodule
EOT
techmap -wb -D EQUIV -autoproc -map +/ice40/cells_sim.v
equiv_make top ref equiv
select -assert-any -module equiv t:$equiv
equiv_induct
equiv_status -assert

22
tests/liberty/dff.lib Normal file
View file

@ -0,0 +1,22 @@
// Test library for different DFF function expressions
library(dff) {
cell (dff) {
area : 1;
ff("IQ", "IQN") {
next_state : "(D)";
clocked_on : "CLK";
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
}
} /* end */

View file

@ -0,0 +1,19 @@
library(dff) {
cell(dff) {
area : 1 ;
ff("IQ", "IQN") {
next_state : "(D)" ;
clocked_on : "CLK" ;
}
pin(D) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : "IQ" ;
}
}
}

View file

@ -0,0 +1,12 @@
module dff (D, CLK, Q);
reg "IQ", "IQN";
input D;
input CLK;
output Q;
assign Q = IQ; // "IQ"
always @(posedge CLK) begin
// "(D)"
"IQ" <= (D);
"IQN" <= ~((D));
end
endmodule

29
tests/liberty/dff.log.ok Normal file
View file

@ -0,0 +1,29 @@
-- Running command `dfflibmap -info -liberty dff.lib' --
1. Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).
cell dff (noninv, pins=3, area=1.00) is a direct match for cell type $_DFF_P_.
final dff cell mappings:
unmapped dff cell: $_DFF_N_
\dff _DFF_P_ (.CLK( C), .D( D), .Q( Q));
unmapped dff cell: $_DFF_NN0_
unmapped dff cell: $_DFF_NN1_
unmapped dff cell: $_DFF_NP0_
unmapped dff cell: $_DFF_NP1_
unmapped dff cell: $_DFF_PN0_
unmapped dff cell: $_DFF_PN1_
unmapped dff cell: $_DFF_PP0_
unmapped dff cell: $_DFF_PP1_
unmapped dff cell: $_DFFE_NN_
unmapped dff cell: $_DFFE_NP_
unmapped dff cell: $_DFFE_PN_
unmapped dff cell: $_DFFE_PP_
unmapped dff cell: $_DFFSR_NNN_
unmapped dff cell: $_DFFSR_NNP_
unmapped dff cell: $_DFFSR_NPN_
unmapped dff cell: $_DFFSR_NPP_
unmapped dff cell: $_DFFSR_PNN_
unmapped dff cell: $_DFFSR_PNP_
unmapped dff cell: $_DFFSR_PPN_
unmapped dff cell: $_DFFSR_PPP_
dfflegalize command line: dfflegalize -cell $_DFF_P_ 01 t:$_DFF* t:$_SDFF*

View file

@ -1,16 +1,21 @@
#!/usr/bin/env bash
set -e
set -eo pipefail
for x in *.lib; do
echo "Testing on $x.."
../../yosys -p "read_verilog small.v; synth -top small; dfflibmap -info -liberty ${x}" -ql ${x%.lib}.log
../../yosys-filterlib - $x 2>/dev/null > $x.filtered
../../yosys-filterlib -verilogsim $x > $x.verilogsim
diff $x.filtered $x.filtered.ok && diff $x.verilogsim $x.verilogsim.ok
done || exit 1
diff $x.filtered $x.filtered.ok
diff $x.verilogsim $x.verilogsim.ok
if [[ -e ${x%.lib}.log.ok ]]; then
../../yosys -p "dfflibmap -info -liberty ${x}" -TqqQl ${x%.lib}.log
diff ${x%.lib}.log ${x%.lib}.log.ok
fi
done
for x in *.ys; do
echo "Running $x.."
../../yosys -q -s $x -l ${x%.ys}.log
done || exit 1
done

View file

@ -0,0 +1,50 @@
# Testing edge cases where ports are signed/have different widths/shift amounts
# greater than the size
read_verilog <<EOT
module top (
input wire [3:0] in_u,
input wire signed [3:0] in_s,
output wire [7:0] shl_uu,
output wire signed [7:0] shl_us,
output wire [7:0] shl_su,
output wire signed [7:0] shl_ss,
output wire [7:0] shr_uu,
output wire signed [7:0] shr_us,
output wire [7:0] shr_su,
output wire signed [7:0] shr_ss,
output wire [7:0] sshl_uu,
output wire signed [7:0] sshl_us,
output wire [7:0] sshl_su,
output wire signed [7:0] sshl_ss,
output wire [7:0] sshr_uu,
output wire signed [7:0] sshr_us,
output wire [7:0] sshr_su,
output wire signed [7:0] sshr_ss
);
assign shl_uu = in_u << 20;
assign shl_us = in_u << 20;
assign shl_su = in_s << 20;
assign shl_ss = in_s << 20;
assign shr_uu = in_u >> 20;
assign shr_us = in_u >> 20;
assign shr_su = in_s >> 20;
assign shr_ss = in_s >> 20;
assign sshl_uu = in_u <<< 20;
assign sshl_us = in_u <<< 20;
assign sshl_su = in_s <<< 20;
assign sshl_ss = in_s <<< 20;
assign sshr_uu = in_u >>> 20;
assign sshr_us = in_u >>> 20;
assign sshr_su = in_s >>> 20;
assign sshr_ss = in_s >>> 20;
endmodule
EOT
equiv_opt opt_expr
design -load postopt
select -assert-none t:$shl
select -assert-none t:$shr
select -assert-none t:$sshl
select -assert-none t:$sshr

View file

@ -0,0 +1,83 @@
# based on the peepopt_formal.ys test
read_verilog -sv <<EOT
module peepopt_formal_clockgateff_0(
input logic clk_i,
input logic ena_i,
input logic enb_i,
input logic enc_i,
input logic d_0_i,
input logic d_1_i,
output logic clk_o,
output logic d_0_o,
output logic d_1_o,
output logic d_2_o
);
logic en_latched;
initial d_0_o = '0;
initial d_1_o = '0;
initial en_latched = '0;
initial d_2_o = '0;
reg mem [4];
initial begin
mem[0] = 0;
mem[1] = 0;
mem[2] = 0;
mem[3] = 0;
end
reg [1:0] counter = 0;
always_latch
if (!clk_i)
en_latched <= ena_i | enb_i;
assign clk_o = en_latched & clk_i;
always @(posedge clk_o)
d_0_o <= d_0_i;
always @(posedge clk_o)
if (enc_i)
d_1_o <= d_1_i;
always @(posedge clk_o) begin
counter <= counter + 1;
mem[counter] <= mem[counter] + 1;
d_2_o <= mem[counter] + 1;
end;
endmodule
EOT
# Check original design has latch
prep -auto-top
opt_dff
select -assert-count 1 t:$dlatch
# Manually execute equiv_opt like pattern so clk2fflogic is called with
# -nopeepopt, otherwise this doesn't test everything
design -save preopt
check -assert
formalff -declockgate
design -save postopt
delete -output */clk_o
clean -purge
select -assert-count 0 t:$dlatch
design -reset
# Create miter and clk2fflogic without peepopt
design -copy-from preopt -as gold A:top
design -copy-from postopt -as gate A:top
clk2fflogic -nopeepopt
miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
memory_map -formal
sat -prove-asserts -seq 16 -show-public -verify equiv

View file

@ -1,4 +1,4 @@
read_rtlil << EOF
read_rtlil << EOT
module \top
wire input 1 \A
wire output 2 \Y
@ -8,7 +8,67 @@ module \top
connect \Y \Y
end
end
EOF
EOT
logger -expect log "Chip area for module '\\top': 9.072000" 1
logger -expect-no-warnings
stat -liberty ../../tests/liberty/foundry_data/sg13g2_stdcell_typ_1p20V_25C.lib.filtered.gz
design -reset
read_rtlil << EOT
module \top
wire input 1 \A
wire output 2 \Y
wire output 3 \N
cell \sg13g2_and2_1 \sub1
connect \A \A
connect \B 1'0
connect \Y \Y
end
cell \child \sequential
connect \A \A
connect \B 1'0
connect \R 1'0
connect \Y \Y
connect \N \N
end
cell \child \sequential1
connect \A \A
connect \B 1'0
connect \R 1'0
connect \Y \Y
connect \N \N
end
cell \sg13g2_and2_1 \sub2
connect \A \A
connect \B 1'0
connect \Y \Y
end
end
module \child
wire input 1 \A
wire input 2 \B
wire input 3 \R
wire output 4 \Y
wire output 5 \N
cell \sg13g2_dfrbp_1 \sequential_ff
connect \CLK \A
connect \D \B
connect \Q \Y
connect \Q_N \N
connect \RESET_B \R
end
end
EOT
logger -expect log "Chip area for top module '\\top': 112.492800" 1
logger -expect log "of which used for sequential elements: 94.348800" 1
logger -expect-no-warnings
stat -liberty ../../tests/liberty/foundry_data/sg13g2_stdcell_typ_1p20V_25C.lib.filtered.gz -top \top

View file

@ -0,0 +1,24 @@
logger -expect-no-warnings
read_verilog << EOF
module bar (
input portname
);
parameter paramname = 7;
endmodule
module empty (
);
bar #() barinstance ();
endmodule
module implicit (
);
bar #(.paramname()) barinstance (.portname());
endmodule
module explicit (
input a
);
bar #(.paramname(3)) barinstance (.portname(a));
endmodule
EOF