From e8f73b6328d881a5b9d6f34dfb01ea93be12e5c9 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 21 May 2020 16:11:59 -0700 Subject: [PATCH 01/90] Copy the verilog backend as vhdl_backend.cc Now need to start the actual conversion of syntax --- src/vhdl_backend.cc | 2013 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2013 insertions(+) create mode 100644 src/vhdl_backend.cc diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc new file mode 100644 index 00000000..b1326c36 --- /dev/null +++ b/src/vhdl_backend.cc @@ -0,0 +1,2013 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * A VHDL backend based on the Verilog backend. + * + */ + +#include "kernel/register.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include "kernel/sigtools.h" +#include +#include +#include +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit; +int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; +std::map auto_name_map; +std::set reg_wires, reg_ct; +std::string auto_prefix, extmem_prefix; + +RTLIL::Module *active_module; +dict active_initdata; +SigMap active_sigmap; + +void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) +{ + const char *str = id.c_str(); + + if (*str == '$' && may_rename && !norename) + auto_name_map[id] = auto_name_counter++; + + if (str[0] != '\\' || str[1] != '_' || str[2] == 0) + return; + + for (int i = 2; str[i] != 0; i++) { + if (str[i] == '_' && str[i+1] == 0) + continue; + if (str[i] < '0' || str[i] > '9') + return; + } + + int num = atoi(str+2); + if (num >= auto_name_offset) + auto_name_offset = num + 1; +} + +void reset_auto_counter(RTLIL::Module *module) +{ + auto_name_map.clear(); + auto_name_counter = 0; + auto_name_offset = 0; + + reset_auto_counter_id(module->name, false); + + for (auto w : module->wires()) + reset_auto_counter_id(w->name, true); + + 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) + reset_auto_counter_id(it->second->name, false); + + auto_name_digits = 1; + for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10) + auto_name_digits++; + + if (verbose) + for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it) + log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second); +} + +std::string next_auto_id() +{ + return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++); +} + +std::string id(RTLIL::IdString internal_id, bool may_rename = true) +{ + const char *str = internal_id.c_str(); + bool do_escape = false; + + if (may_rename && auto_name_map.count(internal_id) != 0) + return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]); + + if (*str == '\\') + str++; + + if ('0' <= *str && *str <= '9') + do_escape = true; + + for (int i = 0; str[i]; i++) + { + if ('0' <= str[i] && str[i] <= '9') + continue; + if ('a' <= str[i] && str[i] <= 'z') + continue; + if ('A' <= str[i] && str[i] <= 'Z') + continue; + if (str[i] == '_') + continue; + do_escape = true; + break; + } + + const pool keywords = { + // IEEE 1800-2017 Annex B + "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", + "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", + "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", + "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", + "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", + "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", + "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", + "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", + "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", + "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", + "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", + "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", + "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", + "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", + "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", + "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", + "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", + "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", + "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", + "wildcard", "wire", "with", "within", "wor", "xnor", "xor", + }; + if (keywords.count(str)) + do_escape = true; + + if (do_escape) + return "\\" + std::string(str) + " "; + return std::string(str); +} + +bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) +{ + if (!sig.is_chunk() || sig.as_chunk().wire == NULL) + return false; + + RTLIL::SigChunk chunk = sig.as_chunk(); + + if (reg_wires.count(chunk.wire->name) == 0) + return false; + + reg_name = id(chunk.wire->name); + if (sig.size() != chunk.wire->width) { + if (sig.size() == 1) + reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset); + else if (chunk.wire->upto) + reg_name += stringf("[%d:%d]", (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, + (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + else + reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1, + chunk.wire->start_offset + chunk.offset); + } + + return true; +} + +void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false) +{ + bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; + if (width < 0) + width = data.bits.size() - offset; + if (width == 0) { + // See IEEE 1364-2005 Clause 5.1.14. + f << "{0{1'b0}}"; + return; + } + if (nostr) + goto dump_hex; + if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) { + if (width == 32 && !no_decimal && !nodec) { + int32_t val = 0; + for (int i = offset+width-1; i >= offset; i--) { + log_assert(i < (int)data.bits.size()); + if (data.bits[i] != State::S0 && data.bits[i] != State::S1) + goto dump_hex; + if (data.bits[i] == State::S1) + val |= 1 << (i - offset); + } + if (decimal) + f << stringf("%d", val); + else if (set_signed && val < 0) + f << stringf("-32'sd%u", -val); + else + f << stringf("32'%sd%u", set_signed ? "s" : "", val); + } else { + dump_hex: + if (nohex) + goto dump_bin; + vector bin_digits, hex_digits; + for (int i = offset; i < offset+width; i++) { + log_assert(i < (int)data.bits.size()); + switch (data.bits[i]) { + 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; + case RTLIL::Sm: log_error("Found marker state in final netlist."); + } + } + if (GetSize(bin_digits) == 0) + goto dump_bin; + while (GetSize(bin_digits) % 4 != 0) + if (bin_digits.back() == '1') + bin_digits.push_back('0'); + else + bin_digits.push_back(bin_digits.back()); + for (int i = 0; i < GetSize(bin_digits); i += 4) + { + char bit_3 = bin_digits[i+3]; + char bit_2 = bin_digits[i+2]; + char bit_1 = bin_digits[i+1]; + char bit_0 = bin_digits[i+0]; + if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') { + if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x') + goto dump_bin; + hex_digits.push_back('x'); + continue; + } + if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') { + if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z') + goto dump_bin; + hex_digits.push_back('z'); + continue; + } + if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') { + if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?') + goto dump_bin; + hex_digits.push_back('?'); + continue; + } + int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); + hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); + } + f << stringf("%d'%sh", width, set_signed ? "s" : ""); + for (int i = GetSize(hex_digits)-1; i >= 0; i--) + f << hex_digits[i]; + } + if (0) { + dump_bin: + f << stringf("%d'%sb", width, set_signed ? "s" : ""); + if (width == 0) + f << stringf("0"); + for (int i = offset+width-1; i >= offset; i--) { + log_assert(i < (int)data.bits.size()); + switch (data.bits[i]) { + case State::S0: f << stringf("0"); break; + case State::S1: f << stringf("1"); break; + case RTLIL::Sx: f << stringf("x"); break; + case RTLIL::Sz: f << stringf("z"); break; + case RTLIL::Sa: f << stringf("?"); break; + case RTLIL::Sm: log_error("Found marker state in final netlist."); + } + } + } + } else { + if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) + f << stringf("\""); + std::string str = data.decode_string(); + for (size_t i = 0; i < str.size(); i++) { + if (str[i] == '\n') + f << stringf("\\n"); + else if (str[i] == '\t') + f << stringf("\\t"); + else if (str[i] < 32) + f << stringf("\\%03o", str[i]); + else if (str[i] == '"') + f << stringf("\\\""); + else if (str[i] == '\\') + f << stringf("\\\\"); + else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*') + f << stringf("\\/"); + else + f << str[i]; + } + if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) + f << stringf("\""); + } +} + +void dump_reg_init(std::ostream &f, SigSpec sig) +{ + Const initval; + bool gotinit = false; + + for (auto bit : active_sigmap(sig)) { + if (active_initdata.count(bit)) { + initval.bits.push_back(active_initdata.at(bit)); + gotinit = true; + } else { + initval.bits.push_back(State::Sx); + } + } + + if (gotinit) { + f << " = "; + dump_const(f, initval); + } +} + +void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false) +{ + if (chunk.wire == NULL) { + dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); + } else { + if (chunk.width == chunk.wire->width && chunk.offset == 0) { + f << stringf("%s", id(chunk.wire->name).c_str()); + } else if (chunk.width == 1) { + if (chunk.wire->upto) + f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + else + f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); + } else { + if (chunk.wire->upto) + f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), + (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, + (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + else + f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), + (chunk.offset + chunk.width - 1) + chunk.wire->start_offset, + chunk.offset + chunk.wire->start_offset); + } + } +} + +void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) +{ + if (GetSize(sig) == 0) { + f << "\"\""; + return; + } + if (sig.is_chunk()) { + dump_sigchunk(f, sig.as_chunk()); + } else { + f << stringf("{ "); + for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { + if (it != sig.chunks().rbegin()) + f << stringf(", "); + dump_sigchunk(f, *it, true); + } + f << stringf(" }"); + } +} + +void dump_attributes(std::ostream &f, std::string indent, dict &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 == State::S0 || it->second == Const(0))) + f << stringf(" 0 "); + 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); + f << stringf(" %s%c", as_comment ? "*/" : "*)", term); + } +} + +void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) +{ + 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 " : ""); + else if (!wire->port_input && wire->port_output) + f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + else if (wire->port_input && wire->port_output) + f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + else + f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire"); + if (wire->width != 1) + f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset); + f << stringf("%s;\n", id(wire->name).c_str()); +#else + // do not use Verilog-2k "output reg" syntax in Verilog export + std::string range = ""; + if (wire->width != 1) { + if (wire->upto) + range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset); + else + range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset); + } + if (wire->port_input && !wire->port_output) + f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + if (!wire->port_input && wire->port_output) + f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + if (wire->port_input && wire->port_output) + 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(ID::init)) { + f << stringf(" = "); + dump_const(f, wire->attributes.at(ID::init)); + } + f << stringf(";\n"); + } else if (!wire->port_input && !wire->port_output) + 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) +{ + 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); +} + +void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) +{ + if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { + f << stringf("$signed("); + dump_sigspec(f, cell->getPort("\\" + port)); + f << stringf(")"); + } else + dump_sigspec(f, cell->getPort("\\" + port)); +} + +std::string cellname(RTLIL::Cell *cell) +{ + if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort(ID::Q)) + { + RTLIL::SigSpec sig = cell->getPort(ID::Q); + if (GetSize(sig) != 1 || sig.is_fully_const()) + goto no_special_reg_name; + + RTLIL::Wire *wire = sig[0].wire; + + if (wire->name[0] != '\\') + goto no_special_reg_name; + + std::string cell_name = wire->name.str(); + + size_t pos = cell_name.find('['); + if (pos != std::string::npos) + cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos); + else + cell_name = cell_name + "_reg"; + + if (wire->width != 1) + cell_name += stringf("[%d]", wire->start_offset + sig[0].offset); + + if (active_module && active_module->count_id(cell_name) > 0) + goto no_special_reg_name; + + return id(cell_name); + } + else + { +no_special_reg_name: + return id(cell->name).c_str(); + } +} + +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(ID::Y)); + f << stringf(" = %s ", op.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", true); + f << stringf(";\n"); +} + +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(ID::Y)); + f << stringf(" = "); + dump_cell_expr_port(f, cell, "A", true); + f << stringf(" %s ", op.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", true); + f << stringf(";\n"); +} + +bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) +{ + if (cell->type == ID($_NOT_)) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + f << stringf("~"); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(";\n"); + return true; + } + + 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(ID::Y)); + f << stringf(" = "); + 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(ID($_AND_), ID($_NAND_), ID($_ANDNOT_))) + f << stringf("&"); + if (cell->type.in(ID($_OR_), ID($_NOR_), ID($_ORNOT_))) + f << stringf("|"); + if (cell->type.in(ID($_XOR_), ID($_XNOR_))) + f << stringf("^"); + dump_attributes(f, "", cell->attributes, ' '); + f << stringf(" "); + if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_))) + f << stringf("~("); + dump_cell_expr_port(f, cell, "B", false); + if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) + f << stringf(")"); + f << stringf(";\n"); + return true; + } + + if (cell->type == ID($_MUX_)) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + dump_cell_expr_port(f, cell, "S", false); + f << stringf(" ? "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", false); + f << stringf(" : "); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(";\n"); + return true; + } + + if (cell->type == ID($_NMUX_)) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = !("); + dump_cell_expr_port(f, cell, "S", false); + f << stringf(" ? "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", false); + f << stringf(" : "); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(");\n"); + return true; + } + + if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = ~(("); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(cell->type == ID($_AOI3_) ? " & " : " | "); + dump_cell_expr_port(f, cell, "B", false); + f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &"); + dump_attributes(f, "", cell->attributes, ' '); + f << stringf(" "); + dump_cell_expr_port(f, cell, "C", false); + f << stringf(");\n"); + return true; + } + + if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = ~(("); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + dump_cell_expr_port(f, cell, "B", false); + 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 == ID($_AOI4_) ? " & " : " | "); + dump_cell_expr_port(f, cell, "D", false); + f << stringf("));\n"); + return true; + } + + if (cell->type.begins_with("$_DFF_")) + { + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::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(ID::Q)); + f << ";\n"; + } + + 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(ID::C)); + if (cell->type[7] != '_') { + f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg"); + dump_sigspec(f, cell->getPort(ID::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(ID::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()); + } + + 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(ID::Q)); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + return true; + } + + if (cell->type.begins_with("$_DFFSR_")) + { + 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(ID::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(ID::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(ID::C)); + f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg"); + dump_sigspec(f, cell->getPort(ID::S)); + f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg"); + dump_sigspec(f, cell->getPort(ID::R)); + f << stringf(")\n"); + + f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!"); + dump_sigspec(f, cell->getPort(ID::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(ID::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"); + + if (!out_is_reg_wire) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Q)); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + 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(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), "||") + +#undef HANDLE_UNIOP +#undef HANDLE_BINOP + + if (cell->type == ID($shift)) + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + if (cell->getParam(ID::B_SIGNED).as_bool()) + { + f << stringf("$signed("); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(")"); + f << stringf(" < 0 ? "); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(" << - "); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(" : "); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(" >> "); + dump_sigspec(f, cell->getPort(ID::B)); + } + else + { + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(" >> "); + dump_sigspec(f, cell->getPort(ID::B)); + } + f << stringf(";\n"); + return true; + } + + 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(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(ID::Y)); + f << stringf(" = %s[", temp_id.c_str()); + if (cell->getParam(ID::B_SIGNED).as_bool()) + f << stringf("$signed("); + dump_sigspec(f, cell->getPort(ID::B)); + if (cell->getParam(ID::B_SIGNED).as_bool()) + f << stringf(")"); + f << stringf(" +: %d", cell->getParam(ID::Y_WIDTH).as_int()); + f << stringf("];\n"); + return true; + } + + if (cell->type == ID($mux)) + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + dump_sigspec(f, cell->getPort(ID::S)); + f << stringf(" ? "); + dump_attributes(f, "", cell->attributes, ' '); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(" : "); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(";\n"); + return true; + } + + if (cell->type == ID($pmux)) + { + 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()); + f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1); + f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1); + f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); + + dump_attributes(f, indent + " ", cell->attributes); + if (!noattr) + f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); + f << stringf("%s" " casez (s)", indent.c_str()); + f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + + for (int i = 0; i < s_width; i++) + { + f << stringf("%s" " %d'b", indent.c_str(), s_width); + + for (int j = s_width-1; j >= 0; j--) + f << stringf("%c", j == i ? '1' : '?'); + + f << stringf(":\n"); + f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); + } + + f << stringf("%s" " default:\n", indent.c_str()); + f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str()); + + f << stringf("%s" " endcase\n", indent.c_str()); + f << stringf("%s" "endfunction\n", indent.c_str()); + + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = %s(", func_name.c_str()); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(", "); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(", "); + dump_sigspec(f, cell->getPort(ID::S)); + f << stringf(");\n"); + return true; + } + + if (cell->type == ID($tribuf)) + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(" ? "); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(" : %d'bz;\n", cell->parameters.at(ID::WIDTH).as_int()); + return true; + } + + if (cell->type == ID($slice)) + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(" >> %d;\n", cell->parameters.at(ID::OFFSET).as_int()); + return true; + } + + if (cell->type == ID($concat)) + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = { "); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(" , "); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(" };\n"); + return true; + } + + if (cell->type == ID($lut)) + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + dump_const(f, cell->parameters.at(ID::LUT)); + f << stringf(" >> "); + dump_attributes(f, "", cell->attributes, ' '); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(";\n"); + return true; + } + + if (cell->type == ID($dffsr)) + { + SigSpec sig_clk = cell->getPort(ID::CLK); + SigSpec sig_set = cell->getPort(ID::SET); + SigSpec sig_clr = cell->getPort(ID::CLR); + SigSpec sig_d = cell->getPort(ID::D); + SigSpec sig_q = cell->getPort(ID::Q); + + int width = cell->parameters[ID::WIDTH].as_int(); + bool pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool(); + bool pol_set = cell->parameters[ID::SET_POLARITY].as_bool(); + bool pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool(); + + 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"); + } + + 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.in(ID($dff), ID($adff), ID($dffe))) + { + RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst; + bool pol_clk, pol_arst = false, pol_en = false; + + sig_clk = cell->getPort(ID::CLK); + pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool(); + + if (cell->type == ID($adff)) { + sig_arst = cell->getPort(ID::ARST); + pol_arst = cell->parameters[ID::ARST_POLARITY].as_bool(); + val_arst = RTLIL::SigSpec(cell->parameters[ID::ARST_VALUE]); + } + + if (cell->type == ID($dffe)) { + sig_en = cell->getPort(ID::EN); + pol_en = cell->parameters[ID::EN_POLARITY].as_bool(); + } + + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); + + if (!out_is_reg_wire) { + f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str()); + dump_reg_init(f, cell->getPort(ID::Q)); + f << ";\n"; + } + + f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); + dump_sigspec(f, sig_clk); + if (cell->type == ID($adff)) { + f << stringf(" or %sedge ", pol_arst ? "pos" : "neg"); + dump_sigspec(f, sig_arst); + } + f << stringf(")\n"); + + if (cell->type == ID($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 == ID($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(ID::Q)); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + return true; + } + + if (cell->type == ID($dlatch)) + { + RTLIL::SigSpec sig_en; + bool pol_en = false; + + sig_en = cell->getPort(ID::EN); + pol_en = cell->parameters[ID::EN_POLARITY].as_bool(); + + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); + + if (!out_is_reg_wire) { + f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str()); + dump_reg_init(f, cell->getPort(ID::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(ID::Q)); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + return true; + } + + if (cell->type == ID($mem)) + { + RTLIL::IdString memid = cell->parameters[ID::MEMID].decode_string(); + std::string mem_id = id(cell->parameters[ID::MEMID].decode_string()); + int abits = cell->parameters[ID::ABITS].as_int(); + int size = cell->parameters[ID::SIZE].as_int(); + int offset = cell->parameters[ID::OFFSET].as_int(); + int width = cell->parameters[ID::WIDTH].as_int(); + bool use_init = !(RTLIL::SigSpec(cell->parameters[ID::INIT]).is_fully_undef()); + + // for memory block make something like: + // reg [7:0] memid [3:0]; + // initial begin + // memid[0] = ... + // end + dump_attributes(f, indent.c_str(), cell->attributes); + 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) + { + 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 + { + for (int i=0; iparameters[ID::INIT].extract(i*width, width); + for (int j=0; j expressions within that clock domain + dict> clk_to_lof_body; + clk_to_lof_body[""] = std::vector(); + std::string clk_domain_str; + // create a list of reg declarations + std::vector lof_reg_declarations; + + int nread_ports = cell->parameters[ID::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(ID::RD_CLK).extract(i); + sig_rd_en = cell->getPort(ID::RD_EN).extract(i); + sig_rd_data = cell->getPort(ID::RD_DATA).extract(i*width, width); + sig_rd_addr = cell->getPort(ID::RD_ADDR).extract(i*abits, abits); + use_rd_clk = cell->parameters[ID::RD_CLK_ENABLE].extract(i).as_bool(); + rd_clk_posedge = cell->parameters[ID::RD_CLK_POLARITY].extract(i).as_bool(); + rd_transparent = cell->parameters[ID::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(); + } + 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()) ); + { + 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()); + } + { + 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); + } + } + 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()) ); + { + 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); + } + { + 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); + } + } + } 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[ID::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(ID::WR_CLK).extract(i); + sig_wr_data = cell->getPort(ID::WR_DATA).extract(i*width, width); + sig_wr_addr = cell->getPort(ID::WR_ADDR).extract(i*abits, abits); + sig_wr_en = cell->getPort(ID::WR_EN).extract(i*width, width); + wr_clk_posedge = cell->parameters[ID::WR_CLK_POLARITY].extract(i).as_bool(); + { + 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(); + } + // 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; + + std::ostringstream os; + if (wen_bit != State::S1) + { + os << stringf("if ("); + dump_sigspec(os, wen_bit); + os << 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 ® : 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 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()); + } + else + { + // the non-clocked assignments + for(auto &line : lof_lines) + f << stringf("%s" "%s", indent.c_str(), line.c_str()); + } + } + + return true; + } + + if (cell->type.in(ID($assert), ID($assume), ID($cover))) + { + f << stringf("%s" "always @* if (", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(") %s(", cell->type.c_str()+1); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(");\n"); + return true; + } + + if (cell->type.in(ID($specify2), ID($specify3))) + { + f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + + SigSpec en = cell->getPort(ID::EN); + if (en != State::S1) { + f << stringf("if ("); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(") "); + } + + f << "("; + 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(ID::SRC)); + + f << " "; + 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 == ID($specify3)) { + f << "("; + dump_sigspec(f, cell->getPort(ID::DST)); + f << " "; + if (cell->getParam(ID::DAT_DST_PEN).as_bool()) + f << (cell->getParam(ID::DAT_DST_POL).as_bool() ? "+": "-"); + f << ": "; + dump_sigspec(f, cell->getPort(ID::DAT)); + f << ")"; + } else { + dump_sigspec(f, cell->getPort(ID::DST)); + } + + bool bak_decimal = decimal; + decimal = 1; + + f << ") = ("; + dump_const(f, cell->getParam(ID::T_RISE_MIN)); + f << ":"; + dump_const(f, cell->getParam(ID::T_RISE_TYP)); + f << ":"; + dump_const(f, cell->getParam(ID::T_RISE_MAX)); + f << ", "; + dump_const(f, cell->getParam(ID::T_FALL_MIN)); + f << ":"; + dump_const(f, cell->getParam(ID::T_FALL_TYP)); + f << ":"; + dump_const(f, cell->getParam(ID::T_FALL_MAX)); + f << ");\n"; + + decimal = bak_decimal; + + f << stringf("%s" "endspecify\n", indent.c_str()); + return true; + } + + if (cell->type == ID($specrule)) + { + f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + + IdString spec_type = cell->getParam(ID::TYPE).decode_string(); + f << stringf("%s(", spec_type.c_str()); + + 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(ID::SRC_EN) != State::S1) { + f << " &&& "; + dump_sigspec(f, cell->getPort(ID::SRC_EN)); + } + + f << ", "; + 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(ID::DST_EN) != State::S1) { + f << " &&& "; + dump_sigspec(f, cell->getPort(ID::DST_EN)); + } + + bool bak_decimal = decimal; + decimal = 1; + + f << ", "; + 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.in(ID($setuphold), ID($recrem), ID($fullskew))) { + f << ", "; + 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"; + decimal = bak_decimal; + + f << stringf("%s" "endspecify\n", indent.c_str()); + return true; + } + + // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_ + // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm + + return false; +} + +void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) +{ + if (cell->type[0] == '$' && !noexpr) { + if (dump_cell_expr(f, indent, cell)) + return; + } + + dump_attributes(f, indent, cell->attributes); + f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str()); + + if (!defparam && cell->parameters.size() > 0) { + f << stringf(" #("); + for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { + if (it != cell->parameters.begin()) + f << stringf(","); + f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); + dump_const(f, it->second); + f << stringf(")"); + } + f << stringf("\n%s" ")", indent.c_str()); + } + + std::string cell_name = cellname(cell); + if (cell_name != id(cell->name)) + f << stringf(" %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str()); + else + f << stringf(" %s (", cell_name.c_str()); + + bool first_arg = true; + std::set numbered_ports; + for (int i = 1; true; i++) { + char str[16]; + snprintf(str, 16, "$%d", i); + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { + if (it->first != str) + continue; + if (!first_arg) + f << stringf(","); + first_arg = false; + f << stringf("\n%s ", indent.c_str()); + dump_sigspec(f, it->second); + numbered_ports.insert(it->first); + goto found_numbered_port; + } + break; + found_numbered_port:; + } + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { + if (numbered_ports.count(it->first)) + continue; + if (!first_arg) + f << stringf(","); + first_arg = false; + f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); + if (it->second.size() > 0) + dump_sigspec(f, it->second); + f << stringf(")"); + } + f << stringf("\n%s" ");\n", indent.c_str()); + + if (defparam && cell->parameters.size() > 0) { + for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { + f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str()); + dump_const(f, it->second); + f << stringf(";\n"); + } + } + + if (siminit && reg_ct.count(cell->type) && cell->hasPort(ID::Q)) { + std::stringstream ss; + 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(); + f << ";\n"; + } + } +} + +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"); +} + +void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw); + +void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) +{ + int number_of_stmts = cs->switches.size() + cs->actions.size(); + + if (!omit_trailing_begin && number_of_stmts >= 2) + f << stringf("%s" "begin\n", indent.c_str()); + + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { + if (it->first.size() == 0) + continue; + f << stringf("%s ", indent.c_str()); + dump_sigspec(f, it->first); + f << stringf(" = "); + dump_sigspec(f, it->second); + f << stringf(";\n"); + } + + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) + dump_proc_switch(f, indent + " ", *it); + + if (!omit_trailing_begin && number_of_stmts == 0) + f << stringf("%s /* empty */;\n", indent.c_str()); + + if (omit_trailing_begin || number_of_stmts >= 2) + f << stringf("%s" "end\n", indent.c_str()); +} + +void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) +{ + if (sw->signal.size() == 0) { + f << stringf("%s" "begin\n", indent.c_str()); + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + if ((*it)->compare.size() == 0) + dump_case_body(f, indent + " ", *it); + } + f << stringf("%s" "end\n", indent.c_str()); + return; + } + + dump_attributes(f, indent, sw->attributes); + f << stringf("%s" "casez (", indent.c_str()); + dump_sigspec(f, sw->signal); + f << stringf(")\n"); + + bool got_default = false; + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true); + if ((*it)->compare.size() == 0) { + if (got_default) + continue; + f << stringf("%s default", indent.c_str()); + got_default = true; + } else { + f << stringf("%s ", indent.c_str()); + for (size_t i = 0; i < (*it)->compare.size(); i++) { + if (i > 0) + f << stringf(", "); + dump_sigspec(f, (*it)->compare[i]); + } + } + f << stringf(":\n"); + dump_case_body(f, indent + " ", *it); + } + + f << stringf("%s" "endcase\n", indent.c_str()); +} + +void case_body_find_regs(RTLIL::CaseRule *cs) +{ + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) + for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) + case_body_find_regs(*it2); + + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { + for (auto &c : it->first.chunks()) + if (c.wire != NULL) + reg_wires.insert(c.wire->name); + } +} + +void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false) +{ + if (find_regs) { + case_body_find_regs(&proc->root_case); + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) + for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) { + for (auto &c : it2->first.chunks()) + if (c.wire != NULL) + reg_wires.insert(c.wire->name); + } + return; + } + + f << stringf("%s" "always @* begin\n", indent.c_str()); + dump_case_body(f, indent, &proc->root_case, true); + + std::string backup_indent = indent; + + for (size_t i = 0; i < proc->syncs.size(); i++) + { + RTLIL::SyncRule *sync = proc->syncs[i]; + indent = backup_indent; + + if (sync->type == RTLIL::STa) { + f << stringf("%s" "always @* begin\n", indent.c_str()); + } else if (sync->type == RTLIL::STi) { + f << stringf("%s" "initial begin\n", indent.c_str()); + } else { + f << stringf("%s" "always @(", indent.c_str()); + if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1) + f << stringf("posedge "); + if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0) + f << stringf("negedge "); + dump_sigspec(f, sync->signal); + f << stringf(") begin\n"); + } + std::string ends = indent + "end\n"; + indent += " "; + + if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) { + f << stringf("%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : ""); + dump_sigspec(f, sync->signal); + f << stringf(") begin\n"); + ends = indent + "end\n" + ends; + indent += " "; + } + + if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) { + for (size_t j = 0; j < proc->syncs.size(); j++) { + RTLIL::SyncRule *sync2 = proc->syncs[j]; + if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) { + f << stringf("%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : ""); + dump_sigspec(f, sync2->signal); + f << stringf(") begin\n"); + ends = indent + "end\n" + ends; + indent += " "; + } + } + } + + for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) { + if (it->first.size() == 0) + continue; + f << stringf("%s ", indent.c_str()); + dump_sigspec(f, it->first); + f << stringf(" <= "); + dump_sigspec(f, it->second); + f << stringf(";\n"); + } + + f << stringf("%s", ends.c_str()); + } +} + +void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) +{ + reg_wires.clear(); + reset_auto_counter(module); + active_module = module; + active_sigmap.set(module); + active_initdata.clear(); + + for (auto wire : module->wires()) + if (wire->attributes.count(ID::init)) { + SigSpec sig = active_sigmap(wire); + 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]; + } + + if (!module->processes.empty()) + log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" + "can't always be mapped directly to Verilog always blocks. Unintended\n" + "changes in simulation behavior are possible! Use \"proc\" to convert\n" + "processes to logic networks and registers.\n", log_id(module)); + + f << stringf("\n"); + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) + dump_process(f, indent + " ", it->second, true); + + if (!noexpr) + { + std::set> reg_bits; + for (auto cell : module->cells()) + { + if (!reg_ct.count(cell->type) || !cell->hasPort(ID::Q)) + continue; + + RTLIL::SigSpec sig = cell->getPort(ID::Q); + + if (sig.is_chunk()) { + RTLIL::SigChunk chunk = sig.as_chunk(); + if (chunk.wire != NULL) + for (int i = 0; i < chunk.width; i++) + reg_bits.insert(std::pair(chunk.wire, chunk.offset+i)); + } + } + for (auto wire : module->wires()) + { + for (int i = 0; i < wire->width; i++) + if (reg_bits.count(std::pair(wire, i)) == 0) + goto this_wire_aint_reg; + if (wire->width) + reg_wires.insert(wire->name); + this_wire_aint_reg:; + } + } + + 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; + for (int port_id = 1; keep_running; port_id++) { + keep_running = false; + 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; + continue; + } + } + } + f << stringf(");\n"); + + 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 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); + + for (auto it = module->connections().begin(); it != module->connections().end(); ++it) + dump_conn(f, indent + " ", it->first, it->second); + + f << stringf("%s" "endmodule\n", indent.c_str()); + active_module = NULL; + active_sigmap.clear(); + active_initdata.clear(); +} + +struct VHDLBackend : public Backend { + VHDLBackend() : Backend("vhdl", "write design to VHDL file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_vhdl [options] [filename]\n"); + log("\n"); + log("Write the current design to a VHDL file (wip).\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"); + log(" format '__'.\n"); + log("\n"); + log(" -renameprefix \n"); + log(" insert this prefix in front of auto-generated instance names\n"); + log("\n"); + log(" -noattr\n"); + log(" with this option no attributes are included in the output\n"); + log("\n"); + log(" -attr2comment\n"); + log(" with this option attributes are included as comments in the output\n"); + log("\n"); + log(" -noexpr\n"); + log(" without this option all internal cells are converted to Verilog\n"); + log(" expressions.\n"); + log("\n"); + log(" -siminit\n"); + log(" add initial statements with hierarchical refs to initialize FFs when\n"); + log(" in -noexpr mode.\n"); + log("\n"); + log(" -nodec\n"); + log(" 32-bit constant values are by default dumped as decimal numbers,\n"); + log(" not bit pattern. This option deactivates this feature and instead\n"); + log(" will write out all constants in binary.\n"); + log("\n"); + log(" -decimal\n"); + log(" dump 32-bit constants in decimal and without size and radix\n"); + log("\n"); + log(" -nohex\n"); + log(" constant values that are compatible with hex output are usually\n"); + log(" dumped as hex values. This option deactivates this feature and\n"); + log(" instead will write out all constants in binary.\n"); + log("\n"); + log(" -nostr\n"); + log(" Parameters and attributes that are specified as strings in the\n"); + log(" original input will be output as strings by this back-end. This\n"); + log(" deactivates this feature and instead will write string constants\n"); + log(" as binary numbers.\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(" cell parameters.\n"); + log("\n"); + log(" -blackboxes\n"); + log(" usually modules with the 'blackbox' attribute are ignored. with\n"); + log(" this option set only the modules with the 'blackbox' attribute\n"); + log(" are written to the output file.\n"); + log("\n"); + log(" -selected\n"); + log(" only write selected modules. modules must be selected entirely or\n"); + log(" not at all.\n"); + log("\n"); + log(" -v\n"); + log(" verbose output (print new names of all renamed wires and cells)\n"); + log("\n"); + log("Note that RTLIL processes can't always be mapped directly to Verilog\n"); + log("always blocks. This frontend should only be used to export an RTLIL\n"); + log("netlist, i.e. after the \"proc\" pass has been used to convert all\n"); + log("processes to logic networks and registers. A warning is generated when\n"); + log("this command is called on a design with RTLIL processes.\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing VHDL backend.\n"); + + verbose = false; + norename = false; + noattr = false; + attr2comment = false; + noexpr = false; + nodec = false; + nohex = false; + nostr = false; + extmem = false; + defparam = false; + decimal = false; + siminit = false; + auto_prefix = ""; + + bool blackboxes = false; + bool selected = false; + + auto_name_map.clear(); + reg_wires.clear(); + reg_ct.clear(); + + reg_ct.insert(ID($dff)); + reg_ct.insert(ID($adff)); + reg_ct.insert(ID($dffe)); + reg_ct.insert(ID($dlatch)); + + reg_ct.insert(ID($_DFF_N_)); + reg_ct.insert(ID($_DFF_P_)); + + reg_ct.insert(ID($_DFF_NN0_)); + reg_ct.insert(ID($_DFF_NN1_)); + reg_ct.insert(ID($_DFF_NP0_)); + reg_ct.insert(ID($_DFF_NP1_)); + reg_ct.insert(ID($_DFF_PN0_)); + reg_ct.insert(ID($_DFF_PN1_)); + reg_ct.insert(ID($_DFF_PP0_)); + reg_ct.insert(ID($_DFF_PP1_)); + + reg_ct.insert(ID($_DFFSR_NNN_)); + reg_ct.insert(ID($_DFFSR_NNP_)); + reg_ct.insert(ID($_DFFSR_NPN_)); + reg_ct.insert(ID($_DFFSR_NPP_)); + reg_ct.insert(ID($_DFFSR_PNN_)); + reg_ct.insert(ID($_DFFSR_PNP_)); + reg_ct.insert(ID($_DFFSR_PPN_)); + reg_ct.insert(ID($_DFFSR_PPP_)); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-norename") { + norename = true; + continue; + } + if (arg == "-renameprefix" && argidx+1 < args.size()) { + auto_prefix = args[++argidx]; + continue; + } + if (arg == "-noattr") { + noattr = true; + continue; + } + if (arg == "-attr2comment") { + attr2comment = true; + continue; + } + if (arg == "-noexpr") { + noexpr = true; + continue; + } + if (arg == "-nodec") { + nodec = true; + continue; + } + if (arg == "-nohex") { + nohex = true; + continue; + } + if (arg == "-nostr") { + nostr = true; + continue; + } + if (arg == "-extmem") { + extmem = true; + extmem_counter = 1; + continue; + } + if (arg == "-defparam") { + defparam = true; + continue; + } + if (arg == "-decimal") { + decimal = true; + continue; + } + if (arg == "-siminit") { + siminit = true; + continue; + } + if (arg == "-blackboxes") { + blackboxes = true; + continue; + } + if (arg == "-selected") { + selected = true; + continue; + } + if (arg == "-v") { + verbose = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + if (extmem) + { + if (filename == "") + log_cmd_error("Option -extmem must be used with a filename.\n"); + extmem_prefix = filename.substr(0, filename.rfind('.')); + } + + design->sort(); + + *f << stringf("/* Generated by %s */\n", yosys_version_str); + for (auto module : design->modules()) { + if (module->get_blackbox_attribute() != blackboxes) + continue; + 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", module->name.c_str()); + dump_module(*f, "", module); + } + + auto_name_map.clear(); + reg_wires.clear(); + reg_ct.clear(); + } +} VerilogBackend; + +PRIVATE_NAMESPACE_END From 0bca471bb132a2ba13e1a47bcb101459fbd5fd21 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 21 May 2020 16:34:23 -0700 Subject: [PATCH 02/90] Replace the copyright header with one styled after the ghdl.cc header --- src/vhdl_backend.cc | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b1326c36..21a93e3d 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1,24 +1,23 @@ /* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * --- - * - * A VHDL backend based on the Verilog backend. - * + Copyright (C) 2020 Ryan Lee + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +/* + * A VHDL backend based on the Verilog backend. */ #include "kernel/register.h" From 6bedabebf332ba5b9dc37c43c5ede42e2eb424b7 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 21 May 2020 17:37:49 -0700 Subject: [PATCH 03/90] Initial labelling of functions as needing porting or not, subject to change --- src/vhdl_backend.cc | 53 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 21a93e3d..b1bba751 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -43,7 +43,7 @@ dict active_initdata; SigMap active_sigmap; void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) -{ +{ // NO PORTING REQUIRED const char *str = id.c_str(); if (*str == '$' && may_rename && !norename) @@ -65,7 +65,7 @@ void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) } void reset_auto_counter(RTLIL::Module *module) -{ +{ // NO PORTING REQUIRED auto_name_map.clear(); auto_name_counter = 0; auto_name_offset = 0; @@ -93,12 +93,12 @@ void reset_auto_counter(RTLIL::Module *module) } std::string next_auto_id() -{ +{ // NO PORTING REQUIRED return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++); } std::string id(RTLIL::IdString internal_id, bool may_rename = true) -{ +{ // PORTING REQUIRED const char *str = internal_id.c_str(); bool do_escape = false; @@ -158,7 +158,7 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) } bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) -{ +{ // PORTING REQUIRED if (!sig.is_chunk() || sig.as_chunk().wire == NULL) return false; @@ -183,7 +183,7 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) } void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false) -{ +{ // PORTING REQUIRED bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; if (width < 0) width = data.bits.size() - offset; @@ -307,7 +307,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } void dump_reg_init(std::ostream &f, SigSpec sig) -{ +{ // PORTING REQUIRED Const initval; bool gotinit = false; @@ -327,7 +327,7 @@ void dump_reg_init(std::ostream &f, SigSpec sig) } void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false) -{ +{ // PORTING REQUIRED if (chunk.wire == NULL) { dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); } else { @@ -352,7 +352,7 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima } void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) -{ +{ // PORTING REQUIRED if (GetSize(sig) == 0) { f << "\"\""; return; @@ -371,7 +371,7 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false) -{ +{ // PORTING REQUIRED if (noattr) return; if (attr2comment) @@ -391,7 +391,7 @@ void dump_attributes(std::ostream &f, std::string indent, dictattributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); #if 0 if (wire->port_input && !wire->port_output) @@ -433,13 +433,13 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) } void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) -{ +{ // PORTING REQUIRED 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); } void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) -{ +{ // PORTING REQUIRED if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { f << stringf("$signed("); dump_sigspec(f, cell->getPort("\\" + port)); @@ -449,7 +449,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b } std::string cellname(RTLIL::Cell *cell) -{ +{ // PORTING REQUIRED if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort(ID::Q)) { RTLIL::SigSpec sig = cell->getPort(ID::Q); @@ -485,7 +485,7 @@ std::string cellname(RTLIL::Cell *cell) } void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) -{ +{ // PORTING REQUIRED f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = %s ", op.c_str()); @@ -495,7 +495,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) -{ +{ // PORTING REQUIRED f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); @@ -507,7 +507,7 @@ 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) -{ +{ // PORTING REQUIRED if (cell->type == ID($_NOT_)) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); @@ -1445,7 +1445,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) -{ +{ // PORTING REQUIRED if (cell->type[0] == '$' && !noexpr) { if (dump_cell_expr(f, indent, cell)) return; @@ -1524,7 +1524,7 @@ 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) -{ +{ // PORTING REQUIRED f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, left); f << stringf(" = "); @@ -1532,10 +1532,11 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, f << stringf(";\n"); } +// This is a forward declaration void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw); void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) -{ +{ // PORTING REQUIRED int number_of_stmts = cs->switches.size() + cs->actions.size(); if (!omit_trailing_begin && number_of_stmts >= 2) @@ -1562,7 +1563,7 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo } void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) -{ +{ // PORTING REQUIRED if (sw->signal.size() == 0) { f << stringf("%s" "begin\n", indent.c_str()); for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { @@ -1602,7 +1603,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw } void case_body_find_regs(RTLIL::CaseRule *cs) -{ +{ // NO PORTING REQUIRED for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) case_body_find_regs(*it2); @@ -1615,7 +1616,7 @@ void case_body_find_regs(RTLIL::CaseRule *cs) } void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false) -{ +{ // PORTING REQUIRED if (find_regs) { case_body_find_regs(&proc->root_case); for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) @@ -1689,7 +1690,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo } void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) -{ +{ // PORTING REQUIRED reg_wires.clear(); reset_auto_counter(module); active_module = module; @@ -1789,7 +1790,7 @@ struct VHDLBackend : public Backend { log("\n"); log(" write_vhdl [options] [filename]\n"); log("\n"); - log("Write the current design to a VHDL file (wip).\n"); + log("Write the current design to a VHDL file (WIP).\n"); log("\n"); log(" -norename\n"); log(" without this option all internal object names (the ones with a dollar\n"); @@ -1864,7 +1865,7 @@ struct VHDLBackend : public Backend { log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE - { + { // PORTING REQUIRED log_header(design, "Executing VHDL backend.\n"); verbose = false; From 7346b71a4cc342ae7b2f7e2a83635d1a107e5b44 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 22 May 2020 14:21:29 -0700 Subject: [PATCH 04/90] Update Makefiles to build the new backend as well --- Makefile | 9 ++++++--- src/Makefile.inc | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d24104b2..7798c45f 100644 --- a/Makefile +++ b/Makefile @@ -18,14 +18,17 @@ ALL_CFLAGS=-fPIC -DYOSYS_ENABLE_GHDL -I$(LIBGHDL_INC) $(CFLAGS) all: ghdl.$(SOEXT) -ghdl.$(SOEXT): ghdl.o - $(YOSYS_CONFIG) --build $@ $< -shared $(ALL_LDFLAGS) +ghdl.$(SOEXT): ghdl.o vhdl_backend.o + $(YOSYS_CONFIG) --build $@ $^ -shared $(ALL_LDFLAGS) ghdl.o: src/ghdl.cc $(YOSYS_CONFIG) --exec --cxx -c --cxxflags -o $@ $< $(ALL_CFLAGS) +vhdl_backend.o: src/vhdl_backend.cc + $(YOSYS_CONFIG) --exec --cxx -c --cxxflags -o $@ $< $(ALL_CFLAGS) + clean: force - $(RM) -f ghdl.$(SOEXT) ghdl.o + $(RM) -f ghdl.$(SOEXT) ghdl.o vhdl_backend.o install: ghdl.$(SOEXT) $(YOSYS_CONFIG) --exec mkdir -p --datdir/plugins diff --git a/src/Makefile.inc b/src/Makefile.inc index a68e7d1a..6e215cb2 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -1,2 +1,3 @@ OBJS += frontends/ghdl/ghdl.o +OBJS += backends/vhdl/vhdl_backend.o From ac43d36bafd1b5994226d3c19057d6873a10ae65 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 22 May 2020 14:23:09 -0700 Subject: [PATCH 05/90] Write VHDL header imports and adjust the autogenerated header comment --- src/vhdl_backend.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b1bba751..51546713 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1782,6 +1782,16 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) active_initdata.clear(); } +void write_header_imports(std::ostream &f, std::string indent) +{ + f << indent << "library IEEE;\n"; + f << indent << "use IEEE.STD_LOGIC_1164.ALL;\n"; + f << indent << "use IEEE.NUMERIC_STD.ALL;\n"; + if (extmem) { + f << indent << "\nuse STD.TEXTIO.ALL\n"; + } +} + struct VHDLBackend : public Backend { VHDLBackend() : Backend("vhdl", "write design to VHDL file") { } void help() YS_OVERRIDE @@ -1991,7 +2001,9 @@ struct VHDLBackend : public Backend { design->sort(); - *f << stringf("/* Generated by %s */\n", yosys_version_str); + *f << stringf("-- Generated by %s\n", yosys_version_str); + log("Warning: the VHDL backend is still experimental\n"); + write_header_imports(*f, ""); for (auto module : design->modules()) { if (module->get_blackbox_attribute() != blackboxes) continue; From 7b08be43fca96c2183a146cbedd78a001901fea6 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 22 May 2020 16:24:17 -0700 Subject: [PATCH 06/90] Remove useless '\n' terminator argument from dump_attributes --- src/vhdl_backend.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 51546713..4b16731e 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -370,8 +370,8 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false) -{ // PORTING REQUIRED +void dump_attributes(std::ostream &f, std::string indent, dict &attributes, bool modattr = false, bool regattr = false, bool as_comment = false) +{ // PORTING IN PROGRESS if (noattr) return; if (attr2comment) @@ -386,13 +386,13 @@ void dump_attributes(std::ostream &f, std::string indent, dictsecond, -1, 0, false, as_comment); - f << stringf(" %s%c", as_comment ? "*/" : "*)", term); + f << stringf(" %s\n", as_comment ? "*/" : "*)"); } } void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) { // PORTING REQUIRED - dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); + dump_attributes(f, indent, wire->attributes, /*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 " : ""); @@ -1581,7 +1581,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, /*regattr=*/false, /*as_comment=*/true); + dump_attributes(f, indent + " ", (*it)->attributes, /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true); if ((*it)->compare.size() == 0) { if (got_default) continue; @@ -1744,7 +1744,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } - dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true); + dump_attributes(f, indent, module->attributes, /*modattr=*/true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; for (int port_id = 1; keep_running; port_id++) { @@ -1875,7 +1875,7 @@ struct VHDLBackend : public Backend { log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE - { // PORTING REQUIRED + { // PORTING TOP COMPLETE, SUBROUTINES IN PROGRESS log_header(design, "Executing VHDL backend.\n"); verbose = false; From 1c8e4cc6e684f39195f7bdc93d38646968a93e85 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 13:17:52 -0700 Subject: [PATCH 07/90] Use log_experimental to log the fact that the VHDL backend is experimental --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 4b16731e..f86fd3a8 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -2002,7 +2002,7 @@ struct VHDLBackend : public Backend { design->sort(); *f << stringf("-- Generated by %s\n", yosys_version_str); - log("Warning: the VHDL backend is still experimental\n"); + log_experimental("VHDL backend"); write_header_imports(*f, ""); for (auto module : design->modules()) { if (module->get_blackbox_attribute() != blackboxes) From 004a03e8a0fdc437f14370acbc1f8651be617e72 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 13:42:55 -0700 Subject: [PATCH 08/90] Mark dump_attributes as PORTING REQUIRED again More nontrivial design decisions need to be made before I actually write the code for this --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index f86fd3a8..ecf21ec9 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -371,7 +371,7 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } void dump_attributes(std::ostream &f, std::string indent, dict &attributes, bool modattr = false, bool regattr = false, bool as_comment = false) -{ // PORTING IN PROGRESS +{ // PORTING REQUIRED if (noattr) return; if (attr2comment) From 698fa75942586f6fd22dd3995c353a344224244d Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 13:45:20 -0700 Subject: [PATCH 09/90] Implement dump_constant and remove decimal option Completely removing decimal functionality may come back to bite me later...I'll see --- src/vhdl_backend.cc | 94 ++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index ecf21ec9..c84b1999 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -32,7 +32,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit; +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, siminit; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; std::set reg_wires, reg_ct; @@ -42,6 +42,15 @@ RTLIL::Module *active_module; dict active_initdata; SigMap active_sigmap; +const char * const ctrl_char_array[]={"NUL", "SOH", "STX", "ETX", + "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", + "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", + "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", + "FSP", "GSP", "RSP", "USP"}; + void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { // NO PORTING REQUIRED const char *str = id.c_str(); @@ -182,14 +191,17 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) return true; } -void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false) -{ // PORTING REQUIRED +void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false) +{ // PORTING NEEDS TESTING bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; + /* TODO: verify correctness + * width==0 is a null range, as defined by IEEE 1076-2008 5.2.1 + * width<0 is ? + */ if (width < 0) width = data.bits.size() - offset; if (width == 0) { - // See IEEE 1364-2005 Clause 5.1.14. - f << "{0{1'b0}}"; + f << "(others => '0')"; return; } if (nostr) @@ -204,12 +216,10 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o if (data.bits[i] == State::S1) val |= 1 << (i - offset); } - if (decimal) - f << stringf("%d", val); - else if (set_signed && val < 0) - f << stringf("-32'sd%u", -val); + if (set_signed) + f << stringf("std_logic_vector(to_signed(%d,%d))", val, width); else - f << stringf("32'%sd%u", set_signed ? "s" : "", val); + f << stringf("std_logic_vector(to_unsigned(%d,%d))", val, width); } else { dump_hex: if (nohex) @@ -222,7 +232,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o 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; + case RTLIL::Sa: bin_digits.push_back('-'); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } @@ -251,22 +261,24 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o hex_digits.push_back('z'); continue; } - if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') { - if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?') + if (bit_3 == '-' || bit_2 == '-' || bit_1 == '-' || bit_0 == '-') { + if (bit_3 != '-' || bit_2 != '-' || bit_1 != '-' || bit_0 != '-') goto dump_bin; - hex_digits.push_back('?'); + hex_digits.push_back('-'); continue; } int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); } - f << stringf("%d'%sh", width, set_signed ? "s" : ""); + // TODO: is this correct when the width is not a multiple of 4? + f << stringf("x\""); for (int i = GetSize(hex_digits)-1; i >= 0; i--) f << hex_digits[i]; + f << stringf("\""); } if (0) { dump_bin: - f << stringf("%d'%sb", width, set_signed ? "s" : ""); + f << stringf("\""); if (width == 0) f << stringf("0"); for (int i = offset+width-1; i >= offset; i--) { @@ -276,28 +288,32 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sz: f << stringf("z"); break; - case RTLIL::Sa: f << stringf("?"); break; + case RTLIL::Sa: f << stringf("-"); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } + f << stringf("\""); } } else { if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) f << stringf("\""); std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { - if (str[i] == '\n') - f << stringf("\\n"); - else if (str[i] == '\t') - f << stringf("\\t"); - else if (str[i] < 32) - f << stringf("\\%03o", str[i]); + unsigned char current_char_unsigned = (unsigned char) str[i]; + /* + * See the following IEEE 1076-2008 sections: + * 15.7 "Character Set" + * 15.9 "String Literals" + * 16.3 "Package STANDARD" + */ + if (current_char_unsigned < 32) + f << stringf("\" & %s & \"", + ctrl_char_array[current_char_unsigned]); + else if (current_char_unsigned >= 128 + && current_char_unsigned <= 159) + f << stringf("\" & C%d & \"", current_char_unsigned); else if (str[i] == '"') - f << stringf("\\\""); - else if (str[i] == '\\') - f << stringf("\\\\"); - else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*') - f << stringf("\\/"); + f << stringf("\"\""); else f << str[i]; } @@ -385,7 +401,7 @@ void dump_attributes(std::ostream &f, std::string indent, dictsecond == State::S1 || it->second == Const(1))) f << stringf(" 1 "); else - dump_const(f, it->second, -1, 0, false, as_comment); + dump_const(f, it->second, -1, 0, false); f << stringf(" %s\n", as_comment ? "*/" : "*)"); } } @@ -1363,8 +1379,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::DST)); } - bool bak_decimal = decimal; - decimal = 1; + //bool bak_decimal = decimal; + //decimal = 1; f << ") = ("; dump_const(f, cell->getParam(ID::T_RISE_MIN)); @@ -1380,7 +1396,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_const(f, cell->getParam(ID::T_FALL_MAX)); f << ");\n"; - decimal = bak_decimal; + //decimal = bak_decimal; f << stringf("%s" "endspecify\n", indent.c_str()); return true; @@ -1412,8 +1428,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::DST_EN)); } - bool bak_decimal = decimal; - decimal = 1; + //bool bak_decimal = decimal; + //decimal = 1; f << ", "; dump_const(f, cell->getParam(ID::T_LIMIT_MIN)); @@ -1432,7 +1448,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } f << ");\n"; - decimal = bak_decimal; + //decimal = bak_decimal; f << stringf("%s" "endspecify\n", indent.c_str()); return true; @@ -1829,9 +1845,6 @@ struct VHDLBackend : public Backend { log(" not bit pattern. This option deactivates this feature and instead\n"); log(" will write out all constants in binary.\n"); log("\n"); - log(" -decimal\n"); - log(" dump 32-bit constants in decimal and without size and radix\n"); - log("\n"); log(" -nohex\n"); log(" constant values that are compatible with hex output are usually\n"); log(" dumped as hex values. This option deactivates this feature and\n"); @@ -1888,7 +1901,6 @@ struct VHDLBackend : public Backend { nostr = false; extmem = false; defparam = false; - decimal = false; siminit = false; auto_prefix = ""; @@ -1969,10 +1981,6 @@ struct VHDLBackend : public Backend { defparam = true; continue; } - if (arg == "-decimal") { - decimal = true; - continue; - } if (arg == "-siminit") { siminit = true; continue; From 14b618c8e317d049bdf012dfb2220b206ec9b6b7 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 16:50:45 -0700 Subject: [PATCH 10/90] Port range identifiers in dump_sigchunk --- src/vhdl_backend.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index c84b1999..964a6046 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -343,7 +343,7 @@ void dump_reg_init(std::ostream &f, SigSpec sig) } void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING if (chunk.wire == NULL) { dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); } else { @@ -351,16 +351,16 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima f << stringf("%s", id(chunk.wire->name).c_str()); } else if (chunk.width == 1) { if (chunk.wire->upto) - f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + f << stringf("%s (%d)", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); + f << stringf("%s (%d)", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); } else { if (chunk.wire->upto) - f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), + f << stringf("%s (%d to %d)", id(chunk.wire->name).c_str(), (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), + f << stringf("%s (%d downto %d)", id(chunk.wire->name).c_str(), (chunk.offset + chunk.width - 1) + chunk.wire->start_offset, chunk.offset + chunk.wire->start_offset); } From cac7e92c402b83e223941ac073fa196ff43986f6 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 17:00:20 -0700 Subject: [PATCH 11/90] Port vector concatenation in dump_sigspec --- src/vhdl_backend.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 964a6046..4ecedc18 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -368,21 +368,20 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima } void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING if (GetSize(sig) == 0) { + // TODO this is a null range that may not be handled correctly f << "\"\""; return; } if (sig.is_chunk()) { dump_sigchunk(f, sig.as_chunk()); } else { - f << stringf("{ "); for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { if (it != sig.chunks().rbegin()) - f << stringf(", "); + f << stringf(" & "); dump_sigchunk(f, *it, true); } - f << stringf(" }"); } } From a6c0fdd04f4c069f857dd4760f73d5ee1f7171f0 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 17:02:19 -0700 Subject: [PATCH 12/90] Remove #if 0 section of dump_wire --- src/vhdl_backend.cc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 4ecedc18..32109a37 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -408,19 +408,6 @@ void dump_attributes(std::ostream &f, std::string indent, dictattributes, /*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 " : ""); - else if (!wire->port_input && wire->port_output) - f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); - else if (wire->port_input && wire->port_output) - f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); - else - f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire"); - if (wire->width != 1) - f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset); - f << stringf("%s;\n", id(wire->name).c_str()); -#else // do not use Verilog-2k "output reg" syntax in Verilog export std::string range = ""; if (wire->width != 1) { @@ -444,7 +431,6 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) f << stringf(";\n"); } else if (!wire->port_input && !wire->port_output) 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) From 3fe06c78c59452276530f6a72934690d40b45b7a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 18:11:31 -0700 Subject: [PATCH 13/90] Port dump_memory function --- src/vhdl_backend.cc | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 32109a37..b371a630 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -40,6 +40,7 @@ std::string auto_prefix, extmem_prefix; RTLIL::Module *active_module; dict active_initdata; +std::unordered_set memory_array_types; SigMap active_sigmap; const char * const ctrl_char_array[]={"NUL", "SOH", "STX", "ETX", @@ -434,9 +435,24 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) } void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING + size_t is_element_present = memory_array_types.count(memory->width); + std::string memory_type_name = stringf("array_type_%d",memory->width); + if (!is_element_present) { + memory_array_types.insert(memory->width); + f << stringf("%s" + "type %s is array (natural range <>) of std_logic_vector(%d downto 0)\n", + indent.c_str(), memory_type_name.c_str(), memory->width-1); + } 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); + // TODO: if memory->size is always positive then this is unnecessary + std::string range_str = stringf("(%d %s %d)", + memory->start_offset+memory->size-1, + memory->size>=0 ? "downto" : "to", memory->start_offset); + // TODO: memory initialization? + f << stringf("%s" "signal %s: %s %s;\n", indent.c_str(), + id(memory->name).c_str(), memory_type_name.c_str(), + range_str.c_str()); } void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) @@ -1697,6 +1713,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) active_module = module; active_sigmap.set(module); active_initdata.clear(); + memory_array_types.clear(); for (auto wire : module->wires()) if (wire->attributes.count(ID::init)) { From 4f56439b41f1bd09fd4c386680aeb4cec81a0a13 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 23 May 2020 19:38:50 -0700 Subject: [PATCH 14/90] Port dump_conn --- src/vhdl_backend.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b371a630..5216111b 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1541,10 +1541,10 @@ 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) -{ // PORTING REQUIRED - f << stringf("%s" "assign ", indent.c_str()); +{ // PORTING NEEDS TESTING + f << stringf("%s", indent.c_str()); dump_sigspec(f, left); - f << stringf(" = "); + f << stringf(" <= "); dump_sigspec(f, right); f << stringf(";\n"); } From d128c86e060305af1a3eaf83c081d4c3a8207d14 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 24 May 2020 20:00:15 -0700 Subject: [PATCH 15/90] Replace keyword list in `id` with VHDL keywords --- src/vhdl_backend.cc | 65 +++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5216111b..002fc950 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -108,7 +108,7 @@ std::string next_auto_id() } std::string id(RTLIL::IdString internal_id, bool may_rename = true) -{ // PORTING REQUIRED +{ // PORTING IN PROGRESS const char *str = internal_id.c_str(); bool do_escape = false; @@ -135,35 +135,48 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) break; } - const pool keywords = { - // IEEE 1800-2017 Annex B - "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", - "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", - "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", - "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", - "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", - "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", - "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", - "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", - "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", - "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", - "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", - "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", - "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", - "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", - "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", - "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", - "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", - "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", - "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", - "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", - "wildcard", "wire", "with", "within", "wor", "xnor", "xor", + /* + * This backend outputs VHDL-93 but maximize compatibility with 08 + * This includes catching all the PSL keywords as well + */ + const pool vhdl_keywords = { + // IEEE 1076-2008 Section 15.10 + "abs", "access", "after", "alias", "all", "and", + "architecture", "array", "assert", "assume", + "assume_guarantee", "attribute", "begin", "block", "body", + "buffer", "bus", "case", "component", "configuration", + "constant", "context", "cover", "default", "disconnect", + "downto", "else", "elsif", "end", "entity", "exit", "fairness", + "file", "for", "force", "function", "generate", "generic", + "group", "guarded", "if", "impure", "in", "inertial", "inout", + "is", "label", "library", "linkage", "literal", "loop", "map", + "mod", "nand", "new", "next", "nor", "not", "null", "of", "on", + "open", "or", "others", "out", "package", "parameter", "port", + "postponed", "procedure", "process", "property", "protected", + "pure", "range", "record", "register", "reject", "release", + "rem", "report", "restrict", "restrict_guarantee", "return", + "rol", "ror", "select", "sequence", "severity", "shared", + "signal", "sla", "sll", "sra", "srl", "strong", "subtype", + "then", "to", "transport", "type", "unaffected", "units", + "until", "use", "variable", "vmode", "vprop", "vunit", "wait", + "when", "while", "with", "xnor", "xor" }; - if (keywords.count(str)) + const pool psl_keywords = { + // IEEE 1076-2008 Section 15.10 + "assert", "assume", "assume_guarantee", "cover", "default", + "fairness", "property", "restrict", "restrict_guarantee", + "sequence", "strong", "vmode", "vprop", "vunit" + }; + if (vhdl_keywords.count(str) || psl_keywords.count(str)) + do_escape = true; + // TODO: check for numbers afterwards, but regex is overkill here + // array_type_(width) is used by the memory dump pass + if (strncmp(str,"array_type_",strlen("array_type_"))==0) do_escape = true; if (do_escape) - return "\\" + std::string(str) + " "; + // VHDL extended identifier + return "\\" + std::string(str) + "\\"; return std::string(str); } From 6e1b43de7f2f71c69c8b6c18d5a8ad2ccf38b0da Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 25 May 2020 13:21:25 -0700 Subject: [PATCH 16/90] Port dump_cell_expr_uniop and dump_cell_expr_binop dump_cell_expr_binop may need adjustments because of STD_LOGIC_VECTOR vs UNSIGNED/SIGNED --- src/vhdl_backend.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 002fc950..cbc12e46 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -515,20 +515,21 @@ std::string cellname(RTLIL::Cell *cell) } void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) -{ // PORTING REQUIRED - f << stringf("%s" "assign ", indent.c_str()); +{ // PORTING NEEDS TESTING + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = %s ", op.c_str()); + f << stringf(" <= %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "A", true); f << stringf(";\n"); } void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) -{ // PORTING REQUIRED - f << stringf("%s" "assign ", indent.c_str()); +{ // PORTING NEEDS TESTING + // TODO: typecasting for arithmetic operations + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); + f << stringf(" <= "); dump_cell_expr_port(f, cell, "A", true); f << stringf(" %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); From 826226b7c4f5adc2f58550316708f5ebfe49ea97 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 25 May 2020 13:26:52 -0700 Subject: [PATCH 17/90] Replace $signed with signed in dump_cell_expr_port I am not marking this as done until I better understand the context in which this function is called --- src/vhdl_backend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index cbc12e46..d65ebb5e 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -469,9 +469,9 @@ void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) } void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { - f << stringf("$signed("); + f << stringf("signed("); dump_sigspec(f, cell->getPort("\\" + port)); f << stringf(")"); } else From 9e1f85965a6e34b82fcb94968652377ab7110dc9 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 30 May 2020 16:32:46 -0700 Subject: [PATCH 18/90] Port syntax gen of is_reg_wire TODO: do reg/wire distinctions make sense for a VHDL backend? --- src/vhdl_backend.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index d65ebb5e..5048d707 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -181,7 +181,7 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) } bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING if (!sig.is_chunk() || sig.as_chunk().wire == NULL) return false; @@ -193,13 +193,16 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) reg_name = id(chunk.wire->name); if (sig.size() != chunk.wire->width) { if (sig.size() == 1) - reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset); + reg_name += stringf("(%d)", + chunk.wire->start_offset + chunk.offset); else if (chunk.wire->upto) - reg_name += stringf("[%d:%d]", (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, - (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + reg_name += stringf("(%d to %d)", + (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, + (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1, - chunk.wire->start_offset + chunk.offset); + reg_name += stringf("(%d downto %d)", + chunk.wire->start_offset + chunk.offset + chunk.width - 1, + chunk.wire->start_offset + chunk.offset); } return true; From ee90888ea209a973599503b6349e715e6e1aa7c0 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 30 May 2020 21:02:25 -0700 Subject: [PATCH 19/90] Port dump_reg_init --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5048d707..55438386 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -354,7 +354,7 @@ void dump_reg_init(std::ostream &f, SigSpec sig) } if (gotinit) { - f << " = "; + f << " := "; dump_const(f, initval); } } From 5b7c9e6c14bc8b449328a50c07862c0d6f2e2e4b Mon Sep 17 00:00:00 2001 From: rlee287 Date: Tue, 2 Jun 2020 22:25:38 -0700 Subject: [PATCH 20/90] Label dump_reg_init as needing testing --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 55438386..5eee289f 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -340,7 +340,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } void dump_reg_init(std::ostream &f, SigSpec sig) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING Const initval; bool gotinit = false; From dad6896cb14140e50c6120388229c6ee3a28cc18 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Tue, 2 Jun 2020 22:26:29 -0700 Subject: [PATCH 21/90] Port range syntax of dump_wire (port dump still need adjusting) --- src/vhdl_backend.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5eee289f..87a42076 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -423,15 +423,15 @@ void dump_attributes(std::ostream &f, std::string indent, dictattributes, /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); // do not use Verilog-2k "output reg" syntax in Verilog export std::string range = ""; if (wire->width != 1) { if (wire->upto) - range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset); + range = stringf(" (%d to %d)", wire->start_offset, wire->width - 1 + wire->start_offset); else - range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset); + range = stringf(" (%d downto %d)", wire->width - 1 + wire->start_offset, wire->start_offset); } if (wire->port_input && !wire->port_output) f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); From ee5c01c97fe01bbdb7ac0dddcfb544a6e4334df2 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Tue, 2 Jun 2020 22:27:49 -0700 Subject: [PATCH 22/90] Initial pass of porting dump_cell_expr Items labelled unported have not been changed yet, and everything else is subject to change --- src/vhdl_backend.cc | 545 ++++++++++++++++++++++---------------------- 1 file changed, 276 insertions(+), 269 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 87a42076..d440925f 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -471,14 +471,19 @@ void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) range_str.c_str()); } -void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) +void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true, bool gen_unsigned = false) { // PORTING NEEDS TESTING if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { f << stringf("signed("); dump_sigspec(f, cell->getPort("\\" + port)); f << stringf(")"); - } else + } else if (gen_unsigned) { + f << stringf("unsigned("); dump_sigspec(f, cell->getPort("\\" + port)); + f << stringf(")"); + } else { + dump_sigspec(f, cell->getPort("\\" + port)); + } } std::string cellname(RTLIL::Cell *cell) @@ -517,23 +522,23 @@ std::string cellname(RTLIL::Cell *cell) } } -void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) +void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool is_arith_op = false) { // PORTING NEEDS TESTING f << stringf("%s", indent.c_str()); 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); + dump_cell_expr_port(f, cell, "A", true, is_arith_op); f << stringf(";\n"); } -void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) +void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool is_arith_op = false) { // PORTING NEEDS TESTING // TODO: typecasting for arithmetic operations f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" <= "); - dump_cell_expr_port(f, cell, "A", true); + dump_cell_expr_port(f, cell, "A", true, is_arith_op); f << stringf(" %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "B", true); @@ -541,12 +546,12 @@ 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) -{ // PORTING REQUIRED +{ // PORTING IN PROGRESS if (cell->type == ID($_NOT_)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); - f << stringf("~"); + f << stringf("not "); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "A", false); f << stringf(";\n"); @@ -554,23 +559,23 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } 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()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_))) - f << stringf("~("); + f << stringf("not ("); dump_cell_expr_port(f, cell, "A", false); f << stringf(" "); if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_ANDNOT_))) - f << stringf("&"); + f << stringf("and"); if (cell->type.in(ID($_OR_), ID($_NOR_), ID($_ORNOT_))) - f << stringf("|"); + f << stringf("or"); if (cell->type.in(ID($_XOR_), ID($_XNOR_))) - f << stringf("^"); + f << stringf("xor"); dump_attributes(f, "", cell->attributes, ' '); f << stringf(" "); if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_))) - f << stringf("~("); + f << stringf("not ("); dump_cell_expr_port(f, cell, "B", false); if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) f << stringf(")"); @@ -579,41 +584,43 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($_MUX_)) { - f << stringf("%s" "assign ", indent.c_str()); + // TODO: attribute dumping was on B + dump_attributes(f, "", cell->attributes, ' '); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); - dump_cell_expr_port(f, cell, "S", false); - f << stringf(" ? "); - dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "B", false); - f << stringf(" : "); + f << stringf(" when "); + dump_cell_expr_port(f, cell, "S", false); + f << stringf(" = '1' else "); dump_cell_expr_port(f, cell, "A", false); f << stringf(";\n"); return true; } if (cell->type == ID($_NMUX_)) { + // TODO: attribute dumping was on B + dump_attributes(f, "", cell->attributes, ' '); f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = !("); - dump_cell_expr_port(f, cell, "S", false); - f << stringf(" ? "); - dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "B", false); - f << stringf(" : "); + f << stringf(" when "); + dump_cell_expr_port(f, cell, "S", false); + f << stringf(" = '1' else "); dump_cell_expr_port(f, cell, "A", false); f << stringf(");\n"); return true; } if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = ~(("); + f << stringf(" = not (("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI3_) ? " & " : " | "); + f << stringf(cell->type == ID($_AOI3_) ? " and " : " or "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &"); + f << stringf(cell->type == ID($_AOI3_) ? ") or" : ") and"); dump_attributes(f, "", cell->attributes, ' '); f << stringf(" "); dump_cell_expr_port(f, cell, "C", false); @@ -622,17 +629,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = ~(("); + f << stringf(" = not (("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << stringf(cell->type == ID($_AOI4_) ? " and " : " or "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &"); + f << stringf(cell->type == ID($_AOI4_) ? ") or" : ") and"); dump_attributes(f, "", cell->attributes, ' '); f << stringf(" ("); dump_cell_expr_port(f, cell, "C", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << stringf(cell->type == ID($_AOI4_) ? " and " : " or "); dump_cell_expr_port(f, cell, "D", false); f << stringf("));\n"); return true; @@ -643,38 +650,56 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::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(ID::Q)); - f << ";\n"; - } + std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; + // Sensitivity list + // [6] == P is rising + // [7] != _ is asynchronous reset dump_attributes(f, indent, cell->attributes); - f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg"); + f << stringf("%s" "process(", indent.c_str()); dump_sigspec(f, cell->getPort(ID::C)); if (cell->type[7] != '_') { - f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg"); + f << stringf(", "); dump_sigspec(f, cell->getPort(ID::R)); } - f << stringf(")\n"); + f << stringf(") is\n"); + // TODO: implicit assumption of width 1 + // TODO: get rid of intermediate variable? + if (!out_is_reg_wire) { + f << stringf("%s" " variable %s: STD_LOGIC ", indent.c_str(), reg_name.c_str()); + dump_reg_init(f, cell->getPort(ID::Q)); + f << "\n"; + } + f << stringf("%s" "begin\n", indent.c_str()); + // Actual edge checking logic (clock, async reset if present) if (cell->type[7] != '_') { - f << stringf("%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!"); + // async reset + f << stringf("%s" " if ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::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()); + f << stringf(" = '%c' then\n", cell->type[7] == 'P' ? '1' : '0'); + f << stringf("%s" " %s %s '%c';\n", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str(), cell->type[8]); } + // clock edge + f << stringf("%s" " %s %s_edge(", indent.c_str(), + cell->type[7] != '_' ? "elsif" : "if", + cell->type[6] == 'P' ? "rising" : "falling"); + dump_sigspec(f, cell->getPort(ID::C)); + f << stringf(") then\n"); - f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str()); + f << stringf("%s" " %s %s ", indent.c_str(), + assignment_operator.c_str(), reg_name.c_str()); dump_cell_expr_port(f, cell, "D", false); f << stringf(";\n"); + f << stringf("%s" " end if;\n", indent.c_str()); if (!out_is_reg_wire) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" = %s;\n", reg_name.c_str()); + f << stringf(" <= %s;\n", reg_name.c_str()); } + f << stringf("%s" "end process;\n", indent.c_str()); return true; } @@ -686,95 +711,118 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::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(ID::Q)); - f << ";\n"; - } + std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; + // Sensitivity list dump_attributes(f, indent, cell->attributes); - f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg"); + f << stringf("%s" "process(", indent.c_str()); dump_sigspec(f, cell->getPort(ID::C)); - f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg"); - dump_sigspec(f, cell->getPort(ID::S)); - f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg"); + f << stringf(", "); dump_sigspec(f, cell->getPort(ID::R)); - f << stringf(")\n"); + f << stringf(", "); + dump_sigspec(f, cell->getPort(ID::S)); + f << stringf(") is\n"); + // TODO: implicit assumption of width 1 + // TODO: get rid of intermediate variable? + if (!out_is_reg_wire) { + f << stringf("%s" " variable %s: STD_LOGIC ", indent.c_str(), reg_name.c_str()); + dump_reg_init(f, cell->getPort(ID::Q)); + f << "\n"; + } + f << stringf("%s" "begin\n", indent.c_str()); - f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!"); + // reset + f << stringf("%s" " if ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::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' ? "" : "!"); + f << stringf("%s" " = '%c' then\n", indent.c_str(), + pol_r == 'P' ? '1' : '0'); + f << stringf("%s" " %s %s '0'", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str()); + // set + f << stringf("%s" " elsif ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::S)); - f << stringf(")\n"); - f << stringf("%s" " %s <= 1;\n", indent.c_str(), reg_name.c_str()); + f << stringf("%s" " = '%c' then\n", indent.c_str(), + pol_s == 'P' ? '1' : '0'); + f << stringf("%s" " %s %s '1'", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str()); + // clock edge + f << stringf("%s" " elsif %s_edge(", indent.c_str(), + pol_c == 'P' ? "rising" : "falling"); + dump_sigspec(f, cell->getPort(ID::C)); + f << stringf(") then\n"); - f << stringf("%s" " else\n", indent.c_str()); - f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str()); + f << stringf("%s" " %s %s ", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str()); dump_cell_expr_port(f, cell, "D", false); f << stringf(";\n"); + f << stringf("%s" " end if;\n", indent.c_str()); if (!out_is_reg_wire) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" = %s;\n", reg_name.c_str()); + f << stringf(" <= %s;\n", reg_name.c_str()); } 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(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), "||") +#define HANDLE_UNIOP(_type, _operator, _is_arith) \ + if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator, _is_arith); return true; } +#define HANDLE_BINOP(_type, _operator, _is_arith) \ + if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator, _is_arith); return true; } + + // UNIOP/BINOP symbols partly ported + // TODO: casts to unsigned/signed as appropriate + HANDLE_UNIOP(ID($not), "not", false) + HANDLE_UNIOP(ID($pos), "+", true) // unported + HANDLE_UNIOP(ID($neg), "-", true) // unported + + HANDLE_BINOP(ID($and), "and", false) + HANDLE_BINOP(ID($or), "or", false) + HANDLE_BINOP(ID($xor), "xor", false) + HANDLE_BINOP(ID($xnor), "xnor", false) + + // Cheat a bit and use the VHDL-2008 operators for now + // TODO: replace the generated VHDL-2008 code with 93-compatible code + HANDLE_UNIOP(ID($reduce_and), "and", false) + HANDLE_UNIOP(ID($reduce_or), "or", false) + HANDLE_UNIOP(ID($reduce_xor), "xor", false) + HANDLE_UNIOP(ID($reduce_xnor), "xnor", false) + HANDLE_UNIOP(ID($reduce_bool), "or", false) + + // TODO: port these + HANDLE_BINOP(ID($shl), "<<", false) + HANDLE_BINOP(ID($shr), ">>", false) + HANDLE_BINOP(ID($sshl), "<<<", true) + HANDLE_BINOP(ID($sshr), ">>>", true) + + // TODO: check if we want to replicate $eq vs $eqx in VHDL + // TODO: port these + HANDLE_BINOP(ID($lt), "<", true) + HANDLE_BINOP(ID($le), "<=", true) + HANDLE_BINOP(ID($eq), "=", false) + HANDLE_BINOP(ID($ne), "!=", true) + HANDLE_BINOP(ID($eqx), "=", false) + HANDLE_BINOP(ID($nex), "!=", true) + HANDLE_BINOP(ID($ge), ">=", true) + HANDLE_BINOP(ID($gt), ">", true) + + HANDLE_BINOP(ID($add), "+", true) + HANDLE_BINOP(ID($sub), "-", true) + HANDLE_BINOP(ID($mul), "*", true) + HANDLE_BINOP(ID($div), "/", true) + HANDLE_BINOP(ID($mod), "mod", true) + HANDLE_BINOP(ID($pow), "**", true) // unported + + HANDLE_UNIOP(ID($logic_not), "not", false) + HANDLE_BINOP(ID($logic_and), "and", false) + HANDLE_BINOP(ID($logic_or), "or", false) #undef HANDLE_UNIOP #undef HANDLE_BINOP if (cell->type == ID($shift)) - { + { // unported for now f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); @@ -803,7 +851,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($shiftx)) - { + { // unported for now 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_sigspec(f, cell->getPort(ID::A)); @@ -824,21 +872,22 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($mux)) { - f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); - dump_sigspec(f, cell->getPort(ID::S)); - f << stringf(" ? "); + // TODO: attribute dumping was on B dump_attributes(f, "", cell->attributes, ' '); + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); dump_sigspec(f, cell->getPort(ID::B)); - f << stringf(" : "); + f << stringf(" when "); + dump_sigspec(f, cell->getPort(ID::S)); + f << stringf(" = '1' else "); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(";\n"); return true; } if (cell->type == ID($pmux)) - { + { // unported for now int width = cell->parameters[ID::WIDTH].as_int(); int s_width = cell->getPort(ID::S).size(); std::string func_name = cellname(cell); @@ -885,18 +934,24 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($tribuf)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); - dump_sigspec(f, cell->getPort(ID::EN)); - f << stringf(" ? "); + f << stringf(" <= "); dump_sigspec(f, cell->getPort(ID::A)); - f << stringf(" : %d'bz;\n", cell->parameters.at(ID::WIDTH).as_int()); + f << stringf(" when "); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(" = '1' else"); + if (cell->parameters.at(ID::WIDTH).as_int()==1) { + f << stringf("'z'"); + } else { + f << stringf("(others => 'z')"); + } + f << stringf(";\n"); return true; } if (cell->type == ID($slice)) - { + { // unported for now f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); @@ -907,18 +962,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($concat)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = { "); + f << stringf(" <= "); dump_sigspec(f, cell->getPort(ID::B)); - f << stringf(" , "); + f << stringf(" & "); dump_sigspec(f, cell->getPort(ID::A)); - f << stringf(" };\n"); + f << stringf(";\n"); return true; } if (cell->type == ID($lut)) - { + { // unported for now f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); @@ -931,7 +986,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($dffsr)) - { + { // porting in progress SigSpec sig_clk = cell->getPort(ID::CLK); SigSpec sig_set = cell->getPort(ID::SET); SigSpec sig_clr = cell->getPort(ID::CLR); @@ -946,12 +1001,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(sig_q, reg_name); + std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; + if (!out_is_reg_wire) { - f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str()); + f << stringf("%s" " variable %s: STD_LOGIC_VECTOR (%d downto 0)", indent.c_str(), reg_name.c_str(), width-1); dump_reg_init(f, sig_q); f << ";\n"; } - + // This could look nicer as a generate statement for (int i = 0; i < width; i++) { f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); dump_sigspec(f, sig_clk); @@ -1005,45 +1062,68 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); - if (!out_is_reg_wire) { - f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str()); - dump_reg_init(f, cell->getPort(ID::Q)); - f << ";\n"; - } + std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; - f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); + f << stringf("%s" "process (", indent.c_str()); dump_sigspec(f, sig_clk); if (cell->type == ID($adff)) { - f << stringf(" or %sedge ", pol_arst ? "pos" : "neg"); + f << stringf(", "); dump_sigspec(f, sig_arst); } - f << stringf(")\n"); + f << stringf(") is\n"); + + if (!out_is_reg_wire) { + f << stringf("%s" " variable %s: STD_LOGIC_VECTOR (%d downto 0)", + indent.c_str(), reg_name.c_str(), + cell->parameters[ID::WIDTH].as_int()-1); + dump_reg_init(f, cell->getPort(ID::Q)); + f << ";\n"; + } + f << stringf("%s" "begin\n", indent.c_str()); if (cell->type == ID($adff)) { - f << stringf("%s" " if (%s", indent.c_str(), pol_arst ? "" : "!"); + f << stringf("%s" " if ", indent.c_str()); dump_sigspec(f, sig_arst); - f << stringf(")\n"); - f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str()); + f << stringf(" = '%c'", pol_arst ? '1' : '0'); + f << stringf(" then\n"); + f << stringf("%s" " %s %s ", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, val_arst); f << stringf(";\n"); - f << stringf("%s" " else\n", indent.c_str()); + f << stringf("%s" " elsif ", indent.c_str()); + } else { + f << stringf("%s" " if ", indent.c_str()); } + f << stringf("%s_edge(", pol_clk ? "rising" : "falling"); + dump_sigspec(f, sig_clk); + f << stringf(") then\n"); if (cell->type == ID($dffe)) { - f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!"); + f << stringf("%s" " if (", indent.c_str()); dump_sigspec(f, sig_en); - f << stringf(")\n"); + f << stringf(" = '%c'", pol_en ? '1' : '0'); + f << stringf(") then\n"); + f << stringf("%s" " %s %s ", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str()); + } else { + f << stringf("%s" " %s %s ", indent.c_str(), + reg_name.c_str(), assignment_operator.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 (cell->type == ID($dffe)) { + f << stringf("%s" " end if;\n", indent.c_str()); + } + f << stringf("%s" " end if;\n", indent.c_str()); + if (!out_is_reg_wire) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" = %s;\n", reg_name.c_str()); + f << stringf(" <= %s;\n", reg_name.c_str()); } + f << stringf("%s" "end process;\n", indent.c_str()); return true; } @@ -1059,33 +1139,47 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); + std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; + + f << stringf("%s" "process(", indent.c_str()); + dump_sigspec(f, sig_en); + f << stringf(", "); + // TODO: the following would break for nontrivial expressions + dump_cell_expr_port(f, cell, "D", false); + f << stringf(") is\n"); + if (!out_is_reg_wire) { - f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str()); + f << stringf("%s" "variable %s: std_logic_vector (%d downto0)", + indent.c_str(), + reg_name.c_str(), cell->parameters[ID::WIDTH].as_int()-1); dump_reg_init(f, cell->getPort(ID::Q)); f << ";\n"; } - f << stringf("%s" "always @*\n", indent.c_str()); + f << stringf("%s" "begin\n", indent.c_str()); - f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!"); + f << stringf("%s" " if (", indent.c_str()); dump_sigspec(f, sig_en); - f << stringf(")\n"); + f << stringf(" = '%c') then\n", pol_en ? '1' : '0'); - f << stringf("%s" " %s = ", indent.c_str(), reg_name.c_str()); + f << stringf("%s" " %s %s ", indent.c_str(), + reg_name.c_str(), assignment_operator.c_str()); dump_cell_expr_port(f, cell, "D", false); f << stringf(";\n"); + f << stringf("%s" " end if;\n", indent.c_str()); if (!out_is_reg_wire) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" = %s;\n", reg_name.c_str()); + f << stringf(" <= %s;\n", reg_name.c_str()); } + f << stringf("%s" "end process;\n", indent.c_str()); return true; } if (cell->type == ID($mem)) - { + { // unported for now RTLIL::IdString memid = cell->parameters[ID::MEMID].decode_string(); std::string mem_id = id(cell->parameters[ID::MEMID].decode_string()); int abits = cell->parameters[ID::ABITS].as_int(); @@ -1354,121 +1448,34 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($assert), ID($assume), ID($cover))) { - f << stringf("%s" "always @* if (", indent.c_str()); + // TODO dump `assert` as a typical VHDL assert with severity, etc. ? + log_warning("Cell of type %s will be dumped as a PSL comment\n", + cell->type.c_str()+1); + log_experimental("Formal cells as PSL comments"); + f << stringf("%s" "-- psl %s (", indent.c_str(), cell->type.c_str()+1); + if (cell->type != ID($cover)) { + f << stringf("always "); + } dump_sigspec(f, cell->getPort(ID::EN)); - f << stringf(") %s(", cell->type.c_str()+1); + f << stringf(" -> "); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(");\n"); return true; } - if (cell->type.in(ID($specify2), ID($specify3))) - { - f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); - - SigSpec en = cell->getPort(ID::EN); - if (en != State::S1) { - f << stringf("if ("); - dump_sigspec(f, cell->getPort(ID::EN)); - f << stringf(") "); - } - - f << "("; - 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(ID::SRC)); - - f << " "; - 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 == ID($specify3)) { - f << "("; - dump_sigspec(f, cell->getPort(ID::DST)); - f << " "; - if (cell->getParam(ID::DAT_DST_PEN).as_bool()) - f << (cell->getParam(ID::DAT_DST_POL).as_bool() ? "+": "-"); - f << ": "; - dump_sigspec(f, cell->getPort(ID::DAT)); - f << ")"; - } else { - dump_sigspec(f, cell->getPort(ID::DST)); - } - - //bool bak_decimal = decimal; - //decimal = 1; - - f << ") = ("; - dump_const(f, cell->getParam(ID::T_RISE_MIN)); - f << ":"; - dump_const(f, cell->getParam(ID::T_RISE_TYP)); - f << ":"; - dump_const(f, cell->getParam(ID::T_RISE_MAX)); - f << ", "; - dump_const(f, cell->getParam(ID::T_FALL_MIN)); - f << ":"; - dump_const(f, cell->getParam(ID::T_FALL_TYP)); - f << ":"; - dump_const(f, cell->getParam(ID::T_FALL_MAX)); - f << ");\n"; - - //decimal = bak_decimal; - - f << stringf("%s" "endspecify\n", indent.c_str()); - return true; - } - - if (cell->type == ID($specrule)) + if (cell->type.in(ID($specify2), ID($specify3), ID($specrule))) { - f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); - - IdString spec_type = cell->getParam(ID::TYPE).decode_string(); - f << stringf("%s(", spec_type.c_str()); - - 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(ID::SRC_EN) != State::S1) { - f << " &&& "; - dump_sigspec(f, cell->getPort(ID::SRC_EN)); - } - - f << ", "; - 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(ID::DST_EN) != State::S1) { - f << " &&& "; - dump_sigspec(f, cell->getPort(ID::DST_EN)); - } - - //bool bak_decimal = decimal; - //decimal = 1; - - f << ", "; - 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.in(ID($setuphold), ID($recrem), ID($fullskew))) { - f << ", "; - 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"; - //decimal = bak_decimal; - - f << stringf("%s" "endspecify\n", indent.c_str()); + /* + * TODO: There are a number of more graceful ways to handle this: + * - Dump this as a subcomponent + * - Leave a more informative comment behind in the generated VHDL + * - Use the information from the src attribute to augment the above + * Suggestions are welcome if you actually need $specify* cells dumped + */ + log_warning("%s cell %s detected\n", cell->type.c_str(), cell->name.c_str()); + log_warning("$specify* cells are not supported by the VHDL backend and will be ignored\n"); + f << stringf("-- Cell of type %s was not dumped\n", cell->type.c_str()); + // Return true regardless to avoid a subcomponent instantiation return true; } From b3e271fae1e7e8a508a7d0dc4cc72be3bdb9d724 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Tue, 2 Jun 2020 22:37:15 -0700 Subject: [PATCH 23/90] Wrap uniop and binop operators in casts when doing arithmetic operation --- src/vhdl_backend.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index d440925f..93c7d721 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -526,9 +526,16 @@ void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell { // PORTING NEEDS TESTING f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" <= %s ", op.c_str()); + f << stringf(" <= "); + if (is_arith_op) { + f << stringf("std_logic_vector("); + } + f << stringf(" %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "A", true, is_arith_op); + if (is_arith_op) { + f << stringf(")"); + } f << stringf(";\n"); } @@ -538,10 +545,16 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" <= "); + if (is_arith_op) { + f << stringf("std_logic_vector("); + } dump_cell_expr_port(f, cell, "A", true, is_arith_op); f << stringf(" %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "B", true); + if (is_arith_op) { + f << stringf(")"); + } f << stringf(";\n"); } From 38472f6ca624f9dcf7bb89693bcb5022dc1b764f Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 4 Jun 2020 19:29:10 -0700 Subject: [PATCH 24/90] Adjustments to autogenerated IDs to ensure numbered internal signals are valid VHDL identifiers --- src/vhdl_backend.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 93c7d721..4c52967a 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -75,7 +75,7 @@ void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) } void reset_auto_counter(RTLIL::Module *module) -{ // NO PORTING REQUIRED +{ // PORTING NEEDS TESTING auto_name_map.clear(); auto_name_counter = 0; auto_name_offset = 0; @@ -99,21 +99,22 @@ void reset_auto_counter(RTLIL::Module *module) if (verbose) for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it) - log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second); + log(" renaming `%s' to `%s%0*d'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second); } std::string next_auto_id() -{ // NO PORTING REQUIRED - return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++); +{ // PORTING NEEDS TESTING + return stringf("%s%0*d", auto_prefix.c_str(), + auto_name_digits, auto_name_offset + auto_name_counter++); } std::string id(RTLIL::IdString internal_id, bool may_rename = true) -{ // PORTING IN PROGRESS +{ // PORTING NEEDS TESTING const char *str = internal_id.c_str(); bool do_escape = false; if (may_rename && auto_name_map.count(internal_id) != 0) - return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]); + return stringf("%s%0*d", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]); if (*str == '\\') str++; From 710332109880b6f0d059384eeca89f447d960954 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 4 Jun 2020 19:36:11 -0700 Subject: [PATCH 25/90] Avoid wrapping constants with signed/unsigned conversion functions --- src/vhdl_backend.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 4c52967a..ab5702d0 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -474,16 +474,21 @@ void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true, bool gen_unsigned = false) { // PORTING NEEDS TESTING - if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { + SigSpec signal_spec = cell->getPort("\\" + port); + bool signal_is_const = signal_spec.is_fully_const(); + if (gen_signed && !signal_is_const && + cell->parameters.count("\\" + port + "_SIGNED") > 0 && + cell->parameters["\\" + port + "_SIGNED"].as_bool()) { + // TODO check how signed arithmetic interacts with x"blah" constants f << stringf("signed("); - dump_sigspec(f, cell->getPort("\\" + port)); + dump_sigspec(f, signal_spec); f << stringf(")"); - } else if (gen_unsigned) { + } else if (gen_unsigned && !signal_is_const) { f << stringf("unsigned("); - dump_sigspec(f, cell->getPort("\\" + port)); + dump_sigspec(f, signal_spec); f << stringf(")"); } else { - dump_sigspec(f, cell->getPort("\\" + port)); + dump_sigspec(f, signal_spec); } } From 81989ce0d33317e3f64e513fa580d00a4e076514 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 4 Jun 2020 19:36:51 -0700 Subject: [PATCH 26/90] Set default autoprefix of 'n' and enforce nonempty prefix --- src/vhdl_backend.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index ab5702d0..301d4c9e 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1869,7 +1869,7 @@ struct VHDLBackend : public Backend { log(" format '__'.\n"); log("\n"); log(" -renameprefix \n"); - log(" insert this prefix in front of auto-generated instance names\n"); + log(" insert this prefix in front of auto-generated instance names, instead of the default \"n\"\n"); log("\n"); log(" -noattr\n"); log(" with this option no attributes are included in the output\n"); @@ -1947,7 +1947,7 @@ struct VHDLBackend : public Backend { extmem = false; defparam = false; siminit = false; - auto_prefix = ""; + auto_prefix = "n"; bool blackboxes = false; bool selected = false; @@ -2051,6 +2051,9 @@ struct VHDLBackend : public Backend { log_cmd_error("Option -extmem must be used with a filename.\n"); extmem_prefix = filename.substr(0, filename.rfind('.')); } + if (auto_prefix.length() == 0) { + log_cmd_error("Prefix specified by -renameprefix must not be empty.\n"); + } design->sort(); From 6e8b878eecd42b415b67fca08a156244971139dc Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 4 Jun 2020 19:38:27 -0700 Subject: [PATCH 27/90] Update dump_cell_expr_(uni/bin)op for dump_cell_expr_port changes --- src/vhdl_backend.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 301d4c9e..8cc4f8d0 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -538,7 +538,7 @@ void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell } f << stringf(" %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); - dump_cell_expr_port(f, cell, "A", true, is_arith_op); + dump_cell_expr_port(f, cell, "A", is_arith_op, is_arith_op); if (is_arith_op) { f << stringf(")"); } @@ -554,10 +554,10 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell if (is_arith_op) { f << stringf("std_logic_vector("); } - dump_cell_expr_port(f, cell, "A", true, is_arith_op); + dump_cell_expr_port(f, cell, "A", is_arith_op, is_arith_op); f << stringf(" %s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); - dump_cell_expr_port(f, cell, "B", true); + dump_cell_expr_port(f, cell, "B", is_arith_op, is_arith_op); if (is_arith_op) { f << stringf(")"); } From a14cf5442381a4e3ea5f3425a5ea8d972bf4bda7 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 10:45:59 -0700 Subject: [PATCH 28/90] Adjust dump_const to use single quote STD_LOGIC for 1-wide constants --- src/vhdl_backend.cc | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 8cc4f8d0..8aa354c1 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -254,7 +254,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o case RTLIL::Sm: log_error("Found marker state in final netlist."); } } - if (GetSize(bin_digits) == 0) + if (GetSize(bin_digits) <= 1) goto dump_bin; while (GetSize(bin_digits) % 4 != 0) if (bin_digits.back() == '1') @@ -296,9 +296,11 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } if (0) { dump_bin: - f << stringf("\""); - if (width == 0) - f << stringf("0"); + if (width > 1) { + f << stringf("\""); + } else { + f << stringf("'"); + } for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { @@ -310,7 +312,11 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o case RTLIL::Sm: log_error("Found marker state in final netlist."); } } - f << stringf("\""); + if (width > 1) { + f << stringf("\""); + } else { + f << stringf("'"); + } } } else { if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) From 9f79c7d47daddb6d4c0c18698c75c3851e5df54d Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 10:48:24 -0700 Subject: [PATCH 29/90] Add warning about 1-length vectors to help string --- src/vhdl_backend.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 8aa354c1..59754f67 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1937,6 +1937,9 @@ struct VHDLBackend : public Backend { log("processes to logic networks and registers. A warning is generated when\n"); log("this command is called on a design with RTLIL processes.\n"); log("\n"); + log("RTLIL does not distinguish between a vector of length 1 and a nonvector.\n"); + log("This may cause pre-synth/post-synth mismatches when a port\n"); + log("is a vector of length 1.\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { // PORTING TOP COMPLETE, SUBROUTINES IN PROGRESS From 402c7f99742805b3e2cd17682691fe259ca85984 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 10:50:20 -0700 Subject: [PATCH 30/90] Initial port of module dumping At this point the backend should produce valid VHDL for a subset of RTLIL netlists Finishing the port of dump_cell_expr and other functions will take time --- src/vhdl_backend.cc | 102 ++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 59754f67..3bf13db4 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -430,31 +430,29 @@ void dump_attributes(std::ostream &f, std::string indent, dictattributes, /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); - // do not use Verilog-2k "output reg" syntax in Verilog export - std::string range = ""; + std::string typestr = ""; if (wire->width != 1) { if (wire->upto) - range = stringf(" (%d to %d)", wire->start_offset, wire->width - 1 + wire->start_offset); + typestr = stringf("STD_LOGIC_VECTOR (%d to %d)", wire->start_offset, + wire->width - 1 + wire->start_offset); else - range = stringf(" (%d downto %d)", wire->width - 1 + wire->start_offset, wire->start_offset); + typestr = stringf("STD_LOGIC_VECTOR (%d downto %d)", + wire->width - 1 + wire->start_offset, wire->start_offset); + } else { + typestr = stringf("STD_LOGIC"); } - if (wire->port_input && !wire->port_output) - f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); - if (!wire->port_input && wire->port_output) - f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); - if (wire->port_input && wire->port_output) - 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(ID::init)) { - f << stringf(" = "); + if (!wire->port_input && !wire->port_output) { + f << stringf("%s" "signal %s: %s", indent.c_str(), + id(wire->name).c_str(), typestr.c_str()); + if (reg_wires.count(wire->name) && wire->attributes.count(ID::init)) { + f << stringf(" := "); dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); - } else if (!wire->port_input && !wire->port_output) - f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + } } void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) @@ -572,6 +570,7 @@ 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) { // PORTING IN PROGRESS + f << stringf("-- Cell type is %s\n", cell->type.c_str()); if (cell->type == ID($_NOT_)) { f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); @@ -1773,6 +1772,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) active_initdata[sig[i]] = val[i]; } + // TODO: remove if unnecessary if (!module->processes.empty()) log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" "can't always be mapped directly to Verilog always blocks. Unintended\n" @@ -1781,6 +1781,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) + // This just updates internal data structures dump_process(f, indent + " ", it->second, true); if (!noexpr) @@ -1811,29 +1812,68 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } + // Entity declaration + // Find ports first before dumping them + std::map port_wires; + // TODO: validate assumption that port_id is port iff it is positive + for (auto wire : module->wires()) { + if (wire->port_id > 0) { + port_wires.insert({wire->port_id, wire}); + } + } dump_attributes(f, indent, module->attributes, /*modattr=*/true); - f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); - bool keep_running = true; - for (int port_id = 1; keep_running; port_id++) { - keep_running = false; - 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; - continue; + f << stringf("%s" "entity %s is\n", indent.c_str(), + id(module->name, false).c_str()); + f << stringf("%s" " port (\n", indent.c_str()); + for (auto wire_it = port_wires.cbegin(); + wire_it != port_wires.cend(); wire_it++) { + Wire* wire = wire_it->second; + f << stringf("%s" " %s :", + indent.c_str(), id(wire->name).c_str()); + // TODO: Verilog inout = VHDL inout? + if (wire->port_input && wire->port_output) { + f << stringf(" inout "); + } else if (wire->port_input && !wire->port_output) { + f << stringf(" in "); + } else if (!wire->port_input && wire->port_output) { + f << stringf(" out "); + } else { + log_error("Port %s is neither an input nor an output\n", + id(wire->name).c_str()); + } + if (wire->width > 1) { + // TODO: verify arithmetic + if (wire->upto) { + f << stringf("std_logic_vector (%d to %d)", + wire->start_offset, wire->start_offset+wire->width-1); + } else { + f << stringf("std_logic_vector (%d downto %d)", + wire->start_offset+wire->width-1, wire->start_offset); } + } else { + f << stringf("std_logic"); + } + auto wire_it_next = std::next(wire_it); + if (wire_it_next != port_wires.cend()) { + f << stringf(";"); + } else { + f << stringf(");"); } + f << stringf("\n"); } - f << stringf(");\n"); + //f << stringf("%s" " );\n", indent.c_str()); + f << stringf("%s" "end %s;\n", + indent.c_str(), id(module->name, false).c_str()); + // Architecture + f << stringf("%s" "architecture rtl of %s is\n", indent.c_str(), + id(module->name, false).c_str()); 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); - + f << stringf("%s" "begin\n", indent.c_str()); for (auto cell : module->cells()) dump_cell(f, indent + " ", cell); @@ -1843,7 +1883,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) for (auto it = module->connections().begin(); it != module->connections().end(); ++it) dump_conn(f, indent + " ", it->first, it->second); - f << stringf("%s" "endmodule\n", indent.c_str()); + f << stringf("%s" "end rtl;\n", indent.c_str()); active_module = NULL; active_sigmap.clear(); active_initdata.clear(); From 46626fd2d0c2de1f00923d3ad380625d352a005a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 10:54:21 -0700 Subject: [PATCH 31/90] Port bit selection of cellname Other changes may be needed relating to invalid VHDL identifier characters --- src/vhdl_backend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 3bf13db4..5872713f 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -497,7 +497,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b } std::string cellname(RTLIL::Cell *cell) -{ // PORTING REQUIRED +{ // PORTING NEEDS TESTING if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort(ID::Q)) { RTLIL::SigSpec sig = cell->getPort(ID::Q); @@ -518,7 +518,7 @@ std::string cellname(RTLIL::Cell *cell) cell_name = cell_name + "_reg"; if (wire->width != 1) - cell_name += stringf("[%d]", wire->start_offset + sig[0].offset); + cell_name += stringf("(%d)", wire->start_offset + sig[0].offset); if (active_module && active_module->count_id(cell_name) > 0) goto no_special_reg_name; From 014f0004c43f418b0d077b1d9b35859e24ef2096 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 21:51:49 -0700 Subject: [PATCH 32/90] Write function to get a sensitivity set given SigSpecs This should be used to create sensitivity lists whenever creating processes --- src/vhdl_backend.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5872713f..5fac1ff3 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -52,6 +52,30 @@ const char * const ctrl_char_array[]={"NUL", "SOH", "STX", "ETX", "CAN", "EM", "SUB", "ESC", "FSP", "GSP", "RSP", "USP"}; +std::set get_sensitivity_set(RTLIL::SigSpec sigspec) +{ + std::set wire_chunks; + for (RTLIL::SigChunk chunk: sigspec.chunks()) { + // Copied from const checks in SigChunk + // TODO: call pack()? + if (chunk.width > 0 && chunk.wire != NULL) { + wire_chunks.emplace(chunk); + } + } + return wire_chunks; +} + +std::set get_sensitivity_set(std::set sigspecs) +{ + std::set wire_chunks; + for (RTLIL::SigSpec sigspec: sigspecs) { + std::set wires_in_chunk; + wires_in_chunk = get_sensitivity_set(sigspec); + wire_chunks.insert(wires_in_chunk.begin(), wires_in_chunk.end()); + } + return wire_chunks; +} + void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { // NO PORTING REQUIRED const char *str = id.c_str(); From 9c31e5b283ee311112b4d70f4903ef847e87dbc6 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 21:54:43 -0700 Subject: [PATCH 33/90] Address Xiretza's review comment The Verilog backend assumes that port_ids>=1 when valid, however... --- src/vhdl_backend.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5fac1ff3..0292387a 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1841,7 +1841,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) std::map port_wires; // TODO: validate assumption that port_id is port iff it is positive for (auto wire : module->wires()) { - if (wire->port_id > 0) { + if (wire->port_input || wire->port_output) { port_wires.insert({wire->port_id, wire}); } } @@ -1862,6 +1862,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } else if (!wire->port_input && wire->port_output) { f << stringf(" out "); } else { + // Should never execute log_error("Port %s is neither an input nor an output\n", id(wire->name).c_str()); } From 7de3d2b25ffeb8a21ada2261e07cb6e4f591c6f8 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 5 Jun 2020 22:53:38 -0700 Subject: [PATCH 34/90] Copy over $divfloor and $modfloor updates from Yosys --- src/vhdl_backend.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 0292387a..bb192c51 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -869,6 +869,95 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) #undef HANDLE_UNIOP #undef HANDLE_BINOP + if (cell->type == ID($divfloor)) + { // unported for now + // 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" "assign ", indent.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; + } + } + + if (cell->type == ID($modfloor)) + { // unported for now + // 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"); + + f << stringf("%s" "assign ", indent.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; + } + } + if (cell->type == ID($shift)) { // unported for now f << stringf("%s" "assign ", indent.c_str()); From af3a5b7e38d4dd148b73d47f3065b64b11e6576f Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 7 Jun 2020 17:03:09 -0700 Subject: [PATCH 35/90] Add get_sensitivity_list signature using initializer_list --- src/vhdl_backend.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index bb192c51..81b624cf 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -76,6 +76,19 @@ std::set get_sensitivity_set(std::set sigspecs) return wire_chunks; } +// Acknowledging code repetition here (remove set input version?) +std::set +get_sensitivity_set(std::initializer_list sigspecs) +{ + std::set wire_chunks; + for (RTLIL::SigSpec sigspec: sigspecs) { + std::set wires_in_chunk; + wires_in_chunk = get_sensitivity_set(sigspec); + wire_chunks.insert(wires_in_chunk.begin(), wires_in_chunk.end()); + } + return wire_chunks; +} + void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { // NO PORTING REQUIRED const char *str = id.c_str(); From c31fc4a8901cd9f4ac4ec8e89167f53e48e735ba Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 7 Jun 2020 17:03:35 -0700 Subject: [PATCH 36/90] Remove extraneous space before operator in dump_cell_expr_uniop --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 81b624cf..6b650e59 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -577,7 +577,7 @@ void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell if (is_arith_op) { f << stringf("std_logic_vector("); } - f << stringf(" %s ", op.c_str()); + f << stringf("%s ", op.c_str()); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "A", is_arith_op, is_arith_op); if (is_arith_op) { From 78cc8115ac9e273a1e19632647c57a28c3f1f0ff Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 7 Jun 2020 17:06:25 -0700 Subject: [PATCH 37/90] Adjust warnings about signals being 1 wide --- src/vhdl_backend.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 6b650e59..4fcda4b9 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -718,13 +718,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // [7] != _ is asynchronous reset dump_attributes(f, indent, cell->attributes); f << stringf("%s" "process(", indent.c_str()); + // TODO: implicit assumptions of width 1 dump_sigspec(f, cell->getPort(ID::C)); if (cell->type[7] != '_') { f << stringf(", "); dump_sigspec(f, cell->getPort(ID::R)); } f << stringf(") is\n"); - // TODO: implicit assumption of width 1 // TODO: get rid of intermediate variable? if (!out_is_reg_wire) { f << stringf("%s" " variable %s: STD_LOGIC ", indent.c_str(), reg_name.c_str()); @@ -777,13 +777,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // Sensitivity list dump_attributes(f, indent, cell->attributes); f << stringf("%s" "process(", indent.c_str()); + // TODO: implicit assumption of width 1 dump_sigspec(f, cell->getPort(ID::C)); f << stringf(", "); dump_sigspec(f, cell->getPort(ID::R)); f << stringf(", "); dump_sigspec(f, cell->getPort(ID::S)); f << stringf(") is\n"); - // TODO: implicit assumption of width 1 // TODO: get rid of intermediate variable? if (!out_is_reg_wire) { f << stringf("%s" " variable %s: STD_LOGIC ", indent.c_str(), reg_name.c_str()); @@ -1215,6 +1215,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; f << stringf("%s" "process (", indent.c_str()); + // Implicit assumption that clock and reset sigs are 1 wide dump_sigspec(f, sig_clk); if (cell->type == ID($adff)) { f << stringf(", "); From 83c27680746b9be83d6b87728e934408bea909ac Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 7 Jun 2020 17:12:50 -0700 Subject: [PATCH 38/90] First pass at porting $pmux Testing in progress, as well as handling 'X' constants --- src/vhdl_backend.cc | 136 +++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 38 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 4fcda4b9..5f257eb6 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1037,48 +1037,108 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($pmux)) - { // unported for now + { + /* + * Use a case statement in a process instead of with..select + * This makes it easier to handle SigSpecs with multiple chunks + * This is a deliberate break from the output of ghdl --synth + */ + // y=pmux(a, b, s) is selected b subset when $onehot(s) else a when s = (others -> '0') else ERROR + /* with s select y <= + b_pieces when (num => '1', others => '0') + a when (others => '0') + (others => 'X') when others + */ + // TODO: this may break with non-singular SigSpecs + /* + * TODO: remove assumption of downto + * (This assumption is also present in Verilog backend)? + */ 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()); - f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1); - f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1); - f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); - dump_attributes(f, indent + " ", cell->attributes); - if (!noattr) - f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); - f << stringf("%s" " casez (s)", indent.c_str()); - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); - - for (int i = 0; i < s_width; i++) - { - f << stringf("%s" " %d'b", indent.c_str(), s_width); - - for (int j = s_width-1; j >= 0; j--) - f << stringf("%c", j == i ? '1' : '?'); - - f << stringf(":\n"); - f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); + std::ostringstream a_str_stream; + std::ostringstream b_str_stream; + std::ostringstream s_str_stream; + std::ostringstream y_str_stream; + dump_sigspec(a_str_stream, cell->getPort(ID::A)); + dump_sigspec(b_str_stream, cell->getPort(ID::B)); + dump_sigspec(s_str_stream, cell->getPort(ID::S)); + dump_sigspec(y_str_stream, cell->getPort(ID::Y)); + std::string a_str, b_str, s_str, y_str; + a_str = a_str_stream.str(); + b_str = b_str_stream.str(); + s_str = s_str_stream.str(); + y_str = y_str_stream.str(); + + // TODO: small chance of a collision with another identifier + std::string a_var_str, b_var_str, s_var_str, y_var_str; + std::string cellname_prefix = cellname(cell); + cellname_prefix = cellname_prefix.substr(1, cellname_prefix.length()-2); + log("Cellname prefix of $pmux is %s\n",cellname_prefix.c_str()); + a_var_str = "var_"+cellname_prefix+"_a"; + b_var_str = "var_"+cellname_prefix+"_b"; + s_var_str = "var_"+cellname_prefix+"_s"; + y_var_str = "var_"+cellname_prefix+"_y"; + + std::set sensitivities = + get_sensitivity_set({cell->getPort(ID::A), + cell->getPort(ID::B), + cell->getPort(ID::S)}); + f << stringf("%s" "process(", indent.c_str()); + bool is_first_sensitivity_chunk = true; + for (RTLIL::SigChunk chunk: sensitivities) { + if (!is_first_sensitivity_chunk) { + f << ", "; + } + dump_sigchunk(f, chunk); + is_first_sensitivity_chunk = false; } - - f << stringf("%s" " default:\n", indent.c_str()); - f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str()); - - f << stringf("%s" " endcase\n", indent.c_str()); - f << stringf("%s" "endfunction\n", indent.c_str()); - - f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = %s(", func_name.c_str()); - dump_sigspec(f, cell->getPort(ID::A)); - f << stringf(", "); - dump_sigspec(f, cell->getPort(ID::B)); - f << stringf(", "); - dump_sigspec(f, cell->getPort(ID::S)); - f << stringf(");\n"); + f << stringf(") is\n"); + f << stringf("%s" " variable %s: std_logic_vector(%d downto 0);\n", + indent.c_str(), a_var_str.c_str(), width-1); + f << stringf("%s" " variable %s: std_logic_vector(%d downto 0);\n", + indent.c_str(), b_var_str.c_str(), s_width*width-1); + f << stringf("%s" " variable %s: std_logic_vector(%d downto 0);\n", + indent.c_str(), s_var_str.c_str(), s_width-1); + f << stringf("%s" " variable %s: std_logic_vector(%d downto 0);\n", + indent.c_str(), y_var_str.c_str(), width-1); + f << stringf("%s" "begin\n", indent.c_str()); + // TODO: see if any of the case functions can do this + // Use intermediate variables to handle multichunk SigSpecs + f << stringf("%s" " %s := %s;\n", indent.c_str(), + a_var_str.c_str(), a_str.c_str()); + f << stringf("%s" " %s := %s;\n", indent.c_str(), + b_var_str.c_str(), b_str.c_str()); + f << stringf("%s" " %s := %s;\n", indent.c_str(), + s_var_str.c_str(), s_str.c_str()); + f << stringf("%s" " case %s is\n", indent.c_str(), + s_var_str.c_str()); + // onehot selection cases... + for (int i = 0; i < s_width; i++) { + f << stringf("%s" + " when (%d => '1', others => '0') =>\n", + indent.c_str(), i); + f << stringf("%s" + " %s := %s(%d downto %d);\n", + indent.c_str(), + y_var_str.c_str(), b_var_str.c_str(), + (i+1)*width-1, i*width); + } + // ...and defaults + f << stringf("%s" " when (others => '0') =>\n", + indent.c_str()); + f << stringf("%s" " %s := %s;\n", + indent.c_str(), + y_var_str.c_str(), a_var_str.c_str()); + f << stringf("%s" " when others =>\n", + indent.c_str()); + f << stringf("%s" " %s := (others => 'X');\n", + indent.c_str(), y_var_str.c_str()); + f << stringf("%s" " end case;\n", indent.c_str()); + f << stringf("%s" " %s <= %s;\n", indent.c_str(), + y_str.c_str(), y_var_str.c_str()); + f << stringf("%s" "end process;\n", indent.c_str()); return true; } From 8529d14ce4d46381e6d6a4357629d6168baeb94b Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 8 Jun 2020 16:56:39 -0700 Subject: [PATCH 39/90] Fix $pmux dump to use onehot strings instead of aggregates This is valid VHDL-93 as well (besides of the aggregates breaking GHDL) --- src/vhdl_backend.cc | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5f257eb6..4a40d427 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1041,7 +1041,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) /* * Use a case statement in a process instead of with..select * This makes it easier to handle SigSpecs with multiple chunks - * This is a deliberate break from the output of ghdl --synth + * This is a deliberate break from the output of ghdl --synth + * + * TODO: could use (a, b) := c & d; instead, if this is valid VHDL-93 + * This would require informing the concatenation generators + * about whether the expression is an LHS or RHS expression */ // y=pmux(a, b, s) is selected b subset when $onehot(s) else a when s = (others -> '0') else ERROR /* with s select y <= @@ -1075,11 +1079,10 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string a_var_str, b_var_str, s_var_str, y_var_str; std::string cellname_prefix = cellname(cell); cellname_prefix = cellname_prefix.substr(1, cellname_prefix.length()-2); - log("Cellname prefix of $pmux is %s\n",cellname_prefix.c_str()); - a_var_str = "var_"+cellname_prefix+"_a"; - b_var_str = "var_"+cellname_prefix+"_b"; - s_var_str = "var_"+cellname_prefix+"_s"; - y_var_str = "var_"+cellname_prefix+"_y"; + a_var_str = "ivar_"+cellname_prefix+"_a"; + b_var_str = "ivar_"+cellname_prefix+"_b"; + s_var_str = "ivar_"+cellname_prefix+"_s"; + y_var_str = "ivar_"+cellname_prefix+"_y"; std::set sensitivities = get_sensitivity_set({cell->getPort(ID::A), @@ -1114,11 +1117,16 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) s_var_str.c_str(), s_str.c_str()); f << stringf("%s" " case %s is\n", indent.c_str(), s_var_str.c_str()); + char case_comparisons[s_width+1]; + case_comparisons[s_width] = '\0'; // onehot selection cases... for (int i = 0; i < s_width; i++) { + for (int j = 0; j < s_width; j++) { + case_comparisons[j] = j==i ? '1' : '0'; + } f << stringf("%s" - " when (%d => '1', others => '0') =>\n", - indent.c_str(), i); + " when \"%s\" =>\n", + indent.c_str(), case_comparisons); f << stringf("%s" " %s := %s(%d downto %d);\n", indent.c_str(), @@ -1126,8 +1134,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) (i+1)*width-1, i*width); } // ...and defaults - f << stringf("%s" " when (others => '0') =>\n", - indent.c_str()); + for (int j = 0; j < s_width; j++) { + case_comparisons[j] = '0'; + } + f << stringf("%s" " when \"%s\" =>\n", + indent.c_str(), case_comparisons); f << stringf("%s" " %s := %s;\n", indent.c_str(), y_var_str.c_str(), a_var_str.c_str()); @@ -1135,6 +1146,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) indent.c_str()); f << stringf("%s" " %s := (others => 'X');\n", indent.c_str(), y_var_str.c_str()); + f << stringf("%s" " end case;\n", indent.c_str()); f << stringf("%s" " %s <= %s;\n", indent.c_str(), y_str.c_str(), y_var_str.c_str()); From d7c10fcaf2ffb329b23631ea8ff70fdc3d5833c5 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 8 Jun 2020 16:57:25 -0700 Subject: [PATCH 40/90] Escape ivar_ id's that are incoming as well --- src/vhdl_backend.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 4a40d427..93e7edea 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -211,6 +211,8 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) // array_type_(width) is used by the memory dump pass if (strncmp(str,"array_type_",strlen("array_type_"))==0) do_escape = true; + if (strncmp(str,"ivar_",strlen("ivar_"))==0) + do_escape = true; if (do_escape) // VHDL extended identifier From 3865d76f92631a554a20ead8c56728b4394e46bc Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 8 Jun 2020 23:08:07 -0700 Subject: [PATCH 41/90] Properly port $eq and related TODOs in code below, especially with $lt and related --- src/vhdl_backend.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 93e7edea..b7794768 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -859,14 +859,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) HANDLE_BINOP(ID($sshl), "<<<", true) HANDLE_BINOP(ID($sshr), ">>>", true) - // TODO: check if we want to replicate $eq vs $eqx in VHDL - // TODO: port these + // TODO: port $eqx and $nex ("=" and "/=" return BIT, not STD_LOGIC) + // TODO: use "?<" instead of "<" (and analogous) for others? HANDLE_BINOP(ID($lt), "<", true) HANDLE_BINOP(ID($le), "<=", true) - HANDLE_BINOP(ID($eq), "=", false) - HANDLE_BINOP(ID($ne), "!=", true) + HANDLE_BINOP(ID($eq), "?=", false) + HANDLE_BINOP(ID($ne), "?/=", false) HANDLE_BINOP(ID($eqx), "=", false) - HANDLE_BINOP(ID($nex), "!=", true) + HANDLE_BINOP(ID($nex), "/=", false) HANDLE_BINOP(ID($ge), ">=", true) HANDLE_BINOP(ID($gt), ">", true) From a5e59d0827ff08a611bcc2ebb20b25a0f3136d22 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 21:03:41 -0700 Subject: [PATCH 42/90] Fix capitalization of X and Z states in constants --- src/vhdl_backend.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b7794768..6ece9edb 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -287,8 +287,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o switch (data.bits[i]) { 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::Sx: bin_digits.push_back('X'); break; + case RTLIL::Sz: bin_digits.push_back('Z'); break; case RTLIL::Sa: bin_digits.push_back('-'); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } @@ -306,16 +306,16 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o char bit_2 = bin_digits[i+2]; char bit_1 = bin_digits[i+1]; char bit_0 = bin_digits[i+0]; - if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') { - if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x') + if (bit_3 == 'X' || bit_2 == 'X' || bit_1 == 'X' || bit_0 == 'X') { + if (bit_3 != 'X' || bit_2 != 'X' || bit_1 != 'X' || bit_0 != 'X') goto dump_bin; - hex_digits.push_back('x'); + hex_digits.push_back('X'); continue; } - if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') { - if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z') + if (bit_3 == 'Z' || bit_2 == 'Z' || bit_1 == 'Z' || bit_0 == 'Z') { + if (bit_3 != 'Z' || bit_2 != 'Z' || bit_1 != 'Z' || bit_0 != 'Z') goto dump_bin; - hex_digits.push_back('z'); + hex_digits.push_back('Z'); continue; } if (bit_3 == '-' || bit_2 == '-' || bit_1 == '-' || bit_0 == '-') { @@ -345,8 +345,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o switch (data.bits[i]) { case State::S0: f << stringf("0"); break; case State::S1: f << stringf("1"); break; - case RTLIL::Sx: f << stringf("x"); break; - case RTLIL::Sz: f << stringf("z"); break; + case RTLIL::Sx: f << stringf("X"); break; + case RTLIL::Sz: f << stringf("Z"); break; case RTLIL::Sa: f << stringf("-"); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } From 9cff95b6dc31b4851918792cedfa62b4094310f2 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 21:05:22 -0700 Subject: [PATCH 43/90] For hex consts, push '0's and include width when needed Old stuff copied from Verilog did not follow VHDL spec Unfortunately all-unknown constants like 5x"ZZ" are invalid... --- src/vhdl_backend.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 6ece9edb..73342373 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -295,11 +295,9 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } if (GetSize(bin_digits) <= 1) goto dump_bin; + bool not_clean_hex = GetSize(bin_digits) % 4 != 0; while (GetSize(bin_digits) % 4 != 0) - if (bin_digits.back() == '1') - bin_digits.push_back('0'); - else - bin_digits.push_back(bin_digits.back()); + bin_digits.push_back('0'); for (int i = 0; i < GetSize(bin_digits); i += 4) { char bit_3 = bin_digits[i+3]; @@ -327,7 +325,10 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); } - // TODO: is this correct when the width is not a multiple of 4? + // TODO: make this produce VHDL-93 compliant code + if (not_clean_hex) { + f << stringf("%d", width); + } f << stringf("x\""); for (int i = GetSize(hex_digits)-1; i >= 0; i--) f << hex_digits[i]; From ad5bbc710891f57a533a88145de51d97eca6c7d4 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 21:05:51 -0700 Subject: [PATCH 44/90] Adjustments to some UNIOPs --- src/vhdl_backend.cc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 73342373..980b9956 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -838,8 +838,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // UNIOP/BINOP symbols partly ported // TODO: casts to unsigned/signed as appropriate HANDLE_UNIOP(ID($not), "not", false) - HANDLE_UNIOP(ID($pos), "+", true) // unported - HANDLE_UNIOP(ID($neg), "-", true) // unported + HANDLE_UNIOP(ID($pos), "+", true) + HANDLE_UNIOP(ID($neg), "-", true) HANDLE_BINOP(ID($and), "and", false) HANDLE_BINOP(ID($or), "or", false) @@ -878,7 +878,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) HANDLE_BINOP(ID($mod), "mod", true) HANDLE_BINOP(ID($pow), "**", true) // unported - HANDLE_UNIOP(ID($logic_not), "not", false) + if (cell->type == ID($logic_not)) { + // TODO: use VHDL-93 compliant syntax + // TODO: attributes were on port "A" + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); + f << stringf("not (or "); + //dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", false, false); + f << stringf(");\n"); + return true; + } HANDLE_BINOP(ID($logic_and), "and", false) HANDLE_BINOP(ID($logic_or), "or", false) From 6920990671f8bfce13778ce950df092db592b1a3 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 22:39:05 -0700 Subject: [PATCH 45/90] Remove std::set argument version of get_setnsitivity_set --- src/vhdl_backend.cc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 980b9956..26d5b2f4 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -65,18 +65,6 @@ std::set get_sensitivity_set(RTLIL::SigSpec sigspec) return wire_chunks; } -std::set get_sensitivity_set(std::set sigspecs) -{ - std::set wire_chunks; - for (RTLIL::SigSpec sigspec: sigspecs) { - std::set wires_in_chunk; - wires_in_chunk = get_sensitivity_set(sigspec); - wire_chunks.insert(wires_in_chunk.begin(), wires_in_chunk.end()); - } - return wire_chunks; -} - -// Acknowledging code repetition here (remove set input version?) std::set get_sensitivity_set(std::initializer_list sigspecs) { From e30fb17a0b1b96900efa14d86862452d501474ce Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 22:39:32 -0700 Subject: [PATCH 46/90] Print error if dump_wire is called on a port --- src/vhdl_backend.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 26d5b2f4..da2f237e 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -480,6 +480,9 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); + } else { + log_error("Internal error: attempted to dump_wire a port \"%s\"\n", + wire->name.c_str()); } } From ff18f663a950c28643dbc6838e33ff67375a01f9 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 22:44:57 -0700 Subject: [PATCH 47/90] Remove outdated TODOs and adjust comments --- src/vhdl_backend.cc | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index da2f237e..d29814fc 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -582,7 +582,6 @@ 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, bool is_arith_op = false) { // PORTING NEEDS TESTING - // TODO: typecasting for arithmetic operations f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" <= "); @@ -827,7 +826,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator, _is_arith); return true; } // UNIOP/BINOP symbols partly ported - // TODO: casts to unsigned/signed as appropriate + // TODO: verify casts to unsigned/signed HANDLE_UNIOP(ID($not), "not", false) HANDLE_UNIOP(ID($pos), "+", true) HANDLE_UNIOP(ID($neg), "-", true) @@ -1048,17 +1047,10 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) * This makes it easier to handle SigSpecs with multiple chunks * This is a deliberate break from the output of ghdl --synth * - * TODO: could use (a, b) := c & d; instead, if this is valid VHDL-93 + * Could use (a, b) := c & d; instead, if this is valid VHDL-93 * This would require informing the concatenation generators * about whether the expression is an LHS or RHS expression */ - // y=pmux(a, b, s) is selected b subset when $onehot(s) else a when s = (others -> '0') else ERROR - /* with s select y <= - b_pieces when (num => '1', others => '0') - a when (others => '0') - (others => 'X') when others - */ - // TODO: this may break with non-singular SigSpecs /* * TODO: remove assumption of downto * (This assumption is also present in Verilog backend)? @@ -1080,7 +1072,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) s_str = s_str_stream.str(); y_str = y_str_stream.str(); - // TODO: small chance of a collision with another identifier + // ivar_ names are escaped above, so there shouldn't be collisions std::string a_var_str, b_var_str, s_var_str, y_var_str; std::string cellname_prefix = cellname(cell); cellname_prefix = cellname_prefix.substr(1, cellname_prefix.length()-2); @@ -1694,11 +1686,12 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($specify2), ID($specify3), ID($specrule))) { /* - * TODO: There are a number of more graceful ways to handle this: + * There are a number of more graceful ways to handle this: * - Dump this as a subcomponent * - Leave a more informative comment behind in the generated VHDL * - Use the information from the src attribute to augment the above * Suggestions are welcome if you actually need $specify* cells dumped + * (Yosys has limited support for these anyway so this should be fine) */ log_warning("%s cell %s detected\n", cell->type.c_str(), cell->name.c_str()); log_warning("$specify* cells are not supported by the VHDL backend and will be ignored\n"); @@ -2019,7 +2012,6 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) // Entity declaration // Find ports first before dumping them std::map port_wires; - // TODO: validate assumption that port_id is port iff it is positive for (auto wire : module->wires()) { if (wire->port_input || wire->port_output) { port_wires.insert({wire->port_id, wire}); From 9a761119490cae16f5b84f04e903cad3ddc7c7bc Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 12 Jun 2020 22:45:21 -0700 Subject: [PATCH 48/90] Split the reg-finding part of dump_process into different function --- src/vhdl_backend.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index d29814fc..a63dfdde 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1877,19 +1877,20 @@ void case_body_find_regs(RTLIL::CaseRule *cs) } } -void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false) -{ // PORTING REQUIRED - if (find_regs) { - case_body_find_regs(&proc->root_case); - for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) +void find_process_regs(RTLIL::Process *proc) +{ + case_body_find_regs(&proc->root_case); + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) { for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) { for (auto &c : it2->first.chunks()) if (c.wire != NULL) reg_wires.insert(c.wire->name); } - return; } - + return; +} +void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc) +{ // PORTING REQUIRED f << stringf("%s" "always @* begin\n", indent.c_str()); dump_case_body(f, indent, &proc->root_case, true); @@ -1979,7 +1980,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) // This just updates internal data structures - dump_process(f, indent + " ", it->second, true); + find_process_regs(it->second); if (!noexpr) { From e2259fb98aa4a271eb650a032056ef05eb4dcc3a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 27 Jul 2020 21:58:35 -0700 Subject: [PATCH 49/90] Replace YS_OVERRIDE macros with override keyword --- src/vhdl_backend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index a63dfdde..a2be05fb 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -2099,7 +2099,7 @@ void write_header_imports(std::ostream &f, std::string indent) struct VHDLBackend : public Backend { VHDLBackend() : Backend("vhdl", "write design to VHDL file") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -2179,7 +2179,7 @@ struct VHDLBackend : public Backend { log("This may cause pre-synth/post-synth mismatches when a port\n"); log("is a vector of length 1.\n"); } - void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { // PORTING TOP COMPLETE, SUBROUTINES IN PROGRESS log_header(design, "Executing VHDL backend.\n"); From 611867686dd8733a6e4530aeb355b48ad6157bb7 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 1 Aug 2020 22:50:17 -0700 Subject: [PATCH 50/90] Part 1 of porting new FF changes See YosysHQ/yosys@8fd43515c5790552e686dbad09e92db2e8d291ca Still need to write the actual new FF code generation --- src/vhdl_backend.cc | 46 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index a2be05fb..c598cd89 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -24,6 +24,8 @@ #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/sigtools.h" +#include "kernel/ff.h" + #include #include #include @@ -35,7 +37,7 @@ PRIVATE_NAMESPACE_BEGIN bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, siminit; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; -std::set reg_wires, reg_ct; +std::set reg_wires; std::string auto_prefix, extmem_prefix; RTLIL::Module *active_module; @@ -529,7 +531,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b std::string cellname(RTLIL::Cell *cell) { // PORTING NEEDS TESTING - if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort(ID::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(ID::Q); if (GetSize(sig) != 1 || sig.is_fully_const()) @@ -699,7 +701,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type.begins_with("$_DFF_")) + /*if (cell->type.begins_with("$_DFF_")) { std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); @@ -818,7 +820,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } return true; - } + }*/ #define HANDLE_UNIOP(_type, _operator, _is_arith) \ if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator, _is_arith); return true; } @@ -1204,6 +1206,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + // Trash the below and reimport/copy new FfData stuff in if (cell->type == ID($dffsr)) { // porting in progress SigSpec sig_clk = cell->getPort(ID::CLK); @@ -1700,8 +1703,7 @@ 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: $memrd, $memwr, $fsm return false; } @@ -1774,7 +1776,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) } } - if (siminit && reg_ct.count(cell->type) && cell->hasPort(ID::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(ID::Q)); if (!ss.str().empty()) { @@ -1987,7 +1989,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) std::set> reg_bits; for (auto cell : module->cells()) { - if (!reg_ct.count(cell->type) || !cell->hasPort(ID::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(ID::Q); @@ -2201,33 +2203,6 @@ struct VHDLBackend : public Backend { auto_name_map.clear(); reg_wires.clear(); - reg_ct.clear(); - - reg_ct.insert(ID($dff)); - reg_ct.insert(ID($adff)); - reg_ct.insert(ID($dffe)); - reg_ct.insert(ID($dlatch)); - - reg_ct.insert(ID($_DFF_N_)); - reg_ct.insert(ID($_DFF_P_)); - - reg_ct.insert(ID($_DFF_NN0_)); - reg_ct.insert(ID($_DFF_NN1_)); - reg_ct.insert(ID($_DFF_NP0_)); - reg_ct.insert(ID($_DFF_NP1_)); - reg_ct.insert(ID($_DFF_PN0_)); - reg_ct.insert(ID($_DFF_PN1_)); - reg_ct.insert(ID($_DFF_PP0_)); - reg_ct.insert(ID($_DFF_PP1_)); - - reg_ct.insert(ID($_DFFSR_NNN_)); - reg_ct.insert(ID($_DFFSR_NNP_)); - reg_ct.insert(ID($_DFFSR_NPN_)); - reg_ct.insert(ID($_DFFSR_NPP_)); - reg_ct.insert(ID($_DFFSR_PNN_)); - reg_ct.insert(ID($_DFFSR_PNP_)); - reg_ct.insert(ID($_DFFSR_PPN_)); - reg_ct.insert(ID($_DFFSR_PPP_)); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -2321,7 +2296,6 @@ struct VHDLBackend : public Backend { auto_name_map.clear(); reg_wires.clear(); - reg_ct.clear(); } } VerilogBackend; From 157e8b3a6cf267d5f8fba68952176ed456480428 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 1 Aug 2020 22:50:48 -0700 Subject: [PATCH 51/90] Misc comments and tidying up --- src/vhdl_backend.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index c598cd89..1aa2fc1f 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -45,6 +45,7 @@ dict active_initdata; std::unordered_set memory_array_types; SigMap active_sigmap; +// ASCII control character mapping const char * const ctrl_char_array[]={"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", @@ -1134,13 +1135,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } // ...and defaults for (int j = 0; j < s_width; j++) { - case_comparisons[j] = '0'; + case_comparisons[j] = '0'; } f << stringf("%s" " when \"%s\" =>\n", indent.c_str(), case_comparisons); f << stringf("%s" " %s := %s;\n", indent.c_str(), y_var_str.c_str(), a_var_str.c_str()); + // Undefined behavior when not all '0' or onehot f << stringf("%s" " when others =>\n", indent.c_str()); f << stringf("%s" " %s := (others => 'X');\n", @@ -2297,6 +2299,6 @@ struct VHDLBackend : public Backend { auto_name_map.clear(); reg_wires.clear(); } -} VerilogBackend; +} VHDLBackend; PRIVATE_NAMESPACE_END From 6738129242de6c1119c0bb4d723206e834368b32 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Tue, 4 Aug 2020 17:38:33 -0700 Subject: [PATCH 52/90] Make memory_array_types a regular (sorted) set It will usually be too small to reap the benefits of an amortized hashing implementation of a map --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 1aa2fc1f..647095d6 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -42,7 +42,7 @@ std::string auto_prefix, extmem_prefix; RTLIL::Module *active_module; dict active_initdata; -std::unordered_set memory_array_types; +std::set memory_array_types; SigMap active_sigmap; // ASCII control character mapping From 1f9b1b63f602f9a386c92cdea25349b258a142e7 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 6 Aug 2020 23:01:55 -0700 Subject: [PATCH 53/90] Ignore dump_wire on port instead of raising error --- src/vhdl_backend.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 647095d6..14e16aba 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -483,9 +483,6 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); - } else { - log_error("Internal error: attempted to dump_wire a port \"%s\"\n", - wire->name.c_str()); } } From f2575f224dd61a367192e1d4b618fd4e25c3fbb6 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 6 Aug 2020 23:02:37 -0700 Subject: [PATCH 54/90] Minor adjustments to PSL cell generation --- src/vhdl_backend.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 14e16aba..179a6f81 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1673,11 +1673,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // TODO dump `assert` as a typical VHDL assert with severity, etc. ? log_warning("Cell of type %s will be dumped as a PSL comment\n", cell->type.c_str()+1); + log("PSL unclocked assertions do not work (yet) with GHDL\n"); log_experimental("Formal cells as PSL comments"); - f << stringf("%s" "-- psl %s (", indent.c_str(), cell->type.c_str()+1); + f << stringf("%s" "-- psl %s ", indent.c_str(), cell->type.c_str()+1); if (cell->type != ID($cover)) { f << stringf("always "); } + f << stringf("("); dump_sigspec(f, cell->getPort(ID::EN)); f << stringf(" -> "); dump_sigspec(f, cell->getPort(ID::A)); From 6e90b3d8de9f698323458311a193d77ab0e13877 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 8 Aug 2020 22:40:57 -0700 Subject: [PATCH 55/90] Dump $assert cells as regular VHDL assert See comment on ghdl/ghdl#1427 --- src/vhdl_backend.cc | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 179a6f81..5dd54b4c 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1670,20 +1670,31 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($assert), ID($assume), ID($cover))) { - // TODO dump `assert` as a typical VHDL assert with severity, etc. ? - log_warning("Cell of type %s will be dumped as a PSL comment\n", - cell->type.c_str()+1); - log("PSL unclocked assertions do not work (yet) with GHDL\n"); - log_experimental("Formal cells as PSL comments"); - f << stringf("%s" "-- psl %s ", indent.c_str(), cell->type.c_str()+1); - if (cell->type != ID($cover)) { - f << stringf("always "); - } - f << stringf("("); - dump_sigspec(f, cell->getPort(ID::EN)); - f << stringf(" -> "); - dump_sigspec(f, cell->getPort(ID::A)); - f << stringf(");\n"); + if (cell->type != ID($assert)) { + log_warning("Cell of type %s will be dumped as a PSL comment\n", + cell->type.c_str()+1); + log("PSL unclocked assertions do not work (yet) with GHDL\n"); + } + log_experimental("Formal cells as asserts/PSL comments"); + std::stringstream en_sstream; + std::stringstream a_sstream; // Not actually arbitrary haha + string en_str; + string a_str; // [A]rticle of interest + dump_sigspec(en_sstream, cell->getPort(ID::EN)); + dump_sigspec(a_sstream, cell->getPort(ID::A)); + en_str = en_sstream.str(); + a_str = a_sstream.str(); + if (cell->type == ID($assert)) { + f << stringf("%s" "assert (not %s) or %s;", indent.c_str(), + en_str.c_str(),a_str.c_str()); + f << stringf(" -- %s -> %s\n", en_str.c_str(), a_str.c_str()); + } else { + f << stringf("%s" "-- psl %s ", indent.c_str(), cell->type.c_str()+1); + if (cell->type != ID($cover)) { + f << stringf("always "); + } + f << stringf("(%s -> %s);\n", en_str.c_str(), a_str.c_str()); + } return true; } From 890084eb9337def6344da9c4e835fa8ab9988c5c Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sat, 8 Aug 2020 22:42:18 -0700 Subject: [PATCH 56/90] Apply unary or to $logic_not when required --- src/vhdl_backend.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5dd54b4c..6650d930 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -874,10 +874,16 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" <= "); - f << stringf("not (or "); + // Unary or only if signal is a vector + bool need_unary_or = cell->getPort(ID::A).as_wire()->width > 1; + if (need_unary_or) { + f << stringf("not (or "); + } else { + f << stringf("not "); + } //dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "A", false, false); - f << stringf(");\n"); + f << stringf("%s;\n", need_unary_or ? ")" : ""); return true; } HANDLE_BINOP(ID($logic_and), "and", false) From 3f9cd3ae5020ba710872c59806f4fa62e827a87c Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 9 Aug 2020 22:07:58 -0700 Subject: [PATCH 57/90] Write get_sensitivity_set that takes std::set --- src/vhdl_backend.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 6650d930..7f03fcc5 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -68,6 +68,19 @@ std::set get_sensitivity_set(RTLIL::SigSpec sigspec) return wire_chunks; } +std::set +get_sensitivity_set(std::set sigspecs) +{ + std::set wire_chunks; + for (RTLIL::SigSpec sigspec: sigspecs) { + std::set wires_in_chunk; + wires_in_chunk = get_sensitivity_set(sigspec); + wire_chunks.insert(wires_in_chunk.begin(), wires_in_chunk.end()); + } + return wire_chunks; +} + +// Takes precedence for braced initialization std::set get_sensitivity_set(std::initializer_list sigspecs) { @@ -80,6 +93,8 @@ get_sensitivity_set(std::initializer_list sigspecs) return wire_chunks; } +// TODO: void dump_sensitivity_set(std::set)? + void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { // NO PORTING REQUIRED const char *str = id.c_str(); From 0d23d588ff3a456c25c659dcb7a5f17863364f2b Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 9 Aug 2020 22:11:14 -0700 Subject: [PATCH 58/90] Copy over new dumping code and port clocked FF portions of it TODO: dump latches (besides of sensitivity list which was ported here) --- src/vhdl_backend.cc | 339 ++++++++++++++++++++++---------------------- 1 file changed, 172 insertions(+), 167 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 7f03fcc5..11e92dfd 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1226,196 +1226,201 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - // Trash the below and reimport/copy new FfData stuff in - if (cell->type == ID($dffsr)) + /* + * Use a single process to wrap the entire thing + * Each bit is programatically unrolled (like a for..generate statement) + * Grouping will be more obvious this way + * Entire FF group shares a cell type so a single sensitivity list suffices + */ + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { // porting in progress - SigSpec sig_clk = cell->getPort(ID::CLK); - SigSpec sig_set = cell->getPort(ID::SET); - SigSpec sig_clr = cell->getPort(ID::CLR); - SigSpec sig_d = cell->getPort(ID::D); - SigSpec sig_q = cell->getPort(ID::Q); + FfData ff(nullptr, cell); - int width = cell->parameters[ID::WIDTH].as_int(); - bool pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool(); - bool pol_set = cell->parameters[ID::SET_POLARITY].as_bool(); - bool pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool(); + // $ff / $_FF_ cell: not supported. + if (ff.has_d && !ff.has_clk && !ff.has_en) + return false; std::string reg_name = cellname(cell); - bool out_is_reg_wire = is_reg_wire(sig_q, reg_name); - + bool out_is_reg_wire = is_reg_wire(ff.sig_q, reg_name); std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; + bool systemverilog = false; // temp - if (!out_is_reg_wire) { - f << stringf("%s" " variable %s: STD_LOGIC_VECTOR (%d downto 0)", indent.c_str(), reg_name.c_str(), width-1); - dump_reg_init(f, sig_q); - f << ";\n"; - } - // This could look nicer as a generate statement - 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"); - } - - 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.in(ID($dff), ID($adff), ID($dffe))) - { - RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst; - bool pol_clk, pol_arst = false, pol_en = false; - - sig_clk = cell->getPort(ID::CLK); - pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool(); - - if (cell->type == ID($adff)) { - sig_arst = cell->getPort(ID::ARST); - pol_arst = cell->parameters[ID::ARST_POLARITY].as_bool(); - val_arst = RTLIL::SigSpec(cell->parameters[ID::ARST_VALUE]); - } - - if (cell->type == ID($dffe)) { - sig_en = cell->getPort(ID::EN); - pol_en = cell->parameters[ID::EN_POLARITY].as_bool(); - } - - std::string reg_name = cellname(cell); - bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); - - std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; - - f << stringf("%s" "process (", indent.c_str()); - // Implicit assumption that clock and reset sigs are 1 wide - dump_sigspec(f, sig_clk); - if (cell->type == ID($adff)) { - f << stringf(", "); - dump_sigspec(f, sig_arst); - } - f << stringf(") is\n"); - - if (!out_is_reg_wire) { - f << stringf("%s" " variable %s: STD_LOGIC_VECTOR (%d downto 0)", - indent.c_str(), reg_name.c_str(), - cell->parameters[ID::WIDTH].as_int()-1); - dump_reg_init(f, cell->getPort(ID::Q)); - f << ";\n"; - } - f << stringf("%s" "begin\n", indent.c_str()); - - if (cell->type == ID($adff)) { - f << stringf("%s" " if ", indent.c_str()); - dump_sigspec(f, sig_arst); - f << stringf(" = '%c'", pol_arst ? '1' : '0'); - f << stringf(" then\n"); - f << stringf("%s" " %s %s ", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); - dump_sigspec(f, val_arst); - f << stringf(";\n"); - f << stringf("%s" " elsif ", indent.c_str()); - } else { - f << stringf("%s" " if ", indent.c_str()); - } - f << stringf("%s_edge(", pol_clk ? "rising" : "falling"); - dump_sigspec(f, sig_clk); - f << stringf(") then\n"); - - if (cell->type == ID($dffe)) { - f << stringf("%s" " if (", indent.c_str()); - dump_sigspec(f, sig_en); - f << stringf(" = '%c'", pol_en ? '1' : '0'); - f << stringf(") then\n"); - f << stringf("%s" " %s %s ", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); + // Sensitivity list + std::set sensitivity_set; + if (ff.has_clk) { + sensitivity_set.insert(ff.sig_clk); } else { - f << stringf("%s" " %s %s ", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); + sensitivity_set.insert(ff.sig_d); + sensitivity_set.insert(ff.sig_en); } - - dump_cell_expr_port(f, cell, "D", false); - f << stringf(";\n"); - - if (cell->type == ID($dffe)) { - f << stringf("%s" " end if;\n", indent.c_str()); - } - f << stringf("%s" " end if;\n", indent.c_str()); - - if (!out_is_reg_wire) { - f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" <= %s;\n", reg_name.c_str()); + if (ff.has_sr) { + sensitivity_set.insert(ff.sig_clr); + sensitivity_set.insert(ff.sig_set); + } else if (ff.has_arst) { + sensitivity_set.insert(ff.sig_arst); } - f << stringf("%s" "end process;\n", indent.c_str()); - - return true; - } - - if (cell->type == ID($dlatch)) - { - RTLIL::SigSpec sig_en; - bool pol_en = false; - - sig_en = cell->getPort(ID::EN); - pol_en = cell->parameters[ID::EN_POLARITY].as_bool(); - - std::string reg_name = cellname(cell); - bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); - - std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; - f << stringf("%s" "process(", indent.c_str()); - dump_sigspec(f, sig_en); - f << stringf(", "); - // TODO: the following would break for nontrivial expressions - dump_cell_expr_port(f, cell, "D", false); + bool is_first_sensitivity_chunk = true; + for (RTLIL::SigChunk chunk: get_sensitivity_set(sensitivity_set)) { + if (!is_first_sensitivity_chunk) { + f << ", "; + } + dump_sigchunk(f, chunk); + is_first_sensitivity_chunk = false; + } f << stringf(") is\n"); if (!out_is_reg_wire) { - f << stringf("%s" "variable %s: std_logic_vector (%d downto0)", - indent.c_str(), - reg_name.c_str(), cell->parameters[ID::WIDTH].as_int()-1); - dump_reg_init(f, cell->getPort(ID::Q)); + if (ff.width == 1) + f << stringf("%s" " variable %s: STD_LOGIC", indent.c_str(), reg_name.c_str()); + else + f << stringf("%s" " variable %s: STD_LOGIC_VECTOR (%d downto 0)", indent.c_str(), reg_name.c_str(), ff.width-1); + dump_reg_init(f, ff.sig_q); f << ";\n"; } f << stringf("%s" "begin\n", indent.c_str()); - f << stringf("%s" " if (", indent.c_str()); - dump_sigspec(f, sig_en); - f << stringf(" = '%c') then\n", pol_en ? '1' : '0'); + // 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; - f << stringf("%s" " %s %s ", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); - dump_cell_expr_port(f, cell, "D", false); - f << stringf(";\n"); - f << stringf("%s" " end if;\n", indent.c_str()); + for (int i = 0; i < chunks; i++) + { + SigSpec sig_d; + Const val_arst, val_srst; + std::string reg_bit_name; + if (chunky) { + reg_bit_name = stringf("%s(%d)", reg_name.c_str(), i); + if (ff.has_d) + sig_d = ff.sig_d[i]; + } else { + reg_bit_name = reg_name; + if (ff.has_d) + sig_d = ff.sig_d; + } + 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; + + dump_attributes(f, indent, cell->attributes); + // TODO: replace dump_sigspec with dump_const when appropriate + // Cannot combine as much string gen because VHDL is stricter + // TODO: avoid code sharing when it decreases legibility + if (ff.has_clk) + { + // FFs. + f << stringf("%s" " if ", indent.c_str()); + // Generate asynchronous behavior syntax + if (ff.has_sr) { + // Async reset in SR pair + dump_sigspec(f, ff.sig_clr[i]); + f << stringf(" = '%c' then\n", ff.pol_clr ? '1' : '0'); + f << stringf("%s ", indent.c_str()); + f << stringf("%s %s '0';\n", reg_bit_name.c_str(), assignment_operator.c_str()); + // Async set in SR pair + f << stringf("%s" " elsif ", indent.c_str()); + dump_sigspec(f, ff.sig_set[i]); + f << stringf(" = '%c' then\n", ff.pol_set ? '1' : '0'); + f << stringf("%s ", indent.c_str()); + f << stringf("%s %s '1';\n", reg_bit_name.c_str(), assignment_operator.c_str()); + f << stringf("%s" " elsif ", indent.c_str()); + } else if (ff.has_arst) { + dump_sigspec(f, ff.sig_arst); + f << stringf(" = '%c' then\n", ff.pol_arst ? '1' : '0'); + f << stringf("%s ", indent.c_str()); + f << stringf("%s %s '0';\n", reg_bit_name.c_str(), assignment_operator.c_str()); + f << stringf("%s" " elsif ", indent.c_str()); + } + f << stringf("%s_edge(", ff.pol_clk ? "rising" : "falling"); + dump_sigspec(f, ff.sig_clk); + f << stringf(") then\n"); + // ff.ce_over_srst means sync-reset is also gated by enable + if (ff.has_srst && ff.has_en && ff.ce_over_srst) { + f << stringf("%s" " if (", indent.c_str()); + dump_sigspec(f, ff.sig_en); + f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); + f << stringf("%s" " if (", indent.c_str()); + dump_sigspec(f, ff.sig_srst); + f << stringf(" = '%c' then\n", ff.pol_srst ? '1' : '0'); + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + dump_sigspec(f, val_srst); + f << stringf(";\n"); + f << stringf("%s" " else\n", indent.c_str()); + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + dump_sigspec(f, sig_d); + f << stringf("%s" " end if;\n", indent.c_str()); + f << stringf("%s" " end if;\n", indent.c_str()); + } else { + // Check this + if (ff.has_srst) { + f << stringf("%s" " if (", indent.c_str()); + dump_sigspec(f, ff.sig_srst); + f << stringf(") then\n"); + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + dump_sigspec(f, val_srst); + f << stringf(";\n"); + } + if (ff.has_en) { + f << stringf("%s" " %s (", indent.c_str(), ff.has_srst ? "elsif" : "if"); + dump_sigspec(f, ff.sig_en); + f << stringf(") then\n"); + } + if (ff.has_srst || ff.has_en) { + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + dump_sigspec(f, sig_d); + f << stringf(";\n"); + f << stringf("%s" " end if;\n", indent.c_str()); + } else { + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + dump_sigspec(f, sig_d); + f << stringf(";\n"); + } + } + f << stringf("%s" " end if;\n", indent.c_str()); + } + else + { + // 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_d) + 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_d) + f << stringf("%s" " else ", indent.c_str()); + } + if (ff.has_d) { + f << stringf("if (%s", ff.pol_en ? "" : "!"); + dump_sigspec(f, ff.sig_en); + f << stringf(") %s = ", reg_bit_name.c_str()); + dump_sigspec(f, sig_d); + f << stringf(";\n"); + } + } + } + // Group inside process for readability if (!out_is_reg_wire) { - f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Q)); + f << stringf("%s ", indent.c_str()); + dump_sigspec(f, ff.sig_q); f << stringf(" <= %s;\n", reg_name.c_str()); } + f << stringf("%s" "end process;\n", indent.c_str()); return true; From 98507ffb2961cbe06f0d32033e934c4bce13b15b Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 10 Aug 2020 18:11:14 -0700 Subject: [PATCH 59/90] When dumping a PSL cover statement, use a Sequence (Braced-SERE) --- src/vhdl_backend.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 11e92dfd..e8779e64 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1717,9 +1717,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } else { f << stringf("%s" "-- psl %s ", indent.c_str(), cell->type.c_str()+1); if (cell->type != ID($cover)) { - f << stringf("always "); + f << stringf("always (%s -> %s);\n", + en_str.c_str(), a_str.c_str()); + } else { + /* + * PSL cover statements require a Sequence (PSL 2005 7.1.6) + * Construct a one-long sequence as a Braced SERE + */ + f << stringf("{%s -> %s};\n", en_str.c_str(), a_str.c_str()); } - f << stringf("(%s -> %s);\n", en_str.c_str(), a_str.c_str()); } return true; } From a319f1ab8fb6550d32b5527df90692a611c670ac Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 13 Aug 2020 18:02:18 -0700 Subject: [PATCH 60/90] Port latch generation code as well This still needs testing, especially since opt_dff does not do latch transforms (yet) --- src/vhdl_backend.cc | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index e8779e64..06599505 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1243,7 +1243,6 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(ff.sig_q, reg_name); std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; - bool systemverilog = false; // temp // Sensitivity list std::set sensitivity_set; @@ -1383,34 +1382,43 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) else { // Latches. - f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*"); - f << stringf("%s" " ", indent.c_str()); + // Assumption that at least one of these ifs will be hit + // Awkward "els" is first half of "elsif" if (ff.has_sr) { - f << stringf("if (%s", ff.pol_clr ? "" : "!"); + f << stringf("if "); 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 ? "" : "!"); + f << stringf(" = '%c' then\n", ff.pol_clr ? '1' : '0'); + f << stringf("%s" " %s %s '0';\n", indent.c_str(), + reg_bit_name.c_str(), assignment_operator.c_str()); + f << stringf("%s" " elsif ", indent.c_str()); dump_sigspec(f, ff.sig_set[i]); - f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str()); + f << stringf(" = '%c' then\n", ff.pol_set ? '1' : '0'); + f << stringf("%s" " %s %s '1';\n", indent.c_str(), + reg_bit_name.c_str(), assignment_operator.c_str()); if (ff.has_d) - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " els", indent.c_str()); } else if (ff.has_arst) { - f << stringf("if (%s", ff.pol_arst ? "" : "!"); + f << stringf("if ("); dump_sigspec(f, ff.sig_arst); - f << stringf(") %s = ", reg_bit_name.c_str()); + f << stringf(" = '%c' then\n", ff.pol_arst ? '1' : '0'); + f << stringf("%s" " %s %s ", indent.c_str(), + reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, val_arst); f << stringf(";\n"); if (ff.has_d) - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " els", indent.c_str()); } if (ff.has_d) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); + f << stringf("if "); dump_sigspec(f, ff.sig_en); - f << stringf(") %s = ", reg_bit_name.c_str()); + f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); + f << stringf("%s" " %s %s ", indent.c_str(), + reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, sig_d); f << stringf(";\n"); } + f << stringf("%s" " end if;\n", indent.c_str()); } } From ef2aea4e7fe1f84e1df7c46cc231433074cff80e Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 13 Aug 2020 19:19:26 -0700 Subject: [PATCH 61/90] Remove obsolete commented-out code for dumping FFs --- src/vhdl_backend.cc | 121 -------------------------------------------- 1 file changed, 121 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 06599505..8d22bcde 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -714,127 +714,6 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - /*if (cell->type.begins_with("$_DFF_")) - { - std::string reg_name = cellname(cell); - bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name); - - std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; - - // Sensitivity list - // [6] == P is rising - // [7] != _ is asynchronous reset - dump_attributes(f, indent, cell->attributes); - f << stringf("%s" "process(", indent.c_str()); - // TODO: implicit assumptions of width 1 - dump_sigspec(f, cell->getPort(ID::C)); - if (cell->type[7] != '_') { - f << stringf(", "); - dump_sigspec(f, cell->getPort(ID::R)); - } - f << stringf(") is\n"); - // TODO: get rid of intermediate variable? - if (!out_is_reg_wire) { - f << stringf("%s" " variable %s: STD_LOGIC ", indent.c_str(), reg_name.c_str()); - dump_reg_init(f, cell->getPort(ID::Q)); - f << "\n"; - } - f << stringf("%s" "begin\n", indent.c_str()); - - // Actual edge checking logic (clock, async reset if present) - if (cell->type[7] != '_') { - // async reset - f << stringf("%s" " if ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::R)); - f << stringf(" = '%c' then\n", cell->type[7] == 'P' ? '1' : '0'); - f << stringf("%s" " %s %s '%c';\n", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str(), cell->type[8]); - } - // clock edge - f << stringf("%s" " %s %s_edge(", indent.c_str(), - cell->type[7] != '_' ? "elsif" : "if", - cell->type[6] == 'P' ? "rising" : "falling"); - dump_sigspec(f, cell->getPort(ID::C)); - f << stringf(") then\n"); - - f << stringf("%s" " %s %s ", indent.c_str(), - assignment_operator.c_str(), reg_name.c_str()); - dump_cell_expr_port(f, cell, "D", false); - f << stringf(";\n"); - f << stringf("%s" " end if;\n", indent.c_str()); - - if (!out_is_reg_wire) { - f << stringf("%s ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" <= %s;\n", reg_name.c_str()); - } - f << stringf("%s" "end process;\n", indent.c_str()); - - return true; - } - - if (cell->type.begins_with("$_DFFSR_")) - { - 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(ID::Q), reg_name); - - std::string assignment_operator = out_is_reg_wire ? "<=" : ":="; - - // Sensitivity list - dump_attributes(f, indent, cell->attributes); - f << stringf("%s" "process(", indent.c_str()); - // TODO: implicit assumption of width 1 - dump_sigspec(f, cell->getPort(ID::C)); - f << stringf(", "); - dump_sigspec(f, cell->getPort(ID::R)); - f << stringf(", "); - dump_sigspec(f, cell->getPort(ID::S)); - f << stringf(") is\n"); - // TODO: get rid of intermediate variable? - if (!out_is_reg_wire) { - f << stringf("%s" " variable %s: STD_LOGIC ", indent.c_str(), reg_name.c_str()); - dump_reg_init(f, cell->getPort(ID::Q)); - f << "\n"; - } - f << stringf("%s" "begin\n", indent.c_str()); - - // reset - f << stringf("%s" " if ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::R)); - f << stringf("%s" " = '%c' then\n", indent.c_str(), - pol_r == 'P' ? '1' : '0'); - f << stringf("%s" " %s %s '0'", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); - // set - f << stringf("%s" " elsif ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::S)); - f << stringf("%s" " = '%c' then\n", indent.c_str(), - pol_s == 'P' ? '1' : '0'); - f << stringf("%s" " %s %s '1'", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); - // clock edge - f << stringf("%s" " elsif %s_edge(", indent.c_str(), - pol_c == 'P' ? "rising" : "falling"); - dump_sigspec(f, cell->getPort(ID::C)); - f << stringf(") then\n"); - - f << stringf("%s" " %s %s ", indent.c_str(), - reg_name.c_str(), assignment_operator.c_str()); - dump_cell_expr_port(f, cell, "D", false); - f << stringf(";\n"); - f << stringf("%s" " end if;\n", indent.c_str()); - - if (!out_is_reg_wire) { - f << stringf("%s ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Q)); - f << stringf(" <= %s;\n", reg_name.c_str()); - } - - return true; - }*/ - #define HANDLE_UNIOP(_type, _operator, _is_arith) \ if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator, _is_arith); return true; } #define HANDLE_BINOP(_type, _operator, _is_arith) \ From f6a24ef0f351aae0cb42552cda5297b5c7d42ec4 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 25 Sep 2020 22:50:02 -0700 Subject: [PATCH 62/90] Remove defparam option Code for dumping modules still needs to be ported --- src/vhdl_backend.cc | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 8d22bcde..8da46d68 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -34,7 +34,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, siminit; +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; std::set reg_wires; @@ -1649,7 +1649,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_attributes(f, indent, cell->attributes); f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str()); - if (!defparam && cell->parameters.size() > 0) { + if (cell->parameters.size() > 0) { f << stringf(" #("); for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { if (it != cell->parameters.begin()) @@ -1699,14 +1699,6 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) } f << stringf("\n%s" ");\n", indent.c_str()); - if (defparam && cell->parameters.size() > 0) { - for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { - f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str()); - dump_const(f, it->second); - f << stringf(";\n"); - } - } - 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(ID::Q)); @@ -2086,10 +2078,6 @@ struct VHDLBackend : public Backend { 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(" cell parameters.\n"); - log("\n"); log(" -blackboxes\n"); log(" usually modules with the 'blackbox' attribute are ignored. with\n"); log(" this option set only the modules with the 'blackbox' attribute\n"); @@ -2125,7 +2113,6 @@ struct VHDLBackend : public Backend { nohex = false; nostr = false; extmem = false; - defparam = false; siminit = false; auto_prefix = "n"; @@ -2175,10 +2162,6 @@ struct VHDLBackend : public Backend { extmem_counter = 1; continue; } - if (arg == "-defparam") { - defparam = true; - continue; - } if (arg == "-siminit") { siminit = true; continue; From f844b440a2e23ac1fa5d60104eb7d989d876b9bc Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 25 Sep 2020 22:51:06 -0700 Subject: [PATCH 63/90] Copy a change from the Verilog backend --- src/vhdl_backend.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 8da46d68..e8530933 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -882,21 +882,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(" = "); if (cell->getParam(ID::B_SIGNED).as_bool()) { - f << stringf("$signed("); - dump_sigspec(f, cell->getPort(ID::B)); - f << stringf(")"); + dump_cell_expr_port(f, cell, "B", true); f << stringf(" < 0 ? "); - dump_sigspec(f, cell->getPort(ID::A)); + dump_cell_expr_port(f, cell, "A", true); f << stringf(" << - "); dump_sigspec(f, cell->getPort(ID::B)); f << stringf(" : "); - dump_sigspec(f, cell->getPort(ID::A)); + dump_cell_expr_port(f, cell, "A", true); f << stringf(" >> "); dump_sigspec(f, cell->getPort(ID::B)); } else { - dump_sigspec(f, cell->getPort(ID::A)); + dump_cell_expr_port(f, cell, "A", true); f << stringf(" >> "); dump_sigspec(f, cell->getPort(ID::B)); } From cf1341ff2d7eb1da1be65aecbeb54687a1c8cb12 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 9 Oct 2020 11:18:56 -0700 Subject: [PATCH 64/90] Create std08 command line option --- src/vhdl_backend.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index e8530933..13271792 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -34,7 +34,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit; +bool std08, verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; std::set reg_wires; @@ -2030,6 +2030,9 @@ struct VHDLBackend : public Backend { log("\n"); log("Write the current design to a VHDL file (WIP).\n"); log("\n"); + log(" -std08\n"); + log(" use some VHDL-2008 syntax for more readable code\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"); @@ -2102,6 +2105,7 @@ struct VHDLBackend : public Backend { { // PORTING TOP COMPLETE, SUBROUTINES IN PROGRESS log_header(design, "Executing VHDL backend.\n"); + std08 = false; verbose = false; norename = false; noattr = false; @@ -2123,6 +2127,10 @@ struct VHDLBackend : public Backend { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; + if (arg == "-std08") { + std08 = true; + continue; + } if (arg == "-norename") { norename = true; continue; From 1e2dd13ae3ac454e209f19ddee1224a78560b864 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 9 Oct 2020 11:19:56 -0700 Subject: [PATCH 65/90] VHDL-93 compliant hex constants when -std08 not specified --- src/vhdl_backend.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 13271792..bdf2999d 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -302,6 +302,10 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o if (GetSize(bin_digits) <= 1) goto dump_bin; bool not_clean_hex = GetSize(bin_digits) % 4 != 0; + // TODO: double-check VHDL-93 language standard + if (not_clean_hex && !std08) { + goto dump_bin; + } while (GetSize(bin_digits) % 4 != 0) bin_digits.push_back('0'); for (int i = 0; i < GetSize(bin_digits); i += 4) @@ -331,7 +335,6 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); } - // TODO: make this produce VHDL-93 compliant code if (not_clean_hex) { f << stringf("%d", width); } From 1cd293d0235dc357d81c57277fce5400f320b4f1 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 9 Oct 2020 11:29:56 -0700 Subject: [PATCH 66/90] More updates on dumping FFs --- src/vhdl_backend.cc | 78 ++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index bdf2999d..eff1cfd1 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -226,6 +226,19 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) return std::string(str); } +/* + * Generate common syntax of comparing STD_LOGIC to a constant + * Bit conversion function (for conversion from SigSpecs) has width assertion + */ +std::string sigbit_equal_bool(RTLIL::SigBit sigBit, bool const_compare) { + // Assert that sigBit is not const + log_assert(sigBit.wire != NULL); + // TODO: Internal testing, delete + //log_assert(sigSpec.as_chunk().wire->name =) + return stringf("%s = '%c'", + id(sigBit.wire->name).c_str(), const_compare ? '1' : '0'); +} + bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) { // PORTING NEEDS TESTING if (!sig.is_chunk() || sig.as_chunk().wire == NULL) @@ -1113,7 +1126,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) * Entire FF group shares a cell type so a single sensitivity list suffices */ if (RTLIL::builtin_ff_cell_types().count(cell->type)) - { // porting in progress + { // porting needs testing FfData ff(nullptr, cell); // $ff / $_FF_ cell: not supported. @@ -1194,20 +1207,25 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // Generate asynchronous behavior syntax if (ff.has_sr) { // Async reset in SR pair - dump_sigspec(f, ff.sig_clr[i]); - f << stringf(" = '%c' then\n", ff.pol_clr ? '1' : '0'); + f << stringf("%s then\n", + sigbit_equal_bool(ff.sig_clr[i], ff.pol_clr).c_str()); + //dump_sigspec(f, ff.sig_clr[i]); + //f << stringf(" = '%c' then\n", ff.pol_clr ? '1' : '0'); f << stringf("%s ", indent.c_str()); f << stringf("%s %s '0';\n", reg_bit_name.c_str(), assignment_operator.c_str()); // Async set in SR pair f << stringf("%s" " elsif ", indent.c_str()); - dump_sigspec(f, ff.sig_set[i]); - f << stringf(" = '%c' then\n", ff.pol_set ? '1' : '0'); + f << stringf("%s then\n", + sigbit_equal_bool(ff.sig_set[i], ff.pol_set).c_str()); + //dump_sigspec(f, ff.sig_set[i]); + //f << stringf(" = '%c' then\n", ff.pol_set ? '1' : '0'); f << stringf("%s ", indent.c_str()); f << stringf("%s %s '1';\n", reg_bit_name.c_str(), assignment_operator.c_str()); f << stringf("%s" " elsif ", indent.c_str()); } else if (ff.has_arst) { - dump_sigspec(f, ff.sig_arst); - f << stringf(" = '%c' then\n", ff.pol_arst ? '1' : '0'); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_arst.as_bit(), ff.pol_arst).c_str()); + //dump_sigspec(f, ff.sig_arst); + //f << stringf(" = '%c' then\n", ff.pol_arst ? '1' : '0'); f << stringf("%s ", indent.c_str()); f << stringf("%s %s '0';\n", reg_bit_name.c_str(), assignment_operator.c_str()); f << stringf("%s" " elsif ", indent.c_str()); @@ -1218,11 +1236,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // ff.ce_over_srst means sync-reset is also gated by enable if (ff.has_srst && ff.has_en && ff.ce_over_srst) { f << stringf("%s" " if (", indent.c_str()); - dump_sigspec(f, ff.sig_en); - f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); + //dump_sigspec(f, ff.sig_en); + //f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); f << stringf("%s" " if (", indent.c_str()); - dump_sigspec(f, ff.sig_srst); - f << stringf(" = '%c' then\n", ff.pol_srst ? '1' : '0'); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_srst.as_bit(), ff.pol_srst).c_str()); + //dump_sigspec(f, ff.sig_srst); + //f << stringf(" = '%c' then\n", ff.pol_srst ? '1' : '0'); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, val_srst); f << stringf(";\n"); @@ -1232,19 +1252,21 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " end if;\n", indent.c_str()); f << stringf("%s" " end if;\n", indent.c_str()); } else { - // Check this + // Commented out stuff was broken if (ff.has_srst) { - f << stringf("%s" " if (", indent.c_str()); - dump_sigspec(f, ff.sig_srst); - f << stringf(") then\n"); + f << stringf("%s" " if ", indent.c_str()); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_srst.as_bit(), ff.pol_srst).c_str()); + //dump_sigspec(f, ff.sig_srst); + //f << stringf(" then\n"); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, val_srst); f << stringf(";\n"); } if (ff.has_en) { - f << stringf("%s" " %s (", indent.c_str(), ff.has_srst ? "elsif" : "if"); - dump_sigspec(f, ff.sig_en); - f << stringf(") then\n"); + f << stringf("%s" " %s ", indent.c_str(), ff.has_srst ? "elsif" : "if"); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); + //dump_sigspec(f, ff.sig_en); + //f << stringf(") then\n"); } if (ff.has_srst || ff.has_en) { f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); @@ -1267,21 +1289,24 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // Awkward "els" is first half of "elsif" if (ff.has_sr) { f << stringf("if "); - dump_sigspec(f, ff.sig_clr[i]); - f << stringf(" = '%c' then\n", ff.pol_clr ? '1' : '0'); + f << stringf("%s then \n", sigbit_equal_bool(ff.sig_clr[i], ff.pol_clr).c_str()); + //dump_sigspec(f, ff.sig_clr[i]); + //f << stringf(" = '%c' then\n", ff.pol_clr ? '1' : '0'); f << stringf("%s" " %s %s '0';\n", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); f << stringf("%s" " elsif ", indent.c_str()); - dump_sigspec(f, ff.sig_set[i]); - f << stringf(" = '%c' then\n", ff.pol_set ? '1' : '0'); + f << stringf("%s then \n", sigbit_equal_bool(ff.sig_set[i], ff.pol_set).c_str()); + //dump_sigspec(f, ff.sig_set[i]); + //f << stringf(" = '%c' then\n", ff.pol_set ? '1' : '0'); f << stringf("%s" " %s %s '1';\n", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); if (ff.has_d) f << stringf("%s" " els", indent.c_str()); } else if (ff.has_arst) { f << stringf("if ("); - dump_sigspec(f, ff.sig_arst); - f << stringf(" = '%c' then\n", ff.pol_arst ? '1' : '0'); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_arst.as_bit(), ff.pol_arst).c_str()); + //dump_sigspec(f, ff.sig_arst); + //f << stringf(" = '%c' then\n", ff.pol_arst ? '1' : '0'); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, val_arst); @@ -1291,8 +1316,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (ff.has_d) { f << stringf("if "); - dump_sigspec(f, ff.sig_en); - f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); + //dump_sigspec(f, ff.sig_en); + //f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, sig_d); From e01417618475a68afe9e1f496c8e14a0ab020a2f Mon Sep 17 00:00:00 2001 From: rlee287 Date: Fri, 9 Oct 2020 11:32:11 -0700 Subject: [PATCH 67/90] More UNIOP handling reduce_(and|or|bool|xn?or) should work perfectly fine now, but s?sh(l|r) is probably still broken --- src/vhdl_backend.cc | 107 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 94 insertions(+), 13 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index eff1cfd1..bc2c6e5f 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -746,19 +746,100 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) HANDLE_BINOP(ID($xor), "xor", false) HANDLE_BINOP(ID($xnor), "xnor", false) - // Cheat a bit and use the VHDL-2008 operators for now - // TODO: replace the generated VHDL-2008 code with 93-compatible code - HANDLE_UNIOP(ID($reduce_and), "and", false) - HANDLE_UNIOP(ID($reduce_or), "or", false) - HANDLE_UNIOP(ID($reduce_xor), "xor", false) - HANDLE_UNIOP(ID($reduce_xnor), "xnor", false) - HANDLE_UNIOP(ID($reduce_bool), "or", false) - - // TODO: port these - HANDLE_BINOP(ID($shl), "<<", false) - HANDLE_BINOP(ID($shr), ">>", false) - HANDLE_BINOP(ID($sshl), "<<<", true) - HANDLE_BINOP(ID($sshr), ">>>", true) + if (std08) { + HANDLE_UNIOP(ID($reduce_and), "and", false) + HANDLE_UNIOP(ID($reduce_or), "or", false) + HANDLE_UNIOP(ID($reduce_xor), "xor", false) + HANDLE_UNIOP(ID($reduce_xnor), "xnor", false) + HANDLE_UNIOP(ID($reduce_bool), "or", false) + } else { + if (cell->type == ID($reduce_and)) { + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); + f << stringf("'1' when "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", false, false); + // TODO: is "others" legal here? + f << stringf(" = (others => '1') else '0';\n"); + return true; + } + if (cell->type.in(ID($reduce_or), ID($reduce_bool))) { + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); + f << stringf("'0' when "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", false, false); + // TODO: is "others" legal here? + f << stringf(" = (others => '0') else '1';\n"); + return true; + } + if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); + if (cell->type == ID($reduce_xnor)) { + f << stringf("not ("); + } + SigSpec port_A_sig = cell->getPort(ID::A); + dump_sigspec(f, port_A_sig); + for (auto it = port_A_sig.begin(); it != port_A_sig.end(); ++it) { + if (it != port_A_sig.begin()) { + f << stringf(" xor "); + } + dump_sigspec(f, *it); + } + if (cell->type == ID($reduce_xnor)) { + f << stringf(")"); + } + f << stringf(";\n"); + return true; + } + } + + // TODO: These will break for nonconstant shifts in B + // Alternate {} aggregate: use always, or only for nonconstant B? + + // TODO: cell attributes on B(?) + /* + * Shift operator cells follow the Verilog behavior + * + * Avoid using built-in operators sll, sla, srl, sra, rol, and rar + * See https://jdebp.eu/FGA/bit-shifts-in-vhdl.html for explanation + */ + + // IEEE 1364-2005: no sign extension done on either of the left shifts + if (cell->type.in(ID($shl), ID($sshl))) { + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); + f << stringf("std_logic_vector("); + f << stringf("shift_left("); + dump_cell_expr_port(f, cell, "A", true, true); + f << stringf(", "); + dump_cell_expr_port(f, cell, "B", true, true); + f << stringf("));\n"); + return true; + } + /* + * Verilog: sign extension determined by >> vs >>> + * VHDL: sign extension determined by type of input + */ + if (cell->type.in(ID($shr), ID($sshr))) { + // Force cast to unsigned when not sign extending + bool sign_extend = (cell->type == ID($sshr)); + f << stringf("%s", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" <= "); + f << stringf("std_logic_vector("); + f << stringf("shift_right("); + dump_cell_expr_port(f, cell, "A", sign_extend, true); + f << stringf(", "); + dump_cell_expr_port(f, cell, "B", sign_extend, true); + f << stringf("));\n"); + return true; + } // TODO: port $eqx and $nex ("=" and "/=" return BIT, not STD_LOGIC) // TODO: use "?<" instead of "<" (and analogous) for others? From 95e3d1d8936e9d18154a38f35d6b2f1ee6f3ef62 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 12 Oct 2020 17:26:30 -0700 Subject: [PATCH 68/90] Remove spacing between chunk name and chunk range --- src/vhdl_backend.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index bc2c6e5f..00eddbbf 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -437,16 +437,16 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima f << stringf("%s", id(chunk.wire->name).c_str()); } else if (chunk.width == 1) { if (chunk.wire->upto) - f << stringf("%s (%d)", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + f << stringf("%s(%d)", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - f << stringf("%s (%d)", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); + f << stringf("%s(%d)", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); } else { if (chunk.wire->upto) - f << stringf("%s (%d to %d)", id(chunk.wire->name).c_str(), + f << stringf("%s(%d to %d)", id(chunk.wire->name).c_str(), (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - f << stringf("%s (%d downto %d)", id(chunk.wire->name).c_str(), + f << stringf("%s(%d downto %d)", id(chunk.wire->name).c_str(), (chunk.offset + chunk.width - 1) + chunk.wire->start_offset, chunk.offset + chunk.wire->start_offset); } From 3b039c3562c65951d85fd55d94b319f0fee8f29f Mon Sep 17 00:00:00 2001 From: rlee287 Date: Mon, 12 Oct 2020 17:27:35 -0700 Subject: [PATCH 69/90] Replace "unclocked assertions" warning with "unclocked directives" This is more accurate, especially since $assert is normal assert now --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 00eddbbf..9f988828 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1694,7 +1694,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type != ID($assert)) { log_warning("Cell of type %s will be dumped as a PSL comment\n", cell->type.c_str()+1); - log("PSL unclocked assertions do not work (yet) with GHDL\n"); + log("PSL unclocked directives do not work (yet) with GHDL\n"); } log_experimental("Formal cells as asserts/PSL comments"); std::stringstream en_sstream; From 506cc97d96df827a83fae3c53c2ac1e3e49c6c4b Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 1 Nov 2020 18:57:37 -0800 Subject: [PATCH 70/90] Rename dump_memory to dump_memory_types Prepare to bring in new refactoring of memory dumping from Verilog backend --- src/vhdl_backend.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 9f988828..92c7be19 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -517,7 +517,9 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) } } -void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) +// Verilog backend's dump_memory is split into two functions +// TODO: actually copy over the other one +void dump_memory_types(std::ostream &f, std::string indent, RTLIL::Memory *memory) { // PORTING NEEDS TESTING size_t is_element_present = memory_array_types.count(memory->width); std::string memory_type_name = stringf("array_type_%d",memory->width); @@ -2103,7 +2105,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) dump_wire(f, indent + " ", w); for (auto it = module->memories.begin(); it != module->memories.end(); ++it) - dump_memory(f, indent + " ", it->second); + dump_memory_types(f, indent + " ", it->second); f << stringf("%s" "begin\n", indent.c_str()); for (auto cell : module->cells()) dump_cell(f, indent + " ", cell); From 61f9b1fd46f0397d91d01786fce01400fd1ce81a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 16 Dec 2020 14:02:16 -0800 Subject: [PATCH 71/90] Include GHDL plugin version in generated VHDL output --- Makefile | 4 +++- src/vhdl_backend.cc | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7798c45f..f92eed42 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ # Name or path to the ghdl executable. GHDL=ghdl +GIT_VERSION_RAW := "$(shell git describe --dirty --always --tags)" +GIT_VERSION = '$(GIT_VERSION_RAW)' YOSYS_CONFIG=yosys-config SOEXT=so @@ -14,7 +16,7 @@ LIBGHDL_INC:=$(shell $(GHDL) --libghdl-include-dir) ALL_LDFLAGS=$(LIBGHDL_LIB) -Wl,-rpath,$(dir $(LIBGHDL_LIB)) $(LDFLAGS) -ALL_CFLAGS=-fPIC -DYOSYS_ENABLE_GHDL -I$(LIBGHDL_INC) $(CFLAGS) +ALL_CFLAGS=-fPIC -DYOSYS_ENABLE_GHDL -DVERSION=$(GIT_VERSION) -I$(LIBGHDL_INC) $(CFLAGS) all: ghdl.$(SOEXT) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 92c7be19..63e02d70 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -2311,7 +2311,11 @@ struct VHDLBackend : public Backend { design->sort(); - *f << stringf("-- Generated by %s\n", yosys_version_str); + *f << stringf("-- Generated by:\n"); + // Already contains string "Yosys" + *f << stringf("-- %s\n",yosys_version_str); + *f << stringf("-- GHDL-Yosys-Plugin %s\n", VERSION); + // TODO: check all log calls to see if \n is needed log_experimental("VHDL backend"); write_header_imports(*f, ""); for (auto module : design->modules()) { From 626a4c059c0f4c44afa6b57210e19daf2111b05e Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 16 Dec 2020 21:48:15 -0800 Subject: [PATCH 72/90] Improve handling of $assert and friends --- src/vhdl_backend.cc | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 63e02d70..48fccfb1 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1703,25 +1703,51 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::stringstream a_sstream; // Not actually arbitrary haha string en_str; string a_str; // [A]rticle of interest - dump_sigspec(en_sstream, cell->getPort(ID::EN)); - dump_sigspec(a_sstream, cell->getPort(ID::A)); + SigSpec en_sigspec = cell->getPort(ID::EN); + dump_sigspec(en_sstream, en_sigspec); + /* + * en_sigspec should be exactly one bit wide + * Use this to skip the hypothesis portion of the implication + * (A false hypothesis causes the property to be a tautology) + */ + bool en_const_on = en_sigspec.is_fully_ones(); en_str = en_sstream.str(); + + dump_sigspec(a_sstream, cell->getPort(ID::A)); a_str = a_sstream.str(); + // TODO: special handling for asserts of x->'1'? if (cell->type == ID($assert)) { - f << stringf("%s" "assert (not %s) or %s;", indent.c_str(), - en_str.c_str(),a_str.c_str()); - f << stringf(" -- %s -> %s\n", en_str.c_str(), a_str.c_str()); + if (en_const_on) { + f << stringf("%s" "assert %s;\n", indent.c_str(), + a_str.c_str()); + } else { + f << stringf("%s" "assert (not %s) or %s;", indent.c_str(), + en_str.c_str(), a_str.c_str()); + f << stringf(" -- %s -> %s\n", en_str.c_str(), a_str.c_str()); + } } else { - f << stringf("%s" "-- psl %s ", indent.c_str(), cell->type.c_str()+1); + f << stringf("%s", indent.c_str()); + if (!std08 && cell->type != ID($assert)) { + // PSL comment that GHDL interprets with -fpsl + f << stringf("-- psl "); + } + f << stringf("%s ", cell->type.c_str()+1); + const char* property_impl_str; + if (en_const_on) { + property_impl_str = a_str.c_str(); + } else { + property_impl_str = stringf("%s -> %s", + en_str.c_str(), a_str.c_str()).c_str(); + } if (cell->type != ID($cover)) { - f << stringf("always (%s -> %s);\n", - en_str.c_str(), a_str.c_str()); + f << stringf("always (%s);\n", + property_impl_str); } else { /* * PSL cover statements require a Sequence (PSL 2005 7.1.6) * Construct a one-long sequence as a Braced SERE */ - f << stringf("{%s -> %s};\n", en_str.c_str(), a_str.c_str()); + f << stringf("{%s};\n", property_impl_str); } } return true; From cac3f480a24ca22e206e6b8891bd7b36b5a955a9 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 16 Dec 2020 21:51:18 -0800 Subject: [PATCH 73/90] Update various comments in VHDL backend code --- src/vhdl_backend.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 48fccfb1..a139e0c4 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -540,6 +540,7 @@ void dump_memory_types(std::ostream &f, std::string indent, RTLIL::Memory *memor range_str.c_str()); } +// TODO: document results of gen_signed, gen_unsigned void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true, bool gen_unsigned = false) { // PORTING NEEDS TESTING SigSpec signal_spec = cell->getPort("\\" + port); @@ -844,6 +845,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } // TODO: port $eqx and $nex ("=" and "/=" return BIT, not STD_LOGIC) + // TODO: ?= and friends may be a VHDL-2008 addition? + // TODO: unported elements // TODO: use "?<" instead of "<" (and analogous) for others? HANDLE_BINOP(ID($lt), "<", true) HANDLE_BINOP(ID($le), "<=", true) @@ -859,7 +862,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) HANDLE_BINOP(ID($mul), "*", true) HANDLE_BINOP(ID($div), "/", true) HANDLE_BINOP(ID($mod), "mod", true) - HANDLE_BINOP(ID($pow), "**", true) // unported + HANDLE_BINOP(ID($pow), "**", true) if (cell->type == ID($logic_not)) { // TODO: use VHDL-93 compliant syntax @@ -941,7 +944,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($modfloor)) - { // unported for now + { // unported for now (rem?) // wire truncated = $signed(A) % $signed(B); // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated); @@ -2152,6 +2155,7 @@ void write_header_imports(std::ostream &f, std::string indent) { f << indent << "library IEEE;\n"; f << indent << "use IEEE.STD_LOGIC_1164.ALL;\n"; + // Could scan for arithmetic-type cells, but this is too cumbersome f << indent << "use IEEE.NUMERIC_STD.ALL;\n"; if (extmem) { f << indent << "\nuse STD.TEXTIO.ALL\n"; From 613f28297c7ea488ce8525961508faf1d7e6ea62 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 17 Dec 2020 10:37:01 -0800 Subject: [PATCH 74/90] Adjust emitting of ?= and write explanatory comments --- src/vhdl_backend.cc | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index a139e0c4..b828b81e 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -844,14 +844,25 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - // TODO: port $eqx and $nex ("=" and "/=" return BIT, not STD_LOGIC) - // TODO: ?= and friends may be a VHDL-2008 addition? - // TODO: unported elements - // TODO: use "?<" instead of "<" (and analogous) for others? + /* + * TODO: port $eqx and $nex + * "=" and "/=" return BIT, not STD_LOGIC, so check what type conversions are needed + * ?= and friends are a VHDL-2008 addition + * TODO: use "?<" instead of "<" (and analogous) for others in 2008 mode? + * TODO: Find a way to create ?= behavior in VHDL-1993 + * = and /= for $eq and $ne are wrong (though hitting the cases where the differ in real code is unlikely) + * + * TODO: misc unported elements + */ HANDLE_BINOP(ID($lt), "<", true) HANDLE_BINOP(ID($le), "<=", true) - HANDLE_BINOP(ID($eq), "?=", false) - HANDLE_BINOP(ID($ne), "?/=", false) + if (std08) { + HANDLE_BINOP(ID($eq), "?=", false) + HANDLE_BINOP(ID($ne), "?/=", false) + } else { + HANDLE_BINOP(ID($eq), "=", false) + HANDLE_BINOP(ID($ne), "/=", false) + } HANDLE_BINOP(ID($eqx), "=", false) HANDLE_BINOP(ID($nex), "/=", false) HANDLE_BINOP(ID($ge), ">=", true) From 14e95622ec70838177123dff004715223030866b Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 17 Dec 2020 11:23:23 -0800 Subject: [PATCH 75/90] Port over Mem helper changes from Verilog backend FF sensitivity lists not ported; see code comment for explanation --- src/vhdl_backend.cc | 563 ++++++++++++++++++++++---------------------- 1 file changed, 281 insertions(+), 282 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b828b81e..0e5b40f1 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -18,6 +18,8 @@ /* * A VHDL backend based on the Verilog backend. + * Code structure here is regularly synced with the Verilog backend, except where noted + * Last synced against Yosys d9af3cadf */ #include "kernel/register.h" @@ -25,6 +27,7 @@ #include "kernel/log.h" #include "kernel/sigtools.h" #include "kernel/ff.h" +#include "kernel/mem.h" #include #include @@ -517,29 +520,273 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) } } -// Verilog backend's dump_memory is split into two functions -// TODO: actually copy over the other one -void dump_memory_types(std::ostream &f, std::string indent, RTLIL::Memory *memory) +// We split Verilog backend's dump_memory into two functions +void dump_memory_types(std::ostream &f, std::string indent, Mem &mem) { // PORTING NEEDS TESTING - size_t is_element_present = memory_array_types.count(memory->width); - std::string memory_type_name = stringf("array_type_%d",memory->width); + size_t is_element_present = memory_array_types.count(mem.width); + std::string memory_type_name = stringf("array_type_%d",mem.width); if (!is_element_present) { - memory_array_types.insert(memory->width); + memory_array_types.insert(mem.width); f << stringf("%s" "type %s is array (natural range <>) of std_logic_vector(%d downto 0)\n", - indent.c_str(), memory_type_name.c_str(), memory->width-1); + indent.c_str(), memory_type_name.c_str(), mem.width-1); } - dump_attributes(f, indent, memory->attributes); + dump_attributes(f, indent, mem.attributes); // TODO: if memory->size is always positive then this is unnecessary std::string range_str = stringf("(%d %s %d)", - memory->start_offset+memory->size-1, - memory->size>=0 ? "downto" : "to", memory->start_offset); + mem.start_offset+mem.size-1, + mem.size>=0 ? "downto" : "to", mem.start_offset); // TODO: memory initialization? f << stringf("%s" "signal %s: %s %s;\n", indent.c_str(), - id(memory->name).c_str(), memory_type_name.c_str(), + id(mem.memid).c_str(), memory_type_name.c_str(), range_str.c_str()); } +// Function signature will change when porting +void dump_memory(std::ostream &f, std::string indent, Mem &mem) +{ // PORTING REQUIRED + std::string mem_id = id(mem.memid); + f << stringf("-- Memory cell %s\n", mem_id.c_str()); + + // 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 expressions within that clock domain + dict> clk_to_lof_body; + clk_to_lof_body[""] = std::vector(); + std::string clk_domain_str; + // create a list of reg declarations + std::vector 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( clk_to_lof_body.count(clk_domain_str) == 0 ) + clk_to_lof_body[clk_domain_str] = std::vector(); + } + if (!port.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", port.data.size() - 1, temp_id.c_str()) ); + { + std::ostringstream os; + if (port.en != RTLIL::SigBit(true)) + { + os << stringf("if ("); + dump_sigspec(os, port.en); + os << stringf(") "); + } + os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str()); + dump_sigspec(os, port.addr); + os << stringf("];\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + } + { + 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, temp_id.c_str()) ); + { + std::ostringstream os; + dump_sigspec(os, port.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); + } + { + std::ostringstream os; + dump_sigspec(os, port.data); + std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str()); + clk_to_lof_body[""].push_back(line); + } + } + } else { + // for non-clocked read-ports make something like: + // assign r_data = array_reg[r_addr]; + std::ostringstream os, os2; + dump_sigspec(os, port.data); + dump_sigspec(os2, port.addr); + std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); + clk_to_lof_body[""].push_back(line); + } + } + + // write ports + for (auto &port : mem.wr_ports) + { + { + std::ostringstream os; + dump_sigspec(os, port.clk); + clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str()); + if( clk_to_lof_body.count(clk_domain_str) == 0 ) + clk_to_lof_body[clk_domain_str] = std::vector(); + } + // make something like: + // always @(posedge clk) + // if (wr_en_bit) memid[w_addr][??] <= w_data[??]; + // ... + for (int i = 0; i < GetSize(port.en); i++) + { + int start_i = i, width = 1; + SigBit wen_bit = port.en[i]; + + while (i+1 < GetSize(port.en) && active_sigmap(port.en[i+1]) == active_sigmap(wen_bit)) + i++, width++; + + if (wen_bit == State::S0) + continue; + + std::ostringstream os; + if (wen_bit != State::S1) + { + os << stringf("if ("); + dump_sigspec(os, wen_bit); + os << stringf(") "); + } + os << stringf("%s[", mem_id.c_str()); + dump_sigspec(os, port.addr); + if (width == GetSize(port.en)) + os << stringf("] <= "); + else + os << stringf("][%d:%d] <= ", i, start_i); + dump_sigspec(os, port.data.extract(start_i, width)); + os << stringf(";\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + } + } + // 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 ® : 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 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()); + } + else + { + // the non-clocked assignments + for(auto &line : lof_lines) + f << stringf("%s" "%s", indent.c_str(), line.c_str()); + } + } +} + // TODO: document results of gen_signed, gen_unsigned void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true, bool gen_unsigned = false) { // PORTING NEEDS TESTING @@ -1274,6 +1521,12 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) int chunks = ff.has_sr ? ff.width : 1; bool chunky = ff.has_sr && ff.width != 1; + /* + * Changes from Yosys 8f1d53e6 were not copied over + * Their rationale doesn't seem to apply here? + * If there is a bug in dumping FFs here but not in Verilog, + * re-examine the above commit as a first troubleshooting step + */ for (int i = 0; i < chunks; i++) { SigSpec sig_d; @@ -1437,274 +1690,6 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type == ID($mem)) - { // unported for now - RTLIL::IdString memid = cell->parameters[ID::MEMID].decode_string(); - std::string mem_id = id(cell->parameters[ID::MEMID].decode_string()); - int abits = cell->parameters[ID::ABITS].as_int(); - int size = cell->parameters[ID::SIZE].as_int(); - int offset = cell->parameters[ID::OFFSET].as_int(); - int width = cell->parameters[ID::WIDTH].as_int(); - bool use_init = !(RTLIL::SigSpec(cell->parameters[ID::INIT]).is_fully_undef()); - - // for memory block make something like: - // reg [7:0] memid [3:0]; - // initial begin - // memid[0] = ... - // end - dump_attributes(f, indent.c_str(), cell->attributes); - 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) - { - 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 - { - for (int i=0; iparameters[ID::INIT].extract(i*width, width); - for (int j=0; j expressions within that clock domain - dict> clk_to_lof_body; - clk_to_lof_body[""] = std::vector(); - std::string clk_domain_str; - // create a list of reg declarations - std::vector lof_reg_declarations; - - int nread_ports = cell->parameters[ID::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(ID::RD_CLK).extract(i); - sig_rd_en = cell->getPort(ID::RD_EN).extract(i); - sig_rd_data = cell->getPort(ID::RD_DATA).extract(i*width, width); - sig_rd_addr = cell->getPort(ID::RD_ADDR).extract(i*abits, abits); - use_rd_clk = cell->parameters[ID::RD_CLK_ENABLE].extract(i).as_bool(); - rd_clk_posedge = cell->parameters[ID::RD_CLK_POLARITY].extract(i).as_bool(); - rd_transparent = cell->parameters[ID::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(); - } - 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()) ); - { - 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()); - } - { - 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); - } - } - 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()) ); - { - 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); - } - { - 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); - } - } - } 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[ID::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(ID::WR_CLK).extract(i); - sig_wr_data = cell->getPort(ID::WR_DATA).extract(i*width, width); - sig_wr_addr = cell->getPort(ID::WR_ADDR).extract(i*abits, abits); - sig_wr_en = cell->getPort(ID::WR_EN).extract(i*width, width); - wr_clk_posedge = cell->parameters[ID::WR_CLK_POLARITY].extract(i).as_bool(); - { - 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(); - } - // 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; - - std::ostringstream os; - if (wen_bit != State::S1) - { - os << stringf("if ("); - dump_sigspec(os, wen_bit); - os << 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 ® : 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 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()); - } - else - { - // the non-clocked assignments - for(auto &line : lof_lines) - f << stringf("%s" "%s", indent.c_str(), line.c_str()); - } - } - - return true; - } - if (cell->type.in(ID($assert), ID($assume), ID($cover))) { if (cell->type != ID($assert)) { @@ -1784,13 +1769,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - // FIXME: $memrd, $memwr, $fsm + // FIXME: $fsm return false; } void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) { // PORTING REQUIRED + // Handled by dump_memory + if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit))) + return; + if (cell->type[0] == '$' && !noexpr) { if (dump_cell_expr(f, indent, cell)) return; @@ -2139,14 +2128,24 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) indent.c_str(), id(module->name, false).c_str()); // Architecture + /* port if needed + if (!systemverilog && !module->processes.empty()) + f << indent + " " << "reg " << id("\\initial") << " = 0;\n"; + */ f << stringf("%s" "architecture rtl of %s is\n", indent.c_str(), id(module->name, false).c_str()); for (auto w : module->wires()) dump_wire(f, indent + " ", w); - for (auto it = module->memories.begin(); it != module->memories.end(); ++it) - dump_memory_types(f, indent + " ", it->second); + for (auto &mem : Mem::get_all_memories(module)) + dump_memory_types(f, indent + " ", mem); + f << stringf("%s" "begin\n", indent.c_str()); + + for (auto &mem : Mem::get_all_memories(module)) + // TODO: fix once ported + dump_memory(f, indent + " ", mem); + for (auto cell : module->cells()) dump_cell(f, indent + " ", cell); From ea00e7be850dcbfc1908959c8cc10d3b60e2f181 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 17 Dec 2020 12:44:46 -0800 Subject: [PATCH 76/90] Port simple_lhs change from Verilog backend --- src/vhdl_backend.cc | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 0e5b40f1..b6bd9c7b 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -19,7 +19,7 @@ /* * A VHDL backend based on the Verilog backend. * Code structure here is regularly synced with the Verilog backend, except where noted - * Last synced against Yosys d9af3cadf + * Last synced against Yosys 871fc34a */ #include "kernel/register.h" @@ -37,7 +37,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool std08, verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit; +bool std08, verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit, simple_lhs; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; std::set reg_wires; @@ -1851,11 +1851,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) { // PORTING NEEDS TESTING - f << stringf("%s", 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", 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", indent.c_str()); + dump_sigspec(f, left); + f << stringf(" <= "); + dump_sigspec(f, right); + f << stringf(";\n"); + } } // This is a forward declaration @@ -2223,6 +2235,9 @@ struct VHDLBackend : 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"); @@ -2268,6 +2283,7 @@ struct VHDLBackend : public Backend { nostr = false; extmem = false; siminit = false; + simple_lhs = false; auto_prefix = "n"; bool blackboxes = false; @@ -2332,6 +2348,10 @@ struct VHDLBackend : public Backend { selected = true; continue; } + if (arg == "-simple-lhs") { + simple_lhs = true; + continue; + } if (arg == "-v") { verbose = true; continue; From c25d2abb455ac2ae178a1b760f17e70dc7a55570 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 20 Dec 2020 14:45:41 -0800 Subject: [PATCH 77/90] Replace -simple-lhs with -nosimple-lhs and only allow this in VHDL-2008 mode --- src/vhdl_backend.cc | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b6bd9c7b..edd6a807 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -37,7 +37,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool std08, verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit, simple_lhs; +bool std08, verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, siminit, nosimple_lhs; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map auto_name_map; std::set reg_wires; @@ -456,7 +456,7 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima } } -void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) +void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool lhs_mode=false) { // PORTING NEEDS TESTING if (GetSize(sig) == 0) { // TODO this is a null range that may not be handled correctly @@ -466,11 +466,18 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) if (sig.is_chunk()) { dump_sigchunk(f, sig.as_chunk()); } else { + // LHS mode is for the LHS of expressions like (a, b) <= c in VHDL-2008 mode + if (lhs_mode) { + f << stringf("("); + } for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { if (it != sig.chunks().rbegin()) - f << stringf(" & "); + f << stringf(lhs_mode ? ", " : " & "); dump_sigchunk(f, *it, true); } + if (lhs_mode) { + f << stringf(")"); + } } } @@ -1851,7 +1858,11 @@ 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) { // PORTING NEEDS TESTING - if (simple_lhs) { + /* + * Split LHS of connection by default + * Use VHDL-2008 style concatenation with -nosimple-lhs and -std08 + */ + if (!(nosimple_lhs && std08)) { int offset = 0; for (auto &chunk : left.chunks()) { f << stringf("%s", indent.c_str()); @@ -1863,7 +1874,7 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, } } else { f << stringf("%s", indent.c_str()); - dump_sigspec(f, left); + dump_sigspec(f, left, true); f << stringf(" <= "); dump_sigspec(f, right); f << stringf(";\n"); @@ -2235,8 +2246,8 @@ struct VHDLBackend : 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(" -nosimple-lhs\n"); + log(" Connection assignments with simple left hand side with concatenations. Only allowed with -std08.\n"); log("\n"); log(" -extmem\n"); log(" instead of initializing memories using assignments to individual\n"); @@ -2283,7 +2294,7 @@ struct VHDLBackend : public Backend { nostr = false; extmem = false; siminit = false; - simple_lhs = false; + nosimple_lhs = false; auto_prefix = "n"; bool blackboxes = false; @@ -2348,8 +2359,8 @@ struct VHDLBackend : public Backend { selected = true; continue; } - if (arg == "-simple-lhs") { - simple_lhs = true; + if (arg == "-nosimple-lhs") { + nosimple_lhs = true; continue; } if (arg == "-v") { @@ -2368,6 +2379,9 @@ struct VHDLBackend : public Backend { if (auto_prefix.length() == 0) { log_cmd_error("Prefix specified by -renameprefix must not be empty.\n"); } + if (nosimple_lhs && !std08) { + log_cmd_error("-nosimple-lhs is only allowed with -std08.\n"); + } design->sort(); From b7967d4f90be2f0a70f481624e3dc938e005e83d Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 20 Dec 2020 14:48:23 -0800 Subject: [PATCH 78/90] Put parentheses around output of dump_cell_expr_port in case concatenation happens All uses of dump_cell_expr_port are on the RHS of an expression --- src/vhdl_backend.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index edd6a807..475d3df7 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -799,6 +799,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b { // PORTING NEEDS TESTING SigSpec signal_spec = cell->getPort("\\" + port); bool signal_is_const = signal_spec.is_fully_const(); + bool signal_is_chunk = signal_spec.is_chunk(); if (gen_signed && !signal_is_const && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) { @@ -811,7 +812,15 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b dump_sigspec(f, signal_spec); f << stringf(")"); } else { + // Put parentheses around concatenation of nets + // Prevent operator precedence things, and make code more readable + if (!signal_is_chunk) { + f << stringf("("); + } dump_sigspec(f, signal_spec); + if (!signal_is_chunk) { + f << stringf(")"); + } } } From fcc110f5a744139350bd822f4dc798d927e0d47a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 20 Dec 2020 14:49:57 -0800 Subject: [PATCH 79/90] Fix lingering assignment = in post-synth cell expr dumping --- src/vhdl_backend.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 475d3df7..746d827f 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -901,7 +901,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($_NOT_)) { f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); + f << stringf(" <= "); f << stringf("not "); dump_attributes(f, "", cell->attributes, ' '); dump_cell_expr_port(f, cell, "A", false); @@ -912,7 +912,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); + f << stringf(" <= "); if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_))) f << stringf("not ("); dump_cell_expr_port(f, cell, "A", false); @@ -939,7 +939,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_attributes(f, "", cell->attributes, ' '); f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); + f << stringf(" <= "); dump_cell_expr_port(f, cell, "B", false); f << stringf(" when "); dump_cell_expr_port(f, cell, "S", false); @@ -954,7 +954,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_attributes(f, "", cell->attributes, ' '); f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = !("); + f << stringf(" <= !("); dump_cell_expr_port(f, cell, "B", false); f << stringf(" when "); dump_cell_expr_port(f, cell, "S", false); @@ -967,7 +967,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = not (("); + f << stringf(" <= not (("); dump_cell_expr_port(f, cell, "A", false); f << stringf(cell->type == ID($_AOI3_) ? " and " : " or "); dump_cell_expr_port(f, cell, "B", false); @@ -982,7 +982,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { f << stringf("%s", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = not (("); + f << stringf(" <= not (("); dump_cell_expr_port(f, cell, "A", false); f << stringf(cell->type == ID($_AOI4_) ? " and " : " or "); dump_cell_expr_port(f, cell, "B", false); From 40704257e75dd5bb1d10e4ef36e8fd12a27bb08e Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 20 Dec 2020 14:57:34 -0800 Subject: [PATCH 80/90] Propagate LHS concatenation mode to various cell_expr dumps TODO: concatenation handling may be broken outside of VHDL-2008 mode; handle this later --- src/vhdl_backend.cc | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 746d827f..487337b0 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -863,7 +863,7 @@ std::string cellname(RTLIL::Cell *cell) void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool is_arith_op = false) { // PORTING NEEDS TESTING f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); if (is_arith_op) { f << stringf("std_logic_vector("); @@ -880,7 +880,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, bool is_arith_op = false) { // PORTING NEEDS TESTING f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); if (is_arith_op) { f << stringf("std_logic_vector("); @@ -900,7 +900,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("-- Cell type is %s\n", cell->type.c_str()); if (cell->type == ID($_NOT_)) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); f << stringf("not "); dump_attributes(f, "", cell->attributes, ' '); @@ -911,7 +911,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_))) f << stringf("not ("); @@ -938,7 +938,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // TODO: attribute dumping was on B dump_attributes(f, "", cell->attributes, ' '); f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); dump_cell_expr_port(f, cell, "B", false); f << stringf(" when "); @@ -953,7 +953,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // TODO: attribute dumping was on B dump_attributes(f, "", cell->attributes, ' '); f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= !("); dump_cell_expr_port(f, cell, "B", false); f << stringf(" when "); @@ -966,7 +966,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= not (("); dump_cell_expr_port(f, cell, "A", false); f << stringf(cell->type == ID($_AOI3_) ? " and " : " or "); @@ -981,7 +981,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= not (("); dump_cell_expr_port(f, cell, "A", false); f << stringf(cell->type == ID($_AOI4_) ? " and " : " or "); @@ -1021,7 +1021,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } else { if (cell->type == ID($reduce_and)) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); f << stringf("'1' when "); dump_attributes(f, "", cell->attributes, ' '); @@ -1032,7 +1032,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($reduce_or), ID($reduce_bool))) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); f << stringf("'0' when "); dump_attributes(f, "", cell->attributes, ' '); @@ -1043,7 +1043,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); if (cell->type == ID($reduce_xnor)) { f << stringf("not ("); @@ -1078,7 +1078,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // IEEE 1364-2005: no sign extension done on either of the left shifts if (cell->type.in(ID($shl), ID($sshl))) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); f << stringf("std_logic_vector("); f << stringf("shift_left("); @@ -1096,7 +1096,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // Force cast to unsigned when not sign extending bool sign_extend = (cell->type == ID($sshr)); f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); f << stringf("std_logic_vector("); f << stringf("shift_right("); @@ -1142,7 +1142,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // TODO: use VHDL-93 compliant syntax // TODO: attributes were on port "A" f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); // Unary or only if signal is a vector bool need_unary_or = cell->getPort(ID::A).as_wire()->width > 1; @@ -1303,7 +1303,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // TODO: attribute dumping was on B dump_attributes(f, "", cell->attributes, ' '); f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); dump_sigspec(f, cell->getPort(ID::B)); f << stringf(" when "); @@ -1321,9 +1321,10 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) * This makes it easier to handle SigSpecs with multiple chunks * This is a deliberate break from the output of ghdl --synth * - * Could use (a, b) := c & d; instead, if this is valid VHDL-93 + * Could use (a, b) := c & d; instead in VHDL-2008 mode? * This would require informing the concatenation generators * about whether the expression is an LHS or RHS expression + * (Should be implemented already but check again before changing this) */ /* * TODO: remove assumption of downto @@ -1339,7 +1340,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(a_str_stream, cell->getPort(ID::A)); dump_sigspec(b_str_stream, cell->getPort(ID::B)); dump_sigspec(s_str_stream, cell->getPort(ID::S)); - dump_sigspec(y_str_stream, cell->getPort(ID::Y)); + dump_sigspec(y_str_stream, cell->getPort(ID::Y), true); std::string a_str, b_str, s_str, y_str; a_str = a_str_stream.str(); b_str = b_str_stream.str(); @@ -1429,7 +1430,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($tribuf)) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(" when "); @@ -1457,7 +1458,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($concat)) { f << stringf("%s", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); + dump_sigspec(f, cell->getPort(ID::Y), true); f << stringf(" <= "); dump_sigspec(f, cell->getPort(ID::B)); f << stringf(" & "); @@ -1697,7 +1698,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // Group inside process for readability if (!out_is_reg_wire) { f << stringf("%s ", indent.c_str()); - dump_sigspec(f, ff.sig_q); + dump_sigspec(f, ff.sig_q, true); f << stringf(" <= %s;\n", reg_name.c_str()); } From 94fec4ec75fbe1af1ff09d6b9eb2b8fc93325732 Mon Sep 17 00:00:00 2001 From: JulianKemmerer Date: Sat, 19 Dec 2020 00:12:04 -0500 Subject: [PATCH 81/90] Fix mult18x18d component to match yosys verilog (cherry picked from commit 6671d0475fb32d549307d3a864455e6915c39aa3) --- library/ecp5u/components.vhdl | 520 +++++++++++++++++----------------- 1 file changed, 260 insertions(+), 260 deletions(-) diff --git a/library/ecp5u/components.vhdl b/library/ecp5u/components.vhdl index e4a40b2e..3f1f428d 100644 --- a/library/ecp5u/components.vhdl +++ b/library/ecp5u/components.vhdl @@ -1486,267 +1486,267 @@ component mult18x18c is signedp : out std_logic ); end component; -component mult18x18d is +component MULT18X18D is generic ( - reg_inputa_clk : string := "NONE"; - reg_inputa_ce : string := "CE0"; - reg_inputa_rst : string := "RST0"; - reg_inputb_clk : string := "NONE"; - reg_inputb_ce : string := "CE0"; - reg_inputb_rst : string := "RST0"; - reg_inputc_clk : string := "NONE"; - reg_inputc_ce : string := "CE0"; - reg_inputc_rst : string := "RST0"; - reg_pipeline_clk : string := "NONE"; - reg_pipeline_ce : string := "CE0"; - reg_pipeline_rst : string := "RST0"; - reg_output_clk : string := "NONE"; - reg_output_ce : string := "CE0"; - reg_output_rst : string := "RST0"; - clk0_div : string := "ENABLED"; - clk1_div : string := "ENABLED"; - clk2_div : string := "ENABLED"; - clk3_div : string := "ENABLED"; - highspeed_clk : string := "NONE"; - gsr : string := "ENABLED"; - cas_match_reg : string := "FALSE"; - sourceb_mode : string := "B_SHIFT"; - mult_bypass : string := "DISABLED"; - resetmode : string := "SYNC" ); - port ( - a17 : in std_logic; - a16 : in std_logic; - a15 : in std_logic; - a14 : in std_logic; - a13 : in std_logic; - a12 : in std_logic; - a11 : in std_logic; - a10 : in std_logic; - a9 : in std_logic; - a8 : in std_logic; - a7 : in std_logic; - a6 : in std_logic; - a5 : in std_logic; - a4 : in std_logic; - a3 : in std_logic; - a2 : in std_logic; - a1 : in std_logic; - a0 : in std_logic; - b17 : in std_logic; - b16 : in std_logic; - b15 : in std_logic; - b14 : in std_logic; - b13 : in std_logic; - b12 : in std_logic; - b11 : in std_logic; - b10 : in std_logic; - b9 : in std_logic; - b8 : in std_logic; - b7 : in std_logic; - b6 : in std_logic; - b5 : in std_logic; - b4 : in std_logic; - b3 : in std_logic; - b2 : in std_logic; - b1 : in std_logic; - b0 : in std_logic; - c17 : in std_logic; - c16 : in std_logic; - c15 : in std_logic; - c14 : in std_logic; - c13 : in std_logic; - c12 : in std_logic; - c11 : in std_logic; - c10 : in std_logic; - c9 : in std_logic; - c8 : in std_logic; - c7 : in std_logic; - c6 : in std_logic; - c5 : in std_logic; - c4 : in std_logic; - c3 : in std_logic; - c2 : in std_logic; - c1 : in std_logic; - c0 : in std_logic; - signeda : in std_logic; - signedb : in std_logic; - sourcea : in std_logic; - sourceb : in std_logic; - clk3 : in std_logic; - clk2 : in std_logic; - clk1 : in std_logic; - clk0 : in std_logic; - ce3 : in std_logic; - ce2 : in std_logic; - ce1 : in std_logic; - ce0 : in std_logic; - rst3 : in std_logic; - rst2 : in std_logic; - rst1 : in std_logic; - rst0 : in std_logic; - sria17 : in std_logic; - sria16 : in std_logic; - sria15 : in std_logic; - sria14 : in std_logic; - sria13 : in std_logic; - sria12 : in std_logic; - sria11 : in std_logic; - sria10 : in std_logic; - sria9 : in std_logic; - sria8 : in std_logic; - sria7 : in std_logic; - sria6 : in std_logic; - sria5 : in std_logic; - sria4 : in std_logic; - sria3 : in std_logic; - sria2 : in std_logic; - sria1 : in std_logic; - sria0 : in std_logic; - srib17 : in std_logic; - srib16 : in std_logic; - srib15 : in std_logic; - srib14 : in std_logic; - srib13 : in std_logic; - srib12 : in std_logic; - srib11 : in std_logic; - srib10 : in std_logic; - srib9 : in std_logic; - srib8 : in std_logic; - srib7 : in std_logic; - srib6 : in std_logic; - srib5 : in std_logic; - srib4 : in std_logic; - srib3 : in std_logic; - srib2 : in std_logic; - srib1 : in std_logic; - srib0 : in std_logic; - sroa17 : out std_logic; - sroa16 : out std_logic; - sroa15 : out std_logic; - sroa14 : out std_logic; - sroa13 : out std_logic; - sroa12 : out std_logic; - sroa11 : out std_logic; - sroa10 : out std_logic; - sroa9 : out std_logic; - sroa8 : out std_logic; - sroa7 : out std_logic; - sroa6 : out std_logic; - sroa5 : out std_logic; - sroa4 : out std_logic; - sroa3 : out std_logic; - sroa2 : out std_logic; - sroa1 : out std_logic; - sroa0 : out std_logic; - srob17 : out std_logic; - srob16 : out std_logic; - srob15 : out std_logic; - srob14 : out std_logic; - srob13 : out std_logic; - srob12 : out std_logic; - srob11 : out std_logic; - srob10 : out std_logic; - srob9 : out std_logic; - srob8 : out std_logic; - srob7 : out std_logic; - srob6 : out std_logic; - srob5 : out std_logic; - srob4 : out std_logic; - srob3 : out std_logic; - srob2 : out std_logic; - srob1 : out std_logic; - srob0 : out std_logic; - roa17 : out std_logic; - roa16 : out std_logic; - roa15 : out std_logic; - roa14 : out std_logic; - roa13 : out std_logic; - roa12 : out std_logic; - roa11 : out std_logic; - roa10 : out std_logic; - roa9 : out std_logic; - roa8 : out std_logic; - roa7 : out std_logic; - roa6 : out std_logic; - roa5 : out std_logic; - roa4 : out std_logic; - roa3 : out std_logic; - roa2 : out std_logic; - roa1 : out std_logic; - roa0 : out std_logic; - rob17 : out std_logic; - rob16 : out std_logic; - rob15 : out std_logic; - rob14 : out std_logic; - rob13 : out std_logic; - rob12 : out std_logic; - rob11 : out std_logic; - rob10 : out std_logic; - rob9 : out std_logic; - rob8 : out std_logic; - rob7 : out std_logic; - rob6 : out std_logic; - rob5 : out std_logic; - rob4 : out std_logic; - rob3 : out std_logic; - rob2 : out std_logic; - rob1 : out std_logic; - rob0 : out std_logic; - roc17 : out std_logic; - roc16 : out std_logic; - roc15 : out std_logic; - roc14 : out std_logic; - roc13 : out std_logic; - roc12 : out std_logic; - roc11 : out std_logic; - roc10 : out std_logic; - roc9 : out std_logic; - roc8 : out std_logic; - roc7 : out std_logic; - roc6 : out std_logic; - roc5 : out std_logic; - roc4 : out std_logic; - roc3 : out std_logic; - roc2 : out std_logic; - roc1 : out std_logic; - roc0 : out std_logic; - p35 : out std_logic; - p34 : out std_logic; - p33 : out std_logic; - p32 : out std_logic; - p31 : out std_logic; - p30 : out std_logic; - p29 : out std_logic; - p28 : out std_logic; - p27 : out std_logic; - p26 : out std_logic; - p25 : out std_logic; - p24 : out std_logic; - p23 : out std_logic; - p22 : out std_logic; - p21 : out std_logic; - p20 : out std_logic; - p19 : out std_logic; - p18 : out std_logic; - p17 : out std_logic; - p16 : out std_logic; - p15 : out std_logic; - p14 : out std_logic; - p13 : out std_logic; - p12 : out std_logic; - p11 : out std_logic; - p10 : out std_logic; - p9 : out std_logic; - p8 : out std_logic; - p7 : out std_logic; - p6 : out std_logic; - p5 : out std_logic; - p4 : out std_logic; - p3 : out std_logic; - p2 : out std_logic; - p1 : out std_logic; - p0 : out std_logic; - signedp : out std_logic ); + REG_INPUTA_CLK : string := "NONE"; + REG_INPUTA_CE : string := "CE0"; + REG_INPUTA_RST : string := "RST0"; + REG_INPUTB_CLK : string := "NONE"; + REG_INPUTB_CE : string := "CE0"; + REG_INPUTB_RST : string := "RST0"; + REG_INPUTC_CLK : string := "NONE"; + --reg_inputc_ce : string := "CE0"; + --reg_inputc_rst : string := "RST0"; + REG_PIPELINE_CLK : string := "NONE"; + REG_PIPELINE_CE : string := "CE0"; + REG_PIPELINE_RST : string := "RST0"; + REG_OUTPUT_CLK : string := "NONE"; + --reg_output_ce : string := "CE0"; + --reg_output_rst : string := "RST0"; + CLK0_DIV : string := "ENABLED"; + CLK1_DIV : string := "ENABLED"; + CLK2_DIV : string := "ENABLED"; + CLK3_DIV : string := "ENABLED"; + --highspeed_clk : string := "NONE"; + GSR : string := "ENABLED"; + --Cas_match_reg : string := "FALSE"; + SOURCEB_MODE : string := "B_SHIFT"; + --mult_bypass : string := "DISABLED"; + RESETMODE : string := "SYNC" ); + port ( + A17 : in std_logic; + A16 : in std_logic; + A15 : in std_logic; + A14 : in std_logic; + A13 : in std_logic; + A12 : in std_logic; + A11 : in std_logic; + A10 : in std_logic; + A9 : in std_logic; + A8 : in std_logic; + A7 : in std_logic; + A6 : in std_logic; + A5 : in std_logic; + A4 : in std_logic; + A3 : in std_logic; + A2 : in std_logic; + A1 : in std_logic; + A0 : in std_logic; + B17 : in std_logic; + B16 : in std_logic; + B15 : in std_logic; + B14 : in std_logic; + B13 : in std_logic; + B12 : in std_logic; + B11 : in std_logic; + B10 : in std_logic; + B9 : in std_logic; + B8 : in std_logic; + B7 : in std_logic; + B6 : in std_logic; + B5 : in std_logic; + B4 : in std_logic; + B3 : in std_logic; + B2 : in std_logic; + B1 : in std_logic; + B0 : in std_logic; + C17 : in std_logic; + C16 : in std_logic; + C15 : in std_logic; + C14 : in std_logic; + C13 : in std_logic; + C12 : in std_logic; + C11 : in std_logic; + C10 : in std_logic; + C9 : in std_logic; + C8 : in std_logic; + C7 : in std_logic; + C6 : in std_logic; + C5 : in std_logic; + C4 : in std_logic; + C3 : in std_logic; + C2 : in std_logic; + C1 : in std_logic; + C0 : in std_logic; + SIGNEDA : in std_logic; + SIGNEDB : in std_logic; + SOURCEA : in std_logic; + SOURCEB : in std_logic; + CLK3 : in std_logic; + CLK2 : in std_logic; + CLK1 : in std_logic; + CLK0 : in std_logic; + CE3 : in std_logic; + CE2 : in std_logic; + CE1 : in std_logic; + CE0 : in std_logic; + RST3 : in std_logic; + RST2 : in std_logic; + RST1 : in std_logic; + RST0 : in std_logic; + -- SRIA17 : in std_logic; + -- SRIA16 : in std_logic; + -- SRIA15 : in std_logic; + -- SRIA14 : in std_logic; + -- SRIA13 : in std_logic; + -- SRIA12 : in std_logic; + -- SRIA11 : in std_logic; + -- SRIA10 : in std_logic; + -- SRIA9 : in std_logic; + -- SRIA8 : in std_logic; + -- SRIA7 : in std_logic; + -- SRIA6 : in std_logic; + -- SRIA5 : in std_logic; + -- SRIA4 : in std_logic; + -- SRIA3 : in std_logic; + -- SRIA2 : in std_logic; + -- SRIA1 : in std_logic; + -- SRIA0 : in std_logic; + -- SRIB17 : in std_logic; + -- SRIB16 : in std_logic; + -- SRIB15 : in std_logic; + -- SRIB14 : in std_logic; + -- SRIB13 : in std_logic; + -- SRIB12 : in std_logic; + -- SRIB11 : in std_logic; + -- SRIB10 : in std_logic; + -- SRIB9 : in std_logic; + -- SRIB8 : in std_logic; + -- SRIB7 : in std_logic; + -- SRIB6 : in std_logic; + -- SRIB5 : in std_logic; + -- SRIB4 : in std_logic; + -- SRIB3 : in std_logic; + -- SRIB2 : in std_logic; + -- SRIB1 : in std_logic; + -- SRIB0 : in std_logic; + SROA17 : out std_logic; + SROA16 : out std_logic; + SROA15 : out std_logic; + SROA14 : out std_logic; + SROA13 : out std_logic; + SROA12 : out std_logic; + SROA11 : out std_logic; + SROA10 : out std_logic; + SROA9 : out std_logic; + SROA8 : out std_logic; + SROA7 : out std_logic; + SROA6 : out std_logic; + SROA5 : out std_logic; + SROA4 : out std_logic; + SROA3 : out std_logic; + SROA2 : out std_logic; + SROA1 : out std_logic; + SROA0 : out std_logic; + SROB17 : out std_logic; + SROB16 : out std_logic; + SROB15 : out std_logic; + SROB14 : out std_logic; + SROB13 : out std_logic; + SROB12 : out std_logic; + SROB11 : out std_logic; + SROB10 : out std_logic; + SROB9 : out std_logic; + SROB8 : out std_logic; + SROB7 : out std_logic; + SROB6 : out std_logic; + SROB5 : out std_logic; + SROB4 : out std_logic; + SROB3 : out std_logic; + SROB2 : out std_logic; + SROB1 : out std_logic; + SROB0 : out std_logic; + ROA17 : out std_logic; + ROA16 : out std_logic; + ROA15 : out std_logic; + ROA14 : out std_logic; + ROA13 : out std_logic; + ROA12 : out std_logic; + ROA11 : out std_logic; + ROA10 : out std_logic; + ROA9 : out std_logic; + ROA8 : out std_logic; + ROA7 : out std_logic; + ROA6 : out std_logic; + ROA5 : out std_logic; + ROA4 : out std_logic; + ROA3 : out std_logic; + ROA2 : out std_logic; + ROA1 : out std_logic; + ROA0 : out std_logic; + ROB17 : out std_logic; + ROB16 : out std_logic; + ROB15 : out std_logic; + ROB14 : out std_logic; + ROB13 : out std_logic; + ROB12 : out std_logic; + ROB11 : out std_logic; + ROB10 : out std_logic; + ROB9 : out std_logic; + ROB8 : out std_logic; + ROB7 : out std_logic; + ROB6 : out std_logic; + ROB5 : out std_logic; + ROB4 : out std_logic; + ROB3 : out std_logic; + ROB2 : out std_logic; + ROB1 : out std_logic; + ROB0 : out std_logic; + ROC17 : out std_logic; + ROC16 : out std_logic; + ROC15 : out std_logic; + ROC14 : out std_logic; + ROC13 : out std_logic; + ROC12 : out std_logic; + ROC11 : out std_logic; + ROC10 : out std_logic; + ROC9 : out std_logic; + ROC8 : out std_logic; + ROC7 : out std_logic; + ROC6 : out std_logic; + ROC5 : out std_logic; + ROC4 : out std_logic; + ROC3 : out std_logic; + ROC2 : out std_logic; + ROC1 : out std_logic; + ROC0 : out std_logic; + P35 : out std_logic; + P34 : out std_logic; + P33 : out std_logic; + P32 : out std_logic; + P31 : out std_logic; + P30 : out std_logic; + P29 : out std_logic; + P28 : out std_logic; + P27 : out std_logic; + P26 : out std_logic; + P25 : out std_logic; + P24 : out std_logic; + P23 : out std_logic; + P22 : out std_logic; + P21 : out std_logic; + P20 : out std_logic; + P19 : out std_logic; + P18 : out std_logic; + P17 : out std_logic; + P16 : out std_logic; + P15 : out std_logic; + P14 : out std_logic; + P13 : out std_logic; + P12 : out std_logic; + P11 : out std_logic; + P10 : out std_logic; + P9 : out std_logic; + P8 : out std_logic; + P7 : out std_logic; + P6 : out std_logic; + P5 : out std_logic; + P4 : out std_logic; + P3 : out std_logic; + P2 : out std_logic; + P1 : out std_logic; + P0 : out std_logic; + SIGNEDP : out std_logic ); end component; component alu24a is From d7a90321665753a6ad1040ff03934778eac62fb7 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 20 Dec 2020 18:13:32 -0800 Subject: [PATCH 82/90] Fix a series of bugs in FF dumping code --- src/vhdl_backend.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 487337b0..41860fb7 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1602,12 +1602,12 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(") then\n"); // ff.ce_over_srst means sync-reset is also gated by enable if (ff.has_srst && ff.has_en && ff.ce_over_srst) { - f << stringf("%s" " if (", indent.c_str()); - f << stringf("%s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); + f << stringf("%s" " if", indent.c_str()); + f << stringf(" %s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); //dump_sigspec(f, ff.sig_en); //f << stringf(" = '%c' then\n", ff.pol_en ? '1' : '0'); - f << stringf("%s" " if (", indent.c_str()); - f << stringf("%s then\n", sigbit_equal_bool(ff.sig_srst.as_bit(), ff.pol_srst).c_str()); + f << stringf("%s" " if", indent.c_str()); + f << stringf(" %s then\n", sigbit_equal_bool(ff.sig_srst.as_bit(), ff.pol_srst).c_str()); //dump_sigspec(f, ff.sig_srst); //f << stringf(" = '%c' then\n", ff.pol_srst ? '1' : '0'); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); @@ -1616,6 +1616,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " else\n", indent.c_str()); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, sig_d); + f << stringf(";\n"); f << stringf("%s" " end if;\n", indent.c_str()); f << stringf("%s" " end if;\n", indent.c_str()); } else { @@ -1636,6 +1637,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) //f << stringf(") then\n"); } if (ff.has_srst || ff.has_en) { + f << stringf("%s" " else\n", indent.c_str()); f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, sig_d); f << stringf(";\n"); From ff3be346de62f88b504facc5aa091092bdbd5702 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Sun, 20 Dec 2020 18:14:50 -0800 Subject: [PATCH 83/90] Write a process_sensitivity_str helper and use it for process sensitivity lists --- src/vhdl_backend.cc | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 41860fb7..b4549ae6 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -481,6 +481,26 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool lhs_mode=fals } } +/* + * Returns a string of the form "chunk_1, chunk_2, chunk_3" + * Intended use is populating process sensitivity lists + * Assumes that set is from get_sensitivity_set, i.e. no constant chunks + */ +std::string process_sensitivity_str(std::set chunks) +{ + bool is_first_element = true; + std::stringstream result_gather; + for (RTLIL::SigChunk sigchunk: chunks) { + if (is_first_element) { + is_first_element = false; + } else { + result_gather << ", "; + } + dump_sigchunk(result_gather, sigchunk); + } + return result_gather.str(); +} + void dump_attributes(std::ostream &f, std::string indent, dict &attributes, bool modattr = false, bool regattr = false, bool as_comment = false) { // PORTING REQUIRED if (noattr) @@ -1360,16 +1380,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) get_sensitivity_set({cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::S)}); - f << stringf("%s" "process(", indent.c_str()); - bool is_first_sensitivity_chunk = true; - for (RTLIL::SigChunk chunk: sensitivities) { - if (!is_first_sensitivity_chunk) { - f << ", "; - } - dump_sigchunk(f, chunk); - is_first_sensitivity_chunk = false; - } - f << stringf(") is\n"); + f << stringf("%s" "process(%s) is\n", + indent.c_str(), process_sensitivity_str(sensitivities).c_str()); f << stringf("%s" " variable %s: std_logic_vector(%d downto 0);\n", indent.c_str(), a_var_str.c_str(), width-1); f << stringf("%s" " variable %s: std_logic_vector(%d downto 0);\n", @@ -1512,16 +1524,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } else if (ff.has_arst) { sensitivity_set.insert(ff.sig_arst); } - f << stringf("%s" "process(", indent.c_str()); - bool is_first_sensitivity_chunk = true; - for (RTLIL::SigChunk chunk: get_sensitivity_set(sensitivity_set)) { - if (!is_first_sensitivity_chunk) { - f << ", "; - } - dump_sigchunk(f, chunk); - is_first_sensitivity_chunk = false; - } - f << stringf(") is\n"); + f << stringf("%s" "process(%s) is\n", indent.c_str(), + process_sensitivity_str(get_sensitivity_set(sensitivity_set)).c_str()); if (!out_is_reg_wire) { if (ff.width == 1) From 0c6ea5863ebfa3d9c2fb0ab991cd77182db60d67 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 10 Feb 2021 19:05:40 -0800 Subject: [PATCH 84/90] Slight adjustments to formal cell dumping * In std08 mode, avoid an explicit `='1'` for non-PSL asserts * Update the log_experiment call to reflect the use of non-PSL asserts --- src/vhdl_backend.cc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index b4549ae6..ef6bc3a5 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1720,7 +1720,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) cell->type.c_str()+1); log("PSL unclocked directives do not work (yet) with GHDL\n"); } - log_experimental("Formal cells as asserts/PSL comments"); + log_experimental("Formal cells as PSL comments"); std::stringstream en_sstream; std::stringstream a_sstream; // Not actually arbitrary haha string en_str; @@ -1740,11 +1740,21 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // TODO: special handling for asserts of x->'1'? if (cell->type == ID($assert)) { if (en_const_on) { - f << stringf("%s" "assert %s;\n", indent.c_str(), - a_str.c_str()); + if (std08) { + f << stringf("%s" "assert %s;\n", indent.c_str(), + a_str.c_str()); + } else { + f << stringf("%s" "assert %s = '1';\n", indent.c_str(), + a_str.c_str()); + } } else { - f << stringf("%s" "assert (not %s) or %s;", indent.c_str(), - en_str.c_str(), a_str.c_str()); + if (std08) { + f << stringf("%s" "assert ((not %s) or %s) = '1';", + indent.c_str(), en_str.c_str(), a_str.c_str()); + } else { + f << stringf("%s" "assert (not %s) or %s;", indent.c_str(), + en_str.c_str(), a_str.c_str()); + } f << stringf(" -- %s -> %s\n", en_str.c_str(), a_str.c_str()); } } else { From f1b5e3c47a77e259878486ee20dce952aaa18999 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 10 Feb 2021 19:08:21 -0800 Subject: [PATCH 85/90] Mark dump_memory_types as porting complete Actually dumping memories though is currently an unresolved thing --- src/vhdl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index ef6bc3a5..8caec917 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -549,7 +549,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) // We split Verilog backend's dump_memory into two functions void dump_memory_types(std::ostream &f, std::string indent, Mem &mem) -{ // PORTING NEEDS TESTING +{ // PORTING COMPLETE size_t is_element_present = memory_array_types.count(mem.width); std::string memory_type_name = stringf("array_type_%d",mem.width); if (!is_element_present) { From ad14b39d468ed99a2c506771c4404eb86c1e030a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 10 Feb 2021 22:52:12 -0800 Subject: [PATCH 86/90] Initial (believed working) port of $lut cell dumping Qualified expression part will probably be moved into dump_sigspec --- src/vhdl_backend.cc | 58 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 8caec917..5480eaa2 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1479,16 +1479,58 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + // TODO: handle LUT1's if (cell->type == ID($lut)) - { // unported for now - f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = "); - dump_const(f, cell->parameters.at(ID::LUT)); - f << stringf(" >> "); - dump_attributes(f, "", cell->attributes, ' '); - dump_sigspec(f, cell->getPort(ID::A)); + { + // Assumes that ID::LUT param is downto (but matches techlib model) + // ghdl --synth for a LUT is unknown as it gets expanded into $mux es + + std::string lut_const_name = stringf("LUTstr_%s",id(cell->name).c_str()); + // Encapsulate LUT constant with process, which is otherwise unnecessary + SigSpec a_port = cell->getPort(ID::A); + Const lut_const = cell->parameters.at(ID::LUT); + // TODO: fix handling of width 1 LUT input + int lut_width = lut_const.size(); + f << stringf("%s" "process (%s) is\n", indent.c_str(), + process_sensitivity_str(get_sensitivity_set(a_port)).c_str()); + f << stringf("%s" " constant %s: STD_LOGIC_VECTOR(%d downto 0) := ", + indent.c_str(), lut_const_name.c_str(), lut_width-1); + // offset=default, no_decimal=true + dump_const(f, lut_const, lut_width, 0, true); f << stringf(";\n"); + + f << stringf("%s" "begin\n", indent.c_str()); + f << stringf("%s ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y), true); + f << stringf(" <= "); + dump_attributes(f, "", cell->attributes, ' '); + f << lut_const_name; + // Outermost parentheses are indexing wrapper + f << stringf("(to_integer("); + + /* + * Use a qualified expression for std_logic concats + * If all elements are non-array type, the aggregate has ambiguous type + * Qualified expressions are defined in IEEE 1076-2008 5.2.1 + * Concatenation return types are defined in IEEE 1076-2008 9.2.5 + * TODO: concatenation qualified expr may need to be used elsewhere + */ + bool use_qualified_expr = true; + for (auto it = a_port.chunks().rbegin(); + it != a_port.chunks().rend(); + it++) { + if (it->width > 1) { + use_qualified_expr = false; + } + } + if (use_qualified_expr) { + f << stringf("unsigned'("); + } else { + f << stringf("unsigned("); + } + dump_sigspec(f, a_port); + f << stringf(")));\n"); + f << stringf("%s" "end process;\n", indent.c_str()); return true; } From 52d1a02b805847781a0733a2a99a9d66d302fc7a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 11 Feb 2021 17:14:19 -0800 Subject: [PATCH 87/90] Log warning when emitting VHDL-2008 LHS aggregates --- src/vhdl_backend.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 5480eaa2..26259fbc 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -467,6 +467,11 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool lhs_mode=fals dump_sigchunk(f, sig.as_chunk()); } else { // LHS mode is for the LHS of expressions like (a, b) <= c in VHDL-2008 mode + if (lhs_mode && !std08) { + // TODO: this is only a warning because other codegen stuff is still generating 2008 syntax outside of VHDL-2008 mode + // This should be converted into an error once that is fixed + log_warning("%s","dump_sigspec called for multi-chunk LHS output when not in VHDL-2008 mode\n"); + } if (lhs_mode) { f << stringf("("); } From 38763f20f7171de6067ceafb93820d0d41ff340a Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 11 Feb 2021 17:14:50 -0800 Subject: [PATCH 88/90] Fix more FF bugs by rewriting synchronous signal portion --- src/vhdl_backend.cc | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 26259fbc..d428c82d 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -1671,32 +1671,28 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " end if;\n", indent.c_str()); f << stringf("%s" " end if;\n", indent.c_str()); } else { - // Commented out stuff was broken - if (ff.has_srst) { - f << stringf("%s" " if ", indent.c_str()); - f << stringf("%s then\n", sigbit_equal_bool(ff.sig_srst.as_bit(), ff.pol_srst).c_str()); - //dump_sigspec(f, ff.sig_srst); - //f << stringf(" then\n"); - f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); - dump_sigspec(f, val_srst); - f << stringf(";\n"); - } - if (ff.has_en) { - f << stringf("%s" " %s ", indent.c_str(), ff.has_srst ? "elsif" : "if"); - f << stringf("%s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); - //dump_sigspec(f, ff.sig_en); - //f << stringf(") then\n"); - } - if (ff.has_srst || ff.has_en) { - f << stringf("%s" " else\n", indent.c_str()); - f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + if (!ff.has_srst && !ff.has_en) { + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, sig_d); f << stringf(";\n"); - f << stringf("%s" " end if;\n", indent.c_str()); } else { - f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + if (ff.has_srst) { + f << stringf("%s" " if ", indent.c_str()); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_srst.as_bit(), ff.pol_srst).c_str()); + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); + dump_sigspec(f, val_srst); + f << stringf(";\n"); + } + if (ff.has_en) { + f << stringf("%s" " %s ", indent.c_str(), ff.has_srst ? "elsif" : "if"); + f << stringf("%s then\n", sigbit_equal_bool(ff.sig_en.as_bit(), ff.pol_en).c_str()); + } else { + f << stringf("%s" " else\n", indent.c_str()); + } + f << stringf("%s" " %s %s ", indent.c_str(), reg_bit_name.c_str(), assignment_operator.c_str()); dump_sigspec(f, sig_d); f << stringf(";\n"); + f << stringf("%s" " end if;\n", indent.c_str()); } } f << stringf("%s" " end if;\n", indent.c_str()); From e6b31e2370fe81447da0123ca7a224f2169bb747 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Thu, 11 Feb 2021 17:15:13 -0800 Subject: [PATCH 89/90] Avoid requiring VERSION macro and print "unknown version" if macro is undefined --- src/vhdl_backend.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index d428c82d..0ae48a11 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -2456,8 +2456,12 @@ struct VHDLBackend : public Backend { *f << stringf("-- Generated by:\n"); // Already contains string "Yosys" - *f << stringf("-- %s\n",yosys_version_str); + *f << stringf("-- %s\n", yosys_version_str); + #ifdef VERSION *f << stringf("-- GHDL-Yosys-Plugin %s\n", VERSION); + #else + *f << stringf("-- GHDL-Yosys-Plugin %s\n", "unknown version"); + #endif // TODO: check all log calls to see if \n is needed log_experimental("VHDL backend"); write_header_imports(*f, ""); From 32251b2fe1bb322d18ea81e987791f5a29358b68 Mon Sep 17 00:00:00 2001 From: rlee287 Date: Wed, 3 Mar 2021 15:53:16 -0800 Subject: [PATCH 90/90] Update some comments, including an archive source for a (now dead) link --- src/vhdl_backend.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vhdl_backend.cc b/src/vhdl_backend.cc index 0ae48a11..94f01d56 100644 --- a/src/vhdl_backend.cc +++ b/src/vhdl_backend.cc @@ -20,6 +20,7 @@ * A VHDL backend based on the Verilog backend. * Code structure here is regularly synced with the Verilog backend, except where noted * Last synced against Yosys 871fc34a + * The commit above references the last commit that changed the Verilog backend */ #include "kernel/register.h" @@ -1098,6 +1099,8 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) * * Avoid using built-in operators sll, sla, srl, sra, rol, and rar * See https://jdebp.eu/FGA/bit-shifts-in-vhdl.html for explanation + * New link via Wayback Machine as webpage is now down: + * http://web.archive.org/web/20200214191101/https://jdebp.eu/FGA/bit-shifts-in-vhdl.html */ // IEEE 1364-2005: no sign extension done on either of the left shifts @@ -1930,6 +1933,7 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, /* * Split LHS of connection by default * Use VHDL-2008 style concatenation with -nosimple-lhs and -std08 + * TODO: any reason to bother with -nosimple-hls and not just split? */ if (!(nosimple_lhs && std08)) { int offset = 0;