mirror of
https://github.com/YosysHQ/yosys
synced 2026-06-29 12:08:51 +00:00
Merge branch 'YosysHQ:main' into master
This commit is contained in:
commit
bdb811bcaa
40 changed files with 1204 additions and 99 deletions
22
CHANGELOG
22
CHANGELOG
|
|
@ -2,9 +2,29 @@
|
||||||
List of major changes and improvements between releases
|
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
|
Yosys 0.51 .. Yosys 0.52
|
||||||
--------------------------
|
--------------------------
|
||||||
* New commands and options
|
* New commands and options
|
||||||
|
|
|
||||||
16
Makefile
16
Makefile
|
|
@ -160,7 +160,7 @@ ifeq ($(OS), Haiku)
|
||||||
CXXFLAGS += -D_DEFAULT_SOURCE
|
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.52+63
|
YOSYS_VER := 0.53+3
|
||||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
|
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
|
||||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
|
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
|
||||||
|
|
@ -183,7 +183,7 @@ endif
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
OBJS = kernel/version_$(GIT_REV).o
|
||||||
|
|
||||||
bumpversion:
|
bumpversion:
|
||||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 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)
|
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"
|
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LINK_ABC),1)
|
||||||
|
ABCMKARGS += "ABC_USE_PIC=1"
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(DISABLE_SPAWN),1)
|
ifeq ($(DISABLE_SPAWN),1)
|
||||||
CXXFLAGS += -DYOSYS_DISABLE_SPAWN
|
CXXFLAGS += -DYOSYS_DISABLE_SPAWN
|
||||||
endif
|
endif
|
||||||
|
|
@ -787,7 +791,7 @@ $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in $(YOSYS_SRC)/Makefile
|
||||||
.PHONY: check-git-abc
|
.PHONY: check-git-abc
|
||||||
|
|
||||||
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 "Error: The 'abc' directory does not exist."; \
|
||||||
echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
|
echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
|
||||||
exit 1; \
|
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 "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."; \
|
echo "4. Reapply your changes: Move your saved changes back to the 'abc' directory, if necessary."; \
|
||||||
exit 1; \
|
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 \
|
else \
|
||||||
echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
|
echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
|
|
|
||||||
|
|
@ -2410,7 +2410,12 @@ struct CxxrtlWorker {
|
||||||
auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
|
auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
|
||||||
cell_attrs.erase(ID::module_not_derived);
|
cell_attrs.erase(ID::module_not_derived);
|
||||||
f << indent << "scopes->add(path, " << escape_cxx_string(get_hdl_name(cell)) << ", ";
|
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);
|
dump_serialized_metadata(module_attrs);
|
||||||
f << ", ";
|
f << ", ";
|
||||||
dump_serialized_metadata(cell_attrs);
|
dump_serialized_metadata(cell_attrs);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
template<size_t BitsY, size_t BitsA, size_t BitsB>
|
||||||
CXXRTL_ALWAYS_INLINE
|
CXXRTL_ALWAYS_INLINE
|
||||||
value<BitsY> shr_su(const value<BitsA> &a, const value<BitsB> &b) {
|
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>
|
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> quotient;
|
||||||
value<Bits> remainder;
|
value<Bits> remainder;
|
||||||
value<Bits> dividend = a.template zext<Bits>();
|
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);
|
std::tie(quotient, remainder) = dividend.udivmod(divisor);
|
||||||
return {quotient.template trunc<BitsY>(), remainder.template trunc<BitsY>()};
|
return {quotient.template trunc<BitsY>(), remainder.template trunc<BitsY>()};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import os
|
||||||
project = 'YosysHQ Yosys'
|
project = 'YosysHQ Yosys'
|
||||||
author = 'YosysHQ GmbH'
|
author = 'YosysHQ GmbH'
|
||||||
copyright ='2025 YosysHQ GmbH'
|
copyright ='2025 YosysHQ GmbH'
|
||||||
yosys_ver = "0.52"
|
yosys_ver = "0.53"
|
||||||
|
|
||||||
# select HTML theme
|
# select HTML theme
|
||||||
html_theme = 'furo-ys'
|
html_theme = 'furo-ys'
|
||||||
|
|
|
||||||
|
|
@ -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)) {
|
if (!str.empty() && str[0] == '\\' && (template_node->type == AST_STRUCT || template_node->type == AST_UNION)) {
|
||||||
// replace instance with wire representing the packed structure
|
// replace instance with wire representing the packed structure
|
||||||
newNode = make_packed_struct(template_node, str, attributes);
|
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));
|
newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str));
|
||||||
// add original input/output attribute to resolved wire
|
// add original input/output attribute to resolved wire
|
||||||
newNode->is_input = this->is_input;
|
newNode->is_input = this->is_input;
|
||||||
|
|
|
||||||
|
|
@ -1446,6 +1446,25 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
||||||
module_name = "\\" + sha1_if_contain_spaces(module_name);
|
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;
|
netlist = nl;
|
||||||
|
|
||||||
if (design->has(module_name)) {
|
if (design->has(module_name)) {
|
||||||
|
|
|
||||||
|
|
@ -2249,7 +2249,8 @@ cell_parameter:
|
||||||
node->children.push_back($1);
|
node->children.push_back($1);
|
||||||
} |
|
} |
|
||||||
'.' TOK_ID '(' ')' {
|
'.' TOK_ID '(' ')' {
|
||||||
// just ignore empty parameters
|
// delete unused TOK_ID
|
||||||
|
delete $2;
|
||||||
} |
|
} |
|
||||||
'.' TOK_ID '(' expr ')' {
|
'.' TOK_ID '(' expr ')' {
|
||||||
AstNode *node = new AstNode(AST_PARASET);
|
AstNode *node = new AstNode(AST_PARASET);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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
|
// left shifts
|
||||||
if (cell->type.in(ID($shl), ID($sshl))) {
|
if (cell->type.in(ID($shl), ID($sshl))) {
|
||||||
if (a_width == 1 && is_signed) {
|
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;
|
bool shift_in_bulk = i < a_width - 1;
|
||||||
// can we jump into the zero-padding by toggling B[k]?
|
// can we jump into the zero-padding by toggling B[k]?
|
||||||
bool zpad_jump = (((y_width - i) & ((1 << (k + 1)) - 1)) != 0 \
|
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))
|
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);
|
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)
|
// bidirectional shifts (positive B shifts right, negative left)
|
||||||
} else if (cell->type.in(ID($shift), ID($shiftx)) && is_b_signed) {
|
} else if (cell->type.in(ID($shift), ID($shiftx)) && is_b_signed) {
|
||||||
if (is_signed) {
|
if (is_signed) {
|
||||||
if (k != b_width - 1) {
|
if (k != b_width_capped - 1) {
|
||||||
bool r_shift_in_bulk = i < a_width - 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]?
|
// 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 \
|
bool r_zpad_jump = (((y_width - i) & ((1 << (k + 1)) - 1)) != 0 \
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,7 @@ int main(int argc, char **argv)
|
||||||
auto result = options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
if (result.count("M")) memhasher_on();
|
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("A")) call_abort = true;
|
||||||
if (result.count("Q")) print_banner = false;
|
if (result.count("Q")) print_banner = false;
|
||||||
if (result.count("T")) print_stats = false;
|
if (result.count("T")) print_stats = false;
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,12 @@ gzip_istream::ibuf::~ibuf() {
|
||||||
|
|
||||||
// Takes a successfully opened ifstream. If it's gzipped, returns an istream. Otherwise,
|
// Takes a successfully opened ifstream. If it's gzipped, returns an istream. Otherwise,
|
||||||
// returns the original ifstream, rewound to the start.
|
// 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::istream* uncompressed(const std::string filename, std::ios_base::openmode mode) {
|
||||||
std::ifstream* f = new std::ifstream();
|
std::ifstream* f = new std::ifstream();
|
||||||
f->open(filename, mode);
|
f->open(filename, mode);
|
||||||
if (f->fail())
|
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
|
// Check for gzip magic
|
||||||
unsigned char magic[3];
|
unsigned char magic[3];
|
||||||
int n = 0;
|
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]));
|
filename.c_str(), unsigned(magic[2]));
|
||||||
gzip_istream* s = new gzip_istream();
|
gzip_istream* s = new gzip_istream();
|
||||||
delete f;
|
delete f;
|
||||||
s->open(filename.c_str());
|
log_assert(s->open(filename.c_str()));
|
||||||
return s;
|
return s;
|
||||||
#else
|
#else
|
||||||
log_cmd_error("File `%s' is a gzip file, but Yosys is compiled without zlib.\n", filename.c_str());
|
log_cmd_error("File `%s' is a gzip file, but Yosys is compiled without zlib.\n", filename.c_str());
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ static inline bool ys_debug(int n = 0) { if (log_force_debug) return true; log_d
|
||||||
#else
|
#else
|
||||||
static inline bool ys_debug(int = 0) { return false; }
|
static inline bool ys_debug(int = 0) { return false; }
|
||||||
#endif
|
#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() {
|
static inline void log_suppressed() {
|
||||||
if (log_debug_suppressed && !log_make_debug) {
|
if (log_debug_suppressed && !log_make_debug) {
|
||||||
|
|
|
||||||
|
|
@ -472,8 +472,6 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s
|
||||||
yosys_input_files.insert(filename);
|
yosys_input_files.insert(filename);
|
||||||
f = uncompressed(filename, bin_input ? std::ifstream::binary : std::ifstream::in);
|
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++)
|
for (size_t i = argidx+1; i < args.size(); i++)
|
||||||
if (args[i].compare(0, 1, "-") == 0)
|
if (args[i].compare(0, 1, "-") == 0)
|
||||||
|
|
|
||||||
|
|
@ -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_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
|
||||||
std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), 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_a = ez->expression(ezSAT::OpOr, undef_a);
|
||||||
int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
|
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);
|
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);
|
undefGating(y, tmp, undef_y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,3 +55,4 @@ OBJS += passes/cmds/wrapcell.o
|
||||||
OBJS += passes/cmds/setenv.o
|
OBJS += passes/cmds/setenv.o
|
||||||
OBJS += passes/cmds/abstract.o
|
OBJS += passes/cmds/abstract.o
|
||||||
OBJS += passes/cmds/test_select.o
|
OBJS += passes/cmds/test_select.o
|
||||||
|
OBJS += passes/cmds/timeest.o
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ struct CleanZeroWidthPass : public Pass {
|
||||||
// A and B to 1-bit if their width is 0.
|
// A and B to 1-bit if their width is 0.
|
||||||
if (cell->getParam(ID::Y_WIDTH).as_int() == 0) {
|
if (cell->getParam(ID::Y_WIDTH).as_int() == 0) {
|
||||||
module->remove(cell);
|
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.
|
// TODO: fixing zero-width A and B not supported.
|
||||||
} else {
|
} else {
|
||||||
if (cell->getParam(ID::A_WIDTH).as_int() == 0) {
|
if (cell->getParam(ID::A_WIDTH).as_int() == 0) {
|
||||||
|
|
|
||||||
|
|
@ -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_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \
|
||||||
X(num_processes)
|
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;
|
#define X(_name) unsigned int _name;
|
||||||
STAT_INT_MEMBERS
|
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());
|
std::istream* f = uncompressed(liberty_file.c_str());
|
||||||
yosys_input_files.insert(liberty_file);
|
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);
|
LibertyParser libparser(*f, liberty_file);
|
||||||
delete f;
|
delete f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ struct TeePass : public Pass {
|
||||||
}
|
}
|
||||||
if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) {
|
if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) {
|
||||||
const char *open_mode = args[argidx] == "-o" ? "w" : "a+";
|
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]);
|
yosys_input_files.insert(args[argidx]);
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
for (auto cf : files_to_close)
|
for (auto cf : files_to_close)
|
||||||
|
|
|
||||||
418
passes/cmds/timeest.cc
Normal file
418
passes/cmds/timeest.cc
Normal 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
|
||||||
|
|
@ -1315,13 +1315,14 @@ skip_fine_alu:
|
||||||
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
|
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());
|
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))
|
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());
|
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++) {
|
for (int i = 0; i < GetSize(sig_y); i++) {
|
||||||
int idx = i + shift_bits;
|
int idx = i + shift_bits;
|
||||||
if (0 <= idx && idx < GetSize(sig_a))
|
if (0 <= idx && idx < GetSize(sig_a))
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include "kernel/ffinit.h"
|
#include "kernel/ffinit.h"
|
||||||
#include "kernel/ff.h"
|
#include "kernel/ff.h"
|
||||||
#include "kernel/modtools.h"
|
#include "kernel/modtools.h"
|
||||||
|
#include "kernel/mem.h"
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
@ -537,6 +538,12 @@ struct FormalFfPass : public Pass {
|
||||||
log(" Add assumptions that constrain wires with the 'replaced_by_gclk'\n");
|
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(" attribute to the value they would have before an active clock edge.\n");
|
||||||
log("\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
|
// 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_setundef = false;
|
||||||
bool flag_hierarchy = false;
|
bool flag_hierarchy = false;
|
||||||
bool flag_assume = false;
|
bool flag_assume = false;
|
||||||
|
bool flag_declockgate = false;
|
||||||
|
|
||||||
log_header(design, "Executing FORMALFF pass.\n");
|
log_header(design, "Executing FORMALFF pass.\n");
|
||||||
|
|
||||||
|
|
@ -583,22 +591,237 @@ struct FormalFfPass : public Pass {
|
||||||
flag_assume = true;
|
flag_assume = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-declockgate") {
|
||||||
|
flag_declockgate = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
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");
|
log_cmd_error("One of the options -clk2ff, -ff2anyinit, -anyinit2ff, -hierarchy or -assume must be specified.\n");
|
||||||
|
|
||||||
if (flag_ff2anyinit && flag_anyinit2ff)
|
if (flag_ff2anyinit && flag_anyinit2ff)
|
||||||
log_cmd_error("The options -ff2anyinit and -anyinit2ff are exclusive.\n");
|
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)
|
if (flag_fine && !flag_anyinit2ff)
|
||||||
log_cmd_error("The option -fine requries the -anyinit2ff option.\n");
|
log_cmd_error("The option -fine requries the -anyinit2ff option.\n");
|
||||||
|
|
||||||
if (flag_fine && flag_clk2ff)
|
if (flag_fine && flag_clk2ff)
|
||||||
log_cmd_error("The options -fine and -clk2ff are exclusive.\n");
|
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())
|
for (auto module : design->selected_modules())
|
||||||
{
|
{
|
||||||
if (flag_setundef)
|
if (flag_setundef)
|
||||||
|
|
|
||||||
|
|
@ -310,8 +310,6 @@ struct ClockgatePass : public Pass {
|
||||||
LibertyMergedCells merged;
|
LibertyMergedCells merged;
|
||||||
for (auto path : liberty_files) {
|
for (auto path : liberty_files) {
|
||||||
std::istream* f = uncompressed(path);
|
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);
|
LibertyParser p(*f, path);
|
||||||
merged.merge(p);
|
merged.merge(p);
|
||||||
delete f;
|
delete f;
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,9 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
|
||||||
} else if (expr[0] == '!') {
|
} else if (expr[0] == '!') {
|
||||||
data_name = expr.substr(1, expr.size()-1);
|
data_name = expr.substr(1, expr.size()-1);
|
||||||
data_not_inverted = false;
|
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 {
|
} else {
|
||||||
data_name = expr;
|
data_name = expr;
|
||||||
data_not_inverted = true;
|
data_not_inverted = true;
|
||||||
|
|
@ -632,8 +635,6 @@ struct DfflibmapPass : public Pass {
|
||||||
LibertyMergedCells merged;
|
LibertyMergedCells merged;
|
||||||
for (auto path : liberty_files) {
|
for (auto path : liberty_files) {
|
||||||
std::istream* f = uncompressed(path);
|
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);
|
LibertyParser p(*f, path);
|
||||||
merged.merge(p);
|
merged.merge(p);
|
||||||
delete f;
|
delete f;
|
||||||
|
|
|
||||||
|
|
@ -349,6 +349,10 @@ struct FlattenPass : public Pass {
|
||||||
log(" -separator <char>\n");
|
log(" -separator <char>\n");
|
||||||
log(" Use this separator char instead of '.' when concatenating design levels.\n");
|
log(" Use this separator char instead of '.' when concatenating design levels.\n");
|
||||||
log("\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
|
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"))
|
if (design->scratchpad.count("flatten.separator"))
|
||||||
worker.separator = design->scratchpad_get_string("flatten.separator");
|
worker.separator = design->scratchpad_get_string("flatten.separator");
|
||||||
|
|
||||||
|
bool cleanup = true;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
if (args[argidx] == "-wb") {
|
if (args[argidx] == "-wb") {
|
||||||
|
|
@ -378,6 +384,10 @@ struct FlattenPass : public Pass {
|
||||||
worker.separator = args[++argidx];
|
worker.separator = args[++argidx];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-nocleanup") {
|
||||||
|
cleanup = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
@ -414,7 +424,7 @@ struct FlattenPass : public Pass {
|
||||||
for (auto module : topo_modules.sorted)
|
for (auto module : topo_modules.sorted)
|
||||||
worker.flatten_module(design, module, used_modules, worker.separator);
|
worker.flatten_module(design, module, used_modules, worker.separator);
|
||||||
|
|
||||||
if (top != nullptr)
|
if (cleanup && top != nullptr)
|
||||||
for (auto module : design->modules().to_vector())
|
for (auto module : design->modules().to_vector())
|
||||||
if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) {
|
if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) {
|
||||||
log("Deleting now unused module %s.\n", log_id(module));
|
log("Deleting now unused module %s.\n", log_id(module));
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ module TRELLIS_IO(
|
||||||
);
|
);
|
||||||
parameter DIR = "INPUT";
|
parameter DIR = "INPUT";
|
||||||
reg T_pd;
|
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
|
generate
|
||||||
if (DIR == "INPUT") begin
|
if (DIR == "INPUT") begin
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ ram block $__CC_BRAM_TDP_ {
|
||||||
}
|
}
|
||||||
portoption "WR_MODE" "WRITE_THROUGH" {
|
portoption "WR_MODE" "WRITE_THROUGH" {
|
||||||
rdwr new;
|
rdwr new;
|
||||||
wrtrans all new;
|
|
||||||
}
|
}
|
||||||
wrbe_separate;
|
wrbe_separate;
|
||||||
optional_rw;
|
optional_rw;
|
||||||
|
|
|
||||||
|
|
@ -115,15 +115,15 @@ generate
|
||||||
.A_CLK(PORT_A_CLK),
|
.A_CLK(PORT_A_CLK),
|
||||||
.A_EN(PORT_A_CLK_EN),
|
.A_EN(PORT_A_CLK_EN),
|
||||||
.A_WE(PORT_A_WR_EN),
|
.A_WE(PORT_A_WR_EN),
|
||||||
.A_BM(PORT_A_WR_BE),
|
.A_BM({{(20-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
|
||||||
.A_DI(PORT_A_WR_DATA),
|
.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_ADDR({PORT_A_ADDR[13:5], 1'b0, PORT_A_ADDR[4:0], 1'b0}),
|
||||||
.A_DO(PORT_A_RD_DATA),
|
.A_DO(PORT_A_RD_DATA),
|
||||||
.B_CLK(PORT_B_CLK),
|
.B_CLK(PORT_B_CLK),
|
||||||
.B_EN(PORT_B_CLK_EN),
|
.B_EN(PORT_B_CLK_EN),
|
||||||
.B_WE(PORT_B_WR_EN),
|
.B_WE(PORT_B_WR_EN),
|
||||||
.B_BM(PORT_B_WR_BE),
|
.B_BM({{(20-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
|
||||||
.B_DI(PORT_B_WR_DATA),
|
.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_ADDR({PORT_B_ADDR[13:5], 1'b0, PORT_B_ADDR[4:0], 1'b0}),
|
||||||
.B_DO(PORT_B_RD_DATA),
|
.B_DO(PORT_B_RD_DATA),
|
||||||
);
|
);
|
||||||
|
|
@ -270,15 +270,15 @@ generate
|
||||||
.A_CLK(PORT_A_CLK),
|
.A_CLK(PORT_A_CLK),
|
||||||
.A_EN(PORT_A_CLK_EN),
|
.A_EN(PORT_A_CLK_EN),
|
||||||
.A_WE(PORT_A_WR_EN),
|
.A_WE(PORT_A_WR_EN),
|
||||||
.A_BM(PORT_A_WR_BE),
|
.A_BM({{(40-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
|
||||||
.A_DI(PORT_A_WR_DATA),
|
.A_DI({{(40-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
|
||||||
.A_ADDR({PORT_A_ADDR[14:0], 1'b0}),
|
.A_ADDR({PORT_A_ADDR[14:0], 1'b0}),
|
||||||
.A_DO(PORT_A_RD_DATA),
|
.A_DO(PORT_A_RD_DATA),
|
||||||
.B_CLK(PORT_B_CLK),
|
.B_CLK(PORT_B_CLK),
|
||||||
.B_EN(PORT_B_CLK_EN),
|
.B_EN(PORT_B_CLK_EN),
|
||||||
.B_WE(PORT_B_WR_EN),
|
.B_WE(PORT_B_WR_EN),
|
||||||
.B_BM(PORT_B_WR_BE),
|
.B_BM({{(40-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
|
||||||
.B_DI(PORT_B_WR_DATA),
|
.B_DI({{(40-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
|
||||||
.B_ADDR({PORT_B_ADDR[14:0], 1'b0}),
|
.B_ADDR({PORT_B_ADDR[14:0], 1'b0}),
|
||||||
.B_DO(PORT_B_RD_DATA),
|
.B_DO(PORT_B_RD_DATA),
|
||||||
);
|
);
|
||||||
|
|
@ -429,14 +429,14 @@ generate
|
||||||
.A_CLK(PORT_A_CLK),
|
.A_CLK(PORT_A_CLK),
|
||||||
.A_EN(PORT_A_CLK_EN),
|
.A_EN(PORT_A_CLK_EN),
|
||||||
.A_WE(PORT_A_WR_EN),
|
.A_WE(PORT_A_WR_EN),
|
||||||
.A_BM(PORT_A_WR_BE),
|
.A_BM({{(40-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
|
||||||
.A_DI(PORT_A_WR_DATA),
|
.A_DI({{(40-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
|
||||||
.A_ADDR({PORT_A_ADDR[14:0], PORT_A_ADDR[15]}),
|
.A_ADDR({PORT_A_ADDR[14:0], PORT_A_ADDR[15]}),
|
||||||
.B_CLK(PORT_B_CLK),
|
.B_CLK(PORT_B_CLK),
|
||||||
.B_EN(PORT_B_CLK_EN),
|
.B_EN(PORT_B_CLK_EN),
|
||||||
.B_WE(PORT_B_WR_EN),
|
.B_WE(PORT_B_WR_EN),
|
||||||
.B_BM(PORT_B_WR_BE),
|
.B_BM({{(40-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
|
||||||
.B_DI(PORT_B_WR_DATA),
|
.B_DI({{(40-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
|
||||||
.B_ADDR({PORT_B_ADDR[14:0], PORT_B_ADDR[15]}),
|
.B_ADDR({PORT_B_ADDR[14:0], PORT_B_ADDR[15]}),
|
||||||
);
|
);
|
||||||
CC_BRAM_40K #(
|
CC_BRAM_40K #(
|
||||||
|
|
@ -584,15 +584,15 @@ generate
|
||||||
.A_CLK(PORT_A_CLK),
|
.A_CLK(PORT_A_CLK),
|
||||||
.A_EN(PORT_A_CLK_EN),
|
.A_EN(PORT_A_CLK_EN),
|
||||||
.A_WE(PORT_A_WR_EN),
|
.A_WE(PORT_A_WR_EN),
|
||||||
.A_BM(PORT_A_WR_BE),
|
.A_BM({{(40-PORT_A_WR_BE_WIDTH){1'bx}}, PORT_A_WR_BE}),
|
||||||
.A_DI(PORT_A_WR_DATA),
|
.A_DI({{(40-PORT_A_WR_WIDTH){1'bx}}, PORT_A_WR_DATA}),
|
||||||
.A_DO(PORT_A_RD_DATA),
|
.A_DO(PORT_A_RD_DATA),
|
||||||
.A_ADDR({PORT_A_ADDR[14:0], PORT_A_ADDR[15]}),
|
.A_ADDR({PORT_A_ADDR[14:0], PORT_A_ADDR[15]}),
|
||||||
.B_CLK(PORT_B_CLK),
|
.B_CLK(PORT_B_CLK),
|
||||||
.B_EN(PORT_B_CLK_EN),
|
.B_EN(PORT_B_CLK_EN),
|
||||||
.B_WE(PORT_B_WR_EN),
|
.B_WE(PORT_B_WR_EN),
|
||||||
.B_BM(PORT_B_WR_BE),
|
.B_BM({{(40-PORT_B_WR_BE_WIDTH){1'bx}}, PORT_B_WR_BE}),
|
||||||
.B_DI(PORT_B_WR_DATA),
|
.B_DI({{(40-PORT_B_WR_WIDTH){1'bx}}, PORT_B_WR_DATA}),
|
||||||
.B_DO(PORT_B_RD_DATA),
|
.B_DO(PORT_B_RD_DATA),
|
||||||
.B_ADDR({PORT_B_ADDR[14:0], PORT_B_ADDR[15]}),
|
.B_ADDR({PORT_B_ADDR[14:0], PORT_B_ADDR[15]}),
|
||||||
);
|
);
|
||||||
|
|
@ -710,9 +710,9 @@ generate
|
||||||
.A_EN(PORT_W_CLK_EN),
|
.A_EN(PORT_W_CLK_EN),
|
||||||
.A_WE(PORT_W_WR_EN),
|
.A_WE(PORT_W_WR_EN),
|
||||||
.A_BM(PORT_W_WR_BE[19:0]),
|
.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]),
|
.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}),
|
.A_ADDR({PORT_W_ADDR[13:5], 1'b0, PORT_W_ADDR[4:0], 1'b0}),
|
||||||
.B_CLK(PORT_R_CLK),
|
.B_CLK(PORT_R_CLK),
|
||||||
.B_EN(PORT_R_CLK_EN),
|
.B_EN(PORT_R_CLK_EN),
|
||||||
|
|
@ -865,9 +865,9 @@ generate
|
||||||
.A_EN(PORT_W_CLK_EN),
|
.A_EN(PORT_W_CLK_EN),
|
||||||
.A_WE(PORT_W_WR_EN),
|
.A_WE(PORT_W_WR_EN),
|
||||||
.A_BM(PORT_W_WR_BE[39:0]),
|
.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]),
|
.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}),
|
.A_ADDR({PORT_W_ADDR[14:0], 1'b0}),
|
||||||
.B_CLK(PORT_R_CLK),
|
.B_CLK(PORT_R_CLK),
|
||||||
.B_EN(PORT_R_CLK_EN),
|
.B_EN(PORT_R_CLK_EN),
|
||||||
|
|
|
||||||
|
|
@ -292,10 +292,10 @@ module CC_DLT #(
|
||||||
always @(*)
|
always @(*)
|
||||||
begin
|
begin
|
||||||
if (sr) begin
|
if (sr) begin
|
||||||
Q <= SR_VAL;
|
Q = SR_VAL;
|
||||||
end
|
end
|
||||||
else if (en) begin
|
else if (en) begin
|
||||||
Q <= D;
|
Q = D;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -407,7 +407,7 @@ module CC_MULT #(
|
||||||
);
|
);
|
||||||
always @(*)
|
always @(*)
|
||||||
begin
|
begin
|
||||||
P <= A * B;
|
P = A * B;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ module GP_COUNT14(input CLK, input wire RST, output reg OUT);
|
||||||
|
|
||||||
//Combinatorially output underflow flag whenever we wrap low
|
//Combinatorially output underflow flag whenever we wrap low
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
OUT <= (count == 14'h0);
|
OUT = (count == 14'h0);
|
||||||
end
|
end
|
||||||
|
|
||||||
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
|
//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
|
//Combinatorially output underflow flag whenever we wrap low
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(UP)
|
if(UP)
|
||||||
OUT <= (count == 14'h3fff);
|
OUT = (count == 14'h3fff);
|
||||||
else
|
else
|
||||||
OUT <= (count == 14'h0);
|
OUT = (count == 14'h0);
|
||||||
POUT <= count[7:0];
|
POUT = count[7:0];
|
||||||
end
|
end
|
||||||
|
|
||||||
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
|
//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
|
//Combinatorially output underflow flag whenever we wrap low
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(UP)
|
if(UP)
|
||||||
OUT <= (count == 8'hff);
|
OUT = (count == 8'hff);
|
||||||
else
|
else
|
||||||
OUT <= (count == 8'h0);
|
OUT = (count == 8'h0);
|
||||||
POUT <= count;
|
POUT = count;
|
||||||
end
|
end
|
||||||
|
|
||||||
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
|
//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
|
//Combinatorially output underflow flag whenever we wrap low
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
OUT <= (count == 8'h0);
|
OUT = (count == 8'h0);
|
||||||
POUT <= count;
|
POUT = count;
|
||||||
end
|
end
|
||||||
|
|
||||||
//POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm.
|
//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
|
always @(*) begin
|
||||||
case(SEL)
|
case(SEL)
|
||||||
2'd00: begin
|
2'd00: begin
|
||||||
OUTA <= IN0;
|
OUTA = IN0;
|
||||||
OUTB <= IN3;
|
OUTB = IN3;
|
||||||
end
|
end
|
||||||
|
|
||||||
2'd01: begin
|
2'd01: begin
|
||||||
OUTA <= IN1;
|
OUTA = IN1;
|
||||||
OUTB <= IN2;
|
OUTB = IN2;
|
||||||
end
|
end
|
||||||
|
|
||||||
2'd02: begin
|
2'd02: begin
|
||||||
OUTA <= IN2;
|
OUTA = IN2;
|
||||||
OUTB <= IN1;
|
OUTB = IN1;
|
||||||
end
|
end
|
||||||
|
|
||||||
2'd03: begin
|
2'd03: begin
|
||||||
OUTA <= IN3;
|
OUTA = IN3;
|
||||||
OUTB <= IN0;
|
OUTB = IN0;
|
||||||
end
|
end
|
||||||
|
|
||||||
endcase
|
endcase
|
||||||
|
|
@ -635,7 +635,7 @@ module GP_DLATCH(input D, input nCLK, output reg Q);
|
||||||
initial Q = INIT;
|
initial Q = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nCLK)
|
if(!nCLK)
|
||||||
Q <= D;
|
Q = D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -644,7 +644,7 @@ module GP_DLATCHI(input D, input nCLK, output reg nQ);
|
||||||
initial nQ = INIT;
|
initial nQ = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nCLK)
|
if(!nCLK)
|
||||||
nQ <= ~D;
|
nQ = ~D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -653,9 +653,9 @@ module GP_DLATCHR(input D, input nCLK, input nRST, output reg Q);
|
||||||
initial Q = INIT;
|
initial Q = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nRST)
|
if(!nRST)
|
||||||
Q <= 1'b0;
|
Q = 1'b0;
|
||||||
else if(!nCLK)
|
else if(!nCLK)
|
||||||
Q <= D;
|
Q = D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -664,9 +664,9 @@ module GP_DLATCHRI(input D, input nCLK, input nRST, output reg nQ);
|
||||||
initial nQ = INIT;
|
initial nQ = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nRST)
|
if(!nRST)
|
||||||
nQ <= 1'b1;
|
nQ = 1'b1;
|
||||||
else if(!nCLK)
|
else if(!nCLK)
|
||||||
nQ <= ~D;
|
nQ = ~D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -675,9 +675,9 @@ module GP_DLATCHS(input D, input nCLK, input nSET, output reg Q);
|
||||||
initial Q = INIT;
|
initial Q = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nSET)
|
if(!nSET)
|
||||||
Q <= 1'b1;
|
Q = 1'b1;
|
||||||
else if(!nCLK)
|
else if(!nCLK)
|
||||||
Q <= D;
|
Q = D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -686,9 +686,9 @@ module GP_DLATCHSI(input D, input nCLK, input nSET, output reg nQ);
|
||||||
initial nQ = INIT;
|
initial nQ = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nSET)
|
if(!nSET)
|
||||||
nQ <= 1'b0;
|
nQ = 1'b0;
|
||||||
else if(!nCLK)
|
else if(!nCLK)
|
||||||
nQ <= ~D;
|
nQ = ~D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -698,9 +698,9 @@ module GP_DLATCHSR(input D, input nCLK, input nSR, output reg Q);
|
||||||
initial Q = INIT;
|
initial Q = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nSR)
|
if(!nSR)
|
||||||
Q <= SRMODE;
|
Q = SRMODE;
|
||||||
else if(!nCLK)
|
else if(!nCLK)
|
||||||
Q <= D;
|
Q = D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -710,9 +710,9 @@ module GP_DLATCHSRI(input D, input nCLK, input nSR, output reg nQ);
|
||||||
initial nQ = INIT;
|
initial nQ = INIT;
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if(!nSR)
|
if(!nSR)
|
||||||
nQ <= ~SRMODE;
|
nQ = ~SRMODE;
|
||||||
else if(!nCLK)
|
else if(!nCLK)
|
||||||
nQ <= ~D;
|
nQ = ~D;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ match mul
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code sigA sigB sigH
|
code sigA sigB sigH
|
||||||
auto unextend = [](const SigSpec &sig) {
|
auto unextend_signed = [](const SigSpec &sig) {
|
||||||
int i;
|
int i;
|
||||||
for (i = GetSize(sig)-1; i > 0; i--)
|
for (i = GetSize(sig)-1; i > 0; i--)
|
||||||
if (sig[i] != sig[i-1])
|
if (sig[i] != sig[i-1])
|
||||||
|
|
@ -32,8 +32,16 @@ code sigA sigB sigH
|
||||||
++i;
|
++i;
|
||||||
return sig.extract(0, i);
|
return sig.extract(0, i);
|
||||||
};
|
};
|
||||||
sigA = unextend(port(mul, \A));
|
auto unextend_unsigned = [](const SigSpec &sig) {
|
||||||
sigB = unextend(port(mul, \B));
|
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;
|
SigSpec O;
|
||||||
if (mul->type == $mul)
|
if (mul->type == $mul)
|
||||||
|
|
|
||||||
80
tests/arch/ice40/ice40_dsp_const.ys
Normal file
80
tests/arch/ice40/ice40_dsp_const.ys
Normal 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
22
tests/liberty/dff.lib
Normal 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 */
|
||||||
19
tests/liberty/dff.lib.filtered.ok
Normal file
19
tests/liberty/dff.lib.filtered.ok
Normal 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" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tests/liberty/dff.lib.verilogsim.ok
Normal file
12
tests/liberty/dff.lib.verilogsim.ok
Normal 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
29
tests/liberty/dff.log.ok
Normal 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*
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -eo pipefail
|
||||||
|
|
||||||
for x in *.lib; do
|
for x in *.lib; do
|
||||||
echo "Testing on $x.."
|
echo "Testing on $x.."
|
||||||
../../yosys -p "read_verilog small.v; synth -top small; dfflibmap -info -liberty ${x}" -ql ${x%.lib}.log
|
../../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 - $x 2>/dev/null > $x.filtered
|
||||||
../../yosys-filterlib -verilogsim $x > $x.verilogsim
|
../../yosys-filterlib -verilogsim $x > $x.verilogsim
|
||||||
diff $x.filtered $x.filtered.ok && diff $x.verilogsim $x.verilogsim.ok
|
diff $x.filtered $x.filtered.ok
|
||||||
done || exit 1
|
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
|
for x in *.ys; do
|
||||||
echo "Running $x.."
|
echo "Running $x.."
|
||||||
../../yosys -q -s $x -l ${x%.ys}.log
|
../../yosys -q -s $x -l ${x%.ys}.log
|
||||||
done || exit 1
|
done
|
||||||
|
|
||||||
|
|
|
||||||
50
tests/opt/opt_expr_shift.ys
Normal file
50
tests/opt/opt_expr_shift.ys
Normal 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
|
||||||
83
tests/various/formalff_declockgate.ys
Normal file
83
tests/various/formalff_declockgate.ys
Normal 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
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
read_rtlil << EOF
|
read_rtlil << EOT
|
||||||
module \top
|
module \top
|
||||||
wire input 1 \A
|
wire input 1 \A
|
||||||
wire output 2 \Y
|
wire output 2 \Y
|
||||||
|
|
@ -8,7 +8,67 @@ module \top
|
||||||
connect \Y \Y
|
connect \Y \Y
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
EOF
|
EOT
|
||||||
logger -expect log "Chip area for module '\\top': 9.072000" 1
|
logger -expect log "Chip area for module '\\top': 9.072000" 1
|
||||||
logger -expect-no-warnings
|
logger -expect-no-warnings
|
||||||
stat -liberty ../../tests/liberty/foundry_data/sg13g2_stdcell_typ_1p20V_25C.lib.filtered.gz
|
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
|
||||||
|
|
|
||||||
24
tests/verilog/param_default.ys
Normal file
24
tests/verilog/param_default.ys
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue