aboutsummaryrefslogtreecommitdiffstats
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/aiger/aiger.cc2
-rw-r--r--backends/aiger/xaiger.cc43
-rw-r--r--backends/blif/blif.cc2
-rw-r--r--backends/btor/btor.cc24
-rw-r--r--backends/cxxrtl/cxxrtl.h102
-rw-r--r--backends/cxxrtl/cxxrtl_backend.cc891
-rw-r--r--backends/cxxrtl/cxxrtl_vcd.h15
-rw-r--r--backends/edif/edif.cc2
-rw-r--r--backends/firrtl/firrtl.cc362
-rw-r--r--backends/intersynth/intersynth.cc4
-rw-r--r--backends/json/json.cc44
-rw-r--r--backends/protobuf/Makefile.inc2
-rw-r--r--backends/protobuf/protobuf.cc2
-rw-r--r--backends/rtlil/rtlil_backend.cc51
-rw-r--r--backends/rtlil/rtlil_backend.h2
-rw-r--r--backends/simplec/simplec.cc2
-rw-r--r--backends/smt2/smt2.cc14
-rw-r--r--backends/smt2/smtbmc.py10
-rw-r--r--backends/smt2/smtio.py12
-rw-r--r--backends/smv/smv.cc9
-rw-r--r--backends/spice/spice.cc2
-rw-r--r--backends/table/table.cc2
-rw-r--r--backends/verilog/verilog_backend.cc510
23 files changed, 1322 insertions, 787 deletions
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index 476b30488..35935b847 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 27499b64a..e223f185e 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -156,7 +156,7 @@ struct XAigerWriter
// promote keep wires
for (auto wire : module->wires())
- if (wire->get_bool_attribute(ID::keep) || wire->get_bool_attribute(ID::abc9_keep))
+ if (wire->get_bool_attribute(ID::keep))
sigmap.add(wire);
for (auto wire : module->wires()) {
@@ -177,11 +177,10 @@ struct XAigerWriter
undriven_bits.insert(bit);
unused_bits.insert(bit);
- bool keep = wire->get_bool_attribute(ID::abc9_keep);
- if (wire->port_input || keep)
+ if (wire->port_input)
input_bits.insert(bit);
- keep = keep || wire->get_bool_attribute(ID::keep);
+ bool keep = wire->get_bool_attribute(ID::keep);
if (wire->port_output || keep) {
if (bit != wirebit)
alias_map[wirebit] = bit;
@@ -262,26 +261,27 @@ struct XAigerWriter
if (!timing.count(inst_module->name))
timing.setup_module(inst_module);
- auto &t = timing.at(inst_module->name).arrival;
- for (const auto &conn : cell->connections()) {
- auto port_wire = inst_module->wire(conn.first);
- if (!port_wire->port_output)
+
+ for (auto &i : timing.at(inst_module->name).arrival) {
+ if (!cell->hasPort(i.first.name))
continue;
- for (int i = 0; i < GetSize(conn.second); i++) {
- auto d = t.at(TimingInfo::NameBit(conn.first,i), 0);
- if (d == 0)
- continue;
+ auto port_wire = inst_module->wire(i.first.name);
+ log_assert(port_wire->port_output);
+
+ auto d = i.second.first;
+ if (d == 0)
+ continue;
+ auto offset = i.first.offset;
#ifndef NDEBUG
- if (ys_debug(1)) {
- static std::set<std::tuple<IdString,IdString,int>> seen;
- if (seen.emplace(inst_module->name, conn.first, i).second) log("%s.%s[%d] abc9_arrival = %d\n",
- log_id(cell->type), log_id(conn.first), i, d);
- }
-#endif
- arrival_times[conn.second[i]] = d;
+ if (ys_debug(1)) {
+ static pool<std::pair<IdString,TimingInfo::NameBit>> seen;
+ if (seen.emplace(inst_module->name, i.first).second) log("%s.%s[%d] abc9_arrival = %d\n",
+ log_id(cell->type), log_id(i.first.name), offset, d);
}
+#endif
+ arrival_times[cell->getPort(i.first.name)[offset]] = d;
}
if (abc9_flop)
@@ -432,7 +432,8 @@ struct XAigerWriter
// that has been padded to its full width
if (bit == State::Sx)
continue;
- log_assert(!aig_map.count(bit));
+ if (aig_map.count(bit))
+ log_error("Visited AIG node more than once; this could be a combinatorial loop that has not been broken\n");
aig_map[bit] = 2*aig_m;
}
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
index 088819042..ba29d9090 100644
--- a/backends/blif/blif.cc
+++ b/backends/blif/blif.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 692452aad..d62cc4c3d 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,7 @@
*/
// [[CITE]] Btor2 , BtorMC and Boolector 3.0
-// Aina Niemetz, Mathias Preiner, Clifford Wolf, Armin Biere
+// Aina Niemetz, Mathias Preiner, C. Wolf, Armin Biere
// Computer Aided Verification - 30th International Conference, CAV 2018
// https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/
@@ -708,7 +708,7 @@ struct BtorWorker
goto okay;
}
- if (cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit)))
+ if (cell->is_mem_cell())
{
Mem *mem = mem_cells[cell];
@@ -728,10 +728,11 @@ struct BtorWorker
log_error("Memory %s.%s has mixed async/sync write ports.\n",
log_id(module), log_id(mem->memid));
- for (auto &port : mem->rd_ports)
+ for (auto &port : mem->rd_ports) {
if (port.clk_enable)
- log_error("Memory %s.%s has sync read ports.\n",
+ log_error("Memory %s.%s has sync read ports. Please use memory_nordff to convert them first.\n",
log_id(module), log_id(mem->memid));
+ }
int data_sid = get_bv_sid(mem->width);
int bool_sid = get_bv_sid(1);
@@ -864,7 +865,7 @@ struct BtorWorker
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_btor`.\n",
log_id(cell->type), log_id(module), log_id(cell));
}
- if (cell->type.in(ID($adff), ID($adffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF") {
+ if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_btor`.\n",
log_id(cell->type), log_id(module), log_id(cell));
}
@@ -1080,10 +1081,12 @@ struct BtorWorker
memories = Mem::get_all_memories(module);
dict<IdString, Mem*> mem_dict;
- for (auto &mem : memories)
+ for (auto &mem : memories) {
+ mem.narrow();
mem_dict[mem.memid] = &mem;
+ }
for (auto cell : module->cells())
- if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit)))
+ if (cell->is_mem_cell())
mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()];
btorf_push("inputs");
@@ -1396,6 +1399,11 @@ struct BtorBackend : public Backend {
log_header(design, "Executing BTOR backend.\n");
+ log_push();
+ Pass::call(design, "bmuxmap");
+ Pass::call(design, "demuxmap");
+ log_pop();
+
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index 0e55c46c2..b4ffa87cd 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -457,6 +457,42 @@ struct value : public expr_base<value<Bits>> {
return shr<AmountBits, /*Signed=*/true>(amount);
}
+ template<size_t ResultBits, size_t SelBits>
+ value<ResultBits> bmux(const value<SelBits> &sel) const {
+ static_assert(ResultBits << SelBits == Bits, "invalid sizes used in bmux()");
+ size_t amount = sel.data[0] * ResultBits;
+ size_t shift_chunks = amount / chunk::bits;
+ size_t shift_bits = amount % chunk::bits;
+ value<ResultBits> result;
+ chunk::type carry = 0;
+ if (ResultBits % chunk::bits + shift_bits > chunk::bits)
+ carry = data[result.chunks + shift_chunks] << (chunk::bits - shift_bits);
+ for (size_t n = 0; n < result.chunks; n++) {
+ result.data[result.chunks - 1 - n] = carry | (data[result.chunks + shift_chunks - 1 - n] >> shift_bits);
+ carry = (shift_bits == 0) ? 0
+ : data[result.chunks + shift_chunks - 1 - n] << (chunk::bits - shift_bits);
+ }
+ return result;
+ }
+
+ template<size_t ResultBits, size_t SelBits>
+ value<ResultBits> demux(const value<SelBits> &sel) const {
+ static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()");
+ size_t amount = sel.data[0] * Bits;
+ size_t shift_chunks = amount / chunk::bits;
+ size_t shift_bits = amount % chunk::bits;
+ value<ResultBits> result;
+ chunk::type carry = 0;
+ for (size_t n = 0; n < chunks; n++) {
+ result.data[shift_chunks + n] = (data[n] << shift_bits) | carry;
+ carry = (shift_bits == 0) ? 0
+ : data[n] >> (chunk::bits - shift_bits);
+ }
+ if (Bits % chunk::bits + shift_bits > chunk::bits)
+ result.data[shift_chunks + chunks] = carry;
+ return result;
+ }
+
size_t ctpop() const {
size_t count = 0;
for (size_t n = 0; n < chunks; n++) {
@@ -722,50 +758,32 @@ std::ostream &operator<<(std::ostream &os, const wire<Bits> &val) {
template<size_t Width>
struct memory {
- std::vector<value<Width>> data;
+ const size_t depth;
+ std::unique_ptr<value<Width>[]> data;
- size_t depth() const {
- return data.size();
- }
-
- memory() = delete;
- explicit memory(size_t depth) : data(depth) {}
+ explicit memory(size_t depth) : depth(depth), data(new value<Width>[depth]) {}
memory(const memory<Width> &) = delete;
memory<Width> &operator=(const memory<Width> &) = delete;
memory(memory<Width> &&) = default;
- memory<Width> &operator=(memory<Width> &&) = default;
-
- // The only way to get the compiler to put the initializer in .rodata and do not copy it on stack is to stuff it
- // into a plain array. You'd think an std::initializer_list would work here, but it doesn't, because you can't
- // construct an initializer_list in a constexpr (or something) and so if you try to do that the whole thing is
- // first copied on the stack (probably overflowing it) and then again into `data`.
- template<size_t Size>
- struct init {
- size_t offset;
- value<Width> data[Size];
- };
-
- template<size_t... InitSize>
- explicit memory(size_t depth, const init<InitSize> &...init) : data(depth) {
- data.resize(depth);
- // This utterly reprehensible construct is the most reasonable way to apply a function to every element
- // of a parameter pack, if the elements all have different types and so cannot be cast to an initializer list.
- auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...};
- (void)_;
+ memory<Width> &operator=(memory<Width> &&other) {
+ assert(depth == other.depth);
+ data = std::move(other.data);
+ write_queue = std::move(other.write_queue);
+ return *this;
}
// 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());
+ assert(index < depth);
return data[index];
}
// 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());
+ assert(index < depth);
return data[index];
}
@@ -790,7 +808,7 @@ struct memory {
std::vector<write> write_queue;
void update(size_t index, const value<Width> &val, const value<Width> &mask, int priority = 0) {
- assert(index < data.size());
+ assert(index < depth);
// Queue up the write while keeping the queue sorted by priority.
write_queue.insert(
std::upper_bound(write_queue.begin(), write_queue.end(), priority,
@@ -947,9 +965,9 @@ struct debug_item : ::cxxrtl_object {
flags = 0;
width = Width;
lsb_at = 0;
- depth = item.data.size();
+ depth = item.depth;
zero_at = zero_offset;
- curr = item.data.empty() ? nullptr : item.data[0].data;
+ curr = item.data ? item.data[0].data : nullptr;
next = nullptr;
outline = nullptr;
}
@@ -999,6 +1017,22 @@ struct debug_item : ::cxxrtl_object {
next = nullptr;
outline = &group;
}
+
+ template<size_t Bits, class IntegerT>
+ IntegerT get() const {
+ assert(width == Bits && depth == 1);
+ value<Bits> item;
+ std::copy(curr, curr + value<Bits>::chunks, item.data);
+ return item.template get<IntegerT>();
+ }
+
+ template<size_t Bits, class IntegerT>
+ void set(IntegerT other) const {
+ assert(width == Bits && depth == 1);
+ value<Bits> item;
+ item.template set<IntegerT>(other);
+ std::copy(item.data, item.data + value<Bits>::chunks, next);
+ }
};
static_assert(std::is_standard_layout<debug_item>::value, "debug_item is not compatible with C layout");
@@ -1035,9 +1069,9 @@ struct debug_items {
}
};
-// Tag class to disambiguate module move constructor and module constructor that takes black boxes
-// out of another instance of the module.
-struct adopt {};
+// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(),
+// and the constructor of interior modules that should not call it.
+struct interior {};
struct module {
module() {}
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index 39046bd78..404755b1e 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -198,7 +198,7 @@ bool is_extending_cell(RTLIL::IdString type)
bool is_inlinable_cell(RTLIL::IdString type)
{
return is_unary_cell(type) || is_binary_cell(type) || type.in(
- ID($mux), ID($concat), ID($slice), ID($pmux));
+ ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux));
}
bool is_ff_cell(RTLIL::IdString type)
@@ -206,6 +206,7 @@ bool is_ff_cell(RTLIL::IdString type)
return type.in(
ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce),
ID($adff), ID($adffe), ID($dffsr), ID($dffsre),
+ ID($aldff), ID($aldffe),
ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr));
}
@@ -216,7 +217,7 @@ bool is_internal_cell(RTLIL::IdString type)
bool is_effectful_cell(RTLIL::IdString type)
{
- return type == ID($memwr) || type.isPublic();
+ return type.isPublic();
}
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
@@ -226,6 +227,14 @@ bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
return cell_module->get_bool_attribute(ID(cxxrtl_blackbox));
}
+bool is_memwr_process(const RTLIL::Process *process)
+{
+ for (auto sync : process->syncs)
+ if (!sync->mem_write_actions.empty())
+ return true;
+ return false;
+}
+
enum class CxxrtlPortType {
UNKNOWN = 0, // or mixed comb/sync
COMB = 1,
@@ -274,12 +283,16 @@ struct FlowGraph {
CELL_EVAL,
PROCESS_SYNC,
PROCESS_CASE,
+ MEM_RDPORT,
+ MEM_WRPORTS,
};
Type type;
RTLIL::SigSig connect = {};
- const RTLIL::Cell *cell = NULL;
- const RTLIL::Process *process = NULL;
+ const RTLIL::Cell *cell = nullptr;
+ const RTLIL::Process *process = nullptr;
+ const Mem *mem = nullptr;
+ int portidx;
};
std::vector<Node*> nodes;
@@ -314,8 +327,14 @@ struct FlowGraph {
for (auto bit : sig.bits())
bit_has_state[bit] |= is_ff;
// Only comb defs of an entire wire in the right order can be inlined.
- if (!is_ff && sig.is_wire())
- wire_def_inlinable[sig.as_wire()] = inlinable;
+ if (!is_ff && sig.is_wire()) {
+ // Only a single def of a wire can be inlined. (Multiple defs of a wire are unsound, but we
+ // handle them anyway to avoid assertion failures later.)
+ if (!wire_def_inlinable.count(sig.as_wire()))
+ wire_def_inlinable[sig.as_wire()] = inlinable;
+ else
+ wire_def_inlinable[sig.as_wire()] = false;
+ }
}
void add_uses(Node *node, const RTLIL::SigSpec &sig)
@@ -414,7 +433,7 @@ struct FlowGraph {
if (cell->output(conn.first)) {
if (is_inlinable_cell(cell->type))
add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/true);
- else if (is_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool()))
+ else if (is_ff_cell(cell->type))
add_defs(node, conn.second, /*is_ff=*/true, /*inlinable=*/false);
else if (is_internal_cell(cell->type))
add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false);
@@ -477,14 +496,20 @@ struct FlowGraph {
void add_sync_rules_defs_uses(Node *node, const RTLIL::Process *process)
{
- for (auto sync : process->syncs)
- for (auto action : sync->actions) {
+ for (auto sync : process->syncs) {
+ for (auto &action : sync->actions) {
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe)
- add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false);
+ add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false);
else
add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false);
add_uses(node, action.second);
}
+ for (auto &memwr : sync->mem_write_actions) {
+ add_uses(node, memwr.address);
+ add_uses(node, memwr.data);
+ add_uses(node, memwr.enable);
+ }
+ }
}
Node *add_node(const RTLIL::Process *process)
@@ -502,6 +527,51 @@ struct FlowGraph {
add_case_rule_defs_uses(node, &process->root_case);
return node;
}
+
+ // Memories
+ void add_node(const Mem *mem) {
+ for (int i = 0; i < GetSize(mem->rd_ports); i++) {
+ auto &port = mem->rd_ports[i];
+ Node *node = new Node;
+ node->type = Node::Type::MEM_RDPORT;
+ node->mem = mem;
+ node->portidx = i;
+ nodes.push_back(node);
+ add_defs(node, port.data, /*is_ff=*/port.clk_enable, /*inlinable=*/false);
+ add_uses(node, port.clk);
+ add_uses(node, port.en);
+ add_uses(node, port.arst);
+ add_uses(node, port.srst);
+ add_uses(node, port.addr);
+ bool transparent = false;
+ for (int j = 0; j < GetSize(mem->wr_ports); j++) {
+ auto &wrport = mem->wr_ports[j];
+ if (port.transparency_mask[j]) {
+ // Our implementation of transparent read ports reads en, addr and data from every write port
+ // the read port is transparent with.
+ add_uses(node, wrport.en);
+ add_uses(node, wrport.addr);
+ add_uses(node, wrport.data);
+ transparent = true;
+ }
+ }
+ // Also we read the read address twice in this case (prevent inlining).
+ if (transparent)
+ add_uses(node, port.addr);
+ }
+ if (!mem->wr_ports.empty()) {
+ Node *node = new Node;
+ node->type = Node::Type::MEM_WRPORTS;
+ node->mem = mem;
+ nodes.push_back(node);
+ for (auto &port : mem->wr_ports) {
+ add_uses(node, port.clk);
+ add_uses(node, port.en);
+ add_uses(node, port.addr);
+ add_uses(node, port.data);
+ }
+ }
+ }
};
std::vector<std::string> split_by(const std::string &str, const std::string &sep)
@@ -614,6 +684,8 @@ struct CxxrtlWorker {
std::ostream *impl_f = nullptr;
std::ostream *intf_f = nullptr;
+ bool print_wire_types = false;
+ bool print_debug_wire_types = false;
bool run_hierarchy = false;
bool run_flatten = false;
bool run_proc = false;
@@ -635,10 +707,11 @@ struct CxxrtlWorker {
int temporary = 0;
dict<const RTLIL::Module*, SigMap> sigmaps;
+ dict<const RTLIL::Module*, std::vector<Mem>> mod_memories;
+ pool<std::pair<const RTLIL::Module*, RTLIL::IdString>> writable_memories;
pool<const RTLIL::Wire*> edge_wires;
+ dict<const RTLIL::Wire*, RTLIL::Const> wire_init;
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::Module*, std::vector<FlowGraph::Node>> schedule, debug_schedule;
dict<const RTLIL::Wire*, WireType> wire_types, debug_wire_types;
dict<RTLIL::SigBit, bool> bit_has_state;
@@ -722,6 +795,11 @@ struct CxxrtlWorker {
return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox)));
}
+ std::string mangle(const Mem *mem)
+ {
+ return mangle_memory_name(mem->memid);
+ }
+
std::string mangle(const RTLIL::Memory *memory)
{
return mangle_memory_name(memory->name);
@@ -856,11 +934,6 @@ struct CxxrtlWorker {
f << "}";
}
- void dump_const_init(const RTLIL::Const &data)
- {
- dump_const_init(data, data.size());
- }
-
void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)
{
f << "value<" << width << ">";
@@ -1081,6 +1154,22 @@ struct CxxrtlWorker {
for (int part = 0; part < s_width; part++) {
f << ")";
}
+ // Big muxes
+ } else if (cell->type == ID($bmux)) {
+ dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
+ f << ".bmux<";
+ f << cell->getParam(ID::WIDTH).as_int();
+ f << ">(";
+ dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
+ f << ").val()";
+ // Demuxes
+ } else if (cell->type == ID($demux)) {
+ dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
+ f << ".demux<";
+ f << GetSize(cell->getPort(ID::Y));
+ f << ">(";
+ dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
+ f << ").val()";
// Concats
} else if (cell->type == ID($concat)) {
dump_sigspec_rhs(cell->getPort(ID::B), for_debug);
@@ -1190,6 +1279,20 @@ struct CxxrtlWorker {
dec_indent();
f << indent << "}\n";
}
+ if (cell->hasPort(ID::ALOAD)) {
+ // Asynchronous load
+ f << indent << "if (";
+ dump_sigspec_rhs(cell->getPort(ID::ALOAD));
+ f << " == value<1> {" << cell->getParam(ID::ALOAD_POLARITY).as_bool() << "u}) {\n";
+ inc_indent();
+ f << indent;
+ dump_sigspec_lhs(cell->getPort(ID::Q));
+ f << " = ";
+ dump_sigspec_rhs(cell->getPort(ID::AD));
+ f << ";\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
if (cell->hasPort(ID::SET)) {
// Asynchronous set (for individual bits)
f << indent;
@@ -1214,112 +1317,6 @@ struct CxxrtlWorker {
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()) {
- log_assert(!for_debug);
- RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0];
- clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
- if (clk_bit.wire) {
- f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_")
- << mangle(clk_bit) << ") {\n";
- } else {
- f << indent << "if (false) {\n";
- }
- inc_indent();
- }
- 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));
- 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();
- if (has_enable) {
- f << indent << "if (";
- dump_sigspec_rhs(cell->getPort(ID::EN));
- f << ") {\n";
- inc_indent();
- }
- // The generated code has two bounds checks; one in an assertion, and another that guards the read.
- // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
- // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG
- // not only for release builds, but also to make sure the simulator (which is presumably embedded in some
- // larger program) will never crash the code that calls into it.
- //
- // If assertions are disabled, out of bounds reads are defined to return zero.
- f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n";
- f << indent << "if(" << valid_index_temp << ".valid) {\n";
- inc_indent();
- if (writable_memories[memory]) {
- std::string lhs_temp = fresh_temporary();
- f << indent << "value<" << memory->width << "> " << lhs_temp << " = "
- << mangle(memory) << "[" << valid_index_temp << ".index];\n";
- std::vector<const RTLIL::Cell*> memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end());
- if (!memwr_cells.empty()) {
- 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 << ";\n";
- 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();
- });
- for (auto memwr_cell : memwr_cells) {
- f << indent << "if (" << addr_temp << " == ";
- 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));
- f << ", ";
- dump_sigspec_rhs(memwr_cell->getPort(ID::EN));
- f << ");\n";
- dec_indent();
- f << indent << "}\n";
- }
- }
- f << indent;
- dump_sigspec_lhs(cell->getPort(ID::DATA));
- f << " = " << lhs_temp << ";\n";
- } else {
- f << indent;
- 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));
- f << " = value<" << memory->width << "> {};\n";
- dec_indent();
- f << indent << "}\n";
- if (has_enable) {
- dec_indent();
- f << indent << "}\n";
- }
- } else /*if (cell->type == ID($memwr))*/ {
- log_assert(writable_memories[memory]);
- // See above for rationale of having both the assert and the condition.
- //
- // If assertions are disabled, out of bounds writes are defined to do nothing.
- f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n";
- 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));
- f << ", ";
- 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()) {
- dec_indent();
- f << indent << "}\n";
- }
// Internal cells
} else if (is_internal_cell(cell->type)) {
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
@@ -1332,7 +1329,7 @@ struct CxxrtlWorker {
for (auto conn : cell->connections())
if (cell->input(conn.first)) {
RTLIL::Module *cell_module = cell->module->design->module(cell->type);
- log_assert(cell_module != nullptr && cell_module->wire(conn.first) && conn.second.is_wire());
+ log_assert(cell_module != nullptr && cell_module->wire(conn.first));
RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first);
f << indent << mangle(cell) << access << mangle_wire_name(conn.first);
if (!is_cxxrtl_blackbox_cell(cell) && wire_types[cell_module_wire].is_buffered()) {
@@ -1342,7 +1339,7 @@ struct CxxrtlWorker {
f << " = ";
dump_sigspec_rhs(conn.second);
f << ";\n";
- if (getenv("CXXRTL_VOID_MY_WARRANTY")) {
+ if (getenv("CXXRTL_VOID_MY_WARRANTY") && conn.second.is_wire()) {
// 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:
@@ -1411,30 +1408,30 @@ struct CxxrtlWorker {
collect_sigspec_rhs(port.second, for_debug, cells);
}
- void dump_assign(const RTLIL::SigSig &sigsig)
+ void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false)
{
f << indent;
- dump_sigspec_lhs(sigsig.first);
+ dump_sigspec_lhs(sigsig.first, for_debug);
f << " = ";
- dump_sigspec_rhs(sigsig.second);
+ dump_sigspec_rhs(sigsig.second, for_debug);
f << ";\n";
}
- void dump_case_rule(const RTLIL::CaseRule *rule)
+ void dump_case_rule(const RTLIL::CaseRule *rule, bool for_debug = false)
{
for (auto action : rule->actions)
- dump_assign(action);
+ dump_assign(action, for_debug);
for (auto switch_ : rule->switches)
- dump_switch_rule(switch_);
+ dump_switch_rule(switch_, for_debug);
}
- void dump_switch_rule(const RTLIL::SwitchRule *rule)
+ void dump_switch_rule(const RTLIL::SwitchRule *rule, bool for_debug = false)
{
// The switch attributes are printed before the switch condition is captured.
dump_attrs(rule);
std::string signal_temp = fresh_temporary();
f << indent << "const value<" << rule->signal.size() << "> &" << signal_temp << " = ";
- dump_sigspec(rule->signal, /*is_lhs=*/false);
+ dump_sigspec(rule->signal, /*is_lhs=*/false, for_debug);
f << ";\n";
bool first = true;
@@ -1454,7 +1451,7 @@ struct CxxrtlWorker {
first = false;
if (compare.is_fully_def()) {
f << signal_temp << " == ";
- dump_sigspec(compare, /*is_lhs=*/false);
+ dump_sigspec(compare, /*is_lhs=*/false, for_debug);
} else if (compare.is_fully_const()) {
RTLIL::Const compare_mask, compare_value;
for (auto bit : compare.as_const()) {
@@ -1488,30 +1485,34 @@ struct CxxrtlWorker {
}
f << "{\n";
inc_indent();
- dump_case_rule(case_);
+ dump_case_rule(case_, for_debug);
dec_indent();
}
f << indent << "}\n";
}
- void dump_process_case(const RTLIL::Process *proc)
+ void dump_process_case(const RTLIL::Process *proc, bool for_debug = false)
{
dump_attrs(proc);
f << indent << "// process " << proc->name.str() << " case\n";
// The case attributes (for root case) are always empty.
log_assert(proc->root_case.attributes.empty());
- dump_case_rule(&proc->root_case);
+ dump_case_rule(&proc->root_case, for_debug);
}
- void dump_process_syncs(const RTLIL::Process *proc)
+ void dump_process_syncs(const RTLIL::Process *proc, bool for_debug = false)
{
dump_attrs(proc);
f << indent << "// process " << proc->name.str() << " syncs\n";
for (auto sync : proc->syncs) {
+ log_assert(!for_debug || sync->type == RTLIL::STa);
+
RTLIL::SigBit sync_bit;
if (!sync->signal.empty()) {
sync_bit = sync->signal[0];
sync_bit = sigmaps[sync_bit.wire->module](sync_bit);
+ if (!sync_bit.is_wire())
+ continue; // a clock, or more commonly a reset, can be tied to a constant driver
}
pool<std::string> events;
@@ -1551,8 +1552,224 @@ struct CxxrtlWorker {
}
f << ") {\n";
inc_indent();
- for (auto action : sync->actions)
- dump_assign(action);
+ for (auto &action : sync->actions)
+ dump_assign(action, for_debug);
+ for (auto &memwr : sync->mem_write_actions) {
+ RTLIL::Memory *memory = proc->module->memories.at(memwr.memid);
+ std::string valid_index_temp = fresh_temporary();
+ f << indent << "auto " << valid_index_temp << " = memory_index(";
+ dump_sigspec_rhs(memwr.address);
+ f << ", " << memory->start_offset << ", " << memory->size << ");\n";
+ // See below for rationale of having both the assert and the condition.
+ //
+ // If assertions are disabled, out of bounds writes are defined to do nothing.
+ f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n";
+ f << indent << "if (" << valid_index_temp << ".valid) {\n";
+ inc_indent();
+ f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, ";
+ dump_sigspec_rhs(memwr.data);
+ f << ", ";
+ dump_sigspec_rhs(memwr.enable);
+ f << ");\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
+ dec_indent();
+ f << indent << "}\n";
+ }
+ }
+ }
+
+ void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false)
+ {
+ auto &port = mem->rd_ports[portidx];
+ dump_attrs(&port);
+ f << indent << "// memory " << mem->memid.str() << " read port " << portidx << "\n";
+ if (port.clk_enable) {
+ log_assert(!for_debug);
+ RTLIL::SigBit clk_bit = port.clk[0];
+ clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
+ if (clk_bit.wire) {
+ f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_")
+ << mangle(clk_bit) << ") {\n";
+ } else {
+ f << indent << "if (false) {\n";
+ }
+ inc_indent();
+ }
+ std::vector<const RTLIL::Cell*> inlined_cells_addr;
+ collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr);
+ if (!inlined_cells_addr.empty())
+ dump_inlined_cells(inlined_cells_addr);
+ std::string valid_index_temp = fresh_temporary();
+ f << indent << "auto " << valid_index_temp << " = memory_index(";
+ // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous
+ // memory read ports can.
+ dump_sigspec_rhs(port.addr, for_debug);
+ f << ", " << mem->start_offset << ", " << mem->size << ");\n";
+ bool has_enable = port.clk_enable && !port.en.is_fully_ones();
+ if (has_enable) {
+ std::vector<const RTLIL::Cell*> inlined_cells_en;
+ collect_sigspec_rhs(port.en, for_debug, inlined_cells_en);
+ if (!inlined_cells_en.empty())
+ dump_inlined_cells(inlined_cells_en);
+ f << indent << "if (";
+ dump_sigspec_rhs(port.en);
+ f << ") {\n";
+ inc_indent();
+ }
+ // The generated code has two bounds checks; one in an assertion, and another that guards the read.
+ // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
+ // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG
+ // not only for release builds, but also to make sure the simulator (which is presumably embedded in some
+ // larger program) will never crash the code that calls into it.
+ //
+ // If assertions are disabled, out of bounds reads are defined to return zero.
+ f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n";
+ f << indent << "if(" << valid_index_temp << ".valid) {\n";
+ inc_indent();
+ if (!mem->wr_ports.empty()) {
+ std::string lhs_temp = fresh_temporary();
+ f << indent << "value<" << mem->width << "> " << lhs_temp << " = "
+ << mangle(mem) << "[" << valid_index_temp << ".index];\n";
+ bool transparent = false;
+ for (auto bit : port.transparency_mask)
+ if (bit)
+ transparent = true;
+ if (transparent) {
+ std::string addr_temp = fresh_temporary();
+ f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = ";
+ dump_sigspec_rhs(port.addr);
+ f << ";\n";
+ for (int i = 0; i < GetSize(mem->wr_ports); i++) {
+ auto &wrport = mem->wr_ports[i];
+ if (!port.transparency_mask[i])
+ continue;
+ f << indent << "if (" << addr_temp << " == ";
+ dump_sigspec_rhs(wrport.addr);
+ f << ") {\n";
+ inc_indent();
+ f << indent << lhs_temp << " = " << lhs_temp;
+ f << ".update(";
+ dump_sigspec_rhs(wrport.data);
+ f << ", ";
+ dump_sigspec_rhs(wrport.en);
+ f << ");\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
+ }
+ f << indent;
+ dump_sigspec_lhs(port.data);
+ f << " = " << lhs_temp << ";\n";
+ } else {
+ f << indent;
+ dump_sigspec_lhs(port.data);
+ f << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n";
+ }
+ dec_indent();
+ f << indent << "} else {\n";
+ inc_indent();
+ f << indent;
+ dump_sigspec_lhs(port.data);
+ f << " = value<" << mem->width << "> {};\n";
+ dec_indent();
+ f << indent << "}\n";
+ if (has_enable && !port.ce_over_srst) {
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (port.srst != State::S0) {
+ // Synchronous reset
+ std::vector<const RTLIL::Cell*> inlined_cells_srst;
+ collect_sigspec_rhs(port.srst, for_debug, inlined_cells_srst);
+ if (!inlined_cells_srst.empty())
+ dump_inlined_cells(inlined_cells_srst);
+ f << indent << "if (";
+ dump_sigspec_rhs(port.srst);
+ f << " == value<1> {1u}) {\n";
+ inc_indent();
+ f << indent;
+ dump_sigspec_lhs(port.data);
+ f << " = ";
+ dump_const(port.srst_value);
+ f << ";\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (has_enable && port.ce_over_srst) {
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (port.clk_enable) {
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (port.arst != State::S0) {
+ // Asynchronous reset
+ std::vector<const RTLIL::Cell*> inlined_cells_arst;
+ collect_sigspec_rhs(port.arst, for_debug, inlined_cells_arst);
+ if (!inlined_cells_arst.empty())
+ dump_inlined_cells(inlined_cells_arst);
+ f << indent << "if (";
+ dump_sigspec_rhs(port.arst);
+ f << " == value<1> {1u}) {\n";
+ inc_indent();
+ f << indent;
+ dump_sigspec_lhs(port.data);
+ f << " = ";
+ dump_const(port.arst_value);
+ f << ";\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
+ }
+
+ void dump_mem_wrports(const Mem *mem, bool for_debug = false)
+ {
+ log_assert(!for_debug);
+ for (int portidx = 0; portidx < GetSize(mem->wr_ports); portidx++) {
+ auto &port = mem->wr_ports[portidx];
+ dump_attrs(&port);
+ f << indent << "// memory " << mem->memid.str() << " write port " << portidx << "\n";
+ if (port.clk_enable) {
+ RTLIL::SigBit clk_bit = port.clk[0];
+ clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
+ if (clk_bit.wire) {
+ f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_")
+ << mangle(clk_bit) << ") {\n";
+ } else {
+ f << indent << "if (false) {\n";
+ }
+ inc_indent();
+ }
+ std::vector<const RTLIL::Cell*> inlined_cells_addr;
+ collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr);
+ if (!inlined_cells_addr.empty())
+ dump_inlined_cells(inlined_cells_addr);
+ std::string valid_index_temp = fresh_temporary();
+ f << indent << "auto " << valid_index_temp << " = memory_index(";
+ dump_sigspec_rhs(port.addr);
+ f << ", " << mem->start_offset << ", " << mem->size << ");\n";
+ // See above for rationale of having both the assert and the condition.
+ //
+ // If assertions are disabled, out of bounds writes are defined to do nothing.
+ f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n";
+ f << indent << "if (" << valid_index_temp << ".valid) {\n";
+ inc_indent();
+ std::vector<const RTLIL::Cell*> inlined_cells;
+ collect_sigspec_rhs(port.data, for_debug, inlined_cells);
+ collect_sigspec_rhs(port.en, for_debug, inlined_cells);
+ if (!inlined_cells.empty())
+ dump_inlined_cells(inlined_cells);
+ f << indent << mangle(mem) << ".update(" << valid_index_temp << ".index, ";
+ dump_sigspec_rhs(port.data);
+ f << ", ";
+ dump_sigspec_rhs(port.en);
+ f << ", " << portidx << ");\n";
+ dec_indent();
+ f << indent << "}\n";
+ if (port.clk_enable) {
dec_indent();
f << indent << "}\n";
}
@@ -1579,20 +1796,10 @@ struct CxxrtlWorker {
} else {
f << "<" << wire->width << ">";
}
- f << " " << mangle(wire);
- if (wire->has_attribute(ID::init)) {
- f << " ";
- dump_const_init(wire->attributes.at(ID::init));
- }
- f << ";\n";
+ f << " " << mangle(wire) << ";\n";
if (edge_wires[wire]) {
if (!wire_type.is_buffered()) {
- f << indent << "value<" << wire->width << "> prev_" << mangle(wire);
- if (wire->has_attribute(ID::init)) {
- f << " ";
- dump_const_init(wire->attributes.at(ID::init));
- }
- f << ";\n";
+ f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n";
}
for (auto edge_type : edge_types) {
if (edge_type.first.wire == wire) {
@@ -1642,49 +1849,67 @@ struct CxxrtlWorker {
f << "value<" << wire->width << "> " << mangle(wire) << ";\n";
}
- void dump_memory(RTLIL::Module *module, const RTLIL::Memory *memory)
+ void dump_reset_method(RTLIL::Module *module)
{
- 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())
- init_cells.push_back(cell);
+ int mem_init_idx = 0;
+ inc_indent();
+ for (auto wire : module->wires()) {
+ const auto &wire_type = wire_types[wire];
+ if (!wire_type.is_named() || wire_type.is_local()) continue;
+ if (!wire_init.count(wire)) continue;
- 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();
- return a_prio > b_prio || (a_prio == b_prio && a_addr < b_addr);
- });
+ f << indent << mangle(wire) << " = ";
+ if (wire_types[wire].is_buffered()) {
+ f << "wire<" << wire->width << ">";
+ } else {
+ f << "value<" << wire->width << ">";
+ }
+ dump_const_init(wire_init.at(wire), wire->width);
+ f << ";\n";
- dump_attrs(memory);
- f << indent << "memory<" << memory->width << "> " << mangle(memory)
- << " { " << memory->size << "u";
- if (init_cells.empty()) {
- f << " };\n";
- } else {
- f << ",\n";
- 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();
- f << indent << "memory<" << memory->width << ">::init<" << words << "> { "
- << stringf("%#x", cell->getPort(ID::ADDR).as_int()) << ", {";
+ if (edge_wires[wire] && !wire_types[wire].is_buffered()) {
+ f << indent << "prev_" << mangle(wire) << " = ";
+ dump_const(wire_init.at(wire), wire->width);
+ f << ";\n";
+ }
+ }
+ for (auto &mem : mod_memories[module]) {
+ for (auto &init : mem.inits) {
+ if (init.removed)
+ continue;
+ dump_attrs(&init);
+ int words = GetSize(init.data) / mem.width;
+ f << indent << "static const value<" << mem.width << "> ";
+ f << "mem_init_" << ++mem_init_idx << "[" << words << "] {";
inc_indent();
- for (size_t n = 0; n < words; n++) {
+ for (int n = 0; n < words; n++) {
if (n % 4 == 0)
f << "\n" << indent;
else
f << " ";
- dump_const(data, width, n * width, /*fixed_width=*/true);
+ dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true);
f << ",";
}
dec_indent();
- f << "\n" << indent << "}},\n";
+ f << "\n";
+ f << indent << "};\n";
+ f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), ";
+ f << "std::end(mem_init_" << mem_init_idx << "), ";
+ f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n";
}
- dec_indent();
- f << indent << "};\n";
- }
+ }
+ for (auto cell : module->cells()) {
+ if (is_internal_cell(cell->type))
+ continue;
+ f << indent << mangle(cell);
+ RTLIL::Module *cell_module = module->design->module(cell->type);
+ if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
+ f << "->reset();\n";
+ } else {
+ f << ".reset();\n";
+ }
+ }
+ dec_indent();
}
void dump_eval_method(RTLIL::Module *module)
@@ -1721,11 +1946,17 @@ struct CxxrtlWorker {
case FlowGraph::Node::Type::CELL_EVAL:
dump_cell_eval(node.cell);
break;
+ case FlowGraph::Node::Type::PROCESS_CASE:
+ dump_process_case(node.process);
+ break;
case FlowGraph::Node::Type::PROCESS_SYNC:
dump_process_syncs(node.process);
break;
- case FlowGraph::Node::Type::PROCESS_CASE:
- dump_process_case(node.process);
+ case FlowGraph::Node::Type::MEM_RDPORT:
+ dump_mem_rdport(node.mem, node.portidx);
+ break;
+ case FlowGraph::Node::Type::MEM_WRPORTS:
+ dump_mem_wrports(node.mem);
break;
}
}
@@ -1750,6 +1981,18 @@ struct CxxrtlWorker {
case FlowGraph::Node::Type::CELL_EVAL:
dump_cell_eval(node.cell, /*for_debug=*/true);
break;
+ case FlowGraph::Node::Type::PROCESS_CASE:
+ dump_process_case(node.process, /*for_debug=*/true);
+ break;
+ case FlowGraph::Node::Type::PROCESS_SYNC:
+ dump_process_syncs(node.process, /*for_debug=*/true);
+ break;
+ case FlowGraph::Node::Type::MEM_RDPORT:
+ dump_mem_rdport(node.mem, node.portidx, /*for_debug=*/true);
+ break;
+ case FlowGraph::Node::Type::MEM_WRPORTS:
+ dump_mem_wrports(node.mem, /*for_debug=*/true);
+ break;
default:
log_abort();
}
@@ -1769,10 +2012,10 @@ struct CxxrtlWorker {
f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n";
}
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
- for (auto memory : module->memories) {
- if (!writable_memories[memory.second])
+ for (auto &mem : mod_memories[module]) {
+ if (!writable_memories.count({module, mem.memid}))
continue;
- f << indent << "if (" << mangle(memory.second) << ".commit()) changed = true;\n";
+ f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n";
}
for (auto cell : module->cells()) {
if (is_internal_cell(cell->type))
@@ -1914,12 +2157,12 @@ struct CxxrtlWorker {
}
}
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
- for (auto &memory_it : module->memories) {
- if (!memory_it.first.isPublic())
+ for (auto &mem : mod_memories[module]) {
+ if (!mem.memid.isPublic())
continue;
- f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(memory_it.second));
- f << ", debug_item(" << mangle(memory_it.second) << ", ";
- f << memory_it.second->start_offset << "));\n";
+ f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
+ f << ", debug_item(" << mangle(&mem) << ", ";
+ f << mem.start_offset << "));\n";
}
for (auto cell : module->cells()) {
if (is_internal_cell(cell->type))
@@ -1987,6 +2230,10 @@ struct CxxrtlWorker {
dump_wire(wire, /*is_local=*/false);
}
f << "\n";
+ f << indent << "void reset() override {\n";
+ dump_reset_method(module);
+ f << indent << "}\n";
+ f << "\n";
f << indent << "bool eval() override {\n";
dump_eval_method(module);
f << indent << "}\n";
@@ -2034,8 +2281,10 @@ struct CxxrtlWorker {
for (auto wire : module->wires())
dump_debug_wire(wire, /*is_local=*/false);
bool has_memories = false;
- for (auto memory : module->memories) {
- dump_memory(module, memory.second);
+ for (auto &mem : mod_memories[module]) {
+ dump_attrs(&mem);
+ f << indent << "memory<" << mem.width << "> " << mangle(&mem)
+ << " { " << mem.size << "u };\n";
has_memories = true;
}
if (has_memories)
@@ -2056,52 +2305,20 @@ struct CxxrtlWorker {
dump_metadata_map(cell->attributes);
f << ");\n";
} else {
- f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n";
+ f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n";
}
has_cells = true;
}
if (has_cells)
f << "\n";
- f << indent << mangle(module) << "() {}\n";
- if (has_cells) {
- f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) :\n";
- bool first = true;
- for (auto cell : module->cells()) {
- if (is_internal_cell(cell->type))
- continue;
- if (first) {
- first = false;
- } else {
- f << ",\n";
- }
- RTLIL::Module *cell_module = module->design->module(cell->type);
- if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
- f << indent << " " << mangle(cell) << "(std::move(other." << mangle(cell) << "))";
- } else {
- f << indent << " " << mangle(cell) << "(adopt {}, std::move(other." << mangle(cell) << "))";
- }
- }
- f << " {\n";
- inc_indent();
- for (auto cell : module->cells()) {
- if (is_internal_cell(cell->type))
- continue;
- RTLIL::Module *cell_module = module->design->module(cell->type);
- if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
- f << indent << mangle(cell) << "->reset();\n";
- }
- dec_indent();
- f << indent << "}\n";
- } else {
- f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) {}\n";
- }
- f << "\n";
- f << indent << "void reset() override {\n";
+ f << indent << mangle(module) << "(interior) {}\n";
+ f << indent << mangle(module) << "() {\n";
inc_indent();
- f << indent << "*this = " << mangle(module) << "(adopt {}, std::move(*this));\n";
+ f << indent << "reset();\n";
dec_indent();
- f << indent << "}\n";
+ f << indent << "};\n";
f << "\n";
+ f << indent << "void reset() override;\n";
f << indent << "bool eval() override;\n";
f << indent << "bool commit() override;\n";
if (debug_info) {
@@ -2128,6 +2345,10 @@ struct CxxrtlWorker {
{
if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
return;
+ f << indent << "void " << mangle(module) << "::reset() {\n";
+ dump_reset_method(module);
+ f << indent << "}\n";
+ f << "\n";
f << indent << "bool " << mangle(module) << "::eval() {\n";
dump_eval_method(module);
f << indent << "}\n";
@@ -2273,6 +2494,8 @@ struct CxxrtlWorker {
void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type)
{
signal = sigmap(signal);
+ if (signal.is_fully_const())
+ return; // a clock, or more commonly a reset, can be tied to a constant driver
log_assert(is_valid_clock(signal));
log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe);
@@ -2297,6 +2520,13 @@ struct CxxrtlWorker {
SigMap &sigmap = sigmaps[module];
sigmap.set(module);
+ std::vector<Mem> &memories = mod_memories[module];
+ memories = Mem::get_all_memories(module);
+ for (auto &mem : memories) {
+ mem.narrow();
+ mem.coalesce_inits();
+ }
+
if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto port : module->ports) {
RTLIL::Wire *wire = module->wire(port);
@@ -2334,6 +2564,10 @@ struct CxxrtlWorker {
continue;
}
+ for (auto wire : module->wires())
+ if (wire->has_attribute(ID::init))
+ wire_init[wire] = wire->attributes.at(ID::init);
+
// Construct a flow graph where each node is a basic computational operation generally corresponding
// to a fragment of the RTLIL netlist.
FlowGraph flow;
@@ -2341,13 +2575,13 @@ struct CxxrtlWorker {
for (auto conn : module->connections())
flow.add_node(conn);
- dict<const RTLIL::Cell*, FlowGraph::Node*> memrw_cell_nodes;
- 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));
+ if (cell->is_mem_cell())
+ continue;
+
RTLIL::Module *cell_module = design->module(cell->type);
if (cell_module &&
cell_module->get_blackbox_attribute() &&
@@ -2359,58 +2593,54 @@ struct CxxrtlWorker {
cell_module->get_bool_attribute(ID(cxxrtl_template)))
blackbox_specializations[cell_module].insert(template_args(cell));
- FlowGraph::Node *node = flow.add_node(cell);
+ 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($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) {
+ if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) {
if (is_valid_clock(cell->getPort(ID::CLK)))
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 (is_valid_clock(cell->getPort(ID::CLK)))
- 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()]);
- // Collect groups of memory write ports in the same domain.
- if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK))) {
- 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
- // of RTLIL memory objects and $memrd/$memwr/$meminit cells.
- if (cell->type.in(ID($mem)))
- log_assert(false);
}
- 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() && is_valid_clock(cell->getPort(ID::CLK)) &&
- 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));
+
+ for (auto &mem : memories) {
+ flow.add_node(&mem);
+
+ // Clocked memory cells are treated like posedge/negedge processes as well.
+ for (auto &port : mem.rd_ports) {
+ if (port.clk_enable)
+ if (is_valid_clock(port.clk))
+ register_edge_signal(sigmap, port.clk,
+ port.clk_polarity ? RTLIL::STp : RTLIL::STn);
+ // For read ports, also move initial value to wire_init (if any).
+ for (int i = 0; i < GetSize(port.data); i++) {
+ if (port.init_value[i] != State::Sx) {
+ SigBit bit = port.data[i];
+ if (bit.wire) {
+ auto &init = wire_init[bit.wire];
+ if (init == RTLIL::Const()) {
+ init = RTLIL::Const(State::Sx, GetSize(bit.wire));
+ }
+ init[bit.offset] = port.init_value[i];
+ }
+ }
}
}
+ for (auto &port : mem.wr_ports) {
+ if (port.clk_enable)
+ if (is_valid_clock(port.clk))
+ register_edge_signal(sigmap, port.clk,
+ port.clk_polarity ? RTLIL::STp : RTLIL::STn);
+ }
+
+ if (!mem.wr_ports.empty())
+ writable_memories.insert({module, mem.memid});
}
for (auto proc : module->processes) {
flow.add_node(proc.second);
- for (auto sync : proc.second->syncs)
+ for (auto sync : proc.second->syncs) {
switch (sync->type) {
// Edge-type sync rules require pre-registration.
case RTLIL::STp:
@@ -2433,6 +2663,10 @@ struct CxxrtlWorker {
case RTLIL::STi:
log_assert(false);
}
+ for (auto &memwr : sync->mem_write_actions) {
+ writable_memories.insert({module, memwr.memid});
+ }
+ }
}
// Construct a linear order of the flow graph that minimizes the amount of feedback arcs. A flow graph
@@ -2502,6 +2736,10 @@ struct CxxrtlWorker {
for (auto node : flow.nodes) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
worklist.insert(node); // node has effects
+ else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS)
+ worklist.insert(node); // node is memory write
+ else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process))
+ worklist.insert(node); // node is memory write
else if (flow.node_sync_defs.count(node))
worklist.insert(node); // node is a flip-flop
else if (flow.node_comb_defs.count(node)) {
@@ -2527,12 +2765,14 @@ struct CxxrtlWorker {
for (auto wire : module->wires()) {
auto &wire_type = wire_types[wire];
if (!wire_type.is_local()) continue;
- if (!wire->name.isPublic() && !inline_internal) continue;
- if (wire->name.isPublic() && !inline_public) continue;
-
if (live_wires[wire].empty()) {
wire_type = {WireType::UNUSED}; // wire never used
- } else if (flow.is_inlinable(wire, live_wires[wire])) {
+ continue;
+ }
+
+ if (!wire->name.isPublic() && !inline_internal) continue;
+ if (wire->name.isPublic() && !inline_public) continue;
+ if (flow.is_inlinable(wire, live_wires[wire])) {
if (flow.wire_comb_defs[wire].size() > 1)
log_cmd_error("Wire %s.%s has multiple drivers!\n", log_id(module), log_id(wire));
log_assert(flow.wire_comb_defs[wire].size() == 1);
@@ -2586,12 +2826,12 @@ struct CxxrtlWorker {
for (auto wire : module->wires()) {
const auto &wire_type = wire_types[wire];
auto &debug_wire_type = debug_wire_types[wire];
- if (wire_type.type == WireType::UNUSED) continue;
- if (!wire->name.isPublic()) continue;
if (!debug_info) continue;
if (wire->port_input || wire_type.is_buffered())
debug_wire_type = wire_type; // wire contains state
+ else if (!wire->name.isPublic())
+ continue; // internal and stateless
if (!debug_member) continue;
if (wire_type.is_member())
@@ -2655,7 +2895,7 @@ struct CxxrtlWorker {
auto &debug_wire_type = debug_wire_types[wire];
if (wire->name.isPublic()) continue;
- if (live_wires[wire].empty() || debug_live_wires[wire].empty()) {
+ if (debug_live_wires[wire].empty()) {
continue; // wire never used
} else if (flow.is_inlinable(wire, debug_live_wires[wire])) {
log_assert(flow.wire_comb_defs[wire].size() == 1);
@@ -2666,16 +2906,25 @@ struct CxxrtlWorker {
debug_wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell
break;
case FlowGraph::Node::Type::CONNECT:
- debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig
+ debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig
break;
default: continue;
}
debug_live_nodes.erase(node);
- } else if (wire_type.is_local()) {
- debug_wire_type = {WireType::LOCAL}; // wire not inlinable
+ } else if (wire_type.is_member() || wire_type.type == WireType::LOCAL) {
+ debug_wire_type = wire_type; // wire not inlinable
} else {
- log_assert(wire_type.is_member());
- debug_wire_type = wire_type; // wire is a member
+ log_assert(wire_type.type == WireType::INLINE ||
+ wire_type.type == WireType::UNUSED);
+ if (flow.wire_comb_defs[wire].size() == 0) {
+ if (wire_init.count(wire)) { // wire never modified
+ debug_wire_type = {WireType::CONST, wire_init.at(wire)};
+ } else {
+ debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)};
+ }
+ } else {
+ debug_wire_type = {WireType::LOCAL}; // wire used only for debug
+ }
}
}
@@ -2684,6 +2933,35 @@ struct CxxrtlWorker {
if (debug_live_nodes[node])
debug_schedule[module].push_back(*node);
}
+
+ auto show_wire_type = [&](const RTLIL::Wire* wire, const WireType &wire_type) {
+ const char *type_str;
+ switch (wire_type.type) {
+ case WireType::UNUSED: type_str = "UNUSED"; break;
+ case WireType::BUFFERED: type_str = "BUFFERED"; break;
+ case WireType::MEMBER: type_str = "MEMBER"; break;
+ case WireType::OUTLINE: type_str = "OUTLINE"; break;
+ case WireType::LOCAL: type_str = "LOCAL"; break;
+ case WireType::INLINE: type_str = "INLINE"; break;
+ case WireType::ALIAS: type_str = "ALIAS"; break;
+ case WireType::CONST: type_str = "CONST"; break;
+ default: type_str = "(invalid)";
+ }
+ if (wire_type.sig_subst.empty())
+ log_debug(" %s: %s\n", log_signal((RTLIL::Wire*)wire), type_str);
+ else
+ log_debug(" %s: %s = %s\n", log_signal((RTLIL::Wire*)wire), type_str, log_signal(wire_type.sig_subst));
+ };
+ if (print_wire_types && !wire_types.empty()) {
+ log_debug("Wire types:\n");
+ for (auto wire_type : wire_types)
+ show_wire_type(wire_type.first, wire_type.second);
+ }
+ if (print_debug_wire_types && !debug_wire_types.empty()) {
+ log_debug("Debug wire types:\n");
+ for (auto debug_wire_type : debug_wire_types)
+ show_wire_type(debug_wire_type.first, debug_wire_type.second);
+ }
}
if (has_feedback_arcs || has_buffered_comb_wires) {
// Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
@@ -2702,9 +2980,9 @@ struct CxxrtlWorker {
}
}
- void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init, bool &has_packed_mem)
+ void check_design(RTLIL::Design *design, bool &has_sync_init)
{
- has_sync_init = has_packed_mem = has_top = false;
+ has_sync_init = false;
for (auto module : design->modules()) {
if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox)))
@@ -2716,28 +2994,20 @@ struct CxxrtlWorker {
if (!design->selected_module(module))
continue;
- if (module->get_bool_attribute(ID::top))
- has_top = true;
-
for (auto proc : module->processes)
for (auto sync : proc.second->syncs)
if (sync->type == RTLIL::STi)
has_sync_init = true;
-
- // The Mem constructor also checks for well-formedness of $meminit cells, if any.
- for (auto &mem : Mem::get_all_memories(module))
- if (mem.packed)
- has_packed_mem = true;
}
}
void prepare_design(RTLIL::Design *design)
{
bool did_anything = false;
- bool has_top, has_sync_init, has_packed_mem;
+ bool has_sync_init;
log_push();
- check_design(design, has_top, has_sync_init, has_packed_mem);
- if (run_hierarchy && !has_top) {
+ check_design(design, has_sync_init);
+ if (run_hierarchy) {
Pass::call(design, "hierarchy -auto-top");
did_anything = true;
}
@@ -2756,14 +3026,10 @@ struct CxxrtlWorker {
Pass::call(design, "proc_init");
did_anything = true;
}
- if (has_packed_mem) {
- Pass::call(design, "memory_unpack");
- did_anything = true;
- }
// Recheck the design if it was modified.
if (did_anything)
- check_design(design, has_top, has_sync_init, has_packed_mem);
- log_assert(has_top && !has_sync_init && !has_packed_mem);
+ check_design(design, has_sync_init);
+ log_assert(!has_sync_init);
log_pop();
if (did_anything)
log_spacer();
@@ -2934,6 +3200,9 @@ struct CxxrtlBackend : public Backend {
log("\n");
log("The following options are supported by this backend:\n");
log("\n");
+ log(" -print-wire-types, -print-debug-wire-types\n");
+ log(" enable additional debug logging, for pass developers.\n");
+ log("\n");
log(" -header\n");
log(" generate separate interface (.h) and implementation (.cc) files.\n");
log(" if specified, the backend must be called with a filename, and filename\n");
@@ -3013,6 +3282,8 @@ struct CxxrtlBackend : public Backend {
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
+ bool print_wire_types = false;
+ bool print_debug_wire_types = false;
bool nohierarchy = false;
bool noflatten = false;
bool noproc = false;
@@ -3025,6 +3296,14 @@ struct CxxrtlBackend : public Backend {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
+ if (args[argidx] == "-print-wire-types") {
+ print_wire_types = true;
+ continue;
+ }
+ if (args[argidx] == "-print-debug-wire-types") {
+ print_debug_wire_types = true;
+ continue;
+ }
if (args[argidx] == "-nohierarchy") {
nohierarchy = true;
continue;
@@ -3076,6 +3355,8 @@ struct CxxrtlBackend : public Backend {
}
extra_args(f, filename, args, argidx);
+ worker.print_wire_types = print_wire_types;
+ worker.print_debug_wire_types = print_debug_wire_types;
worker.run_hierarchy = !nohierarchy;
worker.run_flatten = !noflatten;
worker.run_proc = !noproc;
diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h
index 3f40a8d12..b76922bbd 100644
--- a/backends/cxxrtl/cxxrtl_vcd.h
+++ b/backends/cxxrtl/cxxrtl_vcd.h
@@ -69,12 +69,25 @@ class vcd_writer {
} while (ident != 0);
}
+ void emit_name(const std::string &name) {
+ for (char c : name) {
+ if (c == ':') {
+ // Due to a bug, GTKWave cannot parse a colon in the variable name, causing the VCD file
+ // to be unreadable. It cannot be escaped either, so replace it with the sideways colon.
+ buffer += "..";
+ } else {
+ buffer += c;
+ }
+ }
+ }
+
void emit_var(const variable &var, const std::string &type, const std::string &name,
size_t lsb_at, bool multipart) {
assert(!streaming);
buffer += "$var " + type + " " + std::to_string(var.width) + " ";
emit_ident(var.ident);
- buffer += " " + name;
+ buffer += " ";
+ emit_name(name);
if (multipart || name.back() == ']' || lsb_at != 0) {
if (var.width == 1)
buffer += " [" + std::to_string(lsb_at) + "]";
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index e0013238c..370108444 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index 44c3397da..85c44824f 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -23,6 +23,7 @@
#include "kernel/celltypes.h"
#include "kernel/cellaigs.h"
#include "kernel/log.h"
+#include "kernel/mem.h"
#include <algorithm>
#include <string>
#include <vector>
@@ -366,126 +367,6 @@ struct FirrtlWorker
RTLIL::Design *design;
std::string indent;
- // Define read/write ports and memories.
- // We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction.
- // For the moment, we don't handle $readmemh or $readmemb.
- // These will be part of a subsequent PR.
- struct read_port {
- string name;
- bool clk_enable;
- bool clk_parity;
- bool transparent;
- RTLIL::SigSpec clk;
- RTLIL::SigSpec ena;
- RTLIL::SigSpec addr;
- read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) {
- // Current (3/13/2019) conventions:
- // generate a constant 0 for clock and a constant 1 for enable if they are undefined.
- if (!clk.is_fully_def())
- this->clk = SigSpec(State::S0);
- if (!ena.is_fully_def())
- this->ena = SigSpec(State::S1);
- }
- string gen_read(const char * indent) {
- string addr_expr = make_expr(addr);
- string ena_expr = make_expr(ena);
- string clk_expr = make_expr(clk);
- string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
- string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
- string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
- return addr_str + ena_str + clk_str;
- }
- };
- struct write_port : read_port {
- RTLIL::SigSpec mask;
- write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) {
- if (!clk.is_fully_def())
- this->clk = SigSpec(RTLIL::Const(0));
- if (!ena.is_fully_def())
- this->ena = SigSpec(RTLIL::Const(0));
- if (!mask.is_fully_def())
- this->ena = SigSpec(RTLIL::Const(1));
- }
- string gen_read(const char * /* indent */) {
- log_error("gen_read called on write_port: %s\n", name.c_str());
- return stringf("gen_read called on write_port: %s\n", name.c_str());
- }
- string gen_write(const char * indent) {
- string addr_expr = make_expr(addr);
- string ena_expr = make_expr(ena);
- string clk_expr = make_expr(clk);
- string mask_expr = make_expr(mask);
- string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str());
- string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
- string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
- string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
- return addr_str + ena_str + clk_str + mask_str;
- }
- };
- /* Memories defined within this module. */
- struct memory {
- Cell *pCell; // for error reporting
- string name; // memory name
- int abits; // number of address bits
- int size; // size (in units) of the memory
- int width; // size (in bits) of each element
- int read_latency;
- int write_latency;
- vector<read_port> read_ports;
- vector<write_port> write_ports;
- std::string init_file;
- std::string init_file_srcFileSpec;
- string srcLine;
- memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {
- // Provide defaults for abits or size if one (but not the other) is specified.
- if (this->abits == 0 && this->size != 0) {
- this->abits = ceil_log2(this->size);
- } else if (this->abits != 0 && this->size == 0) {
- this->size = 1 << this->abits;
- }
- // Sanity-check this construction.
- if (this->name == "") {
- log_error("Nameless memory%s\n", this->atLine());
- }
- if (this->abits == 0 && this->size == 0) {
- log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine());
- }
- if (this->width == 0) {
- log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine());
- }
- }
-
- // We need a default constructor for the dict insert.
- memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
-
- const char *atLine() {
- if (srcLine == "") {
- if (pCell) {
- auto p = pCell->attributes.find(ID::src);
- srcLine = " at " + p->second.decode_string();
- }
- }
- return srcLine.c_str();
- }
- void add_memory_read_port(read_port &rp) {
- read_ports.push_back(rp);
- }
- void add_memory_write_port(write_port &wp) {
- write_ports.push_back(wp);
- }
- void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
- this->init_file = init_file;
- this->init_file_srcFileSpec = init_file_srcFileSpec;
- }
-
- };
- dict<string, memory> memories;
-
- void register_memory(memory &m)
- {
- memories[m.name] = m;
- }
-
void register_reverse_wire_map(string id, SigSpec sig)
{
for (int i = 0; i < GetSize(sig); i++)
@@ -658,7 +539,11 @@ struct FirrtlWorker
{
std::string moduleFileinfo = getFileinfo(module);
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
- vector<string> port_decls, wire_decls, cell_exprs, wire_exprs;
+ vector<string> port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs;
+
+ std::vector<Mem> memories = Mem::get_all_memories(module);
+ for (auto &mem : memories)
+ mem.narrow();
for (auto wire : module->wires())
{
@@ -686,14 +571,15 @@ struct FirrtlWorker
for (auto cell : module->cells())
{
- static Const ndef(0, 0);
+ Const ndef(0, 0);
// Is this cell is a module instance?
- if (cell->type[0] != '$')
+ if (module->design->module(cell->type))
{
process_instance(cell, wire_exprs);
continue;
}
+
// Not a module instance. Set up cell properties
bool extract_y_bits = false; // Assume no extraction of final bits will be required.
int a_width = cell->parameters.at(ID::A_WIDTH, ndef).as_int(); // The width of "A"
@@ -1004,126 +890,9 @@ struct FirrtlWorker
continue;
}
- if (cell->type.in(ID($mem)))
- {
- string mem_id = make_id(cell->name);
- int abits = cell->parameters.at(ID::ABITS).as_int();
- int width = cell->parameters.at(ID::WIDTH).as_int();
- int size = cell->parameters.at(ID::SIZE).as_int();
- memory m(cell, mem_id, abits, size, width);
- int rd_ports = cell->parameters.at(ID::RD_PORTS).as_int();
- int wr_ports = cell->parameters.at(ID::WR_PORTS).as_int();
-
- Const initdata = cell->parameters.at(ID::INIT);
- for (State bit : initdata.bits)
- if (bit != State::Sx)
- log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(cell));
-
- Const rd_clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE);
- Const wr_clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE);
- Const wr_clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY);
-
- int offset = cell->parameters.at(ID::OFFSET).as_int();
- if (offset != 0)
- log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell));
-
- for (int i = 0; i < rd_ports; i++)
- {
- if (rd_clk_enable[i] != State::S0)
- log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
-
- SigSpec addr_sig = cell->getPort(ID::RD_ADDR).extract(i*abits, abits);
- SigSpec data_sig = cell->getPort(ID::RD_DATA).extract(i*width, width);
- string addr_expr = make_expr(addr_sig);
- string name(stringf("%s.r%d", m.name.c_str(), i));
- bool clk_enable = false;
- bool clk_parity = true;
- bool transparency = false;
- SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
- SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1);
- read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig);
- m.add_memory_read_port(rp);
- cell_exprs.push_back(rp.gen_read(indent.c_str()));
- register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig);
- }
-
- for (int i = 0; i < wr_ports; i++)
- {
- if (wr_clk_enable[i] != State::S1)
- log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
-
- if (wr_clk_polarity[i] != State::S1)
- log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
-
- string name(stringf("%s.w%d", m.name.c_str(), i));
- bool clk_enable = true;
- bool clk_parity = true;
- bool transparency = false;
- SigSpec addr_sig =cell->getPort(ID::WR_ADDR).extract(i*abits, abits);
- string addr_expr = make_expr(addr_sig);
- SigSpec data_sig =cell->getPort(ID::WR_DATA).extract(i*width, width);
- string data_expr = make_expr(data_sig);
- SigSpec clk_sig = cell->getPort(ID::WR_CLK).extract(i);
- string clk_expr = make_expr(clk_sig);
-
- SigSpec wen_sig = cell->getPort(ID::WR_EN).extract(i*width, width);
- string wen_expr = make_expr(wen_sig[0]);
-
- for (int i = 1; i < GetSize(wen_sig); i++)
- if (wen_sig[0] != wen_sig[i])
- log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
-
- SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
- write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig);
- m.add_memory_write_port(wp);
- cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str()));
- cell_exprs.push_back(wp.gen_write(indent.c_str()));
- }
- register_memory(m);
- continue;
- }
-
- if (cell->type.in(ID($memwr), ID($memrd), ID($meminit)))
+ if (cell->is_mem_cell())
{
- std::string cell_type = fid(cell->type);
- std::string mem_id = make_id(cell->parameters[ID::MEMID].decode_string());
- int abits = cell->parameters.at(ID::ABITS).as_int();
- int width = cell->parameters.at(ID::WIDTH).as_int();
- memory *mp = nullptr;
- if (cell->type == ID($meminit) ) {
- log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
- } else {
- // It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition.
- auto addrSig = cell->getPort(ID::ADDR);
- auto dataSig = cell->getPort(ID::DATA);
- auto enableSig = cell->getPort(ID::EN);
- auto clockSig = cell->getPort(ID::CLK);
- Const clk_enable = cell->parameters.at(ID::CLK_ENABLE);
- Const clk_polarity = cell->parameters.at(ID::CLK_POLARITY);
-
- // Do we already have an entry for this memory?
- if (memories.count(mem_id) == 0) {
- memory m(cell, mem_id, abits, 0, width);
- register_memory(m);
- }
- mp = &memories.at(mem_id);
- int portNum = 0;
- bool transparency = false;
- string data_expr = make_expr(dataSig);
- if (cell->type.in(ID($memwr))) {
- portNum = (int) mp->write_ports.size();
- write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig);
- mp->add_memory_write_port(wp);
- cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str()));
- cell_exprs.push_back(wp.gen_write(indent.c_str()));
- } else if (cell->type.in(ID($memrd))) {
- portNum = (int) mp->read_ports.size();
- read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig);
- mp->add_memory_read_port(rp);
- cell_exprs.push_back(rp.gen_read(indent.c_str()));
- register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig);
- }
- }
+ // Will be handled below, as part of a Mem.
continue;
}
@@ -1145,12 +914,6 @@ struct FirrtlWorker
continue;
}
- // This may be a parameterized module - paramod.
- if (cell->type.begins_with("$paramod"))
- {
- process_instance(cell, wire_exprs);
- continue;
- }
if (cell->type == ID($shiftx)) {
// assign y = a[b +: y_width];
// We'll extract the correct bits as part of the primop.
@@ -1215,6 +978,82 @@ struct FirrtlWorker
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
}
+ for (auto &mem : memories) {
+ string mem_id = make_id(mem.memid);
+
+ Const init_data = mem.get_init_data();
+ if (!init_data.is_fully_undef())
+ log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(mem.memid));
+
+ if (mem.start_offset != 0)
+ log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(mem.memid));
+
+ for (int i = 0; i < GetSize(mem.rd_ports); i++)
+ {
+ auto &port = mem.rd_ports[i];
+ string port_name(stringf("%s.r%d", mem_id.c_str(), i));
+
+ if (port.clk_enable)
+ log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+
+ std::ostringstream rpe;
+
+ string addr_expr = make_expr(port.addr);
+ string ena_expr = make_expr(State::S1);
+ string clk_expr = make_expr(State::S0);
+
+ rpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str());
+ rpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str());
+ rpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str());
+ cell_exprs.push_back(rpe.str());
+ register_reverse_wire_map(stringf("%s.data", port_name.c_str()), port.data);
+ }
+
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ {
+ auto &port = mem.wr_ports[i];
+ string port_name(stringf("%s.w%d", mem_id.c_str(), i));
+
+ if (!port.clk_enable)
+ log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+ if (!port.clk_polarity)
+ log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+ for (int i = 1; i < GetSize(port.en); i++)
+ if (port.en[0] != port.en[i])
+ log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+
+ std::ostringstream wpe;
+
+ string data_expr = make_expr(port.data);
+ string addr_expr = make_expr(port.addr);
+ string ena_expr = make_expr(port.en[0]);
+ string clk_expr = make_expr(port.clk);
+ string mask_expr = make_expr(State::S1);
+ wpe << stringf("%s%s.data <= %s\n", indent.c_str(), port_name.c_str(), data_expr.c_str());
+ wpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str());
+ wpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str());
+ wpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str());
+ wpe << stringf("%s%s.mask <= %s\n", indent.c_str(), port_name.c_str(), mask_expr.c_str());
+
+ cell_exprs.push_back(wpe.str());
+ }
+
+ std::ostringstream me;
+
+ me << stringf(" mem %s:\n", mem_id.c_str());
+ me << stringf(" data-type => UInt<%d>\n", mem.width);
+ me << stringf(" depth => %d\n", mem.size);
+ for (int i = 0; i < GetSize(mem.rd_ports); i++)
+ me << stringf(" reader => r%d\n", i);
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ me << stringf(" writer => w%d\n", i);
+ me << stringf(" read-latency => %d\n", 0);
+ me << stringf(" write-latency => %d\n", 1);
+ me << stringf(" read-under-write => undefined\n");
+
+ mem_exprs.push_back(me.str());
+ }
+
for (auto conn : module->connections())
{
string y_id = next_id();
@@ -1316,22 +1155,9 @@ struct FirrtlWorker
f << stringf("\n");
- // If we have any memory definitions, output them.
- for (auto kv : memories) {
- memory &m = kv.second;
- f << stringf(" mem %s:\n", m.name.c_str());
- f << stringf(" data-type => UInt<%d>\n", m.width);
- f << stringf(" depth => %d\n", m.size);
- for (int i = 0; i < (int) m.read_ports.size(); i += 1) {
- f << stringf(" reader => r%d\n", i);
- }
- for (int i = 0; i < (int) m.write_ports.size(); i += 1) {
- f << stringf(" writer => w%d\n", i);
- }
- f << stringf(" read-latency => %d\n", m.read_latency);
- f << stringf(" write-latency => %d\n", m.write_latency);
- f << stringf(" read-under-write => undefined\n");
- }
+ for (auto str : mem_exprs)
+ f << str;
+
f << stringf("\n");
for (auto str : cell_exprs)
@@ -1362,6 +1188,8 @@ struct FirrtlBackend : public Backend {
log("Write a FIRRTL netlist of the current design.\n");
log("The following commands are executed by this command:\n");
log(" pmuxtree\n");
+ log(" bmuxmap\n");
+ log(" demuxmap\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
@@ -1384,7 +1212,9 @@ struct FirrtlBackend : public Backend {
log_header(design, "Executing FIRRTL backend.\n");
log_push();
- Pass::call(design, stringf("pmuxtree"));
+ Pass::call(design, "pmuxtree");
+ Pass::call(design, "bmuxmap");
+ Pass::call(design, "demuxmap");
namecache.clear();
autoid_counter = 0;
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
index a6b36de6c..59173c4a2 100644
--- a/backends/intersynth/intersynth.cc
+++ b/backends/intersynth/intersynth.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -68,7 +68,7 @@ struct IntersynthBackend : public Backend {
log(" only write selected modules. modules must be selected entirely or\n");
log(" not at all.\n");
log("\n");
- log("http://www.clifford.at/intersynth/\n");
+ log("http://bygone.clairexen.net/intersynth/\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
diff --git a/backends/json/json.cc b/backends/json/json.cc
index eeadc1b89..4aa8046d6 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -135,6 +135,10 @@ struct JsonWriter
// reserve 0 and 1 to avoid confusion with "0" and "1"
sigidcounter = 2;
+ if (module->has_processes()) {
+ log_error("Module %s contains processes, which are not supported by JSON backend (run `proc` first).\n", log_id(module));
+ }
+
f << stringf(" %s: {\n", get_name(module->name).c_str());
f << stringf(" \"attributes\": {");
@@ -216,6 +220,27 @@ struct JsonWriter
}
f << stringf("\n },\n");
+ if (!module->memories.empty()) {
+ f << stringf(" \"memories\": {");
+ first = true;
+ for (auto &it : module->memories) {
+ if (use_selection && !module->selected(it.second))
+ continue;
+ f << stringf("%s\n", first ? "" : ",");
+ f << stringf(" %s: {\n", get_name(it.second->name).c_str());
+ f << stringf(" \"hide_name\": %s,\n", it.second->name[0] == '$' ? "1" : "0");
+ f << stringf(" \"attributes\": {");
+ write_parameters(it.second->attributes);
+ f << stringf("\n },\n");
+ f << stringf(" \"width\": %d,\n", it.second->width);
+ f << stringf(" \"start_offset\": %d,\n", it.second->start_offset);
+ f << stringf(" \"size\": %d\n", it.second->size);
+ f << stringf(" }");
+ first = false;
+ }
+ f << stringf("\n },\n");
+ }
+
f << stringf(" \"netnames\": {");
first = true;
for (auto w : module->wires()) {
@@ -332,6 +357,10 @@ struct JsonBackend : public Backend {
log(" <cell_name>: <cell_details>,\n");
log(" ...\n");
log(" },\n");
+ log(" \"memories\": {\n");
+ log(" <memory_name>: <memory_details>,\n");
+ log(" ...\n");
+ log(" },\n");
log(" \"netnames\": {\n");
log(" <net_name>: <net_details>,\n");
log(" ...\n");
@@ -379,6 +408,19 @@ struct JsonBackend : public Backend {
log(" },\n");
log(" }\n");
log("\n");
+ log("And <memory_details> is:\n");
+ log("\n");
+ log(" {\n");
+ log(" \"hide_name\": <1 | 0>,\n");
+ log(" \"attributes\": {\n");
+ log(" <attribute_name>: <attribute_value>,\n");
+ log(" ...\n");
+ log(" },\n");
+ log(" \"width\": <memory width>\n");
+ log(" \"start_offset\": <the lowest valid memory address>\n");
+ log(" \"size\": <memory size>\n");
+ log(" }\n");
+ log("\n");
log("And <net_details> is:\n");
log("\n");
log(" {\n");
diff --git a/backends/protobuf/Makefile.inc b/backends/protobuf/Makefile.inc
index 834cad42c..9cac9dcaa 100644
--- a/backends/protobuf/Makefile.inc
+++ b/backends/protobuf/Makefile.inc
@@ -3,6 +3,8 @@ ifeq ($(ENABLE_PROTOBUF),1)
backends/protobuf/yosys.pb.cc backends/protobuf/yosys.pb.h: misc/yosys.proto
$(Q) cd misc && protoc --cpp_out "../backends/protobuf" yosys.proto
+backends/protobuf/protobuf.cc: backends/protobuf/yosys.pb.h
+
OBJS += backends/protobuf/protobuf.o backends/protobuf/yosys.pb.o
endif
diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc
index f6623a382..384ce2e8e 100644
--- a/backends/protobuf/protobuf.cc
+++ b/backends/protobuf/protobuf.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc
index 01b4bde53..1b11de5ec 100644
--- a/backends/rtlil/rtlil_backend.cc
+++ b/backends/rtlil/rtlil_backend.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -51,15 +51,19 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
}
}
f << stringf("%d'", width);
- for (int i = offset+width-1; i >= offset; i--) {
- log_assert(i < (int)data.bits.size());
- switch (data.bits[i]) {
- case State::S0: f << stringf("0"); break;
- case State::S1: f << stringf("1"); break;
- case RTLIL::Sx: f << stringf("x"); break;
- case RTLIL::Sz: f << stringf("z"); break;
- case RTLIL::Sa: f << stringf("-"); break;
- case RTLIL::Sm: f << stringf("m"); break;
+ if (data.is_fully_undef()) {
+ f << "x";
+ } else {
+ for (int i = offset+width-1; i >= offset; i--) {
+ log_assert(i < (int)data.bits.size());
+ switch (data.bits[i]) {
+ case State::S0: f << stringf("0"); break;
+ case State::S1: f << stringf("1"); break;
+ case RTLIL::Sx: f << stringf("x"); break;
+ case RTLIL::Sz: f << stringf("z"); break;
+ case RTLIL::Sa: f << stringf("-"); break;
+ case RTLIL::Sm: f << stringf("m"); break;
+ }
}
}
} else {
@@ -242,11 +246,28 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
case RTLIL::STi: f << stringf("init\n"); break;
}
- for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) {
+ for (auto &it: sy->actions) {
f << stringf("%s update ", indent.c_str());
- dump_sigspec(f, it->first);
+ dump_sigspec(f, it.first);
f << stringf(" ");
- dump_sigspec(f, it->second);
+ dump_sigspec(f, it.second);
+ f << stringf("\n");
+ }
+
+ for (auto &it: sy->mem_write_actions) {
+ for (auto it2 = it.attributes.begin(); it2 != it.attributes.end(); ++it2) {
+ f << stringf("%s attribute %s ", indent.c_str(), it2->first.c_str());
+ dump_const(f, it2->second);
+ f << stringf("\n");
+ }
+ f << stringf("%s memwr %s ", indent.c_str(), it.memid.c_str());
+ dump_sigspec(f, it.address);
+ f << stringf(" ");
+ dump_sigspec(f, it.data);
+ f << stringf(" ");
+ dump_sigspec(f, it.enable);
+ f << stringf(" ");
+ dump_const(f, it.priority_mask);
f << stringf("\n");
}
}
@@ -337,8 +358,8 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu
bool first_conn_line = true;
for (auto it = module->connections().begin(); it != module->connections().end(); ++it) {
- bool show_conn = !only_selected;
- if (only_selected) {
+ bool show_conn = !only_selected || design->selected_whole_module(module->name);
+ if (!show_conn) {
RTLIL::SigSpec sigs = it->first;
sigs.append(it->second);
for (auto &c : sigs.chunks()) {
diff --git a/backends/rtlil/rtlil_backend.h b/backends/rtlil/rtlil_backend.h
index 77eea353c..35829729c 100644
--- a/backends/rtlil/rtlil_backend.h
+++ b/backends/rtlil/rtlil_backend.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc
index 3adeaa6c0..e283dcf7c 100644
--- a/backends/simplec/simplec.cc
+++ b/backends/simplec/simplec.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index 8be9e05f1..a928419a1 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -124,6 +124,7 @@ struct Smt2Worker
memories = Mem::get_all_memories(module);
for (auto &mem : memories)
{
+ mem.narrow();
mem_dict[mem.memid] = &mem;
for (auto &port : mem.wr_ports)
{
@@ -182,7 +183,7 @@ struct Smt2Worker
continue;
// Handled above.
- if (cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit))) {
+ if (cell->is_mem_cell()) {
mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()];
continue;
}
@@ -694,7 +695,7 @@ struct Smt2Worker
// FIXME: $slice $concat
}
- if (memmode && cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit)))
+ if (memmode && cell->is_mem_cell())
{
Mem *mem = mem_cells[cell];
@@ -859,7 +860,7 @@ struct Smt2Worker
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smt2`.\n",
log_id(cell->type), log_id(module), log_id(cell));
}
- if (cell->type.in(ID($adff), ID($adffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF") {
+ if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smt2`.\n",
log_id(cell->type), log_id(module), log_id(cell));
}
@@ -1530,6 +1531,11 @@ struct Smt2Backend : public Backend {
log_header(design, "Executing SMT2 backend.\n");
+ log_push();
+ Pass::call(design, "bmuxmap");
+ Pass::call(design, "demuxmap");
+ log_pop();
+
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index da5a7f57e..e5cfcdc08 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -2,7 +2,7 @@
#
# yosys -- Yosys Open SYnthesis Suite
#
-# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+# Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -771,12 +771,12 @@ def write_vcd_trace(steps_start, steps_stop, index):
if gotread:
buf = data[:]
- for i in reversed(range(len(tdata))):
+ for ii in reversed(range(len(tdata))):
for k in range(width):
- if tdata[i][k] == "x":
- tdata[i][k] = buf[k]
+ if tdata[ii][k] == "x":
+ tdata[ii][k] = buf[k]
else:
- buf[k] = tdata[i][k]
+ buf[k] = tdata[ii][k]
if not asyncwr:
tdata.append(data[:])
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 516091011..d73a875ba 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -1,7 +1,7 @@
#
# yosys -- Yosys Open SYnthesis Suite
#
-# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+# Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -203,14 +203,14 @@ class SmtIo:
print('timeout option is not supported for mathsat.')
sys.exit(1)
- if self.solver == "boolector":
+ if self.solver in ["boolector", "bitwuzla"]:
if self.noincr:
- self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts
+ self.popen_vargs = [self.solver, '--smt2'] + self.solver_opts
else:
- self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
+ self.popen_vargs = [self.solver, '--smt2', '-i'] + self.solver_opts
self.unroll = True
if self.timeout != 0:
- print('timeout option is not supported for boolector.')
+ print('timeout option is not supported for %s.' % self.solver)
sys.exit(1)
if self.solver == "abc":
@@ -1010,7 +1010,7 @@ class SmtOpts:
def helpmsg(self):
return """
-s <solver>
- set SMT solver: z3, yices, boolector, cvc4, mathsat, dummy
+ set SMT solver: z3, yices, boolector, bitwuzla, cvc4, mathsat, dummy
default: yices
-S <opt>
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index e41582fea..7d4f94adc 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -578,7 +578,7 @@ struct SmvWorker
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n",
log_id(cell->type), log_id(module), log_id(cell));
}
- if (cell->type.in(ID($adff), ID($adffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF") {
+ if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smv`.\n",
log_id(cell->type), log_id(module), log_id(cell));
}
@@ -741,6 +741,11 @@ struct SmvBackend : public Backend {
log_header(design, "Executing SMV backend.\n");
+ log_push();
+ Pass::call(design, "bmuxmap");
+ Pass::call(design, "demuxmap");
+ log_pop();
+
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
index ca5c680c9..f260276eb 100644
--- a/backends/spice/spice.cc
+++ b/backends/spice/spice.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/table/table.cc b/backends/table/table.cc
index 77642ccbd..2bf64e7b1 100644
--- a/backends/table/table.cc
+++ b/backends/table/table.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 165ce1ea4..aa1d4558c 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -44,6 +44,7 @@ std::string auto_prefix, extmem_prefix;
RTLIL::Module *active_module;
dict<RTLIL::SigBit, RTLIL::State> active_initdata;
SigMap active_sigmap;
+IdString initial_id;
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
{
@@ -357,7 +358,8 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
{
if (GetSize(sig) == 0) {
- f << "\"\"";
+ // See IEEE 1364-2005 Clause 5.1.14.
+ f << "{0{1'b0}}";
return;
}
if (sig.is_chunk()) {
@@ -430,7 +432,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
dump_const(f, wire->attributes.at(ID::init));
}
f << stringf(";\n");
- } else if (!wire->port_input && !wire->port_output)
+ } else
f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
#endif
}
@@ -504,9 +506,24 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
int start = init.addr.as_int();
for (int i=0; i<words; i++)
{
- f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i + start);
- dump_const(f, init.data.extract(i*mem.width, mem.width));
- f << stringf(";\n");
+ for (int j = 0; j < mem.width; j++)
+ {
+ if (init.en[j] != State::S1)
+ continue;
+
+ int start_j = j, width = 1;
+
+ while (j+1 < mem.width && init.en[j+1] == State::S1)
+ j++, width++;
+
+ if (width == mem.width) {
+ f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i + start);
+ } else {
+ f << stringf("%s" " %s[%d][%d:%d] = ", indent.c_str(), mem_id.c_str(), i + start, j, start_j);
+ }
+ dump_const(f, init.data.extract(i*mem.width+start_j, width));
+ f << stringf(";\n");
+ }
}
}
f << stringf("%s" "end\n", indent.c_str());
@@ -515,6 +532,8 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
// create a map : "edge clk" -> expressions within that clock domain
dict<std::string, std::vector<std::string>> clk_to_lof_body;
+ dict<std::string, std::string> clk_to_arst_cond;
+ dict<std::string, std::vector<std::string>> clk_to_arst_body;
clk_to_lof_body[""] = std::vector<std::string>();
std::string clk_domain_str;
// create a list of reg declarations
@@ -529,10 +548,27 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
std::ostringstream os;
dump_sigspec(os, port.clk);
clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str());
- if( clk_to_lof_body.count(clk_domain_str) == 0 )
- clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
+ if (port.arst != State::S0) {
+ std::ostringstream os2;
+ dump_sigspec(os2, port.arst);
+ clk_domain_str += stringf(", posedge %s", os2.str().c_str());
+ clk_to_arst_cond[clk_domain_str] = os2.str();
+ }
}
- if (!port.transparent)
+
+ // Decide how to represent the transparency; same idea as Mem::extract_rdff.
+ bool trans_use_addr = true;
+ for (auto bit : port.transparency_mask)
+ if (!bit)
+ trans_use_addr = false;
+
+ if (GetSize(mem.wr_ports) == 0)
+ trans_use_addr = false;
+
+ if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef())
+ trans_use_addr = false;
+
+ if (!trans_use_addr)
{
// for clocked read ports make something like:
// reg [..] temp_id;
@@ -541,19 +577,148 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
// assign r_data = temp_id;
std::string temp_id = next_auto_id();
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id.c_str()) );
- {
+
+ bool has_indent = false;
+
+ if (port.arst != State::S0) {
std::ostringstream os;
- if (port.en != RTLIL::SigBit(true))
- {
- os << stringf("if (");
- dump_sigspec(os, port.en);
- os << stringf(") ");
+ os << stringf("%s <= ", temp_id.c_str());
+ dump_sigspec(os, port.arst_value);
+ os << ";\n";
+ clk_to_arst_body[clk_domain_str].push_back(os.str());
+ }
+
+ if (port.srst != State::S0 && !port.ce_over_srst) {
+ std::ostringstream os;
+ os << stringf("if (");
+ dump_sigspec(os, port.srst);
+ os << stringf(")\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ std::ostringstream os2;
+ os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str());
+ dump_sigspec(os2, port.srst_value);
+ os2 << ";\n";
+ clk_to_lof_body[clk_domain_str].push_back(os2.str());
+ std::ostringstream os3;
+ if (port.en == State::S1) {
+ os3 << "else begin\n";
+ } else {
+ os3 << "else if (";
+ dump_sigspec(os3, port.en);
+ os3 << ") begin\n";
}
- os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
- dump_sigspec(os, port.addr);
+ clk_to_lof_body[clk_domain_str].push_back(os3.str());
+ has_indent = true;
+ } else if (port.en != State::S1) {
+ std::ostringstream os;
+ os << stringf("if (");
+ dump_sigspec(os, port.en);
+ os << stringf(") begin\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ has_indent = true;
+ }
+
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
+ {
+ SigSpec addr = port.sub_addr(sub);
+ std::ostringstream os;
+ if (has_indent)
+ os << indent;
+ os << temp_id;
+ if (port.wide_log2)
+ os << stringf("[%d:%d]", (sub + 1) * mem.width - 1, sub * mem.width);
+ os << stringf(" <= %s[", mem_id.c_str());
+ dump_sigspec(os, addr);
os << stringf("];\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
}
+
+ for (int i = 0; i < GetSize(mem.wr_ports); i++) {
+ auto &wport = mem.wr_ports[i];
+ if (!port.transparency_mask[i] && !port.collision_x_mask[i])
+ continue;
+ int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2);
+ int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2);
+ bool wide_write = wport.wide_log2 > port.wide_log2;
+ for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
+ SigSpec raddr = port.addr;
+ SigSpec waddr = wport.addr;
+ if (wide_write)
+ waddr = wport.sub_addr(sub);
+ else
+ raddr = port.sub_addr(sub);
+ int pos = 0;
+ int ewidth = mem.width << min_wide_log2;
+ int wsub = wide_write ? sub : 0;
+ int rsub = wide_write ? 0 : sub;
+ while (pos < ewidth) {
+ int epos = pos;
+ while (epos < ewidth && wport.en[epos + wsub * mem.width] == wport.en[pos + wsub * mem.width])
+ epos++;
+
+ std::ostringstream os;
+ if (has_indent)
+ os << indent;
+ os << "if (";
+ dump_sigspec(os, wport.en[pos + wsub * mem.width]);
+ if (raddr != waddr) {
+ os << " && ";
+ dump_sigspec(os, raddr);
+ os << " == ";
+ dump_sigspec(os, waddr);
+ }
+ os << ")\n";
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+
+ std::ostringstream os2;
+ if (has_indent)
+ os2 << indent;
+ os2 << indent;
+ os2 << temp_id;
+ if (epos-pos != GetSize(port.data))
+ os2 << stringf("[%d:%d]", rsub * mem.width + epos-1, rsub * mem.width + pos);
+ os2 << " <= ";
+ if (port.transparency_mask[i])
+ dump_sigspec(os2, wport.data.extract(wsub * mem.width + pos, epos-pos));
+ else
+ dump_sigspec(os2, Const(State::Sx, epos - pos));
+ os2 << ";\n";
+ clk_to_lof_body[clk_domain_str].push_back(os2.str());
+
+ pos = epos;
+ }
+ }
+ }
+
+ if (port.srst != State::S0 && port.ce_over_srst)
+ {
+ std::ostringstream os;
+ if (has_indent)
+ os << indent;
+ os << stringf("if (");
+ dump_sigspec(os, port.srst);
+ os << stringf(")\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ std::ostringstream os2;
+ if (has_indent)
+ os2 << indent;
+ os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str());
+ dump_sigspec(os2, port.srst_value);
+ os2 << ";\n";
+ clk_to_lof_body[clk_domain_str].push_back(os2.str());
+ }
+
+ if (has_indent)
+ clk_to_lof_body[clk_domain_str].push_back("end\n");
+
+ if (!port.init_value.is_fully_undef())
+ {
+ std::ostringstream os;
+ dump_sigspec(os, port.init_value);
+ std::string line = stringf("initial %s = %s;\n", temp_id.c_str(), os.str().c_str());
+ clk_to_lof_body[""].push_back(line);
+ }
+
{
std::ostringstream os;
dump_sigspec(os, port.data);
@@ -569,73 +734,153 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
// temp_id <= r_addr;
// assign r_data = array_reg[temp_id];
std::string temp_id = next_auto_id();
- lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1, temp_id.c_str()) );
+ lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1 - port.wide_log2, temp_id.c_str()) );
{
std::ostringstream os;
- dump_sigspec(os, port.addr);
+ dump_sigspec(os, port.addr.extract_end(port.wide_log2));
std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
clk_to_lof_body[clk_domain_str].push_back(line);
}
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
{
std::ostringstream os;
- dump_sigspec(os, port.data);
- std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str());
- clk_to_lof_body[""].push_back(line);
+ os << "assign ";
+ dump_sigspec(os, port.data.extract(sub * mem.width, mem.width));
+ os << stringf(" = %s[", mem_id.c_str());;
+ if (port.wide_log2) {
+ Const addr_lo;
+ for (int i = 0; i < port.wide_log2; i++)
+ addr_lo.bits.push_back(State(sub >> i & 1));
+ os << "{";
+ os << temp_id;
+ os << ", ";
+ dump_const(os, addr_lo);
+ os << "}";
+ } else {
+ os << temp_id;
+ }
+ os << "];\n";
+ clk_to_lof_body[""].push_back(os.str());
}
}
} else {
// for non-clocked read-ports make something like:
// assign r_data = array_reg[r_addr];
- std::ostringstream os, os2;
- dump_sigspec(os, port.data);
- dump_sigspec(os2, port.addr);
- std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
- clk_to_lof_body[""].push_back(line);
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
+ {
+ SigSpec addr = port.sub_addr(sub);
+
+ std::ostringstream os, os2;
+ dump_sigspec(os, port.data.extract(sub * mem.width, mem.width));
+ dump_sigspec(os2, addr);
+ std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
+ clk_to_lof_body[""].push_back(line);
+ }
}
}
- // write ports
- for (auto &port : mem.wr_ports)
+ // Write ports. Those are messy because we try to preserve priority, as much as we can:
+ //
+ // 1. We split all ports into several disjoint processes.
+ // 2. If a port has priority over another port, the two ports need to share
+ // a process, so that priority can be reconstructed on the other end.
+ // 3. We want each process to be as small as possible, to avoid extra
+ // priorities inferred on the other end.
+ pool<int> wr_ports_done;
+ for (int ridx = 0; ridx < GetSize(mem.wr_ports); ridx++)
{
+ if (wr_ports_done.count(ridx))
+ continue;
+
+ auto &root = mem.wr_ports[ridx];
+
+ // Start from a root.
+ pool<int> wr_ports_now;
+ wr_ports_now.insert(ridx);
+
+ // Transitively fill list of ports in this process by following priority edges.
+ while (true)
{
- std::ostringstream os;
- dump_sigspec(os, port.clk);
- clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str());
- if( clk_to_lof_body.count(clk_domain_str) == 0 )
- clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
+ bool changed = false;
+
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ for (int j = 0; j < i; j++)
+ if (mem.wr_ports[i].priority_mask[j])
+ {
+ if (wr_ports_now.count(i) && !wr_ports_now.count(j)) {
+ wr_ports_now.insert(j);
+ changed = true;
+ }
+ if (!wr_ports_now.count(i) && wr_ports_now.count(j)) {
+ wr_ports_now.insert(i);
+ changed = true;
+ }
+ }
+
+ if (!changed)
+ break;
}
- // make something like:
- // always @(posedge clk)
- // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
- // ...
- for (int i = 0; i < GetSize(port.en); i++)
- {
- int start_i = i, width = 1;
- SigBit wen_bit = port.en[i];
- while (i+1 < GetSize(port.en) && active_sigmap(port.en[i+1]) == active_sigmap(wen_bit))
- i++, width++;
+ if (root.clk_enable) {
+ f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", root.clk_polarity ? "pos" : "neg");
+ dump_sigspec(f, root.clk);
+ f << ") begin\n";
+ } else {
+ f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_latch" : " @*");
+ }
- if (wen_bit == State::S0)
+ for (int pidx = 0; pidx < GetSize(mem.wr_ports); pidx++)
+ {
+ if (!wr_ports_now.count(pidx))
continue;
+ wr_ports_done.insert(pidx);
- std::ostringstream os;
- if (wen_bit != State::S1)
+ auto &port = mem.wr_ports[pidx];
+ log_assert(port.clk_enable == root.clk_enable);
+ if (port.clk_enable) {
+ log_assert(port.clk == root.clk);
+ log_assert(port.clk_polarity == root.clk_polarity);
+ }
+
+ // make something like:
+ // always @(posedge clk)
+ // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
+ // ...
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
{
- os << stringf("if (");
- dump_sigspec(os, wen_bit);
- os << stringf(") ");
+ SigSpec addr = port.sub_addr(sub);
+ for (int i = 0; i < mem.width; i++)
+ {
+ int start_i = i, width = 1;
+ SigBit wen_bit = port.en[sub * mem.width + i];
+
+ while (i+1 < mem.width && active_sigmap(port.en[sub * mem.width + i+1]) == active_sigmap(wen_bit))
+ i++, width++;
+
+ if (wen_bit == State::S0)
+ continue;
+
+ f << stringf("%s%s", indent.c_str(), indent.c_str());
+ if (wen_bit != State::S1)
+ {
+ f << stringf("if (");
+ dump_sigspec(f, wen_bit);
+ f << stringf(")\n");
+ f << stringf("%s%s%s", indent.c_str(), indent.c_str(), indent.c_str());
+ }
+ f << stringf("%s[", mem_id.c_str());
+ dump_sigspec(f, addr);
+ if (width == GetSize(port.en))
+ f << stringf("] <= ");
+ else
+ f << stringf("][%d:%d] <= ", i, start_i);
+ dump_sigspec(f, port.data.extract(sub * mem.width + start_i, width));
+ f << stringf(";\n");
+ }
}
- os << stringf("%s[", mem_id.c_str());
- dump_sigspec(os, port.addr);
- if (width == GetSize(port.en))
- os << stringf("] <= ");
- else
- os << stringf("][%d:%d] <= ", i, start_i);
- dump_sigspec(os, port.data.extract(start_i, width));
- os << stringf(";\n");
- clk_to_lof_body[clk_domain_str].push_back(os.str());
}
+
+ f << stringf("%s" "end\n", indent.c_str());
}
// Output Verilog that looks something like this:
// reg [..] _3_;
@@ -668,8 +913,19 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
if( clk_domain != "")
{
f << stringf("%s" "always%s @(%s) begin\n", indent.c_str(), systemverilog ? "_ff" : "", clk_domain.c_str());
- for(auto &line : lof_lines)
- f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
+ bool has_arst = clk_to_arst_cond.count(clk_domain) != 0;
+ if (has_arst) {
+ f << stringf("%s%s" "if (%s) begin\n", indent.c_str(), indent.c_str(), clk_to_arst_cond[clk_domain].c_str());
+ for(auto &line : clk_to_arst_body[clk_domain])
+ f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str());
+ f << stringf("%s%s" "end else begin\n", indent.c_str(), indent.c_str());
+ for(auto &line : lof_lines)
+ f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str());
+ f << stringf("%s%s" "end\n", indent.c_str(), indent.c_str());
+ } else {
+ for(auto &line : lof_lines)
+ f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
+ }
f << stringf("%s" "end\n", indent.c_str());
}
else
@@ -1144,7 +1400,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
FfData ff(nullptr, cell);
// $ff / $_FF_ cell: not supported.
- if (ff.has_d && !ff.has_clk && !ff.has_en)
+ if (ff.has_gclk)
return false;
std::string reg_name = cellname(cell);
@@ -1165,17 +1421,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
for (int i = 0; i < chunks; i++)
{
- SigSpec sig_d;
+ SigSpec sig_d, sig_ad;
Const val_arst, val_srst;
- std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name;
+ std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name, sig_aload_name;
if (chunky) {
reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i);
- if (ff.has_d)
+ if (ff.has_gclk || ff.has_clk)
sig_d = ff.sig_d[i];
+ if (ff.has_aload)
+ sig_ad = ff.sig_ad[i];
} else {
reg_bit_name = reg_name;
- if (ff.has_d)
- sig_d = ff.sig_d;
+ sig_d = ff.sig_d;
+ sig_ad = ff.sig_ad;
}
if (ff.has_arst)
val_arst = chunky ? ff.val_arst[i] : ff.val_arst;
@@ -1183,28 +1441,38 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
val_srst = chunky ? ff.val_srst[i] : ff.val_srst;
// If there are constants in the sensitivity list, replace them with an intermediate wire
- if (ff.has_sr) {
- if (ff.sig_set[i].wire == NULL)
- {
- sig_set_name = next_auto_id();
- f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str());
- dump_const(f, ff.sig_set[i].data);
- f << stringf(";\n");
- }
- if (ff.sig_clr[i].wire == NULL)
- {
- sig_clr_name = next_auto_id();
- f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str());
- dump_const(f, ff.sig_clr[i].data);
- f << stringf(";\n");
- }
- } else if (ff.has_arst) {
- if (ff.sig_arst[i].wire == NULL)
- {
- sig_arst_name = next_auto_id();
- f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str());
- dump_const(f, ff.sig_arst[i].data);
- f << stringf(";\n");
+ if (ff.has_clk) {
+ if (ff.has_sr) {
+ if (ff.sig_set[i].wire == NULL)
+ {
+ sig_set_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str());
+ dump_const(f, ff.sig_set[i].data);
+ f << stringf(";\n");
+ }
+ if (ff.sig_clr[i].wire == NULL)
+ {
+ sig_clr_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str());
+ dump_const(f, ff.sig_clr[i].data);
+ f << stringf(";\n");
+ }
+ } else if (ff.has_arst) {
+ if (ff.sig_arst[0].wire == NULL)
+ {
+ sig_arst_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str());
+ dump_const(f, ff.sig_arst[0].data);
+ f << stringf(";\n");
+ }
+ } else if (ff.has_aload) {
+ if (ff.sig_aload[0].wire == NULL)
+ {
+ sig_aload_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_aload_name.c_str());
+ dump_const(f, ff.sig_aload[0].data);
+ f << stringf(";\n");
+ }
}
}
@@ -1226,13 +1494,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf("%s", sig_clr_name.c_str());
else
dump_sigspec(f, ff.sig_clr[i]);
-
} else if (ff.has_arst) {
f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg");
- if (ff.sig_arst[i].wire == NULL)
+ if (ff.sig_arst[0].wire == NULL)
f << stringf("%s", sig_arst_name.c_str());
else
dump_sigspec(f, ff.sig_arst);
+ } else if (ff.has_aload) {
+ f << stringf(", %sedge ", ff.pol_aload ? "pos" : "neg");
+ if (ff.sig_aload[0].wire == NULL)
+ f << stringf("%s", sig_aload_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_aload);
}
f << stringf(")\n");
@@ -1253,7 +1526,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf("%s" " else ", indent.c_str());
} else if (ff.has_arst) {
f << stringf("if (%s", ff.pol_arst ? "" : "!");
- if (ff.sig_arst[i].wire == NULL)
+ if (ff.sig_arst[0].wire == NULL)
f << stringf("%s", sig_arst_name.c_str());
else
dump_sigspec(f, ff.sig_arst);
@@ -1261,11 +1534,21 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
dump_sigspec(f, val_arst);
f << stringf(";\n");
f << stringf("%s" " else ", indent.c_str());
+ } else if (ff.has_aload) {
+ f << stringf("if (%s", ff.pol_aload ? "" : "!");
+ if (ff.sig_aload[0].wire == NULL)
+ f << stringf("%s", sig_aload_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_aload);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, sig_ad);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
}
- if (ff.has_srst && ff.has_en && ff.ce_over_srst) {
- f << stringf("if (%s", ff.pol_en ? "" : "!");
- dump_sigspec(f, ff.sig_en);
+ if (ff.has_srst && ff.has_ce && ff.ce_over_srst) {
+ f << stringf("if (%s", ff.pol_ce ? "" : "!");
+ dump_sigspec(f, ff.sig_ce);
f << stringf(")\n");
f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!");
dump_sigspec(f, ff.sig_srst);
@@ -1282,9 +1565,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(";\n");
f << stringf("%s" " else ", indent.c_str());
}
- if (ff.has_en) {
- f << stringf("if (%s", ff.pol_en ? "" : "!");
- dump_sigspec(f, ff.sig_en);
+ if (ff.has_ce) {
+ f << stringf("if (%s", ff.pol_ce ? "" : "!");
+ dump_sigspec(f, ff.sig_ce);
f << stringf(") ");
}
}
@@ -1306,7 +1589,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
dump_sigspec(f, ff.sig_set[i]);
f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str());
- if (ff.has_d)
+ if (ff.has_aload)
f << stringf("%s" " else ", indent.c_str());
} else if (ff.has_arst) {
f << stringf("if (%s", ff.pol_arst ? "" : "!");
@@ -1314,14 +1597,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(") %s = ", reg_bit_name.c_str());
dump_sigspec(f, val_arst);
f << stringf(";\n");
- if (ff.has_d)
+ if (ff.has_aload)
f << stringf("%s" " else ", indent.c_str());
}
- if (ff.has_d) {
- f << stringf("if (%s", ff.pol_en ? "" : "!");
- dump_sigspec(f, ff.sig_en);
+ if (ff.has_aload) {
+ f << stringf("if (%s", ff.pol_aload ? "" : "!");
+ dump_sigspec(f, ff.sig_aload);
f << stringf(") %s = ", reg_bit_name.c_str());
- dump_sigspec(f, sig_d);
+ dump_sigspec(f, sig_ad);
f << stringf(";\n");
}
}
@@ -1464,7 +1747,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
{
// Handled by dump_memory
- if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit)))
+ if (cell->is_mem_cell())
return;
if (cell->type[0] == '$' && !noexpr) {
@@ -1662,7 +1945,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
if (!systemverilog)
- f << indent + " " << "if (" << id("\\initial") << ") begin end\n";
+ f << indent + " " << "if (" << id(initial_id) << ") begin end\n";
dump_case_body(f, indent, &proc->root_case, true);
std::string backup_indent = indent;
@@ -1781,6 +2064,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true);
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
bool keep_running = true;
+ int cnt = 0;
for (int port_id = 1; keep_running; port_id++) {
keep_running = false;
for (auto wire : module->wires()) {
@@ -1789,14 +2073,16 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
f << stringf(", ");
f << stringf("%s", id(wire->name).c_str());
keep_running = true;
+ if (cnt==20) { f << stringf("\n"); cnt = 0; } else cnt++;
continue;
}
}
}
f << stringf(");\n");
-
- if (!systemverilog && !module->processes.empty())
- f << indent + " " << "reg " << id("\\initial") << " = 0;\n";
+ if (!systemverilog && !module->processes.empty()) {
+ initial_id = NEW_ID;
+ f << indent + " " << "reg " << id(initial_id) << " = 0;\n";
+ }
for (auto w : module->wires())
dump_wire(f, indent + " ", w);
@@ -2014,6 +2300,12 @@ struct VerilogBackend : public Backend {
extmem_prefix = filename.substr(0, filename.rfind('.'));
}
+ log_push();
+ Pass::call(design, "bmuxmap");
+ Pass::call(design, "demuxmap");
+ Pass::call(design, "clean_zerowidth");
+ log_pop();
+
design->sort();
*f << stringf("/* Generated by %s */\n", yosys_version_str);