diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/calc.cc | 53 | ||||
-rw-r--r-- | kernel/celltypes.h | 32 | ||||
-rw-r--r-- | kernel/constids.inc | 2 | ||||
-rw-r--r-- | kernel/driver.cc | 79 | ||||
-rw-r--r-- | kernel/ff.cc | 19 | ||||
-rw-r--r-- | kernel/ff.h | 8 | ||||
-rw-r--r-- | kernel/fstdata.cc | 30 | ||||
-rw-r--r-- | kernel/hashlib.h | 2 | ||||
-rw-r--r-- | kernel/json.cc | 172 | ||||
-rw-r--r-- | kernel/json.h | 104 | ||||
-rw-r--r-- | kernel/log.cc | 26 | ||||
-rw-r--r-- | kernel/log.h | 79 | ||||
-rw-r--r-- | kernel/mem.cc | 1 | ||||
-rw-r--r-- | kernel/modtools.h | 4 | ||||
-rw-r--r-- | kernel/register.cc | 187 | ||||
-rw-r--r-- | kernel/rtlil.cc | 245 | ||||
-rw-r--r-- | kernel/rtlil.h | 149 | ||||
-rw-r--r-- | kernel/satgen.cc | 88 | ||||
-rw-r--r-- | kernel/satgen.h | 1 | ||||
-rw-r--r-- | kernel/yosys.cc | 152 | ||||
-rw-r--r-- | kernel/yosys.h | 5 | ||||
-rw-r--r-- | kernel/yw.cc | 209 | ||||
-rw-r--r-- | kernel/yw.h | 182 |
23 files changed, 1430 insertions, 399 deletions
diff --git a/kernel/calc.cc b/kernel/calc.cc index 0865db526..9b02a6e30 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -609,6 +609,36 @@ 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_mux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + log_assert(arg2.size() == arg1.size()); + if (arg3[0] == State::S0) + return arg1; + else if (arg3[0] == State::S1) + return arg2; + + RTLIL::Const ret = arg1; + for (int i = 0; i < ret.size(); i++) + if (ret[i] != arg2[i]) + ret[i] = State::Sx; + return ret; +} + +RTLIL::Const RTLIL::const_pmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + if (arg3.is_fully_zero()) + return arg1; + + if (!arg3.is_onehot()) + return RTLIL::Const(State::Sx, arg1.size()); + + for (int i = 0; i < arg3.size(); i++) + if (arg3[i] == State::S1) + return RTLIL::Const(std::vector<RTLIL::State>(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size())); + + log_abort(); // unreachable +} + RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) { std::vector<RTLIL::State> t = arg1.bits; @@ -660,5 +690,28 @@ RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &ar return res; } +RTLIL::Const RTLIL::const_bweqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2) +{ + log_assert(arg2.size() == arg1.size()); + RTLIL::Const result(RTLIL::State::S0, arg1.size()); + for (int i = 0; i < arg1.size(); i++) + result[i] = arg1[i] == arg2[i] ? State::S1 : State::S0; + + return result; +} + +RTLIL::Const RTLIL::const_bwmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + log_assert(arg2.size() == arg1.size()); + log_assert(arg3.size() == arg1.size()); + RTLIL::Const result(RTLIL::State::Sx, arg1.size()); + for (int i = 0; i < arg1.size(); i++) { + if (arg3[i] != State::Sx || arg1[i] == arg2[i]) + result[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i]; + } + + return result; +} + YOSYS_NAMESPACE_END diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 7e9cfb38d..63e7408c1 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -51,6 +51,7 @@ struct CellTypes setup_internals(); setup_internals_mem(); + setup_internals_anyinit(); setup_stdcells(); setup_stdcells_mem(); } @@ -115,7 +116,8 @@ struct CellTypes ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), - ID($logic_and), ID($logic_or), ID($concat), ID($macc) + ID($logic_and), ID($logic_or), ID($concat), ID($macc), + ID($bweqx) }; for (auto type : unary_ops) @@ -124,7 +126,7 @@ struct CellTypes for (auto type : binary_ops) setup_type(type, {ID::A, ID::B}, {ID::Y}, true); - for (auto type : std::vector<RTLIL::IdString>({ID($mux), ID($pmux)})) + for (auto type : std::vector<RTLIL::IdString>({ID($mux), ID($pmux), ID($bwmux)})) setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true); for (auto type : std::vector<RTLIL::IdString>({ID($bmux), ID($demux)})) @@ -155,6 +157,11 @@ struct CellTypes setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}); } + void setup_internals_anyinit() + { + setup_type(ID($anyinit), {ID::D}, {ID::Q}); + } + void setup_internals_mem() { setup_internals_ff(); @@ -424,6 +431,11 @@ struct CellTypes return const_demux(arg1, arg2); } + if (cell->type == ID($bweqx)) + { + return const_bweqx(arg1, arg2); + } + if (cell->type == ID($lut)) { int width = cell->parameters.at(ID::WIDTH).as_int(); @@ -482,16 +494,12 @@ struct CellTypes static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr) { - if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) { - RTLIL::Const ret = arg1; - for (size_t i = 0; i < arg3.bits.size(); i++) - if (arg3.bits[i] == RTLIL::State::S1) { - std::vector<RTLIL::State> bits(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size()); - ret = RTLIL::Const(bits); - } - return ret; - } - + if (cell->type.in(ID($mux), ID($_MUX_))) + return const_mux(arg1, arg2, arg3); + if (cell->type == ID($bwmux)) + return const_bwmux(arg1, arg2, arg3); + if (cell->type == ID($pmux)) + return const_pmux(arg1, arg2, arg3); if (cell->type == ID($_AOI3_)) return eval_not(const_or(const_and(arg1, arg2, false, false, 1), arg3, false, false, 1)); if (cell->type == ID($_OAI3_)) diff --git a/kernel/constids.inc b/kernel/constids.inc index 0f6dfc29b..39211d0c7 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -171,6 +171,7 @@ X(RD_TRANSPARENCY_MASK) X(RD_TRANSPARENT) X(RD_WIDE_CONTINUATION) X(reg) +X(replaced_by_gclk) X(reprocess_after) X(rom_block) X(rom_style) @@ -258,5 +259,6 @@ X(WR_PORTS) X(WR_PRIORITY_MASK) X(WR_WIDE_CONTINUATION) X(X) +X(xprop_decoder) X(Y) X(Y_WIDTH) diff --git a/kernel/driver.cc b/kernel/driver.cc index f8f940e89..ef8e77924 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -33,6 +33,10 @@ #include <string.h> #include <limits.h> #include <errno.h> +#ifndef __STDC_FORMAT_MACROS +# define __STDC_FORMAT_MACROS +#endif +#include <inttypes.h> #if defined (__linux__) || defined(__FreeBSD__) # include <sys/resource.h> @@ -192,6 +196,19 @@ void yosys_atexit() #endif } +#if defined(__OpenBSD__) +namespace Yosys { +extern char *yosys_argv0; +extern char yosys_path[PATH_MAX]; +}; +#endif +#ifdef YOSYS_ENABLE_TCL +namespace Yosys { + extern int yosys_tcl_iterp_init(Tcl_Interp *interp); + extern void yosys_tcl_activate_repl(); +}; +#endif + int main(int argc, char **argv) { std::string frontend_command = "auto"; @@ -203,12 +220,14 @@ int main(int argc, char **argv) std::string scriptfile = ""; std::string depsfile = ""; std::string topmodule = ""; + std::string perffile = ""; bool scriptfile_tcl = false; bool print_banner = true; bool print_stats = true; bool call_abort = false; bool timing_details = false; bool run_shell = true; + bool run_tcl_shell = false; bool mode_v = false; bool mode_q = false; @@ -272,10 +291,13 @@ int main(int argc, char **argv) printf("\n"); printf(" -c tcl_scriptfile\n"); printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); + printf("\n"); + printf(" -C\n"); + printf(" enters TCL interatcive shell mode\n"); #endif printf("\n"); printf(" -p command\n"); - printf(" execute the commands\n"); + printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n"); printf("\n"); printf(" -m module_file\n"); printf(" load the specified module (aka plugin)\n"); @@ -346,7 +368,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:r:D:P:E:x:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:B:")) != -1) { switch (opt) { @@ -481,6 +503,12 @@ int main(int argc, char **argv) case 'x': log_experimentals_ignored.insert(optarg); break; + case 'B': + perffile = optarg; + break; + case 'C': + run_tcl_shell = true; + break; default: fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); @@ -498,6 +526,12 @@ int main(int argc, char **argv) if (print_stats) log_hasher = new SHA1; +#if defined(__OpenBSD__) + // save the executable origin for proc_self_dirname() + yosys_argv0 = argv[0]; + realpath(yosys_argv0, yosys_path); +#endif + #if defined(__linux__) // set stack size to >= 128 MB { @@ -549,10 +583,19 @@ int main(int argc, char **argv) for (auto it = passes_commands.begin(); it != passes_commands.end(); it++) run_pass(*it); - if (run_shell) - shell(yosys_design); - else - run_backend(output_filename, backend_command); + if (run_tcl_shell) { +#ifdef YOSYS_ENABLE_TCL + yosys_tcl_activate_repl(); + Tcl_Main(argc, argv, yosys_tcl_iterp_init); +#else + log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n"); +#endif + } else { + if (run_shell) + shell(yosys_design); + else + run_backend(output_filename, backend_command); + } yosys_design->check(); for (auto it : saved_designs) @@ -661,6 +704,29 @@ int main(int argc, char **argv) } log("%s\n", out_count ? "" : " no commands executed"); } + if(!perffile.empty()) + { + FILE *f = fopen(perffile.c_str(), "wt"); + if (f == nullptr) + log_error("Can't open performance log file for writing: %s\n", strerror(errno)); + + fprintf(f, "{\n"); + fprintf(f, " \"generator\": \"%s\",\n", yosys_version_str); + fprintf(f, " \"total_ns\": %" PRIu64 ",\n", total_ns); + fprintf(f, " \"passes\": {"); + + bool first = true; + for (auto it = timedat.rbegin(); it != timedat.rend(); it++) { + if (!first) + fprintf(f, ","); + fprintf(f, "\n \"%s\": {\n", std::get<2>(*it).c_str()); + fprintf(f, " \"runtime_ns\": %" PRIu64 ",\n", std::get<0>(*it)); + fprintf(f, " \"num_calls\": %u\n", std::get<1>(*it)); + fprintf(f, " }"); + first = false; + } + fprintf(f, "\n }\n}\n"); + } } #if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) @@ -711,4 +777,3 @@ int main(int argc, char **argv) } #endif /* EMSCRIPTEN */ - diff --git a/kernel/ff.cc b/kernel/ff.cc index b0f1a924f..697ba7342 100644 --- a/kernel/ff.cc +++ b/kernel/ff.cc @@ -33,10 +33,14 @@ FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initva 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)) { + if (cell->type.in(ID($anyinit), 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.in(ID($anyinit), ID($ff))) { has_gclk = true; sig_d = cell->getPort(ID::D); + if (cell->type == ID($anyinit)) { + is_anyinit = true; + log_assert(val_init.is_fully_undef()); + } } else if (cell->type == ID($sr)) { // No data input at all. } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { @@ -274,6 +278,7 @@ FfData FfData::slice(const std::vector<int> &bits) { res.has_sr = has_sr; res.ce_over_srst = ce_over_srst; res.is_fine = is_fine; + res.is_anyinit = is_anyinit; res.pol_clk = pol_clk; res.pol_ce = pol_ce; res.pol_aload = pol_aload; @@ -542,7 +547,7 @@ Cell *FfData::emit() { return nullptr; } } - if (initvals) + if (initvals && !is_anyinit) initvals->set_init(sig_q, val_init); if (!is_fine) { if (has_gclk) { @@ -552,7 +557,12 @@ Cell *FfData::emit() { log_assert(!has_arst); log_assert(!has_srst); log_assert(!has_sr); - cell = module->addFf(name, sig_d, sig_q); + if (is_anyinit) { + cell = module->addAnyinit(name, sig_d, sig_q); + log_assert(val_init.is_fully_undef()); + } else { + 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); @@ -603,6 +613,7 @@ Cell *FfData::emit() { log_assert(!has_arst); log_assert(!has_srst); log_assert(!has_sr); + log_assert(!is_anyinit); cell = module->addFfGate(name, sig_d, sig_q); } else if (!has_aload && !has_clk) { log_assert(has_sr); diff --git a/kernel/ff.h b/kernel/ff.h index 41721b4a1..e684d3c43 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -28,7 +28,10 @@ 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. +// Q is simply previous cycle's D. Additionally if is_anyinit is true, this is +// an $anyinit cell which always has an undefined initialization value. Note +// that $anyinit is not considered to be among the FF celltypes, so a pass has +// to explicitly opt-in to process $anyinit cells with FfData. // // Otherwise, the FF/latch can have any number of features selected by has_* // attributes that determine Q's value (in order of decreasing priority): @@ -126,6 +129,8 @@ struct FfData { // True if this FF is a fine cell, false if it is a coarse cell. // If true, width must be 1. bool is_fine; + // True if this FF is an $anyinit cell. Depends on has_gclk. + bool is_anyinit; // Polarities, corresponding to sig_*. True means active-high, false // means active-low. bool pol_clk; @@ -156,6 +161,7 @@ struct FfData { has_sr = false; ce_over_srst = false; is_fine = false; + is_anyinit = false; pol_clk = false; pol_aload = false; pol_ce = false; diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index b2e574b02..65ae3426c 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -78,7 +78,18 @@ uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); } uint64_t FstData::getEndTime() { return fstReaderGetEndTime(ctx); } +static void normalize_brackets(std::string &str) +{ + for (auto &c : str) { + if (c == '<') + c = '['; + else if (c == '>') + c = ']'; + } +} + fstHandle FstData::getHandle(std::string name) { + normalize_brackets(name); if (name_to_handle.find(name) != name_to_handle.end()) return name_to_handle[name]; else @@ -120,6 +131,7 @@ void FstData::extractVarNames() var.is_reg = (fstVarType)h->u.var.typ == FST_VT_VCD_REG; var.name = remove_spaces(h->u.var.name); var.scope = fst_scope_name; + normalize_brackets(var.scope); var.width = h->u.var.length; vars.push_back(var); if (!var.is_alias) @@ -134,35 +146,34 @@ void FstData::extractVarNames() if (clean_name[0]=='\\') clean_name = clean_name.substr(1); size_t pos = clean_name.find_last_of("<"); - if (pos != std::string::npos) { + if (pos != std::string::npos && clean_name.back() == '>') { std::string mem_cell = clean_name.substr(0, pos); + normalize_brackets(mem_cell); std::string addr = clean_name.substr(pos+1); addr.pop_back(); // remove closing bracket char *endptr; int mem_addr = strtol(addr.c_str(), &endptr, 16); if (*endptr) { - log_warning("Error parsing memory address in : %s\n", clean_name.c_str()); + log_debug("Error parsing memory address in : %s\n", clean_name.c_str()); } else { memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; - name_to_handle[stringf("%s.%s[%d]",var.scope.c_str(),mem_cell.c_str(),mem_addr)] = h->u.var.handle; - continue; } } pos = clean_name.find_last_of("["); - if (pos != std::string::npos) { + if (pos != std::string::npos && clean_name.back() == ']') { std::string mem_cell = clean_name.substr(0, pos); + normalize_brackets(mem_cell); std::string addr = clean_name.substr(pos+1); addr.pop_back(); // remove closing bracket char *endptr; int mem_addr = strtol(addr.c_str(), &endptr, 10); if (*endptr) { - log_warning("Error parsing memory address in : %s\n", clean_name.c_str()); + log_debug("Error parsing memory address in : %s\n", clean_name.c_str()); } else { memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; - name_to_handle[stringf("%s.%s[%d]",var.scope.c_str(),mem_cell.c_str(),mem_addr)] = h->u.var.handle; - continue; } } + normalize_brackets(clean_name); name_to_handle[var.scope+"."+clean_name] = h->u.var.handle; break; } @@ -186,7 +197,7 @@ static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandl void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */) { - if (pnt_time > end_time) return; + if (pnt_time > end_time || !pnt_value) return; // if we are past the timestamp bool is_clock = false; if (!all_samples) { @@ -241,6 +252,7 @@ void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t sta past_data = last_data; callback(last_time); } + past_data = last_data; callback(end_time); } diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 0c9f25287..af2827153 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -189,7 +189,7 @@ inline int hashtable_size(int min_size) if (p >= min_size) return p; if (sizeof(int) == 4) - throw std::length_error("hash table exceeded maximum size. use a ILP64 abi for larger tables."); + throw std::length_error("hash table exceeded maximum size.\nDesign is likely too large for yosys to handle, if possible try not to flatten the design."); for (auto p : zero_and_some_primes) if (100129 * p > min_size) return 100129 * p; diff --git a/kernel/json.cc b/kernel/json.cc new file mode 100644 index 000000000..738746267 --- /dev/null +++ b/kernel/json.cc @@ -0,0 +1,172 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> + * + * 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/json.h" + +USING_YOSYS_NAMESPACE + +void PrettyJson::emit_to_log() +{ + struct LogTarget : public Target { + void emit(const char *data) override { log("%s", data); } + }; + + targets.push_back(std::unique_ptr<Target>(new LogTarget)); +} + +void PrettyJson::append_to_string(std::string &target) +{ + struct AppendStringTarget : public Target { + std::string ⌖ + AppendStringTarget(std::string &target) : target(target) {} + void emit(const char *data) override { target += data; } + }; + + targets.push_back(std::unique_ptr<Target>(new AppendStringTarget(target))); +} + +bool PrettyJson::write_to_file(const std::string &path) +{ + struct WriteFileTarget : public Target { + std::ofstream target; + void emit(const char *data) override { target << data; } + void flush() override { target.flush(); } + }; + + auto target = std::unique_ptr<WriteFileTarget>(new WriteFileTarget); + target->target.open(path); + if (target->target.fail()) + return false; + targets.push_back(std::unique_ptr<Target>(target.release())); + return true; +} + +void PrettyJson::line(bool space_if_inline) +{ + if (compact_depth != INT_MAX) { + if (space_if_inline) + raw(" "); + return; + } + int indent = state.size() - (state.empty() ? 0 : state.back() == VALUE); + newline_indent.resize(1 + 2 * indent, ' '); + raw(newline_indent.c_str()); +} + +void PrettyJson::raw(const char *raw_json) +{ + for (auto &target : targets) + target->emit(raw_json); +} + +void PrettyJson::flush() +{ + for (auto &target : targets) + target->flush(); +} + +void PrettyJson::begin_object() +{ + begin_value(); + raw("{"); + state.push_back(OBJECT_FIRST); +} + +void PrettyJson::begin_array() +{ + begin_value(); + raw("["); + state.push_back(ARRAY_FIRST); +} + +void PrettyJson::end_object() +{ + Scope top_scope = state.back(); + state.pop_back(); + if (top_scope == OBJECT) + line(false); + else + log_assert(top_scope == OBJECT_FIRST); + raw("}"); + end_value(); +} + +void PrettyJson::end_array() +{ + Scope top_scope = state.back(); + state.pop_back(); + if (top_scope == ARRAY) + line(false); + else + log_assert(top_scope == ARRAY_FIRST); + raw("]"); + end_value(); +} + +void PrettyJson::name(const char *name) +{ + if (state.back() == OBJECT_FIRST) { + state.back() = OBJECT; + line(false); + } else { + raw(","); + line(); + } + raw(Json(name).dump().c_str()); + raw(": "); + state.push_back(VALUE); +} + +void PrettyJson::begin_value() +{ + if (state.back() == ARRAY_FIRST) { + line(false); + state.back() = ARRAY; + } else if (state.back() == ARRAY) { + raw(","); + line(); + } else { + log_assert(state.back() == VALUE); + state.pop_back(); + } +} + +void PrettyJson::end_value() +{ + if (state.empty()) { + raw("\n"); + flush(); + } + if (GetSize(state) < compact_depth) + compact_depth = INT_MAX; +} + +void PrettyJson::value_json(const Json &value) +{ + begin_value(); + raw(value.dump().c_str()); + end_value(); +} + +void PrettyJson::entry_json(const char *name, const Json &value) +{ + this->name(name); + this->value(value); +} + diff --git a/kernel/json.h b/kernel/json.h new file mode 100644 index 000000000..c9aa0e045 --- /dev/null +++ b/kernel/json.h @@ -0,0 +1,104 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> + * + * 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 JSON_H +#define JSON_H + +#include "kernel/yosys.h" +#include "libs/json11/json11.hpp" +#include <functional> + +YOSYS_NAMESPACE_BEGIN + +using json11::Json; + +class PrettyJson +{ + enum Scope { + VALUE, + OBJECT_FIRST, + OBJECT, + ARRAY_FIRST, + ARRAY, + }; + + struct Target { + virtual void emit(const char *data) = 0; + virtual void flush() {}; + virtual ~Target() {}; + }; + + std::string newline_indent = "\n"; + std::vector<std::unique_ptr<Target>> targets; + std::vector<Scope> state = {VALUE}; + int compact_depth = INT_MAX; +public: + + void emit_to_log(); + void append_to_string(std::string &target); + bool write_to_file(const std::string &path); + + bool active() { return !targets.empty(); } + + void compact() { compact_depth = GetSize(state); } + + void line(bool space_if_inline = true); + void raw(const char *raw_json); + void flush(); + void begin_object(); + void begin_array(); + void end_object(); + void end_array(); + void name(const char *name); + void begin_value(); + void end_value(); + void value_json(const Json &value); + void value(unsigned int value) { value_json(Json((int)value)); } + template<typename T> + void value(T &&value) { value_json(Json(std::forward<T>(value))); }; + + void entry_json(const char *name, const Json &value); + void entry(const char *name, unsigned int value) { entry_json(name, Json((int)value)); } + template<typename T> + void entry(const char *name, T &&value) { entry_json(name, Json(std::forward<T>(value))); }; + + template<typename T> + void object(const T &&values) + { + begin_object(); + for (auto &item : values) + entry(item.first, item.second); + end_object(); + } + + template<typename T> + void array(const T &&values) + { + begin_object(); + for (auto &item : values) + value(item); + end_object(); + } +}; + + + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/log.cc b/kernel/log.cc index 4bcce3b28..75a1ffb45 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -40,8 +40,9 @@ YOSYS_NAMESPACE_BEGIN std::vector<FILE*> log_files; std::vector<std::ostream*> log_streams; +std::vector<std::string> log_scratchpads; std::map<std::string, std::set<std::string>> log_hdump; -std::vector<YS_REGEX_TYPE> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; +std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; dict<std::string, LogExpectedItem> log_expect_log, log_expect_warning, log_expect_error; std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; @@ -158,6 +159,11 @@ void logv(const char *format, va_list ap) for (auto f : log_streams) *f << str; + RTLIL::Design *design = yosys_get_design(); + if (design != nullptr) + for (auto &scratchpad : log_scratchpads) + design->scratchpad[scratchpad].append(str); + static std::string linebuffer; static bool log_warn_regex_recusion_guard = false; @@ -175,11 +181,11 @@ void logv(const char *format, va_list ap) if (!linebuffer.empty() && linebuffer.back() == '\n') { for (auto &re : log_warn_regexes) - if (YS_REGEX_NS::regex_search(linebuffer, re)) + if (std::regex_search(linebuffer, re)) log_warning("Found log message matching -W regex:\n%s", str.c_str()); for (auto &item : log_expect_log) - if (YS_REGEX_NS::regex_search(linebuffer, item.second.pattern)) + if (std::regex_search(linebuffer, item.second.pattern)) item.second.current_count++; linebuffer.clear(); @@ -236,7 +242,7 @@ static void logv_warning_with_prefix(const char *prefix, bool suppressed = false; for (auto &re : log_nowarn_regexes) - if (YS_REGEX_NS::regex_search(message, re)) + if (std::regex_search(message, re)) suppressed = true; if (suppressed) @@ -249,12 +255,12 @@ static void logv_warning_with_prefix(const char *prefix, log_make_debug = 0; for (auto &re : log_werror_regexes) - if (YS_REGEX_NS::regex_search(message, re)) + if (std::regex_search(message, re)) log_error("%s", message.c_str()); bool warning_match = false; for (auto &item : log_expect_warning) - if (YS_REGEX_NS::regex_search(message, item.second.pattern)) { + if (std::regex_search(message, item.second.pattern)) { item.second.current_count++; warning_match = true; } @@ -343,7 +349,7 @@ static void logv_error_with_prefix(const char *prefix, log_make_debug = bak_log_make_debug; for (auto &item : log_expect_error) - if (YS_REGEX_NS::regex_search(log_last_error, item.second.pattern)) + if (std::regex_search(log_last_error, item.second.pattern)) item.second.current_count++; log_check_expected(); @@ -352,6 +358,9 @@ static void logv_error_with_prefix(const char *prefix, log_error_atexit(); YS_DEBUGTRAP_IF_DEBUGGING; + const char *e = getenv("YOSYS_ABORT_ON_LOG_ERROR"); + if (e && atoi(e)) + abort(); #ifdef EMSCRIPTEN log_files = backup_log_files; @@ -627,7 +636,7 @@ const char *log_const(const RTLIL::Const &value, bool autoint) } } -const char *log_id(RTLIL::IdString str) +const char *log_id(const RTLIL::IdString &str) { log_id_cache.push_back(strdup(str.c_str())); const char *p = log_id_cache.back(); @@ -698,6 +707,7 @@ void log_check_expected() if (item.second.current_count == item.second.expected_count) { log_warn_regexes.clear(); log("Expected error pattern '%s' found !!!\n", item.first.c_str()); + yosys_shutdown(); #ifdef EMSCRIPTEN throw 0; #elif defined(_MSC_VER) diff --git a/kernel/log.h b/kernel/log.h index ea14028dd..3a6ec8758 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -24,51 +24,14 @@ #include <time.h> -// 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 - #define YS_REGEX_NS boost::xpressive - #define YS_REGEX_COMPILE(param) boost::xpressive::sregex::compile(param, \ - boost::xpressive::regex_constants::nosubs | \ - boost::xpressive::regex_constants::optimize) - #define YS_REGEX_COMPILE_WITH_SUBS(param) boost::xpressive::sregex::compile(param, \ - boost::xpressive::regex_constants::optimize) -# else - #include <regex> - #define YS_REGEX_TYPE std::regex - #define YS_REGEX_MATCH_TYPE std::smatch - #define YS_REGEX_NS std - #define YS_REGEX_COMPILE(param) std::regex(param, \ - std::regex_constants::nosubs | \ - std::regex_constants::optimize | \ - std::regex_constants::egrep) - #define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \ - std::regex_constants::optimize | \ - std::regex_constants::egrep) -#endif +#include <regex> +#define YS_REGEX_COMPILE(param) std::regex(param, \ + std::regex_constants::nosubs | \ + std::regex_constants::optimize | \ + std::regex_constants::egrep) +#define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \ + std::regex_constants::optimize | \ + std::regex_constants::egrep) #if defined(_WIN32) # include <intrin.h> @@ -133,8 +96,9 @@ struct log_cmd_error_exception { }; extern std::vector<FILE*> log_files; extern std::vector<std::ostream*> log_streams; +extern std::vector<std::string> log_scratchpads; extern std::map<std::string, std::set<std::string>> log_hdump; -extern std::vector<YS_REGEX_TYPE> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; +extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; extern int log_warnings_count; extern int log_warnings_count_noexpect; @@ -223,11 +187,11 @@ void log_flush(); struct LogExpectedItem { - LogExpectedItem(const YS_REGEX_TYPE &pat, int expected) : + LogExpectedItem(const std::regex &pat, int expected) : pattern(pat), expected_count(expected), current_count(0) {} LogExpectedItem() : expected_count(0), current_count(0) {} - YS_REGEX_TYPE pattern; + std::regex pattern; int expected_count; int current_count; }; @@ -237,7 +201,7 @@ void log_check_expected(); const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true); const char *log_const(const RTLIL::Const &value, bool autoint = true); -const char *log_id(RTLIL::IdString id); +const char *log_id(const RTLIL::IdString &id); template<typename T> static inline const char *log_id(T *obj, const char *nullstr = nullptr) { if (nullstr && obj == nullptr) @@ -393,6 +357,11 @@ void log_dump_val_worker(RTLIL::IdString v); void log_dump_val_worker(RTLIL::SigSpec v); void log_dump_val_worker(RTLIL::State v); +template<typename K, typename T, typename OPS> static inline void log_dump_val_worker(dict<K, T, OPS> &v); +template<typename K, typename OPS> static inline void log_dump_val_worker(pool<K, OPS> &v); +template<typename K> static inline void log_dump_val_worker(std::vector<K> &v); +template<typename T> static inline void log_dump_val_worker(T *ptr); + template<typename K, typename T, typename OPS> static inline void log_dump_val_worker(dict<K, T, OPS> &v) { log("{"); @@ -419,6 +388,18 @@ static inline void log_dump_val_worker(pool<K, OPS> &v) { log(" }"); } +template<typename K> +static inline void log_dump_val_worker(std::vector<K> &v) { + log("{"); + bool first = true; + for (auto &it : v) { + log(first ? " " : ", "); + log_dump_val_worker(it); + first = false; + } + log(" }"); +} + template<typename T> static inline void log_dump_val_worker(T *ptr) { log("%p", ptr); } diff --git a/kernel/mem.cc b/kernel/mem.cc index e5e855ef7..ed01a0867 100644 --- a/kernel/mem.cc +++ b/kernel/mem.cc @@ -504,6 +504,7 @@ void Mem::check() { int mask = (1 << max_wide_log2) - 1; log_assert(!(start_offset & mask)); log_assert(!(size & mask)); + log_assert(width != 0); } namespace { diff --git a/kernel/modtools.h b/kernel/modtools.h index 4cbaf78d0..34a23b379 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -409,7 +409,6 @@ struct ModWalker // get_* methods -- single RTLIL::SigBit - template<typename T> inline bool get_drivers(pool<PortBit> &result, RTLIL::SigBit bit) const { bool found = false; @@ -421,7 +420,6 @@ struct ModWalker return found; } - template<typename T> inline bool get_consumers(pool<PortBit> &result, RTLIL::SigBit bit) const { bool found = false; @@ -433,7 +431,6 @@ struct ModWalker return found; } - template<typename T> inline bool get_inputs(pool<RTLIL::SigBit> &result, RTLIL::SigBit bit) const { bool found = false; @@ -442,7 +439,6 @@ struct ModWalker return found; } - template<typename T> inline bool get_outputs(pool<RTLIL::SigBit> &result, RTLIL::SigBit bit) const { bool found = false; diff --git a/kernel/register.cc b/kernel/register.cc index 226963fda..9ffb17c1a 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -108,7 +108,9 @@ Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_he void Pass::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); + pass_register[pass_name] = this; } @@ -445,10 +447,13 @@ Frontend::Frontend(std::string name, std::string short_help) : void Frontend::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; - log_assert(frontend_register.count(frontend_name) == 0); + if (frontend_register.count(frontend_name)) + log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str()); + frontend_register[frontend_name] = this; } @@ -526,10 +531,11 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s std::ifstream *ff = new std::ifstream; ff->open(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in); yosys_input_files.insert(filename); - if (ff->fail()) + if (ff->fail()) { delete ff; - else - f = ff; + ff = nullptr; + } + f = ff; if (f != NULL) { // Check for gzip magic unsigned char magic[3]; @@ -626,10 +632,12 @@ Backend::Backend(std::string name, std::string short_help) : void Backend::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; - log_assert(backend_register.count(backend_name) == 0); + if (backend_register.count(backend_name)) + log_error("Unable to register backend '%s', backend already exists!\n", backend_name.c_str()); backend_register[backend_name] = this; } @@ -765,61 +773,98 @@ struct HelpPass : public Pass { log(" help <celltype>+ .... print verilog code for given cell type\n"); log("\n"); } - void escape_tex(std::string &tex) - { - for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2) - tex.replace(pos, 1, "\\_"); - for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2) - tex.replace(pos, 1, "\\$"); - } - void write_tex(FILE *f, std::string cmd, std::string title, std::string text) + void write_rst(std::string cmd, std::string title, std::string text) { - size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n"); - if (begin != std::string::npos && end != std::string::npos && begin < end) - text = text.substr(begin, end-begin+1); - std::string cmd_unescaped = cmd; - escape_tex(cmd); - escape_tex(title); - fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str()); - fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str()); - fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n"); - fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str()); - } - void escape_html(std::string &html) - { - size_t pos = 0; - while ((pos = html.find_first_of("<>&", pos)) != std::string::npos) - switch (html[pos]) { - case '<': - html.replace(pos, 1, "<"); - pos += 4; - break; - case '>': - html.replace(pos, 1, ">"); - pos += 4; - break; - case '&': - html.replace(pos, 1, "&"); - pos += 5; - break; + FILE *f = fopen(stringf("docs/source/cmd/%s.rst", cmd.c_str()).c_str(), "wt"); + // make header + size_t char_len = cmd.length() + 3 + title.length(); + std::string title_line = "\n"; + title_line.insert(0, char_len, '='); + fprintf(f, "%s", title_line.c_str()); + fprintf(f, "%s - %s\n", cmd.c_str(), title.c_str()); + fprintf(f, "%s\n", title_line.c_str()); + fprintf(f, ".. raw:: latex\n\n \\begin{comment}\n\n"); + + // render html + fprintf(f, ":code:`yosys> help %s`\n", cmd.c_str()); + fprintf(f, "--------------------------------------------------------------------------------\n\n"); + fprintf(f, ".. container:: cmdref\n"); + std::stringstream ss; + std::string textcp = text; + ss << text; + bool IsUsage = true; + int blank_count = 0; + size_t def_strip_count = 0; + bool WasDefinition = false; + for (std::string line; std::getline(ss, line, '\n');) { + // find position of first non space character + std::size_t first_pos = line.find_first_not_of(" \t"); + std::size_t last_pos = line.find_last_not_of(" \t"); + if (first_pos == std::string::npos) { + // skip formatting empty lines + if (!WasDefinition) + fputc('\n', f); + blank_count += 1; + continue; } - } - void write_html(FILE *idxf, std::string cmd, std::string title, std::string text) - { - FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt"); - fprintf(idxf, "<li><a href=\"cmd_%s.html\"> ", cmd.c_str()); - escape_html(cmd); - escape_html(title); - escape_html(text); - - fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str()); - - fprintf(f, "@cmd_header %s@\n", cmd.c_str()); - fprintf(f, "<h1>%s - %s</h1>\n", cmd.c_str(), title.c_str()); - fprintf(f, "<pre>%s</pre>\n", text.c_str()); - fprintf(f, "@footer@\n"); + // strip leading and trailing whitespace + std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); + bool IsDefinition = stripped_line[0] == '-'; + IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; + bool IsDedent = def_strip_count && first_pos <= def_strip_count; + bool IsIndent = first_pos == 2 || first_pos == 4; + if (cmd.compare(0, 7, "verific") == 0) + // verific.cc has strange and different formatting from the rest + IsIndent = false; + + // another usage block + bool NewUsage = stripped_line.find(cmd) == 0; + + if (IsUsage) { + if (stripped_line.compare(0, 4, "See ") == 0) { + // description refers to another function + fprintf(f, "\n %s\n", stripped_line.c_str()); + } else { + // usage should be the first line of help output + fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + } + IsUsage = false; + } else if (IsIndent && NewUsage && (blank_count >= 2 || WasDefinition)) { + // another usage block + fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + def_strip_count = 0; + } else if (IsIndent && IsDefinition && (blank_count || WasDefinition)) { + // format definition term + fprintf(f, "\n\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + def_strip_count = first_pos; + } else { + if (IsDedent) { + fprintf(f, "\n\n ::\n"); + def_strip_count = first_pos; + } else if (WasDefinition) { + fprintf(f, " ::\n"); + WasDefinition = false; + } + fprintf(f, "\n %s", line.substr(def_strip_count, std::string::npos).c_str()); + } + blank_count = 0; + } + fputc('\n', f); + + // render latex + fprintf(f, ".. raw:: latex\n\n \\end{comment}\n\n"); + fprintf(f, ".. only:: latex\n\n"); + fprintf(f, " ::\n\n"); + std::stringstream ss2; + ss2 << textcp; + for (std::string line; std::getline(ss2, line, '\n');) { + fprintf(f, " %s\n", line.c_str()); + } fclose(f); } void execute(std::vector<std::string> args, RTLIL::Design*) override @@ -864,26 +909,7 @@ struct HelpPass : public Pass { return; } // this option is undocumented as it is for internal use only - else if (args[1] == "-write-tex-command-reference-manual") { - FILE *f = fopen("command-reference-manual.tex", "wt"); - fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n"); - for (auto &it : pass_register) { - std::ostringstream buf; - log_streams.push_back(&buf); - it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } - log_streams.pop_back(); - write_tex(f, it.first, it.second->short_help, buf.str()); - } - fclose(f); - } - // this option is undocumented as it is for internal use only - else if (args[1] == "-write-web-command-reference-manual") { - FILE *f = fopen("templates/cmd_index.in", "wt"); + else if (args[1] == "-write-rst-command-reference-manual") { for (auto &it : pass_register) { std::ostringstream buf; log_streams.push_back(&buf); @@ -894,9 +920,8 @@ struct HelpPass : public Pass { log("\n"); } log_streams.pop_back(); - write_html(f, it.first, it.second->short_help, buf.str()); + write_rst(it.first, it.second->short_help, buf.str()); } - fclose(f); } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index dc4ea9a78..7f3508b2f 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -199,11 +199,6 @@ const pool<IdString> &RTLIL::builtin_ff_cell_types() { return res; } -RTLIL::Const::Const() -{ - flags = RTLIL::CONST_FLAG_NONE; -} - RTLIL::Const::Const(const std::string &str) { flags = RTLIL::CONST_FLAG_STRING; @@ -375,6 +370,17 @@ bool RTLIL::Const::is_fully_undef() const return true; } +bool RTLIL::Const::is_fully_undef_x_only() const +{ + cover("kernel.rtlil.const.is_fully_undef_x_only"); + + for (const auto &bit : bits) + if (bit != RTLIL::State::Sx) + return false; + + return true; +} + bool RTLIL::Const::is_onehot(int *pos) const { cover("kernel.rtlil.const.is_onehot"); @@ -395,12 +401,12 @@ bool RTLIL::Const::is_onehot(int *pos) const return found; } -bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const +bool RTLIL::AttrObject::has_attribute(const RTLIL::IdString &id) const { return attributes.count(id); } -void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) +void RTLIL::AttrObject::set_bool_attribute(const RTLIL::IdString &id, bool value) { if (value) attributes[id] = RTLIL::Const(1); @@ -408,7 +414,7 @@ void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) attributes.erase(id); } -bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const +bool RTLIL::AttrObject::get_bool_attribute(const RTLIL::IdString &id) const { const auto it = attributes.find(id); if (it == attributes.end()) @@ -416,7 +422,7 @@ bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const return it->second.as_bool(); } -void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) +void RTLIL::AttrObject::set_string_attribute(const RTLIL::IdString& id, string value) { if (value.empty()) attributes.erase(id); @@ -424,7 +430,7 @@ void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) attributes[id] = value; } -string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const +string RTLIL::AttrObject::get_string_attribute(const RTLIL::IdString &id) const { std::string value; const auto it = attributes.find(id); @@ -433,7 +439,7 @@ string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const return value; } -void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data) +void RTLIL::AttrObject::set_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data) { string attrval; for (const auto &s : data) { @@ -444,7 +450,7 @@ void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<str set_string_attribute(id, attrval); } -void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool<string> &data) +void RTLIL::AttrObject::add_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data) { pool<string> union_data = get_strpool_attribute(id); union_data.insert(data.begin(), data.end()); @@ -452,7 +458,7 @@ void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool<str set_strpool_attribute(id, union_data); } -pool<string> RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const +pool<string> RTLIL::AttrObject::get_strpool_attribute(const RTLIL::IdString &id) const { pool<string> data; if (attributes.count(id) != 0) @@ -477,7 +483,7 @@ 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) +void RTLIL::AttrObject::set_intvec_attribute(const RTLIL::IdString& id, const vector<int> &data) { std::stringstream attrval; for (auto &i : data) { @@ -488,7 +494,7 @@ void RTLIL::AttrObject::set_intvec_attribute(RTLIL::IdString id, const vector<in attributes[id] = RTLIL::Const(attrval.str()); } -vector<int> RTLIL::AttrObject::get_intvec_attribute(RTLIL::IdString id) const +vector<int> RTLIL::AttrObject::get_intvec_attribute(const RTLIL::IdString &id) const { vector<int> data; auto it = attributes.find(id); @@ -506,7 +512,7 @@ vector<int> RTLIL::AttrObject::get_intvec_attribute(RTLIL::IdString id) const return data; } -bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const +bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const { if (full_selection) return true; @@ -517,7 +523,7 @@ bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const return false; } -bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const +bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) const { if (full_selection) return true; @@ -526,7 +532,7 @@ bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const return false; } -bool RTLIL::Selection::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const +bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const { if (full_selection) return true; @@ -638,12 +644,12 @@ RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules() return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_); } -RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) +RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) { return modules_.count(name) ? modules_.at(name) : NULL; } -const RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) const +const RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) const { return modules_.count(name) ? modules_.at(name) : NULL; } @@ -825,7 +831,7 @@ void RTLIL::Design::optimize() it.second.optimize(this); } -bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const +bool RTLIL::Design::selected_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -834,7 +840,7 @@ bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const return selection_stack.back().selected_module(mod_name); } -bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const +bool RTLIL::Design::selected_whole_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -843,7 +849,7 @@ bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const return selection_stack.back().selected_whole_module(mod_name); } -bool RTLIL::Design::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const +bool RTLIL::Design::selected_member(const RTLIL::IdString& mod_name, const RTLIL::IdString& memb_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -987,7 +993,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); } -size_t RTLIL::Module::count_id(RTLIL::IdString id) +size_t RTLIL::Module::count_id(const RTLIL::IdString& id) { return wires_.count(id) + memories.count(id) + cells_.count(id) + processes.count(id); } @@ -1012,7 +1018,7 @@ namespace { cell->name.c_str(), cell->type.c_str(), __FILE__, linenr, buf.str().c_str()); } - int param(RTLIL::IdString name) + int param(const RTLIL::IdString& name) { auto it = cell->parameters.find(name); if (it == cell->parameters.end()) @@ -1021,7 +1027,7 @@ namespace { return it->second.as_int(); } - int param_bool(RTLIL::IdString name) + int param_bool(const RTLIL::IdString& name) { int v = param(name); if (GetSize(cell->parameters.at(name)) > 32) @@ -1031,7 +1037,7 @@ namespace { return v; } - int param_bool(RTLIL::IdString name, bool expected) + int param_bool(const RTLIL::IdString& name, bool expected) { int v = param_bool(name); if (v != expected) @@ -1039,14 +1045,14 @@ namespace { return v; } - void param_bits(RTLIL::IdString name, int width) + void param_bits(const RTLIL::IdString& name, int width) { param(name); if (GetSize(cell->parameters.at(name).bits) != width) error(__LINE__); } - void port(RTLIL::IdString name, int width) + void port(const RTLIL::IdString& name, int width) { auto it = cell->connections_.find(name); if (it == cell->connections_.end()) @@ -1618,6 +1624,23 @@ namespace { return; } + if (cell->type == ID($bweqx)) { + port(ID::A, param(ID::WIDTH)); + port(ID::B, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($bwmux)) { + port(ID::A, param(ID::WIDTH)); + port(ID::B, param(ID::WIDTH)); + port(ID::S, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) { port(ID::A, 1); port(ID::EN, 1); @@ -1637,6 +1660,13 @@ namespace { return; } + if (cell->type.in(ID($anyinit))) { + port(ID::D, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($equiv)) { port(ID::A, 1); port(ID::B, 1); @@ -2464,6 +2494,7 @@ DEF_METHOD(Sshr, sig_a.size(), ID($sshr)) return sig_y; \ } DEF_METHOD(Mux, ID($mux), 0) +DEF_METHOD(Bwmux, ID($bwmux), 0) DEF_METHOD(Pmux, ID($pmux), 1) #undef DEF_METHOD @@ -2487,6 +2518,24 @@ DEF_METHOD(Bmux, ID($bmux), 0) DEF_METHOD(Demux, ID($demux), 1) #undef DEF_METHOD +#define DEF_METHOD(_func, _type) \ + RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src) { \ + RTLIL::Cell *cell = addCell(name, _type); \ + cell->parameters[ID::WIDTH] = sig_a.size(); \ + cell->setPort(ID::A, sig_a); \ + cell->setPort(ID::B, sig_b); \ + 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, sig_a.size()); \ + add ## _func(name, sig_a, sig_s, sig_y, src); \ + return sig_y; \ + } +DEF_METHOD(Bweqx, ID($bweqx)) +#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); \ @@ -3125,6 +3174,16 @@ RTLIL::Cell* RTLIL::Module::addDlatchsrGate(RTLIL::IdString name, const RTLIL::S return cell; } +RTLIL::Cell* RTLIL::Module::addAnyinit(RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($anyinit)); + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::SigSpec RTLIL::Module::Anyconst(RTLIL::IdString name, int width, const std::string &src) { RTLIL::SigSpec sig = addWire(NEW_ID, width); @@ -3259,12 +3318,12 @@ std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void) } #endif -bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const +bool RTLIL::Cell::hasPort(const RTLIL::IdString& portname) const { return connections_.count(portname) != 0; } -void RTLIL::Cell::unsetPort(RTLIL::IdString portname) +void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname) { RTLIL::SigSpec signal; auto conn_it = connections_.find(portname); @@ -3287,7 +3346,7 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname) } } -void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) +void RTLIL::Cell::setPort(const RTLIL::IdString& portname, RTLIL::SigSpec signal) { auto r = connections_.insert(portname); auto conn_it = r.first; @@ -3309,7 +3368,7 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) conn_it->second = std::move(signal); } -const RTLIL::SigSpec &RTLIL::Cell::getPort(RTLIL::IdString portname) const +const RTLIL::SigSpec &RTLIL::Cell::getPort(const RTLIL::IdString& portname) const { return connections_.at(portname); } @@ -3328,7 +3387,7 @@ bool RTLIL::Cell::known() const return false; } -bool RTLIL::Cell::input(RTLIL::IdString portname) const +bool RTLIL::Cell::input(const RTLIL::IdString& portname) const { if (yosys_celltypes.cell_known(type)) return yosys_celltypes.cell_input(type, portname); @@ -3340,7 +3399,7 @@ bool RTLIL::Cell::input(RTLIL::IdString portname) const return false; } -bool RTLIL::Cell::output(RTLIL::IdString portname) const +bool RTLIL::Cell::output(const RTLIL::IdString& portname) const { if (yosys_celltypes.cell_known(type)) return yosys_celltypes.cell_output(type, portname); @@ -3352,22 +3411,22 @@ bool RTLIL::Cell::output(RTLIL::IdString portname) const return false; } -bool RTLIL::Cell::hasParam(RTLIL::IdString paramname) const +bool RTLIL::Cell::hasParam(const RTLIL::IdString& paramname) const { return parameters.count(paramname) != 0; } -void RTLIL::Cell::unsetParam(RTLIL::IdString paramname) +void RTLIL::Cell::unsetParam(const RTLIL::IdString& paramname) { parameters.erase(paramname); } -void RTLIL::Cell::setParam(RTLIL::IdString paramname, RTLIL::Const value) +void RTLIL::Cell::setParam(const RTLIL::IdString& paramname, RTLIL::Const value) { parameters[paramname] = std::move(value); } -const RTLIL::Const &RTLIL::Cell::getParam(RTLIL::IdString paramname) const +const RTLIL::Const &RTLIL::Cell::getParam(const RTLIL::IdString& paramname) const { const auto &it = parameters.find(paramname); if (it != parameters.end()) @@ -3472,61 +3531,6 @@ bool RTLIL::Cell::is_mem_cell() const return type.in(ID($mem), ID($mem_v2)) || has_memid(); } -RTLIL::SigChunk::SigChunk() -{ - wire = NULL; - width = 0; - offset = 0; -} - -RTLIL::SigChunk::SigChunk(const RTLIL::Const &value) -{ - wire = NULL; - data = value.bits; - width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire) -{ - log_assert(wire != nullptr); - this->wire = wire; - this->width = wire->width; - this->offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire, int offset, int width) -{ - log_assert(wire != nullptr); - this->wire = wire; - this->width = width; - this->offset = offset; -} - -RTLIL::SigChunk::SigChunk(const std::string &str) -{ - wire = NULL; - data = RTLIL::Const(str).bits; - width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(int val, int width) -{ - wire = NULL; - data = RTLIL::Const(val, width).bits; - this->width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::State bit, int width) -{ - wire = NULL; - data = RTLIL::Const(bit, width).bits; - this->width = GetSize(data); - offset = 0; -} - RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) { wire = bit.wire; @@ -3538,11 +3542,6 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) width = 1; } -RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) -{ - *this = sigchunk; -} - RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const { RTLIL::SigChunk ret; @@ -3588,17 +3587,6 @@ bool RTLIL::SigChunk::operator !=(const RTLIL::SigChunk &other) const return true; } -RTLIL::SigSpec::SigSpec() -{ - width_ = 0; - hash_ = 0; -} - -RTLIL::SigSpec::SigSpec(const RTLIL::SigSpec &other) -{ - *this = other; -} - RTLIL::SigSpec::SigSpec(std::initializer_list<RTLIL::SigSpec> parts) { cover("kernel.rtlil.sigspec.init.list"); @@ -3613,23 +3601,26 @@ RTLIL::SigSpec::SigSpec(std::initializer_list<RTLIL::SigSpec> parts) append(*it--); } -RTLIL::SigSpec &RTLIL::SigSpec::operator=(const RTLIL::SigSpec &other) +RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) { - cover("kernel.rtlil.sigspec.assign"); + cover("kernel.rtlil.sigspec.init.const"); - width_ = other.width_; - hash_ = other.hash_; - chunks_ = other.chunks_; - bits_ = other.bits_; - return *this; + if (GetSize(value) != 0) { + chunks_.emplace_back(value); + width_ = chunks_.back().width; + } else { + width_ = 0; + } + hash_ = 0; + check(); } -RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) +RTLIL::SigSpec::SigSpec(RTLIL::Const &&value) { - cover("kernel.rtlil.sigspec.init.const"); + cover("kernel.rtlil.sigspec.init.const.move"); if (GetSize(value) != 0) { - chunks_.emplace_back(value); + chunks_.emplace_back(std::move(value)); width_ = chunks_.back().width; } else { width_ = 0; @@ -3652,6 +3643,20 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk) check(); } +RTLIL::SigSpec::SigSpec(RTLIL::SigChunk &&chunk) +{ + cover("kernel.rtlil.sigspec.init.chunk.move"); + + if (chunk.width != 0) { + chunks_.emplace_back(std::move(chunk)); + width_ = chunks_.back().width; + } else { + width_ = 0; + } + hash_ = 0; + check(); +} + RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire) { cover("kernel.rtlil.sigspec.init.wire"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 7a0b6b9c7..7c7669caa 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -168,7 +168,7 @@ namespace RTLIL log_assert(p[1] != 0); for (const char *c = p; *c; c++) 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); + log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); #ifndef YOSYS_NO_IDS_REFCNT if (global_free_idx_list_.empty()) { @@ -414,11 +414,11 @@ namespace RTLIL return str.substr(1); } - static inline std::string unescape_id(RTLIL::IdString str) { + static inline std::string unescape_id(const RTLIL::IdString &str) { return unescape_id(str.str()); } - static inline const char *id2cstr(RTLIL::IdString str) { + static inline const char *id2cstr(const RTLIL::IdString &str) { return log_id(str); } @@ -435,11 +435,26 @@ namespace RTLIL }; struct sort_by_id_str { - bool operator()(RTLIL::IdString a, RTLIL::IdString b) const { + bool operator()(const RTLIL::IdString &a, const RTLIL::IdString &b) const { return strcmp(a.c_str(), b.c_str()) < 0; } }; + static inline std::string encode_filename(const std::string &filename) + { + std::stringstream val; + if (!std::any_of(filename.begin(), filename.end(), [](char c) { + return static_cast<unsigned char>(c) < 33 || static_cast<unsigned char>(c) > 126; + })) return filename; + for (unsigned char const c : filename) { + if (c < 33 || c > 126) + val << stringf("$%02x", c); + else + val << c; + } + return val.str(); + } + // see calc.cc for the implementation of this functions RTLIL::Const const_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); RTLIL::Const const_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); @@ -485,9 +500,14 @@ 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_mux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); + RTLIL::Const const_pmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + RTLIL::Const const_bweqx (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + RTLIL::Const const_bwmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); + // 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. @@ -635,7 +655,7 @@ struct RTLIL::Const int flags; std::vector<RTLIL::State> bits; - Const(); + Const() : flags(RTLIL::CONST_FLAG_NONE) {} Const(const std::string &str); Const(int val, int width = 32); Const(RTLIL::State bit, int width = 1); @@ -666,6 +686,7 @@ struct RTLIL::Const bool is_fully_ones() const; bool is_fully_def() const; bool is_fully_undef() const; + bool is_fully_undef_x_only() const; bool is_onehot(int *pos = nullptr) const; inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { @@ -696,21 +717,21 @@ struct RTLIL::AttrObject { dict<RTLIL::IdString, RTLIL::Const> attributes; - bool has_attribute(RTLIL::IdString id) const; + bool has_attribute(const RTLIL::IdString &id) const; - void set_bool_attribute(RTLIL::IdString id, bool value=true); - bool get_bool_attribute(RTLIL::IdString id) const; + void set_bool_attribute(const RTLIL::IdString &id, bool value=true); + bool get_bool_attribute(const RTLIL::IdString &id) const; bool get_blackbox_attribute(bool ignore_wb=false) const { return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox)); } - void set_string_attribute(RTLIL::IdString id, string value); - string get_string_attribute(RTLIL::IdString id) const; + void set_string_attribute(const RTLIL::IdString& id, string value); + string get_string_attribute(const RTLIL::IdString &id) const; - void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data); - void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data); - pool<string> get_strpool_attribute(RTLIL::IdString id) const; + void set_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data); + void add_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data); + pool<string> get_strpool_attribute(const RTLIL::IdString &id) const; void set_src_attribute(const std::string &src) { set_string_attribute(ID::src, src); @@ -722,8 +743,8 @@ 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; + void set_intvec_attribute(const RTLIL::IdString& id, const vector<int> &data); + vector<int> get_intvec_attribute(const RTLIL::IdString &id) const; }; struct RTLIL::SigChunk @@ -732,16 +753,15 @@ struct RTLIL::SigChunk std::vector<RTLIL::State> data; // only used if wire == NULL, LSB at index 0 int width, offset; - SigChunk(); - SigChunk(const RTLIL::Const &value); - SigChunk(RTLIL::Wire *wire); - SigChunk(RTLIL::Wire *wire, int offset, int width = 1); - SigChunk(const std::string &str); - SigChunk(int val, int width = 32); - SigChunk(RTLIL::State bit, int width = 1); + SigChunk() : wire(nullptr), width(0), offset(0) {} + SigChunk(const RTLIL::Const &value) : wire(nullptr), data(value.bits), width(GetSize(data)), offset(0) {} + SigChunk(RTLIL::Const &&value) : wire(nullptr), data(std::move(value.bits)), width(GetSize(data)), offset(0) {} + SigChunk(RTLIL::Wire *wire) : wire(wire), width(GetSize(wire)), offset(0) {} + SigChunk(RTLIL::Wire *wire, int offset, int width = 1) : wire(wire), width(width), offset(offset) {} + SigChunk(const std::string &str) : SigChunk(RTLIL::Const(str)) {} + SigChunk(int val, int width = 32) : SigChunk(RTLIL::Const(val, width)) {} + SigChunk(RTLIL::State bit, int width = 1) : SigChunk(RTLIL::Const(bit, width)) {} SigChunk(const RTLIL::SigBit &bit); - SigChunk(const RTLIL::SigChunk &sigchunk); - RTLIL::SigChunk &operator =(const RTLIL::SigChunk &other) = default; RTLIL::SigChunk extract(int offset, int length) const; inline int size() const { return width; } @@ -827,13 +847,13 @@ private: friend struct RTLIL::Module; public: - SigSpec(); - SigSpec(const RTLIL::SigSpec &other); + SigSpec() : width_(0), hash_(0) {} SigSpec(std::initializer_list<RTLIL::SigSpec> parts); - RTLIL::SigSpec &operator=(const RTLIL::SigSpec &other); SigSpec(const RTLIL::Const &value); + SigSpec(RTLIL::Const &&value); SigSpec(const RTLIL::SigChunk &chunk); + SigSpec(RTLIL::SigChunk &&chunk); SigSpec(RTLIL::Wire *wire); SigSpec(RTLIL::Wire *wire, int offset, int width = 1); SigSpec(const std::string &str); @@ -846,21 +866,6 @@ public: SigSpec(const std::set<RTLIL::SigBit> &bits); explicit SigSpec(bool bit); - SigSpec(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - } - - const RTLIL::SigSpec &operator=(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - return *this; - } - size_t get_hash() const { if (!hash_) hash(); return hash_; @@ -985,9 +990,9 @@ struct RTLIL::Selection Selection(bool full = true) : full_selection(full) { } - bool selected_module(RTLIL::IdString mod_name) const; - bool selected_whole_module(RTLIL::IdString mod_name) const; - bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const; + bool selected_module(const RTLIL::IdString &mod_name) const; + bool selected_whole_module(const RTLIL::IdString &mod_name) const; + bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; void optimize(RTLIL::Design *design); template<typename T1> void select(T1 *module) { @@ -1053,11 +1058,11 @@ struct RTLIL::Design ~Design(); RTLIL::ObjRange<RTLIL::Module*> modules(); - RTLIL::Module *module(RTLIL::IdString name); - const RTLIL::Module *module(RTLIL::IdString name) const; + RTLIL::Module *module(const RTLIL::IdString &name); + const RTLIL::Module *module(const RTLIL::IdString &name) const; RTLIL::Module *top_module(); - bool has(RTLIL::IdString id) const { + bool has(const RTLIL::IdString &id) const { return modules_.count(id) != 0; } @@ -1082,9 +1087,9 @@ struct RTLIL::Design void check(); void optimize(); - bool selected_module(RTLIL::IdString mod_name) const; - bool selected_whole_module(RTLIL::IdString mod_name) const; - bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const; + bool selected_module(const RTLIL::IdString &mod_name) const; + bool selected_whole_module(const RTLIL::IdString &mod_name) const; + bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; bool selected_module(RTLIL::Module *mod) const; bool selected_whole_module(RTLIL::Module *mod) const; @@ -1165,7 +1170,7 @@ public: virtual ~Module(); 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 size_t count_id(const RTLIL::IdString& id); virtual void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces); virtual bool reprocess_if_necessary(RTLIL::Design *design); @@ -1200,20 +1205,20 @@ public: return design->selected_member(name, member->name); } - RTLIL::Wire* wire(RTLIL::IdString id) { + RTLIL::Wire* wire(const RTLIL::IdString &id) { auto it = wires_.find(id); return it == wires_.end() ? nullptr : it->second; } - RTLIL::Cell* cell(RTLIL::IdString id) { + RTLIL::Cell* cell(const RTLIL::IdString &id) { auto it = cells_.find(id); return it == cells_.end() ? nullptr : it->second; } - const RTLIL::Wire* wire(RTLIL::IdString id) const{ + const RTLIL::Wire* wire(const RTLIL::IdString &id) const{ auto it = wires_.find(id); return it == wires_.end() ? nullptr : it->second; } - const RTLIL::Cell* cell(RTLIL::IdString id) const { + const RTLIL::Cell* cell(const RTLIL::IdString &id) const { auto it = cells_.find(id); return it == cells_.end() ? nullptr : it->second; } @@ -1302,6 +1307,9 @@ public: 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* addBweqx (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addBwmux (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* 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 = ""); RTLIL::Cell* addLut (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const lut, const std::string &src = ""); @@ -1376,6 +1384,8 @@ public: RTLIL::Cell* addDlatchsrGate (RTLIL::IdString name, 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 en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAnyinit(RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src = ""); + // The methods without the add* prefix create a cell and an output signal. They return the newly created output signal. RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); @@ -1429,6 +1439,9 @@ public: 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::SigSpec Bweqx (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const std::string &src = ""); + RTLIL::SigSpec Bwmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, 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 = ""); RTLIL::SigBit AndGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const RTLIL::SigBit &sig_b, const std::string &src = ""); @@ -1483,6 +1496,10 @@ public: #endif }; +inline int GetSize(RTLIL::Wire *wire) { + return wire->width; +} + struct RTLIL::Memory : public RTLIL::AttrObject { unsigned int hashidx_; @@ -1521,22 +1538,22 @@ public: dict<RTLIL::IdString, RTLIL::Const> parameters; // access cell ports - bool hasPort(RTLIL::IdString portname) const; - void unsetPort(RTLIL::IdString portname); - void setPort(RTLIL::IdString portname, RTLIL::SigSpec signal); - const RTLIL::SigSpec &getPort(RTLIL::IdString portname) const; + bool hasPort(const RTLIL::IdString &portname) const; + void unsetPort(const RTLIL::IdString &portname); + void setPort(const RTLIL::IdString &portname, RTLIL::SigSpec signal); + const RTLIL::SigSpec &getPort(const RTLIL::IdString &portname) const; const dict<RTLIL::IdString, RTLIL::SigSpec> &connections() const; // information about cell ports bool known() const; - bool input(RTLIL::IdString portname) const; - bool output(RTLIL::IdString portname) const; + bool input(const RTLIL::IdString &portname) const; + bool output(const RTLIL::IdString &portname) const; // access cell parameters - bool hasParam(RTLIL::IdString paramname) const; - void unsetParam(RTLIL::IdString paramname); - void setParam(RTLIL::IdString paramname, RTLIL::Const value); - const RTLIL::Const &getParam(RTLIL::IdString paramname) const; + bool hasParam(const RTLIL::IdString ¶mname) const; + void unsetParam(const RTLIL::IdString ¶mname); + void setParam(const RTLIL::IdString ¶mname, RTLIL::Const value); + const RTLIL::Const &getParam(const RTLIL::IdString ¶mname) const; void sort(); void check(); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 9c40ec66d..3a2fa4735 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -223,7 +223,33 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (cell->type.in(ID($_MUX_), ID($mux), ID($_NMUX_))) + if (cell->type == ID($bweqx)) + { + std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep); + std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep); + + std::vector<int> bweqx = ez->vec_not(ez->vec_xor(a, b)); + + if (model_undef) + { + std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep); + std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + + std::vector<int> both_undef = ez->vec_and(undef_a, undef_b); + std::vector<int> both_def = ez->vec_and(ez->vec_not(undef_a), ez->vec_not(undef_b)); + + bweqx = ez->vec_or(both_undef, ez->vec_and(both_def, bweqx)); + + for (int yx : undef_y) + ez->assume(ez->NOT(yx)); + } + ez->assume(ez->vec_eq(bweqx, y)); + return true; + } + + if (cell->type.in(ID($_MUX_), ID($mux), ID($_NMUX_), ID($bwmux))) { std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep); std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep); @@ -233,6 +259,8 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y; if (cell->type == ID($_NMUX_)) ez->assume(ez->vec_eq(ez->vec_not(ez->vec_ite(s.at(0), b, a)), yy)); + else if (cell->type == ID($bwmux)) + ez->assume(ez->vec_eq(ez->vec_ite(s, b, a), yy)); else ez->assume(ez->vec_eq(ez->vec_ite(s.at(0), b, a), yy)); @@ -245,7 +273,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a, b)); std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a, undef_b)); - std::vector<int> yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a)); + std::vector<int> yX; + if (cell->type == ID($bwmux)) + yX = ez->vec_ite(undef_s, undef_ab, ez->vec_ite(s, undef_b, undef_a)); + else + yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a)); ez->assume(ez->vec_eq(yX, undef_y)); undefGating(y, yy, undef_y); } @@ -375,29 +407,24 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector<int> undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep); std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); - int maybe_a = ez->CONST_TRUE; + int all_undef = ez->CONST_FALSE; + int found_active = ez->CONST_FALSE; - std::vector<int> bits_set = std::vector<int>(undef_y.size(), ez->CONST_FALSE); - std::vector<int> bits_clr = std::vector<int>(undef_y.size(), ez->CONST_FALSE); + std::vector<int> undef_tmp = undef_a; for (size_t i = 0; i < s.size(); i++) { - std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size()); std::vector<int> part_of_undef_b(undef_b.begin()+i*a.size(), undef_b.begin()+(i+1)*a.size()); - int maybe_s = ez->OR(s.at(i), undef_s.at(i)); - int sure_s = ez->AND(s.at(i), ez->NOT(undef_s.at(i))); - - maybe_a = ez->AND(maybe_a, ez->NOT(sure_s)); - - bits_set = ez->vec_ite(maybe_s, ez->vec_or(bits_set, ez->vec_or(part_of_b, part_of_undef_b)), bits_set); - bits_clr = ez->vec_ite(maybe_s, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(part_of_b), part_of_undef_b)), bits_clr); + undef_tmp = ez->vec_ite(s.at(i), part_of_undef_b, undef_tmp); + all_undef = ez->OR(all_undef, undef_s.at(i)); + all_undef = ez->OR(all_undef, ez->AND(s.at(i), found_active)); + found_active = ez->OR(found_active, s.at(i)); } - bits_set = ez->vec_ite(maybe_a, ez->vec_or(bits_set, ez->vec_or(bits_set, ez->vec_or(a, undef_a))), bits_set); - bits_clr = ez->vec_ite(maybe_a, ez->vec_or(bits_clr, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(a), undef_a))), bits_clr); + undef_tmp = ez->vec_or(undef_tmp, std::vector<int>(a.size(), all_undef)); - ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(bits_set, bits_clr)), undef_y)); + ez->assume(ez->vec_eq(undef_tmp, undef_y)); undefGating(y, yy, undef_y); } return true; @@ -1176,7 +1203,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (timestep > 0 && RTLIL::builtin_ff_cell_types().count(cell->type)) + if (timestep > 0 && (RTLIL::builtin_ff_cell_types().count(cell->type) || cell->type == ID($anyinit))) { FfData ff(nullptr, cell); @@ -1187,6 +1214,10 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) if (timestep == 1) { initial_state.add((*sigmap)(cell->getPort(ID::Q))); + if (model_undef && def_formal) { + std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Q), timestep); + ez->assume(ez->NOT(ez->vec_reduce_or(undef_q))); + } } else { @@ -1254,13 +1285,18 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) if (cell->type == ID($anyconst)) { - if (timestep < 2) + if (timestep < 2) { + if (model_undef && def_formal) { + std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + ez->assume(ez->NOT(ez->vec_reduce_or(undef_y))); + } return true; + } std::vector<int> d = importDefSigSpec(cell->getPort(ID::Y), timestep-1); std::vector<int> q = importDefSigSpec(cell->getPort(ID::Y), timestep); - std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q; + std::vector<int> qq = (model_undef && !def_formal) ? ez->vec_var(q.size()) : q; ez->assume(ez->vec_eq(d, qq)); if (model_undef) @@ -1268,14 +1304,24 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector<int> undef_d = importUndefSigSpec(cell->getPort(ID::Y), timestep-1); std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Y), timestep); - ez->assume(ez->vec_eq(undef_d, undef_q)); - undefGating(q, qq, undef_q); + if (def_formal) { + for (auto &undef_q_bit : undef_q) + ez->SET(ez->CONST_FALSE, undef_q_bit); + } else { + ez->assume(ez->vec_eq(undef_d, undef_q)); + undefGating(q, qq, undef_q); + } } return true; } if (cell->type == ID($anyseq)) { + if (model_undef && def_formal) { + std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Y), timestep); + for (auto &undef_q_bit : undef_q) + ez->SET(ez->CONST_FALSE, undef_q_bit); + } return true; } diff --git a/kernel/satgen.h b/kernel/satgen.h index da2cec222..8a89ff9db 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -73,6 +73,7 @@ struct SatGen std::map<std::pair<std::string, int>, bool> initstates; bool ignore_div_by_zero; bool model_undef; + bool def_formal = false; SatGen(ezSAT *ez, SigMap *sigmap, std::string prefix = std::string()) : ez(ez), sigmap(sigmap), prefix(prefix), ignore_div_by_zero(false), model_undef(false) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 64d2b4def..bd8dded4b 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -73,6 +73,8 @@ #include <limits.h> #include <errno.h> +#include "libs/json11/json11.hpp" + YOSYS_NAMESPACE_BEGIN int autoidx = 1; @@ -82,6 +84,7 @@ CellTypes yosys_celltypes; #ifdef YOSYS_ENABLE_TCL Tcl_Interp *yosys_tcl_interp = NULL; +bool yosys_tcl_repl_active = false; #endif std::set<std::string> yosys_input_files, yosys_output_files; @@ -466,8 +469,8 @@ std::string make_temp_dir(std::string template_str) # endif char *p = strdup(template_str.c_str()); - p = mkdtemp(p); - log_assert(p != NULL); + char *res = mkdtemp(p); + log_assert(res != NULL); template_str = p; free(p); @@ -534,11 +537,6 @@ std::string escape_filename_spaces(const std::string& filename) return out; } -int GetSize(RTLIL::Wire *wire) -{ - return wire->width; -} - bool already_setup = false; void yosys_setup() @@ -595,7 +593,9 @@ void yosys_shutdown() #ifdef YOSYS_ENABLE_TCL if (yosys_tcl_interp != NULL) { - Tcl_DeleteInterp(yosys_tcl_interp); + if (!Tcl_InterpDeleted(yosys_tcl_interp)) { + Tcl_DeleteInterp(yosys_tcl_interp); + } Tcl_Finalize(); yosys_tcl_interp = NULL; } @@ -712,6 +712,42 @@ void rewrite_filename(std::string &filename) } #ifdef YOSYS_ENABLE_TCL + +static Tcl_Obj *json_to_tcl(Tcl_Interp *interp, const json11::Json &json) +{ + if (json.is_null()) + return Tcl_NewStringObj("null", 4); + else if (json.is_string()) { + auto string = json.string_value(); + return Tcl_NewStringObj(string.data(), string.size()); + } else if (json.is_number()) { + double value = json.number_value(); + double round_val = std::nearbyint(value); + if (std::isfinite(round_val) && value == round_val && value >= LONG_MIN && value < -double(LONG_MIN)) + return Tcl_NewLongObj((long)round_val); + else + return Tcl_NewDoubleObj(value); + } else if (json.is_bool()) { + return Tcl_NewBooleanObj(json.bool_value()); + } else if (json.is_array()) { + auto list = json.array_items(); + Tcl_Obj *result = Tcl_NewListObj(list.size(), nullptr); + for (auto &item : list) + Tcl_ListObjAppendElement(interp, result, json_to_tcl(interp, item)); + return result; + } else if (json.is_object()) { + auto map = json.object_items(); + Tcl_Obj *result = Tcl_NewListObj(map.size() * 2, nullptr); + for (auto &item : map) { + Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(item.first.data(), item.first.size())); + Tcl_ListObjAppendElement(interp, result, json_to_tcl(interp, item.second)); + } + return result; + } else { + log_abort(); + } +} + static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { std::vector<std::string> args; @@ -736,22 +772,73 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a return TCL_OK; } - if (args.size() == 1) { - Pass::call(yosys_get_design(), args[0]); - return TCL_OK; + yosys_get_design()->scratchpad_unset("result.json"); + yosys_get_design()->scratchpad_unset("result.string"); + + bool in_repl = yosys_tcl_repl_active; + bool restore_log_cmd_error_throw = log_cmd_error_throw; + + log_cmd_error_throw = true; + + try { + if (args.size() == 1) { + Pass::call(yosys_get_design(), args[0]); + } else { + Pass::call(yosys_get_design(), args); + } + } catch (log_cmd_error_exception) { + if (in_repl) { + auto design = yosys_get_design(); + while (design->selection_stack.size() > 1) + design->selection_stack.pop_back(); + log_reset_stack(); + } + Tcl_SetResult(interp, (char *)"Yosys command produced an error", TCL_STATIC); + + yosys_tcl_repl_active = in_repl; + log_cmd_error_throw = restore_log_cmd_error_throw; + return TCL_ERROR; + } catch (...) { + log_error("uncaught exception during Yosys command invoked from TCL\n"); + } + + yosys_tcl_repl_active = in_repl; + log_cmd_error_throw = restore_log_cmd_error_throw; + + auto &scratchpad = yosys_get_design()->scratchpad; + auto result = scratchpad.find("result.json"); + if (result != scratchpad.end()) { + std::string err; + auto json = json11::Json::parse(result->second, err); + if (err.empty()) { + Tcl_SetObjResult(interp, json_to_tcl(interp, json)); + } else + log_warning("Ignoring result.json scratchpad value due to parse error: %s\n", err.c_str()); + } else if ((result = scratchpad.find("result.string")) != scratchpad.end()) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(result->second.data(), result->second.size())); } - Pass::call(yosys_get_design(), args); return TCL_OK; } +int yosys_tcl_iterp_init(Tcl_Interp *interp) +{ + if (Tcl_Init(interp)!=TCL_OK) + log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); + Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); + return TCL_OK ; +} + +void yosys_tcl_activate_repl() +{ + yosys_tcl_repl_active = true; +} + extern Tcl_Interp *yosys_get_tcl_interp() { if (yosys_tcl_interp == NULL) { yosys_tcl_interp = Tcl_CreateInterp(); - if (Tcl_Init(yosys_tcl_interp)!=TCL_OK) - log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); - Tcl_CreateCommand(yosys_tcl_interp, "yosys", tcl_yosys_cmd, NULL, NULL); + yosys_tcl_iterp_init(yosys_tcl_interp); } return yosys_tcl_interp; } @@ -774,6 +861,10 @@ struct TclPass : public Pass { log("If any arguments are specified, these arguments are provided to the script via\n"); log("the standard $argc and $argv variables.\n"); log("\n"); + log("Note, tcl will not recieve the output of any yosys command. If the output\n"); + log("of the tcl commands are needed, use the yosys command 'tee -s result.string'\n"); + log("to redirect yosys's output to the 'result.string' scratchpad value.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *) override { if (args.size() < 2) @@ -784,11 +875,13 @@ struct TclPass : public Pass { script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size())); Tcl_Interp *interp = yosys_get_tcl_interp(); + Tcl_Preserve(interp); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0); if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); + Tcl_Release(interp); } } TclPass; #endif @@ -867,6 +960,35 @@ std::string proc_self_dirname() { return "/"; } +#elif defined(__OpenBSD__) +char yosys_path[PATH_MAX]; +char *yosys_argv0; + +std::string proc_self_dirname(void) +{ + char buf[PATH_MAX + 1] = "", *path, *p; + // if case argv[0] contains a valid path, return it + if (strlen(yosys_path) > 0) { + p = strrchr(yosys_path, '/'); + snprintf(buf, sizeof buf, "%*s/", (int)(yosys_path - p), yosys_path); + return buf; + } + // if argv[0] does not, reconstruct the path out of $PATH + path = strdup(getenv("PATH")); + if (!path) + log_error("getenv(\"PATH\") failed: %s\n", strerror(errno)); + for (p = strtok(path, ":"); p; p = strtok(NULL, ":")) { + snprintf(buf, sizeof buf, "%s/%s", p, yosys_argv0); + if (access(buf, X_OK) == 0) { + *(strrchr(buf, '/') + 1) = '\0'; + free(path); + return buf; + } + } + free(path); + log_error("Can't determine yosys executable path\n."); + return NULL; +} #else #error "Don't know how to determine process executable base path!" #endif diff --git a/kernel/yosys.h b/kernel/yosys.h index 448f896d4..29415ff84 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -82,6 +82,9 @@ # ifdef YOSYS_MXE_HACKS extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); extern Tcl_Interp *Tcl_CreateInterp(void); +extern void Tcl_Preserve(ClientData data); +extern void Tcl_Release(ClientData clientData); +extern int Tcl_InterpDeleted(Tcl_Interp *interp); extern void Tcl_DeleteInterp(Tcl_Interp *interp); extern int Tcl_Eval(Tcl_Interp *interp, const char *script); extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); @@ -287,7 +290,7 @@ void remove_directory(std::string dirname); std::string escape_filename_spaces(const std::string& filename); template<typename T> int GetSize(const T &obj) { return obj.size(); } -int GetSize(RTLIL::Wire *wire); +inline int GetSize(RTLIL::Wire *wire); extern int autoidx; extern int yosys_xtrace; diff --git a/kernel/yw.cc b/kernel/yw.cc new file mode 100644 index 000000000..73e7710db --- /dev/null +++ b/kernel/yw.cc @@ -0,0 +1,209 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> + * + * 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/yw.h" +#include "libs/json11/json11.hpp" + +USING_YOSYS_NAMESPACE + +// Use the same formatting as witness.py uses +static const char *pretty_name(IdString id) +{ + const char *c_str = id.c_str(); + const char *p = c_str; + + if (*p != '\\') + return c_str; + p++; + + if (*p == '[') { + p++; + while (*p >= '0' && *p <= '9') + p++; + if (p[0] != ']' || p[1] != 0) + return c_str; + return c_str + 1; + } + + if (!(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && *p != '_') + return c_str; + p++; + while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') + p++; + + if (*p != 0) + return c_str; + return c_str + 1; +} + +std::string IdPath::str() const +{ + std::string result; + + for (auto &item : *this) { + const char *pretty = pretty_name(item); + if (pretty[0] == '[') { + result += pretty; + continue; + } + if (!result.empty()) + result += '.'; + result += pretty; + if (pretty[0] == '\\' || pretty[0] == '$') + result += ' '; + } + + return result; +} + +bool IdPath::get_address(int &addr) const +{ + if (empty()) + return false; + auto &last = back(); + if (!last.begins_with("\\[")) + return false; + if (last == "\\[0]") { + addr = 0; + return true; + } + char first = last.c_str()[2]; + if (first < '1' || first > '9') + return false; + char *endptr; + addr = std::strtol(last.c_str() + 2, &endptr, 10); + return endptr[0] == ']' && endptr[1] == 0; +} + +static std::vector<IdString> get_path(const json11::Json &json) +{ + std::vector<IdString> result; + for (auto &path_item : json.array_items()) { + auto const &path_item_str = path_item.string_value(); + if (path_item_str.empty()) + return {};; + result.push_back(path_item_str); + } + return result; +} + +ReadWitness::ReadWitness(const std::string &filename) : + filename(filename) +{ + std::ifstream f(filename.c_str()); + if (f.fail() || GetSize(filename) == 0) + log_error("Cannot open file `%s`\n", filename.c_str()); + std::stringstream buf; + buf << f.rdbuf(); + std::string err; + json11::Json json = json11::Json::parse(buf.str(), err); + if (!err.empty()) + log_error("Failed to parse `%s`: %s\n", filename.c_str(), err.c_str()); + + std::string format = json["format"].string_value(); + + if (format.empty()) + log_error("Failed to parse `%s`: Unknown format\n", filename.c_str()); + if (format != "Yosys Witness Trace") + log_error("Failed to parse `%s`: Unsupported format `%s`\n", filename.c_str(), format.c_str()); + + for (auto &clock_json : json["clocks"].array_items()) { + Clock clock; + clock.path = get_path(clock_json["path"]); + if (clock.path.empty()) + log_error("Failed to parse `%s`: Missing path for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + auto edge_str = clock_json["edge"]; + if (edge_str.string_value() == "posedge") + clock.is_posedge = true; + else if (edge_str.string_value() == "negedge") + clock.is_negedge = true; + else + log_error("Failed to parse `%s`: Unknown edge type for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + if (!clock_json["offset"].is_number()) + log_error("Failed to parse `%s`: Unknown offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + clock.offset = clock_json["offset"].int_value(); + if (clock.offset < 0) + log_error("Failed to parse `%s`: Invalid offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + clocks.push_back(clock); + } + + int bits_offset = 0; + for (auto &signal_json : json["signals"].array_items()) { + Signal signal; + signal.bits_offset = bits_offset; + signal.path = get_path(signal_json["path"]); + if (signal.path.empty()) + log_error("Failed to parse `%s`: Missing path for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + if (!signal_json["width"].is_number()) + log_error("Failed to parse `%s`: Unknown width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + signal.width = signal_json["width"].int_value(); + if (signal.width < 0) + log_error("Failed to parse `%s`: Invalid width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + bits_offset += signal.width; + if (!signal_json["offset"].is_number()) + log_error("Failed to parse `%s`: Unknown offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + signal.offset = signal_json["offset"].int_value(); + if (signal.offset < 0) + log_error("Failed to parse `%s`: Invalid offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + signal.init_only = signal_json["init_only"].bool_value(); + signals.push_back(signal); + } + + for (auto &step_json : json["steps"].array_items()) { + Step step; + if (!step_json["bits"].is_string()) + log_error("Failed to parse `%s`: Expected string as bits value for step %d\n", filename.c_str(), GetSize(steps)); + step.bits = step_json["bits"].string_value(); + for (char c : step.bits) { + if (c != '0' && c != '1' && c != 'x' && c != '?') + log_error("Failed to parse `%s`: Invalid bit '%c' value for step %d\n", filename.c_str(), c, GetSize(steps)); + } + steps.push_back(step); + } +} + +RTLIL::Const ReadWitness::get_bits(int t, int bits_offset, int width) const +{ + log_assert(t >= 0 && t < GetSize(steps)); + + const std::string &bits = steps[t].bits; + + RTLIL::Const result(State::Sa, width); + result.bits.reserve(width); + + int read_begin = GetSize(bits) - 1 - bits_offset; + int read_end = max(-1, read_begin - width); + + min(width, GetSize(bits) - bits_offset); + + for (int i = read_begin, j = 0; i > read_end; i--, j++) { + RTLIL::State bit = State::Sa; + switch (bits[i]) { + case '0': bit = State::S0; break; + case '1': bit = State::S1; break; + case 'x': bit = State::Sx; break; + case '?': bit = State::Sa; break; + default: + log_abort(); + } + result.bits[j] = bit; + } + + return result; +} diff --git a/kernel/yw.h b/kernel/yw.h new file mode 100644 index 000000000..c2f5921b1 --- /dev/null +++ b/kernel/yw.h @@ -0,0 +1,182 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> + * + * 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 YW_H +#define YW_H + +#include "kernel/yosys.h" +#include "kernel/mem.h" + +YOSYS_NAMESPACE_BEGIN + +struct IdPath : public std::vector<RTLIL::IdString> +{ + template<typename... T> + IdPath(T&&... args) : std::vector<RTLIL::IdString>(std::forward<T>(args)...) { } + IdPath prefix() const { return {begin(), end() - !empty()}; } + std::string str() const; + + bool has_address() const { int tmp; return get_address(tmp); }; + bool get_address(int &addr) const; + + int hash() const { return hashlib::hash_ops<std::vector<RTLIL::IdString>>::hash(*this); } +}; + +struct WitnessHierarchyItem { + RTLIL::Module *module; + RTLIL::Wire *wire = nullptr; + RTLIL::Cell *cell = nullptr; + Mem *mem = nullptr; + + WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Wire *wire) : module(module), wire(wire) {} + WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) {} + WitnessHierarchyItem(RTLIL::Module *module, Mem *mem) : module(module), mem(mem) {} +}; + +template<typename D, typename T> +void witness_hierarchy(RTLIL::Module *module, D data, T callback); + +template<class T> static std::vector<std::string> witness_path(T *obj) { + std::vector<std::string> path; + if (obj->name.isPublic()) { + auto hdlname = obj->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + path.push_back("\\" + token); + } + if (path.empty()) + path.push_back(obj->name.str()); + return path; +} + +struct ReadWitness +{ + struct Clock { + IdPath path; + int offset; + bool is_posedge = false; + bool is_negedge = false; + }; + + struct Signal { + IdPath path; + int offset; + int width; + bool init_only; + + int bits_offset; + }; + + struct Step { + std::string bits; + }; + + std::string filename; + std::vector<Clock> clocks; + std::vector<Signal> signals; + std::vector<Step> steps; + + ReadWitness(const std::string &filename); + + RTLIL::Const get_bits(int t, int bits_offset, int width) const; +}; + +template<typename D, typename T> +void witness_hierarchy_recursion(IdPath &path, int hdlname_mode, RTLIL::Module *module, D data, T &callback) +{ + auto const &const_path = path; + size_t path_size = path.size(); + for (auto wire : module->wires()) + { + auto hdlname = hdlname_mode < 0 ? std::vector<std::string>() : wire->get_hdlname_attribute(); + for (auto item : hdlname) + path.push_back("\\" + item); + if (hdlname.size() == 1 && path.back() == wire->name) + hdlname.clear(); + if (!hdlname.empty()) + callback(const_path, WitnessHierarchyItem(module, wire), data); + path.resize(path_size); + if (hdlname.empty() || hdlname_mode <= 0) { + path.push_back(wire->name); + callback(const_path, WitnessHierarchyItem(module, wire), data); + path.pop_back(); + } + } + + for (auto cell : module->cells()) + { + Module *child = module->design->module(cell->type); + if (child == nullptr) + continue; + + auto hdlname = hdlname_mode < 0 ? std::vector<std::string>() : cell->get_hdlname_attribute(); + for (auto item : hdlname) + path.push_back("\\" + item); + if (hdlname.size() == 1 && path.back() == cell->name) + hdlname.clear(); + if (!hdlname.empty()) { + D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data); + witness_hierarchy_recursion<D, T>(path, 1, child, child_data, callback); + } + path.resize(path_size); + if (hdlname.empty() || hdlname_mode <= 0) { + path.push_back(cell->name); + D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data); + witness_hierarchy_recursion<D, T>(path, hdlname.empty() ? hdlname_mode : -1, child, child_data, callback); + path.pop_back(); + } + } + + for (auto mem : Mem::get_all_memories(module)) { + std::vector<std::string> hdlname; + + if (hdlname_mode >= 0 && mem.cell != nullptr) + hdlname = mem.cell->get_hdlname_attribute(); + for (auto item : hdlname) + path.push_back("\\" + item); + if (hdlname.size() == 1 && path.back() == mem.cell->name) + hdlname.clear(); + if (!hdlname.empty()) { + callback(const_path, WitnessHierarchyItem(module, &mem), data); + } + path.resize(path_size); + + if (hdlname.empty() || hdlname_mode <= 0) { + path.push_back(mem.memid); + callback(const_path, WitnessHierarchyItem(module, &mem), data); + path.pop_back(); + + if (mem.cell != nullptr && mem.cell->name != mem.memid) { + path.push_back(mem.cell->name); + callback(const_path, WitnessHierarchyItem(module, &mem), data); + path.pop_back(); + } + } + } +} + +template<typename D, typename T> +void witness_hierarchy(RTLIL::Module *module, D data, T callback) +{ + IdPath path; + witness_hierarchy_recursion<D, T>(path, 0, module, data, callback); +} + +YOSYS_NAMESPACE_END + +#endif |