Compare commits

...

29 Commits

Author SHA1 Message Date
George Rennie 1b30680beb
Merge 33d5138673 into 6f3376cbe6 2024-11-28 10:11:50 +01:00
KrystalDelusion 6f3376cbe6
Merge pull request #4730 from YosysHQ/krys/downstream-docs
Improvements for downstream-distro maintainability.
2024-11-28 14:35:16 +13:00
github-actions[bot] 87742fa688 Bump version 2024-11-28 01:26:26 +00:00
Martin Povišer 646c5a19a8
Merge pull request #4776 from YosysHQ/krys/get_blackbox_attribute
Move get_blackbox_attribute method to Module instead of AttrObject
2024-11-28 00:25:16 +01:00
Martin Povišer 1717a0b9c0
Merge pull request #4721 from ldoolitt/main
kernel/drivertools.h: avoid maybe-uninitialized compile warnings
2024-11-28 00:09:43 +01:00
Martin Povišer 956313efe8
Merge pull request #4742 from YosysHQ/hierarchy_notify_top_attr
Print a note about finding attribute (* top *) in hierarchy
2024-11-28 00:07:18 +01:00
Martin Povišer 3bab837bc9
Merge pull request #4765 from georgerennie/george/rtlil_case_rule
read_rtlil: Warn on assigns after switches in case rules
2024-11-28 00:01:21 +01:00
KrystalDelusion 698c464109
Merge pull request #4767 from YosysHQ/krys/latest-compilers
test-compile: Use newer clang and gcc versions
2024-11-28 11:51:38 +13:00
KrystalDelusion f428163252
Move get_blackbox_attribute method to Module instead of AttrObject 2024-11-28 11:19:16 +13:00
KrystalDelusion 1e0e367aed
test-compile: Drop back to gcc-13 2024-11-26 10:18:09 +13:00
KrystalDelusion 6ff5823d6a
test-compile: Use clang-18 and gcc-14
The 'newest' compilers are actually not all that new, they're just the default for the image.  Instead provide explicit versions.
2024-11-26 09:59:52 +13:00
George Rennie 8148ebd1ad docs: document that assigns must come before switches in case rules 2024-11-21 22:41:13 +01:00
George Rennie 4a057b3c44 read_rtlil: warn on assigns after switches in case rules 2024-11-21 22:41:13 +01:00
George Rennie 33d5138673 prep: add -barriers flag
* This uses optbarriers and flatten -barriers to insert barriers on
  public wires in the design, limiting the optimization that can affect
  them
2024-11-20 18:57:51 +01:00
George Rennie c6e8fb2432 optbarriers: add command to add/remove optimization barriers
* This can optionally ignore rewriting the outputs of cells or processes
* This by default rewrites drivers of wires with public names but can
  also optionally rewrite drivers of wires with private names
* A -remove flag allows cleaning up the design by replacing barriers
  with connections
2024-11-20 18:57:51 +01:00
George Rennie c352f71fc0 flatten: add -barriers flag
* This uses $barrier optimization barriers to connect wires into the
  flattened module instead of connections
2024-11-20 18:57:51 +01:00
George Rennie 73ba5da10f write_verilog: support $buf and $barrier 2024-11-20 18:57:51 +01:00
George Rennie cbe86afd5c write_smt2: support $buf and $barrier 2024-11-20 18:57:51 +01:00
George Rennie ab293d667e write_firrtl: support $buf and $barrier 2024-11-20 18:57:51 +01:00
George Rennie 6b3901e435 write_btor: support $barrier, $buf and $_BUF_ 2024-11-20 18:57:51 +01:00
George Rennie 88816ccc45 write_aiger2: support $barrier 2024-11-20 18:57:51 +01:00
George Rennie 70646da6bb opt_merge: don't merge $barrier cells 2024-11-20 18:57:51 +01:00
George Rennie bdc978380c kernel: define $barrier cell
* This acts as an optimization barrier, limiting the rewriting allowed
* For now this forbids all optimization, but attributes can be added to
  opt in to specific optimizations as required
2024-11-20 18:57:51 +01:00
Krystine Sherwin e649c1a8e1
Docs: Accept empty string for release envvar 2024-11-20 12:31:12 +13:00
Krystine Sherwin 44b68fb498
Docs: Add check for envvar to disable todos 2024-11-20 12:18:17 +13:00
N. Engelhardt 96c526d1ba Print a note about finding attribute (* top *) in hierarchy 2024-11-13 10:21:44 +01:00
Krystine Sherwin 1476eaba00
Docs: Add fallback for missing furo_ys
This is mainly intended for (latex)pdf builds which do not use the furo-ys html theme, where the yosys script syntax highlighting can safely fallback to plaintext.  This effectively makes `furo-ys` an optional dependency to simplify distro-package maintainability.
See also #4725.
2024-11-12 16:23:12 +13:00
Larry Doolittle 3ae9ca7c2b drivertools.h: switch from log_assert(0) to log_abort() for new feature 2024-11-08 10:30:11 -08:00
Larry Doolittle d36a387aca kernel/drivertools.h: avoid maybe-uninitialized compile warnings
Initialize "unsigned int inner" in hash() functions
Includes a log_assert() that might help catch corrupted data structures
or future incomplete modification of DriveType definition
2024-11-07 19:49:25 -08:00
27 changed files with 384 additions and 47 deletions

View File

@ -32,9 +32,9 @@ jobs:
# oldest supported
- 'clang-14'
- 'gcc-10'
# newest
- 'clang'
- 'gcc'
# newest, make sure to update maximum standard step to match
- 'clang-18'
- 'gcc-13'
include:
# macOS
- os: macos-13
@ -72,7 +72,7 @@ jobs:
# maximum standard, only on newest compilers
- name: Build C++20
if: ${{ matrix.compiler == 'clang' || matrix.compiler == 'gcc'}}
if: ${{ matrix.compiler == 'clang-18' || matrix.compiler == 'gcc-13' }}
shell: bash
run: |
make config-$CC_SHORT

View File

@ -155,7 +155,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.47+121
YOSYS_VER := 0.47+135
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo

View File

@ -28,8 +28,8 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa), \
ID($bwmux)
#define BITWISE_OPS ID($buf), ID($barrier), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), \
ID($fa), ID($bwmux)
#define REDUCE_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)
@ -331,7 +331,7 @@ struct Index {
a = CFALSE;
}
if (cell->type.in(ID($buf), ID($pos), ID($_BUF_))) {
if (cell->type.in(ID($buf), ID($barrier), ID($pos), ID($_BUF_))) {
return a;
} else if (cell->type.in(ID($not), ID($_NOT_))) {
return NOT(a);

View File

@ -508,7 +508,7 @@ struct BtorWorker
goto okay;
}
if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos)))
if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_), ID($barrier)))
{
string btor_op;
if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not";
@ -520,9 +520,9 @@ struct BtorWorker
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
SigSpec sig = sigmap(cell->getPort(ID::Y));
// the $pos cell just passes through, all other cells need an actual operation applied
// buffer cells just pass through, all other cells need an actual operation applied
int nid = nid_a;
if (cell->type != ID($pos))
if (!cell->type.in(ID($pos), ID($buf), ID($_BUF_), ID($barrier)))
{
log_assert(!btor_op.empty());
int sid = get_bv_sid(width);

View File

@ -966,7 +966,7 @@ struct FirrtlWorker
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
if (cell->type == ID($pos)) {
if (cell->type.in(ID($pos), ID($buf), ID($barrier))) {
// assign y = a;
// printCell(cell);
string a_expr = make_expr(cell->getPort(ID::A));

View File

@ -677,7 +677,7 @@ struct Smt2Worker
if (cell->type == ID($eqx)) return export_bvop(cell, "(= A B)", 'b');
if (cell->type == ID($not)) return export_bvop(cell, "(bvnot A)");
if (cell->type == ID($pos)) return export_bvop(cell, "A");
if (cell->type.in(ID($pos), ID($buf), ID($barrier))) return export_bvop(cell, "A");
if (cell->type == ID($neg)) return export_bvop(cell, "(bvneg A)");
if (cell->type == ID($add)) return export_bvop(cell, "(bvadd A B)");

View File

@ -1071,7 +1071,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
if (cell->type == ID($_BUF_)) {
if (cell->type.in(ID($buf), ID($_BUF_), ID($barrier))) {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort(ID::Y));
f << stringf(" = ");

View File

@ -242,7 +242,7 @@ Processes
Declares a process, with zero or more attributes, with the given identifier in
the enclosing module. The body of a process consists of zero or more
assignments, exactly one switch, and zero or more syncs.
assignments followed by zero or more switches and zero or more syncs.
See :ref:`sec:rtlil_process` for an overview of processes.
@ -250,7 +250,7 @@ See :ref:`sec:rtlil_process` for an overview of processes.
<process> ::= <attr-stmt>* <proc-stmt> <process-body> <proc-end-stmt>
<proc-stmt> ::= process <id> <eol>
<process-body> ::= <assign-stmt>* <switch>? <assign-stmt>* <sync>*
<process-body> ::= <assign-stmt>* <switch>* <sync>*
<assign-stmt> ::= assign <dest-sigspec> <src-sigspec> <eol>
<dest-sigspec> ::= <sigspec>
<src-sigspec> ::= <sigspec>
@ -262,8 +262,8 @@ Switches
Switches test a signal for equality against a list of cases. Each case specifies
a comma-separated list of signals to check against. If there are no signals in
the list, then the case is the default case. The body of a case consists of zero
or more switches and assignments. Both switches and cases may have zero or more
attributes.
or more assignments followed by zero or more switches. Both switches and cases
may have zero or more attributes.
.. code:: BNF
@ -272,7 +272,7 @@ attributes.
<case> ::= <attr-stmt>* <case-stmt> <case-body>
<case-stmt> ::= case <compare>? <eol>
<compare> ::= <sigspec> (, <sigspec>)*
<case-body> ::= (<switch> | <assign-stmt>)*
<case-body> ::= <assign-stmt>* <switch>*
<switch-end-stmt> ::= end <eol>
Syncs

View File

@ -56,6 +56,9 @@ if os.getenv("READTHEDOCS"):
else:
release = yosys_ver
todo_include_todos = False
elif os.getenv("YOSYS_DOCS_RELEASE") is not None:
release = yosys_ver
todo_include_todos = False
else:
release = yosys_ver
todo_include_todos = True
@ -87,5 +90,9 @@ def setup(app: Sphinx) -> None:
from util.RtlilLexer import RtlilLexer
app.add_lexer("RTLIL", RtlilLexer)
from furo_ys.lexers.YoscryptLexer import YoscryptLexer
app.add_lexer("yoscrypt", YoscryptLexer)
try:
from furo_ys.lexers.YoscryptLexer import YoscryptLexer
app.add_lexer("yoscrypt", YoscryptLexer)
except ModuleNotFoundError:
from pygments.lexers.special import TextLexer
app.add_lexer("yoscrypt", TextLexer)

View File

@ -31,6 +31,11 @@ void rtlil_frontend_yyerror(char const *s)
YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s);
}
void rtlil_frontend_yywarning(char const *s)
{
YOSYS_NAMESPACE_PREFIX log_warning("In line %d: %s\n", rtlil_frontend_yyget_lineno(), s);
}
YOSYS_NAMESPACE_BEGIN
struct RTLILFrontend : public Frontend {

View File

@ -42,6 +42,7 @@ YOSYS_NAMESPACE_END
extern int rtlil_frontend_yydebug;
int rtlil_frontend_yylex(void);
void rtlil_frontend_yyerror(char const *s);
void rtlil_frontend_yywarning(char const *s);
void rtlil_frontend_yyrestart(FILE *f);
int rtlil_frontend_yyparse(void);
int rtlil_frontend_yylex_destroy(void);

View File

@ -344,6 +344,16 @@ assign_stmt:
TOK_ASSIGN sigspec sigspec EOL {
if (attrbuf.size() != 0)
rtlil_frontend_yyerror("dangling attribute");
// See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this
// warning
if (!switch_stack.back()->empty()) {
rtlil_frontend_yywarning(
"case rule assign statements after switch statements may cause unexpected behaviour. "
"The assign statement is reordered to come before all switch statements."
);
}
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
delete $2;
delete $3;

View File

@ -290,7 +290,7 @@ Aig::Aig(Cell *cell)
}
}
if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_)))
if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($barrier), ID($_BUF_)))
{
for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) {
int A = mk.inport(ID::A, i);

View File

@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
void bitwise_unary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
bool is_signed = (cell->type != ID($buf)) && cell->getParam(ID::A_SIGNED).as_bool();
bool is_signed = !cell->type.in(ID($buf), ID($barrier)) && cell->getParam(ID::A_SIGNED).as_bool();
int a_width = GetSize(cell->getPort(ID::A));
int y_width = GetSize(cell->getPort(ID::Y));
@ -392,7 +392,7 @@ PRIVATE_NAMESPACE_END
bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
{
if (cell->type.in(ID($not), ID($pos), ID($buf))) {
if (cell->type.in(ID($not), ID($pos), ID($buf), ID($barrier))) {
bitwise_unary_op(this, cell);
return true;
}

View File

@ -87,6 +87,8 @@ struct CellTypes
{
setup_internals_eval();
setup_type(ID($barrier), {ID::A}, {ID::Y});
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true);
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);

View File

@ -364,7 +364,7 @@ public:
unsigned int hash() const
{
unsigned int inner;
unsigned int inner = 0;
switch (type_)
{
case DriveType::NONE:
@ -385,6 +385,9 @@ public:
case DriveType::MULTIPLE:
inner = multiple_.hash();
break;
default:
log_abort();
break;
}
return mkhash((unsigned int)type_, inner);
}
@ -912,7 +915,7 @@ public:
unsigned int hash() const
{
unsigned int inner;
unsigned int inner = 0;
switch (type_)
{
case DriveType::NONE:
@ -933,6 +936,9 @@ public:
case DriveType::MULTIPLE:
inner = multiple_.hash();
break;
default:
log_abort();
break;
}
return mkhash((unsigned int)type_, inner);
}

View File

@ -77,7 +77,7 @@ void QuickConeSat::prepare()
int QuickConeSat::cell_complexity(RTLIL::Cell *cell)
{
if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($buf), ID($_BUF_)))
if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($buf), ID($barrier), ID($_BUF_)))
return 0;
if (cell->type.in(ID($not), ID($and), ID($or), ID($xor), ID($xnor),
ID($reduce_and), ID($reduce_or), ID($reduce_xor),

View File

@ -1340,7 +1340,7 @@ namespace {
cell->type.begins_with("$verific$") || cell->type.begins_with("$array:") || cell->type.begins_with("$extern:"))
return;
if (cell->type == ID($buf)) {
if (cell->type.in(ID($buf), ID($barrier))) {
port(ID::A, param(ID::WIDTH));
port(ID::Y, param(ID::WIDTH));
check_expected();
@ -2746,7 +2746,8 @@ DEF_METHOD(LogicNot, 1, ID($logic_not))
add ## _func(name, sig_a, sig_y, is_signed, src); \
return sig_y; \
}
DEF_METHOD(Buf, sig_a.size(), ID($buf))
DEF_METHOD(Buf, sig_a.size(), ID($buf))
DEF_METHOD(Barrier, sig_a.size(), ID($barrier))
#undef DEF_METHOD
#define DEF_METHOD(_func, _y_size, _type) \
@ -4048,9 +4049,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:"))
return;
if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux)) {
if (type.in(ID($buf), ID($barrier), ID($mux), ID($pmux), ID($bmux))) {
parameters[ID::WIDTH] = GetSize(connections_[ID::Y]);
if (type != ID($buf) && type != ID($mux))
if (!type.in(ID($buf), ID($barrier), ID($mux)))
parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]);
check();
return;

View File

@ -814,6 +814,7 @@ struct RTLIL::AttrObject
void set_bool_attribute(const RTLIL::IdString &id, bool value=true);
bool get_bool_attribute(const RTLIL::IdString &id) const;
[[deprecated("Use Module::get_blackbox_attribute() instead.")]]
bool get_blackbox_attribute(bool ignore_wb=false) const {
return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox));
}
@ -1291,6 +1292,10 @@ public:
virtual void optimize();
virtual void makeblackbox();
bool get_blackbox_attribute(bool ignore_wb=false) const {
return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox));
}
void connect(const RTLIL::SigSig &conn);
void connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs);
void new_connections(const std::vector<RTLIL::SigSig> &new_conn);
@ -1371,10 +1376,11 @@ public:
// The add* methods create a cell and return the created cell. All signals must exist in advance.
RTLIL::Cell* addNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addPos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addBuf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addNeg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addPos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addBuf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addBarrier (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addNeg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addAnd (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addOr (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
@ -1506,10 +1512,11 @@ public:
// The methods without the add* prefix create a cell and an output signal. They return the newly created output signal.
RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Buf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Buf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Barrier (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec And (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Or (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");

View File

@ -430,7 +430,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true;
}
if (cell->type.in(ID($pos), ID($buf), ID($neg)))
if (cell->type.in(ID($pos), ID($buf), ID($barrier), ID($neg)))
{
std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
@ -438,7 +438,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
if (cell->type.in(ID($pos), ID($buf))) {
if (cell->type.in(ID($pos), ID($buf), ID($barrier))) {
ez->assume(ez->vec_eq(a, yy));
} else {
std::vector<int> zero(a.size(), ez->CONST_FALSE);
@ -451,7 +451,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
extendSignalWidthUnary(undef_a, undef_y, cell);
if (cell->type.in(ID($pos), ID($buf))) {
if (cell->type.in(ID($pos), ID($buf), ID($barrier))) {
ez->assume(ez->vec_eq(undef_a, undef_y));
} else {
int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);

View File

@ -51,3 +51,4 @@ OBJS += passes/cmds/future.o
OBJS += passes/cmds/box_derive.o
OBJS += passes/cmds/example_dt.o
OBJS += passes/cmds/portarcs.o
OBJS += passes/cmds/optbarriers.o

228
passes/cmds/optbarriers.cc Normal file
View File

@ -0,0 +1,228 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 George Rennie <georgrennie@gmail.com>
*
* 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/yosys.h"
#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
// Standard visitor helper
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
struct OptBarriersPass : public Pass {
OptBarriersPass() : Pass("optbarriers", "insert optimization barriers") {}
void help() override {
log("\n");
log(" optbarriers [options] [selection]\n");
log("\n");
log("Insert optimization barriers to drivers of selected public wires.\n");
log("\n");
log("\n");
log(" -nocells\n");
log(" don't add optimization barriers to the outputs of cells\n");
log("\n");
log(" -noprocs\n");
log(" don't add optimization barriers to the outputs of processes\n");
log("\n");
log(" -private\n");
log(" also add optimization barriers to private wires\n");
log("\n");
log(" -remove\n");
log(" replace selected optimization barriers with connections\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing OPTBARRIERS pass (insert optimization barriers).\n");
bool nocells_mode = false;
bool noprocs_mode = false;
bool private_mode = false;
bool remove_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
if (arg == "-nocells") {
nocells_mode = true;
continue;
}
if (arg == "-noprocs") {
noprocs_mode = true;
continue;
}
if (arg == "-private") {
private_mode = true;
continue;
}
if (arg == "-remove") {
remove_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (remove_mode) {
log("Replacing optimization barriers with connections.\n");
remove_barriers(design);
return;
}
for (auto* module : design->selected_modules()) {
// We can't just sigmap and iterate through wires for rewriting as
// we want to maintain the structure in connections, and sigmap
// will just return a canonical wire which does not have to be one
// that is directly driving the wire. Therefore for each type of
// object that could be driving the wires (cells, processes,
// connections) we rewrite the sigspecs.
// Keep track of which wires we have allocated new wires for
dict<RTLIL::Wire*, RTLIL::Wire*> new_wires;
// Keep track of bit pairs we need to construct barriers for from
// Y to A
dict<RTLIL::SigBit, RTLIL::SigBit> new_barriers;
// Skip constants, unselected wires and private wires when not in
// private mode. This works for SigChunk or SigBit input.
const auto skip = [&](const auto& chunk) {
if (!chunk.is_wire())
return true;
if (!design->selected(module, chunk.wire))
return true;
if (!private_mode && !chunk.wire->name.isPublic())
return true;
return false;
};
const auto rewrite_sigspec = [&](const SigSpec& sig) {
RTLIL::SigSpec new_output;
for (const auto& chunk : sig.chunks()) {
if (skip(chunk)) {
new_output.append(chunk);
continue;
}
// Add a wire to drive if one does not already exist
auto* new_wire = new_wires.at(chunk.wire, nullptr);
if (!new_wire) {
new_wire = module->addWire(NEW_ID, GetSize(chunk.wire));
new_wires.emplace(chunk.wire, new_wire);
}
RTLIL::SigChunk new_chunk = chunk;
new_chunk.wire = new_wire;
// Rewrite output to drive new wire, and schedule adding
// barrier bits from new wire to original
new_output.append(new_chunk);
for (int i = 0; i < GetSize(chunk); i++)
new_barriers.emplace(chunk[i], new_chunk[i]);
}
return new_output;
};
// Rewrite cell outputs
if (!nocells_mode)
for (auto* cell : module->cells())
if (cell->type != ID($barrier))
for (const auto& [name, sig] : cell->connections())
if (cell->output(name))
cell->setPort(name, rewrite_sigspec(sig));
// Rewrite connections in processes
if (!noprocs_mode) {
const auto proc_rewriter = overloaded{
// Don't do anything for input sigspecs
[&](const SigSpec&) {},
// Rewrite connections to drive barrier if needed
[&](SigSpec& lhs, const SigSpec&) {
lhs = rewrite_sigspec(lhs);
}
};
for (auto& proc : module->processes)
proc.second->rewrite_sigspecs2(proc_rewriter);
}
// Add all the scheduled barriers. To minimize the number of cells,
// first construct a sigspec of all bits, then sort and unify before
// creating barriers
SigSpec barrier_y;
for (const auto&[y_bit, _] : new_barriers)
barrier_y.append(y_bit);
barrier_y.sort_and_unify();
for (const auto& sig_y : barrier_y.chunks()) {
log_assert(sig_y.is_wire());
SigSpec sig_a;
for (int i = 0; i < GetSize(sig_y); i++)
sig_a.append(new_barriers[sig_y[i]]);
module->addBarrier(NEW_ID, sig_a, sig_y);
}
// Rewrite connections
std::vector<RTLIL::SigSig> new_connections;
for (const auto& conn : module->connections()) {
RTLIL::SigSig skip_conn, barrier_conn;
for (int i = 0; i < GetSize(conn.first); i++) {
auto& sigsig = skip(conn.first[i]) ? skip_conn : barrier_conn;
sigsig.first.append(conn.first[i]);
sigsig.second.append(conn.second[i]);
}
if (!skip_conn.first.empty())
new_connections.emplace_back(std::move(skip_conn));
if (!barrier_conn.first.empty())
module->addBarrier(NEW_ID, barrier_conn.second, barrier_conn.first);
}
module->new_connections(new_connections);
}
}
void remove_barriers(RTLIL::Design* design) {
for (auto* module : design->selected_modules()) {
std::vector<RTLIL::Cell*> barriers;
for (auto* cell : module->selected_cells())
if (cell->type == ID($barrier))
barriers.emplace_back(cell);
for (auto* cell : barriers) {
const auto lhs = cell->getPort(ID::Y), rhs = cell->getPort(ID::A);
module->connect(lhs, rhs);
module->remove(cell);
}
}
}
} OptBarriersPass;
PRIVATE_NAMESPACE_END

View File

@ -1003,8 +1003,10 @@ struct HierarchyPass : public Pass {
if (top_mod == nullptr)
for (auto mod : design->modules())
if (mod->get_bool_attribute(ID::top))
if (mod->get_bool_attribute(ID::top)) {
log("Attribute `top' found on module `%s'. Setting top module to %s.\n", log_id(mod), log_id(mod));
top_mod = mod;
}
if (top_mod == nullptr)
{

View File

@ -245,6 +245,7 @@ struct OptMergeWorker
ct.cell_types.erase(ID($anyconst));
ct.cell_types.erase(ID($allseq));
ct.cell_types.erase(ID($allconst));
ct.cell_types.erase(ID($barrier));
log("Finding identical cells in module `%s'.\n", module->name.c_str());
assign_map.set(module);

View File

@ -60,6 +60,7 @@ struct FlattenWorker
bool ignore_wb = false;
bool create_scopeinfo = true;
bool create_scopename = false;
bool barriers = false;
template<class T>
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
@ -246,7 +247,27 @@ struct FlattenWorker
log_error("Cell port %s.%s.%s is driving constant bits: %s <= %s\n",
log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second));
module->connect(new_conn);
if (barriers) {
// Drive public output wires with barriers and the rest with
// connections
RTLIL::SigSig skip_conn, barrier_conn;
for (int i = 0; i < GetSize(new_conn.first); i++) {
const auto lhs = new_conn.first[i], rhs = new_conn.second[i];
auto& sigsig = !lhs.is_wire() || !lhs.wire->name.isPublic() ? skip_conn : barrier_conn;
sigsig.first.append(lhs);
sigsig.second.append(rhs);
}
if (!skip_conn.first.empty())
module->connect(skip_conn);
if (!barrier_conn.first.empty())
module->addBarrier(NEW_ID, barrier_conn.second, barrier_conn.first);
} else {
module->connect(new_conn);
}
sigmap.add(new_conn.first, new_conn.second);
}
@ -345,6 +366,10 @@ struct FlattenPass : public Pass {
log(" with a public name the enclosing scope can be found via their\n");
log(" 'hdlname' attribute.\n");
log("\n");
log(" -barriers\n");
log(" Use $barrier cells to connect flattened modules to their surrounding\n");
log(" scope instead of connections for public wires.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
@ -367,6 +392,10 @@ struct FlattenPass : public Pass {
worker.create_scopename = true;
continue;
}
if (args[argidx] == "-barriers") {
worker.barriers = true;
continue;
}
break;
}
extra_args(args, argidx, design);

View File

@ -67,6 +67,11 @@ struct PrepPass : public ScriptPass
log(" -nokeepdc\n");
log(" do not call opt_* with -keepdc\n");
log("\n");
log(" -barriers\n");
log(" add optimization barriers to all public wires to preserve their structure.\n");
log(" this limits the optimizations that can be applied to the design to only\n");
log(" those involving private wires.\n");
log("\n");
log(" -run <from_label>[:<to_label>]\n");
log(" only run the commands between the labels (see below). an empty\n");
log(" from label is synonymous to 'begin', and empty to label is\n");
@ -79,7 +84,7 @@ struct PrepPass : public ScriptPass
}
string top_module, fsm_opts;
bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc, rdff;
bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc, rdff, barriers;
void clear_flags() override
{
@ -92,6 +97,7 @@ struct PrepPass : public ScriptPass
nomemmode = false;
nokeepdc = false;
rdff = false;
barriers = false;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
@ -148,6 +154,10 @@ struct PrepPass : public ScriptPass
nokeepdc = true;
continue;
}
if (args[argidx] == "-barriers") {
barriers = true;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -183,12 +193,16 @@ struct PrepPass : public ScriptPass
if (check_label("coarse"))
{
if (help_mode || barriers)
run("optbarriers", "(if -barriers)");
if (help_mode)
run("proc [-ifx]");
else
run(ifxmode ? "proc -ifx" : "proc");
if (help_mode || flatten)
run("flatten", "(if -flatten)");
if (help_mode)
run("flatten [-barriers]", "(if -flatten)");
else if (flatten)
run(barriers ? "flatten -barriers" : "flatten");
run("future");
run(nokeepdc ? "opt_expr" : "opt_expr -keepdc");
run("opt_clean");

View File

@ -108,6 +108,29 @@ endmodule
// --------------------------------------------------------
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
//-
//- $barrier (A, Y)
//* group unary
//-
//- A coarse-grain buffer cell type that acts as a barrier for optimizations.
//- Optimization passes are forbidden from rewriting patterns that include
//- this cell (by merging, constant propagation etc) with the exception of
//- opt_clean that can remove it if the output is unused.
//-
module \$barrier (A, Y);
parameter WIDTH = 0;
input [WIDTH-1:0] A;
output [WIDTH-1:0] Y;
assign Y = A;
endmodule
// --------------------------------------------------------
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
//-
//- $neg (A, Y)