+// The core logic primitive of the Cyclone V/10GX is the Adaptive Logic Module
+// (ALM). Each ALM is made up of an 8-input, 2-output look-up table, covered
+// in this file, connected to combinational outputs, a carry chain, and four
+// D flip-flops (which are covered as MISTRAL_FF in dff_sim.v).
+//
+// The ALM is vertically symmetric, so I find it helps to think in terms of
+// half-ALMs, as that's predominantly the unit that synth_intel_alm uses.
+//
+// ALMs are quite flexible, having multiple modes.
+//
+// Normal (combinational) mode
+// ---------------------------
+// The ALM can implement:
+// - a single 6-input function (with the other inputs usable for flip-flop access)
+// - two 5-input functions that share two inputs
+// - a 5-input and a 4-input function that share one input
+// - a 5-input and a 3-or-less-input function that share no inputs
+// - two 4-or-less-input functions that share no inputs
+//
+// Normal-mode functions are represented as MISTRAL_ALUTN cells with N inputs.
+// It would be possible to represent a normal mode function as a single cell -
+// the vendor cyclone{v,10gx}_lcell_comb cell does exactly that - but I felt
+// it was more user-friendly to print out the specific function sizes
+// separately.
+//
+// With the exception of MISTRAL_ALUT6, you can think of two normal-mode cells
+// fitting inside a single ALM.
+//
+// Extended (7-input) mode
+// -----------------------
+// The ALM can also fit a 7-input function made of two 5-input functions that
+// share four inputs, multiplexed by another input.
+//
+// Because this can't accept arbitrary 7-input functions, Yosys can't handle
+// it, so it doesn't have a cell, but I would likely call it MISTRAL_ALUT7(E?)
+// if it did, and it would take up a full ALM.
+//
+// It might be possible to add an extraction pass to examine all ALUT5 cells
+// that feed into ALUT3 cells to see if they can be combined into an extended
+// ALM, but I don't think it will be worth it.
+//
+// Arithmetic mode
+// ---------------
+// In arithmetic mode, each half-ALM uses its carry chain to perform fast addition
+// of two four-input functions that share three inputs. Oddly, the result of
+// one of the functions is inverted before being added (you can see this as
+// the dot on a full-adder input of Figure 1-8 in the Handbook).
+//
+// The cell for an arithmetic-mode half-ALM is MISTRAL_ALM_ARITH. One idea
+// I've had (or rather was suggested by mwk) is that functions that feed into
+// arithmetic-mode cells could be packed directly into the arithmetic-mode
+// cell as a function, which reduces the number of ALMs needed.
+//
+// Shared arithmetic mode
+// ----------------------
+// Shared arithmetic mode looks a lot like arithmetic mode, but here the
+// output of every other four-input function goes to the input of the adder
+// the next bit along. What this means is that adding three bits together can
+// be done in an ALM, because functions can be used to implement addition that
+// then feeds into the carry chain. This means that three bits can be added per
+// ALM, as opposed to two in the arithmetic mode.
+//
+// Shared arithmetic mode doesn't currently have a cell, but I intend to add
+// it as MISTRAL_ALM_SHARED, and have it occupy a full ALM. Because it adds
+// three bits per cell, it makes addition shorter and use less ALMs, but
+// I don't know enough to tell whether it's more efficient to use shared
+// arithmetic mode to shorten the carry chain, or plain arithmetic mode with
+// the functions packed in.
+
`default_nettype none
(* abc9_lut=2, lib_whitebox *)
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(1'b1), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_P_ with INIT=1");
+end else $error("Cannot implement a flip-flop that initialises to one");
endmodule
module \$_DFF_N_ (input D, C, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(1'b1), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_N_ with INIT=1");
+end else $error("Cannot implement a flip-flop that initialises to one");
endmodule
// D flip-flops with reset
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_PP0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
module \$_DFF_PN0_ (input D, C, R, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_PN0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
module \$_DFF_NP0_ (input D, C, R, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_NP0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
module \$_DFF_NN0_ (input D, C, R, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFF_NN0_ with INIT=1");
+end else $error("Cannot implement a flip-flop with reset that initialises to one");
endmodule
// D flip-flops with set
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
assign Q = ~Q_tmp;
-end else $error("Unsupported flop: $_DFF_PP1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
module \$_DFF_PN1_ (input D, C, R, output Q);
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
-end else $error("Unsupported flop: $_DFF_PN1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
module \$_DFF_NP1_ (input D, C, R, output Q);
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(~C), .ACLR(~R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
assign Q = ~Q_tmp;
-end else $error("Unsupported flop: $_DFF_NP1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
module \$_DFF_NN1_ (input D, C, R, output Q);
wire Q_tmp;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(~D), .CLK(~C), .ACLR(R), .ENA(1'b1), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q_tmp));
assign Q = ~Q_tmp;
-end else $error("Unsupported flop: $_DFF_NN1_ with INIT=0");
+end else $error("Cannot implement a flip-flop with set that initialises to zero");
endmodule
// D flip-flops with clock enable
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(1'b1), .ENA(E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_PP_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
module \$_DFFE_PN_ (input D, C, E, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(C), .ACLR(1'b1), .ENA(~E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_PN_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
module \$_DFFE_NP_ (input D, C, E, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(1'b1), .ENA(E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_NP_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
module \$_DFFE_NN_ (input D, C, E, output Q);
if (_TECHMAP_WIREINIT_Q_ !== 1'b1) begin
wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
MISTRAL_FF _TECHMAP_REPLACE_(.DATAIN(D), .CLK(~C), .ACLR(1'b1), .ENA(~E), .SCLR(1'b0), .SLOAD(1'b0), .SDATA(1'b0), .Q(Q));
-end else $error("Unsupported flop: $_DFFE_NN_ with INIT=1");
+end else $error("Cannot implement a flip-flop with enable that initialises to one");
endmodule
+// The four D flip-flops (DFFs) in a Cyclone V/10GX Adaptive Logic Module (ALM)
+// act as one-bit memory cells that can be placed very flexibly (wherever there's
+// an ALM); each flop is represented by a MISTRAL_FF cell.
+//
+// The flops in these chips are rather flexible in some ways, but in practice
+// quite crippled by FPGA standards.
+//
+// What the flops can do
+// ---------------------
+// The core flop acts as a single-bit memory that initialises to zero at chip
+// reset. It takes in data on the rising edge of CLK if ENA is high,
+// and outputs it to Q. The ENA (clock enable) pin can therefore be used to
+// capture the input only if a condition is true.
+//
+// The data itself is zero if SCLR (synchronous clear) is high, else it comes
+// from SDATA (synchronous data) if SLOAD (synchronous load) is high, or DATAIN
+// if SLOAD is low.
+//
+// If ACLR (asynchronous clear) is low then Q is forced to zero, regardless of
+// the synchronous inputs or CLK edge. This is most often used for an FPGA-wide
+// power-on reset.
+//
+// An asynchronous set that sets Q to one can be emulated by inverting the input
+// and output of the flop, resulting in ACLR forcing Q to zero, which then gets
+// inverted to produce one. Likewise, logic can operate on the falling edge of
+// CLK if CLK is inverted before being passed as an input.
+//
+// What the flops *can't* do
+// -------------------------
+// The trickiest part of the above capabilities is the lack of configurable
+// initialisation state. For example, it isn't possible to implement a flop with
+// asynchronous clear that initialises to one, because the hardware initialises
+// to zero. Likewise, you can't emulate a flop with asynchronous set that
+// initialises to zero, because the inverters mean the flop initialises to one.
+//
+// If the input design requires one of these cells (which appears to be rare
+// in practice) then synth_intel_alm will fail to synthesize the design where
+// other Yosys synthesis scripts might succeed.
+//
+// This stands in notable contrast to e.g. Xilinx flip-flops, which have
+// configurable initialisation state and native synchronous/asynchronous
+// set/clear (although not at the same time), which means they can generally
+// implement a much wider variety of logic.
+
// DATAIN: synchronous data input
// CLK: clock input (positive edge)
// ACLR: asynchronous clear (negative-true)