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:
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
|
||||
=======================================================
|
||||
|
||||
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
|
||||
|
|
|
|||
16
Makefile
16
Makefile
|
|
@ -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; \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>()};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
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_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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ ram block $__CC_BRAM_TDP_ {
|
|||
}
|
||||
portoption "WR_MODE" "WRITE_THROUGH" {
|
||||
rdwr new;
|
||||
wrtrans all new;
|
||||
}
|
||||
wrbe_separate;
|
||||
optional_rw;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
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
|
||||
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
|
||||
|
||||
|
|
|
|||
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
|
||||
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
|
||||
|
|
|
|||
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