aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--backends/cxxrtl/cxxrtl.cc1305
-rw-r--r--backends/cxxrtl/cxxrtl.h67
-rw-r--r--backends/edif/edif.cc122
-rw-r--r--backends/firrtl/firrtl.cc13
-rw-r--r--backends/ilang/ilang_backend.cc12
-rw-r--r--backends/json/json.cc10
-rw-r--r--frontends/ast/ast.cc10
-rw-r--r--frontends/ast/ast.h4
-rw-r--r--frontends/ast/genrtlil.cc136
-rw-r--r--frontends/ast/simplify.cc128
-rw-r--r--frontends/ilang/ilang_parser.y11
-rw-r--r--frontends/rpc/rpc_frontend.cc2
-rw-r--r--frontends/verilog/verilog_parser.y6
-rw-r--r--kernel/hashlib.h24
-rw-r--r--kernel/rtlil.cc53
-rw-r--r--kernel/rtlil.h16
-rw-r--r--passes/cmds/bugpoint.cc18
-rw-r--r--passes/cmds/rename.cc222
-rw-r--r--passes/cmds/scatter.cc28
-rw-r--r--passes/cmds/select.cc25
-rw-r--r--passes/hierarchy/hierarchy.cc30
-rw-r--r--passes/sat/qbfsat.cc6
-rw-r--r--passes/sat/sim.cc6
-rw-r--r--passes/techmap/abc.cc179
-rw-r--r--passes/techmap/abc9_ops.cc49
-rw-r--r--techlibs/intel_alm/common/alm_sim.v69
-rw-r--r--techlibs/intel_alm/common/dff_map.v28
-rw-r--r--techlibs/intel_alm/common/dff_sim.v44
-rw-r--r--techlibs/xilinx/xilinx_dffopt.cc6
-rw-r--r--tests/arch/ecp5/memories.ys2
-rw-r--r--tests/arch/ice40/memories.ys1
-rw-r--r--tests/select/unset.ys10
-rw-r--r--tests/select/unset2.ys10
-rw-r--r--tests/various/abc9.ys19
-rw-r--r--tests/various/dynamic_part_select.ys106
-rw-r--r--tests/various/dynamic_part_select/forloop_select.v19
-rw-r--r--tests/various/dynamic_part_select/forloop_select_gate.v559
-rw-r--r--tests/various/dynamic_part_select/multiple_blocking.v19
-rw-r--r--tests/various/dynamic_part_select/multiple_blocking_gate.v83
-rw-r--r--tests/various/dynamic_part_select/nonblocking.v14
-rw-r--r--tests/various/dynamic_part_select/nonblocking_gate.v77
-rw-r--r--tests/various/dynamic_part_select/original.v12
-rw-r--r--tests/various/dynamic_part_select/original_gate.v74
-rw-r--r--tests/various/dynamic_part_select/reset_test.v23
-rw-r--r--tests/various/dynamic_part_select/reset_test_gate.v151
-rw-r--r--tests/various/dynamic_part_select/reversed.v13
-rw-r--r--tests/various/dynamic_part_select/reversed_gate.v74
-rw-r--r--tests/various/hierarchy_param.ys23
-rw-r--r--tests/various/sim_const.ys13
50 files changed, 3218 insertions, 714 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f9e420a09..df8e14b26 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -65,6 +65,7 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "logger" pass
- Removed "dffsr2dff" (use opt_rmdff instead)
- Added "design -delete"
+ - Added "select -unset"
Yosys 0.8 .. Yosys 0.9
----------------------
diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc
index d1a855bf0..237700b29 100644
--- a/backends/cxxrtl/cxxrtl.cc
+++ b/backends/cxxrtl/cxxrtl.cc
@@ -171,14 +171,19 @@ struct Scheduler {
}
};
-static bool is_unary_cell(RTLIL::IdString type)
+bool is_input_wire(const RTLIL::Wire *wire)
+{
+ return wire->port_input && !wire->port_output;
+}
+
+bool is_unary_cell(RTLIL::IdString type)
{
return type.in(
ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
ID($pos), ID($neg));
}
-static bool is_binary_cell(RTLIL::IdString type)
+bool is_binary_cell(RTLIL::IdString type)
{
return type.in(
ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),
@@ -187,34 +192,77 @@ static bool is_binary_cell(RTLIL::IdString type)
ID($add), ID($sub), ID($mul), ID($div), ID($mod));
}
-static bool is_elidable_cell(RTLIL::IdString type)
+bool is_elidable_cell(RTLIL::IdString type)
{
return is_unary_cell(type) || is_binary_cell(type) || type.in(
ID($mux), ID($concat), ID($slice));
}
-static bool is_sync_ff_cell(RTLIL::IdString type)
+bool is_sync_ff_cell(RTLIL::IdString type)
{
return type.in(
ID($dff), ID($dffe));
}
-static bool is_ff_cell(RTLIL::IdString type)
+bool is_ff_cell(RTLIL::IdString type)
{
return is_sync_ff_cell(type) || type.in(
ID($adff), ID($dffsr), ID($dlatch), ID($dlatchsr), ID($sr));
}
-static bool is_internal_cell(RTLIL::IdString type)
+bool is_internal_cell(RTLIL::IdString type)
{
return type[0] == '$' && !type.begins_with("$paramod\\");
}
+bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
+{
+ RTLIL::Module *cell_module = cell->module->design->module(cell->type);
+ log_assert(cell_module != nullptr);
+ return cell_module->get_bool_attribute(ID(cxxrtl.blackbox));
+}
+
+enum class CxxrtlPortType {
+ UNKNOWN = 0, // or mixed comb/sync
+ COMB = 1,
+ SYNC = 2,
+};
+
+CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port)
+{
+ RTLIL::Module *cell_module = cell->module->design->module(cell->type);
+ if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ return CxxrtlPortType::UNKNOWN;
+ RTLIL::Wire *cell_output_wire = cell_module->wire(port);
+ log_assert(cell_output_wire != nullptr);
+ bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl.comb));
+ bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl.sync));
+ if (is_comb && is_sync)
+ log_cmd_error("Port `%s.%s' is marked as both `cxxrtl.comb` and `cxxrtl.sync`.\n",
+ log_id(cell_module), log_signal(cell_output_wire));
+ else if (is_comb)
+ return CxxrtlPortType::COMB;
+ else if (is_sync)
+ return CxxrtlPortType::SYNC;
+ return CxxrtlPortType::UNKNOWN;
+}
+
+bool is_cxxrtl_comb_port(const RTLIL::Cell *cell, RTLIL::IdString port)
+{
+ return cxxrtl_port_type(cell, port) == CxxrtlPortType::COMB;
+}
+
+bool is_cxxrtl_sync_port(const RTLIL::Cell *cell, RTLIL::IdString port)
+{
+ return cxxrtl_port_type(cell, port) == CxxrtlPortType::SYNC;
+}
+
struct FlowGraph {
struct Node {
enum class Type {
CONNECT,
- CELL,
+ CELL_SYNC,
+ CELL_EVAL,
PROCESS
};
@@ -225,7 +273,7 @@ struct FlowGraph {
};
std::vector<Node*> nodes;
- dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_defs, wire_uses;
+ dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<const RTLIL::Wire*, bool> wire_def_elidable, wire_use_elidable;
~FlowGraph()
@@ -234,13 +282,17 @@ struct FlowGraph {
delete node;
}
- void add_defs(Node *node, const RTLIL::SigSpec &sig, bool elidable)
+ void add_defs(Node *node, const RTLIL::SigSpec &sig, bool fully_sync, bool elidable)
{
for (auto chunk : sig.chunks())
- if (chunk.wire)
- wire_defs[chunk.wire].insert(node);
- // Only defs of an entire wire in the right order can be elided.
- if (sig.is_wire())
+ if (chunk.wire) {
+ if (fully_sync)
+ wire_sync_defs[chunk.wire].insert(node);
+ else
+ wire_comb_defs[chunk.wire].insert(node);
+ }
+ // Only comb defs of an entire wire in the right order can be elided.
+ if (!fully_sync && sig.is_wire())
wire_def_elidable[sig.as_wire()] = elidable;
}
@@ -268,7 +320,7 @@ struct FlowGraph {
// Connections
void add_connect_defs_uses(Node *node, const RTLIL::SigSig &conn)
{
- add_defs(node, conn.first, /*elidable=*/true);
+ add_defs(node, conn.first, /*fully_sync=*/false, /*elidable=*/true);
add_uses(node, conn.second);
}
@@ -283,21 +335,59 @@ struct FlowGraph {
}
// Cells
- void add_cell_defs_uses(Node *node, const RTLIL::Cell *cell)
+ void add_cell_sync_defs(Node *node, const RTLIL::Cell *cell)
+ {
+ // To understand why this node type is necessary and why it produces comb defs, consider a cell
+ // with input \i and sync output \o, used in a design such that \i is connected to \o. This does
+ // not result in a feedback arc because the output is synchronous. However, a naive implementation
+ // of code generation for cells that assigns to inputs, evaluates cells, assigns from outputs
+ // would not be able to immediately converge...
+ //
+ // wire<1> i_tmp;
+ // cell->p_i = i_tmp.curr;
+ // cell->eval();
+ // i_tmp.next = cell->p_o.curr;
+ //
+ // ... since the wire connecting the input and output ports would not be localizable. To solve
+ // this, the cell is split into two scheduling nodes; one exclusively for sync outputs, and
+ // another for inputs and all non-sync outputs. This way the generated code can be rearranged...
+ //
+ // value<1> i_tmp;
+ // i_tmp = cell->p_o.curr;
+ // cell->p_i = i_tmp;
+ // cell->eval();
+ //
+ // eliminating the unnecessary delta cycle. Conceptually, the CELL_SYNC node type is a series of
+ // connections of the form `connect \lhs \cell.\sync_output`; the right-hand side of these is not
+ // as a wire in RTLIL. If it was expressible, then `\cell.\sync_output` would have a sync def,
+ // and this node would be an ordinary CONNECT node, with `\lhs` having a comb def. Because it isn't,
+ // a special node type is used, the right-hand side does not appear anywhere, and the left-hand
+ // side has a comb def.
+ for (auto conn : cell->connections())
+ if (cell->output(conn.first))
+ if (is_cxxrtl_sync_port(cell, conn.first)) {
+ // See note regarding elidability below.
+ add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/false);
+ }
+ }
+
+ void add_cell_eval_defs_uses(Node *node, const RTLIL::Cell *cell)
{
- log_assert(cell->known());
for (auto conn : cell->connections()) {
if (cell->output(conn.first)) {
- if (is_sync_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID(CLK_ENABLE)).as_bool()))
- /* non-combinatorial outputs do not introduce defs */;
- else if (is_elidable_cell(cell->type))
- add_defs(node, conn.second, /*elidable=*/true);
+ if (is_elidable_cell(cell->type))
+ add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/true);
+ else if (is_sync_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool()))
+ add_defs(node, conn.second, /*fully_sync=*/true, /*elidable=*/false);
else if (is_internal_cell(cell->type))
- add_defs(node, conn.second, /*elidable=*/false);
- else {
- // Unlike outputs of internal cells (which generate code that depends on the ability to set the output
- // wire bits), outputs of user cells are normal wires, and the wires connected to them can be elided.
- add_defs(node, conn.second, /*elidable=*/true);
+ add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/false);
+ else if (!is_cxxrtl_sync_port(cell, conn.first)) {
+ // Although at first it looks like outputs of user-defined cells may always be elided, the reality is
+ // more complex. Fully sync outputs produce no defs and so don't participate in elision. Fully comb
+ // outputs are assigned in a different way depending on whether the cell's eval() immediately converged.
+ // Unknown/mixed outputs could be elided, but should be rare in practical designs and don't justify
+ // the infrastructure required to elide outputs of cells with many of them.
+ add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/false);
}
}
if (cell->input(conn.first))
@@ -307,11 +397,27 @@ struct FlowGraph {
Node *add_node(const RTLIL::Cell *cell)
{
+ log_assert(cell->known());
+
+ bool has_fully_sync_outputs = false;
+ for (auto conn : cell->connections())
+ if (cell->output(conn.first) && is_cxxrtl_sync_port(cell, conn.first)) {
+ has_fully_sync_outputs = true;
+ break;
+ }
+ if (has_fully_sync_outputs) {
+ Node *node = new Node;
+ node->type = Node::Type::CELL_SYNC;
+ node->cell = cell;
+ nodes.push_back(node);
+ add_cell_sync_defs(node, cell);
+ }
+
Node *node = new Node;
- node->type = Node::Type::CELL;
+ node->type = Node::Type::CELL_EVAL;
node->cell = cell;
nodes.push_back(node);
- add_cell_defs_uses(node, cell);
+ add_cell_eval_defs_uses(node, cell);
return node;
}
@@ -319,7 +425,7 @@ struct FlowGraph {
void add_case_defs_uses(Node *node, const RTLIL::CaseRule *case_)
{
for (auto &action : case_->actions) {
- add_defs(node, action.first, /*elidable=*/false);
+ add_defs(node, action.first, /*is_sync=*/false, /*elidable=*/false);
add_uses(node, action.second);
}
for (auto sub_switch : case_->switches) {
@@ -338,9 +444,9 @@ struct FlowGraph {
for (auto sync : process->syncs)
for (auto action : sync->actions) {
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe)
- /* sync actions do not introduce feedback */;
+ add_defs(node, action.first, /*is_sync=*/true, /*elidable=*/false);
else
- add_defs(node, action.first, /*elidable=*/false);
+ add_defs(node, action.first, /*is_sync=*/false, /*elidable=*/false);
add_uses(node, action.second);
}
}
@@ -356,6 +462,46 @@ struct FlowGraph {
}
};
+std::vector<std::string> split_by(const std::string &str, const std::string &sep)
+{
+ std::vector<std::string> result;
+ size_t prev = 0;
+ while (true) {
+ size_t curr = str.find_first_of(sep, prev + 1);
+ if (curr > str.size())
+ curr = str.size();
+ if (curr > prev + 1)
+ result.push_back(str.substr(prev, curr - prev));
+ if (curr == str.size())
+ break;
+ prev = curr;
+ }
+ return result;
+}
+
+std::string escape_cxx_string(const std::string &input)
+{
+ std::string output = "\"";
+ for (auto c : input) {
+ if (::isprint(c)) {
+ if (c == '\\')
+ output.push_back('\\');
+ output.push_back(c);
+ } else {
+ char l = c & 0xf, h = (c >> 4) & 0xf;
+ output.append("\\x");
+ output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
+ output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
+ }
+ }
+ output.push_back('"');
+ if (output.find('\0') != std::string::npos) {
+ output.insert(0, "std::string {");
+ output.append(stringf(", %zu}", input.size()));
+ }
+ return output;
+}
+
struct CxxrtlWorker {
bool split_intf = false;
std::string intf_filename;
@@ -367,21 +513,24 @@ struct CxxrtlWorker {
bool elide_public = false;
bool localize_internal = false;
bool localize_public = false;
- bool run_splitnets = false;
+ bool run_opt_clean_purge = false;
+ bool run_proc_flatten = false;
+ bool max_opt_level = false;
std::ostringstream f;
std::string indent;
int temporary = 0;
dict<const RTLIL::Module*, SigMap> sigmaps;
- pool<const RTLIL::Wire*> sync_wires;
- dict<RTLIL::SigBit, RTLIL::SyncType> sync_types;
+ pool<const RTLIL::Wire*> edge_wires;
+ dict<RTLIL::SigBit, RTLIL::SyncType> edge_types;
pool<const RTLIL::Memory*> writable_memories;
dict<const RTLIL::Cell*, pool<const RTLIL::Cell*>> transparent_for;
- dict<const RTLIL::Cell*, dict<RTLIL::Wire*, RTLIL::IdString>> cell_wire_defs;
dict<const RTLIL::Wire*, FlowGraph::Node> elided_wires;
dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule;
pool<const RTLIL::Wire*> localized_wires;
+ dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations;
+ dict<const RTLIL::Module*, bool> eval_converges;
void inc_indent() {
indent += "\t";
@@ -429,9 +578,11 @@ struct CxxrtlWorker {
return mangled;
}
- std::string mangle_module_name(const RTLIL::IdString &name)
+ std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false)
{
// Class namespace.
+ if (is_blackbox)
+ return "bb_" + mangle_name(name);
return mangle_name(name);
}
@@ -455,7 +606,7 @@ struct CxxrtlWorker {
std::string mangle(const RTLIL::Module *module)
{
- return mangle_module_name(module->name);
+ return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl.blackbox)));
}
std::string mangle(const RTLIL::Memory *memory)
@@ -481,6 +632,80 @@ struct CxxrtlWorker {
return mangle(sigbit.wire) + "_" + std::to_string(sigbit.offset);
}
+ std::vector<std::string> template_param_names(const RTLIL::Module *module)
+ {
+ if (!module->has_attribute(ID(cxxrtl.template)))
+ return {};
+
+ if (module->attributes.at(ID(cxxrtl.template)).flags != RTLIL::CONST_FLAG_STRING)
+ log_cmd_error("Attribute `cxxrtl.template' of module `%s' is not a string.\n", log_id(module));
+
+ std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl.template)), " \t");
+ for (const auto &param_name : param_names) {
+ // Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require
+ // parameters to start with an uppercase letter to avoid name conflicts. (This is the convention
+ // in both Verilog and C++, anyway.)
+ if (!isupper(param_name[0]))
+ log_cmd_error("Attribute `cxxrtl.template' of module `%s' includes a parameter `%s', "
+ "which does not start with an uppercase letter.\n",
+ log_id(module), param_name.c_str());
+ }
+ return param_names;
+ }
+
+ std::string template_params(const RTLIL::Module *module, bool is_decl)
+ {
+ std::vector<std::string> param_names = template_param_names(module);
+ if (param_names.empty())
+ return "";
+
+ std::string params = "<";
+ bool first = true;
+ for (const auto &param_name : param_names) {
+ if (!first)
+ params += ", ";
+ first = false;
+ if (is_decl)
+ params += "size_t ";
+ params += param_name;
+ }
+ params += ">";
+ return params;
+ }
+
+ std::string template_args(const RTLIL::Cell *cell)
+ {
+ RTLIL::Module *cell_module = cell->module->design->module(cell->type);
+ log_assert(cell_module != nullptr);
+ if (!cell_module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ return "";
+
+ std::vector<std::string> param_names = template_param_names(cell_module);
+ if (param_names.empty())
+ return "";
+
+ std::string params = "<";
+ bool first = true;
+ for (const auto &param_name : param_names) {
+ if (!first)
+ params += ", ";
+ first = false;
+ params += "/*" + param_name + "=*/";
+ RTLIL::IdString id_param_name = '\\' + param_name;
+ if (!cell->hasParam(id_param_name))
+ log_cmd_error("Cell `%s.%s' does not have a parameter `%s', which is required by the templated module `%s'.\n",
+ log_id(cell->module), log_id(cell), param_name.c_str(), log_id(cell_module));
+ RTLIL::Const param_value = cell->getParam(id_param_name);
+ if (((param_value.flags & ~RTLIL::CONST_FLAG_SIGNED) != 0) || param_value.as_int() < 0)
+ log_cmd_error("Parameter `%s' of cell `%s.%s', which is required by the templated module `%s', "
+ "is not a positive integer.\n",
+ param_name.c_str(), log_id(cell->module), log_id(cell), log_id(cell_module));
+ params += std::to_string(cell->getParam(id_param_name).as_int());
+ }
+ params += ">";
+ return params;
+ }
+
std::string fresh_temporary()
{
return stringf("tmp_%d", temporary++);
@@ -545,17 +770,14 @@ struct CxxrtlWorker {
case FlowGraph::Node::Type::CONNECT:
dump_connect_elided(node.connect);
break;
- case FlowGraph::Node::Type::CELL:
- if (is_elidable_cell(node.cell->type)) {
- dump_cell_elided(node.cell);
- } else {
- f << mangle(node.cell) << "." << mangle_wire_name(cell_wire_defs[node.cell][chunk.wire]) << ".curr";
- }
+ case FlowGraph::Node::Type::CELL_EVAL:
+ log_assert(is_elidable_cell(node.cell->type));
+ dump_cell_elided(node.cell);
break;
default:
log_assert(false);
}
- } else if (localized_wires[chunk.wire]) {
+ } else if (localized_wires[chunk.wire] || is_input_wire(chunk.wire)) {
f << mangle(chunk.wire);
} else {
f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr");
@@ -615,8 +837,8 @@ struct CxxrtlWorker {
case FlowGraph::Node::Type::CONNECT:
collect_connect(node.connect, cells);
break;
- case FlowGraph::Node::Type::CELL:
- collect_cell(node.cell, cells);
+ case FlowGraph::Node::Type::CELL_EVAL:
+ collect_cell_eval(node.cell, cells);
break;
default:
log_assert(false);
@@ -655,47 +877,60 @@ struct CxxrtlWorker {
f << ";\n";
}
+ void dump_cell_sync(const RTLIL::Cell *cell)
+ {
+ const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
+ f << indent << "// cell " << cell->name.str() << " syncs\n";
+ for (auto conn : cell->connections())
+ if (cell->output(conn.first))
+ if (is_cxxrtl_sync_port(cell, conn.first)) {
+ f << indent;
+ dump_sigspec_lhs(conn.second);
+ f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n";
+ }
+ }
+
void dump_cell_elided(const RTLIL::Cell *cell)
{
// Unary cells
if (is_unary_cell(cell->type)) {
f << cell->type.substr(1) << '_' <<
- (cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') <<
- "<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">(";
- dump_sigspec_rhs(cell->getPort(ID(A)));
+ (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') <<
+ "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">(";
+ dump_sigspec_rhs(cell->getPort(ID::A));
f << ")";
// Binary cells
} else if (is_binary_cell(cell->type)) {
f << cell->type.substr(1) << '_' <<
- (cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') <<
- (cell->getParam(ID(B_SIGNED)).as_bool() ? 's' : 'u') <<
- "<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">(";
- dump_sigspec_rhs(cell->getPort(ID(A)));
+ (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') <<
+ (cell->getParam(ID::B_SIGNED).as_bool() ? 's' : 'u') <<
+ "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">(";
+ dump_sigspec_rhs(cell->getPort(ID::A));
f << ", ";
- dump_sigspec_rhs(cell->getPort(ID(B)));
+ dump_sigspec_rhs(cell->getPort(ID::B));
f << ")";
// Muxes
} else if (cell->type == ID($mux)) {
f << "(";
- dump_sigspec_rhs(cell->getPort(ID(S)));
+ dump_sigspec_rhs(cell->getPort(ID::S));
f << " ? ";
- dump_sigspec_rhs(cell->getPort(ID(B)));
+ dump_sigspec_rhs(cell->getPort(ID::B));
f << " : ";
- dump_sigspec_rhs(cell->getPort(ID(A)));
+ dump_sigspec_rhs(cell->getPort(ID::A));
f << ")";
// Concats
} else if (cell->type == ID($concat)) {
- dump_sigspec_rhs(cell->getPort(ID(B)));
+ dump_sigspec_rhs(cell->getPort(ID::B));
f << ".concat(";
- dump_sigspec_rhs(cell->getPort(ID(A)));
+ dump_sigspec_rhs(cell->getPort(ID::A));
f << ").val()";
// Slices
} else if (cell->type == ID($slice)) {
- dump_sigspec_rhs(cell->getPort(ID(A)));
+ dump_sigspec_rhs(cell->getPort(ID::A));
f << ".slice<";
- f << cell->getParam(ID(OFFSET)).as_int() + cell->getParam(ID(Y_WIDTH)).as_int() - 1;
+ f << cell->getParam(ID::OFFSET).as_int() + cell->getParam(ID::Y_WIDTH).as_int() - 1;
f << ",";
- f << cell->getParam(ID(OFFSET)).as_int();
+ f << cell->getParam(ID::OFFSET).as_int();
f << ">().val()";
} else {
log_assert(false);
@@ -704,22 +939,22 @@ struct CxxrtlWorker {
bool is_cell_elided(const RTLIL::Cell *cell)
{
- return is_elidable_cell(cell->type) && cell->hasPort(ID(Y)) && cell->getPort(ID(Y)).is_wire() &&
- elided_wires.count(cell->getPort(ID(Y)).as_wire());
+ return is_elidable_cell(cell->type) && cell->hasPort(ID::Y) && cell->getPort(ID::Y).is_wire() &&
+ elided_wires.count(cell->getPort(ID::Y).as_wire());
}
- void collect_cell(const RTLIL::Cell *cell, std::vector<RTLIL::IdString> &cells)
+ void collect_cell_eval(const RTLIL::Cell *cell, std::vector<RTLIL::IdString> &cells)
{
if (!is_cell_elided(cell))
return;
cells.push_back(cell->name);
for (auto port : cell->connections())
- if (port.first != ID(Y))
+ if (port.first != ID::Y)
collect_sigspec_rhs(port.second, cells);
}
- void dump_cell(const RTLIL::Cell *cell)
+ void dump_cell_eval(const RTLIL::Cell *cell)
{
if (is_cell_elided(cell))
return;
@@ -729,7 +964,7 @@ struct CxxrtlWorker {
std::vector<RTLIL::IdString> elided_cells;
if (is_elidable_cell(cell->type)) {
for (auto port : cell->connections())
- if (port.first != ID(Y))
+ if (port.first != ID::Y)
collect_sigspec_rhs(port.second, elided_cells);
}
if (elided_cells.empty()) {
@@ -745,26 +980,26 @@ struct CxxrtlWorker {
// Elidable cells
if (is_elidable_cell(cell->type)) {
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Y)));
+ dump_sigspec_lhs(cell->getPort(ID::Y));
f << " = ";
dump_cell_elided(cell);
f << ";\n";
// Parallel (one-hot) muxes
} else if (cell->type == ID($pmux)) {
- int width = cell->getParam(ID(WIDTH)).as_int();
- int s_width = cell->getParam(ID(S_WIDTH)).as_int();
+ int width = cell->getParam(ID::WIDTH).as_int();
+ int s_width = cell->getParam(ID::S_WIDTH).as_int();
bool first = true;
for (int part = 0; part < s_width; part++) {
f << (first ? indent : " else ");
first = false;
f << "if (";
- dump_sigspec_rhs(cell->getPort(ID(S)).extract(part));
+ dump_sigspec_rhs(cell->getPort(ID::S).extract(part));
f << ") {\n";
inc_indent();
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Y)));
+ dump_sigspec_lhs(cell->getPort(ID::Y));
f << " = ";
- dump_sigspec_rhs(cell->getPort(ID(B)).extract(part * width, width));
+ dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width));
f << ";\n";
dec_indent();
f << indent << "}";
@@ -772,31 +1007,31 @@ struct CxxrtlWorker {
f << " else {\n";
inc_indent();
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Y)));
+ dump_sigspec_lhs(cell->getPort(ID::Y));
f << " = ";
- dump_sigspec_rhs(cell->getPort(ID(A)));
+ dump_sigspec_rhs(cell->getPort(ID::A));
f << ";\n";
dec_indent();
f << indent << "}\n";
// Flip-flops
} else if (is_ff_cell(cell->type)) {
- if (cell->hasPort(ID(CLK)) && cell->getPort(ID(CLK)).is_wire()) {
+ if (cell->hasPort(ID::CLK) && cell->getPort(ID::CLK).is_wire()) {
// Edge-sensitive logic
- RTLIL::SigBit clk_bit = cell->getPort(ID(CLK))[0];
+ RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0];
clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
- f << indent << "if (" << (cell->getParam(ID(CLK_POLARITY)).as_bool() ? "posedge_" : "negedge_")
+ f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_")
<< mangle(clk_bit) << ") {\n";
inc_indent();
if (cell->type == ID($dffe)) {
f << indent << "if (";
- dump_sigspec_rhs(cell->getPort(ID(EN)));
- f << " == value<1> {" << cell->getParam(ID(EN_POLARITY)).as_bool() << "u}) {\n";
+ dump_sigspec_rhs(cell->getPort(ID::EN));
+ f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n";
inc_indent();
}
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
- dump_sigspec_rhs(cell->getPort(ID(D)));
+ dump_sigspec_rhs(cell->getPort(ID::D));
f << ";\n";
if (cell->type == ID($dffe)) {
dec_indent();
@@ -804,77 +1039,77 @@ struct CxxrtlWorker {
}
dec_indent();
f << indent << "}\n";
- } else if (cell->hasPort(ID(EN))) {
+ } else if (cell->hasPort(ID::EN)) {
// Level-sensitive logic
f << indent << "if (";
- dump_sigspec_rhs(cell->getPort(ID(EN)));
- f << " == value<1> {" << cell->getParam(ID(EN_POLARITY)).as_bool() << "u}) {\n";
+ dump_sigspec_rhs(cell->getPort(ID::EN));
+ f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n";
inc_indent();
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
- dump_sigspec_rhs(cell->getPort(ID(D)));
+ dump_sigspec_rhs(cell->getPort(ID::D));
f << ";\n";
dec_indent();
f << indent << "}\n";
}
- if (cell->hasPort(ID(ARST))) {
+ if (cell->hasPort(ID::ARST)) {
// Asynchronous reset (entire coarse cell at once)
f << indent << "if (";
- dump_sigspec_rhs(cell->getPort(ID(ARST)));
- f << " == value<1> {" << cell->getParam(ID(ARST_POLARITY)).as_bool() << "u}) {\n";
+ dump_sigspec_rhs(cell->getPort(ID::ARST));
+ f << " == value<1> {" << cell->getParam(ID::ARST_POLARITY).as_bool() << "u}) {\n";
inc_indent();
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
- dump_const(cell->getParam(ID(ARST_VALUE)));
+ dump_const(cell->getParam(ID::ARST_VALUE));
f << ";\n";
dec_indent();
f << indent << "}\n";
}
- if (cell->hasPort(ID(SET))) {
+ if (cell->hasPort(ID::SET)) {
// Asynchronous set (for individual bits)
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << ".update(";
- dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID(WIDTH)).as_int()));
+ dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
f << ", ";
- dump_sigspec_rhs(cell->getPort(ID(SET)));
- f << (cell->getParam(ID(SET_POLARITY)).as_bool() ? "" : ".bit_not()") << ");\n";
+ dump_sigspec_rhs(cell->getPort(ID::SET));
+ f << (cell->getParam(ID::SET_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n";
}
- if (cell->hasPort(ID(CLR))) {
+ if (cell->hasPort(ID::CLR)) {
// Asynchronous clear (for individual bits; priority over set)
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
- dump_sigspec_lhs(cell->getPort(ID(Q)));
+ dump_sigspec_lhs(cell->getPort(ID::Q));
f << ".update(";
- dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID(WIDTH)).as_int()));
+ dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
f << ", ";
- dump_sigspec_rhs(cell->getPort(ID(CLR)));
- f << (cell->getParam(ID(CLR_POLARITY)).as_bool() ? "" : ".bit_not()") << ");\n";
+ dump_sigspec_rhs(cell->getPort(ID::CLR));
+ f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n";
}
// Memory ports
} else if (cell->type.in(ID($memrd), ID($memwr))) {
- if (cell->getParam(ID(CLK_ENABLE)).as_bool()) {
- RTLIL::SigBit clk_bit = cell->getPort(ID(CLK))[0];
+ if (cell->getParam(ID::CLK_ENABLE).as_bool()) {
+ RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0];
clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
- f << indent << "if (" << (cell->getParam(ID(CLK_POLARITY)).as_bool() ? "posedge_" : "negedge_")
+ f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_")
<< mangle(clk_bit) << ") {\n";
inc_indent();
}
- RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID(MEMID)).decode_string()];
+ RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID::MEMID).decode_string()];
std::string valid_index_temp = fresh_temporary();
f << indent << "auto " << valid_index_temp << " = memory_index(";
- dump_sigspec_rhs(cell->getPort(ID(ADDR)));
+ dump_sigspec_rhs(cell->getPort(ID::ADDR));
f << ", " << memory->start_offset << ", " << memory->size << ");\n";
if (cell->type == ID($memrd)) {
- bool has_enable = cell->getParam(ID(CLK_ENABLE)).as_bool() && !cell->getPort(ID(EN)).is_fully_ones();
+ bool has_enable = cell->getParam(ID::CLK_ENABLE).as_bool() && !cell->getPort(ID::EN).is_fully_ones();
if (has_enable) {
f << indent << "if (";
- dump_sigspec_rhs(cell->getPort(ID(EN)));
+ dump_sigspec_rhs(cell->getPort(ID::EN));
f << ") {\n";
inc_indent();
}
@@ -890,8 +1125,8 @@ struct CxxrtlWorker {
inc_indent();
if (writable_memories[memory]) {
std::string addr_temp = fresh_temporary();
- f << indent << "const value<" << cell->getPort(ID(ADDR)).size() << "> &" << addr_temp << " = ";
- dump_sigspec_rhs(cell->getPort(ID(ADDR)));
+ f << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = ";
+ dump_sigspec_rhs(cell->getPort(ID::ADDR));
f << ";\n";
std::string lhs_temp = fresh_temporary();
f << indent << "value<" << memory->width << "> " << lhs_temp << " = "
@@ -899,35 +1134,35 @@ struct CxxrtlWorker {
std::vector<const RTLIL::Cell*> memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end());
std::sort(memwr_cells.begin(), memwr_cells.end(),
[](const RTLIL::Cell *a, const RTLIL::Cell *b) {
- return a->getParam(ID(PRIORITY)).as_int() < b->getParam(ID(PRIORITY)).as_int();
+ return a->getParam(ID::PRIORITY).as_int() < b->getParam(ID::PRIORITY).as_int();
});
for (auto memwr_cell : memwr_cells) {
f << indent << "if (" << addr_temp << " == ";
- dump_sigspec_rhs(memwr_cell->getPort(ID(ADDR)));
+ dump_sigspec_rhs(memwr_cell->getPort(ID::ADDR));
f << ") {\n";
inc_indent();
f << indent << lhs_temp << " = " << lhs_temp;
f << ".update(";
- dump_sigspec_rhs(memwr_cell->getPort(ID(DATA)));
+ dump_sigspec_rhs(memwr_cell->getPort(ID::DATA));
f << ", ";
- dump_sigspec_rhs(memwr_cell->getPort(ID(EN)));
+ dump_sigspec_rhs(memwr_cell->getPort(ID::EN));
f << ");\n";
dec_indent();
f << indent << "}\n";
}
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(DATA)));
+ dump_sigspec_lhs(cell->getPort(ID::DATA));
f << " = " << lhs_temp << ";\n";
} else {
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(DATA)));
+ dump_sigspec_lhs(cell->getPort(ID::DATA));
f << " = " << mangle(memory) << "[" << valid_index_temp << ".index];\n";
}
dec_indent();
f << indent << "} else {\n";
inc_indent();
f << indent;
- dump_sigspec_lhs(cell->getPort(ID(DATA)));
+ dump_sigspec_lhs(cell->getPort(ID::DATA));
f << " = value<" << memory->width << "> {};\n";
dec_indent();
f << indent << "}\n";
@@ -944,14 +1179,14 @@ struct CxxrtlWorker {
f << indent << "if (" << valid_index_temp << ".valid) {\n";
inc_indent();
f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, ";
- dump_sigspec_rhs(cell->getPort(ID(DATA)));
+ dump_sigspec_rhs(cell->getPort(ID::DATA));
f << ", ";
- dump_sigspec_rhs(cell->getPort(ID(EN)));
- f << ", " << cell->getParam(ID(PRIORITY)).as_int() << ");\n";
+ dump_sigspec_rhs(cell->getPort(ID::EN));
+ f << ", " << cell->getParam(ID::PRIORITY).as_int() << ");\n";
dec_indent();
f << indent << "}\n";
}
- if (cell->getParam(ID(CLK_ENABLE)).as_bool()) {
+ if (cell->getParam(ID::CLK_ENABLE).as_bool()) {
dec_indent();
f << indent << "}\n";
}
@@ -961,27 +1196,71 @@ struct CxxrtlWorker {
// User cells
} else {
log_assert(cell->known());
+ const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
for (auto conn : cell->connections())
- if (cell->input(conn.first)) {
- f << indent << mangle(cell) << "." << mangle_wire_name(conn.first) << ".next = ";
+ if (cell->input(conn.first) && !cell->output(conn.first)) {
+ f << indent << mangle(cell) << access << mangle_wire_name(conn.first) << " = ";
+ dump_sigspec_rhs(conn.second);
+ f << ";\n";
+ if (getenv("CXXRTL_VOID_MY_WARRANTY")) {
+ // Until we have proper clock tree detection, this really awful hack that opportunistically
+ // propagates prev_* values for clocks can be used to estimate how much faster a design could
+ // be if only one clock edge was simulated by replacing:
+ // top.p_clk = value<1>{0u}; top.step();
+ // top.p_clk = value<1>{1u}; top.step();
+ // with:
+ // top.prev_p_clk = value<1>{0u}; top.p_clk = value<1>{1u}; top.step();
+ // Don't rely on this; it will be removed without warning.
+ RTLIL::Module *cell_module = cell->module->design->module(cell->type);
+ if (cell_module != nullptr && cell_module->wire(conn.first) && conn.second.is_wire()) {
+ RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first);
+ if (edge_wires[conn.second.as_wire()] && edge_wires[cell_module_wire]) {
+ f << indent << mangle(cell) << access << "prev_" << mangle(cell_module_wire) << " = ";
+ f << "prev_" << mangle(conn.second.as_wire()) << ";\n";
+ }
+ }
+ }
+ } else if (cell->input(conn.first)) {
+ f << indent << mangle(cell) << access << mangle_wire_name(conn.first) << ".next = ";
dump_sigspec_rhs(conn.second);
f << ";\n";
}
- f << indent << mangle(cell) << ".eval();\n";
- for (auto conn : cell->connections()) {
- if (conn.second.is_wire()) {
- RTLIL::Wire *wire = conn.second.as_wire();
- if (elided_wires.count(wire) && cell_wire_defs[cell].count(wire))
- continue;
- }
- if (cell->output(conn.first)) {
- if (conn.second.empty())
- continue; // ignore disconnected ports
- f << indent;
- dump_sigspec_lhs(conn.second);
- f << " = " << mangle(cell) << "." << mangle_wire_name(conn.first) << ".curr;\n";
+ auto assign_from_outputs = [&](bool cell_converged) {
+ for (auto conn : cell->connections()) {
+ if (cell->output(conn.first)) {
+ if (conn.second.empty())
+ continue; // ignore disconnected ports
+ if (is_cxxrtl_sync_port(cell, conn.first))
+ continue; // fully sync ports are handled in CELL_SYNC nodes
+ f << indent;
+ dump_sigspec_lhs(conn.second);
+ f << " = " << mangle(cell) << access << mangle_wire_name(conn.first);
+ // Similarly to how there is no purpose to buffering cell inputs, there is also no purpose to buffering
+ // combinatorial cell outputs in case the cell converges within one cycle. (To convince yourself that
+ // this optimization is valid, consider that, since the cell converged within one cycle, it would not
+ // have any buffered wires if they were not output ports. Imagine inlining the cell's eval() function,
+ // and consider the fate of the localized wires that used to be output ports.)
+ //
+ // Unlike cell inputs (which are never buffered), it is not possible to know apriori whether the cell
+ // (which may be late bound) will converge immediately. Because of this, the choice between using .curr
+ // (appropriate for buffered outputs) and .next (appropriate for unbuffered outputs) is made at runtime.
+ if (cell_converged && is_cxxrtl_comb_port(cell, conn.first))
+ f << ".next;\n";
+ else
+ f << ".curr;\n";
+ }
}
- }
+ };
+ f << indent << "if (" << mangle(cell) << access << "eval()) {\n";
+ inc_indent();
+ assign_from_outputs(/*cell_converged=*/true);
+ dec_indent();
+ f << indent << "} else {\n";
+ inc_indent();
+ f << indent << "converged = false;\n";
+ assign_from_outputs(/*cell_converged=*/false);
+ dec_indent();
+ f << indent << "}\n";
}
}
@@ -1076,24 +1355,34 @@ struct CxxrtlWorker {
log_assert(proc->root_case.attributes.empty());
dump_case_rule(&proc->root_case);
for (auto sync : proc->syncs) {
- RTLIL::SigBit sync_bit = sync->signal[0];
- sync_bit = sigmaps[sync_bit.wire->module](sync_bit);
+ RTLIL::SigBit sync_bit;
+ if (!sync->signal.empty()) {
+ sync_bit = sync->signal[0];
+ sync_bit = sigmaps[sync_bit.wire->module](sync_bit);
+ }
pool<std::string> events;
switch (sync->type) {
case RTLIL::STp:
+ log_assert(sync_bit.wire != nullptr);
events.insert("posedge_" + mangle(sync_bit));
break;
case RTLIL::STn:
+ log_assert(sync_bit.wire != nullptr);
events.insert("negedge_" + mangle(sync_bit));
+ break;
case RTLIL::STe:
+ log_assert(sync_bit.wire != nullptr);
events.insert("posedge_" + mangle(sync_bit));
events.insert("negedge_" + mangle(sync_bit));
break;
+ case RTLIL::STa:
+ events.insert("true");
+ break;
+
case RTLIL::ST0:
case RTLIL::ST1:
- case RTLIL::STa:
case RTLIL::STg:
case RTLIL::STi:
log_assert(false);
@@ -1117,35 +1406,66 @@ struct CxxrtlWorker {
}
}
- void dump_wire(const RTLIL::Wire *wire, bool is_local)
+ void dump_wire(const RTLIL::Wire *wire, bool is_local_context)
{
if (elided_wires.count(wire))
return;
+ if (localized_wires.count(wire) != is_local_context)
+ return;
- if (is_local) {
- if (!localized_wires.count(wire))
- return;
-
+ if (is_local_context) {
dump_attrs(wire);
f << indent << "value<" << wire->width << "> " << mangle(wire) << ";\n";
} else {
- if (localized_wires.count(wire))
- return;
+ std::string width;
+ if (wire->module->has_attribute(ID(cxxrtl.blackbox)) && wire->has_attribute(ID(cxxrtl.width))) {
+ width = wire->get_string_attribute(ID(cxxrtl.width));
+ } else {
+ width = std::to_string(wire->width);
+ }
dump_attrs(wire);
- f << indent << "wire<" << wire->width << "> " << mangle(wire);
- if (wire->attributes.count(ID(init))) {
+ f << indent << (is_input_wire(wire) ? "value" : "wire") << "<" << width << "> " << mangle(wire);
+ if (wire->has_attribute(ID::init)) {
f << " ";
- dump_const_init(wire->attributes.at(ID(init)));
+ dump_const_init(wire->attributes.at(ID::init));
}
f << ";\n";
- if (sync_wires[wire]) {
- for (auto sync_type : sync_types) {
- if (sync_type.first.wire == wire) {
- if (sync_type.second != RTLIL::STn)
- f << indent << "bool posedge_" << mangle(sync_type.first) << " = false;\n";
- if (sync_type.second != RTLIL::STp)
- f << indent << "bool negedge_" << mangle(sync_type.first) << " = false;\n";
+ if (edge_wires[wire]) {
+ if (is_input_wire(wire)) {
+ f << indent << "value<" << width << "> prev_" << mangle(wire);
+ if (wire->has_attribute(ID::init)) {
+ f << " ";
+ dump_const_init(wire->attributes.at(ID::init));
+ }
+ f << ";\n";
+ }
+ for (auto edge_type : edge_types) {
+ if (edge_type.first.wire == wire) {
+ std::string prev, next;
+ if (is_input_wire(wire)) {
+ prev = "prev_" + mangle(edge_type.first.wire);
+ next = mangle(edge_type.first.wire);
+ } else {
+ prev = mangle(edge_type.first.wire) + ".curr";
+ next = mangle(edge_type.first.wire) + ".next";
+ }
+ prev += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()";
+ next += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()";
+ if (edge_type.second != RTLIL::STn) {
+ f << indent << "bool posedge_" << mangle(edge_type.first) << "() const {\n";
+ inc_indent();
+ f << indent << "return !" << prev << " && " << next << ";\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (edge_type.second != RTLIL::STp) {
+ f << indent << "bool negedge_" << mangle(edge_type.first) << "() const {\n";
+ inc_indent();
+ f << indent << "return " << prev << " && !" << next << ";\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
}
}
}
@@ -1156,18 +1476,17 @@ struct CxxrtlWorker {
{
vector<const RTLIL::Cell*> init_cells;
for (auto cell : module->cells())
- if (cell->type == ID($meminit) && cell->getParam(ID(MEMID)).decode_string() == memory->name.str())
+ if (cell->type == ID($meminit) && cell->getParam(ID::MEMID).decode_string() == memory->name.str())
init_cells.push_back(cell);
std::sort(init_cells.begin(), init_cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
- int a_addr = a->getPort(ID(ADDR)).as_int(), b_addr = b->getPort(ID(ADDR)).as_int();
- int a_prio = a->getParam(ID(PRIORITY)).as_int(), b_prio = b->getParam(ID(PRIORITY)).as_int();
+ int a_addr = a->getPort(ID::ADDR).as_int(), b_addr = b->getPort(ID::ADDR).as_int();
+ int a_prio = a->getParam(ID::PRIORITY).as_int(), b_prio = b->getParam(ID::PRIORITY).as_int();
return a_prio > b_prio || (a_prio == b_prio && a_addr < b_addr);
});
dump_attrs(memory);
- f << indent << (writable_memories[memory] ? "" : "const ")
- << "memory<" << memory->width << "> " << mangle(memory)
+ f << indent << "memory<" << memory->width << "> " << mangle(memory)
<< " { " << memory->size << "u";
if (init_cells.empty()) {
f << " };\n";
@@ -1176,11 +1495,11 @@ struct CxxrtlWorker {
inc_indent();
for (auto cell : init_cells) {
dump_attrs(cell);
- RTLIL::Const data = cell->getPort(ID(DATA)).as_const();
- size_t width = cell->getParam(ID(WIDTH)).as_int();
- size_t words = cell->getParam(ID(WORDS)).as_int();
+ RTLIL::Const data = cell->getPort(ID::DATA).as_const();
+ size_t width = cell->getParam(ID::WIDTH).as_int();
+ size_t words = cell->getParam(ID::WORDS).as_int();
f << indent << "memory<" << memory->width << ">::init<" << words << "> { "
- << stringf("%#x", cell->getPort(ID(ADDR)).as_int()) << ", {";
+ << stringf("%#x", cell->getPort(ID::ADDR).as_int()) << ", {";
inc_indent();
for (size_t n = 0; n < words; n++) {
if (n % 4 == 0)
@@ -1198,140 +1517,235 @@ struct CxxrtlWorker {
}
}
- void dump_module_intf(RTLIL::Module *module)
+ void dump_eval_method(RTLIL::Module *module)
{
- dump_attrs(module);
- f << "struct " << mangle(module) << " : public module {\n";
inc_indent();
- for (auto wire : module->wires())
- dump_wire(wire, /*is_local=*/false);
- f << "\n";
- bool has_memories = false;
- for (auto memory : module->memories) {
- dump_memory(module, memory.second);
- has_memories = true;
- }
- if (has_memories)
- f << "\n";
- bool has_cells = false;
- for (auto cell : module->cells()) {
- if (is_internal_cell(cell->type))
- continue;
- f << indent << mangle_module_name(cell->type) << " " << mangle(cell) << ";\n";
- has_cells = true;
+ f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n";
+ if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ for (auto wire : module->wires()) {
+ if (edge_wires[wire]) {
+ for (auto edge_type : edge_types) {
+ if (edge_type.first.wire == wire) {
+ if (edge_type.second != RTLIL::STn) {
+ f << indent << "bool posedge_" << mangle(edge_type.first) << " = ";
+ f << "this->posedge_" << mangle(edge_type.first) << "();\n";
+ }
+ if (edge_type.second != RTLIL::STp) {
+ f << indent << "bool negedge_" << mangle(edge_type.first) << " = ";
+ f << "this->negedge_" << mangle(edge_type.first) << "();\n";
+ }
+ }
+ }
+ }
+ }
+ for (auto wire : module->wires())
+ dump_wire(wire, /*is_local_context=*/true);
+ for (auto node : schedule[module]) {
+ switch (node.type) {
+ case FlowGraph::Node::Type::CONNECT:
+ dump_connect(node.connect);
+ break;
+ case FlowGraph::Node::Type::CELL_SYNC:
+ dump_cell_sync(node.cell);
+ break;
+ case FlowGraph::Node::Type::CELL_EVAL:
+ dump_cell_eval(node.cell);
+ break;
+ case FlowGraph::Node::Type::PROCESS:
+ dump_process(node.process);
+ break;
+ }
+ }
}
- if (has_cells)
- f << "\n";
- f << indent << "void eval() override;\n";
- f << indent << "bool commit() override;\n";
+ f << indent << "return converged;\n";
dec_indent();
- f << "}; // struct " << mangle(module) << "\n";
- f << "\n";
}
- void dump_module_impl(RTLIL::Module *module)
+ void dump_commit_method(RTLIL::Module *module)
{
- f << "void " << mangle(module) << "::eval() {\n";
inc_indent();
- for (auto wire : module->wires())
- dump_wire(wire, /*is_local=*/true);
- for (auto node : schedule[module]) {
- switch (node.type) {
- case FlowGraph::Node::Type::CONNECT:
- dump_connect(node.connect);
- break;
- case FlowGraph::Node::Type::CELL:
- dump_cell(node.cell);
- break;
- case FlowGraph::Node::Type::PROCESS:
- dump_process(node.process);
- break;
+ f << indent << "bool changed = false;\n";
+ for (auto wire : module->wires()) {
+ if (elided_wires.count(wire) || localized_wires.count(wire))
+ continue;
+ if (is_input_wire(wire)) {
+ if (edge_wires[wire])
+ f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
+ continue;
}
+ if (!module->get_bool_attribute(ID(cxxrtl.blackbox)) || wire->port_id != 0)
+ f << indent << "changed |= " << mangle(wire) << ".commit();\n";
}
- for (auto sync_type : sync_types) {
- if (sync_type.first.wire->module == module) {
- if (sync_type.second != RTLIL::STn)
- f << indent << "posedge_" << mangle(sync_type.first) << " = false;\n";
- if (sync_type.second != RTLIL::STp)
- f << indent << "negedge_" << mangle(sync_type.first) << " = false;\n";
+ if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ for (auto memory : module->memories) {
+ if (!writable_memories[memory.second])
+ continue;
+ f << indent << "changed |= " << mangle(memory.second) << ".commit();\n";
+ }
+ for (auto cell : module->cells()) {
+ if (is_internal_cell(cell->type))
+ continue;
+ const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
+ f << indent << "changed |= " << mangle(cell) << access << "commit();\n";
}
}
+ f << indent << "return changed;\n";
dec_indent();
- f << "}\n";
- f << "\n";
+ }
- f << "bool " << mangle(module) << "::commit() {\n";
+ void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
+ {
+ if (metadata_map.empty()) {
+ f << "metadata_map()";
+ return;
+ }
+ f << "metadata_map({\n";
inc_indent();
- f << indent << "bool changed = false;\n";
- for (auto wire : module->wires()) {
- if (elided_wires.count(wire) || localized_wires.count(wire))
+ for (auto metadata_item : metadata_map) {
+ if (!metadata_item.first.begins_with("\\"))
continue;
- if (sync_wires[wire]) {
- std::string wire_prev = mangle(wire) + "_prev";
- std::string wire_curr = mangle(wire) + ".curr";
- std::string wire_edge = mangle(wire) + "_edge";
- f << indent << "value<" << wire->width << "> " << wire_prev << " = " << wire_curr << ";\n";
- f << indent << "if (" << mangle(wire) << ".commit()) {\n";
- inc_indent();
- f << indent << "value<" << wire->width << "> " << wire_edge << " = "
- << wire_prev << ".bit_xor(" << wire_curr << ");\n";
- for (auto sync_type : sync_types) {
- if (sync_type.first.wire != wire)
- continue;
- if (sync_type.second != RTLIL::STn) {
- f << indent << "if (" << wire_edge << ".slice<" << sync_type.first.offset << ">().val() && "
- << wire_curr << ".slice<" << sync_type.first.offset << ">().val())\n";
- inc_indent();
- f << indent << "posedge_" << mangle(sync_type.first) << " = true;\n";
- dec_indent();
- }
- if (sync_type.second != RTLIL::STp) {
- f << indent << "if (" << wire_edge << ".slice<" << sync_type.first.offset << ">().val() && "
- << "!" << wire_curr << ".slice<" << sync_type.first.offset << ">().val())\n";
- inc_indent();
- f << indent << "negedge_" << mangle(sync_type.first) << " = true;\n";
- dec_indent();
- }
- f << indent << "changed = true;\n";
- }
- dec_indent();
- f << indent << "}\n";
+ f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
+ if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
+ f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint;
+ } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
+ f << escape_cxx_string(metadata_item.second.decode_string());
} else {
- f << indent << "changed |= " << mangle(wire) << ".commit();\n";
+ f << metadata_item.second.as_int(/*is_signed=*/metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED);
+ if (!(metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED))
+ f << "u";
}
+ f << " },\n";
}
- for (auto memory : module->memories) {
- if (!writable_memories[memory.second])
- continue;
- f << indent << "changed |= " << mangle(memory.second) << ".commit();\n";
- }
- for (auto cell : module->cells()) {
- if (is_internal_cell(cell->type))
- continue;
- f << indent << "changed |= " << mangle(cell) << ".commit();\n";
- }
- f << indent << "return changed;\n";
dec_indent();
- f << "}\n";
+ f << indent << "})";
+ }
+
+ void dump_module_intf(RTLIL::Module *module)
+ {
+ dump_attrs(module);
+ if (module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ if (module->has_attribute(ID(cxxrtl.template)))
+ f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n";
+ f << indent << "struct " << mangle(module) << " : public module {\n";
+ inc_indent();
+ for (auto wire : module->wires()) {
+ if (wire->port_id != 0)
+ dump_wire(wire, /*is_local_context=*/false);
+ }
+ f << "\n";
+ f << indent << "bool eval() override {\n";
+ dump_eval_method(module);
+ f << indent << "}\n";
+ f << "\n";
+ f << indent << "bool commit() override {\n";
+ dump_commit_method(module);
+ f << indent << "}\n";
+ f << "\n";
+ f << indent << "static std::unique_ptr<" << mangle(module);
+ f << template_params(module, /*is_decl=*/false) << "> ";
+ f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
+ dec_indent();
+ f << indent << "}; // struct " << mangle(module) << "\n";
+ f << "\n";
+ if (blackbox_specializations.count(module)) {
+ // If templated black boxes are used, the constructor of any module which includes the black box cell
+ // (which calls the declared but not defined in the generated code `create` function) may only be used
+ // if (a) the create function is defined in the same translation unit, or (b) the create function has
+ // a forward-declared explicit specialization.
+ //
+ // Option (b) makes it possible to have the generated code and the black box implementation in different
+ // translation units, which is convenient. Of course, its downside is that black boxes must predefine
+ // a specialization for every combination of parameters the generated code may use; but since the main
+ // purpose of templated black boxes is abstracting over datapath width, it is expected that there would
+ // be very few such combinations anyway.
+ for (auto specialization : blackbox_specializations[module]) {
+ f << indent << "template<>\n";
+ f << indent << "std::unique_ptr<" << mangle(module) << specialization << "> ";
+ f << mangle(module) << specialization << "::";
+ f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
+ f << "\n";
+ }
+ }
+ } else {
+ f << indent << "struct " << mangle(module) << " : public module {\n";
+ inc_indent();
+ for (auto wire : module->wires())
+ dump_wire(wire, /*is_local_context=*/false);
+ f << "\n";
+ bool has_memories = false;
+ for (auto memory : module->memories) {
+ dump_memory(module, memory.second);
+ has_memories = true;
+ }
+ if (has_memories)
+ f << "\n";
+ bool has_cells = false;
+ for (auto cell : module->cells()) {
+ if (is_internal_cell(cell->type))
+ continue;
+ dump_attrs(cell);
+ RTLIL::Module *cell_module = module->design->module(cell->type);
+ log_assert(cell_module != nullptr);
+ if (cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> ";
+ f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell);
+ f << "::create(" << escape_cxx_string(cell->name.str()) << ", ";
+ dump_metadata_map(cell->parameters);
+ f << ", ";
+ dump_metadata_map(cell->attributes);
+ f << ");\n";
+ } else {
+ f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n";
+ }
+ has_cells = true;
+ }
+ if (has_cells)
+ f << "\n";
+ f << indent << "bool eval() override;\n";
+ f << indent << "bool commit() override;\n";
+ dec_indent();
+ f << indent << "}; // struct " << mangle(module) << "\n";
+ f << "\n";
+ }
+ }
+
+ void dump_module_impl(RTLIL::Module *module)
+ {
+ if (module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ return;
+ f << indent << "bool " << mangle(module) << "::eval() {\n";
+ dump_eval_method(module);
+ f << indent << "}\n";
+ f << "\n";
+ f << indent << "bool " << mangle(module) << "::commit() {\n";
+ dump_commit_method(module);
+ f << indent << "}\n";
f << "\n";
}
void dump_design(RTLIL::Design *design)
{
+ std::vector<RTLIL::Module*> modules;
TopoSort<RTLIL::Module*> topo_design;
for (auto module : design->modules()) {
- if (module->get_blackbox_attribute() || !design->selected_module(module))
+ if (!design->selected_module(module))
+ continue;
+ if (module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ modules.push_back(module); // cxxrtl blackboxes first
+ if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl.blackbox)))
continue;
- topo_design.node(module);
+ topo_design.node(module);
for (auto cell : module->cells()) {
- if (is_internal_cell(cell->type))
+ if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell))
continue;
- log_assert(design->has(cell->type));
- topo_design.edge(design->module(cell->type), module);
+ RTLIL::Module *cell_module = design->module(cell->type);
+ log_assert(cell_module != nullptr);
+ topo_design.edge(cell_module, module);
}
}
log_assert(topo_design.sort());
+ modules.insert(modules.end(), topo_design.sorted.begin(), topo_design.sorted.end());
if (split_intf) {
// The only thing more depraved than include guards, is mangling filenames to turn them into include guards.
@@ -1347,11 +1761,8 @@ struct CxxrtlWorker {
f << "\n";
f << "namespace " << design_ns << " {\n";
f << "\n";
- for (auto module : topo_design.sorted) {
- if (!design->selected_module(module))
- continue;
+ for (auto module : modules)
dump_module_intf(module);
- }
f << "} // namespace " << design_ns << "\n";
f << "\n";
f << "#endif\n";
@@ -1367,9 +1778,7 @@ struct CxxrtlWorker {
f << "\n";
f << "namespace " << design_ns << " {\n";
f << "\n";
- for (auto module : topo_design.sorted) {
- if (!design->selected_module(module))
- continue;
+ for (auto module : modules) {
if (!split_intf)
dump_module_intf(module);
dump_module_impl(module);
@@ -1393,24 +1802,59 @@ struct CxxrtlWorker {
log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe);
RTLIL::SigBit sigbit = signal[0];
- if (!sync_types.count(sigbit))
- sync_types[sigbit] = type;
- else if (sync_types[sigbit] != type)
- sync_types[sigbit] = RTLIL::STe;
- sync_wires.insert(signal.as_wire());
+ if (!edge_types.count(sigbit))
+ edge_types[sigbit] = type;
+ else if (edge_types[sigbit] != type)
+ edge_types[sigbit] = RTLIL::STe;
+ edge_wires.insert(signal.as_wire());
}
void analyze_design(RTLIL::Design *design)
{
bool has_feedback_arcs = false;
+ bool has_buffered_wires = false;
+
for (auto module : design->modules()) {
if (!design->selected_module(module))
continue;
- FlowGraph flow;
SigMap &sigmap = sigmaps[module];
sigmap.set(module);
+ if (module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ for (auto port : module->ports) {
+ RTLIL::Wire *wire = module->wire(port);
+ if (wire->has_attribute(ID(cxxrtl.edge))) {
+ RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl.edge)];
+ if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire))
+ log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' is not a string with one character per bit.\n",
+ log_id(module), log_signal(wire));
+
+ std::string edges = wire->get_string_attribute(ID(cxxrtl.edge));
+ for (int i = 0; i < GetSize(wire); i++) {
+ RTLIL::SigSpec wire_sig = wire;
+ switch (edges[i]) {
+ case '-': break;
+ case 'p': register_edge_signal(sigmap, wire_sig[i], RTLIL::STp); break;
+ case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break;
+ case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break;
+ default:
+ log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' contains specifiers "
+ "other than '-', 'p', 'n', or 'a'.\n",
+ log_id(module), log_signal(wire));
+ }
+ }
+ }
+ }
+
+ // Black boxes converge by default, since their implementations are quite unlikely to require
+ // internal propagation of comb signals.
+ eval_converges[module] = true;
+ continue;
+ }
+
+ FlowGraph flow;
+
for (auto conn : module->connections())
flow.add_node(conn);
@@ -1418,32 +1862,44 @@ struct CxxrtlWorker {
dict<std::pair<RTLIL::SigBit, const RTLIL::Memory*>,
pool<const RTLIL::Cell*>> memwr_per_domain;
for (auto cell : module->cells()) {
+ if (!cell->known())
+ log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type));
+
+ RTLIL::Module *cell_module = design->module(cell->type);
+ if (cell_module &&
+ cell_module->get_blackbox_attribute() &&
+ !cell_module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type));
+
+ if (cell_module &&
+ cell_module->get_bool_attribute(ID(cxxrtl.blackbox)) &&
+ cell_module->get_bool_attribute(ID(cxxrtl.template)))
+ blackbox_specializations[cell_module].insert(template_args(cell));
+
FlowGraph::Node *node = flow.add_node(cell);
// Various DFF cells are treated like posedge/negedge processes, see above for details.
if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($dffsr))) {
- if (cell->getPort(ID(CLK)).is_wire())
- register_edge_signal(sigmap, cell->getPort(ID(CLK)),
- cell->parameters[ID(CLK_POLARITY)].as_bool() ? RTLIL::STp : RTLIL::STn);
- // The $adff and $dffsr cells are level-sensitive, not edge-sensitive (in spite of the fact that they
- // are inferred from an edge-sensitive Verilog process) and do not correspond to an edge-type sync rule.
+ if (cell->getPort(ID::CLK).is_wire())
+ register_edge_signal(sigmap, cell->getPort(ID::CLK),
+ cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
}
// Similar for memory port cells.
if (cell->type.in(ID($memrd), ID($memwr))) {
- if (cell->getParam(ID(CLK_ENABLE)).as_bool()) {
- if (cell->getPort(ID(CLK)).is_wire())
- register_edge_signal(sigmap, cell->getPort(ID(CLK)),
- cell->parameters[ID(CLK_POLARITY)].as_bool() ? RTLIL::STp : RTLIL::STn);
+ if (cell->getParam(ID::CLK_ENABLE).as_bool()) {
+ if (cell->getPort(ID::CLK).is_wire())
+ register_edge_signal(sigmap, cell->getPort(ID::CLK),
+ cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
}
memrw_cell_nodes[cell] = node;
}
// Optimize access to read-only memories.
if (cell->type == ID($memwr))
- writable_memories.insert(module->memories[cell->getParam(ID(MEMID)).decode_string()]);
+ writable_memories.insert(module->memories[cell->getParam(ID::MEMID).decode_string()]);
// Collect groups of memory write ports in the same domain.
- if (cell->type == ID($memwr) && cell->getParam(ID(CLK_ENABLE)).as_bool() && cell->getPort(ID(CLK)).is_wire()) {
- RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID(CLK)))[0];
- const RTLIL::Memory *memory = module->memories[cell->getParam(ID(MEMID)).decode_string()];
+ if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire()) {
+ RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0];
+ const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()];
memwr_per_domain[{clk_bit, memory}].insert(cell);
}
// Handling of packed memories is delegated to the `memory_unpack` pass, so we can rely on the presence
@@ -1453,17 +1909,17 @@ struct CxxrtlWorker {
}
for (auto cell : module->cells()) {
// Collect groups of memory write ports read by every transparent read port.
- if (cell->type == ID($memrd) && cell->getParam(ID(CLK_ENABLE)).as_bool() && cell->getPort(ID(CLK)).is_wire() &&
- cell->getParam(ID(TRANSPARENT)).as_bool()) {
- RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID(CLK)))[0];
- const RTLIL::Memory *memory = module->memories[cell->getParam(ID(MEMID)).decode_string()];
+ if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire() &&
+ cell->getParam(ID::TRANSPARENT).as_bool()) {
+ RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0];
+ const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()];
for (auto memwr_cell : memwr_per_domain[{clk_bit, memory}]) {
transparent_for[cell].insert(memwr_cell);
// Our implementation of transparent $memrd cells reads \EN, \ADDR and \DATA from every $memwr cell
// in the same domain, which isn't directly visible in the netlist. Add these uses explicitly.
- flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID(EN)));
- flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID(ADDR)));
- flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID(DATA)));
+ flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::EN));
+ flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::ADDR));
+ flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::DATA));
}
}
}
@@ -1499,26 +1955,18 @@ struct CxxrtlWorker {
for (auto wire : module->wires()) {
if (!flow.is_elidable(wire)) continue;
if (wire->port_id != 0) continue;
- if (wire->get_bool_attribute(ID(keep))) continue;
+ if (wire->get_bool_attribute(ID::keep)) continue;
if (wire->name.begins_with("$") && !elide_internal) continue;
if (wire->name.begins_with("\\") && !elide_public) continue;
- if (sync_wires[wire]) continue;
- log_assert(flow.wire_defs[wire].size() == 1);
- elided_wires[wire] = **flow.wire_defs[wire].begin();
+ if (edge_wires[wire]) continue;
+ log_assert(flow.wire_comb_defs[wire].size() == 1);
+ elided_wires[wire] = **flow.wire_comb_defs[wire].begin();
}
- // Elided wires that are outputs of internal cells are always connected to a well known port (Y).
- // For user cells, there could be multiple of them, and we need a way to look up the port name
- // knowing only the wire.
- for (auto cell : module->cells())
- for (auto conn : cell->connections())
- if (conn.second.is_wire() && elided_wires.count(conn.second.as_wire()))
- cell_wire_defs[cell][conn.second.as_wire()] = conn.first;
-
dict<FlowGraph::Node*, pool<const RTLIL::Wire*>, hash_ptr_ops> node_defs;
- for (auto wire_def : flow.wire_defs)
- for (auto node : wire_def.second)
- node_defs[node].insert(wire_def.first);
+ for (auto wire_comb_def : flow.wire_comb_defs)
+ for (auto node : wire_comb_def.second)
+ node_defs[node].insert(wire_comb_def.first);
Scheduler<FlowGraph::Node> scheduler;
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*, hash_ptr_ops> node_map;
@@ -1557,26 +2005,57 @@ struct CxxrtlWorker {
if (!feedback_wires.empty()) {
has_feedback_arcs = true;
- log("Module `%s` contains feedback arcs through wires:\n", module->name.c_str());
- for (auto wire : feedback_wires) {
- log(" %s\n", wire->name.c_str());
- }
+ log("Module `%s' contains feedback arcs through wires:\n", log_id(module));
+ for (auto wire : feedback_wires)
+ log(" %s\n", log_id(wire));
}
for (auto wire : module->wires()) {
if (feedback_wires[wire]) continue;
if (wire->port_id != 0) continue;
- if (wire->get_bool_attribute(ID(keep))) continue;
+ if (wire->get_bool_attribute(ID::keep)) continue;
if (wire->name.begins_with("$") && !localize_internal) continue;
if (wire->name.begins_with("\\") && !localize_public) continue;
- if (sync_wires[wire]) continue;
- // Outputs of FF/$memrd cells and LHS of sync actions do not end up in defs.
- if (flow.wire_defs[wire].size() != 1) continue;
+ if (edge_wires[wire]) continue;
+ if (flow.wire_sync_defs.count(wire) > 0) continue;
localized_wires.insert(wire);
}
+
+ // For maximum performance, the state of the simulation (which is the same as the set of its double buffered
+ // wires, since using a singly buffered wire for any kind of state introduces a race condition) should contain
+ // no wires attached to combinatorial outputs. Feedback wires, by definition, make that impossible. However,
+ // it is possible that a design with no feedback arcs would end up with doubly buffered wires in such cases
+ // as a wire with multiple drivers where one of them is combinatorial and the other is synchronous. Such designs
+ // also require more than one delta cycle to converge.
+ pool<const RTLIL::Wire*> buffered_wires;
+ for (auto wire : module->wires()) {
+ if (flow.wire_comb_defs[wire].size() > 0 && !elided_wires.count(wire) && !localized_wires[wire]) {
+ if (!feedback_wires[wire])
+ buffered_wires.insert(wire);
+ }
+ }
+ if (!buffered_wires.empty()) {
+ has_buffered_wires = true;
+ log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));
+ for (auto wire : buffered_wires)
+ log(" %s\n", log_id(wire));
+ }
+
+ eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
}
- if (has_feedback_arcs) {
- log("Feedback arcs require delta cycles during evaluation.\n");
+ if (has_feedback_arcs || has_buffered_wires) {
+ // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
+ // by optimizing the design, if after `opt_clean -purge` there are any feedback wires remaining, it is very
+ // likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message.
+ const char *why_pessimistic = nullptr;
+ if (has_feedback_arcs)
+ why_pessimistic = "feedback wires";
+ else if (has_buffered_wires)
+ why_pessimistic = "buffered combinatorial wires";
+ log("\n");
+ log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic);
+ if (!max_opt_level)
+ log("Increasing the optimization level may eliminate %s from the design.\n", why_pessimistic);
}
}
@@ -1585,12 +2064,12 @@ struct CxxrtlWorker {
has_sync_init = has_packed_mem = false;
for (auto module : design->modules()) {
- if (module->get_blackbox_attribute())
+ if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl.blackbox)))
continue;
if (!design->selected_whole_module(module))
if (design->selected_module(module))
- log_cmd_error("Can't handle partially selected module `%s`!\n", id2cstr(module->name));
+ log_cmd_error("Can't handle partially selected module `%s'!\n", id2cstr(module->name));
if (!design->selected_module(module))
continue;
@@ -1608,8 +2087,12 @@ struct CxxrtlWorker {
void prepare_design(RTLIL::Design *design)
{
bool has_sync_init, has_packed_mem;
+ log_push();
check_design(design, has_sync_init, has_packed_mem);
- if (has_sync_init) {
+ if (run_proc_flatten) {
+ Pass::call(design, "proc");
+ Pass::call(design, "flatten");
+ } else if (has_sync_init) {
// We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those
// in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.)
Pass::call(design, "proc_prune");
@@ -1622,18 +2105,15 @@ struct CxxrtlWorker {
if (has_sync_init || has_packed_mem)
check_design(design, has_sync_init, has_packed_mem);
log_assert(!(has_sync_init || has_packed_mem));
-
- if (run_splitnets) {
- Pass::call(design, "splitnets -driver");
+ if (run_opt_clean_purge)
Pass::call(design, "opt_clean -purge");
- }
- log("\n");
+ log_pop();
analyze_design(design);
}
};
struct CxxrtlBackend : public Backend {
- static const int DEFAULT_OPT_LEVEL = 5;
+ static const int DEFAULT_OPT_LEVEL = 6;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
void help() YS_OVERRIDE
@@ -1642,21 +2122,156 @@ struct CxxrtlBackend : public Backend {
log("\n");
log(" write_cxxrtl [options] [filename]\n");
log("\n");
- log("Write C++ code for simulating the design. The generated code requires a driver;\n");
- log("the following simple driver is provided as an example:\n");
+ log("Write C++ code that simulates the design. The generated code requires a driver\n");
+ log("that instantiates the design, toggles its clock, and interacts with its ports.\n");
+ log("\n");
+ log("The following driver may be used as an example for a design with a single clock\n");
+ log("driving rising edge triggered flip-flops:\n");
log("\n");
log(" #include \"top.cc\"\n");
log("\n");
log(" int main() {\n");
log(" cxxrtl_design::p_top top;\n");
+ log(" top.step();\n");
log(" while (1) {\n");
- log(" top.p_clk.next = value<1> {1u};\n");
+ log(" /* user logic */\n");
+ log(" top.p_clk = value<1> {0u};\n");
log(" top.step();\n");
- log(" top.p_clk.next = value<1> {0u};\n");
+ log(" top.p_clk = value<1> {1u};\n");
log(" top.step();\n");
log(" }\n");
log(" }\n");
log("\n");
+ log("Note that CXXRTL simulations, just like the hardware they are simulating, are\n");
+ log("subject to race conditions. If, in the example above, the user logic would run\n");
+ log("simultaneously with the rising edge of the clock, the design would malfunction.\n");
+ log("\n");
+ log("This backend supports replacing parts of the design with black boxes implemented\n");
+ log("in C++. If a module marked as a CXXRTL black box, its implementation is ignored,\n");
+ log("and the generated code consists only of an interface and a factory function.\n");
+ log("The driver must implement the factory function that creates an implementation of\n");
+ log("the black box, taking into account the parameters it is instantiated with.\n");
+ log("\n");
+ log("For example, the following Verilog code defines a CXXRTL black box interface for\n");
+ log("a synchronous debug sink:\n");
+ log("\n");
+ log(" (* cxxrtl.blackbox *)\n");
+ log(" module debug(...);\n");
+ log(" (* cxxrtl.edge = \"p\" *) input clk;\n");
+ log(" input en;\n");
+ log(" input [7:0] i_data;\n");
+ log(" (* cxxrtl.sync *) output [7:0] o_data;\n");
+ log(" endmodule\n");
+ log("\n");
+ log("For this HDL interface, this backend will generate the following C++ interface:\n");
+ log("\n");
+ log(" struct bb_p_debug : public module {\n");
+ log(" value<1> p_clk;\n");
+ log(" bool posedge_p_clk() const { /* ... */ }\n");
+ log(" value<1> p_en;\n");
+ log(" value<8> p_i_data;\n");
+ log(" wire<8> p_o_data;\n");
+ log("\n");
+ log(" bool eval() override;\n");
+ log(" bool commit() override;\n");
+ log("\n");
+ log(" static std::unique_ptr<bb_p_debug>\n");
+ log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n");
+ log(" };\n");
+ log("\n");
+ log("The `create' function must be implemented by the driver. For example, it could\n");
+ log("always provide an implementation logging the values to standard error stream:\n");
+ log("\n");
+ log(" namespace cxxrtl_design {\n");
+ log("\n");
+ log(" struct stderr_debug : public bb_p_debug {\n");
+ log(" bool eval() override {\n");
+ log(" if (posedge_p_clk() && p_en)\n");
+ log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n");
+ log(" p_o_data.next = p_i_data;\n");
+ log(" return bb_p_debug::eval();\n");
+ log(" }\n");
+ log(" };\n");
+ log("\n");
+ log(" std::unique_ptr<bb_p_debug>\n");
+ log(" bb_p_debug::create(std::string name, cxxrtl::metadata_map parameters,\n");
+ log(" cxxrtl::metadata_map attributes) {\n");
+ log(" return std::make_unique<stderr_debug>();\n");
+ log(" }\n");
+ log("\n");
+ log(" }\n");
+ log("\n");
+ log("For complex applications of black boxes, it is possible to parameterize their\n");
+ log("port widths. For example, the following Verilog code defines a CXXRTL black box\n");
+ log("interface for a configurable width debug sink:\n");
+ log("\n");
+ log(" (* cxxrtl.blackbox, cxxrtl.template = \"WIDTH\" *)\n");
+ log(" module debug(...);\n");
+ log(" parameter WIDTH = 8;\n");
+ log(" (* cxxrtl.edge = \"p\" *) input clk;\n");
+ log(" input en;\n");
+ log(" (* cxxrtl.width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n");
+ log(" (* cxxrtl.width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n");
+ log(" endmodule\n");
+ log("\n");
+ log("For this parametric HDL interface, this backend will generate the following C++\n");
+ log("interface (only the differences are shown):\n");
+ log("\n");
+ log(" template<size_t WIDTH>\n");
+ log(" struct bb_p_debug : public module {\n");
+ log(" // ...\n");
+ log(" value<WIDTH> p_i_data;\n");
+ log(" wire<WIDTH> p_o_data;\n");
+ log(" // ...\n");
+ log(" static std::unique_ptr<bb_p_debug<WIDTH>>\n");
+ log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n");
+ log(" };\n");
+ log("\n");
+ log("The `create' function must be implemented by the driver, specialized for every\n");
+ log("possible combination of template parameters. (Specialization is necessary to\n");
+ log("enable separate compilation of generated code and black box implementations.)\n");
+ log("\n");
+ log(" template<size_t SIZE>\n");
+ log(" struct stderr_debug : public bb_p_debug<SIZE> {\n");
+ log(" // ...\n");
+ log(" };\n");
+ log("\n");
+ log(" template<>\n");
+ log(" std::unique_ptr<bb_p_debug<8>>\n");
+ log(" bb_p_debug<8>::create(std::string name, cxxrtl::metadata_map parameters,\n");
+ log(" cxxrtl::metadata_map attributes) {\n");
+ log(" return std::make_unique<stderr_debug<8>>();\n");
+ log(" }\n");
+ log("\n");
+ log("The following attributes are recognized by this backend:\n");
+ log("\n");
+ log(" cxxrtl.blackbox\n");
+ log(" only valid on modules. if specified, the module contents are ignored,\n");
+ log(" and the generated code includes only the module interface and a factory\n");
+ log(" function, which will be called to instantiate the module.\n");
+ log("\n");
+ log(" cxxrtl.edge\n");
+ log(" only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n");
+ log(" if specified on signal `clk`, the generated code includes edge detectors\n");
+ log(" `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n");
+ log(" \"a\"), simplifying implementation of clocked black boxes.\n");
+ log("\n");
+ log(" cxxrtl.template\n");
+ log(" only valid on black boxes. must contain a space separated sequence of\n");
+ log(" identifiers that have a corresponding black box parameters. for each\n");
+ log(" of them, the generated code includes a `size_t` template parameter.\n");
+ log("\n");
+ log(" cxxrtl.width\n");
+ log(" only valid on ports of black boxes. must be a constant expression, which\n");
+ log(" is directly inserted into generated code.\n");
+ log("\n");
+ log(" cxxrtl.comb, cxxrtl.sync\n");
+ log(" only valid on outputs of black boxes. if specified, indicates that every\n");
+ log(" bit of the output port is driven, correspondingly, by combinatorial or\n");
+ log(" synchronous logic. this knowledge is used for scheduling optimizations.\n");
+ log(" if neither is specified, the output will be pessimistically treated as\n");
+ log(" driven by both combinatorial and synchronous logic.\n");
+ log("\n");
log("The following options are supported by this backend:\n");
log("\n");
log(" -header\n");
@@ -1690,7 +2305,10 @@ struct CxxrtlBackend : public Backend {
log(" like -O3, and localize public wires not marked (*keep*) if possible.\n");
log("\n");
log(" -O5\n");
- log(" like -O4, and run `splitnets -driver; opt_clean -purge` first.\n");
+ log(" like -O4, and run `opt_clean -purge` first.\n");
+ log("\n");
+ log(" -O6\n");
+ log(" like -O5, and run `proc; flatten` first.\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -1724,8 +2342,11 @@ struct CxxrtlBackend : public Backend {
extra_args(f, filename, args, argidx);
switch (opt_level) {
+ case 6:
+ worker.max_opt_level = true;
+ worker.run_proc_flatten = true;
case 5:
- worker.run_splitnets = true;
+ worker.run_opt_clean_purge = true;
case 4:
worker.localize_public = true;
case 3:
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index 593c31c28..b79bbbc72 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -28,7 +28,9 @@
#include <type_traits>
#include <tuple>
#include <vector>
+#include <map>
#include <algorithm>
+#include <memory>
#include <sstream>
// The cxxrtl support library implements compile time specialized arbitrary width arithmetics, as well as provides
@@ -604,12 +606,15 @@ struct memory {
auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...};
}
- value<Width> &operator [](size_t index) {
+ // An operator for direct memory reads. May be used at any time during the simulation.
+ const value<Width> &operator [](size_t index) const {
assert(index < data.size());
return data[index];
}
- const value<Width> &operator [](size_t index) const {
+ // An operator for direct memory writes. May only be used before the simulation is started. If used
+ // after the simulation is started, the design may malfunction.
+ value<Width> &operator [](size_t index) {
assert(index < data.size());
return data[index];
}
@@ -654,6 +659,57 @@ struct memory {
}
};
+struct metadata {
+ const enum {
+ MISSING = 0,
+ UINT = 1,
+ SINT = 2,
+ STRING = 3,
+ DOUBLE = 4,
+ } value_type;
+
+ // In debug mode, using the wrong .as_*() function will assert.
+ // In release mode, using the wrong .as_*() function will safely return a default value.
+ union {
+ const unsigned uint_value = 0;
+ const signed sint_value;
+ };
+ const std::string string_value = "";
+ const double double_value = 0.0;
+
+ metadata() : value_type(MISSING) {}
+ metadata(unsigned value) : value_type(UINT), uint_value(value) {}
+ metadata(signed value) : value_type(SINT), sint_value(value) {}
+ metadata(const std::string &value) : value_type(STRING), string_value(value) {}
+ metadata(const char *value) : value_type(STRING), string_value(value) {}
+ metadata(double value) : value_type(DOUBLE), double_value(value) {}
+
+ metadata(const metadata &) = default;
+ metadata &operator=(const metadata &) = delete;
+
+ unsigned as_uint() const {
+ assert(value_type == UINT);
+ return uint_value;
+ }
+
+ signed as_sint() const {
+ assert(value_type == SINT);
+ return sint_value;
+ }
+
+ const std::string &as_string() const {
+ assert(value_type == STRING);
+ return string_value;
+ }
+
+ double as_double() const {
+ assert(value_type == DOUBLE);
+ return double_value;
+ }
+};
+
+typedef std::map<std::string, metadata> metadata_map;
+
struct module {
module() {}
virtual ~module() {}
@@ -661,15 +717,16 @@ struct module {
module(const module &) = delete;
module &operator=(const module &) = delete;
- virtual void eval() = 0;
+ virtual bool eval() = 0;
virtual bool commit() = 0;
size_t step() {
size_t deltas = 0;
+ bool converged = false;
do {
- eval();
+ converged = eval();
deltas++;
- } while (commit());
+ } while (commit() && !converged);
return deltas;
}
};
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index cc20f17fc..7e24468c0 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -113,6 +113,9 @@ struct EdifBackend : public Backend {
log(" -attrprop\n");
log(" create EDIF properties for cell attributes\n");
log("\n");
+ log(" -keep\n");
+ log(" create extra KEEP nets by allowing a cell to drive multiple nets.\n");
+ log("\n");
log(" -pvector {par|bra|ang}\n");
log(" sets the delimiting character for module port rename clauses to\n");
log(" parentheses, square brackets, or angle brackets.\n");
@@ -130,7 +133,7 @@ struct EdifBackend : public Backend {
bool port_rename = false;
bool attr_properties = false;
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
- bool nogndvcc = false, gndvccy = false;
+ bool nogndvcc = false, gndvccy = false, keepmode = false;
CellTypes ct(design);
EdifNames edif_names;
@@ -153,6 +156,10 @@ struct EdifBackend : public Backend {
attr_properties = true;
continue;
}
+ if (args[argidx] == "-keep") {
+ keepmode = true;
+ continue;
+ }
if (args[argidx] == "-pvector" && argidx+1 < args.size()) {
std::string parray;
port_rename = true;
@@ -337,6 +344,71 @@ struct EdifBackend : public Backend {
*f << stringf(" (view VIEW_NETLIST\n");
*f << stringf(" (viewType NETLIST)\n");
*f << stringf(" (interface\n");
+
+ for (auto cell : module->cells()) {
+ for (auto &conn : cell->connections())
+ if (cell->output(conn.first))
+ sigmap.add(conn.second);
+ }
+
+ for (auto wire : module->wires())
+ for (auto b1 : SigSpec(wire))
+ {
+ auto b2 = sigmap(b1);
+
+ if (b1 == b2 || !b2.wire)
+ continue;
+
+ log_assert(b1.wire != nullptr);
+
+ Wire *w1 = b1.wire;
+ Wire *w2 = b2.wire;
+
+ {
+ int c1 = w1->get_bool_attribute(ID::keep);
+ int c2 = w2->get_bool_attribute(ID::keep);
+
+ if (c1 > c2) goto promote;
+ if (c1 < c2) goto nopromote;
+ }
+
+ {
+ int c1 = w1->name[0] == '\\';
+ int c2 = w2->name[0] == '\\';
+
+ if (c1 > c2) goto promote;
+ if (c1 < c2) goto nopromote;
+ }
+
+ {
+ auto count_nontrivial_attr = [](Wire *w) {
+ int count = w->attributes.size();
+ count -= w->attributes.count(ID::src);
+ count -= w->attributes.count(ID::unused_bits);
+ return count;
+ };
+
+ int c1 = count_nontrivial_attr(w1);
+ int c2 = count_nontrivial_attr(w2);
+
+ if (c1 > c2) goto promote;
+ if (c1 < c2) goto nopromote;
+ }
+
+ {
+ int c1 = w1->port_id ? INT_MAX - w1->port_id : 0;
+ int c2 = w2->port_id ? INT_MAX - w2->port_id : 0;
+
+ if (c1 > c2) goto promote;
+ if (c1 < c2) goto nopromote;
+ }
+
+ nopromote:
+ if (0)
+ promote:
+ sigmap.add(b1);
+ }
+
for (auto wire : module->wires()) {
if (wire->port_id == 0)
continue;
@@ -369,12 +441,15 @@ struct EdifBackend : public Backend {
}
}
}
+
*f << stringf(" )\n");
*f << stringf(" (contents\n");
+
if (!nogndvcc) {
*f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n");
*f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n");
}
+
for (auto cell : module->cells()) {
*f << stringf(" (instance %s\n", EDIF_DEF(cell->name));
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
@@ -412,6 +487,7 @@ struct EdifBackend : public Backend {
}
}
}
+
for (auto &it : net_join_db) {
RTLIL::SigBit sig = it.first;
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
@@ -440,7 +516,7 @@ struct EdifBackend : public Backend {
}
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
for (auto &ref : it.second)
- *f << stringf(" %s\n", ref.first.c_str());
+ *f << stringf(" %s\n", ref.first.c_str());
if (sig.wire == NULL) {
if (nogndvcc)
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
@@ -455,30 +531,48 @@ struct EdifBackend : public Backend {
add_prop(p.first, p.second);
*f << stringf("\n )\n");
}
- for (auto wire : module->wires()) {
+
+ for (auto wire : module->wires())
+ {
if (!wire->get_bool_attribute(ID::keep))
continue;
- for(int i = 0; i < wire->width; i++) {
+
+ for(int i = 0; i < wire->width; i++)
+ {
SigBit raw_sig = RTLIL::SigSpec(wire, i);
SigBit mapped_sig = sigmap(raw_sig);
+
if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0)
continue;
+
std::string netname = log_signal(raw_sig);
for (size_t i = 0; i < netname.size(); i++)
if (netname[i] == ' ' || netname[i] == '\\')
netname.erase(netname.begin() + i--);
- *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
- auto &refs = net_join_db.at(mapped_sig);
- for (auto &ref : refs)
- if (ref.second)
- *f << stringf(" %s\n", ref.first.c_str());
- *f << stringf(" )");
- if (attr_properties && raw_sig.wire != NULL)
- for (auto &p : raw_sig.wire->attributes)
- add_prop(p.first, p.second);
- *f << stringf("\n )\n");
+
+ if (keepmode)
+ {
+ *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
+
+ auto &refs = net_join_db.at(mapped_sig);
+ for (auto &ref : refs)
+ if (ref.second)
+ *f << stringf(" %s\n", ref.first.c_str());
+ *f << stringf(" )");
+
+ if (attr_properties && raw_sig.wire != NULL)
+ for (auto &p : raw_sig.wire->attributes)
+ add_prop(p.first, p.second);
+
+ *f << stringf("\n )\n");
+ }
+ else
+ {
+ log_warning("Ignoring conflicting 'keep' property on net %s. Use -keep to generate the extra net nevertheless.\n", EDIF_DEF(netname));
+ }
}
}
+
*f << stringf(" )\n");
*f << stringf(" )\n");
*f << stringf(" )\n");
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index fd7f20cc6..40d05a036 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -306,17 +306,8 @@ struct FirrtlWorker
// If this is a parameterized module, its parent module is encoded in the cell type
if (cell->type.begins_with("$paramod"))
{
- std::string::iterator it;
- for (it = cell_type.begin(); it < cell_type.end(); it++)
- {
- switch (*it) {
- case '\\': /* FALL_THROUGH */
- case '=': /* FALL_THROUGH */
- case '\'': /* FALL_THROUGH */
- case '$': instanceOf.append("_"); break;
- default: instanceOf.append(1, *it); break;
- }
- }
+ log_assert(cell->has_attribute(ID::hdlname));
+ instanceOf = cell->get_string_attribute(ID::hdlname);
}
else
{
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
index 5445fad90..6e3882d2d 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/ilang/ilang_backend.cc
@@ -290,8 +290,16 @@ void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu
if (!module->avail_parameters.empty()) {
if (only_selected)
f << stringf("\n");
- for (auto &p : module->avail_parameters)
- f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str());
+ for (const auto &p : module->avail_parameters) {
+ const auto &it = module->parameter_default_values.find(p);
+ if (it == module->parameter_default_values.end()) {
+ f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str());
+ } else {
+ f << stringf("%s" " parameter %s ", indent.c_str(), p.c_str());
+ dump_const(f, it->second);
+ f << stringf("\n");
+ }
+ }
}
}
diff --git a/backends/json/json.cc b/backends/json/json.cc
index 1da23bb7d..1a8b757ef 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -141,6 +141,12 @@ struct JsonWriter
write_parameters(module->attributes, /*for_module=*/true);
f << stringf("\n },\n");
+ if (module->parameter_default_values.size()) {
+ f << stringf(" \"parameter_default_values\": {");
+ write_parameters(module->parameter_default_values, /*for_module=*/true);
+ f << stringf("\n },\n");
+ }
+
f << stringf(" \"ports\": {");
bool first = true;
for (auto n : module->ports) {
@@ -310,6 +316,10 @@ struct JsonBackend : public Backend {
log(" <attribute_name>: <attribute_value>,\n");
log(" ...\n");
log(" },\n");
+ log(" \"parameter_default_values\": {\n");
+ log(" <parameter_name>: <parameter_value>,\n");
+ log(" ...\n");
+ log(" },\n");
log(" \"ports\": {\n");
log(" <port_name>: <port_details>,\n");
log(" ...\n");
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 245a53611..733556621 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -218,6 +218,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
realvalue = 0;
id2ast = NULL;
basic_prep = false;
+ lookahead = false;
if (child1)
children.push_back(child1);
@@ -310,6 +311,10 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " reg");
if (is_signed)
fprintf(f, " signed");
+ if (basic_prep)
+ fprintf(f, " basic_prep");
+ if (lookahead)
+ fprintf(f, " lookahead");
if (port_id > 0)
fprintf(f, " port=%d", port_id);
if (range_valid || range_left != -1 || range_right != 0)
@@ -1069,8 +1074,6 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
new_children.push_back(child);
} else if (child->type == AST_PARAMETER) {
- child->delete_children();
- child->children.push_back(AstNode::mkconst_int(0, false, 0));
new_children.push_back(child);
} else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE &&
(child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) {
@@ -1565,6 +1568,9 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
rewritten.reserve(GetSize(parameters));
AstNode *new_ast = ast->clone();
+ if (!new_ast->attributes.count(ID::hdlname))
+ new_ast->attributes[ID::hdlname] = AstNode::mkconst_str(stripped_name);
+
para_counter = 0;
for (auto child : new_ast->children) {
if (child->type != AST_PARAMETER)
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 3dd40238f..3f6329112 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -202,6 +202,9 @@ namespace AST
// this is used by simplify to detect if basic analysis has been performed already on the node
bool basic_prep;
+ // this is used for ID references in RHS expressions that should use the "new" value for non-blocking assignments
+ bool lookahead;
+
// this is the original sourcecode location that resulted in this AST node
// it is automatically set by the constructor using AST::current_filename and
// the AST::get_line_num() callback function.
@@ -352,6 +355,7 @@ namespace AST_INTERNAL
extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
extern AST::AstModule *current_module;
extern bool current_always_clocked;
+ struct LookaheadRewriter;
struct ProcessGenerator;
}
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index ab368fdb0..d35335747 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -157,6 +157,126 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
return wire;
}
+// helper class for rewriting simple lookahead references in AST always blocks
+struct AST_INTERNAL::LookaheadRewriter
+{
+ dict<IdString, pair<AstNode*, AstNode*>> lookaheadids;
+
+ void collect_lookaheadids(AstNode *node)
+ {
+ if (node->lookahead) {
+ log_assert(node->type == AST_IDENTIFIER);
+ if (!lookaheadids.count(node->str)) {
+ AstNode *wire = new AstNode(AST_WIRE);
+ for (auto c : node->id2ast->children)
+ wire->children.push_back(c->clone());
+ wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++);
+ wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire->is_logic = true;
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+ current_ast_mod->children.push_back(wire);
+ lookaheadids[node->str] = make_pair(node->id2ast, wire);
+ wire->genRTLIL();
+ }
+ }
+
+ for (auto child : node->children)
+ collect_lookaheadids(child);
+ }
+
+ bool has_lookaheadids(AstNode *node)
+ {
+ if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) != 0)
+ return true;
+
+ for (auto child : node->children)
+ if (has_lookaheadids(child))
+ return true;
+
+ return false;
+ }
+
+ bool has_nonlookaheadids(AstNode *node)
+ {
+ if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) == 0)
+ return true;
+
+ for (auto child : node->children)
+ if (has_nonlookaheadids(child))
+ return true;
+
+ return false;
+ }
+
+ void rewrite_lookaheadids(AstNode *node, bool lhs = false)
+ {
+ if (node->type == AST_ASSIGN_LE)
+ {
+ if (has_lookaheadids(node->children[0]))
+ {
+ if (has_nonlookaheadids(node->children[0]))
+ log_error("incompatible mix of lookahead and non-lookahead IDs in LHS expression.\n");
+
+ rewrite_lookaheadids(node->children[0], true);
+ node->type = AST_ASSIGN_EQ;
+ }
+
+ rewrite_lookaheadids(node->children[1], lhs);
+ return;
+ }
+
+ if (node->type == AST_IDENTIFIER && (node->lookahead || lhs)) {
+ AstNode *newwire = lookaheadids.at(node->str).second;
+ node->str = newwire->str;
+ node->id2ast = newwire;
+ lhs = false;
+ }
+
+ for (auto child : node->children)
+ rewrite_lookaheadids(child, lhs);
+ }
+
+ LookaheadRewriter(AstNode *top)
+ {
+ // top->dumpAst(NULL, "REWRITE-BEFORE> ");
+ // top->dumpVlog(NULL, "REWRITE-BEFORE> ");
+
+ AstNode *block = nullptr;
+
+ for (auto c : top->children)
+ if (c->type == AST_BLOCK) {
+ log_assert(block == nullptr);
+ block = c;
+ }
+ log_assert(block != nullptr);
+
+ collect_lookaheadids(block);
+ rewrite_lookaheadids(block);
+
+ for (auto it : lookaheadids)
+ {
+ AstNode *ref_orig = new AstNode(AST_IDENTIFIER);
+ ref_orig->str = it.second.first->str;
+ ref_orig->id2ast = it.second.first;
+ ref_orig->was_checked = true;
+
+ AstNode *ref_temp = new AstNode(AST_IDENTIFIER);
+ ref_temp->str = it.second.second->str;
+ ref_temp->id2ast = it.second.second;
+ ref_temp->was_checked = true;
+
+ AstNode *init_assign = new AstNode(AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone());
+ AstNode *final_assign = new AstNode(AST_ASSIGN_LE, ref_orig, ref_temp);
+
+ block->children.insert(block->children.begin(), init_assign);
+ block->children.push_back(final_assign);
+ }
+
+ // top->dumpAst(NULL, "REWRITE-AFTER> ");
+ // top->dumpVlog(NULL, "REWRITE-AFTER> ");
+ }
+};
+
// helper class for converting AST always nodes to RTLIL processes
struct AST_INTERNAL::ProcessGenerator
{
@@ -191,6 +311,9 @@ struct AST_INTERNAL::ProcessGenerator
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg)
{
+ // rewrite lookahead references
+ LookaheadRewriter la_rewriter(always);
+
// generate process and simple root case
proc = new RTLIL::Process;
proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column);
@@ -338,7 +461,7 @@ struct AST_INTERNAL::ProcessGenerator
return chunks;
}
- // recursively traverse the AST an collect all assigned signals
+ // recursively traverse the AST and collect all assigned signals
void collect_lvalues(RTLIL::SigSpec &reg, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true)
{
switch (ast->type)
@@ -892,7 +1015,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// remember the parameter, needed for example in techmap
case AST_PARAMETER:
- current_module->avail_parameters.insert(str);
+ current_module->avail_parameters(str);
+ if (GetSize(children) >= 1 && children[0]->type == AST_CONSTANT) {
+ current_module->parameter_default_values[str] = children[0]->asParaConst();
+ }
/* fall through */
case AST_LOCALPARAM:
if (flag_pwires)
@@ -1010,7 +1136,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
int add_undef_bits_msb = 0;
int add_undef_bits_lsb = 0;
- if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {
+ log_assert(id2ast != nullptr);
+
+ if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) {
RTLIL::Wire *wire = current_module->addWire(str);
wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column);
wire->name = str;
@@ -1025,7 +1153,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk = RTLIL::Const(id2ast->children[0]->bits);
goto use_const_chunk;
}
- else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
+ else if ((id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
RTLIL::Wire *current_wire = current_module->wire(str);
if (current_wire->get_bool_attribute(ID::is_interface))
is_interface = true;
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 372dcf95c..837c14ad7 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -64,6 +64,21 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg
continue;
}
+ bool got_len = false;
+ bool got_zlen = false;
+ int len_value = 0;
+
+ while ('0' <= cformat && cformat <= '9')
+ {
+ if (!got_len && cformat == '0')
+ got_zlen = true;
+
+ got_len = true;
+ len_value = 10*len_value + (cformat - '0');
+
+ cformat = sformat[++i];
+ }
+
// Simplify the argument
AstNode *node_arg = nullptr;
@@ -74,6 +89,9 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg
case 'S':
case 'd':
case 'D':
+ if (got_len)
+ goto unsupported_format;
+ /* fall through */
case 'x':
case 'X':
if (next_arg >= GetSize(children))
@@ -88,9 +106,12 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg
case 'm':
case 'M':
+ if (got_len)
+ goto unsupported_format;
break;
default:
+ unsupported_format:
log_file_error(filename, location.first_line, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
break;
}
@@ -104,19 +125,28 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg
case 'd':
case 'D':
- {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int());
- sout += tmp;
- }
+ sout += stringf("%d", node_arg->bitsAsConst().as_int());
break;
case 'x':
case 'X':
{
- char tmp[128];
- snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int());
- sout += tmp;
+ Const val = node_arg->bitsAsConst();
+
+ while (GetSize(val) % 4 != 0)
+ val.bits.push_back(State::S0);
+
+ int len = GetSize(val) / 4;
+ for (int i = len; i < len_value; i++)
+ sout += got_zlen ? '0' : ' ';
+
+ for (int i = len-1; i >= 0; i--) {
+ Const digit = val.extract(4*i, 4);
+ if (digit.is_fully_def())
+ sout += stringf(cformat == 'x' ? "%x" : "%X", digit.as_int());
+ else
+ sout += cformat == 'x' ? "x" : "X";
+ }
}
break;
@@ -1724,8 +1754,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with
- // a big case block that selects the correct single-bit assignment.
- if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) {
+ // either a big case block that selects the correct single-bit assignment, or mask and
+ // shift operations.
+ if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)
+ {
if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0)
goto skip_dynamic_range_lvalue_expansion;
if (children[0]->children[0]->range_valid || did_something)
@@ -1734,10 +1766,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
goto skip_dynamic_range_lvalue_expansion;
if (!children[0]->id2ast->range_valid)
goto skip_dynamic_range_lvalue_expansion;
+
int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1;
int result_width = 1;
+
AstNode *shift_expr = NULL;
AstNode *range = children[0]->children[0];
+
if (range->children.size() == 1) {
shift_expr = range->children[0]->clone();
} else {
@@ -1750,19 +1785,72 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
}
- did_something = true;
- newNode = new AstNode(AST_CASE, shift_expr);
- for (int i = 0; i < source_width; i++) {
- int start_bit = children[0]->id2ast->range_right + i;
- AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
+
+ if (0)
+ {
+ // big case block
+
+ did_something = true;
+ newNode = new AstNode(AST_CASE, shift_expr);
+ for (int i = 0; i < source_width; i++) {
+ int start_bit = children[0]->id2ast->range_right + i;
+ AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
+ AstNode *lvalue = children[0]->clone();
+ lvalue->delete_children();
+ int end_bit = std::min(start_bit+result_width,source_width) - 1;
+ lvalue->children.push_back(new AstNode(AST_RANGE,
+ mkconst_int(end_bit, true), mkconst_int(start_bit, true)));
+ cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone())));
+ newNode->children.push_back(cond);
+ }
+ }
+ else
+ {
+ // mask and shift operations, disabled for now
+
+ AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true)));
+ wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", filename.c_str(), location.first_line, autoidx++);
+ wire_mask->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire_mask->is_logic = true;
+ while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { }
+ current_ast_mod->children.push_back(wire_mask);
+
+ AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true)));
+ wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", filename.c_str(), location.first_line, autoidx++);
+ wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire_data->is_logic = true;
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
+ current_ast_mod->children.push_back(wire_data);
+
+ did_something = true;
+ newNode = new AstNode(AST_BLOCK);
+
AstNode *lvalue = children[0]->clone();
lvalue->delete_children();
- int end_bit = std::min(start_bit+result_width,source_width) - 1;
- lvalue->children.push_back(new AstNode(AST_RANGE,
- mkconst_int(end_bit, true), mkconst_int(start_bit, true)));
- cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone())));
- newNode->children.push_back(cond);
+
+ AstNode *ref_mask = new AstNode(AST_IDENTIFIER);
+ ref_mask->str = wire_mask->str;
+ ref_mask->id2ast = wire_mask;
+ ref_mask->was_checked = true;
+
+ AstNode *ref_data = new AstNode(AST_IDENTIFIER);
+ ref_data->str = wire_data->str;
+ ref_data->id2ast = wire_data;
+ ref_data->was_checked = true;
+
+ AstNode *old_data = lvalue->clone();
+ if (type == AST_ASSIGN_LE)
+ old_data->lookahead = true;
+
+ AstNode *shamt = shift_expr;
+
+ newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_mask->clone(),
+ new AstNode(AST_SHIFT_LEFT, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), shamt->clone())));
+ newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_data->clone(),
+ new AstNode(AST_SHIFT_LEFT, new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone()), shamt)));
+ newNode->children.push_back(new AstNode(type, lvalue, new AstNode(AST_BIT_OR, new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)), ref_data)));
}
+
goto apply_newNode;
}
skip_dynamic_range_lvalue_expansion:;
diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y
index 0522fa72a..8e21fb176 100644
--- a/frontends/ilang/ilang_parser.y
+++ b/frontends/ilang/ilang_parser.y
@@ -143,11 +143,18 @@ module_body:
/* empty */;
module_stmt:
- param_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
+ param_stmt | param_defval_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
param_stmt:
TOK_PARAMETER TOK_ID EOL {
- current_module->avail_parameters.insert($2);
+ current_module->avail_parameters($2);
+ free($2);
+ };
+
+param_defval_stmt:
+ TOK_PARAMETER TOK_ID constant EOL {
+ current_module->avail_parameters($2);
+ current_module->parameter_default_values[$2] = *$3;
free($2);
};
diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc
index a23f7548e..46ee6a733 100644
--- a/frontends/rpc/rpc_frontend.cc
+++ b/frontends/rpc/rpc_frontend.cc
@@ -217,6 +217,8 @@ struct RpcModule : RTLIL::Module {
module.second->name = mangled_name;
module.second->design = design;
module.second->attributes.erase(ID::top);
+ if (!module.second->has_attribute(ID::hdlname))
+ module.second->set_string_attribute(ID::hdlname, module.first.str());
design->modules_[mangled_name] = module.second;
derived_design->modules_.erase(module.first);
}
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 76373c2e4..4a5aba79e 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -1924,11 +1924,13 @@ always_events:
always_event:
TOK_POSEDGE expr {
AstNode *node = new AstNode(AST_POSEDGE);
+ SET_AST_NODE_LOC(node, @1, @1);
ast_stack.back()->children.push_back(node);
node->children.push_back($2);
} |
TOK_NEGEDGE expr {
AstNode *node = new AstNode(AST_NEGEDGE);
+ SET_AST_NODE_LOC(node, @1, @1);
ast_stack.back()->children.push_back(node);
node->children.push_back($2);
} |
@@ -2244,6 +2246,7 @@ behavioral_stmt:
exitTypeScope();
if ($4 != NULL && $8 != NULL && *$4 != *$8)
frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1);
+ SET_AST_NODE_LOC(ast_stack.back(), @2, @8);
delete $4;
delete $8;
ast_stack.pop_back();
@@ -2618,6 +2621,7 @@ basic_expr:
bits->str = *$1;
SET_AST_NODE_LOC(bits, @1, @1);
AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
+ SET_AST_NODE_LOC(val, @2, @2);
if (val == NULL)
log_error("Value conversion failed: `%s'\n", $2->c_str());
$$ = new AstNode(AST_TO_BITS, bits, val);
@@ -2626,6 +2630,7 @@ basic_expr:
} |
integral_number {
$$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
+ SET_AST_NODE_LOC($$, @1, @1);
if ($$ == NULL)
log_error("Value conversion failed: `%s'\n", $1->c_str());
delete $1;
@@ -2644,6 +2649,7 @@ basic_expr:
} |
TOK_STRING {
$$ = AstNode::mkconst_str(*$1);
+ SET_AST_NODE_LOC($$, @1, @1);
delete $1;
} |
hierarchical_id attr {
diff --git a/kernel/hashlib.h b/kernel/hashlib.h
index 996bda38e..97fadea0e 100644
--- a/kernel/hashlib.h
+++ b/kernel/hashlib.h
@@ -569,7 +569,7 @@ public:
return entries[i].udata.second;
}
- T at(const K &key, const T &defval) const
+ const T& at(const K &key, const T &defval) const
{
int hash = do_hash(key);
int i = do_lookup(key, hash);
@@ -961,7 +961,21 @@ class idict
pool<K, OPS> database;
public:
- typedef typename pool<K, OPS>::const_iterator const_iterator;
+ class const_iterator : public std::iterator<std::forward_iterator_tag, K>
+ {
+ friend class idict;
+ protected:
+ const idict &container;
+ int index;
+ const_iterator(const idict &container, int index) : container(container), index(index) { }
+ public:
+ const_iterator() { }
+ const_iterator operator++() { index++; return *this; }
+ bool operator==(const const_iterator &other) const { return index == other.index; }
+ bool operator!=(const const_iterator &other) const { return index != other.index; }
+ const K &operator*() const { return container[index]; }
+ const K *operator->() const { return &container[index]; }
+ };
int operator()(const K &key)
{
@@ -1019,9 +1033,9 @@ public:
bool empty() const { return database.empty(); }
void clear() { database.clear(); }
- const_iterator begin() const { return database.begin(); }
- const_iterator element(int n) const { return database.element(n); }
- const_iterator end() const { return database.end(); }
+ const_iterator begin() const { return const_iterator(*this, offset); }
+ const_iterator element(int n) const { return const_iterator(*this, n); }
+ const_iterator end() const { return const_iterator(*this, offset + size()); }
};
template<typename K, typename OPS>
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index 6996a02c4..2aefe30b1 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -273,6 +273,11 @@ bool RTLIL::Const::is_fully_undef() const
return true;
}
+bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const
+{
+ return attributes.count(id);
+}
+
void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value)
{
if (value)
@@ -289,6 +294,23 @@ bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const
return it->second.as_bool();
}
+void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value)
+{
+ if (value.empty())
+ attributes.erase(id);
+ else
+ attributes[id] = value;
+}
+
+string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const
+{
+ std::string value;
+ const auto it = attributes.find(id);
+ if (it != attributes.end())
+ value = it->second.decode_string();
+ return value;
+}
+
void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data)
{
string attrval;
@@ -317,23 +339,6 @@ pool<string> RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const
return data;
}
-void RTLIL::AttrObject::set_src_attribute(const std::string &src)
-{
- if (src.empty())
- attributes.erase(ID::src);
- else
- attributes[ID::src] = src;
-}
-
-std::string RTLIL::AttrObject::get_src_attribute() const
-{
- std::string src;
- const auto it = attributes.find(ID::src);
- if (it != attributes.end())
- src = it->second.decode_string();
- return src;
-}
-
bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const
{
if (full_selection)
@@ -1384,7 +1389,7 @@ void RTLIL::Module::sort()
{
wires_.sort(sort_by_id_str());
cells_.sort(sort_by_id_str());
- avail_parameters.sort(sort_by_id_str());
+ parameter_default_values.sort(sort_by_id_str());
memories.sort(sort_by_id_str());
processes.sort(sort_by_id_str());
for (auto &it : cells_)
@@ -1503,6 +1508,7 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod) const
log_assert(new_mod->refcount_cells_ == 0);
new_mod->avail_parameters = avail_parameters;
+ new_mod->parameter_default_values = parameter_default_values;
for (auto &conn : connections_)
new_mod->connect(conn);
@@ -2613,7 +2619,16 @@ void RTLIL::Cell::setParam(RTLIL::IdString paramname, RTLIL::Const value)
const RTLIL::Const &RTLIL::Cell::getParam(RTLIL::IdString paramname) const
{
- return parameters.at(paramname);
+ static const RTLIL::Const empty;
+ const auto &it = parameters.find(paramname);
+ if (it != parameters.end())
+ return it->second;
+ if (module && module->design) {
+ RTLIL::Module *m = module->design->module(type);
+ if (m)
+ return m->parameter_default_values.at(paramname, empty);
+ }
+ return empty;
}
void RTLIL::Cell::sort()
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 17f038e36..11c45bbec 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -656,6 +656,8 @@ struct RTLIL::AttrObject
{
dict<RTLIL::IdString, RTLIL::Const> attributes;
+ bool has_attribute(RTLIL::IdString id) const;
+
void set_bool_attribute(RTLIL::IdString id, bool value=true);
bool get_bool_attribute(RTLIL::IdString id) const;
@@ -663,12 +665,19 @@ struct RTLIL::AttrObject
return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox));
}
+ void set_string_attribute(RTLIL::IdString id, string value);
+ string get_string_attribute(RTLIL::IdString id) const;
+
void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
pool<string> get_strpool_attribute(RTLIL::IdString id) const;
- void set_src_attribute(const std::string &src);
- std::string get_src_attribute() const;
+ void set_src_attribute(const std::string &src) {
+ set_string_attribute(ID::src, src);
+ }
+ std::string get_src_attribute() const {
+ return get_string_attribute(ID::src);
+ }
};
struct RTLIL::SigChunk
@@ -1082,7 +1091,8 @@ public:
std::vector<RTLIL::SigSig> connections_;
RTLIL::IdString name;
- pool<RTLIL::IdString> avail_parameters;
+ idict<RTLIL::IdString> avail_parameters;
+ dict<RTLIL::IdString, RTLIL::Const> parameter_default_values;
dict<RTLIL::IdString, RTLIL::Memory*> memories;
dict<RTLIL::IdString, RTLIL::Process*> processes;
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
index ad6a07fa0..a75927393 100644
--- a/passes/cmds/bugpoint.cc
+++ b/passes/cmds/bugpoint.cc
@@ -133,6 +133,7 @@ struct BugpointPass : public Pass {
int index = 0;
if (modules)
{
+ Module *removed_module = nullptr;
for (auto module : design_copy->modules())
{
if (module->get_blackbox_attribute())
@@ -141,10 +142,14 @@ struct BugpointPass : public Pass {
if (index++ == seed)
{
log("Trying to remove module %s.\n", module->name.c_str());
- design_copy->remove(module);
- return design_copy;
+ removed_module = module;
+ break;
}
}
+ if (removed_module) {
+ design_copy->remove(removed_module);
+ return design_copy;
+ }
}
if (ports)
{
@@ -178,15 +183,20 @@ struct BugpointPass : public Pass {
if (mod->get_blackbox_attribute())
continue;
+ Cell *removed_cell = nullptr;
for (auto cell : mod->cells())
{
if (index++ == seed)
{
log("Trying to remove cell %s.%s.\n", mod->name.c_str(), cell->name.c_str());
- mod->remove(cell);
- return design_copy;
+ removed_cell = cell;
+ break;
}
}
+ if (removed_cell) {
+ mod->remove(removed_cell);
+ return design_copy;
+ }
}
}
if (connections)
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index 9b1830b7b..7d6d84d42 100644
--- a/passes/cmds/rename.cc
+++ b/passes/cmds/rename.cc
@@ -32,27 +32,27 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
if (module->count_id(to_name))
log_cmd_error("There is already an object `%s' in module `%s'.\n", to_name.c_str(), module->name.c_str());
- for (auto &it : module->wires_)
- if (it.first == from_name) {
- Wire *w = it.second;
- log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
- module->rename(w, to_name);
- if (w->port_id || flag_output) {
- if (flag_output)
- w->port_output = true;
- module->fixup_ports();
- }
- return;
- }
+ RTLIL::Wire *wire_to_rename = module->wire(from_name);
+ RTLIL::Cell *cell_to_rename = module->cell(from_name);
- for (auto &it : module->cells_)
- if (it.first == from_name) {
+ if (wire_to_rename != nullptr) {
+ log("Renaming wire %s to %s in module %s.\n", log_id(wire_to_rename), log_id(to_name), log_id(module));
+ module->rename(wire_to_rename, to_name);
+ if (wire_to_rename->port_id || flag_output) {
if (flag_output)
- log_cmd_error("Called with -output but the specified object is a cell.\n");
- log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
- module->rename(it.second, to_name);
- return;
+ wire_to_rename->port_output = true;
+ module->fixup_ports();
}
+ return;
+ }
+
+ if (cell_to_rename != nullptr) {
+ if (flag_output)
+ log_cmd_error("Called with -output but the specified object is a cell.\n");
+ log("Renaming cell %s to %s in module %s.\n", log_id(cell_to_rename), log_id(to_name), log_id(module));
+ module->rename(cell_to_rename, to_name);
+ return;
+ }
log_cmd_error("Object `%s' not found!\n", from_name.c_str());
}
@@ -66,26 +66,26 @@ static std::string derive_name_from_src(const std::string &src, int counter)
return stringf("\\%s$%d", src_base.c_str(), counter);
}
-static IdString derive_name_from_wire(const RTLIL::Cell &cell)
+static IdString derive_name_from_cell_output_wire(const RTLIL::Cell *cell)
{
// Find output
const SigSpec *output = nullptr;
int num_outputs = 0;
- for (auto &connection : cell.connections()) {
- if (cell.output(connection.first)) {
+ for (auto &connection : cell->connections()) {
+ if (cell->output(connection.first)) {
output = &connection.second;
num_outputs++;
}
}
if (num_outputs != 1) // Skip cells thad drive multiple outputs
- return cell.name;
+ return cell->name;
std::string name = "";
for (auto &chunk : output->chunks()) {
// Skip cells that drive privately named wires
if (!chunk.wire || chunk.wire->name.str()[0] == '$')
- return cell.name;
+ return cell->name;
if (name != "")
name += "$";
@@ -99,7 +99,7 @@ static IdString derive_name_from_wire(const RTLIL::Cell &cell)
}
}
- return name + cell.type.str();
+ return name + cell->type.str();
}
struct RenamePass : public Pass {
@@ -210,30 +210,25 @@ struct RenamePass : public Pass {
{
extra_args(args, argidx, design);
- for (auto &mod : design->modules_)
+ for (auto module : design->selected_modules())
{
int counter = 0;
+ dict<RTLIL::Wire *, IdString> new_wire_names;
+ dict<RTLIL::Cell *, IdString> new_cell_names;
+
+ for (auto wire : module->selected_wires())
+ if (wire->name[0] == '$')
+ new_wire_names.emplace(wire, derive_name_from_src(wire->get_src_attribute(), counter++));
+
+ for (auto cell : module->selected_cells())
+ if (cell->name[0] == '$')
+ new_cell_names.emplace(cell, derive_name_from_src(cell->get_src_attribute(), counter++));
- RTLIL::Module *module = mod.second;
- if (!design->selected(module))
- continue;
-
- dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
- for (auto &it : module->wires_) {
- if (it.first[0] == '$' && design->selected(module, it.second))
- it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
- new_wires[it.second->name] = it.second;
- }
- module->wires_.swap(new_wires);
- module->fixup_ports();
-
- dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
- for (auto &it : module->cells_) {
- if (it.first[0] == '$' && design->selected(module, it.second))
- it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
- new_cells[it.second->name] = it.second;
- }
- module->cells_.swap(new_cells);
+ for (auto &it : new_wire_names)
+ module->rename(it.first, it.second);
+
+ for (auto &it : new_cell_names)
+ module->rename(it.first, it.second);
}
}
else
@@ -241,19 +236,13 @@ struct RenamePass : public Pass {
{
extra_args(args, argidx, design);
- for (auto &mod : design->modules_)
- {
- RTLIL::Module *module = mod.second;
- if (!design->selected(module))
- continue;
-
- dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
- for (auto &it : module->cells_) {
- if (it.first[0] == '$' && design->selected(module, it.second))
- it.second->name = derive_name_from_wire(*it.second);
- new_cells[it.second->name] = it.second;
- }
- module->cells_.swap(new_cells);
+ for (auto module : design->selected_modules()) {
+ dict<RTLIL::Cell *, IdString> new_cell_names;
+ for (auto cell : module->selected_cells())
+ if (cell->name[0] == '$')
+ new_cell_names[cell] = derive_name_from_cell_output_wire(cell);
+ for (auto &it : new_cell_names)
+ module->rename(it.first, it.second);
}
}
else
@@ -261,32 +250,33 @@ struct RenamePass : public Pass {
{
extra_args(args, argidx, design);
- for (auto &mod : design->modules_)
+ for (auto module : design->selected_modules())
{
int counter = 0;
+ dict<RTLIL::Wire *, IdString> new_wire_names;
+ dict<RTLIL::Cell *, IdString> new_cell_names;
+
+ for (auto wire : module->selected_wires())
+ if (wire->name[0] == '$') {
+ RTLIL::IdString buf;
+ do buf = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str());
+ while (module->wire(buf) != nullptr);
+ new_wire_names[wire] = buf;
+ }
+
+ for (auto cell : module->selected_cells())
+ if (cell->name[0] == '$') {
+ RTLIL::IdString buf;
+ do buf = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str());
+ while (module->cell(buf) != nullptr);
+ new_cell_names[cell] = buf;
+ }
- RTLIL::Module *module = mod.second;
- if (!design->selected(module))
- continue;
-
- dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
- for (auto &it : module->wires_) {
- if (it.first[0] == '$' && design->selected(module, it.second))
- do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str());
- while (module->count_id(it.second->name) > 0);
- new_wires[it.second->name] = it.second;
- }
- module->wires_.swap(new_wires);
- module->fixup_ports();
-
- dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
- for (auto &it : module->cells_) {
- if (it.first[0] == '$' && design->selected(module, it.second))
- do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str());
- while (module->count_id(it.second->name) > 0);
- new_cells[it.second->name] = it.second;
- }
- module->cells_.swap(new_cells);
+ for (auto &it : new_wire_names)
+ module->rename(it.first, it.second);
+
+ for (auto &it : new_cell_names)
+ module->rename(it.first, it.second);
}
}
else
@@ -294,30 +284,24 @@ struct RenamePass : public Pass {
{
extra_args(args, argidx, design);
- for (auto &mod : design->modules_)
+ for (auto module : design->selected_modules())
{
- RTLIL::Module *module = mod.second;
- if (!design->selected(module))
- continue;
-
- dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
- for (auto &it : module->wires_) {
- if (design->selected(module, it.second))
- if (it.first[0] == '\\' && it.second->port_id == 0)
- it.second->name = NEW_ID;
- new_wires[it.second->name] = it.second;
- }
- module->wires_.swap(new_wires);
- module->fixup_ports();
-
- dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
- for (auto &it : module->cells_) {
- if (design->selected(module, it.second))
- if (it.first[0] == '\\')
- it.second->name = NEW_ID;
- new_cells[it.second->name] = it.second;
- }
- module->cells_.swap(new_cells);
+ dict<RTLIL::Wire *, IdString> new_wire_names;
+ dict<RTLIL::Cell *, IdString> new_cell_names;
+
+ for (auto wire : module->selected_wires())
+ if (wire->name[0] == '\\' && wire->port_id == 0)
+ new_wire_names[wire] = NEW_ID;
+
+ for (auto cell : module->selected_cells())
+ if (cell->name[0] == '\\')
+ new_cell_names[cell] = NEW_ID;
+
+ for (auto &it : new_wire_names)
+ module->rename(it.first, it.second);
+
+ for (auto &it : new_cell_names)
+ module->rename(it.first, it.second);
}
}
else
@@ -329,7 +313,7 @@ struct RenamePass : public Pass {
IdString new_name = RTLIL::escape_id(args[argidx]);
RTLIL::Module *module = design->top_module();
- if (module == NULL)
+ if (module == nullptr)
log_cmd_error("No top module found!\n");
log("Renaming module %s to %s.\n", log_id(module), log_id(new_name));
@@ -345,27 +329,27 @@ struct RenamePass : public Pass {
if (!design->selected_active_module.empty())
{
- if (design->modules_.count(design->selected_active_module) > 0)
- rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
+ if (design->module(design->selected_active_module) != nullptr)
+ rename_in_module(design->module(design->selected_active_module), from_name, to_name, flag_output);
}
else
{
if (flag_output)
log_cmd_error("Mode -output requires that there is an active module selected.\n");
- for (auto &mod : design->modules_) {
- if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
- to_name = RTLIL::escape_id(to_name);
- log("Renaming module %s to %s.\n", mod.first.c_str(), to_name.c_str());
- RTLIL::Module *module = mod.second;
- design->modules_.erase(module->name);
- module->name = to_name;
- design->modules_[module->name] = module;
- goto rename_ok;
+
+ RTLIL::Module *module_to_rename = nullptr;
+ for (auto module : design->modules())
+ if (module->name == from_name || RTLIL::unescape_id(module->name) == from_name) {
+ module_to_rename = module;
+ break;
}
- }
- log_cmd_error("Object `%s' not found!\n", from_name.c_str());
- rename_ok:;
+ if (module_to_rename != nullptr) {
+ to_name = RTLIL::escape_id(to_name);
+ log("Renaming module %s to %s.\n", module_to_rename->name.c_str(), to_name.c_str());
+ design->rename(module_to_rename, to_name);
+ } else
+ log_cmd_error("Object `%s' not found!\n", from_name.c_str());
}
}
}
diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc
index 7123ba9fb..a5ef95f02 100644
--- a/passes/cmds/scatter.cc
+++ b/passes/cmds/scatter.cc
@@ -46,25 +46,19 @@ struct ScatterPass : public Pass {
CellTypes ct(design);
extra_args(args, 1, design);
- for (auto &mod_it : design->modules_)
+ for (auto module : design->selected_modules())
{
- if (!design->selected(mod_it.second))
- continue;
-
- for (auto &c : mod_it.second->cells_)
- for (auto &p : c.second->connections_)
- {
- RTLIL::Wire *wire = mod_it.second->addWire(NEW_ID, p.second.size());
-
- if (ct.cell_output(c.second->type, p.first)) {
- RTLIL::SigSig sigsig(p.second, wire);
- mod_it.second->connect(sigsig);
- } else {
- RTLIL::SigSig sigsig(wire, p.second);
- mod_it.second->connect(sigsig);
+ for (auto cell : module->cells()) {
+ dict<RTLIL::IdString, RTLIL::SigSig> new_connections;
+ for (auto conn : cell->connections())
+ new_connections.emplace(conn.first, RTLIL::SigSig(conn.second, module->addWire(NEW_ID, GetSize(conn.second))));
+ for (auto &it : new_connections) {
+ if (ct.cell_output(cell->type, it.first))
+ module->connect(RTLIL::SigSig(it.second.first, it.second.second));
+ else
+ module->connect(RTLIL::SigSig(it.second.second, it.second.first));
+ cell->setPort(it.first, it.second.second);
}
-
- p.second = wire;
}
}
}
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index b64b077e4..c04ff438a 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -1007,6 +1007,7 @@ struct SelectPass : public Pass {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n");
+ log(" select [ -unset <name> ]\n");
log(" select [ <assert_option> ] {-read <filename> | <selection>}\n");
log(" select [ -list | -write <filename> | -count | -clear ]\n");
log(" select -module <modname>\n");
@@ -1029,6 +1030,10 @@ struct SelectPass : public Pass {
log(" under the given name (see @<name> below). to save the current selection,\n");
log(" use \"select -set <name> %%\"\n");
log("\n");
+ log(" -unset <name>\n");
+ log(" do not modify the current selection. instead remove a previously saved\n");
+ log(" selection under the given name (see @<name> below).");
+ log("\n");
log(" -assert-none\n");
log(" do not modify the current selection. instead assert that the given\n");
log(" selection is empty. i.e. produce an error if any object matching the\n");
@@ -1238,7 +1243,7 @@ struct SelectPass : public Pass {
int assert_max = -1;
int assert_min = -1;
std::string write_file, read_file;
- std::string set_name, sel_str;
+ std::string set_name, unset_name, sel_str;
work_stack.clear();
@@ -1310,6 +1315,10 @@ struct SelectPass : public Pass {
set_name = RTLIL::escape_id(args[++argidx]);
continue;
}
+ if (arg == "-unset" && argidx+1 < args.size()) {
+ unset_name = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
if (arg.size() > 0 && arg[0] == '-')
log_cmd_error("Unknown option %s.\n", arg.c_str());
bool disable_empty_warning = count_mode || assert_none || assert_any || (assert_count != -1) || (assert_max != -1) || (assert_min != -1);
@@ -1358,8 +1367,11 @@ struct SelectPass : public Pass {
if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n");
- if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
- log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
+ if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || !unset_name.empty() || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
+ log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -unset, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
+
+ if (!unset_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || !set_name.empty() || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
+ log_cmd_error("Option -unset can not be combined with -list, -write, -count, -add, -del, -set, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
if (work_stack.size() == 0 && got_module) {
RTLIL::Selection sel;
@@ -1527,6 +1539,13 @@ struct SelectPass : public Pass {
return;
}
+ if (!unset_name.empty())
+ {
+ if (!design->selection_vars.erase(unset_name))
+ log_error("Selection '%s' does not exist!\n", unset_name.c_str());
+ return;
+ }
+
if (work_stack.size() == 0) {
RTLIL::Selection &sel = design->selection_stack.back();
if (sel.full_selection)
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 3880b19fe..95d74d1eb 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -334,10 +334,16 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first));
}
- for (auto &param : cell->parameters)
- if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
+ for (auto &param : cell->parameters) {
+ if (param.first[0] == '$' && '0' <= param.first[1] && param.first[1] <= '9') {
+ int id = atoi(param.first.c_str()+1);
+ if (id <= 0 || id > GetSize(mod->avail_parameters))
+ log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d parameters, requested parameter %d.\n",
+ log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->avail_parameters), id);
+ } else if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(param.first));
+ }
}
}
@@ -939,7 +945,8 @@ struct HierarchyPass : public Pass {
for (auto mod : design->modules())
for (auto cell : mod->cells()) {
- if (design->module(cell->type) == nullptr)
+ RTLIL::Module *cell_mod = design->module(cell->type);
+ if (cell_mod == nullptr)
continue;
for (auto &conn : cell->connections())
if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
@@ -947,6 +954,23 @@ struct HierarchyPass : public Pass {
pos_work.push_back(std::pair<RTLIL::Module*,RTLIL::Cell*>(mod, cell));
break;
}
+
+ pool<std::pair<IdString, IdString>> params_rename;
+ for (const auto &p : cell->parameters) {
+ if (p.first[0] == '$' && '0' <= p.first[1] && p.first[1] <= '9') {
+ int id = atoi(p.first.c_str()+1);
+ if (id <= 0 || id > GetSize(cell_mod->avail_parameters)) {
+ log(" Failed to map positional parameter %d of cell %s.%s (%s).\n",
+ id, RTLIL::id2cstr(mod->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ } else {
+ params_rename.insert(std::make_pair(p.first, cell_mod->avail_parameters[id - 1]));
+ }
+ }
+ }
+ for (const auto &p : params_rename) {
+ cell->setParam(p.second, cell->getParam(p.first));
+ cell->unsetParam(p.first);
+ }
}
for (auto module : pos_mods)
diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc
index 44691425f..981271770 100644
--- a/passes/sat/qbfsat.cc
+++ b/passes/sat/qbfsat.cc
@@ -39,7 +39,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct QbfSolutionType {
- std::vector<std::string> stdout;
+ std::vector<std::string> stdout_lines;
dict<std::string, std::string> hole_to_value;
bool sat;
bool unknown; //true if neither 'sat' nor 'unsat'
@@ -72,7 +72,7 @@ void recover_solution(QbfSolutionType &sol) {
bool sat_regex_found = false;
bool unsat_regex_found = false;
dict<std::string, bool> hole_value_recovered;
- for (const std::string &x : sol.stdout) {
+ for (const std::string &x : sol.stdout_lines) {
if(YS_REGEX_NS::regex_search(x, m, hole_value_regex)) {
std::string loc = m[1].str();
std::string val = m[2].str();
@@ -294,7 +294,7 @@ QbfSolutionType qbf_solve(RTLIL::Module *mod, const QbfSolveOptions &opt) {
{
const std::string cmd = yosys_smtbmc_exe + " -s z3 -t 1 -g --binary " + (opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file + " " : "") + tempdir_name + "/problem.smt2 2>&1";
auto process_line = [&ret, &smtbmc_warning, &show_smtbmc](const std::string &line) {
- ret.stdout.push_back(line.substr(0, line.size()-1)); //don't include trailing newline
+ ret.stdout_lines.push_back(line.substr(0, line.size()-1)); //don't include trailing newline
auto warning_pos = line.find(smtbmc_warning);
if (warning_pos != std::string::npos)
log_warning("%s", line.substr(warning_pos + smtbmc_warning.size() + 1).c_str());
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index 59bf5a712..03ca42cf3 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -128,8 +128,12 @@ struct SimInstance
for (auto &port : cell->connections()) {
if (cell->input(port.first))
- for (auto bit : sigmap(port.second))
+ for (auto bit : sigmap(port.second)) {
upd_cells[bit].insert(cell);
+ // Make sure cell inputs connected to constants are updated in the first cycle
+ if (bit.wire == nullptr)
+ dirty_bits.insert(bit);
+ }
}
if (cell->type.in(ID($dff))) {
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index 0ee495abd..aff0baa44 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -160,7 +160,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1,
void mark_port(RTLIL::SigSpec sig)
{
for (auto &bit : assign_map(sig))
- if (bit.wire != NULL && signal_map.count(bit) > 0)
+ if (bit.wire != nullptr && signal_map.count(bit) > 0)
signal_list[signal_map[bit]].is_port = true;
}
@@ -197,7 +197,7 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
if (keepff)
for (auto &c : sig_q.chunks())
- if (c.wire != NULL)
+ if (c.wire != nullptr)
c.wire->attributes[ID::keep] = 1;
assign_map.apply(sig_d);
@@ -370,7 +370,7 @@ std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullp
void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts)
{
- if (f == NULL)
+ if (f == nullptr)
return;
log("Dumping loop state graph to slide %d.\n", ++nr);
@@ -406,7 +406,7 @@ void handle_loops()
std::vector<int> in_edges_count(signal_list.size());
std::set<int> workpool;
- FILE *dot_f = NULL;
+ FILE *dot_f = nullptr;
int dot_nr = 0;
// uncomment for troubleshooting the loop detection code
@@ -464,9 +464,9 @@ void handle_loops()
int id2 = edge_it.first;
RTLIL::Wire *w1 = signal_list[id1].bit.wire;
RTLIL::Wire *w2 = signal_list[id2].bit.wire;
- if (w1 == NULL)
+ if (w1 == nullptr)
id1 = id2;
- else if (w2 == NULL)
+ else if (w2 == nullptr)
continue;
else if (w1->name[0] == '$' && w2->name[0] == '\\')
id1 = id2;
@@ -485,7 +485,7 @@ void handle_loops()
continue;
}
- log_assert(signal_list[id1].bit.wire != NULL);
+ log_assert(signal_list[id1].bit.wire != nullptr);
std::stringstream sstr;
sstr << "$abcloop$" << (autoidx++);
@@ -526,7 +526,7 @@ void handle_loops()
}
}
- if (dot_f != NULL)
+ if (dot_f != nullptr)
fclose(dot_f);
}
@@ -688,15 +688,15 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
en_polarity = false;
en_str = en_str.substr(1);
}
- if (module->wires_.count(RTLIL::escape_id(en_str)) != 0)
- en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0));
+ if (module->wire(RTLIL::escape_id(en_str)) != nullptr)
+ en_sig = assign_map(module->wire(RTLIL::escape_id(en_str)));
}
if (clk_str[0] == '!') {
clk_polarity = false;
clk_str = clk_str.substr(1);
}
- if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0)
- clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0));
+ if (module->wire(RTLIL::escape_id(clk_str)) != nullptr)
+ clk_sig = assign_map(module->wire(RTLIL::escape_id(clk_str)));
}
if (dff_mode && clk_sig.empty())
@@ -790,13 +790,13 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto c : cells)
extract_cell(c, keepff);
- for (auto &wire_it : module->wires_) {
- if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute(ID::keep))
- mark_port(RTLIL::SigSpec(wire_it.second));
+ for (auto wire : module->wires()) {
+ if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep))
+ mark_port(wire);
}
- for (auto &cell_it : module->cells_)
- for (auto &port_it : cell_it.second->connections())
+ for (auto cell : module->cells())
+ for (auto &port_it : cell->connections())
mark_port(port_it.second);
if (clk_sig.size() != 0)
@@ -809,7 +809,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
std::string buffer = stringf("%s/input.blif", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
- if (f == NULL)
+ if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
fprintf(f, ".model netlist\n");
@@ -840,7 +840,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));
for (auto &si : signal_list) {
- if (si.bit.wire == NULL) {
+ if (si.bit.wire == nullptr) {
fprintf(f, ".names ys__n%d\n", si.id);
if (si.bit == RTLIL::State::S1)
fprintf(f, "1\n");
@@ -936,7 +936,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
- if (f == NULL)
+ if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
fprintf(f, "GATE ZERO 1 Y=CONST0;\n");
fprintf(f, "GATE ONE 1 Y=CONST1;\n");
@@ -981,7 +981,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (!lut_costs.empty()) {
buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
- if (f == NULL)
+ if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
for (int i = 0; i < GetSize(lut_costs); i++)
fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
@@ -1025,11 +1025,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
ifs.close();
log_header(design, "Re-integrating ABC results.\n");
- RTLIL::Module *mapped_mod = mapped_design->modules_[ID(netlist)];
- if (mapped_mod == NULL)
+ RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist));
+ if (mapped_mod == nullptr)
log_error("ABC output file does not contain a module `netlist'.\n");
- for (auto &it : mapped_mod->wires_) {
- RTLIL::Wire *w = it.second;
+ for (auto w : mapped_mod->wires()) {
RTLIL::Wire *orig_wire = nullptr;
RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
if (orig_wire != nullptr && orig_wire->attributes.count(ID::src))
@@ -1046,121 +1045,99 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
cell_stats[RTLIL::unescape_id(c->type)]++;
if (c->type.in(ID(ZERO), ID(ONE))) {
RTLIL::SigSig conn;
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]);
+ RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name);
+ conn.first = module->wire(name_y);
conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1);
module->connect(conn);
continue;
}
if (c->type == ID(BUF)) {
RTLIL::SigSig conn;
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]);
- conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]);
+ RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name);
+ RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name);
+ conn.first = module->wire(name_y);
+ conn.second = module->wire(name_a);
module->connect(conn);
continue;
}
if (c->type == ID(NOT)) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type.in(ID(MUX), ID(NMUX))) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::S, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type == ID(MUX4)) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)]));
- cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)]));
- cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)]));
- cell->setPort(ID::T, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::T).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type == ID(MUX8)) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)]));
- cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)]));
- cell->setPort(ID::E, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::E).as_wire()->name)]));
- cell->setPort(ID::F, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::F).as_wire()->name)]));
- cell->setPort(ID::G, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::G).as_wire()->name)]));
- cell->setPort(ID::H, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::H).as_wire()->name)]));
- cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)]));
- cell->setPort(ID::T, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::T).as_wire()->name)]));
- cell->setPort(ID::U, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::U).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type == ID(MUX16)) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)]));
- cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)]));
- cell->setPort(ID::E, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::E).as_wire()->name)]));
- cell->setPort(ID::F, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::F).as_wire()->name)]));
- cell->setPort(ID::G, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::G).as_wire()->name)]));
- cell->setPort(ID::H, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::H).as_wire()->name)]));
- cell->setPort(ID::I, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::I).as_wire()->name)]));
- cell->setPort(ID::J, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::J).as_wire()->name)]));
- cell->setPort(ID::K, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::K).as_wire()->name)]));
- cell->setPort(ID::L, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::L).as_wire()->name)]));
- cell->setPort(ID::M, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::M).as_wire()->name)]));
- cell->setPort(ID::N, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::N).as_wire()->name)]));
- cell->setPort(ID::O, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::O).as_wire()->name)]));
- cell->setPort(ID::P, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::P).as_wire()->name)]));
- cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)]));
- cell->setPort(ID::T, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::T).as_wire()->name)]));
- cell->setPort(ID::U, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::U).as_wire()->name)]));
- cell->setPort(ID::V, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::V).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K,
+ ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type.in(ID(AOI3), ID(OAI3))) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::C, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
if (c->type.in(ID(AOI4), ID(OAI4))) {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
- cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
- cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)]));
- cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)]));
- cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
+ for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::Y}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
design->select(module, cell);
continue;
}
@@ -1175,8 +1152,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
cell->setPort(ID::E, en_sig);
}
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)]));
- cell->setPort(ID::Q, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Q).as_wire()->name)]));
+ for (auto name : {ID::D, ID::Q}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
cell->setPort(ID::C, clk_sig);
design->select(module, cell);
continue;
@@ -1187,7 +1166,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (c->type.in(ID(_const0_), ID(_const1_))) {
RTLIL::SigSig conn;
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]);
+ conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name));
conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1);
module->connect(conn);
continue;
@@ -1204,16 +1183,18 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
cell->setPort(ID::E, en_sig);
}
if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx;
- cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)]));
- cell->setPort(ID::Q, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Q).as_wire()->name)]));
+ for (auto name : {ID::D, ID::Q}) {
+ RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name);
+ cell->setPort(name, module->wire(remapped_name));
+ }
cell->setPort(ID::C, clk_sig);
design->select(module, cell);
continue;
}
if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) {
- SigSpec my_a = module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)];
- SigSpec my_y = module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)];
+ SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name));
+ SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name));
module->connect(my_y, my_a);
continue;
}
@@ -1227,7 +1208,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (c.width == 0)
continue;
log_assert(c.width == 1);
- newsig.append(module->wires_[remap_name(c.wire->name)]);
+ newsig.append(module->wire(remap_name(c.wire->name)));
}
cell->setPort(conn.first, newsig);
}
@@ -1236,16 +1217,16 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto conn : mapped_mod->connections()) {
if (!conn.first.is_fully_const())
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(conn.first.as_wire()->name)]);
+ conn.first = module->wire(remap_name(conn.first.as_wire()->name));
if (!conn.second.is_fully_const())
- conn.second = RTLIL::SigSpec(module->wires_[remap_name(conn.second.as_wire()->name)]);
+ conn.second = module->wire(remap_name(conn.second.as_wire()->name));
module->connect(conn);
}
if (recover_init)
for (auto wire : mapped_mod->wires()) {
if (wire->attributes.count(ID::init)) {
- Wire *w = module->wires_[remap_name(wire->name)];
+ Wire *w = module->wire(remap_name(wire->name));
log_assert(w->attributes.count(ID::init) == 0);
w->attributes[ID::init] = wire->attributes.at(ID::init);
}
@@ -1261,10 +1242,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
RTLIL::SigSig conn;
if (si.type != G(NONE)) {
conn.first = si.bit;
- conn.second = RTLIL::SigSpec(module->wires_[remap_name(buffer)]);
+ conn.second = module->wire(remap_name(buffer));
out_wires++;
} else {
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(buffer)]);
+ conn.first = module->wire(remap_name(buffer));
conn.second = si.bit;
in_wires++;
}
diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc
index 8ae1b51ff..78c902866 100644
--- a/passes/techmap/abc9_ops.cc
+++ b/passes/techmap/abc9_ops.cc
@@ -467,7 +467,12 @@ void prep_lut(RTLIL::Design *design, int maxlut)
{
TimingInfo timing;
- std::vector<std::tuple<int, IdString, int, std::vector<int>>> table;
+ struct t_lut {
+ IdString name;
+ int area;
+ std::vector<int> delays;
+ };
+ std::map<int,t_lut> table;
for (auto module : design->modules()) {
auto it = module->attributes.find(ID::abc9_lut);
if (it == module->attributes.end())
@@ -476,40 +481,52 @@ void prep_lut(RTLIL::Design *design, int maxlut)
auto &t = timing.setup_module(module);
TimingInfo::NameBit o;
- std::vector<int> specify;
+ std::vector<int> delays;
for (const auto &i : t.comb) {
auto &d = i.first.second;
if (o == TimingInfo::NameBit())
o = d;
else if (o != d)
- log_error("(* abc9_lut *) module '%s' with has more than one output.\n", log_id(module));
- specify.push_back(i.second);
+ log_error("Module '%s' with (* abc9_lut *) has more than one output.\n", log_id(module));
+ delays.push_back(i.second);
}
- if (maxlut && GetSize(specify) > maxlut)
+ if (GetSize(delays) == 0)
+ log_error("Module '%s' with (* abc9_lut *) has no specify entries.\n", log_id(module));
+ if (maxlut && GetSize(delays) > maxlut)
continue;
// ABC requires non-decreasing LUT input delays
- std::sort(specify.begin(), specify.end());
- table.emplace_back(GetSize(specify), module->name, it->second.as_int(), std::move(specify));
+ std::sort(delays.begin(), delays.end());
+
+ int K = GetSize(delays);
+ auto entry = t_lut{module->name, it->second.as_int(), std::move(delays)};
+ auto r = table.emplace(K, entry);
+ if (!r.second) {
+ if (r.first->second.area != entry.area)
+ log_error("Modules '%s' and '%s' have conflicting (* abc9_lut *) values.\n", log_id(module), log_id(r.first->second.name));
+ if (r.first->second.delays != entry.delays)
+ log_error("Modules '%s' and '%s' have conflicting specify entries.\n", log_id(module), log_id(r.first->second.name));
+ }
}
- // ABC requires ascending size
- std::sort(table.begin(), table.end());
+
+ if (table.empty())
+ log_error("Design does not contain any modules with (* abc9_lut *).\n");
std::stringstream ss;
- const auto &first = table.front();
+ const auto &front = *table.begin();
// If the first entry does not start from a 1-input LUT,
// (as ABC requires) crop the first entry to do so
- for (int i = 1; i < std::get<0>(first); i++) {
+ for (int i = 1; i < front.first; i++) {
ss << "# $__ABC9_LUT" << i << std::endl;
- ss << i << " " << std::get<2>(first);
+ ss << i << " " << front.second.area;
for (int j = 0; j < i; j++)
- ss << " " << std::get<3>(first)[j];
+ ss << " " << front.second.delays[j];
ss << std::endl;
}
for (const auto &i : table) {
- ss << "# " << log_id(std::get<1>(i)) << std::endl;
- ss << std::get<0>(i) << " " << std::get<2>(i);
- for (const auto &j : std::get<3>(i))
+ ss << "# " << log_id(i.second.name) << std::endl;
+ ss << i.first << " " << i.second.area;
+ for (const auto &j : i.second.delays)
ss << " " << j;
ss << std::endl;
}
diff --git a/techlibs/intel_alm/common/alm_sim.v b/techlibs/intel_alm/common/alm_sim.v
index 69768d9f7..979c51132 100644
--- a/techlibs/intel_alm/common/alm_sim.v
+++ b/techlibs/intel_alm/common/alm_sim.v
@@ -1,3 +1,72 @@
+// The core logic primitive of the Cyclone V/10GX is the Adaptive Logic Module
+// (ALM). Each ALM is made up of an 8-input, 2-output look-up table, covered
+// in this file, connected to combinational outputs, a carry chain, and four
+// D flip-flops (which are covered as MISTRAL_FF in dff_sim.v).
+//
+// The ALM is vertically symmetric, so I find it helps to think in terms of
+// half-ALMs, as that's predominantly the unit that synth_intel_alm uses.
+//
+// ALMs are quite flexible, having multiple modes.
+//
+// Normal (combinational) mode
+// ---------------------------
+// The ALM can implement:
+// - a single 6-input function (with the other inputs usable for flip-flop access)
+// - two 5-input functions that share two inputs
+// - a 5-input and a 4-input function that share one input
+// - a 5-input and a 3-or-less-input function that share no inputs
+// - two 4-or-less-input functions that share no inputs
+//
+// Normal-mode functions are represented as MISTRAL_ALUTN cells with N inputs.
+// It would be possible to represent a normal mode function as a single cell -
+// the vendor cyclone{v,10gx}_lcell_comb cell does exactly that - but I felt
+// it was more user-friendly to print out the specific function sizes
+// separately.
+//
+// With the exception of MISTRAL_ALUT6, you can think of two normal-mode cells
+// fitting inside a single ALM.
+//
+// Extended (7-input) mode
+// -----------------------
+// The ALM can also fit a 7-input function made of two 5-input functions that
+// share four inputs, multiplexed by another input.
+//
+// Because this can't accept arbitrary 7-input functions, Yosys can't handle
+// it, so it doesn't have a cell, but I would likely call it MISTRAL_ALUT7(E?)
+// if it did, and it would take up a full ALM.
+//
+// It might be possible to add an extraction pass to examine all ALUT5 cells
+// that feed into ALUT3 cells to see if they can be combined into an extended
+// ALM, but I don't think it will be worth it.
+//
+// Arithmetic mode
+// ---------------
+// In arithmetic mode, each half-ALM uses its carry chain to perform fast addition
+// of two four-input functions that share three inputs. Oddly, the result of
+// one of the functions is inverted before being added (you can see this as
+// the dot on a full-adder input of Figure 1-8 in the Handbook).
+//
+// The cell for an arithmetic-mode half-ALM is MISTRAL_ALM_ARITH. One idea
+// I've had (or rather was suggested by mwk) is that functions that feed into
+// arithmetic-mode cells could be packed directly into the arithmetic-mode
+// cell as a function, which reduces the number of ALMs needed.
+//
+// Shared arithmetic mode
+// ----------------------
+// Shared arithmetic mode looks a lot like arithmetic mode, but here the
+// output of every other four-input function goes to the input of the adder
+// the next bit along. What this means is that adding three bits together can
+// be done in an ALM, because functions can be used to implement addition that
+// then feeds into the carry chain. This means that three bits can be added per
+// ALM, as opposed to two in the arithmetic mode.
+//
+// Shared arithmetic mode doesn't currently have a cell, but I intend to add
+// it as MISTRAL_ALM_SHARED, and have it occupy a full ALM. Because it adds
+// three bits per cell, it makes addition shorter and use less ALMs, but
+// I don't know enough to tell whether it's more efficient to use shared
+// arithmetic mode to shorten the carry chain, or plain arithmetic mode with
+// the functions packed in.
+
`default_nettype none
(* abc9_lut=2, lib_whitebox *)
diff --git a/techlibs/intel_alm/common/dff_map.v b/techlibs/intel_alm/common/dff_map.v
index f7f2fe3c3..962be670c 100644
--- a/techlibs/intel_alm/common/dff_map.v
+++ b/techlibs/intel_alm/common/dff_map.v
@@ -6,7 +6,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(1'b1), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_P_ with INIT=1");
+end else $error("Cannot implement a flip-flop that initialises to one");
endmodule
module \$_DFF_N_ (input D, C, output Q);
@@ -14,7 +14,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(1'b1), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_N_ with INIT=1");
+end else $error("Cannot implement a flip-flop that initialises to one");
endmodule
// D flip-flops with reset
@@ -23,7 +23,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_PP0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
module \$_DFF_PN0_ (input D, C, R, output Q);
@@ -31,7 +31,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_PN0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
module \$_DFF_NP0_ (input D, C, R, output Q);
@@ -39,7 +39,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_NP0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
module \$_DFF_NN0_ (input D, C, R, output Q);
@@ -47,7 +47,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_NN0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
// D flip-flops with set
@@ -58,7 +58,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
assign Q = ~Q_tmp;
-end else $error("Unsupported flop: $_DFF_PP1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
module \$_DFF_PN1_ (input D, C, R, output Q);
@@ -67,7 +67,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
-end else $error("Unsupported flop: $_DFF_PN1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
module \$_DFF_NP1_ (input D, C, R, output Q);
@@ -77,7 +77,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(~C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
assign Q = ~Q_tmp;
-end else $error("Unsupported flop: $_DFF_NP1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
module \$_DFF_NN1_ (input D, C, R, output Q);
@@ -87,7 +87,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(~C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
assign Q = ~Q_tmp;
-end else $error("Unsupported flop: $_DFF_NN1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
// D flip-flops with clock enable
@@ -96,7 +96,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(1'b1), .ENA(E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_PP_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
module \$_DFFE_PN_ (input D, C, E, output Q);
@@ -104,7 +104,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(1'b1), .ENA(~E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_PN_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
module \$_DFFE_NP_ (input D, C, E, output Q);
@@ -112,7 +112,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(1'b1), .ENA(E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_NP_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
module \$_DFFE_NN_ (input D, C, E, output Q);
@@ -120,5 +120,5 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0;
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(1'b1), .ENA(~E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_NN_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
diff --git a/techlibs/intel_alm/common/dff_sim.v b/techlibs/intel_alm/common/dff_sim.v
index 07865905f..32444dd46 100644
--- a/techlibs/intel_alm/common/dff_sim.v
+++ b/techlibs/intel_alm/common/dff_sim.v
@@ -1,3 +1,47 @@
+// The four D flip-flops (DFFs) in a Cyclone V/10GX Adaptive Logic Module (ALM)
+// act as one-bit memory cells that can be placed very flexibly (wherever there's
+// an ALM); each flop is represented by a MISTRAL_FF cell.
+//
+// The flops in these chips are rather flexible in some ways, but in practice
+// quite crippled by FPGA standards.
+//
+// What the flops can do
+// ---------------------
+// The core flop acts as a single-bit memory that initialises to zero at chip
+// reset. It takes in data on the rising edge of CLK if ENA is high,
+// and outputs it to Q. The ENA (clock enable) pin can therefore be used to
+// capture the input only if a condition is true.
+//
+// The data itself is zero if SCLR (synchronous clear) is high, else it comes
+// from SDATA (synchronous data) if SLOAD (synchronous load) is high, or DATAIN
+// if SLOAD is low.
+//
+// If ACLR (asynchronous clear) is low then Q is forced to zero, regardless of
+// the synchronous inputs or CLK edge. This is most often used for an FPGA-wide
+// power-on reset.
+//
+// An asynchronous set that sets Q to one can be emulated by inverting the input
+// and output of the flop, resulting in ACLR forcing Q to zero, which then gets
+// inverted to produce one. Likewise, logic can operate on the falling edge of
+// CLK if CLK is inverted before being passed as an input.
+//
+// What the flops *can't* do
+// -------------------------
+// The trickiest part of the above capabilities is the lack of configurable
+// initialisation state. For example, it isn't possible to implement a flop with
+// asynchronous clear that initialises to one, because the hardware initialises
+// to zero. Likewise, you can't emulate a flop with asynchronous set that
+// initialises to zero, because the inverters mean the flop initialises to one.
+//
+// If the input design requires one of these cells (which appears to be rare
+// in practice) then synth_intel_alm will fail to synthesize the design where
+// other Yosys synthesis scripts might succeed.
+//
+// This stands in notable contrast to e.g. Xilinx flip-flops, which have
+// configurable initialisation state and native synchronous/asynchronous
+// set/clear (although not at the same time), which means they can generally
+// implement a much wider variety of logic.
+
// DATAIN: synchronous data input
// CLK: clock input (positive edge)
// ACLR: asynchronous clear (negative-true)
diff --git a/techlibs/xilinx/xilinx_dffopt.cc b/techlibs/xilinx/xilinx_dffopt.cc
index ac9b57fe1..c608db883 100644
--- a/techlibs/xilinx/xilinx_dffopt.cc
+++ b/techlibs/xilinx/xilinx_dffopt.cc
@@ -209,7 +209,7 @@ lut_sigin_done:
continue;
LutData lut_d = it_D->second.first;
Cell *cell_d = it_D->second.second;
- if (cell->hasParam(ID(IS_D_INVERTED)) && cell->getParam(ID(IS_D_INVERTED)).as_bool()) {
+ if (cell->getParam(ID(IS_D_INVERTED)).as_bool()) {
// Flip all bits in the LUT.
for (int i = 0; i < GetSize(lut_d.first); i++)
lut_d.first.bits[i] = (lut_d.first.bits[i] == State::S1) ? State::S0 : State::S1;
@@ -249,7 +249,7 @@ lut_sigin_done:
if (has_s) {
SigBit sig_S = sigmap(cell->getPort(ID::S));
LutData lut_s = LutData(Const(2, 2), {sig_S});
- bool inv_s = cell->hasParam(ID(IS_S_INVERTED)) && cell->getParam(ID(IS_S_INVERTED)).as_bool();
+ bool inv_s = cell->getParam(ID(IS_S_INVERTED)).as_bool();
auto it_S = bit_to_lut.find(sig_S);
if (it_S != bit_to_lut.end())
lut_s = it_S->second.first;
@@ -271,7 +271,7 @@ lut_sigin_done:
if (has_r) {
SigBit sig_R = sigmap(cell->getPort(ID::R));
LutData lut_r = LutData(Const(2, 2), {sig_R});
- bool inv_r = cell->hasParam(ID(IS_R_INVERTED)) && cell->getParam(ID(IS_R_INVERTED)).as_bool();
+ bool inv_r = cell->getParam(ID(IS_R_INVERTED)).as_bool();
auto it_R = bit_to_lut.find(sig_R);
if (it_R != bit_to_lut.end())
lut_r = it_R->second.first;
diff --git a/tests/arch/ecp5/memories.ys b/tests/arch/ecp5/memories.ys
index e1f748e26..f55bf01d2 100644
--- a/tests/arch/ecp5/memories.ys
+++ b/tests/arch/ecp5/memories.ys
@@ -208,7 +208,6 @@ select -assert-count 1 t:PDPW16KD
design -reset; read_verilog ../common/blockrom.v
chparam -set ADDRESS_WIDTH 3 -set DATA_WIDTH 36 sync_rom
-write_ilang
synth_ecp5 -top sync_rom; cd sync_rom
select -assert-count 0 t:PDPW16KD # too inefficient
select -assert-min 18 t:LUT4
@@ -274,7 +273,6 @@ select -assert-count 1 t:DP16KD
design -reset; read_verilog ../common/blockrom.v
chparam -set ADDRESS_WIDTH 3 -set DATA_WIDTH 18 sync_rom
-write_ilang
synth_ecp5 -top sync_rom; cd sync_rom
select -assert-count 0 t:DP16KD # too inefficient
select -assert-min 9 t:LUT4
diff --git a/tests/arch/ice40/memories.ys b/tests/arch/ice40/memories.ys
index 571edec1d..c32f12315 100644
--- a/tests/arch/ice40/memories.ys
+++ b/tests/arch/ice40/memories.ys
@@ -112,7 +112,6 @@ select -assert-count 1 t:SB_RAM40_4K
design -reset; read_verilog ../common/blockrom.v
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_rom
-write_ilang
synth_ice40 -top sync_rom; cd sync_rom
select -assert-count 0 t:SB_RAM40_4K # too inefficient
select -assert-min 1 t:SB_LUT4
diff --git a/tests/select/unset.ys b/tests/select/unset.ys
new file mode 100644
index 000000000..4f60781c2
--- /dev/null
+++ b/tests/select/unset.ys
@@ -0,0 +1,10 @@
+read_verilog <<EOT
+module top(input i, output o);
+assign o = i;
+endmodule
+EOT
+select -set foo w:*
+select -assert-any @foo
+select -unset foo
+logger -expect error "Selection '\\foo' does not exist!" 1
+select -unset foo
diff --git a/tests/select/unset2.ys b/tests/select/unset2.ys
new file mode 100644
index 000000000..456b84c22
--- /dev/null
+++ b/tests/select/unset2.ys
@@ -0,0 +1,10 @@
+read_verilog <<EOT
+module top(input i, output o);
+assign o = i;
+endmodule
+EOT
+select -set foo w:*
+select -assert-any @foo
+select -unset foo
+logger -expect error "Selection @foo is not defined!" 1
+select -list @foo
diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys
index 0c7695089..6e2415ad7 100644
--- a/tests/various/abc9.ys
+++ b/tests/various/abc9.ys
@@ -53,3 +53,22 @@ assign q = w;
endmodule
EOT
abc9 -lut 4 -dff
+
+
+design -reset
+read_verilog -icells -specify <<EOT
+(* abc9_lut=1, blackbox *)
+module LUT2(input [1:0] i, output o);
+parameter [3:0] mask = 0;
+assign o = i[0] ? (i[1] ? mask[3] : mask[2])
+ : (i[1] ? mask[1] : mask[0]);
+specify
+ (i *> o) = 1;
+endspecify
+endmodule
+
+module top(input [1:0] i, output o);
+LUT2 #(.mask(4'b0)) lut (.i(i), .o(o));
+endmodule
+EOT
+abc9
diff --git a/tests/various/dynamic_part_select.ys b/tests/various/dynamic_part_select.ys
new file mode 100644
index 000000000..abc1daad6
--- /dev/null
+++ b/tests/various/dynamic_part_select.ys
@@ -0,0 +1,106 @@
+### Original testcase ###
+read_verilog ./dynamic_part_select/original.v
+proc
+rename -top gold
+design -stash gold
+
+read_verilog ./dynamic_part_select/original_gate.v
+proc
+rename -top gate
+design -stash gate
+
+design -copy-from gold -as gold gold
+design -copy-from gate -as gate gate
+
+miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
+sat -prove-asserts -seq 10 -show-public -verify -set-init-zero equiv
+
+### Multiple blocking assingments ###
+design -reset
+read_verilog ./dynamic_part_select/multiple_blocking.v
+proc
+rename -top gold
+design -stash gold
+
+read_verilog ./dynamic_part_select/multiple_blocking_gate.v
+proc
+rename -top gate
+design -stash gate
+
+design -copy-from gold -as gold gold
+design -copy-from gate -as gate gate
+
+miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
+sat -prove-asserts -seq 10 -show-public -verify -set-init-zero equiv
+
+### Non-blocking to the same output register ###
+design -reset
+read_verilog ./dynamic_part_select/nonblocking.v
+proc
+rename -top gold
+design -stash gold
+
+read_verilog ./dynamic_part_select/nonblocking_gate.v
+proc
+rename -top gate
+design -stash gate
+
+design -copy-from gold -as gold gold
+design -copy-from gate -as gate gate
+
+miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
+sat -prove-asserts -seq 10 -show-public -verify -set-init-zero equiv
+
+### For-loop select, one dynamic input
+design -reset
+read_verilog ./dynamic_part_select/forloop_select.v
+proc
+rename -top gold
+design -stash gold
+
+read_verilog ./dynamic_part_select/forloop_select_gate.v
+proc
+rename -top gate
+design -stash gate
+
+design -copy-from gold -as gold gold
+design -copy-from gate -as gate gate
+
+miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
+sat -prove-asserts -seq 10 -show-public -verify -set-init-zero equiv
+
+#### Double loop (part-select, reset) ###
+design -reset
+read_verilog ./dynamic_part_select/reset_test.v
+proc
+rename -top gold
+design -stash gold
+
+read_verilog ./dynamic_part_select/reset_test_gate.v
+proc
+rename -top gate
+design -stash gate
+
+design -copy-from gold -as gold gold
+design -copy-from gate -as gate gate
+
+miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
+sat -prove-asserts -seq 10 -show-public -verify -set-init-zero equiv
+
+### Reversed part-select case ###
+design -reset
+read_verilog ./dynamic_part_select/reversed.v
+proc
+rename -top gold
+design -stash gold
+
+read_verilog ./dynamic_part_select/reversed_gate.v
+proc
+rename -top gate
+design -stash gate
+
+design -copy-from gold -as gold gold
+design -copy-from gate -as gate gate
+
+miter -equiv -make_assert -make_outcmp -flatten gold gate equiv
+sat -prove-asserts -seq 10 -show-public -verify -set-init-zero equiv
diff --git a/tests/various/dynamic_part_select/forloop_select.v b/tests/various/dynamic_part_select/forloop_select.v
new file mode 100644
index 000000000..8260f3186
--- /dev/null
+++ b/tests/various/dynamic_part_select/forloop_select.v
@@ -0,0 +1,19 @@
+module forloop_select #(parameter WIDTH=16, SELW=4, CTRLW=$clog2(WIDTH), DINW=2**SELW)
+ (input clk,
+ input [CTRLW-1:0] ctrl,
+ input [DINW-1:0] din,
+ input en,
+ output reg [WIDTH-1:0] dout);
+
+ reg [SELW:0] sel;
+ localparam SLICE = WIDTH/(SELW**2);
+
+ always @(posedge clk)
+ begin
+ if (en) begin
+ for (sel = 0; sel <= 4'hf; sel=sel+1'b1)
+ dout[(ctrl*sel)+:SLICE] <= din;
+ end
+ end
+endmodule
+
diff --git a/tests/various/dynamic_part_select/forloop_select_gate.v b/tests/various/dynamic_part_select/forloop_select_gate.v
new file mode 100644
index 000000000..71ae88537
--- /dev/null
+++ b/tests/various/dynamic_part_select/forloop_select_gate.v
@@ -0,0 +1,559 @@
+module forloop_select_gate (clk, ctrl, din, en, dout);
+ input clk;
+ input [3:0] ctrl;
+ input [15:0] din;
+ input en;
+ output reg [15:0] dout;
+ reg [4:0] sel;
+ always @(posedge clk)
+ case (|(en))
+ 1'b 1:
+ begin
+ case (({(ctrl)*(0)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00001)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00010)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00011)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00100)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00101)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00110)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 00111)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01000)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01001)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01010)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01011)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01100)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01101)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01110)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ case (({(ctrl)*(5'b 01111)})+(0))
+ 0:
+ dout[0:0] <= din;
+ 1:
+ dout[1:1] <= din;
+ 2:
+ dout[2:2] <= din;
+ 3:
+ dout[3:3] <= din;
+ 4:
+ dout[4:4] <= din;
+ 5:
+ dout[5:5] <= din;
+ 6:
+ dout[6:6] <= din;
+ 7:
+ dout[7:7] <= din;
+ 8:
+ dout[8:8] <= din;
+ 9:
+ dout[9:9] <= din;
+ 10:
+ dout[10:10] <= din;
+ 11:
+ dout[11:11] <= din;
+ 12:
+ dout[12:12] <= din;
+ 13:
+ dout[13:13] <= din;
+ 14:
+ dout[14:14] <= din;
+ 15:
+ dout[15:15] <= din;
+ endcase
+ sel = 5'b 10000;
+ end
+ endcase
+ endmodule
diff --git a/tests/various/dynamic_part_select/multiple_blocking.v b/tests/various/dynamic_part_select/multiple_blocking.v
new file mode 100644
index 000000000..2858f7741
--- /dev/null
+++ b/tests/various/dynamic_part_select/multiple_blocking.v
@@ -0,0 +1,19 @@
+module multiple_blocking #(parameter WIDTH=32, SELW=1, CTRLW=$clog2(WIDTH), DINW=2**SELW)
+ (input clk,
+ input [CTRLW-1:0] ctrl,
+ input [DINW-1:0] din,
+ input [SELW-1:0] sel,
+ output reg [WIDTH-1:0] dout);
+
+ localparam SLICE = WIDTH/(SELW**2);
+ reg [CTRLW:0] a;
+ reg [SELW-1:0] b;
+ reg [DINW:0] c;
+ always @(posedge clk) begin
+ a = ctrl + 1;
+ b = sel - 1;
+ c = ~din;
+ dout = dout + 1;
+ dout[a*b+:SLICE] = c;
+ end
+endmodule
diff --git a/tests/various/dynamic_part_select/multiple_blocking_gate.v b/tests/various/dynamic_part_select/multiple_blocking_gate.v
new file mode 100644
index 000000000..073b559dc
--- /dev/null
+++ b/tests/various/dynamic_part_select/multiple_blocking_gate.v
@@ -0,0 +1,83 @@
+module multiple_blocking_gate (clk, ctrl, din, sel, dout);
+ input clk;
+ input [4:0] ctrl;
+ input [1:0] din;
+ input [0:0] sel;
+ output reg [31:0] dout;
+ reg [5:0] a;
+ reg [0:0] b;
+ reg [2:0] c;
+ always @(posedge clk)
+ begin
+ a = (ctrl)+(1);
+ b = (sel)-(1);
+ c = ~(din);
+ dout = (dout)+(1);
+ case (({(a)*(b)})+(0))
+ 0:
+ dout[31:0] = c;
+ 1:
+ dout[31:1] = c;
+ 2:
+ dout[31:2] = c;
+ 3:
+ dout[31:3] = c;
+ 4:
+ dout[31:4] = c;
+ 5:
+ dout[31:5] = c;
+ 6:
+ dout[31:6] = c;
+ 7:
+ dout[31:7] = c;
+ 8:
+ dout[31:8] = c;
+ 9:
+ dout[31:9] = c;
+ 10:
+ dout[31:10] = c;
+ 11:
+ dout[31:11] = c;
+ 12:
+ dout[31:12] = c;
+ 13:
+ dout[31:13] = c;
+ 14:
+ dout[31:14] = c;
+ 15:
+ dout[31:15] = c;
+ 16:
+ dout[31:16] = c;
+ 17:
+ dout[31:17] = c;
+ 18:
+ dout[31:18] = c;
+ 19:
+ dout[31:19] = c;
+ 20:
+ dout[31:20] = c;
+ 21:
+ dout[31:21] = c;
+ 22:
+ dout[31:22] = c;
+ 23:
+ dout[31:23] = c;
+ 24:
+ dout[31:24] = c;
+ 25:
+ dout[31:25] = c;
+ 26:
+ dout[31:26] = c;
+ 27:
+ dout[31:27] = c;
+ 28:
+ dout[31:28] = c;
+ 29:
+ dout[31:29] = c;
+ 30:
+ dout[31:30] = c;
+ 31:
+ dout[31:31] = c;
+ endcase
+ end
+endmodule
diff --git a/tests/various/dynamic_part_select/nonblocking.v b/tests/various/dynamic_part_select/nonblocking.v
new file mode 100644
index 000000000..0949b31a9
--- /dev/null
+++ b/tests/various/dynamic_part_select/nonblocking.v
@@ -0,0 +1,14 @@
+module nonblocking #(parameter WIDTH=32, SELW=1, CTRLW=$clog2(WIDTH), DINW=2**SELW)
+ (input clk,
+ input [CTRLW-1:0] ctrl,
+ input [DINW-1:0] din,
+ input [SELW-1:0] sel,
+ output reg [WIDTH-1:0] dout);
+
+ localparam SLICE = WIDTH/(SELW**2);
+ always @(posedge clk) begin
+ dout <= dout + 1;
+ dout[ctrl*sel+:SLICE] <= din ;
+ end
+
+endmodule
diff --git a/tests/various/dynamic_part_select/nonblocking_gate.v b/tests/various/dynamic_part_select/nonblocking_gate.v
new file mode 100644
index 000000000..ed1ee2776
--- /dev/null
+++ b/tests/various/dynamic_part_select/nonblocking_gate.v
@@ -0,0 +1,77 @@
+module nonblocking_gate (clk, ctrl, din, sel, dout);
+ input clk;
+ input [4:0] ctrl;
+ input [1:0] din;
+ input [0:0] sel;
+ output reg [31:0] dout;
+ always @(posedge clk)
+ begin
+ dout <= (dout)+(1);
+ case (({(ctrl)*(sel)})+(0))
+ 0:
+ dout[31:0] <= din;
+ 1:
+ dout[31:1] <= din;
+ 2:
+ dout[31:2] <= din;
+ 3:
+ dout[31:3] <= din;
+ 4:
+ dout[31:4] <= din;
+ 5:
+ dout[31:5] <= din;
+ 6:
+ dout[31:6] <= din;
+ 7:
+ dout[31:7] <= din;
+ 8:
+ dout[31:8] <= din;
+ 9:
+ dout[31:9] <= din;
+ 10:
+ dout[31:10] <= din;
+ 11:
+ dout[31:11] <= din;
+ 12:
+ dout[31:12] <= din;
+ 13:
+ dout[31:13] <= din;
+ 14:
+ dout[31:14] <= din;
+ 15:
+ dout[31:15] <= din;
+ 16:
+ dout[31:16] <= din;
+ 17:
+ dout[31:17] <= din;
+ 18:
+ dout[31:18] <= din;
+ 19:
+ dout[31:19] <= din;
+ 20:
+ dout[31:20] <= din;
+ 21:
+ dout[31:21] <= din;
+ 22:
+ dout[31:22] <= din;
+ 23:
+ dout[31:23] <= din;
+ 24:
+ dout[31:24] <= din;
+ 25:
+ dout[31:25] <= din;
+ 26:
+ dout[31:26] <= din;
+ 27:
+ dout[31:27] <= din;
+ 28:
+ dout[31:28] <= din;
+ 29:
+ dout[31:29] <= din;
+ 30:
+ dout[31:30] <= din;
+ 31:
+ dout[31:31] <= din;
+ endcase
+ end
+endmodule
diff --git a/tests/various/dynamic_part_select/original.v b/tests/various/dynamic_part_select/original.v
new file mode 100644
index 000000000..f7dfed1a1
--- /dev/null
+++ b/tests/various/dynamic_part_select/original.v
@@ -0,0 +1,12 @@
+module original #(parameter WIDTH=32, SELW=1, CTRLW=$clog2(WIDTH), DINW=2**SELW)
+ (input clk,
+ input [CTRLW-1:0] ctrl,
+ input [DINW-1:0] din,
+ input [SELW-1:0] sel,
+ output reg [WIDTH-1:0] dout);
+ localparam SLICE = WIDTH/(SELW**2);
+ always @(posedge clk)
+ begin
+ dout[ctrl*sel+:SLICE] <= din ;
+ end
+endmodule
diff --git a/tests/various/dynamic_part_select/original_gate.v b/tests/various/dynamic_part_select/original_gate.v
new file mode 100644
index 000000000..22093bf63
--- /dev/null
+++ b/tests/various/dynamic_part_select/original_gate.v
@@ -0,0 +1,74 @@
+module original_gate (clk, ctrl, din, sel, dout);
+ input clk;
+ input [4:0] ctrl;
+ input [1:0] din;
+ input [0:0] sel;
+ output reg [31:0] dout;
+ always @(posedge clk)
+ case (({(ctrl)*(sel)})+(0))
+ 0:
+ dout[31:0] <= din;
+ 1:
+ dout[31:1] <= din;
+ 2:
+ dout[31:2] <= din;
+ 3:
+ dout[31:3] <= din;
+ 4:
+ dout[31:4] <= din;
+ 5:
+ dout[31:5] <= din;
+ 6:
+ dout[31:6] <= din;
+ 7:
+ dout[31:7] <= din;
+ 8:
+ dout[31:8] <= din;
+ 9:
+ dout[31:9] <= din;
+ 10:
+ dout[31:10] <= din;
+ 11:
+ dout[31:11] <= din;
+ 12:
+ dout[31:12] <= din;
+ 13:
+ dout[31:13] <= din;
+ 14:
+ dout[31:14] <= din;
+ 15:
+ dout[31:15] <= din;
+ 16:
+ dout[31:16] <= din;
+ 17:
+ dout[31:17] <= din;
+ 18:
+ dout[31:18] <= din;
+ 19:
+ dout[31:19] <= din;
+ 20:
+ dout[31:20] <= din;
+ 21:
+ dout[31:21] <= din;
+ 22:
+ dout[31:22] <= din;
+ 23:
+ dout[31:23] <= din;
+ 24:
+ dout[31:24] <= din;
+ 25:
+ dout[31:25] <= din;
+ 26:
+ dout[31:26] <= din;
+ 27:
+ dout[31:27] <= din;
+ 28:
+ dout[31:28] <= din;
+ 29:
+ dout[31:29] <= din;
+ 30:
+ dout[31:30] <= din;
+ 31:
+ dout[31:31] <= din;
+ endcase
+endmodule
diff --git a/tests/various/dynamic_part_select/reset_test.v b/tests/various/dynamic_part_select/reset_test.v
new file mode 100644
index 000000000..29355aafb
--- /dev/null
+++ b/tests/various/dynamic_part_select/reset_test.v
@@ -0,0 +1,23 @@
+module reset_test #(parameter WIDTH=32, SELW=1, CTRLW=$clog2(WIDTH), DINW=2**SELW)
+ (input clk,
+ input [CTRLW-1:0] ctrl,
+ input [DINW-1:0] din,
+ input [SELW-1:0] sel,
+ output reg [WIDTH-1:0] dout);
+
+ reg [SELW:0] i;
+ wire [SELW-1:0] rval = {reset, {SELW-1{1'b0}}};
+ localparam SLICE = WIDTH/(SELW**2);
+ // Doing exotic reset. masking 2 LSB bits to 0, 6 MSB bits to 1 for
+ // whatever reason.
+ always @(posedge clk) begin
+ if (reset) begin: reset_mask
+ for (i = 0; i < {SELW{1'b1}}; i=i+1) begin
+ dout[i*rval+:SLICE] <= 32'hDEAD;
+ end
+ end
+ //else begin
+ dout[ctrl*sel+:SLICE] <= din;
+ //end
+ end
+endmodule
diff --git a/tests/various/dynamic_part_select/reset_test_gate.v b/tests/various/dynamic_part_select/reset_test_gate.v
new file mode 100644
index 000000000..96dff4135
--- /dev/null
+++ b/tests/various/dynamic_part_select/reset_test_gate.v
@@ -0,0 +1,151 @@
+module reset_test_gate (clk, ctrl, din, sel, dout);
+ input clk;
+ input [4:0] ctrl;
+ input [1:0] din;
+ input [0:0] sel;
+ output reg [31:0] dout;
+ reg [1:0] i;
+ wire [0:0] rval;
+ assign rval = {reset, 1'b0 };
+ always @(posedge clk)
+ begin
+ case (|(reset))
+ 1'b 1:
+ begin
+ case (({(0)*(rval)})+(0))
+ 0:
+ dout[31:0] <= 57005;
+ 1:
+ dout[31:1] <= 57005;
+ 2:
+ dout[31:2] <= 57005;
+ 3:
+ dout[31:3] <= 57005;
+ 4:
+ dout[31:4] <= 57005;
+ 5:
+ dout[31:5] <= 57005;
+ 6:
+ dout[31:6] <= 57005;
+ 7:
+ dout[31:7] <= 57005;
+ 8:
+ dout[31:8] <= 57005;
+ 9:
+ dout[31:9] <= 57005;
+ 10:
+ dout[31:10] <= 57005;
+ 11:
+ dout[31:11] <= 57005;
+ 12:
+ dout[31:12] <= 57005;
+ 13:
+ dout[31:13] <= 57005;
+ 14:
+ dout[31:14] <= 57005;
+ 15:
+ dout[31:15] <= 57005;
+ 16:
+ dout[31:16] <= 57005;
+ 17:
+ dout[31:17] <= 57005;
+ 18:
+ dout[31:18] <= 57005;
+ 19:
+ dout[31:19] <= 57005;
+ 20:
+ dout[31:20] <= 57005;
+ 21:
+ dout[31:21] <= 57005;
+ 22:
+ dout[31:22] <= 57005;
+ 23:
+ dout[31:23] <= 57005;
+ 24:
+ dout[31:24] <= 57005;
+ 25:
+ dout[31:25] <= 57005;
+ 26:
+ dout[31:26] <= 57005;
+ 27:
+ dout[31:27] <= 57005;
+ 28:
+ dout[31:28] <= 57005;
+ 29:
+ dout[31:29] <= 57005;
+ 30:
+ dout[31:30] <= 57005;
+ 31:
+ dout[31:31] <= 57005;
+ endcase
+ i = 1;
+ end
+ endcase
+ case (({(ctrl)*(sel)})+(0))
+ 0:
+ dout[31:0] <= din;
+ 1:
+ dout[31:1] <= din;
+ 2:
+ dout[31:2] <= din;
+ 3:
+ dout[31:3] <= din;
+ 4:
+ dout[31:4] <= din;
+ 5:
+ dout[31:5] <= din;
+ 6:
+ dout[31:6] <= din;
+ 7:
+ dout[31:7] <= din;
+ 8:
+ dout[31:8] <= din;
+ 9:
+ dout[31:9] <= din;
+ 10:
+ dout[31:10] <= din;
+ 11:
+ dout[31:11] <= din;
+ 12:
+ dout[31:12] <= din;
+ 13:
+ dout[31:13] <= din;
+ 14:
+ dout[31:14] <= din;
+ 15:
+ dout[31:15] <= din;
+ 16:
+ dout[31:16] <= din;
+ 17:
+ dout[31:17] <= din;
+ 18:
+ dout[31:18] <= din;
+ 19:
+ dout[31:19] <= din;
+ 20:
+ dout[31:20] <= din;
+ 21:
+ dout[31:21] <= din;
+ 22:
+ dout[31:22] <= din;
+ 23:
+ dout[31:23] <= din;
+ 24:
+ dout[31:24] <= din;
+ 25:
+ dout[31:25] <= din;
+ 26:
+ dout[31:26] <= din;
+ 27:
+ dout[31:27] <= din;
+ 28:
+ dout[31:28] <= din;
+ 29:
+ dout[31:29] <= din;
+ 30:
+ dout[31:30] <= din;
+ 31:
+ dout[31:31] <= din;
+ endcase
+ end
+endmodule
diff --git a/tests/various/dynamic_part_select/reversed.v b/tests/various/dynamic_part_select/reversed.v
new file mode 100644
index 000000000..8b114ac77
--- /dev/null
+++ b/tests/various/dynamic_part_select/reversed.v
@@ -0,0 +1,13 @@
+module reversed #(parameter WIDTH=32, SELW=4, CTRLW=$clog2(WIDTH), DINW=2**SELW)
+ (input clk,
+ input [CTRLW-1:0] ctrl,
+ input [DINW-1:0] din,
+ input [SELW-1:0] sel,
+ output reg [WIDTH-1:0] dout);
+
+ localparam SLICE = WIDTH/(SELW**2);
+ always @(posedge clk) begin
+ dout[(WIDTH-ctrl*sel)-:SLICE] <= din;
+ end
+endmodule
+
diff --git a/tests/various/dynamic_part_select/reversed_gate.v b/tests/various/dynamic_part_select/reversed_gate.v
new file mode 100644
index 000000000..9349d45ee
--- /dev/null
+++ b/tests/various/dynamic_part_select/reversed_gate.v
@@ -0,0 +1,74 @@
+module reversed_gate (clk, ctrl, din, sel, dout);
+ input clk;
+ input [4:0] ctrl;
+ input [15:0] din;
+ input [3:0] sel;
+ output reg [31:0] dout;
+ always @(posedge clk)
+ case ((({(32)-((ctrl)*(sel))})+(1))-(2))
+ 0:
+ dout[1:0] <= din;
+ 1:
+ dout[2:1] <= din;
+ 2:
+ dout[3:2] <= din;
+ 3:
+ dout[4:3] <= din;
+ 4:
+ dout[5:4] <= din;
+ 5:
+ dout[6:5] <= din;
+ 6:
+ dout[7:6] <= din;
+ 7:
+ dout[8:7] <= din;
+ 8:
+ dout[9:8] <= din;
+ 9:
+ dout[10:9] <= din;
+ 10:
+ dout[11:10] <= din;
+ 11:
+ dout[12:11] <= din;
+ 12:
+ dout[13:12] <= din;
+ 13:
+ dout[14:13] <= din;
+ 14:
+ dout[15:14] <= din;
+ 15:
+ dout[16:15] <= din;
+ 16:
+ dout[17:16] <= din;
+ 17:
+ dout[18:17] <= din;
+ 18:
+ dout[19:18] <= din;
+ 19:
+ dout[20:19] <= din;
+ 20:
+ dout[21:20] <= din;
+ 21:
+ dout[22:21] <= din;
+ 22:
+ dout[23:22] <= din;
+ 23:
+ dout[24:23] <= din;
+ 24:
+ dout[25:24] <= din;
+ 25:
+ dout[26:25] <= din;
+ 26:
+ dout[27:26] <= din;
+ 27:
+ dout[28:27] <= din;
+ 28:
+ dout[29:28] <= din;
+ 29:
+ dout[30:29] <= din;
+ 30:
+ dout[31:30] <= din;
+ 31:
+ dout[31:31] <= din;
+ endcase
+endmodule
diff --git a/tests/various/hierarchy_param.ys b/tests/various/hierarchy_param.ys
new file mode 100644
index 000000000..d703bb713
--- /dev/null
+++ b/tests/various/hierarchy_param.ys
@@ -0,0 +1,23 @@
+read_verilog <<EOT
+
+module bb (...);
+parameter A = "abc";
+parameter B = 1;
+parameter C = 2;
+input a;
+output b;
+endmodule
+
+module top (...);
+input a;
+output b;
+bb #("def", 3) my_bb (a, b);
+endmodule
+
+EOT
+
+hierarchy -top top
+dump
+
+select -assert-count 1 t:bb r:A=def %i
+select -assert-count 1 t:bb r:B=3 %i
diff --git a/tests/various/sim_const.ys b/tests/various/sim_const.ys
new file mode 100644
index 000000000..d778b92cd
--- /dev/null
+++ b/tests/various/sim_const.ys
@@ -0,0 +1,13 @@
+read_verilog <<EOT
+
+module top(input clk, output reg [1:0] q);
+ wire [1:0] x = 2'b10;
+ always @(posedge clk)
+ q <= x & 2'b11;
+endmodule
+EOT
+
+proc
+sim -clock clk -n 1 -w top
+select -assert-count 1 a:init=2'b10 top/q %i
+