diff options
author | Claire Xen <claire@clairexen.net> | 2022-02-11 16:03:12 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-11 16:03:12 +0100 |
commit | 49545c73f7f5a5cf73d287fd371f2ff39311f621 (patch) | |
tree | d0f20b8def36e551c6735d4fc6033aaa2633fe80 /kernel | |
parent | 90b40aa51f7d666792d4f0b1830ee75b81678a1f (diff) | |
parent | e0165188669fcef2c5784c9916683889a2164e5d (diff) | |
download | yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.tar.gz yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.tar.bz2 yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.zip |
Merge branch 'master' into clk2ff-better-names
Diffstat (limited to 'kernel')
40 files changed, 4431 insertions, 673 deletions
diff --git a/kernel/binding.cc b/kernel/binding.cc new file mode 100644 index 000000000..621f7007b --- /dev/null +++ b/kernel/binding.cc @@ -0,0 +1,29 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "binding.h" + +YOSYS_NAMESPACE_BEGIN + +RTLIL::Binding::Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name) + : target_type(target_type), target_name(target_name) +{} + +YOSYS_NAMESPACE_END diff --git a/kernel/binding.h b/kernel/binding.h new file mode 100644 index 000000000..3b64e76da --- /dev/null +++ b/kernel/binding.h @@ -0,0 +1,60 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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. + * + */ + +#ifndef BINDING_H +#define BINDING_H + +#include "kernel/rtlil.h" + +YOSYS_NAMESPACE_BEGIN + +struct RTLIL::Binding +{ + // Represents a bind construct. + // + // The target of the binding is represented by target_type and + // target_name (see comments above the fields). + + Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name); + + virtual ~Binding() {} + + // Return a string describing the binding + virtual std::string describe() const = 0; + +protected: + // May be empty. If not, it's the name of the module or interface to + // bind to. + RTLIL::IdString target_type; + + // If target_type is nonempty (the usual case), this is a hierarchical + // reference to the bind target. If target_type is empty, we have to + // wait until the hierarchy pass to figure out whether this was the name + // of a module/interface type or an instance. + RTLIL::IdString target_name; + + // An attribute name which contains an ID that's unique across binding + // instances (used to ensure we don't apply a binding twice to a module) + RTLIL::IdString attr_name; +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 894a95ed1..7a8eb39f9 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/calc.cc b/kernel/calc.cc index d54ccbc10..0865db526 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -609,5 +609,56 @@ RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, boo return RTLIL::const_sub(zero, arg1_ext, true, signed1, result_len); } +RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) +{ + std::vector<RTLIL::State> t = arg1.bits; + + for (int i = GetSize(arg2)-1; i >= 0; i--) + { + RTLIL::State sel = arg2.bits.at(i); + std::vector<RTLIL::State> new_t; + if (sel == State::S0) + new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2); + else if (sel == State::S1) + new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end()); + else + for (int j = 0; j < GetSize(t)/2; j++) + new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx); + t.swap(new_t); + } + + return t; +} + +RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) +{ + int width = GetSize(arg1); + int s_width = GetSize(arg2); + std::vector<RTLIL::State> res; + for (int i = 0; i < (1 << s_width); i++) + { + bool ne = false; + bool x = false; + for (int j = 0; j < s_width; j++) { + bool bit = i & 1 << j; + if (arg2[j] == (bit ? RTLIL::S0 : RTLIL::S1)) + ne = true; + else if (arg2[j] != RTLIL::S0 && arg2[j] != RTLIL::S1) + x = true; + } + if (ne) { + for (int j = 0; j < width; j++) + res.push_back(State::S0); + } else if (x) { + for (int j = 0; j < width; j++) + res.push_back(arg1.bits[j] == State::S0 ? State::S0 : State::Sx); + } else { + for (int j = 0; j < width; j++) + res.push_back(arg1.bits[j]); + } + } + return res; +} + YOSYS_NAMESPACE_END diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index 2c82b1bca..292af3f51 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h index 1417a614c..8f6d69ba6 100644 --- a/kernel/cellaigs.h +++ b/kernel/cellaigs.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 314e7c77e..c43ba8db3 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -142,6 +142,36 @@ void mux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) } } +void bmux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + int width = GetSize(cell->getPort(ID::Y)); + int a_width = GetSize(cell->getPort(ID::A)); + int s_width = GetSize(cell->getPort(ID::S)); + + for (int i = 0; i < width; i++) + { + for (int k = i; k < a_width; k += width) + db->add_edge(cell, ID::A, k, ID::Y, i, -1); + + for (int k = 0; k < s_width; k++) + db->add_edge(cell, ID::S, k, ID::Y, i, -1); + } +} + +void demux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + int width = GetSize(cell->getPort(ID::Y)); + int a_width = GetSize(cell->getPort(ID::A)); + int s_width = GetSize(cell->getPort(ID::S)); + + for (int i = 0; i < width; i++) + { + db->add_edge(cell, ID::A, i % a_width, ID::Y, i, -1); + for (int k = 0; k < s_width; k++) + db->add_edge(cell, ID::S, k, ID::Y, i, -1); + } +} + PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) @@ -187,6 +217,16 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } + if (cell->type == ID($bmux)) { + bmux_op(this, cell); + return true; + } + + if (cell->type == ID($demux)) { + demux_op(this, cell); + return true; + } + // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat // FIXME: $lut $sop $alu $lcu $macc $fa diff --git a/kernel/celledges.h b/kernel/celledges.h index d105e4009..d5e374f05 100644 --- a/kernel/celledges.h +++ b/kernel/celledges.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 944cb301a..7e9cfb38d 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -127,6 +127,9 @@ struct CellTypes for (auto type : std::vector<RTLIL::IdString>({ID($mux), ID($pmux)})) setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true); + for (auto type : std::vector<RTLIL::IdString>({ID($bmux), ID($demux)})) + setup_type(type, {ID::A, ID::S}, {ID::Y}, true); + setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true); setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true); setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); @@ -142,6 +145,8 @@ struct CellTypes setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}); setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}); setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}); + setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}); + setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}); setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}); setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); @@ -155,9 +160,13 @@ struct CellTypes setup_internals_ff(); setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}); + setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}); setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool<RTLIL::IdString>()); + setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool<RTLIL::IdString>()); setup_type(ID($meminit), {ID::ADDR, ID::DATA}, pool<RTLIL::IdString>()); + setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool<RTLIL::IdString>()); setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); + setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}); } @@ -222,6 +231,15 @@ struct CellTypes for (auto c1 : list_np) for (auto c2 : list_np) + setup_type(stringf("$_ALDFF_%c%c_", c1, c2), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_np) + setup_type(stringf("$_ALDFFE_%c%c%c_", c1, c2, c3), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}); + + for (auto c1 : list_np) + for (auto c2 : list_np) for (auto c3 : list_np) setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}); @@ -396,6 +414,16 @@ struct CellTypes return ret; } + if (cell->type == ID($bmux)) + { + return const_bmux(arg1, arg2); + } + + if (cell->type == ID($demux)) + { + return const_demux(arg1, arg2); + } + if (cell->type == ID($lut)) { int width = cell->parameters.at(ID::WIDTH).as_int(); @@ -405,21 +433,7 @@ struct CellTypes t.push_back(State::S0); t.resize(1 << width); - for (int i = width-1; i >= 0; i--) { - RTLIL::State sel = arg1.bits.at(i); - std::vector<RTLIL::State> new_t; - if (sel == State::S0) - new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2); - else if (sel == State::S1) - new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end()); - else - for (int j = 0; j < GetSize(t)/2; j++) - new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx); - t.swap(new_t); - } - - log_assert(GetSize(t) == 1); - return t; + return const_bmux(t, arg1); } if (cell->type == ID($sop)) diff --git a/kernel/consteval.h b/kernel/consteval.h index ff8cf86d6..642eb42b2 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -135,8 +135,6 @@ struct ConstEval if (cell->hasPort(ID::S)) { sig_s = cell->getPort(ID::S); - if (!eval(sig_s, undef, cell)) - return false; } if (cell->hasPort(ID::A)) @@ -151,6 +149,9 @@ struct ConstEval int count_maybe_set_s_bits = 0; int count_set_s_bits = 0; + if (!eval(sig_s, undef, cell)) + return false; + for (int i = 0; i < sig_s.size(); i++) { RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0); @@ -198,6 +199,36 @@ struct ConstEval else set(sig_y, y_values.front()); } + else if (cell->type == ID($bmux)) + { + if (!eval(sig_s, undef, cell)) + return false; + + if (sig_s.is_fully_def()) { + int sel = sig_s.as_int(); + int width = GetSize(sig_y); + SigSpec res = sig_a.extract(sel * width, width); + if (!eval(res, undef, cell)) + return false; + set(sig_y, res.as_const()); + } else { + if (!eval(sig_a, undef, cell)) + return false; + set(sig_y, const_bmux(sig_a.as_const(), sig_s.as_const())); + } + } + else if (cell->type == ID($demux)) + { + if (!eval(sig_a, undef, cell)) + return false; + if (sig_a.is_fully_zero()) { + set(sig_y, Const(0, GetSize(sig_y))); + } else { + if (!eval(sig_s, undef, cell)) + return false; + set(sig_y, const_demux(sig_a.as_const(), sig_s.as_const())); + } + } else if (cell->type == ID($fa)) { RTLIL::SigSpec sig_c = cell->getPort(ID::C); diff --git a/kernel/constids.inc b/kernel/constids.inc index 3c2ff9beb..566b76217 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -11,9 +11,12 @@ X(abc9_mergeability) X(abc9_scc_id) X(abcgroup) X(ABITS) +X(AD) X(ADDR) X(allconst) X(allseq) +X(ALOAD) +X(ALOAD_POLARITY) X(always_comb) X(always_ff) X(always_latch) @@ -32,6 +35,7 @@ X(bugpoint_keep) X(B_WIDTH) X(C) X(cells_not_processed) +X(CE_OVER_SRST) X(CFG_ABITS) X(CFG_DBITS) X(CFG_INIT) @@ -46,6 +50,7 @@ X(CLK_POLARITY) X(CLR) X(CLR_POLARITY) X(CO) +X(COLLISION_X_MASK) X(CONFIG) X(CONFIG_WIDTH) X(CTRL_IN) @@ -95,6 +100,7 @@ X(hdlname) X(hierconn) X(I) X(INIT) +X(INIT_VALUE) X(init) X(initial_top) X(interface_modport) @@ -133,19 +139,31 @@ X(onehot) X(P) X(parallel_case) X(parameter) +X(PORTID) X(PRIORITY) +X(PRIORITY_MASK) X(Q) X(qwp_position) X(R) X(RD_ADDR) +X(RD_ARST) +X(RD_ARST_VALUE) +X(RD_CE_OVER_SRST) X(RD_CLK) X(RD_CLK_ENABLE) X(RD_CLK_POLARITY) +X(RD_COLLISION_X_MASK) X(RD_DATA) X(RD_EN) +X(RD_INIT_VALUE) X(RD_PORTS) +X(RD_SRST) +X(RD_SRST_VALUE) +X(RD_TRANSPARENCY_MASK) X(RD_TRANSPARENT) +X(RD_WIDE_CONTINUATION) X(reg) +X(reprocess_after) X(S) X(SET) X(SET_POLARITY) @@ -161,6 +179,7 @@ X(SRC_WIDTH) X(SRST) X(SRST_POLARITY) X(SRST_VALUE) +X(sta_arrival) X(STATE_BITS) X(STATE_NUM) X(STATE_NUM_LOG2) @@ -195,6 +214,7 @@ X(T_LIMIT_TYP) X(to_delete) X(top) X(TRANS_NUM) +X(TRANSPARENCY_MASK) X(TRANSPARENT) X(TRANS_TABLE) X(T_RISE_MAX) @@ -220,6 +240,8 @@ X(WR_CLK_POLARITY) X(WR_DATA) X(WR_EN) X(WR_PORTS) +X(WR_PRIORITY_MASK) +X(WR_WIDE_CONTINUATION) X(X) X(Y) X(Y_WIDTH) diff --git a/kernel/cost.h b/kernel/cost.h index ea2a4c1f0..b81420af7 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/driver.cc b/kernel/driver.cc index b55f02837..f8f940e89 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -118,7 +118,7 @@ int main(int argc, char **argv) if (argc == 2) { // Run the first argument as a script file - run_frontend(argv[1], "script", 0, 0, 0); + run_frontend(argv[1], "script"); } } @@ -202,12 +202,13 @@ int main(int argc, char **argv) std::string output_filename = ""; std::string scriptfile = ""; std::string depsfile = ""; + std::string topmodule = ""; bool scriptfile_tcl = false; - bool got_output_filename = false; bool print_banner = true; bool print_stats = true; bool call_abort = false; bool timing_details = false; + bool run_shell = true; bool mode_v = false; bool mode_q = false; @@ -288,6 +289,9 @@ int main(int argc, char **argv) printf(" -A\n"); printf(" will call abort() at the end of the script. for debugging\n"); printf("\n"); + printf(" -r <module_name>\n"); + printf(" elaborate command line arguments using the specified top module\n"); + printf("\n"); printf(" -D <macro>[=<value>]\n"); printf(" set the specified Verilog define (via \"read -define\")\n"); printf("\n"); @@ -342,7 +346,7 @@ int main(int argc, char **argv) } int opt; - while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:")) != -1) { switch (opt) { @@ -366,6 +370,7 @@ int main(int argc, char **argv) exit(0); case 'S': passes_commands.push_back("synth"); + run_shell = false; break; case 'g': log_force_debug++; @@ -378,19 +383,23 @@ int main(int argc, char **argv) break; case 'H': passes_commands.push_back("help"); + run_shell = false; break; case 'h': passes_commands.push_back(stringf("help %s", optarg)); + run_shell = false; break; case 'b': backend_command = optarg; + run_shell = false; break; case 'p': passes_commands.push_back(optarg); + run_shell = false; break; case 'o': output_filename = optarg; - got_output_filename = true; + run_shell = false; break; case 'l': case 'L': @@ -422,10 +431,12 @@ int main(int argc, char **argv) case 's': scriptfile = optarg; scriptfile_tcl = false; + run_shell = false; break; case 'c': scriptfile = optarg; scriptfile_tcl = true; + run_shell = false; break; case 'W': log_warn_regexes.push_back(YS_REGEX_COMPILE(optarg)); @@ -436,6 +447,9 @@ int main(int argc, char **argv) case 'e': log_werror_regexes.push_back(YS_REGEX_COMPILE(optarg)); break; + case 'r': + topmodule = optarg; + break; case 'D': vlog_defines.push_back(optarg); break; @@ -506,12 +520,6 @@ int main(int argc, char **argv) for (auto &fn : plugin_filenames) load_plugin(fn, {}); - if (optind == argc && passes_commands.size() == 0 && scriptfile.empty()) { - if (!got_output_filename) - backend_command = ""; - shell(yosys_design); - } - if (!vlog_defines.empty()) { std::string vdef_cmd = "read -define"; for (auto vdef : vlog_defines) @@ -520,7 +528,11 @@ int main(int argc, char **argv) } while (optind < argc) - run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL); + if (run_frontend(argv[optind++], frontend_command)) + run_shell = false; + + if (!topmodule.empty()) + run_pass("hierarchy -top " + topmodule); if (!scriptfile.empty()) { if (scriptfile_tcl) { @@ -531,13 +543,15 @@ int main(int argc, char **argv) log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n"); #endif } else - run_frontend(scriptfile, "script", output_filename == "-" ? &backend_command : NULL); + run_frontend(scriptfile, "script"); } for (auto it = passes_commands.begin(); it != passes_commands.end(); it++) run_pass(*it); - if (!backend_command.empty()) + if (run_shell) + shell(yosys_design); + else run_backend(output_filename, backend_command); yosys_design->check(); diff --git a/kernel/ff.cc b/kernel/ff.cc new file mode 100644 index 000000000..c43482bd2 --- /dev/null +++ b/kernel/ff.cc @@ -0,0 +1,755 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/ff.h" + +USING_YOSYS_NAMESPACE + +FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name) +{ + cell = cell_; + sig_q = cell->getPort(ID::Q); + width = GetSize(sig_q); + attributes = cell->attributes; + + if (initvals) + val_init = (*initvals)(sig_q); + + std::string type_str = cell->type.str(); + + if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { + if (cell->type == ID($ff)) { + has_gclk = true; + sig_d = cell->getPort(ID::D); + } else if (cell->type == ID($sr)) { + // No data input at all. + } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { + has_aload = true; + sig_aload = cell->getPort(ID::EN); + pol_aload = cell->getParam(ID::EN_POLARITY).as_bool(); + sig_ad = cell->getPort(ID::D); + } else { + has_clk = true; + sig_clk = cell->getPort(ID::CLK); + pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); + sig_d = cell->getPort(ID::D); + } + if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) { + has_ce = true; + sig_ce = cell->getPort(ID::EN); + pol_ce = cell->getParam(ID::EN_POLARITY).as_bool(); + } + if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { + has_sr = true; + sig_clr = cell->getPort(ID::CLR); + sig_set = cell->getPort(ID::SET); + pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool(); + pol_set = cell->getParam(ID::SET_POLARITY).as_bool(); + } + if (cell->type.in(ID($aldff), ID($aldffe))) { + has_aload = true; + sig_aload = cell->getPort(ID::ALOAD); + pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool(); + sig_ad = cell->getPort(ID::AD); + } + if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) { + has_arst = true; + sig_arst = cell->getPort(ID::ARST); + pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool(); + val_arst = cell->getParam(ID::ARST_VALUE); + } + if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { + has_srst = true; + sig_srst = cell->getPort(ID::SRST); + pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool(); + val_srst = cell->getParam(ID::SRST_VALUE); + ce_over_srst = cell->type == ID($sdffce); + } + } else if (cell->type == ID($_FF_)) { + is_fine = true; + has_gclk = true; + sig_d = cell->getPort(ID::D); + } else if (type_str.substr(0, 5) == "$_SR_") { + is_fine = true; + has_sr = true; + pol_set = type_str[5] == 'P'; + pol_clr = type_str[6] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[6] == 'P'; + sig_clk = cell->getPort(ID::C); + } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[7] == 'P'; + sig_clk = cell->getPort(ID::C); + has_ce = true; + pol_ce = type_str[8] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[6] == 'P'; + sig_clk = cell->getPort(ID::C); + has_arst = true; + pol_arst = type_str[7] == 'P'; + sig_arst = cell->getPort(ID::R); + val_arst = type_str[8] == '1' ? State::S1 : State::S0; + } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[7] == 'P'; + sig_clk = cell->getPort(ID::C); + has_arst = true; + pol_arst = type_str[8] == 'P'; + sig_arst = cell->getPort(ID::R); + val_arst = type_str[9] == '1' ? State::S1 : State::S0; + has_ce = true; + pol_ce = type_str[10] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[8] == 'P'; + sig_clk = cell->getPort(ID::C); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::L); + sig_ad = cell->getPort(ID::AD); + } else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[9] == 'P'; + sig_clk = cell->getPort(ID::C); + has_aload = true; + pol_aload = type_str[10] == 'P'; + sig_aload = cell->getPort(ID::L); + sig_ad = cell->getPort(ID::AD); + has_ce = true; + pol_ce = type_str[11] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[8] == 'P'; + sig_clk = cell->getPort(ID::C); + has_sr = true; + pol_set = type_str[9] == 'P'; + pol_clr = type_str[10] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[9] == 'P'; + sig_clk = cell->getPort(ID::C); + has_sr = true; + pol_set = type_str[10] == 'P'; + pol_clr = type_str[11] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + has_ce = true; + pol_ce = type_str[12] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[7] == 'P'; + sig_clk = cell->getPort(ID::C); + has_srst = true; + pol_srst = type_str[8] == 'P'; + sig_srst = cell->getPort(ID::R); + val_srst = type_str[9] == '1' ? State::S1 : State::S0; + } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[8] == 'P'; + sig_clk = cell->getPort(ID::C); + has_srst = true; + pol_srst = type_str[9] == 'P'; + sig_srst = cell->getPort(ID::R); + val_srst = type_str[10] == '1' ? State::S1 : State::S0; + has_ce = true; + pol_ce = type_str[11] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[9] == 'P'; + sig_clk = cell->getPort(ID::C); + has_srst = true; + pol_srst = type_str[10] == 'P'; + sig_srst = cell->getPort(ID::R); + val_srst = type_str[11] == '1' ? State::S1 : State::S0; + has_ce = true; + pol_ce = type_str[12] == 'P'; + sig_ce = cell->getPort(ID::E); + ce_over_srst = true; + } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { + is_fine = true; + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::E); + } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { + is_fine = true; + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::E); + has_arst = true; + pol_arst = type_str[10] == 'P'; + sig_arst = cell->getPort(ID::R); + val_arst = type_str[11] == '1' ? State::S1 : State::S0; + } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { + is_fine = true; + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[11] == 'P'; + sig_aload = cell->getPort(ID::E); + has_sr = true; + pol_set = type_str[12] == 'P'; + pol_clr = type_str[13] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + } else { + log_assert(0); + } + if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) { + // Plain D latches with const D treated specially. + has_aload = false; + has_arst = true; + sig_arst = sig_aload; + pol_arst = pol_aload; + val_arst = sig_ad.as_const(); + } +} + +FfData FfData::slice(const std::vector<int> &bits) { + FfData res(module, initvals, NEW_ID); + res.sig_clk = sig_clk; + res.sig_ce = sig_ce; + res.sig_aload = sig_aload; + res.sig_arst = sig_arst; + res.sig_srst = sig_srst; + res.has_clk = has_clk; + res.has_gclk = has_gclk; + res.has_ce = has_ce; + res.has_aload = has_aload; + res.has_arst = has_arst; + res.has_srst = has_srst; + res.has_sr = has_sr; + res.ce_over_srst = ce_over_srst; + res.is_fine = is_fine; + res.pol_clk = pol_clk; + res.pol_ce = pol_ce; + res.pol_aload = pol_aload; + res.pol_arst = pol_arst; + res.pol_srst = pol_srst; + res.pol_clr = pol_clr; + res.pol_set = pol_set; + res.attributes = attributes; + for (int i : bits) { + res.sig_q.append(sig_q[i]); + if (has_clk || has_gclk) + res.sig_d.append(sig_d[i]); + if (has_aload) + res.sig_ad.append(sig_ad[i]); + if (has_sr) { + res.sig_clr.append(sig_clr[i]); + res.sig_set.append(sig_set[i]); + } + if (has_arst) + res.val_arst.bits.push_back(val_arst[i]); + if (has_srst) + res.val_srst.bits.push_back(val_srst[i]); + if (initvals) + res.val_init.bits.push_back(val_init[i]); + } + res.width = GetSize(res.sig_q); + return res; +} + +void FfData::add_dummy_ce() { + if (has_ce) + return; + has_ce = true; + pol_ce = true; + sig_ce = State::S1; + ce_over_srst = false; +} + +void FfData::add_dummy_srst() { + if (has_srst) + return; + has_srst = true; + pol_srst = true; + sig_srst = State::S0; + val_srst = Const(State::Sx, width); + ce_over_srst = false; +} + +void FfData::add_dummy_arst() { + if (has_arst) + return; + has_arst = true; + pol_arst = true; + sig_arst = State::S0; + val_arst = Const(State::Sx, width); +} + +void FfData::add_dummy_aload() { + if (has_aload) + return; + has_aload = true; + pol_aload = true; + sig_aload = State::S0; + sig_ad = Const(State::Sx, width); +} + +void FfData::add_dummy_sr() { + if (has_sr) + return; + has_sr = true; + pol_clr = true; + pol_set = true; + sig_clr = Const(State::S0, width); + sig_set = Const(State::S0, width); +} + +void FfData::add_dummy_clk() { + if (has_clk) + return; + has_clk = true; + pol_clk = true; + sig_clk = State::S0; + sig_d = Const(State::Sx, width); +} + +void FfData::arst_to_aload() { + log_assert(has_arst); + log_assert(!has_aload); + pol_aload = pol_arst; + sig_aload = sig_arst; + sig_ad = val_arst; + has_aload = true; + has_arst = false; +} + +void FfData::arst_to_sr() { + log_assert(has_arst); + log_assert(!has_sr); + pol_clr = pol_arst; + pol_set = pol_arst; + sig_clr = Const(pol_arst ? State::S0 : State::S1, width); + sig_set = Const(pol_arst ? State::S0 : State::S1, width); + has_sr = true; + has_arst = false; + for (int i = 0; i < width; i++) { + if (val_arst[i] == State::S1) + sig_set[i] = sig_arst; + else + sig_clr[i] = sig_arst; + } +} + +void FfData::aload_to_sr() { + log_assert(has_aload); + log_assert(!has_sr); + has_sr = true; + has_aload = false; + if (!is_fine) { + pol_clr = false; + pol_set = true; + if (pol_aload) { + sig_clr = module->Mux(NEW_ID, Const(State::S1, width), sig_ad, sig_aload); + sig_set = module->Mux(NEW_ID, Const(State::S0, width), sig_ad, sig_aload); + } else { + sig_clr = module->Mux(NEW_ID, sig_ad, Const(State::S1, width), sig_aload); + sig_set = module->Mux(NEW_ID, sig_ad, Const(State::S0, width), sig_aload); + } + } else { + pol_clr = pol_aload; + pol_set = pol_aload; + if (pol_aload) { + sig_clr = module->AndnotGate(NEW_ID, sig_aload, sig_ad); + sig_set = module->AndGate(NEW_ID, sig_aload, sig_ad); + } else { + sig_clr = module->OrGate(NEW_ID, sig_aload, sig_ad); + sig_set = module->OrnotGate(NEW_ID, sig_aload, sig_ad); + } + } +} + +void FfData::convert_ce_over_srst(bool val) { + if (!has_ce || !has_srst || ce_over_srst == val) + return; + if (val) { + // sdffe to sdffce + if (!is_fine) { + if (pol_ce) { + if (pol_srst) { + sig_ce = module->Or(NEW_ID, sig_ce, sig_srst); + } else { + SigSpec tmp = module->Not(NEW_ID, sig_srst); + sig_ce = module->Or(NEW_ID, sig_ce, tmp); + } + } else { + if (pol_srst) { + SigSpec tmp = module->Not(NEW_ID, sig_srst); + sig_ce = module->And(NEW_ID, sig_ce, tmp); + } else { + sig_ce = module->And(NEW_ID, sig_ce, sig_srst); + } + } + } else { + if (pol_ce) { + if (pol_srst) { + sig_ce = module->OrGate(NEW_ID, sig_ce, sig_srst); + } else { + sig_ce = module->OrnotGate(NEW_ID, sig_ce, sig_srst); + } + } else { + if (pol_srst) { + sig_ce = module->AndnotGate(NEW_ID, sig_ce, sig_srst); + } else { + sig_ce = module->AndGate(NEW_ID, sig_ce, sig_srst); + } + } + } + } else { + // sdffce to sdffe + if (!is_fine) { + if (pol_srst) { + if (pol_ce) { + sig_srst = cell->module->And(NEW_ID, sig_srst, sig_ce); + } else { + SigSpec tmp = module->Not(NEW_ID, sig_ce); + sig_srst = cell->module->And(NEW_ID, sig_srst, tmp); + } + } else { + if (pol_ce) { + SigSpec tmp = module->Not(NEW_ID, sig_ce); + sig_srst = cell->module->Or(NEW_ID, sig_srst, tmp); + } else { + sig_srst = cell->module->Or(NEW_ID, sig_srst, sig_ce); + } + } + } else { + if (pol_srst) { + if (pol_ce) { + sig_srst = cell->module->AndGate(NEW_ID, sig_srst, sig_ce); + } else { + sig_srst = cell->module->AndnotGate(NEW_ID, sig_srst, sig_ce); + } + } else { + if (pol_ce) { + sig_srst = cell->module->OrnotGate(NEW_ID, sig_srst, sig_ce); + } else { + sig_srst = cell->module->OrGate(NEW_ID, sig_srst, sig_ce); + } + } + } + } + ce_over_srst = val; +} + +void FfData::unmap_ce() { + if (!has_ce) + return; + log_assert(has_clk); + if (has_srst && ce_over_srst) + unmap_srst(); + + if (!is_fine) { + if (pol_ce) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_ce); + else + sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_ce); + } else { + if (pol_ce) + sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_ce); + else + sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_ce); + } + has_ce = false; +} + +void FfData::unmap_srst() { + if (!has_srst) + return; + if (has_ce && !ce_over_srst) + unmap_ce(); + + if (!is_fine) { + if (pol_srst) + sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst); + else + sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst); + } else { + if (pol_srst) + sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst); + else + sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst); + } + has_srst = false; +} + +Cell *FfData::emit() { + remove(); + if (!width) + return nullptr; + if (!has_aload && !has_clk && !has_gclk && !has_sr) { + if (has_arst) { + // Convert this case to a D latch. + arst_to_aload(); + } else { + // No control inputs left. Turn into a const driver. + module->connect(sig_q, val_init); + return nullptr; + } + } + if (initvals) + initvals->set_init(sig_q, val_init); + if (!is_fine) { + if (has_gclk) { + log_assert(!has_clk); + log_assert(!has_ce); + log_assert(!has_aload); + log_assert(!has_arst); + log_assert(!has_srst); + log_assert(!has_sr); + cell = module->addFf(name, sig_d, sig_q); + } else if (!has_aload && !has_clk) { + log_assert(has_sr); + cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); + } else if (!has_clk) { + log_assert(!has_srst); + if (has_sr) + cell = module->addDlatchsr(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr); + else if (has_arst) + cell = module->addAdlatch(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst, pol_aload, pol_arst); + else + cell = module->addDlatch(name, sig_aload, sig_ad, sig_q, pol_aload); + } else { + if (has_sr) { + if (has_ce) + cell = module->addDffsre(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr); + else + cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); + } else if (has_arst) { + if (has_ce) + cell = module->addAdffe(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_ce, pol_arst); + else + cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst); + } else if (has_aload) { + if (has_ce) + cell = module->addAldffe(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload); + else + cell = module->addAldff(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload); + } else if (has_srst) { + if (has_ce) + if (ce_over_srst) + cell = module->addSdffce(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst); + else + cell = module->addSdffe(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst); + else + cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst); + } else { + if (has_ce) + cell = module->addDffe(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce); + else + cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk); + } + } + } else { + if (has_gclk) { + log_assert(!has_clk); + log_assert(!has_ce); + log_assert(!has_aload); + log_assert(!has_arst); + log_assert(!has_srst); + log_assert(!has_sr); + cell = module->addFfGate(name, sig_d, sig_q); + } else if (!has_aload && !has_clk) { + log_assert(has_sr); + cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); + } else if (!has_clk) { + log_assert(!has_srst); + if (has_sr) + cell = module->addDlatchsrGate(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr); + else if (has_arst) + cell = module->addAdlatchGate(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst.as_bool(), pol_aload, pol_arst); + else + cell = module->addDlatchGate(name, sig_aload, sig_ad, sig_q, pol_aload); + } else { + if (has_sr) { + if (has_ce) + cell = module->addDffsreGate(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr); + else + cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); + } else if (has_arst) { + if (has_ce) + cell = module->addAdffeGate(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_ce, pol_arst); + else + cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst); + } else if (has_aload) { + if (has_ce) + cell = module->addAldffeGate(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload); + else + cell = module->addAldffGate(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload); + } else if (has_srst) { + if (has_ce) + if (ce_over_srst) + cell = module->addSdffceGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst); + else + cell = module->addSdffeGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst); + else + cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst); + } else { + if (has_ce) + cell = module->addDffeGate(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce); + else + cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk); + } + } + } + cell->attributes = attributes; + return cell; +} + +void FfData::remove() { + if (cell) { + remove_init(); + module->remove(cell); + cell = nullptr; + } +} + +namespace { + State invert(State s) { + switch (s) { + case State::S0: return State::S1; + case State::S1: return State::S0; + default: return s; + } + } +} + +void FfData::flip_bits(const pool<int> &bits) { + if (!bits.size()) + return; + + remove_init(); + + Wire *new_q = module->addWire(NEW_ID, width); + + for (auto bit: bits) { + if (has_arst) + val_arst[bit] = invert(val_arst[bit]); + if (has_srst) + val_srst[bit] = invert(val_srst[bit]); + val_init[bit] = invert(val_init[bit]); + } + + if (has_sr && cell) { + log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", log_id(module->name), log_id(cell->name), log_id(cell->type)); + } + + if (is_fine) { + if (has_sr) { + bool new_pol_clr = pol_set; + SigSpec new_sig_clr; + if (pol_set) { + if (pol_clr) { + new_sig_clr = module->AndnotGate(NEW_ID, sig_set, sig_clr); + } else { + new_sig_clr = module->AndGate(NEW_ID, sig_set, sig_clr); + } + } else { + if (pol_clr) { + new_sig_clr = module->OrGate(NEW_ID, sig_set, sig_clr); + } else { + new_sig_clr = module->OrnotGate(NEW_ID, sig_set, sig_clr); + } + } + pol_set = pol_clr; + sig_set = sig_clr; + pol_clr = new_pol_clr; + sig_clr = new_sig_clr; + } + if (has_clk || has_gclk) + sig_d = module->NotGate(NEW_ID, sig_d); + if (has_aload) + sig_ad = module->NotGate(NEW_ID, sig_ad); + module->addNotGate(NEW_ID, new_q, sig_q); + } + else + { + if (has_sr) { + SigSpec not_clr; + if (!pol_clr) { + not_clr = sig_clr; + sig_clr = module->Not(NEW_ID, sig_clr); + pol_clr = true; + } else { + not_clr = module->Not(NEW_ID, sig_clr); + } + if (!pol_set) { + sig_set = module->Not(NEW_ID, sig_set); + pol_set = true; + } + + SigSpec masked_set = module->And(NEW_ID, sig_set, not_clr); + for (auto bit: bits) { + sig_set[bit] = sig_clr[bit]; + sig_clr[bit] = masked_set[bit]; + } + } + + Const mask = Const(State::S0, width); + for (auto bit: bits) + mask.bits[bit] = State::S1; + + if (has_clk || has_gclk) + sig_d = module->Xor(NEW_ID, sig_d, mask); + if (has_aload) + sig_ad = module->Xor(NEW_ID, sig_ad, mask); + module->addXor(NEW_ID, new_q, mask, sig_q); + } + + sig_q = new_q; +} diff --git a/kernel/ff.h b/kernel/ff.h index 0aecbaa2a..5a629d5dd 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -25,460 +25,190 @@ YOSYS_NAMESPACE_BEGIN +// Describes a flip-flop or a latch. +// +// If has_gclk, this is a formal verification FF with implicit global clock: +// Q is simply previous cycle's D. +// +// Otherwise, the FF/latch can have any number of features selected by has_* +// attributes that determine Q's value (in order of decreasing priority): +// +// - on start, register is initialized to val_init +// - if has_sr is present: +// - sig_clr is per-bit async clear, and sets the corresponding bit to 0 +// if active +// - sig_set is per-bit async set, and sets the corresponding bit to 1 +// if active +// - if has_arst is present: +// - sig_arst is whole-reg async reset, and sets the whole register to val_arst +// - if has_aload is present: +// - sig_aload is whole-reg async load (aka latch gate enable), and sets the whole +// register to sig_ad +// - if has_clk is present, and we're currently on a clock edge: +// - if has_ce is present and ce_over_srst is true: +// - ignore clock edge (don't change value) unless sig_ce is active +// - if has_srst is present: +// - sig_srst is whole-reg sync reset and sets the register to val_srst +// - if has_ce is present and ce_over_srst is false: +// - ignore clock edge (don't change value) unless sig_ce is active +// - set whole reg to sig_d +// - if nothing of the above applies, the reg value remains unchanged +// +// Since the yosys FF cell library isn't fully generic, not all combinations +// of the features above can be supported: +// +// - only one of has_srst, has_arst, has_sr can be used +// - if has_clk is used together with has_aload, then has_srst, has_arst, +// has_sr cannot be used +// +// The valid feature combinations are thus: +// +// - has_clk + optional has_ce [dff/dffe] +// - has_clk + optional has_ce + has_arst [adff/adffe] +// - has_clk + optional has_ce + has_aload [aldff/aldffe] +// - has_clk + optional has_ce + has_sr [dffsr/dffsre] +// - has_clk + optional has_ce + has_srst [sdff/sdffe/sdffce] +// - has_aload [dlatch] +// - has_aload + has_arst [adlatch] +// - has_aload + has_sr [dlatchsr] +// - has_sr [sr] +// - has_arst [does not correspond to a native cell, represented as dlatch with const D input] +// - empty set [not a cell — will be emitted as a simple direct connection] + struct FfData { + Module *module; FfInitVals *initvals; + Cell *cell; + IdString name; + // The FF output. SigSpec sig_q; + // The sync data input, present if has_clk or has_gclk. SigSpec sig_d; + // The async data input, present if has_aload. + SigSpec sig_ad; + // The sync clock, present if has_clk. SigSpec sig_clk; - SigSpec sig_en; + // The clock enable, present if has_ce. + SigSpec sig_ce; + // The async load enable, present if has_aload. + SigSpec sig_aload; + // The async reset, preset if has_arst. SigSpec sig_arst; + // The sync reset, preset if has_srst. SigSpec sig_srst; + // The async clear (per-lane), present if has_sr. SigSpec sig_clr; + // The async set (per-lane), present if has_sr. SigSpec sig_set; - bool has_d; + // True if this is a clocked (edge-sensitive) flip-flop. bool has_clk; - bool has_en; + // True if this is a $ff, exclusive with every other has_*. + bool has_gclk; + // True if this FF has a clock enable. Depends on has_clk. + bool has_ce; + // True if this FF has async load function — this includes D latches. + // If this and has_clk are both set, has_arst and has_sr cannot be set. + bool has_aload; + // True if this FF has sync set/reset. Depends on has_clk, exclusive + // with has_arst, has_sr, has_aload. bool has_srst; + // True if this FF has async set/reset. Exclusive with has_srst, + // has_sr. If this and has_clk are both set, has_aload cannot be set. bool has_arst; + // True if this FF has per-bit async set + clear. Exclusive with + // has_srst, has_arst. If this and has_clk are both set, has_aload + // cannot be set. bool has_sr; + // If has_ce and has_srst are both set, determines their relative + // priorities: if true, inactive ce disables srst; if false, srst + // operates independent of ce. bool ce_over_srst; + // True if this FF is a fine cell, false if it is a coarse cell. + // If true, width must be 1. bool is_fine; + // Polarities, corresponding to sig_*. True means active-high, false + // means active-low. bool pol_clk; - bool pol_en; + bool pol_ce; + bool pol_aload; bool pol_arst; bool pol_srst; bool pol_clr; bool pol_set; + // The value loaded by sig_arst. Const val_arst; + // The value loaded by sig_srst. Const val_srst; + // The initial value at power-up. Const val_init; - Const val_d; - bool d_is_const; + // The FF data width in bits. int width; dict<IdString, Const> attributes; - FfData(FfInitVals *initvals, Cell *cell = nullptr) : initvals(initvals) { + FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) { width = 0; - has_d = true; has_clk = false; - has_en = false; + has_gclk = false; + has_ce = false; + has_aload = false; has_srst = false; has_arst = false; has_sr = false; ce_over_srst = false; is_fine = false; pol_clk = false; - pol_en = false; + pol_aload = false; + pol_ce = false; pol_arst = false; pol_srst = false; pol_clr = false; pol_set = false; - d_is_const = false; + } - if (!cell) - return; + FfData(FfInitVals *initvals, Cell *cell_); - sig_q = cell->getPort(ID::Q); - width = GetSize(sig_q); - attributes = cell->attributes; + // Returns a FF identical to this one, but only keeping bit indices from the argument. + FfData slice(const std::vector<int> &bits); - if (initvals) - val_init = (*initvals)(sig_q); + void add_dummy_ce(); + void add_dummy_srst(); + void add_dummy_arst(); + void add_dummy_aload(); + void add_dummy_sr(); + void add_dummy_clk(); - std::string type_str = cell->type.str(); + void arst_to_aload(); + void arst_to_sr(); - if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - if (cell->type == ID($sr)) { - has_d = false; - } else { - sig_d = cell->getPort(ID::D); - } - if (!cell->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - has_clk = true; - sig_clk = cell->getPort(ID::CLK); - pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); - } - if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr))) { - has_en = true; - sig_en = cell->getPort(ID::EN); - pol_en = cell->getParam(ID::EN_POLARITY).as_bool(); - } - if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { - has_sr = true; - sig_clr = cell->getPort(ID::CLR); - sig_set = cell->getPort(ID::SET); - pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool(); - pol_set = cell->getParam(ID::SET_POLARITY).as_bool(); - } - if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) { - has_arst = true; - sig_arst = cell->getPort(ID::ARST); - pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool(); - val_arst = cell->getParam(ID::ARST_VALUE); - } - if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { - has_srst = true; - sig_srst = cell->getPort(ID::SRST); - pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool(); - val_srst = cell->getParam(ID::SRST_VALUE); - ce_over_srst = cell->type == ID($sdffce); - } - } else if (cell->type == ID($_FF_)) { - is_fine = true; - sig_d = cell->getPort(ID::D); - } else if (type_str.substr(0, 5) == "$_SR_") { - is_fine = true; - has_d = false; - has_sr = true; - pol_set = type_str[5] == 'P'; - pol_clr = type_str[6] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[6] == 'P'; - sig_clk = cell->getPort(ID::C); - } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_en = true; - pol_en = type_str[8] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[6] == 'P'; - sig_clk = cell->getPort(ID::C); - has_arst = true; - pol_arst = type_str[7] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[8] == '1' ? State::S1 : State::S0; - } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_arst = true; - pol_arst = type_str[8] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[9] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[10] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_sr = true; - pol_set = type_str[9] == 'P'; - pol_clr = type_str[10] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_sr = true; - pol_set = type_str[10] == 'P'; - pol_clr = type_str[11] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - has_en = true; - pol_en = type_str[12] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[8] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[9] == '1' ? State::S1 : State::S0; - } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[9] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[10] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[11] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[10] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[11] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[12] == 'P'; - sig_en = cell->getPort(ID::E); - ce_over_srst = true; - } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[9] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[9] == 'P'; - sig_en = cell->getPort(ID::E); - has_arst = true; - pol_arst = type_str[10] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[11] == '1' ? State::S1 : State::S0; - } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[11] == 'P'; - sig_en = cell->getPort(ID::E); - has_sr = true; - pol_set = type_str[12] == 'P'; - pol_clr = type_str[13] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - } else { - log_assert(0); - } - if (has_d && sig_d.is_fully_const()) { - d_is_const = true; - val_d = sig_d.as_const(); - if (has_en && !has_clk && !has_sr && !has_arst) { - // Plain D latches with const D treated specially. - has_en = has_d = false; - has_arst = true; - sig_arst = sig_en; - pol_arst = pol_en; - val_arst = val_d; - } - } - } + void aload_to_sr(); - // Returns a FF identical to this one, but only keeping bit indices from the argument. - FfData slice(const std::vector<int> &bits) { - FfData res(initvals); - res.sig_clk = sig_clk; - res.sig_en = sig_en; - res.sig_arst = sig_arst; - res.sig_srst = sig_srst; - res.has_d = has_d; - res.has_clk = has_clk; - res.has_en = has_en; - res.has_arst = has_arst; - res.has_srst = has_srst; - res.has_sr = has_sr; - res.ce_over_srst = ce_over_srst; - res.is_fine = is_fine; - res.pol_clk = pol_clk; - res.pol_en = pol_en; - res.pol_arst = pol_arst; - res.pol_srst = pol_srst; - res.pol_clr = pol_clr; - res.pol_set = pol_set; - res.attributes = attributes; - for (int i : bits) { - res.sig_q.append(sig_q[i]); - if (has_d) - res.sig_d.append(sig_d[i]); - if (has_sr) { - res.sig_clr.append(sig_clr[i]); - res.sig_set.append(sig_set[i]); - } - if (has_arst) - res.val_arst.bits.push_back(val_arst[i]); - if (has_srst) - res.val_srst.bits.push_back(val_srst[i]); - res.val_init.bits.push_back(val_init[i]); - } - res.width = GetSize(res.sig_q); - // Slicing bits out may cause D to become const. - if (has_d && res.sig_d.is_fully_const()) { - res.d_is_const = true; - res.val_d = res.sig_d.as_const(); - } - return res; - } + // Given a FF with both has_ce and has_srst, sets ce_over_srst to the given value and + // fixes up control signals appropriately to preserve semantics. + void convert_ce_over_srst(bool val); - void unmap_ce(Module *module) { - if (!has_en) - return; - log_assert(has_clk); - if (has_srst && ce_over_srst) - unmap_srst(module); + void unmap_ce(); + void unmap_srst(); - if (!is_fine) { - if (pol_en) - sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_en); - else - sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_en); - } else { - if (pol_en) - sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_en); - else - sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_en); - } - has_en = false; + void unmap_ce_srst() { + unmap_ce(); + unmap_srst(); } - void unmap_srst(Module *module) { - if (!has_srst) - return; - if (has_en && !ce_over_srst) - unmap_ce(module); + Cell *emit(); - if (!is_fine) { - if (pol_srst) - sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst); - else - sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst); - } else { - if (pol_srst) - sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst); - else - sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst); - } - has_srst = false; + // Removes init attribute from the Q output, but keeps val_init unchanged. + // It will be automatically reattached on emit. Use this before changing sig_q. + void remove_init() { + if (initvals) + initvals->remove_init(sig_q); } - void unmap_ce_srst(Module *module) { - unmap_ce(module); - unmap_srst(module); - } + void remove(); - Cell *emit(Module *module, IdString name) { - if (!width) - return nullptr; - if (!has_d && !has_sr) { - if (has_arst) { - // Convert this case to a D latch. - has_d = has_en = true; - has_arst = false; - sig_d = val_arst; - sig_en = sig_arst; - pol_en = pol_arst; - } else { - // No control inputs left. Turn into a const driver. - initvals->remove_init(sig_q); - module->connect(sig_q, val_init); - return nullptr; - } - } - initvals->set_init(sig_q, val_init); - Cell *cell; - if (!is_fine) { - if (!has_d) { - log_assert(has_sr); - cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); - } else if (!has_clk && !has_en) { - log_assert(!has_arst); - log_assert(!has_srst); - log_assert(!has_sr); - cell = module->addFf(name, sig_d, sig_q); - } else if (!has_clk) { - log_assert(!has_srst); - if (has_sr) - cell = module->addDlatchsr(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr); - else if (has_arst) - cell = module->addAdlatch(name, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_en, pol_arst); - else - cell = module->addDlatch(name, sig_en, sig_d, sig_q, pol_en); - } else { - if (has_sr) { - if (has_en) - cell = module->addDffsre(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); - else - cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); - } else if (has_arst) { - if (has_en) - cell = module->addAdffe(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_en, pol_arst); - else - cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst); - } else if (has_srst) { - if (has_en) - if (ce_over_srst) - cell = module->addSdffce(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst); - else - cell = module->addSdffe(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst); - else - cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst); - } else { - if (has_en) - cell = module->addDffe(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); - else - cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk); - } - } - } else { - if (!has_d) { - log_assert(has_sr); - cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); - } else if (!has_clk && !has_en) { - log_assert(!has_arst); - log_assert(!has_srst); - log_assert(!has_sr); - cell = module->addFfGate(name, sig_d, sig_q); - } else if (!has_clk) { - log_assert(!has_srst); - if (has_sr) - cell = module->addDlatchsrGate(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr); - else if (has_arst) - cell = module->addAdlatchGate(name, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_en, pol_arst); - else - cell = module->addDlatchGate(name, sig_en, sig_d, sig_q, pol_en); - } else { - if (has_sr) { - if (has_en) - cell = module->addDffsreGate(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); - else - cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); - } else if (has_arst) { - if (has_en) - cell = module->addAdffeGate(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_en, pol_arst); - else - cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst); - } else if (has_srst) { - if (has_en) - if (ce_over_srst) - cell = module->addSdffceGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst); - else - cell = module->addSdffeGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst); - else - cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst); - } else { - if (has_en) - cell = module->addDffeGate(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); - else - cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk); - } - } - } - cell->attributes = attributes; - return cell; - } + // Flip the sense of the given bit slices of the FF: insert inverters on data + // inputs and output, flip the corresponding init/reset bits, swap clr/set + // inputs with proper priority fix. + void flip_bits(const pool<int> &bits); }; YOSYS_NAMESPACE_END diff --git a/kernel/ffinit.h b/kernel/ffinit.h index 025b0c862..9d33ac572 100644 --- a/kernel/ffinit.h +++ b/kernel/ffinit.h @@ -28,7 +28,6 @@ YOSYS_NAMESPACE_BEGIN struct FfInitVals { const SigMap *sigmap; - RTLIL::Module *module; dict<SigBit, std::pair<State,SigBit>> initbits; void set(const SigMap *sigmap_, RTLIL::Module *module) diff --git a/kernel/ffmerge.cc b/kernel/ffmerge.cc new file mode 100644 index 000000000..c65108413 --- /dev/null +++ b/kernel/ffmerge.cc @@ -0,0 +1,359 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/ffmerge.h" + +USING_YOSYS_NAMESPACE + +bool FfMergeHelper::is_output_unused(RTLIL::SigSpec sig) { + for (auto bit : (*sigmap)(sig)) + if (sigbit_users_count[bit] != 0) + return false; + return true; +} + +bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits) { + ff = FfData(module, initvals, NEW_ID); + sigmap->apply(sig); + + bool found = false; + + for (auto bit : sig) + { + if (bit.wire == NULL || sigbit_users_count[bit] == 0) { + ff.width++; + ff.sig_q.append(bit); + ff.sig_d.append(bit); + ff.sig_clr.append(State::Sx); + ff.sig_set.append(State::Sx); + ff.val_init.bits.push_back(State::Sx); + ff.val_srst.bits.push_back(State::Sx); + ff.val_arst.bits.push_back(State::Sx); + continue; + } + + if (sigbit_users_count[bit] != 1) + return false; + + auto &sinks = dff_sink[bit]; + if (sinks.size() != 1) + return false; + + Cell *cell; + int idx; + std::tie(cell, idx) = *sinks.begin(); + bits.insert(std::make_pair(cell, idx)); + + FfData cur_ff(initvals, cell); + + // Reject latches and $ff. + if (!cur_ff.has_clk) + return false; + + log_assert((*sigmap)(cur_ff.sig_d[idx]) == bit); + + if (!found) { + ff.sig_clk = cur_ff.sig_clk; + ff.sig_ce = cur_ff.sig_ce; + ff.sig_aload = cur_ff.sig_aload; + ff.sig_srst = cur_ff.sig_srst; + ff.sig_arst = cur_ff.sig_arst; + ff.has_clk = cur_ff.has_clk; + ff.has_ce = cur_ff.has_ce; + ff.has_aload = cur_ff.has_aload; + ff.has_srst = cur_ff.has_srst; + ff.has_arst = cur_ff.has_arst; + ff.has_sr = cur_ff.has_sr; + ff.ce_over_srst = cur_ff.ce_over_srst; + ff.pol_clk = cur_ff.pol_clk; + ff.pol_ce = cur_ff.pol_ce; + ff.pol_aload = cur_ff.pol_aload; + ff.pol_arst = cur_ff.pol_arst; + ff.pol_srst = cur_ff.pol_srst; + ff.pol_clr = cur_ff.pol_clr; + ff.pol_set = cur_ff.pol_set; + } else { + if (ff.has_clk != cur_ff.has_clk) + return false; + if (ff.has_ce != cur_ff.has_ce) + return false; + if (ff.has_aload != cur_ff.has_aload) + return false; + if (ff.has_srst != cur_ff.has_srst) + return false; + if (ff.has_arst != cur_ff.has_arst) + return false; + if (ff.has_sr != cur_ff.has_sr) + return false; + if (ff.has_clk) { + if (ff.sig_clk != cur_ff.sig_clk) + return false; + if (ff.pol_clk != cur_ff.pol_clk) + return false; + } + if (ff.has_ce) { + if (ff.sig_ce != cur_ff.sig_ce) + return false; + if (ff.pol_ce != cur_ff.pol_ce) + return false; + } + if (ff.has_aload) { + if (ff.sig_aload != cur_ff.sig_aload) + return false; + if (ff.pol_aload != cur_ff.pol_aload) + return false; + } + if (ff.has_srst) { + if (ff.sig_srst != cur_ff.sig_srst) + return false; + if (ff.pol_srst != cur_ff.pol_srst) + return false; + if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst) + return false; + } + if (ff.has_arst) { + if (ff.sig_arst != cur_ff.sig_arst) + return false; + if (ff.pol_arst != cur_ff.pol_arst) + return false; + } + if (ff.has_sr) { + if (ff.pol_clr != cur_ff.pol_clr) + return false; + if (ff.pol_set != cur_ff.pol_set) + return false; + } + } + + ff.width++; + ff.sig_d.append(cur_ff.sig_d[idx]); + ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx); + ff.sig_q.append(cur_ff.sig_q[idx]); + ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0); + ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0); + ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx); + ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx); + ff.val_init.bits.push_back(cur_ff.val_init[idx]); + found = true; + } + + return found; +} + +bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits) { + ff = FfData(module, initvals, NEW_ID); + sigmap->apply(sig); + + bool found = false; + + pool<int> const_bits; + + for (auto bit : sig) + { + if (bit.wire == NULL) { + const_bits.insert(ff.width); + ff.width++; + ff.sig_q.append(bit); + ff.sig_d.append(bit); + // These two will be fixed up later. + ff.sig_clr.append(State::Sx); + ff.sig_set.append(State::Sx); + ff.val_init.bits.push_back(bit.data); + ff.val_srst.bits.push_back(bit.data); + ff.val_arst.bits.push_back(bit.data); + continue; + } + + if (!dff_driver.count(bit)) + return false; + + Cell *cell; + int idx; + std::tie(cell, idx) = dff_driver[bit]; + bits.insert(std::make_pair(cell, idx)); + + FfData cur_ff(initvals, cell); + + log_assert((*sigmap)(cur_ff.sig_q[idx]) == bit); + + if (!found) { + ff.sig_clk = cur_ff.sig_clk; + ff.sig_ce = cur_ff.sig_ce; + ff.sig_aload = cur_ff.sig_aload; + ff.sig_srst = cur_ff.sig_srst; + ff.sig_arst = cur_ff.sig_arst; + ff.has_clk = cur_ff.has_clk; + ff.has_gclk = cur_ff.has_gclk; + ff.has_ce = cur_ff.has_ce; + ff.has_aload = cur_ff.has_aload; + ff.has_srst = cur_ff.has_srst; + ff.has_arst = cur_ff.has_arst; + ff.has_sr = cur_ff.has_sr; + ff.ce_over_srst = cur_ff.ce_over_srst; + ff.pol_clk = cur_ff.pol_clk; + ff.pol_ce = cur_ff.pol_ce; + ff.pol_aload = cur_ff.pol_aload; + ff.pol_arst = cur_ff.pol_arst; + ff.pol_srst = cur_ff.pol_srst; + ff.pol_clr = cur_ff.pol_clr; + ff.pol_set = cur_ff.pol_set; + } else { + if (ff.has_gclk != cur_ff.has_gclk) + return false; + if (ff.has_clk != cur_ff.has_clk) + return false; + if (ff.has_ce != cur_ff.has_ce) + return false; + if (ff.has_aload != cur_ff.has_aload) + return false; + if (ff.has_srst != cur_ff.has_srst) + return false; + if (ff.has_arst != cur_ff.has_arst) + return false; + if (ff.has_sr != cur_ff.has_sr) + return false; + if (ff.has_clk) { + if (ff.sig_clk != cur_ff.sig_clk) + return false; + if (ff.pol_clk != cur_ff.pol_clk) + return false; + } + if (ff.has_ce) { + if (ff.sig_ce != cur_ff.sig_ce) + return false; + if (ff.pol_ce != cur_ff.pol_ce) + return false; + } + if (ff.has_aload) { + if (ff.sig_aload != cur_ff.sig_aload) + return false; + if (ff.pol_aload != cur_ff.pol_aload) + return false; + } + if (ff.has_srst) { + if (ff.sig_srst != cur_ff.sig_srst) + return false; + if (ff.pol_srst != cur_ff.pol_srst) + return false; + if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst) + return false; + } + if (ff.has_arst) { + if (ff.sig_arst != cur_ff.sig_arst) + return false; + if (ff.pol_arst != cur_ff.pol_arst) + return false; + } + if (ff.has_sr) { + if (ff.pol_clr != cur_ff.pol_clr) + return false; + if (ff.pol_set != cur_ff.pol_set) + return false; + } + } + + ff.width++; + ff.sig_d.append((ff.has_clk || ff.has_gclk) ? cur_ff.sig_d[idx] : State::Sx); + ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx); + ff.sig_q.append(cur_ff.sig_q[idx]); + ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0); + ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0); + ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx); + ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx); + ff.val_init.bits.push_back(cur_ff.val_init[idx]); + found = true; + } + + if (found && ff.has_sr) { + for (auto i: const_bits) { + if (ff.sig_d[i] == State::S0) { + ff.sig_set[i] = ff.pol_set ? State::S0 : State::S1; + } else if (ff.sig_d[i] == State::S1) { + ff.sig_clr[i] = ff.pol_clr ? State::S0 : State::S1; + } + } + } + + return found; +} + + +void FfMergeHelper::remove_output_ff(const pool<std::pair<Cell *, int>> &bits) { + for (auto &it : bits) { + Cell *cell = it.first; + int idx = it.second; + SigSpec q = cell->getPort(ID::Q); + initvals->remove_init(q[idx]); + dff_driver.erase((*sigmap)(q[idx])); + q[idx] = module->addWire(stringf("$ffmerge_disconnected$%d", autoidx++)); + cell->setPort(ID::Q, q); + } +} + +void FfMergeHelper::mark_input_ff(const pool<std::pair<Cell *, int>> &bits) { + for (auto &it : bits) { + Cell *cell = it.first; + int idx = it.second; + if (cell->hasPort(ID::D)) { + SigSpec d = cell->getPort(ID::D); + // The user count was already at least 1 + // (for the D port). Bump it as it is now connected + // to the merged-to cell as well. This suffices for + // it to not be considered for output merging. + sigbit_users_count[d[idx]]++; + } + } +} + +void FfMergeHelper::set(FfInitVals *initvals_, RTLIL::Module *module_) +{ + clear(); + initvals = initvals_; + sigmap = initvals->sigmap; + module = module_; + + for (auto wire : module->wires()) { + if (wire->port_output) + for (auto bit : (*sigmap)(wire)) + sigbit_users_count[bit]++; + } + + for (auto cell : module->cells()) { + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + if (cell->hasPort(ID::D)) { + SigSpec d = (*sigmap)(cell->getPort(ID::D)); + for (int i = 0; i < GetSize(d); i++) + dff_sink[d[i]].insert(std::make_pair(cell, i)); + } + SigSpec q = (*sigmap)(cell->getPort(ID::Q)); + for (int i = 0; i < GetSize(q); i++) + dff_driver[q[i]] = std::make_pair(cell, i); + } + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : (*sigmap)(conn.second)) + sigbit_users_count[bit]++; + } +} + +void FfMergeHelper::clear() { + dff_driver.clear(); + dff_sink.clear(); + sigbit_users_count.clear(); +} diff --git a/kernel/ffmerge.h b/kernel/ffmerge.h new file mode 100644 index 000000000..5428da324 --- /dev/null +++ b/kernel/ffmerge.h @@ -0,0 +1,141 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net> + * + * 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. + * + */ + +#ifndef FFMERGE_H +#define FFMERGE_H + +#include "kernel/ffinit.h" +#include "kernel/ff.h" + +YOSYS_NAMESPACE_BEGIN + +// A helper class for passes that want to merge FFs on the input or output +// of a cell into the cell itself. +// +// The procedure is: +// +// 1. Construct this class (at beginning of processing for a given module). +// 2. For every considered cell: +// +// a. Call find_output_ff for every considered output. +// b. Call find_input_ff for every considered input. +// c. Look at the FF description returned (if any) from each call, reject +// results that cannot be merged into given cell for any reason. +// If both inputs and outputs are being merged, take care of FF bits that +// are returned in both input and output results (a FF bit cannot be +// merged to both). Decide on the final set of FF bits to merge. +// d. Call remove_output_ff for every find_output_ff result that will be used +// for merging. This removes the actual FF bits from design and from index. +// e. Call mark_input_ff for every find_input_ff result that will be used +// for merging. This updates the index disallowing further usage of these +// FF bits for output FF merging, if they were eligible before. The actual +// FF bits are still left in the design and can be merged into other inputs. +// If the FF bits are not otherwise used, they will be removed by later +// opt passes. +// f. Merge the FFs into the cell. +// +// Note that, if both inputs and outputs are being considered for merging in +// a single pass, the result may be nondeterministic (depending on cell iteration +// order) because a given FF bit could be eligible for both input and output merge, +// perhaps in different cells. For this reason, it may be a good idea to separate +// input and output merging. + +struct FfMergeHelper +{ + const SigMap *sigmap; + RTLIL::Module *module; + FfInitVals *initvals; + + dict<SigBit, std::pair<Cell*, int>> dff_driver; + dict<SigBit, pool<std::pair<Cell*, int>>> dff_sink; + dict<SigBit, int> sigbit_users_count; + + // Returns true if all bits in sig are completely unused. + bool is_output_unused(RTLIL::SigSpec sig); + + // Finds the FF to merge into a given cell output. Takes sig, which + // is the current cell output — it will be the sig_d of the found FF. + // If found, returns true, and fills the two output arguments. + // + // For every bit of sig, this function finds a FF bit that has + // the same sig_d, and fills the output FfData according to the FF + // bits found. This function will only consider FF bits that are + // the only user of the given sig bits — if any bit in sig is used + // by anything other than a single FF, this function will return false. + // + // The returned FfData structure does not correspond to any actual FF + // cell in the design — it is the amalgamation of extracted FF bits, + // possibly coming from several FF cells. + // + // If some of the bits in sig have no users at all, this function + // will accept them as well (and fill returned FfData with dummy values + // for the given bit, effectively synthesizing an unused FF bit of the + // appropriate type). However, if all bits in sig are completely + // unused, this function will fail and return false (having no idea + // what kind of FF to produce) — use the above helper if that case + // is important to handle. + // + // Note that this function does not remove the FF bits returned from + // the design — this is so that the caller can decide whether to accept + // this FF for merging or not. If the result is accepted, + // remove_output_ff should be called on the second output argument. + bool find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits); + + // Like above, but returns a FF to merge into a given cell input. Takes + // sig_q, which is the current cell input — it will search for FFs with + // matching sig_q. + // + // As opposed to find_output_ff, this function doesn't care about usage + // counts, and may return FF bits that also have other fanout. This + // should not be a problem for input FF merging. + // + // As a special case, if some of the bits in sig_q are constant, this + // function will accept them as well, by synthesizing in-place + // a constant-input FF bit (with matching initial value and reset value). + // However, this will not work if the input is all-constant — if the caller + // cares about this case, it needs to check for it explicitely. + bool find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits); + + // To be called on find_output_ff result that will be merged. This + // marks the given FF bits as used up (and not to be considered for + // further merging as inputs), and reconnects their Q ports to a dummy + // wire (since the wire previously connected there will now be driven + // by the merged-to cell instead). + void remove_output_ff(const pool<std::pair<Cell *, int>> &bits); + + // To be called on find_input_ff result that will be merged. This + // marks the given FF bits as used, and disallows merging them as + // outputs. They can, however, still be merged as inputs again + // (perhaps for another cell). + void mark_input_ff(const pool<std::pair<Cell *, int>> &bits); + + void set(FfInitVals *initvals_, RTLIL::Module *module_); + + void clear(); + + FfMergeHelper(FfInitVals *initvals, RTLIL::Module *module) { + set(initvals, module); + } + + FfMergeHelper() {} +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc new file mode 100644 index 000000000..330c4d189 --- /dev/null +++ b/kernel/fstdata.cc @@ -0,0 +1,252 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/fstdata.h" + +USING_YOSYS_NAMESPACE + + +FstData::FstData(std::string filename) : ctx(nullptr) +{ + const std::vector<std::string> g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" }; + ctx = (fstReaderContext *)fstReaderOpen(filename.c_str()); + if (!ctx) + log_error("Error opening '%s'\n", filename.c_str()); + int scale = (int)fstReaderGetTimescale(ctx); + timescale = pow(10.0, scale); + timescale_str = ""; + int unit = 0; + int zeros = 0; + if (scale > 0) { + zeros = scale; + } else { + if ((scale % 3) == 0) { + zeros = (-scale % 3); + unit = (-scale / 3); + } else { + zeros = 3 - (-scale % 3); + unit = (-scale / 3) + 1; + } + } + for (int i=0;i<zeros; i++) timescale_str += "0"; + timescale_str += g_units[unit]; + extractVarNames(); +} + +FstData::~FstData() +{ + if (ctx) + fstReaderClose(ctx); +} + +uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); } + +uint64_t FstData::getEndTime() { return fstReaderGetEndTime(ctx); } + +fstHandle FstData::getHandle(std::string name) { + if (name_to_handle.find(name) != name_to_handle.end()) + return name_to_handle[name]; + else + return 0; +}; + +static std::string remove_spaces(std::string str) +{ + str.erase(std::remove(str.begin(), str.end(), ' '), str.end()); + return str; +} + +void FstData::extractVarNames() +{ + struct fstHier *h; + intptr_t snum = 0; + + while ((h = fstReaderIterateHier(ctx))) { + switch (h->htyp) { + case FST_HT_SCOPE: { + snum++; + std::string fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, (void *)(snum)); + scopes.push_back(fst_scope_name); + break; + } + case FST_HT_UPSCOPE: { + fstReaderPopScope(ctx); + snum = fstReaderGetCurrentScopeLen(ctx) ? (intptr_t)fstReaderGetCurrentScopeUserInfo(ctx) : 0; + break; + } + case FST_HT_VAR: { + FstVar var; + var.id = h->u.var.handle; + var.is_alias = h->u.var.is_alias; + var.name = remove_spaces(h->u.var.name); + var.scope = scopes.back(); + var.width = h->u.var.length; + vars.push_back(var); + if (!var.is_alias) + handle_to_var[h->u.var.handle] = var; + std::string clean_name; + for(size_t i=0;i<strlen(h->u.var.name);i++) + { + char c = h->u.var.name[i]; + if(c==' ') break; + clean_name += c; + } + if (clean_name[0]=='\\') + clean_name = clean_name.substr(1); + //log("adding %s.%s\n",var.scope.c_str(), clean_name.c_str()); + + name_to_handle[var.scope+"."+clean_name] = h->u.var.handle; + break; + } + } + } +} + +static void reconstruct_edges_varlen(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen) +{ + FstData *ptr = (FstData*)user_data; + ptr->reconstruct_edges_callback(pnt_time, pnt_facidx, pnt_value, plen); +} + +static void reconstruct_edges(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value) +{ + FstData *ptr = (FstData*)user_data; + uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0; + ptr->reconstruct_edges_callback(pnt_time, pnt_facidx, pnt_value, plen); +} + +void FstData::reconstruct_edges_callback(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */) +{ + std::string val = std::string((const char *)pnt_value); + std::string prev = last_data[pnt_facidx]; + if (pnt_time>=start_time) { + if (prev!="1" && val=="1") + edges.push_back(pnt_time); + if (prev!="0" && val=="0") + edges.push_back(pnt_time); + } + last_data[pnt_facidx] = val; +} + +std::vector<uint64_t> FstData::getAllEdges(std::vector<fstHandle> &signal, uint64_t start, uint64_t end) +{ + start_time = start; + end_time = end; + last_data.clear(); + for(auto &s : signal) { + last_data[s] = "x"; + } + edges.clear(); + fstReaderSetLimitTimeRange(ctx, start_time, end_time); + fstReaderClrFacProcessMaskAll(ctx); + for(const auto sig : signal) + fstReaderSetFacProcessMask(ctx,sig); + fstReaderIterBlocks2(ctx, reconstruct_edges, reconstruct_edges_varlen, this, nullptr); + return edges; +} + +static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen) +{ + FstData *ptr = (FstData*)user_data; + ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen); +} + +static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value) +{ + FstData *ptr = (FstData*)user_data; + uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0; + ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen); +} + +void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */) +{ + if (sample_times_ndx >= sample_times.size()) return; + + uint64_t time = sample_times[sample_times_ndx]; + // if we are past the timestamp + if (pnt_time > time) { + for (auto const& c : last_data) + { + handle_to_data[c.first].push_back(std::make_pair(time,c.second)); + size_t index = handle_to_data[c.first].size() - 1; + time_to_index[c.first][time] = index; + } + sample_times_ndx++; + } + // always update last_data + last_data[pnt_facidx] = std::string((const char *)pnt_value); +} + +void FstData::reconstructAtTimes(std::vector<fstHandle> &signal, std::vector<uint64_t> time) +{ + handle_to_data.clear(); + time_to_index.clear(); + last_data.clear(); + sample_times_ndx = 0; + sample_times = time; + fstReaderSetUnlimitedTimeRange(ctx); + fstReaderClrFacProcessMaskAll(ctx); + for(const auto sig : signal) + fstReaderSetFacProcessMask(ctx,sig); + fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr); + + if (time_to_index[signal.back()].count(time.back())==0) { + for (auto const& c : last_data) + { + handle_to_data[c.first].push_back(std::make_pair(time.back(),c.second)); + size_t index = handle_to_data[c.first].size() - 1; + time_to_index[c.first][time.back()] = index; + } + } +} + +void FstData::reconstructAllAtTimes(std::vector<uint64_t> time) +{ + handle_to_data.clear(); + time_to_index.clear(); + last_data.clear(); + sample_times_ndx = 0; + sample_times = time; + + fstReaderSetUnlimitedTimeRange(ctx); + fstReaderSetFacProcessMaskAll(ctx); + fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr); + + if (time_to_index[1].count(time.back())==0) { + for (auto const& c : last_data) + { + handle_to_data[c.first].push_back(std::make_pair(time.back(),c.second)); + size_t index = handle_to_data[c.first].size() - 1; + time_to_index[c.first][time.back()] = index; + } + } +} + +std::string FstData::valueAt(fstHandle signal, uint64_t time) +{ + if (handle_to_data.find(signal) == handle_to_data.end()) + log_error("Signal id %d not found\n", (int)signal); + auto &data = handle_to_data[signal]; + if (time_to_index[signal].count(time)!=0) { + size_t index = time_to_index[signal][time]; + return data.at(index).second; + } else { + log_error("No data for signal %d at time %d\n", (int)signal, (int)time); + } +} diff --git a/kernel/fstdata.h b/kernel/fstdata.h new file mode 100644 index 000000000..c069ff5e5 --- /dev/null +++ b/kernel/fstdata.h @@ -0,0 +1,81 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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. + * + */ + +#ifndef FSTDATA_H +#define FSTDATA_H + +#include "kernel/yosys.h" +#include "libs/fst/fstapi.h" + +YOSYS_NAMESPACE_BEGIN + +struct FstVar +{ + fstHandle id; + std::string name; + bool is_alias; + std::string scope; + int width; +}; + +class FstData +{ + public: + FstData(std::string filename); + ~FstData(); + + uint64_t getStartTime(); + uint64_t getEndTime(); + + std::vector<FstVar>& getVars() { return vars; }; + + void reconstruct_edges_callback(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen); + std::vector<uint64_t> getAllEdges(std::vector<fstHandle> &signal, uint64_t start_time, uint64_t end_time); + + void reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen); + void reconstructAtTimes(std::vector<fstHandle> &signal,std::vector<uint64_t> time); + void reconstructAllAtTimes(std::vector<uint64_t> time); + + std::string valueAt(fstHandle signal, uint64_t time); + fstHandle getHandle(std::string name); + double getTimescale() { return timescale; } + const char *getTimescaleString() { return timescale_str.c_str(); } +private: + void extractVarNames(); + + struct fstReaderContext *ctx; + std::vector<std::string> scopes; + std::vector<FstVar> vars; + std::map<fstHandle, FstVar> handle_to_var; + std::map<std::string, fstHandle> name_to_handle; + std::map<fstHandle, std::vector<std::pair<uint64_t, std::string>>> handle_to_data; + std::map<fstHandle, std::string> last_data; + std::map<fstHandle, std::map<uint64_t, size_t>> time_to_index; + std::vector<uint64_t> sample_times; + size_t sample_times_ndx; + double timescale; + std::string timescale_str; + uint64_t start_time; + uint64_t end_time; + std::vector<uint64_t> edges; +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/hashlib.h b/kernel/hashlib.h index a523afadd..0c9f25287 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -6,7 +6,7 @@ // means. // ------------------------------------------------------- -// Written by Clifford Wolf <clifford@clifford.at> in 2014 +// Written by Claire Xenia Wolf <claire@yosyshq.com> in 2014 // ------------------------------------------------------- #ifndef HASHLIB_H @@ -66,6 +66,12 @@ struct hash_int_ops { } }; +template<> struct hash_ops<bool> : hash_int_ops +{ + static inline unsigned int hash(bool a) { + return a ? 1 : 0; + } +}; template<> struct hash_ops<int32_t> : hash_int_ops { static inline unsigned int hash(int32_t a) { diff --git a/kernel/log.cc b/kernel/log.cc index 41e91119e..4bcce3b28 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -71,8 +71,6 @@ int string_buf_index = -1; static struct timeval initial_tv = { 0, 0 }; static bool next_print_log = false; static int log_newline_count = 0; -static bool check_expected_logs = true; -static bool display_error_log_msg = true; static void log_id_cache_clear() { @@ -339,8 +337,7 @@ static void logv_error_with_prefix(const char *prefix, f = stderr; log_last_error = vstringf(format, ap); - if (display_error_log_msg) - log("%s%s", prefix, log_last_error.c_str()); + log("%s%s", prefix, log_last_error.c_str()); log_flush(); log_make_debug = bak_log_make_debug; @@ -349,8 +346,7 @@ static void logv_error_with_prefix(const char *prefix, if (YS_REGEX_NS::regex_search(log_last_error, item.second.pattern)) item.second.current_count++; - if (check_expected_logs) - log_check_expected(); + log_check_expected(); if (log_error_atexit) log_error_atexit(); @@ -667,9 +663,14 @@ void log_wire(RTLIL::Wire *wire, std::string indent) void log_check_expected() { - check_expected_logs = false; + // copy out all of the expected logs so that they cannot be re-checked + // or match against themselves + dict<std::string, LogExpectedItem> expect_log, expect_warning, expect_error; + std::swap(expect_warning, log_expect_warning); + std::swap(expect_log, log_expect_log); + std::swap(expect_error, log_expect_error); - for (auto &item : log_expect_warning) { + for (auto &item : expect_warning) { if (item.second.current_count == 0) { log_warn_regexes.clear(); log_error("Expected warning pattern '%s' not found !\n", item.first.c_str()); @@ -681,7 +682,7 @@ void log_check_expected() } } - for (auto &item : log_expect_log) { + for (auto &item : expect_log) { if (item.second.current_count == 0) { log_warn_regexes.clear(); log_error("Expected log pattern '%s' not found !\n", item.first.c_str()); @@ -693,7 +694,7 @@ void log_check_expected() } } - for (auto &item : log_expect_error) + for (auto &item : expect_error) if (item.second.current_count == item.second.expected_count) { log_warn_regexes.clear(); log("Expected error pattern '%s' found !!!\n", item.first.c_str()); @@ -705,7 +706,6 @@ void log_check_expected() _Exit(0); #endif } else { - display_error_log_msg = false; log_warn_regexes.clear(); log_error("Expected error pattern '%s' not found !\n", item.first.c_str()); } diff --git a/kernel/log.h b/kernel/log.h index 8981c4cde..ea14028dd 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,9 +24,29 @@ #include <time.h> -// In GCC 4.8 std::regex is not working correctlty, in order to make features -// using regular expressions to work replacement regex library is used -#if defined(__GNUC__) && !defined( __clang__) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 8) +// In the libstdc++ headers that are provided by GCC 4.8, std::regex is not +// working correctly. In order to make features using regular expressions +// work, a replacement regex library is used. Just checking for GCC version +// is not enough though, because at least on RHEL7/CentOS7 even when compiling +// with Clang instead of GCC, the GCC 4.8 headers are still used for std::regex. +// We have to check the version of the libstdc++ headers specifically, not the +// compiler version. GCC headers of libstdc++ before version 3.4 define +// __GLIBCPP__, later versions define __GLIBCXX__. GCC 7 and newer additionaly +// define _GLIBCXX_RELEASE with a version number. +// Include limits std C++ header, so we get the version macros defined: +#if defined(__cplusplus) +# include <limits> +#endif +// Check if libstdc++ is from GCC +#if defined(__GLIBCPP__) || defined(__GLIBCXX__) +// Check if version could be 4.8 or lower (this also matches for some 4.9 and +// 5.0 releases). See: +// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning +# if !defined(_GLIBCXX_RELEASE) && (defined(__GLIBCPP__) || __GLIBCXX__ <= 20150623) +# define YS_HAS_BAD_STD_REGEX +# endif +#endif +#if defined(YS_HAS_BAD_STD_REGEX) #include <boost/xpressive/xpressive.hpp> #define YS_REGEX_TYPE boost::xpressive::sregex #define YS_REGEX_MATCH_TYPE boost::xpressive::smatch diff --git a/kernel/macc.h b/kernel/macc.h index d216e6772..e4e1ebf52 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/mem.cc b/kernel/mem.cc index 0301a913c..059f8f934 100644 --- a/kernel/mem.cc +++ b/kernel/mem.cc @@ -18,6 +18,7 @@ */ #include "kernel/mem.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE @@ -52,6 +53,67 @@ void Mem::remove() { } void Mem::emit() { + check(); + std::vector<int> rd_left; + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &port = rd_ports[i]; + if (port.removed) { + if (port.cell) { + module->remove(port.cell); + } + } else { + rd_left.push_back(i); + } + } + std::vector<int> wr_left; + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &port = wr_ports[i]; + if (port.removed) { + if (port.cell) { + module->remove(port.cell); + } + } else { + wr_left.push_back(i); + } + } + std::vector<int> init_left; + for (int i = 0; i < GetSize(inits); i++) { + auto &init = inits[i]; + if (init.removed) { + if (init.cell) { + module->remove(init.cell); + } + } else { + init_left.push_back(i); + } + } + for (int i = 0; i < GetSize(rd_left); i++) + if (i != rd_left[i]) + std::swap(rd_ports[i], rd_ports[rd_left[i]]); + rd_ports.resize(GetSize(rd_left)); + for (int i = 0; i < GetSize(wr_left); i++) + if (i != wr_left[i]) + std::swap(wr_ports[i], wr_ports[wr_left[i]]); + wr_ports.resize(GetSize(wr_left)); + for (int i = 0; i < GetSize(init_left); i++) + if (i != init_left[i]) + std::swap(inits[i], inits[init_left[i]]); + inits.resize(GetSize(init_left)); + + for (auto &port : rd_ports) { + for (int i = 0; i < GetSize(wr_left); i++) { + port.transparency_mask[i] = port.transparency_mask[wr_left[i]]; + port.collision_x_mask[i] = port.collision_x_mask[wr_left[i]]; + } + port.transparency_mask.resize(GetSize(wr_left)); + port.collision_x_mask.resize(GetSize(wr_left)); + } + for (auto &port : wr_ports) { + for (int i = 0; i < GetSize(wr_left); i++) + port.priority_mask[i] = port.priority_mask[wr_left[i]]; + port.priority_mask.resize(GetSize(wr_left)); + } + if (packed) { if (mem) { module->memories.erase(mem->name); @@ -61,54 +123,90 @@ void Mem::emit() { if (!cell) { if (memid.empty()) memid = NEW_ID; - cell = module->addCell(memid, ID($mem)); + cell = module->addCell(memid, ID($mem_v2)); } + cell->type = ID($mem_v2); cell->attributes = attributes; cell->parameters[ID::MEMID] = Const(memid.str()); cell->parameters[ID::WIDTH] = Const(width); cell->parameters[ID::OFFSET] = Const(start_offset); cell->parameters[ID::SIZE] = Const(size); - cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_ports)); - cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_ports)); - Const rd_clk_enable, rd_clk_polarity, rd_transparent; - Const wr_clk_enable, wr_clk_polarity; + Const rd_wide_continuation, rd_clk_enable, rd_clk_polarity, rd_transparency_mask, rd_collision_x_mask; + Const wr_wide_continuation, wr_clk_enable, wr_clk_polarity, wr_priority_mask; + Const rd_ce_over_srst, rd_arst_value, rd_srst_value, rd_init_value; SigSpec rd_clk, rd_en, rd_addr, rd_data; SigSpec wr_clk, wr_en, wr_addr, wr_data; + SigSpec rd_arst, rd_srst; int abits = 0; for (auto &port : rd_ports) abits = std::max(abits, GetSize(port.addr)); for (auto &port : wr_ports) abits = std::max(abits, GetSize(port.addr)); cell->parameters[ID::ABITS] = Const(abits); + std::vector<int> wr_port_xlat; + for (int i = 0; i < GetSize(wr_ports); i++) + for (int j = 0; j < (1 << wr_ports[i].wide_log2); j++) + wr_port_xlat.push_back(i); for (auto &port : rd_ports) { if (port.cell) { module->remove(port.cell); port.cell = nullptr; } - rd_clk_enable.bits.push_back(State(port.clk_enable)); - rd_clk_polarity.bits.push_back(State(port.clk_polarity)); - rd_transparent.bits.push_back(State(port.transparent)); - rd_clk.append(port.clk); - log_assert(GetSize(port.clk) == 1); - rd_en.append(port.en); - log_assert(GetSize(port.en) == 1); - SigSpec addr = port.addr; - addr.extend_u0(abits, false); - rd_addr.append(addr); - log_assert(GetSize(addr) == abits); + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + rd_wide_continuation.bits.push_back(State(sub != 0)); + rd_clk_enable.bits.push_back(State(port.clk_enable)); + rd_clk_polarity.bits.push_back(State(port.clk_polarity)); + rd_ce_over_srst.bits.push_back(State(port.ce_over_srst)); + rd_clk.append(port.clk); + rd_arst.append(port.arst); + rd_srst.append(port.srst); + rd_en.append(port.en); + SigSpec addr = port.sub_addr(sub); + addr.extend_u0(abits, false); + rd_addr.append(addr); + log_assert(GetSize(addr) == abits); + for (auto idx : wr_port_xlat) { + rd_transparency_mask.bits.push_back(State(bool(port.transparency_mask[idx]))); + rd_collision_x_mask.bits.push_back(State(bool(port.collision_x_mask[idx]))); + } + } rd_data.append(port.data); - log_assert(GetSize(port.data) == width); + for (auto &bit : port.arst_value) + rd_arst_value.bits.push_back(bit); + for (auto &bit : port.srst_value) + rd_srst_value.bits.push_back(bit); + for (auto &bit : port.init_value) + rd_init_value.bits.push_back(bit); } if (rd_ports.empty()) { + rd_wide_continuation = State::S0; rd_clk_enable = State::S0; rd_clk_polarity = State::S0; - rd_transparent = State::S0; + rd_ce_over_srst = State::S0; + rd_arst_value = State::S0; + rd_srst_value = State::S0; + rd_init_value = State::S0; + } + if (rd_ports.empty() || wr_ports.empty()) { + rd_transparency_mask = State::S0; + rd_collision_x_mask = State::S0; } + cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_clk)); cell->parameters[ID::RD_CLK_ENABLE] = rd_clk_enable; cell->parameters[ID::RD_CLK_POLARITY] = rd_clk_polarity; - cell->parameters[ID::RD_TRANSPARENT] = rd_transparent; + cell->parameters[ID::RD_TRANSPARENCY_MASK] = rd_transparency_mask; + cell->parameters[ID::RD_COLLISION_X_MASK] = rd_collision_x_mask; + cell->parameters[ID::RD_WIDE_CONTINUATION] = rd_wide_continuation; + cell->parameters[ID::RD_CE_OVER_SRST] = rd_ce_over_srst; + cell->parameters[ID::RD_ARST_VALUE] = rd_arst_value; + cell->parameters[ID::RD_SRST_VALUE] = rd_srst_value; + cell->parameters[ID::RD_INIT_VALUE] = rd_init_value; + cell->parameters.erase(ID::RD_TRANSPARENT); cell->setPort(ID::RD_CLK, rd_clk); cell->setPort(ID::RD_EN, rd_en); + cell->setPort(ID::RD_ARST, rd_arst); + cell->setPort(ID::RD_SRST, rd_srst); cell->setPort(ID::RD_ADDR, rd_addr); cell->setPort(ID::RD_DATA, rd_data); for (auto &port : wr_ports) { @@ -116,25 +214,33 @@ void Mem::emit() { module->remove(port.cell); port.cell = nullptr; } - wr_clk_enable.bits.push_back(State(port.clk_enable)); - wr_clk_polarity.bits.push_back(State(port.clk_polarity)); - wr_clk.append(port.clk); - log_assert(GetSize(port.clk) == 1); + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + wr_wide_continuation.bits.push_back(State(sub != 0)); + wr_clk_enable.bits.push_back(State(port.clk_enable)); + wr_clk_polarity.bits.push_back(State(port.clk_polarity)); + wr_clk.append(port.clk); + for (auto idx : wr_port_xlat) + wr_priority_mask.bits.push_back(State(bool(port.priority_mask[idx]))); + SigSpec addr = port.sub_addr(sub); + addr.extend_u0(abits, false); + wr_addr.append(addr); + log_assert(GetSize(addr) == abits); + } wr_en.append(port.en); - log_assert(GetSize(port.en) == width); - SigSpec addr = port.addr; - addr.extend_u0(abits, false); - wr_addr.append(addr); - log_assert(GetSize(addr) == abits); wr_data.append(port.data); - log_assert(GetSize(port.data) == width); } if (wr_ports.empty()) { + wr_wide_continuation = State::S0; wr_clk_enable = State::S0; wr_clk_polarity = State::S0; + wr_priority_mask = State::S0; } + cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_clk)); cell->parameters[ID::WR_CLK_ENABLE] = wr_clk_enable; cell->parameters[ID::WR_CLK_POLARITY] = wr_clk_polarity; + cell->parameters[ID::WR_PRIORITY_MASK] = wr_priority_mask; + cell->parameters[ID::WR_WIDE_CONTINUATION] = wr_wide_continuation; cell->setPort(ID::WR_CLK, wr_clk); cell->setPort(ID::WR_EN, wr_en); cell->setPort(ID::WR_ADDR, wr_addr); @@ -161,30 +267,46 @@ void Mem::emit() { mem->width = width; mem->start_offset = start_offset; mem->size = size; + mem->attributes = attributes; for (auto &port : rd_ports) { if (!port.cell) - port.cell = module->addCell(NEW_ID, ID($memrd)); + port.cell = module->addCell(NEW_ID, ID($memrd_v2)); + port.cell->type = ID($memrd_v2); + port.cell->attributes = port.attributes; port.cell->parameters[ID::MEMID] = memid.str(); port.cell->parameters[ID::ABITS] = GetSize(port.addr); - port.cell->parameters[ID::WIDTH] = width; + port.cell->parameters[ID::WIDTH] = width << port.wide_log2; port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable; port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity; - port.cell->parameters[ID::TRANSPARENT] = port.transparent; + port.cell->parameters[ID::CE_OVER_SRST] = port.ce_over_srst; + port.cell->parameters[ID::ARST_VALUE] = port.arst_value; + port.cell->parameters[ID::SRST_VALUE] = port.srst_value; + port.cell->parameters[ID::INIT_VALUE] = port.init_value; + port.cell->parameters[ID::TRANSPARENCY_MASK] = port.transparency_mask; + port.cell->parameters[ID::COLLISION_X_MASK] = port.collision_x_mask; + port.cell->parameters.erase(ID::TRANSPARENT); port.cell->setPort(ID::CLK, port.clk); port.cell->setPort(ID::EN, port.en); + port.cell->setPort(ID::ARST, port.arst); + port.cell->setPort(ID::SRST, port.srst); port.cell->setPort(ID::ADDR, port.addr); port.cell->setPort(ID::DATA, port.data); } int idx = 0; for (auto &port : wr_ports) { if (!port.cell) - port.cell = module->addCell(NEW_ID, ID($memwr)); + port.cell = module->addCell(NEW_ID, ID($memwr_v2)); + port.cell->type = ID($memwr_v2); + port.cell->attributes = port.attributes; + if (port.cell->parameters.count(ID::PRIORITY)) + port.cell->parameters.erase(ID::PRIORITY); port.cell->parameters[ID::MEMID] = memid.str(); port.cell->parameters[ID::ABITS] = GetSize(port.addr); - port.cell->parameters[ID::WIDTH] = width; + port.cell->parameters[ID::WIDTH] = width << port.wide_log2; port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable; port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity; - port.cell->parameters[ID::PRIORITY] = idx++; + port.cell->parameters[ID::PORTID] = idx++; + port.cell->parameters[ID::PRIORITY_MASK] = port.priority_mask; port.cell->setPort(ID::CLK, port.clk); port.cell->setPort(ID::EN, port.en); port.cell->setPort(ID::ADDR, port.addr); @@ -192,8 +314,12 @@ void Mem::emit() { } idx = 0; for (auto &init : inits) { + bool v2 = !init.en.is_fully_ones(); if (!init.cell) - init.cell = module->addCell(NEW_ID, ID($meminit)); + init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit)); + else + init.cell->type = v2 ? ID($meminit_v2) : ID($meminit); + init.cell->attributes = init.attributes; init.cell->parameters[ID::MEMID] = memid.str(); init.cell->parameters[ID::ABITS] = GetSize(init.addr); init.cell->parameters[ID::WIDTH] = width; @@ -201,42 +327,185 @@ void Mem::emit() { init.cell->parameters[ID::PRIORITY] = idx++; init.cell->setPort(ID::ADDR, init.addr); init.cell->setPort(ID::DATA, init.data); + if (v2) + init.cell->setPort(ID::EN, init.en); + else + init.cell->unsetPort(ID::EN); } } } -void Mem::remove_wr_port(int idx) { - if (wr_ports[idx].cell) { - module->remove(wr_ports[idx].cell); - } - wr_ports.erase(wr_ports.begin() + idx); +void Mem::clear_inits() { + for (auto &init : inits) + init.removed = true; } -void Mem::remove_rd_port(int idx) { - if (rd_ports[idx].cell) { - module->remove(rd_ports[idx].cell); +void Mem::coalesce_inits() { + // start address -> end address + std::map<int, int> chunks; + // Figure out chunk boundaries. + for (auto &init : inits) { + if (init.removed) + continue; + bool valid = false; + for (auto bit : init.en) + if (bit == State::S1) + valid = true; + if (!valid) { + init.removed = true; + continue; + } + int addr = init.addr.as_int(); + int addr_e = addr + GetSize(init.data) / width; + auto it_e = chunks.upper_bound(addr_e); + auto it = it_e; + while (it != chunks.begin()) { + --it; + if (it->second < addr) { + ++it; + break; + } + } + if (it == it_e) { + // No overlapping inits — add this one to index. + chunks[addr] = addr_e; + } else { + // We have an overlap — all chunks in the [it, it_e) + // range will be merged with this init. + if (it->first < addr) + addr = it->first; + auto it_last = it_e; + it_last--; + if (it_last->second > addr_e) + addr_e = it_last->second; + chunks.erase(it, it_e); + chunks[addr] = addr_e; + } + } + // Group inits by the chunk they belong to. + dict<int, std::vector<int>> inits_by_chunk; + for (int i = 0; i < GetSize(inits); i++) { + auto &init = inits[i]; + if (init.removed) + continue; + auto it = chunks.upper_bound(init.addr.as_int()); + --it; + inits_by_chunk[it->first].push_back(i); + int addr = init.addr.as_int(); + int addr_e = addr + GetSize(init.data) / width; + log_assert(addr >= it->first && addr_e <= it->second); + } + // Process each chunk. + for (auto &it : inits_by_chunk) { + int caddr = it.first; + int caddr_e = chunks[caddr]; + auto &chunk_inits = it.second; + if (GetSize(chunk_inits) == 1) { + auto &init = inits[chunk_inits[0]]; + if (!init.en.is_fully_ones()) { + for (int i = 0; i < GetSize(init.data); i++) + if (init.en[i % width] != State::S1) + init.data[i] = State::Sx; + init.en = Const(State::S1, width); + } + continue; + } + Const cdata(State::Sx, (caddr_e - caddr) * width); + for (int idx : chunk_inits) { + auto &init = inits[idx]; + int offset = (init.addr.as_int() - caddr) * width; + log_assert(offset >= 0); + log_assert(offset + GetSize(init.data) <= GetSize(cdata)); + for (int i = 0; i < GetSize(init.data); i++) + if (init.en[i % width] == State::S1) + cdata.bits[i+offset] = init.data.bits[i]; + init.removed = true; + } + MemInit new_init; + new_init.addr = caddr; + new_init.data = cdata; + new_init.en = Const(State::S1, width); + inits.push_back(new_init); } - rd_ports.erase(rd_ports.begin() + idx); -} - -void Mem::clear_inits() { - for (auto &init : inits) - if (init.cell) - module->remove(init.cell); - inits.clear(); } Const Mem::get_init_data() const { Const init_data(State::Sx, width * size); for (auto &init : inits) { + if (init.removed) + continue; int offset = (init.addr.as_int() - start_offset) * width; for (int i = 0; i < GetSize(init.data); i++) - if (0 <= i+offset && i+offset < GetSize(init_data)) + if (0 <= i+offset && i+offset < GetSize(init_data) && init.en[i % width] == State::S1) init_data.bits[i+offset] = init.data.bits[i]; } return init_data; } +void Mem::check() { + int max_wide_log2 = 0; + for (auto &port : rd_ports) { + if (port.removed) + continue; + log_assert(GetSize(port.clk) == 1); + log_assert(GetSize(port.en) == 1); + log_assert(GetSize(port.arst) == 1); + log_assert(GetSize(port.srst) == 1); + log_assert(GetSize(port.data) == (width << port.wide_log2)); + log_assert(GetSize(port.init_value) == (width << port.wide_log2)); + log_assert(GetSize(port.arst_value) == (width << port.wide_log2)); + log_assert(GetSize(port.srst_value) == (width << port.wide_log2)); + if (!port.clk_enable) { + log_assert(port.en == State::S1); + log_assert(port.arst == State::S0); + log_assert(port.srst == State::S0); + } + for (int j = 0; j < port.wide_log2; j++) { + log_assert(port.addr[j] == State::S0); + } + max_wide_log2 = std::max(max_wide_log2, port.wide_log2); + log_assert(GetSize(port.transparency_mask) == GetSize(wr_ports)); + log_assert(GetSize(port.collision_x_mask) == GetSize(wr_ports)); + for (int j = 0; j < GetSize(wr_ports); j++) { + auto &wport = wr_ports[j]; + if ((port.transparency_mask[j] || port.collision_x_mask[j]) && !wport.removed) { + log_assert(port.clk_enable); + log_assert(wport.clk_enable); + log_assert(port.clk == wport.clk); + log_assert(port.clk_polarity == wport.clk_polarity); + } + log_assert(!port.transparency_mask[j] || !port.collision_x_mask[j]); + } + } + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &port = wr_ports[i]; + if (port.removed) + continue; + log_assert(GetSize(port.clk) == 1); + log_assert(GetSize(port.en) == (width << port.wide_log2)); + log_assert(GetSize(port.data) == (width << port.wide_log2)); + for (int j = 0; j < port.wide_log2; j++) { + log_assert(port.addr[j] == State::S0); + } + max_wide_log2 = std::max(max_wide_log2, port.wide_log2); + log_assert(GetSize(port.priority_mask) == GetSize(wr_ports)); + for (int j = 0; j < GetSize(wr_ports); j++) { + auto &wport = wr_ports[j]; + if (port.priority_mask[j] && !wport.removed) { + log_assert(j < i); + log_assert(port.clk_enable == wport.clk_enable); + if (port.clk_enable) { + log_assert(port.clk == wport.clk); + log_assert(port.clk_polarity == wport.clk_polarity); + } + } + } + } + int mask = (1 << max_wide_log2) - 1; + log_assert(!(start_offset & mask)); + log_assert(!(size & mask)); +} + namespace { struct MemIndex { @@ -245,11 +514,11 @@ namespace { dict<IdString, pool<Cell *>> inits; MemIndex (Module *module) { for (auto cell: module->cells()) { - if (cell->type == ID($memwr)) + if (cell->type.in(ID($memwr), ID($memwr_v2))) wr_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell); - else if (cell->type == ID($memrd)) + else if (cell->type.in(ID($memrd), ID($memrd_v2))) rd_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell); - else if (cell->type == ID($meminit)) + else if (cell->type.in(ID($meminit), ID($meminit_v2))) inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell); } } @@ -260,25 +529,56 @@ namespace { res.packed = false; res.mem = mem; res.attributes = mem->attributes; + std::vector<bool> rd_transparent; + std::vector<int> wr_portid; if (index.rd_ports.count(mem->name)) { for (auto cell : index.rd_ports.at(mem->name)) { MemRd mrd; + bool is_compat = cell->type == ID($memrd); mrd.cell = cell; mrd.attributes = cell->attributes; mrd.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool(); mrd.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool(); - mrd.transparent = cell->parameters.at(ID::TRANSPARENT).as_bool(); mrd.clk = cell->getPort(ID::CLK); mrd.en = cell->getPort(ID::EN); mrd.addr = cell->getPort(ID::ADDR); mrd.data = cell->getPort(ID::DATA); + mrd.wide_log2 = ceil_log2(GetSize(mrd.data) / mem->width); + bool transparent = false; + if (is_compat) { + transparent = cell->parameters.at(ID::TRANSPARENT).as_bool(); + mrd.ce_over_srst = false; + mrd.arst_value = Const(State::Sx, mem->width << mrd.wide_log2); + mrd.srst_value = Const(State::Sx, mem->width << mrd.wide_log2); + mrd.init_value = Const(State::Sx, mem->width << mrd.wide_log2); + mrd.srst = State::S0; + mrd.arst = State::S0; + if (!mrd.clk_enable) { + // Fix some patterns that we'll allow for backwards compatibility, + // but don't want to see moving forwards: async transparent + // ports (inherently meaningless) and async ports without + // const 1 tied to EN bit (which may mean a latch in the future). + transparent = false; + if (mrd.en == State::Sx) + mrd.en = State::S1; + } + } else { + mrd.ce_over_srst = cell->parameters.at(ID::CE_OVER_SRST).as_bool(); + mrd.arst_value = cell->parameters.at(ID::ARST_VALUE); + mrd.srst_value = cell->parameters.at(ID::SRST_VALUE); + mrd.init_value = cell->parameters.at(ID::INIT_VALUE); + mrd.arst = cell->getPort(ID::ARST); + mrd.srst = cell->getPort(ID::SRST); + } res.rd_ports.push_back(mrd); + rd_transparent.push_back(transparent); } } if (index.wr_ports.count(mem->name)) { std::vector<std::pair<int, MemWr>> ports; for (auto cell : index.wr_ports.at(mem->name)) { MemWr mwr; + bool is_compat = cell->type == ID($memwr); mwr.cell = cell; mwr.attributes = cell->attributes; mwr.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool(); @@ -287,11 +587,37 @@ namespace { mwr.en = cell->getPort(ID::EN); mwr.addr = cell->getPort(ID::ADDR); mwr.data = cell->getPort(ID::DATA); - ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), mwr)); + mwr.wide_log2 = ceil_log2(GetSize(mwr.data) / mem->width); + ports.push_back(std::make_pair(cell->parameters.at(is_compat ? ID::PRIORITY : ID::PORTID).as_int(), mwr)); } std::sort(ports.begin(), ports.end(), [](const std::pair<int, MemWr> &a, const std::pair<int, MemWr> &b) { return a.first < b.first; }); - for (auto &it : ports) + for (auto &it : ports) { res.wr_ports.push_back(it.second); + wr_portid.push_back(it.first); + } + for (int i = 0; i < GetSize(res.wr_ports); i++) { + auto &port = res.wr_ports[i]; + bool is_compat = port.cell->type == ID($memwr); + if (is_compat) { + port.priority_mask.resize(GetSize(res.wr_ports)); + for (int j = 0; j < i; j++) { + auto &oport = res.wr_ports[j]; + if (port.clk_enable != oport.clk_enable) + continue; + if (port.clk_enable && port.clk != oport.clk) + continue; + if (port.clk_enable && port.clk_polarity != oport.clk_polarity) + continue; + port.priority_mask[j] = true; + } + } else { + Const orig_prio_mask = port.cell->parameters.at(ID::PRIORITY_MASK); + for (int orig_portid : wr_portid) { + bool has_prio = orig_portid < GetSize(orig_prio_mask) && orig_prio_mask[orig_portid] == State::S1; + port.priority_mask.push_back(has_prio); + } + } + } } if (index.inits.count(mem->name)) { std::vector<std::pair<int, MemInit>> inits; @@ -307,12 +633,50 @@ namespace { log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell)); init.addr = addr.as_const(); init.data = data.as_const(); + if (cell->type == ID($meminit_v2)) { + auto en = cell->getPort(ID::EN); + if (!en.is_fully_const()) + log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell)); + init.en = en.as_const(); + } else { + init.en = RTLIL::Const(State::S1, mem->width); + } inits.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), init)); } std::sort(inits.begin(), inits.end(), [](const std::pair<int, MemInit> &a, const std::pair<int, MemInit> &b) { return a.first < b.first; }); for (auto &it : inits) res.inits.push_back(it.second); } + for (int i = 0; i < GetSize(res.rd_ports); i++) { + auto &port = res.rd_ports[i]; + bool is_compat = port.cell->type == ID($memrd); + if (is_compat) { + port.transparency_mask.resize(GetSize(res.wr_ports)); + port.collision_x_mask.resize(GetSize(res.wr_ports)); + if (!rd_transparent[i]) + continue; + if (!port.clk_enable) + continue; + for (int j = 0; j < GetSize(res.wr_ports); j++) { + auto &wport = res.wr_ports[j]; + if (!wport.clk_enable) + continue; + if (port.clk != wport.clk) + continue; + if (port.clk_polarity != wport.clk_polarity) + continue; + port.transparency_mask[j] = true; + } + } else { + Const orig_trans_mask = port.cell->parameters.at(ID::TRANSPARENCY_MASK); + Const orig_cx_mask = port.cell->parameters.at(ID::COLLISION_X_MASK); + for (int orig_portid : wr_portid) { + port.transparency_mask.push_back(orig_portid < GetSize(orig_trans_mask) && orig_trans_mask[orig_portid] == State::S1); + port.collision_x_mask.push_back(orig_portid < GetSize(orig_cx_mask) && orig_cx_mask[orig_portid] == State::S1); + } + } + } + res.check(); return res; } @@ -322,6 +686,7 @@ namespace { cell->parameters.at(ID::OFFSET).as_int(), cell->parameters.at(ID::SIZE).as_int() ); + bool is_compat = cell->type == ID($mem); int abits = cell->parameters.at(ID::ABITS).as_int(); res.packed = true; res.cell = cell; @@ -343,32 +708,112 @@ namespace { MemInit minit; minit.addr = res.start_offset + pos; minit.data = init.extract(pos * res.width, (epos - pos) * res.width, State::Sx); + minit.en = RTLIL::Const(State::S1, res.width); res.inits.push_back(minit); pos = epos; } } } - for (int i = 0; i < cell->parameters.at(ID::RD_PORTS).as_int(); i++) { + int n_rd_ports = cell->parameters.at(ID::RD_PORTS).as_int(); + int n_wr_ports = cell->parameters.at(ID::WR_PORTS).as_int(); + Const rd_wide_continuation = is_compat ? Const(State::S0, n_rd_ports) : cell->parameters.at(ID::RD_WIDE_CONTINUATION); + Const wr_wide_continuation = is_compat ? Const(State::S0, n_wr_ports) : cell->parameters.at(ID::WR_WIDE_CONTINUATION); + for (int i = 0, ni; i < n_rd_ports; i = ni) { + ni = i + 1; + while (ni < n_rd_ports && rd_wide_continuation[ni] == State::S1) + ni++; MemRd mrd; + mrd.wide_log2 = ceil_log2(ni - i); + log_assert(ni - i == (1 << mrd.wide_log2)); mrd.clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE).extract(i, 1).as_bool(); mrd.clk_polarity = cell->parameters.at(ID::RD_CLK_POLARITY).extract(i, 1).as_bool(); - mrd.transparent = cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool(); mrd.clk = cell->getPort(ID::RD_CLK).extract(i, 1); mrd.en = cell->getPort(ID::RD_EN).extract(i, 1); mrd.addr = cell->getPort(ID::RD_ADDR).extract(i * abits, abits); - mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, res.width); + mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, (ni - i) * res.width); + if (is_compat) { + mrd.ce_over_srst = false; + mrd.arst_value = Const(State::Sx, res.width << mrd.wide_log2); + mrd.srst_value = Const(State::Sx, res.width << mrd.wide_log2); + mrd.init_value = Const(State::Sx, res.width << mrd.wide_log2); + mrd.arst = State::S0; + mrd.srst = State::S0; + } else { + mrd.ce_over_srst = cell->parameters.at(ID::RD_CE_OVER_SRST).extract(i, 1).as_bool(); + mrd.arst_value = cell->parameters.at(ID::RD_ARST_VALUE).extract(i * res.width, (ni - i) * res.width); + mrd.srst_value = cell->parameters.at(ID::RD_SRST_VALUE).extract(i * res.width, (ni - i) * res.width); + mrd.init_value = cell->parameters.at(ID::RD_INIT_VALUE).extract(i * res.width, (ni - i) * res.width); + mrd.arst = cell->getPort(ID::RD_ARST).extract(i, 1); + mrd.srst = cell->getPort(ID::RD_SRST).extract(i, 1); + } + if (!is_compat) { + Const transparency_mask = cell->parameters.at(ID::RD_TRANSPARENCY_MASK).extract(i * n_wr_ports, n_wr_ports); + Const collision_x_mask = cell->parameters.at(ID::RD_COLLISION_X_MASK).extract(i * n_wr_ports, n_wr_ports); + for (int j = 0; j < n_wr_ports; j++) + if (wr_wide_continuation[j] != State::S1) { + mrd.transparency_mask.push_back(transparency_mask[j] == State::S1); + mrd.collision_x_mask.push_back(collision_x_mask[j] == State::S1); + } + } res.rd_ports.push_back(mrd); } - for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) { + for (int i = 0, ni; i < n_wr_ports; i = ni) { + ni = i + 1; + while (ni < n_wr_ports && wr_wide_continuation[ni] == State::S1) + ni++; MemWr mwr; + mwr.wide_log2 = ceil_log2(ni - i); + log_assert(ni - i == (1 << mwr.wide_log2)); mwr.clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE).extract(i, 1).as_bool(); mwr.clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY).extract(i, 1).as_bool(); mwr.clk = cell->getPort(ID::WR_CLK).extract(i, 1); - mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, res.width); + mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, (ni - i) * res.width); mwr.addr = cell->getPort(ID::WR_ADDR).extract(i * abits, abits); - mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, res.width); + mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, (ni - i) * res.width); + if (!is_compat) { + Const priority_mask = cell->parameters.at(ID::WR_PRIORITY_MASK).extract(i * n_wr_ports, n_wr_ports); + for (int j = 0; j < n_wr_ports; j++) + if (wr_wide_continuation[j] != State::S1) + mwr.priority_mask.push_back(priority_mask[j] == State::S1); + } res.wr_ports.push_back(mwr); } + if (is_compat) { + for (int i = 0; i < GetSize(res.wr_ports); i++) { + auto &port = res.wr_ports[i]; + port.priority_mask.resize(GetSize(res.wr_ports)); + for (int j = 0; j < i; j++) { + auto &oport = res.wr_ports[j]; + if (port.clk_enable != oport.clk_enable) + continue; + if (port.clk_enable && port.clk != oport.clk) + continue; + if (port.clk_enable && port.clk_polarity != oport.clk_polarity) + continue; + port.priority_mask[j] = true; + } + } + for (int i = 0; i < GetSize(res.rd_ports); i++) { + auto &port = res.rd_ports[i]; + port.transparency_mask.resize(GetSize(res.wr_ports)); + port.collision_x_mask.resize(GetSize(res.wr_ports)); + if (!cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool()) + continue; + if (!port.clk_enable) + continue; + for (int j = 0; j < GetSize(res.wr_ports); j++) { + auto &wport = res.wr_ports[j]; + if (!wport.clk_enable) + continue; + if (port.clk != wport.clk) + continue; + if (port.clk_polarity != wport.clk_polarity) + continue; + port.transparency_mask[j] = true; + } + } + } + res.check(); return res; } @@ -381,7 +826,7 @@ std::vector<Mem> Mem::get_all_memories(Module *module) { res.push_back(mem_from_memory(module, it.second, index)); } for (auto cell: module->cells()) { - if (cell->type == ID($mem)) + if (cell->type.in(ID($mem), ID($mem_v2))) res.push_back(mem_from_cell(cell)); } return res; @@ -395,13 +840,13 @@ std::vector<Mem> Mem::get_selected_memories(Module *module) { res.push_back(mem_from_memory(module, it.second, index)); } for (auto cell: module->selected_cells()) { - if (cell->type == ID($mem)) + if (cell->type.in(ID($mem), ID($mem_v2))) res.push_back(mem_from_cell(cell)); } return res; } -Cell *Mem::extract_rdff(int idx) { +Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) { MemRd &port = rd_ports[idx]; if (!port.clk_enable) @@ -409,28 +854,813 @@ Cell *Mem::extract_rdff(int idx) { Cell *c; - if (port.transparent) + // There are two ways to handle rdff extraction when transparency is involved: + // + // - if all of the following conditions are true, put the FF on address input: + // + // - the port has no clock enable, no reset, and no initial value + // - the port is transparent wrt all write ports (implying they also share + // the clock domain) + // + // - otherwise, put the FF on the data output, and make bypass paths for + // all write ports wrt which this port is transparent + bool trans_use_addr = true; + for (int i = 0; i < GetSize(wr_ports); i++) + if (!port.transparency_mask[i] && !wr_ports[i].removed) + trans_use_addr = false; + + // If there are no write ports at all, we could possibly use either way; do data + // FF in this case. + if (GetSize(wr_ports) == 0) + trans_use_addr = false; + + if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef()) + trans_use_addr = false; + + if (trans_use_addr) { - SigSpec sig_q = module->addWire(stringf("%s$rdreg[%d]$q", memid.c_str(), idx), GetSize(port.addr)); - SigSpec sig_d = port.addr; - port.addr = sig_q; - c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true); + // Do not put a register in front of constant address bits — this is both + // unnecessary and will break wide ports. + int width = 0; + for (int i = 0; i < GetSize(port.addr); i++) + if (port.addr[i].wire) + width++; + + if (width) + { + SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid.c_str(), idx), width); + SigSpec sig_d; + + int pos = 0; + for (int i = 0; i < GetSize(port.addr); i++) + if (port.addr[i].wire) { + sig_d.append(port.addr[i]); + port.addr[i] = sig_q[pos++]; + } + + c = module->addDff(stringf("$%s$rdreg[%d]", memid.c_str(), idx), port.clk, sig_d, sig_q, port.clk_polarity); + } else { + c = nullptr; + } } else { - SigSpec sig_d = module->addWire(stringf("%s$rdreg[%d]$d", memid.c_str(), idx), width); - SigSpec sig_q = port.data; - port.data = sig_d; - c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true); + log_assert(port.arst == State::S0 || port.srst == State::S0); + + SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid.c_str(), idx), GetSize(port.data)); + SigSpec sig_d = async_d; + + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &wport = wr_ports[i]; + if (wport.removed) + continue; + if (port.transparency_mask[i] || port.collision_x_mask[i]) { + log_assert(wport.clk_enable); + log_assert(wport.clk == port.clk); + log_assert(wport.clk_enable == port.clk_enable); + int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2); + int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2); + bool wide_write = wport.wide_log2 > port.wide_log2; + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec raddr = port.addr; + SigSpec waddr = wport.addr; + if (wide_write) + waddr = wport.sub_addr(sub); + else + raddr = port.sub_addr(sub); + SigSpec addr_eq; + if (raddr != waddr) + addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid.c_str(), idx, i, sub), raddr, waddr); + int pos = 0; + int ewidth = width << min_wide_log2; + int wsub = wide_write ? sub : 0; + int rsub = wide_write ? 0 : sub; + while (pos < ewidth) { + int epos = pos; + while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width]) + epos++; + SigSpec cur = sig_d.extract(pos + rsub * width, epos-pos); + SigSpec other = port.transparency_mask[i] ? wport.data.extract(pos + wsub * width, epos-pos) : Const(State::Sx, epos-pos); + SigSpec cond; + if (raddr != waddr) + cond = module->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), wport.en[pos + wsub * width], addr_eq); + else + cond = wport.en[pos + wsub * width]; + SigSpec merged = module->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), cur, other, cond); + sig_d.replace(pos + rsub * width, merged); + pos = epos; + } + } + } + } + + IdString name = stringf("$%s$rdreg[%d]", memid.c_str(), idx); + FfData ff(module, initvals, name); + ff.width = GetSize(port.data); + ff.has_clk = true; + ff.sig_clk = port.clk; + ff.pol_clk = port.clk_polarity; + if (port.en != State::S1) { + ff.has_ce = true; + ff.pol_ce = true; + ff.sig_ce = port.en; + } + if (port.arst != State::S0) { + ff.has_arst = true; + ff.pol_arst = true; + ff.sig_arst = port.arst; + ff.val_arst = port.arst_value; + } + if (port.srst != State::S0) { + ff.has_srst = true; + ff.pol_srst = true; + ff.sig_srst = port.srst; + ff.val_srst = port.srst_value; + ff.ce_over_srst = ff.has_ce && port.ce_over_srst; + } + ff.sig_d = sig_d; + ff.sig_q = port.data; + ff.val_init = port.init_value; + port.data = async_d; + c = ff.emit(); } - log("Extracted %s FF from read port %d of %s.%s: %s\n", port.transparent ? "addr" : "data", + if (c) + log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data", idx, log_id(module), log_id(memid), log_id(c)); port.en = State::S1; port.clk = State::S0; + port.arst = State::S0; + port.srst = State::S0; port.clk_enable = false; port.clk_polarity = true; + port.ce_over_srst = false; + port.arst_value = Const(State::Sx, GetSize(port.data)); + port.srst_value = Const(State::Sx, GetSize(port.data)); + port.init_value = Const(State::Sx, GetSize(port.data)); + + for (int i = 0; i < GetSize(wr_ports); i++) { + port.transparency_mask[i] = false; + port.collision_x_mask[i] = false; + } return c; } + +void Mem::narrow() { + // NOTE: several passes depend on this function not modifying + // the design at all until (and unless) emit() is called. + // Be careful to preserve this. + std::vector<MemRd> new_rd_ports; + std::vector<MemWr> new_wr_ports; + std::vector<std::pair<int, int>> new_rd_map; + std::vector<std::pair<int, int>> new_wr_map; + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &port = rd_ports[i]; + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { + new_rd_map.push_back(std::make_pair(i, sub)); + } + } + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &port = wr_ports[i]; + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { + new_wr_map.push_back(std::make_pair(i, sub)); + } + } + for (auto &it : new_rd_map) { + MemRd &orig = rd_ports[it.first]; + MemRd port = orig; + if (it.second != 0) + port.cell = nullptr; + if (port.wide_log2) { + port.data = port.data.extract(it.second * width, width); + port.init_value = port.init_value.extract(it.second * width, width); + port.arst_value = port.arst_value.extract(it.second * width, width); + port.srst_value = port.srst_value.extract(it.second * width, width); + port.addr = port.sub_addr(it.second); + port.wide_log2 = 0; + } + port.transparency_mask.clear(); + port.collision_x_mask.clear(); + for (auto &it2 : new_wr_map) + port.transparency_mask.push_back(orig.transparency_mask[it2.first]); + for (auto &it2 : new_wr_map) + port.collision_x_mask.push_back(orig.collision_x_mask[it2.first]); + new_rd_ports.push_back(port); + } + for (auto &it : new_wr_map) { + MemWr &orig = wr_ports[it.first]; + MemWr port = orig; + if (it.second != 0) + port.cell = nullptr; + if (port.wide_log2) { + port.data = port.data.extract(it.second * width, width); + port.en = port.en.extract(it.second * width, width); + port.addr = port.sub_addr(it.second); + port.wide_log2 = 0; + } + port.priority_mask.clear(); + for (auto &it2 : new_wr_map) + port.priority_mask.push_back(orig.priority_mask[it2.first]); + new_wr_ports.push_back(port); + } + std::swap(rd_ports, new_rd_ports); + std::swap(wr_ports, new_wr_ports); +} + +void Mem::emulate_priority(int idx1, int idx2, FfInitVals *initvals) +{ + auto &port1 = wr_ports[idx1]; + auto &port2 = wr_ports[idx2]; + if (!port2.priority_mask[idx1]) + return; + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &rport = rd_ports[i]; + if (rport.removed) + continue; + if (rport.transparency_mask[idx1] && !(rport.transparency_mask[idx2] || rport.collision_x_mask[idx2])) + emulate_transparency(idx1, i, initvals); + } + int min_wide_log2 = std::min(port1.wide_log2, port2.wide_log2); + int max_wide_log2 = std::max(port1.wide_log2, port2.wide_log2); + bool wide1 = port1.wide_log2 > port2.wide_log2; + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec addr1 = port1.addr; + SigSpec addr2 = port2.addr; + if (wide1) + addr1 = port1.sub_addr(sub); + else + addr2 = port2.sub_addr(sub); + SigSpec addr_eq = module->Eq(NEW_ID, addr1, addr2); + int ewidth = width << min_wide_log2; + int sub1 = wide1 ? sub : 0; + int sub2 = wide1 ? 0 : sub; + dict<std::pair<SigBit, SigBit>, SigBit> cache; + for (int pos = 0; pos < ewidth; pos++) { + SigBit &en1 = port1.en[pos + sub1 * width]; + SigBit &en2 = port2.en[pos + sub2 * width]; + std::pair<SigBit, SigBit> key(en1, en2); + if (cache.count(key)) { + en1 = cache[key]; + } else { + SigBit active2 = module->And(NEW_ID, addr_eq, en2); + SigBit nactive2 = module->Not(NEW_ID, active2); + en1 = cache[key] = module->And(NEW_ID, en1, nactive2); + } + } + } + port2.priority_mask[idx1] = false; +} + +void Mem::emulate_transparency(int widx, int ridx, FfInitVals *initvals) { + auto &wport = wr_ports[widx]; + auto &rport = rd_ports[ridx]; + log_assert(rport.transparency_mask[widx]); + // If other write ports have priority over this one, emulate their transparency too. + for (int i = GetSize(wr_ports) - 1; i > widx; i--) { + if (wr_ports[i].removed) + continue; + if (rport.transparency_mask[i] && wr_ports[i].priority_mask[widx]) + emulate_transparency(i, ridx, initvals); + } + int min_wide_log2 = std::min(rport.wide_log2, wport.wide_log2); + int max_wide_log2 = std::max(rport.wide_log2, wport.wide_log2); + bool wide_write = wport.wide_log2 > rport.wide_log2; + // The write data FF doesn't need full reset/init behavior, as it'll be masked by + // the mux whenever this would be relevant. It does, however, need to have the same + // clock enable signal as the read port. + SigSpec wdata_q = module->addWire(NEW_ID, GetSize(wport.data)); + module->addDffe(NEW_ID, rport.clk, rport.en, wport.data, wdata_q, rport.clk_polarity, true); + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec raddr = rport.addr; + SigSpec waddr = wport.addr; + for (int j = min_wide_log2; j < max_wide_log2; j++) + if (wide_write) + waddr = wport.sub_addr(sub); + else + raddr = rport.sub_addr(sub); + SigSpec addr_eq; + if (raddr != waddr) + addr_eq = module->Eq(NEW_ID, raddr, waddr); + int pos = 0; + int ewidth = width << min_wide_log2; + int wsub = wide_write ? sub : 0; + int rsub = wide_write ? 0 : sub; + SigSpec rdata_a = module->addWire(NEW_ID, ewidth); + while (pos < ewidth) { + int epos = pos; + while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width]) + epos++; + SigSpec cond; + if (raddr != waddr) + cond = module->And(NEW_ID, wport.en[pos + wsub * width], addr_eq); + else + cond = wport.en[pos + wsub * width]; + SigSpec cond_q = module->addWire(NEW_ID); + // The FF for storing the bypass enable signal must be carefully + // constructed to preserve the overall init/reset/enable behavior + // of the whole port. + FfData ff(module, initvals, NEW_ID); + ff.width = 1; + ff.sig_q = cond_q; + ff.sig_d = cond; + ff.has_clk = true; + ff.sig_clk = rport.clk; + ff.pol_clk = rport.clk_polarity; + if (rport.en != State::S1) { + ff.has_ce = true; + ff.sig_ce = rport.en; + ff.pol_ce = true; + } + if (rport.arst != State::S0) { + ff.has_arst = true; + ff.sig_arst = rport.arst; + ff.pol_arst = true; + ff.val_arst = State::S0; + } + if (rport.srst != State::S0) { + ff.has_srst = true; + ff.sig_srst = rport.srst; + ff.pol_srst = true; + ff.val_srst = State::S0; + ff.ce_over_srst = rport.ce_over_srst; + } + if (!rport.init_value.is_fully_undef()) + ff.val_init = State::S0; + else + ff.val_init = State::Sx; + ff.emit(); + // And the final bypass mux. + SigSpec cur = rdata_a.extract(pos, epos-pos); + SigSpec other = wdata_q.extract(pos + wsub * width, epos-pos); + SigSpec dest = rport.data.extract(pos + rsub * width, epos-pos); + module->addMux(NEW_ID, cur, other, cond_q, dest); + pos = epos; + } + rport.data.replace(rsub * width, rdata_a); + } + rport.transparency_mask[widx] = false; + rport.collision_x_mask[widx] = true; +} + +void Mem::prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals) { + log_assert(idx1 < idx2); + auto &port1 = wr_ports[idx1]; + auto &port2 = wr_ports[idx2]; + // If port 2 has priority over a port before port 1, make port 1 have priority too. + for (int i = 0; i < idx1; i++) + if (port2.priority_mask[i]) + port1.priority_mask[i] = true; + // If port 2 has priority over a port after port 1, emulate it. + for (int i = idx1 + 1; i < idx2; i++) + if (port2.priority_mask[i] && !wr_ports[i].removed) + emulate_priority(i, idx2, initvals); + // If some port had priority over port 2, make it have priority over the merged port too. + for (int i = idx2 + 1; i < GetSize(wr_ports); i++) { + auto &oport = wr_ports[i]; + if (oport.priority_mask[idx2]) + oport.priority_mask[idx1] = true; + } + // Make sure all read ports have identical collision/transparency behavior wrt both + // ports. + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &rport = rd_ports[i]; + if (rport.removed) + continue; + // If collision already undefined with both ports, it's fine. + if (rport.collision_x_mask[idx1] && rport.collision_x_mask[idx2]) + continue; + // If one port has undefined collision, change it to the behavior + // of the other port. + if (rport.collision_x_mask[idx1]) { + rport.collision_x_mask[idx1] = false; + rport.transparency_mask[idx1] = rport.transparency_mask[idx2]; + continue; + } + if (rport.collision_x_mask[idx2]) { + rport.collision_x_mask[idx2] = false; + rport.transparency_mask[idx2] = rport.transparency_mask[idx1]; + continue; + } + // If transparent with both ports, also fine. + if (rport.transparency_mask[idx1] && rport.transparency_mask[idx2]) + continue; + // If transparent with only one, emulate it, and remove the collision-X + // flag that emulate_transparency will set (to align with the other port). + if (rport.transparency_mask[idx1]) { + emulate_transparency(i, idx1, initvals); + rport.collision_x_mask[idx1] = false; + continue; + } + if (rport.transparency_mask[idx2]) { + emulate_transparency(i, idx2, initvals); + rport.collision_x_mask[idx2] = false; + continue; + } + // If we got here, it's transparent with neither port, which is fine. + } +} + +void Mem::prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals) { + auto &port1 = rd_ports[idx1]; + auto &port2 = rd_ports[idx2]; + // Note that going through write ports in order is important, since + // emulating transparency of a write port can change transparency + // mask for higher-numbered ports (due to transitive transparency + // emulation needed because of write port priority). + for (int i = 0; i < GetSize(wr_ports); i++) { + if (wr_ports[i].removed) + continue; + // Both ports undefined, OK. + if (port1.collision_x_mask[i] && port2.collision_x_mask[i]) + continue; + // Only one port undefined — change its behavior + // to align with the other port. + if (port1.collision_x_mask[i]) { + port1.collision_x_mask[i] = false; + port1.transparency_mask[i] = port2.transparency_mask[i]; + continue; + } + if (port2.collision_x_mask[i]) { + port2.collision_x_mask[i] = false; + port2.transparency_mask[i] = port1.transparency_mask[i]; + continue; + } + // Both ports transparent, OK. + if (port1.transparency_mask[i] && port2.transparency_mask[i]) + continue; + // Only one port transparent — emulate transparency + // on the other. + if (port1.transparency_mask[i]) { + emulate_transparency(i, idx1, initvals); + port1.collision_x_mask[i] = false; + continue; + } + if (port2.transparency_mask[i]) { + emulate_transparency(i, idx2, initvals); + port2.collision_x_mask[i] = false; + continue; + } + // No ports transparent, OK. + } + +} + +void Mem::widen_prep(int wide_log2) { + // Make sure start_offset and size are aligned to the port width, + // adjust if necessary. + int mask = ((1 << wide_log2) - 1); + int delta = start_offset & mask; + start_offset -= delta; + size += delta; + if (size & mask) { + size |= mask; + size++; + } +} + +void Mem::widen_wr_port(int idx, int wide_log2) { + widen_prep(wide_log2); + auto &port = wr_ports[idx]; + log_assert(port.wide_log2 <= wide_log2); + if (port.wide_log2 < wide_log2) { + SigSpec new_data, new_en; + SigSpec addr_lo = port.addr.extract(0, wide_log2); + for (int sub = 0; sub < (1 << wide_log2); sub += (1 << port.wide_log2)) + { + Const cur_addr_lo(sub, wide_log2); + if (addr_lo == cur_addr_lo) { + // Always writes to this subword. + new_data.append(port.data); + new_en.append(port.en); + } else if (addr_lo.is_fully_const()) { + // Never writes to this subword. + new_data.append(Const(State::Sx, GetSize(port.data))); + new_en.append(Const(State::S0, GetSize(port.data))); + } else { + // May or may not write to this subword. + new_data.append(port.data); + SigSpec addr_eq = module->Eq(NEW_ID, addr_lo, cur_addr_lo); + SigSpec en = module->Mux(NEW_ID, Const(State::S0, GetSize(port.data)), port.en, addr_eq); + new_en.append(en); + } + } + port.addr.replace(port.wide_log2, Const(State::S0, wide_log2 - port.wide_log2)); + port.data = new_data; + port.en = new_en; + port.wide_log2 = wide_log2; + } +} + +void Mem::emulate_rden(int idx, FfInitVals *initvals) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + emulate_rd_ce_over_srst(idx); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *prev_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + FfData ff_data(module, initvals, NEW_ID); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = port.en; + ff_sel.sig_q = sel; + ff_data.width = GetSize(port.data); + ff_data.has_clk = true; + ff_data.sig_clk = port.clk; + ff_data.pol_clk = port.clk_polarity; + ff_data.sig_d = port.data; + ff_data.sig_q = prev_data; + if (!port.init_value.is_fully_undef()) { + ff_sel.val_init = State::S0; + ff_data.val_init = port.init_value; + port.init_value = Const(State::Sx, GetSize(port.data)); + } else { + ff_sel.val_init = State::Sx; + ff_data.val_init = Const(State::Sx, GetSize(port.data)); + } + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.val_arst = State::S0; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_data.has_arst = true; + ff_data.val_arst = port.arst_value; + ff_data.sig_arst = port.arst; + ff_data.pol_arst = true; + port.arst = State::S0; + } + if (port.srst != State::S0) { + log_assert(!port.ce_over_srst); + ff_sel.has_srst = true; + ff_sel.val_srst = State::S0; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + ff_sel.ce_over_srst = false; + ff_data.has_srst = true; + ff_data.val_srst = port.srst_value; + ff_data.sig_srst = port.srst; + ff_data.pol_srst = true; + ff_data.ce_over_srst = false; + port.srst = State::S0; + } + ff_sel.emit(); + ff_data.emit(); + module->addMux(NEW_ID, prev_data, new_data, sel, port.data); + port.data = new_data; + port.en = State::S1; +} + +void Mem::emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals) { + auto &port = rd_ports[idx]; + if (emu_init && !port.init_value.is_fully_undef()) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + ff_sel.val_init = State::S0; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + if (emu_arst && port.arst_value == port.init_value) { + // If we're going to emulate async reset anyway, and the reset + // value is the same as init value, reuse the same mux. + ff_sel.val_arst = State::S0; + port.arst = State::S0; + } else { + ff_sel.val_arst = State::S1; + } + } + if (port.srst != State::S0) { + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + if (emu_srst && port.srst_value == port.init_value) { + ff_sel.val_srst = State::S0; + port.srst = State::S0; + } else { + ff_sel.val_srst = State::S1; + } + } + ff_sel.emit(); + module->addMux(NEW_ID, port.init_value, new_data, sel, port.data); + port.data = new_data; + port.init_value = Const(State::Sx, GetSize(port.data)); + } + if (emu_arst && port.arst != State::S0) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + if (port.init_value.is_fully_undef()) + ff_sel.val_init = State::Sx; + else + ff_sel.val_init = State::S1; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_sel.val_arst = State::S0; + if (port.srst != State::S0) { + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + if (emu_srst && port.srst_value == port.arst_value) { + ff_sel.val_srst = State::S0; + port.srst = State::S0; + } else { + ff_sel.val_srst = State::S1; + } + } + ff_sel.emit(); + module->addMux(NEW_ID, port.arst_value, new_data, sel, port.data); + port.data = new_data; + port.arst = State::S0; + } + if (emu_srst && port.srst != State::S0) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + if (port.init_value.is_fully_undef()) + ff_sel.val_init = State::Sx; + else + ff_sel.val_init = State::S1; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + ff_sel.val_srst = State::S0; + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_sel.val_arst = State::S1; + } + ff_sel.emit(); + module->addMux(NEW_ID, port.srst_value, new_data, sel, port.data); + port.data = new_data; + port.srst = State::S0; + } +} + +void Mem::emulate_rd_ce_over_srst(int idx) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + if (port.en == State::S1 || port.srst == State::S0 || !port.ce_over_srst) { + port.ce_over_srst = false; + return; + } + port.ce_over_srst = false; + port.srst = module->And(NEW_ID, port.en, port.srst); +} + +void Mem::emulate_rd_srst_over_ce(int idx) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + if (port.en == State::S1 || port.srst == State::S0 || port.ce_over_srst) { + port.ce_over_srst = true; + return; + } + port.ce_over_srst = true; + port.en = module->Or(NEW_ID, port.en, port.srst); +} + +bool Mem::emulate_read_first_ok() { + if (wr_ports.empty()) + return false; + SigSpec clk = wr_ports[0].clk; + bool clk_polarity = wr_ports[0].clk_polarity; + for (auto &port: wr_ports) { + if (!port.clk_enable) + return false; + if (port.clk != clk) + return false; + if (port.clk_polarity != clk_polarity) + return false; + } + bool found_read_first = false; + for (auto &port: rd_ports) { + if (!port.clk_enable) + return false; + if (port.clk != clk) + return false; + if (port.clk_polarity != clk_polarity) + return false; + // No point doing this operation if there is no read-first relationship + // in the first place. + for (int j = 0; j < GetSize(wr_ports); j++) + if (!port.transparency_mask[j] && !port.collision_x_mask[j]) + found_read_first = true; + } + return found_read_first; +} + +void Mem::emulate_read_first(FfInitVals *initvals) { + log_assert(emulate_read_first_ok()); + for (int i = 0; i < GetSize(rd_ports); i++) + for (int j = 0; j < GetSize(wr_ports); j++) + if (rd_ports[i].transparency_mask[j]) + emulate_transparency(j, i, initvals); + for (int i = 0; i < GetSize(rd_ports); i++) + for (int j = 0; j < GetSize(wr_ports); j++) { + log_assert(!rd_ports[i].transparency_mask[j]); + rd_ports[i].collision_x_mask[j] = false; + rd_ports[i].transparency_mask[j] = true; + } + for (auto &port: wr_ports) { + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *new_addr = module->addWire(NEW_ID, GetSize(port.addr)); + auto compressed = port.compress_en(); + Wire *new_en = module->addWire(NEW_ID, GetSize(compressed.first)); + FfData ff_data(module, initvals, NEW_ID); + FfData ff_addr(module, initvals, NEW_ID); + FfData ff_en(module, initvals, NEW_ID); + ff_data.width = GetSize(port.data); + ff_data.has_clk = true; + ff_data.sig_clk = port.clk; + ff_data.pol_clk = port.clk_polarity; + ff_data.sig_d = port.data; + ff_data.sig_q = new_data;; + ff_data.val_init = Const(State::Sx, ff_data.width); + ff_data.emit(); + ff_addr.width = GetSize(port.addr); + ff_addr.has_clk = true; + ff_addr.sig_clk = port.clk; + ff_addr.pol_clk = port.clk_polarity; + ff_addr.sig_d = port.addr; + ff_addr.sig_q = new_addr;; + ff_addr.val_init = Const(State::Sx, ff_addr.width); + ff_addr.emit(); + ff_en.width = GetSize(compressed.first); + ff_en.has_clk = true; + ff_en.sig_clk = port.clk; + ff_en.pol_clk = port.clk_polarity; + ff_en.sig_d = compressed.first; + ff_en.sig_q = new_en;; + ff_en.val_init = Const(State::S0, ff_en.width); + ff_en.emit(); + port.data = new_data; + port.addr = new_addr; + port.en = port.decompress_en(compressed.second, new_en); + } +} + +std::pair<SigSpec, std::vector<int>> MemWr::compress_en() { + SigSpec sig = en[0]; + std::vector<int> swizzle; + SigBit prev_bit = en[0]; + int idx = 0; + for (auto &bit: en) { + if (bit != prev_bit) { + sig.append(bit); + prev_bit = bit; + idx++; + } + swizzle.push_back(idx); + } + log_assert(idx + 1 == GetSize(sig)); + return {sig, swizzle}; +} + +SigSpec MemWr::decompress_en(const std::vector<int> &swizzle, SigSpec sig) { + SigSpec res; + for (int i: swizzle) + res.append(sig[i]); + return res; +} diff --git a/kernel/mem.h b/kernel/mem.h index 6d727e71d..ae87b1285 100644 --- a/kernel/mem.h +++ b/kernel/mem.h @@ -21,38 +21,76 @@ #define MEM_H #include "kernel/yosys.h" +#include "kernel/ffinit.h" YOSYS_NAMESPACE_BEGIN -struct MemRd { - dict<IdString, Const> attributes; +struct MemRd : RTLIL::AttrObject { + bool removed; Cell *cell; - bool clk_enable, clk_polarity; - bool transparent; - SigSpec clk, en, addr, data; - MemRd() : cell(nullptr) {} + int wide_log2; + bool clk_enable, clk_polarity, ce_over_srst; + Const arst_value, srst_value, init_value; + // One bit for every write port, true iff simultanous read on this + // port and write on the other port will bypass the written data + // to this port's output (default behavior is to read old value). + // Can only be set for write ports that have the same clock domain. + std::vector<bool> transparency_mask; + // One bit for every write port, true iff simultanous read on this + // port and write on the other port will return an all-X (don't care) + // value. Mutually exclusive with transparency_mask. + // Can only be set for write ports that have the same clock domain. + // For optimization purposes, this will also be set if we can + // determine that the two ports can never be active simultanously + // (making the above vacuously true). + std::vector<bool> collision_x_mask; + SigSpec clk, en, arst, srst, addr, data; + + MemRd() : removed(false), cell(nullptr) {} + + // Returns the address of given subword index accessed by this port. + SigSpec sub_addr(int sub) { + SigSpec res = addr; + for (int i = 0; i < wide_log2; i++) + res[i] = State(sub >> i & 1); + return res; + } }; -struct MemWr { - dict<IdString, Const> attributes; +struct MemWr : RTLIL::AttrObject { + bool removed; Cell *cell; + int wide_log2; bool clk_enable, clk_polarity; + std::vector<bool> priority_mask; SigSpec clk, en, addr, data; - MemWr() : cell(nullptr) {} + + MemWr() : removed(false), cell(nullptr) {} + + // Returns the address of given subword index accessed by this port. + SigSpec sub_addr(int sub) { + SigSpec res = addr; + for (int i = 0; i < wide_log2; i++) + res[i] = State(sub >> i & 1); + return res; + } + + std::pair<SigSpec, std::vector<int>> compress_en(); + SigSpec decompress_en(const std::vector<int> &swizzle, SigSpec sig); }; -struct MemInit { - dict<IdString, Const> attributes; +struct MemInit : RTLIL::AttrObject { + bool removed; Cell *cell; Const addr; Const data; - MemInit() : cell(nullptr) {} + Const en; + MemInit() : removed(false), cell(nullptr) {} }; -struct Mem { +struct Mem : RTLIL::AttrObject { Module *module; IdString memid; - dict<IdString, Const> attributes; bool packed; RTLIL::Memory *mem; Cell *cell; @@ -61,15 +99,128 @@ struct Mem { std::vector<MemRd> rd_ports; std::vector<MemWr> wr_ports; + // Removes this memory from the module. The data in helper structures + // is unaffected except for the cell/mem fields. void remove(); + + // Commits all changes in helper structures into the module — ports and + // inits marked as removed are actually removed, new ports/inits create + // new cells, modified port/inits are commited into their existing + // cells. Note that this reindexes the ports and inits array (actually + // removing the ports/inits marked as removed). void emit(); - void remove_wr_port(int idx); - void remove_rd_port(int idx); + + // Marks all inits as removed. void clear_inits(); + + // Coalesces inits: whenever two inits have overlapping or touching + // address ranges, they are combined into one, with the higher-priority + // one's data overwriting the other. Running this results in + // an inits list equivalent to the original, in which all entries + // cover disjoint (and non-touching) address ranges, and all enable + // masks are all-1. + void coalesce_inits(); + + // Checks consistency of this memory and all its ports/inits, using + // log_assert. + void check(); + + // Gathers all initialization data into a single big const covering + // the whole memory. For all non-initialized bits, Sx will be returned. Const get_init_data() const; + + // Constructs and returns the helper structures for all memories + // in a module. static std::vector<Mem> get_all_memories(Module *module); + + // Constructs and returns the helper structures for all selected + // memories in a module. static std::vector<Mem> get_selected_memories(Module *module); - Cell *extract_rdff(int idx); + + // Converts a synchronous read port into an asynchronous one by + // extracting the data (or, in some rare cases, address) register + // into a separate cell, together with any soft-transparency + // logic necessary to preserve its semantics. Returns the created + // register cell, if any. Note that in some rare cases this function + // may succeed and perform a conversion without creating a new + // register — a nullptr result doesn't imply nothing was done. + Cell *extract_rdff(int idx, FfInitVals *initvals); + + // Splits all wide ports in this memory into equivalent narrow ones. + // This function performs no modifications at all to the actual + // netlist unless and until emit() is called. + void narrow(); + + // If write port idx2 currently has priority over write port idx1, + // inserts extra logic on idx1's enable signal to disable writes + // when idx2 is writing to the same address, then removes the priority + // from the priority mask. If there is a memory port that is + // transparent with idx1, but not with idx2, that port is converted + // to use soft transparency logic. + void emulate_priority(int idx1, int idx2, FfInitVals *initvals); + + // Creates soft-transparency logic on read port ridx, bypassing the + // data from write port widx. Should only be called when ridx is + // transparent wrt widx in the first place. Once we're done, the + // transparency_mask bit will be cleared, and the collision_x_mask + // bit will be set instead (since whatever value is read will be + // replaced by the soft transparency logic). + void emulate_transparency(int widx, int ridx, FfInitVals *initvals); + + // Prepares for merging write port idx2 into idx1 (where idx1 < idx2). + // Specifically, takes care of priority masks: any priority relations + // that idx2 had are replicated onto idx1, unless they conflict with + // priorities already present on idx1, in which case emulate_priority + // is called. Likewise, ensures transparency and undefined collision + // masks of all read ports have the same values for both ports, + // calling emulate_transparency if necessary. + void prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals); + + // Prepares for merging read port idx2 into idx1. + // Specifically, makes sure the transparency and undefined collision + // masks of both ports are equal, by changing undefined behavior + // of one port to the other's defined behavior, or by calling + // emulate_transparency if necessary. + void prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals); + + // Prepares the memory for widening a port to a given width. This + // involves ensuring that start_offset and size are aligned to the + // target width. + void widen_prep(int wide_log2); + + // Widens a write port up to a given width. The newly port is + // equivalent to the original, made by replicating enable/data bits + // and masking enable bits with decoders on the low part of the + // original address. + void widen_wr_port(int idx, int wide_log2); + + // Emulates a sync read port's enable functionality in soft logic, + // changing the actual read port's enable to be always-on. + void emulate_rden(int idx, FfInitVals *initvals); + + // Emulates a sync read port's initial/reset value functionality in + // soft logic, removing it from the actual read port. + void emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals); + + // Given a read port with ce_over_srst set, converts it to a port + // with ce_over_srst unset without changing its behavior by adding + // emulation logic. + void emulate_rd_ce_over_srst(int idx); + + // Given a read port with ce_over_srst unset, converts it to a port + // with ce_over_srst set without changing its behavior by adding + // emulation logic. + void emulate_rd_srst_over_ce(int idx); + + // Returns true iff emulate_read_first makes sense to call. + bool emulate_read_first_ok(); + + // Emulates all read-first read-write port relationships in terms of + // all-transparent ports, by delaying all write ports by one cycle. + // This can only be used when all read ports and all write ports are + // in the same clock domain. + void emulate_read_first(FfInitVals *initvals); + Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {} }; diff --git a/kernel/modtools.h b/kernel/modtools.h index 29c510059..4cbaf78d0 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -380,9 +380,11 @@ struct ModWalker } } - ModWalker(RTLIL::Design *design) : design(design), module(NULL) + ModWalker(RTLIL::Design *design, RTLIL::Module *module = nullptr) : design(design), module(NULL) { - ct.setup(design); + ct.setup(design); + if (module) + setup(module); } void setup(RTLIL::Module *module, CellTypes *filter_ct = NULL) @@ -395,6 +397,8 @@ struct ModWalker signal_consumers.clear(); signal_inputs.clear(); signal_outputs.clear(); + cell_inputs.clear(); + cell_outputs.clear(); for (auto &it : module->wires_) add_wire(it.second); diff --git a/kernel/qcsat.cc b/kernel/qcsat.cc new file mode 100644 index 000000000..aaee984fb --- /dev/null +++ b/kernel/qcsat.cc @@ -0,0 +1,102 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/qcsat.h" + +USING_YOSYS_NAMESPACE + +std::vector<int> QuickConeSat::importSig(SigSpec sig) +{ + sig = modwalker.sigmap(sig); + for (auto bit : sig) + bits_queue.insert(bit); + return satgen.importSigSpec(sig); +} + +int QuickConeSat::importSigBit(SigBit bit) +{ + bit = modwalker.sigmap(bit); + bits_queue.insert(bit); + return satgen.importSigBit(bit); +} + +void QuickConeSat::prepare() +{ + while (!bits_queue.empty()) + { + pool<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, bits_queue); + + for (auto bit : bits_queue) + if (bit.wire && bit.wire->get_bool_attribute(ID::onehot) && !imported_onehot.count(bit.wire)) + { + std::vector<int> bits = satgen.importSigSpec(bit.wire); + for (int i : bits) + for (int j : bits) + if (i != j) + ez->assume(ez->NOT(i), j); + imported_onehot.insert(bit.wire); + } + + bits_queue.clear(); + + for (auto &pbit : portbits) + { + if (imported_cells.count(pbit.cell)) + continue; + if (cell_complexity(pbit.cell) > max_cell_complexity) + continue; + if (max_cell_outs && GetSize(modwalker.cell_outputs[pbit.cell]) > max_cell_outs) + continue; + auto &inputs = modwalker.cell_inputs[pbit.cell]; + bits_queue.insert(inputs.begin(), inputs.end()); + satgen.importCell(pbit.cell); + imported_cells.insert(pbit.cell); + } + + if (max_cell_count && GetSize(imported_cells) > max_cell_count) + break; + } +} + +int QuickConeSat::cell_complexity(RTLIL::Cell *cell) +{ + if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($_BUF_))) + return 0; + if (cell->type.in(ID($not), ID($and), ID($or), ID($xor), ID($xnor), + ID($reduce_and), ID($reduce_or), ID($reduce_xor), + ID($reduce_xnor), ID($reduce_bool), + ID($logic_not), ID($logic_and), ID($logic_or), + ID($eq), ID($ne), ID($eqx), ID($nex), ID($fa), + ID($mux), ID($pmux), ID($bmux), ID($demux), ID($lut), ID($sop), + ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), + ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), + ID($_MUX_), ID($_NMUX_), ID($_MUX4_), ID($_MUX8_), ID($_MUX16_), + ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) + return 1; + if (cell->type.in(ID($neg), ID($add), ID($sub), ID($alu), ID($lcu), + ID($lt), ID($le), ID($gt), ID($ge))) + return 2; + if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) + return 3; + if (cell->type.in(ID($mul), ID($macc), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow))) + return 4; + // Unknown cell. + return 5; +} diff --git a/kernel/qcsat.h b/kernel/qcsat.h new file mode 100644 index 000000000..e4d3c3c5d --- /dev/null +++ b/kernel/qcsat.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net> + * + * 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. + * + */ + +#ifndef QCSAT_H +#define QCSAT_H + +#include "kernel/satgen.h" +#include "kernel/modtools.h" + +YOSYS_NAMESPACE_BEGIN + +// This is a helper class meant for easy construction of quick SAT queries +// to a combinatorial input cone of some set of signals, meant for SAT-based +// optimizations. Various knobs are provided to set just how much of the +// cone should be included in the model — since this class is meant for +// optimization, it should not be a correctness problem when some cells are +// skipped and the solver spuriously returns SAT with a solution that +// cannot exist in reality due to skipped constraints (ie. only UNSAT results +// from this class should be considered binding). +struct QuickConeSat { + ModWalker &modwalker; + ezSatPtr ez; + SatGen satgen; + + // The effort level knobs. + + // The maximum "complexity level" of cells that will be imported. + // - 1: bitwise operations, muxes, equality comparisons, lut, sop, fa + // - 2: addition, subtraction, greater/less than comparisons, lcu + // - 3: shifts + // - 4: multiplication, division, power + int max_cell_complexity = 2; + // The maximum number of cells to import, or 0 for no limit. + int max_cell_count = 0; + // If non-0, skip importing cells with more than this number of output bits. + int max_cell_outs = 0; + + // Internal state. + pool<RTLIL::Cell*> imported_cells; + pool<RTLIL::Wire*> imported_onehot; + pool<RTLIL::SigBit> bits_queue; + + QuickConeSat(ModWalker &modwalker) : modwalker(modwalker), ez(), satgen(ez.get(), &modwalker.sigmap) {} + + // Imports a signal into the SAT solver, queues its input cone to be + // imported in the next prepare() call. + std::vector<int> importSig(SigSpec sig); + int importSigBit(SigBit bit); + + // Imports the input cones of all previously importSig'd signals into + // the SAT solver. + void prepare(); + + // Returns the "complexity level" of a given cell. + static int cell_complexity(RTLIL::Cell *cell); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/register.cc b/kernel/register.cc index 34735a608..226963fda 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/register.h b/kernel/register.h index 5cd849082..15750af2a 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 1faf376e7..a89edd992 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/macc.h" #include "kernel/celltypes.h" +#include "kernel/binding.h" #include "frontends/verilog/verilog_frontend.h" #include "frontends/verilog/preproc.h" #include "backends/rtlil/rtlil_backend.h" @@ -57,6 +58,8 @@ const pool<IdString> &RTLIL::builtin_ff_cell_types() { ID($dffsre), ID($adff), ID($adffe), + ID($aldff), + ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), @@ -117,6 +120,18 @@ const pool<IdString> &RTLIL::builtin_ff_cell_types() { ID($_DFFE_PP0P_), ID($_DFFE_PP1N_), ID($_DFFE_PP1P_), + ID($_ALDFF_NN_), + ID($_ALDFF_NP_), + ID($_ALDFF_PN_), + ID($_ALDFF_PP_), + ID($_ALDFFE_NNN_), + ID($_ALDFFE_NNP_), + ID($_ALDFFE_NPN_), + ID($_ALDFFE_NPP_), + ID($_ALDFFE_PNN_), + ID($_ALDFFE_PNP_), + ID($_ALDFFE_PPN_), + ID($_ALDFFE_PPP_), ID($_SDFF_NN0_), ID($_SDFF_NN1_), ID($_SDFF_NP0_), @@ -363,6 +378,26 @@ bool RTLIL::Const::is_fully_undef() const return true; } +bool RTLIL::Const::is_onehot(int *pos) const +{ + cover("kernel.rtlil.const.is_onehot"); + + bool found = false; + for (int i = 0; i < GetSize(*this); i++) { + auto &bit = bits[i]; + if (bit != RTLIL::State::S0 && bit != RTLIL::State::S1) + return false; + if (bit == RTLIL::State::S1) { + if (found) + return false; + if (pos) + *pos = i; + found = true; + } + } + return found; +} + bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const { return attributes.count(id); @@ -445,6 +480,35 @@ vector<string> RTLIL::AttrObject::get_hdlname_attribute() const return split_tokens(get_string_attribute(ID::hdlname), " "); } +void RTLIL::AttrObject::set_intvec_attribute(RTLIL::IdString id, const vector<int> &data) +{ + std::stringstream attrval; + for (auto &i : data) { + if (attrval.tellp() > 0) + attrval << " "; + attrval << i; + } + attributes[id] = RTLIL::Const(attrval.str()); +} + +vector<int> RTLIL::AttrObject::get_intvec_attribute(RTLIL::IdString id) const +{ + vector<int> data; + auto it = attributes.find(id); + if (it != attributes.end()) + for (const auto &s : split_tokens(attributes.at(id).decode_string())) { + char *end = nullptr; + errno = 0; + long value = strtol(s.c_str(), &end, 10); + if (end != s.c_str() + s.size()) + log_cmd_error("Literal for intvec attribute has invalid format"); + if (errno == ERANGE || value < INT_MIN || value > INT_MAX) + log_cmd_error("Literal for intvec attribute is out of range"); + data.push_back(value); + } + return data; +} + bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const { if (full_selection) @@ -551,8 +615,10 @@ RTLIL::Design::Design() RTLIL::Design::~Design() { - for (auto it = modules_.begin(); it != modules_.end(); ++it) - delete it->second; + for (auto &pr : modules_) + delete pr.second; + for (auto n : bindings_) + delete n; for (auto n : verilog_packages) delete n; for (auto n : verilog_globals) @@ -580,6 +646,11 @@ RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) return modules_.count(name) ? modules_.at(name) : NULL; } +const RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) const +{ + return modules_.count(name) ? modules_.at(name) : NULL; +} + RTLIL::Module *RTLIL::Design::top_module() { RTLIL::Module *module = nullptr; @@ -611,9 +682,16 @@ void RTLIL::Design::add(RTLIL::Module *module) } } +void RTLIL::Design::add(RTLIL::Binding *binding) +{ + log_assert(binding != nullptr); + bindings_.push_back(binding); +} + RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name) { - log_assert(modules_.count(name) == 0); + if (modules_.count(name) != 0) + log_error("Attempted to add new module named '%s', but a module by that name already exists\n", name.c_str()); log_assert(refcount_modules_ == 0); RTLIL::Module *module = new RTLIL::Module; @@ -807,12 +885,12 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules() const return result; } -std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn() const +std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn(bool include_wb) const { std::vector<RTLIL::Module*> result; result.reserve(modules_.size()); for (auto &it : modules_) - if (it.second->get_blackbox_attribute()) + if (it.second->get_blackbox_attribute(include_wb)) continue; else if (selected_whole_module(it.first)) result.push_back(it.second); @@ -838,14 +916,16 @@ RTLIL::Module::Module() RTLIL::Module::~Module() { - for (auto it = wires_.begin(); it != wires_.end(); ++it) - delete it->second; - for (auto it = memories.begin(); it != memories.end(); ++it) - delete it->second; - for (auto it = cells_.begin(); it != cells_.end(); ++it) - delete it->second; - for (auto it = processes.begin(); it != processes.end(); ++it) - delete it->second; + for (auto &pr : wires_) + delete pr.second; + for (auto &pr : memories) + delete pr.second; + for (auto &pr : cells_) + delete pr.second; + for (auto &pr : processes) + delete pr.second; + for (auto binding : bindings_) + delete binding; #ifdef WITH_PYTHON RTLIL::Module::get_all_modules()->erase(hashidx_); #endif @@ -885,9 +965,14 @@ void RTLIL::Module::makeblackbox() set_bool_attribute(ID::blackbox); } -void RTLIL::Module::reprocess_module(RTLIL::Design *, const dict<RTLIL::IdString, RTLIL::Module *> &) +void RTLIL::Module::expand_interfaces(RTLIL::Design *, const dict<RTLIL::IdString, RTLIL::Module *> &) { - log_error("Cannot reprocess_module module `%s' !\n", id2cstr(name)); + log_error("Class doesn't support expand_interfaces (module: `%s')!\n", id2cstr(name)); +} + +bool RTLIL::Module::reprocess_if_necessary(RTLIL::Design *) +{ + return false; } RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString, RTLIL::Const> &, bool mayfail) @@ -1166,6 +1251,22 @@ namespace { return; } + if (cell->type == ID($bmux)) { + port(ID::A, param(ID::WIDTH) << param(ID::S_WIDTH)); + port(ID::S, param(ID::S_WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($demux)) { + port(ID::A, param(ID::WIDTH)); + port(ID::S, param(ID::S_WIDTH)); + port(ID::Y, param(ID::WIDTH) << param(ID::S_WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($lut)) { param(ID::LUT); port(ID::A, param(ID::WIDTH)); @@ -1300,6 +1401,32 @@ namespace { return; } + if (cell->type == ID($aldff)) { + param_bool(ID::CLK_POLARITY); + param_bool(ID::ALOAD_POLARITY); + port(ID::CLK, 1); + port(ID::ALOAD, 1); + port(ID::D, param(ID::WIDTH)); + port(ID::AD, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($aldffe)) { + param_bool(ID::CLK_POLARITY); + param_bool(ID::EN_POLARITY); + param_bool(ID::ALOAD_POLARITY); + port(ID::CLK, 1); + port(ID::EN, 1); + port(ID::ALOAD, 1); + port(ID::D, param(ID::WIDTH)); + port(ID::AD, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($dlatch)) { param_bool(ID::EN_POLARITY); port(ID::EN, 1); @@ -1366,6 +1493,26 @@ namespace { return; } + if (cell->type == ID($memrd_v2)) { + param(ID::MEMID); + param_bool(ID::CLK_ENABLE); + param_bool(ID::CLK_POLARITY); + param(ID::TRANSPARENCY_MASK); + param(ID::COLLISION_X_MASK); + param_bool(ID::CE_OVER_SRST); + param_bits(ID::ARST_VALUE, param(ID::WIDTH)); + param_bits(ID::SRST_VALUE, param(ID::WIDTH)); + param_bits(ID::INIT_VALUE, param(ID::WIDTH)); + port(ID::CLK, 1); + port(ID::EN, 1); + port(ID::ARST, 1); + port(ID::SRST, 1); + port(ID::ADDR, param(ID::ABITS)); + port(ID::DATA, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($memwr)) { param(ID::MEMID); param_bool(ID::CLK_ENABLE); @@ -1379,6 +1526,20 @@ namespace { return; } + if (cell->type == ID($memwr_v2)) { + param(ID::MEMID); + param_bool(ID::CLK_ENABLE); + param_bool(ID::CLK_POLARITY); + param(ID::PORTID); + param(ID::PRIORITY_MASK); + port(ID::CLK, 1); + port(ID::EN, param(ID::WIDTH)); + port(ID::ADDR, param(ID::ABITS)); + port(ID::DATA, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($meminit)) { param(ID::MEMID); param(ID::PRIORITY); @@ -1388,6 +1549,16 @@ namespace { return; } + if (cell->type == ID($meminit_v2)) { + param(ID::MEMID); + param(ID::PRIORITY); + port(ID::ADDR, param(ID::ABITS)); + port(ID::DATA, param(ID::WIDTH) * param(ID::WORDS)); + port(ID::EN, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($mem)) { param(ID::MEMID); param(ID::SIZE); @@ -1410,6 +1581,38 @@ namespace { return; } + if (cell->type == ID($mem_v2)) { + param(ID::MEMID); + param(ID::SIZE); + param(ID::OFFSET); + param(ID::INIT); + param_bits(ID::RD_CLK_ENABLE, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_CLK_POLARITY, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_TRANSPARENCY_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS))); + param_bits(ID::RD_COLLISION_X_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS))); + param_bits(ID::RD_WIDE_CONTINUATION, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_CE_OVER_SRST, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_ARST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH)); + param_bits(ID::RD_SRST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH)); + param_bits(ID::RD_INIT_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH)); + param_bits(ID::WR_CLK_ENABLE, max(1, param(ID::WR_PORTS))); + param_bits(ID::WR_CLK_POLARITY, max(1, param(ID::WR_PORTS))); + param_bits(ID::WR_WIDE_CONTINUATION, max(1, param(ID::WR_PORTS))); + param_bits(ID::WR_PRIORITY_MASK, max(1, param(ID::WR_PORTS) * param(ID::WR_PORTS))); + port(ID::RD_CLK, param(ID::RD_PORTS)); + port(ID::RD_EN, param(ID::RD_PORTS)); + port(ID::RD_ARST, param(ID::RD_PORTS)); + port(ID::RD_SRST, param(ID::RD_PORTS)); + port(ID::RD_ADDR, param(ID::RD_PORTS) * param(ID::ABITS)); + port(ID::RD_DATA, param(ID::RD_PORTS) * param(ID::WIDTH)); + port(ID::WR_CLK, param(ID::WR_PORTS)); + port(ID::WR_EN, param(ID::WR_PORTS) * param(ID::WIDTH)); + port(ID::WR_ADDR, param(ID::WR_PORTS) * param(ID::ABITS)); + port(ID::WR_DATA, param(ID::WR_PORTS) * param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($tribuf)) { port(ID::A, param(ID::WIDTH)); port(ID::Y, param(ID::WIDTH)); @@ -1536,6 +1739,15 @@ namespace { { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::R,1); port(ID::E,1); check_expected(); return; } if (cell->type.in( + ID($_ALDFF_NN_), ID($_ALDFF_NP_), ID($_ALDFF_PN_), ID($_ALDFF_PP_))) + { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::L,1); port(ID::AD,1); check_expected(); return; } + + if (cell->type.in( + ID($_ALDFFE_NNN_), ID($_ALDFFE_NNP_), ID($_ALDFFE_NPN_), ID($_ALDFFE_NPP_), + ID($_ALDFFE_PNN_), ID($_ALDFFE_PNP_), ID($_ALDFFE_PPN_), ID($_ALDFFE_PPP_))) + { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::L,1); port(ID::AD,1); port(ID::E,1); check_expected(); return; } + + if (cell->type.in( ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) { port(ID::C,1); port(ID::S,1); port(ID::R,1); port(ID::D,1); port(ID::Q,1); check_expected(); return; } @@ -1640,7 +1852,7 @@ void RTLIL::Module::check() log_assert(!it.second->type.empty()); for (auto &it2 : it.second->connections()) { log_assert(!it2.first.empty()); - it2.second.check(); + it2.second.check(this); } for (auto &it2 : it.second->attributes) log_assert(!it2.first.empty()); @@ -1686,8 +1898,8 @@ void RTLIL::Module::check() for (auto &it : connections_) { log_assert(it.first.size() == it.second.size()); log_assert(!it.first.has_const()); - it.first.check(); - it.second.check(); + it.first.check(this); + it.second.check(this); } for (auto &it : attributes) @@ -1813,6 +2025,20 @@ void RTLIL::Module::add(RTLIL::Cell *cell) cell->module = this; } +void RTLIL::Module::add(RTLIL::Process *process) +{ + log_assert(!process->name.empty()); + log_assert(count_id(process->name) == 0); + processes[process->name] = process; + process->module = this; +} + +void RTLIL::Module::add(RTLIL::Binding *binding) +{ + log_assert(binding != nullptr); + bindings_.push_back(binding); +} + void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires) { log_assert(refcount_wires_ == 0); @@ -1869,6 +2095,13 @@ void RTLIL::Module::remove(RTLIL::Cell *cell) delete cell; } +void RTLIL::Module::remove(RTLIL::Process *process) +{ + log_assert(processes.count(process->name) != 0); + processes.erase(process->name); + delete process; +} + void RTLIL::Module::rename(RTLIL::Wire *wire, RTLIL::IdString new_name) { log_assert(wires_[wire->name] == wire); @@ -2094,11 +2327,19 @@ RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memor return mem; } +RTLIL::Process *RTLIL::Module::addProcess(RTLIL::IdString name) +{ + RTLIL::Process *proc = new RTLIL::Process; + proc->name = name; + add(proc); + return proc; +} + RTLIL::Process *RTLIL::Module::addProcess(RTLIL::IdString name, const RTLIL::Process *other) { RTLIL::Process *proc = other->clone(); proc->name = name; - processes[name] = proc; + add(proc); return proc; } @@ -2219,6 +2460,26 @@ DEF_METHOD(Mux, ID($mux), 0) DEF_METHOD(Pmux, ID($pmux), 1) #undef DEF_METHOD +#define DEF_METHOD(_func, _type, _demux) \ + RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src) { \ + RTLIL::Cell *cell = addCell(name, _type); \ + cell->parameters[ID::WIDTH] = _demux ? sig_a.size() : sig_y.size(); \ + cell->parameters[ID::S_WIDTH] = sig_s.size(); \ + cell->setPort(ID::A, sig_a); \ + cell->setPort(ID::S, sig_s); \ + cell->setPort(ID::Y, sig_y); \ + cell->set_src_attribute(src); \ + return cell; \ + } \ + RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src) { \ + RTLIL::SigSpec sig_y = addWire(NEW_ID, _demux ? sig_a.size() << sig_s.size() : sig_a.size() >> sig_s.size()); \ + add ## _func(name, sig_a, sig_s, sig_y, src); \ + return sig_y; \ + } +DEF_METHOD(Bmux, ID($bmux), 0) +DEF_METHOD(Demux, ID($demux), 1) +#undef DEF_METHOD + #define DEF_METHOD_2(_func, _type, _P1, _P2) \ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigBit &sig1, const RTLIL::SigBit &sig2, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ @@ -2533,6 +2794,40 @@ RTLIL::Cell* RTLIL::Module::addAdffe(RTLIL::IdString name, const RTLIL::SigSpec return cell; } +RTLIL::Cell* RTLIL::Module::addAldff(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($aldff)); + cell->parameters[ID::CLK_POLARITY] = clk_polarity; + cell->parameters[ID::ALOAD_POLARITY] = aload_polarity; + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::CLK, sig_clk); + cell->setPort(ID::ALOAD, sig_aload); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addAldffe(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool en_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($aldffe)); + cell->parameters[ID::CLK_POLARITY] = clk_polarity; + cell->parameters[ID::EN_POLARITY] = en_polarity; + cell->parameters[ID::ALOAD_POLARITY] = aload_polarity; + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::CLK, sig_clk); + cell->setPort(ID::EN, sig_en); + cell->setPort(ID::ALOAD, sig_aload); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::Cell* RTLIL::Module::addSdff(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity, bool srst_polarity, const std::string &src) { @@ -2723,6 +3018,33 @@ RTLIL::Cell* RTLIL::Module::addAdffeGate(RTLIL::IdString name, const RTLIL::SigS return cell; } +RTLIL::Cell* RTLIL::Module::addAldffGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, stringf("$_ALDFF_%c%c_", clk_polarity ? 'P' : 'N', aload_polarity ? 'P' : 'N')); + cell->setPort(ID::C, sig_clk); + cell->setPort(ID::L, sig_aload); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addAldffeGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool en_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, stringf("$_ALDFFE_%c%c%c_", clk_polarity ? 'P' : 'N', aload_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort(ID::C, sig_clk); + cell->setPort(ID::L, sig_aload); + cell->setPort(ID::E, sig_en); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::Cell* RTLIL::Module::addSdffGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool srst_value, bool clk_polarity, bool srst_polarity, const std::string &src) { @@ -2894,6 +3216,13 @@ RTLIL::Memory::Memory() #endif } +RTLIL::Process::Process() : module(nullptr) +{ + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; +} + RTLIL::Cell::Cell() : module(nullptr) { static unsigned int hashidx_count = 123456789; @@ -3065,14 +3394,21 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")) return; - if (type == ID($mux) || type == ID($pmux)) { + if (type == ID($mux) || type == ID($pmux) || type == ID($bmux)) { parameters[ID::WIDTH] = GetSize(connections_[ID::Y]); - if (type == ID($pmux)) + if (type != ID($mux)) parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); check(); return; } + if (type == ID($demux)) { + parameters[ID::WIDTH] = GetSize(connections_[ID::A]); + parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); + check(); + return; + } + if (type == ID($lut) || type == ID($sop)) { parameters[ID::WIDTH] = GetSize(connections_[ID::A]); return; @@ -3119,6 +3455,16 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) check(); } +bool RTLIL::Cell::has_memid() const +{ + return type.in(ID($memwr), ID($memwr_v2), ID($memrd), ID($memrd_v2), ID($meminit), ID($meminit_v2)); +} + +bool RTLIL::Cell::is_mem_cell() const +{ + return type.in(ID($mem), ID($mem_v2)) || has_memid(); +} + RTLIL::SigChunk::SigChunk() { wire = NULL; @@ -3275,8 +3621,12 @@ RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) { cover("kernel.rtlil.sigspec.init.const"); - chunks_.emplace_back(value); - width_ = chunks_.back().width; + if (GetSize(value) != 0) { + chunks_.emplace_back(value); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3285,8 +3635,12 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk) { cover("kernel.rtlil.sigspec.init.chunk"); - chunks_.emplace_back(chunk); - width_ = chunks_.back().width; + if (chunk.width != 0) { + chunks_.emplace_back(chunk); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3295,8 +3649,12 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire) { cover("kernel.rtlil.sigspec.init.wire"); - chunks_.emplace_back(wire); - width_ = chunks_.back().width; + if (wire->width != 0) { + chunks_.emplace_back(wire); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3305,8 +3663,12 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire, int offset, int width) { cover("kernel.rtlil.sigspec.init.wire_part"); - chunks_.emplace_back(wire, offset, width); - width_ = chunks_.back().width; + if (width != 0) { + chunks_.emplace_back(wire, offset, width); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3315,8 +3677,12 @@ RTLIL::SigSpec::SigSpec(const std::string &str) { cover("kernel.rtlil.sigspec.init.str"); - chunks_.emplace_back(str); - width_ = chunks_.back().width; + if (str.size() != 0) { + chunks_.emplace_back(str); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3325,7 +3691,8 @@ RTLIL::SigSpec::SigSpec(int val, int width) { cover("kernel.rtlil.sigspec.init.int"); - chunks_.emplace_back(val, width); + if (width != 0) + chunks_.emplace_back(val, width); width_ = width; hash_ = 0; check(); @@ -3335,7 +3702,8 @@ RTLIL::SigSpec::SigSpec(RTLIL::State bit, int width) { cover("kernel.rtlil.sigspec.init.state"); - chunks_.emplace_back(bit, width); + if (width != 0) + chunks_.emplace_back(bit, width); width_ = width; hash_ = 0; check(); @@ -3345,11 +3713,13 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigBit &bit, int width) { cover("kernel.rtlil.sigspec.init.bit"); - if (bit.wire == NULL) - chunks_.emplace_back(bit.data, width); - else - for (int i = 0; i < width; i++) - chunks_.push_back(bit); + if (width != 0) { + if (bit.wire == NULL) + chunks_.emplace_back(bit.data, width); + else + for (int i = 0; i < width; i++) + chunks_.push_back(bit); + } width_ = width; hash_ = 0; check(); @@ -3794,7 +4164,13 @@ void RTLIL::SigSpec::remove_const() width_ = 0; for (auto &chunk : chunks_) if (chunk.wire != NULL) { - new_chunks.push_back(chunk); + if (!new_chunks.empty() && + new_chunks.back().wire == chunk.wire && + new_chunks.back().offset + new_chunks.back().width == chunk.offset) { + new_chunks.back().width += chunk.width; + } else { + new_chunks.push_back(chunk); + } width_ += chunk.width; } @@ -3941,7 +4317,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const } #ifndef NDEBUG -void RTLIL::SigSpec::check() const +void RTLIL::SigSpec::check(Module *mod) const { if (width_ > 64) { @@ -3954,6 +4330,7 @@ void RTLIL::SigSpec::check() const int w = 0; for (size_t i = 0; i < chunks_.size(); i++) { const RTLIL::SigChunk &chunk = chunks_[i]; + log_assert(chunk.width != 0); if (chunk.wire == NULL) { if (i > 0) log_assert(chunks_[i-1].wire != NULL); @@ -3966,6 +4343,8 @@ void RTLIL::SigSpec::check() const log_assert(chunk.width >= 0); log_assert(chunk.offset + chunk.width <= chunk.wire->width); log_assert(chunk.data.size() == 0); + if (mod != nullptr) + log_assert(chunk.wire->module == mod); } w += chunk.width; } @@ -3976,6 +4355,12 @@ void RTLIL::SigSpec::check() const { cover("kernel.rtlil.sigspec.check.unpacked"); + if (mod != nullptr) { + for (size_t i = 0; i < bits_.size(); i++) + if (bits_[i].wire != nullptr) + log_assert(bits_[i].wire->module == mod); + } + log_assert(width_ == GetSize(bits_)); log_assert(chunks_.empty()); } @@ -4164,6 +4549,19 @@ bool RTLIL::SigSpec::has_marked_bits() const return false; } +bool RTLIL::SigSpec::is_onehot(int *pos) const +{ + cover("kernel.rtlil.sigspec.is_onehot"); + + pack(); + if (!is_fully_const()) + return false; + log_assert(GetSize(chunks_) <= 1); + if (width_) + return RTLIL::Const(chunks_[0].data).is_onehot(pos); + return false; +} + bool RTLIL::SigSpec::as_bool() const { cover("kernel.rtlil.sigspec.as_bool"); @@ -4537,6 +4935,7 @@ RTLIL::SyncRule *RTLIL::SyncRule::clone() const new_syncrule->type = type; new_syncrule->signal = signal; new_syncrule->actions = actions; + new_syncrule->mem_write_actions = mem_write_actions; return new_syncrule; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 6170ea55e..d8300f159 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -69,8 +69,10 @@ namespace RTLIL struct SigSpec; struct CaseRule; struct SwitchRule; + struct MemWriteAction; struct SyncRule; struct Process; + struct Binding; typedef std::pair<SigSpec, SigSpec> SigSig; @@ -165,7 +167,8 @@ namespace RTLIL log_assert(p[0] == '$' || p[0] == '\\'); log_assert(p[1] != 0); for (const char *c = p; *c; c++) - log_assert((unsigned)*c > (unsigned)' '); + if ((unsigned)*c <= (unsigned)' ') + log_error("Found control character or space (0x%02hhx) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); #ifndef YOSYS_NO_IDS_REFCNT if (global_free_idx_list_.empty()) { @@ -482,6 +485,9 @@ namespace RTLIL RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + // This iterator-range-pair is used for Design::modules(), Module::wires() and Module::cells(). // It maintains a reference counter that is used to make sure that the container is not modified while being iterated over. @@ -660,6 +666,7 @@ struct RTLIL::Const bool is_fully_ones() const; bool is_fully_def() const; bool is_fully_undef() const; + bool is_onehot(int *pos = nullptr) const; inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { RTLIL::Const ret; @@ -714,6 +721,9 @@ struct RTLIL::AttrObject void set_hdlname_attribute(const vector<string> &hierarchy); vector<string> get_hdlname_attribute() const; + + void set_intvec_attribute(RTLIL::IdString id, const vector<int> &data); + vector<int> get_intvec_attribute(RTLIL::IdString id) const; }; struct RTLIL::SigChunk @@ -752,7 +762,7 @@ struct RTLIL::SigBit SigBit(); SigBit(RTLIL::State bit); - SigBit(bool bit); + explicit SigBit(bool bit); SigBit(RTLIL::Wire *wire); SigBit(RTLIL::Wire *wire, int offset); SigBit(const RTLIL::SigChunk &chunk); @@ -834,7 +844,7 @@ public: SigSpec(const std::vector<RTLIL::SigBit> &bits); SigSpec(const pool<RTLIL::SigBit> &bits); SigSpec(const std::set<RTLIL::SigBit> &bits); - SigSpec(bool bit); + explicit SigSpec(bool bit); SigSpec(RTLIL::SigSpec &&other) { width_ = other.width_; @@ -932,6 +942,7 @@ public: bool is_fully_undef() const; bool has_const() const; bool has_marked_bits() const; + bool is_onehot(int *pos = nullptr) const; bool as_bool() const; int as_int(bool is_signed = false) const; @@ -960,9 +971,9 @@ public: unsigned int hash() const { if (!hash_) updhash(); return hash_; }; #ifndef NDEBUG - void check() const; + void check(Module *mod = nullptr) const; #else - void check() const { } + void check(Module *mod = nullptr) const { (void)mod; } #endif }; @@ -1029,6 +1040,8 @@ struct RTLIL::Design int refcount_modules_; dict<RTLIL::IdString, RTLIL::Module*> modules_; + std::vector<RTLIL::Binding*> bindings_; + std::vector<AST::AstNode*> verilog_packages, verilog_globals; std::unique_ptr<define_map_t> verilog_defines; @@ -1041,6 +1054,7 @@ struct RTLIL::Design RTLIL::ObjRange<RTLIL::Module*> modules(); RTLIL::Module *module(RTLIL::IdString name); + const RTLIL::Module *module(RTLIL::IdString name) const; RTLIL::Module *top_module(); bool has(RTLIL::IdString id) const { @@ -1048,6 +1062,8 @@ struct RTLIL::Design } void add(RTLIL::Module *module); + void add(RTLIL::Binding *binding); + RTLIL::Module *addModule(RTLIL::IdString name); void remove(RTLIL::Module *module); void rename(RTLIL::Module *module, RTLIL::IdString new_name); @@ -1110,7 +1126,7 @@ struct RTLIL::Design std::vector<RTLIL::Module*> selected_modules() const; std::vector<RTLIL::Module*> selected_whole_modules() const; - std::vector<RTLIL::Module*> selected_whole_modules_warn() const; + std::vector<RTLIL::Module*> selected_whole_modules_warn(bool include_wb = false) const; #ifdef WITH_PYTHON static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void); #endif @@ -1124,6 +1140,7 @@ struct RTLIL::Module : public RTLIL::AttrObject protected: void add(RTLIL::Wire *wire); void add(RTLIL::Cell *cell); + void add(RTLIL::Process *process); public: RTLIL::Design *design; @@ -1134,7 +1151,9 @@ public: dict<RTLIL::IdString, RTLIL::Wire*> wires_; dict<RTLIL::IdString, RTLIL::Cell*> cells_; - std::vector<RTLIL::SigSig> connections_; + + std::vector<RTLIL::SigSig> connections_; + std::vector<RTLIL::Binding*> bindings_; RTLIL::IdString name; idict<RTLIL::IdString> avail_parameters; @@ -1147,7 +1166,8 @@ public: virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail = false); virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail = false); virtual size_t count_id(RTLIL::IdString id); - virtual void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces); + virtual void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces); + virtual bool reprocess_if_necessary(RTLIL::Design *design); virtual void sort(); virtual void check(); @@ -1189,12 +1209,24 @@ public: return it == cells_.end() ? nullptr : it->second; } + const RTLIL::Wire* wire(RTLIL::IdString id) const{ + auto it = wires_.find(id); + return it == wires_.end() ? nullptr : it->second; + } + const RTLIL::Cell* cell(RTLIL::IdString id) const { + auto it = cells_.find(id); + return it == cells_.end() ? nullptr : it->second; + } + RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); } RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); } + void add(RTLIL::Binding *binding); + // Removing wires is expensive. If you have to remove wires, remove them all at once. void remove(const pool<RTLIL::Wire*> &wires); void remove(RTLIL::Cell *cell); + void remove(RTLIL::Process *process); void rename(RTLIL::Wire *wire, RTLIL::IdString new_name); void rename(RTLIL::Cell *cell, RTLIL::IdString new_name); @@ -1214,6 +1246,7 @@ public: RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other); + RTLIL::Process *addProcess(RTLIL::IdString name); RTLIL::Process *addProcess(RTLIL::IdString name, const RTLIL::Process *other); // The add* methods create a cell and return the created cell. All signals must exist in advance. @@ -1266,6 +1299,8 @@ public: RTLIL::Cell* addMux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); RTLIL::Cell* addPmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addBmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addDemux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); RTLIL::Cell* addSlice (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src = ""); RTLIL::Cell* addConcat (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src = ""); @@ -1286,6 +1321,8 @@ public: RTLIL::Cell* addDffsre (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); RTLIL::Cell* addAdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = ""); RTLIL::Cell* addAdffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool en_polarity = true, bool arst_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool aload_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool en_polarity = true, bool aload_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool srst_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool en_polarity = true, bool srst_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffce (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool en_polarity = true, bool srst_polarity = true, const std::string &src = ""); @@ -1323,6 +1360,10 @@ public: bool arst_value = false, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = ""); RTLIL::Cell* addAdffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool arst_value = false, bool clk_polarity = true, bool en_polarity = true, bool arst_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldffGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool aload_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool en_polarity = true, bool aload_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool srst_value = false, bool clk_polarity = true, bool srst_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, @@ -1339,7 +1380,6 @@ public: RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); - RTLIL::SigSpec Bu0 (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec And (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = ""); @@ -1386,6 +1426,8 @@ public: RTLIL::SigSpec Mux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = ""); RTLIL::SigSpec Pmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = ""); + RTLIL::SigSpec Bmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = ""); + RTLIL::SigSpec Demux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = ""); RTLIL::SigBit BufGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = ""); RTLIL::SigBit NotGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = ""); @@ -1511,6 +1553,9 @@ public: #ifdef WITH_PYTHON static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void); #endif + + bool has_memid() const; + bool is_mem_cell() const; }; struct RTLIL::CaseRule : public RTLIL::AttrObject @@ -1520,7 +1565,6 @@ struct RTLIL::CaseRule : public RTLIL::AttrObject std::vector<RTLIL::SwitchRule*> switches; ~CaseRule(); - void optimize(); bool empty() const; @@ -1543,11 +1587,21 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject RTLIL::SwitchRule *clone() const; }; +struct RTLIL::MemWriteAction : RTLIL::AttrObject +{ + RTLIL::IdString memid; + RTLIL::SigSpec address; + RTLIL::SigSpec data; + RTLIL::SigSpec enable; + RTLIL::Const priority_mask; +}; + struct RTLIL::SyncRule { RTLIL::SyncType type; RTLIL::SigSpec signal; std::vector<RTLIL::SigSig> actions; + std::vector<RTLIL::MemWriteAction> mem_write_actions; template<typename T> void rewrite_sigspecs(T &functor); template<typename T> void rewrite_sigspecs2(T &functor); @@ -1556,12 +1610,21 @@ struct RTLIL::SyncRule struct RTLIL::Process : public RTLIL::AttrObject { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + +protected: + // use module->addProcess() and module->remove() to create or destroy processes + friend struct RTLIL::Module; + Process(); + ~Process(); + +public: RTLIL::IdString name; + RTLIL::Module *module; RTLIL::CaseRule root_case; std::vector<RTLIL::SyncRule*> syncs; - ~Process(); - template<typename T> void rewrite_sigspecs(T &functor); template<typename T> void rewrite_sigspecs2(T &functor); RTLIL::Process *clone() const; @@ -1695,6 +1758,11 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor) functor(it.first); functor(it.second); } + for (auto &it : mem_write_actions) { + functor(it.address); + functor(it.data); + functor(it.enable); + } } template<typename T> @@ -1704,6 +1772,11 @@ void RTLIL::SyncRule::rewrite_sigspecs2(T &functor) for (auto &it : actions) { functor(it.first, it.second); } + for (auto &it : mem_write_actions) { + functor(it.address); + functor(it.data); + functor(it.enable); + } } template<typename T> diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 2a54e78ec..9c40ec66d 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -252,6 +252,106 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } + if (cell->type == ID($bmux)) + { + std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep); + std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep); + std::vector<int> undef_a, undef_s, undef_y; + + if (model_undef) + { + undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep); + undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + } + + if (GetSize(s) == 0) { + ez->vec_set(a, y); + if (model_undef) + ez->vec_set(undef_a, undef_y); + } else { + for (int i = GetSize(s)-1; i >= 0; i--) + { + std::vector<int> out = (i == 0) ? y : ez->vec_var(a.size() / 2); + std::vector<int> yy = model_undef ? ez->vec_var(out.size()) : out; + + std::vector<int> a0(a.begin(), a.begin() + a.size() / 2); + std::vector<int> a1(a.begin() + a.size() / 2, a.end()); + ez->assume(ez->vec_eq(ez->vec_ite(s.at(i), a1, a0), yy)); + + if (model_undef) + { + std::vector<int> undef_out = (i == 0) ? undef_y : ez->vec_var(a.size() / 2); + std::vector<int> undef_a0(undef_a.begin(), undef_a.begin() + a.size() / 2); + std::vector<int> undef_a1(undef_a.begin() + a.size() / 2, undef_a.end()); + std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a0, a1)); + std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a0, undef_a1)); + std::vector<int> yX = ez->vec_ite(undef_s.at(i), undef_ab, ez->vec_ite(s.at(i), undef_a1, undef_a0)); + ez->assume(ez->vec_eq(yX, undef_out)); + undefGating(out, yy, undef_out); + + undef_a = undef_out; + } + + a = out; + } + } + return true; + } + + if (cell->type == ID($demux)) + { + std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep); + std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep); + std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y; + std::vector<int> undef_a, undef_s, undef_y; + + if (model_undef) + { + undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep); + undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + } + + if (GetSize(s) == 0) { + ez->vec_set(a, y); + if (model_undef) + ez->vec_set(undef_a, undef_y); + } else { + for (int i = 0; i < (1 << GetSize(s)); i++) + { + std::vector<int> ss; + for (int j = 0; j < GetSize(s); j++) { + if (i & 1 << j) + ss.push_back(s[j]); + else + ss.push_back(ez->NOT(s[j])); + } + int sss = ez->expression(ezSAT::OpAnd, ss); + + for (int j = 0; j < GetSize(a); j++) { + ez->SET(ez->AND(sss, a[j]), yy.at(i * GetSize(a) + j)); + } + + if (model_undef) + { + int s0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(ss), ez->vec_not(undef_s))); + int us = ez->AND(ez->NOT(s0), ez->expression(ezSAT::OpOr, undef_s)); + for (int j = 0; j < GetSize(a); j++) { + int a0 = ez->AND(ez->NOT(a[j]), ez->NOT(undef_a[j])); + int yX = ez->AND(ez->OR(us, undef_a[j]), ez->NOT(ez->OR(s0, a0))); + ez->SET(yX, undef_y.at(i * GetSize(a) + j)); + } + } + } + if (model_undef) + undefGating(y, yy, undef_y); + } + return true; + } + if (cell->type == ID($pmux)) { std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep); @@ -1081,7 +1181,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) FfData ff(nullptr, cell); // Latches and FFs with async inputs are not supported — use clk2fflogic or async2sync first. - if (!ff.has_d || ff.has_arst || ff.has_sr || (ff.has_en && !ff.has_clk)) + if (ff.has_aload || ff.has_arst || ff.has_sr) return false; if (timestep == 1) @@ -1094,7 +1194,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector<int> undef_d; if (model_undef) undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1); - if (ff.has_srst && ff.has_en && ff.ce_over_srst) { + if (ff.has_srst && ff.has_ce && ff.ce_over_srst) { int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0); std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1); int undef_srst; @@ -1108,21 +1208,21 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) else std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d); } - if (ff.has_en) { - int en = importDefSigSpec(ff.sig_en, timestep-1).at(0); + if (ff.has_ce) { + int ce = importDefSigSpec(ff.sig_ce, timestep-1).at(0); std::vector<int> old_q = importDefSigSpec(ff.sig_q, timestep-1); - int undef_en; + int undef_ce; std::vector<int> undef_old_q; if (model_undef) { - undef_en = importUndefSigSpec(ff.sig_en, timestep-1).at(0); + undef_ce = importUndefSigSpec(ff.sig_ce, timestep-1).at(0); undef_old_q = importUndefSigSpec(ff.sig_q, timestep-1); } - if (ff.pol_en) - std::tie(d, undef_d) = mux(en, undef_en, old_q, undef_old_q, d, undef_d); + if (ff.pol_ce) + std::tie(d, undef_d) = mux(ce, undef_ce, old_q, undef_old_q, d, undef_d); else - std::tie(d, undef_d) = mux(en, undef_en, d, undef_d, old_q, undef_old_q); + std::tie(d, undef_d) = mux(ce, undef_ce, d, undef_d, old_q, undef_old_q); } - if (ff.has_srst && !(ff.has_en && ff.ce_over_srst)) { + if (ff.has_srst && !(ff.has_ce && ff.ce_over_srst)) { int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0); std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1); int undef_srst; diff --git a/kernel/satgen.h b/kernel/satgen.h index cf2db733f..da2cec222 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/sigtools.h b/kernel/sigtools.h index c631fa481..4ea43d743 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h index eba3386d6..e7e4eab6e 100644 --- a/kernel/timinginfo.h +++ b/kernel/timinginfo.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * (C) 2020 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -49,9 +49,9 @@ struct TimingInfo struct ModuleTiming { - RTLIL::IdString type; dict<BitBit, int> comb; - dict<NameBit, int> arrival, required; + dict<NameBit, std::pair<int,NameBit>> arrival, required; + bool has_inputs; }; dict<RTLIL::IdString, ModuleTiming> data; @@ -120,11 +120,10 @@ struct TimingInfo } } else if (cell->type == ID($specify3)) { - auto src = cell->getPort(ID::SRC); + auto src = cell->getPort(ID::SRC).as_bit(); auto dst = cell->getPort(ID::DST); - for (const auto &c : src.chunks()) - if (!c.wire->port_input) - log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); + if (!src.wire || !src.wire->port_input) + log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); for (const auto &c : dst.chunks()) if (!c.wire->port_output) log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst)); @@ -136,34 +135,49 @@ struct TimingInfo max = 0; } for (const auto &d : dst) { - auto &v = t.arrival[NameBit(d)]; - v = std::max(v, max); + auto r = t.arrival.insert(NameBit(d)); + auto &v = r.first->second; + if (r.second || v.first < max) { + v.first = max; + v.second = NameBit(src); + } } } else if (cell->type == ID($specrule)) { - auto type = cell->getParam(ID::TYPE).decode_string(); - if (type != "$setup" && type != "$setuphold") + IdString type = cell->getParam(ID::TYPE).decode_string(); + if (type != ID($setup) && type != ID($setuphold)) continue; auto src = cell->getPort(ID::SRC); - auto dst = cell->getPort(ID::DST); + auto dst = cell->getPort(ID::DST).as_bit(); for (const auto &c : src.chunks()) - if (!c.wire->port_input) + if (!c.wire || !c.wire->port_input) log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); - for (const auto &c : dst.chunks()) - if (!c.wire->port_input) - log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst)); + if (!dst.wire || !dst.wire->port_input) + log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst)); int max = cell->getParam(ID::T_LIMIT_MAX).as_int(); if (max < 0) { log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell)); max = 0; } for (const auto &s : src) { - auto &v = t.required[NameBit(s)]; - v = std::max(v, max); + auto r = t.required.insert(NameBit(s)); + auto &v = r.first->second; + if (r.second || v.first < max) { + v.first = max; + v.second = NameBit(dst); + } } } } + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + if (wire->port_input) { + t.has_inputs = true; + break; + } + } + return t; } diff --git a/kernel/utils.h b/kernel/utils.h index 8942905fe..d37f045ff 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 1caf80c11..09909696b 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -67,6 +67,7 @@ # define INIT_MODULE initlibyosys extern "C" void INIT_MODULE(); #endif +#include <signal.h> #endif #include <limits.h> @@ -137,7 +138,7 @@ void yosys_banner() log(" | |\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n"); log(" | |\n"); - log(" | Copyright (C) 2012 - 2020 Claire Wolf <claire@symbioticeda.com> |\n"); + log(" | Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> |\n"); log(" | |\n"); log(" | Permission to use, copy, modify, and/or distribute this software for any |\n"); log(" | purpose with or without fee is hereby granted, provided that the above |\n"); @@ -540,6 +541,7 @@ void yosys_setup() PyImport_AppendInittab((char*)"libyosys", INIT_MODULE); Py_Initialize(); PyRun_SimpleString("import sys"); + signal(SIGINT, SIG_DFL); #endif Pass::init_register(); @@ -814,7 +816,9 @@ std::string proc_self_dirname() path = (char *) realloc((void *) path, buflen); while (buflen > 0 && path[buflen-1] != '/') buflen--; - return std::string(path, buflen); + std::string str(path, buflen); + free(path); + return str; } #elif defined(_WIN32) std::string proc_self_dirname() @@ -969,7 +973,7 @@ static void handle_label(std::string &command, bool &from_to_active, const std:: } } -void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label, RTLIL::Design *design) +bool run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *from_to_label) { if (design == nullptr) design = yosys_design; @@ -979,11 +983,11 @@ void run_frontend(std::string filename, std::string command, std::string *backen if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".gz") == 0) filename_trim.erase(filename_trim.size()-3); if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-2, std::string::npos, ".v") == 0) - command = "verilog"; + command = " -vlog2k"; else if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".sv") == 0) - command = "verilog -sv"; + command = " -sv"; else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vhd") == 0) - command = "vhdl"; + command = " -vhdl"; else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-5, std::string::npos, ".blif") == 0) command = "blif"; else if (filename_trim.size() > 5 && filename_trim.compare(filename_trim.size()-6, std::string::npos, ".eblif") == 0) @@ -1069,10 +1073,12 @@ void run_frontend(std::string filename, std::string command, std::string *backen if (filename != "-") fclose(f); - if (backend_command != NULL && *backend_command == "auto") - *backend_command = ""; + return true; + } - return; + if (command == "tcl") { + Pass::call(design, vector<string>({command, filename})); + return true; } if (filename == "-") { @@ -1081,16 +1087,15 @@ void run_frontend(std::string filename, std::string command, std::string *backen log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str()); } - if (command == "tcl") - Pass::call(design, vector<string>({command, filename})); - else + if (command[0] == ' ') { + auto argv = split_tokens("read" + command); + argv.push_back(filename); + Pass::call(design, argv); + } else Frontend::frontend_call(design, NULL, filename, command); - design->check(); -} -void run_frontend(std::string filename, std::string command, RTLIL::Design *design) -{ - run_frontend(filename, command, nullptr, nullptr, design); + design->check(); + return false; } void run_pass(std::string command, RTLIL::Design *design) @@ -1404,7 +1409,7 @@ struct ScriptCmdPass : public Pass { else if (args.size() == 2) run_frontend(args[1], "script", design); else if (args.size() == 3) - run_frontend(args[1], "script", NULL, &args[2], design); + run_frontend(args[1], "script", design, &args[2]); else extra_args(args, 2, design, false); } diff --git a/kernel/yosys.h b/kernel/yosys.h index 5df7e2df0..93e7ff23e 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,7 +33,7 @@ // This header is very boring. It just defines some general things that // belong nowhere else and includes the interesting headers. // -// Find more information in the "CodingReadme" file. +// Find more information in the "guidelines/GettingStarted" file. #ifndef YOSYS_H @@ -147,9 +147,7 @@ extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *p # define YS_ATTRIBUTE(...) #endif -#if __cplusplus >= 201703L -# define YS_MAYBE_UNUSED [[maybe_unused]]; -#elif defined(__GNUC__) || defined(__clang__) +#if defined(__GNUC__) || defined(__clang__) # define YS_MAYBE_UNUSED __attribute__((__unused__)) #else # define YS_MAYBE_UNUSED @@ -222,6 +220,7 @@ namespace RTLIL { struct Wire; struct Cell; struct Memory; + struct Process; struct Module; struct Design; struct Monitor; @@ -245,6 +244,7 @@ namespace hashlib { template<> struct hash_ops<RTLIL::Wire*> : hash_obj_ops {}; template<> struct hash_ops<RTLIL::Cell*> : hash_obj_ops {}; template<> struct hash_ops<RTLIL::Memory*> : hash_obj_ops {}; + template<> struct hash_ops<RTLIL::Process*> : hash_obj_ops {}; template<> struct hash_ops<RTLIL::Module*> : hash_obj_ops {}; template<> struct hash_ops<RTLIL::Design*> : hash_obj_ops {}; template<> struct hash_ops<RTLIL::Monitor*> : hash_obj_ops {}; @@ -253,6 +253,7 @@ namespace hashlib { template<> struct hash_ops<const RTLIL::Wire*> : hash_obj_ops {}; template<> struct hash_ops<const RTLIL::Cell*> : hash_obj_ops {}; template<> struct hash_ops<const RTLIL::Memory*> : hash_obj_ops {}; + template<> struct hash_ops<const RTLIL::Process*> : hash_obj_ops {}; template<> struct hash_ops<const RTLIL::Module*> : hash_obj_ops {}; template<> struct hash_ops<const RTLIL::Design*> : hash_obj_ops {}; template<> struct hash_ops<const RTLIL::Monitor*> : hash_obj_ops {}; @@ -349,8 +350,7 @@ std::vector<std::string> glob_filename(const std::string &filename_pattern); void rewrite_filename(std::string &filename); void run_pass(std::string command, RTLIL::Design *design = nullptr); -void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label = nullptr, RTLIL::Design *design = nullptr); -void run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr); +bool run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr, std::string *from_to_label = nullptr); void run_backend(std::string filename, std::string command, RTLIL::Design *design = nullptr); void shell(RTLIL::Design *design); |