Coarse arithmetics

The $macc cell type represents a generalized multiply and accumulate operation. The cell is purely combinational. It outputs the result of summing up a sequence of products and other injected summands.

Y = 0 +- a0factor1 * a0factor2 +- a1factor1 * a1factor2 +- ...
     + B[0] + B[1] + ...

The A port consists of concatenated pairs of multiplier inputs (“factors”). A zero length factor2 acts as a constant 1, turning factor1 into a simple summand.

In this pseudocode, u(foo) means an unsigned int that’s foo bits long.

struct A {
   u(CONFIG.mul_info[0].factor1_len) a0factor1;
   u(CONFIG.mul_info[0].factor2_len) a0factor2;
   u(CONFIG.mul_info[1].factor1_len) a1factor1;
   u(CONFIG.mul_info[1].factor2_len) a1factor2;
   ...
};

The cell’s CONFIG parameter determines the layout of cell port A. The CONFIG parameter carries the following information:

struct CONFIG {
   u4 num_bits;
   struct mul_info {
      bool is_signed;
      bool is_subtract;
      u(num_bits) factor1_len;
      u(num_bits) factor2_len;
   }[num_ports];
};

B is an array of concatenated 1-bit-wide unsigned integers to also be summed up.

yosys> help $alu

Arithmetic logic unit

A building block supporting both binary addition/subtraction operations, and indirectly, comparison operations. Typically created by the alumacc pass, which transforms: $add, $sub, $lt, $le, $ge, $gt, $eq, $eqx, $ne, $nex cells into this $alu cell.

Properties:

is_evaluable

Simulation model (verilog)
Listing 217 simlib.v
688module \$alu (A, B, CI, BI, X, Y, CO);
689
690    parameter A_SIGNED = 0;
691    parameter B_SIGNED = 0;
692    parameter A_WIDTH = 1;
693    parameter B_WIDTH = 1;
694    parameter Y_WIDTH = 1;
695
696    input [A_WIDTH-1:0] A;      // Input operand
697    input [B_WIDTH-1:0] B;      // Input operand
698    output [Y_WIDTH-1:0] X;     // A xor B (sign-extended, optional B inversion,
699                                //          used in combination with
700                                //          reduction-AND for $eq/$ne ops)
701    output [Y_WIDTH-1:0] Y;     // Sum
702
703    input CI;                   // Carry-in (set for $sub)
704    input BI;                   // Invert-B (set for $sub)
705    output [Y_WIDTH-1:0] CO;    // Carry-out
706
707    wire [Y_WIDTH-1:0] AA, BB;
708
709    generate
710        if (A_SIGNED && B_SIGNED) begin:BLOCK1
711            assign AA = $signed(A), BB = BI ? ~$signed(B) : $signed(B);
712        end else begin:BLOCK2
713            assign AA = $unsigned(A), BB = BI ? ~$unsigned(B) : $unsigned(B);
714        end
715    endgenerate
716
717    // this is 'x' if Y and CO should be all 'x', and '0' otherwise
718    wire y_co_undef = ^{A, A, B, B, CI, CI, BI, BI};
719
720    assign X = AA ^ BB;
721    // Full adder
722    assign Y = (AA + BB + CI) ^ {Y_WIDTH{y_co_undef}};
723
724    function get_carry;
725        input a, b, c;
726        get_carry = (a&b) | (a&c) | (b&c);
727    endfunction
728
729    genvar i;
730    generate
731        assign CO[0] = get_carry(AA[0], BB[0], CI) ^ y_co_undef;
732        for (i = 1; i < Y_WIDTH; i = i+1) begin:BLOCK3
733            assign CO[i] = get_carry(AA[i], BB[i], CO[i-1]) ^ y_co_undef;
734        end
735    endgenerate
736
737endmodule
yosys> help $fa
Properties:

is_evaluable

Simulation model (verilog)
Listing 218 simlib.v
628module \$fa (A, B, C, X, Y);
629
630    parameter WIDTH = 1;
631
632    input [WIDTH-1:0] A, B, C;
633    output [WIDTH-1:0] X, Y;
634
635    wire [WIDTH-1:0] t1, t2, t3;
636
637    assign t1 = A ^ B, t2 = A & B, t3 = C & t1;
638    assign Y = t1 ^ C, X = (t2 | t3) ^ (Y ^ Y);
639
640endmodule
yosys> help $lcu

Lookahead carry unit A building block dedicated to fast computation of carry-bits used in binary arithmetic operations. By replacing the ripple carry structure used in full-adder blocks, the more significant bits of the sum can be expected to be computed more quickly. Typically created during techmap of $alu cells (see the “_90_alu” rule in +/techmap.v).

Properties:

is_evaluable

Simulation model (verilog)
Listing 219 simlib.v
656module \$lcu (P, G, CI, CO);
657
658    parameter WIDTH = 1;
659
660    input [WIDTH-1:0] P;    // Propagate
661    input [WIDTH-1:0] G;    // Generate
662    input CI;               // Carry-in
663
664    output reg [WIDTH-1:0] CO; // Carry-out
665
666    integer i;
667    always @* begin
668        CO = 'bx;
669        if (^{P, G, CI} !== 1'bx) begin
670            CO[0] = G[0] || (P[0] && CI);
671            for (i = 1; i < WIDTH; i = i+1)
672                CO[i] = G[i] || (P[i] && CO[i-1]);
673        end
674    end
675
676endmodule
yosys> help $macc

Multiply and accumulate. A building block for summing any number of negated and unnegated signals and arithmetic products of pairs of signals. Cell port A concatenates pairs of signals to be multiplied together. When the second signal in a pair is zero length, a constant 1 is used instead as the second factor. Cell port B concatenates 1-bit-wide signals to also be summed, such as “carry in” in adders. Typically created by the alumacc pass, which transforms $add and $mul into $macc cells.

Properties:

is_evaluable

Simulation model (verilog)
Listing 220 simlib.v
1104module \$macc (A, B, Y);
1105
1106    parameter A_WIDTH = 0;
1107    parameter B_WIDTH = 0;
1108    parameter Y_WIDTH = 0;
1109    // CONFIG determines the layout of A, as explained below
1110    parameter CONFIG = 4'b0000;
1111    parameter CONFIG_WIDTH = 4;
1112
1113    // In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate:
1114    // A cell port is for example the A input (it is constructed in C++ as cell->setPort(ID::A, ...))
1115    // Multiplier ports are pairs of multiplier inputs ("factors").
1116    // If the second signal in such a pair is zero length, no multiplication is necessary, and the first signal is just added to the sum.
1117    input [A_WIDTH-1:0] A; // Cell port A is the concatenation of all arithmetic ports
1118    input [B_WIDTH-1:0] B; // Cell port B is the concatenation of single-bit unsigned signals to be also added to the sum
1119    output reg [Y_WIDTH-1:0] Y; // Output sum
1120
1121    // Xilinx XSIM does not like $clog2() below..
1122    function integer my_clog2;
1123        input integer v;
1124        begin
1125            if (v > 0)
1126                v = v - 1;
1127            my_clog2 = 0;
1128            while (v) begin
1129                v = v >> 1;
1130                my_clog2 = my_clog2 + 1;
1131            end
1132        end
1133    endfunction
1134
1135    // Bits that a factor's length field in CONFIG per factor in cell port A
1136    localparam integer num_bits = CONFIG[3:0] > 0 ? CONFIG[3:0] : 1;
1137    // Number of multiplier ports
1138    localparam integer num_ports = (CONFIG_WIDTH-4) / (2 + 2*num_bits);
1139    // Minium bit width of an induction variable to iterate over all bits of cell port A
1140    localparam integer num_abits = my_clog2(A_WIDTH) > 0 ? my_clog2(A_WIDTH) : 1;
1141
1142    // In this pseudocode, u(foo) means an unsigned int that's foo bits long.
1143    // The CONFIG parameter carries the following information:
1144    //    struct CONFIG {
1145    //        u4 num_bits;
1146    //        struct port_field {
1147    //            bool is_signed;
1148    //            bool is_subtract;
1149    //            u(num_bits) factor1_len;
1150    //            u(num_bits) factor2_len;
1151    //        }[num_ports];
1152    //    };
1153
1154    // The A cell port carries the following information:
1155    //    struct A {
1156    //        u(CONFIG.port_field[0].factor1_len) port0factor1;
1157    //        u(CONFIG.port_field[0].factor2_len) port0factor2;
1158    //        u(CONFIG.port_field[1].factor1_len) port1factor1;
1159    //        u(CONFIG.port_field[1].factor2_len) port1factor2;
1160    //        ...
1161    //    };
1162    // and log(sizeof(A)) is num_abits.
1163    // No factor1 may have a zero length.
1164    // A factor2 having a zero length implies factor2 is replaced with a constant 1.
1165
1166    // Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up.
1167    // Finally, we have:
1168    // Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ...
1169    //     * B[0] + B[1] + ...
1170
1171    function [2*num_ports*num_abits-1:0] get_port_offsets;
1172        input [CONFIG_WIDTH-1:0] cfg;
1173        integer i, cursor;
1174        begin
1175            cursor = 0;
1176            get_port_offsets = 0;
1177            for (i = 0; i < num_ports; i = i+1) begin
1178                get_port_offsets[(2*i + 0)*num_abits +: num_abits] = cursor;
1179                cursor = cursor + cfg[4 + i*(2 + 2*num_bits) + 2 +: num_bits];
1180                get_port_offsets[(2*i + 1)*num_abits +: num_abits] = cursor;
1181                cursor = cursor + cfg[4 + i*(2 + 2*num_bits) + 2 + num_bits +: num_bits];
1182            end
1183        end
1184    endfunction
1185
1186    localparam [2*num_ports*num_abits-1:0] port_offsets = get_port_offsets(CONFIG);
1187
1188    `define PORT_IS_SIGNED   (0 + CONFIG[4 + i*(2 + 2*num_bits)])
1189    `define PORT_DO_SUBTRACT (0 + CONFIG[4 + i*(2 + 2*num_bits) + 1])
1190    `define PORT_SIZE_A      (0 + CONFIG[4 + i*(2 + 2*num_bits) + 2 +: num_bits])
1191    `define PORT_SIZE_B      (0 + CONFIG[4 + i*(2 + 2*num_bits) + 2 + num_bits +: num_bits])
1192    `define PORT_OFFSET_A    (0 + port_offsets[2*i*num_abits +: num_abits])
1193    `define PORT_OFFSET_B    (0 + port_offsets[2*i*num_abits + num_abits +: num_abits])
1194
1195    integer i, j;
1196    reg [Y_WIDTH-1:0] tmp_a, tmp_b;
1197
1198    always @* begin
1199        Y = 0;
1200        for (i = 0; i < num_ports; i = i+1)
1201        begin
1202            tmp_a = 0;
1203            tmp_b = 0;
1204
1205            for (j = 0; j < `PORT_SIZE_A; j = j+1)
1206                tmp_a[j] = A[`PORT_OFFSET_A + j];
1207
1208            if (`PORT_IS_SIGNED && `PORT_SIZE_A > 0)
1209                for (j = `PORT_SIZE_A; j < Y_WIDTH; j = j+1)
1210                    tmp_a[j] = tmp_a[`PORT_SIZE_A-1];
1211
1212            for (j = 0; j < `PORT_SIZE_B; j = j+1)
1213                tmp_b[j] = A[`PORT_OFFSET_B + j];
1214
1215            if (`PORT_IS_SIGNED && `PORT_SIZE_B > 0)
1216                for (j = `PORT_SIZE_B; j < Y_WIDTH; j = j+1)
1217                    tmp_b[j] = tmp_b[`PORT_SIZE_B-1];
1218
1219            if (`PORT_SIZE_B > 0)
1220                tmp_a = tmp_a * tmp_b;
1221
1222            if (`PORT_DO_SUBTRACT)
1223                Y = Y - tmp_a;
1224            else
1225                Y = Y + tmp_a;
1226        end
1227        for (i = 0; i < B_WIDTH; i = i+1) begin
1228            Y = Y + B[i];
1229        end
1230    end
1231
1232    `undef PORT_IS_SIGNED
1233    `undef PORT_DO_SUBTRACT
1234    `undef PORT_SIZE_A
1235    `undef PORT_SIZE_B
1236    `undef PORT_OFFSET_A
1237    `undef PORT_OFFSET_B
1238
1239endmodule
yosys> help $macc_v2

Multiply and add. This cell represents a generic fused multiply-add operation, it supersedes the earlier $macc cell.

Properties:

is_evaluable

Simulation model (verilog)
Listing 221 simlib.v
1251module \$macc_v2 (A, B, C, Y);
1252
1253    parameter NPRODUCTS = 0;
1254    parameter NADDENDS = 0;
1255    parameter A_WIDTHS = 16'h0000;
1256    parameter B_WIDTHS = 16'h0000;
1257    parameter C_WIDTHS = 16'h0000;
1258    parameter Y_WIDTH = 0;
1259
1260    parameter PRODUCT_NEGATED = 1'bx;
1261    parameter ADDEND_NEGATED = 1'bx;
1262    parameter A_SIGNED = 1'bx;
1263    parameter B_SIGNED = 1'bx;
1264    parameter C_SIGNED = 1'bx;
1265
1266    function integer sum_widths1;
1267        input [(16*NPRODUCTS)-1:0] widths;
1268        integer i;
1269        begin
1270            sum_widths1 = 0;
1271            for (i = 0; i < NPRODUCTS; i++) begin
1272                sum_widths1 = sum_widths1 + widths[16*i+:16];
1273            end
1274        end
1275    endfunction
1276
1277    function integer sum_widths2;
1278        input [(16*NADDENDS)-1:0] widths;
1279        integer i;
1280        begin
1281            sum_widths2 = 0;
1282            for (i = 0; i < NADDENDS; i++) begin
1283                sum_widths2 = sum_widths2 + widths[16*i+:16];
1284            end
1285        end
1286    endfunction
1287
1288    input [sum_widths1(A_WIDTHS)-1:0] A; // concatenation of LHS factors
1289    input [sum_widths1(B_WIDTHS)-1:0] B; // concatenation of RHS factors
1290    input [sum_widths2(C_WIDTHS)-1:0] C; // concatenation of summands
1291    output reg [Y_WIDTH-1:0] Y; // output sum
1292
1293    integer i, j, ai, bi, ci, aw, bw, cw;
1294    reg [Y_WIDTH-1:0] product;
1295    reg [Y_WIDTH-1:0] addend, oper_a, oper_b;
1296
1297    always @* begin
1298        Y = 0;
1299        ai = 0;
1300        bi = 0;
1301        for (i = 0; i < NPRODUCTS; i = i+1)
1302        begin
1303            aw = A_WIDTHS[16*i+:16];
1304            bw = B_WIDTHS[16*i+:16];
1305
1306            oper_a = 0;
1307            oper_b = 0;
1308            for (j = 0; j < Y_WIDTH && j < aw; j = j + 1)
1309                oper_a[j] = A[ai + j];
1310            for (j = 0; j < Y_WIDTH && j < bw; j = j + 1)
1311                oper_b[j] = B[bi + j];
1312            // A_SIGNED[i] == B_SIGNED[i] as RTLIL invariant
1313            if (A_SIGNED[i] && B_SIGNED[i]) begin
1314                for (j = aw; j > 0 && j < Y_WIDTH; j = j + 1)
1315                    oper_a[j] = oper_a[j - 1];
1316                for (j = bw; j > 0 && j < Y_WIDTH; j = j + 1)
1317                    oper_b[j] = oper_b[j - 1];
1318            end
1319
1320            product = oper_a * oper_b;
1321
1322            if (PRODUCT_NEGATED[i])
1323                Y = Y - product;
1324            else
1325                Y = Y + product;
1326
1327            ai = ai + aw;
1328            bi = bi + bw;
1329        end
1330
1331        ci = 0;
1332        for (i = 0; i < NADDENDS; i = i+1)
1333        begin
1334            cw = C_WIDTHS[16*i+:16];
1335
1336            addend = 0;
1337            for (j = 0; j < Y_WIDTH && j < cw; j = j + 1)
1338                addend[j] = C[ci + j];
1339            if (C_SIGNED[i]) begin
1340                for (j = cw; j > 0 && j < Y_WIDTH; j = j + 1)
1341                    addend[j] = addend[j - 1];
1342            end
1343
1344            if (ADDEND_NEGATED[i])
1345                Y = Y - addend;
1346            else
1347                Y = Y + addend;
1348
1349            ci = ci + cw;
1350        end
1351    end
1352
1353endmodule