pass metadata: fixed some of the output formatting
[yosys.git] / backends / verilog / verilog_backend.cc
index 776f4eacbbe1c4d360fd1e44f0fd49bf6627cec0..aa1d4558c34d51093c898d20207e28a49fab2b5d 100644 (file)
@@ -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
@@ -25,6 +25,8 @@
 #include "kernel/celltypes.h"
 #include "kernel/log.h"
 #include "kernel/sigtools.h"
+#include "kernel/ff.h"
+#include "kernel/mem.h"
 #include <string>
 #include <sstream>
 #include <set>
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal, siminit;
-int auto_name_counter, auto_name_offset, auto_name_digits;
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs;
+int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
 std::map<RTLIL::IdString, int> auto_name_map;
-std::set<RTLIL::IdString> reg_wires, reg_ct;
-std::string auto_prefix;
+std::set<RTLIL::IdString> reg_wires;
+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)
 {
@@ -73,12 +76,12 @@ void reset_auto_counter(RTLIL::Module *module)
 
        reset_auto_counter_id(module->name, false);
 
-       for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
-               reset_auto_counter_id(it->second->name, true);
+       for (auto w : module->wires())
+               reset_auto_counter_id(w->name, true);
 
-       for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) {
-               reset_auto_counter_id(it->second->name, true);
-               reset_auto_counter_id(it->second->type, false);
+       for (auto cell : module->cells()) {
+               reset_auto_counter_id(cell->name, true);
+               reset_auto_counter_id(cell->type, false);
        }
 
        for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
@@ -200,9 +203,9 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
                        int32_t val = 0;
                        for (int i = offset+width-1; i >= offset; i--) {
                                log_assert(i < (int)data.bits.size());
-                               if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
+                               if (data.bits[i] != State::S0 && data.bits[i] != State::S1)
                                        goto dump_hex;
-                               if (data.bits[i] == RTLIL::S1)
+                               if (data.bits[i] == State::S1)
                                        val |= 1 << (i - offset);
                        }
                        if (decimal)
@@ -219,8 +222,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
                        for (int i = offset; i < offset+width; i++) {
                                log_assert(i < (int)data.bits.size());
                                switch (data.bits[i]) {
-                               case RTLIL::S0: bin_digits.push_back('0'); break;
-                               case RTLIL::S1: bin_digits.push_back('1'); break;
+                               case State::S0: bin_digits.push_back('0'); break;
+                               case State::S1: bin_digits.push_back('1'); break;
                                case RTLIL::Sx: bin_digits.push_back('x'); break;
                                case RTLIL::Sz: bin_digits.push_back('z'); break;
                                case RTLIL::Sa: bin_digits.push_back('?'); break;
@@ -273,8 +276,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
                        for (int i = offset+width-1; i >= offset; i--) {
                                log_assert(i < (int)data.bits.size());
                                switch (data.bits[i]) {
-                               case RTLIL::S0: f << stringf("0"); break;
-                               case RTLIL::S1: f << stringf("1"); break;
+                               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;
@@ -355,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()) {
@@ -371,18 +375,19 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
        }
 }
 
-void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool as_comment = false)
+void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false)
 {
        if (noattr)
                return;
        if (attr2comment)
                as_comment = true;
        for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+               if (it->first == ID::init && regattr) continue;
                f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
                f << stringf(" = ");
-               if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
+               if (modattr && (it->second == State::S0 || it->second == Const(0)))
                        f << stringf(" 0 ");
-               else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
+               else if (modattr && (it->second == State::S1 || it->second == Const(1)))
                        f << stringf(" 1 ");
                else
                        dump_const(f, it->second, -1, 0, false, as_comment);
@@ -392,7 +397,7 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString,
 
 void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
 {
-       dump_attributes(f, indent, wire->attributes);
+       dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name));
 #if 0
        if (wire->port_input && !wire->port_output)
                f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
@@ -422,20 +427,514 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
                f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
        if (reg_wires.count(wire->name)) {
                f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str());
-               if (wire->attributes.count("\\init")) {
+               if (wire->attributes.count(ID::init)) {
                        f << stringf(" = ");
-                       dump_const(f, wire->attributes.at("\\init"));
+                       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
 }
 
-void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
+void dump_memory(std::ostream &f, std::string indent, Mem &mem)
 {
-       dump_attributes(f, indent, memory->attributes);
-       f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset);
+       std::string mem_id = id(mem.memid);
+
+       dump_attributes(f, indent, mem.attributes);
+       f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), mem.width-1, mem_id.c_str(), mem.size+mem.start_offset-1, mem.start_offset);
+
+       // for memory block make something like:
+       //  reg [7:0] memid [3:0];
+       //  initial begin
+       //    memid[0] = ...
+       //  end
+       if (!mem.inits.empty())
+       {
+               if (extmem)
+               {
+                       std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++);
+
+                       std::string extmem_filename_esc;
+                       for (auto c : extmem_filename)
+                       {
+                               if (c == '\n')
+                                       extmem_filename_esc += "\\n";
+                               else if (c == '\t')
+                                       extmem_filename_esc += "\\t";
+                               else if (c < 32)
+                                       extmem_filename_esc += stringf("\\%03o", c);
+                               else if (c == '"')
+                                       extmem_filename_esc += "\\\"";
+                               else if (c == '\\')
+                                       extmem_filename_esc += "\\\\";
+                               else
+                                       extmem_filename_esc += c;
+                       }
+                       f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent.c_str(), extmem_filename_esc.c_str(), mem_id.c_str());
+
+                       std::ofstream extmem_f(extmem_filename, std::ofstream::trunc);
+                       if (extmem_f.fail())
+                               log_error("Can't open file `%s' for writing: %s\n", extmem_filename.c_str(), strerror(errno));
+                       else
+                       {
+                               Const data = mem.get_init_data();
+                               for (int i=0; i<mem.size; i++)
+                               {
+                                       RTLIL::Const element = data.extract(i*mem.width, mem.width);
+                                       for (int j=0; j<element.size(); j++)
+                                       {
+                                               switch (element[element.size()-j-1])
+                                               {
+                                                       case State::S0: extmem_f << '0'; break;
+                                                       case State::S1: extmem_f << '1'; break;
+                                                       case State::Sx: extmem_f << 'x'; break;
+                                                       case State::Sz: extmem_f << 'z'; break;
+                                                       case State::Sa: extmem_f << '_'; break;
+                                                       case State::Sm: log_error("Found marker state in final netlist.");
+                                               }
+                                       }
+                                       extmem_f << '\n';
+                               }
+                       }
+               }
+               else
+               {
+                       f << stringf("%s" "initial begin\n", indent.c_str());
+                       for (auto &init : mem.inits) {
+                               int words = GetSize(init.data) / mem.width;
+                               int start = init.addr.as_int();
+                               for (int i=0; i<words; i++)
+                               {
+                                       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());
+               }
+       }
+
+       // 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
+       std::vector<std::string> lof_reg_declarations;
+
+       // read ports
+       for (auto &port : mem.rd_ports)
+       {
+               if (port.clk_enable)
+               {
+                       {
+                               std::ostringstream os;
+                               dump_sigspec(os, port.clk);
+                               clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str());
+                               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();
+                               }
+                       }
+
+                       // 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;
+                               //   always @(posedge clk)
+                               //      if (rd_en) temp_id <= array_reg[r_addr];
+                               //   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;
+                                       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";
+                                       }
+                                       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);
+                                       std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
+                                       clk_to_lof_body[""].push_back(line);
+                               }
+                       }
+                       else
+                       {
+                               // for rd-transparent read-ports make something like:
+                               //   reg [..] temp_id;
+                               //   always @(posedge clk)
+                               //     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 - port.wide_log2, temp_id.c_str()) );
+                               {
+                                       std::ostringstream os;
+                                       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;
+                                       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];
+                       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.  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)
+               {
+                       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;
+               }
+
+               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" : " @*");
+               }
+
+               for (int pidx = 0; pidx < GetSize(mem.wr_ports); pidx++)
+               {
+                       if (!wr_ports_now.count(pidx))
+                               continue;
+                       wr_ports_done.insert(pidx);
+
+                       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++)
+                       {
+                               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");
+                               }
+                       }
+               }
+
+               f << stringf("%s" "end\n", indent.c_str());
+       }
+       // Output Verilog that looks something like this:
+       // reg [..] _3_;
+       // always @(posedge CLK2) begin
+       //   _3_ <= memory[D1ADDR];
+       //   if (A1EN)
+       //     memory[A1ADDR] <= A1DATA;
+       //   if (A2EN)
+       //     memory[A2ADDR] <= A2DATA;
+       //   ...
+       // end
+       // always @(negedge CLK1) begin
+       //   if (C1EN)
+       //     memory[C1ADDR] <= C1DATA;
+       // end
+       // ...
+       // assign D1DATA = _3_;
+       // assign D2DATA <= memory[D2ADDR];
+
+       // the reg ... definitions
+       for(auto &reg : lof_reg_declarations)
+       {
+               f << stringf("%s" "%s", indent.c_str(), reg.c_str());
+       }
+       // the block of expressions by clock domain
+       for(auto &pair : clk_to_lof_body)
+       {
+               std::string clk_domain = pair.first;
+               std::vector<std::string> lof_lines = pair.second;
+               if( clk_domain != "")
+               {
+                       f << stringf("%s" "always%s @(%s) begin\n", indent.c_str(), systemverilog ? "_ff" : "", clk_domain.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
+               {
+                       // the non-clocked assignments
+                       for(auto &line : lof_lines)
+                               f << stringf("%s" "%s", indent.c_str(), line.c_str());
+               }
+       }
 }
 
 void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
@@ -450,9 +949,9 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b
 
 std::string cellname(RTLIL::Cell *cell)
 {
-       if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort("\\Q"))
+       if (!norename && cell->name[0] == '$' && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_)))
        {
-               RTLIL::SigSpec sig = cell->getPort("\\Q");
+               RTLIL::SigSpec sig = cell->getPort(ID::Q);
                if (GetSize(sig) != 1 || sig.is_fully_const())
                        goto no_special_reg_name;
 
@@ -487,7 +986,7 @@ no_special_reg_name:
 void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
 {
        f << stringf("%s" "assign ", indent.c_str());
-       dump_sigspec(f, cell->getPort("\\Y"));
+       dump_sigspec(f, cell->getPort(ID::Y));
        f << stringf(" = %s ", op.c_str());
        dump_attributes(f, "", cell->attributes, ' ');
        dump_cell_expr_port(f, cell, "A", true);
@@ -497,7 +996,7 @@ void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell
 void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
 {
        f << stringf("%s" "assign ", indent.c_str());
-       dump_sigspec(f, cell->getPort("\\Y"));
+       dump_sigspec(f, cell->getPort(ID::Y));
        f << stringf(" = ");
        dump_cell_expr_port(f, cell, "A", true);
        f << stringf(" %s ", op.c_str());
@@ -508,9 +1007,9 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell
 
 bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 {
-       if (cell->type == "$_NOT_") {
+       if (cell->type == ID($_NOT_)) {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
                f << stringf("~");
                dump_attributes(f, "", cell->attributes, ' ');
@@ -519,34 +1018,34 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                return true;
        }
 
-       if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) {
+       if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
-               if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
+               if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_)))
                        f << stringf("~(");
                dump_cell_expr_port(f, cell, "A", false);
                f << stringf(" ");
-               if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_"))
+               if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_ANDNOT_)))
                        f << stringf("&");
-               if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_"))
+               if (cell->type.in(ID($_OR_), ID($_NOR_), ID($_ORNOT_)))
                        f << stringf("|");
-               if (cell->type.in("$_XOR_", "$_XNOR_"))
+               if (cell->type.in(ID($_XOR_), ID($_XNOR_)))
                        f << stringf("^");
                dump_attributes(f, "", cell->attributes, ' ');
                f << stringf(" ");
-               if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
+               if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_)))
                        f << stringf("~(");
                dump_cell_expr_port(f, cell, "B", false);
-               if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
+               if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_)))
                        f << stringf(")");
                f << stringf(";\n");
                return true;
        }
 
-       if (cell->type == "$_MUX_") {
+       if (cell->type == ID($_MUX_)) {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
                dump_cell_expr_port(f, cell, "S", false);
                f << stringf(" ? ");
@@ -558,9 +1057,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                return true;
        }
 
-       if (cell->type == "$_NMUX_") {
+       if (cell->type == ID($_NMUX_)) {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = !(");
                dump_cell_expr_port(f, cell, "S", false);
                f << stringf(" ? ");
@@ -572,14 +1071,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                return true;
        }
 
-       if (cell->type.in("$_AOI3_", "$_OAI3_")) {
+       if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ~((");
                dump_cell_expr_port(f, cell, "A", false);
-               f << stringf(cell->type == "$_AOI3_" ? " & " : " | ");
+               f << stringf(cell->type == ID($_AOI3_) ? " & " : " | ");
                dump_cell_expr_port(f, cell, "B", false);
-               f << stringf(cell->type == "$_AOI3_" ? ") |" : ") &");
+               f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &");
                dump_attributes(f, "", cell->attributes, ' ');
                f << stringf(" ");
                dump_cell_expr_port(f, cell, "C", false);
@@ -587,226 +1086,226 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                return true;
        }
 
-       if (cell->type.in("$_AOI4_", "$_OAI4_")) {
+       if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ~((");
                dump_cell_expr_port(f, cell, "A", false);
-               f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
+               f << stringf(cell->type == ID($_AOI4_) ? " & " : " | ");
                dump_cell_expr_port(f, cell, "B", false);
-               f << stringf(cell->type == "$_AOI4_" ? ") |" : ") &");
+               f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &");
                dump_attributes(f, "", cell->attributes, ' ');
                f << stringf(" (");
                dump_cell_expr_port(f, cell, "C", false);
-               f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
+               f << stringf(cell->type == ID($_AOI4_) ? " & " : " | ");
                dump_cell_expr_port(f, cell, "D", false);
                f << stringf("));\n");
                return true;
        }
 
-       if (cell->type.substr(0, 6) == "$_DFF_")
-       {
-               std::string reg_name = cellname(cell);
-               bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
+#define HANDLE_UNIOP(_type, _operator) \
+       if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
+#define HANDLE_BINOP(_type, _operator) \
+       if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
 
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
-                       dump_reg_init(f, cell->getPort("\\Q"));
-                       f << ";\n";
-               }
+       HANDLE_UNIOP(ID($not), "~")
+       HANDLE_UNIOP(ID($pos), "+")
+       HANDLE_UNIOP(ID($neg), "-")
+
+       HANDLE_BINOP(ID($and),  "&")
+       HANDLE_BINOP(ID($or),   "|")
+       HANDLE_BINOP(ID($xor),  "^")
+       HANDLE_BINOP(ID($xnor), "~^")
+
+       HANDLE_UNIOP(ID($reduce_and),  "&")
+       HANDLE_UNIOP(ID($reduce_or),   "|")
+       HANDLE_UNIOP(ID($reduce_xor),  "^")
+       HANDLE_UNIOP(ID($reduce_xnor), "~^")
+       HANDLE_UNIOP(ID($reduce_bool), "|")
+
+       HANDLE_BINOP(ID($shl),  "<<")
+       HANDLE_BINOP(ID($shr),  ">>")
+       HANDLE_BINOP(ID($sshl), "<<<")
+       HANDLE_BINOP(ID($sshr), ">>>")
+
+       HANDLE_BINOP(ID($lt),  "<")
+       HANDLE_BINOP(ID($le),  "<=")
+       HANDLE_BINOP(ID($eq),  "==")
+       HANDLE_BINOP(ID($ne),  "!=")
+       HANDLE_BINOP(ID($eqx), "===")
+       HANDLE_BINOP(ID($nex), "!==")
+       HANDLE_BINOP(ID($ge),  ">=")
+       HANDLE_BINOP(ID($gt),  ">")
+
+       HANDLE_BINOP(ID($add), "+")
+       HANDLE_BINOP(ID($sub), "-")
+       HANDLE_BINOP(ID($mul), "*")
+       HANDLE_BINOP(ID($div), "/")
+       HANDLE_BINOP(ID($mod), "%")
+       HANDLE_BINOP(ID($pow), "**")
+
+       HANDLE_UNIOP(ID($logic_not), "!")
+       HANDLE_BINOP(ID($logic_and), "&&")
+       HANDLE_BINOP(ID($logic_or),  "||")
 
-               dump_attributes(f, indent, cell->attributes);
-               f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
-               dump_sigspec(f, cell->getPort("\\C"));
-               if (cell->type[7] != '_') {
-                       f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
-                       dump_sigspec(f, cell->getPort("\\R"));
-               }
-               f << stringf(")\n");
-
-               if (cell->type[7] != '_') {
-                       f << stringf("%s" "  if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
-                       dump_sigspec(f, cell->getPort("\\R"));
-                       f << stringf(")\n");
-                       f << stringf("%s" "    %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
-                       f << stringf("%s" "  else\n", indent.c_str());
-               }
+#undef HANDLE_UNIOP
+#undef HANDLE_BINOP
+
+       if (cell->type == ID($divfloor))
+       {
+               // wire [MAXLEN+1:0] _0_, _1_, _2_;
+               // assign _0_ = $signed(A);
+               // assign _1_ = $signed(B);
+               // assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1));
+               // assign Y = $signed(_2_) / $signed(_1_);
+
+               if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+                       SigSpec sig_a = cell->getPort(ID::A);
+                       SigSpec sig_b = cell->getPort(ID::B);
+
+                       std::string buf_a = next_auto_id();
+                       std::string buf_b = next_auto_id();
+                       std::string buf_num = next_auto_id();
+                       int size_a = GetSize(sig_a);
+                       int size_b = GetSize(sig_b);
+                       int size_y = GetSize(cell->getPort(ID::Y));
+                       int size_max = std::max(size_a, std::max(size_b, size_y));
+
+                       // intentionally one wider than maximum width
+                       f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str());
+                       f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str());
+                       dump_cell_expr_port(f, cell, "A", true);
+                       f << stringf(";\n");
+                       f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str());
+                       dump_cell_expr_port(f, cell, "B", true);
+                       f << stringf(";\n");
+
+                       f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str());
+                       f << stringf("(");
+                       dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+                       f << stringf(" == ");
+                       dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+                       f << stringf(") || ");
+                       dump_sigspec(f, sig_a);
+                       f << stringf(" == 0 ? %s : ", buf_a.c_str());
+                       f << stringf("$signed(%s - (", buf_a.c_str());
+                       dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+                       f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str());
 
-               f << stringf("%s" "    %s <= ", indent.c_str(), reg_name.c_str());
-               dump_cell_expr_port(f, cell, "D", false);
-               f << stringf(";\n");
 
-               if (!out_is_reg_wire) {
                        f << stringf("%s" "assign ", indent.c_str());
-                       dump_sigspec(f, cell->getPort("\\Q"));
-                       f << stringf(" = %s;\n", reg_name.c_str());
+                       dump_sigspec(f, cell->getPort(ID::Y));
+                       f << stringf(" = $signed(%s) / ", buf_num.c_str());
+                       dump_attributes(f, "", cell->attributes, ' ');
+                       f << stringf("$signed(%s);\n", buf_b.c_str());
+                       return true;
+               } else {
+                       // same as truncating division
+                       dump_cell_expr_binop(f, indent, cell, "/");
+                       return true;
                }
-
-               return true;
        }
 
-       if (cell->type.substr(0, 8) == "$_DFFSR_")
+       if (cell->type == ID($modfloor))
        {
-               char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
-
-               std::string reg_name = cellname(cell);
-               bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
-
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
-                       dump_reg_init(f, cell->getPort("\\Q"));
-                       f << ";\n";
-               }
-
-               dump_attributes(f, indent, cell->attributes);
-               f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
-               dump_sigspec(f, cell->getPort("\\C"));
-               f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg");
-               dump_sigspec(f, cell->getPort("\\S"));
-               f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg");
-               dump_sigspec(f, cell->getPort("\\R"));
-               f << stringf(")\n");
-
-               f << stringf("%s" "  if (%s", indent.c_str(), pol_r == 'P' ? "" : "!");
-               dump_sigspec(f, cell->getPort("\\R"));
-               f << stringf(")\n");
-               f << stringf("%s" "    %s <= 0;\n", indent.c_str(), reg_name.c_str());
-
-               f << stringf("%s" "  else if (%s", indent.c_str(), pol_s == 'P' ? "" : "!");
-               dump_sigspec(f, cell->getPort("\\S"));
-               f << stringf(")\n");
-               f << stringf("%s" "    %s <= 1;\n", indent.c_str(), reg_name.c_str());
-
-               f << stringf("%s" "  else\n", indent.c_str());
-               f << stringf("%s" "    %s <= ", indent.c_str(), reg_name.c_str());
-               dump_cell_expr_port(f, cell, "D", false);
-               f << stringf(";\n");
+               // wire truncated = $signed(A) % $signed(B);
+               // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated);
+
+               if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+                       SigSpec sig_a = cell->getPort(ID::A);
+                       SigSpec sig_b = cell->getPort(ID::B);
+
+                       std::string temp_id = next_auto_id();
+                       f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
+                       dump_cell_expr_port(f, cell, "A", true);
+                       f << stringf(" %% ");
+                       dump_attributes(f, "", cell->attributes, ' ');
+                       dump_cell_expr_port(f, cell, "B", true);
+                       f << stringf(";\n");
 
-               if (!out_is_reg_wire) {
                        f << stringf("%s" "assign ", indent.c_str());
-                       dump_sigspec(f, cell->getPort("\\Q"));
-                       f << stringf(" = %s;\n", reg_name.c_str());
+                       dump_sigspec(f, cell->getPort(ID::Y));
+                       f << stringf(" = (");
+                       dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+                       f << stringf(" == ");
+                       dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+                       f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str());
+                       dump_cell_expr_port(f, cell, "B", true);
+                       f << stringf(" + $signed(%s);\n", temp_id.c_str());
+                       return true;
+               } else {
+                       // same as truncating modulo
+                       dump_cell_expr_binop(f, indent, cell, "%");
+                       return true;
                }
-
-               return true;
        }
 
-#define HANDLE_UNIOP(_type, _operator) \
-       if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
-#define HANDLE_BINOP(_type, _operator) \
-       if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
-
-       HANDLE_UNIOP("$not", "~")
-       HANDLE_UNIOP("$pos", "+")
-       HANDLE_UNIOP("$neg", "-")
-
-       HANDLE_BINOP("$and",  "&")
-       HANDLE_BINOP("$or",   "|")
-       HANDLE_BINOP("$xor",  "^")
-       HANDLE_BINOP("$xnor", "~^")
-
-       HANDLE_UNIOP("$reduce_and",  "&")
-       HANDLE_UNIOP("$reduce_or",   "|")
-       HANDLE_UNIOP("$reduce_xor",  "^")
-       HANDLE_UNIOP("$reduce_xnor", "~^")
-       HANDLE_UNIOP("$reduce_bool", "|")
-
-       HANDLE_BINOP("$shl",  "<<")
-       HANDLE_BINOP("$shr",  ">>")
-       HANDLE_BINOP("$sshl", "<<<")
-       HANDLE_BINOP("$sshr", ">>>")
-
-       HANDLE_BINOP("$lt",  "<")
-       HANDLE_BINOP("$le",  "<=")
-       HANDLE_BINOP("$eq",  "==")
-       HANDLE_BINOP("$ne",  "!=")
-       HANDLE_BINOP("$eqx", "===")
-       HANDLE_BINOP("$nex", "!==")
-       HANDLE_BINOP("$ge",  ">=")
-       HANDLE_BINOP("$gt",  ">")
-
-       HANDLE_BINOP("$add", "+")
-       HANDLE_BINOP("$sub", "-")
-       HANDLE_BINOP("$mul", "*")
-       HANDLE_BINOP("$div", "/")
-       HANDLE_BINOP("$mod", "%")
-       HANDLE_BINOP("$pow", "**")
-
-       HANDLE_UNIOP("$logic_not", "!")
-       HANDLE_BINOP("$logic_and", "&&")
-       HANDLE_BINOP("$logic_or",  "||")
-
-#undef HANDLE_UNIOP
-#undef HANDLE_BINOP
-
-       if (cell->type == "$shift")
+       if (cell->type == ID($shift))
        {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
-               if (cell->getParam("\\B_SIGNED").as_bool())
+               if (cell->getParam(ID::B_SIGNED).as_bool())
                {
-                       f << stringf("$signed(");
-                       dump_sigspec(f, cell->getPort("\\B"));
-                       f << stringf(")");
+                       dump_cell_expr_port(f, cell, "B", true);
                        f << stringf(" < 0 ? ");
-                       dump_sigspec(f, cell->getPort("\\A"));
+                       dump_cell_expr_port(f, cell, "A", true);
                        f << stringf(" << - ");
-                       dump_sigspec(f, cell->getPort("\\B"));
+                       dump_sigspec(f, cell->getPort(ID::B));
                        f << stringf(" : ");
-                       dump_sigspec(f, cell->getPort("\\A"));
+                       dump_cell_expr_port(f, cell, "A", true);
                        f << stringf(" >> ");
-                       dump_sigspec(f, cell->getPort("\\B"));
+                       dump_sigspec(f, cell->getPort(ID::B));
                }
                else
                {
-                       dump_sigspec(f, cell->getPort("\\A"));
+                       dump_cell_expr_port(f, cell, "A", true);
                        f << stringf(" >> ");
-                       dump_sigspec(f, cell->getPort("\\B"));
+                       dump_sigspec(f, cell->getPort(ID::B));
                }
                f << stringf(";\n");
                return true;
        }
 
-       if (cell->type == "$shiftx")
+       if (cell->type == ID($shiftx))
        {
                std::string temp_id = next_auto_id();
-               f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str());
-               dump_sigspec(f, cell->getPort("\\A"));
+               f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
+               dump_sigspec(f, cell->getPort(ID::A));
                f << stringf(";\n");
 
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = %s[", temp_id.c_str());
-               if (cell->getParam("\\B_SIGNED").as_bool())
+               if (cell->getParam(ID::B_SIGNED).as_bool())
                        f << stringf("$signed(");
-               dump_sigspec(f, cell->getPort("\\B"));
-               if (cell->getParam("\\B_SIGNED").as_bool())
+               dump_sigspec(f, cell->getPort(ID::B));
+               if (cell->getParam(ID::B_SIGNED).as_bool())
                        f << stringf(")");
-               f << stringf(" +: %d", cell->getParam("\\Y_WIDTH").as_int());
+               f << stringf(" +: %d", cell->getParam(ID::Y_WIDTH).as_int());
                f << stringf("];\n");
                return true;
        }
 
-       if (cell->type == "$mux")
+       if (cell->type == ID($mux))
        {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
-               dump_sigspec(f, cell->getPort("\\S"));
+               dump_sigspec(f, cell->getPort(ID::S));
                f << stringf(" ? ");
                dump_attributes(f, "", cell->attributes, ' ');
-               dump_sigspec(f, cell->getPort("\\B"));
+               dump_sigspec(f, cell->getPort(ID::B));
                f << stringf(" : ");
-               dump_sigspec(f, cell->getPort("\\A"));
+               dump_sigspec(f, cell->getPort(ID::A));
                f << stringf(";\n");
                return true;
        }
 
-       if (cell->type == "$pmux")
+       if (cell->type == ID($pmux))
        {
-               int width = cell->parameters["\\WIDTH"].as_int();
-               int s_width = cell->getPort("\\S").size();
+               int width = cell->parameters[ID::WIDTH].as_int();
+               int s_width = cell->getPort(ID::S).size();
                std::string func_name = cellname(cell);
 
                f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str());
@@ -838,495 +1337,348 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                f << stringf("%s" "endfunction\n", indent.c_str());
 
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = %s(", func_name.c_str());
-               dump_sigspec(f, cell->getPort("\\A"));
+               dump_sigspec(f, cell->getPort(ID::A));
                f << stringf(", ");
-               dump_sigspec(f, cell->getPort("\\B"));
+               dump_sigspec(f, cell->getPort(ID::B));
                f << stringf(", ");
-               dump_sigspec(f, cell->getPort("\\S"));
+               dump_sigspec(f, cell->getPort(ID::S));
                f << stringf(");\n");
                return true;
        }
 
-       if (cell->type == "$tribuf")
+       if (cell->type == ID($tribuf))
        {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
-               dump_sigspec(f, cell->getPort("\\EN"));
+               dump_sigspec(f, cell->getPort(ID::EN));
                f << stringf(" ? ");
-               dump_sigspec(f, cell->getPort("\\A"));
-               f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int());
+               dump_sigspec(f, cell->getPort(ID::A));
+               f << stringf(" : %d'bz;\n", cell->parameters.at(ID::WIDTH).as_int());
                return true;
        }
 
-       if (cell->type == "$slice")
+       if (cell->type == ID($slice))
        {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
-               dump_sigspec(f, cell->getPort("\\A"));
-               f << stringf(" >> %d;\n", cell->parameters.at("\\OFFSET").as_int());
+               dump_sigspec(f, cell->getPort(ID::A));
+               f << stringf(" >> %d;\n", cell->parameters.at(ID::OFFSET).as_int());
                return true;
        }
 
-       if (cell->type == "$concat")
+       if (cell->type == ID($concat))
        {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = { ");
-               dump_sigspec(f, cell->getPort("\\B"));
+               dump_sigspec(f, cell->getPort(ID::B));
                f << stringf(" , ");
-               dump_sigspec(f, cell->getPort("\\A"));
+               dump_sigspec(f, cell->getPort(ID::A));
                f << stringf(" };\n");
                return true;
        }
 
-       if (cell->type == "$lut")
+       if (cell->type == ID($lut))
        {
                f << stringf("%s" "assign ", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\Y"));
+               dump_sigspec(f, cell->getPort(ID::Y));
                f << stringf(" = ");
-               dump_const(f, cell->parameters.at("\\LUT"));
+               dump_const(f, cell->parameters.at(ID::LUT));
                f << stringf(" >> ");
                dump_attributes(f, "", cell->attributes, ' ');
-               dump_sigspec(f, cell->getPort("\\A"));
+               dump_sigspec(f, cell->getPort(ID::A));
                f << stringf(";\n");
                return true;
        }
 
-       if (cell->type == "$dffsr")
+       if (RTLIL::builtin_ff_cell_types().count(cell->type))
        {
-               SigSpec sig_clk = cell->getPort("\\CLK");
-               SigSpec sig_set = cell->getPort("\\SET");
-               SigSpec sig_clr = cell->getPort("\\CLR");
-               SigSpec sig_d = cell->getPort("\\D");
-               SigSpec sig_q = cell->getPort("\\Q");
+               FfData ff(nullptr, cell);
 
-               int width = cell->parameters["\\WIDTH"].as_int();
-               bool pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
-               bool pol_set = cell->parameters["\\SET_POLARITY"].as_bool();
-               bool pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool();
+               // $ff / $_FF_ cell: not supported.
+               if (ff.has_gclk)
+                       return false;
 
                std::string reg_name = cellname(cell);
-               bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
-
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str());
-                       dump_reg_init(f, sig_q);
-                       f << ";\n";
-               }
-
-               for (int i = 0; i < width; i++) {
-                       f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
-                       dump_sigspec(f, sig_clk);
-                       f << stringf(", %sedge ", pol_set ? "pos" : "neg");
-                       dump_sigspec(f, sig_set);
-                       f << stringf(", %sedge ", pol_clr ? "pos" : "neg");
-                       dump_sigspec(f, sig_clr);
-                       f << stringf(")\n");
-
-                       f << stringf("%s" "  if (%s", indent.c_str(), pol_clr ? "" : "!");
-                       dump_sigspec(f, sig_clr);
-                       f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i);
-
-                       f << stringf("%s" "  else if (%s", indent.c_str(), pol_set ? "" : "!");
-                       dump_sigspec(f, sig_set);
-                       f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i);
-
-                       f << stringf("%s" "  else  %s[%d] <= ", indent.c_str(), reg_name.c_str(), i);
-                       dump_sigspec(f, sig_d[i]);
-                       f << stringf(";\n");
-               }
+               bool out_is_reg_wire = is_reg_wire(ff.sig_q, reg_name);
 
                if (!out_is_reg_wire) {
-                       f << stringf("%s" "assign ", indent.c_str());
-                       dump_sigspec(f, sig_q);
-                       f << stringf(" = %s;\n", reg_name.c_str());
-               }
-
-               return true;
-       }
-
-       if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe")
-       {
-               RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
-               bool pol_clk, pol_arst = false, pol_en = false;
-
-               sig_clk = cell->getPort("\\CLK");
-               pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
-
-               if (cell->type == "$adff") {
-                       sig_arst = cell->getPort("\\ARST");
-                       pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool();
-                       val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]);
-               }
-
-               if (cell->type == "$dffe") {
-                       sig_en = cell->getPort("\\EN");
-                       pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
-               }
-
-               std::string reg_name = cellname(cell);
-               bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
-
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
-                       dump_reg_init(f, cell->getPort("\\Q"));
-                       f << ";\n";
-               }
-
-               f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
-               dump_sigspec(f, sig_clk);
-               if (cell->type == "$adff") {
-                       f << stringf(" or %sedge ", pol_arst ? "pos" : "neg");
-                       dump_sigspec(f, sig_arst);
-               }
-               f << stringf(")\n");
-
-               if (cell->type == "$adff") {
-                       f << stringf("%s" "  if (%s", indent.c_str(), pol_arst ? "" : "!");
-                       dump_sigspec(f, sig_arst);
-                       f << stringf(")\n");
-                       f << stringf("%s" "    %s <= ", indent.c_str(), reg_name.c_str());
-                       dump_sigspec(f, val_arst);
-                       f << stringf(";\n");
-                       f << stringf("%s" "  else\n", indent.c_str());
-               }
-
-               if (cell->type == "$dffe") {
-                       f << stringf("%s" "  if (%s", indent.c_str(), pol_en ? "" : "!");
-                       dump_sigspec(f, sig_en);
-                       f << stringf(")\n");
-               }
-
-               f << stringf("%s" "    %s <= ", indent.c_str(), reg_name.c_str());
-               dump_cell_expr_port(f, cell, "D", false);
-               f << stringf(";\n");
-
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "assign ", indent.c_str());
-                       dump_sigspec(f, cell->getPort("\\Q"));
-                       f << stringf(" = %s;\n", reg_name.c_str());
-               }
-
-               return true;
-       }
-
-       if (cell->type == "$dlatch")
-       {
-               RTLIL::SigSpec sig_en;
-               bool pol_en = false;
-
-               sig_en = cell->getPort("\\EN");
-               pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
-
-               std::string reg_name = cellname(cell);
-               bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
-
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
-                       dump_reg_init(f, cell->getPort("\\Q"));
+                       if (ff.width == 1)
+                               f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
+                       else
+                               f << stringf("%s" "reg [%d:0] %s", indent.c_str(), ff.width-1, reg_name.c_str());
+                       dump_reg_init(f, ff.sig_q);
                        f << ";\n";
                }
 
-               f << stringf("%s" "always @*\n", indent.c_str());
-
-               f << stringf("%s" "  if (%s", indent.c_str(), pol_en ? "" : "!");
-               dump_sigspec(f, sig_en);
-               f << stringf(")\n");
-
-               f << stringf("%s" "    %s = ", indent.c_str(), reg_name.c_str());
-               dump_cell_expr_port(f, cell, "D", false);
-               f << stringf(";\n");
-
-               if (!out_is_reg_wire) {
-                       f << stringf("%s" "assign ", indent.c_str());
-                       dump_sigspec(f, cell->getPort("\\Q"));
-                       f << stringf(" = %s;\n", reg_name.c_str());
-               }
-
-               return true;
-       }
+               // If the FF has CLR/SET inputs, emit every bit slice separately.
+               int chunks = ff.has_sr ? ff.width : 1;
+               bool chunky = ff.has_sr && ff.width != 1;
 
-       if (cell->type == "$mem")
-       {
-               RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
-               std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
-               int abits = cell->parameters["\\ABITS"].as_int();
-               int size = cell->parameters["\\SIZE"].as_int();
-               int offset = cell->parameters["\\OFFSET"].as_int();
-               int width = cell->parameters["\\WIDTH"].as_int();
-               bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
-
-               // for memory block make something like:
-               //  reg [7:0] memid [3:0];
-               //  initial begin
-               //    memid[0] = ...
-               //  end
-               f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
-               if (use_init)
+               for (int i = 0; i < chunks; i++)
                {
-                       f << stringf("%s" "initial begin\n", indent.c_str());
-                       for (int i=0; i<size; i++)
-                       {
-                               f << stringf("%s" "  %s[%d] = ", indent.c_str(), mem_id.c_str(), i);
-                               dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
-                               f << stringf(";\n");
+                       SigSpec sig_d, sig_ad;
+                       Const val_arst, val_srst;
+                       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_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;
+                               sig_d = ff.sig_d;
+                               sig_ad = ff.sig_ad;
                        }
-                       f << stringf("%s" "end\n", indent.c_str());
-               }
-
-               // create a map : "edge clk" -> expressions within that clock domain
-               dict<std::string, std::vector<std::string>> clk_to_lof_body;
-               clk_to_lof_body[""] = std::vector<std::string>();
-               std::string clk_domain_str;
-               // create a list of reg declarations
-               std::vector<std::string> lof_reg_declarations;
-
-               int nread_ports = cell->parameters["\\RD_PORTS"].as_int();
-               RTLIL::SigSpec sig_rd_clk, sig_rd_en, sig_rd_data, sig_rd_addr;
-               bool use_rd_clk, rd_clk_posedge, rd_transparent;
-               // read ports
-               for (int i=0; i < nread_ports; i++)
-               {
-                       sig_rd_clk = cell->getPort("\\RD_CLK").extract(i);
-                       sig_rd_en = cell->getPort("\\RD_EN").extract(i);
-                       sig_rd_data = cell->getPort("\\RD_DATA").extract(i*width, width);
-                       sig_rd_addr = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
-                       use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool();
-                       rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool();
-                       rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool();
-                       if (use_rd_clk)
-                       {
-                               {
-                                       std::ostringstream os;
-                                       dump_sigspec(os, sig_rd_clk);
-                                       clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "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 (!rd_transparent)
-                               {
-                                       // for clocked read ports make something like:
-                                       //   reg [..] temp_id;
-                                       //   always @(posedge clk)
-                                       //      if (rd_en) temp_id <= array_reg[r_addr];
-                                       //   assign r_data = temp_id;
-                                       std::string temp_id = next_auto_id();
-                                       lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
+                       if (ff.has_arst)
+                               val_arst = chunky ? ff.val_arst[i] : ff.val_arst;
+                       if (ff.has_srst)
+                               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_clk) {
+                               if (ff.has_sr) {
+                                       if (ff.sig_set[i].wire == NULL)
                                        {
-                                               std::ostringstream os;
-                                               if (sig_rd_en != RTLIL::SigBit(true))
-                                               {
-                                                       os << stringf("if (");
-                                                       dump_sigspec(os, sig_rd_en);
-                                                       os << stringf(") ");
-                                               }
-                                               os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
-                                               dump_sigspec(os, sig_rd_addr);
-                                               os << stringf("];\n");
-                                               clk_to_lof_body[clk_domain_str].push_back(os.str());
+                                               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)
                                        {
-                                               std::ostringstream os;
-                                               dump_sigspec(os, sig_rd_data);
-                                               std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
-                                               clk_to_lof_body[""].push_back(line);
+                                               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
-                               {
-                                       // for rd-transparent read-ports make something like:
-                                       //   reg [..] temp_id;
-                                       //   always @(posedge clk)
-                                       //     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", sig_rd_addr.size() - 1, temp_id.c_str()) );
+                               } else if (ff.has_arst) {
+                                       if (ff.sig_arst[0].wire == NULL)
                                        {
-                                               std::ostringstream os;
-                                               dump_sigspec(os, sig_rd_addr);
-                                               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);
+                                               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)
                                        {
-                                               std::ostringstream os;
-                                               dump_sigspec(os, sig_rd_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);
+                                               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");
                                        }
                                }
-                       } else {
-                               // for non-clocked read-ports make something like:
-                               //   assign r_data = array_reg[r_addr];
-                               std::ostringstream os, os2;
-                               dump_sigspec(os, sig_rd_data);
-                               dump_sigspec(os2, sig_rd_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);
                        }
-               }
 
-               int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
-               RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
-               bool wr_clk_posedge;
-
-               // write ports
-               for (int i=0; i < nwrite_ports; i++)
-               {
-                       sig_wr_clk = cell->getPort("\\WR_CLK").extract(i);
-                       sig_wr_data = cell->getPort("\\WR_DATA").extract(i*width, width);
-                       sig_wr_addr = cell->getPort("\\WR_ADDR").extract(i*abits, abits);
-                       sig_wr_en = cell->getPort("\\WR_EN").extract(i*width, width);
-                       wr_clk_posedge = cell->parameters["\\WR_CLK_POLARITY"].extract(i).as_bool();
+                       dump_attributes(f, indent, cell->attributes);
+                       if (ff.has_clk)
                        {
-                               std::ostringstream os;
-                               dump_sigspec(os, sig_wr_clk);
-                               clk_domain_str = stringf("%sedge %s", wr_clk_posedge ? "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>();
-                       }
-                       //   make something like:
-                       //   always @(posedge clk)
-                       //      if (wr_en_bit) memid[w_addr][??] <= w_data[??];
-                       //   ...
-                       for (int i = 0; i < GetSize(sig_wr_en); i++)
-                       {
-                               int start_i = i, width = 1;
-                               SigBit wen_bit = sig_wr_en[i];
-
-                               while (i+1 < GetSize(sig_wr_en) && active_sigmap(sig_wr_en[i+1]) == active_sigmap(wen_bit))
-                                       i++, width++;
-
-                               if (wen_bit == State::S0)
-                                       continue;
+                               // FFs.
+                               f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", ff.pol_clk ? "pos" : "neg");
+                               dump_sigspec(f, ff.sig_clk);
+                               if (ff.has_sr) {
+                                       f << stringf(", %sedge ", ff.pol_set ? "pos" : "neg");
+                                       if (ff.sig_set[i].wire == NULL)
+                                               f << stringf("%s", sig_set_name.c_str());
+                                       else
+                                               dump_sigspec(f, ff.sig_set[i]);
+
+                                       f << stringf(", %sedge ", ff.pol_clr ? "pos" : "neg");
+                                       if (ff.sig_clr[i].wire == NULL)
+                                               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[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");
+
+                               f << stringf("%s" "  ", indent.c_str());
+                               if (ff.has_sr) {
+                                       f << stringf("if (%s", ff.pol_clr ? "" : "!");
+                                       if (ff.sig_clr[i].wire == NULL)
+                                               f << stringf("%s", sig_clr_name.c_str());
+                                       else
+                                               dump_sigspec(f, ff.sig_clr[i]);
+                                       f << stringf(") %s <= 1'b0;\n", reg_bit_name.c_str());
+                                       f << stringf("%s" "  else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
+                                       if (ff.sig_set[i].wire == NULL)
+                                               f << stringf("%s", sig_set_name.c_str());
+                                       else
+                                               dump_sigspec(f, ff.sig_set[i]);
+                                       f << stringf(") %s <= 1'b1;\n", reg_bit_name.c_str());
+                                       f << stringf("%s" "  else ", indent.c_str());
+                               } else if (ff.has_arst) {
+                                       f << stringf("if (%s", ff.pol_arst ? "" : "!");
+                                       if (ff.sig_arst[0].wire == NULL)
+                                               f << stringf("%s", sig_arst_name.c_str());
+                                       else
+                                               dump_sigspec(f, ff.sig_arst);
+                                       f << stringf(") %s <= ", reg_bit_name.c_str());
+                                       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());
+                               }
 
-                               std::ostringstream os;
-                               if (wen_bit != State::S1)
-                               {
-                                       os << stringf("if (");
-                                       dump_sigspec(os, wen_bit);
-                                       os << stringf(") ");
+                               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);
+                                       f << stringf(") %s <= ", reg_bit_name.c_str());
+                                       dump_sigspec(f, val_srst);
+                                       f << stringf(";\n");
+                                       f << stringf("%s" "    else ", indent.c_str());
+                               } else {
+                                       if (ff.has_srst) {
+                                               f << stringf("if (%s", ff.pol_srst ? "" : "!");
+                                               dump_sigspec(f, ff.sig_srst);
+                                               f << stringf(") %s <= ", reg_bit_name.c_str());
+                                               dump_sigspec(f, val_srst);
+                                               f << stringf(";\n");
+                                               f << stringf("%s" "  else ", indent.c_str());
+                                       }
+                                       if (ff.has_ce) {
+                                               f << stringf("if (%s", ff.pol_ce ? "" : "!");
+                                               dump_sigspec(f, ff.sig_ce);
+                                               f << stringf(") ");
+                                       }
                                }
-                               os << stringf("%s[", mem_id.c_str());
-                               dump_sigspec(os, sig_wr_addr);
-                               if (width == GetSize(sig_wr_en))
-                                       os << stringf("] <= ");
-                               else
-                                       os << stringf("][%d:%d] <= ", i, start_i);
-                               dump_sigspec(os, sig_wr_data.extract(start_i, width));
-                               os << stringf(";\n");
-                               clk_to_lof_body[clk_domain_str].push_back(os.str());
-                       }
-               }
-               // Output Verilog that looks something like this:
-               // reg [..] _3_;
-               // always @(posedge CLK2) begin
-               //   _3_ <= memory[D1ADDR];
-               //   if (A1EN)
-               //     memory[A1ADDR] <= A1DATA;
-               //   if (A2EN)
-               //     memory[A2ADDR] <= A2DATA;
-               //   ...
-               // end
-               // always @(negedge CLK1) begin
-               //   if (C1EN)
-               //     memory[C1ADDR] <= C1DATA;
-               // end
-               // ...
-               // assign D1DATA = _3_;
-               // assign D2DATA <= memory[D2ADDR];
-
-               // the reg ... definitions
-               for(auto &reg : lof_reg_declarations)
-               {
-                       f << stringf("%s" "%s", indent.c_str(), reg.c_str());
-               }
-               // the block of expressions by clock domain
-               for(auto &pair : clk_to_lof_body)
-               {
-                       std::string clk_domain = pair.first;
-                       std::vector<std::string> lof_lines = pair.second;
-                       if( clk_domain != "")
-                       {
-                               f << stringf("%s" "always @(%s) begin\n", indent.c_str(), clk_domain.c_str());
-                               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());
+
+                               f << stringf("%s <= ", reg_bit_name.c_str());
+                               dump_sigspec(f, sig_d);
+                               f << stringf(";\n");
                        }
                        else
                        {
-                               // the non-clocked assignments
-                               for(auto &line : lof_lines)
-                                       f << stringf("%s" "%s", indent.c_str(), line.c_str());
+                               // Latches.
+                               f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*");
+
+                               f << stringf("%s" "  ", indent.c_str());
+                               if (ff.has_sr) {
+                                       f << stringf("if (%s", ff.pol_clr ? "" : "!");
+                                       dump_sigspec(f, ff.sig_clr[i]);
+                                       f << stringf(") %s = 1'b0;\n", reg_bit_name.c_str());
+                                       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_aload)
+                                               f << stringf("%s" "  else ", indent.c_str());
+                               } else if (ff.has_arst) {
+                                       f << stringf("if (%s", ff.pol_arst ? "" : "!");
+                                       dump_sigspec(f, ff.sig_arst);
+                                       f << stringf(") %s = ", reg_bit_name.c_str());
+                                       dump_sigspec(f, val_arst);
+                                       f << stringf(";\n");
+                                       if (ff.has_aload)
+                                               f << stringf("%s" "  else ", indent.c_str());
+                               }
+                               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_ad);
+                                       f << stringf(";\n");
+                               }
                        }
                }
 
+               if (!out_is_reg_wire) {
+                       f << stringf("%s" "assign ", indent.c_str());
+                       dump_sigspec(f, ff.sig_q);
+                       f << stringf(" = %s;\n", reg_name.c_str());
+               }
+
                return true;
        }
 
-       if (cell->type.in("$assert", "$assume", "$cover"))
+       if (cell->type.in(ID($assert), ID($assume), ID($cover)))
        {
-               f << stringf("%s" "always @* if (", indent.c_str());
-               dump_sigspec(f, cell->getPort("\\EN"));
+               f << stringf("%s" "always%s if (", indent.c_str(), systemverilog ? "_comb" : " @*");
+               dump_sigspec(f, cell->getPort(ID::EN));
                f << stringf(") %s(", cell->type.c_str()+1);
-               dump_sigspec(f, cell->getPort("\\A"));
+               dump_sigspec(f, cell->getPort(ID::A));
                f << stringf(");\n");
                return true;
        }
 
-       if (cell->type.in("$specify2", "$specify3"))
+       if (cell->type.in(ID($specify2), ID($specify3)))
        {
                f << stringf("%s" "specify\n%s  ", indent.c_str(), indent.c_str());
 
-               SigSpec en = cell->getPort("\\EN");
+               SigSpec en = cell->getPort(ID::EN);
                if (en != State::S1) {
                        f << stringf("if (");
-                       dump_sigspec(f, cell->getPort("\\EN"));
+                       dump_sigspec(f, cell->getPort(ID::EN));
                        f << stringf(") ");
                }
 
                f << "(";
-               if (cell->type == "$specify3" && cell->getParam("\\EDGE_EN").as_bool())
-                       f << (cell->getParam("\\EDGE_POL").as_bool() ? "posedge ": "negedge ");
+               if (cell->type == ID($specify3) && cell->getParam(ID::EDGE_EN).as_bool())
+                       f << (cell->getParam(ID::EDGE_POL).as_bool() ? "posedge ": "negedge ");
 
-               dump_sigspec(f, cell->getPort("\\SRC"));
+               dump_sigspec(f, cell->getPort(ID::SRC));
 
                f << " ";
-               if (cell->getParam("\\SRC_DST_PEN").as_bool())
-                       f << (cell->getParam("\\SRC_DST_POL").as_bool() ? "+": "-");
-               f << (cell->getParam("\\FULL").as_bool() ? "*> ": "=> ");
+               if (cell->getParam(ID::SRC_DST_PEN).as_bool())
+                       f << (cell->getParam(ID::SRC_DST_POL).as_bool() ? "+": "-");
+               f << (cell->getParam(ID::FULL).as_bool() ? "*> ": "=> ");
 
-               if (cell->type == "$specify3") {
+               if (cell->type == ID($specify3)) {
                        f << "(";
-                       dump_sigspec(f, cell->getPort("\\DST"));
+                       dump_sigspec(f, cell->getPort(ID::DST));
                        f << " ";
-                       if (cell->getParam("\\DAT_DST_PEN").as_bool())
-                               f << (cell->getParam("\\DAT_DST_POL").as_bool() ? "+": "-");
+                       if (cell->getParam(ID::DAT_DST_PEN).as_bool())
+                               f << (cell->getParam(ID::DAT_DST_POL).as_bool() ? "+": "-");
                        f << ": ";
-                       dump_sigspec(f, cell->getPort("\\DAT"));
+                       dump_sigspec(f, cell->getPort(ID::DAT));
                        f << ")";
                } else {
-                       dump_sigspec(f, cell->getPort("\\DST"));
+                       dump_sigspec(f, cell->getPort(ID::DST));
                }
 
                bool bak_decimal = decimal;
                decimal = 1;
 
                f << ") = (";
-               dump_const(f, cell->getParam("\\T_RISE_MIN"));
+               dump_const(f, cell->getParam(ID::T_RISE_MIN));
                f << ":";
-               dump_const(f, cell->getParam("\\T_RISE_TYP"));
+               dump_const(f, cell->getParam(ID::T_RISE_TYP));
                f << ":";
-               dump_const(f, cell->getParam("\\T_RISE_MAX"));
+               dump_const(f, cell->getParam(ID::T_RISE_MAX));
                f << ", ";
-               dump_const(f, cell->getParam("\\T_FALL_MIN"));
+               dump_const(f, cell->getParam(ID::T_FALL_MIN));
                f << ":";
-               dump_const(f, cell->getParam("\\T_FALL_TYP"));
+               dump_const(f, cell->getParam(ID::T_FALL_TYP));
                f << ":";
-               dump_const(f, cell->getParam("\\T_FALL_MAX"));
+               dump_const(f, cell->getParam(ID::T_FALL_MAX));
                f << ");\n";
 
                decimal = bak_decimal;
@@ -1335,41 +1687,49 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                return true;
        }
 
-       if (cell->type == "$specrule")
+       if (cell->type == ID($specrule))
        {
                f << stringf("%s" "specify\n%s  ", indent.c_str(), indent.c_str());
 
-               string spec_type = cell->getParam("\\TYPE").decode_string();
+               IdString spec_type = cell->getParam(ID::TYPE).decode_string();
                f << stringf("%s(", spec_type.c_str());
 
-               if (cell->getParam("\\SRC_PEN").as_bool())
-                       f << (cell->getParam("\\SRC_POL").as_bool() ? "posedge ": "negedge ");
-               dump_sigspec(f, cell->getPort("\\SRC"));
+               if (cell->getParam(ID::SRC_PEN).as_bool())
+                       f << (cell->getParam(ID::SRC_POL).as_bool() ? "posedge ": "negedge ");
+               dump_sigspec(f, cell->getPort(ID::SRC));
 
-               if (cell->getPort("\\SRC_EN") != State::S1) {
+               if (cell->getPort(ID::SRC_EN) != State::S1) {
                        f << " &&& ";
-                       dump_sigspec(f, cell->getPort("\\SRC_EN"));
+                       dump_sigspec(f, cell->getPort(ID::SRC_EN));
                }
 
                f << ", ";
-               if (cell->getParam("\\DST_PEN").as_bool())
-                       f << (cell->getParam("\\DST_POL").as_bool() ? "posedge ": "negedge ");
-               dump_sigspec(f, cell->getPort("\\DST"));
+               if (cell->getParam(ID::DST_PEN).as_bool())
+                       f << (cell->getParam(ID::DST_POL).as_bool() ? "posedge ": "negedge ");
+               dump_sigspec(f, cell->getPort(ID::DST));
 
-               if (cell->getPort("\\DST_EN") != State::S1) {
+               if (cell->getPort(ID::DST_EN) != State::S1) {
                        f << " &&& ";
-                       dump_sigspec(f, cell->getPort("\\DST_EN"));
+                       dump_sigspec(f, cell->getPort(ID::DST_EN));
                }
 
                bool bak_decimal = decimal;
                decimal = 1;
 
                f << ", ";
-               dump_const(f, cell->getParam("\\T_LIMIT"));
+               dump_const(f, cell->getParam(ID::T_LIMIT_MIN));
+               f << ": ";
+               dump_const(f, cell->getParam(ID::T_LIMIT_TYP));
+               f << ": ";
+               dump_const(f, cell->getParam(ID::T_LIMIT_MAX));
 
-               if (spec_type == "$setuphold" || spec_type == "$recrem" || spec_type == "$fullskew") {
+               if (spec_type.in(ID($setuphold), ID($recrem), ID($fullskew))) {
                        f << ", ";
-                       dump_const(f, cell->getParam("\\T_LIMIT2"));
+                       dump_const(f, cell->getParam(ID::T_LIMIT2_MIN));
+                       f << ": ";
+                       dump_const(f, cell->getParam(ID::T_LIMIT2_TYP));
+                       f << ": ";
+                       dump_const(f, cell->getParam(ID::T_LIMIT2_MAX));
                }
 
                f << ");\n";
@@ -1379,14 +1739,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                return true;
        }
 
-       // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
-       // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
+       // FIXME: $fsm
 
        return false;
 }
 
 void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 {
+       // Handled by dump_memory
+       if (cell->is_mem_cell())
+               return;
+
        if (cell->type[0] == '$' && !noexpr) {
                if (dump_cell_expr(f, indent, cell))
                        return;
@@ -1453,9 +1816,9 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
                }
        }
 
-       if (siminit && reg_ct.count(cell->type) && cell->hasPort("\\Q")) {
+       if (siminit && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) {
                std::stringstream ss;
-               dump_reg_init(ss, cell->getPort("\\Q"));
+               dump_reg_init(ss, cell->getPort(ID::Q));
                if (!ss.str().empty()) {
                        f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
                        f << ss.str();
@@ -1466,11 +1829,23 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 
 void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
 {
-       f << stringf("%s" "assign ", indent.c_str());
-       dump_sigspec(f, left);
-       f << stringf(" = ");
-       dump_sigspec(f, right);
-       f << stringf(";\n");
+       if (simple_lhs) {
+               int offset = 0;
+               for (auto &chunk : left.chunks()) {
+                       f << stringf("%s" "assign ", indent.c_str());
+                       dump_sigspec(f, chunk);
+                       f << stringf(" = ");
+                       dump_sigspec(f, right.extract(offset, GetSize(chunk)));
+                       f << stringf(";\n");
+                       offset += GetSize(chunk);
+               }
+       } else {
+               f << stringf("%s" "assign ", indent.c_str());
+               dump_sigspec(f, left);
+               f << stringf(" = ");
+               dump_sigspec(f, right);
+               f << stringf(";\n");
+       }
 }
 
 void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
@@ -1521,7 +1896,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
 
        bool got_default = false;
        for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
-               dump_attributes(f, indent + "  ", (*it)->attributes, '\n', /*modattr=*/false, /*as_comment=*/true);
+               dump_attributes(f, indent + "  ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true);
                if ((*it)->compare.size() == 0) {
                        if (got_default)
                                continue;
@@ -1568,7 +1943,9 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
                return;
        }
 
-       f << stringf("%s" "always @* begin\n", indent.c_str());
+       f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
+       if (!systemverilog)
+               f << indent + "  " << "if (" << id(initial_id) << ") begin end\n";
        dump_case_body(f, indent, &proc->root_case, true);
 
        std::string backup_indent = indent;
@@ -1579,11 +1956,11 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
                indent = backup_indent;
 
                if (sync->type == RTLIL::STa) {
-                       f << stringf("%s" "always @* begin\n", indent.c_str());
+                       f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
                } else if (sync->type == RTLIL::STi) {
                        f << stringf("%s" "initial begin\n", indent.c_str());
                } else {
-                       f << stringf("%s" "always @(", indent.c_str());
+                       f << stringf("%s" "always%s @(", indent.c_str(), systemverilog ? "_ff" : "");
                        if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
                                f << stringf("posedge ");
                        if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
@@ -1638,9 +2015,9 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
        active_initdata.clear();
 
        for (auto wire : module->wires())
-               if (wire->attributes.count("\\init")) {
+               if (wire->attributes.count(ID::init)) {
                        SigSpec sig = active_sigmap(wire);
-                       Const val = wire->attributes.at("\\init");
+                       Const val = wire->attributes.at(ID::init);
                        for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++)
                                if (val[i] == State::S0 || val[i] == State::S1)
                                        active_initdata[sig[i]] = val[i];
@@ -1659,13 +2036,12 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
        if (!noexpr)
        {
                std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
-               for (auto &it : module->cells_)
+               for (auto cell : module->cells())
                {
-                       RTLIL::Cell *cell = it.second;
-                       if (!reg_ct.count(cell->type) || !cell->hasPort("\\Q"))
+                       if (!RTLIL::builtin_ff_cell_types().count(cell->type) || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_)))
                                continue;
 
-                       RTLIL::SigSpec sig = cell->getPort("\\Q");
+                       RTLIL::SigSpec sig = cell->getPort(ID::Q);
 
                        if (sig.is_chunk()) {
                                RTLIL::SigChunk chunk = sig.as_chunk();
@@ -1674,9 +2050,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
                                                reg_bits.insert(std::pair<RTLIL::Wire*,int>(chunk.wire, chunk.offset+i));
                        }
                }
-               for (auto &it : module->wires_)
+               for (auto wire : module->wires())
                {
-                       RTLIL::Wire *wire = it.second;
                        for (int i = 0; i < wire->width; i++)
                                if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
                                        goto this_wire_aint_reg;
@@ -1686,32 +2061,37 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
                }
        }
 
-       dump_attributes(f, indent, module->attributes, '\n', /*attr2comment=*/true);
+       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 it = module->wires_.begin(); it != module->wires_.end(); ++it) {
-                       RTLIL::Wire *wire = it->second;
+               for (auto wire : module->wires()) {
                        if (wire->port_id == port_id) {
                                if (port_id != 1)
                                        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()) {
+               initial_id = NEW_ID;
+               f << indent + "  " << "reg " << id(initial_id) << " = 0;\n";
+       }
 
-       for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
-               dump_wire(f, indent + "  ", it->second);
+       for (auto w : module->wires())
+               dump_wire(f, indent + "  ", w);
 
-       for (auto it = module->memories.begin(); it != module->memories.end(); ++it)
-               dump_memory(f, indent + "  ", it->second);
+       for (auto &mem : Mem::get_all_memories(module))
+               dump_memory(f, indent + "  ", mem);
 
-       for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it)
-               dump_cell(f, indent + "  ", it->second);
+       for (auto cell : module->cells())
+               dump_cell(f, indent + "  ", cell);
 
        for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
                dump_process(f, indent + "  ", it->second);
@@ -1727,7 +2107,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
 
 struct VerilogBackend : public Backend {
        VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
-       void help() YS_OVERRIDE
+       void help() override
        {
                //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
                log("\n");
@@ -1735,6 +2115,9 @@ struct VerilogBackend : public Backend {
                log("\n");
                log("Write the current design to a Verilog file.\n");
                log("\n");
+               log("    -sv\n");
+               log("        with this option, SystemVerilog constructs like always_comb are used\n");
+               log("\n");
                log("    -norename\n");
                log("        without this option all internal object names (the ones with a dollar\n");
                log("        instead of a backslash prefix) are changed to short names in the\n");
@@ -1776,8 +2159,19 @@ struct VerilogBackend : public Backend {
                log("        deactivates this feature and instead will write string constants\n");
                log("        as binary numbers.\n");
                log("\n");
+               log("    -simple-lhs\n");
+               log("        Connection assignments with simple left hand side without concatenations.\n");
+               log("\n");
+               log("    -extmem\n");
+               log("        instead of initializing memories using assignments to individual\n");
+               log("        elements, use the '$readmemh' function to read initialization data\n");
+               log("        from a file. This data is written to a file named by appending\n");
+               log("        a sequential index to the Verilog filename and replacing the extension\n");
+               log("        with '.mem', e.g. 'write_verilog -extmem foo.v' writes 'foo-1.mem',\n");
+               log("        'foo-2.mem' and so on.\n");
+               log("\n");
                log("    -defparam\n");
-               log("        Use 'defparam' statements instead of the Verilog-2001 syntax for\n");
+               log("        use 'defparam' statements instead of the Verilog-2001 syntax for\n");
                log("        cell parameters.\n");
                log("\n");
                log("    -blackboxes\n");
@@ -1799,7 +2193,7 @@ struct VerilogBackend : public Backend {
                log("this command is called on a design with RTLIL processes.\n");
                log("\n");
        }
-       void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+       void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
        {
                log_header(design, "Executing Verilog backend.\n");
 
@@ -1811,9 +2205,11 @@ struct VerilogBackend : public Backend {
                nodec = false;
                nohex = false;
                nostr = false;
+               extmem = false;
                defparam = false;
                decimal = false;
                siminit = false;
+               simple_lhs = false;
                auto_prefix = "";
 
                bool blackboxes = false;
@@ -1821,37 +2217,14 @@ struct VerilogBackend : public Backend {
 
                auto_name_map.clear();
                reg_wires.clear();
-               reg_ct.clear();
-
-               reg_ct.insert("$dff");
-               reg_ct.insert("$adff");
-               reg_ct.insert("$dffe");
-               reg_ct.insert("$dlatch");
-
-               reg_ct.insert("$_DFF_N_");
-               reg_ct.insert("$_DFF_P_");
-
-               reg_ct.insert("$_DFF_NN0_");
-               reg_ct.insert("$_DFF_NN1_");
-               reg_ct.insert("$_DFF_NP0_");
-               reg_ct.insert("$_DFF_NP1_");
-               reg_ct.insert("$_DFF_PN0_");
-               reg_ct.insert("$_DFF_PN1_");
-               reg_ct.insert("$_DFF_PP0_");
-               reg_ct.insert("$_DFF_PP1_");
-
-               reg_ct.insert("$_DFFSR_NNN_");
-               reg_ct.insert("$_DFFSR_NNP_");
-               reg_ct.insert("$_DFFSR_NPN_");
-               reg_ct.insert("$_DFFSR_NPP_");
-               reg_ct.insert("$_DFFSR_PNN_");
-               reg_ct.insert("$_DFFSR_PNP_");
-               reg_ct.insert("$_DFFSR_PPN_");
-               reg_ct.insert("$_DFFSR_PPP_");
 
                size_t argidx;
                for (argidx = 1; argidx < args.size(); argidx++) {
                        std::string arg = args[argidx];
+                       if (arg == "-sv") {
+                               systemverilog = true;
+                               continue;
+                       }
                        if (arg == "-norename") {
                                norename = true;
                                continue;
@@ -1884,6 +2257,11 @@ struct VerilogBackend : public Backend {
                                nostr = true;
                                continue;
                        }
+                       if (arg == "-extmem") {
+                               extmem = true;
+                               extmem_counter = 1;
+                               continue;
+                       }
                        if (arg == "-defparam") {
                                defparam = true;
                                continue;
@@ -1904,6 +2282,10 @@ struct VerilogBackend : public Backend {
                                selected = true;
                                continue;
                        }
+                       if (arg == "-simple-lhs") {
+                               simple_lhs = true;
+                               continue;
+                       }
                        if (arg == "-v") {
                                verbose = true;
                                continue;
@@ -1911,25 +2293,36 @@ struct VerilogBackend : public Backend {
                        break;
                }
                extra_args(f, filename, args, argidx);
+               if (extmem)
+               {
+                       if (filename == "<stdout>")
+                               log_cmd_error("Option -extmem must be used with a filename.\n");
+                       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);
-               for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
-                       if (it->second->get_blackbox_attribute() != blackboxes)
+               for (auto module : design->modules()) {
+                       if (module->get_blackbox_attribute() != blackboxes)
                                continue;
-                       if (selected && !design->selected_whole_module(it->first)) {
-                               if (design->selected_module(it->first))
-                                       log_cmd_error("Can't handle partially selected module %s!\n", RTLIL::id2cstr(it->first));
+                       if (selected && !design->selected_whole_module(module->name)) {
+                               if (design->selected_module(module->name))
+                                       log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name));
                                continue;
                        }
-                       log("Dumping module `%s'.\n", it->first.c_str());
-                       dump_module(*f, "", it->second);
+                       log("Dumping module `%s'.\n", module->name.c_str());
+                       dump_module(*f, "", module);
                }
 
                auto_name_map.clear();
                reg_wires.clear();
-               reg_ct.clear();
        }
 } VerilogBackend;