The DSP48 Primitive - Instantiating the DSP48
Behavioral inference has many advantages - relatively simple and compact code, works with signed and unsigned operands of any size, hides the intricacies of the DSP48 primitive from the user. It should definitely be the first choice when coding a DSP based design if it produces the desired results in terms of device utilization and clock speed.
That's a big if, when things do not go as you want there isn't much you can do - fighting with the synthesis tool is a waste of time and a game you normally cannot win.
Primitive instantiation gives you the total level of control you need but that's the only advantage and the list of drawbacks is long: the HDL code is large, verbose, hard to understand and maintain, the operands are of a very particular type and size with no flexibility, functional simulation is slower and you really need to understand how the primitive works and know what you are doing (I am not sure this is a disadvantage).
The DSP48 is a case apart from the other primitives that can be inferred through HDL synthesis, it is much bigger and more complex compared with the other CLB primitives. The 7-series DSP48E1 version has 25 generics and 49 ports. The UltraScale/UltraScale+ DSP48E2 version has 46 generics and 50 ports - a single DSP48 primitive instantiation is about 100 lines of HDL code and it is hard to tell what the primitive is doing just by looking at the code. This post is too short to include here example code even for a single DSP48 but you can find it in the Vivado GUI, in the Tools/Language Templates menu under Device Primitive Instantiation/ARITHMETIC/DSP, both VHDL and Verilog version for every FPGA family.
On top of that, there are complex interactions between the generic values and the ports, especially in the pre-adder input portion of the primitive and it is very easy to misconfigure it and get unexpected results- you should always simulate any DSP48 instantiation to make sure it does what you think it does. Corner case testing is another thorny issue, your design seems to work with a few simple testcases that you tried but how do you know you have not missed some rare combination of inputs that produces a functional failure?
Is there a way to combine the advantages of the two design flows while avoiding most of their disadvantages? There is no perfect solution but there are ways to make the life of the user instantiating DSP48 primitives easier. The solution I normally use is to create a wrapper file around the primitive and hide all the unpleasantness inside this wrapper.
The basic requirements in creating this wrapper are:
1. It should make instantiation easier, typically 10 lines instead of 100 lines of HDL code - we achieve this by having sensible default values on all generics and input ports so if you do not need to change them we can omit them in the instantiation. Similarly, output ports we do not use can be left unconnected and omitted
2. It should provide access to every possible DSP48 feature and every operating mode - every single DSP48 generic and port is also a generic and port at the wrapper level
3. The main operand ports A, B, C and D and the output result P are unconstrained arbitrary precision fixed point types instead of STD_LOGIC_VECTORs of a fixed size - the operands are sign extended at the MSB end and zero padded at the LSB end automatically and the result is truncated by dropping MSBs and LSBs as needed, no rounding or saturation is done explicitly and it is the user's responsibility to handle that outside the wrapper
4. The binary points of the input operands and the result are aligned automatically by the wrapper, there is no need for the user to consider implicit binary point position or shift the operands and the result to maintain proper numerical representation when using fractional numbers
5. No extra logic is generated inside the wrapper module, it will always contain only the DSP48 primitive
6. Ability to use X and Y generics to floorplan the primitive from HDL code
These features make the instantiation flow almost as compact and generic as behavioral inference, while still giving you access to all possible features and operating modes of the DSP48, something inference can never achieve. You still have to understand thoroughly the inner working of the primitive and simulate it to make sure it is configured properly. A good design practice is to keep the behavioral equivalent of the primitive commented out as part of the code for design documentation purposes. And of course, reading and understanding UG579, The UltraScale Architecture DSP Slice User Guide https://www.xilinx.com/support/documentation/user_guides/ug579-ultrascale-dsp.pdf is mandatory.
So here is how the DSP48E2 version of this generic wrapper would look:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use work.TYPES_PKG.all; –- this gives us access to the user defined SFIXED type
library UNISIM;
use UNISIM.vcomponents.all; –- this lets us instantiate the DSP48E2 primitive without the need of a component definition
entity DSP48E2GW is
generic(X,Y:INTEGER:=-1; –- if positive place DSp48E2 at these coordinates, if negative leave floating
–- Feature Control Attributes: Data Path Selection
AMULTSEL:STRING:="A"; –- Selects A input to multiplier (A, AD)
A_INPUT:STRING:="DIRECT"; –- Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port)
BMULTSEL:STRING:="B"; –- Selects B input to multiplier (AD, B)
B_INPUT:STRING:="DIRECT"; –- Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port)
PREADDINSEL:STRING:="A"; –- Selects input to preadder (A, B)
RND:STD_LOGIC_VECTOR(47 downto 0):=X"000000000000"; –- Rounding Constant
USE_MULT:STRING:="MULTIPLY"; –- Select multiplier usage (DYNAMIC, MULTIPLY, NONE)
USE_SIMD:STRING:="ONE48"; –- SIMD selection (FOUR12, ONE48, TWO24)
USE_WIDEXOR:STRING:="FALSE"; –- Use the Wide XOR function (FALSE, TRUE)
XORSIMD:STRING:="XOR24_48_96"; –- Mode of operation for the Wide XOR (XOR12, XOR24_48_96)
–- Pattern Detector Attributes: Pattern Detection Configuration
AUTORESET_PATDET:STRING:="NO_RESET"; –- NO_RESET, RESET_MATCH, RESET_NOT_MATCH
AUTORESET_PRIORITY:STRING:="RESET"; –- Priority of AUTORESET vs.CEP (CEP, RESET).
MASK:STD_LOGIC_VECTOR(47 downto 0):=X"3fffffffffff"; –- 48-bit mask value for pattern detect (1=ignore)
PATTERN:STD_LOGIC_VECTOR(47 downto 0):=X"000000000000"; –- 48-bit pattern match for pattern detect
SEL_MASK:STRING:="MASK"; –- C, MASK, ROUNDING_MODE1, ROUNDING_MODE2
SEL_PATTERN:STRING:="PATTERN"; –- Select pattern value (C, PATTERN)
USE_PATTERN_DETECT:STRING:="NO_PATDET"; –- Enable pattern detect (NO_PATDET, PATDET)
– Programmable Inversion Attributes: Specifies built-in programmable inversion on specific pins
IS_ALUMODE_INVERTED:STD_LOGIC_VECTOR(3 downto 0):=X"0"; –- Optional inversion for ALUMODE
IS_CARRYIN_INVERTED:BIT:='0'; –- Optional inversion for CARRYIN
IS_CLK_INVERTED:BIT:='0'; –- Optional inversion for CLK
IS_INMODE_INVERTED:STD_LOGIC_VECTOR(4 downto 0):="00000"; –- Optional inversion for INMODE
IS_OPMODE_INVERTED:STD_LOGIC_VECTOR(8 downto 0):="000000000"; –- Optional inversion for OPMODE
IS_RSTALLCARRYIN_INVERTED:BIT:='0'; –- Optional inversion for RSTALLCARRYIN
IS_RSTALUMODE_INVERTED:BIT:='0'; –- Optional inversion for RSTALUMODE
IS_RSTA_INVERTED:BIT:='0'; –- Optional inversion for RSTA
IS_RSTB_INVERTED:BIT:='0'; –- Optional inversion for RSTB
IS_RSTCTRL_INVERTED:BIT:='0'; –- Optional inversion for RSTCTRL
IS_RSTC_INVERTED:BIT:='0'; –- Optional inversion for RSTC
IS_RSTD_INVERTED:BIT:='0'; –- Optional inversion for RSTD
IS_RSTINMODE_INVERTED:BIT:='0'; –- Optional inversion for RSTINMODE
IS_RSTM_INVERTED:BIT:='0'; –- Optional inversion for RSTM
IS_RSTP_INVERTED:BIT:='0'; –- Optional inversion for RSTP
– Register Control Attributes: Pipeline Register Configuration
ACASCREG:INTEGER:=1; –- Number of pipeline stages between A/ACIN and ACOUT (0-2)
ADREG:INTEGER:=1; –- Pipeline stages for pre-adder (0-1)
ALUMODEREG:INTEGER:=1; –- Pipeline stages for ALUMODE (0-1)
AREG:INTEGER:=1; –- Pipeline stages for A (0-2)
BCASCREG:INTEGER:=1; –- Number of pipeline stages between B/BCIN and BCOUT (0-2)
BREG:INTEGER:=1; –- Pipeline stages for B (0-2)
CARRYINREG:INTEGER:=1; –- Pipeline stages for CARRYIN (0-1)
CARRYINSELREG:INTEGER:=1; –- Pipeline stages for CARRYINSEL (0-1)
CREG:INTEGER:=1; –- Pipeline stages for C (0-1)
DREG:INTEGER:=1; –- Pipeline stages for D (0-1)
INMODEREG:INTEGER:=1; –- Pipeline stages for INMODE (0-1)
MREG:INTEGER:=1; –- Multiplier pipeline stages (0-1)
OPMODEREG:INTEGER:=1; –- Pipeline stages for OPMODE (0-1)
PREG:INTEGER:=1); –- Number of pipeline stages for P (0-1)
port(
–- Cascade inputs: Cascade Ports
ACIN:in STD_LOGIC_VECTOR(29 downto 0):=(others=>'0'); –- 30-bit input: A cascade data
BCIN:in STD_LOGIC_VECTOR(17 downto 0):=(others=>'0'); –- 18-bit input: B cascade
CARRYCASCIN:in STD_LOGIC:='0'; –- 1-bit input: Cascade carry
MULTSIGNIN:in STD_LOGIC:='0'; –- 1-bit input: Multiplier sign cascade
PCIN:in STD_LOGIC_VECTOR(47 downto 0):=(others=>'0'); –- 48-bit input: P cascade
–- Control inputs: Control Inputs/Status Bits
ALUMODE:in STD_LOGIC_VECTOR(3 downto 0):=X"0"; –- 4-bit input: ALU control
CARRYINSEL:in STD_LOGIC_VECTOR(2 downto 0):="000"; –- 3-bit input: Carry select
CLK:in STD_LOGIC:='0'; –- 1-bit input: Clock
INMODE:in STD_LOGIC_VECTOR(4 downto 0):="00000"; –- 5-bit input: INMODE control
OPMODE:in STD_LOGIC_VECTOR(8 downto 0):="000110101"; –- 9-bit input: Operation mode - default is P<=C+A*B
–- Data inputs: Data Ports
A:in SFIXED; -– up to 30-bit input: A data
B:in SFIXED; -– up to 18-bit input: B data
C:in SFIXED; -– up to 48-bit input: C data
CARRYIN:in STD_LOGIC:='0'; –- 1-bit input: Carry-in
D:in SFIXED; –- up to 27-bit input: D data
–- Reset/Clock Enable inputs: Reset/Clock Enable Inputs
CEA1:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for 1st stage AREG
CEA2:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for 2nd stage AREG
CEAD:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for ADREG
CEALUMODE:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for ALUMODE
CEB1:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for 1st stage BREG
CEB2:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for 2nd stage BREG
CEC:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for CREG
CECARRYIN:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for CARRYINREG
CECTRL:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for OPMODEREG and CARRYINSELREG
CED:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for DREG
CEINMODE:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for INMODEREG
CEM:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for MREG
CEP:in STD_LOGIC:='1'; –- 1-bit input: Clock enable for PREG
RSTA:in STD_LOGIC:='0'; –- 1-bit input: Reset for AREG
RSTALLCARRYIN:in STD_LOGIC:='0'; –- 1-bit input: Reset for CARRYINREG
RSTALUMODE:in STD_LOGIC:='0'; –- 1-bit input: Reset for ALUMODEREG
RSTB:in STD_LOGIC:='0'; –- 1-bit input: Reset for BREG
RSTC:in STD_LOGIC:='0'; –- 1-bit input: Reset for CREG
RSTCTRL:in STD_LOGIC:='0'; –- 1-bit input: Reset for OPMODEREG and CARRYINSELREG
RSTD:in STD_LOGIC:='0'; –- 1-bit input: Reset for DREG and ADREG
RSTINMODE:in STD_LOGIC:='0'; –- 1-bit input: Reset for INMODEREG
RSTM:in STD_LOGIC:='0'; –- 1-bit input: Reset for MREG
RSTP:in STD_LOGIC:='0'; –- 1-bit input: Reset for PREG
–- Cascade outputs: Cascade Ports
ACOUT:out STD_LOGIC_VECTOR(29 downto 0); –- 30-bit output: A port cascade
BCOUT:out STD_LOGIC_VECTOR(17 downto 0); –- 18-bit output: B cascade
CARRYCASCOUT:out STD_LOGIC; –- 1-bit output: Cascade carry
MULTSIGNOUT:out STD_LOGIC; –- 1-bit output: Multiplier sign cascade
PCOUT:out STD_LOGIC_VECTOR(47 downto 0); –- 48-bit output: Cascade output
–- Control outputs: Control Inputs/Status Bits
OVERFLOW:out STD_LOGIC; –- 1-bit output: Overflow in add/acc
PATTERNBDETECT:out STD_LOGIC; –- 1-bit output: Pattern bar detect
PATTERNDETECT:out STD_LOGIC; –- 1-bit output: Pattern detect
UNDERFLOW:out STD_LOGIC; –- 1-bit output: Underflow in add/acc
–- Data outputs: Data Ports
CARRYOUT:out STD_LOGIC_VECTOR(3 downto 0); –- 4-bit output: Carry
P:out SFIXED; –- up to 48-bit output: Primary data
XOROUT:out STD_LOGIC_VECTOR(7 downto 0)); –- 8-bit output: XOR data
end entity; architecture WRAPPER of DSP48E2GW is
-– internal STD_LOGIC_VECTOR signals of the right range for DSP48E2 primitive instantiation
signal slvA:STD_LOGIC_VECTOR(29 downto 0);
signal slvB:STD_LOGIC_VECTOR(17 downto 0);
signal slvD:STD_LOGIC_VECTOR(26 downto 0);
signal slvC,slvP:STD_LOGIC_VECTOR(47 downto 0);
-– resize SFIXED and convert to STD_LOGIC_VECTOR
function SFIXED_TO_SLV_RESIZE(I:SFIXED;hi,lo:INTEGER) return STD_LOGIC_VECTOR is
variable O:STD_LOGIC_VECTOR(hi-lo downto 0);
begin
for K in O'range loop
if K<I'low-lo then
O(K):='0';
elsif K<I'length then
O(K):=I(K+lo);
else
O(K):=I(I'high);
end if;
end loop;
return O;
end;
-– convert STD_LOGIC_VECTOR to SFIXED and resize
function SLV_TO_SFIXED_RESIZE(I:STD_LOGIC_VECTOR;hi,lo:INTEGER;ofs:INTEGER:=0) return SFIXED is
variable O:SFIXED(hi downto lo);
begin
for K in O'range loop
if K<I'low+lo+ofs then
O(K):='0';
elsif K-lo-ofs<I'length then
O(K):=I(K-lo-ofs);
else
O(K):=I(I'high);
end if;
end loop;
return O;
end;
function MIN(X,Y:INTEGER) return INTEGER is
begin
if X<Y then
return X;
else
return Y;
end if;
end;
function LOC_STRING(X,Y:INTEGER) return STRING is
begin
if (X>=0) and (Y>=0) then
return "DSP48E2_X"&INTEGER'image(X)&"Y"&INTEGER'image(Y);
else
return "";
end if;
end;
attribute loc:STRING;
attribute loc of ds:label is LOC_STRING(X,Y);
constant AD_low:INTEGER:=MIN(A'low,D'low);
constant BD_low:INTEGER:=MIN(B'low,D'low);
constant CAD_low:INTEGER:=MIN(AD_low+B'low,P'low);
constant CBD_low:INTEGER:=MIN(BD_low+A'low,P'low);
begin
-– sign extend and zero pad inputs and convert from SFIXED to STD_LOGIC_VECTOR
slvA<=SFIXED_TO_SLV_RESIZE(A,AD_low+slvA'length-1,AD_low) when PREADDINSEL="A" else
SFIXED_TO_SLV_RESIZE(A,A'low+slvA'length-1,A'low); -– when PREADDINSEL="B"
slvB<=SFIXED_TO_SLV_RESIZE(B,B'low+slvB'length-1,B'low) when PREADDINSEL="A" else
SFIXED_TO_SLV_RESIZE(B,BD_low+slvB'length-1,BD_low); -– when PREADDINSEL="B"
slvC<=SFIXED_TO_SLV_RESIZE(C,CAD_low+slvC'length-1,CAD_low) when PREADDINSEL="A" else
SFIXED_TO_SLV_RESIZE(C,CBD_low+slvC'length-1,CBD_low); -– when PREADDINSEL="B"
slvD<=SFIXED_TO_SLV_RESIZE(D,AD_low+slvD'length-1,AD_low) when PREADDINSEL="A" else
SFIXED_TO_SLV_RESIZE(D,BD_low+slvD'length-1,BD_low); –- when PREADDINSEL="B"
-– actual DSP48E2 primitive instantiation
ds:DSP48E2 generic map(
-– Feature Control Attributes: Data Path Selection
AMULTSEL => AMULTSEL, –- Selects A input to multiplier (A, AD)
A_INPUT => A_INPUT, –- Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port)
BMULTSEL => BMULTSEL, –- Selects B input to multiplier (AD, B)
B_INPUT => B_INPUT, –- Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port)
PREADDINSEL => PREADDINSEL, –- Selects input to preadder (A, B)
RND => RND, –- Rounding Constant
USE_MULT => USE_MULT, –- Select multiplier usage (DYNAMIC, MULTIPLY, NONE)
USE_SIMD => USE_SIMD, –- SIMD selection (FOUR12, ONE48, TWO24)
USE_WIDEXOR => USE_WIDEXOR, –- Use the Wide XOR function (FALSE, TRUE)
XORSIMD => XORSIMD, –- Mode of operation for the Wide XOR (XOR12, XOR24_48_96)
-– Pattern Detector Attributes: Pattern Detection Configuration
AUTORESET_PATDET => AUTORESET_PATDET, –- NO_RESET, RESET_MATCH, RESET_NOT_MATCH
AUTORESET_PRIORITY => AUTORESET_PRIORITY, –- Priority of AUTORESET vs.CEP (CEP, RESET).
MASK => MASK, –- 48-bit mask value for pattern detect (1=ignore)
PATTERN => PATTERN, –- 48-bit pattern match for pattern detect
SEL_MASK => SEL_MASK, –- C, MASK, ROUNDING_MODE1, ROUNDING_MODE2
SEL_PATTERN => SEL_PATTERN, –- Select pattern value (C, PATTERN)
USE_PATTERN_DETECT => USE_PATTERN_DETECT, –- Enable pattern detect (NO_PATDET, PATDET)
-– Programmable Inversion Attributes: Specifies built-in programmable inversion on specific pins
IS_ALUMODE_INVERTED => IS_ALUMODE_INVERTED, –- Optional inversion for ALUMODE
IS_CARRYIN_INVERTED => IS_CARRYIN_INVERTED, –- Optional inversion for CARRYIN
IS_CLK_INVERTED => IS_CLK_INVERTED, –- Optional inversion for CLK
IS_INMODE_INVERTED => IS_INMODE_INVERTED, –- Optional inversion for INMODE
IS_OPMODE_INVERTED => IS_OPMODE_INVERTED, –- Optional inversion for OPMODE
IS_RSTALLCARRYIN_INVERTED => IS_RSTALLCARRYIN_INVERTED, –- Optional inversion for RSTALLCARRYIN
IS_RSTALUMODE_INVERTED => IS_RSTALUMODE_INVERTED, –- Optional inversion for RSTALUMODE
IS_RSTA_INVERTED => IS_RSTA_INVERTED, –- Optional inversion for RSTA
IS_RSTB_INVERTED => IS_RSTB_INVERTED, –- Optional inversion for RSTB
IS_RSTCTRL_INVERTED => IS_RSTCTRL_INVERTED, –- Optional inversion for RSTCTRL
IS_RSTC_INVERTED => IS_RSTC_INVERTED, –- Optional inversion for RSTC
IS_RSTD_INVERTED => IS_RSTD_INVERTED, –- Optional inversion for RSTD
IS_RSTINMODE_INVERTED => IS_RSTINMODE_INVERTED, –- Optional inversion for RSTINMODE
IS_RSTM_INVERTED => IS_RSTM_INVERTED, –- Optional inversion for RSTM
IS_RSTP_INVERTED => IS_RSTP_INVERTED, –- Optional inversion for RSTP
-– Register Control Attributes: Pipeline Register Configuration
ACASCREG => ACASCREG, –- Number of pipeline stages between A/ACIN and ACOUT (0-2)
ADREG => ADREG, –- Pipeline stages for pre-adder (0-1)
ALUMODEREG => ALUMODEREG, –- Pipeline stages for ALUMODE (0-1)
AREG => AREG, –- Pipeline stages for A (0-2)
BCASCREG => BCASCREG, –- Number of pipeline stages between B/BCIN and BCOUT (0-2)
BREG => BREG, –- Pipeline stages for B (0-2)
CARRYINREG => CARRYINREG, –- Pipeline stages for CARRYIN (0-1)
CARRYINSELREG => CARRYINSELREG, –- Pipeline stages for CARRYINSEL (0-1)
CREG => CREG, –- Pipeline stages for C (0-1)
DREG => DREG, –- Pipeline stages for D (0-1)
INMODEREG => INMODEREG, –- Pipeline stages for INMODE (0-1)
MREG => MREG, –- Multiplier pipeline stages (0-1)
OPMODEREG => OPMODEREG, –- Pipeline stages for OPMODE (0-1)
PREG => PREG) –- Number of pipeline stages for P (0-1)
port map(
– Cascade inputs: Cascade Ports
ACIN => ACIN, –- 30-bit input: A cascade data
BCIN => BCIN, –- 18-bit input: B cascade
CARRYCASCIN => CARRYCASCIN, –- 1-bit input: Cascade carry
MULTSIGNIN => MULTSIGNIN, –- 1-bit input: Multiplier sign cascade
PCIN => PCIN, –- 48-bit input: P cascade
-– Control inputs: Control Inputs/Status Bits
ALUMODE => ALUMODE, –- 4-bit input: ALU control
CARRYINSEL => CARRYINSEL, –- 3-bit input: Carry select
CLK => CLK, –- 1-bit input: Clock
INMODE => INMODE, –- 5-bit input: INMODE control
OPMODE => OPMODE, –- 9-bit input: Operation mode
-– Data inputs: Data Ports
A => slvA, –- 30-bit input: A data
B => slvB, –- 18-bit input: B data
C => slvC, –- 48-bit input: C data
CARRYIN => CARRYIN, –- 1-bit input: Carry-in
D => slvD, –- 27-bit input: D data
-– Reset/Clock Enable inputs: Reset/Clock Enable Inputs
CEA1 => CEA1, –- 1-bit input: Clock enable for 1st stage AREG
CEA2 => CEA2, –- 1-bit input: Clock enable for 2nd stage AREG
CEAD => CEAD, –- 1-bit input: Clock enable for ADREG
CEALUMODE => CEALUMODE, –- 1-bit input: Clock enable for ALUMODE
CEB1 => CEB1, –- 1-bit input: Clock enable for 1st stage BREG
CEB2 => CEB2, –- 1-bit input: Clock enable for 2nd stage BREG
CEC => CEC, –- 1-bit input: Clock enable for CREG
CECARRYIN => CECARRYIN, –- 1-bit input: Clock enable for CARRYINREG
CECTRL => CECTRL, –- 1-bit input: Clock enable for OPMODEREG and CARRYINSELREG
CED => CED, –- 1-bit input: Clock enable for DREG
CEINMODE => CEINMODE, –- 1-bit input: Clock enable for INMODEREG
CEM => CEM, –- 1-bit input: Clock enable for MREG
CEP => CEP, –- 1-bit input: Clock enable for PREG
RSTA => RSTA, –- 1-bit input: Reset for AREG
RSTALLCARRYIN => RSTALLCARRYIN, –- 1-bit input: Reset for CARRYINREG
RSTALUMODE => RSTALUMODE, –- 1-bit input: Reset for ALUMODEREG
RSTB => RSTB, –- 1-bit input: Reset for BREG
RSTC => RSTC, –- 1-bit input: Reset for CREG
RSTCTRL => RSTCTRL, –- 1-bit input: Reset for OPMODEREG and CARRYINSELREG
RSTD => RSTD, –- 1-bit input: Reset for DREG and ADREG
RSTINMODE => RSTINMODE, –- 1-bit input: Reset for INMODEREG
RSTM => RSTM, –- 1-bit input: Reset for MREG
RSTP => RSTP, –- 1-bit input: Reset for PREG
-– Cascade outputs: Cascade Ports
ACOUT => ACOUT, –- 30-bit output: A port cascade
BCOUT => BCOUT, –- 18-bit output: B cascade
CARRYCASCOUT => CARRYCASCOUT, –- 1-bit output: Cascade carry
MULTSIGNOUT => MULTSIGNOUT, –- 1-bit output: Multiplier sign cascade
PCOUT => PCOUT, –- 48-bit output: Cascade output
-– Control outputs: Control Inputs/Status Bits
OVERFLOW => OVERFLOW, –- 1-bit output: Overflow in add/acc
PATTERNBDETECT => PATTERNBDETECT, –- 1-bit output: Pattern bar detect
PATTERNDETECT => PATTERNDETECT, –- 1-bit output: Pattern detect
UNDERFLOW => UNDERFLOW, –- 1-bit output: Underflow in add/acc
-– Data outputs: Data Ports
CARRYOUT => CARRYOUT, –- 4-bit output: Carry
P => slvP, –- 48-bit output: Primary data
XOROUT => XOROUT); –- 8-bit output: XOR data – resize output result
P<=SLV_TO_SFIXED_RESIZE(slvP,P'high,P'low,A'low+B'low-P'low);
end WRAPPER;
It is quite big, this is the kind of primitive instantiation verbosity that the wrapper will help us avoid. The wrapper is called DSP48E2GW and it has exactly the same generics and ports as the actual primitive, with a few notable exceptions. All generics and input ports have sensible default values, for example, all clock enable inputs are tied high, all synchronous resets are tied low, all internal pipeline registers are enabled and so on. You can even change these default values if you would prefer slightly different ones. There are two new generics X and Y, if you omit them the -1 default values will ensure there is no LOC constraint but if you assign them both positive values the DSP48E2 primitive will be placed at that particular device location. The five ports A, B, C, D and P are not STD_LOGIC_VECTOR but instead unconstrained SFIXED, which means that they cannot be omitted, even when not used - just define a constant SFIXED signal of proper range, give it a value of 0.0 and tie the unused ports low. Everything else is exactly as the DSP48E2 primitive itself. Inside the wrapper a couple of functions are used to convert between SFIXED and STD_LOGIC_VECTOR, sign extending and zero padding or truncating when needed. The binary point alignment between these five SFIXED ports is also handled automatically by the wrapper.
This DSP48E2 generic wrapper module is not trivial and is the result of a lot of frustration with the instantiation of the raw primitive. I no longer instantiate DSP48E2 primitives in my designs as such, when not using behavioral inference I exclusively use this generic wrapper and I strongly suggest you do the same or at least develop your own along the same lines.
In the next post we will see how to use this DSP48 generic wrapper to create the non-symmetric and symmetric FIR examples we tried to infer in the previous posts with mixed results.
Back to the top: The Art of FPGA Design
Top Comments