From 16a3048308f20e4761f491e81a8d4c0b80f5a149 Mon Sep 17 00:00:00 2001 From: Dan Ravensloft Date: Tue, 21 Apr 2020 16:43:21 +0100 Subject: [PATCH] intel_alm: Documentation improvements --- techlibs/intel_alm/common/alm_sim.v | 69 +++++++++++++++++++++++++++++ techlibs/intel_alm/common/dff_map.v | 28 ++++++------ techlibs/intel_alm/common/dff_sim.v | 44 ++++++++++++++++++ 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/techlibs/intel_alm/common/alm_sim.v b/techlibs/intel_alm/common/alm_sim.v index 69768d9f7..979c51132 100644 --- a/techlibs/intel_alm/common/alm_sim.v +++ b/techlibs/intel_alm/common/alm_sim.v @@ -1,3 +1,72 @@ +// 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 *) diff --git a/techlibs/intel_alm/common/dff_map.v b/techlibs/intel_alm/common/dff_map.v index f7f2fe3c3..962be670c 100644 --- a/techlibs/intel_alm/common/dff_map.v +++ b/techlibs/intel_alm/common/dff_map.v @@ -6,7 +6,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -14,7 +14,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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 @@ -23,7 +23,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -31,7 +31,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -39,7 +39,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -47,7 +47,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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 @@ -58,7 +58,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin 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); @@ -67,7 +67,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin 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); @@ -77,7 +77,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin 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); @@ -87,7 +87,7 @@ if (_TECHMAP_WIREINIT_Q_ !== 1'b0) begin 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 @@ -96,7 +96,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -104,7 +104,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -112,7 +112,7 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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); @@ -120,5 +120,5 @@ parameter _TECHMAP_WIREINIT_Q_ = 1'b0; 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 diff --git a/techlibs/intel_alm/common/dff_sim.v b/techlibs/intel_alm/common/dff_sim.v index 07865905f..32444dd46 100644 --- a/techlibs/intel_alm/common/dff_sim.v +++ b/techlibs/intel_alm/common/dff_sim.v @@ -1,3 +1,47 @@ +// 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) -- 2.30.2