From 23cd23efc56147c5f2a8645dccd65ae7c189b89d Mon Sep 17 00:00:00 2001 From: Dag Lem Date: Tue, 12 Dec 2023 13:37:34 +0100 Subject: [PATCH 01/57] Simplify and correct AST for array slice assignment Corrects sign extension of the right hand side, and hopefully makes the code simpler to understand. Fixes #4064 --- frontends/ast/simplify.cc | 111 ++++++++++++++------------------------ 1 file changed, 41 insertions(+), 70 deletions(-) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 945f286a1..51ed95621 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -2966,96 +2966,67 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } else { // mask and shift operations - - AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true))); - wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); - wire_mask->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - wire_mask->is_logic = true; - while (wire_mask->simplify(true, 1, -1, false)) { } - current_ast_mod->children.push_back(wire_mask); - - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true))); - wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); - wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - wire_data->is_logic = true; - while (wire_data->simplify(true, 1, -1, false)) { } - current_ast_mod->children.push_back(wire_data); - - int shamt_width_hint = -1; - bool shamt_sign_hint = true; - shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint); - - AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true))); - wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); - wire_sel->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - wire_sel->is_logic = true; - wire_sel->is_signed = shamt_sign_hint; - while (wire_sel->simplify(true, 1, -1, false)) { } - current_ast_mod->children.push_back(wire_sel); - - did_something = true; - newNode = new AstNode(AST_BLOCK); + // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) AstNode *lvalue = children[0]->clone(); lvalue->delete_children(); if (member_node) lvalue->set_attribute(ID::wiretype, member_node->clone()); - AstNode *ref_mask = new AstNode(AST_IDENTIFIER); - ref_mask->str = wire_mask->str; - ref_mask->id2ast = wire_mask; - ref_mask->was_checked = true; - - AstNode *ref_data = new AstNode(AST_IDENTIFIER); - ref_data->str = wire_data->str; - ref_data->id2ast = wire_data; - ref_data->was_checked = true; - - AstNode *ref_sel = new AstNode(AST_IDENTIFIER); - ref_sel->str = wire_sel->str; - ref_sel->id2ast = wire_sel; - ref_sel->was_checked = true; - AstNode *old_data = lvalue->clone(); if (type == AST_ASSIGN_LE) old_data->lookahead = true; - AstNode *s = new AstNode(AST_ASSIGN_EQ, ref_sel->clone(), shift_expr); - newNode->children.push_back(s); + int shift_width_hint; + bool shift_sign_hint; + shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint); - AstNode *shamt = ref_sel; + // All operations are carried out in a new block. + newNode = new AstNode(AST_BLOCK); - // convert to signed while preserving the sign and value - shamt = new AstNode(AST_CAST_SIZE, mkconst_int(shamt_width_hint + 1, true), shamt); - shamt = new AstNode(AST_TO_SIGNED, shamt); + // Temporary register holding the result of the bit- or part-select position expression. + AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint); + newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, pos, shift_expr)); + + // Calculate lsb from position. + AstNode *shift_val = pos->clone(); + + // If the expression is signed, we must add an extra bit for possible negation of the most negative number. + // If the expression is unsigned, we must add an extra bit for sign. + shift_val = new AstNode(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), shift_val); + if (!shift_sign_hint) + shift_val = new AstNode(AST_TO_SIGNED, shift_val); // offset the shift amount by the lower bound of the dimension - int start_bit = wire_offset; - shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true)); + if (wire_offset != 0) + shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true)); // reflect the shift amount if the dimension is swapped if (children[0]->id2ast->range_swapped) - shamt = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shamt); + shift_val = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shift_val); // AST_SHIFT uses negative amounts for shifting left - shamt = new AstNode(AST_NEG, shamt); + shift_val = new AstNode(AST_NEG, shift_val); - AstNode *t; - - t = mkconst_bits(std::vector(result_width, State::S1), false); - t = new AstNode(AST_SHIFT, t, shamt->clone()); - t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t); - newNode->children.push_back(t); - - t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector(result_width, State::S1), false), children[1]->clone()); - t = new AstNode(AST_SHIFT, t, shamt); - t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t); - newNode->children.push_back(t); - - t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)); - t = new AstNode(AST_BIT_OR, t, ref_data); - t = new AstNode(type, lvalue, t); - newNode->children.push_back(t); + // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) + did_something = true; + AstNode *bitmask = mkconst_bits(std::vector(result_width, State::S1), false); + newNode->children.push_back( + new AstNode(type, + lvalue, + new AstNode(AST_BIT_OR, + new AstNode(AST_BIT_AND, + old_data, + new AstNode(AST_BIT_NOT, + new AstNode(AST_SHIFT, + bitmask, + shift_val->clone()))), + new AstNode(AST_SHIFT, + new AstNode(AST_TO_UNSIGNED, + new AstNode(AST_CAST_SIZE, + mkconst_int(result_width, true), + children[1]->clone())), + shift_val)))); newNode->fixup_hierarchy_flags(true); } From e0566eafdbdbe0b99b944edb73fe351149dc380a Mon Sep 17 00:00:00 2001 From: Dag Lem Date: Tue, 12 Dec 2023 14:16:45 +0100 Subject: [PATCH 02/57] Add test for rhs sign extension in array slice assignment --- tests/simple/sign_part_assign.v | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/simple/sign_part_assign.v diff --git a/tests/simple/sign_part_assign.v b/tests/simple/sign_part_assign.v new file mode 100644 index 000000000..5f65ac284 --- /dev/null +++ b/tests/simple/sign_part_assign.v @@ -0,0 +1,20 @@ +module test ( + offset_i, + data_o, + data_ref_o +); +input wire [ 2:0] offset_i; +output reg [15:0] data_o; +output reg [15:0] data_ref_o; + +always @(*) begin + // defaults + data_o = '0; + data_ref_o = '0; + + // partial assigns + data_ref_o[offset_i+:4] = 4'b1111; // unsigned + data_o[offset_i+:4] = 1'sb1; // sign extension to 4'b1111 +end + +endmodule From 57b4e16acdf715092e948dc791d017aeb8130e17 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 22 Sep 2023 17:52:25 +0200 Subject: [PATCH 03/57] sim: Include $display output in JSON summary This allows tools like SBY to capture the $display output independent from anything else sim might log. Additionally it provides source and hierarchy locations for everything printed. --- passes/sat/sim.cc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 8c4fadb69..b07534184 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -88,6 +88,17 @@ struct TriggeredAssertion { { } }; +struct DisplayOutput { + int step; + SimInstance *instance; + Cell *cell; + std::string output; + + DisplayOutput(int step, SimInstance *instance, Cell *cell, std::string output) : + step(step), instance(instance), cell(cell), output(output) + { } +}; + struct SimShared { bool debug = false; @@ -110,6 +121,7 @@ struct SimShared int next_output_id = 0; int step = 0; std::vector triggered_assertions; + std::vector display_output; bool serious_asserts = false; bool initstate = true; }; @@ -870,6 +882,7 @@ struct SimInstance std::string rendered = print.fmt.render(); log("%s", rendered.c_str()); + shared->display_output.emplace_back(shared->step, this, cell, rendered); } update_print: @@ -2055,6 +2068,20 @@ struct SimWorker : SimShared json.end_object(); } json.end_array(); + json.name("display_output"); + json.begin_array(); + for (auto &output : display_output) { + json.begin_object(); + json.entry("step", output.step); + json.entry("path", output.instance->witness_full_path(output.cell)); + auto src = output.cell->get_string_attribute(ID::src); + if (!src.empty()) { + json.entry("src", src); + } + json.entry("output", output.output); + json.end_object(); + } + json.end_array(); json.end_object(); } From 510d137996c3beb7588aa86687315908928a9778 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 22 Sep 2023 17:56:34 +0200 Subject: [PATCH 04/57] fmt: Allow non-constant $display calls in initial blocks These are useful for formal verification with SBY where they can be used to display solver chosen `rand const reg` signals and signals derived from those. The previous error message for non-constant initial $display statements is downgraded to a log message. Constant initial $display statements will be shown both during elaboration and become part of the RTLIL so that the `sim` output is complete. --- frontends/ast/ast.h | 2 +- frontends/ast/simplify.cc | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 97903d0a0..31679e4df 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -287,7 +287,7 @@ namespace AST bool is_simple_const_expr(); // helper for parsing format strings - Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0); + Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0, bool may_fail = false); bool is_recursive_function() const; std::pair get_tern_choice(); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 51ed95621..d45b53db5 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -145,7 +145,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) // Process a format string and arguments for $display, $write, $sprintf, etc -Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at) { +Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at, bool may_fail) { std::vector args; for (size_t index = first_arg_at; index < children.size(); index++) { AstNode *node_arg = children[index]; @@ -169,6 +169,9 @@ Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_ arg.type = VerilogFmtArg::INTEGER; arg.sig = node_arg->bitsAsConst(); arg.signed_ = node_arg->is_signed; + } else if (may_fail) { + log_file_info(filename, location.first_line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); + return Fmt(); } else { log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); } @@ -1065,10 +1068,13 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin default_base = 16; // when $display()/$write() functions are used in an initial block, print them during synthesis - Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base); + Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); if (str.substr(0, 8) == "$display") fmt.append_string("\n"); log("%s", fmt.render().c_str()); + for (auto node : children) + while (node->simplify(true, stage, -1, false)) {} + return false; } else { // when $display()/$write() functions are used in an always block, simplify the expressions and // convert them to a special cell later in genrtlil From 3ed9030eb4263710f0a808f57b0b8c896d7792ab Mon Sep 17 00:00:00 2001 From: Dag Lem Date: Thu, 11 Jan 2024 10:32:44 +0100 Subject: [PATCH 05/57] Optionally suppress output from display system tasks in read_verilog --- frontends/ast/ast.cc | 5 ++-- frontends/ast/ast.h | 4 +-- frontends/ast/simplify.cc | 42 +++++++++++++-------------- frontends/verilog/verilog_frontend.cc | 11 ++++++- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 4b2b7a822..fe075b270 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -45,7 +45,7 @@ namespace AST { // instantiate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map current_scope; @@ -1320,11 +1320,12 @@ static void rename_in_package_stmts(AstNode *pkg) } // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, +void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; current_ast_mod = nullptr; + flag_nodisplay = nodisplay; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; flag_no_dump_ptr = no_dump_ptr; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 31679e4df..c44746131 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -376,7 +376,7 @@ namespace AST }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code - void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, + void process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library @@ -432,7 +432,7 @@ namespace AST namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + extern bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map current_scope; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index d45b53db5..5370c4187 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1058,33 +1058,31 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin { if (!current_always) { log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str()); - } else if (current_always->type == AST_INITIAL) { - int default_base = 10; - if (str.back() == 'b') - default_base = 2; - else if (str.back() == 'o') - default_base = 8; - else if (str.back() == 'h') - default_base = 16; - - // when $display()/$write() functions are used in an initial block, print them during synthesis - Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); - if (str.substr(0, 8) == "$display") - fmt.append_string("\n"); - log("%s", fmt.render().c_str()); - for (auto node : children) - while (node->simplify(true, stage, -1, false)) {} - return false; + delete_children(); + str = std::string(); } else { - // when $display()/$write() functions are used in an always block, simplify the expressions and - // convert them to a special cell later in genrtlil + // simplify the expressions and convert them to a special cell later in genrtlil for (auto node : children) while (node->simplify(true, stage, -1, false)) {} + + if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) { + int default_base = 10; + if (str.back() == 'b') + default_base = 2; + else if (str.back() == 'o') + default_base = 8; + else if (str.back() == 'h') + default_base = 16; + + // when $display()/$write() functions are used in an initial block, print them during synthesis + Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); + if (str.substr(0, 8) == "$display") + fmt.append_string("\n"); + log("%s", fmt.render().c_str()); + } + return false; } - - delete_children(); - str = std::string(); } // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 9b277c6b9..5c59fe3af 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -100,6 +100,10 @@ struct VerilogFrontend : public Frontend { log(" -assert-assumes\n"); log(" treat all assume() statements like assert() statements\n"); log("\n"); + log(" -nodisplay\n"); + log(" suppress output from display system tasks ($display et. al).\n"); + log(" This does not affect the output from a later 'sim' command.\n"); + log("\n"); log(" -debug\n"); log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n"); log("\n"); @@ -235,6 +239,7 @@ struct VerilogFrontend : public Frontend { } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { + bool flag_nodisplay = false; bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; bool flag_no_dump_ptr = false; @@ -308,6 +313,10 @@ struct VerilogFrontend : public Frontend { assert_assumes_mode = true; continue; } + if (arg == "-nodisplay") { + flag_nodisplay = true; + continue; + } if (arg == "-debug") { flag_dump_ast1 = true; flag_dump_ast2 = true; @@ -510,7 +519,7 @@ struct VerilogFrontend : public Frontend { if (flag_nodpi) error_on_dpi_function(current_ast); - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, + AST::process(design, current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); From 0486f61a35331e3cf557424940da6f20253e2d92 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 11 Jan 2024 10:53:30 +0000 Subject: [PATCH 06/57] write_verilog: emit zero width parameters as `.PARAM()`. --- backends/verilog/verilog_backend.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 9ff2c5c86..71b9c5fd8 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1830,7 +1830,8 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (it != cell->parameters.begin()) f << stringf(","); f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); - dump_const(f, it->second); + if (it->second.size() > 0) + dump_const(f, it->second); f << stringf(")"); } f << stringf("\n%s" ")", indent.c_str()); From 1159e487217096cde0aa7a19ca4db095b19cf01f Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 11 Jan 2024 11:47:55 +0000 Subject: [PATCH 07/57] write_verilog: emit `initial $display` correctly. --- backends/verilog/verilog_backend.cc | 24 ++++++++++++++---------- docs/source/CHAPTER_CellLib.rst | 4 +++- frontends/ast/genrtlil.cc | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 71b9c5fd8..1fa31e31e 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1896,17 +1896,21 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector &cells) { - f << stringf("%s" "always @(", indent.c_str()); - for (int i = 0; i < trg.size(); i++) { - if (i != 0) - f << " or "; - if (polarity[i]) - f << "posedge "; - else - f << "negedge "; - dump_sigspec(f, trg[i]); + if (trg.size() == 0) { + f << stringf("%s" "initial begin\n", indent.c_str()); + } else { + f << stringf("%s" "always @(", indent.c_str()); + for (int i = 0; i < trg.size(); i++) { + if (i != 0) + f << " or "; + if (polarity[i]) + f << "posedge "; + else + f << "negedge "; + dump_sigspec(f, trg[i]); + } + f << ") begin\n"; } - f << ") begin\n"; std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int(); diff --git a/docs/source/CHAPTER_CellLib.rst b/docs/source/CHAPTER_CellLib.rst index 494c0651c..0f0d79123 100644 --- a/docs/source/CHAPTER_CellLib.rst +++ b/docs/source/CHAPTER_CellLib.rst @@ -120,7 +120,7 @@ All binary RTL cells have two input ports ``\A`` and ``\B`` and one output port :verilog:`Y = A >>> B` $sshr :verilog:`Y = A - B` $sub :verilog:`Y = A && B` $logic_and :verilog:`Y = A * B` $mul :verilog:`Y = A || B` $logic_or :verilog:`Y = A / B` $div - :verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod + :verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod :verilog:`Y = A !== B` $nex ``N/A`` $divfloor :verilog:`Y = A ** B` $pow ``N/A`` $modfoor ======================= ============= ======================= ========= @@ -661,6 +661,8 @@ Ports: ``\TRG`` The signals that control when this ``$print`` cell is triggered. + If the width of this port is zero and ``\TRG_ENABLE`` is true, the cell is + triggered during initial evaluation (time zero) only. ``\EN`` Enable signal for the whole cell. diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 0bae0f673..0a502162e 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -718,7 +718,7 @@ struct AST_INTERNAL::ProcessGenerator } } cell->parameters[ID::TRG_WIDTH] = triggers.size(); - cell->parameters[ID::TRG_ENABLE] = !triggers.empty(); + cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty(); cell->parameters[ID::TRG_POLARITY] = polarity; cell->parameters[ID::PRIORITY] = --last_print_priority; cell->setPort(ID::TRG, triggers); From d49322531398208df42f175bd02cc737af314aa3 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 11 Jan 2024 14:10:20 +0000 Subject: [PATCH 08/57] write_cxxrtl: reset state value of comb `$print` cells. --- backends/cxxrtl/cxxrtl_backend.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 2c35a1943..ac2f756d6 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2002,6 +2002,8 @@ struct CxxrtlWorker { } } for (auto cell : module->cells()) { + if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) + f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n"; if (is_internal_cell(cell->type)) continue; f << indent << mangle(cell); From 7142e20dab1479987033f402607f6267b5484f20 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 11 Jan 2024 14:24:56 +0000 Subject: [PATCH 09/57] write_cxxrtl: support initial `$print` cells. --- backends/cxxrtl/cxxrtl_backend.cc | 49 +++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ac2f756d6..ff11d7fe0 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1291,20 +1291,29 @@ struct CxxrtlWorker { log_assert(!for_debug); // Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph. - log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool()); + log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)); - f << indent << "auto " << mangle(cell) << "_curr = "; - dump_sigspec_rhs(cell->getPort(ID::EN)); - f << ".concat("; - dump_sigspec_rhs(cell->getPort(ID::ARGS)); - f << ").val();\n"; + if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell + f << indent << "auto " << mangle(cell) << "_curr = "; + dump_sigspec_rhs(cell->getPort(ID::EN)); + f << ".concat("; + dump_sigspec_rhs(cell->getPort(ID::ARGS)); + f << ").val();\n"; - f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n"; - inc_indent(); - dump_print(cell); - f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n"; - dec_indent(); - f << indent << "}\n"; + f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n"; + inc_indent(); + dump_print(cell); + f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n"; + dec_indent(); + f << indent << "}\n"; + } else { // initial $print cell + f << indent << "if (!" << mangle(cell) << ") {\n"; + inc_indent(); + dump_print(cell); + f << indent << mangle(cell) << " = value<1>{1u};\n"; + dec_indent(); + f << indent << "}\n"; + } // Flip-flops } else if (is_ff_cell(cell->type)) { log_assert(!for_debug); @@ -2002,8 +2011,11 @@ struct CxxrtlWorker { } } for (auto cell : module->cells()) { + // Certain $print cells have additional state, which must be reset as well. if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n"; + if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0) + f << indent << mangle(cell) << " = value<1>();\n"; if (is_internal_cell(cell->type)) continue; f << indent << mangle(cell); @@ -2432,11 +2444,11 @@ struct CxxrtlWorker { f << "\n"; bool has_cells = false; for (auto cell : module->cells()) { - if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) { - // comb $print cell -- store the last EN/ARGS values to know when they change. - dump_attrs(cell); + // Certain $print cells have additional state, which requires storage. + if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; - } + if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0) + f << indent << "value<1> " << mangle(cell) << ";\n"; if (is_internal_cell(cell->type)) continue; dump_attrs(cell); @@ -2966,8 +2978,9 @@ struct CxxrtlWorker { for (auto node : node_order) if (live_nodes[node]) { if (node->type == FlowGraph::Node::Type::CELL_EVAL && - node->cell->type == ID($print) && - node->cell->getParam(ID::TRG_ENABLE).as_bool()) + node->cell->type == ID($print) && + node->cell->getParam(ID::TRG_ENABLE).as_bool() && + node->cell->getParam(ID::TRG_WIDTH).as_int() != 0) sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell); else schedule[module].push_back(*node); From 1eb823bd0e1daa35bd00b09177fd5be260905157 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 00:16:23 +0000 Subject: [PATCH 10/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8bde98a1c..0a9392696 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.36+85 +YOSYS_VER := 0.36+97 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From acf916f654574045574ab4f7d6b1cb0d8cec6f85 Mon Sep 17 00:00:00 2001 From: Dag Lem Date: Sun, 14 Jan 2024 09:08:47 +0100 Subject: [PATCH 11/57] Restore sim output from initial $display --- passes/sat/sim.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index b07534184..542eb5c18 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -185,6 +185,7 @@ struct SimInstance struct print_state_t { + bool initial_done; Const past_trg; Const past_en; Const past_args; @@ -350,6 +351,7 @@ struct SimInstance print.past_trg = Const(State::Sx, cell->getPort(ID::TRG).size()); print.past_args = Const(State::Sx, cell->getPort(ID::ARGS).size()); print.past_en = State::Sx; + print.initial_done = false; } } @@ -852,13 +854,14 @@ struct SimInstance bool triggered = false; Const trg = get_state(cell->getPort(ID::TRG)); + bool trg_en = cell->getParam(ID::TRG_ENABLE).as_bool(); Const en = get_state(cell->getPort(ID::EN)); Const args = get_state(cell->getPort(ID::ARGS)); if (!en.as_bool()) goto update_print; - if (cell->getParam(ID::TRG_ENABLE).as_bool()) { + if (trg.size() > 0 && trg_en) { Const trg_pol = cell->getParam(ID::TRG_POLARITY); for (int i = 0; i < trg.size(); i++) { bool pol = trg_pol[i] == State::S1; @@ -868,7 +871,12 @@ struct SimInstance if (!pol && curr == State::S0 && past == State::S1) triggered = true; } + } else if (trg_en) { + // initial $print (TRG width = 0, TRG_ENABLE = true) + if (!print.initial_done && en != print.past_en) + triggered = true; } else { + // always @(*) $print if (args != print.past_args || en != print.past_en) triggered = true; } @@ -889,6 +897,7 @@ struct SimInstance print.past_trg = trg; print.past_en = en; print.past_args = args; + print.initial_done = true; } if (check_assertions) From fac843f4801c2d7faf467ee054145baa261f6639 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 00:17:14 +0000 Subject: [PATCH 12/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0a9392696..274a93b98 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.36+97 +YOSYS_VER := 0.36+100 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 5902b2826d4d96b374d1ef7d612e25b5cbb1bba4 Mon Sep 17 00:00:00 2001 From: uis Date: Sat, 11 Nov 2023 17:29:43 +0300 Subject: [PATCH 13/57] Fix printf formats --- frontends/ast/simplify.cc | 4 ++-- passes/memory/memory_libmap.cc | 6 +++--- passes/sat/recover_names.cc | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 2a500b56b..aa5093ec7 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -204,8 +204,8 @@ void AstNode::annotateTypedEnums(AstNode *template_node) log_assert(enum_item->children[1]->type == AST_RANGE); is_signed = enum_item->children[1]->is_signed; } else { - log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n", - enum_item->children.size(), + log_error("enum_item children size==%zu, expected 1 or 2 for %s (%s)\n", + (size_t) enum_item->children.size(), enum_item->str.c_str(), enum_node->str.c_str() ); } diff --git a/passes/memory/memory_libmap.cc b/passes/memory/memory_libmap.cc index ec181a142..2e683b8eb 100644 --- a/passes/memory/memory_libmap.cc +++ b/passes/memory/memory_libmap.cc @@ -690,7 +690,7 @@ bool apply_clock(MemConfig &cfg, const PortVariant &def, SigBit clk, bool clk_po // Perform write port assignment, validating clock options as we go. void MemMapping::assign_wr_ports() { - log_reject(stringf("Assigning write ports... (candidate configs: %lu)", cfgs.size())); + log_reject(stringf("Assigning write ports... (candidate configs: %zu)", (size_t) cfgs.size())); for (auto &port: mem.wr_ports) { if (!port.clk_enable) { // Async write ports not supported. @@ -739,7 +739,7 @@ void MemMapping::assign_wr_ports() { // Perform read port assignment, validating clock and rden options as we go. void MemMapping::assign_rd_ports() { - log_reject(stringf("Assigning read ports... (candidate configs: %lu)", cfgs.size())); + log_reject(stringf("Assigning read ports... (candidate configs: %zu)", (size_t) cfgs.size())); for (int pidx = 0; pidx < GetSize(mem.rd_ports); pidx++) { auto &port = mem.rd_ports[pidx]; MemConfigs new_cfgs; @@ -900,7 +900,7 @@ void MemMapping::assign_rd_ports() { // Validate transparency restrictions, determine where to add soft transparency logic. void MemMapping::handle_trans() { - log_reject(stringf("Handling transparency... (candidate configs: %lu)", cfgs.size())); + log_reject(stringf("Handling transparency... (candidate configs: %zu)", (size_t) cfgs.size())); if (mem.emulate_read_first_ok()) { MemConfigs new_cfgs; for (auto &cfg: cfgs) { diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc index 4c30a3632..4870e2cac 100644 --- a/passes/sat/recover_names.cc +++ b/passes/sat/recover_names.cc @@ -26,6 +26,7 @@ #include #include +#include USING_YOSYS_NAMESPACE @@ -623,7 +624,7 @@ struct RecoverNamesWorker { if (pop == 1 || pop == (8*sizeof(equiv_cls_t) - 1)) continue; - log_debug("equivalence class: %016lx\n", cls.first); + log_debug("equivalence class: %016" PRIx64 "\n", cls.first); const pool &gold_bits = cls2bits.at(cls.first).first; const pool &gate_bits = cls2bits.at(cls.first).second; if (gold_bits.empty() || gate_bits.empty()) From 568418b50b7b4d7c715f62fd2c2d5d7bd8e3a2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 15 Jan 2024 12:35:02 +0100 Subject: [PATCH 14/57] opt_lut: Replace `-dlogic` with `-tech ice40` --- passes/opt/opt_lut.cc | 56 +++++++++++++++++------------------ techlibs/ice40/synth_ice40.cc | 2 +- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 3b079d964..3907285f3 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -24,6 +24,10 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +// Type represents the following constraint: Preserve connections to dedicated +// logic cell that has ports connected to LUT inputs. This includes +// the case where both LUT and dedicated logic input are connected to the same +// constant. struct dlogic_t { IdString cell_type; // LUT input idx -> hard cell's port name @@ -515,16 +519,6 @@ struct OptLutWorker } }; -static void split(std::vector &tokens, const std::string &text, char sep) -{ - size_t start = 0, end = 0; - while ((end = text.find(sep, start)) != std::string::npos) { - tokens.push_back(text.substr(start, end - start)); - start = end + 1; - } - tokens.push_back(text.substr(start)); -} - struct OptLutPass : public Pass { OptLutPass() : Pass("opt_lut", "optimize LUT cells") { } void help() override @@ -541,6 +535,10 @@ struct OptLutPass : public Pass { log(" the case where both LUT and dedicated logic input are connected to\n"); log(" the same constant.\n"); log("\n"); + log(" -tech ice40\n"); + log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n"); + log(" and preserve connections to SB_CARRY as appropriate\n"); + log("\n"); log(" -limit N\n"); log(" only perform the first N combines, then stop. useful for debugging.\n"); log("\n"); @@ -555,28 +553,28 @@ struct OptLutPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-dlogic" && argidx+1 < args.size()) + if (args[argidx] == "-tech" && argidx+1 < args.size()) { - std::vector tokens; - split(tokens, args[++argidx], ':'); - if (tokens.size() < 2) - log_cmd_error("The -dlogic option requires at least one connection.\n"); - dlogic_t entry; - entry.cell_type = "\\" + tokens[0]; - for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { - std::vector conn_tokens; - split(conn_tokens, *it, '='); - if (conn_tokens.size() != 2) - log_cmd_error("Invalid format of -dlogic signal mapping.\n"); - IdString logic_port = "\\" + conn_tokens[0]; - int lut_input = atoi(conn_tokens[1].c_str()); - entry.lut_input_port[lut_input] = logic_port; - } - dlogic.push_back(entry); + std::string tech = args[++argidx]; + if (tech != "ice40") + log_cmd_error("Unsupported -tech argument: %s\n", tech.c_str()); + + dlogic = {{ + ID(SB_CARRY), + dict{ + std::make_pair(1, ID(I0)), + std::make_pair(2, ID(I1)), + std::make_pair(3, ID(CI)) + } + }, { + ID(SB_CARRY), + dict{ + std::make_pair(3, ID(CO)) + } + }}; continue; } - if (args[argidx] == "-limit" && argidx + 1 < args.size()) - { + if (args[argidx] == "-limit" && argidx + 1 < args.size()) { limit = atoi(args[++argidx].c_str()); continue; } diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index a9982649b..4c691c7a5 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -432,7 +432,7 @@ struct SynthIce40Pass : public ScriptPass run("ice40_wrapcarry -unwrap"); run("techmap -map +/ice40/ff_map.v"); run("clean"); - run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3 -dlogic SB_CARRY:CO=3"); + run("opt_lut -tech ice40"); } if (check_label("map_cells")) From fb4eeb134411cc620163855b84e5c539287337a2 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Mon, 15 Jan 2024 17:47:59 +0100 Subject: [PATCH 15/57] show: allow setting colors via selection on PROC boxes --- passes/cmds/show.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index d57cbc6bb..8c2695dbe 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -539,7 +539,7 @@ struct ShowWorker std::string proc_src = RTLIL::unescape_id(proc->name); if (proc->attributes.count(ID::src) > 0) proc_src = proc->attributes.at(ID::src).decode_string(); - fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\"];\n", pidx, findLabel(proc->name.str()), proc_src.c_str()); + fprintf(f, "p%d [shape=box, style=rounded, label=\"PROC %s\\n%s\", %s];\n", pidx, findLabel(proc->name.str()), proc_src.c_str(), findColor(proc->name).c_str()); } for (auto &conn : module->connections()) From 740265bfbd593295f4886b6c6893ecb849eeecb4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 00:16:26 +0000 Subject: [PATCH 16/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 274a93b98..f6c28adeb 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.36+100 +YOSYS_VER := 0.36+103 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From a5c7f69ed8f1e263f356e384ab7983ea6c7d2fe1 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 16 Jan 2024 08:13:21 +0100 Subject: [PATCH 17/57] Release version 0.37 --- CHANGELOG | 15 +++++++++++++-- Makefile | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4fb60904a..0570554cd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,12 +2,23 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.36 .. Yosys 0.37-dev +Yosys 0.36 .. Yosys 0.37 -------------------------- + * New commands and options + - Added option "-nodisplay" to read_verilog. + + * SystemVerilog + - Correct hierarchical path names for structs and unions. + + * Various + - Print hierarchy for failed assertions in "sim" pass. + - Add "--present-only" option to "yosys-witness" to omit unused signals. + - Implement a generic record/replay interface for CXXRTL. + - Improved readability of emitted code with "write_verilog". Yosys 0.35 .. Yosys 0.36 -------------------------- -* New commands and options + * New commands and options - Added option "--" to pass arguments down to tcl when using -c option. - Added ability on MacOS and Windows to pass options after arguments on cli. - Added option "-cmp2softlogic" to synth_lattice. diff --git a/Makefile b/Makefile index f6c28adeb..e0e850b11 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.36+103 +YOSYS_VER := 0.37 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -157,7 +157,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8f07a0d.. | wc -l`/;" Makefile +# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8f07a0d.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # From bd956d76ba0332dc433e34161e7fb2c9b89761fb Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 16 Jan 2024 08:16:07 +0100 Subject: [PATCH 18/57] Next dev cycle --- CHANGELOG | 3 +++ Makefile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0570554cd..990f9e17d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,9 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.37 .. Yosys 0.38-dev +-------------------------- + Yosys 0.36 .. Yosys 0.37 -------------------------- * New commands and options diff --git a/Makefile b/Makefile index e0e850b11..5428a5daf 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37 +YOSYS_VER := 0.37+0 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -157,7 +157,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: -# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8f07a0d.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a5c7f69.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # From ed81cc5f8106134e08c9aeaca74d86839945fd57 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 16 Jan 2024 09:22:39 +0000 Subject: [PATCH 19/57] =?UTF-8?q?cxxrtl:=20rename=20observer::{on=5Fcommit?= =?UTF-8?q?=E2=86=92on=5Fupdate}.=20(breaking=20change)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name `on_commit` was terrible since it would not be called, as one may conclude from the name, on each `commit()`, but only whenever that method actually updates a value. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 14 +++++++------- backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 3f8247226..e2729bfe4 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -865,19 +865,19 @@ struct observer { // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and // `base` points to the first chunk. - virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) = 0; + virtual void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) = 0; // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to // the memory element chunk count and `base` points to the first chunk of the first element of the memory. - virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) = 0; + virtual void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) = 0; }; // The `null_observer` class has the same interface as `observer`, but has no invocation overhead, since its methods // are final and have no implementation. This allows the observer feature to be zero-cost when not in use. struct null_observer final: observer { - void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override {} - void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {} + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) override {} + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {} }; template @@ -916,12 +916,12 @@ struct wire { // This method intentionally takes a mandatory argument (to make it more difficult to misuse in // black box implementations, leading to missed observer events). It is generic over its argument - // to make sure the `on_commit` call is devirtualized. This is somewhat awkward but lets us keep + // to make sure the `on_update` call is devirtualized. This is somewhat awkward but lets us keep // a single implementation for both this method and the one in `memory`. template bool commit(ObserverT &observer) { if (curr != next) { - observer.on_commit(curr.chunks, curr.data, next.data); + observer.on_update(curr.chunks, curr.data, next.data); curr = next; return true; } @@ -1003,7 +1003,7 @@ struct memory { value elem = data[entry.index]; elem = elem.update(entry.val, entry.mask); if (data[entry.index] != elem) { - observer.on_commit(value::chunks, data[0].data, elem.data, entry.index); + observer.on_update(value::chunks, data[0].data, elem.data, entry.index); changed |= true; } data[entry.index] = elem; diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h index 94f59bb0d..e44b1c4e1 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h @@ -561,12 +561,12 @@ public: spool::writer *writer; CXXRTL_ALWAYS_INLINE - void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override { + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) override { writer->write_change(ident_lookup->at(base), chunks, value); } CXXRTL_ALWAYS_INLINE - void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override { + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override { writer->write_change(ident_lookup->at(base), chunks, value, index); } } record_observer; From bf1a99da09b2db6eb8a44e324de48f8e4092e9a1 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 16 Jan 2024 09:53:43 +0000 Subject: [PATCH 20/57] cxxrtl: make observer methods non-virtual. (breaking change) This avoids having to devirtualize them later to get performance back, and simplifies the code a bit. The change is prompted by the desire to add a similar observer object to `eval()`, and a repeated consideration of whether the dispatch on these should be virtual in first place. --- backends/cxxrtl/cxxrtl_backend.cc | 4 ++-- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 14 +++----------- backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h | 10 ++++------ 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ff11d7fe0..30995ea11 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2391,7 +2391,7 @@ struct CxxrtlWorker { f << indent << "}\n"; f << "\n"; f << indent << "bool commit() override {\n"; - f << indent << indent << "null_observer observer;\n"; + f << indent << indent << "observer observer;\n"; f << indent << indent << "return commit<>(observer);\n"; f << indent << "}\n"; if (debug_info) { @@ -2486,7 +2486,7 @@ struct CxxrtlWorker { f << indent << "}\n"; f << "\n"; f << indent << "bool commit() override {\n"; - f << indent << indent << "null_observer observer;\n"; + f << indent << indent << "observer observer;\n"; f << indent << indent << "return commit<>(observer);\n"; f << indent << "}\n"; if (debug_info) { diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index e2729bfe4..bd336f481 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -865,19 +865,12 @@ struct observer { // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and // `base` points to the first chunk. - virtual void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) = 0; + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {} // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to // the memory element chunk count and `base` points to the first chunk of the first element of the memory. - virtual void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) = 0; -}; - -// The `null_observer` class has the same interface as `observer`, but has no invocation overhead, since its methods -// are final and have no implementation. This allows the observer feature to be zero-cost when not in use. -struct null_observer final: observer { - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) override {} - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {} + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} }; template @@ -916,8 +909,7 @@ struct wire { // This method intentionally takes a mandatory argument (to make it more difficult to misuse in // black box implementations, leading to missed observer events). It is generic over its argument - // to make sure the `on_update` call is devirtualized. This is somewhat awkward but lets us keep - // a single implementation for both this method and the one in `memory`. + // to allow the `on_update` method to be non-virtual. template bool commit(ObserverT &observer) { if (curr != next) { diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h index e44b1c4e1..d824fdb83 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h @@ -556,22 +556,20 @@ public: bool record_incremental(ModuleT &module) { assert(streaming); - struct : public observer { + struct { std::unordered_map *ident_lookup; spool::writer *writer; CXXRTL_ALWAYS_INLINE - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) override { + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) { writer->write_change(ident_lookup->at(base), chunks, value); } CXXRTL_ALWAYS_INLINE - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override { + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) { writer->write_change(ident_lookup->at(base), chunks, value, index); } - } record_observer; - record_observer.ident_lookup = &ident_lookup; - record_observer.writer = &writer; + } record_observer = { &ident_lookup, &writer }; writer.write_sample(/*incremental=*/true, pointer++, timestamp); for (auto input_index : inputs) { From a33acb7cd9a2366df55036db039b01b72efcc60b Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 16 Jan 2024 09:08:07 +0000 Subject: [PATCH 21/57] cxxrtl: refactor the formatter and use a closure. This commit achieves three roughly equally important goals: 1. To bring the rendering code in kernel/fmt.cc and in cxxrtl.h as close together as possible, with an ideal of only having the bigint library as the difference between the render functions. 2. To make the treatment of `$time` and `$realtime` in CXXRTL closer to the Verilog semantics, at least in the formatting code. 3. To change the code generator so that all of the `$print`-to-`string` conversion code is contained inside of a closure. There are two reasons to aim for goal (3): a. Because output redirection through definition of a global ostream object is neither convenient nor useful for environments where the output is consumed by other code rather than being printed on a terminal. b. Because it may be desirable to, in some cases, ignore the `$print` cells that are present in the netlist based on a runtime decision. This is doubly true for an upcoming `$check` cell implementing assertions, since failing a `$check` would by default cause a crash. --- backends/cxxrtl/cxxrtl_backend.cc | 9 +- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 220 ++++++++++++++---------- kernel/fmt.cc | 124 ++++++------- kernel/fmt.h | 5 +- tests/fmt/always_full_tb.cc | 7 +- 5 files changed, 192 insertions(+), 173 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 30995ea11..e4a000627 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1072,9 +1072,12 @@ struct CxxrtlWorker { dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1>{1u}) {\n"; inc_indent(); - f << indent << print_output; - fmt.emit_cxxrtl(f, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); - f << ";\n"; + f << indent << "auto formatter = [&](int64_t itime, double ftime) {\n"; + inc_indent(); + fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); + dec_indent(); + f << indent << "};\n"; + f << indent << print_output << " << formatter(steps, steps);\n"; dec_indent(); f << indent << "}\n"; } diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index bd336f481..654f13b4a 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -565,7 +565,7 @@ struct value : public expr_base> { } value neg() const { - return value { 0u }.sub(*this); + return value().sub(*this); } bool ucmp(const value &other) const { @@ -763,102 +763,134 @@ std::ostream &operator<<(std::ostream &os, const value &val) { return os; } -template -struct value_formatted { - const value &val; - bool character; - bool justify_left; - char padding; - int width; - int base; - bool signed_; - bool plus; +// Must be kept in sync with `struct FmtPart` in kernel/fmt.h! +// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. +struct fmt_part { + enum { + STRING = 0, + INTEGER = 1, + CHARACTER = 2, + TIME = 3, + } type; - value_formatted(const value &val, bool character, bool justify_left, char padding, int width, int base, bool signed_, bool plus) : - val(val), character(character), justify_left(justify_left), padding(padding), width(width), base(base), signed_(signed_), plus(plus) {} - value_formatted(const value_formatted &) = delete; - value_formatted &operator=(const value_formatted &rhs) = delete; + // STRING type + std::string str; + + // INTEGER/CHARACTER types + // + value val; + + // INTEGER/CHARACTER/TIME types + enum { + RIGHT = 0, + LEFT = 1, + } justify; // = RIGHT; + char padding; // = '\0'; + size_t width; // = 0; + + // INTEGER type + unsigned base; // = 10; + bool signed_; // = false; + bool plus; // = false; + + // TIME type + bool realtime; // = false; + // + int64_t itime; + // + double ftime; + + // Format the part as a string. + // + // The values of `itime` and `ftime` are used for `$time` and `$realtime`, correspondingly. + template + std::string render(value val, int64_t itime, double ftime) + { + // We might want to replace some of these bit() calls with direct + // chunk access if it turns out to be slow enough to matter. + std::string buf; + switch (type) { + case STRING: + return str; + + case CHARACTER: { + buf.reserve(Bits/8); + for (int i = 0; i < Bits; i += 8) { + char ch = 0; + for (int j = 0; j < 8 && i + j < int(Bits); j++) + if (val.bit(i + j)) + ch |= 1 << j; + if (ch != 0) + buf.append({ch}); + } + std::reverse(buf.begin(), buf.end()); + break; + } + + case INTEGER: { + size_t width = Bits; + if (base != 10) { + width = 0; + for (size_t index = 0; index < Bits; index++) + if (val.bit(index)) + width = index + 1; + } + + if (base == 2) { + for (size_t i = width; i > 0; i--) + buf += (val.bit(i - 1) ? '1' : '0'); + } else if (base == 8 || base == 16) { + size_t step = (base == 16) ? 4 : 3; + for (size_t index = 0; index < width; index += step) { + uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); + if (step == 4) + value |= val.bit(index + 3) << 3; + buf += "0123456789abcdef"[value]; + } + std::reverse(buf.begin(), buf.end()); + } else if (base == 10) { + bool negative = signed_ && val.is_neg(); + if (negative) + val = val.neg(); + if (val.is_zero()) + buf += '0'; + value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); + while (!xval.is_zero()) { + value<(Bits > 4 ? Bits : 4)> quotient, remainder; + if (Bits >= 4) + std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); + else + std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); + buf += '0' + remainder.template trunc<4>().template get(); + xval = quotient; + } + if (negative || plus) + buf += negative ? '-' : '+'; + std::reverse(buf.begin(), buf.end()); + } else assert(false && "Unsupported base for fmt_part"); + break; + } + + case TIME: { + buf = realtime ? std::to_string(ftime) : std::to_string(itime); + break; + } + } + + std::string str; + assert(width == 0 || padding != '\0'); + if (justify == RIGHT && buf.size() < width) { + size_t pad_width = width - buf.size(); + if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { + str += buf.front(); + buf.erase(0, 1); + } + str += std::string(pad_width, padding); + } + str += buf; + if (justify == LEFT && buf.size() < width) + str += std::string(width - buf.size(), padding); + return str; + } }; -template -std::ostream &operator<<(std::ostream &os, const value_formatted &vf) -{ - value val = vf.val; - - std::string buf; - - // We might want to replace some of these bit() calls with direct - // chunk access if it turns out to be slow enough to matter. - - if (!vf.character) { - size_t width = Bits; - if (vf.base != 10) { - width = 0; - for (size_t index = 0; index < Bits; index++) - if (val.bit(index)) - width = index + 1; - } - - if (vf.base == 2) { - for (size_t i = width; i > 0; i--) - buf += (val.bit(i - 1) ? '1' : '0'); - } else if (vf.base == 8 || vf.base == 16) { - size_t step = (vf.base == 16) ? 4 : 3; - for (size_t index = 0; index < width; index += step) { - uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); - if (step == 4) - value |= val.bit(index + 3) << 3; - buf += "0123456789abcdef"[value]; - } - std::reverse(buf.begin(), buf.end()); - } else if (vf.base == 10) { - bool negative = vf.signed_ && val.is_neg(); - if (negative) - val = val.neg(); - if (val.is_zero()) - buf += '0'; - while (!val.is_zero()) { - value quotient, remainder; - if (Bits >= 4) - std::tie(quotient, remainder) = val.udivmod(value{10u}); - else - std::tie(quotient, remainder) = std::make_pair(value{0u}, val); - buf += '0' + remainder.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get(); - val = quotient; - } - if (negative || vf.plus) - buf += negative ? '-' : '+'; - std::reverse(buf.begin(), buf.end()); - } else assert(false); - } else { - buf.reserve(Bits/8); - for (int i = 0; i < Bits; i += 8) { - char ch = 0; - for (int j = 0; j < 8 && i + j < int(Bits); j++) - if (val.bit(i + j)) - ch |= 1 << j; - if (ch != 0) - buf.append({ch}); - } - std::reverse(buf.begin(), buf.end()); - } - - assert(vf.width == 0 || vf.padding != '\0'); - if (!vf.justify_left && buf.size() < vf.width) { - size_t pad_width = vf.width - buf.size(); - if (vf.padding == '0' && (buf.front() == '+' || buf.front() == '-')) { - os << buf.front(); - buf.erase(0, 1); - } - os << std::string(pad_width, vf.padding); - } - os << buf; - if (vf.justify_left && buf.size() < vf.width) - os << std::string(vf.width - buf.size(), vf.padding); - - return os; -} - // An object that can be passed to a `commit()` method in order to produce a replay log of every state change in // the simulation. struct observer { diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 383fa7de1..7d16b20c1 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -569,82 +569,60 @@ std::vector Fmt::emit_verilog() const return args; } -void Fmt::emit_cxxrtl(std::ostream &f, std::function emit_sig) const +std::string escape_cxx_string(const std::string &input) { - for (auto &part : parts) { - switch (part.type) { - case FmtPart::STRING: - f << " << \""; - for (char c : part.str) { - switch (c) { - case '\\': - YS_FALLTHROUGH - case '"': - f << '\\' << c; - break; - case '\a': - f << "\\a"; - break; - case '\b': - f << "\\b"; - break; - case '\f': - f << "\\f"; - break; - case '\n': - f << "\\n"; - break; - case '\r': - f << "\\r"; - break; - case '\t': - f << "\\t"; - break; - case '\v': - f << "\\v"; - break; - default: - f << c; - break; - } - } - f << '"'; - break; - - case FmtPart::INTEGER: - case FmtPart::CHARACTER: { - f << " << value_formatted<" << part.sig.size() << ">("; - emit_sig(part.sig); - f << ", " << (part.type == FmtPart::CHARACTER); - f << ", " << (part.justify == FmtPart::LEFT); - f << ", (char)" << (int)part.padding; - f << ", " << part.width; - f << ", " << part.base; - f << ", " << part.signed_; - f << ", " << part.plus; - f << ')'; - break; - } - - case FmtPart::TIME: { - // CXXRTL only records steps taken, so there's no difference between - // the values taken by $time and $realtime. - f << " << value_formatted<64>("; - f << "value<64>{steps}"; - f << ", " << (part.type == FmtPart::CHARACTER); - f << ", " << (part.justify == FmtPart::LEFT); - f << ", (char)" << (int)part.padding; - f << ", " << part.width; - f << ", " << part.base; - f << ", " << part.signed_; - f << ", " << part.plus; - f << ')'; - break; - } - - default: log_abort(); + std::string output = "\""; + for (auto c : input) { + if (::isprint(c)) { + if (c == '\\') + output.push_back('\\'); + output.push_back(c); + } else { + char l = c & 0xf, h = (c >> 4) & 0xf; + output.append("\\x"); + output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); + output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); } } + output.push_back('"'); + if (output.find('\0') != std::string::npos) { + output.insert(0, "std::string {"); + output.append(stringf(", %zu}", input.size())); + } + return output; +} + +void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig) const +{ + os << indent << "std::string buf;\n"; + for (auto &part : parts) { + os << indent << "buf += fmt_part { "; + os << "fmt_part::"; + switch (part.type) { + case FmtPart::STRING: os << "STRING"; break; + case FmtPart::INTEGER: os << "INTEGER"; break; + case FmtPart::CHARACTER: os << "CHARACTER"; break; + case FmtPart::TIME: os << "TIME"; break; + } + os << ", "; + os << escape_cxx_string(part.str) << ", "; + os << "fmt_part::"; + switch (part.justify) { + case FmtPart::LEFT: os << "LEFT"; break; + case FmtPart::RIGHT: os << "RIGHT"; break; + } + os << ", "; + os << "(char)" << (int)part.padding << ", "; + os << part.width << ", "; + os << part.base << ", "; + os << part.signed_ << ", "; + os << part.plus << ", "; + os << part.realtime; + os << " }.render("; + emit_sig(part.sig); + os << ", itime, ftime);\n"; + } + os << indent << "return buf;\n"; } std::string Fmt::render() const diff --git a/kernel/fmt.h b/kernel/fmt.h index 39a278c56..126d020c0 100644 --- a/kernel/fmt.h +++ b/kernel/fmt.h @@ -50,6 +50,7 @@ struct VerilogFmtArg { // RTLIL format part, such as the substitutions in: // "foo {4:> 4du} bar {2:<01hs}" +// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h! struct FmtPart { enum { STRING = 0, @@ -71,7 +72,7 @@ struct FmtPart { } justify = RIGHT; char padding = '\0'; size_t width = 0; - + // INTEGER type unsigned base = 10; bool signed_ = false; @@ -93,7 +94,7 @@ public: void parse_verilog(const std::vector &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name); std::vector emit_verilog() const; - void emit_cxxrtl(std::ostream &f, std::function emit_sig) const; + void emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig) const; std::string render() const; diff --git a/tests/fmt/always_full_tb.cc b/tests/fmt/always_full_tb.cc index 229f78aeb..a29a8ae05 100644 --- a/tests/fmt/always_full_tb.cc +++ b/tests/fmt/always_full_tb.cc @@ -2,8 +2,13 @@ int main() { + struct : public performer { + int64_t time() const override { return 1; } + void on_print(const std::string &output, const cxxrtl::metadata_map &) override { std::cerr << output; } + } performer; + cxxrtl_design::p_always__full uut; uut.p_clk.set(!uut.p_clk); - uut.step(); + uut.step(&performer); return 0; } From 02e3d508fad9c07692022555c8cefd76575d8c98 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 16 Jan 2024 10:45:22 +0000 Subject: [PATCH 22/57] cxxrtl: remove `module::steps`. (breaking change) This approach to tracking simulation time was a mistake that I did not catch in review. It has several issues: 1. There is absolutely no requirement to call `step()`, as it is a convenience function. In particular, `steps` will not be incremented in submodules if `-noflatten` is used. 2. The semantics of `steps` does not match that of the Verilog `$time` construct. 3. There is no way to make the semantics of `%t` match that of Verilog. 4. The `module` interface is intentionally very barebones. It is little more than a container for three method pointers, `reset`, `eval`, and `commit`. Adding ancillary data to it goes against this. If similar functionality is introduced again it should probably be a variable that is global per toplevel design using some object that is unique for an entire hierarchy of modules, and ideally exposed via the C API. For now, it is being removed (in this commit) and (in next commit) the capability is being reintroduced through a context object that can be specified for `eval()`. --- backends/cxxrtl/cxxrtl_backend.cc | 2 +- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index e4a000627..3b0c2827d 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1077,7 +1077,7 @@ struct CxxrtlWorker { fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); dec_indent(); f << indent << "};\n"; - f << indent << print_output << " << formatter(steps, steps);\n"; + f << indent << print_output << " << formatter(0, 0.0);\n"; dec_indent(); f << indent << "}\n"; } diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 654f13b4a..7d6a75d4a 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1331,10 +1331,7 @@ struct module { virtual bool eval() = 0; virtual bool commit() = 0; - unsigned int steps = 0; - size_t step() { - ++steps; size_t deltas = 0; bool converged = false; do { From 905f07c03fedefd2be6333ab5425b2623955630f Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 16 Jan 2024 11:12:32 +0000 Subject: [PATCH 23/57] cxxrtl: introduce `performer`, a context object for `eval()`. (breaking change) At the moment the only thing it allows is redirecting `$print` cell output in a context-dependent manner. In the future, it will allow customizing handling of `$check` cells (where the default is to abort), of out-of-range memory accesses, and other runtime conditions with effects. This context object also allows a suitably written testbench to add Verilog-compliant `$time`/`$realtime` handling, albeit it involves the ceremony of defining a `performer` subclass. Most people will want something like this to customize `$time`: int64_t time = 0; struct : public performer { int64_t *time_ptr; int64_t time() const override { return *time_ptr; } } performer = { &time }; p_top.step(&performer); --- backends/cxxrtl/cxxrtl_backend.cc | 34 ++++++++++++++----------- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 24 +++++++++++++---- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 3b0c2827d..c6a4265fc 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1077,7 +1077,15 @@ struct CxxrtlWorker { fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); dec_indent(); f << indent << "};\n"; - f << indent << print_output << " << formatter(0, 0.0);\n"; + f << indent << "if (performer) {\n"; + inc_indent(); + f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()));\n"; + dec_indent(); + f << indent << "} else {\n"; + inc_indent(); + f << indent << print_output << " << formatter(0, 0.0);\n"; + dec_indent(); + f << indent << "}\n"; dec_indent(); f << indent << "}\n"; } @@ -1497,11 +1505,11 @@ struct CxxrtlWorker { }; if (buffered_inputs) { // If we have any buffered inputs, there's no chance of converging immediately. - f << indent << mangle(cell) << access << "eval();\n"; + f << indent << mangle(cell) << access << "eval(performer);\n"; f << indent << "converged = false;\n"; assign_from_outputs(/*cell_converged=*/false); } else { - f << indent << "if (" << mangle(cell) << access << "eval()) {\n"; + f << indent << "if (" << mangle(cell) << access << "eval(performer)) {\n"; inc_indent(); assign_from_outputs(/*cell_converged=*/true); dec_indent(); @@ -2384,7 +2392,8 @@ struct CxxrtlWorker { dump_reset_method(module); f << indent << "}\n"; f << "\n"; - f << indent << "bool eval() override {\n"; + // No default argument, to prevent unintentional `return bb_foo::eval();` calls that drop performer. + f << indent << "bool eval(performer *performer) override {\n"; dump_eval_method(module); f << indent << "}\n"; f << "\n"; @@ -2481,7 +2490,7 @@ struct CxxrtlWorker { f << "\n"; f << indent << "void reset() override;\n"; f << "\n"; - f << indent << "bool eval() override;\n"; + f << indent << "bool eval(performer *performer = nullptr) override;\n"; f << "\n"; f << indent << "template\n"; f << indent << "bool commit(ObserverT &observer) {\n"; @@ -2520,7 +2529,7 @@ struct CxxrtlWorker { dump_reset_method(module); f << indent << "}\n"; f << "\n"; - f << indent << "bool " << mangle(module) << "::eval() {\n"; + f << indent << "bool " << mangle(module) << "::eval(performer *performer) {\n"; dump_eval_method(module); f << indent << "}\n"; if (debug_info) { @@ -2544,7 +2553,6 @@ struct CxxrtlWorker { RTLIL::Module *top_module = nullptr; std::vector modules; TopoSort topo_design; - bool has_prints = false; for (auto module : design->modules()) { if (!design->selected_module(module)) continue; @@ -2557,8 +2565,6 @@ struct CxxrtlWorker { topo_design.node(module); for (auto cell : module->cells()) { - if (cell->type == ID($print)) - has_prints = true; if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell)) continue; RTLIL::Module *cell_module = design->module(cell->type); @@ -2617,8 +2623,6 @@ struct CxxrtlWorker { f << "#include \"" << basename(intf_filename) << "\"\n"; else f << "#include \n"; - if (has_prints) - f << "#include \n"; f << "\n"; f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n"; f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; @@ -3296,7 +3300,7 @@ struct CxxrtlBackend : public Backend { log(" value<8> p_i_data;\n"); log(" wire<8> p_o_data;\n"); log("\n"); - log(" bool eval() override;\n"); + log(" bool eval(performer *performer) override;\n"); log(" template\n"); log(" bool commit(ObserverT &observer);\n"); log(" bool commit() override;\n"); @@ -3311,11 +3315,11 @@ struct CxxrtlBackend : public Backend { log(" namespace cxxrtl_design {\n"); log("\n"); log(" struct stderr_debug : public bb_p_debug {\n"); - log(" bool eval() override {\n"); + log(" bool eval(performer *performer) override {\n"); log(" if (posedge_p_clk() && p_en)\n"); log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n"); log(" p_o_data.next = p_i_data;\n"); - log(" return bb_p_debug::eval();\n"); + log(" return bb_p_debug::eval(performer);\n"); log(" }\n"); log(" };\n"); log("\n"); @@ -3416,7 +3420,7 @@ struct CxxrtlBackend : public Backend { log(" -print-output \n"); log(" $print cells in the generated code direct their output to .\n"); log(" must be one of \"std::cout\", \"std::cerr\". if not specified,\n"); - log(" \"std::cout\" is used.\n"); + log(" \"std::cout\" is used. explicitly provided performer overrides this.\n"); log("\n"); log(" -nohierarchy\n"); log(" use design hierarchy as-is. in most designs, a top module should be\n"); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 7d6a75d4a..d15772a3c 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -39,6 +39,7 @@ #include #include #include +#include // `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements. #include @@ -891,8 +892,21 @@ struct fmt_part { } }; +// An object that can be passed to a `eval()` method in order to act on side effects. +struct performer { + // Called to evaluate a Verilog `$time` expression. + virtual int64_t time() const { return 0; } + + // Called to evaluate a Verilog `$realtime` expression. + virtual double realtime() const { return time(); } + + // Called when a `$print` cell is triggered. + virtual void on_print(const std::string &output) { std::cout << output; } +}; + // An object that can be passed to a `commit()` method in order to produce a replay log of every state change in -// the simulation. +// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and +// a comparatively heavyweight template-based solution is justified. struct observer { // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and @@ -1328,14 +1342,14 @@ struct module { virtual void reset() = 0; - virtual bool eval() = 0; - virtual bool commit() = 0; + virtual bool eval(performer *performer = nullptr) = 0; + virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls - size_t step() { + size_t step(performer *performer = nullptr) { size_t deltas = 0; bool converged = false; do { - converged = eval(); + converged = eval(performer); deltas++; } while (commit() && !converged); return deltas; From 5a1fcdea137585ecaffb602dcd1fa324984cce0a Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 16 Jan 2024 16:17:10 +0000 Subject: [PATCH 24/57] cxxrtl: include attributes in `performer::on_print` callback. This is useful primarily to determine the source location, but can also be used for any other purpose. --- backends/cxxrtl/cxxrtl_backend.cc | 5 ++- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 54 ++++++++++++------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index c6a4265fc..ef718067f 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1079,7 +1079,10 @@ struct CxxrtlWorker { f << indent << "};\n"; f << indent << "if (performer) {\n"; inc_indent(); - f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()));\n"; + f << indent << "static const metadata_map attributes = "; + dump_metadata_map(cell->attributes); + f << ";\n"; + f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()), attributes);\n"; dec_indent(); f << indent << "} else {\n"; inc_indent(); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index d15772a3c..d3b9cf9db 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -892,33 +892,6 @@ struct fmt_part { } }; -// An object that can be passed to a `eval()` method in order to act on side effects. -struct performer { - // Called to evaluate a Verilog `$time` expression. - virtual int64_t time() const { return 0; } - - // Called to evaluate a Verilog `$realtime` expression. - virtual double realtime() const { return time(); } - - // Called when a `$print` cell is triggered. - virtual void on_print(const std::string &output) { std::cout << output; } -}; - -// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in -// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and -// a comparatively heavyweight template-based solution is justified. -struct observer { - // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks - // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and - // `base` points to the first chunk. - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {} - - // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` - // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to - // the memory element chunk count and `base` points to the first chunk of the first element of the memory. - void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} -}; - template struct wire { static constexpr size_t bits = Bits; @@ -1100,6 +1073,33 @@ struct metadata { typedef std::map metadata_map; +// An object that can be passed to a `eval()` method in order to act on side effects. +struct performer { + // Called to evaluate a Verilog `$time` expression. + virtual int64_t time() const { return 0; } + + // Called to evaluate a Verilog `$realtime` expression. + virtual double realtime() const { return time(); } + + // Called when a `$print` cell is triggered. + virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; } +}; + +// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in +// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and +// a comparatively heavyweight template-based solution is justified. +struct observer { + // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks + // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and + // `base` points to the first chunk. + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {} + + // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` + // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to + // the memory element chunk count and `base` points to the first chunk of the first element of the memory. + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} +}; + // Tag class to disambiguate values/wires and their aliases. struct debug_alias {}; From 37a6c9a097ddb7e3fff6463b6e4f2d26f671f710 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:16:14 +0000 Subject: [PATCH 25/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5428a5daf..a3447e005 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+0 +YOSYS_VER := 0.37+1 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 1764c0ee3c6db3cdc07f78dbdc3d24d3a9cf61a5 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Thu, 18 Jan 2024 08:47:04 +0100 Subject: [PATCH 26/57] Fix verific clocking when no driver exist --- frontends/verific/verific.cc | 2 +- tests/verific/clocking.ys | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/verific/clocking.ys diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 9737fde89..adabd2700 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -2110,7 +2110,7 @@ VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_a if (sva_at_only) do { Instance *inst_mux = net->Driver(); - if (inst_mux->Type() != PRIM_MUX) + if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX) break; bool pwr1 = inst_mux->GetInput1()->IsPwr(); diff --git a/tests/verific/clocking.ys b/tests/verific/clocking.ys new file mode 100644 index 000000000..bfdbeb748 --- /dev/null +++ b/tests/verific/clocking.ys @@ -0,0 +1,10 @@ +read -sv < Date: Fri, 19 Jan 2024 00:16:38 +0000 Subject: [PATCH 27/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a3447e005..f2af053be 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+1 +YOSYS_VER := 0.37+15 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From ae991abf2e4a4074a32091b2b6f1eea81dd68514 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 19 Jan 2024 15:26:37 +1000 Subject: [PATCH 28/57] gowin: fix the BRAM mapping. The primitives used have been corrected and changes have been made to the set of signals. The empirically established need to set the OCEx signal to 1 when using READ_MODE=0 is reflected. Signed-off-by: YRabbit --- techlibs/gowin/brams_map.v | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/techlibs/gowin/brams_map.v b/techlibs/gowin/brams_map.v index 7ffc91bac..5e1299c28 100644 --- a/techlibs/gowin/brams_map.v +++ b/techlibs/gowin/brams_map.v @@ -131,7 +131,7 @@ if (PORT_A_WIDTH < 9) begin .CE(PORT_A_CLK_EN), .WRE(WRE), .RESET(RST), - .OCE(1'b0), + .OCE(1'b1), .AD(AD), .DI(DI), .DO(DO), @@ -157,7 +157,7 @@ end else begin .CE(PORT_A_CLK_EN), .WRE(WRE), .RESET(RST), - .OCE(1'b0), + .OCE(1'b1), .AD(AD), .DI(DI), .DO(DO), @@ -224,7 +224,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin assign PORT_A_RD_DATA = `x8_rd_data(DOA); assign PORT_B_RD_DATA = `x8_rd_data(DOB); - DP #( + DPB #( `INIT(init_slice_x8) .READ_MODE0(1'b0), .READ_MODE1(1'b0), @@ -232,16 +232,18 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin .WRITE_MODE1(PORT_B_OPTION_WRITE_MODE), .BIT_WIDTH_0(`x8_width(PORT_A_WIDTH)), .BIT_WIDTH_1(`x8_width(PORT_B_WIDTH)), - .BLK_SEL(3'b000), + .BLK_SEL_0(3'b000), + .BLK_SEL_1(3'b000), .RESET_MODE(OPTION_RESET_MODE), ) _TECHMAP_REPLACE_ ( - .BLKSEL(3'b000), + .BLKSELA(3'b000), + .BLKSELB(3'b000), .CLKA(PORT_A_CLK), .CEA(PORT_A_CLK_EN), .WREA(WREA), .RESETA(RSTA), - .OCEA(1'b0), + .OCEA(1'b1), .ADA(ADA), .DIA(DIA), .DOA(DOA), @@ -250,7 +252,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin .CEB(PORT_B_CLK_EN), .WREB(WREB), .RESETB(RSTB), - .OCEB(1'b0), + .OCEB(1'b1), .ADB(ADB), .DIB(DIB), .DOB(DOB), @@ -266,7 +268,7 @@ end else begin assign PORT_A_RD_DATA = DOA; assign PORT_B_RD_DATA = DOB; - DPX9 #( + DPX9B #( `INIT(init_slice_x9) .READ_MODE0(1'b0), .READ_MODE1(1'b0), @@ -274,16 +276,18 @@ end else begin .WRITE_MODE1(PORT_B_OPTION_WRITE_MODE), .BIT_WIDTH_0(PORT_A_WIDTH), .BIT_WIDTH_1(PORT_B_WIDTH), - .BLK_SEL(3'b000), + .BLK_SEL_0(3'b000), + .BLK_SEL_1(3'b000), .RESET_MODE(OPTION_RESET_MODE), ) _TECHMAP_REPLACE_ ( - .BLKSEL(3'b000), + .BLKSELA(3'b000), + .BLKSELB(3'b000), .CLKA(PORT_A_CLK), .CEA(PORT_A_CLK_EN), .WREA(WREA), .RESETA(RSTA), - .OCEA(1'b0), + .OCEA(1'b1), .ADA(ADA), .DIA(DIA), .DOA(DOA), @@ -292,7 +296,7 @@ end else begin .CEB(PORT_B_CLK_EN), .WREB(WREB), .RESETB(RSTB), - .OCEB(1'b0), + .OCEB(1'b1), .ADB(ADB), .DIB(DIB), .DOB(DOB), @@ -344,28 +348,28 @@ if (PORT_W_WIDTH < 9 || PORT_R_WIDTH < 9) begin assign PORT_R_RD_DATA = `x8_rd_data(DO); - SDP #( + SDPB #( `INIT(init_slice_x8) .READ_MODE(1'b0), .BIT_WIDTH_0(`x8_width(PORT_W_WIDTH)), .BIT_WIDTH_1(`x8_width(PORT_R_WIDTH)), - .BLK_SEL(3'b000), + .BLK_SEL_0(3'b000), + .BLK_SEL_1(3'b000), .RESET_MODE(OPTION_RESET_MODE), ) _TECHMAP_REPLACE_ ( - .BLKSEL(3'b000), + .BLKSELA(3'b000), + .BLKSELB(3'b000), .CLKA(PORT_W_CLK), .CEA(PORT_W_CLK_EN), - .WREA(WRE), .RESETA(1'b0), .ADA(ADW), .DI(DI), .CLKB(PORT_R_CLK), .CEB(PORT_R_CLK_EN), - .WREB(1'b0), .RESETB(RST), - .OCE(1'b0), + .OCE(1'b1), .ADB(PORT_R_ADDR), .DO(DO), ); @@ -377,28 +381,28 @@ end else begin assign PORT_R_RD_DATA = DO; - SDPX9 #( + SDPX9B #( `INIT(init_slice_x9) .READ_MODE(1'b0), .BIT_WIDTH_0(PORT_W_WIDTH), .BIT_WIDTH_1(PORT_R_WIDTH), - .BLK_SEL(3'b000), + .BLK_SEL_0(3'b000), + .BLK_SEL_1(3'b000), .RESET_MODE(OPTION_RESET_MODE), ) _TECHMAP_REPLACE_ ( - .BLKSEL(3'b000), + .BLKSELA(3'b000), + .BLKSELB(3'b000), .CLKA(PORT_W_CLK), .CEA(PORT_W_CLK_EN), - .WREA(WRE), .RESETA(1'b0), .ADA(ADW), .DI(DI), .CLKB(PORT_R_CLK), .CEB(PORT_R_CLK_EN), - .WREB(1'b0), .RESETB(RST), - .OCE(1'b0), + .OCE(1'b1), .ADB(PORT_R_ADDR), .DO(DO), ); From ac6fcb2547085d041a432a3182a3bfb2c3d2e6ea Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 19 Jan 2024 15:36:14 +0100 Subject: [PATCH 29/57] write_aiger: Detect and error out on combinational loops Without this it will overflow the stack when loops are present. --- backends/aiger/aiger.cc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index bb804f230..f77a64978 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -54,6 +54,8 @@ struct AigerWriter vector> aig_gates; vector aig_latchin, aig_latchinit, aig_outputs; + vector bit2aig_stack; + size_t next_loop_check = 1024; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0; @@ -81,6 +83,23 @@ struct AigerWriter return it->second; } + if (bit2aig_stack.size() == next_loop_check) { + for (size_t i = 0; i < next_loop_check; ++i) + { + SigBit report_bit = bit2aig_stack[i]; + if (report_bit != bit) + continue; + for (size_t j = i; j < next_loop_check; ++j) { + report_bit = bit2aig_stack[j]; + if (report_bit.is_wire() && report_bit.wire->name.isPublic()) + break; + } + log_error("Found combinational logic loop while processing signal %s.\n", log_signal(report_bit)); + } + next_loop_check *= 2; + } + bit2aig_stack.push_back(bit); + // NB: Cannot use iterator returned from aig_map.insert() // since this function is called recursively @@ -101,6 +120,8 @@ struct AigerWriter a = initstate_ff; } + bit2aig_stack.pop_back(); + if (bit == State::Sx || bit == State::Sz) log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); From 08d7f5472670f02beb498905a4ea3b5e55701e6d Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 19 Jan 2024 13:37:48 +0000 Subject: [PATCH 30/57] cxxrtl: move a definition around. NFC --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 256 ++++++++++++------------ 1 file changed, 128 insertions(+), 128 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index d3b9cf9db..cfdb66f5c 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -764,134 +764,6 @@ std::ostream &operator<<(std::ostream &os, const value &val) { return os; } -// Must be kept in sync with `struct FmtPart` in kernel/fmt.h! -// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. -struct fmt_part { - enum { - STRING = 0, - INTEGER = 1, - CHARACTER = 2, - TIME = 3, - } type; - - // STRING type - std::string str; - - // INTEGER/CHARACTER types - // + value val; - - // INTEGER/CHARACTER/TIME types - enum { - RIGHT = 0, - LEFT = 1, - } justify; // = RIGHT; - char padding; // = '\0'; - size_t width; // = 0; - - // INTEGER type - unsigned base; // = 10; - bool signed_; // = false; - bool plus; // = false; - - // TIME type - bool realtime; // = false; - // + int64_t itime; - // + double ftime; - - // Format the part as a string. - // - // The values of `itime` and `ftime` are used for `$time` and `$realtime`, correspondingly. - template - std::string render(value val, int64_t itime, double ftime) - { - // We might want to replace some of these bit() calls with direct - // chunk access if it turns out to be slow enough to matter. - std::string buf; - switch (type) { - case STRING: - return str; - - case CHARACTER: { - buf.reserve(Bits/8); - for (int i = 0; i < Bits; i += 8) { - char ch = 0; - for (int j = 0; j < 8 && i + j < int(Bits); j++) - if (val.bit(i + j)) - ch |= 1 << j; - if (ch != 0) - buf.append({ch}); - } - std::reverse(buf.begin(), buf.end()); - break; - } - - case INTEGER: { - size_t width = Bits; - if (base != 10) { - width = 0; - for (size_t index = 0; index < Bits; index++) - if (val.bit(index)) - width = index + 1; - } - - if (base == 2) { - for (size_t i = width; i > 0; i--) - buf += (val.bit(i - 1) ? '1' : '0'); - } else if (base == 8 || base == 16) { - size_t step = (base == 16) ? 4 : 3; - for (size_t index = 0; index < width; index += step) { - uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); - if (step == 4) - value |= val.bit(index + 3) << 3; - buf += "0123456789abcdef"[value]; - } - std::reverse(buf.begin(), buf.end()); - } else if (base == 10) { - bool negative = signed_ && val.is_neg(); - if (negative) - val = val.neg(); - if (val.is_zero()) - buf += '0'; - value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); - while (!xval.is_zero()) { - value<(Bits > 4 ? Bits : 4)> quotient, remainder; - if (Bits >= 4) - std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); - else - std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); - buf += '0' + remainder.template trunc<4>().template get(); - xval = quotient; - } - if (negative || plus) - buf += negative ? '-' : '+'; - std::reverse(buf.begin(), buf.end()); - } else assert(false && "Unsupported base for fmt_part"); - break; - } - - case TIME: { - buf = realtime ? std::to_string(ftime) : std::to_string(itime); - break; - } - } - - std::string str; - assert(width == 0 || padding != '\0'); - if (justify == RIGHT && buf.size() < width) { - size_t pad_width = width - buf.size(); - if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { - str += buf.front(); - buf.erase(0, 1); - } - str += std::string(pad_width, padding); - } - str += buf; - if (justify == LEFT && buf.size() < width) - str += std::string(width - buf.size(), padding); - return str; - } -}; - template struct wire { static constexpr size_t bits = Bits; @@ -1100,6 +972,134 @@ struct observer { void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} }; +// Must be kept in sync with `struct FmtPart` in kernel/fmt.h! +// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. +struct fmt_part { + enum { + STRING = 0, + INTEGER = 1, + CHARACTER = 2, + TIME = 3, + } type; + + // STRING type + std::string str; + + // INTEGER/CHARACTER types + // + value val; + + // INTEGER/CHARACTER/TIME types + enum { + RIGHT = 0, + LEFT = 1, + } justify; // = RIGHT; + char padding; // = '\0'; + size_t width; // = 0; + + // INTEGER type + unsigned base; // = 10; + bool signed_; // = false; + bool plus; // = false; + + // TIME type + bool realtime; // = false; + // + int64_t itime; + // + double ftime; + + // Format the part as a string. + // + // The values of `itime` and `ftime` are used for `$time` and `$realtime`, correspondingly. + template + std::string render(value val, int64_t itime, double ftime) + { + // We might want to replace some of these bit() calls with direct + // chunk access if it turns out to be slow enough to matter. + std::string buf; + switch (type) { + case STRING: + return str; + + case CHARACTER: { + buf.reserve(Bits/8); + for (int i = 0; i < Bits; i += 8) { + char ch = 0; + for (int j = 0; j < 8 && i + j < int(Bits); j++) + if (val.bit(i + j)) + ch |= 1 << j; + if (ch != 0) + buf.append({ch}); + } + std::reverse(buf.begin(), buf.end()); + break; + } + + case INTEGER: { + size_t width = Bits; + if (base != 10) { + width = 0; + for (size_t index = 0; index < Bits; index++) + if (val.bit(index)) + width = index + 1; + } + + if (base == 2) { + for (size_t i = width; i > 0; i--) + buf += (val.bit(i - 1) ? '1' : '0'); + } else if (base == 8 || base == 16) { + size_t step = (base == 16) ? 4 : 3; + for (size_t index = 0; index < width; index += step) { + uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); + if (step == 4) + value |= val.bit(index + 3) << 3; + buf += "0123456789abcdef"[value]; + } + std::reverse(buf.begin(), buf.end()); + } else if (base == 10) { + bool negative = signed_ && val.is_neg(); + if (negative) + val = val.neg(); + if (val.is_zero()) + buf += '0'; + value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); + while (!xval.is_zero()) { + value<(Bits > 4 ? Bits : 4)> quotient, remainder; + if (Bits >= 4) + std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); + else + std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); + buf += '0' + remainder.template trunc<4>().template get(); + xval = quotient; + } + if (negative || plus) + buf += negative ? '-' : '+'; + std::reverse(buf.begin(), buf.end()); + } else assert(false && "Unsupported base for fmt_part"); + break; + } + + case TIME: { + buf = realtime ? std::to_string(ftime) : std::to_string(itime); + break; + } + } + + std::string str; + assert(width == 0 || padding != '\0'); + if (justify == RIGHT && buf.size() < width) { + size_t pad_width = width - buf.size(); + if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { + str += buf.front(); + buf.erase(0, 1); + } + str += std::string(pad_width, padding); + } + str += buf; + if (justify == LEFT && buf.size() < width) + str += std::string(width - buf.size(), padding); + return str; + } +}; + // Tag class to disambiguate values/wires and their aliases. struct debug_alias {}; From b74d33d1b8329ecda69ff6889479a097d8835664 Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 19 Jan 2024 13:33:14 +0000 Subject: [PATCH 31/57] fmt: rename TIME to VLOG_TIME. The behavior of these format specifiers is highly specific to Verilog (`$time` and `$realtime` are only defined relative to `$timescale`) and may not fit other languages well, if at all. If they choose to use it, it is now clear what they are opting into. This commit also simplifies the CXXRTL code generation for these format specifiers. --- backends/cxxrtl/cxxrtl_backend.cc | 8 ++++---- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 26 ++++++++++++++----------- kernel/fmt.cc | 26 ++++++++++++------------- kernel/fmt.h | 8 ++++---- tests/fmt/always_full_tb.cc | 2 +- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ef718067f..620b530b0 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1072,9 +1072,9 @@ struct CxxrtlWorker { dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1>{1u}) {\n"; inc_indent(); - f << indent << "auto formatter = [&](int64_t itime, double ftime) {\n"; + f << indent << "auto formatter = [&](struct performer *performer) {\n"; inc_indent(); - fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); + fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }, "performer"); dec_indent(); f << indent << "};\n"; f << indent << "if (performer) {\n"; @@ -1082,11 +1082,11 @@ struct CxxrtlWorker { f << indent << "static const metadata_map attributes = "; dump_metadata_map(cell->attributes); f << ";\n"; - f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()), attributes);\n"; + f << indent << "performer->on_print(formatter(performer), attributes);\n"; dec_indent(); f << indent << "} else {\n"; inc_indent(); - f << indent << print_output << " << formatter(0, 0.0);\n"; + f << indent << print_output << " << formatter(performer);\n"; dec_indent(); f << indent << "}\n"; dec_indent(); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index cfdb66f5c..a8a011d15 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -948,10 +948,10 @@ typedef std::map metadata_map; // An object that can be passed to a `eval()` method in order to act on side effects. struct performer { // Called to evaluate a Verilog `$time` expression. - virtual int64_t time() const { return 0; } + virtual int64_t vlog_time() const { return 0; } // Called to evaluate a Verilog `$realtime` expression. - virtual double realtime() const { return time(); } + virtual double vlog_realtime() const { return vlog_time(); } // Called when a `$print` cell is triggered. virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; } @@ -976,10 +976,10 @@ struct observer { // Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. struct fmt_part { enum { - STRING = 0, - INTEGER = 1, + STRING = 0, + INTEGER = 1, CHARACTER = 2, - TIME = 3, + VLOG_TIME = 3, } type; // STRING type @@ -988,7 +988,7 @@ struct fmt_part { // INTEGER/CHARACTER types // + value val; - // INTEGER/CHARACTER/TIME types + // INTEGER/CHARACTER/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, @@ -1001,16 +1001,16 @@ struct fmt_part { bool signed_; // = false; bool plus; // = false; - // TIME type + // VLOG_TIME type bool realtime; // = false; // + int64_t itime; // + double ftime; // Format the part as a string. // - // The values of `itime` and `ftime` are used for `$time` and `$realtime`, correspondingly. + // The values of `vlog_time` and `vlog_realtime` are used for Verilog `$time` and `$realtime`, correspondingly. template - std::string render(value val, int64_t itime, double ftime) + std::string render(value val, performer *performer = nullptr) { // We might want to replace some of these bit() calls with direct // chunk access if it turns out to be slow enough to matter. @@ -1077,8 +1077,12 @@ struct fmt_part { break; } - case TIME: { - buf = realtime ? std::to_string(ftime) : std::to_string(itime); + case VLOG_TIME: { + if (performer) { + buf = realtime ? std::to_string(performer->vlog_realtime()) : std::to_string(performer->vlog_time()); + } else { + buf = realtime ? std::to_string(0.0) : std::to_string(0); + } break; } } diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 7d16b20c1..18eb7cb71 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -110,9 +110,9 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt[i] == 'c') { part.type = FmtPart::CHARACTER; } else if (fmt[i] == 't') { - part.type = FmtPart::TIME; + part.type = FmtPart::VLOG_TIME; } else if (fmt[i] == 'r') { - part.type = FmtPart::TIME; + part.type = FmtPart::VLOG_TIME; part.realtime = true; } else { log_assert(false && "Unexpected character in format substitution"); @@ -172,7 +172,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { } break; - case FmtPart::TIME: + case FmtPart::VLOG_TIME: log_assert(part.sig.size() == 0); YS_FALLTHROUGH case FmtPart::CHARACTER: @@ -205,7 +205,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { fmt += part.signed_ ? 's' : 'u'; } else if (part.type == FmtPart::CHARACTER) { fmt += 'c'; - } else if (part.type == FmtPart::TIME) { + } else if (part.type == FmtPart::VLOG_TIME) { if (part.realtime) fmt += 'r'; else @@ -328,7 +328,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik case VerilogFmtArg::TIME: { FmtPart part = {}; - part.type = FmtPart::TIME; + part.type = FmtPart::VLOG_TIME; part.realtime = arg->realtime; part.padding = ' '; part.width = 20; @@ -419,7 +419,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.padding = ' '; } else if (fmt[i] == 't' || fmt[i] == 'T') { if (arg->type == VerilogFmtArg::TIME) { - part.type = FmtPart::TIME; + part.type = FmtPart::VLOG_TIME; part.realtime = arg->realtime; if (!has_width && !has_leading_zero) part.width = 20; @@ -541,7 +541,7 @@ std::vector Fmt::emit_verilog() const break; } - case FmtPart::TIME: { + case FmtPart::VLOG_TIME: { VerilogFmtArg arg; arg.type = VerilogFmtArg::TIME; if (part.realtime) @@ -592,7 +592,7 @@ std::string escape_cxx_string(const std::string &input) return output; } -void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig) const +void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig, const std::string &context) const { os << indent << "std::string buf;\n"; for (auto &part : parts) { @@ -602,7 +602,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name); std::vector emit_verilog() const; - void emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig) const; + void emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig, const std::string &context) const; std::string render() const; diff --git a/tests/fmt/always_full_tb.cc b/tests/fmt/always_full_tb.cc index a29a8ae05..1dd80d0a2 100644 --- a/tests/fmt/always_full_tb.cc +++ b/tests/fmt/always_full_tb.cc @@ -3,7 +3,7 @@ int main() { struct : public performer { - int64_t time() const override { return 1; } + int64_t vlog_time() const override { return 1; } void on_print(const std::string &output, const cxxrtl::metadata_map &) override { std::cerr << output; } } performer; From b11449badbd29b0a28fb972271e31897d81e158d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 19 Jan 2024 16:30:35 +0100 Subject: [PATCH 32/57] Make small build links, and support verific small build --- Makefile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f2af053be..a3cc2446f 100644 --- a/Makefile +++ b/Makefile @@ -679,12 +679,8 @@ OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o OBJS += libs/sha1/sha1.o -ifneq ($(SMALL),1) - OBJS += libs/json11/json11.o -OBJS += libs/subcircuit/subcircuit.o - OBJS += libs/ezsat/ezsat.o OBJS += libs/ezsat/ezminisat.o @@ -699,6 +695,10 @@ OBJS += libs/fst/fastlz.o OBJS += libs/fst/lz4.o endif +ifneq ($(SMALL),1) + +OBJS += libs/subcircuit/subcircuit.o + include $(YOSYS_SRC)/frontends/*/Makefile.inc include $(YOSYS_SRC)/passes/*/Makefile.inc include $(YOSYS_SRC)/backends/*/Makefile.inc @@ -707,6 +707,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc else include $(YOSYS_SRC)/frontends/verilog/Makefile.inc +ifeq ($(ENABLE_VERIFIC),1) +include $(YOSYS_SRC)/frontends/verific/Makefile.inc +endif include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc include $(YOSYS_SRC)/frontends/ast/Makefile.inc include $(YOSYS_SRC)/frontends/blif/Makefile.inc From fc5ff7a265293099e25812afbbbe8e687f0b62e8 Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 19 Jan 2024 15:54:33 +0000 Subject: [PATCH 33/57] cxxrtl: always lazily format print messages. This is mostly useful for collecting coverage for the future `$check` cell, where, depending on the flavor, formatting a message may not be wanted even for a failed assertion. --- backends/cxxrtl/cxxrtl_backend.cc | 32 +++++++++++++++++++++---- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 15 +++++++++--- tests/fmt/always_full_tb.cc | 2 +- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 620b530b0..c9c644a52 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1072,21 +1072,43 @@ struct CxxrtlWorker { dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1>{1u}) {\n"; inc_indent(); - f << indent << "auto formatter = [&](struct performer *performer) {\n"; + dict fmt_args; + f << indent << "struct : public lazy_fmt {\n"; inc_indent(); - fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }, "performer"); + f << indent << "std::string operator() () const override {\n"; + inc_indent(); + fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) { + if (sig.size() == 0) + f << "value<0>()"; + else { + std::string arg_name = "arg" + std::to_string(fmt_args.size()); + fmt_args[arg_name] = sig; + f << arg_name; + } + }, "performer"); + dec_indent(); + f << indent << "}\n"; + f << indent << "struct performer *performer;\n"; + for (auto arg : fmt_args) + f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n"; dec_indent(); - f << indent << "};\n"; + f << indent << "} formatter;\n"; + f << indent << "formatter.performer = performer;\n"; + for (auto arg : fmt_args) { + f << indent << "formatter." << arg.first << " = "; + dump_sigspec_rhs(arg.second); + f << ";\n"; + } f << indent << "if (performer) {\n"; inc_indent(); f << indent << "static const metadata_map attributes = "; dump_metadata_map(cell->attributes); f << ";\n"; - f << indent << "performer->on_print(formatter(performer), attributes);\n"; + f << indent << "performer->on_print(formatter, attributes);\n"; dec_indent(); f << indent << "} else {\n"; inc_indent(); - f << indent << print_output << " << formatter(performer);\n"; + f << indent << print_output << " << formatter();\n"; dec_indent(); f << indent << "}\n"; dec_indent(); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index a8a011d15..550571497 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -945,16 +945,25 @@ struct metadata { typedef std::map metadata_map; +struct performer; + +// An object that allows formatting a string lazily. +struct lazy_fmt { + virtual std::string operator() () const = 0; +}; + // An object that can be passed to a `eval()` method in order to act on side effects. struct performer { - // Called to evaluate a Verilog `$time` expression. + // Called by generated formatting code to evaluate a Verilog `$time` expression. virtual int64_t vlog_time() const { return 0; } - // Called to evaluate a Verilog `$realtime` expression. + // Called by generated formatting code to evaluate a Verilog `$realtime` expression. virtual double vlog_realtime() const { return vlog_time(); } // Called when a `$print` cell is triggered. - virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; } + virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) { + std::cout << formatter(); + } }; // An object that can be passed to a `commit()` method in order to produce a replay log of every state change in diff --git a/tests/fmt/always_full_tb.cc b/tests/fmt/always_full_tb.cc index 1dd80d0a2..94991ca25 100644 --- a/tests/fmt/always_full_tb.cc +++ b/tests/fmt/always_full_tb.cc @@ -4,7 +4,7 @@ int main() { struct : public performer { int64_t vlog_time() const override { return 1; } - void on_print(const std::string &output, const cxxrtl::metadata_map &) override { std::cerr << output; } + void on_print(const lazy_fmt &output, const cxxrtl::metadata_map &) override { std::cerr << output(); } } performer; cxxrtl_design::p_always__full uut; From 8649e306682e44b7f3e1b869d1043041db00b13e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 00:16:07 +0000 Subject: [PATCH 34/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f2af053be..88dfb4374 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+15 +YOSYS_VER := 0.37+21 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From b3e7390c0eb89544c0da6d9bc3b381f6ce450228 Mon Sep 17 00:00:00 2001 From: Stephen Tong <14918218+stong@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:32:05 -0500 Subject: [PATCH 35/57] Fix typo in stat help --- passes/cmds/stat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index f0021cf87..d34373c1c 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -366,7 +366,7 @@ struct StatPass : public Pass { log(" use cell area information from the provided liberty file\n"); log("\n"); log(" -tech \n"); - log(" print area estemate for the specified technology. Currently supported\n"); + log(" print area estimate for the specified technology. Currently supported\n"); log(" values for : xilinx, cmos\n"); log("\n"); log(" -width\n"); From 3d9e44d18227c136cea031c667a5158fe31ac996 Mon Sep 17 00:00:00 2001 From: Catherine Date: Fri, 19 Jan 2024 14:23:08 +0000 Subject: [PATCH 36/57] hierarchy: keep display statements, like formal assertions. --- passes/hierarchy/hierarchy.cc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index bf0137503..69cc6fb91 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -655,6 +655,17 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) log("Removed %d unused modules.\n", del_counter); } +bool set_keep_print(std::map &cache, RTLIL::Module *mod) +{ + if (cache.count(mod) == 0) + for (auto c : mod->cells()) { + RTLIL::Module *m = mod->design->module(c->type); + if ((m != nullptr && set_keep_print(cache, m)) || c->type == ID($print)) + return cache[mod] = true; + } + return cache[mod]; +} + bool set_keep_assert(std::map &cache, RTLIL::Module *mod) { if (cache.count(mod) == 0) @@ -762,6 +773,11 @@ struct HierarchyPass : public Pass { log(" -nodefaults\n"); log(" do not resolve input port default values\n"); log("\n"); + log(" -nokeep_prints\n"); + log(" per default this pass sets the \"keep\" attribute on all modules\n"); + log(" that directly or indirectly display text on the terminal.\n"); + log(" This option disables this behavior.\n"); + log("\n"); log(" -nokeep_asserts\n"); log(" per default this pass sets the \"keep\" attribute on all modules\n"); log(" that directly or indirectly contain one or more formal properties.\n"); @@ -818,6 +834,7 @@ struct HierarchyPass : public Pass { bool keep_positionals = false; bool keep_portwidths = false; bool nodefaults = false; + bool nokeep_prints = false; bool nokeep_asserts = false; std::vector generate_cells; std::vector generate_ports; @@ -893,6 +910,10 @@ struct HierarchyPass : public Pass { nodefaults = true; continue; } + if (args[argidx] == "-nokeep_prints") { + nokeep_prints = true; + continue; + } if (args[argidx] == "-nokeep_asserts") { nokeep_asserts = true; continue; @@ -1091,6 +1112,15 @@ struct HierarchyPass : public Pass { } } + if (!nokeep_prints) { + std::map cache; + for (auto mod : design->modules()) + if (set_keep_print(cache, mod)) { + log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", log_id(mod)); + mod->set_bool_attribute(ID::keep); + } + } + if (!nokeep_asserts) { std::map cache; for (auto mod : design->modules()) From cfcd0b57299a9f2b13453e396c7a7b72d0aee16e Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 22 Jan 2024 17:18:39 +0100 Subject: [PATCH 37/57] Checkout specific iverilog version (can be master as well) --- .github/workflows/test-linux.yml | 5 ++++- .github/workflows/test-macos.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index a16481726..103c060a2 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -85,13 +85,16 @@ jobs: shell: bash run: | git clone https://github.com/steveicarus/iverilog.git + cd iverilog + git checkout ${{ vars.IVERILOG_VERSION }} + echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV - name: Cache iverilog id: cache-iverilog uses: actions/cache@v3 with: path: .local/ - key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }} + key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} - name: Build iverilog if: steps.cache-iverilog.outputs.cache-hit != 'true' diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index 2b48b7252..62fbc59e8 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -41,13 +41,16 @@ jobs: shell: bash run: | git clone https://github.com/steveicarus/iverilog.git + cd iverilog + git checkout ${{ vars.IVERILOG_VERSION }} + echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV - name: Cache iverilog id: cache-iverilog uses: actions/cache@v3 with: path: .local/ - key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }} + key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} - name: Build iverilog if: steps.cache-iverilog.outputs.cache-hit != 'true' From 2f9fcc2e501b5f0a449d315738c4a8d06cf38434 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:16:43 +0000 Subject: [PATCH 38/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 88dfb4374..836425847 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+21 +YOSYS_VER := 0.37+27 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From ddfd867d2998eeba9fe224047fc8ab533432c695 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 23 Jan 2024 17:22:56 +0100 Subject: [PATCH 39/57] hardcode iverilog version so it works on forkes and in PRs --- .github/workflows/test-linux.yml | 2 +- .github/workflows/test-macos.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 103c060a2..f90e65f4f 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -86,7 +86,7 @@ jobs: run: | git clone https://github.com/steveicarus/iverilog.git cd iverilog - git checkout ${{ vars.IVERILOG_VERSION }} + git checkout 192b6aec96fde982e6ddcb28b346d5893aa8e874 echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV - name: Cache iverilog diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index 62fbc59e8..b70bc4549 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -42,7 +42,7 @@ jobs: run: | git clone https://github.com/steveicarus/iverilog.git cd iverilog - git checkout ${{ vars.IVERILOG_VERSION }} + git checkout 192b6aec96fde982e6ddcb28b346d5893aa8e874 echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV - name: Cache iverilog From 3c3788ee287017802c1b77879b5f8e9ceb1be8ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 00:16:36 +0000 Subject: [PATCH 40/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 836425847..d1fe4dc99 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+27 +YOSYS_VER := 0.37+29 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From b841d1bcbed73b47d98bc6b1f888a7070cc81c3d Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 24 Jan 2024 16:30:01 +0000 Subject: [PATCH 41/57] cxxrtl: fix typo in codegen for async set/clear. --- backends/cxxrtl/cxxrtl_backend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index c9c644a52..d2bca3065 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1446,7 +1446,7 @@ struct CxxrtlWorker { f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_lhs(cell->getPort(ID::Q)); + dump_sigspec_rhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); f << ", "; @@ -1458,7 +1458,7 @@ struct CxxrtlWorker { f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_lhs(cell->getPort(ID::Q)); + dump_sigspec_rhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int())); f << ", "; From 9cbfad2691b5956d936ea118e5321ff5daac30c1 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 23 Jan 2024 03:26:58 +0000 Subject: [PATCH 42/57] write_verilog: don't emit code with dangling else related to wrong condition. --- backends/verilog/verilog_backend.cc | 62 +++++++++++++++-------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 1fa31e31e..47f1c4c2b 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1949,13 +1949,8 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw); -void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) +void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs) { - int number_of_stmts = cs->switches.size() + cs->actions.size(); - - if (!omit_trailing_begin && number_of_stmts >= 2) - f << stringf("%s" "begin\n", indent.c_str()); - for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { if (it->first.size() == 0) continue; @@ -1965,15 +1960,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo dump_sigspec(f, it->second); f << stringf(";\n"); } - - for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) - dump_proc_switch(f, indent + " ", *it); - - if (!omit_trailing_begin && number_of_stmts == 0) - f << stringf("%s /* empty */;\n", indent.c_str()); - - if (omit_trailing_begin || number_of_stmts >= 2) - f << stringf("%s" "end\n", indent.c_str()); } bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) @@ -1996,36 +1982,52 @@ bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchR } } + dump_attributes(f, indent, sw->attributes); f << indent; auto sig_it = sw->signal.begin(); for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) { - bool had_newline = true; if (it != sw->cases.begin()) { - if ((*it)->compare.empty()) { - f << indent << "else\n"; - had_newline = true; - } else { - f << indent << "else "; - had_newline = false; - } + if ((*it)->compare.empty()) + f << " else begin\n"; + else + f << " else "; } if (!(*it)->compare.empty()) { - if (!(*it)->attributes.empty()) { - if (!had_newline) - f << "\n" << indent; - dump_attributes(f, "", (*it)->attributes, "\n" + indent); - } f << stringf("if ("); dump_sigspec(f, *sig_it); - f << stringf(")\n"); + f << stringf(") begin\n"); } - dump_case_body(f, indent, *it); + + dump_case_actions(f, indent, (*it)); + for (auto it2 = (*it)->switches.begin(); it2 != (*it)->switches.end(); ++it2) + dump_proc_switch(f, indent + " ", *it2); + + f << indent << "end"; if ((*it)->compare.empty()) break; } + f << "\n"; return true; } +void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) +{ + int number_of_stmts = cs->switches.size() + cs->actions.size(); + + if (!omit_trailing_begin && number_of_stmts >= 2) + f << stringf("%s" "begin\n", indent.c_str()); + + dump_case_actions(f, indent, cs); + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) + dump_proc_switch(f, indent + " ", *it); + + if (!omit_trailing_begin && number_of_stmts == 0) + f << stringf("%s /* empty */;\n", indent.c_str()); + + if (omit_trailing_begin || number_of_stmts >= 2) + f << stringf("%s" "end\n", indent.c_str()); +} + void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) { if (sw->signal.size() == 0) { From 08fd47e97017c2e5f0dcbbd6c31377362e0b0a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 24 Jan 2024 16:46:44 +0100 Subject: [PATCH 43/57] Test roundtripping some processes to Verilog and back --- tests/verilog/roundtrip_proc.ys | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/verilog/roundtrip_proc.ys diff --git a/tests/verilog/roundtrip_proc.ys b/tests/verilog/roundtrip_proc.ys new file mode 100644 index 000000000..7a57ed310 --- /dev/null +++ b/tests/verilog/roundtrip_proc.ys @@ -0,0 +1,65 @@ +# Test "casez to if/elif/else conversion" in backend + +read_verilog < Date: Thu, 25 Jan 2024 00:16:42 +0000 Subject: [PATCH 44/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d1fe4dc99..04b2acef7 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+29 +YOSYS_VER := 0.37+34 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From efe4d6dbdc7366ee6e90df307bcc401a79af787b Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Thu, 25 Jan 2024 12:28:17 +0100 Subject: [PATCH 45/57] SigSpec/SigChunk::extract(): assert offset/length are not out of range --- kernel/rtlil.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index ffbd4b3ff..e065314dd 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -3693,6 +3693,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const { + log_assert(offset >= 0); + log_assert(length >= 0); + log_assert(offset + length <= width); RTLIL::SigChunk ret; if (wire) { ret.wire = wire; @@ -4377,6 +4380,9 @@ void RTLIL::SigSpec::remove(int offset, int length) RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const { + log_assert(offset >= 0); + log_assert(length >= 0); + log_assert(offset + length <= width_); unpack(); cover("kernel.rtlil.sigspec.extract_pos"); return std::vector(bits_.begin() + offset, bits_.begin() + offset + length); From 7c818d30f73c9d6820587781189a4f3990c0c55d Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 25 Jan 2024 16:08:52 +0100 Subject: [PATCH 46/57] sim: Bring $print trigger/sampling semantics in line with FFs --- passes/sat/sim.cc | 66 +++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 542eb5c18..aeed16b97 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -813,7 +813,7 @@ struct SimInstance } } - void update_ph3(bool check_assertions) + void update_ph3(bool gclk_trigger) { for (auto &it : ff_database) { @@ -858,49 +858,53 @@ struct SimInstance Const en = get_state(cell->getPort(ID::EN)); Const args = get_state(cell->getPort(ID::ARGS)); - if (!en.as_bool()) - goto update_print; + bool sampled = trg_en && trg.size() > 0; - if (trg.size() > 0 && trg_en) { - Const trg_pol = cell->getParam(ID::TRG_POLARITY); - for (int i = 0; i < trg.size(); i++) { - bool pol = trg_pol[i] == State::S1; - State curr = trg[i], past = print.past_trg[i]; - if (pol && curr == State::S1 && past == State::S0) + if (sampled ? print.past_en.as_bool() : en.as_bool()) { + if (sampled) { + sampled = true; + Const trg_pol = cell->getParam(ID::TRG_POLARITY); + for (int i = 0; i < trg.size(); i++) { + bool pol = trg_pol[i] == State::S1; + State curr = trg[i], past = print.past_trg[i]; + if (pol && curr == State::S1 && past == State::S0) + triggered = true; + if (!pol && curr == State::S0 && past == State::S1) + triggered = true; + } + } else if (trg_en) { + // initial $print (TRG width = 0, TRG_ENABLE = true) + if (!print.initial_done && en != print.past_en) triggered = true; - if (!pol && curr == State::S0 && past == State::S1) + } else if (cell->get_bool_attribute(ID(trg_on_gclk))) { + // unified $print for cycle based FV semantics + triggered = gclk_trigger; + } else { + // always @(*) $print + if (args != print.past_args || en != print.past_en) triggered = true; } - } else if (trg_en) { - // initial $print (TRG width = 0, TRG_ENABLE = true) - if (!print.initial_done && en != print.past_en) - triggered = true; - } else { - // always @(*) $print - if (args != print.past_args || en != print.past_en) - triggered = true; - } - if (triggered) { - int pos = 0; - for (auto &part : print.fmt.parts) { - part.sig = args.extract(pos, part.sig.size()); - pos += part.sig.size(); + if (triggered) { + int pos = 0; + for (auto &part : print.fmt.parts) { + part.sig = (sampled ? print.past_args : args).extract(pos, part.sig.size()); + pos += part.sig.size(); + } + + std::string rendered = print.fmt.render(); + log("%s", rendered.c_str()); + shared->display_output.emplace_back(shared->step, this, cell, rendered); } - - std::string rendered = print.fmt.render(); - log("%s", rendered.c_str()); - shared->display_output.emplace_back(shared->step, this, cell, rendered); } - update_print: print.past_trg = trg; print.past_en = en; print.past_args = args; print.initial_done = true; } - if (check_assertions) + if (gclk_trigger) { for (auto cell : formal_database) { @@ -932,7 +936,7 @@ struct SimInstance } for (auto it : children) - it.second->update_ph3(check_assertions); + it.second->update_ph3(gclk_trigger); } void set_initstate_outputs(State state) From d2a04cca0e422ad73efe7364b395db07e7eedd28 Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Thu, 25 Jan 2024 17:00:05 +0000 Subject: [PATCH 47/57] write_verilog: Making sure BUF cells are converted to expressions. These were previously not being converted correctly leading to yosys internal cells being written to my netlist. Signed-off-by: Ethan Mahintorabi --- backends/verilog/verilog_backend.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 47f1c4c2b..8febf0a96 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1053,6 +1053,16 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(";\n"); return true; } + + if (cell->type == ID($_BUF_)) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(";\n"); + return true; + } if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { f << stringf("%s" "assign ", indent.c_str()); From 33fe2e46138f956827bbd057b8df6ed7af57841c Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Thu, 25 Jan 2024 17:39:18 +0000 Subject: [PATCH 48/57] fixes char* to string conversion issue Signed-off-by: Ethan Mahintorabi --- backends/verilog/verilog_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 8febf0a96..51ea14bf4 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1058,7 +1058,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "A", false); f << stringf(";\n"); return true; From 7e524e05885539a20f191ad3f15287c50f98c4e2 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:20:48 +1300 Subject: [PATCH 49/57] Update workflows to Node.js 20 Node.js 16 actions are deprecated. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/. --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/emcc.yml | 6 +++--- .github/workflows/test-linux.yml | 4 ++-- .github/workflows/test-macos.yml | 4 ++-- .github/workflows/version.yml | 2 +- .github/workflows/vs.yml | 6 +++--- .github/workflows/wasi.yml | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2a046703b..4a77d2234 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -14,10 +14,10 @@ jobs: run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: cpp queries: security-extended,security-and-quality @@ -26,4 +26,4 @@ jobs: run: make yosys -j6 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/emcc.yml b/.github/workflows/emcc.yml index 295d9554b..7c6409c1b 100644 --- a/.github/workflows/emcc.yml +++ b/.github/workflows/emcc.yml @@ -6,13 +6,13 @@ jobs: emcc: runs-on: ubuntu-latest steps: - - uses: mymindstorm/setup-emsdk@v11 - - uses: actions/checkout@v3 + - uses: mymindstorm/setup-emsdk@v14 + - uses: actions/checkout@v4 - name: Build run: | make config-emcc make YOSYS_VER=latest - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: yosysjs path: yosysjs-latest.zip diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index f90e65f4f..5d929f581 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -79,7 +79,7 @@ jobs: $CXX --version - name: Checkout Yosys - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get iverilog shell: bash @@ -91,7 +91,7 @@ jobs: - name: Cache iverilog id: cache-iverilog - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: .local/ key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index b70bc4549..17e2ae331 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -35,7 +35,7 @@ jobs: cc --version - name: Checkout Yosys - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get iverilog shell: bash @@ -47,7 +47,7 @@ jobs: - name: Cache iverilog id: cache-iverilog - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: .local/ key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index c2a1756e9..47a7fe1a3 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Take last commit diff --git a/.github/workflows/vs.yml b/.github/workflows/vs.yml index 428770e72..799c5f259 100644 --- a/.github/workflows/vs.yml +++ b/.github/workflows/vs.yml @@ -6,10 +6,10 @@ jobs: yosys-vcxsrc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build run: make vcxsrc YOSYS_VER=latest - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: vcxsrc path: yosys-win32-vcxsrc-latest.zip @@ -18,7 +18,7 @@ jobs: runs-on: windows-2019 needs: yosys-vcxsrc steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: vcxsrc path: . diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml index 74900c388..d6d200d92 100644 --- a/.github/workflows/wasi.yml +++ b/.github/workflows/wasi.yml @@ -6,7 +6,7 @@ jobs: wasi: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build run: | WASI_SDK=wasi-sdk-19.0 From a5fdf3f881cac408e6c8ce061469dfeae8562c62 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 27 Jan 2024 17:19:49 +1000 Subject: [PATCH 50/57] gowin: Change BYTE ENABLE handling. When inferring we allow writing to all bytes for now. Signed-off-by: YRabbit --- techlibs/gowin/brams.txt | 6 ------ techlibs/gowin/brams_map.v | 36 +++++++++++------------------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/techlibs/gowin/brams.txt b/techlibs/gowin/brams.txt index 0c0d8fa3e..435d3b5cf 100644 --- a/techlibs/gowin/brams.txt +++ b/techlibs/gowin/brams.txt @@ -1,13 +1,11 @@ ram block $__GOWIN_SP_ { abits 14; widths 1 2 4 9 18 36 per_port; - byte 9; cost 128; init no_undef; port srsw "A" { clock posedge; clken; - wrbe_separate; option "RESET_MODE" "SYNC" { rdsrst zero ungated; } @@ -30,13 +28,11 @@ ram block $__GOWIN_SP_ { ram block $__GOWIN_DP_ { abits 14; widths 1 2 4 9 18 per_port; - byte 9; cost 128; init no_undef; port srsw "A" "B" { clock posedge; clken; - wrbe_separate; option "RESET_MODE" "SYNC" { rdsrst zero ungated; } @@ -59,7 +55,6 @@ ram block $__GOWIN_DP_ { ram block $__GOWIN_SDP_ { abits 14; widths 1 2 4 9 18 36 per_port; - byte 9; cost 128; init no_undef; port sr "R" { @@ -76,6 +71,5 @@ ram block $__GOWIN_SDP_ { port sw "W" { clock posedge; clken; - wrbe_separate; } } diff --git a/techlibs/gowin/brams_map.v b/techlibs/gowin/brams_map.v index 5e1299c28..d536cd636 100644 --- a/techlibs/gowin/brams_map.v +++ b/techlibs/gowin/brams_map.v @@ -14,8 +14,7 @@ `define x8_width(width) (width / 9 * 8 + width % 9) `define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]} `define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]} -`define wre(width, wr_en, wr_be) (width < 18 ? wr_en | wr_be[0] : wr_en) -`define addrbe(width, addr, wr_be) (width < 18 ? addr : {addr[13:4], wr_be}) +`define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111}) `define INIT(func) \ @@ -90,7 +89,6 @@ parameter INIT = 0; parameter OPTION_RESET_MODE = "SYNC"; parameter PORT_A_WIDTH = 36; -parameter PORT_A_WR_BE_WIDTH = 4; parameter PORT_A_OPTION_WRITE_MODE = 0; input PORT_A_CLK; @@ -99,15 +97,13 @@ input PORT_A_WR_EN; input PORT_A_RD_SRST; input PORT_A_RD_ARST; input [13:0] PORT_A_ADDR; -input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE; input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA; output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA; `DEF_FUNCS wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST; -wire WRE = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE); -wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE); +wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR); generate @@ -129,7 +125,7 @@ if (PORT_A_WIDTH < 9) begin .BLKSEL(3'b000), .CLK(PORT_A_CLK), .CE(PORT_A_CLK_EN), - .WRE(WRE), + .WRE(PORT_A_WR_EN), .RESET(RST), .OCE(1'b1), .AD(AD), @@ -155,7 +151,7 @@ end else begin .BLKSEL(3'b000), .CLK(PORT_A_CLK), .CE(PORT_A_CLK_EN), - .WRE(WRE), + .WRE(PORT_A_WR_EN), .RESET(RST), .OCE(1'b1), .AD(AD), @@ -176,11 +172,9 @@ parameter INIT = 0; parameter OPTION_RESET_MODE = "SYNC"; parameter PORT_A_WIDTH = 18; -parameter PORT_A_WR_BE_WIDTH = 2; parameter PORT_A_OPTION_WRITE_MODE = 0; parameter PORT_B_WIDTH = 18; -parameter PORT_B_WR_BE_WIDTH = 2; parameter PORT_B_OPTION_WRITE_MODE = 0; input PORT_A_CLK; @@ -189,7 +183,6 @@ input PORT_A_WR_EN; input PORT_A_RD_SRST; input PORT_A_RD_ARST; input [13:0] PORT_A_ADDR; -input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE; input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA; output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA; @@ -199,7 +192,6 @@ input PORT_B_WR_EN; input PORT_B_RD_SRST; input PORT_B_RD_ARST; input [13:0] PORT_B_ADDR; -input [PORT_A_WR_BE_WIDTH-1:0] PORT_B_WR_BE; input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA; output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA; @@ -207,10 +199,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA; wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST; wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST; -wire WREA = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE); -wire WREB = `wre(PORT_B_WIDTH, PORT_B_WR_EN, PORT_B_WR_BE); -wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE); -wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE); +wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR); +wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR); generate @@ -241,7 +231,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin .CLKA(PORT_A_CLK), .CEA(PORT_A_CLK_EN), - .WREA(WREA), + .WREA(PORT_A_WR_EN), .RESETA(RSTA), .OCEA(1'b1), .ADA(ADA), @@ -250,7 +240,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin .CLKB(PORT_B_CLK), .CEB(PORT_B_CLK_EN), - .WREB(WREB), + .WREB(PORT_B_WR_EN), .RESETB(RSTB), .OCEB(1'b1), .ADB(ADB), @@ -285,7 +275,7 @@ end else begin .CLKA(PORT_A_CLK), .CEA(PORT_A_CLK_EN), - .WREA(WREA), + .WREA(PORT_A_WR_EN), .RESETA(RSTA), .OCEA(1'b1), .ADA(ADA), @@ -294,7 +284,7 @@ end else begin .CLKB(PORT_B_CLK), .CEB(PORT_B_CLK_EN), - .WREB(WREB), + .WREB(PORT_B_WR_EN), .RESETB(RSTB), .OCEB(1'b1), .ADB(ADB), @@ -315,9 +305,7 @@ parameter INIT = 0; parameter OPTION_RESET_MODE = "SYNC"; parameter PORT_R_WIDTH = 18; - parameter PORT_W_WIDTH = 18; -parameter PORT_W_WR_BE_WIDTH = 2; input PORT_R_CLK; input PORT_R_CLK_EN; @@ -330,14 +318,12 @@ input PORT_W_CLK; input PORT_W_CLK_EN; input PORT_W_WR_EN; input [13:0] PORT_W_ADDR; -input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE; input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; `DEF_FUNCS wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST; -wire WRE = `wre(PORT_W_WIDTH, PORT_W_WR_EN, PORT_W_WR_BE); -wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE); +wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR); generate From 4585d60b8aae42c4cedf43a22d649e4d7fe823ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 00:17:09 +0000 Subject: [PATCH 51/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 04b2acef7..b83e541c7 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+34 +YOSYS_VER := 0.37+39 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From d6600fb1d508fe7526b4157b8e5e66baaff367ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 8 Jan 2024 14:06:38 +0100 Subject: [PATCH 52/57] rtlil: Fix handling of connections on wire deletion --- kernel/rtlil.cc | 45 ++++++++++++++++++++++++++++++++++----------- kernel/rtlil.h | 1 + 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index ffbd4b3ff..badb72f2b 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2157,17 +2157,12 @@ void RTLIL::Module::remove(const pool &wires) } void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { - log_assert(GetSize(lhs) == GetSize(rhs)); - lhs.unpack(); - rhs.unpack(); - for (int i = 0; i < GetSize(lhs); i++) { - RTLIL::SigBit &lhs_bit = lhs.bits_[i]; - RTLIL::SigBit &rhs_bit = rhs.bits_[i]; - if ((lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) || (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))) { - lhs_bit = State::Sx; - rhs_bit = State::Sx; - } - } + // When a deleted wire occurs on the lhs we can just remove that part + // of the assignment + lhs.remove2(*wires_p, &rhs); + + // Then replace all rhs occurrences with a dummy wire + (*this)(rhs); } }; @@ -4238,6 +4233,34 @@ void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigS check(); } +void RTLIL::SigSpec::remove2(const pool &pattern, RTLIL::SigSpec *other) +{ + if (other) + cover("kernel.rtlil.sigspec.remove_other"); + else + cover("kernel.rtlil.sigspec.remove"); + + unpack(); + + if (other != NULL) { + log_assert(width_ == other->width_); + other->unpack(); + } + + for (int i = GetSize(bits_) - 1; i >= 0; i--) { + if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) { + bits_.erase(bits_.begin() + i); + width_--; + if (other != NULL) { + other->bits_.erase(other->bits_.begin() + i); + other->width_--; + } + } + } + + check(); +} + RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const { if (other) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index d419872c6..928bc0440 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -924,6 +924,7 @@ public: void remove(const pool &pattern, RTLIL::SigSpec *other) const; void remove2(const pool &pattern, RTLIL::SigSpec *other); void remove2(const std::set &pattern, RTLIL::SigSpec *other); + void remove2(const pool &pattern, RTLIL::SigSpec *other); void remove(int offset, int length = 1); void remove_const(); From c035289383d6151d508519777e110e7fff2d12c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 9 Jan 2024 17:25:31 +0100 Subject: [PATCH 53/57] rtlil: Do not create dummy wires when deleting wires in connections --- kernel/rtlil.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index badb72f2b..475988256 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2157,12 +2157,10 @@ void RTLIL::Module::remove(const pool &wires) } void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { - // When a deleted wire occurs on the lhs we can just remove that part + // If a deleted wire occurs on the lhs or rhs we just remove that part // of the assignment lhs.remove2(*wires_p, &rhs); - - // Then replace all rhs occurrences with a dummy wire - (*this)(rhs); + rhs.remove2(*wires_p, &lhs); } }; From ea3dc7c1b4f8446693edb4c5918a6d9e0366a595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 22 Jan 2024 14:42:45 +0100 Subject: [PATCH 54/57] rtlil: Add wire deletion test --- tests/various/bug4082.ys | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/various/bug4082.ys diff --git a/tests/various/bug4082.ys b/tests/various/bug4082.ys new file mode 100644 index 000000000..272be2fad --- /dev/null +++ b/tests/various/bug4082.ys @@ -0,0 +1,8 @@ +read_verilog < Date: Tue, 30 Jan 2024 00:15:11 +0000 Subject: [PATCH 55/57] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 51e98d951..0ccdbe2c9 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.37+39 +YOSYS_VER := 0.37+52 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 3076875fffb5fd29ca177bc9a4245f05d3636099 Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Tue, 30 Jan 2024 00:56:07 +0000 Subject: [PATCH 56/57] removing call to dump_attributes to remove possibility of generating invalid verilog Signed-off-by: Ethan Mahintorabi --- backends/verilog/verilog_backend.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 51ea14bf4..41e51f328 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1058,7 +1058,6 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); - dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "A", false); f << stringf(";\n"); return true; From 79c5a06673f0fcbf7d4794123477220ff7ad49b4 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 30 Jan 2024 17:06:59 +1000 Subject: [PATCH 57/57] gowin: Fix SDP write enable port. This primitive does not have a separate WRE port, so we regulate writing using Clock Enable. Signed-off-by: YRabbit --- techlibs/gowin/brams_map.v | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/techlibs/gowin/brams_map.v b/techlibs/gowin/brams_map.v index d536cd636..8e6cc6140 100644 --- a/techlibs/gowin/brams_map.v +++ b/techlibs/gowin/brams_map.v @@ -324,6 +324,7 @@ input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST; wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR); +wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN; generate @@ -347,7 +348,7 @@ if (PORT_W_WIDTH < 9 || PORT_R_WIDTH < 9) begin .BLKSELB(3'b000), .CLKA(PORT_W_CLK), - .CEA(PORT_W_CLK_EN), + .CEA(WRE), .RESETA(1'b0), .ADA(ADW), .DI(DI), @@ -380,7 +381,7 @@ end else begin .BLKSELB(3'b000), .CLKA(PORT_W_CLK), - .CEA(PORT_W_CLK_EN), + .CEA(WRE), .RESETA(1'b0), .ADA(ADW), .DI(DI),