From 160e3e089a14b79fa76de7c303140d3d4c0fd1da Mon Sep 17 00:00:00 2001 From: Peter Gadfort Date: Fri, 22 Mar 2024 09:20:08 -0400 Subject: [PATCH 01/29] add port statistics to stat command --- passes/cmds/stat.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 85107b68f..c2bb72f3b 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -36,7 +36,8 @@ struct cell_area_t { struct statdata_t { #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ - X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) + X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \ + X(num_processes) #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) @@ -90,6 +91,11 @@ struct statdata_t for (auto wire : mod->selected_wires()) { + if (wire->port_input || wire->port_output) { + num_ports++; + num_port_bits += wire->width; + } + if (wire->name.isPublic()) { num_pub_wires++; num_pub_wire_bits += wire->width; @@ -239,6 +245,8 @@ struct statdata_t log(" Number of wire bits: %6u\n", num_wire_bits); log(" Number of public wires: %6u\n", num_pub_wires); log(" Number of public wire bits: %6u\n", num_pub_wire_bits); + log(" Number of ports: %6u\n", num_ports); + log(" Number of port bits: %6u\n", num_port_bits); log(" Number of memories: %6u\n", num_memories); log(" Number of memory bits: %6u\n", num_memory_bits); log(" Number of processes: %6u\n", num_processes); @@ -284,6 +292,8 @@ struct statdata_t log(" \"num_wire_bits\": %u,\n", num_wire_bits); log(" \"num_pub_wires\": %u,\n", num_pub_wires); log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); + log(" \"num_ports\": %u,\n", num_ports); + log(" \"num_port_bits\": %u,\n", num_port_bits); log(" \"num_memories\": %u,\n", num_memories); log(" \"num_memory_bits\": %u,\n", num_memory_bits); log(" \"num_processes\": %u,\n", num_processes); @@ -494,6 +504,8 @@ struct StatPass : public Pass { design->scratchpad_set_int("stat.num_wire_bits", data.num_wire_bits); design->scratchpad_set_int("stat.num_pub_wires", data.num_pub_wires); design->scratchpad_set_int("stat.num_pub_wire_bits", data.num_pub_wire_bits); + design->scratchpad_set_int("stat.num_ports", data.num_ports); + design->scratchpad_set_int("stat.num_port_bits", data.num_port_bits); design->scratchpad_set_int("stat.num_memories", data.num_memories); design->scratchpad_set_int("stat.num_memory_bits", data.num_memory_bits); design->scratchpad_set_int("stat.num_processes", data.num_processes); From c38201e15d457388b26d712df4aa10266015c22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 25 Mar 2024 14:56:17 +0100 Subject: [PATCH 02/29] techmap: Add a Kogge-Stone option for `$lcu` mapping --- techlibs/common/techmap.v | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 7fb8173b0..38ca33cb9 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -230,6 +230,7 @@ module _90_lcu (P, G, CI, CO); // in almost all cases CI will be constant zero g[0] = g[0] | (p[0] & CI); +`ifndef KOGGE_STONE // [[CITE]] Brent Kung Adder // R. P. Brent and H. T. Kung, "A Regular Layout for Parallel Adders", // IEEE Transaction on Computers, Vol. C-31, No. 3, p. 260-264, March, 1982 @@ -249,6 +250,14 @@ module _90_lcu (P, G, CI, CO); p[j] = p[j] & p[j - 2**(i-1)]; end end +`else + for (i = 0; i < $clog2(WIDTH); i = i + 1) begin + for (j = 2**i; j < WIDTH; j = j + 1) begin + g[j] = g[j] | p[j] & g[j - 2**i]; + p[j] = p[j] & p[j - 2**i]; + end + end +`endif end assign CO = g; From 4570d064e5bac128f4057874457e306d4e275eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 25 Mar 2024 18:39:55 +0100 Subject: [PATCH 03/29] techmap: Split out Kogge-Stone into a separate file --- techlibs/common/Makefile.inc | 1 + techlibs/common/choices/kogge-stone.v | 53 +++++++++++++++++++++++++++ techlibs/common/techmap.v | 11 +----- 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 techlibs/common/choices/kogge-stone.v diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 6b377855e..fa5c2a825 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -35,3 +35,4 @@ $(eval $(call add_share_file,share,techlibs/common/abc9_map.v)) $(eval $(call add_share_file,share,techlibs/common/abc9_unmap.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2lcu.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2softlogic.v)) +$(eval $(call add_share_file,share/choices,techlibs/common/choices/kogge-stone.v)) diff --git a/techlibs/common/choices/kogge-stone.v b/techlibs/common/choices/kogge-stone.v new file mode 100644 index 000000000..eb97a75d8 --- /dev/null +++ b/techlibs/common/choices/kogge-stone.v @@ -0,0 +1,53 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Martin PoviĊĦer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +(* techmap_celltype = "$lcu" *) +module _80_lcu_kogge_stone (P, G, CI, CO); + parameter WIDTH = 2; + + (* force_downto *) + input [WIDTH-1:0] P, G; + input CI; + + (* force_downto *) + output [WIDTH-1:0] CO; + + integer i, j; + (* force_downto *) + reg [WIDTH-1:0] p, g; + + wire [1023:0] _TECHMAP_DO_ = "proc; opt -fast"; + + always @* begin + p = P; + g = G; + + // in almost all cases CI will be constant zero + g[0] = g[0] | (p[0] & CI); + + for (i = 0; i < $clog2(WIDTH); i = i + 1) begin + for (j = 2**i; j < WIDTH; j = j + 1) begin + g[j] = g[j] | p[j] & g[j - 2**i]; + p[j] = p[j] & p[j - 2**i]; + end + end + end + + assign CO = g; +endmodule diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 38ca33cb9..68b276588 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -207,7 +207,7 @@ module _90_fa (A, B, C, X, Y); endmodule (* techmap_celltype = "$lcu" *) -module _90_lcu (P, G, CI, CO); +module _90_lcu_brent_kung (P, G, CI, CO); parameter WIDTH = 2; (* force_downto *) @@ -230,7 +230,6 @@ module _90_lcu (P, G, CI, CO); // in almost all cases CI will be constant zero g[0] = g[0] | (p[0] & CI); -`ifndef KOGGE_STONE // [[CITE]] Brent Kung Adder // R. P. Brent and H. T. Kung, "A Regular Layout for Parallel Adders", // IEEE Transaction on Computers, Vol. C-31, No. 3, p. 260-264, March, 1982 @@ -250,14 +249,6 @@ module _90_lcu (P, G, CI, CO); p[j] = p[j] & p[j - 2**(i-1)]; end end -`else - for (i = 0; i < $clog2(WIDTH); i = i + 1) begin - for (j = 2**i; j < WIDTH; j = j + 1) begin - g[j] = g[j] | p[j] & g[j - 2**i]; - p[j] = p[j] & p[j - 2**i]; - end - end -`endif end assign CO = g; From c49d6e78743fb51335c0640af9d4dc8b6c9407cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 27 Mar 2024 11:08:26 +0100 Subject: [PATCH 04/29] techmap: Add Kogge-Stone test --- tests/techmap/kogge-stone.ys | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/techmap/kogge-stone.ys diff --git a/tests/techmap/kogge-stone.ys b/tests/techmap/kogge-stone.ys new file mode 100644 index 000000000..fc3637f10 --- /dev/null +++ b/tests/techmap/kogge-stone.ys @@ -0,0 +1 @@ +test_cell -s 1711533949 -n 10 -map +/techmap.v -map +/choices/kogge-stone.v $lcu From bc087f91ed2779fec32c94d99f56cebca3c6a4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 27 Mar 2024 18:32:25 +0100 Subject: [PATCH 05/29] techmap: Fix using overwritten results in Kogge-Stone --- techlibs/common/choices/kogge-stone.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/common/choices/kogge-stone.v b/techlibs/common/choices/kogge-stone.v index eb97a75d8..0e25fe860 100644 --- a/techlibs/common/choices/kogge-stone.v +++ b/techlibs/common/choices/kogge-stone.v @@ -42,7 +42,7 @@ module _80_lcu_kogge_stone (P, G, CI, CO); g[0] = g[0] | (p[0] & CI); for (i = 0; i < $clog2(WIDTH); i = i + 1) begin - for (j = 2**i; j < WIDTH; j = j + 1) begin + for (j = WIDTH - 1; j >= 2**i; j = j - 1) begin g[j] = g[j] | p[j] & g[j - 2**i]; p[j] = p[j] & p[j - 2**i]; end From d07a55a852e3e9430daba9068878b1ac10605f0a Mon Sep 17 00:00:00 2001 From: Merry <8682882+merryhime@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:53:26 +0000 Subject: [PATCH 06/29] cxxrtl: Fix sdivmod x = x.neg(); results in the subsequent x.is_neg() always being false. Ditto for the dividend.is_neg() != divisor.is_neg() test. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 8 +- tests/cxxrtl/test_value_fuzz.cc | 105 ++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index b834cd120..16aa900f1 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -625,11 +625,11 @@ struct value : public expr_base> { value remainder; value dividend = sext(); value divisor = other.template sext(); - if (dividend.is_neg()) dividend = dividend.neg(); - if (divisor.is_neg()) divisor = divisor.neg(); + if (is_neg()) dividend = dividend.neg(); + if (other.is_neg()) divisor = divisor.neg(); std::tie(quotient, remainder) = dividend.udivmod(divisor); - if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg(); - if (dividend.is_neg()) remainder = remainder.neg(); + if (is_neg() != other.is_neg()) quotient = quotient.neg(); + if (is_neg()) remainder = remainder.neg(); return {quotient.template trunc(), remainder.template trunc()}; } }; diff --git a/tests/cxxrtl/test_value_fuzz.cc b/tests/cxxrtl/test_value_fuzz.cc index 4428e9f6e..a7bbb293a 100644 --- a/tests/cxxrtl/test_value_fuzz.cc +++ b/tests/cxxrtl/test_value_fuzz.cc @@ -25,6 +25,11 @@ T rand_int(T min = std::numeric_limits::min(), T max = std::numeric_limits return dist(generator); } +int64_t sext(size_t bits, uint64_t value) +{ + return (int64_t)(value << (64 - bits)) >> (64 - bits); +} + struct BinaryOperationBase { void tweak_input(uint64_t &a, uint64_t &b) {} @@ -246,6 +251,106 @@ struct CtlzTest } } ctlz; +struct UdivTest : BinaryOperationBase +{ + UdivTest() + { + std::printf("Randomized tests for value::udivmod (div):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return a / b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<0>(a.udivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} udiv; + +struct UmodTest : BinaryOperationBase +{ + UmodTest() + { + std::printf("Randomized tests for value::udivmod (mod):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return a % b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<1>(a.udivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} umod; + +struct SdivTest : BinaryOperationBase +{ + SdivTest() + { + std::printf("Randomized tests for value::sdivmod (div):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return (uint64_t)(sext(bits, a) / sext(bits, b)); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<0>(a.sdivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} sdiv; + +struct SmodTest : BinaryOperationBase +{ + SmodTest() + { + std::printf("Randomized tests for value::sdivmod (mod):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return (uint64_t)(sext(bits, a) % sext(bits, b)); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<1>(a.sdivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} smod; + int main() { } From b9d3bffda5abcbc5356936a7192c4a3c2b427c3e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:18:11 +0000 Subject: [PATCH 07/29] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 391fb7288..ca229ca4a 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ LIBS += -lrt endif endif -YOSYS_VER := 0.39+147 +YOSYS_VER := 0.39+149 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 73cacd543c98ab29b9ea23104339c5405ce1f6ef Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 2 Apr 2024 11:23:56 +0200 Subject: [PATCH 08/29] docs: Update linux kernel coding style link --- guidelines/CodingStyle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guidelines/CodingStyle b/guidelines/CodingStyle index ee1e1a2b6..8a3df2d62 100644 --- a/guidelines/CodingStyle +++ b/guidelines/CodingStyle @@ -19,7 +19,7 @@ Formatting of code blank lines. - Otherwise stick to the Linux Kernel Coding Style: - https://www.kernel.org/doc/Documentation/CodingStyle + https://www.kernel.org/doc/Documentation/process/coding-style.rst C++ Language From a5441bc00c957c268279b35924d055418eb79446 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 28 Mar 2024 06:30:16 +0000 Subject: [PATCH 09/29] =?UTF-8?q?fmt:=20`FmtPart::{STRING=E2=86=92LITERAL}?= =?UTF-8?q?,{CHARACTER=E2=86=92STRING}`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, the `STRING` variant inserted a literal string; the `CHARACTER` variant inserted a string. This commit renames them to `LITERAL` and `STRING` respectively. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 14 ++++---- frontends/ast/genrtlil.cc | 2 +- frontends/ast/simplify.cc | 2 +- kernel/fmt.cc | 44 ++++++++++++------------- kernel/fmt.h | 12 +++---- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 16aa900f1..b1fc7e05c 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1010,19 +1010,19 @@ 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, + LITERAL = 0, INTEGER = 1, - CHARACTER = 2, + STRING = 2, VLOG_TIME = 3, } type; - // STRING type + // LITERAL type std::string str; - // INTEGER/CHARACTER types + // INTEGER/STRING types // + value val; - // INTEGER/CHARACTER/VLOG_TIME types + // INTEGER/STRING/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, @@ -1050,10 +1050,10 @@ struct fmt_part { // chunk access if it turns out to be slow enough to matter. std::string buf; switch (type) { - case STRING: + case LITERAL: return str; - case CHARACTER: { + case STRING: { buf.reserve(Bits/8); for (int i = 0; i < Bits; i += 8) { char ch = 0; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index fe67f00c6..bc7f1ddd1 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -790,7 +790,7 @@ struct AST_INTERNAL::ProcessGenerator Fmt fmt; fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); if (ast->str.substr(0, 8) == "$display") - fmt.append_string("\n"); + fmt.append_literal("\n"); fmt.emit_rtlil(cell); } else if (!ast->str.empty()) { log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 43a4e03a2..3d8478ef1 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1079,7 +1079,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // 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"); + fmt.append_literal("\n"); log("%s", fmt.render().c_str()); } diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 18eb7cb71..336e3e2ab 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -22,9 +22,9 @@ USING_YOSYS_NAMESPACE -void Fmt::append_string(const std::string &str) { +void Fmt::append_literal(const std::string &str) { FmtPart part = {}; - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; part.str = str; parts.push_back(part); } @@ -46,7 +46,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected '}' in format string"); else if (fmt[i] == '{') { if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); part = {}; } @@ -108,7 +108,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.type = FmtPart::INTEGER; part.base = 16; } else if (fmt[i] == 'c') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; } else if (fmt[i] == 't') { part.type = FmtPart::VLOG_TIME; } else if (fmt[i] == 'r') { @@ -150,7 +150,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } } if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); } } @@ -161,7 +161,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { for (auto &part : parts) { switch (part.type) { - case FmtPart::STRING: + case FmtPart::LITERAL: for (char c : part.str) { if (c == '{') fmt += "{{"; @@ -175,7 +175,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case FmtPart::VLOG_TIME: log_assert(part.sig.size() == 0); YS_FALLTHROUGH - case FmtPart::CHARACTER: + case FmtPart::STRING: log_assert(part.sig.size() % 8 == 0); YS_FALLTHROUGH case FmtPart::INTEGER: @@ -203,7 +203,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { if (part.plus) fmt += '+'; fmt += part.signed_ ? 's' : 'u'; - } else if (part.type == FmtPart::CHARACTER) { + } else if (part.type == FmtPart::STRING) { fmt += 'c'; } else if (part.type == FmtPart::VLOG_TIME) { if (part.realtime) @@ -299,12 +299,12 @@ void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part) part.width = places; if (part.justify == FmtPart::RIGHT) { - append_string(gap); + append_literal(gap); parts.push_back(part); } else { part.justify = FmtPart::RIGHT; parts.push_back(part); - append_string(gap); + append_literal(gap); } } } @@ -355,7 +355,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.str += module_name.str(); } else { if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); part = {}; } @@ -408,11 +408,11 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.type = FmtPart::INTEGER; part.base = 16; } else if (fmt[i] == 'c' || fmt[i] == 'C') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; part.sig.extend_u0(8); // %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog } else if (fmt[i] == 's' || fmt[i] == 'S') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; if ((part.sig.size() % 8) != 0) part.sig.extend_u0((part.sig.size() + 7) / 8 * 8); // %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog @@ -449,12 +449,12 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik } } if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); } } else { FmtPart part = {}; - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; part.str = arg->str; parts.push_back(part); } @@ -474,7 +474,7 @@ std::vector Fmt::emit_verilog() const for (auto &part : parts) { switch (part.type) { - case FmtPart::STRING: + case FmtPart::LITERAL: for (char c : part.str) { if (c == '%') fmt.str += "%%"; @@ -513,7 +513,7 @@ std::vector Fmt::emit_verilog() const break; } - case FmtPart::CHARACTER: { + case FmtPart::STRING: { VerilogFmtArg arg; arg.type = VerilogFmtArg::INTEGER; arg.sig = part.sig; @@ -599,9 +599,9 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function parts; - void append_string(const std::string &str); + void append_literal(const std::string &str); void parse_rtlil(const RTLIL::Cell *cell); void emit_rtlil(RTLIL::Cell *cell) const; From 8388846e3a727f42aa6086226863e530c22f07f6 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 28 Mar 2024 06:13:35 +0000 Subject: [PATCH 10/29] fmt,cxxrtl: add support for uppercase hex format. This is necessary for translating Python format strings in Amaranth. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 3 ++- kernel/fmt.cc | 11 ++++++++--- kernel/fmt.h | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index b1fc7e05c..b3a537435 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1034,6 +1034,7 @@ struct fmt_part { unsigned base; // = 10; bool signed_; // = false; bool plus; // = false; + bool hex_upper; // = false; // VLOG_TIME type bool realtime; // = false; @@ -1085,7 +1086,7 @@ struct fmt_part { 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]; + buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } std::reverse(buf.begin(), buf.end()); } else if (base == 10) { diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 336e3e2ab..6fdc18ad0 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -107,6 +107,10 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt[i] == 'h') { part.type = FmtPart::INTEGER; part.base = 16; + } else if (fmt[i] == 'H') { + part.type = FmtPart::INTEGER; + part.base = 16; + part.hex_upper = true; } else if (fmt[i] == 'c') { part.type = FmtPart::STRING; } else if (fmt[i] == 't') { @@ -197,7 +201,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case 2: fmt += 'b'; break; case 8: fmt += 'o'; break; case 10: fmt += 'd'; break; - case 16: fmt += 'h'; break; + case 16: fmt += part.hex_upper ? 'H' : 'h'; break; default: log_abort(); } if (part.plus) @@ -507,7 +511,7 @@ std::vector Fmt::emit_verilog() const case 2: fmt.str += 'b'; break; case 8: fmt.str += 'o'; break; case 10: fmt.str += 'd'; break; - case 16: fmt.str += 'h'; break; + case 16: fmt.str += 'h'; break; // treat uppercase hex as lowercase default: log_abort(); } break; @@ -617,6 +621,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function Date: Thu, 28 Mar 2024 06:59:23 +0000 Subject: [PATCH 11/29] fmt,cxxrtl: support `{,PLUS_,SPACE_}MINUS` integer formats. The first two were already supported with the `plus` boolean flag. The third one is a new specifier, which is allocated the ` ` character. In addition, `MINUS` is now allocated the `-` character, but old format where there is no `+`, `-`, or `-` in the respective position is also accepted for compatibility. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 13 +++++-- kernel/fmt.cc | 47 ++++++++++++++++++------- kernel/fmt.h | 6 +++- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index b3a537435..02050df58 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1033,7 +1033,11 @@ struct fmt_part { // INTEGER type unsigned base; // = 10; bool signed_; // = false; - bool plus; // = false; + enum { + MINUS = 0, + PLUS_MINUS = 1, + SPACE_MINUS = 2, + } sign; // = MINUS; bool hex_upper; // = false; // VLOG_TIME type @@ -1105,8 +1109,11 @@ struct fmt_part { buf += '0' + remainder.template trunc<4>().template get(); xval = quotient; } - if (negative || plus) - buf += negative ? '-' : '+'; + switch (sign) { + case MINUS: buf += negative ? "-" : ""; break; + case PLUS_MINUS: buf += negative ? "-" : "+"; break; + case SPACE_MINUS: buf += negative ? "-" : " "; break; + } std::reverse(buf.begin(), buf.end()); } else assert(false && "Unsupported base for fmt_part"); break; diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 6fdc18ad0..342b508d9 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -128,10 +128,20 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected end in format substitution"); if (part.type == FmtPart::INTEGER) { - if (fmt[i] == '+') { - part.plus = true; + if (fmt[i] == '-') { + part.sign = FmtPart::MINUS; if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); + } else if (fmt[i] == '+') { + part.sign = FmtPart::PLUS_MINUS; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } else if (fmt[i] == ' ') { + part.sign = FmtPart::SPACE_MINUS; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } else { + // also accept no sign character and treat like MINUS for compatibility } if (fmt[i] == 'u') @@ -204,8 +214,11 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case 16: fmt += part.hex_upper ? 'H' : 'h'; break; default: log_abort(); } - if (part.plus) - fmt += '+'; + switch (part.sign) { + case FmtPart::MINUS: fmt += '-'; break; + case FmtPart::PLUS_MINUS: fmt += '+'; break; + case FmtPart::SPACE_MINUS: fmt += ' '; break; + } fmt += part.signed_ ? 's' : 'u'; } else if (part.type == FmtPart::STRING) { fmt += 'c'; @@ -379,7 +392,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.justify = FmtPart::LEFT; } else if (fmt[i] == '+') { // always show sign; not in IEEE 1800-2017 or verilator but iverilog has it - part.plus = true; + part.sign = FmtPart::PLUS_MINUS; } else break; } if (i == fmt.size()) { @@ -442,7 +455,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik if (part.padding == '\0') part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' '; - if (part.type == FmtPart::INTEGER && part.base != 10 && part.plus) + if (part.type == FmtPart::INTEGER && part.base != 10 && part.sign != FmtPart::MINUS) log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); if (part.type == FmtPart::INTEGER && !has_leading_zero) @@ -495,8 +508,8 @@ std::vector Fmt::emit_verilog() const args.push_back(arg); fmt.str += '%'; - if (part.plus) - fmt.str += '+'; + if (part.sign == FmtPart::PLUS_MINUS || part.sign == FmtPart::SPACE_MINUS) + fmt.str += '+'; // treat space/minus as plus/minus if (part.justify == FmtPart::LEFT) fmt.str += '-'; if (part.width == 0) { @@ -553,7 +566,8 @@ std::vector Fmt::emit_verilog() const args.push_back(arg); fmt.str += '%'; - if (part.plus) + log_assert(part.sign == FmtPart::MINUS || part.sign == FmtPart::PLUS_MINUS); + if (part.sign == FmtPart::PLUS_MINUS) fmt.str += '+'; if (part.justify == FmtPart::LEFT) fmt.str += '-'; @@ -620,7 +634,13 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function Date: Thu, 28 Mar 2024 07:23:09 +0000 Subject: [PATCH 12/29] fmt,cxxrtl: add support for `NUMERIC` justification. Before this commit, the existing alignments were `LEFT` and `RIGHT`, which added the `padding` character to the right and left just before finishing formatting. However, if `padding == '0'` and the alignment is to the right, then the padding character (digit zero) was added after the sign, if one is present. After this commit, the special case for `padding == '0'` is removed, and the new justification `NUMERIC` adds the padding character like the justification `RIGHT`, except after the sign, if one is present. (Space, for the `SPACE_MINUS` sign mode, counts as the sign.) --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 5 +++-- kernel/fmt.cc | 23 +++++++++++++++++------ kernel/fmt.h | 1 + 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 02050df58..a98736012 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1026,6 +1026,7 @@ struct fmt_part { enum { RIGHT = 0, LEFT = 1, + NUMERIC = 2, } justify; // = RIGHT; char padding; // = '\0'; size_t width; // = 0; @@ -1131,9 +1132,9 @@ struct fmt_part { std::string str; assert(width == 0 || padding != '\0'); - if (justify == RIGHT && buf.size() < width) { + if (justify != LEFT && buf.size() < width) { size_t pad_width = width - buf.size(); - if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { + if (justify == NUMERIC && (buf.front() == '+' || buf.front() == '-' || buf.front() == ' ')) { str += buf.front(); buf.erase(0, 1); } diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 342b508d9..43f60b1d8 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -78,6 +78,8 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.justify = FmtPart::RIGHT; else if (fmt[i] == '<') part.justify = FmtPart::LEFT; + else if (fmt[i] == '=') + part.justify = FmtPart::NUMERIC; else log_assert(false && "Unexpected justification in format substitution"); if (++i == fmt.size()) @@ -201,6 +203,8 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { fmt += '>'; else if (part.justify == FmtPart::LEFT) fmt += '<'; + else if (part.justify == FmtPart::NUMERIC) + fmt += '='; else log_abort(); log_assert(part.width == 0 || part.padding != '\0'); fmt += part.padding != '\0' ? part.padding : ' '; @@ -452,8 +456,14 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); } - if (part.padding == '\0') - part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' '; + if (part.padding == '\0') { + if (has_leading_zero && part.justify == FmtPart::RIGHT) { + part.padding = '0'; + part.justify = FmtPart::NUMERIC; + } else { + part.padding = ' '; + } + } if (part.type == FmtPart::INTEGER && part.base != 10 && part.sign != FmtPart::MINUS) log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); @@ -626,8 +636,9 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function Date: Thu, 28 Mar 2024 07:55:46 +0000 Subject: [PATCH 13/29] fmt,cxxrtl: add `UNICHAR` format type. This format type is used to print an Unicode character (code point) as its UTF-8 serialization. To this end, two UTF-8 decoders (one for fmt, one for cxxrtl) are added for rendering. When converted to a Verilog format specifier, `UNICHAR` degrades to `%c` with the low 7 bits of the code point, which has equivalent behavior for inputs not exceeding ASCII. (SystemVerilog leaves source and display encodings completely undefined.) --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 24 +++++++++++-- kernel/fmt.cc | 47 +++++++++++++++++++++++-- kernel/fmt.h | 5 +-- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index a98736012..a67915346 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1013,13 +1013,14 @@ struct fmt_part { LITERAL = 0, INTEGER = 1, STRING = 2, - VLOG_TIME = 3, + UNICHAR = 3, + VLOG_TIME = 4, } type; // LITERAL type std::string str; - // INTEGER/STRING types + // INTEGER/STRING/UNICHAR types // + value val; // INTEGER/STRING/VLOG_TIME types @@ -1073,6 +1074,25 @@ struct fmt_part { break; } + case UNICHAR: { + uint32_t codepoint = val.template get(); + if (codepoint >= 0x10000) + buf += (char)(0xf0 | (codepoint >> 18)); + else if (codepoint >= 0x800) + buf += (char)(0xe0 | (codepoint >> 12)); + else if (codepoint >= 0x80) + buf += (char)(0xc0 | (codepoint >> 6)); + else + buf += (char)codepoint; + if (codepoint >= 0x10000) + buf += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + if (codepoint >= 0x800) + buf += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + if (codepoint >= 0x80) + buf += (char)(0x80 | ((codepoint >> 0) & 0x3f)); + break; + } + case INTEGER: { size_t width = Bits; if (base != 10) { diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 43f60b1d8..5bd4fd9c8 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -42,9 +42,9 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt.substr(i, 2) == "{{") { part.str += '{'; ++i; - } else if (fmt[i] == '}') + } else if (fmt[i] == '}') { log_assert(false && "Unexpected '}' in format string"); - else if (fmt[i] == '{') { + } else if (fmt[i] == '{') { if (!part.str.empty()) { part.type = FmtPart::LITERAL; parts.push_back(part); @@ -74,6 +74,12 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.sig = args.extract(0, arg_size); args.remove(0, arg_size); + if (fmt[i] == 'U') { + part.type = FmtPart::UNICHAR; + ++i; + goto success; + } + if (fmt[i] == '>') part.justify = FmtPart::RIGHT; else if (fmt[i] == '<') @@ -156,6 +162,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected end in format substitution"); } + success: if (fmt[i] != '}') log_assert(false && "Expected '}' after format substitution"); @@ -188,6 +195,11 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { } break; + case FmtPart::UNICHAR: + log_assert(part.sig.size() <= 32); + fmt += "{U}"; + break; + case FmtPart::VLOG_TIME: log_assert(part.sig.size() == 0); YS_FALLTHROUGH @@ -568,6 +580,16 @@ std::vector Fmt::emit_verilog() const break; } + case FmtPart::UNICHAR: { + VerilogFmtArg arg; + arg.type = VerilogFmtArg::INTEGER; + arg.sig = part.sig.extract(0, 7); // only ASCII + args.push_back(arg); + + fmt.str += "%c"; + break; + } + case FmtPart::VLOG_TIME: { VerilogFmtArg arg; arg.type = VerilogFmtArg::TIME; @@ -630,6 +652,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function= 0x10000) + str += (char)(0xf0 | (codepoint >> 18)); + else if (codepoint >= 0x800) + str += (char)(0xe0 | (codepoint >> 12)); + else if (codepoint >= 0x80) + str += (char)(0xc0 | (codepoint >> 6)); + else + str += (char)codepoint; + if (codepoint >= 0x10000) + str += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + if (codepoint >= 0x800) + str += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + if (codepoint >= 0x80) + str += (char)(0x80 | ((codepoint >> 0) & 0x3f)); + break; + } + case FmtPart::INTEGER: case FmtPart::STRING: case FmtPart::VLOG_TIME: { diff --git a/kernel/fmt.h b/kernel/fmt.h index 9dc6341ba..7fe6fd55e 100644 --- a/kernel/fmt.h +++ b/kernel/fmt.h @@ -56,13 +56,14 @@ struct FmtPart { LITERAL = 0, INTEGER = 1, STRING = 2, - VLOG_TIME = 3, + UNICHAR = 3, + VLOG_TIME = 4, } type; // LITERAL type std::string str; - // INTEGER/STRING types + // INTEGER/STRING/UNICHAR types RTLIL::SigSpec sig; // INTEGER/STRING/VLOG_TIME types From 7b94599162cf11713299ed1263b4723211d084d9 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 28 Mar 2024 08:33:29 +0000 Subject: [PATCH 14/29] fmt,cxxrtl: add option to print numeric base (`0x`, etc). The option is serialized to RTLIL as `#` (to match Python's and Rust's option with the same symbol), and sets the `show_base` flag. Because the flag is called `show_base` and not e.g. `alternate_format` (which is what Python and Rust call it), in addition to the prefixes `0x`, `0X`, `0o`, `0b`, the RTLIL option also prints the `0d` prefix. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 7 +++++++ kernel/fmt.cc | 13 +++++++++++++ kernel/fmt.h | 1 + 3 files changed, 21 insertions(+) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index a67915346..008401232 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1041,6 +1041,7 @@ struct fmt_part { SPACE_MINUS = 2, } sign; // = MINUS; bool hex_upper; // = false; + bool show_base; // = false; // VLOG_TIME type bool realtime; // = false; @@ -1103,6 +1104,8 @@ struct fmt_part { } if (base == 2) { + if (show_base) + buf += "0b"; for (size_t i = width; i > 0; i--) buf += (val.bit(i - 1) ? '1' : '0'); } else if (base == 8 || base == 16) { @@ -1113,6 +1116,8 @@ struct fmt_part { value |= val.bit(index + 3) << 3; buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } + if (show_base) + buf += (base == 16) ? (hex_upper ? "X0" : "x0") : "o0"; std::reverse(buf.begin(), buf.end()); } else if (base == 10) { bool negative = signed_ && val.is_neg(); @@ -1130,6 +1135,8 @@ struct fmt_part { buf += '0' + remainder.template trunc<4>().template get(); xval = quotient; } + if (show_base) + buf += "d0"; switch (sign) { case MINUS: buf += negative ? "-" : ""; break; case PLUS_MINUS: buf += negative ? "-" : "+"; break; diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 5bd4fd9c8..8f4e61722 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -152,6 +152,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { // also accept no sign character and treat like MINUS for compatibility } + if (fmt[i] == '#') { + part.show_base = true; + ++i; + } + if (fmt[i] == 'u') part.signed_ = false; else if (fmt[i] == 's') @@ -235,6 +240,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case FmtPart::PLUS_MINUS: fmt += '+'; break; case FmtPart::SPACE_MINUS: fmt += ' '; break; } + fmt += part.show_base ? "#" : ""; fmt += part.signed_ ? 's' : 'u'; } else if (part.type == FmtPart::STRING) { fmt += 'c'; @@ -676,6 +682,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function Date: Thu, 28 Mar 2024 08:55:26 +0000 Subject: [PATCH 15/29] fmt,cxxrtl: add option to group digits in numbers. The option is serialized to RTLIL as `_` (to match Python's option with the same symbol), and sets the `group` flag. This flag inserts an `_` symbol between each group of 3 digits (for decimal) or four digits (for binary, hex, and octal). --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 17 ++++++++++++--- kernel/fmt.cc | 29 +++++++++++++++++++++++-- kernel/fmt.h | 1 + 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 008401232..31085b745 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1042,6 +1042,7 @@ struct fmt_part { } sign; // = MINUS; bool hex_upper; // = false; bool show_base; // = false; + bool group; // = false; // VLOG_TIME type bool realtime; // = false; @@ -1104,13 +1105,19 @@ struct fmt_part { } if (base == 2) { + for (size_t index = 0; index < width; index++) { + if (group && index > 0 && index % 4 == 0) + buf += '_'; + buf += (val.bit(index) ? '1' : '0'); + } if (show_base) - buf += "0b"; - for (size_t i = width; i > 0; i--) - buf += (val.bit(i - 1) ? '1' : '0'); + buf += "b0"; + std::reverse(buf.begin(), buf.end()); } else if (base == 8 || base == 16) { size_t step = (base == 16) ? 4 : 3; for (size_t index = 0; index < width; index += step) { + if (group && index > 0 && index % (4 * step) == 0) + buf += '_'; 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; @@ -1126,7 +1133,10 @@ struct fmt_part { if (val.is_zero()) buf += '0'; value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); + size_t index = 0; while (!xval.is_zero()) { + if (group && index > 0 && index % 3 == 0) + buf += '_'; value<(Bits > 4 ? Bits : 4)> quotient, remainder; if (Bits >= 4) std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); @@ -1134,6 +1144,7 @@ struct fmt_part { std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); buf += '0' + remainder.template trunc<4>().template get(); xval = quotient; + index++; } if (show_base) buf += "d0"; diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 8f4e61722..f5793d796 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -156,6 +156,10 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.show_base = true; ++i; } + if (fmt[i] == '_') { + part.group = true; + ++i; + } if (fmt[i] == 'u') part.signed_ = false; @@ -241,6 +245,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case FmtPart::SPACE_MINUS: fmt += ' '; break; } fmt += part.show_base ? "#" : ""; + fmt += part.group ? "_" : ""; fmt += part.signed_ ? 's' : 'u'; } else if (part.type == FmtPart::STRING) { fmt += 'c'; @@ -683,6 +688,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function 0 && index % 4 == 0) + buf += '_'; + RTLIL::State bit = value[index]; + if (bit == State::Sx) + buf += 'x'; + else if (bit == State::Sz) + buf += 'z'; + else if (bit == State::S1) + buf += '1'; + else /* if (bit == State::S0) */ + buf += '0'; + } if (part.show_base) - buf += "0b"; - buf = value.as_string(); + buf += "b0"; + std::reverse(buf.begin(), buf.end()); } else if (part.base == 8 || part.base == 16) { size_t step = (part.base == 16) ? 4 : 3; for (size_t index = 0; index < (size_t)value.size(); index += step) { + if (part.group && index > 0 && index % (4 * step) == 0) + buf += '_'; RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index)); bool has_x = false, all_x = true, has_z = false, all_z = true; for (State bit : subvalue) { @@ -799,9 +820,13 @@ std::string Fmt::render() const log_assert(absvalue.is_fully_def()); if (absvalue.is_fully_zero()) buf += '0'; + size_t index = 0; while (!absvalue.is_fully_zero()) { + if (part.group && index > 0 && index % 3 == 0) + buf += '_'; buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int(); absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size()); + index++; } if (part.show_base) buf += "d0"; diff --git a/kernel/fmt.h b/kernel/fmt.h index 10b1711f1..2d4b24979 100644 --- a/kernel/fmt.h +++ b/kernel/fmt.h @@ -85,6 +85,7 @@ struct FmtPart { } sign = MINUS; bool hex_upper = false; bool show_base = false; + bool group = false; // VLOG_TIME type bool realtime = false; From ddf7b469559b976225fc35bea340383bcf5c5097 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 28 Mar 2024 09:45:10 +0000 Subject: [PATCH 16/29] fmt,cxxrtl: fix printing of non-decimal signed numbers. Also fix interaction of `NUMERIC` justification with `show_base`. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 67 ++++++++++------ kernel/fmt.cc | 101 ++++++++++++++---------- 2 files changed, 100 insertions(+), 68 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 31085b745..da107e037 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1058,6 +1058,7 @@ struct fmt_part { // 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; + std::string prefix; switch (type) { case LITERAL: return str; @@ -1096,24 +1097,38 @@ struct fmt_part { } case INTEGER: { + bool negative = signed_ && val.is_neg(); + if (negative) { + prefix = "-"; + val = val.neg(); + } else { + switch (sign) { + case MINUS: break; + case PLUS_MINUS: prefix = "+"; break; + case SPACE_MINUS: prefix = " "; break; + } + } + size_t width = Bits; if (base != 10) { - width = 0; + width = 1; for (size_t index = 0; index < Bits; index++) if (val.bit(index)) width = index + 1; } if (base == 2) { + if (show_base) + prefix += "0b"; for (size_t index = 0; index < width; index++) { if (group && index > 0 && index % 4 == 0) buf += '_'; buf += (val.bit(index) ? '1' : '0'); } - if (show_base) - buf += "b0"; std::reverse(buf.begin(), buf.end()); } else if (base == 8 || base == 16) { + if (show_base) + prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o"; size_t step = (base == 16) ? 4 : 3; for (size_t index = 0; index < width; index += step) { if (group && index > 0 && index % (4 * step) == 0) @@ -1123,13 +1138,10 @@ struct fmt_part { value |= val.bit(index + 3) << 3; buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } - if (show_base) - buf += (base == 16) ? (hex_upper ? "X0" : "x0") : "o0"; std::reverse(buf.begin(), buf.end()); } else if (base == 10) { - bool negative = signed_ && val.is_neg(); - if (negative) - val = val.neg(); + if (show_base) + prefix += "0d"; if (val.is_zero()) buf += '0'; value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); @@ -1146,13 +1158,6 @@ struct fmt_part { xval = quotient; index++; } - if (show_base) - buf += "d0"; - switch (sign) { - case MINUS: buf += negative ? "-" : ""; break; - case PLUS_MINUS: buf += negative ? "-" : "+"; break; - case SPACE_MINUS: buf += negative ? "-" : " "; break; - } std::reverse(buf.begin(), buf.end()); } else assert(false && "Unsupported base for fmt_part"); break; @@ -1170,17 +1175,29 @@ struct fmt_part { std::string str; assert(width == 0 || padding != '\0'); - if (justify != LEFT && buf.size() < width) { - size_t pad_width = width - buf.size(); - if (justify == NUMERIC && (buf.front() == '+' || buf.front() == '-' || buf.front() == ' ')) { - str += buf.front(); - buf.erase(0, 1); - } - str += std::string(pad_width, padding); + if (prefix.size() + buf.size() < width) { + size_t pad_width = width - prefix.size() - buf.size(); + switch (justify) { + case LEFT: + str += prefix; + str += buf; + str += std::string(pad_width, padding); + break; + case RIGHT: + str += std::string(pad_width, padding); + str += prefix; + str += buf; + break; + case NUMERIC: + str += prefix; + str += std::string(pad_width, padding); + str += buf; + break; + } + } else { + str += prefix; + str += buf; } - str += buf; - if (justify == LEFT && buf.size() < width) - str += std::string(width - buf.size(), padding); return str; } }; diff --git a/kernel/fmt.cc b/kernel/fmt.cc index f5793d796..f70f6d390 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -491,6 +491,8 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik if (part.type == FmtPart::INTEGER && part.base != 10 && part.sign != FmtPart::MINUS) log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + if (part.base != 10) + part.signed_ = false; if (part.type == FmtPart::INTEGER && !has_leading_zero) apply_verilog_automatic_sizing_and_add(part); else @@ -731,11 +733,34 @@ std::string Fmt::render() const case FmtPart::STRING: case FmtPart::VLOG_TIME: { std::string buf; + std::string prefix; if (part.type == FmtPart::INTEGER) { RTLIL::Const value = part.sig.as_const(); + bool has_x = false, all_x = true, has_z = false, all_z = true; + for (State bit : value) { + if (bit == State::Sx) + has_x = true; + else + all_x = false; + if (bit == State::Sz) + has_z = true; + else + all_z = false; + } + + if (!has_z && !has_x && part.signed_ && value[value.size() - 1]) { + prefix = "-"; + value = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); + } else { + switch (part.sign) { + case FmtPart::MINUS: break; + case FmtPart::PLUS_MINUS: prefix = "+"; break; + case FmtPart::SPACE_MINUS: prefix = " "; break; + } + } if (part.base != 10) { - size_t minimum_size = 0; + size_t minimum_size = 1; for (size_t index = 0; index < (size_t)value.size(); index++) if (value[index] != State::S0) minimum_size = index + 1; @@ -743,6 +768,8 @@ std::string Fmt::render() const } if (part.base == 2) { + if (part.show_base) + prefix += "0b"; for (size_t index = 0; index < (size_t)value.size(); index++) { if (part.group && index > 0 && index % 4 == 0) buf += '_'; @@ -756,10 +783,10 @@ std::string Fmt::render() const else /* if (bit == State::S0) */ buf += '0'; } - if (part.show_base) - buf += "b0"; std::reverse(buf.begin(), buf.end()); } else if (part.base == 8 || part.base == 16) { + if (part.show_base) + prefix += (part.base == 16) ? (part.hex_upper ? "0X" : "0x") : "0o"; size_t step = (part.base == 16) ? 4 : 3; for (size_t index = 0; index < (size_t)value.size(); index += step) { if (part.group && index > 0 && index % (4 * step) == 0) @@ -787,21 +814,10 @@ std::string Fmt::render() const else buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()]; } - if (part.show_base) - buf += (part.base == 16) ? (part.hex_upper ? "X0" : "x0") : "o0"; std::reverse(buf.begin(), buf.end()); } else if (part.base == 10) { - bool has_x = false, all_x = true, has_z = false, all_z = true; - for (State bit : value) { - if (bit == State::Sx) - has_x = true; - else - all_x = false; - if (bit == State::Sz) - has_z = true; - else - all_z = false; - } + if (part.show_base) + prefix += "0d"; if (all_x) buf += 'x'; else if (all_z) @@ -811,30 +827,17 @@ std::string Fmt::render() const else if (has_z) buf += 'Z'; else { - bool negative = part.signed_ && value[value.size() - 1]; - RTLIL::Const absvalue; - if (negative) - absvalue = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); - else - absvalue = value; - log_assert(absvalue.is_fully_def()); - if (absvalue.is_fully_zero()) + log_assert(value.is_fully_def()); + if (value.is_fully_zero()) buf += '0'; size_t index = 0; - while (!absvalue.is_fully_zero()) { + while (!value.is_fully_zero()) { if (part.group && index > 0 && index % 3 == 0) buf += '_'; - buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int(); - absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size()); + buf += '0' + RTLIL::const_mod(value, 10, false, false, 4).as_int(); + value = RTLIL::const_div(value, 10, false, false, value.size()); index++; } - if (part.show_base) - buf += "d0"; - switch (part.sign) { - case FmtPart::MINUS: buf += negative ? "-" : ""; break; - case FmtPart::PLUS_MINUS: buf += negative ? "-" : "+"; break; - case FmtPart::SPACE_MINUS: buf += negative ? "-" : " "; break; - } std::reverse(buf.begin(), buf.end()); } } else log_abort(); @@ -846,17 +849,29 @@ std::string Fmt::render() const } log_assert(part.width == 0 || part.padding != '\0'); - if (part.justify != FmtPart::LEFT && buf.size() < part.width) { - size_t pad_width = part.width - buf.size(); - if (part.justify == FmtPart::NUMERIC && (!buf.empty() && (buf.front() == '+' || buf.front() == '-' || buf.front() == ' '))) { - str += buf.front(); - buf.erase(0, 1); + if (prefix.size() + buf.size() < part.width) { + size_t pad_width = part.width - prefix.size() - buf.size(); + switch (part.justify) { + case FmtPart::LEFT: + str += prefix; + str += buf; + str += std::string(pad_width, part.padding); + break; + case FmtPart::RIGHT: + str += std::string(pad_width, part.padding); + str += prefix; + str += buf; + break; + case FmtPart::NUMERIC: + str += prefix; + str += std::string(pad_width, part.padding); + str += buf; + break; } - str += std::string(pad_width, part.padding); + } else { + str += prefix; + str += buf; } - str += buf; - if (part.justify == FmtPart::LEFT && buf.size() < part.width) - str += std::string(part.width - buf.size(), part.padding); break; } } From 27cb4c52b4cffd049dc825865852af75867fce11 Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 28 Mar 2024 09:52:59 +0000 Subject: [PATCH 17/29] fmt: allow padding characters other than `'0'` and `' '`. When converted to Verilog, padding characters are replaced with one of these two. Otherwise padding is performed with exactly that character. --- kernel/fmt.cc | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/kernel/fmt.cc b/kernel/fmt.cc index f70f6d390..43d8feddf 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -91,10 +91,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); - if (fmt[i] == '0' || fmt[i] == ' ') - part.padding = fmt[i]; - else - log_assert(false && "Unexpected padding in format substitution"); + part.padding = fmt[i]; if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); @@ -550,7 +547,6 @@ std::vector Fmt::emit_verilog() const if (part.width == 0) { fmt.str += '0'; } else if (part.width > 0) { - log_assert(part.padding == ' ' || part.padding == '0'); if (part.base != 10 || part.padding == '0') fmt.str += '0'; fmt.str += std::to_string(part.width); @@ -576,7 +572,6 @@ std::vector Fmt::emit_verilog() const fmt.str += '-'; if (part.sig.size() == 8) { if (part.width > 0) { - log_assert(part.padding == '0' || part.padding == ' '); if (part.padding == '0') fmt.str += part.padding; fmt.str += std::to_string(part.width); @@ -585,7 +580,6 @@ std::vector Fmt::emit_verilog() const } else { log_assert(part.sig.size() % 8 == 0); if (part.width > 0) { - log_assert(part.padding == ' '); // no zero padding fmt.str += std::to_string(part.width); } fmt.str += 's'; @@ -616,7 +610,6 @@ std::vector Fmt::emit_verilog() const fmt.str += '+'; if (part.justify == FmtPart::LEFT) fmt.str += '-'; - log_assert(part.padding == ' ' || part.padding == '0'); if (part.padding == '0' && part.width > 0) fmt.str += '0'; fmt.str += std::to_string(part.width); From 94170388a9acd639474387de14dfe7145d8436ee Mon Sep 17 00:00:00 2001 From: Catherine Date: Thu, 28 Mar 2024 10:06:18 +0000 Subject: [PATCH 18/29] fmt: if enabled, group padding zeroes. Before this commit, the combination of `_` and `0` format characters would produce a result like `000000001010_1010`. After this commit, it would be `0000_0000_1010_1010`. This has a slight quirk where a format like `{:020_b}` results in the output `0_0000_0000_1010_1010`, which is one character longer than requested. Python has the same behavior, and it's not clear what would be strictly speaking correct, so Python behavior is implemented. --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 22 ++++++++++++++-------- kernel/fmt.cc | 12 +++++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index da107e037..8546a8411 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1109,28 +1109,27 @@ struct fmt_part { } } - size_t width = Bits; + size_t val_width = Bits; if (base != 10) { - width = 1; + val_width = 1; for (size_t index = 0; index < Bits; index++) if (val.bit(index)) - width = index + 1; + val_width = index + 1; } if (base == 2) { if (show_base) prefix += "0b"; - for (size_t index = 0; index < width; index++) { + for (size_t index = 0; index < val_width; index++) { if (group && index > 0 && index % 4 == 0) buf += '_'; buf += (val.bit(index) ? '1' : '0'); } - std::reverse(buf.begin(), buf.end()); } else if (base == 8 || base == 16) { if (show_base) prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o"; size_t step = (base == 16) ? 4 : 3; - for (size_t index = 0; index < width; index += step) { + for (size_t index = 0; index < val_width; index += step) { if (group && index > 0 && index % (4 * step) == 0) buf += '_'; uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); @@ -1138,7 +1137,6 @@ struct fmt_part { value |= val.bit(index + 3) << 3; buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } - std::reverse(buf.begin(), buf.end()); } else if (base == 10) { if (show_base) prefix += "0d"; @@ -1158,8 +1156,16 @@ struct fmt_part { xval = quotient; index++; } - std::reverse(buf.begin(), buf.end()); } else assert(false && "Unsupported base for fmt_part"); + if (justify == NUMERIC && group && padding == '0') { + int group_size = base == 10 ? 3 : 4; + while (prefix.size() + buf.size() < width) { + if (buf.size() % (group_size + 1) == group_size) + buf += '_'; + buf += '0'; + } + } + std::reverse(buf.begin(), buf.end()); break; } diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 43d8feddf..cbf2d12e9 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -776,7 +776,6 @@ std::string Fmt::render() const else /* if (bit == State::S0) */ buf += '0'; } - std::reverse(buf.begin(), buf.end()); } else if (part.base == 8 || part.base == 16) { if (part.show_base) prefix += (part.base == 16) ? (part.hex_upper ? "0X" : "0x") : "0o"; @@ -807,7 +806,6 @@ std::string Fmt::render() const else buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()]; } - std::reverse(buf.begin(), buf.end()); } else if (part.base == 10) { if (part.show_base) prefix += "0d"; @@ -831,9 +829,17 @@ std::string Fmt::render() const value = RTLIL::const_div(value, 10, false, false, value.size()); index++; } - std::reverse(buf.begin(), buf.end()); } } else log_abort(); + if (part.justify == FmtPart::NUMERIC && part.group && part.padding == '0') { + int group_size = part.base == 10 ? 3 : 4; + while (prefix.size() + buf.size() < part.width) { + if (buf.size() % (group_size + 1) == group_size) + buf += '_'; + buf += '0'; + } + } + std::reverse(buf.begin(), buf.end()); } else if (part.type == FmtPart::STRING) { buf = part.sig.as_const().decode_string(); } else if (part.type == FmtPart::VLOG_TIME) { From cb077101622683716c93707be4c89106ae161359 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 2 Apr 2024 11:26:58 +0000 Subject: [PATCH 19/29] write_verilog: only warn on processes with sync rules. Processes without sync rules correspond to simple decision trees that directly correspond to `always @*` or `always_comb` blocks in Verilog, and do not need a warning. This removes the need to suppress warnings during the RTLIL-to-Verilog conversion performed by Amaranth. --- backends/verilog/verilog_backend.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 05b7c6c40..a09ae984b 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1070,7 +1070,7 @@ 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)); @@ -2276,11 +2276,15 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) active_initdata[sig[i]] = val[i]; } - if (!module->processes.empty()) - log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" - "can't always be mapped directly to Verilog always blocks. Unintended\n" - "changes in simulation behavior are possible! Use \"proc\" to convert\n" - "processes to logic networks and registers.\n", log_id(module)); + bool has_sync_rules = false; + for (auto process : module->processes) + if (!process.second->syncs.empty()) + has_sync_rules = true; + if (has_sync_rules) + log_warning("Module %s contains RTLIL processes with sync rules. Such RTLIL " + "processes can't always be mapped directly to Verilog always blocks. " + "unintended changes in simulation behavior are possible! Use \"proc\" " + "to convert processes to logic networks and registers.\n", log_id(module)); f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) From d8687e87b1f7286636d50c42dd281eb3e93e4898 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 2 Apr 2024 15:58:06 +0200 Subject: [PATCH 20/29] kernel: Avoid including files outside include guards This adjusts the way the headers kernel/{yosys,rtlil,register,log}.h include each other to avoid the need of including headers outside of include guards as well as avoiding the inclusion of rtlil.h in the middle of yosys.h with rtlil.h depending on the prefix of yosys.h, and the suffix of yosys.h depending on rtlil.h. To do this I moved some of the declaration in yosys.h into a new header yosys_common.h. I'm not sure if that is strictly necessary. Including any of these files still results in the declarations of all these headers being included, so this shouldn't be a breaking change for any passes or external plugins. My main motivation for this is that ccls's (clang based language server) include guard handling gets confused by the previous way the includes were done. It often ends up treating the include guard as a generic disabled preprocessor conditional, breaking navigation and highlighting for the core RTLIL data structures. Additionally I think avoiding cyclic includes in the middle of header files that depend on includes being outside of include guards will also be less confusing for developers reading the code, not only for tools like ccls. --- Makefile | 1 + kernel/log.h | 6 +- kernel/register.h | 5 +- kernel/rtlil.h | 5 +- kernel/yosys.h | 346 +------------------------------------- kernel/yosys_common.h | 379 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 391 insertions(+), 351 deletions(-) create mode 100644 kernel/yosys_common.h diff --git a/Makefile b/Makefile index ca229ca4a..b62b78fb1 100644 --- a/Makefile +++ b/Makefile @@ -629,6 +629,7 @@ $(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/yosys.h)) +$(eval $(call add_include_file,kernel/yosys_common.h)) $(eval $(call add_include_file,kernel/yw.h)) $(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) diff --git a/kernel/log.h b/kernel/log.h index e4f06c69d..53aae58c6 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -17,11 +17,11 @@ * */ -#include "kernel/yosys.h" - #ifndef LOG_H #define LOG_H +#include "kernel/yosys_common.h" + #include #include @@ -449,4 +449,6 @@ void log_dump_args_worker(const char *p, T first, Args ... args) YOSYS_NAMESPACE_END +#include "kernel/yosys.h" + #endif diff --git a/kernel/register.h b/kernel/register.h index 08ce4b287..25ea9f232 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -17,11 +17,12 @@ * */ -#include "kernel/yosys.h" - #ifndef REGISTER_H #define REGISTER_H +#include "kernel/yosys_common.h" +#include "kernel/yosys.h" + YOSYS_NAMESPACE_BEGIN struct Pass diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 40422dce5..f9da29495 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -17,11 +17,12 @@ * */ -#include "kernel/yosys.h" - #ifndef RTLIL_H #define RTLIL_H +#include "kernel/yosys_common.h" +#include "kernel/yosys.h" + YOSYS_NAMESPACE_BEGIN namespace RTLIL diff --git a/kernel/yosys.h b/kernel/yosys.h index 0a4641d18..5922d9ab7 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -39,323 +39,7 @@ #ifndef YOSYS_H #define YOSYS_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef WITH_PYTHON -#include -#endif - -#ifndef _YOSYS_ -# error It looks like you are trying to build Yosys without the config defines set. \ - When building Yosys with a custom make system, make sure you set all the \ - defines the Yosys Makefile would set for your build configuration. -#endif - -#ifdef YOSYS_ENABLE_TCL -# include -# ifdef YOSYS_MXE_HACKS -extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); -extern Tcl_Interp *Tcl_CreateInterp(void); -extern void Tcl_Preserve(ClientData data); -extern void Tcl_Release(ClientData clientData); -extern int Tcl_InterpDeleted(Tcl_Interp *interp); -extern void Tcl_DeleteInterp(Tcl_Interp *interp); -extern int Tcl_Eval(Tcl_Interp *interp, const char *script); -extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); -extern void Tcl_Finalize(void); -extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); -extern const char *Tcl_GetStringResult(Tcl_Interp *interp); -extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length); -extern Tcl_Obj *Tcl_NewIntObj(int intValue); -extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); -extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); -# endif -# undef CONST -# undef INLINE -#endif - -#ifdef _WIN32 -# undef NOMINMAX -# define NOMINMAX 1 -# undef YY_NO_UNISTD_H -# define YY_NO_UNISTD_H 1 - -# include -# include -# include - -# define strtok_r strtok_s -# define strdup _strdup -# define snprintf _snprintf -# define getcwd _getcwd -# define mkdir _mkdir -# define popen _popen -# define pclose _pclose - -# ifndef __MINGW32__ -# define PATH_MAX MAX_PATH -# define isatty _isatty -# define fileno _fileno -# endif - -// The following defines conflict with our identifiers: -# undef CONST -// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc -# undef TRANSPARENT -#endif - -#ifndef PATH_MAX -# define PATH_MAX 4096 -#endif - -#define YOSYS_NAMESPACE Yosys -#define PRIVATE_NAMESPACE_BEGIN namespace { -#define PRIVATE_NAMESPACE_END } -#define YOSYS_NAMESPACE_BEGIN namespace Yosys { -#define YOSYS_NAMESPACE_END } -#define YOSYS_NAMESPACE_PREFIX Yosys:: -#define USING_YOSYS_NAMESPACE using namespace Yosys; - -#if defined(__GNUC__) || defined(__clang__) -# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) -#elif defined(_MSC_VER) -# define YS_ATTRIBUTE(...) -#else -# define YS_ATTRIBUTE(...) -#endif - -#if defined(__GNUC__) || defined(__clang__) -# define YS_MAYBE_UNUSED __attribute__((__unused__)) -#else -# define YS_MAYBE_UNUSED -#endif - -#if __cplusplus >= 201703L -# define YS_FALLTHROUGH [[fallthrough]]; -#elif defined(__clang__) -# define YS_FALLTHROUGH [[clang::fallthrough]]; -#elif defined(__GNUC__) -# define YS_FALLTHROUGH [[gnu::fallthrough]]; -#else -# define YS_FALLTHROUGH -#endif - -YOSYS_NAMESPACE_BEGIN - -// Note: All headers included in hashlib.h must be included -// outside of YOSYS_NAMESPACE before this or bad things will happen. -#ifdef HASHLIB_H -# undef HASHLIB_H -# include "kernel/hashlib.h" -#else -# include "kernel/hashlib.h" -# undef HASHLIB_H -#endif - -using std::vector; -using std::string; -using std::tuple; -using std::pair; - -using std::make_tuple; -using std::make_pair; -using std::get; -using std::min; -using std::max; - -// A primitive shared string implementation that does not -// move its .c_str() when the object is copied or moved. -struct shared_str { - std::shared_ptr content; - shared_str() { } - shared_str(string s) { content = std::shared_ptr(new string(s)); } - shared_str(const char *s) { content = std::shared_ptr(new string(s)); } - const char *c_str() const { return content->c_str(); } - const string &str() const { return *content; } - bool operator==(const shared_str &other) const { return *content == *other.content; } - unsigned int hash() const { return hashlib::hash_ops::hash(*content); } -}; - -using hashlib::mkhash; -using hashlib::mkhash_init; -using hashlib::mkhash_add; -using hashlib::mkhash_xorshift; -using hashlib::hash_ops; -using hashlib::hash_cstr_ops; -using hashlib::hash_ptr_ops; -using hashlib::hash_obj_ops; -using hashlib::dict; -using hashlib::idict; -using hashlib::pool; -using hashlib::mfp; - -namespace RTLIL { - struct IdString; - struct Const; - struct SigBit; - struct SigSpec; - struct Wire; - struct Cell; - struct Memory; - struct Process; - struct Module; - struct Design; - struct Monitor; - enum State : unsigned char; -} - -namespace AST { - struct AstNode; -} - -using RTLIL::IdString; -using RTLIL::Const; -using RTLIL::SigBit; -using RTLIL::SigSpec; -using RTLIL::Wire; -using RTLIL::Cell; -using RTLIL::Module; -using RTLIL::Design; - -namespace hashlib { - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; -} - -void memhasher_on(); -void memhasher_off(); -void memhasher_do(); - -extern bool memhasher_active; -inline void memhasher() { if (memhasher_active) memhasher_do(); } - -void yosys_banner(); -int ceil_log2(int x) YS_ATTRIBUTE(const); - -inline std::string vstringf(const char *fmt, va_list ap) -{ - // For the common case of strings shorter than 128, save a heap - // allocation by using a stack allocated buffer. - const int kBufSize = 128; - char buf[kBufSize]; - buf[0] = '\0'; - va_list apc; - va_copy(apc, ap); - int n = vsnprintf(buf, kBufSize, fmt, apc); - va_end(apc); - if (n < kBufSize) - return std::string(buf); - - std::string string; - char *str = NULL; -#if defined(_WIN32 )|| defined(__CYGWIN__) - int sz = 2 * kBufSize, rc; - while (1) { - va_copy(apc, ap); - str = (char*)realloc(str, sz); - rc = vsnprintf(str, sz, fmt, apc); - va_end(apc); - if (rc >= 0 && rc < sz) - break; - sz *= 2; - } - if (str != NULL) { - string = str; - free(str); - } - return string; -#else - if (vasprintf(&str, fmt, ap) < 0) - str = NULL; - if (str != NULL) { - string = str; - free(str); - } - return string; -#endif -} - -std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); - -inline std::string stringf(const char *fmt, ...) -{ - std::string string; - va_list ap; - - va_start(ap, fmt); - string = vstringf(fmt, ap); - va_end(ap); - - return string; -} - -int readsome(std::istream &f, char *s, int n); -std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); -std::vector split_tokens(const std::string &text, const char *sep = " \t\r\n"); -bool patmatch(const char *pattern, const char *string); -#if !defined(YOSYS_DISABLE_SPAWN) -int run_command(const std::string &command, std::function process_line = std::function()); -#endif -std::string get_base_tmpdir(); -std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); -std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); -bool check_file_exists(std::string filename, bool is_exec = false); -bool check_directory_exists(const std::string& dirname); -bool is_absolute_path(std::string filename); -void remove_directory(std::string dirname); -bool create_directory(const std::string& dirname); -std::string escape_filename_spaces(const std::string& filename); - -template int GetSize(const T &obj) { return obj.size(); } -inline int GetSize(RTLIL::Wire *wire); - -extern int autoidx; -extern int yosys_xtrace; - -YOSYS_NAMESPACE_END +#include "kernel/yosys_common.h" #include "kernel/log.h" #include "kernel/rtlil.h" @@ -363,14 +47,6 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN -using RTLIL::State; -using RTLIL::SigChunk; -using RTLIL::SigSig; - -namespace hashlib { - template<> struct hash_ops : hash_ops {}; -} - void yosys_setup(); #ifdef WITH_PYTHON @@ -385,26 +61,6 @@ Tcl_Interp *yosys_get_tcl_interp(); extern RTLIL::Design *yosys_design; -RTLIL::IdString new_id(std::string file, int line, std::string func); -RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix); - -#define NEW_ID \ - YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__) -#define NEW_ID_SUFFIX(suffix) \ - YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) - -// Create a statically allocated IdString object, using for example ID::A or ID($add). -// -// Recipe for Converting old code that is using conversion of strings like ID::A and -// "$add" for creating IdStrings: Run below SED command on the .cc file and then use for -// example "meld foo.cc foo.cc.orig" to manually compile errors, if necessary. -// -// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' -// -#define ID(_id) ([]() { const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \ - static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })() -namespace ID = RTLIL::ID; - RTLIL::Design *yosys_get_design(); std::string proc_self_dirname(); std::string proc_share_dirname(); diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h new file mode 100644 index 000000000..9f1bc2f58 --- /dev/null +++ b/kernel/yosys_common.h @@ -0,0 +1,379 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef YOSYS_COMMON_H +#define YOSYS_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_PYTHON +#include +#endif + +#ifndef _YOSYS_ +# error It looks like you are trying to build Yosys without the config defines set. \ + When building Yosys with a custom make system, make sure you set all the \ + defines the Yosys Makefile would set for your build configuration. +#endif + +#ifdef YOSYS_ENABLE_TCL +# include +# ifdef YOSYS_MXE_HACKS +extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); +extern Tcl_Interp *Tcl_CreateInterp(void); +extern void Tcl_Preserve(ClientData data); +extern void Tcl_Release(ClientData clientData); +extern int Tcl_InterpDeleted(Tcl_Interp *interp); +extern void Tcl_DeleteInterp(Tcl_Interp *interp); +extern int Tcl_Eval(Tcl_Interp *interp, const char *script); +extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); +extern void Tcl_Finalize(void); +extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); +extern const char *Tcl_GetStringResult(Tcl_Interp *interp); +extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length); +extern Tcl_Obj *Tcl_NewIntObj(int intValue); +extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); +extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); +# endif +# undef CONST +# undef INLINE +#endif + +#ifdef _WIN32 +# undef NOMINMAX +# define NOMINMAX 1 +# undef YY_NO_UNISTD_H +# define YY_NO_UNISTD_H 1 + +# include +# include +# include + +# define strtok_r strtok_s +# define strdup _strdup +# define snprintf _snprintf +# define getcwd _getcwd +# define mkdir _mkdir +# define popen _popen +# define pclose _pclose + +# ifndef __MINGW32__ +# define PATH_MAX MAX_PATH +# define isatty _isatty +# define fileno _fileno +# endif + +// The following defines conflict with our identifiers: +# undef CONST +// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc +# undef TRANSPARENT +#endif + +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + + +#define YOSYS_NAMESPACE Yosys +#define PRIVATE_NAMESPACE_BEGIN namespace { +#define PRIVATE_NAMESPACE_END } +#define YOSYS_NAMESPACE_BEGIN namespace Yosys { +#define YOSYS_NAMESPACE_END } +#define YOSYS_NAMESPACE_PREFIX Yosys:: +#define USING_YOSYS_NAMESPACE using namespace Yosys; + +#if defined(__GNUC__) || defined(__clang__) +# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) +#elif defined(_MSC_VER) +# define YS_ATTRIBUTE(...) +#else +# define YS_ATTRIBUTE(...) +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define YS_MAYBE_UNUSED __attribute__((__unused__)) +#else +# define YS_MAYBE_UNUSED +#endif + +#if __cplusplus >= 201703L +# define YS_FALLTHROUGH [[fallthrough]]; +#elif defined(__clang__) +# define YS_FALLTHROUGH [[clang::fallthrough]]; +#elif defined(__GNUC__) +# define YS_FALLTHROUGH [[gnu::fallthrough]]; +#else +# define YS_FALLTHROUGH +#endif + + +YOSYS_NAMESPACE_BEGIN + +// Note: All headers included in hashlib.h must be included +// outside of YOSYS_NAMESPACE before this or bad things will happen. +#ifdef HASHLIB_H +# undef HASHLIB_H +# include "kernel/hashlib.h" +#else +# include "kernel/hashlib.h" +# undef HASHLIB_H +#endif + + +using std::vector; +using std::string; +using std::tuple; +using std::pair; + +using std::make_tuple; +using std::make_pair; +using std::get; +using std::min; +using std::max; + +// A primitive shared string implementation that does not +// move its .c_str() when the object is copied or moved. +struct shared_str { + std::shared_ptr content; + shared_str() { } + shared_str(string s) { content = std::shared_ptr(new string(s)); } + shared_str(const char *s) { content = std::shared_ptr(new string(s)); } + const char *c_str() const { return content->c_str(); } + const string &str() const { return *content; } + bool operator==(const shared_str &other) const { return *content == *other.content; } + unsigned int hash() const { return hashlib::hash_ops::hash(*content); } +}; + +using hashlib::mkhash; +using hashlib::mkhash_init; +using hashlib::mkhash_add; +using hashlib::mkhash_xorshift; +using hashlib::hash_ops; +using hashlib::hash_cstr_ops; +using hashlib::hash_ptr_ops; +using hashlib::hash_obj_ops; +using hashlib::dict; +using hashlib::idict; +using hashlib::pool; +using hashlib::mfp; + +namespace RTLIL { + struct IdString; + struct Const; + struct SigBit; + struct SigSpec; + struct Wire; + struct Cell; + struct Memory; + struct Process; + struct Module; + struct Design; + struct Monitor; + struct Selection; + struct SigChunk; + enum State : unsigned char; + + typedef std::pair SigSig; + + namespace ID {} +} + +namespace AST { + struct AstNode; +} + +using RTLIL::IdString; +using RTLIL::Const; +using RTLIL::SigBit; +using RTLIL::SigSpec; +using RTLIL::Wire; +using RTLIL::Cell; +using RTLIL::Module; +using RTLIL::Design; + +using RTLIL::State; +using RTLIL::SigChunk; +using RTLIL::SigSig; + +namespace hashlib { + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; +} + +void memhasher_on(); +void memhasher_off(); +void memhasher_do(); + +extern bool memhasher_active; +inline void memhasher() { if (memhasher_active) memhasher_do(); } + +void yosys_banner(); +int ceil_log2(int x) YS_ATTRIBUTE(const); + +inline std::string vstringf(const char *fmt, va_list ap) +{ + // For the common case of strings shorter than 128, save a heap + // allocation by using a stack allocated buffer. + const int kBufSize = 128; + char buf[kBufSize]; + buf[0] = '\0'; + va_list apc; + va_copy(apc, ap); + int n = vsnprintf(buf, kBufSize, fmt, apc); + va_end(apc); + if (n < kBufSize) + return std::string(buf); + + std::string string; + char *str = NULL; +#if defined(_WIN32 )|| defined(__CYGWIN__) + int sz = 2 * kBufSize, rc; + while (1) { + va_copy(apc, ap); + str = (char*)realloc(str, sz); + rc = vsnprintf(str, sz, fmt, apc); + va_end(apc); + if (rc >= 0 && rc < sz) + break; + sz *= 2; + } + if (str != NULL) { + string = str; + free(str); + } + return string; +#else + if (vasprintf(&str, fmt, ap) < 0) + str = NULL; + if (str != NULL) { + string = str; + free(str); + } + return string; +#endif +} + +std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); + +inline std::string stringf(const char *fmt, ...) +{ + std::string string; + va_list ap; + + va_start(ap, fmt); + string = vstringf(fmt, ap); + va_end(ap); + + return string; +} + +int readsome(std::istream &f, char *s, int n); +std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); +std::vector split_tokens(const std::string &text, const char *sep = " \t\r\n"); +bool patmatch(const char *pattern, const char *string); +#if !defined(YOSYS_DISABLE_SPAWN) +int run_command(const std::string &command, std::function process_line = std::function()); +#endif +std::string get_base_tmpdir(); +std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); +std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); +bool check_file_exists(std::string filename, bool is_exec = false); +bool check_directory_exists(const std::string& dirname); +bool is_absolute_path(std::string filename); +void remove_directory(std::string dirname); +bool create_directory(const std::string& dirname); +std::string escape_filename_spaces(const std::string& filename); + +template int GetSize(const T &obj) { return obj.size(); } +inline int GetSize(RTLIL::Wire *wire); + +extern int autoidx; +extern int yosys_xtrace; + +RTLIL::IdString new_id(std::string file, int line, std::string func); +RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix); + +#define NEW_ID \ + YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__) +#define NEW_ID_SUFFIX(suffix) \ + YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) + +// Create a statically allocated IdString object, using for example ID::A or ID($add). +// +// Recipe for Converting old code that is using conversion of strings like ID::A and +// "$add" for creating IdStrings: Run below SED command on the .cc file and then use for +// example "meld foo.cc foo.cc.orig" to manually compile errors, if necessary. +// +// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' +// +#define ID(_id) ([]() { const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \ + static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })() +namespace ID = RTLIL::ID; + +namespace hashlib { + template<> struct hash_ops : hash_ops {}; +} + + +YOSYS_NAMESPACE_END + +#endif From 040605b047e425341a484df3f2f986977834be10 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Apr 2024 00:15:49 +0000 Subject: [PATCH 21/29] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ca229ca4a..e31b72261 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ LIBS += -lrt endif endif -YOSYS_VER := 0.39+149 +YOSYS_VER := 0.39+163 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From d9a4a423899d8c3d90304440761e47d4664211db Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 3 Apr 2024 08:01:58 +0000 Subject: [PATCH 22/29] write_verilog: don't `assign` to a `reg`. Fixes #2035. --- backends/verilog/verilog_backend.cc | 23 +++++++++++++++-------- tests/simple/.gitignore | 1 + tests/verilog/.gitignore | 4 ++++ tests/verilog/assign_to_reg.ys | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 tests/verilog/assign_to_reg.ys diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index a09ae984b..31bbc996f 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2014,22 +2014,29 @@ void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - if (simple_lhs) { + bool all_chunks_wires = true; + for (auto &chunk : left.chunks()) + if (chunk.is_wire() && reg_wires.count(chunk.wire->name)) + all_chunks_wires = false; + if (!simple_lhs && all_chunks_wires) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, left); + f << stringf(" = "); + dump_sigspec(f, right); + f << stringf(";\n"); + } else { int offset = 0; for (auto &chunk : left.chunks()) { - f << stringf("%s" "assign ", indent.c_str()); + if (chunk.is_wire() && reg_wires.count(chunk.wire->name)) + f << stringf("%s" "always%s\n%s ", indent.c_str(), systemverilog ? "_comb" : " @*", indent.c_str()); + else + f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, chunk); f << stringf(" = "); dump_sigspec(f, right.extract(offset, GetSize(chunk))); f << stringf(";\n"); offset += GetSize(chunk); } - } else { - f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, left); - f << stringf(" = "); - dump_sigspec(f, right); - f << stringf(";\n"); } } diff --git a/tests/simple/.gitignore b/tests/simple/.gitignore index 073f46157..5daaadbd7 100644 --- a/tests/simple/.gitignore +++ b/tests/simple/.gitignore @@ -1,2 +1,3 @@ *.log *.out +*.err diff --git a/tests/verilog/.gitignore b/tests/verilog/.gitignore index 96ebe20ba..cfd72076e 100644 --- a/tests/verilog/.gitignore +++ b/tests/verilog/.gitignore @@ -1,6 +1,10 @@ /*.log /*.out +/*.err /run-test.mk /const_arst.v /const_sr.v /doubleslash.v +/roundtrip_proc_1.v +/roundtrip_proc_2.v +/assign_to_reg.v diff --git a/tests/verilog/assign_to_reg.ys b/tests/verilog/assign_to_reg.ys new file mode 100644 index 000000000..80080e9f3 --- /dev/null +++ b/tests/verilog/assign_to_reg.ys @@ -0,0 +1,22 @@ +# https://github.com/yosyshq/yosys/issues/2035 + +read_ilang < Date: Wed, 3 Apr 2024 20:37:54 +0200 Subject: [PATCH 23/29] docs: Document $macc --- .../yosys_internals/formats/cell_library.rst | 48 ++++++++++++++++- techlibs/common/simlib.v | 51 +++++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/docs/source/yosys_internals/formats/cell_library.rst b/docs/source/yosys_internals/formats/cell_library.rst index c80b07402..a4e5adfb7 100644 --- a/docs/source/yosys_internals/formats/cell_library.rst +++ b/docs/source/yosys_internals/formats/cell_library.rst @@ -619,6 +619,52 @@ Finite state machines Add a brief description of the ``$fsm`` cell type. +Coarse arithmetics +~~~~~~~~~~~~~~~~~~~~~ + +The ``$macc`` cell type represents a multiply and accumulate block, for summing any number of negated and unnegated signals and arithmetic products of pairs of signals. Cell port A concatenates pairs of signals to be multiplied together. When the second signal in a pair is zero length, a constant 1 is used instead as the second factor. Cell port B concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders. + +The cell's ``CONFIG`` parameter determines the layout of cell port ``A``. +In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate: +A cell port is for example the A input (it is constructed in C++ as ``cell->setPort(ID::A, ...))`` +Multiplier ports are pairs of multiplier inputs ("factors"). +If the second signal in such a pair is zero length, no multiplication is necessary, and the first signal is just added to the sum. + +In this pseudocode, ``u(foo)`` means an unsigned int that's foo bits long. +The CONFIG parameter carries the following information: +.. code-block:: + :force: + struct CONFIG { + u4 num_bits; + struct port_field { + bool is_signed; + bool is_subtract; + u(num_bits) factor1_len; + u(num_bits) factor2_len; + }[num_ports]; + }; + +The A cell port carries the following information: +.. code-block:: + :force: + struct A { + u(CONFIG.port_field[0].factor1_len) port0factor1; + u(CONFIG.port_field[0].factor2_len) port0factor2; + u(CONFIG.port_field[1].factor1_len) port1factor1; + u(CONFIG.port_field[1].factor2_len) port1factor2; + ... + }; + +No factor1 may have a zero length. +A factor2 having a zero length implies factor2 is replaced with a constant 1. + +Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up. +Finally, we have: +.. code-block:: + :force: + Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ... + * B[0] + B[1] + ... + Specify rules ~~~~~~~~~~~~~ @@ -1152,4 +1198,4 @@ file via ABC using the abc pass. .. todo:: Add information about ``$lut`` and ``$sop`` cells. -.. todo:: Add information about ``$alu``, ``$macc``, ``$fa``, and ``$lcu`` cells. +.. todo:: Add information about ``$alu``, ``$fa``, and ``$lcu`` cells. diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 489281f26..1383a2a13 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -902,18 +902,29 @@ endgenerate endmodule // -------------------------------------------------------- - +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $macc (A, B, Y) +//- +//- Multiply and accumulate. +//- A building block for summing any number of negated and unnegated signals and arithmetic products of pairs of signals. Cell port A concatenates pairs of signals to be multiplied together. When the second signal in a pair is zero length, a constant 1 is used instead as the second factor. Cell port B concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders. +//- Typically created by the `alumacc` pass, which transforms $add and $mul into $macc cells. module \$macc (A, B, Y); parameter A_WIDTH = 0; parameter B_WIDTH = 0; parameter Y_WIDTH = 0; +// CONFIG determines the layout of A, as explained below parameter CONFIG = 4'b0000; parameter CONFIG_WIDTH = 4; -input [A_WIDTH-1:0] A; -input [B_WIDTH-1:0] B; -output reg [Y_WIDTH-1:0] Y; +// In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate: +// A cell port is for example the A input (it is constructed in C++ as cell->setPort(ID::A, ...)) +// Multiplier ports are pairs of multiplier inputs ("factors"). +// If the second signal in such a pair is zero length, no multiplication is necessary, and the first signal is just added to the sum. +input [A_WIDTH-1:0] A; // Cell port A is the concatenation of all arithmetic ports +input [B_WIDTH-1:0] B; // Cell port B is the concatenation of single-bit unsigned signals to be also added to the sum +output reg [Y_WIDTH-1:0] Y; // Output sum // Xilinx XSIM does not like $clog2() below.. function integer my_clog2; @@ -929,10 +940,42 @@ function integer my_clog2; end endfunction +// Bits that a factor's length field in CONFIG per factor in cell port A localparam integer num_bits = CONFIG[3:0] > 0 ? CONFIG[3:0] : 1; +// Number of multiplier ports localparam integer num_ports = (CONFIG_WIDTH-4) / (2 + 2*num_bits); +// Minium bit width of an induction variable to iterate over all bits of cell port A localparam integer num_abits = my_clog2(A_WIDTH) > 0 ? my_clog2(A_WIDTH) : 1; +// In this pseudocode, u(foo) means an unsigned int that's foo bits long. +// The CONFIG parameter carries the following information: +// struct CONFIG { +// u4 num_bits; +// struct port_field { +// bool is_signed; +// bool is_subtract; +// u(num_bits) factor1_len; +// u(num_bits) factor2_len; +// }[num_ports]; +// }; + +// The A cell port carries the following information: +// struct A { +// u(CONFIG.port_field[0].factor1_len) port0factor1; +// u(CONFIG.port_field[0].factor2_len) port0factor2; +// u(CONFIG.port_field[1].factor1_len) port1factor1; +// u(CONFIG.port_field[1].factor2_len) port1factor2; +// ... +// }; +// and log(sizeof(A)) is num_abits. +// No factor1 may have a zero length. +// A factor2 having a zero length implies factor2 is replaced with a constant 1. + +// Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up. +// Finally, we have: +// Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ... +// * B[0] + B[1] + ... + function [2*num_ports*num_abits-1:0] get_port_offsets; input [CONFIG_WIDTH-1:0] cfg; integer i, cursor; From 22c5ab90d1580b6d515a58cf1c8be380d188b989 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:16:37 +0000 Subject: [PATCH 24/29] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e31b72261..998347002 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ LIBS += -lrt endif endif -YOSYS_VER := 0.39+163 +YOSYS_VER := 0.39+165 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 9510293a9472a239f448cc7dc7b1bed7f1a35e36 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 4 Apr 2024 18:16:58 +0200 Subject: [PATCH 25/29] fixup --- docs/source/yosys_internals/formats/cell_library.rst | 11 +++++++---- techlibs/common/simlib.v | 9 +++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/source/yosys_internals/formats/cell_library.rst b/docs/source/yosys_internals/formats/cell_library.rst index a4e5adfb7..1e0012f46 100644 --- a/docs/source/yosys_internals/formats/cell_library.rst +++ b/docs/source/yosys_internals/formats/cell_library.rst @@ -632,8 +632,9 @@ If the second signal in such a pair is zero length, no multiplication is necessa In this pseudocode, ``u(foo)`` means an unsigned int that's foo bits long. The CONFIG parameter carries the following information: + .. code-block:: - :force: + struct CONFIG { u4 num_bits; struct port_field { @@ -645,8 +646,9 @@ The CONFIG parameter carries the following information: }; The A cell port carries the following information: + .. code-block:: - :force: + struct A { u(CONFIG.port_field[0].factor1_len) port0factor1; u(CONFIG.port_field[0].factor2_len) port0factor2; @@ -660,10 +662,11 @@ A factor2 having a zero length implies factor2 is replaced with a constant 1. Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up. Finally, we have: + .. code-block:: - :force: + Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ... - * B[0] + B[1] + ... + + B[0] + B[1] + ... Specify rules ~~~~~~~~~~~~~ diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 1383a2a13..7dc03da6d 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -907,8 +907,13 @@ endmodule //- $macc (A, B, Y) //- //- Multiply and accumulate. -//- A building block for summing any number of negated and unnegated signals and arithmetic products of pairs of signals. Cell port A concatenates pairs of signals to be multiplied together. When the second signal in a pair is zero length, a constant 1 is used instead as the second factor. Cell port B concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders. -//- Typically created by the `alumacc` pass, which transforms $add and $mul into $macc cells. +//- A building block for summing any number of negated and unnegated signals +//- and arithmetic products of pairs of signals. Cell port A concatenates pairs +//- of signals to be multiplied together. When the second signal in a pair is zero +//- length, a constant 1 is used instead as the second factor. Cell port B +//- concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders. +//- Typically created by the `alumacc` pass, which transforms $add and $mul +//- into $macc cells. module \$macc (A, B, Y); parameter A_WIDTH = 0; From 43ef916f8683e68bbaee9f2358f3719d5000bf1a Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 5 Apr 2024 14:01:25 +0200 Subject: [PATCH 26/29] Restructure rst --- .../yosys_internals/formats/cell_library.rst | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/source/yosys_internals/formats/cell_library.rst b/docs/source/yosys_internals/formats/cell_library.rst index 1e0012f46..2b8dc3001 100644 --- a/docs/source/yosys_internals/formats/cell_library.rst +++ b/docs/source/yosys_internals/formats/cell_library.rst @@ -622,22 +622,36 @@ Add a brief description of the ``$fsm`` cell type. Coarse arithmetics ~~~~~~~~~~~~~~~~~~~~~ -The ``$macc`` cell type represents a multiply and accumulate block, for summing any number of negated and unnegated signals and arithmetic products of pairs of signals. Cell port A concatenates pairs of signals to be multiplied together. When the second signal in a pair is zero length, a constant 1 is used instead as the second factor. Cell port B concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders. +The ``$macc`` cell type represents a generalized multiply and accumulate operation. The cell is purely combinational. It outputs the result of summing up a sequence of products and other injected summands. -The cell's ``CONFIG`` parameter determines the layout of cell port ``A``. -In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate: -A cell port is for example the A input (it is constructed in C++ as ``cell->setPort(ID::A, ...))`` -Multiplier ports are pairs of multiplier inputs ("factors"). -If the second signal in such a pair is zero length, no multiplication is necessary, and the first signal is just added to the sum. +.. code-block:: + + Y = 0 +- a0factor1 * a0factor2 +- a1factor1 * a1factor2 +- ... + + B[0] + B[1] + ... + +The A port consists of concatenated pairs of multiplier inputs ("factors"). +A zero length factor2 acts as a constant 1, turning factor1 into a simple summand. In this pseudocode, ``u(foo)`` means an unsigned int that's foo bits long. + +.. code-block:: + + struct A { + u(CONFIG.mul_info[0].factor1_len) a0factor1; + u(CONFIG.mul_info[0].factor2_len) a0factor2; + u(CONFIG.mul_info[1].factor1_len) a1factor1; + u(CONFIG.mul_info[1].factor2_len) a1factor2; + ... + }; + +The cell's ``CONFIG`` parameter determines the layout of cell port ``A``. The CONFIG parameter carries the following information: .. code-block:: struct CONFIG { u4 num_bits; - struct port_field { + struct mul_info { bool is_signed; bool is_subtract; u(num_bits) factor1_len; @@ -645,28 +659,7 @@ The CONFIG parameter carries the following information: }[num_ports]; }; -The A cell port carries the following information: - -.. code-block:: - - struct A { - u(CONFIG.port_field[0].factor1_len) port0factor1; - u(CONFIG.port_field[0].factor2_len) port0factor2; - u(CONFIG.port_field[1].factor1_len) port1factor1; - u(CONFIG.port_field[1].factor2_len) port1factor2; - ... - }; - -No factor1 may have a zero length. -A factor2 having a zero length implies factor2 is replaced with a constant 1. - -Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up. -Finally, we have: - -.. code-block:: - - Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ... - + B[0] + B[1] + ... +B is an array of concatenated 1-bit-wide unsigned integers to also be summed up. Specify rules ~~~~~~~~~~~~~ From 91e41d8c803562d43e2debe30677a86d55035357 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 8 Apr 2024 12:44:37 +0200 Subject: [PATCH 27/29] Move parameters to module declaration --- tests/memlib/memlib_9b1B.v | 20 +++++++++++--------- tests/memlib/memlib_wren.v | 16 ++++++++-------- tests/memories/issue00335.v | 6 ++++-- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/tests/memlib/memlib_9b1B.v b/tests/memlib/memlib_9b1B.v index 55545ebd4..9a8246c9f 100644 --- a/tests/memlib/memlib_9b1B.v +++ b/tests/memlib/memlib_9b1B.v @@ -1,4 +1,14 @@ -module RAM_9b1B ( +module RAM_9b1B +#( + parameter INIT = 0, + parameter OPTION_INIT = "UNDEFINED", + parameter PORT_R_WIDTH = 9, + parameter PORT_W_WIDTH = 9, + parameter PORT_R_CLK_POL = 0, + parameter PORT_W_CLK_POL = 0, + parameter PORT_W_WR_EN_WIDTH = 1 +) +( input PORT_R_CLK, input [6:0] PORT_R_ADDR, output reg [PORT_R_WIDTH-1:0] PORT_R_RD_DATA, @@ -8,14 +18,6 @@ module RAM_9b1B ( input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA ); -parameter INIT = 0; -parameter OPTION_INIT = "UNDEFINED"; -parameter PORT_R_WIDTH = 9; -parameter PORT_W_WIDTH = 9; -parameter PORT_R_CLK_POL = 0; -parameter PORT_W_CLK_POL = 0; -parameter PORT_W_WR_EN_WIDTH = 1; - reg [8:0] mem [0:15]; integer i; diff --git a/tests/memlib/memlib_wren.v b/tests/memlib/memlib_wren.v index b9433d42e..c687184bd 100644 --- a/tests/memlib/memlib_wren.v +++ b/tests/memlib/memlib_wren.v @@ -1,4 +1,11 @@ -module RAM_WREN ( +module RAM_WREN #( + parameter ABITS=4, + parameter WIDTH=8, + parameter PORT_A_WR_EN_WIDTH=1, + parameter PORT_A_WR_BE_WIDTH=0, + parameter OPTION_BYTESIZE=WIDTH, + parameter WB=OPTION_BYTESIZE +)( input PORT_A_CLK, input [ABITS-1:0] PORT_A_ADDR, input [WIDTH-1:0] PORT_A_WR_DATA, @@ -7,13 +14,6 @@ module RAM_WREN ( input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE ); -parameter ABITS=4; -parameter WIDTH=8; -parameter PORT_A_WR_EN_WIDTH=1; -parameter PORT_A_WR_BE_WIDTH=0; -parameter OPTION_BYTESIZE=WIDTH; -parameter WB=OPTION_BYTESIZE; - reg [WIDTH-1:0] mem [0:2**ABITS-1]; integer i; diff --git a/tests/memories/issue00335.v b/tests/memories/issue00335.v index f3b6e5dfe..305339dd9 100644 --- a/tests/memories/issue00335.v +++ b/tests/memories/issue00335.v @@ -2,13 +2,15 @@ // expect-rd-ports 1 // expect-rd-clk \clk -module ram2 (input clk, +module ram2 #( + parameter SIZE = 5 // Address size + ) (input clk, input sel, input we, input [SIZE-1:0] adr, input [63:0] dat_i, output reg [63:0] dat_o); - parameter SIZE = 5; // Address size + reg [63:0] mem [0:(1 << SIZE)-1]; integer i; From 4ac10040ce79b2d17f3f72c2c61d6e8558b8dab3 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 8 Apr 2024 12:45:43 +0200 Subject: [PATCH 28/29] Enable SV for localparam use by Efinix cell_sim --- techlibs/efinix/cells_sim.v | 49 +++++++++++++++++++------------------ tests/arch/run-test.sh | 2 +- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/techlibs/efinix/cells_sim.v b/techlibs/efinix/cells_sim.v index 22c7bc776..26e454646 100644 --- a/techlibs/efinix/cells_sim.v +++ b/techlibs/efinix/cells_sim.v @@ -113,7 +113,31 @@ module EFX_GBUFCE( endmodule -module EFX_RAM_5K( +module EFX_RAM_5K +# ( + parameter READ_WIDTH = 20, + parameter WRITE_WIDTH = 20, + localparam READ_ADDR_WIDTH = + (READ_WIDTH == 16) ? 8 : // 256x16 + (READ_WIDTH == 8) ? 9 : // 512x8 + (READ_WIDTH == 4) ? 10 : // 1024x4 + (READ_WIDTH == 2) ? 11 : // 2048x2 + (READ_WIDTH == 1) ? 12 : // 4096x1 + (READ_WIDTH == 20) ? 8 : // 256x20 + (READ_WIDTH == 10) ? 9 : // 512x10 + (READ_WIDTH == 5) ? 10 : -1, // 1024x5 + + localparam WRITE_ADDR_WIDTH = + (WRITE_WIDTH == 16) ? 8 : // 256x16 + (WRITE_WIDTH == 8) ? 9 : // 512x8 + (WRITE_WIDTH == 4) ? 10 : // 1024x4 + (WRITE_WIDTH == 2) ? 11 : // 2048x2 + (WRITE_WIDTH == 1) ? 12 : // 4096x1 + (WRITE_WIDTH == 20) ? 8 : // 256x20 + (WRITE_WIDTH == 10) ? 9 : // 512x10 + (WRITE_WIDTH == 5) ? 10 : -1 // 1024x5 +) +( input [WRITE_WIDTH-1:0] WDATA, input [WRITE_ADDR_WIDTH-1:0] WADDR, input WE, @@ -126,8 +150,6 @@ module EFX_RAM_5K( (* clkbuf_sink *) input RCLK ); - parameter READ_WIDTH = 20; - parameter WRITE_WIDTH = 20; parameter OUTPUT_REG = 1'b0; parameter RCLK_POLARITY = 1'b1; parameter RE_POLARITY = 1'b1; @@ -155,25 +177,4 @@ module EFX_RAM_5K( parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; - - localparam READ_ADDR_WIDTH = - (READ_WIDTH == 16) ? 8 : // 256x16 - (READ_WIDTH == 8) ? 9 : // 512x8 - (READ_WIDTH == 4) ? 10 : // 1024x4 - (READ_WIDTH == 2) ? 11 : // 2048x2 - (READ_WIDTH == 1) ? 12 : // 4096x1 - (READ_WIDTH == 20) ? 8 : // 256x20 - (READ_WIDTH == 10) ? 9 : // 512x10 - (READ_WIDTH == 5) ? 10 : -1; // 1024x5 - - localparam WRITE_ADDR_WIDTH = - (WRITE_WIDTH == 16) ? 8 : // 256x16 - (WRITE_WIDTH == 8) ? 9 : // 512x8 - (WRITE_WIDTH == 4) ? 10 : // 1024x4 - (WRITE_WIDTH == 2) ? 11 : // 2048x2 - (WRITE_WIDTH == 1) ? 12 : // 4096x1 - (WRITE_WIDTH == 20) ? 8 : // 256x20 - (WRITE_WIDTH == 10) ? 9 : // 512x10 - (WRITE_WIDTH == 5) ? 10 : -1; // 1024x5 - endmodule diff --git a/tests/arch/run-test.sh b/tests/arch/run-test.sh index a14b79509..68f925b34 100755 --- a/tests/arch/run-test.sh +++ b/tests/arch/run-test.sh @@ -16,7 +16,7 @@ for arch in ../../techlibs/*; do done else echo -n "Test $path ->" - iverilog -t null -I$arch $path + iverilog -t null -I$arch -g2005-sv $path echo " ok" fi done From 5f4d13ee3fdf49d371fe29712b95c07eba9f7f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 8 Apr 2024 16:44:43 +0200 Subject: [PATCH 29/29] techmap: Note down iteration in Kogge-Stone --- techlibs/common/choices/kogge-stone.v | 1 + 1 file changed, 1 insertion(+) diff --git a/techlibs/common/choices/kogge-stone.v b/techlibs/common/choices/kogge-stone.v index 0e25fe860..ad2e2b169 100644 --- a/techlibs/common/choices/kogge-stone.v +++ b/techlibs/common/choices/kogge-stone.v @@ -42,6 +42,7 @@ module _80_lcu_kogge_stone (P, G, CI, CO); g[0] = g[0] | (p[0] & CI); for (i = 0; i < $clog2(WIDTH); i = i + 1) begin + // iterate in reverse so we don't confuse a result from this stage and the previous for (j = WIDTH - 1; j >= 2**i; j = j - 1) begin g[j] = g[j] | p[j] & g[j - 2**i]; p[j] = p[j] & p[j - 2**i];