From b16cdaab35050ca51a696b6c8e191ffb19946d0f Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Wed, 6 Mar 2024 02:43:30 +0100 Subject: [PATCH 001/123] dsp_map for MAX10 --- techlibs/intel/max10/dsp_map.v | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 techlibs/intel/max10/dsp_map.v diff --git a/techlibs/intel/max10/dsp_map.v b/techlibs/intel/max10/dsp_map.v new file mode 100644 index 000000000..1c7827b5e --- /dev/null +++ b/techlibs/intel/max10/dsp_map.v @@ -0,0 +1,73 @@ +module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + wire [A_WIDTH+B_WIDTH-1:0] mult_result; + + fiftyfivenm_mac_mult #( + .dataa_clock ("none"), + .datab_clock ("none"), + .signa_clock ("none"), + .signb_clock ("none"), + .dataa_width (A_WIDTH), + .datab_width (B_WIDTH), + .lpm_type ("fiftyfivenm_mac_mult") + ) _TECHMAP_REPLACE_mac_mult ( + //Data path + .dataa ( A ), + .datab ( B ), + .dataout( mult_result ), + .signa ( A_SIGNED != 0 ? 1'b1 : 1'b0), + .signb ( B_SIGNED != 0 ? 1'b1 : 1'b0) + ); + + fiftyfivenm_mac_out #( + .dataa_width (A_WIDTH + B_WIDTH), + .output_clock ("none"), + .lpm_type ("fiftyfivenm_mac_out") + ) _TECHMAP_REPLACE_mac_out ( + .dataa (mult_result), + .dataout (Y) + ); +endmodule + + +module \$__MUL9X9 (input [8:0] A, input [8:0] B, output [17:0] Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + wire [A_WIDTH+B_WIDTH-1:0] mult_result; + + fiftyfivenm_mac_mult #( + .dataa_clock ("none"), + .datab_clock ("none"), + .signa_clock ("none"), + .signb_clock ("none"), + .dataa_width (A_WIDTH), + .datab_width (B_WIDTH), + .lpm_type ("fiftyfivenm_mac_mult") + ) _TECHMAP_REPLACE_mac_mult ( + //Data path + .dataa ( A ), + .datab ( B ), + .dataout( mult_result ), + .signa ( A_SIGNED != 0 ? 1'b1 : 1'b0), + .signb ( B_SIGNED != 0 ? 1'b1 : 1'b0) + ); + + fiftyfivenm_mac_out #( + .dataa_width (A_WIDTH + B_WIDTH), + .output_clock ("none"), + .lpm_type ("fiftyfivenm_mac_out") + ) _TECHMAP_REPLACE_mac_out ( + .dataa (mult_result), + .dataout (Y) + ); +endmodule + From 2fde482629dd523f196b231c6659609ff768a20f Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Wed, 6 Mar 2024 02:45:07 +0100 Subject: [PATCH 002/123] Fixed data/address width parameters --- techlibs/intel/common/brams_map_m9k.v | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/techlibs/intel/common/brams_map_m9k.v b/techlibs/intel/common/brams_map_m9k.v index d0f07c1de..c80ebe3f4 100644 --- a/techlibs/intel/common/brams_map_m9k.v +++ b/techlibs/intel/common/brams_map_m9k.v @@ -63,10 +63,10 @@ module \$__M9K_ALTSYNCRAM_SINGLEPORT_FULL (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1A .width_byteena_a (1), // Forced value .numwords_b ( NUMWORDS ), .numwords_a ( NUMWORDS ), - .widthad_b ( CFG_DBITS ), - .width_b ( CFG_ABITS ), - .widthad_a ( CFG_DBITS ), - .width_a ( CFG_ABITS ) + .widthad_b ( CFG_ABITS ), + .width_b ( CFG_DBITS ), + .widthad_a ( CFG_ABITS ), + .width_a ( CFG_DBITS ) ) _TECHMAP_REPLACE_ ( .data_a(B1DATA), .address_a(B1ADDR), From 58b1522a20ef9a4ec914280bcaf4f4e637f4368e Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Wed, 6 Mar 2024 02:45:29 +0100 Subject: [PATCH 003/123] Added DSP macros --- techlibs/intel/max10/cells_sim.v | 56 +++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/techlibs/intel/max10/cells_sim.v b/techlibs/intel/max10/cells_sim.v index 7705fa27a..d273b47d6 100644 --- a/techlibs/intel/max10/cells_sim.v +++ b/techlibs/intel/max10/cells_sim.v @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2024 Richard Herveille * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -289,4 +290,57 @@ module fiftyfivenm_pll output vcooverrange; output vcounderrange; -endmodule // cycloneive_pll +endmodule // max10_pll + + +//rih +/* MAX10 MULT clearbox model */ +(* blackbox *) +module fiftyfivenm_mac_mult ( + dataa, + datab, + dataout, + signa, + signb, + + aclr, + clk, + ena +); + parameter dataa_clock = "none"; + parameter dataa_width = 18; + parameter datab_clock = "none"; + parameter datab_width = 18; + parameter signa_clock = "none"; + parameter signb_clock = "none"; + parameter lpm_type = "fiftyfivenm_mac_mult"; + + input [dataa_width -1:0] dataa; + input [datab_width -1:0] datab; + output [(dataa_width+datab_width)-1:0] dataout; + input signa; + input signb; + input aclr; + input clk; + input ena; +endmodule : fiftyfivenm_mac_mult + +module fiftyfivenm_mac_out ( + dataa, + dataout, + + aclr, + clk, + ena +); + + parameter dataa_width = 38; + parameter output_clock = "none"; + parameter lpm_type = "fiftyfivenm_mac_out"; + + input [dataa_width-1:0] dataa; + output [dataa_width-1:0] dataout; + input aclr; + input clk; + input ena; +endmodule : fiftyfivenm_mac_out From 1723aa251afa492898b4046ff4dc867a1265d6c5 Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Wed, 6 Mar 2024 02:45:40 +0100 Subject: [PATCH 004/123] Added DSP support and updates for performance --- techlibs/intel/synth_intel.cc | 65 ++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index e9594e6d8..00e5766e9 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2024 Richard Herveille * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -62,12 +63,19 @@ struct SynthIntelPass : public ScriptPass { log(" from label is synonymous to 'begin', and empty to label is\n"); log(" synonymous to the end of the command list.\n"); log("\n"); + log(" -dff\n"); + log(" pass DFFs to ABC to perform sequential logic optimisations\n"); + log(" (EXPERIMENTAL)\n"); + log("\n"); log(" -iopads\n"); log(" use IO pad cells in output netlist\n"); log("\n"); log(" -nobram\n"); log(" do not use block RAM cells in output netlist\n"); log("\n"); + log(" -nodsp\n"); + log(" do not map multipliers to MUL18/MUL9 cells\n"); + log("\n"); log(" -noflatten\n"); log(" do not flatten design before synthesis\n"); log("\n"); @@ -80,7 +88,7 @@ struct SynthIntelPass : public ScriptPass { } string top_opt, family_opt, vout_file, blif_file; - bool retime, flatten, nobram, iopads; + bool retime, flatten, nobram, dff, nodsp, iopads; void clear_flags() override { @@ -91,6 +99,8 @@ struct SynthIntelPass : public ScriptPass { retime = false; flatten = true; nobram = false; + dff = false; + nodsp = false; iopads = false; } @@ -130,6 +140,14 @@ struct SynthIntelPass : public ScriptPass { iopads = true; continue; } + if (args[argidx] == "-dff") { + dff = true; + continue; + } + if (args[argidx] == "-nodsp") { + nodsp = true; + continue; + } if (args[argidx] == "-nobram") { nobram = true; continue; @@ -177,16 +195,52 @@ struct SynthIntelPass : public ScriptPass { run("read_verilog -sv -lib +/intel/common/altpll_bb.v"); run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt.c_str())); } - +/* if (flatten && check_label("flatten", "(unless -noflatten)")) { run("proc"); run("flatten"); run("tribuf -logic"); run("deminout"); } +*/ if (check_label("coarse")) { - run("synth -run coarse"); +// run("synth -run coarse"); + run("proc"); + if (flatten || help_mode) + run("flatten", "(skip if -noflatten)"); + run("tribuf -logic"); + run("deminout"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt -nodffe -nosdff"); + run("fsm"); + run("opt"); + run("wreduce"); + run("peepopt"); + run("opt_clean"); + run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); + run("opt_expr"); + run("opt_clean"); + + if (help_mode) { + run("techmap -map +mul2dsp.v [...]", "(unless -nodsp)"); + } else if (!nodsp) { + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=10 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL18X18"); + run("chtype -set $mul t:$__soft_mul"); + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__MUL18X18"); + run("chtype -set $mul t:$__soft_mul"); + run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=9 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL9X9"); + run("chtype -set $mul t:$__soft_mul"); + run("alumacc"); + run(stringf("techmap -map +/intel/%s/dsp_map.v", family_opt.c_str())); + } else { + run("alumacc"); + } + run("opt"); + run("memory -nomap"); + run("opt_clean"); } if (!nobram && check_label("map_bram", "(skip if -nobram)")) { @@ -219,7 +273,10 @@ struct SynthIntelPass : public ScriptPass { } if (check_label("map_luts")) { - run("abc -lut 4" + string(retime ? " -dff" : "")); + run("abc9 -lut 4 -W 300" + string(dff ? " -dff" : "")); + run("clean"); + run("opt -fast"); + run("autoname"); run("clean"); } From 2d11c5e2f8199065039b7ac07e60622f9c2c583d Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Fri, 15 Mar 2024 01:48:06 +0100 Subject: [PATCH 005/123] removed comment --- techlibs/intel/max10/cells_sim.v | 1 - 1 file changed, 1 deletion(-) diff --git a/techlibs/intel/max10/cells_sim.v b/techlibs/intel/max10/cells_sim.v index d273b47d6..fe8c92b0a 100644 --- a/techlibs/intel/max10/cells_sim.v +++ b/techlibs/intel/max10/cells_sim.v @@ -293,7 +293,6 @@ module fiftyfivenm_pll endmodule // max10_pll -//rih /* MAX10 MULT clearbox model */ (* blackbox *) module fiftyfivenm_mac_mult ( From 7647eb70a6906979a7001c47e9d02c7f64f0d1f5 Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Fri, 15 Mar 2024 01:48:22 +0100 Subject: [PATCH 006/123] removed commented out code --- techlibs/intel/synth_intel.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index 00e5766e9..11567ece9 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -195,17 +195,8 @@ struct SynthIntelPass : public ScriptPass { run("read_verilog -sv -lib +/intel/common/altpll_bb.v"); run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt.c_str())); } -/* - if (flatten && check_label("flatten", "(unless -noflatten)")) { - run("proc"); - run("flatten"); - run("tribuf -logic"); - run("deminout"); - } -*/ if (check_label("coarse")) { -// run("synth -run coarse"); run("proc"); if (flatten || help_mode) run("flatten", "(skip if -noflatten)"); From 2893938355d786a0188753272cef5d95c3e10ccb Mon Sep 17 00:00:00 2001 From: Richard Herveille Date: Tue, 19 Mar 2024 01:31:36 +0100 Subject: [PATCH 007/123] Removed SystemVerilog module end label --- techlibs/intel/max10/cells_sim.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/techlibs/intel/max10/cells_sim.v b/techlibs/intel/max10/cells_sim.v index fe8c92b0a..b163aac40 100644 --- a/techlibs/intel/max10/cells_sim.v +++ b/techlibs/intel/max10/cells_sim.v @@ -322,7 +322,7 @@ module fiftyfivenm_mac_mult ( input aclr; input clk; input ena; -endmodule : fiftyfivenm_mac_mult +endmodule //fiftyfivenm_mac_mult module fiftyfivenm_mac_out ( dataa, @@ -342,4 +342,4 @@ module fiftyfivenm_mac_out ( input aclr; input clk; input ena; -endmodule : fiftyfivenm_mac_out +endmodule //fiftyfivenm_mac_out From 94913a9f5ada47ec714933d014a9e206e7ebec61 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Tue, 27 Aug 2024 17:34:56 +0300 Subject: [PATCH 008/123] cxxrtl: use octal encoding of non-printables. "\x0a" is a perfectly valid escape sequence, but unfortunately "\x0ac" is equivalent to "\xac", and not "\x0a" "c" as we might expect --- *any* number of hexadecimal characters after the "\x" is accepted. This can be hit pretty easily if a newline is present in a format string. "\x{...}" syntax is only available as of C++23, so use octal format instead; a maximum of 3 digits following the backslash is accepted. The alternative would be to render every escape like `" "\x0a" "`, but it seems more effort that way. --- kernel/fmt.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/fmt.cc b/kernel/fmt.cc index d1c6b8ac9..44ad8351d 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -634,10 +634,11 @@ std::string escape_cxx_string(const std::string &input) output.push_back('\\'); output.push_back(c); } else { - char l = c & 0xf, h = (c >> 4) & 0xf; - output.append("\\x"); - output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); - output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); + char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3; + output.push_back('\\'); + output.push_back('0' + h); + output.push_back('0' + m); + output.push_back('0' + l); } } output.push_back('"'); From d0da1b56beb90f9633ed58cf0b52ea79d979ac86 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Tue, 27 Aug 2024 18:36:43 +0300 Subject: [PATCH 009/123] cxxrtl: backend: don't drop bits 2 and 5 on non-printable format. --- backends/cxxrtl/cxxrtl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 8dc14863d..7e7bace6c 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -616,7 +616,7 @@ std::string escape_c_string(const std::string &input) output.push_back('\\'); output.push_back(c); } else { - char l = c & 0x3, m = (c >> 3) & 0x3, h = (c >> 6) & 0x3; + char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3; output.append("\\"); output.push_back('0' + h); output.push_back('0' + m); From 9a112043294ddca6f0b09892def8db59d277d147 Mon Sep 17 00:00:00 2001 From: Aritz Erkiaga Date: Sun, 22 Dec 2024 18:26:30 +0100 Subject: [PATCH 010/123] Update ALU MULT mode in gowin to match nextpnr --- techlibs/gowin/cells_sim.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v index 76635355d..58a2f52e6 100644 --- a/techlibs/gowin/cells_sim.v +++ b/techlibs/gowin/cells_sim.v @@ -1005,7 +1005,7 @@ always @* begin C = I0; end MULT: begin - S = I0 & I1; + S = (I0 & I1) ^ I3; C = I0 & I1; end endcase From ef7734d610305c8a396ec43717d98de7472d8143 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:01:21 +1300 Subject: [PATCH 011/123] functional_ir.rst: Formatting Line breaks. Put intro under sub-heading. --- .../extending_yosys/functional_ir.rst | 181 +++++++++++------- 1 file changed, 116 insertions(+), 65 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index a763e0508..11f5c579f 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -1,94 +1,145 @@ Writing a new backend using FunctionalIR -=========================================== +======================================== -To simplify the writing of backends for functional languages or similar targets, Yosys provides an alternative intermediate representation called FunctionalIR which maps more directly on those targets. +What is FunctionalIR +-------------------- -FunctionalIR represents the design as a function ``(inputs, current_state) -> (outputs, next_state)``. -This function is broken down into a series of assignments to variables. -Each assignment is a simple operation, such as an addition. -Complex operations are broken up into multiple steps. -For example, an RTLIL addition will be translated into a sign/zero extension of the inputs, followed by an addition. +To simplify the writing of backends for functional languages or similar targets, +Yosys provides an alternative intermediate representation called FunctionalIR +which maps more directly on those targets. -Like SSA form, each variable is assigned to exactly once. -We can thus treat variables and assignments as equivalent and, since this is a graph-like representation, those variables are also called "nodes". -Unlike RTLIL's cells and wires representation, this representation is strictly ordered (topologically sorted) with definitions preceding their use. +FunctionalIR represents the design as a function ``(inputs, current_state) -> +(outputs, next_state)``. This function is broken down into a series of +assignments to variables. Each assignment is a simple operation, such as an +addition. Complex operations are broken up into multiple steps. For example, an +RTLIL addition will be translated into a sign/zero extension of the inputs, +followed by an addition. -Every node has a "sort" (the FunctionalIR term for what might otherwise be called a "type"). The sorts available are +Like SSA form, each variable is assigned to exactly once. We can thus treat +variables and assignments as equivalent and, since this is a graph-like +representation, those variables are also called "nodes". Unlike RTLIL's cells +and wires representation, this representation is strictly ordered (topologically +sorted) with definitions preceding their use. + +Every node has a "sort" (the FunctionalIR term for what might otherwise be +called a "type"). The sorts available are - ``bit[n]`` for an ``n``-bit bitvector, and - ``memory[n,m]`` for an immutable array of ``2**n`` values of sort ``bit[m]``. -In terms of actual code, Yosys provides a class ``Functional::IR`` that represents a design in FunctionalIR. -``Functional::IR::from_module`` generates an instance from an RTLIL module. -The entire design is stored as a whole in an internal data structure. -To access the design, the ``Functional::Node`` class provides a reference to a particular node in the design. -The ``Functional::IR`` class supports the syntax ``for(auto node : ir)`` to iterate over every node. +In terms of actual code, Yosys provides a class ``Functional::IR`` that +represents a design in FunctionalIR. ``Functional::IR::from_module`` generates +an instance from an RTLIL module. The entire design is stored as a whole in an +internal data structure. To access the design, the ``Functional::Node`` class +provides a reference to a particular node in the design. The ``Functional::IR`` +class supports the syntax ``for(auto node : ir)`` to iterate over every node. -``Functional::IR`` also keeps track of inputs, outputs and states. -By a "state" we mean a pair of a "current state" input and a "next state" output. -One such pair is created for every register and for every memory. -Every input, output and state has a name (equal to their name in RTLIL), a sort and a kind. -The kind field usually remains as the default value ``$input``, ``$output`` or ``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate auxiliary inputs/outputs/states that are given a different kind to distinguish them from ordinary RTLIL inputs/outputs/states. +``Functional::IR`` also keeps track of inputs, outputs and states. By a "state" +we mean a pair of a "current state" input and a "next state" output. One such +pair is created for every register and for every memory. Every input, output and +state has a name (equal to their name in RTLIL), a sort and a kind. The kind +field usually remains as the default value ``$input``, ``$output`` or +``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate +auxiliary inputs/outputs/states that are given a different kind to distinguish +them from ordinary RTLIL inputs/outputs/states. -- To access an individual input/output/state, use ``ir.input(name, kind)``, ``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to the default kind. -- To iterate over all inputs/outputs/states of a certain kind, methods ``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument defaults to the default kinds mentioned. -- To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``, ``ir.all_outputs`` and ``ir.all_states``. -- Outputs have a node that indicate the value of the output, this can be retrieved via ``output.value()``. -- States have a node that indicate the next value of the state, this can be retrieved via ``state.next_value()``. - They also have an initial value that is accessed as either ``state.initial_value_signal()`` or ``state.initial_value_memory()``, depending on their sort. +- To access an individual input/output/state, use ``ir.input(name, kind)``, + ``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to + the default kind. +- To iterate over all inputs/outputs/states of a certain kind, methods + ``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument + defaults to the default kinds mentioned. +- To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``, + ``ir.all_outputs`` and ``ir.all_states``. +- Outputs have a node that indicate the value of the output, this can be + retrieved via ``output.value()``. +- States have a node that indicate the next value of the state, this can be + retrieved via ``state.next_value()``. They also have an initial value that is + accessed as either ``state.initial_value_signal()`` or + ``state.initial_value_memory()``, depending on their sort. -Each node has a "function", which defines its operation (for a complete list of functions and a specification of their operation, see ``functional.h``). -Functions are represented as an enum ``Functional::Fn`` and the function field can be accessed as ``node.fn()``. -Since the most common operation is a switch over the function that also accesses the arguments, the ``Node`` class provides a method ``visit`` that implements the visitor pattern. -For example, for an addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)`` would call ``visitor.add(node, n1, n2)``. -Thus typically one would implement a class with a method for every function. -Visitors should inherit from either ``Functional::AbstractVisitor`` or ``Functional::DefaultVisitor``. -The former will produce a compiler error if a case is unhandled, the latter will call ``default_handler(node)`` instead. -Visitor methods should be marked as ``override`` to provide compiler errors if the arguments are wrong. +Each node has a "function", which defines its operation (for a complete list of +functions and a specification of their operation, see ``functional.h``). +Functions are represented as an enum ``Functional::Fn`` and the function field +can be accessed as ``node.fn()``. Since the most common operation is a switch +over the function that also accesses the arguments, the ``Node`` class provides +a method ``visit`` that implements the visitor pattern. For example, for an +addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)`` +would call ``visitor.add(node, n1, n2)``. Thus typically one would implement a +class with a method for every function. Visitors should inherit from either +``Functional::AbstractVisitor`` or +``Functional::DefaultVisitor``. The former will produce a compiler +error if a case is unhandled, the latter will call ``default_handler(node)`` +instead. Visitor methods should be marked as ``override`` to provide compiler +errors if the arguments are wrong. Utility classes ------------------ +~~~~~~~~~~~~~~~ -``functional.h`` also provides utility classes that are independent of the main FunctionalIR representation but are likely to be useful for backends. +``functional.h`` also provides utility classes that are independent of the main +FunctionalIR representation but are likely to be useful for backends. -``Functional::Writer`` provides a simple formatting class that wraps a ``std::ostream`` and provides the following methods: +``Functional::Writer`` provides a simple formatting class that wraps a +``std::ostream`` and provides the following methods: - ``writer << value`` wraps ``os << value``. -- ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``, ``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``, resp. - Each value is formatted using ``os << value``. - It is also possible to write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is equivalent to ``{1} {2} {3} {7} {8}``. -- ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the same as ``print`` but it uses ``os << fn(value)`` to print each value and falls back to ``os << value`` if ``fn(value)`` is not legal. +- ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``, + ``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``, + resp. Each value is formatted using ``os << value``. It is also possible to + write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is + equivalent to ``{1} {2} {3} {7} {8}``. +- ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the + same as ``print`` but it uses ``os << fn(value)`` to print each value and + falls back to ``os << value`` if ``fn(value)`` is not legal. -``Functional::Scope`` keeps track of variable names in a target language. -It is used to translate between different sets of legal characters and to avoid accidentally re-defining identifiers. -Users should derive a class from ``Scope`` and supply the following: +``Functional::Scope`` keeps track of variable names in a target language. It is +used to translate between different sets of legal characters and to avoid +accidentally re-defining identifiers. Users should derive a class from ``Scope`` +and supply the following: -- ``Scope`` takes a template argument that specifies a type that's used to uniquely distinguish variables. - Typically this would be ``int`` (if variables are used for ``Functional::IR`` nodes) or ``IdString``. -- The derived class should provide a constructor that calls ``reserve`` for every reserved word in the target language. -- A method ``bool is_legal_character(char c, int index)`` has to be provided that returns ``true`` iff ``c`` is legal in an identifier at position ``index``. +- ``Scope`` takes a template argument that specifies a type that's used to + uniquely distinguish variables. Typically this would be ``int`` (if variables + are used for ``Functional::IR`` nodes) or ``IdString``. +- The derived class should provide a constructor that calls ``reserve`` for + every reserved word in the target language. +- A method ``bool is_legal_character(char c, int index)`` has to be provided + that returns ``true`` iff ``c`` is legal in an identifier at position + ``index``. -Given an instance ``scope`` of the derived class, the following methods are then available: +Given an instance ``scope`` of the derived class, the following methods are then +available: - ``scope.reserve(std::string name)`` marks the given name as being in-use -- ``scope.unique_name(IdString suggestion)`` generates a previously unused name and attempts to make it similar to ``suggestion``. -- ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``, except that multiple calls with the same ``id`` are guaranteed to retrieve the same name (independent of ``suggestion``). +- ``scope.unique_name(IdString suggestion)`` generates a previously unused name + and attempts to make it similar to ``suggestion``. +- ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``, + except that multiple calls with the same ``id`` are guaranteed to retrieve the + same name (independent of ``suggestion``). ``sexpr.h`` provides classes that represent and pretty-print s-expressions. -S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr = SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x (mul y z))`` -(by adding ``using SExprUtil::list`` to the top of the file, ``list`` can be used as shorthand for ``SExpr::list``). -For prettyprinting, ``SExprWriter`` wraps an ``std::ostream`` and provides the following methods: +S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr += SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x +(mul y z))`` (by adding ``using SExprUtil::list`` to the top of the file, +``list`` can be used as shorthand for ``SExpr::list``). For prettyprinting, +``SExprWriter`` wraps an ``std::ostream`` and provides the following methods: -- ``writer << sexpr`` writes the provided expression to the output, breaking long lines and adding appropriate indentation. -- ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the last closing parenthesis. - Further arguments can then be added separately with ``<<`` or ``open``. - This allows for printing large s-expressions without needing to construct the whole expression in memory first. -- ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further arguments will not be indented. - This is used to avoid unlimited indentation on structures with unlimited nesting. +- ``writer << sexpr`` writes the provided expression to the output, breaking + long lines and adding appropriate indentation. +- ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the + last closing parenthesis. Further arguments can then be added separately with + ``<<`` or ``open``. This allows for printing large s-expressions without + needing to construct the whole expression in memory first. +- ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further + arguments will not be indented. This is used to avoid unlimited indentation on + structures with unlimited nesting. - ``writer.close(n = 1)`` closes the last ``n`` open s-expressions. -- ``writer.push()`` and ``writer.pop()`` are used to automatically close s-expressions. - ``writer.pop()`` closes all s-expressions opened since the last call to ``writer.push()``. +- ``writer.push()`` and ``writer.pop()`` are used to automatically close + s-expressions. ``writer.pop()`` closes all s-expressions opened since the last + call to ``writer.push()``. - ``writer.comment(string)`` writes a comment on a separate-line. - ``writer.comment(string, true)`` appends a comment to the last printed s-expression. -- ``writer.flush()`` flushes any buffering and should be called before any direct access to the underlying ``std::ostream``. It does not close unclosed parentheses. + ``writer.comment(string, true)`` appends a comment to the last printed + s-expression. +- ``writer.flush()`` flushes any buffering and should be called before any + direct access to the underlying ``std::ostream``. It does not close unclosed + parentheses. - The destructor calls ``flush`` but also closes all unclosed parentheses. From 23fa9b201421dc95642ba96f0cbda45bcf32e75d Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:30:29 +1300 Subject: [PATCH 012/123] functional_ir.rst: Initial skeleton for Rosette --- .../extending_yosys/functional_ir.rst | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 11f5c579f..30fc63899 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -143,3 +143,73 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr direct access to the underlying ``std::ostream``. It does not close unclosed parentheses. - The destructor calls ``flush`` but also closes all unclosed parentheses. + +Example: Adapting SMT-LIB backend for Rosette +--------------------------------------------- + +Overview +~~~~~~~~ + +- What is Rosette + - Racket package + - solver-aided programming language + - uses symbolic expressions + - able to perform functional verification + +- link to file (both smtlib and rosette) + +Scope +~~~~~ + +- as above, prevents namespace collision +- reserved keywords + - language (Racket) keywords + - output keywords + +- ``is_character_legal`` + +Sort +~~~~ + +- map variable declarations to s-expressions +- handles signals (bitvectors) and memories (arrays of bitvectors) + +Struct +~~~~~~ + +- helpers for defining inputs/outputs/state + - each is a single (transparent) struct with zero or more fields + - each field has a name, with the type (sort) as a comment + - struct fields in Rosette are accessed as ``-`` + - field names only need to be unique within the struct, while accessors + are unique within the module + +- writing outputs/next state + +PrintVisitor +~~~~~~~~~~~~ + +- map FunctionalIR operations to Rosette + - most functions are the same or very similar to their corresponding smtlib + function + +- reading inputs/current state + +Module +~~~~~~ + +- map RTLIL module to FunctionalIR +- iterate over FunctionalIR and map to Rosette + - defines the mapping function, ``(inputs, current_state) -> (outputs, + next_state)`` + +Backend +~~~~~~~ + +- registers the `write_functional_rosette` command +- options (``-provides``) + - allows file to be treated as a Racket package with structs and mapping + function available for use externally + +- opens and prepares file for writing +- iterates over modules in design From f4c7377ac1637b6159ac7f7eb1b40fcb1048a4ab Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:30:55 +1300 Subject: [PATCH 013/123] functional_ir.rst: Fix typo --- docs/source/yosys_internals/extending_yosys/functional_ir.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 30fc63899..7a3799ac1 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -102,7 +102,7 @@ and supply the following: are used for ``Functional::IR`` nodes) or ``IdString``. - The derived class should provide a constructor that calls ``reserve`` for every reserved word in the target language. -- A method ``bool is_legal_character(char c, int index)`` has to be provided +- A method ``bool is_character_legal(char c, int index)`` has to be provided that returns ``true`` iff ``c`` is legal in an identifier at position ``index``. From 7698dfba5e14dc766c387b5fca2e4e3554ad2b20 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:31:50 +1300 Subject: [PATCH 014/123] smtr: Fix help text Can't take both [selection] and [filename] optional arguments. --- backends/functional/smtlib_rosette.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/functional/smtlib_rosette.cc b/backends/functional/smtlib_rosette.cc index ea14da854..a93bd04b0 100644 --- a/backends/functional/smtlib_rosette.cc +++ b/backends/functional/smtlib_rosette.cc @@ -267,7 +267,7 @@ struct FunctionalSmtrBackend : public Backend { void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" write_functional_rosette [options] [selection] [filename]\n"); + log(" write_functional_rosette [options] [filename]\n"); log("\n"); log("Functional Rosette Backend.\n"); log("\n"); From 242c03715810319928b5917ccade7a1df3314397 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 24 Jan 2025 13:10:34 +1300 Subject: [PATCH 015/123] docs/rosette: Add overview --- .../extending_yosys/functional_ir.rst | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 7a3799ac1..662f3df8b 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -147,16 +147,29 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr Example: Adapting SMT-LIB backend for Rosette --------------------------------------------- +This section will walk through the process of adapting the SMT-LIB functional +backend (`write_functional_smt2`) to work with another s-expression target, +`Rosette`_. + +.. _Rosette: http://emina.github.io/rosette/ + Overview ~~~~~~~~ -- What is Rosette - - Racket package - - solver-aided programming language - - uses symbolic expressions - - able to perform functional verification + Rosette is a solver-aided programming language that extends `Racket`_ with + language constructs for program synthesis, verification, and more. To verify + or synthesize code, Rosette compiles it to logical constraints solved with + off-the-shelf `SMT`_ solvers. -- link to file (both smtlib and rosette) + -- https://emina.github.io/rosette/ + +.. _Racket: http://racket-lang.org/ +.. _SMT: http://smtlib.cs.uiowa.edu/ + +Full code listings for the initial SMT-LIB backend and the converted Rosette +backend are included in the Yosys source repository under +:file:`backends/functional` as ``smtlib.cc`` and ``smtlib_rosette.cc`` +respectively. Scope ~~~~~ From 08394c51a2fb99caf52394931e91120f2a1aae07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 13 Dec 2024 18:59:35 +0100 Subject: [PATCH 016/123] rtlil: Add const append helper --- kernel/rtlil.cc | 6 ++++++ kernel/rtlil.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index cb0f7da78..d705813d9 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -540,6 +540,12 @@ void RTLIL::Const::bitvectorize() const { } } +void RTLIL::Const::append(const RTLIL::Const &other) { + bitvectorize(); + bitvectype& bv = get_bits(); + bv.insert(bv.end(), other.begin(), other.end()); +} + RTLIL::State RTLIL::Const::const_iterator::operator*() const { if (auto bv = parent.get_if_bits()) return (*bv)[idx]; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f9cacd151..33742cb70 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -738,6 +738,8 @@ public: bool empty() const; void bitvectorize() const; + void append(const RTLIL::Const &other); + class const_iterator { private: const Const& parent; From c5fd96ebb0c9067927bd4df381af92783bb4b547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 13 Dec 2024 19:01:41 +0100 Subject: [PATCH 017/123] macc_v2: Start new cell --- kernel/constids.inc | 4 ++ kernel/macc.h | 102 ++++++++++++++++++---------- kernel/rtlil.cc | 29 ++++++++ passes/techmap/maccmap.cc | 2 +- tests/alumacc/macc_infer_n_unmap.ys | 19 ++++++ 5 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 tests/alumacc/macc_infer_n_unmap.ys diff --git a/kernel/constids.inc b/kernel/constids.inc index d68e2dfe6..15d3cc83b 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -276,3 +276,7 @@ X(Y) X(Y_WIDTH) X(area) X(capacitance) +X(NTERMS) +X(TERM_NEGATED) +X(A_WIDTHS) +X(B_WIDTHS) diff --git a/kernel/macc.h b/kernel/macc.h index 55940769d..e969dd86d 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -82,7 +82,7 @@ struct Macc new_ports.swap(ports); } - void from_cell(RTLIL::Cell *cell) + void from_cell_v1(RTLIL::Cell *cell) { RTLIL::SigSpec port_a = cell->getPort(ID::A); @@ -136,52 +136,82 @@ struct Macc log_assert(port_a_cursor == GetSize(port_a)); } - void to_cell(RTLIL::Cell *cell) const + void from_cell(RTLIL::Cell *cell) { - RTLIL::SigSpec port_a; - std::vector config_bits; - int max_size = 0, num_bits = 0; - - for (auto &port : ports) { - max_size = max(max_size, GetSize(port.in_a)); - max_size = max(max_size, GetSize(port.in_b)); + if (cell->type == ID($macc)) { + from_cell_v1(cell); + return; } + log_assert(cell->type == ID($macc_v2)); - while (max_size) - num_bits++, max_size /= 2; + RTLIL::SigSpec port_a = cell->getPort(ID::A); + RTLIL::SigSpec port_b = cell->getPort(ID::B); - log_assert(num_bits < 16); - config_bits.push_back(num_bits & 1 ? State::S1 : State::S0); - config_bits.push_back(num_bits & 2 ? State::S1 : State::S0); - config_bits.push_back(num_bits & 4 ? State::S1 : State::S0); - config_bits.push_back(num_bits & 8 ? State::S1 : State::S0); + ports.clear(); - for (auto &port : ports) - { - if (GetSize(port.in_a) == 0) - continue; + int nterms = cell->getParam(ID::NTERMS).as_int(); + const Const &neg = cell->getParam(ID::TERM_NEGATED); + const Const &a_widths = cell->getParam(ID::A_WIDTHS); + const Const &b_widths = cell->getParam(ID::B_WIDTHS); + const Const &a_signed = cell->getParam(ID::A_SIGNED); + const Const &b_signed = cell->getParam(ID::B_SIGNED); - config_bits.push_back(port.is_signed ? State::S1 : State::S0); - config_bits.push_back(port.do_subtract ? State::S1 : State::S0); + int ai = 0, bi = 0; + for (int i = 0; i < nterms; i++) { + port_t term; - int size_a = GetSize(port.in_a); - for (int i = 0; i < num_bits; i++) - config_bits.push_back(size_a & (1 << i) ? State::S1 : State::S0); + log_assert(a_signed[i] == b_signed[i]); + term.is_signed = (a_signed[i] == State::S1); + int a_width = a_widths.extract(16 * i, 16).as_int(false); + int b_width = b_widths.extract(16 * i, 16).as_int(false); - int size_b = GetSize(port.in_b); - for (int i = 0; i < num_bits; i++) - config_bits.push_back(size_b & (1 << i) ? State::S1 : State::S0); + term.in_a = port_a.extract(ai, a_width); + ai += a_width; + term.in_b = port_b.extract(bi, b_width); + bi += b_width; + term.do_subtract = (neg[i] == State::S1); - port_a.append(port.in_a); - port_a.append(port.in_b); + ports.push_back(term); } + log_assert(port_a.size() == ai); + log_assert(port_b.size() == bi); + } - cell->setPort(ID::A, port_a); - cell->setPort(ID::B, {}); - cell->setParam(ID::CONFIG, config_bits); - cell->setParam(ID::CONFIG_WIDTH, GetSize(config_bits)); - cell->setParam(ID::A_WIDTH, GetSize(port_a)); - cell->setParam(ID::B_WIDTH, 0); + void to_cell(RTLIL::Cell *cell) + { + cell->type = ID($macc_v2); + + int nterms = ports.size(); + const auto Sx = State::Sx; + Const a_signed(Sx, nterms), b_signed(Sx, nterms), negated(Sx, nterms); + Const a_widths, b_widths; + SigSpec a, b; + + for (int i = 0; i < nterms; i++) { + SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b; + + a_widths.append(Const(term_a.size(), 16)); + b_widths.append(Const(term_b.size(), 16)); + + a_signed.bits()[i] = b_signed.bits()[i] = + (ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + negated.bits()[i] = (ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + + a.append(term_a); + b.append(term_b); + } + negated.is_fully_def(); + a_signed.is_fully_def(); + b_signed.is_fully_def(); + + cell->setParam(ID::NTERMS, nterms); + cell->setParam(ID::TERM_NEGATED, negated); + cell->setParam(ID::A_SIGNED, a_signed); + cell->setParam(ID::B_SIGNED, b_signed); + cell->setParam(ID::A_WIDTHS, a_widths); + cell->setParam(ID::B_WIDTHS, b_widths); + cell->setPort(ID::A, a); + cell->setPort(ID::B, b); } bool eval(RTLIL::Const &result) const diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index d705813d9..ac491f4cd 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1467,6 +1467,30 @@ namespace { return; } + if (cell->type == ID($macc_v2)) { + if (param(ID::NTERMS) <= 0) + error(__LINE__); + param_bits(ID::TERM_NEGATED, param(ID::NTERMS)); + param_bits(ID::A_SIGNED, param(ID::NTERMS)); + param_bits(ID::B_SIGNED, param(ID::NTERMS)); + if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) + error(__LINE__); + param_bits(ID::A_WIDTHS, param(ID::NTERMS) * 16); + param_bits(ID::B_WIDTHS, param(ID::NTERMS) * 16); + const Const &a_width = cell->getParam(ID::A_WIDTHS); + const Const &b_width = cell->getParam(ID::B_WIDTHS); + int a_width_sum = 0, b_width_sum = 0; + for (int i = 0; i < param(ID::NTERMS); i++) { + a_width_sum += a_width.extract(16 * i, 16).as_int(false); + b_width_sum += b_width.extract(16 * i, 16).as_int(false); + } + port(ID::A, a_width_sum); + port(ID::B, b_width_sum); + port(ID::Y, param(ID::Y_WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($logic_not)) { param_bool(ID::A_SIGNED); port(ID::A, param(ID::A_WIDTH)); @@ -4099,6 +4123,11 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) return; } + if (type == ID($macc_v2)) { + parameters[ID::Y_WIDTH] = GetSize(connections_[ID::Y]); + return; + } + bool signedness_ab = !type.in(ID($slice), ID($concat), ID($macc)); if (connections_.count(ID::A)) { diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 911d66cfa..3dde92438 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -403,7 +403,7 @@ struct MaccmapPass : public Pass { for (auto mod : design->selected_modules()) for (auto cell : mod->selected_cells()) - if (cell->type == ID($macc)) { + if (cell->type.in(ID($macc), ID($macc_v2))) { log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type)); maccmap(mod, cell, unmap_mode); mod->remove(cell); diff --git a/tests/alumacc/macc_infer_n_unmap.ys b/tests/alumacc/macc_infer_n_unmap.ys new file mode 100644 index 000000000..569511b64 --- /dev/null +++ b/tests/alumacc/macc_infer_n_unmap.ys @@ -0,0 +1,19 @@ +read_verilog < Date: Fri, 10 Jan 2025 11:44:54 +0100 Subject: [PATCH 018/123] macc_v2: Add C port --- kernel/constids.inc | 8 +++-- kernel/macc.h | 79 +++++++++++++++++++++++++++++++-------------- kernel/rtlil.cc | 26 ++++++++++----- 3 files changed, 78 insertions(+), 35 deletions(-) diff --git a/kernel/constids.inc b/kernel/constids.inc index 15d3cc83b..4fdbb3dc8 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -276,7 +276,11 @@ X(Y) X(Y_WIDTH) X(area) X(capacitance) -X(NTERMS) -X(TERM_NEGATED) +X(NPRODUCTS) +X(NADDENDS) +X(PRODUCT_NEGATED) +X(ADDEND_NEGATED) X(A_WIDTHS) X(B_WIDTHS) +X(C_WIDTHS) +X(C_SIGNED) diff --git a/kernel/macc.h b/kernel/macc.h index e969dd86d..fdd738d45 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -146,18 +146,18 @@ struct Macc RTLIL::SigSpec port_a = cell->getPort(ID::A); RTLIL::SigSpec port_b = cell->getPort(ID::B); + RTLIL::SigSpec port_c = cell->getPort(ID::C); ports.clear(); - int nterms = cell->getParam(ID::NTERMS).as_int(); - const Const &neg = cell->getParam(ID::TERM_NEGATED); + int nproducts = cell->getParam(ID::NPRODUCTS).as_int(); + const Const &product_neg = cell->getParam(ID::PRODUCT_NEGATED); const Const &a_widths = cell->getParam(ID::A_WIDTHS); const Const &b_widths = cell->getParam(ID::B_WIDTHS); const Const &a_signed = cell->getParam(ID::A_SIGNED); const Const &b_signed = cell->getParam(ID::B_SIGNED); - int ai = 0, bi = 0; - for (int i = 0; i < nterms; i++) { + for (int i = 0; i < nproducts; i++) { port_t term; log_assert(a_signed[i] == b_signed[i]); @@ -169,49 +169,78 @@ struct Macc ai += a_width; term.in_b = port_b.extract(bi, b_width); bi += b_width; - term.do_subtract = (neg[i] == State::S1); + term.do_subtract = (product_neg[i] == State::S1); ports.push_back(term); } log_assert(port_a.size() == ai); log_assert(port_b.size() == bi); + + int naddends = cell->getParam(ID::NADDENDS).as_int(); + const Const &addend_neg = cell->getParam(ID::ADDEND_NEGATED); + const Const &c_widths = cell->getParam(ID::C_WIDTHS); + const Const &c_signed = cell->getParam(ID::C_SIGNED); + int ci = 0; + for (int i = 0; i < naddends; i++) { + port_t term; + + term.is_signed = (c_signed[i] == State::S1); + int c_width = c_widths.extract(16 * i, 16).as_int(false); + + term.in_a = port_c.extract(ci, c_width); + ci += c_width; + term.do_subtract = (addend_neg[i] == State::S1); + + ports.push_back(term); + } + log_assert(port_c.size() == ci); } void to_cell(RTLIL::Cell *cell) { cell->type = ID($macc_v2); - int nterms = ports.size(); - const auto Sx = State::Sx; - Const a_signed(Sx, nterms), b_signed(Sx, nterms), negated(Sx, nterms); - Const a_widths, b_widths; - SigSpec a, b; + int nproducts = 0, naddends = 0; + Const a_signed, b_signed, a_widths, b_widths, product_negated; + Const c_signed, c_widths, addend_negated; + SigSpec a, b, c; - for (int i = 0; i < nterms; i++) { + for (int i = 0; i < (int) ports.size(); i++) { SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b; - a_widths.append(Const(term_a.size(), 16)); - b_widths.append(Const(term_b.size(), 16)); - - a_signed.bits()[i] = b_signed.bits()[i] = - (ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - negated.bits()[i] = (ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); - - a.append(term_a); - b.append(term_b); + if (term_b.empty()) { + // addend + c_widths.append(Const(term_a.size(), 16)); + c_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + addend_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + c.append(term_a); + naddends++; + } else { + // product + a_widths.append(Const(term_a.size(), 16)); + b_widths.append(Const(term_b.size(), 16)); + a_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + b_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + product_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + a.append(term_a); + b.append(term_b); + nproducts++; + } } - negated.is_fully_def(); - a_signed.is_fully_def(); - b_signed.is_fully_def(); - cell->setParam(ID::NTERMS, nterms); - cell->setParam(ID::TERM_NEGATED, negated); + cell->setParam(ID::NPRODUCTS, nproducts); + cell->setParam(ID::PRODUCT_NEGATED, product_negated); + cell->setParam(ID::NADDENDS, naddends); + cell->setParam(ID::ADDEND_NEGATED, addend_negated); cell->setParam(ID::A_SIGNED, a_signed); cell->setParam(ID::B_SIGNED, b_signed); + cell->setParam(ID::C_SIGNED, c_signed); cell->setParam(ID::A_WIDTHS, a_widths); cell->setParam(ID::B_WIDTHS, b_widths); + cell->setParam(ID::C_WIDTHS, c_widths); cell->setPort(ID::A, a); cell->setPort(ID::B, b); + cell->setPort(ID::C, c); } bool eval(RTLIL::Const &result) const diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index ac491f4cd..f2f36ba30 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1468,24 +1468,34 @@ namespace { } if (cell->type == ID($macc_v2)) { - if (param(ID::NTERMS) <= 0) + if (param(ID::NPRODUCTS) <= 0) error(__LINE__); - param_bits(ID::TERM_NEGATED, param(ID::NTERMS)); - param_bits(ID::A_SIGNED, param(ID::NTERMS)); - param_bits(ID::B_SIGNED, param(ID::NTERMS)); + if (param(ID::NADDENDS) <= 0) + error(__LINE__); + param_bits(ID::PRODUCT_NEGATED, param(ID::NPRODUCTS)); + param_bits(ID::ADDEND_NEGATED, param(ID::NADDENDS)); + param_bits(ID::A_SIGNED, param(ID::NPRODUCTS)); + param_bits(ID::B_SIGNED, param(ID::NPRODUCTS)); + param_bits(ID::C_SIGNED, param(ID::NADDENDS)); if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) error(__LINE__); - param_bits(ID::A_WIDTHS, param(ID::NTERMS) * 16); - param_bits(ID::B_WIDTHS, param(ID::NTERMS) * 16); + param_bits(ID::A_WIDTHS, param(ID::NPRODUCTS) * 16); + param_bits(ID::B_WIDTHS, param(ID::NPRODUCTS) * 16); + param_bits(ID::C_WIDTHS, param(ID::NADDENDS) * 16); const Const &a_width = cell->getParam(ID::A_WIDTHS); const Const &b_width = cell->getParam(ID::B_WIDTHS); - int a_width_sum = 0, b_width_sum = 0; - for (int i = 0; i < param(ID::NTERMS); i++) { + const Const &c_width = cell->getParam(ID::C_WIDTHS); + int a_width_sum = 0, b_width_sum = 0, c_width_sum = 0; + for (int i = 0; i < param(ID::NPRODUCTS); i++) { a_width_sum += a_width.extract(16 * i, 16).as_int(false); b_width_sum += b_width.extract(16 * i, 16).as_int(false); } + for (int i = 0; i < param(ID::NADDENDS); i++) { + c_width_sum += c_width.extract(16 * i, 16).as_int(false); + } port(ID::A, a_width_sum); port(ID::B, b_width_sum); + port(ID::C, c_width_sum); port(ID::Y, param(ID::Y_WIDTH)); check_expected(); return; From 61450e8b6eb246342971fb0460e1d3ce0091c6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 10 Jan 2025 13:42:26 +0100 Subject: [PATCH 019/123] Update codebase for macc_v2 --- docs/source/cell/word_arith.rst | 2 +- docs/source/code_examples/fifo/fifo.ys | 2 +- docs/source/getting_started/example_synth.rst | 2 +- kernel/celledges.cc | 2 +- kernel/celltypes.h | 1 + kernel/consteval.h | 2 +- kernel/rtlil.cc | 16 ++++++++-------- kernel/satgen.cc | 2 +- passes/techmap/booth.cc | 2 +- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/source/cell/word_arith.rst b/docs/source/cell/word_arith.rst index 49070814a..8e3daa27f 100644 --- a/docs/source/cell/word_arith.rst +++ b/docs/source/cell/word_arith.rst @@ -1,7 +1,7 @@ Coarse arithmetics ------------------ -.. todo:: Add information about `$alu`, `$fa`, and `$lcu` cells. +.. todo:: Add information about `$alu`, `$fa`, `$macc_v2`, and `$lcu` cells. The `$macc` cell type represents a generalized multiply and accumulate operation. The cell is purely combinational. It outputs the result of summing up diff --git a/docs/source/code_examples/fifo/fifo.ys b/docs/source/code_examples/fifo/fifo.ys index 57a28e63e..e6b9bf69d 100644 --- a/docs/source/code_examples/fifo/fifo.ys +++ b/docs/source/code_examples/fifo/fifo.ys @@ -67,7 +67,7 @@ show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdat # ======================================================== alumacc -select -set new_cells t:$alu t:$macc +select -set new_cells t:$alu t:$macc_v2 show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci* # ======================================================== diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index 189eaddfa..e215586cc 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -523,7 +523,7 @@ That brings us to the fourth and final part for the iCE40 synthesis flow: :name: synth_coarse4 Where before each type of arithmetic operation had its own cell, e.g. `$add`, we -now want to extract these into `$alu` and `$macc` cells which can help identify +now want to extract these into `$alu` and `$macc_v2` cells which can help identify opportunities for reusing logic. We do this by running `alumacc`, which we can see produce the following changes in our example design: diff --git a/kernel/celledges.cc b/kernel/celledges.cc index bad7124d9..95f160612 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -453,7 +453,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL } // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat - // FIXME: $lut $sop $alu $lcu $macc $fa + // FIXME: $lut $sop $alu $lcu $macc $macc_v2 $fa // FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx // FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 3167a9add..0ce5db54d 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -144,6 +144,7 @@ struct CellTypes setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true); setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true); + setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, true); setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); } diff --git a/kernel/consteval.h b/kernel/consteval.h index 331d8f128..05e94ab86 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -310,7 +310,7 @@ struct ConstEval } } } - else if (cell->type == ID($macc)) + else if (cell->type.in(ID($macc), ID($macc_v2))) { Macc macc; macc.from_cell(cell); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index f2f36ba30..29a7ebf3e 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1472,16 +1472,16 @@ namespace { error(__LINE__); if (param(ID::NADDENDS) <= 0) error(__LINE__); - param_bits(ID::PRODUCT_NEGATED, param(ID::NPRODUCTS)); - param_bits(ID::ADDEND_NEGATED, param(ID::NADDENDS)); - param_bits(ID::A_SIGNED, param(ID::NPRODUCTS)); - param_bits(ID::B_SIGNED, param(ID::NPRODUCTS)); - param_bits(ID::C_SIGNED, param(ID::NADDENDS)); + param_bits(ID::PRODUCT_NEGATED, min(param(ID::NPRODUCTS), 1)); + param_bits(ID::ADDEND_NEGATED, min(param(ID::NADDENDS), 1)); + param_bits(ID::A_SIGNED, min(param(ID::NPRODUCTS), 1)); + param_bits(ID::B_SIGNED, min(param(ID::NPRODUCTS), 1)); + param_bits(ID::C_SIGNED, min(param(ID::NADDENDS), 1)); if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) error(__LINE__); - param_bits(ID::A_WIDTHS, param(ID::NPRODUCTS) * 16); - param_bits(ID::B_WIDTHS, param(ID::NPRODUCTS) * 16); - param_bits(ID::C_WIDTHS, param(ID::NADDENDS) * 16); + param_bits(ID::A_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::B_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::C_WIDTHS, min(param(ID::NADDENDS) * 16, 1)); const Const &a_width = cell->getParam(ID::A_WIDTHS); const Const &b_width = cell->getParam(ID::B_WIDTHS); const Const &c_width = cell->getParam(ID::C_WIDTHS); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index dd15b51f3..9e5fa9111 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -740,7 +740,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (cell->type == ID($macc)) + if (cell->type.in(ID($macc), ID($macc_v2))) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); diff --git a/passes/techmap/booth.cc b/passes/techmap/booth.cc index dce7da486..cd9012e6c 100644 --- a/passes/techmap/booth.cc +++ b/passes/techmap/booth.cc @@ -218,7 +218,7 @@ struct BoothPassWorker { log_assert(cell->getParam(ID::A_SIGNED).as_bool() == cell->getParam(ID::B_SIGNED).as_bool()); is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - } else if (cell->type == ID($macc)) { + } else if (cell->type.in(ID($macc), ID($macc_v2))) { Macc macc; macc.from_cell(cell); From 1e8aa56f7fdcf7510159927bef9b81040354c119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 10 Jan 2025 13:43:54 +0100 Subject: [PATCH 020/123] macc_v2: Init simlib model --- techlibs/common/simlib.v | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index df9e1feb1..248c93513 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1207,6 +1207,111 @@ end endmodule +// -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $macc_v2 (A, B, C, Y) +//* group arith +//- +//- Multiply and add. +//- This cell represents a generic fused multiply-add operation, it supersedes the +//- earlier $macc cell. +//- +module \$macc_v2 (A, B, C, Y); + +parameter NPRODUCTS = 0; +parameter NADDENDS = 0; +parameter A_WIDTHS = 16'h0000; +parameter B_WIDTHS = 16'h0000; +parameter C_WIDTHS = 16'h0000; +parameter Y_WIDTH = 0; + +parameter PRODUCT_NEGATED = 1'bx; +parameter ADDEND_NEGATED = 1'bx; +parameter A_SIGNED = 1'bx; +parameter B_SIGNED = 1'bx; +parameter C_SIGNED = 1'bx; + +function integer sum_widths1; + input [(16*NPRODUCTS)-1:0] widths; + int i; + sum_widths1 = 0; + begin + for (i = 0; i < NPRODUCTS; i++) begin + sum_widths1 += widths[16*i+:16]; + end + end +endfunction + +function integer sum_widths2; + input [(16*NADDENDS)-1:0] widths; + int i; + sum_widths2 = 0; + begin + for (i = 0; i < NADDENDS; i++) begin + sum_widths2 += widths[16*i+:16]; + end + end +endfunction + +input [sum_widths1(A_WIDTHS)-1:0] A; // concatenation of LHS factors +input [sum_widths1(B_WIDTHS)-1:0] B; // concatenation of RHS factors +input [sum_widths2(C_WIDTHS)-1:0] C; // concatenation of summands +output reg [Y_WIDTH-1:0] Y; // output sum + +integer i, j, ai, bi, ci, aw, bw, cw; +reg [Y_WIDTH-1:0] product; +reg signed [Y_WIDTH-1:0] product_signed; +reg [Y_WIDTH-1:0] addend; +reg signed [Y_WIDTH-1:0] addend_signed; + +always @* begin + Y = 0; + ai = 0; + bi = 0; + for (i = 0; i < NPRODUCTS; i = i+1) + begin + aw = A_WIDTHS[16*i+:16]; + bw = B_WIDTHS[16*i+:16]; + + product = A[ai +: aw] * B[bi +: bw]; + product_signed = $signed(A[ai +: aw]) * $signed(B[bi +: bw]); + + // A_SIGNED[i] == B_SIGNED[i] as RTLIL invariant + if (A_SIGNED[i] && B_SIGNED[i]) + product = product_signed; + + if (PRODUCT_NEGATED[i]) + Y = Y - product; + else + Y = Y + product; + + ai += aw; + bi += bw; + end + + ci = 0; + for (i = 0; i < NADDENDS; i = i+1) + begin + cw = C_WIDTHS[16*i+:16]; + + addend = C[ci +: cw]; + addend_signed = $signed(C[ci +: cw]); + + if (C_SIGNED[i]) + addend = addend_signed; + + if (ADDEND_NEGATED[i]) + Y = Y - addend; + else + Y = Y + addend; + + ci += cw; + end +end + +endmodule + // -------------------------------------------------------- //* ver 2 //* title Divider From cb8f855f347da751d65eb002f80247d107b25e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 24 Jan 2025 12:35:23 +0100 Subject: [PATCH 021/123] rtlil: Fix macc_v2 cell check --- kernel/rtlil.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 29a7ebf3e..c10e879c5 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1472,16 +1472,16 @@ namespace { error(__LINE__); if (param(ID::NADDENDS) <= 0) error(__LINE__); - param_bits(ID::PRODUCT_NEGATED, min(param(ID::NPRODUCTS), 1)); - param_bits(ID::ADDEND_NEGATED, min(param(ID::NADDENDS), 1)); - param_bits(ID::A_SIGNED, min(param(ID::NPRODUCTS), 1)); - param_bits(ID::B_SIGNED, min(param(ID::NPRODUCTS), 1)); - param_bits(ID::C_SIGNED, min(param(ID::NADDENDS), 1)); + param_bits(ID::PRODUCT_NEGATED, max(param(ID::NPRODUCTS), 1)); + param_bits(ID::ADDEND_NEGATED, max(param(ID::NADDENDS), 1)); + param_bits(ID::A_SIGNED, max(param(ID::NPRODUCTS), 1)); + param_bits(ID::B_SIGNED, max(param(ID::NPRODUCTS), 1)); + param_bits(ID::C_SIGNED, max(param(ID::NADDENDS), 1)); if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) error(__LINE__); - param_bits(ID::A_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); - param_bits(ID::B_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); - param_bits(ID::C_WIDTHS, min(param(ID::NADDENDS) * 16, 1)); + param_bits(ID::A_WIDTHS, max(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::B_WIDTHS, max(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::C_WIDTHS, max(param(ID::NADDENDS) * 16, 1)); const Const &a_width = cell->getParam(ID::A_WIDTHS); const Const &b_width = cell->getParam(ID::B_WIDTHS); const Const &c_width = cell->getParam(ID::C_WIDTHS); From 3184b335dacc42dc4631931058630a04c4bcf277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 24 Jan 2025 12:35:36 +0100 Subject: [PATCH 022/123] macc_v2: Fix language constructs in simlib model --- techlibs/common/simlib.v | 53 +++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 248c93513..028e4e5a9 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1234,22 +1234,22 @@ parameter C_SIGNED = 1'bx; function integer sum_widths1; input [(16*NPRODUCTS)-1:0] widths; - int i; - sum_widths1 = 0; + integer i; begin + sum_widths1 = 0; for (i = 0; i < NPRODUCTS; i++) begin - sum_widths1 += widths[16*i+:16]; + sum_widths1 = sum_widths1 + widths[16*i+:16]; end end endfunction function integer sum_widths2; input [(16*NADDENDS)-1:0] widths; - int i; - sum_widths2 = 0; + integer i; begin + sum_widths2 = 0; for (i = 0; i < NADDENDS; i++) begin - sum_widths2 += widths[16*i+:16]; + sum_widths2 = sum_widths2 + widths[16*i+:16]; end end endfunction @@ -1261,9 +1261,7 @@ output reg [Y_WIDTH-1:0] Y; // output sum integer i, j, ai, bi, ci, aw, bw, cw; reg [Y_WIDTH-1:0] product; -reg signed [Y_WIDTH-1:0] product_signed; -reg [Y_WIDTH-1:0] addend; -reg signed [Y_WIDTH-1:0] addend_signed; +reg [Y_WIDTH-1:0] addend, oper_a, oper_b; always @* begin Y = 0; @@ -1274,20 +1272,29 @@ always @* begin aw = A_WIDTHS[16*i+:16]; bw = B_WIDTHS[16*i+:16]; - product = A[ai +: aw] * B[bi +: bw]; - product_signed = $signed(A[ai +: aw]) * $signed(B[bi +: bw]); - + oper_a = 0; + oper_b = 0; + for (j = 0; j < Y_WIDTH && j < aw; j = j + 1) + oper_a[j] = A[ai + j]; + for (j = 0; j < Y_WIDTH && j < bw; j = j + 1) + oper_b[j] = B[bi + j]; // A_SIGNED[i] == B_SIGNED[i] as RTLIL invariant - if (A_SIGNED[i] && B_SIGNED[i]) - product = product_signed; + if (A_SIGNED[i] && B_SIGNED[i]) begin + for (j = aw; j > 0 && j < Y_WIDTH; j = j + 1) + oper_a[j] = oper_a[j - 1]; + for (j = bw; j > 0 && j < Y_WIDTH; j = j + 1) + oper_b[j] = oper_b[j - 1]; + end + + product = oper_a * oper_b; if (PRODUCT_NEGATED[i]) Y = Y - product; else Y = Y + product; - ai += aw; - bi += bw; + ai = ai + aw; + bi = bi + bw; end ci = 0; @@ -1295,18 +1302,20 @@ always @* begin begin cw = C_WIDTHS[16*i+:16]; - addend = C[ci +: cw]; - addend_signed = $signed(C[ci +: cw]); - - if (C_SIGNED[i]) - addend = addend_signed; + addend = 0; + for (j = 0; j < Y_WIDTH && j < cw; j = j + 1) + addend[j] = C[ci + j]; + if (C_SIGNED[i]) begin + for (j = cw; j > 0 && j < Y_WIDTH; j = j + 1) + addend[j] = addend[j - 1]; + end if (ADDEND_NEGATED[i]) Y = Y - addend; else Y = Y + addend; - ci += cw; + ci = ci + cw; end end From 1cf8e7c7db1be4c6b26bea796663581de21dd019 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Fri, 24 Jan 2025 18:48:09 +0100 Subject: [PATCH 023/123] add ioff inference for qlf_k6n10f --- techlibs/quicklogic/Makefile.inc | 3 +- techlibs/quicklogic/ql_ioff.cc | 82 +++++++++++++++++++++ techlibs/quicklogic/synth_quicklogic.cc | 13 +++- tests/arch/quicklogic/qlf_k6n10f/ioff.ys | 91 ++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 techlibs/quicklogic/ql_ioff.cc create mode 100644 tests/arch/quicklogic/qlf_k6n10f/ioff.ys diff --git a/techlibs/quicklogic/Makefile.inc b/techlibs/quicklogic/Makefile.inc index ade144371..a54a7ec03 100644 --- a/techlibs/quicklogic/Makefile.inc +++ b/techlibs/quicklogic/Makefile.inc @@ -6,6 +6,7 @@ OBJS += techlibs/quicklogic/ql_bram_merge.o OBJS += techlibs/quicklogic/ql_bram_types.o OBJS += techlibs/quicklogic/ql_dsp_simd.o OBJS += techlibs/quicklogic/ql_dsp_io_regs.o +OBJS += techlibs/quicklogic/ql_ioff.o # -------------------------------------- @@ -40,4 +41,4 @@ $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_final_map.v)) $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/TDP18K_FIFO.v)) $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/ufifo_ctl.v)) -$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/sram1024x18_mem.v)) \ No newline at end of file +$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/sram1024x18_mem.v)) diff --git a/techlibs/quicklogic/ql_ioff.cc b/techlibs/quicklogic/ql_ioff.cc new file mode 100644 index 000000000..c857fc35d --- /dev/null +++ b/techlibs/quicklogic/ql_ioff.cc @@ -0,0 +1,82 @@ +#include "kernel/log.h" +#include "kernel/modtools.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct QlIoffPass : public Pass { + QlIoffPass() : Pass("ql_ioff", "Infer I/O FFs for qlf_k6n10f architecture") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ql_ioff [selection]\n"); + log("\n"); + log("This pass promotes qlf_k6n10f registers directly connected to a top-level I/O\n"); + log("port to I/O FFs.\n"); + log("\n"); + } + + void execute(std::vector, RTLIL::Design *design) override + { + log_header(design, "Executing QL_IOFF pass.\n"); + + ModWalker modwalker(design); + Module *module = design->top_module(); + if (!module) + return; + modwalker.setup(module); + pool cells_to_replace; + for (auto cell : module->selected_cells()) { + if (cell->type.in(ID(dffsre), ID(sdffsre))) { + bool e_const = cell->getPort(ID::E).is_fully_ones(); + bool r_const = cell->getPort(ID::R).is_fully_ones(); + bool s_const = cell->getPort(ID::S).is_fully_ones(); + + if (!(e_const && r_const && s_const)) + continue; + + auto d_sig = modwalker.sigmap(cell->getPort(ID::D)); + if (d_sig.is_wire() && d_sig.as_wire()->port_input) { + log_debug("Cell %s is potentially eligible for promotion to input IOFF.\n", cell->name.c_str()); + // check that d_sig has no other consumers + if (GetSize(d_sig) != 1) continue; + pool portbits; + modwalker.get_consumers(portbits, d_sig[0]); + if (GetSize(portbits) > 1) { + log_debug("not promoting: d_sig has other consumers\n"); + continue; + } + cells_to_replace.insert(cell); + continue; // no need to check Q if we already put it on the list + } + auto q_sig = modwalker.sigmap(cell->getPort(ID::Q)); + if (q_sig.is_wire() && q_sig.as_wire()->port_output) { + log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str()); + // check that q_sig has no other consumers + if (GetSize(q_sig) != 1) continue; + pool portbits; + modwalker.get_consumers(portbits, q_sig[0]); + if (GetSize(portbits) > 0) { + log_debug("not promoting: q_sig has other consumers\n"); + continue; + } + cells_to_replace.insert(cell); + } + } + } + + for (auto cell : cells_to_replace) { + log("Promoting register %s to IOFF.\n", log_signal(cell->getPort(ID::Q))); + cell->type = ID(dff); + cell->unsetPort(ID::E); + cell->unsetPort(ID::R); + cell->unsetPort(ID::S); + } + } +} QlIoffPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/quicklogic/synth_quicklogic.cc b/techlibs/quicklogic/synth_quicklogic.cc index 76ef44570..16f660943 100644 --- a/techlibs/quicklogic/synth_quicklogic.cc +++ b/techlibs/quicklogic/synth_quicklogic.cc @@ -78,7 +78,7 @@ struct SynthQuickLogicPass : public ScriptPass { } string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path; - bool abc9, inferAdder, nobram, bramTypes, dsp; + bool abc9, inferAdder, nobram, bramTypes, dsp, ioff; void clear_flags() override { @@ -94,6 +94,7 @@ struct SynthQuickLogicPass : public ScriptPass { bramTypes = false; lib_path = "+/quicklogic/"; dsp = true; + ioff = true; } void set_scratchpad_defaults(RTLIL::Design *design) { @@ -158,6 +159,10 @@ struct SynthQuickLogicPass : public ScriptPass { dsp = false; continue; } + if (args[argidx] == "-noioff") { + ioff = false; + continue; + } break; } extra_args(args, argidx, design); @@ -328,6 +333,12 @@ struct SynthQuickLogicPass : public ScriptPass { run("clean"); run("opt_lut"); } + + if (check_label("iomap", "(for qlf_k6n10f)") && (family == "qlf_k6n10f" || help_mode)) { + if (ioff || help_mode) { + run("ql_ioff", "(unless -noioff)"); + } + } if (check_label("check")) { run("autoname"); diff --git a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys new file mode 100644 index 000000000..bd69a28d4 --- /dev/null +++ b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys @@ -0,0 +1,91 @@ +# test: acceptable for output IOFF promotion +read_verilog < Date: Fri, 24 Jan 2025 21:29:10 +0100 Subject: [PATCH 024/123] fix tests not expecting ioffs --- tests/arch/quicklogic/qlf_k6n10f/counter.ys | 2 +- tests/arch/quicklogic/qlf_k6n10f/dffs.ys | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/arch/quicklogic/qlf_k6n10f/counter.ys b/tests/arch/quicklogic/qlf_k6n10f/counter.ys index ebb6ce243..d1a9ec692 100644 --- a/tests/arch/quicklogic/qlf_k6n10f/counter.ys +++ b/tests/arch/quicklogic/qlf_k6n10f/counter.ys @@ -2,7 +2,7 @@ read_verilog ../../common/counter.v hierarchy -top top proc flatten -equiv_opt -assert -multiclock -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check +equiv_opt -assert -multiclock -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f -noioff # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module select -assert-count 4 t:$lut diff --git a/tests/arch/quicklogic/qlf_k6n10f/dffs.ys b/tests/arch/quicklogic/qlf_k6n10f/dffs.ys index 79a16c941..e12963ae6 100644 --- a/tests/arch/quicklogic/qlf_k6n10f/dffs.ys +++ b/tests/arch/quicklogic/qlf_k6n10f/dffs.ys @@ -5,7 +5,7 @@ design -save read hierarchy -top my_dff proc -equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check +equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f -noioff # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd my_dff # Constrain all select calls below inside the top module select -assert-count 1 t:sdffsre @@ -14,7 +14,7 @@ select -assert-none t:sdffsre %% t:* %D design -load read hierarchy -top my_dffe proc -equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check +equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f -noioff # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd my_dffe # Constrain all select calls below inside the top module select -assert-count 1 t:sdffsre From 1b6b6a77ba9ec4fb5ed40346371cc3df78f20168 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 25 Jan 2025 09:51:20 +1300 Subject: [PATCH 025/123] docs/rosette: Add details for Scope Add a diff file and use it for showing changes from smtlib to rosette. Also add relevant sections of diff to Sort and Struct sections. --- .../code_examples/functional/rosette.diff | 386 ++++++++++++++++++ .../extending_yosys/functional_ir.rst | 82 +++- 2 files changed, 456 insertions(+), 12 deletions(-) create mode 100644 docs/source/code_examples/functional/rosette.diff diff --git a/docs/source/code_examples/functional/rosette.diff b/docs/source/code_examples/functional/rosette.diff new file mode 100644 index 000000000..60fb60c1b --- /dev/null +++ b/docs/source/code_examples/functional/rosette.diff @@ -0,0 +1,386 @@ +diff --git a/smtlib.cc b/smtlib_rosette.cc +index 3eacf407c..a93bd04b0 100644 +--- a/smtlib.cc ++++ b/smtlib_rosette.cc +@@ -29,80 +29,86 @@ PRIVATE_NAMESPACE_BEGIN + using SExprUtil::list; + + const char *reserved_keywords[] = { +- // reserved keywords from the smtlib spec +- "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", +- "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", +- "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", +- "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", +- "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", +- "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", ++ // reserved keywords from the racket spec ++ "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", ++ "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", ++ "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", ++ "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", ++ "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", ++ "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", ++ "define-generics", "set", + + // reserved for our own purposes +- "pair", "Pair", "first", "second", +- "inputs", "state", ++ "inputs", "state", "name", + nullptr + }; + +-struct SmtScope : public Functional::Scope { +- SmtScope() { ++struct SmtrScope : public Functional::Scope { ++ SmtrScope() { + for(const char **p = reserved_keywords; *p != nullptr; p++) + reserve(*p); + } + bool is_character_legal(char c, int index) override { +- return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); ++ return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); + } + }; + +-struct SmtSort { ++struct SmtrSort { + Functional::Sort sort; +- SmtSort(Functional::Sort sort) : sort(sort) {} ++ SmtrSort(Functional::Sort sort) : sort(sort) {} + SExpr to_sexpr() const { + if(sort.is_memory()) { +- return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); ++ return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); + } else if(sort.is_signal()) { +- return list("_", "BitVec", sort.width()); ++ return list("bitvector", sort.width()); + } else { + log_error("unknown sort"); + } + } + }; + +-class SmtStruct { ++class SmtrStruct { + struct Field { +- SmtSort sort; ++ SmtrSort sort; + std::string accessor; ++ std::string name; + }; + idict field_names; + vector fields; +- SmtScope &scope; ++ SmtrScope &global_scope; ++ SmtrScope local_scope; + public: + std::string name; +- SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} +- void insert(IdString field_name, SmtSort sort) { ++ SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} ++ void insert(IdString field_name, SmtrSort sort) { + field_names(field_name); +- auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); +- fields.emplace_back(Field{sort, accessor}); ++ auto base_name = local_scope.unique_name(field_name); ++ auto accessor = name + "-" + base_name; ++ global_scope.reserve(accessor); ++ fields.emplace_back(Field{sort, accessor, base_name}); + } + void write_definition(SExprWriter &w) { +- w.open(list("declare-datatype", name)); +- w.open(list()); +- w.open(list(name)); +- for(const auto &field : fields) +- w << list(field.accessor, field.sort.to_sexpr()); +- w.close(3); ++ vector field_list; ++ for(const auto &field : fields) { ++ field_list.emplace_back(field.name); ++ } ++ w.push(); ++ w.open(list("struct", name, field_list, "#:transparent")); ++ if (field_names.size()) { ++ for (const auto &field : fields) { ++ auto bv_type = field.sort.to_sexpr(); ++ w.comment(field.name + " " + bv_type.to_string()); ++ } ++ } ++ w.pop(); + } + template void write_value(SExprWriter &w, Fn fn) { +- if(field_names.empty()) { +- // Zero-argument constructors in SMTLIB must not be called as functions. +- w << name; +- } else { +- w.open(list(name)); +- for(auto field_name : field_names) { +- w << fn(field_name); +- w.comment(RTLIL::unescape_id(field_name), true); +- } +- w.close(); ++ w.open(list(name)); ++ for(auto field_name : field_names) { ++ w << fn(field_name); ++ w.comment(RTLIL::unescape_id(field_name), true); + } ++ w.close(); + } + SExpr access(SExpr record, IdString name) { + size_t i = field_names.at(name); +@@ -117,28 +123,28 @@ std::string smt_const(RTLIL::Const const &c) { + return s; + } + +-struct SmtPrintVisitor : public Functional::AbstractVisitor { ++struct SmtrPrintVisitor : public Functional::AbstractVisitor { + using Node = Functional::Node; + std::function n; +- SmtStruct &input_struct; +- SmtStruct &state_struct; ++ SmtrStruct &input_struct; ++ SmtrStruct &state_struct; + +- SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} ++ SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} + + SExpr from_bool(SExpr &&arg) { +- return list("ite", std::move(arg), "#b1", "#b0"); ++ return list("bool->bitvector", std::move(arg)); + } + SExpr to_bool(SExpr &&arg) { +- return list("=", std::move(arg), "#b1"); ++ return list("bitvector->bool", std::move(arg)); + } +- SExpr extract(SExpr &&arg, int offset, int out_width = 1) { +- return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); ++ SExpr to_list(SExpr &&arg) { ++ return list("bitvector->bits", std::move(arg)); + } + + SExpr buf(Node, Node a) override { return n(a); } +- SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } +- SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } +- SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } ++ SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } ++ SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } ++ SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } + SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } + SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } + SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } +@@ -150,16 +156,11 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { + SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } + SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } + SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } +- SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } +- SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } +- SExpr reduce_xor(Node, Node a) override { +- vector s { "bvxor" }; +- for(int i = 0; i < a.width(); i++) +- s.push_back(extract(n(a), i)); +- return s; +- } +- SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } +- SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } ++ SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } ++ SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } ++ SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } ++ SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } ++ SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); } + SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } + SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } + SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } +@@ -167,32 +168,32 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { + + SExpr extend(SExpr &&a, int in_width, int out_width) { + if(in_width < out_width) +- return list(list("_", "zero_extend", out_width - in_width), std::move(a)); ++ return list("zero-extend", std::move(a), list("bitvector", out_width)); + else + return std::move(a); + } + SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } + SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } + SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } +- SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } +- SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } +- SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } +- SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } ++ SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } ++ SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } ++ SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } ++ SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } + + SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } + SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } + }; + +-struct SmtModule { ++struct SmtrModule { + Functional::IR ir; +- SmtScope scope; ++ SmtrScope scope; + std::string name; + +- SmtStruct input_struct; +- SmtStruct output_struct; +- SmtStruct state_struct; ++ SmtrStruct input_struct; ++ SmtrStruct output_struct; ++ SmtrStruct state_struct; + +- SmtModule(Module *module) ++ SmtrModule(Module *module) + : ir(Functional::IR::from_module(module)) + , scope() + , name(scope.unique_name(module->name)) +@@ -200,7 +201,7 @@ struct SmtModule { + , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) + , state_struct(scope.unique_name(module->name.str() + "_State"), scope) + { +- scope.reserve(name + "-initial"); ++ scope.reserve(name + "_initial"); + for (auto input : ir.inputs()) + input_struct.insert(input->name, input->sort); + for (auto output : ir.outputs()) +@@ -209,17 +210,20 @@ struct SmtModule { + state_struct.insert(state->name, state->sort); + } + +- void write_eval(SExprWriter &w) +- { ++ void write(std::ostream &out) ++ { ++ SExprWriter w(out); ++ ++ input_struct.write_definition(w); ++ output_struct.write_definition(w); ++ state_struct.write_definition(w); ++ + w.push(); +- w.open(list("define-fun", name, +- list(list("inputs", input_struct.name), +- list("state", state_struct.name)), +- list("Pair", output_struct.name, state_struct.name))); ++ w.open(list("define", list(name, "inputs", "state"))); + auto inlined = [&](Functional::Node n) { + return n.fn() == Functional::Fn::constant; + }; +- SmtPrintVisitor visitor(input_struct, state_struct); ++ SmtrPrintVisitor visitor(input_struct, state_struct); + auto node_to_sexpr = [&](Functional::Node n) -> SExpr { + if(inlined(n)) + return n.visit(visitor); +@@ -230,66 +234,75 @@ struct SmtModule { + for(auto n : ir) + if(!inlined(n)) { + w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); +- w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); ++ w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); + } +- w.open(list("pair")); ++ w.open(list("cons")); + output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); + state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); + w.pop(); +- } + +- void write_initial(SExprWriter &w) +- { +- std::string initial = name + "-initial"; +- w << list("declare-const", initial, state_struct.name); ++ w.push(); ++ auto initial = name + "_initial"; ++ w.open(list("define", initial)); ++ w.open(list(state_struct.name)); + for (auto state : ir.states()) { +- if(state->sort.is_signal()) +- w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); +- else if(state->sort.is_memory()) { ++ if (state->sort.is_signal()) ++ w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); ++ else if (state->sort.is_memory()) { + const auto &contents = state->initial_value_memory(); ++ w.open(list("list")); + for(int i = 0; i < 1<sort.addr_width(); i++) { +- auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); +- w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); ++ w << list("bv", smt_const(contents[i]), state->sort.data_width()); + } ++ w.close(); + } + } +- } +- +- void write(std::ostream &out) +- { +- SExprWriter w(out); +- +- input_struct.write_definition(w); +- output_struct.write_definition(w); +- state_struct.write_definition(w); +- +- w << list("declare-datatypes", +- list(list("Pair", 2)), +- list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); +- +- write_eval(w); +- write_initial(w); ++ w.pop(); + } + }; + +-struct FunctionalSmtBackend : public Backend { +- FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} ++struct FunctionalSmtrBackend : public Backend { ++ FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} + +- void help() override { log("\nFunctional SMT Backend.\n\n"); } ++ void help() override { ++ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| ++ log("\n"); ++ log(" write_functional_rosette [options] [filename]\n"); ++ log("\n"); ++ log("Functional Rosette Backend.\n"); ++ log("\n"); ++ log(" -provides\n"); ++ log(" include 'provide' statement(s) for loading output as a module\n"); ++ log("\n"); ++ } + + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { +- log_header(design, "Executing Functional SMT Backend.\n"); ++ auto provides = false; ++ ++ log_header(design, "Executing Functional Rosette Backend.\n"); + +- size_t argidx = 1; +- extra_args(f, filename, args, argidx, design); ++ size_t argidx; ++ for (argidx = 1; argidx < args.size(); argidx++) ++ { ++ if (args[argidx] == "-provides") ++ provides = true; ++ else ++ break; ++ } ++ extra_args(f, filename, args, argidx); ++ ++ *f << "#lang rosette/safe\n"; ++ if (provides) { ++ *f << "(provide (all-defined-out))\n"; ++ } + + for (auto module : design->selected_modules()) { + log("Processing module `%s`.\n", module->name.c_str()); +- SmtModule smt(module); +- smt.write(*f); ++ SmtrModule smtr(module); ++ smtr.write(*f); + } + } +-} FunctionalSmtBackend; ++} FunctionalSmtrBackend; + + PRIVATE_NAMESPACE_END diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 662f3df8b..0eb9451be 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -169,17 +169,50 @@ Overview Full code listings for the initial SMT-LIB backend and the converted Rosette backend are included in the Yosys source repository under :file:`backends/functional` as ``smtlib.cc`` and ``smtlib_rosette.cc`` -respectively. +respectively. Note that the Rosette language is an extension of the Racket +language; this guide tends to refer to Racket when talking about the underlying +semantics/syntax of the language. Scope ~~~~~ -- as above, prevents namespace collision -- reserved keywords - - language (Racket) keywords - - output keywords +As described above, the ``Functional::Scope`` class is derived in order to avoid +collisions between identifiers in the generated output. We switch out ``Smt`` +in the class name for ``Smtr`` for ``smtlib_rosette``; which will happen through +the rest of the code too. We also update the ``is_character_legal`` method to +reject ascii characters which are not allowed in Racket variable names. -- ``is_character_legal`` +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of ``Scope`` close + :start-at: -struct SmtScope : public Functional::Scope { + :end-at: }; + +For the reserved keywords we trade the SMT-LIB specification for Racket to +prevent parts of our design from accidentally being treated as Racket code. We +also no longer need to reserve ``pair``, ``first``, and ``second``. In +`write_functional_smt2` these are used for combining the ``(inputs, +current_state)`` and ``(outputs, next_state)`` into a single variable. Racket +provides this functionality natively with ``cons``, which we will see later. + +.. code-block:: diff + :caption: diff of ``reserved_keywords`` list + + const char *reserved_keywords[] = { + - // reserved keywords from the smtlib spec + - ... + + // reserved keywords from the racket spec + + ... + + // reserved for our own purposes + - "pair", "Pair", "first", "second", + - "inputs", "state", + + "inputs", "state", "name", + nullptr + }; + +Note that we skip over the actual list of reserved keywords from both the smtlib +and racket specifications to save on space in this document. Sort ~~~~ @@ -187,18 +220,43 @@ Sort - map variable declarations to s-expressions - handles signals (bitvectors) and memories (arrays of bitvectors) +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of ``Sort`` wrapper + :start-at: -struct SmtSort { + :end-at: }; + Struct ~~~~~~ - helpers for defining inputs/outputs/state - - each is a single (transparent) struct with zero or more fields - - each field has a name, with the type (sort) as a comment - - struct fields in Rosette are accessed as ``-`` - - field names only need to be unique within the struct, while accessors - are unique within the module - +- struct fields in Rosette are accessed as ``-`` + - field names only need to be unique within the struct, while accessors + are unique within the module + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of struct constructor + :start-at: - SmtStruct(std::string name, SmtScope &scope) + :end-before: void write_definition + +- each is a single (transparent) struct with zero or more fields +- each field has a name, with the type (sort) as a comment + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of ``write_definition`` method + :start-at: void write_definition + :end-before: template void write_value + - writing outputs/next state +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of ``write_value`` method + :start-at: template void write_value + :end-before: SExpr access + PrintVisitor ~~~~~~~~~~~~ From aabb4ea1df3d5ef216b6fce95caf989333a14e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 27 Jan 2025 13:08:19 +0100 Subject: [PATCH 026/123] macc_v2: Fix checks and parameter padding --- kernel/macc.h | 17 +++++++++++++++++ kernel/rtlil.cc | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/kernel/macc.h b/kernel/macc.h index fdd738d45..5cf8d634d 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -228,6 +228,23 @@ struct Macc } } + if (a_signed.empty()) + a_signed = {RTLIL::Sx}; + if (b_signed.empty()) + a_signed = {RTLIL::Sx}; + if (c_signed.empty()) + c_signed = {RTLIL::Sx}; + if (a_widths.empty()) + a_widths = {RTLIL::Sx}; + if (b_widths.empty()) + b_widths = {RTLIL::Sx}; + if (c_widths.empty()) + c_widths = {RTLIL::Sx}; + if (product_negated.empty()) + product_negated = {RTLIL::Sx}; + if (addend_negated.empty()) + addend_negated = {RTLIL::Sx}; + cell->setParam(ID::NPRODUCTS, nproducts); cell->setParam(ID::PRODUCT_NEGATED, product_negated); cell->setParam(ID::NADDENDS, naddends); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index c10e879c5..d197660bd 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1470,7 +1470,7 @@ namespace { if (cell->type == ID($macc_v2)) { if (param(ID::NPRODUCTS) <= 0) error(__LINE__); - if (param(ID::NADDENDS) <= 0) + if (param(ID::NADDENDS) < 0) error(__LINE__); param_bits(ID::PRODUCT_NEGATED, max(param(ID::NPRODUCTS), 1)); param_bits(ID::ADDEND_NEGATED, max(param(ID::NADDENDS), 1)); From 6c76dcec3ea59d31b4fdda59be50ea87f2d8294d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 27 Jan 2025 13:08:44 +0100 Subject: [PATCH 027/123] macc_v2: Fix v2 omissions --- passes/techmap/techmap.cc | 8 ++++---- techlibs/common/techmap.v | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index b77bab69e..95c733f62 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -554,8 +554,8 @@ struct TechmapWorker if (extmapper_name == "maccmap") { log("Creating %s with maccmap.\n", log_id(extmapper_module)); - if (extmapper_cell->type != ID($macc)) - log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type)); + if (!extmapper_cell->type.in(ID($macc), ID($macc_v2))) + log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(extmapper_cell->type)); maccmap(extmapper_module, extmapper_cell); extmapper_module->remove(extmapper_cell); } @@ -600,8 +600,8 @@ struct TechmapWorker } if (extmapper_name == "maccmap") { - if (cell->type != ID($macc)) - log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type)); + if (!cell->type.in(ID($macc), ID($macc_v2))) + log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(cell->type)); maccmap(module, cell); } diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 119296147..75651bb79 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -290,7 +290,7 @@ module _90_alu (A, B, CI, BI, X, Y, CO); endmodule (* techmap_maccmap *) -(* techmap_celltype = "$macc" *) +(* techmap_celltype = "$macc $macc_v2" *) module _90_macc; endmodule From 916fe998abfca65fabb417fec5737684dd323933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 27 Jan 2025 13:18:35 +0100 Subject: [PATCH 028/123] macc_v2: Add test --- tests/alumacc/basic.ys | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/alumacc/basic.ys diff --git a/tests/alumacc/basic.ys b/tests/alumacc/basic.ys new file mode 100644 index 000000000..cdd20c472 --- /dev/null +++ b/tests/alumacc/basic.ys @@ -0,0 +1,61 @@ +read_verilog < Date: Mon, 27 Jan 2025 15:52:49 +0100 Subject: [PATCH 029/123] macc_v2: Relax and fix cell check --- kernel/macc.h | 2 +- kernel/rtlil.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/macc.h b/kernel/macc.h index 5cf8d634d..5fc6036ed 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -231,7 +231,7 @@ struct Macc if (a_signed.empty()) a_signed = {RTLIL::Sx}; if (b_signed.empty()) - a_signed = {RTLIL::Sx}; + b_signed = {RTLIL::Sx}; if (c_signed.empty()) c_signed = {RTLIL::Sx}; if (a_widths.empty()) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index d197660bd..3b9a4a8b1 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1468,7 +1468,7 @@ namespace { } if (cell->type == ID($macc_v2)) { - if (param(ID::NPRODUCTS) <= 0) + if (param(ID::NPRODUCTS) < 0) error(__LINE__); if (param(ID::NADDENDS) < 0) error(__LINE__); From 09ead2daa90a92236f162ef8a9612a740439393e Mon Sep 17 00:00:00 2001 From: David Sawatzke Date: Tue, 28 Jan 2025 09:42:39 +0100 Subject: [PATCH 030/123] tcl: Update help message to clarify usage of `result.string` In the current documentation, it's unclear if `result.string` serves as an example of a string name. It is furthermore unclear what the purpose of `result.string` is. Clarify this by adding a small explanation (and fix a typo) --- kernel/yosys.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index de25d20e2..4bccb1f14 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -754,9 +754,10 @@ struct TclPass : public Pass { log("If any arguments are specified, these arguments are provided to the script via\n"); log("the standard $argc and $argv variables.\n"); log("\n"); - log("Note, tcl will not recieve the output of any yosys command. If the output\n"); + log("Note, tcl will not receive the output of any yosys command. If the output\n"); log("of the tcl commands are needed, use the yosys command 'tee -s result.string'\n"); log("to redirect yosys's output to the 'result.string' scratchpad value.\n"); + log("The 'result.string' value is then used as the tcl output value of the command.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *) override { From 9da4fe747e96e68a0c8ca8420659ff016cc8481b Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Tue, 28 Jan 2025 11:23:36 +0100 Subject: [PATCH 031/123] fix bus ioff inference --- techlibs/quicklogic/ql_ioff.cc | 18 +++--- tests/arch/quicklogic/qlf_k6n10f/ioff.ys | 81 ++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/techlibs/quicklogic/ql_ioff.cc b/techlibs/quicklogic/ql_ioff.cc index c857fc35d..e08850fb7 100644 --- a/techlibs/quicklogic/ql_ioff.cc +++ b/techlibs/quicklogic/ql_ioff.cc @@ -39,13 +39,14 @@ struct QlIoffPass : public Pass { if (!(e_const && r_const && s_const)) continue; - auto d_sig = modwalker.sigmap(cell->getPort(ID::D)); - if (d_sig.is_wire() && d_sig.as_wire()->port_input) { + SigSpec d = cell->getPort(ID::D); + if (GetSize(d) != 1) continue; + SigBit d_sig = modwalker.sigmap(d[0]); + if (d_sig.is_wire() && d_sig.wire->port_input) { log_debug("Cell %s is potentially eligible for promotion to input IOFF.\n", cell->name.c_str()); // check that d_sig has no other consumers - if (GetSize(d_sig) != 1) continue; pool portbits; - modwalker.get_consumers(portbits, d_sig[0]); + modwalker.get_consumers(portbits, d_sig); if (GetSize(portbits) > 1) { log_debug("not promoting: d_sig has other consumers\n"); continue; @@ -53,13 +54,14 @@ struct QlIoffPass : public Pass { cells_to_replace.insert(cell); continue; // no need to check Q if we already put it on the list } - auto q_sig = modwalker.sigmap(cell->getPort(ID::Q)); - if (q_sig.is_wire() && q_sig.as_wire()->port_output) { + SigSpec q = cell->getPort(ID::Q); + if (GetSize(q) != 1) continue; + SigBit q_sig = modwalker.sigmap(q[0]); + if (q_sig.is_wire() && q_sig.wire->port_output) { log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str()); // check that q_sig has no other consumers - if (GetSize(q_sig) != 1) continue; pool portbits; - modwalker.get_consumers(portbits, q_sig[0]); + modwalker.get_consumers(portbits, q_sig); if (GetSize(portbits) > 0) { log_debug("not promoting: q_sig has other consumers\n"); continue; diff --git a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys index bd69a28d4..3144eda13 100644 --- a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys +++ b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys @@ -9,6 +9,18 @@ EOF synth_quicklogic -family qlf_k6n10f -top top select -assert-count 1 t:dff +design -reset +# test: acceptable for output IOFF promotion +read_verilog < Date: Tue, 28 Jan 2025 17:37:23 +0100 Subject: [PATCH 032/123] detect aliased I/O ports --- techlibs/quicklogic/ql_ioff.cc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/techlibs/quicklogic/ql_ioff.cc b/techlibs/quicklogic/ql_ioff.cc index e08850fb7..4cc9785d5 100644 --- a/techlibs/quicklogic/ql_ioff.cc +++ b/techlibs/quicklogic/ql_ioff.cc @@ -32,38 +32,39 @@ struct QlIoffPass : public Pass { pool cells_to_replace; for (auto cell : module->selected_cells()) { if (cell->type.in(ID(dffsre), ID(sdffsre))) { + log_debug("Checking cell %s.\n", cell->name.c_str()); bool e_const = cell->getPort(ID::E).is_fully_ones(); bool r_const = cell->getPort(ID::R).is_fully_ones(); bool s_const = cell->getPort(ID::S).is_fully_ones(); - if (!(e_const && r_const && s_const)) + if (!(e_const && r_const && s_const)) { + log_debug("not promoting: E, R, or S is used\n"); continue; + } SigSpec d = cell->getPort(ID::D); - if (GetSize(d) != 1) continue; - SigBit d_sig = modwalker.sigmap(d[0]); - if (d_sig.is_wire() && d_sig.wire->port_input) { + log_assert(GetSize(d) == 1); + if (modwalker.has_inputs(d)) { log_debug("Cell %s is potentially eligible for promotion to input IOFF.\n", cell->name.c_str()); // check that d_sig has no other consumers pool portbits; - modwalker.get_consumers(portbits, d_sig); + modwalker.get_consumers(portbits, d); if (GetSize(portbits) > 1) { - log_debug("not promoting: d_sig has other consumers\n"); + log_debug("not promoting: D has other consumers\n"); continue; } cells_to_replace.insert(cell); continue; // no need to check Q if we already put it on the list } SigSpec q = cell->getPort(ID::Q); - if (GetSize(q) != 1) continue; - SigBit q_sig = modwalker.sigmap(q[0]); - if (q_sig.is_wire() && q_sig.wire->port_output) { + log_assert(GetSize(q) == 1); + if (modwalker.has_outputs(q)) { log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str()); // check that q_sig has no other consumers pool portbits; - modwalker.get_consumers(portbits, q_sig); + modwalker.get_consumers(portbits, q); if (GetSize(portbits) > 0) { - log_debug("not promoting: q_sig has other consumers\n"); + log_debug("not promoting: Q has other consumers\n"); continue; } cells_to_replace.insert(cell); From 7050b3c9925d4c7f5a01bd26bc5c237154af20d6 Mon Sep 17 00:00:00 2001 From: KrystalDelusion <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 31 Jan 2025 12:30:25 +1300 Subject: [PATCH 033/123] Makefile: Add run-test.mk to clean --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 371380cd2..05b286b4e 100644 --- a/Makefile +++ b/Makefile @@ -1053,6 +1053,7 @@ clean: rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_* rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff rm -f tests/tools/cmp_tbdata + rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS)) -$(MAKE) -C docs clean -$(MAKE) -C docs/images clean rm -rf docs/source/cmd docs/util/__pycache__ From 0ec5f1b75608ab6d0511be331ff5ce9a4f01c9de Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:06:09 +1300 Subject: [PATCH 034/123] pmgen: Move passes out of pmgen folder - Techlib pmgens are now in relevant techlibs/*. - `peepopt` pmgens are now in passes/opt. - `test_pmgen` is still in passes/pmgen. - Update `Makefile.inc` and `.gitignore` file(s) to match new `*_pm.h` location, as well as the `#include`s. - Change default `%_pm.h` make target to `techlibs/%_pm.h` and move it to the top level Makefile. - Update pmgen target to use `$(notdir $*)` (where `$*` is the part of the file name that matched the '%' in the target) instead of `$(subst _pm.h,,$(notdir $@))`. --- Makefile | 3 + passes/opt/.gitignore | 1 + passes/opt/Makefile.inc | 14 ++++ passes/{pmgen => opt}/peepopt.cc | 2 +- .../peepopt_formal_clockgateff.pmg | 0 passes/{pmgen => opt}/peepopt_muldiv.pmg | 0 passes/{pmgen => opt}/peepopt_shiftadd.pmg | 0 .../{pmgen => opt}/peepopt_shiftmul_left.pmg | 0 .../{pmgen => opt}/peepopt_shiftmul_right.pmg | 0 passes/pmgen/Makefile.inc | 66 +------------------ passes/pmgen/README.md | 2 +- passes/pmgen/test_pmgen.cc | 4 +- techlibs/.gitignore | 1 + techlibs/ice40/Makefile.inc | 10 +++ {passes/pmgen => techlibs/ice40}/ice40_dsp.cc | 2 +- .../pmgen => techlibs/ice40}/ice40_dsp.pmg | 0 .../ice40}/ice40_wrapcarry.cc | 2 +- .../ice40}/ice40_wrapcarry.pmg | 0 techlibs/microchip/Makefile.inc | 9 +++ .../microchip}/microchip_dsp.cc | 6 +- .../microchip}/microchip_dsp.pmg | 0 .../microchip}/microchip_dsp_CREG.pmg | 0 .../microchip}/microchip_dsp_cascade.pmg | 0 techlibs/xilinx/Makefile.inc | 16 +++++ .../pmgen => techlibs/xilinx}/xilinx_dsp.cc | 8 +-- .../pmgen => techlibs/xilinx}/xilinx_dsp.pmg | 0 .../xilinx}/xilinx_dsp48a.pmg | 0 .../xilinx}/xilinx_dsp_CREG.pmg | 0 .../xilinx}/xilinx_dsp_cascade.pmg | 0 .../pmgen => techlibs/xilinx}/xilinx_srl.cc | 2 +- .../pmgen => techlibs/xilinx}/xilinx_srl.pmg | 0 31 files changed, 71 insertions(+), 77 deletions(-) create mode 100644 passes/opt/.gitignore rename passes/{pmgen => opt}/peepopt.cc (99%) rename passes/{pmgen => opt}/peepopt_formal_clockgateff.pmg (100%) rename passes/{pmgen => opt}/peepopt_muldiv.pmg (100%) rename passes/{pmgen => opt}/peepopt_shiftadd.pmg (100%) rename passes/{pmgen => opt}/peepopt_shiftmul_left.pmg (100%) rename passes/{pmgen => opt}/peepopt_shiftmul_right.pmg (100%) rename {passes/pmgen => techlibs/ice40}/ice40_dsp.cc (99%) rename {passes/pmgen => techlibs/ice40}/ice40_dsp.pmg (100%) rename {passes/pmgen => techlibs/ice40}/ice40_wrapcarry.cc (99%) rename {passes/pmgen => techlibs/ice40}/ice40_wrapcarry.pmg (100%) rename {passes/pmgen => techlibs/microchip}/microchip_dsp.cc (98%) rename {passes/pmgen => techlibs/microchip}/microchip_dsp.pmg (100%) rename {passes/pmgen => techlibs/microchip}/microchip_dsp_CREG.pmg (100%) rename {passes/pmgen => techlibs/microchip}/microchip_dsp_cascade.pmg (100%) rename {passes/pmgen => techlibs/xilinx}/xilinx_dsp.cc (99%) rename {passes/pmgen => techlibs/xilinx}/xilinx_dsp.pmg (100%) rename {passes/pmgen => techlibs/xilinx}/xilinx_dsp48a.pmg (100%) rename {passes/pmgen => techlibs/xilinx}/xilinx_dsp_CREG.pmg (100%) rename {passes/pmgen => techlibs/xilinx}/xilinx_dsp_cascade.pmg (100%) rename {passes/pmgen => techlibs/xilinx}/xilinx_srl.cc (99%) rename {passes/pmgen => techlibs/xilinx}/xilinx_srl.pmg (100%) diff --git a/Makefile b/Makefile index fb3e5654e..e82d69124 100644 --- a/Makefile +++ b/Makefile @@ -651,6 +651,9 @@ OBJS += libs/fst/fastlz.o OBJS += libs/fst/lz4.o endif +techlibs/%_pm.h: passes/pmgen/pmgen.py techlibs/%.pmg + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(notdir $*) $(filter-out $<,$^) + ifneq ($(SMALL),1) OBJS += libs/subcircuit/subcircuit.o diff --git a/passes/opt/.gitignore b/passes/opt/.gitignore new file mode 100644 index 000000000..4b40d3505 --- /dev/null +++ b/passes/opt/.gitignore @@ -0,0 +1 @@ +/peepopt*_pm.h \ No newline at end of file diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 76bf8a84e..b796535e3 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -22,4 +22,18 @@ OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/opt_ffinv.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o + +OBJS += passes/opt/peepopt.o +GENFILES += passes/opt/peepopt_pm.h +passes/opt/peepopt.o: passes/opt/peepopt_pm.h +$(eval $(call add_extra_objs,passes/opt/peepopt_pm.h)) + +PEEPOPT_PATTERN = passes/opt/peepopt_shiftmul_right.pmg +PEEPOPT_PATTERN += passes/opt/peepopt_shiftmul_left.pmg +PEEPOPT_PATTERN += passes/opt/peepopt_shiftadd.pmg +PEEPOPT_PATTERN += passes/opt/peepopt_muldiv.pmg +PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg + +passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) endif diff --git a/passes/pmgen/peepopt.cc b/passes/opt/peepopt.cc similarity index 99% rename from passes/pmgen/peepopt.cc rename to passes/opt/peepopt.cc index 5b678ee55..b12f4777e 100644 --- a/passes/pmgen/peepopt.cc +++ b/passes/opt/peepopt.cc @@ -28,7 +28,7 @@ bool did_something; // scratchpad configurations for pmgen int shiftadd_max_ratio; -#include "passes/pmgen/peepopt_pm.h" +#include "passes/opt/peepopt_pm.h" struct PeepoptPass : public Pass { PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { } diff --git a/passes/pmgen/peepopt_formal_clockgateff.pmg b/passes/opt/peepopt_formal_clockgateff.pmg similarity index 100% rename from passes/pmgen/peepopt_formal_clockgateff.pmg rename to passes/opt/peepopt_formal_clockgateff.pmg diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/opt/peepopt_muldiv.pmg similarity index 100% rename from passes/pmgen/peepopt_muldiv.pmg rename to passes/opt/peepopt_muldiv.pmg diff --git a/passes/pmgen/peepopt_shiftadd.pmg b/passes/opt/peepopt_shiftadd.pmg similarity index 100% rename from passes/pmgen/peepopt_shiftadd.pmg rename to passes/opt/peepopt_shiftadd.pmg diff --git a/passes/pmgen/peepopt_shiftmul_left.pmg b/passes/opt/peepopt_shiftmul_left.pmg similarity index 100% rename from passes/pmgen/peepopt_shiftmul_left.pmg rename to passes/opt/peepopt_shiftmul_left.pmg diff --git a/passes/pmgen/peepopt_shiftmul_right.pmg b/passes/opt/peepopt_shiftmul_right.pmg similarity index 100% rename from passes/pmgen/peepopt_shiftmul_right.pmg rename to passes/opt/peepopt_shiftmul_right.pmg diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 6fa7d1fd7..17abfd43e 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -1,70 +1,10 @@ -%_pm.h: passes/pmgen/pmgen.py %.pmg - $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^) +passes/pmgen/%_pm.h: passes/pmgen/pmgen.py passes/pmgen/%.pmg + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(notdir $*) $(filter-out $<,$^) # -------------------------------------- OBJS += passes/pmgen/test_pmgen.o GENFILES += passes/pmgen/test_pmgen_pm.h -passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h +passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h techlibs/ice40/ice40_dsp_pm.h techlibs/xilinx/xilinx_srl_pm.h $(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h)) -# -------------------------------------- - -OBJS += passes/pmgen/ice40_dsp.o -GENFILES += passes/pmgen/ice40_dsp_pm.h -passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h -$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h)) - -# -------------------------------------- - -OBJS += passes/pmgen/ice40_wrapcarry.o -GENFILES += passes/pmgen/ice40_wrapcarry_pm.h -passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h -$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h)) - -# -------------------------------------- - -OBJS += passes/pmgen/xilinx_dsp.o -GENFILES += passes/pmgen/xilinx_dsp_pm.h -GENFILES += passes/pmgen/xilinx_dsp48a_pm.h -GENFILES += passes/pmgen/xilinx_dsp_CREG_pm.h -GENFILES += passes/pmgen/xilinx_dsp_cascade_pm.h -passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h -$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) -$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h)) -$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h)) -$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) - -# -------------------------------------- - -OBJS += passes/pmgen/microchip_dsp.o -GENFILES += passes/pmgen/microchip_dsp_pm.h -GENFILES += passes/pmgen/microchip_dsp_CREG_pm.h -GENFILES += passes/pmgen/microchip_dsp_cascade_pm.h -passes/pmgen/microchip_dsp.o: passes/pmgen/microchip_dsp_pm.h passes/pmgen/microchip_dsp_CREG_pm.h passes/pmgen/microchip_dsp_cascade_pm.h -$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_pm.h)) -$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_CREG_pm.h)) -$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_cascade_pm.h)) - -# -------------------------------------- - -OBJS += passes/pmgen/peepopt.o -GENFILES += passes/pmgen/peepopt_pm.h -passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h -$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) - -PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul_right.pmg -PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftmul_left.pmg -PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftadd.pmg -PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg -PEEPOPT_PATTERN += passes/pmgen/peepopt_formal_clockgateff.pmg - -passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) - $(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) - -# -------------------------------------- - -OBJS += passes/pmgen/xilinx_srl.o -GENFILES += passes/pmgen/xilinx_srl_pm.h -passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h -$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h)) diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 3205be1b5..15569ebfc 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -22,7 +22,7 @@ list of cells from that module: foobar_pm pm(module, module->selected_cells()); The caller must make sure that none of the cells in the 2nd argument are -deleted for as long as the patter matcher instance is used. +deleted for as long as the pattern matcher instance is used. At any time it is possible to disable cells, preventing them from showing up in any future matches: diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index beff59778..fc41848f7 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -24,8 +24,8 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN #include "passes/pmgen/test_pmgen_pm.h" -#include "passes/pmgen/ice40_dsp_pm.h" -#include "passes/pmgen/xilinx_srl_pm.h" +#include "techlibs/ice40/ice40_dsp_pm.h" +#include "techlibs/xilinx/xilinx_srl_pm.h" #include "generate.h" diff --git a/techlibs/.gitignore b/techlibs/.gitignore index e81e4e4b8..ddb102bec 100644 --- a/techlibs/.gitignore +++ b/techlibs/.gitignore @@ -1 +1,2 @@ blackbox.v +*_pm.h diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc index 4bf8e4e86..027a0dd33 100644 --- a/techlibs/ice40/Makefile.inc +++ b/techlibs/ice40/Makefile.inc @@ -14,3 +14,13 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/spram.txt)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/spram_map.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v)) $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_model.v)) + +OBJS += techlibs/ice40/ice40_dsp.o +GENFILES += techlibs/ice40/ice40_dsp_pm.h +techlibs/ice40/ice40_dsp.o: techlibs/ice40/ice40_dsp_pm.h +$(eval $(call add_extra_objs,techlibs/ice40/ice40_dsp_pm.h)) + +OBJS += techlibs/ice40/ice40_wrapcarry.o +GENFILES += techlibs/ice40/ice40_wrapcarry_pm.h +techlibs/ice40/ice40_wrapcarry.o: techlibs/ice40/ice40_wrapcarry_pm.h +$(eval $(call add_extra_objs,techlibs/ice40/ice40_wrapcarry_pm.h)) diff --git a/passes/pmgen/ice40_dsp.cc b/techlibs/ice40/ice40_dsp.cc similarity index 99% rename from passes/pmgen/ice40_dsp.cc rename to techlibs/ice40/ice40_dsp.cc index 5720c9b1e..3f00028c3 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/techlibs/ice40/ice40_dsp.cc @@ -23,7 +23,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#include "passes/pmgen/ice40_dsp_pm.h" +#include "techlibs/ice40/ice40_dsp_pm.h" void create_ice40_dsp(ice40_dsp_pm &pm) { diff --git a/passes/pmgen/ice40_dsp.pmg b/techlibs/ice40/ice40_dsp.pmg similarity index 100% rename from passes/pmgen/ice40_dsp.pmg rename to techlibs/ice40/ice40_dsp.pmg diff --git a/passes/pmgen/ice40_wrapcarry.cc b/techlibs/ice40/ice40_wrapcarry.cc similarity index 99% rename from passes/pmgen/ice40_wrapcarry.cc rename to techlibs/ice40/ice40_wrapcarry.cc index c936d02dc..014490564 100644 --- a/passes/pmgen/ice40_wrapcarry.cc +++ b/techlibs/ice40/ice40_wrapcarry.cc @@ -23,7 +23,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#include "passes/pmgen/ice40_wrapcarry_pm.h" +#include "techlibs/ice40/ice40_wrapcarry_pm.h" void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm) { diff --git a/passes/pmgen/ice40_wrapcarry.pmg b/techlibs/ice40/ice40_wrapcarry.pmg similarity index 100% rename from passes/pmgen/ice40_wrapcarry.pmg rename to techlibs/ice40/ice40_wrapcarry.pmg diff --git a/techlibs/microchip/Makefile.inc b/techlibs/microchip/Makefile.inc index b1905e2cf..ab294b465 100644 --- a/techlibs/microchip/Makefile.inc +++ b/techlibs/microchip/Makefile.inc @@ -29,3 +29,12 @@ $(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM_map.v)) $(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM.txt)) $(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM_map.v)) $(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM.txt)) + +OBJS += techlibs/microchip/microchip_dsp.o +GENFILES += techlibs/microchip/microchip_dsp_pm.h +GENFILES += techlibs/microchip/microchip_dsp_CREG_pm.h +GENFILES += techlibs/microchip/microchip_dsp_cascade_pm.h +techlibs/microchip/microchip_dsp.o: techlibs/microchip/microchip_dsp_pm.h techlibs/microchip/microchip_dsp_CREG_pm.h techlibs/microchip/microchip_dsp_cascade_pm.h +$(eval $(call add_extra_objs,techlibs/microchip/microchip_dsp_pm.h)) +$(eval $(call add_extra_objs,techlibs/microchip/microchip_dsp_CREG_pm.h)) +$(eval $(call add_extra_objs,techlibs/microchip/microchip_dsp_cascade_pm.h)) diff --git a/passes/pmgen/microchip_dsp.cc b/techlibs/microchip/microchip_dsp.cc similarity index 98% rename from passes/pmgen/microchip_dsp.cc rename to techlibs/microchip/microchip_dsp.cc index 31fd24194..3dfcb8905 100644 --- a/passes/pmgen/microchip_dsp.cc +++ b/techlibs/microchip/microchip_dsp.cc @@ -23,9 +23,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#include "passes/pmgen/microchip_dsp_CREG_pm.h" -#include "passes/pmgen/microchip_dsp_cascade_pm.h" -#include "passes/pmgen/microchip_dsp_pm.h" +#include "techlibs/microchip/microchip_dsp_CREG_pm.h" +#include "techlibs/microchip/microchip_dsp_cascade_pm.h" +#include "techlibs/microchip/microchip_dsp_pm.h" void microchip_dsp_pack(microchip_dsp_pm &pm) { diff --git a/passes/pmgen/microchip_dsp.pmg b/techlibs/microchip/microchip_dsp.pmg similarity index 100% rename from passes/pmgen/microchip_dsp.pmg rename to techlibs/microchip/microchip_dsp.pmg diff --git a/passes/pmgen/microchip_dsp_CREG.pmg b/techlibs/microchip/microchip_dsp_CREG.pmg similarity index 100% rename from passes/pmgen/microchip_dsp_CREG.pmg rename to techlibs/microchip/microchip_dsp_CREG.pmg diff --git a/passes/pmgen/microchip_dsp_cascade.pmg b/techlibs/microchip/microchip_dsp_cascade.pmg similarity index 100% rename from passes/pmgen/microchip_dsp_cascade.pmg rename to techlibs/microchip/microchip_dsp_cascade.pmg diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index 2d3d0f63c..a9ca35233 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -46,3 +46,19 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_dsp_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcu_dsp_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_model.v)) + +OBJS += techlibs/xilinx/xilinx_dsp.o +GENFILES += techlibs/xilinx/xilinx_dsp_pm.h +GENFILES += techlibs/xilinx/xilinx_dsp48a_pm.h +GENFILES += techlibs/xilinx/xilinx_dsp_CREG_pm.h +GENFILES += techlibs/xilinx/xilinx_dsp_cascade_pm.h +techlibs/xilinx/xilinx_dsp.o: techlibs/xilinx/xilinx_dsp_pm.h techlibs/xilinx/xilinx_dsp48a_pm.h techlibs/xilinx/xilinx_dsp_CREG_pm.h techlibs/xilinx/xilinx_dsp_cascade_pm.h +$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp_pm.h)) +$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp48a_pm.h)) +$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp_CREG_pm.h)) +$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp_cascade_pm.h)) + +OBJS += techlibs/xilinx/xilinx_srl.o +GENFILES += techlibs/xilinx/xilinx_srl_pm.h +techlibs/xilinx/xilinx_srl.o: techlibs/xilinx/xilinx_srl_pm.h +$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_srl_pm.h)) diff --git a/passes/pmgen/xilinx_dsp.cc b/techlibs/xilinx/xilinx_dsp.cc similarity index 99% rename from passes/pmgen/xilinx_dsp.cc rename to techlibs/xilinx/xilinx_dsp.cc index 9af7d1eec..f95930a90 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/techlibs/xilinx/xilinx_dsp.cc @@ -25,10 +25,10 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#include "passes/pmgen/xilinx_dsp_pm.h" -#include "passes/pmgen/xilinx_dsp48a_pm.h" -#include "passes/pmgen/xilinx_dsp_CREG_pm.h" -#include "passes/pmgen/xilinx_dsp_cascade_pm.h" +#include "techlibs/xilinx/xilinx_dsp_pm.h" +#include "techlibs/xilinx/xilinx_dsp48a_pm.h" +#include "techlibs/xilinx/xilinx_dsp_CREG_pm.h" +#include "techlibs/xilinx/xilinx_dsp_cascade_pm.h" static Cell* addDsp(Module *module) { Cell *cell = module->addCell(NEW_ID, ID(DSP48E1)); diff --git a/passes/pmgen/xilinx_dsp.pmg b/techlibs/xilinx/xilinx_dsp.pmg similarity index 100% rename from passes/pmgen/xilinx_dsp.pmg rename to techlibs/xilinx/xilinx_dsp.pmg diff --git a/passes/pmgen/xilinx_dsp48a.pmg b/techlibs/xilinx/xilinx_dsp48a.pmg similarity index 100% rename from passes/pmgen/xilinx_dsp48a.pmg rename to techlibs/xilinx/xilinx_dsp48a.pmg diff --git a/passes/pmgen/xilinx_dsp_CREG.pmg b/techlibs/xilinx/xilinx_dsp_CREG.pmg similarity index 100% rename from passes/pmgen/xilinx_dsp_CREG.pmg rename to techlibs/xilinx/xilinx_dsp_CREG.pmg diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/techlibs/xilinx/xilinx_dsp_cascade.pmg similarity index 100% rename from passes/pmgen/xilinx_dsp_cascade.pmg rename to techlibs/xilinx/xilinx_dsp_cascade.pmg diff --git a/passes/pmgen/xilinx_srl.cc b/techlibs/xilinx/xilinx_srl.cc similarity index 99% rename from passes/pmgen/xilinx_srl.cc rename to techlibs/xilinx/xilinx_srl.cc index 7b20a7d40..32a0064a6 100644 --- a/passes/pmgen/xilinx_srl.cc +++ b/techlibs/xilinx/xilinx_srl.cc @@ -24,7 +24,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#include "passes/pmgen/xilinx_srl_pm.h" +#include "techlibs/xilinx/xilinx_srl_pm.h" void run_fixed(xilinx_srl_pm &pm) { diff --git a/passes/pmgen/xilinx_srl.pmg b/techlibs/xilinx/xilinx_srl.pmg similarity index 100% rename from passes/pmgen/xilinx_srl.pmg rename to techlibs/xilinx/xilinx_srl.pmg From 303a386ecc89bbff7cfbc0d4a64535087826a372 Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Fri, 31 Jan 2025 11:28:57 +0100 Subject: [PATCH 035/123] create duplicate IOFFs if multiple output ports are connected to the same register --- techlibs/quicklogic/ql_ioff.cc | 66 +++++++++++++++++++----- techlibs/quicklogic/synth_quicklogic.cc | 5 +- tests/arch/quicklogic/qlf_k6n10f/ioff.ys | 37 +++++++++++++ 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/techlibs/quicklogic/ql_ioff.cc b/techlibs/quicklogic/ql_ioff.cc index 4cc9785d5..87b62e855 100644 --- a/techlibs/quicklogic/ql_ioff.cc +++ b/techlibs/quicklogic/ql_ioff.cc @@ -29,7 +29,17 @@ struct QlIoffPass : public Pass { if (!module) return; modwalker.setup(module); - pool cells_to_replace; + pool input_ffs; + dict> output_ffs; + dict> output_bit_aliases; + + for (Wire* wire : module->wires()) + if (wire->port_output) { + output_ffs[wire].resize(wire->width, nullptr); + for (SigBit bit : SigSpec(wire)) + output_bit_aliases[modwalker.sigmap(bit)].insert(bit); + } + for (auto cell : module->selected_cells()) { if (cell->type.in(ID(dffsre), ID(sdffsre))) { log_debug("Checking cell %s.\n", cell->name.c_str()); @@ -53,32 +63,62 @@ struct QlIoffPass : public Pass { log_debug("not promoting: D has other consumers\n"); continue; } - cells_to_replace.insert(cell); - continue; // no need to check Q if we already put it on the list + input_ffs.insert(cell); + continue; // prefer input FFs over output FFs } + SigSpec q = cell->getPort(ID::Q); log_assert(GetSize(q) == 1); - if (modwalker.has_outputs(q)) { + if (modwalker.has_outputs(q) && !modwalker.has_consumers(q)) { log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str()); - // check that q_sig has no other consumers - pool portbits; - modwalker.get_consumers(portbits, q); - if (GetSize(portbits) > 0) { - log_debug("not promoting: Q has other consumers\n"); - continue; + for (SigBit bit : output_bit_aliases[modwalker.sigmap(q)]) { + log_assert(bit.is_wire()); + output_ffs[bit.wire][bit.offset] = cell; } - cells_to_replace.insert(cell); + } } } - for (auto cell : cells_to_replace) { - log("Promoting register %s to IOFF.\n", log_signal(cell->getPort(ID::Q))); + for (auto cell : input_ffs) { + log("Promoting register %s to input IOFF.\n", log_signal(cell->getPort(ID::Q))); cell->type = ID(dff); cell->unsetPort(ID::E); cell->unsetPort(ID::R); cell->unsetPort(ID::S); } + for (auto & [old_port_output, ioff_cells] : output_ffs) { + if (std::any_of(ioff_cells.begin(), ioff_cells.end(), [](Cell * c) { return c != nullptr; })) + { + // create replacement output wire + RTLIL::Wire* new_port_output = module->addWire(NEW_ID, old_port_output->width); + new_port_output->start_offset = old_port_output->start_offset; + module->swap_names(old_port_output, new_port_output); + std::swap(old_port_output->port_id, new_port_output->port_id); + std::swap(old_port_output->port_input, new_port_output->port_input); + std::swap(old_port_output->port_output, new_port_output->port_output); + std::swap(old_port_output->upto, new_port_output->upto); + std::swap(old_port_output->is_signed, new_port_output->is_signed); + std::swap(old_port_output->attributes, new_port_output->attributes); + + // create new output FFs + SigSpec sig_o(old_port_output); + SigSpec sig_n(new_port_output); + for (int i = 0; i < new_port_output->width; i++) { + if (ioff_cells[i]) { + log("Promoting %s to output IOFF.\n", log_signal(sig_n[i])); + + RTLIL::Cell *new_cell = module->addCell(NEW_ID, ID(dff)); + new_cell->setPort(ID::C, ioff_cells[i]->getPort(ID::C)); + new_cell->setPort(ID::D, ioff_cells[i]->getPort(ID::D)); + new_cell->setPort(ID::Q, sig_n[i]); + new_cell->set_bool_attribute(ID::keep); + } else { + module->connect(sig_n[i], sig_o[i]); + } + } + } + } } } QlIoffPass; diff --git a/techlibs/quicklogic/synth_quicklogic.cc b/techlibs/quicklogic/synth_quicklogic.cc index 16f660943..07ec769b5 100644 --- a/techlibs/quicklogic/synth_quicklogic.cc +++ b/techlibs/quicklogic/synth_quicklogic.cc @@ -334,9 +334,10 @@ struct SynthQuickLogicPass : public ScriptPass { run("opt_lut"); } - if (check_label("iomap", "(for qlf_k6n10f)") && (family == "qlf_k6n10f" || help_mode)) { + if (check_label("iomap", "(for qlf_k6n10f, skip if -noioff)") && (family == "qlf_k6n10f" || help_mode)) { if (ioff || help_mode) { - run("ql_ioff", "(unless -noioff)"); + run("ql_ioff"); + run("opt_clean"); } } diff --git a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys index 3144eda13..da1fb2946 100644 --- a/tests/arch/quicklogic/qlf_k6n10f/ioff.ys +++ b/tests/arch/quicklogic/qlf_k6n10f/ioff.ys @@ -21,6 +21,21 @@ EOF synth_quicklogic -family qlf_k6n10f -top top select -assert-count 4 t:dff +design -reset +# test: acceptable for output IOFF promotion; duplicate output FF +read_verilog < Date: Tue, 4 Feb 2025 12:24:34 +1300 Subject: [PATCH 036/123] docs/rosette: Sort, Struct, and PrintVisitor sections --- .../extending_yosys/functional_ir.rst | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 0eb9451be..f44693c16 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -217,31 +217,27 @@ and racket specifications to save on space in this document. Sort ~~~~ -- map variable declarations to s-expressions -- handles signals (bitvectors) and memories (arrays of bitvectors) +The ``Sort`` class is a wrapper for the ``Functional::Sort`` class, providing +the additional functionality of mapping variable declarations to s-expressions +with the ``to_sexpr()`` method. The main change from ``SmtSort`` to +``SmtrSort`` is a syntactical one with signals represented as ``bitvector``\ +s, and memories as ``list``\ s of signals. .. literalinclude:: /code_examples/functional/rosette.diff :language: diff :caption: diff of ``Sort`` wrapper - :start-at: -struct SmtSort { - :end-at: }; + :start-at: SExpr to_sexpr() const { + :end-before: }; Struct ~~~~~~ -- helpers for defining inputs/outputs/state -- struct fields in Rosette are accessed as ``-`` - - field names only need to be unique within the struct, while accessors - are unique within the module - -.. literalinclude:: /code_examples/functional/rosette.diff - :language: diff - :caption: diff of struct constructor - :start-at: - SmtStruct(std::string name, SmtScope &scope) - :end-before: void write_definition - -- each is a single (transparent) struct with zero or more fields -- each field has a name, with the type (sort) as a comment +The SMT-LIB backend uses a class, ``SmtStruct``, to help with describing the +input, output, and state data structs. Where each struct in the SMT-LIB output +is a new ``datatype`` with each element having its type declared using the +`Sort`_ above, in Rosette we use the native ``struct`` with each field only +being referred to by name. For ease of use, we include comments for each field +to indicate the expected type. .. literalinclude:: /code_examples/functional/rosette.diff :language: diff @@ -249,7 +245,22 @@ Struct :start-at: void write_definition :end-before: template void write_value -- writing outputs/next state +Struct fields in Rosette are accessed as ``-``, where +field names only need to be unique within the struct, while accessors are unique +within the module. We thus modify the class constructor and ``insert`` method +to support this; providing one scope that is local to the struct +(``local_scope``) and one which is shared across the whole module +(``global_scope``). + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of struct constructor + :start-at: - SmtStruct(std::string name, SmtScope &scope) + :end-before: void write_definition + +For writing outputs/next state (the ``write_value`` method), the only change is +to remove the check for zero-argument constructors since this is not necessary +with Rosette ``struct``\ s. .. literalinclude:: /code_examples/functional/rosette.diff :language: diff @@ -260,11 +271,25 @@ Struct PrintVisitor ~~~~~~~~~~~~ -- map FunctionalIR operations to Rosette - - most functions are the same or very similar to their corresponding smtlib - function +The ``PrintVisitor`` implements the abstract ``Functional::AbstractVisitor`` +class for converting FunctionalIR functions into s-expressions, including +reading inputs/current state. For most functions, the Rosette output is very +similar to the corresponding SMT-LIB function with minor adjustments for syntax. -- reading inputs/current state +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities + :start-at: SExpr zero_extend + :end-at: SExpr sub + +However there are some differences in the two formats with regards to how +booleans are handled, with Rosette providing built-in functions for conversion. + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences + :start-at: SExpr from_bool + :end-before: SExpr extract Module ~~~~~~ From fa2d45a9220c5d650a4b14d3dc183324325f0e5c Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 7 Feb 2025 13:58:09 +1300 Subject: [PATCH 037/123] smtr: Refactor write back into _eval and _initial Easier for comparisons, and the structure still works. (I don't remember why I moved away from it in the first place.) --- backends/functional/smtlib_rosette.cc | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/backends/functional/smtlib_rosette.cc b/backends/functional/smtlib_rosette.cc index a93bd04b0..c9e737d19 100644 --- a/backends/functional/smtlib_rosette.cc +++ b/backends/functional/smtlib_rosette.cc @@ -210,14 +210,8 @@ struct SmtrModule { state_struct.insert(state->name, state->sort); } - void write(std::ostream &out) - { - SExprWriter w(out); - - input_struct.write_definition(w); - output_struct.write_definition(w); - state_struct.write_definition(w); - + void write_eval(SExprWriter &w) + { w.push(); w.open(list("define", list(name, "inputs", "state"))); auto inlined = [&](Functional::Node n) { @@ -240,7 +234,10 @@ struct SmtrModule { output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); w.pop(); + } + void write_initial(SExprWriter &w) + { w.push(); auto initial = name + "_initial"; w.open(list("define", initial)); @@ -259,6 +256,18 @@ struct SmtrModule { } w.pop(); } + + void write(std::ostream &out) + { + SExprWriter w(out); + + input_struct.write_definition(w); + output_struct.write_definition(w); + state_struct.write_definition(w); + + write_eval(w); + write_initial(w); + } }; struct FunctionalSmtrBackend : public Backend { From dc5a5b7bd1518341fc983061c818f40190e9f8c4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 7 Feb 2025 14:13:34 +1300 Subject: [PATCH 038/123] docs/rosette: Regen rosette.diff This time from the actual source, which apparently means changing all of the spaces for tabs. --- .../code_examples/functional/rosette.diff | 654 +++++++++--------- 1 file changed, 320 insertions(+), 334 deletions(-) diff --git a/docs/source/code_examples/functional/rosette.diff b/docs/source/code_examples/functional/rosette.diff index 60fb60c1b..35c7936d1 100644 --- a/docs/source/code_examples/functional/rosette.diff +++ b/docs/source/code_examples/functional/rosette.diff @@ -1,385 +1,371 @@ -diff --git a/smtlib.cc b/smtlib_rosette.cc -index 3eacf407c..a93bd04b0 100644 ---- a/smtlib.cc -+++ b/smtlib_rosette.cc +diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib_rosette.cc +index 3eacf407c..c9e737d19 100644 +--- a/backends/functional/smtlib.cc ++++ b/backends/functional/smtlib_rosette.cc @@ -29,80 +29,86 @@ PRIVATE_NAMESPACE_BEGIN using SExprUtil::list; const char *reserved_keywords[] = { -- // reserved keywords from the smtlib spec -- "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", -- "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", -- "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", -- "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", -- "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", -- "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", -+ // reserved keywords from the racket spec -+ "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", -+ "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", -+ "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", -+ "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", -+ "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", -+ "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", -+ "define-generics", "set", +- // reserved keywords from the smtlib spec +- "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", +- "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", +- "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", +- "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", +- "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", +- "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", ++ // reserved keywords from the racket spec ++ "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", ++ "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", ++ "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", ++ "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", ++ "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", ++ "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", ++ "define-generics", "set", - // reserved for our own purposes -- "pair", "Pair", "first", "second", -- "inputs", "state", -+ "inputs", "state", "name", - nullptr + // reserved for our own purposes +- "pair", "Pair", "first", "second", +- "inputs", "state", ++ "inputs", "state", "name", + nullptr }; -struct SmtScope : public Functional::Scope { -- SmtScope() { +- SmtScope() { +struct SmtrScope : public Functional::Scope { -+ SmtrScope() { - for(const char **p = reserved_keywords; *p != nullptr; p++) - reserve(*p); - } - bool is_character_legal(char c, int index) override { -- return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); -+ return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); - } ++ SmtrScope() { + for(const char **p = reserved_keywords; *p != nullptr; p++) + reserve(*p); + } + bool is_character_legal(char c, int index) override { +- return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); ++ return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); + } }; -struct SmtSort { +struct SmtrSort { - Functional::Sort sort; -- SmtSort(Functional::Sort sort) : sort(sort) {} -+ SmtrSort(Functional::Sort sort) : sort(sort) {} - SExpr to_sexpr() const { - if(sort.is_memory()) { -- return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); -+ return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); - } else if(sort.is_signal()) { -- return list("_", "BitVec", sort.width()); -+ return list("bitvector", sort.width()); - } else { - log_error("unknown sort"); - } - } + Functional::Sort sort; +- SmtSort(Functional::Sort sort) : sort(sort) {} ++ SmtrSort(Functional::Sort sort) : sort(sort) {} + SExpr to_sexpr() const { + if(sort.is_memory()) { +- return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); ++ return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); + } else if(sort.is_signal()) { +- return list("_", "BitVec", sort.width()); ++ return list("bitvector", sort.width()); + } else { + log_error("unknown sort"); + } + } }; -class SmtStruct { +class SmtrStruct { - struct Field { -- SmtSort sort; -+ SmtrSort sort; - std::string accessor; -+ std::string name; - }; - idict field_names; - vector fields; -- SmtScope &scope; -+ SmtrScope &global_scope; -+ SmtrScope local_scope; + struct Field { +- SmtSort sort; ++ SmtrSort sort; + std::string accessor; ++ std::string name; + }; + idict field_names; + vector fields; +- SmtScope &scope; ++ SmtrScope &global_scope; ++ SmtrScope local_scope; public: - std::string name; -- SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} -- void insert(IdString field_name, SmtSort sort) { -+ SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} -+ void insert(IdString field_name, SmtrSort sort) { - field_names(field_name); -- auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); -- fields.emplace_back(Field{sort, accessor}); -+ auto base_name = local_scope.unique_name(field_name); -+ auto accessor = name + "-" + base_name; -+ global_scope.reserve(accessor); -+ fields.emplace_back(Field{sort, accessor, base_name}); - } - void write_definition(SExprWriter &w) { -- w.open(list("declare-datatype", name)); -- w.open(list()); -- w.open(list(name)); -- for(const auto &field : fields) -- w << list(field.accessor, field.sort.to_sexpr()); -- w.close(3); -+ vector field_list; -+ for(const auto &field : fields) { -+ field_list.emplace_back(field.name); -+ } -+ w.push(); -+ w.open(list("struct", name, field_list, "#:transparent")); -+ if (field_names.size()) { -+ for (const auto &field : fields) { -+ auto bv_type = field.sort.to_sexpr(); -+ w.comment(field.name + " " + bv_type.to_string()); -+ } -+ } -+ w.pop(); - } - template void write_value(SExprWriter &w, Fn fn) { -- if(field_names.empty()) { -- // Zero-argument constructors in SMTLIB must not be called as functions. -- w << name; -- } else { -- w.open(list(name)); -- for(auto field_name : field_names) { -- w << fn(field_name); -- w.comment(RTLIL::unescape_id(field_name), true); -- } -- w.close(); -+ w.open(list(name)); -+ for(auto field_name : field_names) { -+ w << fn(field_name); -+ w.comment(RTLIL::unescape_id(field_name), true); - } -+ w.close(); - } - SExpr access(SExpr record, IdString name) { - size_t i = field_names.at(name); + std::string name; +- SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} +- void insert(IdString field_name, SmtSort sort) { ++ SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} ++ void insert(IdString field_name, SmtrSort sort) { + field_names(field_name); +- auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); +- fields.emplace_back(Field{sort, accessor}); ++ auto base_name = local_scope.unique_name(field_name); ++ auto accessor = name + "-" + base_name; ++ global_scope.reserve(accessor); ++ fields.emplace_back(Field{sort, accessor, base_name}); + } + void write_definition(SExprWriter &w) { +- w.open(list("declare-datatype", name)); +- w.open(list()); +- w.open(list(name)); +- for(const auto &field : fields) +- w << list(field.accessor, field.sort.to_sexpr()); +- w.close(3); ++ vector field_list; ++ for(const auto &field : fields) { ++ field_list.emplace_back(field.name); ++ } ++ w.push(); ++ w.open(list("struct", name, field_list, "#:transparent")); ++ if (field_names.size()) { ++ for (const auto &field : fields) { ++ auto bv_type = field.sort.to_sexpr(); ++ w.comment(field.name + " " + bv_type.to_string()); ++ } ++ } ++ w.pop(); + } + template void write_value(SExprWriter &w, Fn fn) { +- if(field_names.empty()) { +- // Zero-argument constructors in SMTLIB must not be called as functions. +- w << name; +- } else { +- w.open(list(name)); +- for(auto field_name : field_names) { +- w << fn(field_name); +- w.comment(RTLIL::unescape_id(field_name), true); +- } +- w.close(); ++ w.open(list(name)); ++ for(auto field_name : field_names) { ++ w << fn(field_name); ++ w.comment(RTLIL::unescape_id(field_name), true); + } ++ w.close(); + } + SExpr access(SExpr record, IdString name) { + size_t i = field_names.at(name); @@ -117,28 +123,28 @@ std::string smt_const(RTLIL::Const const &c) { - return s; + return s; } -struct SmtPrintVisitor : public Functional::AbstractVisitor { +struct SmtrPrintVisitor : public Functional::AbstractVisitor { - using Node = Functional::Node; - std::function n; -- SmtStruct &input_struct; -- SmtStruct &state_struct; -+ SmtrStruct &input_struct; -+ SmtrStruct &state_struct; + using Node = Functional::Node; + std::function n; +- SmtStruct &input_struct; +- SmtStruct &state_struct; ++ SmtrStruct &input_struct; ++ SmtrStruct &state_struct; -- SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} -+ SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} +- SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} ++ SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} - SExpr from_bool(SExpr &&arg) { -- return list("ite", std::move(arg), "#b1", "#b0"); -+ return list("bool->bitvector", std::move(arg)); - } - SExpr to_bool(SExpr &&arg) { -- return list("=", std::move(arg), "#b1"); -+ return list("bitvector->bool", std::move(arg)); - } -- SExpr extract(SExpr &&arg, int offset, int out_width = 1) { -- return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); -+ SExpr to_list(SExpr &&arg) { -+ return list("bitvector->bits", std::move(arg)); - } + SExpr from_bool(SExpr &&arg) { +- return list("ite", std::move(arg), "#b1", "#b0"); ++ return list("bool->bitvector", std::move(arg)); + } + SExpr to_bool(SExpr &&arg) { +- return list("=", std::move(arg), "#b1"); ++ return list("bitvector->bool", std::move(arg)); + } +- SExpr extract(SExpr &&arg, int offset, int out_width = 1) { +- return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); ++ SExpr to_list(SExpr &&arg) { ++ return list("bitvector->bits", std::move(arg)); + } - SExpr buf(Node, Node a) override { return n(a); } -- SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } -- SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } -- SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } -+ SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } -+ SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } -+ SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } - SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } - SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } - SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } + SExpr buf(Node, Node a) override { return n(a); } +- SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } +- SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } +- SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } ++ SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } ++ SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } ++ SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } + SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } + SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } + SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } @@ -150,16 +156,11 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { - SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } - SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } - SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } -- SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } -- SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } -- SExpr reduce_xor(Node, Node a) override { -- vector s { "bvxor" }; -- for(int i = 0; i < a.width(); i++) -- s.push_back(extract(n(a), i)); -- return s; -- } -- SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } -- SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } -+ SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } -+ SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } -+ SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } -+ SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } -+ SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); } - SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } - SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } - SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } + SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } + SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } + SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } +- SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } +- SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } +- SExpr reduce_xor(Node, Node a) override { +- vector s { "bvxor" }; +- for(int i = 0; i < a.width(); i++) +- s.push_back(extract(n(a), i)); +- return s; +- } +- SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } +- SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } ++ SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } ++ SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } ++ SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } ++ SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } ++ SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); } + SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } + SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } + SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } @@ -167,32 +168,32 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { - SExpr extend(SExpr &&a, int in_width, int out_width) { - if(in_width < out_width) -- return list(list("_", "zero_extend", out_width - in_width), std::move(a)); -+ return list("zero-extend", std::move(a), list("bitvector", out_width)); - else - return std::move(a); - } - SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } - SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } - SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } -- SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } -- SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } -- SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } -- SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } -+ SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } -+ SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } -+ SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } -+ SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } + SExpr extend(SExpr &&a, int in_width, int out_width) { + if(in_width < out_width) +- return list(list("_", "zero_extend", out_width - in_width), std::move(a)); ++ return list("zero-extend", std::move(a), list("bitvector", out_width)); + else + return std::move(a); + } + SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } + SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } + SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } +- SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } +- SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } +- SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } +- SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } ++ SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } ++ SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } ++ SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } ++ SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } - SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } - SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } + SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } + SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } }; -struct SmtModule { +struct SmtrModule { - Functional::IR ir; -- SmtScope scope; -+ SmtrScope scope; - std::string name; - -- SmtStruct input_struct; -- SmtStruct output_struct; -- SmtStruct state_struct; -+ SmtrStruct input_struct; -+ SmtrStruct output_struct; -+ SmtrStruct state_struct; + Functional::IR ir; +- SmtScope scope; ++ SmtrScope scope; + std::string name; + +- SmtStruct input_struct; +- SmtStruct output_struct; +- SmtStruct state_struct; ++ SmtrStruct input_struct; ++ SmtrStruct output_struct; ++ SmtrStruct state_struct; -- SmtModule(Module *module) -+ SmtrModule(Module *module) - : ir(Functional::IR::from_module(module)) - , scope() - , name(scope.unique_name(module->name)) +- SmtModule(Module *module) ++ SmtrModule(Module *module) + : ir(Functional::IR::from_module(module)) + , scope() + , name(scope.unique_name(module->name)) @@ -200,7 +201,7 @@ struct SmtModule { - , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) - , state_struct(scope.unique_name(module->name.str() + "_State"), scope) - { -- scope.reserve(name + "-initial"); -+ scope.reserve(name + "_initial"); - for (auto input : ir.inputs()) - input_struct.insert(input->name, input->sort); - for (auto output : ir.outputs()) -@@ -209,17 +210,20 @@ struct SmtModule { - state_struct.insert(state->name, state->sort); - } + , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) + , state_struct(scope.unique_name(module->name.str() + "_State"), scope) + { +- scope.reserve(name + "-initial"); ++ scope.reserve(name + "_initial"); + for (auto input : ir.inputs()) + input_struct.insert(input->name, input->sort); + for (auto output : ir.outputs()) +@@ -212,14 +213,11 @@ struct SmtModule { + void write_eval(SExprWriter &w) + { + w.push(); +- w.open(list("define-fun", name, +- list(list("inputs", input_struct.name), +- list("state", state_struct.name)), +- list("Pair", output_struct.name, state_struct.name))); ++ w.open(list("define", list(name, "inputs", "state"))); + auto inlined = [&](Functional::Node n) { + return n.fn() == Functional::Fn::constant; + }; +- SmtPrintVisitor visitor(input_struct, state_struct); ++ SmtrPrintVisitor visitor(input_struct, state_struct); + auto node_to_sexpr = [&](Functional::Node n) -> SExpr { + if(inlined(n)) + return n.visit(visitor); +@@ -230,9 +228,9 @@ struct SmtModule { + for(auto n : ir) + if(!inlined(n)) { + w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); +- w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); ++ w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); + } +- w.open(list("pair")); ++ w.open(list("cons")); + output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); + state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); + w.pop(); +@@ -240,19 +238,23 @@ struct SmtModule { -- void write_eval(SExprWriter &w) -- { -+ void write(std::ostream &out) -+ { -+ SExprWriter w(out); -+ -+ input_struct.write_definition(w); -+ output_struct.write_definition(w); -+ state_struct.write_definition(w); -+ - w.push(); -- w.open(list("define-fun", name, -- list(list("inputs", input_struct.name), -- list("state", state_struct.name)), -- list("Pair", output_struct.name, state_struct.name))); -+ w.open(list("define", list(name, "inputs", "state"))); - auto inlined = [&](Functional::Node n) { - return n.fn() == Functional::Fn::constant; - }; -- SmtPrintVisitor visitor(input_struct, state_struct); -+ SmtrPrintVisitor visitor(input_struct, state_struct); - auto node_to_sexpr = [&](Functional::Node n) -> SExpr { - if(inlined(n)) - return n.visit(visitor); -@@ -230,66 +234,75 @@ struct SmtModule { - for(auto n : ir) - if(!inlined(n)) { - w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); -- w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); -+ w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); - } -- w.open(list("pair")); -+ w.open(list("cons")); - output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); - state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); - w.pop(); -- } + void write_initial(SExprWriter &w) + { +- std::string initial = name + "-initial"; +- w << list("declare-const", initial, state_struct.name); ++ w.push(); ++ auto initial = name + "_initial"; ++ w.open(list("define", initial)); ++ w.open(list(state_struct.name)); + for (auto state : ir.states()) { +- if(state->sort.is_signal()) +- w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); +- else if(state->sort.is_memory()) { ++ if (state->sort.is_signal()) ++ w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); ++ else if (state->sort.is_memory()) { + const auto &contents = state->initial_value_memory(); ++ w.open(list("list")); + for(int i = 0; i < 1<sort.addr_width(); i++) { +- auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); +- w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); ++ w << list("bv", smt_const(contents[i]), state->sort.data_width()); + } ++ w.close(); + } + } ++ w.pop(); + } -- void write_initial(SExprWriter &w) -- { -- std::string initial = name + "-initial"; -- w << list("declare-const", initial, state_struct.name); -+ w.push(); -+ auto initial = name + "_initial"; -+ w.open(list("define", initial)); -+ w.open(list(state_struct.name)); - for (auto state : ir.states()) { -- if(state->sort.is_signal()) -- w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); -- else if(state->sort.is_memory()) { -+ if (state->sort.is_signal()) -+ w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); -+ else if (state->sort.is_memory()) { - const auto &contents = state->initial_value_memory(); -+ w.open(list("list")); - for(int i = 0; i < 1<sort.addr_width(); i++) { -- auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); -- w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); -+ w << list("bv", smt_const(contents[i]), state->sort.data_width()); - } -+ w.close(); - } - } -- } -- -- void write(std::ostream &out) -- { -- SExprWriter w(out); -- -- input_struct.write_definition(w); -- output_struct.write_definition(w); -- state_struct.write_definition(w); -- -- w << list("declare-datatypes", -- list(list("Pair", 2)), -- list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); -- -- write_eval(w); -- write_initial(w); -+ w.pop(); - } + void write(std::ostream &out) +@@ -263,33 +265,53 @@ struct SmtModule { + output_struct.write_definition(w); + state_struct.write_definition(w); + +- w << list("declare-datatypes", +- list(list("Pair", 2)), +- list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); +- + write_eval(w); + write_initial(w); + } }; -struct FunctionalSmtBackend : public Backend { -- FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} +- FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} +struct FunctionalSmtrBackend : public Backend { -+ FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} ++ FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} -- void help() override { log("\nFunctional SMT Backend.\n\n"); } -+ void help() override { -+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| -+ log("\n"); -+ log(" write_functional_rosette [options] [filename]\n"); -+ log("\n"); -+ log("Functional Rosette Backend.\n"); -+ log("\n"); -+ log(" -provides\n"); -+ log(" include 'provide' statement(s) for loading output as a module\n"); -+ log("\n"); -+ } +- void help() override { log("\nFunctional SMT Backend.\n\n"); } ++ void help() override { ++ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| ++ log("\n"); ++ log(" write_functional_rosette [options] [filename]\n"); ++ log("\n"); ++ log("Functional Rosette Backend.\n"); ++ log("\n"); ++ log(" -provides\n"); ++ log(" include 'provide' statement(s) for loading output as a module\n"); ++ log("\n"); ++ } - void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override - { -- log_header(design, "Executing Functional SMT Backend.\n"); -+ auto provides = false; + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { +- log_header(design, "Executing Functional SMT Backend.\n"); ++ auto provides = false; + -+ log_header(design, "Executing Functional Rosette Backend.\n"); ++ log_header(design, "Executing Functional Rosette Backend.\n"); -- size_t argidx = 1; -- extra_args(f, filename, args, argidx, design); -+ size_t argidx; -+ for (argidx = 1; argidx < args.size(); argidx++) -+ { -+ if (args[argidx] == "-provides") -+ provides = true; -+ else -+ break; -+ } -+ extra_args(f, filename, args, argidx); +- size_t argidx = 1; +- extra_args(f, filename, args, argidx, design); ++ size_t argidx; ++ for (argidx = 1; argidx < args.size(); argidx++) ++ { ++ if (args[argidx] == "-provides") ++ provides = true; ++ else ++ break; ++ } ++ extra_args(f, filename, args, argidx); + -+ *f << "#lang rosette/safe\n"; -+ if (provides) { -+ *f << "(provide (all-defined-out))\n"; -+ } ++ *f << "#lang rosette/safe\n"; ++ if (provides) { ++ *f << "(provide (all-defined-out))\n"; ++ } - for (auto module : design->selected_modules()) { - log("Processing module `%s`.\n", module->name.c_str()); -- SmtModule smt(module); -- smt.write(*f); -+ SmtrModule smtr(module); -+ smtr.write(*f); - } - } + for (auto module : design->selected_modules()) { + log("Processing module `%s`.\n", module->name.c_str()); +- SmtModule smt(module); +- smt.write(*f); ++ SmtrModule smtr(module); ++ smtr.write(*f); + } + } -} FunctionalSmtBackend; +} FunctionalSmtrBackend; From 34c424be68dab9c9160e8e2b7acf2f5c1cdf6463 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 7 Feb 2025 15:08:24 +1300 Subject: [PATCH 039/123] docs/rosette: Minor updates - Fixing typo - Reference `write_functional_rosette` - Adjusting/fixing diff sections - Comment on why the `code-block:: diff` isn't a `literalinclude` --- .../yosys_internals/extending_yosys/functional_ir.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index f44693c16..30d2b3b45 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -149,7 +149,7 @@ Example: Adapting SMT-LIB backend for Rosette This section will walk through the process of adapting the SMT-LIB functional backend (`write_functional_smt2`) to work with another s-expression target, -`Rosette`_. +`Rosette`_ (`write_functional_rosette`). .. _Rosette: http://emina.github.io/rosette/ @@ -184,7 +184,7 @@ reject ascii characters which are not allowed in Racket variable names. .. literalinclude:: /code_examples/functional/rosette.diff :language: diff - :caption: diff of ``Scope`` close + :caption: diff of ``Scope`` class :start-at: -struct SmtScope : public Functional::Scope { :end-at: }; @@ -195,6 +195,7 @@ also no longer need to reserve ``pair``, ``first``, and ``second``. In current_state)`` and ``(outputs, next_state)`` into a single variable. Racket provides this functionality natively with ``cons``, which we will see later. +.. inlined diff for skipping the actual lists .. code-block:: diff :caption: diff of ``reserved_keywords`` list @@ -255,7 +256,7 @@ to support this; providing one scope that is local to the struct .. literalinclude:: /code_examples/functional/rosette.diff :language: diff :caption: diff of struct constructor - :start-at: - SmtStruct(std::string name, SmtScope &scope) + :start-at: SmtStruct(std::string name, SmtScope &scope) :end-before: void write_definition For writing outputs/next state (the ``write_value`` method), the only change is @@ -279,8 +280,8 @@ similar to the corresponding SMT-LIB function with minor adjustments for syntax. .. literalinclude:: /code_examples/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities - :start-at: SExpr zero_extend - :end-at: SExpr sub + :start-at: SExpr logical_shift_left + :end-before: SExpr input However there are some differences in the two formats with regards to how booleans are handled, with Rosette providing built-in functions for conversion. From b02d2c633e9ba617a8b69ac86a26fac2ffc6690e Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 7 Feb 2025 15:08:49 +1300 Subject: [PATCH 040/123] docs/rosette: Module section body --- .../extending_yosys/functional_ir.rst | 85 ++++++++++++++++++- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 30d2b3b45..73589dfe4 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -295,10 +295,87 @@ booleans are handled, with Rosette providing built-in functions for conversion. Module ~~~~~~ -- map RTLIL module to FunctionalIR -- iterate over FunctionalIR and map to Rosette - - defines the mapping function, ``(inputs, current_state) -> (outputs, - next_state)`` +The ``Functional::IR`` is wrapped in the ``Module`` class, with the mapping from +RTLIL module to FunctionalIR happening in the constructor. Each of the three +structs; inputs, outputs, and state; are then created from the corresponding +lists in the IR. The only change here is rename the initial state to use ``_`` +instead of ``-``, since the ``-`` in Rosette can be used to access struct +fields. + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of ``Module`` constructor + :start-at: scope.reserve(name + :end-before: for (auto input + +The ``write`` method is then responsible for writing the FunctionalIR to the +output file, formatted for the corresponding backend. ``SmtModule::write()`` +breaks the output file down into four parts: defining the three structs, +declaring the ``pair`` datatype, defining the mapping function ``(inputs, +current_state) -> (outputs, next_state)`` with ``write_eval``, and declaring the +initial state with ``write_initial``. The only change for the ``SmtrModule`` is +that the ``pair`` declaration isn't needed. + +.. inlined diff to show the complete function +.. code-block:: diff + :caption: diff of ``Module::write()`` method + + void write(std::ostream &out) + { + SExprWriter w(out); + + input_struct.write_definition(w); + output_struct.write_definition(w); + state_struct.write_definition(w); + + - w << list("declare-datatypes", + - list(list("Pair", 2)), + - list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); + - + write_eval(w); + write_initial(w); + } + +For the ``write_eval`` method, the main differences are syntactical. First we +change the function declaration line for the Rosette style which drops the +explicit output typing and uses the ``define`` keyword instead of +``define-fun``. And then we change the final result from a ``pair`` to the +native ``cons`` which acts in much the same way, returning both the ``outputs`` +and the ``next_state`` in a single variable. Iteration over all of the +``Functional::Node``\ s in the IR is the same in both. + +.. inlined diff for showing the whole function while skipping the middle part +.. code-block:: diff + :caption: diff of ``Module::write_eval()`` method + + void write_eval(SExprWriter &w) + { + w.push(); + - w.open(list("define-fun", name, + - list(list("inputs", input_struct.name), + - list("state", state_struct.name)), + - list("Pair", output_struct.name, state_struct.name))); + + w.open(list("define", list(name, "inputs", "state"))); + ... + - w.open(list("pair")); + + w.open(list("cons")); + output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); + state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); + w.pop(); + } + +For the ``write_initial`` method, the SMT-LIB backend uses ``declare-const`` and +`assert`\ s which must always hold true. For Rosette we instead define the +initial state as any other variable that can be used by external code. This +variable, ``[name]_initial``, can then be used in the ``[name]`` function call; +allowing the Rosette code to be used in the generation of the ``next_state``, +whereas the SMT-LIB code can only verify that a given ``next_state`` is correct. + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: diff of ``Module::write_initial()`` method + :start-at: void write_initial + :end-before: void write Backend ~~~~~~~ From 819c3260ec28797d12d5afe6ff356c10b7c786ee Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 7 Feb 2025 15:33:40 +1300 Subject: [PATCH 041/123] docs/rosette: Backend section body --- .../extending_yosys/functional_ir.rst | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 73589dfe4..81d987f0e 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -380,10 +380,24 @@ whereas the SMT-LIB code can only verify that a given ``next_state`` is correct. Backend ~~~~~~~ -- registers the `write_functional_rosette` command -- options (``-provides``) - - allows file to be treated as a Racket package with structs and mapping - function available for use externally +The final part is the ``Backend`` itself, which registers the command in Yosys. +The ``execute`` method is the part that runs when the user calls +`write_functional_rosette`, handling any options, preparing the output file for +writing, and iterating over selected modules in the design. For more on adding +new commands to Yosys and how they work, refer to +:doc:`/yosys_internals/extending_yosys/extensions`. -- opens and prepares file for writing -- iterates over modules in design +There are two additions here for Rosette. The first is that the output file +needs to start with the ``#lang`` definition which tells the +compiler/interpreter that we want to use the Rosette language module. The +second is that the `write_functional_rosette` command takes an optional +argument, ``-provides``. If this argument is given, then the output file gets +an additional line declaring that everything in the file should be exported for +use; allowing the file to be treated as a Racket package with structs and +mapping function available for use externally. + +.. literalinclude:: /code_examples/functional/rosette.diff + :language: diff + :caption: relevant portion of diff of ``Backend::execute()`` method + :start-at: lang rosette/safe + :end-before: for (auto module From 01d2bfcf00b1de487558cbe026164c66d69b0b9c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 12 Feb 2025 10:16:44 +0100 Subject: [PATCH 042/123] share: fix infinite loop in find_terminal_bits on $mux loop --- passes/opt/share.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 6081d140e..40d6d1d20 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -87,7 +87,7 @@ struct ShareWorker queue_bits.clear(); for (auto &pbit : portbits) { - if (pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) { + if ((pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) && visited_cells.count(pbit.cell) == 0) { pool bits = modwalker.sigmap(pbit.cell->getPort(ID::S)).to_sigbit_pool(); terminal_bits.insert(bits.begin(), bits.end()); queue_bits.insert(bits.begin(), bits.end()); From a8052f653a216f9501150abc94a09c14f8459254 Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Fri, 14 Feb 2025 01:21:39 +0200 Subject: [PATCH 043/123] write_xaiger: Detect and error on combinatorial loops --- backends/aiger/xaiger.cc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 3ca8b205a..baf504ba2 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -53,6 +53,8 @@ struct XAigerWriter dict arrival_times; vector> aig_gates; + vector bit2aig_stack; + int next_loop_check = 1024; vector aig_outputs; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; @@ -76,6 +78,24 @@ struct XAigerWriter return it->second; } + if (GetSize(bit2aig_stack)== next_loop_check) { + for (int i = 0; i < next_loop_check; ++i) + { + SigBit report_bit = bit2aig_stack[i]; + if (report_bit != bit) + continue; + for (int j = i; j < next_loop_check; ++j) { + report_bit = bit2aig_stack[j]; + if (report_bit.is_wire() && report_bit.wire->name.isPublic()) + break; + } + log_error("Found combinatorial logic loop while processing signal %s.\n", log_signal(report_bit)); + } + next_loop_check *= 2; + } + + bit2aig_stack.push_back(bit); + // NB: Cannot use iterator returned from aig_map.insert() // since this function is called recursively @@ -93,6 +113,8 @@ struct XAigerWriter a = bit2aig(alias_map.at(bit)); } + bit2aig_stack.pop_back(); + if (bit == State::Sx || bit == State::Sz) { log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n"); a = aig_map.at(State::S0); From db5b76edc14649e82d87c258481a2b0a46b1c345 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 14 Feb 2025 13:28:24 +1300 Subject: [PATCH 044/123] Add test for shifting by INT_MAX Currently resulting in CI failing on main during fsm checks which generate a circuit that simplifies to this. --- tests/opt/opt_expr_shr_int_max.ys | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/opt/opt_expr_shr_int_max.ys diff --git a/tests/opt/opt_expr_shr_int_max.ys b/tests/opt/opt_expr_shr_int_max.ys new file mode 100644 index 000000000..5fb3c9d37 --- /dev/null +++ b/tests/opt/opt_expr_shr_int_max.ys @@ -0,0 +1,9 @@ +read_verilog << EOF +module uut_00034(b, y); + input signed [30:0] b; + output [11:0] y = b >> ~31'b0; // shift by INT_MAX +endmodule +EOF + +# This should succeed, even with UBSAN halt_on_error +opt_expr From 4c728968a3cedb90dae206e34624d831997d1ecf Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 14 Feb 2025 13:29:24 +1300 Subject: [PATCH 045/123] Fix runtime error on shr INT_MAX --- passes/opt/opt_expr.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index ac4a65156..62a0ffc48 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1315,6 +1315,10 @@ skip_fine_alu: RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); RTLIL::SigSpec sig_y(cell->type == ID($shiftx) ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam(ID::Y_WIDTH).as_int()); + // Limit indexing to the size of a, which is behaviourally identical (result is all 0) + // and avoids integer overflow of i + shift_bits when e.g. ID::B == INT_MAX + shift_bits = min(shift_bits, GetSize(sig_a)); + if (cell->type != ID($shiftx) && GetSize(sig_a) < GetSize(sig_y)) sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool()); From 36764296341bb9491bba907610dc9ddb277a35cb Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Fri, 14 Feb 2025 10:53:51 -0800 Subject: [PATCH 046/123] `aldff`s do not get split by splitcells --- passes/cmds/splitcells.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/splitcells.cc b/passes/cmds/splitcells.cc index 82ed49074..074bf0f64 100644 --- a/passes/cmds/splitcells.cc +++ b/passes/cmds/splitcells.cc @@ -134,7 +134,7 @@ struct SplitcellsWorker return GetSize(slices)-1; } - if (cell->type.in("$ff", "$dff", "$dffe", "$dffsr", "$dffsre", "$adff", "$adffe", "$aldffe", + if (cell->type.in("$ff", "$dff", "$dffe", "$dffsr", "$dffsre", "$adff", "$adffe", "$aldff", "$aldffe", "$sdff", "$sdffce", "$sdffe", "$dlatch", "$dlatchsr", "$adlatch")) { auto splitports = {ID::D, ID::Q, ID::AD, ID::SET, ID::CLR}; From 3bca4c10d8e4bb32e6071eeef943e8b40ab8ab38 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Fri, 14 Feb 2025 11:43:00 -0800 Subject: [PATCH 047/123] Fix macOS boost-python issues --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 5bb6496c4..87572261e 100644 --- a/Makefile +++ b/Makefile @@ -123,8 +123,8 @@ ifneq ($(shell :; command -v brew),) BREW_PREFIX := $(shell brew --prefix)/opt $(info $$BREW_PREFIX is [${BREW_PREFIX}]) ifeq ($(ENABLE_PYOSYS),1) -CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost -LINKFLAGS += -L$(BREW_PREFIX)/boost/lib +CXXFLAGS += -I$(BREW_PREFIX)/boost/include +LINKFLAGS += -L$(BREW_PREFIX)/boost/lib -L$(BREW_PREFIX)/boost-python3/lib endif CXXFLAGS += -I$(BREW_PREFIX)/readline/include LINKFLAGS += -L$(BREW_PREFIX)/readline/lib @@ -331,7 +331,7 @@ endif ifeq ($(ENABLE_PYOSYS),1) # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers -CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") +CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) $(LINKFLAGS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ From 8968986b543a6d3b42408ec5d3eb5996210ad209 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 14 Feb 2025 18:57:36 +0100 Subject: [PATCH 048/123] share: add -pattern-limit to set analysis effort on branch-unbranch mux regions --- passes/opt/share.cc | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 6081d140e..bf583dd32 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -23,6 +23,7 @@ #include "kernel/modtools.h" #include "kernel/utils.h" #include "kernel/macc.h" +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -33,6 +34,7 @@ typedef std::pair ssc_pair_t; struct ShareWorkerConfig { int limit; + size_t pattern_limit; bool opt_force; bool opt_aggressive; bool opt_fast; @@ -853,6 +855,21 @@ struct ShareWorker optimize_activation_patterns(patterns); } + template + void insert_capped(pool& cache, Iterator begin_pattern, Iterator end_pattern) + { + if (cache.size() + std::distance(begin_pattern, end_pattern) > config.pattern_limit) { + cache.clear(); + cache.insert(ssc_pair_t()); + } else { + cache.insert(begin_pattern, end_pattern); + } + } + void insert_capped(pool& cache, ssc_pair_t pattern) + { + insert_capped(cache, &pattern, &pattern + 1); + } + const pool &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) { if (recursion_state.count(cell)) { @@ -909,20 +926,20 @@ struct ShareWorker for (int i = 0; i < GetSize(sig_s); i++) p.first.append(sig_s[i]), p.second.bits().push_back(RTLIL::State::S0); if (sort_check_activation_pattern(p)) - activation_patterns_cache[cell].insert(p); + insert_capped(activation_patterns_cache[cell], p); } for (int idx : used_in_b_parts) for (auto p : c_patterns) { p.first.append(sig_s[idx]), p.second.bits().push_back(RTLIL::State::S1); if (sort_check_activation_pattern(p)) - activation_patterns_cache[cell].insert(p); + insert_capped(activation_patterns_cache[cell], p); } } for (auto c : driven_cells) { const pool &c_patterns = find_cell_activation_patterns(c, indent); - activation_patterns_cache[cell].insert(c_patterns.begin(), c_patterns.end()); + insert_capped(activation_patterns_cache[cell], c_patterns.begin(), c_patterns.end()); } log_assert(recursion_state.count(cell) != 0); @@ -1437,6 +1454,10 @@ struct SharePass : public Pass { log("\n"); log(" -limit N\n"); log(" Only perform the first N merges, then stop. This is useful for debugging.\n"); + log(" -pattern-limit N\n"); + log(" Only analyze up to N activation patterns per cell, otherwise assume active.\n"); + log(" N is 1000 by default. Higher values may merge more resources at the cost of\n"); + log(" more runtime and memory consumption.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override @@ -1444,6 +1465,7 @@ struct SharePass : public Pass { ShareWorkerConfig config; config.limit = -1; + config.pattern_limit = 1000; config.opt_force = false; config.opt_aggressive = false; config.opt_fast = false; @@ -1508,6 +1530,10 @@ struct SharePass : public Pass { config.limit = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-pattern-limit" && argidx+1 < args.size()) { + config.pattern_limit = atoi(args[++argidx].c_str()); + continue; + } break; } extra_args(args, argidx, design); From 38f858374c0ed83a6ac3ea66715263af015d7974 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 00:20:50 +0000 Subject: [PATCH 049/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5bb6496c4..fd4d27915 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+1 +YOSYS_VER := 0.50+7 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 2b33937ab81fa1fda60a7e4a0122ec8e08a78d8c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 17 Feb 2025 17:36:51 +0100 Subject: [PATCH 050/123] liberty: fix clear and preset latches --- frontends/liberty/liberty.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index b29984ecd..cda13ff8b 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -348,7 +348,7 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_)); enable_gate->setPort(ID::A, enable_sig); enable_gate->setPort(ID::B, clear_enable); - enable_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID)); + enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID)); } if (preset_sig.size() == 1) @@ -376,7 +376,7 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_)); enable_gate->setPort(ID::A, enable_sig); enable_gate->setPort(ID::B, preset_enable); - enable_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID)); + enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID)); } cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N')); From b8777d789300ee9c2d860c7156f4cd9b8da43f76 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 18 Feb 2025 11:25:04 +1300 Subject: [PATCH 051/123] Makefile: Warn if submodule status '+'-prefixed A '+'-prefix means that the submodule is initialized and checked out, but a different commit is checked out. If this is accidental then the user should run `git submodule update` to fix it. If it is intentional (because e.g. the user is explicitly wanting to test Yosys with a different version of abc), then creating a new commit in Yosys to update the expected commit is also a valid solution. --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index fd4d27915..b1853f77a 100644 --- a/Makefile +++ b/Makefile @@ -775,6 +775,11 @@ check-git-abc: elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && ! grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "'abc' comes from a tarball. Continuing."; \ exit 0; \ + elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^+'; then \ + echo "'abc' submodule does not match expected commit."; \ + echo "Run 'git submodule update' to check out the correct version."; \ + echo "Note: If testing a different version of abc, call `git commit abc` in the Yosys source directory to update the expected commit."; \ + exit 1; \ elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "Error: 'abc' is not configured as a git submodule."; \ echo "To resolve this:"; \ From 03d9138744907d9747b4fd394ba39de4609716a4 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 18 Feb 2025 11:33:06 +1300 Subject: [PATCH 052/123] Makefile: 'U'-prefixed submodule status Covering all the bases, I guess? '-'-prefix is already correctly handled by the base case message. If the user somehow gets merge conflicts in abc, hopefully they know what they're doing. --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index b1853f77a..61eac585d 100644 --- a/Makefile +++ b/Makefile @@ -780,6 +780,10 @@ check-git-abc: echo "Run 'git submodule update' to check out the correct version."; \ echo "Note: If testing a different version of abc, call `git commit abc` in the Yosys source directory to update the expected commit."; \ exit 1; \ + elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^U'; then \ + echo "'abc' submodule has merge conflicts."; \ + echo "Please resolve merge conflicts before continuing."; \ + exit 1; \ elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "Error: 'abc' is not configured as a git submodule."; \ echo "To resolve this:"; \ From dc03e174af93ccdf86996f82b46ad911f0e923d0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 18 Feb 2025 11:46:11 +1300 Subject: [PATCH 053/123] fixup! Makefile: Warn if submodule status '+'-prefixed --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 61eac585d..ea9fbc9e3 100644 --- a/Makefile +++ b/Makefile @@ -778,7 +778,7 @@ check-git-abc: elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^+'; then \ echo "'abc' submodule does not match expected commit."; \ echo "Run 'git submodule update' to check out the correct version."; \ - echo "Note: If testing a different version of abc, call `git commit abc` in the Yosys source directory to update the expected commit."; \ + echo "Note: If testing a different version of abc, call 'git commit abc' in the Yosys source directory to update the expected commit."; \ exit 1; \ elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^U'; then \ echo "'abc' submodule has merge conflicts."; \ From e4ca7b8846b8684a30ba809369f2d2be983734dd Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 30 Jan 2025 17:26:23 +0100 Subject: [PATCH 054/123] abstract: -state MVP --- passes/cmds/Makefile.inc | 1 + passes/cmds/abstract.cc | 169 ++++++++++++++++++++++++++++++++++++++ tests/various/abstract.ys | 97 ++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 passes/cmds/abstract.cc create mode 100644 tests/various/abstract.ys diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 5a2af4df5..b15e91edb 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -53,3 +53,4 @@ OBJS += passes/cmds/example_dt.o OBJS += passes/cmds/portarcs.o OBJS += passes/cmds/wrapcell.o OBJS += passes/cmds/setenv.o +OBJS += passes/cmds/abstract.o diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc new file mode 100644 index 000000000..e336fad47 --- /dev/null +++ b/passes/cmds/abstract.cc @@ -0,0 +1,169 @@ +#include "kernel/yosys.h" +#include "kernel/celltypes.h" +#include "kernel/ff.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool abstract_state(Module* mod, Cell* cell, Wire* enable, bool enable_pol) { + FfData ff(nullptr, cell); + if (ff.has_sr) + log_cmd_error("SR not supported\n"); + + // Normalize to simpler FF + ff.unmap_ce(); + ff.unmap_srst(); + if (ff.has_arst) + ff.arst_to_aload(); + + // Construct abstract value + auto anyseq = mod->Anyseq(NEW_ID, ff.width); + + if (ff.has_aload) { + // ad := enable ? anyseq : ad + Wire* abstracted_ad = mod->addWire(NEW_ID, ff.sig_ad.size()); + SigSpec mux_a, mux_b; + if (enable_pol) { + mux_a = ff.sig_ad; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = ff.sig_ad; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable, + abstracted_ad); + ff.sig_ad = abstracted_ad; + } + // d := enable ? anyseq : d + Wire* abstracted_d = mod->addWire(NEW_ID, ff.sig_d.size()); + SigSpec mux_a, mux_b; + if (enable_pol) { + mux_a = ff.sig_d; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = ff.sig_d; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable, + abstracted_d); + ff.sig_d = abstracted_d; + (void)ff.emit(); + return true; +} + +bool abstract_value(Module* mod, Wire* wire, Wire* enable, bool enable_pol) { + // (void)mod->addMux(NEW_ID, + // mux_a, + // mux_b, + // enable, + // abstracted); + // cell->setPort(ID::D, SigSpec(abstracted)); + return false; +} + +bool abstract_init(Module* mod, Cell* cell) { + CellTypes ct; + ct.setup_internals_ff(); + if (!ct.cell_types.count(cell->type)) + return false; + // TODO figure out memory cells? + + cell->unsetParam(ID::init); + return true; +} + + +struct AbstractPass : public Pass { + AbstractPass() : Pass("abstract", "extract clock gating out of flip flops") { } + void help() override { + // TODO + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override { + log_header(design, "Executing ABSTRACT pass.\n"); + + size_t argidx; + enum Mode { + None, + State, + Initial, + Value, + }; + Mode mode; + std::string enable_name; + bool enable_pol; // true is high + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-state") { + mode = State; + continue; + } + if (arg == "-init") { + mode = Initial; + continue; + } + if (arg == "-value") { + mode = Value; + continue; + } + if (arg == "-enable") { + enable_name = args[++argidx]; + enable_pol = true; + continue; + } + if (arg == "-enablen") { + enable_name = args[++argidx]; + enable_pol = false; + continue; + } + break; + } + extra_args(args, argidx, design); + + unsigned int changed = 0; + if ((mode == State) || (mode == Value)) { + if (!enable_name.length()) + log_cmd_error("Unspecified enable wire\n"); + CellTypes ct; + if (mode == State) + ct.setup_internals_ff(); + for (auto mod : design->selected_modules()) { + log_debug("module %s\n", mod->name.c_str()); + Wire *enable_wire = mod->wire("\\" + enable_name); + if (!enable_wire) + log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + if (mode == State) { + for (auto cell : mod->selected_cells()) + if (ct.cell_types.count(cell->type)) + changed += abstract_state(mod, cell, enable_wire, enable_pol); + } else { + // Value + for (auto wire : mod->selected_wires()) { + changed += abstract_value(mod, wire, enable_wire, enable_pol); + } + log_cmd_error("Unsupported (TODO)\n"); + } + } + log("Abstracted %d cells.\n", changed); + } else if (mode == Initial) { + for (auto mod : design->selected_modules()) { + for (auto cell : mod->selected_cells()) { + changed += abstract_init(mod, cell); + } + } + log("Abstracted %d wires.\n", changed); + } else { + log_cmd_error("No mode selected, see help message\n"); + } + } +} AbstractPass; + +PRIVATE_NAMESPACE_END diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys new file mode 100644 index 000000000..8536960ca --- /dev/null +++ b/tests/various/abstract.ys @@ -0,0 +1,97 @@ +read_verilog < Date: Mon, 3 Feb 2025 22:25:09 +0100 Subject: [PATCH 055/123] abstract: -init MVP --- passes/cmds/abstract.cc | 68 ++++++++++++++++++++++++++++++++------- tests/various/abstract.ys | 13 ++------ 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index e336fad47..58fa2c513 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -67,17 +67,63 @@ bool abstract_value(Module* mod, Wire* wire, Wire* enable, bool enable_pol) { return false; } -bool abstract_init(Module* mod, Cell* cell) { - CellTypes ct; - ct.setup_internals_ff(); - if (!ct.cell_types.count(cell->type)) - return false; - // TODO figure out memory cells? +struct AbstractInitCtx { + Module* mod; + SigMap sigmap; + pool init_bits; +}; - cell->unsetParam(ID::init); - return true; +void collect_init_bits_cells(AbstractInitCtx& ctx) { + // TODO Should this discriminate between FFs and other cells? + for (auto cell : ctx.mod->selected_cells()) { + // Add all sigbits on all cell outputs to init_bits + for (auto &conn : cell->connections()) { + if (cell->output(conn.first)) { + for (auto bit : conn.second) { + log_debug("init: cell %s output %s\n", cell->name.c_str(), log_signal(bit)); + ctx.init_bits.insert(ctx.sigmap(bit)); + } + } + } + } } +void collect_init_bits_wires(AbstractInitCtx& ctx) { + for (auto wire : ctx.mod->selected_wires()) { + auto canonical = ctx.sigmap(wire); + // Find canonical drivers of all the wire bits and add them to init_bits + for (auto bit : canonical.bits()) { + log_debug("init: wire %s bit %s\n", wire->name.c_str(), log_signal(bit)); + ctx.init_bits.insert(ctx.sigmap(bit)); + } + } +} + +unsigned int abstract_init(Module* mod) { + AbstractInitCtx ctx {mod, SigMap(mod), pool()}; + pool init_bits; + collect_init_bits_cells(ctx); + collect_init_bits_wires(ctx); + unsigned int changed = 0; + + for (SigBit bit : ctx.init_bits) { +next_sigbit: + if (!bit.is_wire() || !bit.wire->has_attribute(ID::init)) + continue; + + Const init = bit.wire->attributes.at(ID::init); + std::vector& bits = init.bits(); + bits[bit.offset] = RTLIL::State::Sx; + changed++; + + for (auto bit : bits) + if (bit != RTLIL::State::Sx) + goto next_sigbit; + // All bits are Sx, erase init attribute entirely + bit.wire->attributes.erase(ID::init); + } + return changed; +} struct AbstractPass : public Pass { AbstractPass() : Pass("abstract", "extract clock gating out of flip flops") { } @@ -155,11 +201,9 @@ struct AbstractPass : public Pass { log("Abstracted %d cells.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { - for (auto cell : mod->selected_cells()) { - changed += abstract_init(mod, cell); - } + changed += abstract_init(mod); } - log("Abstracted %d wires.\n", changed); + log("Abstracted %d bits.\n", changed); } else { log_cmd_error("No mode selected, see help message\n"); } diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index 8536960ca..fb3b3143a 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -10,12 +10,10 @@ endmodule EOT proc -# dump # show -prefix before_base abstract -state -enablen magic check # show -prefix after_base -# dump design -reset read_verilog < Date: Fri, 7 Feb 2025 10:46:50 +0100 Subject: [PATCH 056/123] abstract: -value MVP, use buffer-normalized mode --- passes/cmds/abstract.cc | 107 +++++++++++++++++++------------------- tests/various/abstract.ys | 76 +++++++++++++++++++++++---- 2 files changed, 121 insertions(+), 62 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 58fa2c513..27213a394 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -57,70 +57,70 @@ bool abstract_state(Module* mod, Cell* cell, Wire* enable, bool enable_pol) { return true; } -bool abstract_value(Module* mod, Wire* wire, Wire* enable, bool enable_pol) { - // (void)mod->addMux(NEW_ID, - // mux_a, - // mux_b, - // enable, - // abstracted); - // cell->setPort(ID::D, SigSpec(abstracted)); - return false; -} - -struct AbstractInitCtx { +struct AbstractPortCtx { Module* mod; SigMap sigmap; - pool init_bits; + pool> outs; }; -void collect_init_bits_cells(AbstractInitCtx& ctx) { - // TODO Should this discriminate between FFs and other cells? - for (auto cell : ctx.mod->selected_cells()) { - // Add all sigbits on all cell outputs to init_bits - for (auto &conn : cell->connections()) { - if (cell->output(conn.first)) { - for (auto bit : conn.second) { - log_debug("init: cell %s output %s\n", cell->name.c_str(), log_signal(bit)); - ctx.init_bits.insert(ctx.sigmap(bit)); - } - } +void collect_selected_ports(AbstractPortCtx& ctx) { + for (Cell* cell : ctx.mod->cells()) { + for (auto& conn : cell->connections()) { + // we bufnorm + log_assert(conn.second.is_wire() || conn.second.is_fully_const()); + if (conn.second.is_wire() && cell->output(conn.first)) + if (ctx.mod->selected(cell) || ctx.mod->selected(conn.second.as_wire())) + ctx.outs.insert(std::make_pair(cell, conn.first)); } } } -void collect_init_bits_wires(AbstractInitCtx& ctx) { - for (auto wire : ctx.mod->selected_wires()) { - auto canonical = ctx.sigmap(wire); - // Find canonical drivers of all the wire bits and add them to init_bits - for (auto bit : canonical.bits()) { - log_debug("init: wire %s bit %s\n", wire->name.c_str(), log_signal(bit)); - ctx.init_bits.insert(ctx.sigmap(bit)); +unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { + AbstractPortCtx ctx {mod, SigMap(mod), {}}; + collect_selected_ports(ctx); + unsigned int changed = 0; + for (auto [cell, port] : ctx.outs) { + SigSpec sig = cell->getPort(port); + log_assert(sig.is_wire()); + Wire* original = mod->addWire(NEW_ID, sig.size()); + cell->setPort(port, original); + auto anyseq = mod->Anyseq(NEW_ID, sig.size()); + // This code differs from abstract_state + // in that we reuse the original signal as the mux output, + // not input + SigSpec mux_a, mux_b; + if (enable_pol) { + mux_a = original; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = original; } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable, + sig); + changed++; } + return changed; } unsigned int abstract_init(Module* mod) { - AbstractInitCtx ctx {mod, SigMap(mod), pool()}; - pool init_bits; - collect_init_bits_cells(ctx); - collect_init_bits_wires(ctx); + AbstractPortCtx ctx {mod, SigMap(mod), {}}; + collect_selected_ports(ctx); + unsigned int changed = 0; - for (SigBit bit : ctx.init_bits) { -next_sigbit: - if (!bit.is_wire() || !bit.wire->has_attribute(ID::init)) + for (auto [cell, port] : ctx.outs) { + SigSpec sig = cell->getPort(port); + log_assert(sig.is_wire()); + if (!sig.as_wire()->has_attribute(ID::init)) continue; - Const init = bit.wire->attributes.at(ID::init); - std::vector& bits = init.bits(); - bits[bit.offset] = RTLIL::State::Sx; - changed++; - - for (auto bit : bits) - if (bit != RTLIL::State::Sx) - goto next_sigbit; - // All bits are Sx, erase init attribute entirely - bit.wire->attributes.erase(ID::init); + Const init = sig.as_wire()->attributes.at(ID::init); + sig.as_wire()->attributes.erase(ID::init); + changed += sig.size(); } return changed; } @@ -174,6 +174,7 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); + design->bufNormalize(true); unsigned int changed = 0; if ((mode == State) || (mode == Value)) { if (!enable_name.length()) @@ -191,14 +192,13 @@ struct AbstractPass : public Pass { if (ct.cell_types.count(cell->type)) changed += abstract_state(mod, cell, enable_wire, enable_pol); } else { - // Value - for (auto wire : mod->selected_wires()) { - changed += abstract_value(mod, wire, enable_wire, enable_pol); - } - log_cmd_error("Unsupported (TODO)\n"); + changed += abstract_value(mod, enable_wire, enable_pol); } } - log("Abstracted %d cells.\n", changed); + if (mode == State) + log("Abstracted %d cells.\n", changed); + else + log("Abstracted %d values.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { changed += abstract_init(mod); @@ -207,6 +207,7 @@ struct AbstractPass : public Pass { } else { log_cmd_error("No mode selected, see help message\n"); } + design->bufNormalize(false); } } AbstractPass; diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index fb3b3143a..b122c7dc6 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -1,9 +1,9 @@ read_verilog < Date: Fri, 7 Feb 2025 13:38:50 +0100 Subject: [PATCH 057/123] abstract: -state allow partial abstraction, don't use buffer-normalized mode --- passes/cmds/abstract.cc | 136 ++++++++++++++++++++++++-------------- tests/various/abstract.ys | 21 ++++++ 2 files changed, 108 insertions(+), 49 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 27213a394..706636d59 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -5,58 +5,96 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool abstract_state(Module* mod, Cell* cell, Wire* enable, bool enable_pol) { - FfData ff(nullptr, cell); - if (ff.has_sr) - log_cmd_error("SR not supported\n"); - - // Normalize to simpler FF - ff.unmap_ce(); - ff.unmap_srst(); - if (ff.has_arst) - ff.arst_to_aload(); +struct EnableLogic { + Wire* wire; + bool pol; +}; +bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, EnableLogic enable) { // Construct abstract value - auto anyseq = mod->Anyseq(NEW_ID, ff.width); - - if (ff.has_aload) { - // ad := enable ? anyseq : ad - Wire* abstracted_ad = mod->addWire(NEW_ID, ff.sig_ad.size()); - SigSpec mux_a, mux_b; - if (enable_pol) { - mux_a = ff.sig_ad; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = ff.sig_ad; + auto anyseq = ff.module->Anyseq(NEW_ID, offsets.size()); + Wire* abstracted = ff.module->addWire(NEW_ID, offsets.size()); + SigSpec mux_input; + int abstracted_idx = 0; + for (int d_idx = 0; d_idx < ff.width; d_idx++) { + if (offsets.count(d_idx)) { + log_debug("bit %d: abstracted\n", d_idx); + mux_input.append(port_sig[d_idx]); + port_sig[d_idx].wire = abstracted; + port_sig[d_idx].offset = abstracted_idx; + log_assert(abstracted_idx < abstracted->width); + abstracted_idx++; } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable, - abstracted_ad); - ff.sig_ad = abstracted_ad; } - // d := enable ? anyseq : d - Wire* abstracted_d = mod->addWire(NEW_ID, ff.sig_d.size()); SigSpec mux_a, mux_b; - if (enable_pol) { - mux_a = ff.sig_d; + if (enable.pol) { + mux_a = mux_input; mux_b = anyseq; } else { mux_a = anyseq; - mux_b = ff.sig_d; + mux_b = mux_input; } - (void)mod->addMux(NEW_ID, + (void)ff.module->addMux(NEW_ID, mux_a, mux_b, - enable, - abstracted_d); - ff.sig_d = abstracted_d; + enable.wire, + abstracted); (void)ff.emit(); return true; } +unsigned int abstract_state(Module* mod, EnableLogic enable) { + CellTypes ct; + ct.setup_internals_ff(); + SigMap sigmap(mod); + pool selected_representatives; + + // Collect reps for all wire bits of selected wires + for (auto wire : mod->selected_wires()) + for (auto bit : sigmap(wire)) + selected_representatives.insert(bit); + + // Collect reps for all output wire bits of selected cells + for (auto cell : mod->selected_cells()) + for (auto conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : conn.second.bits()) + selected_representatives.insert(sigmap(bit)); + + unsigned int changed = 0; + std::vector ffs; + // Abstract flop inputs if they're driving a selected output rep + for (auto cell : mod->cells()) { + if (!ct.cell_types.count(cell->type)) + continue; + FfData ff(nullptr, cell); + if (ff.has_sr) + log_cmd_error("SR not supported\n"); + ffs.push_back(ff); + } + for (auto ff : ffs) { + // A bit inefficient + std::set offsets_to_abstract; + for (auto bit : ff.sig_q) + if (selected_representatives.count(sigmap(bit))) + offsets_to_abstract.insert(bit.offset); + + if (offsets_to_abstract.empty()) + continue; + + // Normalize to simpler FF + ff.unmap_ce(); + ff.unmap_srst(); + if (ff.has_arst) + ff.arst_to_aload(); + + if (ff.has_aload) + changed += abstract_state_port(ff, ff.sig_ad, offsets_to_abstract, enable); + changed += abstract_state_port(ff, ff.sig_d, offsets_to_abstract, enable); + } + return changed; +} + struct AbstractPortCtx { Module* mod; SigMap sigmap; @@ -75,7 +113,7 @@ void collect_selected_ports(AbstractPortCtx& ctx) { } } -unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { +unsigned int abstract_value(Module* mod, EnableLogic enable) { AbstractPortCtx ctx {mod, SigMap(mod), {}}; collect_selected_ports(ctx); unsigned int changed = 0; @@ -89,7 +127,7 @@ unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { // in that we reuse the original signal as the mux output, // not input SigSpec mux_a, mux_b; - if (enable_pol) { + if (enable.pol) { mux_a = original; mux_b = anyseq; } else { @@ -99,7 +137,7 @@ unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { (void)mod->addMux(NEW_ID, mux_a, mux_b, - enable, + enable.wire, sig); changed++; } @@ -174,25 +212,24 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); - design->bufNormalize(true); + if (mode != State) + design->bufNormalize(true); + unsigned int changed = 0; if ((mode == State) || (mode == Value)) { if (!enable_name.length()) log_cmd_error("Unspecified enable wire\n"); - CellTypes ct; - if (mode == State) - ct.setup_internals_ff(); for (auto mod : design->selected_modules()) { log_debug("module %s\n", mod->name.c_str()); Wire *enable_wire = mod->wire("\\" + enable_name); if (!enable_wire) log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); if (mode == State) { - for (auto cell : mod->selected_cells()) - if (ct.cell_types.count(cell->type)) - changed += abstract_state(mod, cell, enable_wire, enable_pol); + // for (auto cell : mod->selected_cells()) + // if (ct.cell_types.count(cell->type)) + changed += abstract_state(mod, {enable_wire, enable_pol}); } else { - changed += abstract_value(mod, enable_wire, enable_pol); + changed += abstract_value(mod, {enable_wire, enable_pol}); } } if (mode == State) @@ -207,7 +244,8 @@ struct AbstractPass : public Pass { } else { log_cmd_error("No mode selected, see help message\n"); } - design->bufNormalize(false); + if (mode != State) + design->bufNormalize(false); } } AbstractPass; diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index b122c7dc6..0892ca1c4 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -15,6 +15,27 @@ abstract -state -enablen magic check -assert # show -prefix after_base +design -reset +read_verilog < Date: Mon, 10 Feb 2025 12:01:26 +0100 Subject: [PATCH 058/123] abstract: -state refactor sigbit rep pool collection --- passes/cmds/abstract.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 706636d59..743ff2dd8 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -43,10 +43,7 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E return true; } -unsigned int abstract_state(Module* mod, EnableLogic enable) { - CellTypes ct; - ct.setup_internals_ff(); - SigMap sigmap(mod); +pool gather_selected_reps(Module* mod, SigMap& sigmap) { pool selected_representatives; // Collect reps for all wire bits of selected wires @@ -60,6 +57,14 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { if (cell->output(conn.first)) for (auto bit : conn.second.bits()) selected_representatives.insert(sigmap(bit)); + return selected_representatives; +} + +unsigned int abstract_state(Module* mod, EnableLogic enable) { + CellTypes ct; + ct.setup_internals_ff(); + SigMap sigmap(mod); + pool selected_representatives = gather_selected_reps(mod, sigmap); unsigned int changed = 0; std::vector ffs; From e9bba13a0d6537e153ed8f8d50a53ab68be0e8d2 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 13:06:40 +0100 Subject: [PATCH 059/123] abstract: no more bufnorm, -value has bit selection consistent with -state, -init temporarily gutted --- passes/cmds/abstract.cc | 119 ++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 743ff2dd8..84ec0ce14 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -100,71 +100,78 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { return changed; } -struct AbstractPortCtx { - Module* mod; - SigMap sigmap; - pool> outs; -}; - -void collect_selected_ports(AbstractPortCtx& ctx) { - for (Cell* cell : ctx.mod->cells()) { - for (auto& conn : cell->connections()) { - // we bufnorm - log_assert(conn.second.is_wire() || conn.second.is_fully_const()); - if (conn.second.is_wire() && cell->output(conn.first)) - if (ctx.mod->selected(cell) || ctx.mod->selected(conn.second.as_wire())) - ctx.outs.insert(std::make_pair(cell, conn.first)); - } - } -} - unsigned int abstract_value(Module* mod, EnableLogic enable) { - AbstractPortCtx ctx {mod, SigMap(mod), {}}; - collect_selected_ports(ctx); + SigMap sigmap(mod); + pool selected_representatives = gather_selected_reps(mod, sigmap); unsigned int changed = 0; - for (auto [cell, port] : ctx.outs) { - SigSpec sig = cell->getPort(port); - log_assert(sig.is_wire()); - Wire* original = mod->addWire(NEW_ID, sig.size()); - cell->setPort(port, original); - auto anyseq = mod->Anyseq(NEW_ID, sig.size()); - // This code differs from abstract_state - // in that we reuse the original signal as the mux output, - // not input - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = original; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = original; - } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - sig); - changed++; + std::vector cells_snapshot = mod->cells(); + for (auto cell : cells_snapshot) { + for (auto conn : cell->connections()) + if (cell->output(conn.first)) { + std::set offsets_to_abstract; + for (int i = 0; i < conn.second.size(); i++) { + if (selected_representatives.count(sigmap(conn.second[i]))) { + offsets_to_abstract.insert(i); + } + } + if (offsets_to_abstract.empty()) + continue; + + auto anyseq = mod->Anyseq(NEW_ID, offsets_to_abstract.size()); + Wire* to_abstract = mod->addWire(NEW_ID, offsets_to_abstract.size()); + SigSpec mux_input; + SigSpec mux_output; + SigSpec new_port = conn.second; + int to_abstract_idx = 0; + for (int port_idx = 0; port_idx < conn.second.size(); port_idx++) { + if (offsets_to_abstract.count(port_idx)) { + log_debug("bit %d: abstracted\n", port_idx); + mux_output.append(conn.second[port_idx]); + SigBit in_bit {to_abstract, to_abstract_idx}; + new_port.replace(port_idx, in_bit); + conn.second[port_idx] = {mod->addWire(NEW_ID, 1), 0}; + mux_input.append(in_bit); + log_assert(to_abstract_idx < to_abstract->width); + to_abstract_idx++; + } + } + cell->setPort(conn.first, new_port); + SigSpec mux_a, mux_b; + if (enable.pol) { + mux_a = mux_input; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = mux_input; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable.wire, + mux_output); + changed++; + } } return changed; } unsigned int abstract_init(Module* mod) { - AbstractPortCtx ctx {mod, SigMap(mod), {}}; - collect_selected_ports(ctx); + // AbstractPortCtx ctx {mod, SigMap(mod), {}}; + // collect_selected_ports(ctx); unsigned int changed = 0; - for (auto [cell, port] : ctx.outs) { - SigSpec sig = cell->getPort(port); - log_assert(sig.is_wire()); - if (!sig.as_wire()->has_attribute(ID::init)) - continue; + // for (auto [cell, port] : ctx.outs) { + // SigSpec sig = cell->getPort(port); + // log_assert(sig.is_wire()); + // if (!sig.as_wire()->has_attribute(ID::init)) + // continue; - Const init = sig.as_wire()->attributes.at(ID::init); - sig.as_wire()->attributes.erase(ID::init); - changed += sig.size(); - } + // Const init = sig.as_wire()->attributes.at(ID::init); + // sig.as_wire()->attributes.erase(ID::init); + // changed += sig.size(); + // } + log_cmd_error("Not implemented\n"); (void)mod; return changed; } @@ -217,8 +224,6 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); - if (mode != State) - design->bufNormalize(true); unsigned int changed = 0; if ((mode == State) || (mode == Value)) { @@ -249,8 +254,6 @@ struct AbstractPass : public Pass { } else { log_cmd_error("No mode selected, see help message\n"); } - if (mode != State) - design->bufNormalize(false); } } AbstractPass; From 164699109254f04f7b5042618e8808878f7fec72 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 13:10:53 +0100 Subject: [PATCH 060/123] abstract: refactor -value --- passes/cmds/abstract.cc | 70 ++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 84ec0ce14..23817e5c1 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -100,6 +100,42 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { return changed; } +bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdString port_name, EnableLogic enable) { + auto anyseq = mod->Anyseq(NEW_ID, offsets.size()); + Wire* to_abstract = mod->addWire(NEW_ID, offsets.size()); + SigSpec mux_input; + SigSpec mux_output; + const SigSpec& old_port = cell->getPort(port_name); + SigSpec new_port = old_port; + int to_abstract_idx = 0; + for (int port_idx = 0; port_idx < old_port.size(); port_idx++) { + if (offsets.count(port_idx)) { + log_debug("bit %d: abstracted\n", port_idx); + mux_output.append(old_port[port_idx]); + SigBit in_bit {to_abstract, to_abstract_idx}; + new_port.replace(port_idx, in_bit); + mux_input.append(in_bit); + log_assert(to_abstract_idx < to_abstract->width); + to_abstract_idx++; + } + } + cell->setPort(port_name, new_port); + SigSpec mux_a, mux_b; + if (enable.pol) { + mux_a = mux_input; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = mux_input; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable.wire, + mux_output); + return true; +} + unsigned int abstract_value(Module* mod, EnableLogic enable) { SigMap sigmap(mod); pool selected_representatives = gather_selected_reps(mod, sigmap); @@ -117,39 +153,7 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { if (offsets_to_abstract.empty()) continue; - auto anyseq = mod->Anyseq(NEW_ID, offsets_to_abstract.size()); - Wire* to_abstract = mod->addWire(NEW_ID, offsets_to_abstract.size()); - SigSpec mux_input; - SigSpec mux_output; - SigSpec new_port = conn.second; - int to_abstract_idx = 0; - for (int port_idx = 0; port_idx < conn.second.size(); port_idx++) { - if (offsets_to_abstract.count(port_idx)) { - log_debug("bit %d: abstracted\n", port_idx); - mux_output.append(conn.second[port_idx]); - SigBit in_bit {to_abstract, to_abstract_idx}; - new_port.replace(port_idx, in_bit); - conn.second[port_idx] = {mod->addWire(NEW_ID, 1), 0}; - mux_input.append(in_bit); - log_assert(to_abstract_idx < to_abstract->width); - to_abstract_idx++; - } - } - cell->setPort(conn.first, new_port); - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = mux_input; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = mux_input; - } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - mux_output); - changed++; + changed += abstract_value_port(mod, cell, offsets_to_abstract, conn.first, enable); } } return changed; From 9895370b329898900bab8e1fa9a02f81fc7f1c9e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 14:56:52 +0100 Subject: [PATCH 061/123] abstract: rework -init without bufnorm, with logging --- passes/cmds/abstract.cc | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 23817e5c1..48ec09f8c 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -1,6 +1,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/ff.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -160,22 +161,24 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { } unsigned int abstract_init(Module* mod) { - // AbstractPortCtx ctx {mod, SigMap(mod), {}}; - // collect_selected_ports(ctx); - unsigned int changed = 0; + FfInitVals initvals; + SigMap sigmap(mod); + initvals.set(&sigmap, mod); + for (auto wire : mod->selected_wires()) + for (auto bit : SigSpec(wire)) { + // TODO these don't seem too informative + log_debug("Removing init bit on %s due to selected wire %s\n", log_signal(bit), wire->name.c_str()); + initvals.remove_init(bit); + } - // for (auto [cell, port] : ctx.outs) { - // SigSpec sig = cell->getPort(port); - // log_assert(sig.is_wire()); - // if (!sig.as_wire()->has_attribute(ID::init)) - // continue; - - // Const init = sig.as_wire()->attributes.at(ID::init); - // sig.as_wire()->attributes.erase(ID::init); - // changed += sig.size(); - // } - log_cmd_error("Not implemented\n"); (void)mod; + for (auto cell : mod->selected_cells()) + for (auto conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : conn.second.bits()) { + log_debug("Removing init bit on %s due to selected cell %s\n", log_signal(bit), cell->name.c_str()); + initvals.remove_init(bit); + } return changed; } From cee06cecd0991bf95ffbff0a9fe769c30d973f03 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 14:59:50 +0100 Subject: [PATCH 062/123] abstract: factor out emit_mux_anyseq --- passes/cmds/abstract.cc | 48 ++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 48ec09f8c..d770a8575 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -11,9 +11,24 @@ struct EnableLogic { bool pol; }; +void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) { + auto anyseq = mod->Anyseq(NEW_ID, mux_input.size()); + SigSpec mux_a, mux_b; + if (enable.pol) { + mux_a = mux_input; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = mux_input; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable.wire, + mux_output); +} + bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, EnableLogic enable) { - // Construct abstract value - auto anyseq = ff.module->Anyseq(NEW_ID, offsets.size()); Wire* abstracted = ff.module->addWire(NEW_ID, offsets.size()); SigSpec mux_input; int abstracted_idx = 0; @@ -27,19 +42,7 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E abstracted_idx++; } } - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = mux_input; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = mux_input; - } - (void)ff.module->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - abstracted); + emit_mux_anyseq(ff.module, mux_input, abstracted, enable); (void)ff.emit(); return true; } @@ -102,7 +105,6 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { } bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdString port_name, EnableLogic enable) { - auto anyseq = mod->Anyseq(NEW_ID, offsets.size()); Wire* to_abstract = mod->addWire(NEW_ID, offsets.size()); SigSpec mux_input; SigSpec mux_output; @@ -121,19 +123,7 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin } } cell->setPort(port_name, new_port); - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = mux_input; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = mux_input; - } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - mux_output); + emit_mux_anyseq(mod, mux_input, mux_output, enable); return true; } From aca4d44a40ec8863978d02f77378817923629f58 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:24:42 +0100 Subject: [PATCH 063/123] abstract: improve debug logs for -state and -value --- passes/cmds/abstract.cc | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d770a8575..55f0f4bcd 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -2,6 +2,7 @@ #include "kernel/celltypes.h" #include "kernel/ff.h" #include "kernel/ffinit.h" +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -47,28 +48,40 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E return true; } -pool gather_selected_reps(Module* mod, SigMap& sigmap) { - pool selected_representatives; +using SelReason=std::variant; +dict> gather_selected_reps(Module* mod, SigMap& sigmap) { + dict> selected_reps; // Collect reps for all wire bits of selected wires for (auto wire : mod->selected_wires()) for (auto bit : sigmap(wire)) - selected_representatives.insert(bit); + selected_reps.insert(bit).first->second.push_back(wire); // Collect reps for all output wire bits of selected cells for (auto cell : mod->selected_cells()) for (auto conn : cell->connections()) if (cell->output(conn.first)) for (auto bit : conn.second.bits()) - selected_representatives.insert(sigmap(bit)); - return selected_representatives; + selected_reps.insert(sigmap(bit)).first->second.push_back(cell); + return selected_reps; +} + +void explain_selections(const std::vector& reasons) { + for (std::variant reason : reasons) { + if (Cell** cell_reason = std::get_if(&reason)) + log_debug("\tcell %s\n", (*cell_reason)->name.c_str()); + else if (Wire** wire_reason = std::get_if(&reason)) + log_debug("\twire %s\n", (*wire_reason)->name.c_str()); + else + log_assert(false && "insane reason variant\n"); + } } unsigned int abstract_state(Module* mod, EnableLogic enable) { CellTypes ct; ct.setup_internals_ff(); SigMap sigmap(mod); - pool selected_representatives = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, sigmap); unsigned int changed = 0; std::vector ffs; @@ -85,8 +98,11 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { // A bit inefficient std::set offsets_to_abstract; for (auto bit : ff.sig_q) - if (selected_representatives.count(sigmap(bit))) + if (selected_reps.count(sigmap(bit))) { + log_debug("Abstracting state for bit %s due to selections:\n", log_signal(bit)); + explain_selections(selected_reps.at(sigmap(bit))); offsets_to_abstract.insert(bit.offset); + } if (offsets_to_abstract.empty()) continue; @@ -129,7 +145,7 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin unsigned int abstract_value(Module* mod, EnableLogic enable) { SigMap sigmap(mod); - pool selected_representatives = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, sigmap); unsigned int changed = 0; std::vector cells_snapshot = mod->cells(); for (auto cell : cells_snapshot) { @@ -137,7 +153,9 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { if (cell->output(conn.first)) { std::set offsets_to_abstract; for (int i = 0; i < conn.second.size(); i++) { - if (selected_representatives.count(sigmap(conn.second[i]))) { + if (selected_reps.count(sigmap(conn.second[i]))) { + log_debug("Abstracting value for bit %s due to selections:\n", log_signal(conn.second[i])); + explain_selections(selected_reps.at(sigmap(conn.second[i]))); offsets_to_abstract.insert(i); } } From 9de890c87417cbed4dd8a93d5fc6e5a6dcb3bdbc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:27:40 +0100 Subject: [PATCH 064/123] abstract: fix -init log_debug bit count, remove unnecessary log_debug --- passes/cmds/abstract.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 55f0f4bcd..ab3d215fa 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -35,7 +35,6 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E int abstracted_idx = 0; for (int d_idx = 0; d_idx < ff.width; d_idx++) { if (offsets.count(d_idx)) { - log_debug("bit %d: abstracted\n", d_idx); mux_input.append(port_sig[d_idx]); port_sig[d_idx].wire = abstracted; port_sig[d_idx].offset = abstracted_idx; @@ -49,6 +48,7 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E } using SelReason=std::variant; + dict> gather_selected_reps(Module* mod, SigMap& sigmap) { dict> selected_reps; @@ -129,7 +129,6 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin int to_abstract_idx = 0; for (int port_idx = 0; port_idx < old_port.size(); port_idx++) { if (offsets.count(port_idx)) { - log_debug("bit %d: abstracted\n", port_idx); mux_output.append(old_port[port_idx]); SigBit in_bit {to_abstract, to_abstract_idx}; new_port.replace(port_idx, in_bit); @@ -178,6 +177,7 @@ unsigned int abstract_init(Module* mod) { // TODO these don't seem too informative log_debug("Removing init bit on %s due to selected wire %s\n", log_signal(bit), wire->name.c_str()); initvals.remove_init(bit); + changed++; } for (auto cell : mod->selected_cells()) @@ -186,6 +186,7 @@ unsigned int abstract_init(Module* mod) { for (auto bit : conn.second.bits()) { log_debug("Removing init bit on %s due to selected cell %s\n", log_signal(bit), cell->name.c_str()); initvals.remove_init(bit); + changed++; } return changed; } @@ -245,7 +246,6 @@ struct AbstractPass : public Pass { if (!enable_name.length()) log_cmd_error("Unspecified enable wire\n"); for (auto mod : design->selected_modules()) { - log_debug("module %s\n", mod->name.c_str()); Wire *enable_wire = mod->wire("\\" + enable_name); if (!enable_wire) log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); From 3dd697fc8ad410d7f324daa3998ee8657ce7c318 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:33:20 +0100 Subject: [PATCH 065/123] abstract: improve -init logging --- passes/cmds/abstract.cc | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index ab3d215fa..3b0145d9a 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -171,23 +171,14 @@ unsigned int abstract_init(Module* mod) { unsigned int changed = 0; FfInitVals initvals; SigMap sigmap(mod); + dict> selected_reps = gather_selected_reps(mod, sigmap); initvals.set(&sigmap, mod); - for (auto wire : mod->selected_wires()) - for (auto bit : SigSpec(wire)) { - // TODO these don't seem too informative - log_debug("Removing init bit on %s due to selected wire %s\n", log_signal(bit), wire->name.c_str()); - initvals.remove_init(bit); - changed++; - } - - for (auto cell : mod->selected_cells()) - for (auto conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : conn.second.bits()) { - log_debug("Removing init bit on %s due to selected cell %s\n", log_signal(bit), cell->name.c_str()); - initvals.remove_init(bit); - changed++; - } + for (auto bit : selected_reps) { + log_debug("Removing init bit on %s due to selections:\n", log_signal(bit.first)); + explain_selections(bit.second); + initvals.remove_init(bit.first); + changed++; + } return changed; } From 28c768e7b8943f37b59fe7b86281fe9a361ddf63 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:36:41 +0100 Subject: [PATCH 066/123] abstract: better present changes done --- passes/cmds/abstract.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 3b0145d9a..d0e1603ce 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -240,23 +240,20 @@ struct AbstractPass : public Pass { Wire *enable_wire = mod->wire("\\" + enable_name); if (!enable_wire) log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); - if (mode == State) { - // for (auto cell : mod->selected_cells()) - // if (ct.cell_types.count(cell->type)) + if (mode == State) changed += abstract_state(mod, {enable_wire, enable_pol}); - } else { + else changed += abstract_value(mod, {enable_wire, enable_pol}); - } } if (mode == State) - log("Abstracted %d cells.\n", changed); + log("Abstracted %d stateful cells.\n", changed); else - log("Abstracted %d values.\n", changed); + log("Abstracted %d driver ports.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { changed += abstract_init(mod); } - log("Abstracted %d bits.\n", changed); + log("Abstracted %d init bits.\n", changed); } else { log_cmd_error("No mode selected, see help message\n"); } From 7cd822b7f5ef3fb1d41cf880aa3e4f0549506bb1 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:34:51 +0100 Subject: [PATCH 067/123] rtlil: Add {from,to}_hdl_index methods to Wire In the past we had the occasional bug due to some place not handling all 4 combinations of upto/downto and zero/nonzero start_offset correctly. --- kernel/rtlil.h | 13 +++++ tests/unit/kernel/rtlilTest.cc | 94 +++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f9cacd151..54735bacb 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1673,6 +1673,19 @@ public: RTLIL::Cell *driverCell() const { log_assert(driverCell_); return driverCell_; }; RTLIL::IdString driverPort() const { log_assert(driverCell_); return driverPort_; }; + int from_hdl_index(int hdl_index) { + int zero_index = hdl_index - start_offset; + int rtlil_index = upto ? width - 1 - zero_index : zero_index; + return rtlil_index >= 0 && rtlil_index < width ? rtlil_index : INT_MIN; + } + + int to_hdl_index(int rtlil_index) { + if (rtlil_index < 0 || rtlil_index >= width) + return INT_MIN; + int zero_index = upto ? width - 1 - rtlil_index : rtlil_index; + return zero_index + start_offset; + } + #ifdef WITH_PYTHON static std::map *get_all_wires(void); #endif diff --git a/tests/unit/kernel/rtlilTest.cc b/tests/unit/kernel/rtlilTest.cc index 1d5ef3a83..cb773202d 100644 --- a/tests/unit/kernel/rtlilTest.cc +++ b/tests/unit/kernel/rtlilTest.cc @@ -5,7 +5,22 @@ YOSYS_NAMESPACE_BEGIN namespace RTLIL { - class KernelRtlilTest : public testing::Test {}; + struct SafePrintToStringParamName { + template + std::string operator()(const testing::TestParamInfo& info) const { + std::string name = testing::PrintToString(info.param); + for (auto &c : name) + if (!('0' <= c && c <= '9') && !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') ) c = '_'; + return name; + } + }; + + class KernelRtlilTest : public testing::Test { + protected: + KernelRtlilTest() { + if (log_files.empty()) log_files.emplace_back(stdout); + } + }; TEST_F(KernelRtlilTest, ConstAssignCompare) { @@ -74,6 +89,83 @@ namespace RTLIL { } } + + class WireRtlVsHdlIndexConversionTest : + public KernelRtlilTest, + public testing::WithParamInterface> + {}; + + INSTANTIATE_TEST_SUITE_P( + WireRtlVsHdlIndexConversionInstance, + WireRtlVsHdlIndexConversionTest, + testing::Values( + std::make_tuple(false, 0, 10), + std::make_tuple(true, 0, 10), + std::make_tuple(false, 4, 10), + std::make_tuple(true, 4, 10), + std::make_tuple(false, -4, 10), + std::make_tuple(true, -4, 10), + std::make_tuple(false, 0, 1), + std::make_tuple(true, 0, 1), + std::make_tuple(false, 4, 1), + std::make_tuple(true, 4, 1), + std::make_tuple(false, -4, 1), + std::make_tuple(true, -4, 1) + ), + SafePrintToStringParamName() + ); + + TEST_P(WireRtlVsHdlIndexConversionTest, WireRtlVsHdlIndexConversion) { + std::unique_ptr mod = std::make_unique(); + Wire *wire = mod->addWire(ID(test), 10); + + auto [upto, start_offset, width] = GetParam(); + + wire->upto = upto; + wire->start_offset = start_offset; + wire->width = width; + + int smallest = INT_MAX; + int largest = INT_MIN; + + for (int i = 0; i < wire->width; i++) { + int j = wire->to_hdl_index(i); + smallest = std::min(smallest, j); + largest = std::max(largest, j); + EXPECT_EQ( + std::make_pair(wire->from_hdl_index(j), j), + std::make_pair(i, wire->to_hdl_index(i)) + ); + } + + EXPECT_EQ(smallest, start_offset); + EXPECT_EQ(largest, start_offset + wire->width - 1); + + for (int i = 1; i < wire->width; i++) { + EXPECT_EQ( + wire->to_hdl_index(i) - wire->to_hdl_index(i - 1), + upto ? -1 : 1 + ); + } + + for (int j = smallest; j < largest; j++) { + int i = wire->from_hdl_index(j); + EXPECT_EQ( + std::make_pair(wire->from_hdl_index(j), j), + std::make_pair(i, wire->to_hdl_index(i)) + ); + } + + for (int i = -10; i < 0; i++) + EXPECT_EQ(wire->to_hdl_index(i), INT_MIN); + for (int i = wire->width; i < wire->width + 10; i++) + EXPECT_EQ(wire->to_hdl_index(i), INT_MIN); + for (int j = smallest - 10; j < smallest; j++) + EXPECT_EQ(wire->from_hdl_index(j), INT_MIN); + for (int j = largest + 1; j < largest + 11; j++) + EXPECT_EQ(wire->from_hdl_index(j), INT_MIN); + } + } YOSYS_NAMESPACE_END From 37aa2e6cd829b26acbc61a8ad7569fcc315b6251 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:38:46 +0100 Subject: [PATCH 068/123] abstract: Wire vs port offset confusion bugfix This fixes the offsets_to_abstract collection in abstract_state so that it now works the same way as in abstract_value which was already correct. --- passes/cmds/abstract.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d0e1603ce..d07f19081 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -97,12 +97,14 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { for (auto ff : ffs) { // A bit inefficient std::set offsets_to_abstract; - for (auto bit : ff.sig_q) + for (int i = 0; i < GetSize(ff.sig_q); i++) { + SigBit bit = ff.sig_q[i]; if (selected_reps.count(sigmap(bit))) { log_debug("Abstracting state for bit %s due to selections:\n", log_signal(bit)); explain_selections(selected_reps.at(sigmap(bit))); - offsets_to_abstract.insert(bit.offset); + offsets_to_abstract.insert(i); } + } if (offsets_to_abstract.empty()) continue; From 4766c92e5976d629819d4d3a03100ef43855fa58 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:45:47 +0100 Subject: [PATCH 069/123] abstract: Allow unconditional value and state abstractions Also improves -enable and -enablen command line handling --- passes/cmds/abstract.cc | 54 +++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d07f19081..da8c96af8 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -8,12 +8,15 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct EnableLogic { - Wire* wire; + SigBit bit; bool pol; }; void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) { auto anyseq = mod->Anyseq(NEW_ID, mux_input.size()); + if (enable.bit == (enable.pol ? State::S1 : State::S0)) { + mod->connect(mux_output, anyseq); + } SigSpec mux_a, mux_b; if (enable.pol) { mux_a = mux_input; @@ -25,7 +28,7 @@ void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_o (void)mod->addMux(NEW_ID, mux_a, mux_b, - enable.wire, + enable.bit, mux_output); } @@ -201,9 +204,14 @@ struct AbstractPass : public Pass { Initial, Value, }; - Mode mode; + Mode mode = Mode::None; + enum Enable { + Always = -1, + ActiveLow = false, // ensuring we can use bool(enable) + ActiveHigh = true, + }; + Enable enable = Enable::Always; std::string enable_name; - bool enable_pol; // true is high for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -219,33 +227,49 @@ struct AbstractPass : public Pass { mode = Value; continue; } - if (arg == "-enable") { + if (arg == "-enable" && argidx + 1 < args.size()) { + if (enable != Enable::Always) + log_cmd_error("Multiple enable condition are not supported\n"); enable_name = args[++argidx]; - enable_pol = true; + enable = Enable::ActiveHigh; continue; } - if (arg == "-enablen") { + if (arg == "-enablen" && argidx + 1 < args.size()) { + if (enable != Enable::Always) + log_cmd_error("Multiple enable condition are not supported\n"); enable_name = args[++argidx]; - enable_pol = false; + enable = Enable::ActiveLow; continue; } break; } extra_args(args, argidx, design); + if (enable != Enable::Always) { + if (mode == Mode::Initial) + log_cmd_error("Conditional initial value abstraction is not supported\n"); + + if (enable_name.empty()) + log_cmd_error("Unspecified enable wire\n"); + } unsigned int changed = 0; if ((mode == State) || (mode == Value)) { - if (!enable_name.length()) - log_cmd_error("Unspecified enable wire\n"); for (auto mod : design->selected_modules()) { - Wire *enable_wire = mod->wire("\\" + enable_name); - if (!enable_wire) - log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + EnableLogic enable_logic = { State::S1, true }; + if (enable != Enable::Always) { + Wire *enable_wire = mod->wire("\\" + enable_name); + if (!enable_wire) + log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + if (GetSize(enable_wire) != 1) + log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n", + enable_name.c_str(), GetSize(enable_wire), mod->name.c_str()); + enable_logic = { enable_wire, enable == Enable::ActiveHigh }; + } if (mode == State) - changed += abstract_state(mod, {enable_wire, enable_pol}); + changed += abstract_state(mod, enable_logic); else - changed += abstract_value(mod, {enable_wire, enable_pol}); + changed += abstract_value(mod, enable_logic); } if (mode == State) log("Abstracted %d stateful cells.\n", changed); From a0987195f2de7081f8bb223e9d7c541f52d44b08 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:50:34 +0100 Subject: [PATCH 070/123] abstract: Support slicing of individual wires --- passes/cmds/abstract.cc | 133 ++++++++++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 20 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index da8c96af8..43940a6ea 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -3,6 +3,7 @@ #include "kernel/ff.h" #include "kernel/ffinit.h" #include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -12,6 +13,74 @@ struct EnableLogic { bool pol; }; +enum SliceIndices { + RtlilSlice, + HdlSlice, +}; + +struct Slice { + SliceIndices indices; + int first; + int last; + + Slice(SliceIndices indices, const std::string &slice) : + indices(indices) + { + if (slice.empty()) + syntax_error(slice); + auto sep = slice.find(':'); + const char *first_begin, *first_end, *last_begin, *last_end; + if (sep == std::string::npos) { + first_begin = last_begin = slice.c_str(); + first_end = last_end = slice.c_str() + slice.length(); + } else { + first_begin = slice.c_str(); + first_end = first_begin + sep; + + last_begin = first_end + 1; + last_end = slice.c_str() + slice.length(); + } + first = parse_index(first_begin, first_end, slice); + last = parse_index(last_begin, last_end, slice); + } + + static int parse_index(const char *begin, const char *end, const std::string &slice) { + int value; + auto result = std::from_chars(begin, end, value, 10); + if (result.ptr != end || result.ptr == begin) + syntax_error(slice); + return value; + } + + static void syntax_error(const std::string &slice) { + log_cmd_error("Invalid slice '%s', expected ':' or ''", slice.c_str()); + } + + std::string to_string() const { + const char *option = indices == RtlilSlice ? "-rawslice" : "-slice"; + if (first == last) + return stringf("%s %d", option, first); + else + return stringf("%s %d:%d", option, first, last); + } + + int wire_offset(RTLIL::Wire *wire, int index) const { + int rtl_offset = indices == RtlilSlice ? index : wire->from_hdl_index(index); + if (rtl_offset < 0 || rtl_offset >= wire->width) { + log_error("Slice %s is out of bounds for wire %s in module %s", to_string().c_str(), log_id(wire), log_id(wire->module)); + } + return rtl_offset; + } + + std::pair wire_range(RTLIL::Wire *wire) const { + int rtl_first = wire_offset(wire, first); + int rtl_last = wire_offset(wire, last); + if (rtl_first > rtl_last) + std::swap(rtl_first, rtl_last); + return {rtl_first, rtl_last + 1}; + } +}; + void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) { auto anyseq = mod->Anyseq(NEW_ID, mux_input.size()); if (enable.bit == (enable.pol ? State::S1 : State::S0)) { @@ -52,20 +121,35 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E using SelReason=std::variant; -dict> gather_selected_reps(Module* mod, SigMap& sigmap) { +dict> gather_selected_reps(Module* mod, const std::vector &slices, SigMap& sigmap) { dict> selected_reps; - // Collect reps for all wire bits of selected wires - for (auto wire : mod->selected_wires()) - for (auto bit : sigmap(wire)) - selected_reps.insert(bit).first->second.push_back(wire); + if (slices.empty()) { + // Collect reps for all wire bits of selected wires + for (auto wire : mod->selected_wires()) + for (auto bit : sigmap(wire)) + selected_reps.insert(bit).first->second.push_back(wire); - // Collect reps for all output wire bits of selected cells - for (auto cell : mod->selected_cells()) - for (auto conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : conn.second.bits()) - selected_reps.insert(sigmap(bit)).first->second.push_back(cell); + // Collect reps for all output wire bits of selected cells + for (auto cell : mod->selected_cells()) + for (auto conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : conn.second.bits()) + selected_reps.insert(sigmap(bit)).first->second.push_back(cell); + } else { + if (mod->selected_wires().size() != 1 || !mod->selected_cells().empty()) + log_error("Slices are only supported for single-wire selections\n"); + + auto wire = mod->selected_wires()[0]; + + for (auto slice : slices) { + auto [begin, end] = slice.wire_range(wire); + for (int i = begin; i < end; i++) { + selected_reps.insert(sigmap(SigBit(wire, i))).first->second.push_back(wire); + } + } + + } return selected_reps; } @@ -80,11 +164,11 @@ void explain_selections(const std::vector& reasons) { } } -unsigned int abstract_state(Module* mod, EnableLogic enable) { +unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector &slices) { CellTypes ct; ct.setup_internals_ff(); SigMap sigmap(mod); - dict> selected_reps = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, slices, sigmap); unsigned int changed = 0; std::vector ffs; @@ -147,9 +231,9 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin return true; } -unsigned int abstract_value(Module* mod, EnableLogic enable) { +unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector &slices) { SigMap sigmap(mod); - dict> selected_reps = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, slices, sigmap); unsigned int changed = 0; std::vector cells_snapshot = mod->cells(); for (auto cell : cells_snapshot) { @@ -172,11 +256,11 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { return changed; } -unsigned int abstract_init(Module* mod) { +unsigned int abstract_init(Module* mod, const std::vector &slices) { unsigned int changed = 0; FfInitVals initvals; SigMap sigmap(mod); - dict> selected_reps = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, slices, sigmap); initvals.set(&sigmap, mod); for (auto bit : selected_reps) { log_debug("Removing init bit on %s due to selections:\n", log_signal(bit.first)); @@ -212,6 +296,7 @@ struct AbstractPass : public Pass { }; Enable enable = Enable::Always; std::string enable_name; + std::vector slices; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -241,6 +326,14 @@ struct AbstractPass : public Pass { enable = Enable::ActiveLow; continue; } + if (arg == "-slice" && argidx + 1 < args.size()) { + slices.emplace_back(SliceIndices::HdlSlice, args[++argidx]); + continue; + } + if (arg == "-rtlilslice" && argidx + 1 < args.size()) { + slices.emplace_back(SliceIndices::RtlilSlice, args[++argidx]); + continue; + } break; } extra_args(args, argidx, design); @@ -267,9 +360,9 @@ struct AbstractPass : public Pass { enable_logic = { enable_wire, enable == Enable::ActiveHigh }; } if (mode == State) - changed += abstract_state(mod, enable_logic); + changed += abstract_state(mod, enable_logic, slices); else - changed += abstract_value(mod, enable_logic); + changed += abstract_value(mod, enable_logic, slices); } if (mode == State) log("Abstracted %d stateful cells.\n", changed); @@ -277,7 +370,7 @@ struct AbstractPass : public Pass { log("Abstracted %d driver ports.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { - changed += abstract_init(mod); + changed += abstract_init(mod, slices); } log("Abstracted %d init bits.\n", changed); } else { From 2943c2142dd5badb57ae1d35179db98e5b25e412 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 16:27:59 +0100 Subject: [PATCH 071/123] abstract: Improve debug logging Print the port bit instead of the arbitrary representative sigbit to identify the target of the abstraction operation. --- passes/cmds/abstract.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 43940a6ea..5639758bb 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -187,7 +187,7 @@ unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector offsets_to_abstract; for (int i = 0; i < conn.second.size(); i++) { if (selected_reps.count(sigmap(conn.second[i]))) { - log_debug("Abstracting value for bit %s due to selections:\n", log_signal(conn.second[i])); + log_debug("Abstracting value for %s.%s[%i] in module %s due to selections:\n", + log_id(cell), log_id(conn.first), i, log_id(mod)); explain_selections(selected_reps.at(sigmap(conn.second[i]))); offsets_to_abstract.insert(i); } From 212224dfe8409c77299ddeec3e315915e8e7df57 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 17:17:06 +0100 Subject: [PATCH 072/123] abstract: Add help message --- passes/cmds/abstract.cc | 68 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 5639758bb..4a97a640c 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -276,11 +276,75 @@ unsigned int abstract_init(Module* mod, const std::vector &slices) { } struct AbstractPass : public Pass { - AbstractPass() : Pass("abstract", "extract clock gating out of flip flops") { } + AbstractPass() : Pass("abstract", "replace signals with abstract values during formal verification") { } void help() override { - // TODO // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); + log(" abstract [mode] [options] [selection]\n"); + log("\n"); + log("Perform abstraction of signals within the design. Abstraction replaces a signal\n"); + log("with an unconstrained abstract value that can take an arbitrary concrete value\n"); + log("during formal verification. The mode and options control when a signal should\n"); + log("be abstracted and how it should affect FFs present in the design.\n"); + log("\n"); + log("Modes:"); + log("\n"); + log(" -state\n"); + log(" The selected FFs will be modified to load a new abstract value on every\n"); + log(" active clock edge, async reset or async load. This is independent of any\n"); + log(" clock enable that may be present on the FF cell. Conditional abstraction\n"); + log(" is supported with the -enable/-enabeln options. If present, the condition\n"); + log(" is sampled at the same time as the FF would smaple the correspnding data\n"); + log(" or async-data input whose value will be replaced with an abstract value.\n"); + log("\n"); + log(" The selection can be used to specify which state bits to abstract. For\n"); + log(" each selected wire, any state bits that the wire is driven by will be\n"); + log(" abstracted. For a selected FF cell, all of its state is abstracted.\n"); + log(" Individual bits of a single wire can be abtracted using the -slice and\n"); + log(" -rtlilslice options.\n"); + log("\n"); + log(" -init\n"); + log(" The selected FFs will be modified to have an abstract initial value.\n"); + log(" The -enable/-enablen options are not supported in this mode.\n"); + log(" \n"); + log(" The selection is used in the same way as it is for the -state mode.\n"); + log("\n"); + log(" -value\n"); + log(" The drivers of the selected signals will be replaced with an abstract\n"); + log(" value. In this mode, the abstract value can change at any time and is\n"); + log(" not synchronized to any clock or other signal. Conditional abstraction\n"); + log(" is supported with the -enable/-enablen options. The condition will\n"); + log(" combinationally select between the original driver and the abstract\n"); + log(" value.\n"); + log("\n"); + log(" The selection can be used to specify which output bits of which drivers\n"); + log(" to abtract. For a selected cell, all its output bits will be abstracted.\n"); + log(" For a selected wire, every output bit that is driving the wire will be\n"); + log(" abstracted. Individual bits of a single wire can be abstracted using the\n"); + log(" -slice and -rtlilslice options.\n"); + log("\n"); + log(" -enable \n"); + log(" -enablen \n"); + log(" Perform conditional abstraction with a named single bit wire as\n"); + log(" condition. For -enable the wire is used as an active-high condition and\n"); + log(" for -enablen as an active-low condition. See the description of the\n"); + log(" -state and -value modes for details on how the condition affects the\n"); + log(" abstractions performed by either mode. This option is not supported in\n"); + log(" the -init mode.\n"); + log("\n"); + log(" -slice :\n"); + log(" -slice \n"); + log(" -rtlilslice :\n"); + log(" -rtlilslice \n"); + log(" Limit the abstraction to a slice of a single selected wire. The targeted\n"); + log(" bits of the wire can be given as an inclusive range of indices or as a\n"); + log(" single index. When using the -slice option, the indices are interpreted\n"); + log(" following the source level declaration of the wire. This means the\n"); + log(" -slice option will respect declarations with a non-zero-based index range\n"); + log(" or with reversed bitorder. The -rtlilslice options will always use\n"); + log(" zero-based indexing where index 0 corresponds to the least significant\n"); + log(" bit of the wire.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing ABSTRACT pass.\n"); From d3a90021ada18dff7dbc95fc8075fd1d1dedf14e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 18 Feb 2025 13:50:24 +0100 Subject: [PATCH 073/123] abstract: test -state --- tests/various/abstract.ys | 213 +++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 116 deletions(-) diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index 0892ca1c4..30aa8eb39 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -10,15 +10,59 @@ endmodule EOT proc -# show -prefix before_base +design -save half_clock + +# ----------------------------------------------------------------------------- +# An empty selection causes no change +select -none + +logger -expect log "Abstracted 0 stateful cells" 1 + abstract -state -enablen magic +logger -check-expected + +logger -expect log "Abstracted 0 init bits" 1 + abstract -init +logger -check-expected + +logger -expect log "Abstracted 0 driver ports" 1 + abstract -value -enablen magic +logger -check-expected + +select -clear +# ----------------------------------------------------------------------------- +design -load half_clock +# Basic -state test abstract -state -enablen magic check -assert -# show -prefix after_base +# Connections to dff D input port +select -set conn_to_d t:$dff %x:+[D] t:$dff %d +# The D input port is fed with a mux +select -set mux @conn_to_d %ci t:$mux %i +select -assert-count 1 @mux +# The S input port is fed with the magic wire +select -assert-count 1 @mux %x:+[S] w:magic %i +# The A input port is fed with an anyseq +select -assert-count 1 @mux %x:+[A] %ci t:$anyseq %i +# The B input port is fed with the negated Q +select -set not @mux %x:+[B] %ci t:$not %i +select -assert-count 1 @not %x:+[A] o:Q %i +design -load half_clock +# Same thing, inverted polarity +abstract -state -enable magic +check -assert +select -set conn_to_d t:$dff %x:+[D] t:$dff %d +select -set mux @conn_to_d %ci t:$mux %i +select -assert-count 1 @mux +select -assert-count 1 @mux %x:+[S] w:magic %i +# so we get swapped A and B +select -assert-count 1 @mux %x:+[B] %ci t:$anyseq %i +select -set not @mux %x:+[A] %ci t:$not %i +select -assert-count 1 @not %x:+[A] o:Q %i +# ----------------------------------------------------------------------------- design -reset read_verilog < Date: Tue, 18 Feb 2025 15:20:09 +0100 Subject: [PATCH 074/123] abstract: test -value --- .../{abstract.ys => abstract_state.ys} | 0 tests/various/abstract_value.ys | 74 +++++++++++++++++++ 2 files changed, 74 insertions(+) rename tests/various/{abstract.ys => abstract_state.ys} (100%) create mode 100644 tests/various/abstract_value.ys diff --git a/tests/various/abstract.ys b/tests/various/abstract_state.ys similarity index 100% rename from tests/various/abstract.ys rename to tests/various/abstract_state.ys diff --git a/tests/various/abstract_value.ys b/tests/various/abstract_value.ys new file mode 100644 index 000000000..643d55a65 --- /dev/null +++ b/tests/various/abstract_value.ys @@ -0,0 +1,74 @@ +read_verilog < muxes on inputs and outputs +design -load split_output +select -assert-count 0 t:$mux +abstract -value -enable magic w:* w:magic %d +select -assert-count 3 t:$mux +# All cells selected -> muxes on outputs only +design -load split_output +select -assert-count 0 t:$mux +abstract -value -enable magic t:* +select -assert-count 1 t:$mux +# ----------------------------------------------------------------------------- From 925c617c52f545dfe47a7564e64755a2d5445282 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 18 Feb 2025 17:05:14 +0100 Subject: [PATCH 075/123] abstract: add module input -value abstraction --- passes/cmds/abstract.cc | 45 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 4a97a640c..918330f0f 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -212,7 +212,7 @@ unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector offsets, IdString port_name, EnableLogic enable) { +bool abstract_value_cell_port(Module* mod, Cell* cell, std::set offsets, IdString port_name, EnableLogic enable) { Wire* to_abstract = mod->addWire(NEW_ID, offsets.size()); SigSpec mux_input; SigSpec mux_output; @@ -234,6 +234,31 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin return true; } +bool abstract_value_mod_port(Module* mod, Wire* wire, std::set offsets, EnableLogic enable) { + Wire* to_abstract = mod->addWire(NEW_ID, wire); + to_abstract->port_input = true; + to_abstract->port_id = wire->port_id; + wire->port_input = false; + wire->port_id = 0; + mod->swap_names(wire, to_abstract); + SigSpec mux_input; + SigSpec mux_output; + SigSpec direct_lhs; + SigSpec direct_rhs; + for (int port_idx = 0; port_idx < wire->width; port_idx++) { + if (offsets.count(port_idx)) { + mux_output.append(SigBit(wire, port_idx)); + mux_input.append(SigBit(to_abstract, port_idx)); + } else { + direct_lhs.append(SigBit(wire, port_idx)); + direct_rhs.append(SigBit(to_abstract, port_idx)); + } + } + mod->connections_.push_back(SigSig(direct_lhs, direct_rhs)); + emit_mux_anyseq(mod, mux_input, mux_output, enable); + return true; +} + unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector &slices) { SigMap sigmap(mod); dict> selected_reps = gather_selected_reps(mod, slices, sigmap); @@ -254,9 +279,25 @@ unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector wires_snapshot = mod->wires(); + for (auto wire : wires_snapshot) + if (wire->port_input) { + std::set offsets_to_abstract; + for (auto bit : SigSpec(wire)) + if (selected_reps.count(sigmap(bit))) { + log_debug("Abstracting value for module input port bit %s in module %s due to selections:\n", + log_signal(bit), log_id(mod)); + explain_selections(selected_reps.at(sigmap(bit))); + offsets_to_abstract.insert(bit.offset); + } + if (offsets_to_abstract.empty()) + continue; + + changed += abstract_value_mod_port(mod, wire, offsets_to_abstract, enable); + } return changed; } From 5bd18613bb0fbdb1809fc40be33fa5f04df2707f Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 19 Feb 2025 23:03:43 +0100 Subject: [PATCH 076/123] abstract: test -init --- tests/various/abstract_init.ys | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/various/abstract_init.ys diff --git a/tests/various/abstract_init.ys b/tests/various/abstract_init.ys new file mode 100644 index 000000000..e848ddf0a --- /dev/null +++ b/tests/various/abstract_init.ys @@ -0,0 +1,39 @@ +design -reset +read_verilog < Date: Wed, 19 Feb 2025 16:04:53 -0800 Subject: [PATCH 077/123] Add python ldflags to LINKFLAGS --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 87572261e..0a6d06391 100644 --- a/Makefile +++ b/Makefile @@ -201,6 +201,8 @@ ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1) PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed else PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config + +LINKFLAGS += $(shell $(PYTHON_CONFIG) --ldflags) endif PYTHON_DESTDIR := $(shell $(PYTHON_EXECUTABLE) -c "import site; print(site.getsitepackages()[-1]);") @@ -331,7 +333,7 @@ endif ifeq ($(ENABLE_PYOSYS),1) # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers -CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) $(LINKFLAGS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") +CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ From 1eaf1847b2724b796f081dd34a208d553a8c95d8 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:15:34 +1300 Subject: [PATCH 078/123] CONTRIBUTING.md: Initial version --- CONTRIBUTING.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..86df13777 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Introduction + +Thanks for thinking about contributing to the Yosys project. If this is your +first time contributing to an open source project, please take a look at the +following guide: +https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project. + + +# Using the issue tracker + +The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for +tracking bugs or other problems with Yosys or its documentation. It is also the +place to go for requesting new features. +When [creating a new issue](https://github.com/YosysHQ/yosys/issues/new/choose), +we have a few templates available. Please make use of these! It will make it +much easier for someone to respond and help. + +### Bug reports + +Before you submit an issue, please have a search of the existing issues in case +one already exists. Making sure that you have a minimal, complete and +verifiable example (MVCE) is a great way to quickly check an existing issue +against a new one. Stack overflow has a guide on [how to create an +MVCE](https://stackoverflow.com/help/minimal-reproducible-example). The +[`bugpoint` +command](https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/bugpoint.html) +in Yosys can be helpful for this process. + + +# Using pull requests + +If you are working on something to add to Yosys, or fix something that isn't +working quite right, make a PR! An open PR, even as a draft, tells everyone +that you're working on it and they don't have to. It can also be a useful way +to solicit feedback on in-progress changes. See below to find the best way to +[ask us questions](#asking-questions). + +In general, all changes to the code are done as a +[PR](https://github.com/YosysHQ/yosys/pulls), with [Continuous Integration +(CI)](https://github.com/YosysHQ/yosys/actions) tools that automatically run the +full suite of tests compiling and running Yosys. Please make use of this! If +you're adding a feature: add a test! Not only does it verify that your feature +is working as expected, but it can also be a handy way for people to see how the +feature is used. If you're fixing a bug: add a test! If you can, do this +first; it's okay if the test starts off failing - you already know there is a +bug. CI also helps to make sure that your changes still work under a range of +compilers, settings, and targets. + + +### Labels + +We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise +issues and PRs. If a label seems relevant to your work, please do add it; this +also includes the labels beggining with 'status-'. The 'merge-' labels are used +by maintainers for tracking and communicating which PRs are ready and pending +merge; please do not use these labels if you are not a maintainer. + + +# Asking questions + +If you have a question about how to use Yosys, please ask on our [discussions +page](https://github.com/YosysHQ/yosys/discussions) or in our [community +slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA). +The slack is also a great place to ask questions about developing or +contributing to Yosys. + +We have open dev 'jour fixe' (JF) meetings where developers from YosysHQ and the +community come together to discuss open issues and PRs. This is also a good +place to talk to us about how to implement larger PRs. Please join the +community slack if you would like to join the next meeting, the link is +available in the description of the #devel-discuss channel. From 9c9b3ebf9e0ab3e74ce8694090f49de34fa7f4aa Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:15:38 +1300 Subject: [PATCH 079/123] CONTRIBUTING.md: Move PR link --- CONTRIBUTING.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86df13777..36940ae1b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,21 +30,21 @@ in Yosys can be helpful for this process. # Using pull requests If you are working on something to add to Yosys, or fix something that isn't -working quite right, make a PR! An open PR, even as a draft, tells everyone -that you're working on it and they don't have to. It can also be a useful way -to solicit feedback on in-progress changes. See below to find the best way to -[ask us questions](#asking-questions). +working quite right, make a [PR](https://github.com/YosysHQ/yosys/pulls)! An +open PR, even as a draft, tells everyone that you're working on it and they +don't have to. It can also be a useful way to solicit feedback on in-progress +changes. See below to find the best way to [ask us +questions](#asking-questions). -In general, all changes to the code are done as a -[PR](https://github.com/YosysHQ/yosys/pulls), with [Continuous Integration -(CI)](https://github.com/YosysHQ/yosys/actions) tools that automatically run the -full suite of tests compiling and running Yosys. Please make use of this! If -you're adding a feature: add a test! Not only does it verify that your feature -is working as expected, but it can also be a handy way for people to see how the -feature is used. If you're fixing a bug: add a test! If you can, do this -first; it's okay if the test starts off failing - you already know there is a -bug. CI also helps to make sure that your changes still work under a range of -compilers, settings, and targets. +In general, all changes to the code are done as a PR, with [Continuous +Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools that +automatically run the full suite of tests compiling and running Yosys. Please +make use of this! If you're adding a feature: add a test! Not only does it +verify that your feature is working as expected, but it can also be a handy way +for people to see how the feature is used. If you're fixing a bug: add a test! +If you can, do this first; it's okay if the test starts off failing - you +already know there is a bug. CI also helps to make sure that your changes still +work under a range of compilers, settings, and targets. ### Labels From 38746ea5fad7018f00d2143c491ebd637552e7e0 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:15:39 +1300 Subject: [PATCH 080/123] docs: Cross reference contributing .md points to .html on latest .rst points to .md on main (not that it will work before this is merged) --- CONTRIBUTING.md | 2 ++ .../yosys_internals/extending_yosys/contributing.rst | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 36940ae1b..6c4376cc4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,8 @@ first time contributing to an open source project, please take a look at the following guide: https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project. +Information about the Yosys coding style is available on our Read the Docs: +https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html. # Using the issue tracker diff --git a/docs/source/yosys_internals/extending_yosys/contributing.rst b/docs/source/yosys_internals/extending_yosys/contributing.rst index e06de61b1..69258aa5f 100644 --- a/docs/source/yosys_internals/extending_yosys/contributing.rst +++ b/docs/source/yosys_internals/extending_yosys/contributing.rst @@ -1,6 +1,14 @@ Contributing to Yosys ===================== +.. note:: + + For information on making a pull request on github, refer to our + |CONTRIBUTING|_ file. + +.. |CONTRIBUTING| replace:: :file:`CONTRIBUTING.md` +.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/CONTRIBUTING.md + Coding Style ------------ From 700b32bc389525428671a1d6dd23d089a423ba4d Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:32:30 +1300 Subject: [PATCH 081/123] Makefile: Fix CHECK_BOOST_PYTHON `python-config --ldflag` includes output of `python-config --libs`; and the `$(CXX)` call in `CHECK_BOOST_PYTHON` still needs those libs. Move all of the `$(shell $(PYTHON_CONFIG) ..` lines to the top of the block. --- Makefile | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 0a6d06391..f4b74f7f5 100644 --- a/Makefile +++ b/Makefile @@ -201,8 +201,6 @@ ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1) PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed else PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config - -LINKFLAGS += $(shell $(PYTHON_CONFIG) --ldflags) endif PYTHON_DESTDIR := $(shell $(PYTHON_EXECUTABLE) -c "import site; print(site.getsitepackages()[-1]);") @@ -332,8 +330,13 @@ TARGETS += libyosys.so endif ifeq ($(ENABLE_PYOSYS),1) +# python-config --ldflags includes -l and -L, but LINKFLAGS is only -L +LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) +LIBS += $(shell $(PYTHON_CONFIG) --libs) +CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON + # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers -CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") +CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ @@ -345,11 +348,7 @@ ifeq ($(BOOST_PYTHON_LIB),) $(error BOOST_PYTHON_LIB could not be detected. Please define manually) endif -LIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem -# python-config --ldflags includes LIBS for some reason -LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) -CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON - +LIBS += $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem PY_WRAPPER_FILE = kernel/python_wrappers OBJS += $(PY_WRAPPER_FILE).o PY_GEN_SCRIPT= py_wrap_generator From 077819572222094b30b3cc08e62132456095ca0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 00:21:16 +0000 Subject: [PATCH 082/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e32a6a368..78534805f 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+7 +YOSYS_VER := 0.50+14 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From c429aef60f60cf405cf9e35e019446b37c430617 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 22 Feb 2025 17:14:13 +1300 Subject: [PATCH 083/123] docs/rosette: More intro of SMT-LIB backend As the intro paragraph (now) says: > This section will introduce the SMT-LIB functional backend and what changes are needed... The example is intended to be read without prior knowledge of the SMT-LIB backend, but the previous version glossed over a lot and instead focused on *just* what was changed. This version should now be easier to follow without prior knowledge, while still being able to learn enough about the `Smt` version to adapt it to a different s-expression target that isn't Rosette. Also adds a few `literalinclude`s of smtlib.cc, which is now copied to `docs/source/generated` along with producing the rosette diff on the fly (which now also has up to 20 lines of context, enabling the full `Module::write()` diff to be literal included). --- Makefile | 14 +- .../code_examples/functional/rosette.diff | 372 ------------------ .../extending_yosys/functional_ir.rst | 149 ++++--- 3 files changed, 98 insertions(+), 437 deletions(-) delete mode 100644 docs/source/code_examples/functional/rosette.diff diff --git a/Makefile b/Makefile index 6f5fec6a2..73dd2e335 100644 --- a/Makefile +++ b/Makefile @@ -996,6 +996,18 @@ docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' +docs/source/generated/%.cc: backends/%.cc + $(Q) mkdir -p $(@D) + $(Q) cp $< $@ + +# diff returns exit code 1 if the files are different, but it's not an error +docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc backends/functional/smtlib_rosette.cc + $(Q) mkdir -p $(@D) + $(Q) diff -U 20 $^ > $@ || exit 0 + +PHONY: docs/gen/functional_ir +docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff + PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen @@ -1031,7 +1043,7 @@ docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep -docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage +docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir DOC_TARGET ?= html docs: docs/prep diff --git a/docs/source/code_examples/functional/rosette.diff b/docs/source/code_examples/functional/rosette.diff deleted file mode 100644 index 35c7936d1..000000000 --- a/docs/source/code_examples/functional/rosette.diff +++ /dev/null @@ -1,372 +0,0 @@ -diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib_rosette.cc -index 3eacf407c..c9e737d19 100644 ---- a/backends/functional/smtlib.cc -+++ b/backends/functional/smtlib_rosette.cc -@@ -29,80 +29,86 @@ PRIVATE_NAMESPACE_BEGIN - using SExprUtil::list; - - const char *reserved_keywords[] = { -- // reserved keywords from the smtlib spec -- "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", -- "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", -- "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", -- "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", -- "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", -- "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", -+ // reserved keywords from the racket spec -+ "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", -+ "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", -+ "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", -+ "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", -+ "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", -+ "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", -+ "define-generics", "set", - - // reserved for our own purposes -- "pair", "Pair", "first", "second", -- "inputs", "state", -+ "inputs", "state", "name", - nullptr - }; - --struct SmtScope : public Functional::Scope { -- SmtScope() { -+struct SmtrScope : public Functional::Scope { -+ SmtrScope() { - for(const char **p = reserved_keywords; *p != nullptr; p++) - reserve(*p); - } - bool is_character_legal(char c, int index) override { -- return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); -+ return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); - } - }; - --struct SmtSort { -+struct SmtrSort { - Functional::Sort sort; -- SmtSort(Functional::Sort sort) : sort(sort) {} -+ SmtrSort(Functional::Sort sort) : sort(sort) {} - SExpr to_sexpr() const { - if(sort.is_memory()) { -- return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); -+ return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); - } else if(sort.is_signal()) { -- return list("_", "BitVec", sort.width()); -+ return list("bitvector", sort.width()); - } else { - log_error("unknown sort"); - } - } - }; - --class SmtStruct { -+class SmtrStruct { - struct Field { -- SmtSort sort; -+ SmtrSort sort; - std::string accessor; -+ std::string name; - }; - idict field_names; - vector fields; -- SmtScope &scope; -+ SmtrScope &global_scope; -+ SmtrScope local_scope; - public: - std::string name; -- SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} -- void insert(IdString field_name, SmtSort sort) { -+ SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} -+ void insert(IdString field_name, SmtrSort sort) { - field_names(field_name); -- auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); -- fields.emplace_back(Field{sort, accessor}); -+ auto base_name = local_scope.unique_name(field_name); -+ auto accessor = name + "-" + base_name; -+ global_scope.reserve(accessor); -+ fields.emplace_back(Field{sort, accessor, base_name}); - } - void write_definition(SExprWriter &w) { -- w.open(list("declare-datatype", name)); -- w.open(list()); -- w.open(list(name)); -- for(const auto &field : fields) -- w << list(field.accessor, field.sort.to_sexpr()); -- w.close(3); -+ vector field_list; -+ for(const auto &field : fields) { -+ field_list.emplace_back(field.name); -+ } -+ w.push(); -+ w.open(list("struct", name, field_list, "#:transparent")); -+ if (field_names.size()) { -+ for (const auto &field : fields) { -+ auto bv_type = field.sort.to_sexpr(); -+ w.comment(field.name + " " + bv_type.to_string()); -+ } -+ } -+ w.pop(); - } - template void write_value(SExprWriter &w, Fn fn) { -- if(field_names.empty()) { -- // Zero-argument constructors in SMTLIB must not be called as functions. -- w << name; -- } else { -- w.open(list(name)); -- for(auto field_name : field_names) { -- w << fn(field_name); -- w.comment(RTLIL::unescape_id(field_name), true); -- } -- w.close(); -+ w.open(list(name)); -+ for(auto field_name : field_names) { -+ w << fn(field_name); -+ w.comment(RTLIL::unescape_id(field_name), true); - } -+ w.close(); - } - SExpr access(SExpr record, IdString name) { - size_t i = field_names.at(name); -@@ -117,28 +123,28 @@ std::string smt_const(RTLIL::Const const &c) { - return s; - } - --struct SmtPrintVisitor : public Functional::AbstractVisitor { -+struct SmtrPrintVisitor : public Functional::AbstractVisitor { - using Node = Functional::Node; - std::function n; -- SmtStruct &input_struct; -- SmtStruct &state_struct; -+ SmtrStruct &input_struct; -+ SmtrStruct &state_struct; - -- SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} -+ SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} - - SExpr from_bool(SExpr &&arg) { -- return list("ite", std::move(arg), "#b1", "#b0"); -+ return list("bool->bitvector", std::move(arg)); - } - SExpr to_bool(SExpr &&arg) { -- return list("=", std::move(arg), "#b1"); -+ return list("bitvector->bool", std::move(arg)); - } -- SExpr extract(SExpr &&arg, int offset, int out_width = 1) { -- return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); -+ SExpr to_list(SExpr &&arg) { -+ return list("bitvector->bits", std::move(arg)); - } - - SExpr buf(Node, Node a) override { return n(a); } -- SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } -- SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } -- SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } -+ SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } -+ SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } -+ SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } - SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } - SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } - SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } -@@ -150,16 +156,11 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { - SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } - SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } - SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } -- SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } -- SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } -- SExpr reduce_xor(Node, Node a) override { -- vector s { "bvxor" }; -- for(int i = 0; i < a.width(); i++) -- s.push_back(extract(n(a), i)); -- return s; -- } -- SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } -- SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } -+ SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } -+ SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } -+ SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } -+ SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } -+ SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); } - SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } - SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } - SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } -@@ -167,32 +168,32 @@ struct SmtPrintVisitor : public Functional::AbstractVisitor { - - SExpr extend(SExpr &&a, int in_width, int out_width) { - if(in_width < out_width) -- return list(list("_", "zero_extend", out_width - in_width), std::move(a)); -+ return list("zero-extend", std::move(a), list("bitvector", out_width)); - else - return std::move(a); - } - SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } - SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } - SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } -- SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } -- SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } -- SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } -- SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } -+ SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } -+ SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } -+ SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } -+ SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } - - SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } - SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } - }; - --struct SmtModule { -+struct SmtrModule { - Functional::IR ir; -- SmtScope scope; -+ SmtrScope scope; - std::string name; - -- SmtStruct input_struct; -- SmtStruct output_struct; -- SmtStruct state_struct; -+ SmtrStruct input_struct; -+ SmtrStruct output_struct; -+ SmtrStruct state_struct; - -- SmtModule(Module *module) -+ SmtrModule(Module *module) - : ir(Functional::IR::from_module(module)) - , scope() - , name(scope.unique_name(module->name)) -@@ -200,7 +201,7 @@ struct SmtModule { - , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) - , state_struct(scope.unique_name(module->name.str() + "_State"), scope) - { -- scope.reserve(name + "-initial"); -+ scope.reserve(name + "_initial"); - for (auto input : ir.inputs()) - input_struct.insert(input->name, input->sort); - for (auto output : ir.outputs()) -@@ -212,14 +213,11 @@ struct SmtModule { - void write_eval(SExprWriter &w) - { - w.push(); -- w.open(list("define-fun", name, -- list(list("inputs", input_struct.name), -- list("state", state_struct.name)), -- list("Pair", output_struct.name, state_struct.name))); -+ w.open(list("define", list(name, "inputs", "state"))); - auto inlined = [&](Functional::Node n) { - return n.fn() == Functional::Fn::constant; - }; -- SmtPrintVisitor visitor(input_struct, state_struct); -+ SmtrPrintVisitor visitor(input_struct, state_struct); - auto node_to_sexpr = [&](Functional::Node n) -> SExpr { - if(inlined(n)) - return n.visit(visitor); -@@ -230,9 +228,9 @@ struct SmtModule { - for(auto n : ir) - if(!inlined(n)) { - w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); -- w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); -+ w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); - } -- w.open(list("pair")); -+ w.open(list("cons")); - output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); - state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); - w.pop(); -@@ -240,19 +238,23 @@ struct SmtModule { - - void write_initial(SExprWriter &w) - { -- std::string initial = name + "-initial"; -- w << list("declare-const", initial, state_struct.name); -+ w.push(); -+ auto initial = name + "_initial"; -+ w.open(list("define", initial)); -+ w.open(list(state_struct.name)); - for (auto state : ir.states()) { -- if(state->sort.is_signal()) -- w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); -- else if(state->sort.is_memory()) { -+ if (state->sort.is_signal()) -+ w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); -+ else if (state->sort.is_memory()) { - const auto &contents = state->initial_value_memory(); -+ w.open(list("list")); - for(int i = 0; i < 1<sort.addr_width(); i++) { -- auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); -- w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); -+ w << list("bv", smt_const(contents[i]), state->sort.data_width()); - } -+ w.close(); - } - } -+ w.pop(); - } - - void write(std::ostream &out) -@@ -263,33 +265,53 @@ struct SmtModule { - output_struct.write_definition(w); - state_struct.write_definition(w); - -- w << list("declare-datatypes", -- list(list("Pair", 2)), -- list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); -- - write_eval(w); - write_initial(w); - } - }; - --struct FunctionalSmtBackend : public Backend { -- FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} -+struct FunctionalSmtrBackend : public Backend { -+ FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} - -- void help() override { log("\nFunctional SMT Backend.\n\n"); } -+ void help() override { -+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| -+ log("\n"); -+ log(" write_functional_rosette [options] [filename]\n"); -+ log("\n"); -+ log("Functional Rosette Backend.\n"); -+ log("\n"); -+ log(" -provides\n"); -+ log(" include 'provide' statement(s) for loading output as a module\n"); -+ log("\n"); -+ } - - void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override - { -- log_header(design, "Executing Functional SMT Backend.\n"); -+ auto provides = false; -+ -+ log_header(design, "Executing Functional Rosette Backend.\n"); - -- size_t argidx = 1; -- extra_args(f, filename, args, argidx, design); -+ size_t argidx; -+ for (argidx = 1; argidx < args.size(); argidx++) -+ { -+ if (args[argidx] == "-provides") -+ provides = true; -+ else -+ break; -+ } -+ extra_args(f, filename, args, argidx); -+ -+ *f << "#lang rosette/safe\n"; -+ if (provides) { -+ *f << "(provide (all-defined-out))\n"; -+ } - - for (auto module : design->selected_modules()) { - log("Processing module `%s`.\n", module->name.c_str()); -- SmtModule smt(module); -- smt.write(*f); -+ SmtrModule smtr(module); -+ smtr.write(*f); - } - } --} FunctionalSmtBackend; -+} FunctionalSmtrBackend; - - PRIVATE_NAMESPACE_END diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 81d987f0e..42eabc0b1 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -147,9 +147,9 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr Example: Adapting SMT-LIB backend for Rosette --------------------------------------------- -This section will walk through the process of adapting the SMT-LIB functional -backend (`write_functional_smt2`) to work with another s-expression target, -`Rosette`_ (`write_functional_rosette`). +This section will introduce the SMT-LIB functional backend +(`write_functional_smt2`) and what changes are needed to work with another +s-expression target, `Rosette`_ (`write_functional_rosette`). .. _Rosette: http://emina.github.io/rosette/ @@ -177,12 +177,19 @@ Scope ~~~~~ As described above, the ``Functional::Scope`` class is derived in order to avoid -collisions between identifiers in the generated output. We switch out ``Smt`` -in the class name for ``Smtr`` for ``smtlib_rosette``; which will happen through -the rest of the code too. We also update the ``is_character_legal`` method to -reject ascii characters which are not allowed in Racket variable names. +collisions between identifiers in the generated output. In the SMT-LIB version +the ``SmtScope`` class implements ``Scope``; provides a constructor that +iterates over a list of reserved keywords, calling ``reserve`` on each; and +defines the ``is_character_legal`` method to reject any characters which are not +allowed in SMT-LIB variable names to then be replaced with underscores in the +output. -.. literalinclude:: /code_examples/functional/rosette.diff +In the Rosette version we switch out ``Smt`` in the class name for ``Smtr`` to +mean ``smtlib_rosette``; this will happen through the rest of the code too. We +also update list of legal ascii characters in the ``is_character_legal`` method +to only those allowed in Racket variable names. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Scope`` class :start-at: -struct SmtScope : public Functional::Scope { @@ -218,13 +225,13 @@ and racket specifications to save on space in this document. Sort ~~~~ -The ``Sort`` class is a wrapper for the ``Functional::Sort`` class, providing -the additional functionality of mapping variable declarations to s-expressions -with the ``to_sexpr()`` method. The main change from ``SmtSort`` to -``SmtrSort`` is a syntactical one with signals represented as ``bitvector``\ -s, and memories as ``list``\ s of signals. +Next up in `write_functional_smt2` we see the ``Sort`` class. This is a wrapper +for the ``Functional::Sort`` class, providing the additional functionality of +mapping variable declarations to s-expressions with the ``to_sexpr()`` method. +The main change from ``SmtSort`` to ``SmtrSort`` is a syntactical one with +signals represented as ``bitvector``\ s, and memories as ``list``\ s of signals. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Sort`` wrapper :start-at: SExpr to_sexpr() const { @@ -233,37 +240,56 @@ s, and memories as ``list``\ s of signals. Struct ~~~~~~ -The SMT-LIB backend uses a class, ``SmtStruct``, to help with describing the -input, output, and state data structs. Where each struct in the SMT-LIB output -is a new ``datatype`` with each element having its type declared using the -`Sort`_ above, in Rosette we use the native ``struct`` with each field only -being referred to by name. For ease of use, we include comments for each field -to indicate the expected type. +The ``Functional::IR`` class tracks the set of inputs, the set of outputs, and +the set of "state" variables. The SMT-LIB backend maps each of these sets into +its own ``SmtStruct``, with each variable getting a corresponding field in the +struct and a specified `Sort`_. `write_functional_smt2` then defines each of +these structs as a new ``datatype``, with each element being strongly-typed. -.. literalinclude:: /code_examples/functional/rosette.diff +In Rosette, rather than defining new datatypes for our structs, we use the +native ``struct``. We also only declare each field by name because Racket is +not as strongly-typed. For ease of use, we provide the expected type for each +field as comments. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``write_definition`` method :start-at: void write_definition :end-before: template void write_value -Struct fields in Rosette are accessed as ``-``, where -field names only need to be unique within the struct, while accessors are unique -within the module. We thus modify the class constructor and ``insert`` method -to support this; providing one scope that is local to the struct -(``local_scope``) and one which is shared across the whole module -(``global_scope``). +Each field is added to the ``SmtStruct`` with the ``insert`` method, which also +reserves a unique name (or accessor) within the `Scope`_. These accessors +combine the struct name and field name and are globally unique, being used in +the ``access`` method for reading values from the input/current state. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/smtlib.cc + :language: c++ + :caption: ``Struct::access()`` method + :start-at: SExpr access( + :end-before: }; + +In Rosette, struct fields are accessed as ``-`` so +including the struct name in the field name would be redundant. For +`write_functional_rosette` we instead choose to make field names unique only +within the struct, while accessors are unique across the whole module. We thus +modify the class constructor and ``insert`` method to support this; providing +one scope that is local to the struct (``local_scope``) and one which is shared +across the whole module (``global_scope``), leaving the ``access`` method +unchanged. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of struct constructor :start-at: SmtStruct(std::string name, SmtScope &scope) :end-before: void write_definition -For writing outputs/next state (the ``write_value`` method), the only change is -to remove the check for zero-argument constructors since this is not necessary -with Rosette ``struct``\ s. +Finally, ``SmtStruct`` also provides a ``write_value`` template method which +calls a provided function on each element in the struct. This is used later for +assigning values to the output/next state pair. The only change here is to +remove the check for zero-argument constructors since this is not necessary with +Rosette ``struct``\ s. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``write_value`` method :start-at: template void write_value @@ -273,11 +299,13 @@ PrintVisitor ~~~~~~~~~~~~ The ``PrintVisitor`` implements the abstract ``Functional::AbstractVisitor`` -class for converting FunctionalIR functions into s-expressions, including -reading inputs/current state. For most functions, the Rosette output is very -similar to the corresponding SMT-LIB function with minor adjustments for syntax. +class, described above in `What is FunctionalIR`_, with a return type of +``SExpr``. This class converts FunctionalIR functions into s-expressions, +including reading inputs/current state with the ``access`` method from the +`Struct`_. For most functions, the Rosette output is very similar to the +corresponding SMT-LIB function with minor adjustments for syntax. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities :start-at: SExpr logical_shift_left @@ -286,7 +314,7 @@ similar to the corresponding SMT-LIB function with minor adjustments for syntax. However there are some differences in the two formats with regards to how booleans are handled, with Rosette providing built-in functions for conversion. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences :start-at: SExpr from_bool @@ -295,14 +323,21 @@ booleans are handled, with Rosette providing built-in functions for conversion. Module ~~~~~~ -The ``Functional::IR`` is wrapped in the ``Module`` class, with the mapping from -RTLIL module to FunctionalIR happening in the constructor. Each of the three -structs; inputs, outputs, and state; are then created from the corresponding -lists in the IR. The only change here is rename the initial state to use ``_`` -instead of ``-``, since the ``-`` in Rosette can be used to access struct -fields. +The ``Functional::IR`` is wrapped in the ``SmtModule`` class, with the mapping +from RTLIL module to FunctionalIR happening in the constructor. Each of the +three ``SmtStruct``\ s; inputs, outputs, and state; are created, with each value +in the corresponding lists in the IR being ``insert``\ ed. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/smtlib.cc + :language: c++ + :caption: ``SmtModule`` constructor + :start-at: SmtModule(Module + :end-at: } + +Since Racket uses the ``-`` to access struct fields, the ``SmtrModule`` instead +uses an underscore for the name of the initial state. + +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module`` constructor :start-at: scope.reserve(name @@ -316,25 +351,11 @@ current_state) -> (outputs, next_state)`` with ``write_eval``, and declaring the initial state with ``write_initial``. The only change for the ``SmtrModule`` is that the ``pair`` declaration isn't needed. -.. inlined diff to show the complete function -.. code-block:: diff +.. literalinclude:: /generated/functional/rosette.diff + :language: diff :caption: diff of ``Module::write()`` method - - void write(std::ostream &out) - { - SExprWriter w(out); - - input_struct.write_definition(w); - output_struct.write_definition(w); - state_struct.write_definition(w); - - - w << list("declare-datatypes", - - list(list("Pair", 2)), - - list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); - - - write_eval(w); - write_initial(w); - } + :start-at: void write(std::ostream &out) + :end-at: } For the ``write_eval`` method, the main differences are syntactical. First we change the function declaration line for the Rosette style which drops the @@ -371,7 +392,7 @@ variable, ``[name]_initial``, can then be used in the ``[name]`` function call; allowing the Rosette code to be used in the generation of the ``next_state``, whereas the SMT-LIB code can only verify that a given ``next_state`` is correct. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module::write_initial()`` method :start-at: void write_initial @@ -396,7 +417,7 @@ an additional line declaring that everything in the file should be exported for use; allowing the file to be treated as a Racket package with structs and mapping function available for use externally. -.. literalinclude:: /code_examples/functional/rosette.diff +.. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: relevant portion of diff of ``Backend::execute()`` method :start-at: lang rosette/safe From 732ed67014fe8bd4fcd651e14665bc90cbaab898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 24 Feb 2025 17:32:30 +0100 Subject: [PATCH 084/123] ast/dpicall: Stop using variable length array Fix the compiler warning variable length arrays in C++ are a Clang extension [-Wvla-cxx-extension] --- frontends/ast/dpicall.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index 12a7e1183..d6fcc26bd 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -67,9 +67,10 @@ static ffi_fptr resolve_fn (std::string symbol_name) AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args) { AST::AstNode *newNode = nullptr; - union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1]; - ffi_type *types [args.size() + 1]; - void *values [args.size() + 1]; + union value { double f64; float f32; int32_t i32; void *ptr; }; + std::vector value_store(args.size() + 1); + std::vector types(args.size() + 1); + std::vector values(args.size() + 1); ffi_cif cif; int status; @@ -118,10 +119,10 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, log_error("invalid rtype '%s'.\n", rtype.c_str()); } - if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK) + if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types.data())) != FFI_OK) log_error("ffi_prep_cif failed: status %d.\n", status); - ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values); + ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values.data()); if (rtype == "real") { newNode = new AstNode(AST_REALVALUE); From 3cb7054e534b0cca12faa783e5fe4ffa2be56af3 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 25 Feb 2025 00:18:16 +0100 Subject: [PATCH 085/123] abstract: test -slice for all modes, -rtlilslice for -init --- tests/various/abstract_init.ys | 76 +++++++++++++++++++++++++++++++++ tests/various/abstract_state.ys | 40 ++++++++++++++--- tests/various/abstract_value.ys | 25 ++++++++++- 3 files changed, 133 insertions(+), 8 deletions(-) diff --git a/tests/various/abstract_init.ys b/tests/various/abstract_init.ys index e848ddf0a..06f29153c 100644 --- a/tests/various/abstract_init.ys +++ b/tests/various/abstract_init.ys @@ -37,3 +37,79 @@ select -assert-count 1 w:Q a:init=2'b01 %i abstract -init w:QQQ check -assert select -assert-count 1 w:Q a:init=2'b0x %i + +design -reset +read_verilog < Date: Tue, 25 Feb 2025 00:19:15 +0100 Subject: [PATCH 086/123] abstract: typo? --- passes/cmds/abstract.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 918330f0f..1e67dcd88 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -57,7 +57,7 @@ struct Slice { } std::string to_string() const { - const char *option = indices == RtlilSlice ? "-rawslice" : "-slice"; + const char *option = indices == RtlilSlice ? "-rtlilslice" : "-slice"; if (first == last) return stringf("%s %d", option, first); else From 3f60a2cc67a27db7592e6029f5acdda6749f6ed4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 25 Feb 2025 00:22:14 +0100 Subject: [PATCH 087/123] abstract: test -slice from:to for -init --- tests/various/abstract_init.ys | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/various/abstract_init.ys b/tests/various/abstract_init.ys index 06f29153c..2e0cf2e64 100644 --- a/tests/various/abstract_init.ys +++ b/tests/various/abstract_init.ys @@ -52,10 +52,16 @@ EOT proc opt_expr opt_dff +design -save basic select -assert-count 1 w:Q a:init=2'b01 %i abstract -init -slice 0 w:Q check -assert select -assert-count 1 w:Q a:init=2'b0x %i +design -load basic +select -assert-count 1 w:Q a:init=2'b01 %i +abstract -init -slice 0:1 w:Q +check -assert +select -assert-count 0 w:Q a:init %i design -reset read_verilog < Date: Tue, 25 Feb 2025 17:00:12 +1300 Subject: [PATCH 088/123] libs/fst: Update from upstream Fixes stringop-overread warning --- libs/fst/fstapi.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc index a4329cf32..dc52c3c09 100644 --- a/libs/fst/fstapi.cc +++ b/libs/fst/fstapi.cc @@ -3907,16 +3907,18 @@ while (value) static int fstVcdIDForFwrite(char *buf, unsigned int value) { char *pnt = buf; + int len = 0; /* zero is illegal for a value...it is assumed they start at one */ -while (value) +while (value && len < 14) { value--; + ++len; *(pnt++) = (char)('!' + value % 94); value = value / 94; } -return(pnt - buf); +return len; } From 68e9317f1f0f33dc61d806624bda15265a29a3b9 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 25 Feb 2025 17:18:15 +1300 Subject: [PATCH 089/123] libs/fst: Patch wx_len overread --- libs/fst/00_PATCH_wx_len_overread.patch | 10 ++++++++++ libs/fst/00_UPDATE.sh | 1 + libs/fst/fstapi.cc | 1 + 3 files changed, 12 insertions(+) create mode 100644 libs/fst/00_PATCH_wx_len_overread.patch diff --git a/libs/fst/00_PATCH_wx_len_overread.patch b/libs/fst/00_PATCH_wx_len_overread.patch new file mode 100644 index 000000000..7fba1c2d6 --- /dev/null +++ b/libs/fst/00_PATCH_wx_len_overread.patch @@ -0,0 +1,10 @@ +--- fstapi.cc ++++ fstapi.cc +@@ -6072,6 +6072,7 @@ for(;;) + } + + wx_len = snprintf(wx_buf, 32, "r%.16g", d); ++ if (wx_len > 32 || wx_len < 0) wx_len = 32; + fstWritex(xc, wx_buf, wx_len); + } + } diff --git a/libs/fst/00_UPDATE.sh b/libs/fst/00_UPDATE.sh index 7ab74d7cd..66a0fd8df 100755 --- a/libs/fst/00_UPDATE.sh +++ b/libs/fst/00_UPDATE.sh @@ -18,3 +18,4 @@ sed -i -e 's,"fastlz.c","fastlz.cc",' *.cc *.h patch -p0 < 00_PATCH_win_zlib.patch patch -p0 < 00_PATCH_win_io.patch patch -p1 < 00_PATCH_strict_alignment.patch +patch -p0 < 00_PATCH_wx_len_overread.patch diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc index dc52c3c09..ab3c54469 100644 --- a/libs/fst/fstapi.cc +++ b/libs/fst/fstapi.cc @@ -6072,6 +6072,7 @@ for(;;) } wx_len = snprintf(wx_buf, 32, "r%.16g", d); + if (wx_len > 32 || wx_len < 0) wx_len = 32; fstWritex(xc, wx_buf, wx_len); } } From 05c81b3f14adf23eddbc772df84f25f90ef794a1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 00:21:19 +0000 Subject: [PATCH 090/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0f3b451f7..a29bca316 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+14 +YOSYS_VER := 0.50+49 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 78cb10da696fcf619d345d6ea51e1552189bfcb9 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 26 Feb 2025 12:06:54 +0100 Subject: [PATCH 091/123] improve mkdtemp error message --- kernel/yosys.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index de25d20e2..0ff38e52f 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -413,8 +413,11 @@ std::string make_temp_dir(std::string template_str) # endif char *p = strdup(template_str.c_str()); + log_assert(p); char *res = mkdtemp(p); - log_assert(res != NULL); + if (!res) + log_error("mkdtemp failed for '%s': %s [Error %d]\n", + p, strerror(errno), errno); template_str = p; free(p); From 78960292d09e1841fcf926d091adb97bee686c1c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 00:21:40 +0000 Subject: [PATCH 092/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a29bca316..bf4a16beb 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+49 +YOSYS_VER := 0.50+56 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From db823a6acb772de4b06c4cf4b136f0b8dff5a86a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:11:44 +1300 Subject: [PATCH 093/123] docs/rosette: Fix inline code --- docs/source/yosys_internals/extending_yosys/functional_ir.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 42eabc0b1..c82a2828a 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -386,7 +386,7 @@ and the ``next_state`` in a single variable. Iteration over all of the } For the ``write_initial`` method, the SMT-LIB backend uses ``declare-const`` and -`assert`\ s which must always hold true. For Rosette we instead define the +``assert``\ s which must always hold true. For Rosette we instead define the initial state as any other variable that can be used by external code. This variable, ``[name]_initial``, can then be used in the ``[name]`` function call; allowing the Rosette code to be used in the generation of the ``next_state``, From 560b5fe6bd53dd174d16698b4639a8b168d20f79 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:14:59 +1300 Subject: [PATCH 094/123] docs/functional: Add dummy toy example Add `dummy.cc`, loosely based on `backends/functional/test_generic.cc` but as an actualy backend and without the memory testing. Skeleton section for minimal functional backend, describing the aforementioned `dummy.cc`. --- docs/source/code_examples/functional/dummy.cc | 44 +++++++++++++++++++ .../extending_yosys/functional_ir.rst | 35 +++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 docs/source/code_examples/functional/dummy.cc diff --git a/docs/source/code_examples/functional/dummy.cc b/docs/source/code_examples/functional/dummy.cc new file mode 100644 index 000000000..3d84b84ba --- /dev/null +++ b/docs/source/code_examples/functional/dummy.cc @@ -0,0 +1,44 @@ +#include "kernel/functional.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct FunctionalDummyBackend : public Backend { + FunctionalDummyBackend() : Backend("functional_dummy", "dump generated Functional IR") {} + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { + // backend pass boiler plate + log_header(design, "Executing dummy functional backend.\n"); + + size_t argidx = 1; + extra_args(f, filename, args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("Processing module `%s`.\n", module->name.c_str()); + + // convert module to FunctionalIR + auto ir = Functional::IR::from_module(module); + *f << "module " << module->name.c_str() << "\n"; + + // write node functions + for (auto node : ir) + *f << " assign " << id2cstr(node.name()) + << " = " << node.to_string() << "\n"; + *f << "\n"; + + // write outputs and next state + for (auto output : ir.outputs()) + *f << " " << id2cstr(output->kind) + << " " << id2cstr(output->name) + << " = " << id2cstr(output->value().name()) << "\n"; + for (auto state : ir.states()) + *f << " " << id2cstr(state->kind) + << " " << id2cstr(state->name) + << " = " << id2cstr(state->next_value().name()) << "\n"; + } + } +} FunctionalDummyBackend; + +PRIVATE_NAMESPACE_END diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index c82a2828a..80eeeebf2 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -144,6 +144,41 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr parentheses. - The destructor calls ``flush`` but also closes all unclosed parentheses. +Example: A minimal functional backend +------------------------------------- + +.. literalinclude:: /code_examples/functional/dummy.cc + :language: c++ + :caption: Example source code for a minimal functional backend + +- three main steps needed + + + convert to FunctionalIR + + handle nodes + + handle outputs and next state + +- backend pass boiler plate gives us ``write_functional_dummy`` command +- pointer ``f`` is a ``std::ostream`` we can write to, being either a file or + stdout +- FunctionalIR conversion done by ``Functional::IR::from_module()`` +- each node performs some function or operation (including reading input/current + state) + + + each variable is assigned exactly once before being used + + ``node.name()`` returns a ``RTLIL::IdString``, which we convert for + printing with ``id2cstr()`` + + ``node.to_string()`` converts the node into a string of the form + ``function(args)`` + + * ``function`` is the result of ``Functional::IR::fn_to_string(node.fn())`` + * ``args`` are the zero or more arguments passed to the function, most + commonly the name of another node + * wraps ``node.visit()`` with a private visitor with return type + ``std::string`` + +- ``Functional::IROutput::value()`` and ``Functional::IRState::next_value()`` + represent the outputs of our function + Example: Adapting SMT-LIB backend for Rosette --------------------------------------------- From e01a4137222d6c2f76da1c017aaa70cd91a290da Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:49:05 +1300 Subject: [PATCH 095/123] docs/rosette: List major changes in overview Also a little paragraph on why SMT-LIB is used as the base. --- .../extending_yosys/functional_ir.rst | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 80eeeebf2..8395e6504 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -201,6 +201,11 @@ Overview .. _Racket: http://racket-lang.org/ .. _SMT: http://smtlib.cs.uiowa.edu/ +Rosette, being backed by SMT solvers and written with s-expressions, uses code +very similar to the `write_functional_smt2` output. As a result, the SMT-LIB +functional backend can be used as a starting point for implementing a Rosette +backend. + Full code listings for the initial SMT-LIB backend and the converted Rosette backend are included in the Yosys source repository under :file:`backends/functional` as ``smtlib.cc`` and ``smtlib_rosette.cc`` @@ -208,6 +213,23 @@ respectively. Note that the Rosette language is an extension of the Racket language; this guide tends to refer to Racket when talking about the underlying semantics/syntax of the language. +The major changes from the SMT-LIB backend are as follows: + +- all of the ``Smt`` prefixes in names are replace with ``Smtr`` to mean + ``smtlib_rosette``; +- syntax is adjusted for Racket; +- data structures for input/output/state are changed from using + ``declare-datatype`` with statically typed fields, to using ``struct`` with no + static typing; +- the transfer function also loses its static typing; +- sign/zero extension in Rosette uses the output width instead of the number of + extra bits, gaining static typing; +- the single scope is traded for a global scope with local scope for each + struct; +- initial state is provided as a constant value instead of a set of assertions; +- and the ``-provides`` option is introduced to more easily use generated code + within Rosette based applications. + Scope ~~~~~ From 3c493d2bef628fb92f5d5bb375a38b97197762e5 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 28 Feb 2025 16:33:57 +1300 Subject: [PATCH 096/123] docs/rosette: Reword Acknowledge the minimal functional backend, highlighting what's new/different for our SExpr targets. Add and use the reference `minimal backend`. Use `note` directives to point out missing code sections (highlighting that the included diffs are not complete). Racket *is* still strongly-typed, it's just dynamic instead of static. Adjust phrasing to reflect that. Adjust some of the literal includes, adding a new section from the `Functional::AbstractVisitor`, splitting the `Module::write_eval()` in twain and adding a `smtlib.cc` literal include for the node iteration, as well as for the `FunctionalSmtBackend` to compare against the minimal backend. Move `Backend` description up to minimal functional backend section. --- .../extending_yosys/functional_ir.rst | 166 +++++++++++------- 1 file changed, 102 insertions(+), 64 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 8395e6504..689d45d32 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -144,6 +144,8 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr parentheses. - The destructor calls ``flush`` but also closes all unclosed parentheses. +.. _minimal backend: + Example: A minimal functional backend ------------------------------------- @@ -158,6 +160,13 @@ Example: A minimal functional backend + handle outputs and next state - backend pass boiler plate gives us ``write_functional_dummy`` command + +The final part is the ``Backend`` itself, which registers the command in Yosys. +The ``execute`` method is the part that runs when the user calls the command, +handling any options, preparing the output file for writing, and iterating over +selected modules in the design. For more on adding new commands to Yosys and +how they work, refer to :doc:`/yosys_internals/extending_yosys/extensions`. + - pointer ``f`` is a ``std::ostream`` we can write to, being either a file or stdout - FunctionalIR conversion done by ``Functional::IR::from_module()`` @@ -233,18 +242,23 @@ The major changes from the SMT-LIB backend are as follows: Scope ~~~~~ -As described above, the ``Functional::Scope`` class is derived in order to avoid -collisions between identifiers in the generated output. In the SMT-LIB version -the ``SmtScope`` class implements ``Scope``; provides a constructor that -iterates over a list of reserved keywords, calling ``reserve`` on each; and -defines the ``is_character_legal`` method to reject any characters which are not -allowed in SMT-LIB variable names to then be replaced with underscores in the -output. +Our first addition to the `minimal backend`_ above is that for both SMT-LIB and +Rosette backends, we are now targetting real languages which bring with them +their own sets of constraints with what we can use as identifiers. This is +where the ``Functional::Scope`` class described above comes in; by using this +class we can safely rename our identifiers in the generated output without +worrying about collisions or illegal names/characters. -In the Rosette version we switch out ``Smt`` in the class name for ``Smtr`` to -mean ``smtlib_rosette``; this will happen through the rest of the code too. We -also update list of legal ascii characters in the ``is_character_legal`` method -to only those allowed in Racket variable names. +In the SMT-LIB version, the ``SmtScope`` class implements ``Scope``; +provides a constructor that iterates over a list of reserved keywords, calling +``reserve`` on each; and defines the ``is_character_legal`` method to reject any +characters which are not allowed in SMT-LIB variable names to then be replaced +with underscores in the output. To use this scope we create an instance of it, +and call the ``Scope::unique_name()`` method to generate a unique and legal name +for each of our identifiers. + +In the Rosette version we update the list of legal ascii characters in the +``is_character_legal`` method to only those allowed in Racket variable names. .. literalinclude:: /generated/functional/rosette.diff :language: diff @@ -276,8 +290,8 @@ provides this functionality natively with ``cons``, which we will see later. nullptr }; -Note that we skip over the actual list of reserved keywords from both the smtlib -and racket specifications to save on space in this document. +.. note:: We skip over the actual list of reserved keywords from both the smtlib + and racket specifications to save on space in this document. Sort ~~~~ @@ -297,16 +311,17 @@ signals represented as ``bitvector``\ s, and memories as ``list``\ s of signals. Struct ~~~~~~ -The ``Functional::IR`` class tracks the set of inputs, the set of outputs, and -the set of "state" variables. The SMT-LIB backend maps each of these sets into -its own ``SmtStruct``, with each variable getting a corresponding field in the -struct and a specified `Sort`_. `write_functional_smt2` then defines each of -these structs as a new ``datatype``, with each element being strongly-typed. +As we saw in the `minimal backend`_ above, the ``Functional::IR`` class tracks +the set of inputs, the set of outputs, and the set of "state" variables. The +SMT-LIB backend maps each of these sets into its own ``SmtStruct``, with each +variable getting a corresponding field in the struct and a specified `Sort`_. +`write_functional_smt2` then defines each of these structs as a new +``datatype``, with each element being strongly-typed. In Rosette, rather than defining new datatypes for our structs, we use the -native ``struct``. We also only declare each field by name because Racket is -not as strongly-typed. For ease of use, we provide the expected type for each -field as comments. +native ``struct``. We also only declare each field by name because Racket +provides less static typing. For ease of use, we provide the expected type for +each field as comments. .. literalinclude:: /generated/functional/rosette.diff :language: diff @@ -355,18 +370,19 @@ Rosette ``struct``\ s. PrintVisitor ~~~~~~~~~~~~ -The ``PrintVisitor`` implements the abstract ``Functional::AbstractVisitor`` -class, described above in `What is FunctionalIR`_, with a return type of -``SExpr``. This class converts FunctionalIR functions into s-expressions, -including reading inputs/current state with the ``access`` method from the -`Struct`_. For most functions, the Rosette output is very similar to the -corresponding SMT-LIB function with minor adjustments for syntax. +Remember in the `minimal backend`_ we converted nodes into strings for writing +using the ``node.to_string()`` method, which wrapped ``node.visit()`` with a +private visitor. We now want a custom visitor which can convert nodes into +s-expressions. This is where the ``PrintVisitor`` comes in, implementing the +abstract ``Functional::AbstractVisitor`` class with a return type of ``SExpr``. +For most functions, the Rosette output is very similar to the corresponding +SMT-LIB function with minor adjustments for syntax. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities :start-at: SExpr logical_shift_left - :end-before: SExpr input + :end-at: "list-set-bv" However there are some differences in the two formats with regards to how booleans are handled, with Rosette providing built-in functions for conversion. @@ -377,13 +393,30 @@ booleans are handled, with Rosette providing built-in functions for conversion. :start-at: SExpr from_bool :end-before: SExpr extract +Of note here is the rare instance of the Rosette implementation *gaining* static +typing rather than losing it. Where SMT_LIB calls zero/sign extension with the +number of extra bits needed (given by ``out_width - a.width()``), Rosette +instead specifies the type of the output (given by ``list("bitvector", +out_width)``). + +.. literalinclude:: /generated/functional/rosette.diff + :language: diff + :caption: zero/sign extension implementation diff + :start-after: SExpr buf( + :end-before: SExpr concat( + :lines: 2-3, 5-6 + +.. note:: Be sure to check the source code for the full list of differences here. + Module ~~~~~~ -The ``Functional::IR`` is wrapped in the ``SmtModule`` class, with the mapping -from RTLIL module to FunctionalIR happening in the constructor. Each of the -three ``SmtStruct``\ s; inputs, outputs, and state; are created, with each value -in the corresponding lists in the IR being ``insert``\ ed. +With most of the supporting classes out of the way, we now reach our three main +steps from the `minimal backend`_. These are all handled by the ``SmtModule`` +class, with the mapping from RTLIL module to FunctionalIR happening in the +constructor. Each of the three ``SmtStruct``\ s; inputs, outputs, and state; +are also created in the constructor, with each value in the corresponding lists +in the IR being ``insert``\ ed. .. literalinclude:: /generated/functional/smtlib.cc :language: c++ @@ -403,7 +436,7 @@ uses an underscore for the name of the initial state. The ``write`` method is then responsible for writing the FunctionalIR to the output file, formatted for the corresponding backend. ``SmtModule::write()`` breaks the output file down into four parts: defining the three structs, -declaring the ``pair`` datatype, defining the mapping function ``(inputs, +declaring the ``pair`` datatype, defining the transfer function ``(inputs, current_state) -> (outputs, next_state)`` with ``write_eval``, and declaring the initial state with ``write_initial``. The only change for the ``SmtrModule`` is that the ``pair`` declaration isn't needed. @@ -414,33 +447,35 @@ that the ``pair`` declaration isn't needed. :start-at: void write(std::ostream &out) :end-at: } -For the ``write_eval`` method, the main differences are syntactical. First we -change the function declaration line for the Rosette style which drops the -explicit output typing and uses the ``define`` keyword instead of -``define-fun``. And then we change the final result from a ``pair`` to the -native ``cons`` which acts in much the same way, returning both the ``outputs`` -and the ``next_state`` in a single variable. Iteration over all of the -``Functional::Node``\ s in the IR is the same in both. +The ``write_eval`` method is where the FunctionalIR nodes, outputs, and next +state are handled. Just as with the `minimal backend`_, we iterate over the +nodes with ``for(auto n : ir)``, and then use the ``Struct::write_value()`` +method for the ``output_struct`` and ``state_struct`` to iterate over the +outputs and next state respectively. -.. inlined diff for showing the whole function while skipping the middle part -.. code-block:: diff - :caption: diff of ``Module::write_eval()`` method +.. literalinclude:: /generated/functional/smtlib.cc + :language: c++ + :caption: iterating over FunctionalIR nodes in ``SmtModule::write_eval()`` + :start-at: for(auto n : ir) + :end-at: } - void write_eval(SExprWriter &w) - { - w.push(); - - w.open(list("define-fun", name, - - list(list("inputs", input_struct.name), - - list("state", state_struct.name)), - - list("Pair", output_struct.name, state_struct.name))); - + w.open(list("define", list(name, "inputs", "state"))); - ... - - w.open(list("pair")); - + w.open(list("cons")); - output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); - state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); - w.pop(); - } +The main differences between our two backends here are syntactical. First we +change the ``define-fun`` for the Racket style ``define`` which drops the +explicitly typed inputs/outputs. And then we change the final result from a +``pair`` to the native ``cons`` which acts in much the same way, returning both +the ``outputs`` and the ``next_state`` in a single variable. + +.. literalinclude:: /generated/functional/rosette.diff + :language: diff + :caption: diff of ``Module::write_eval()`` transfer function declaration + :start-at: w.open(list("define-fun" + :end-at: w.open(list("define" + +.. literalinclude:: /generated/functional/rosette.diff + :language: diff + :caption: diff of output/next state handling ``Module::write_eval()`` + :start-at: w.open(list("pair" + :end-at: w.pop(); For the ``write_initial`` method, the SMT-LIB backend uses ``declare-const`` and ``assert``\ s which must always hold true. For Rosette we instead define the @@ -458,12 +493,15 @@ whereas the SMT-LIB code can only verify that a given ``next_state`` is correct. Backend ~~~~~~~ -The final part is the ``Backend`` itself, which registers the command in Yosys. -The ``execute`` method is the part that runs when the user calls -`write_functional_rosette`, handling any options, preparing the output file for -writing, and iterating over selected modules in the design. For more on adding -new commands to Yosys and how they work, refer to -:doc:`/yosys_internals/extending_yosys/extensions`. +The final part is the ``Backend`` itself, with much of the same boiler plate as +the `minimal backend`_. The main difference is that we use the `Module`_ to +perform the actual processing. + +.. literalinclude:: /generated/functional/smtlib.cc + :language: c++ + :caption: The ``FunctionalSmtBackend`` + :start-at: struct FunctionalSmtBackend + :end-at: } FunctionalSmtBackend; There are two additions here for Rosette. The first is that the output file needs to start with the ``#lang`` definition which tells the From 9a14ab8d98cdaaf2a680ad744aeb11db2226c592 Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Fri, 28 Feb 2025 13:51:22 +0200 Subject: [PATCH 097/123] splitcells: Fix the assertion bug caused by out-of-bound offset --- passes/cmds/splitcells.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/passes/cmds/splitcells.cc b/passes/cmds/splitcells.cc index 074bf0f64..e3d509564 100644 --- a/passes/cmds/splitcells.cc +++ b/passes/cmds/splitcells.cc @@ -103,8 +103,7 @@ struct SplitcellsWorker auto slice_signal = [&](SigSpec old_sig) -> SigSpec { SigSpec new_sig; - for (int i = 0; i < GetSize(old_sig); i += GetSize(outsig)) { - int offset = i+slice_lsb; + for (int offset = slice_lsb; offset <= GetSize(old_sig); offset += GetSize(outsig)) { int length = std::min(GetSize(old_sig)-offset, slice_msb-slice_lsb+1); new_sig.append(old_sig.extract(offset, length)); } From de032d2e2ac766df596ab4a11ecf14175b9c8fb4 Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Fri, 28 Feb 2025 23:16:04 +0200 Subject: [PATCH 098/123] splitcells: change for-loop condition --- passes/cmds/splitcells.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/splitcells.cc b/passes/cmds/splitcells.cc index e3d509564..d2063a0c8 100644 --- a/passes/cmds/splitcells.cc +++ b/passes/cmds/splitcells.cc @@ -103,7 +103,7 @@ struct SplitcellsWorker auto slice_signal = [&](SigSpec old_sig) -> SigSpec { SigSpec new_sig; - for (int offset = slice_lsb; offset <= GetSize(old_sig); offset += GetSize(outsig)) { + for (int offset = slice_lsb; offset < GetSize(old_sig); offset += GetSize(outsig)) { int length = std::min(GetSize(old_sig)-offset, slice_msb-slice_lsb+1); new_sig.append(old_sig.extract(offset, length)); } From b56e3ec6e407c75a67d26f9b1badee1b3b79d4de Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 1 Mar 2025 16:15:54 +1300 Subject: [PATCH 099/123] docs/functional: Minimal backend paragraphs I was thinking about compiling the dummy example and including a simple example usage, but it turns out functional.h isn't (currently) available for extensions. --- .../extending_yosys/functional_ir.rst | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 689d45d32..37a7e33e1 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -149,44 +149,49 @@ S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr Example: A minimal functional backend ------------------------------------- +At its most basic, there are three steps we need to accomplish for a minimal +functional backend. First, we need to convert our design into FunctionalIR. +This is most easily done by calling the ``Functional::IR::from_module()`` static +method with our top-level module, or iterating over and converting each of the +modules in our design. Second, we need to handle each of the +``Functional::Node``\ s in our design. Iterating over the ``Functional::IR`` +includes reading the module inputs and current state, but not writing the +results. So our final step is to handle the outputs and next state. + +In order to add an output command to Yosys, we implement the ``Yosys::Backend`` +class and provide an instance of it: + .. literalinclude:: /code_examples/functional/dummy.cc :language: c++ - :caption: Example source code for a minimal functional backend + :caption: Example source code for a minimal functional backend, ``dummy.cc`` -- three main steps needed +Because we are using the ``Backend`` class, our ``"functional_dummy"`` is +registered as the ``write_functional_dummy`` command. The ``execute`` method is +the part that runs when the user calls the command, handling any options, +preparing the output file for writing, and iterating over selected modules in +the design. Since we don't have any options here, we set ``argidx = 1`` and +call the ``extra_args()`` method. This method will read the command arguments, +raising an error if there are any unexpected ones. It will also assign the +pointer ``f`` to the output file, or stdout if none is given. - + convert to FunctionalIR - + handle nodes - + handle outputs and next state +.. note:: -- backend pass boiler plate gives us ``write_functional_dummy`` command + For more on adding new commands to Yosys and how they work, refer to + :doc:`/yosys_internals/extending_yosys/extensions`. -The final part is the ``Backend`` itself, which registers the command in Yosys. -The ``execute`` method is the part that runs when the user calls the command, -handling any options, preparing the output file for writing, and iterating over -selected modules in the design. For more on adding new commands to Yosys and -how they work, refer to :doc:`/yosys_internals/extending_yosys/extensions`. +For this minimal example all we are doing is printing out each node. The +``node.name()`` method returns an ``RTLIL::IdString``, which we convert for +printing with ``id2cstr()``. Then, to print the function of the node, we use +``node.to_string()`` which gives us a string of the form ``function(args)``. The +``function`` part is the result of ``Functional::IR::fn_to_string(node.fn())``; +while ``args`` is the zero or more arguments passed to the function, most +commonly the name of another node. Behind the scenes, the ``node.to_string()`` +method actually wraps ``node.visit(visitor)`` with a private visitor whose +return type is ``std::string``. -- pointer ``f`` is a ``std::ostream`` we can write to, being either a file or - stdout -- FunctionalIR conversion done by ``Functional::IR::from_module()`` -- each node performs some function or operation (including reading input/current - state) - - + each variable is assigned exactly once before being used - + ``node.name()`` returns a ``RTLIL::IdString``, which we convert for - printing with ``id2cstr()`` - + ``node.to_string()`` converts the node into a string of the form - ``function(args)`` - - * ``function`` is the result of ``Functional::IR::fn_to_string(node.fn())`` - * ``args`` are the zero or more arguments passed to the function, most - commonly the name of another node - * wraps ``node.visit()`` with a private visitor with return type - ``std::string`` - -- ``Functional::IROutput::value()`` and ``Functional::IRState::next_value()`` - represent the outputs of our function +Finally we iterate over the module's outputs and states, using +``Functional::IROutput::value()`` and ``Functional::IRState::next_value()`` +respectively in order to get the results of the transfer function. Example: Adapting SMT-LIB backend for Rosette --------------------------------------------- From 912c93ca0ba75e109c184aae32e10f2df4bc5b4d Mon Sep 17 00:00:00 2001 From: Emil J Date: Mon, 3 Mar 2025 18:52:05 +0100 Subject: [PATCH 100/123] share: help message formatting Co-authored-by: KrystalDelusion <93062060+KrystalDelusion@users.noreply.github.com> --- passes/opt/share.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index bf583dd32..20ec8d358 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1454,6 +1454,7 @@ struct SharePass : public Pass { log("\n"); log(" -limit N\n"); log(" Only perform the first N merges, then stop. This is useful for debugging.\n"); + log("\n"); log(" -pattern-limit N\n"); log(" Only analyze up to N activation patterns per cell, otherwise assume active.\n"); log(" N is 1000 by default. Higher values may merge more resources at the cost of\n"); From c9785584da449b55e3e0117bf178a58e237f09ef Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Fri, 7 Mar 2025 08:39:21 +1300 Subject: [PATCH 101/123] docs/rosette: Fix tenses --- docs/source/yosys_internals/extending_yosys/functional_ir.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst index 37a7e33e1..4f363623e 100644 --- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -229,14 +229,14 @@ semantics/syntax of the language. The major changes from the SMT-LIB backend are as follows: -- all of the ``Smt`` prefixes in names are replace with ``Smtr`` to mean +- all of the ``Smt`` prefixes in names are replaced with ``Smtr`` to mean ``smtlib_rosette``; - syntax is adjusted for Racket; - data structures for input/output/state are changed from using ``declare-datatype`` with statically typed fields, to using ``struct`` with no static typing; - the transfer function also loses its static typing; -- sign/zero extension in Rosette uses the output width instead of the number of +- sign/zero extension in Rosette use the output width instead of the number of extra bits, gaining static typing; - the single scope is traded for a global scope with local scope for each struct; From 557047fe1eda02d1c0bc34478a7bbfb4cebc1fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 7 Mar 2025 12:07:02 +0100 Subject: [PATCH 102/123] opt_clean, simplemap: Add `$buf` handling --- passes/opt/opt_clean.cc | 2 +- passes/techmap/simplemap.cc | 9 +++++++++ techlibs/common/techmap.v | 2 +- tests/techmap/buf.ys | 13 +++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/techmap/buf.ys diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 51212fa0e..c37c03607 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -601,7 +601,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool std::vector delcells; for (auto cell : module->cells()) - if (cell->type.in(ID($pos), ID($_BUF_)) && !cell->has_keep_attr()) { + if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) { bool is_signed = cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool(); RTLIL::SigSpec a = cell->getPort(ID::A); RTLIL::SigSpec y = cell->getPort(ID::Y); diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 7461460fe..b23985770 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -42,6 +42,14 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) } } +void simplemap_buf(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + module->connect(RTLIL::SigSig(sig_y, sig_a)); +} + void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort(ID::A); @@ -411,6 +419,7 @@ void simplemap_get_mappers(dict { mappers[ID($not)] = simplemap_not; mappers[ID($pos)] = simplemap_pos; + mappers[ID($buf)] = simplemap_buf; mappers[ID($and)] = simplemap_bitop; mappers[ID($or)] = simplemap_bitop; mappers[ID($xor)] = simplemap_bitop; diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 119296147..6cf4f3415 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -59,7 +59,7 @@ module _90_simplemap_compare_ops; endmodule (* techmap_simplemap *) -(* techmap_celltype = "$pos $slice $concat $mux $tribuf $bmux $bwmux $bweqx" *) +(* techmap_celltype = "$buf $pos $slice $concat $mux $tribuf $bmux $bwmux $bweqx" *) module _90_simplemap_various; endmodule diff --git a/tests/techmap/buf.ys b/tests/techmap/buf.ys new file mode 100644 index 000000000..2b2534fab --- /dev/null +++ b/tests/techmap/buf.ys @@ -0,0 +1,13 @@ +read_verilog -icells < Date: Sun, 2 Mar 2025 12:24:57 -0500 Subject: [PATCH 103/123] Initial file list support --- frontends/verilog/verilog_frontend.cc | 76 +++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index d363d71fb..5328ad404 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -26,6 +26,8 @@ * */ +#include + #include "verilog_frontend.h" #include "preproc.h" #include "kernel/yosys.h" @@ -672,6 +674,80 @@ struct VerilogDefines : public Pass { } } VerilogDefines; +struct VerilogFilelist : public Pass { + VerilogFilelist() : Pass("verilog_filelist", "use filelist") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" verilog_filelist [options]\n"); + log("\n"); + log("Load a Verilog file list, and pass list of Verilog files to read_verilog command.\n"); + log("\n"); + log(" -Fflist\n"); + log(" File list file contains list of Verilog to be parsed, any\n"); + log(" ' path is relative to the file list file'\n"); + log("\n"); + log(" -Fflist\n"); + log(" File list file contains list of Verilog to be parsed, any\n"); + log(" ' path is relative to current working directroy'\n"); + log("\n"); + } + + void parse_flist_rel_filelist(const std::string &flist_path, RTLIL::Design *design) + { + std::ifstream ifs(flist_path); + if (!ifs.is_open()) { + log_error("file list file does not exist"); + exit(1); + } + + std::filesystem::path flist_parent_dir = std::filesystem::path(flist_path).parent_path(); + + std::string v_file_name; + while (std::getline(ifs, v_file_name)) { + if (v_file_name.empty()) { + continue; + } + std::string v_file_path = flist_parent_dir.string() + '/' + v_file_name; + log("Verilog file %s\n", v_file_path.c_str()); + + bool is_sv = (std::filesystem::path(v_file_path).extension() == ".sv"); + + std::string command = "read_verilog"; + if (is_sv) { + command += " -sv"; + } + command = command + ' ' + v_file_path; + Pass::call(design, command); + } + } + + void parse_flist_rel_pwd(const std::string &flist_path, RTLIL::Design *design) { log("pwd %s\n", flist_path.c_str()); } + + void execute(std::vector args, RTLIL::Design *design) override + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-F" && argidx + 1 < args.size()) { + std::string flist_path = args[++argidx]; + parse_flist_rel_filelist(flist_path, design); + continue; + } + if (arg == "-f" && argidx + 1 < args.size()) { + std::string flist_path = args[++argidx]; + parse_flist_rel_pwd(flist_path, design); + continue; + } + break; + } + + if (args.size() != argidx) + cmd_error(args, argidx, "Extra argument."); + } +} VerilogFilelist; + YOSYS_NAMESPACE_END // the yyerror function used by bison to report parser errors From 8f46f53f183b9e4cb7011b185ba0632efdf95514 Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Sun, 2 Mar 2025 12:29:56 -0500 Subject: [PATCH 104/123] Add clangd cache to gitignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5c3d8f4e9..3c209ff4f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.gcno *~ __pycache__ +/.cache /.cproject /.project /.settings @@ -52,3 +53,4 @@ __pycache__ /venv /boost /ffi +/compile_commands.json From 0678c4dec97d0ad3d83220c75cee8d65f419a4f1 Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Sun, 2 Mar 2025 21:07:00 -0500 Subject: [PATCH 105/123] Coding style update --- frontends/verilog/verilog_frontend.cc | 60 +++++++++++++++------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 5328ad404..d2cea3599 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -674,56 +674,61 @@ struct VerilogDefines : public Pass { } } VerilogDefines; -struct VerilogFilelist : public Pass { - VerilogFilelist() : Pass("verilog_filelist", "use filelist") {} +struct VerilogFileList : public Pass { + VerilogFileList() : Pass("read_verilog_file_list", "Parse a Verilog file list") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" verilog_filelist [options]\n"); + log(" read_verilog_file_list [options]\n"); log("\n"); - log("Load a Verilog file list, and pass list of Verilog files to read_verilog command.\n"); + log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog command.\n"); log("\n"); - log(" -Fflist\n"); - log(" File list file contains list of Verilog to be parsed, any\n"); - log(" ' path is relative to the file list file'\n"); + log(" -F file_list_path\n"); + log(" File list file contains list of Verilog files to be parsed, any\n"); + log(" ' path is treated relative to the file list file'\n"); log("\n"); - log(" -Fflist\n"); - log(" File list file contains list of Verilog to be parsed, any\n"); - log(" ' path is relative to current working directroy'\n"); + log(" -f file_list_path\n"); + log(" File list file contains list of Verilog files to be parsed, any\n"); + log(" ' path is treated relative to current working directroy'\n"); log("\n"); } - void parse_flist_rel_filelist(const std::string &flist_path, RTLIL::Design *design) + void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path) { - std::ifstream ifs(flist_path); - if (!ifs.is_open()) { - log_error("file list file does not exist"); + std::ifstream flist(file_list_path); + if (!flist.is_open()) { + log_error("Verilog file list file does not exist"); exit(1); } - std::filesystem::path flist_parent_dir = std::filesystem::path(flist_path).parent_path(); + std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path(); std::string v_file_name; - while (std::getline(ifs, v_file_name)) { + while (std::getline(flist, v_file_name)) { if (v_file_name.empty()) { continue; } - std::string v_file_path = flist_parent_dir.string() + '/' + v_file_name; - log("Verilog file %s\n", v_file_path.c_str()); - bool is_sv = (std::filesystem::path(v_file_path).extension() == ".sv"); + std::string verilog_file_path; + if (relative_to_file_list_path) { + verilog_file_path = file_list_parent_dir.string() + '/' + v_file_name; + } else { + verilog_file_path = std::filesystem::current_path().string() + '/' + v_file_name; + } + + bool is_sv = (std::filesystem::path(verilog_file_path).extension() == ".sv"); std::string command = "read_verilog"; if (is_sv) { command += " -sv"; } - command = command + ' ' + v_file_path; + command = command + ' ' + verilog_file_path; Pass::call(design, command); } - } - void parse_flist_rel_pwd(const std::string &flist_path, RTLIL::Design *design) { log("pwd %s\n", flist_path.c_str()); } + flist.close(); + } void execute(std::vector args, RTLIL::Design *design) override { @@ -731,20 +736,21 @@ struct VerilogFilelist : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-F" && argidx + 1 < args.size()) { - std::string flist_path = args[++argidx]; - parse_flist_rel_filelist(flist_path, design); + std::string file_list_path = args[++argidx]; + parse_file_list(file_list_path, design, true); continue; } if (arg == "-f" && argidx + 1 < args.size()) { - std::string flist_path = args[++argidx]; - parse_flist_rel_pwd(flist_path, design); + std::string file_list_path = args[++argidx]; + parse_file_list(file_list_path, design, false); continue; } break; } - if (args.size() != argidx) + if (args.size() != argidx) { cmd_error(args, argidx, "Extra argument."); + } } } VerilogFilelist; From 8ec96ec8065bb42570ddc5ecd843331a31990032 Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Fri, 7 Mar 2025 00:50:28 -0500 Subject: [PATCH 106/123] Address most comments --- .gitignore | 2 +- frontends/verilog/verilog_frontend.cc | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 3c209ff4f..239ae742b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ __pycache__ /qtcreator.config /qtcreator.creator /qtcreator.creator.user +/compile_commands.json /coverage.info /coverage_html /Makefile.conf @@ -53,4 +54,3 @@ __pycache__ /venv /boost /ffi -/compile_commands.json diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index d2cea3599..fd317bf22 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -686,11 +686,11 @@ struct VerilogFileList : public Pass { log("\n"); log(" -F file_list_path\n"); log(" File list file contains list of Verilog files to be parsed, any\n"); - log(" ' path is treated relative to the file list file'\n"); + log(" path is treated relative to the file list file\n"); log("\n"); log(" -f file_list_path\n"); log(" File list file contains list of Verilog files to be parsed, any\n"); - log(" ' path is treated relative to current working directroy'\n"); + log(" path is treated relative to current working directroy\n"); log("\n"); } @@ -710,21 +710,21 @@ struct VerilogFileList : public Pass { continue; } - std::string verilog_file_path; + std::filesystem::path verilog_file_path; if (relative_to_file_list_path) { - verilog_file_path = file_list_parent_dir.string() + '/' + v_file_name; + verilog_file_path = file_list_parent_dir / v_file_name; } else { - verilog_file_path = std::filesystem::current_path().string() + '/' + v_file_name; + verilog_file_path = std::filesystem::current_path() / v_file_name; } - bool is_sv = (std::filesystem::path(verilog_file_path).extension() == ".sv"); + bool is_sv = (verilog_file_path.extension() == ".sv"); - std::string command = "read_verilog"; + std::vector read_verilog_cmd = {"read_verilog", "-defer"}; if (is_sv) { - command += " -sv"; + read_verilog_cmd.push_back("-sv"); } - command = command + ' ' + verilog_file_path; - Pass::call(design, command); + read_verilog_cmd.push_back(verilog_file_path.string()); + Pass::call(design, read_verilog_cmd); } flist.close(); @@ -748,9 +748,7 @@ struct VerilogFileList : public Pass { break; } - if (args.size() != argidx) { - cmd_error(args, argidx, "Extra argument."); - } + extra_args(args, argidx, design); } } VerilogFilelist; From ac31bad6569306450a28d9cd0bdf52ed51aceb93 Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Fri, 7 Mar 2025 00:57:39 -0500 Subject: [PATCH 107/123] Address all comments --- frontends/verilog/verilog_frontend.cc | 72 +++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index fd317bf22..723f2d589 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -674,6 +674,42 @@ struct VerilogDefines : public Pass { } } VerilogDefines; +static void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path) +{ + std::ifstream flist(file_list_path); + if (!flist.is_open()) { + log_error("Verilog file list file does not exist"); + exit(1); + } + + std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path(); + + std::string v_file_name; + while (std::getline(flist, v_file_name)) { + if (v_file_name.empty()) { + continue; + } + + std::filesystem::path verilog_file_path; + if (relative_to_file_list_path) { + verilog_file_path = file_list_parent_dir / v_file_name; + } else { + verilog_file_path = std::filesystem::current_path() / v_file_name; + } + + bool is_sv = (verilog_file_path.extension() == ".sv"); + + std::vector read_verilog_cmd = {"read_verilog", "-defer"}; + if (is_sv) { + read_verilog_cmd.push_back("-sv"); + } + read_verilog_cmd.push_back(verilog_file_path.string()); + Pass::call(design, read_verilog_cmd); + } + + flist.close(); +} + struct VerilogFileList : public Pass { VerilogFileList() : Pass("read_verilog_file_list", "Parse a Verilog file list") {} void help() override @@ -694,42 +730,6 @@ struct VerilogFileList : public Pass { log("\n"); } - void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path) - { - std::ifstream flist(file_list_path); - if (!flist.is_open()) { - log_error("Verilog file list file does not exist"); - exit(1); - } - - std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path(); - - std::string v_file_name; - while (std::getline(flist, v_file_name)) { - if (v_file_name.empty()) { - continue; - } - - std::filesystem::path verilog_file_path; - if (relative_to_file_list_path) { - verilog_file_path = file_list_parent_dir / v_file_name; - } else { - verilog_file_path = std::filesystem::current_path() / v_file_name; - } - - bool is_sv = (verilog_file_path.extension() == ".sv"); - - std::vector read_verilog_cmd = {"read_verilog", "-defer"}; - if (is_sv) { - read_verilog_cmd.push_back("-sv"); - } - read_verilog_cmd.push_back(verilog_file_path.string()); - Pass::call(design, read_verilog_cmd); - } - - flist.close(); - } - void execute(std::vector args, RTLIL::Design *design) override { size_t argidx; From bf1eab565b0f843b9472794f0fbfeabaa49091f7 Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Fri, 7 Mar 2025 20:20:27 -0500 Subject: [PATCH 108/123] Fix compile on WASI platform --- frontends/verilog/verilog_frontend.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 723f2d589..737481743 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -26,7 +26,9 @@ * */ +#if !defined(__wasm) #include +#endif #include "verilog_frontend.h" #include "preproc.h" @@ -674,6 +676,8 @@ struct VerilogDefines : public Pass { } } VerilogDefines; +#if !defined(__wasm) + static void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path) { std::ifstream flist(file_list_path); @@ -721,12 +725,10 @@ struct VerilogFileList : public Pass { log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog command.\n"); log("\n"); log(" -F file_list_path\n"); - log(" File list file contains list of Verilog files to be parsed, any\n"); - log(" path is treated relative to the file list file\n"); + log(" File list file contains list of Verilog files to be parsed, any path is treated relative to the file list file\n"); log("\n"); log(" -f file_list_path\n"); - log(" File list file contains list of Verilog files to be parsed, any\n"); - log(" path is treated relative to current working directroy\n"); + log(" File list file contains list of Verilog files to be parsed, any path is treated relative to current working directroy\n"); log("\n"); } @@ -752,6 +754,8 @@ struct VerilogFileList : public Pass { } } VerilogFilelist; +#endif + YOSYS_NAMESPACE_END // the yyerror function used by bison to report parser errors From 98eefc5d1adcf6aec1dce8877c2f4f0780fa085b Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Fri, 7 Mar 2025 20:44:21 -0500 Subject: [PATCH 109/123] Add file list support to read pass --- frontends/verific/verific.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index cdd0ed802..19f17b0ff 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -4298,7 +4298,7 @@ struct ReadPass : public Pass { log("\n"); log(" read {-f|-F} \n"); log("\n"); - log("Load and execute the specified command file. (Requires Verific.)\n"); + log("Load and execute the specified command file.\n"); log("Check verific command for more information about supported commands in file.\n"); log("\n"); log("\n"); @@ -4412,10 +4412,10 @@ struct ReadPass : public Pass { if (args[1] == "-f" || args[1] == "-F") { if (use_verific) { args[0] = "verific"; - Pass::call(design, args); } else { - cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); + args[0] = "read_verilog_file_list"; } + Pass::call(design, args); return; } From ecd8e4303d84e84373d847b9db566c4c79936041 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 19 Nov 2024 19:40:35 +0100 Subject: [PATCH 110/123] Makefile: add YOSYS_VER and friends to CXXFLAGS for plugin compat check fallback --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index cfa05dfa3..49a3025c2 100644 --- a/Makefile +++ b/Makefile @@ -154,6 +154,13 @@ CXXFLAGS += -D_DEFAULT_SOURCE endif YOSYS_VER := 0.50+56 +YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) +YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) +YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) +CXXFLAGS += -DYOSYS_VER=\\"$(YOSYS_VER)\\" \ + -DYOSYS_MAJOR=$(YOSYS_MAJOR) \ + -DYOSYS_MINOR=$(YOSYS_MINOR) \ + -DYOSYS_COMMIT=$(YOSYS_COMMIT) # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From ca9176cd0f7268fabd5bc9f56a66b4cfc09936d7 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Mar 2025 17:55:04 +0100 Subject: [PATCH 111/123] share: bail better on too many patterns --- passes/opt/share.cc | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 20ec8d358..7bb48eddc 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -856,18 +856,20 @@ struct ShareWorker } template - void insert_capped(pool& cache, Iterator begin_pattern, Iterator end_pattern) + bool insert_capped(pool& cache, Iterator begin_pattern, Iterator end_pattern) { if (cache.size() + std::distance(begin_pattern, end_pattern) > config.pattern_limit) { cache.clear(); cache.insert(ssc_pair_t()); + return false; } else { cache.insert(begin_pattern, end_pattern); } + return true; } - void insert_capped(pool& cache, ssc_pair_t pattern) + bool insert_capped(pool& cache, ssc_pair_t pattern) { - insert_capped(cache, &pattern, &pattern + 1); + return insert_capped(cache, &pattern, &pattern + 1); } const pool &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) @@ -926,20 +928,29 @@ struct ShareWorker for (int i = 0; i < GetSize(sig_s); i++) p.first.append(sig_s[i]), p.second.bits().push_back(RTLIL::State::S0); if (sort_check_activation_pattern(p)) - insert_capped(activation_patterns_cache[cell], p); + if (!insert_capped(activation_patterns_cache[cell], p)) { + recursion_state.erase(cell); + return activation_patterns_cache[cell]; + } } for (int idx : used_in_b_parts) for (auto p : c_patterns) { p.first.append(sig_s[idx]), p.second.bits().push_back(RTLIL::State::S1); if (sort_check_activation_pattern(p)) - insert_capped(activation_patterns_cache[cell], p); + if (!insert_capped(activation_patterns_cache[cell], p)) { + recursion_state.erase(cell); + return activation_patterns_cache[cell]; + } } } for (auto c : driven_cells) { const pool &c_patterns = find_cell_activation_patterns(c, indent); - insert_capped(activation_patterns_cache[cell], c_patterns.begin(), c_patterns.end()); + if (!insert_capped(activation_patterns_cache[cell], c_patterns.begin(), c_patterns.end())) { + recursion_state.erase(cell); + return activation_patterns_cache[cell]; + } } log_assert(recursion_state.count(cell) != 0); From be3dfdc5ad01019d7f843f1acb9a1640316e60ab Mon Sep 17 00:00:00 2001 From: Anhijkt Date: Mon, 10 Mar 2025 19:41:22 +0200 Subject: [PATCH 112/123] splitcells: add tests --- tests/various/bug4909.ys | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/various/bug4909.ys diff --git a/tests/various/bug4909.ys b/tests/various/bug4909.ys new file mode 100644 index 000000000..bf8cfb45b --- /dev/null +++ b/tests/various/bug4909.ys @@ -0,0 +1,44 @@ +read_rtlil << EOF +autoidx 20 +attribute \src "3510.v:2.1-26.10" +attribute \cells_not_processed 1 +attribute \tamara_triplicate 1 +module \top + attribute \src "3510.v:14.3-17.8" + wire width 4 $0\reg5[3:0] + attribute $bugpoint 1 + wire width 4 $auto$bugpoint.cc:258:simplify_something$12 + wire $delete_wire$14 + attribute \src "3510.v:13.19-13.59" + wire width 4 $xnor$3510.v:13$1_Y + attribute \src "3510.v:11.23-11.27" + wire width 4 \reg5 + attribute \src "3510.v:8.24-8.29" + wire width 3 \wire4 + attribute \src "3510.v:3.33-3.34" + wire width 12 output 1 \y + attribute \src "3510.v:13.19-13.59" + cell $xnor $xnor$3510.v:13$1 + parameter \A_SIGNED 0 + parameter \A_WIDTH 3 + parameter \B_SIGNED 0 + parameter \B_WIDTH 4 + parameter \Y_WIDTH 4 + connect \A 3'x + connect \B $auto$bugpoint.cc:258:simplify_something$12 + connect \Y $xnor$3510.v:13$1_Y + end + attribute \src "3510.v:14.3-17.8" + process $proc$3510.v:14$2 + assign $0\reg5[3:0] { \wire4 [2] \wire4 } + sync posedge $delete_wire$14 + update \reg5 $0\reg5[3:0] + end + connect \y [4:0] { \reg5 1'0 } + connect \wire4 $xnor$3510.v:13$1_Y [2:0] +end +EOF + +prep +splitcells + From 6462cd2167ac4831035c8df834a8bf4d608e9e9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 00:22:03 +0000 Subject: [PATCH 113/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b868c273f..6e440af2a 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+56 +YOSYS_VER := 0.50+107 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 6c9857403cb112fc36db73a9eece2fcf34637518 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 11 Mar 2025 14:57:00 +0100 Subject: [PATCH 114/123] share: use share.pattern_limit from scratchpad --- passes/opt/share.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 7bb48eddc..cb1377626 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1477,7 +1477,7 @@ struct SharePass : public Pass { ShareWorkerConfig config; config.limit = -1; - config.pattern_limit = 1000; + config.pattern_limit = design->scratchpad_get_int("share.pattern_limit", 1000); config.opt_force = false; config.opt_aggressive = false; config.opt_fast = false; From f82a26e7e882027f347792f6e90633480f0557da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 11 Mar 2025 19:31:38 +0100 Subject: [PATCH 115/123] Bump ABC --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index cac8f99ea..43b9a4def 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit cac8f99eaa220a5e3db5caeb87cef0a975c953a2 +Subproject commit 43b9a4defe7276697f25f24ed431efb91dcb838f From 1ed0719a0a39d5e160bd5b9c13afc5e9e524db4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 11 Mar 2025 20:18:45 +0100 Subject: [PATCH 116/123] Revert CaDiCaL addition --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index 43b9a4def..5ecc7c333 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit 43b9a4defe7276697f25f24ed431efb91dcb838f +Subproject commit 5ecc7c333ceeadef9b2f3e4a82f7ec72fa512d86 From a5f34d04f8f3f9305c18bdfb79e13e59ac15ac39 Mon Sep 17 00:00:00 2001 From: Jason Xu <40355221+JasonBrave@users.noreply.github.com> Date: Tue, 11 Mar 2025 18:50:44 -0400 Subject: [PATCH 117/123] Address comments --- frontends/verific/verific.cc | 4 ++++ frontends/verilog/verilog_frontend.cc | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 19f17b0ff..a320a6efd 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -4413,7 +4413,11 @@ struct ReadPass : public Pass { if (use_verific) { args[0] = "verific"; } else { +#if !defined(__wasm) args[0] = "read_verilog_file_list"; +#else + cmd_error(args, 1, "Command files are not supported on this platform.\n"); +#endif } Pass::call(design, args); return; diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 737481743..e4e705c39 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -722,13 +722,16 @@ struct VerilogFileList : public Pass { log("\n"); log(" read_verilog_file_list [options]\n"); log("\n"); - log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog command.\n"); + log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog\n"); + log("command\n"); log("\n"); log(" -F file_list_path\n"); - log(" File list file contains list of Verilog files to be parsed, any path is treated relative to the file list file\n"); + log(" File list file contains list of Verilog files to be parsed, any path is\n"); + log(" treated relative to the file list file\n"); log("\n"); log(" -f file_list_path\n"); - log(" File list file contains list of Verilog files to be parsed, any path is treated relative to current working directroy\n"); + log(" File list file contains list of Verilog files to be parsed, any path is\n"); + log(" treated relative to current working directroy\n"); log("\n"); } From d1222c57af306ffea6063d622c1efb5fe6447521 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 00:21:49 +0000 Subject: [PATCH 118/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6e440af2a..3afaa53f3 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+107 +YOSYS_VER := 0.50+111 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 81639073a364b5e2f5a48bcf728207c1a89e648e Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 12 Mar 2025 07:34:33 +0100 Subject: [PATCH 119/123] Update ABC to fix mingw build issue --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index 5ecc7c333..f2d68d590 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit 5ecc7c333ceeadef9b2f3e4a82f7ec72fa512d86 +Subproject commit f2d68d590fa6f8fc32295a2edd79afc0d14a1414 From c4b5190229616f7ebf8197f43990b4429de3e420 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 12 Mar 2025 08:31:37 +0100 Subject: [PATCH 120/123] Release version 0.51 --- CHANGELOG | 16 +++++++++++++++- Makefile | 4 ++-- docs/source/conf.py | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6fb7a92e5..fe5da681d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,22 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.50 .. Yosys 0.51-dev +Yosys 0.50 .. Yosys 0.51 -------------------------- + * New commands and options + - Added "abstract" pass to allow reducing and never increasing + the constraints on a circuit's behavior in a formal verification setting. + + * Various + - "splitcells" pass now splits "aldff" cells. + - FunctionalIR documentation + + * QuickLogic support + - Added IOFF inference for qlf_k6n10f + + * Intel support + - Fixed RAM and DSP support. + - Overall performance improvement for "synth_intel". Yosys 0.49 .. Yosys 0.50 -------------------------- diff --git a/Makefile b/Makefile index 3afaa53f3..ac8e7de24 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.50+111 +YOSYS_VER := 0.51 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -176,7 +176,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline b5170e1.. | wc -l`/;" Makefile +# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline b5170e1.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) diff --git a/docs/source/conf.py b/docs/source/conf.py index f6c4b307a..607e088da 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2025 YosysHQ GmbH' -yosys_ver = "0.50" +yosys_ver = "0.51" # select HTML theme html_theme = 'furo-ys' From 1b07d204bb066b3835db93c3c369cf31eefe106e Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 12 Mar 2025 09:11:41 +0100 Subject: [PATCH 121/123] Next dev cycle --- CHANGELOG | 3 +++ Makefile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fe5da681d..716939992 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,9 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.51 .. Yosys 0.52-dev +-------------------------- + Yosys 0.50 .. Yosys 0.51 -------------------------- * New commands and options diff --git a/Makefile b/Makefile index ac8e7de24..8f0355c8e 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.51 +YOSYS_VER := 0.51+0 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -176,7 +176,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: -# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline b5170e1.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline c4b5190.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) From e44d1d404a2ff2e042c36300f986bd0fa0b8d643 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 00:22:18 +0000 Subject: [PATCH 122/123] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8f0355c8e..89ffaef66 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.51+0 +YOSYS_VER := 0.51+17 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From d8af6b06168b025ea4b7165dfaf6fb3cad8ba3b8 Mon Sep 17 00:00:00 2001 From: Scott Ashcroft Date: Fri, 14 Mar 2025 21:47:26 +0100 Subject: [PATCH 123/123] docs: Add latex magic to make PDFs reproducible again --- docs/source/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 607e088da..6a0e92167 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -93,6 +93,9 @@ bibtex_bibfiles = ['literature.bib'] latex_elements = { 'releasename': 'Version', 'preamble': r''' +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} \usepackage{lmodern} \usepackage{comment}