diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/BDELAY.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/BDELAY.vhd index 6cb46fb12..3ef0bc1f6 100755 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/BDELAY.vhd +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/BDELAY.vhd @@ -4,7 +4,10 @@ library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.NUMERIC_STD.all; -use work.TYPES_PKG.all; +--CB use work.TYPES_PKG.all; +use work.fixed_pkg.all; -- VHDL-2008 IEEE standard SFIXED type and functions +use work.cfixed_pkg.all; -- user package, SFIXED_VECTOR, SFIXED_MATRIX and many other goodies +--CB entity BDELAY is generic(SIZE:NATURAL:=1; -- SIZE has a default value of 1 and cannot be negative, this would require traveling back in time @@ -27,6 +30,7 @@ begin -- l2n:if (SIZE>=66 and SIZE<=65+33) or (SIZE>=130 and SIZE<=129+65+33) or (SIZE>=258 and SIZE<=257+129+65+33) or (SIZE>513) generate l2n:if DDR and (SIZE>3) generate signal ID:BOOLEAN:=FALSE; + signal OB:BOOLEAN:=FALSE; signal T,TD,SEL:STD_LOGIC:='0'; type TBV is array(0 to SIZE/2-1) of BOOLEAN; signal OE,OO:TBV:=(others=>FALSE); -- delay line signal is SIZE/2 in length @@ -38,7 +42,7 @@ begin end; else generate process(CLK) - begin + begin if rising_edge(CLK) then ID<=I after 1 ps; end if; @@ -71,12 +75,28 @@ begin TD<=T after 1 ps; SEL<=TD xor T after 1 ps; if SEL='0' then - O<=OE(OE'high); + OB<=OE(OE'high); + -- BF O<=OE(OE'high); else - O<=OO(OO'high); + OB<=OO(OO'high); + -- BF O<=OO(OO'high); end if; end if; end process; + + --BF Added for DDR Even Filters + o0:if SIZE mod 2=1 generate + O<=OB; + end; + else generate + process(CLK) + begin + if rising_edge(CLK) then + O<=OB after 1 ps; + end if; + end process; + end generate; + end; elsif (SIZE>=66 and SIZE<=65+33) or (SIZE>=130 and SIZE<=129+65+33) or (SIZE>=258 and SIZE<=257+129+65+33) generate signal IO:BOOLEAN:=FALSE; @@ -158,4 +178,4 @@ begin O<=D(D'high); end; end generate; -end TEST; \ No newline at end of file +end TEST; diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/DSPx8_WRAPPER.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/DSPx8_WRAPPER.vhd index 704c01706..a6c2a0e5d 100755 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/DSPx8_WRAPPER.vhd +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/DSPx8_WRAPPER.vhd @@ -4,7 +4,10 @@ library IEEE; use IEEE.STD_LOGIC_1164.all; -use work.TYPES_PKG.all; +--CB use work.TYPES_PKG.all; +use work.fixed_pkg.all; -- VHDL-2008 IEEE standard SFIXED type and functions +use work.cfixed_pkg.all; -- user package, SFIXED_VECTOR, SFIXED_MATRIX and many other goodies +--CB library unisims_ver; library UNISIM; @@ -81,11 +84,11 @@ entity DSPx8_WRAPPER is INMODE:in STD_LOGIC_VECTOR(4 downto 0):=5x"00"; -- 5-bit input: INMODE control OPMODE:in STD_LOGIC_VECTOR(8 downto 0):=9x"035"; -- 9-bit input: Operation mode - default is P<=C+A*B -- Data inputs: Data Ports - A:in SFIXED; -- 30/34-bit input: A data - B:in SFIXED; -- 18/24-bit input: B data - C:in SFIXED; -- 48/58-bit input: C data + A:in SFIXED:="0"; -- 30/34-bit input: A data + B:in SFIXED:="0"; -- 18/24-bit input: B data + C:in SFIXED:="0"; -- 48/58-bit input: C data CARRYIN:in STD_LOGIC:='0'; -- 1-bit input: Carry-in - D:in SFIXED; -- 25/27-bit input: D data + D:in SFIXED:="0"; -- 25/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 diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/Images/SSR_FIR_screen_shot.PNG b/Examples/HDL/Digital_Filtering/SSR_FIR/Images/SSR_FIR_screen_shot.PNG old mode 100755 new mode 100644 index 36b8af84d..05076c456 Binary files a/Examples/HDL/Digital_Filtering/SSR_FIR/Images/SSR_FIR_screen_shot.PNG and b/Examples/HDL/Digital_Filtering/SSR_FIR/Images/SSR_FIR_screen_shot.PNG differ diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/README.html b/Examples/HDL/Digital_Filtering/SSR_FIR/README.html index 27672f0cc..c0381df55 100644 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/README.html +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/README.html @@ -5,7 +5,7 @@ -

High Speed SSR FIR

+

High Speed SSR FIR — Direct and Transpose Forms

Open Example -

This reference design can be used as a starting design point when efficient implementations of very high data rate (over 1 Gsps) Single Rate FIRs are required. This PL based design can be used in any modern AMD device - 7-Series/UltraScale/UltraScale+/Versal.
+

This reference design can be used as a starting design point when efficient implementations of very high data rate (over 1 Gsps) Single Rate FIRs are required. This PL based design can be used in any modern AMD device - 7-Series/UltraScale/UltraScale+/Versal.


-




-This Single Rate SSR FIR reference design illustrates a number of advanced Vitis Model Composer design techniques:

+



+

The model now contains two functionally-equivalent FIR architectures side by side, both driven by the same stimulus. The Direct form is checked sample-by-sample against a golden floating-point reference, and the Direct and Transpose outputs are overlaid in the Output spectrum analyzer to confirm the two architectures produce equivalent results (matching to within one LSB):

+

The two architectures are functionally equivalent, with outputs agreeing to within one output LSB (2^-16). Each form is independently verified against the golden floating-point reference. The two forms have different pipeline latencies (direct = TAPS+2 clocks, transpose = 3×SSR+2 clocks), so the transpose output leads the direct output in time.

+

To generate code for one architecture, point the Vitis Model Composer Hub block's subsystem selection at DUT (direct) or DUT_T (transpose).

+

Why two forms?

+

The critical path of the direct form grows with TAPS (the PCOUT cascade), while the transpose form's critical path is one DSP multiply-accumulate that is independent of TAPS. This lets the transpose form close timing at high SSR and large TAPS where the direct form's cascade does not.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDirect FormTranspose Form
SSR range1, 2, 4, 8, 16 (2^n)1, 2, 4, 8, 16 (2^n)
Fmax, Versal (SSR≤4)~822 MHz (device ceiling)~834–841 MHz
Fmax, SSR=8~670–740 MHz~834 MHz
Latency (SSR>1, NS)TAPS + 23×SSR + 2
PCOUT/PREG chain depthTAPS hopsTAPS/SSR hops per lane
DSP count, NSSSR × TAPSSSR × TAPS
+

Rule of thumb: use the direct form for SSR 2–4 with TAPS ≤ 64 (shorter latency, simplest dataflow); use the transpose form for SSR ≥ 8, or for TAPS > 64 at SSR = 4, or whenever a TAPS-independent (short, fixed) latency is required.

+

Model configuration

+

The model is parameterized through Model Settings → Model Properties → Callbacks → InitFcn:

+
SSR  = 8;               % clocks per sample — MUST be a power of two (1,2,4,8,16,...)
+TAPS = 64;              % number of FIR coefficients
+Ts   = 1/SSR;           % input sample time; SSR*Ts must equal the Simulink system period (1)
+FCOEFF = fir1(TAPS-1,0.25);  % shared coefficient set (hardware + golden reference)
+
+

Notes:

+ -

This design only works in Vivado 2022.1 or later.


Copyright (c) 2026 Advanced Micro Devices, Inc.

diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/README.md b/Examples/HDL/Digital_Filtering/SSR_FIR/README.md index 350d1147a..eb322b546 100644 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/README.md +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/README.md @@ -1,24 +1,54 @@ -# High Speed SSR FIR +# High Speed SSR FIR — Direct and Transpose Forms -This reference design can be used as a starting design point when efficient implementations of very high data rate (over 1 Gsps) Single Rate FIRs are required. This PL based design can be used in any modern AMD device - 7-Series/UltraScale/UltraScale+/Versal. +This reference design can be used as a starting design point when efficient implementations of very high data rate (over 1 Gsps) Single Rate FIRs are required. This PL based design can be used in any modern AMD device - 7-Series/UltraScale/UltraScale+/Versal.

- +

-This Single Rate SSR FIR reference design illustrates a number of advanced Vitis Model Composer design techniques: -- This is a fully parameterizable design - you can control the filter order N, the number of clocks per sample SSR, the filter type (non/even/odd-symmetric), the input, output and coefficient fixed point quantization; you can set SSR and TAPS in Model Settings/Model Properties/Callbacks/InitFcnall the rest of the SSR FIR parameters can be set in the Model Composer block dialog -- If desired, you can floorplan the DSP blocks directly from Vitis Model Composer -- This is achieved through the use of Model Composer HDL Black Box flow, all these features are implemented in the underlying VHDL-2008 code; the reference design shows how to pass ports and parameters with generic sizes between Model Composer and VHDL code; the underlying VHDL-2008 code shows how to use arbitrary precision fixed point types, recursive component instantiations, how to instantiate DSP primitives independent of FPGA family (7-Series/UltraScale+/Versal) and floorplan them from VHDL code -- The Vitis Model Composer design includes a top level Simulink self-checking testbench, which compares the fixed point HDL Black Box based implementation to a golden reference floating point model -- This is a very high speed design, clock frequencies up to the maximum data sheet values of 741/891/1150 MHz in the fastest speed grade 7-Series/UltraScale+/Versal devices are possible -- The design also shows a technique to avoid the Versal 795/824/872 MHz fMAX limitation due to SRL16 minimum pulse width spec -- The VHDL code inside the Model Composer HDL Black Box can be instantiated stand alone in a Vivado RTL project. MATLAB and Vitis Model Composer are used here to illustrate the flow and make functional verification easier. +The model now contains **two functionally-equivalent FIR architectures side by side**, both driven by the same stimulus. The Direct form is checked sample-by-sample against a golden floating-point reference, and the Direct and Transpose outputs are overlaid in the **Output** spectrum analyzer to confirm the two architectures produce equivalent results (matching to within one LSB): + +- **`DUT` – Direct Form** (`SSR_FIR.vhd`): partial products cascade from the first to the last tap through the DSP `PCOUT` chain. The cascade is `TAPS` deep. +- **`DUT_T` – Transpose Form** (`TRANSPOSE_SSR_FIR.vhd`): the input is broadcast to all DSPs and partial sums accumulate stage-by-stage through the DSP `PREG`/`PCOUT` registers. For SSR>1 each of the SSR lanes implements a transposed sub-FIR, so the cascade is only **`TAPS/SSR` deep per lane**. + +The two architectures are **functionally equivalent, with outputs agreeing to within one output LSB (2^-16)**. Each form is independently verified against the golden floating-point reference. The two forms have different pipeline latencies (direct = TAPS+2 clocks, transpose = 3×SSR+2 clocks), so the transpose output leads the direct output in time. + +To generate code for one architecture, point the **Vitis Model Composer Hub** block's subsystem selection at `DUT` (direct) or `DUT_T` (transpose). + +## Why two forms? + +The critical path of the direct form grows with `TAPS` (the `PCOUT` cascade), while the transpose form's critical path is one DSP multiply-accumulate that is **independent of `TAPS`**. This lets the transpose form close timing at high `SSR` and large `TAPS` where the direct form's cascade does not. + +| Property | **Direct Form** | **Transpose Form** | +|---|---|---| +| SSR range | 1, 2, 4, 8, 16 (2^n) | 1, 2, 4, 8, 16 (2^n) | +| Fmax, Versal (SSR≤4) | ~822 MHz (device ceiling) | ~834–841 MHz | +| Fmax, SSR=8 | ~670–740 MHz | ~834 MHz | +| Latency (SSR>1, NS) | TAPS + 2 | 3×SSR + 2 | +| PCOUT/PREG chain depth | TAPS hops | **TAPS/SSR hops per lane** | +| DSP count, NS | SSR × TAPS | SSR × TAPS | + +**Rule of thumb:** use the **direct form** for SSR 2–4 with TAPS ≤ 64 (shorter latency, simplest dataflow); use the **transpose form** for SSR ≥ 8, or for TAPS > 64 at SSR = 4, or whenever a TAPS-independent (short, fixed) latency is required. + +## Model configuration + +The model is parameterized through `Model Settings → Model Properties → Callbacks → InitFcn`: + +```matlab +SSR = 8; % clocks per sample — MUST be a power of two (1,2,4,8,16,...) +TAPS = 64; % number of FIR coefficients +Ts = 1/SSR; % input sample time; SSR*Ts must equal the Simulink system period (1) +FCOEFF = fir1(TAPS-1,0.25); % shared coefficient set (hardware + golden reference) +``` + +Notes: -This design only works in Vivado 2022.1 or later. +- **`SSR` must be a power of two.** Both cores assert this; non-power-of-2 SSR is not supported by the SSR>1 accumulation chain. +- **`Ts = 1/SSR`** keeps the buffered input frame rate aligned with the Vitis Model Composer Hub's Simulink system period so the golden-vs-DUT comparison lines up. Changing `SSR` automatically rescales `Ts`. +- The remaining FIR parameters (input/output/coefficient fixed-point ranges, rounding, symmetry, optional DSP floorplanning) are set on the `SSRFIR` Model Composer block mask inside each DUT. -------------- Copyright (c) 2026 Advanced Micro Devices, Inc. diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/SDELAY.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/SDELAY.vhd index 8e08543e7..0d311f7d6 100755 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/SDELAY.vhd +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/SDELAY.vhd @@ -1,212 +1,223 @@ --- Copyright (C) 2024, Advanced Micro Devices, Inc. - -library IEEE; -use IEEE.STD_LOGIC_1164.all; -use IEEE.NUMERIC_STD.ALL; - -use work.TYPES_PKG.all; - -entity SDELAY is - generic(SIZE:NATURAL:=1; -- SIZE has a default value of 1 and cannot be negative, this would require traveling back in time - DDR:BOOLEAN:=FALSE; -- DDR implementation for Versal -3 clock rates over 872MHz - STYLE:STRING:="srl_reg"); -- acceptable values are "reg", "srl", "srl_reg", "reg_srl", "reg_srl_reg" or "block" - port(CLK:in STD_LOGIC:='0'; -- an input port with a default value can be left unconnected, this could be useful when SIZE=0 (a wire) - CLKH:in STD_LOGIC:='0'; -- half rate clock, an input port with a default value can be left unconnected, this could be useful when SIZE=0 (a wire) - CE:in STD_LOGIC:='1'; -- clock enable, if not used can be left unconnected --- I:in SIGNED; --- O:out SIGNED); - I:in SFIXED; - O:out SFIXED); - attribute secure_config:STRING; - attribute secure_config of SDELAY:entity is "PROTECT"; -end SDELAY; - -architecture TEST of SDELAY is - attribute syn_hier:STRING; - attribute syn_hier of all:architecture is "hard"; - attribute srl_style:STRING; -begin - l2n:if DDR and (SIZE<4) generate --- type TD is array(0 to SIZE) of SIGNED(I'range); - type TD is array(0 to SIZE) of SFIXED(I'range); - signal D:TD:=(others=>(others=>'0')); -- delay line signal is SIZE+1 in length - attribute shreg_extract:STRING; - attribute shreg_extract of D:signal is "no"; --- attribute ram_style:STRING; --- attribute ram_style of D:signal is "registers"; - begin - D(D'low)<=I; - lk:for K in 1 to SIZE generate - begin - process(CLK) - begin - if rising_edge(CLK) then - if CE='1' then - D(K)<=D(K-1); - end if; - end if; - end process; - end generate; --- O<=RESIZE(D(D'high),O'length); - O<=RESIZE(D(D'high),O); - end; - elsif DDR and (SIZE>3) generate - signal ID:SFIXED(I'range):=(others=>'0'); - signal T,TD,SEL:STD_LOGIC:='0'; - type TSV is array(0 to SIZE/2-1) of SFIXED(I'range); - signal OE,OO:TSV:=(others=>(others=>'0')); -- delay line signal is SIZE/2 in length - signal iO:SFIXED(O'range):=(others=>'0'); - attribute srl_style of OE:signal is STYLE; - attribute srl_style of OO:signal is STYLE; - begin - i0:if SIZE mod 2=0 generate - ID<=I after 1 ps; - end; - else generate - process(CLK) - begin - if rising_edge(CLK) then - if CE='1' then - ID<=I after 1 ps; - end if; - end if; - end process; - end generate; - process(CLKH) - begin - if rising_edge(CLKH) then - for K in OE'low to OE'high loop - if CE='1' then - if K=OE'low then - OE(K)<=ID after 1 ps; - else - OE(K)<=OE(K-1) after 1 ps; - end if; - end if; - end loop; - end if; - end process; - - process(CLKH) - begin - if falling_edge(CLKH) then - for K in OO'low to OO'high loop - if CE='1' then - if K=OO'low then - OO(K)<=ID after 1 ps; - else - OO(K)<=OO(K-1) after 1 ps; - end if; - end if; - end loop; - end if; - end process; - - process(CLKH) - begin - if rising_edge(CLKH) then - T<=not T after 1 ps; - end if; - end process; - - process(CLK) - begin - if rising_edge(CLK) then - TD<=T after 1 ps; - SEL<=TD xor T after 1 ps; - if SEL='0' then - iO<=OE(OE'high); - else - iO<=OO(OO'high); - end if; - end if; - end process; - O<=iO; - end; - elsif (SIZE>=66 and SIZE<=65+33) or (SIZE>=130 and SIZE<=129+65+33) or (SIZE>=258 and SIZE<=257+129+65+33) generate --- signal IO:SIGNED(I'range):=(others=>'0'); - signal IO:SFIXED(I'range):=(others=>'0'); - constant HSIZE:INTEGER:=2**(LOG2(SIZE)-1)+1; - begin - d2n:entity work.SDELAY generic map(SIZE=>HSIZE, -- 65/129/257 - STYLE=>STYLE) - port map(CLK=>CLK, - CE=>CE, - I=>I, - O=>IO); - d1:entity work.SDELAY generic map(SIZE=>SIZE-HSIZE, -- 2..34/2..66/2..130 - STYLE=>STYLE) - port map(CLK=>CLK, - CE=>CE, - I=>IO, - O=>O); - end; - elsif l513: (SIZE>17 and SIZE<=513) generate -- infer a counter plus distributed RAM implementation - signal A:UNSIGNED(LOG2(SIZE-1)-1 downto 0):=(others=>'0'); --- type TMEM is array(0 to 2**A'length-1) of SIGNED(I'range); - type TMEM is array(0 to 2**A'length-1) of SFIXED(I'range); - signal MEM:TMEM:=(others=>(others=>'0')); - attribute ram_style:STRING; - attribute ram_style of MEM:signal is "distributed"; --- signal iO:SIGNED(O'range):=(others=>'0'); - signal iO:SFIXED(O'range):=(others=>'0'); - begin - process(CLK) - begin - if rising_edge(CLK) then - if CE='1' then - if A=SIZE-2 then - A<=(others=>'0'); - else - A<=A+1; - end if; - MEM(TO_INTEGER(A))<=I; --- iO<=RESIZE(MEM(TO_INTEGER(A)),O'length); - iO<=RESIZE(MEM(TO_INTEGER(A)),O); - end if; - end if; - end process; - O<=iO; - end; - elsif (SIZE>513) generate --- signal IO:SIGNED(I'range):=(others=>'0'); - signal IO:SFIXED(I'range):=(others=>'0'); - constant HSIZE:INTEGER:=513; - begin - d2n:entity work.SDELAY generic map(SIZE=>HSIZE, -- 513 - STYLE=>STYLE) - port map(CLK=>CLK, - CE=>CE, - I=>I, - O=>IO); - d1:entity work.SDELAY generic map(SIZE=>SIZE-HSIZE, -- SIZE-513 - STYLE=>STYLE) - port map(CLK=>CLK, - CE=>CE, - I=>IO, - O=>O); - end; - else generate -- otherwise use the behavioral implementation and the STYLE generic --- type TD is array(0 to SIZE) of SIGNED(I'range); - type TD is array(0 to SIZE) of SFIXED(I'range); - signal D:TD:=(others=>(others=>'0')); -- delay line signal is SIZE+1 in length - attribute srl_style:STRING; - attribute srl_style of D:signal is STYLE; - begin - D(D'low)<=I; - lk:for K in 1 to SIZE generate - begin - process(CLK) - begin - if rising_edge(CLK) then - if CE='1' then - D(K)<=D(K-1); - end if; - end if; - end process; - end generate; --- O<=RESIZE(D(D'high),O'length); - O<=RESIZE(D(D'high),O); - end; - end generate; +-- Copyright © Advanced Micro Devices, Inc., or its affiliates. All rights reserved. +-- Portions of this content consists of AI generated content. +-- You may not use this software and documentation (if any) (collectively, the "Materials") +-- except in compliance with the terms and conditions of the Software License Agreement +-- included with the Materials or otherwise as set forth in writing and signed by you and +-- an authorized signatory of AMD. If you do not have a copy of the Software License +-- Agreement, contact your AMD representative for a copy. You agree that you will not +-- reverse engineer or decompile the Materials, in whole or in part, except as allowed by +-- applicable law. +-- THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +-- REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +library IEEE; +use IEEE.STD_LOGIC_1164.all; +use IEEE.NUMERIC_STD.ALL; + +use work.FIXED_PKG.all; +use work.CFIXED_PKG.all; + +entity SDELAY is + generic(SIZE:NATURAL:=1; -- SIZE has a default value of 1 and cannot be negative, this would require traveling back in time + DDR:BOOLEAN:=FALSE; -- DDR implementation for Versal -3 clock rates over 872MHz + STYLE:STRING:="srl_reg"); -- acceptable values are "reg", "srl", "srl_reg", "reg_srl", "reg_srl_reg" or "block" + port(CLK:in STD_LOGIC:='0'; -- an input port with a default value can be left unconnected, this could be useful when SIZE=0 (a wire) + CLKH:in STD_LOGIC:='0'; -- half rate clock, an input port with a default value can be left unconnected, this could be useful when SIZE=0 (a wire) + CE:in STD_LOGIC:='1'; -- clock enable, if not used can be left unconnected +-- I:in SIGNED; +-- O:out SIGNED); + I:in SFIXED; + O:out SFIXED); + attribute secure_config:STRING; + attribute secure_config of SDELAY:entity is "PROTECT"; +end SDELAY; + +architecture TEST of SDELAY is + attribute syn_hier:STRING; + attribute syn_hier of all:architecture is "hard"; + attribute srl_style:STRING; +begin + l2n:if DDR and (SIZE<4) generate +-- type TD is array(0 to SIZE) of SIGNED(I'range); + type TD is array(0 to SIZE) of SFIXED(I'range); + signal D:TD:=(others=>(others=>'0')); -- delay line signal is SIZE+1 in length + attribute shreg_extract:STRING; + attribute shreg_extract of D:signal is "no"; +-- attribute ram_style:STRING; +-- attribute ram_style of D:signal is "registers"; + begin + D(D'low)<=I; + lk:for K in 1 to SIZE generate + begin + process(CLK) + begin + if rising_edge(CLK) then + if CE='1' then + D(K)<=D(K-1); + end if; + end if; + end process; + end generate; +-- O<=RESIZE(D(D'high),O'length); + O<=RESIZE(D(D'high),O); + end; + elsif DDR and (SIZE>3) generate + signal ID:SFIXED(I'range):=(others=>'0'); + signal T,TD,SEL:STD_LOGIC:='0'; + type TSV is array(0 to SIZE/2-1) of SFIXED(I'range); + signal OE,OO:TSV:=(others=>(others=>'0')); -- delay line signal is SIZE/2 in length + signal iO:SFIXED(O'range):=(others=>'0'); + attribute srl_style of OE:signal is STYLE; + attribute srl_style of OO:signal is STYLE; + begin + i0:if SIZE mod 2=0 generate + ID<=I after 1 ps; + end; + else generate + process(CLK) + begin + if rising_edge(CLK) then + if CE='1' then + ID<=I after 1 ps; + end if; + end if; + end process; + end generate; + process(CLKH) + begin + if rising_edge(CLKH) then + for K in OE'low to OE'high loop + if CE='1' then + if K=OE'low then + OE(K)<=ID after 1 ps; + else + OE(K)<=OE(K-1) after 1 ps; + end if; + end if; + end loop; + end if; + end process; + + process(CLKH) + begin + if falling_edge(CLKH) then + for K in OO'low to OO'high loop + if CE='1' then + if K=OO'low then + OO(K)<=ID after 1 ps; + else + OO(K)<=OO(K-1) after 1 ps; + end if; + end if; + end loop; + end if; + end process; + + process(CLKH) + begin + if rising_edge(CLKH) then + T<=not T after 1 ps; + end if; + end process; + + process(CLK) + begin + if rising_edge(CLK) then + TD<=T after 1 ps; + SEL<=TD xor T after 1 ps; + if SEL='0' then + iO<=OE(OE'high); + else + iO<=OO(OO'high); + end if; + end if; + end process; + O<=iO; + end; + elsif (SIZE>=66 and SIZE<=65+33) or (SIZE>=130 and SIZE<=129+65+33) or (SIZE>=258 and SIZE<=257+129+65+33) generate +-- signal IO:SIGNED(I'range):=(others=>'0'); + signal IO:SFIXED(I'range):=(others=>'0'); + constant HSIZE:INTEGER:=2**(LOG2(SIZE)-1)+1; + begin + d2n:entity work.SDELAY generic map(SIZE=>HSIZE, -- 65/129/257 + STYLE=>STYLE) + port map(CLK=>CLK, + CE=>CE, + I=>I, + O=>IO); + d1:entity work.SDELAY generic map(SIZE=>SIZE-HSIZE, -- 2..34/2..66/2..130 + STYLE=>STYLE) + port map(CLK=>CLK, + CE=>CE, + I=>IO, + O=>O); + end; + elsif l513: (SIZE>17 and SIZE<=513) generate -- infer a counter plus distributed RAM implementation + signal A:UNSIGNED(LOG2(SIZE-1)-1 downto 0):=(others=>'0'); +-- type TMEM is array(0 to 2**A'length-1) of SIGNED(I'range); + type TMEM is array(0 to 2**A'length-1) of SFIXED(I'range); + signal MEM:TMEM:=(others=>(others=>'0')); + attribute ram_style:STRING; + attribute ram_style of MEM:signal is "distributed"; +-- signal iO:SIGNED(O'range):=(others=>'0'); + signal iO:SFIXED(O'range):=(others=>'0'); + begin + process(CLK) + begin + if rising_edge(CLK) then + if CE='1' then + if A=SIZE-2 then + A<=(others=>'0'); + else + A<=A+1; + end if; + MEM(TO_INTEGER(A))<=I; +-- iO<=RESIZE(MEM(TO_INTEGER(A)),O'length); + iO<=RESIZE(MEM(TO_INTEGER(A)),O); + end if; + end if; + end process; + O<=iO; + end; + elsif (SIZE>513) generate +-- signal IO:SIGNED(I'range):=(others=>'0'); + signal IO:SFIXED(I'range):=(others=>'0'); + constant HSIZE:INTEGER:=513; + begin + d2n:entity work.SDELAY generic map(SIZE=>HSIZE, -- 513 + STYLE=>STYLE) + port map(CLK=>CLK, + CE=>CE, + I=>I, + O=>IO); + d1:entity work.SDELAY generic map(SIZE=>SIZE-HSIZE, -- SIZE-513 + STYLE=>STYLE) + port map(CLK=>CLK, + CE=>CE, + I=>IO, + O=>O); + end; + else generate -- otherwise use the behavioral implementation and the STYLE generic +-- type TD is array(0 to SIZE) of SIGNED(I'range); + type TD is array(0 to SIZE) of SFIXED(I'range); + signal D:TD:=(others=>(others=>'0')); -- delay line signal is SIZE+1 in length + attribute srl_style:STRING; + attribute srl_style of D:signal is STYLE; + begin + D(D'low)<=I; + lk:for K in 1 to SIZE generate + begin + process(CLK) + begin + if rising_edge(CLK) then + if CE='1' then + D(K)<=D(K-1); + end if; + end if; + end process; + end generate; +-- O<=RESIZE(D(D'high),O'length); + O<=RESIZE(D(D'high),O); + end; + end generate; end TEST; \ No newline at end of file diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.slx b/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.slx index 42e390644..8d4406fad 100644 Binary files a/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.slx and b/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.slx differ diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.vhd index 9cf53d6d3..d704e9252 100755 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.vhd +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR.vhd @@ -1,31 +1,91 @@ --- Copyright (C) 2024, Advanced Micro Devices, Inc. - +----------------------------------------------------------------------------------------------- +-- © Copyright (C) 2024, Advanced Micro Devices, Inc. All rights reserved. +-- This file contains confidential and proprietary information of AMD, Inc. and is +-- protected under U.S. and international copyright and other intellectual property laws. +----------------------------------------------------------------------------------------------- +-- +-- Disclaimer: +-- This disclaimer is not a license and does not grant any rights to the materials +-- distributed herewith. Except as otherwise provided in a valid license issued to you +-- by Xilinx, and to the maximum extent permitted by applicable law: (1) THESE MATERIALS +-- ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, AND AMD HEREBY DISCLAIMS ALL +-- WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED +-- TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR +-- PURPOSE; and (2) AMD shall not be liable (whether in contract or tort, including +-- negligence, or under any other theory of liability) for any loss or damage of any +-- kind or nature related to, arising under or in connection with these materials, +-- including for any direct, or any indirect, special, incidental, or consequential +-- loss or damage (including loss of data, profits, goodwill, or any type of loss or +-- damage suffered as a result of any action brought by a third party) even if such +-- damage or loss was reasonably foreseeable or Xilinx had been advised of the +-- possibility of the same. +-- +-- CRITICAL APPLICATIONS +-- AMD products are not designed or intended to be fail-safe, or for use in any +-- application requiring fail-safe performance, such as life-support or safety devices +-- or systems, Class III medical devices, nuclear facilities, applications related to +-- the deployment of airbags, or any other applications that could lead to death, +-- personal injury, or severe property or environmental damage (individually and +-- collectively, "Critical Applications"). Customer assumes the sole risk and +-- liability of any use of AMD products in Critical Applications, subject only to +-- applicable laws and regulations governing limitations on product liability. +-- +-- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES. +-- +-- Contact: e-mail ben.fell@amd.com - this design is not supported by AMD +-- Worldwide Technical Support (WTS), for support please contact the author +-- ____ ____ +-- / /\/ / +-- /___/ \ / Vendor: AMD Inc. +-- \ \ \/ Version: 1.1 +-- \ \ Filename: SSR_FIR.vhd +-- / / Date Last Modified: 23 Oct 2024 +-- /___/ /\ Date Created: +-- \ \ / \ +-- \___\/\___\ +-- +-- Device: Any 7-Series, UltraScale or Versal AMD FPGA +-- Author: Catalin Baetoniu +-- Entity Name: SSR_FFT +-- Purpose: Generic SSR Single Rate FIR, any SSR, any number of taps, non/even/odd/anti symmetric +-- +-- Revision History: +-- Revision 1.1 2024-Oct-23 Full VHDL-2008 version +-------------------------------------------------------------------------------- +-- +-- Module Description: +-- +-------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; -use work.TYPES_PKG.all; +use work.fixed_pkg.all; -- VHDL-2008 IEEE standard SFIXED type and functions +use work.cfixed_pkg.all; -- user package, SFIXED_VECTOR, SFIXED_MATRIX and many other goodies entity SSR_FIR is generic(OX,OY:INTEGER:=-2147483648; -- Coordinates of the lower left corner DSPx8, use -2147483648 if you do not want floorplanning FAMILY:INTEGER:=3; -- use 1 for 7-series, 2 for US/US+ and 3 for Versal DDR:BOOLEAN:=FALSE; -- DDR implementation for Versal -3 clock rates over 872MHz - COEFFICIENT:REAL_VECTOR; -- Number of Rows=COEFFICIENT'length + COEFFICIENT:REAL_MATRIX; -- Number of Rows=COEFFICIENT'length C_HIGH:INTEGER:=0; C_LOW:INTEGER:=-17; ROUNDING:BOOLEAN:=FALSE; + ANTI:BOOLEAN:=FALSE; -- for anti-symmetric filters, when ANTI=TRUE and SYMMETRY="OS" the center tap must be 0.0 SYMMETRY:STRING:="ES"); -- Filter Symmetry, use "ES" for Even-Symmetric, "OS" for Odd-Symmetric and "NS" for Non-Symmetric port(CLK:in STD_LOGIC; CLKH:in STD_LOGIC:='0'; -- if DDR=FALSE can be left unconnected I:in SFIXED_VECTOR; VI:in BOOLEAN:=TRUE; + CSA:in UNSIGNED:="0"; -- Coefficient Set Address, CSA'length must be equal to LOG2(COEFFICIENT'length(1)) O:out SFIXED_VECTOR; VO:out BOOLEAN); end SSR_FIR; architecture TEST of SSR_FIR is - constant SSR:INTEGER:=I'length(1); -- Number of Columns - constant TAPS:INTEGER:=COEFFICIENT'length; -- Number of Rows + constant COEFFICIENT_SETS:INTEGER:=COEFFICIENT'length; + constant TAPS:INTEGER:=COEFFICIENT(COEFFICIENT'low)'length; + constant SSR:INTEGER:=I'length; function DIL(SYMMETRY:STRING;TAPS:INTEGER) return INTEGER is begin if SYMMETRY="ES" then @@ -40,19 +100,16 @@ architecture TEST of SSR_FIR is end if; end; constant LDI:INTEGER:=DIL(SYMMETRY,TAPS); --- these two type definitions overload the ones in TYPES_PKG, they are slightly different, this is a workaround for Vivado Sim VHDL-2008 limited support - type SFIXED_VECTOR is array(INTEGER range <>) of SFIXED(I'range(2)); - type SFIXED_MATRIX is array(INTEGER range <>,INTEGER range <>) of SFIXED(I'range(2)); type AC_VECTOR is array(INTEGER range <>) of STD_LOGIC_VECTOR(FAMILY/3*4+29 downto 0); type PC_VECTOR is array(INTEGER range <>) of STD_LOGIC_VECTOR(FAMILY/3*10+47 downto 0); - signal SW,SE:SFIXED_MATRIX(0 to TAPS-1,0 to SSR-1); + signal SW,SE:SFIXED_MATRIX(0 to TAPS-1)(0 to SSR-1)(I(I'low)'range); function LATENCY(SSR:INTEGER;SYMMETRY:STRING;TAPS:INTEGER) return INTEGER is begin if SSR=1 then if SYMMETRY="ES" then return TAPS+4; elsif SYMMETRY="OS" then - return TAPS+4; + return TAPS+4-BOOLEAN'pos(ANTI); elsif SYMMETRY="NS" then return TAPS+3; else @@ -63,7 +120,7 @@ architecture TEST of SSR_FIR is if SYMMETRY="ES" then return TAPS+3; elsif SYMMETRY="OS" then - return TAPS+3; + return TAPS+3-BOOLEAN'pos(ANTI); elsif SYMMETRY="NS" then return TAPS+2; else @@ -93,22 +150,39 @@ architecture TEST of SSR_FIR is return "A"; end if; end; - signal II:SFIXED_VECTOR(I'range(1)); begin assert I'length=O'length report "Ports I and O must have the same length!" severity warning; - li:for L in I'range(1) generate - II(L)<=GET(I,L); - end generate; + assert CSA'length=MAX(LOG2(COEFFICIENT'length(1)),1) report "Inconsistent CSA length!" severity warning; + assert not (ANTI and SYMMETRY="NS") report "ANTI=TRUE has no effect for SYMMETRY=""NS"" - anti-symmetric pairs require ES or OS symmetry" severity warning; + assert SSR=1 or 2**LOG2(SSR)=SSR report "SSR must be 1 or a power of 2 - non-power-of-2 SSR causes diagonal register misalignment in the SSR>1 accumulation chain" severity failure; + -- For ES/OS: the SE reverse-delay index DI(-LDI-1+SSR+J) becomes invalid + -- when SSR > TAPS (ES) or SSR >= TAPS (OS), where TAPS=COEFFICIENT'length. + -- ES rule: 2*SSR <= NUM_TAPS => SSR <= COEFFICIENT'length + -- OS rule: 2*SSR < NUM_TAPS => SSR < COEFFICIENT'length + assert SYMMETRY="NS" or SSR<=TAPS-BOOLEAN'pos(SYMMETRY="OS") report "For ES: SSR must be <= NUM_TAPS/2. For OS: SSR must be < (NUM_TAPS+1)/2. Increase TAPS or use NS." severity failure; + -- For ES/OS, the DSP pre-adder computes D+A. D and A are both data_width bits wide, + -- so D+A = data_width+1 bits must fit the multiplier AD port. + -- FAMILY=3 (DSPx8): AD port = 27 bits → D+A ≤ 27 → data_width ≤ 26. + -- FAMILY=1,2 (DSP48/DSP58): AD port = 30 bits → D+A ≤ 30 → data_width ≤ 29. + assert SYMMETRY="NS" or I(I'low)'length <= FAMILY/3*26+(1-FAMILY/3)*29 + report "For ES/OS: data width (I_HIGH-I_LOW+1=" & INTEGER'image(I(I'low)'length) & ") exceeds the DSP pre-adder AD port limit (" & INTEGER'image(FAMILY/3*26+(1-FAMILY/3)*29) & " bits for FAMILY=" & INTEGER'image(FAMILY) & "). Reduce I_HIGH-I_LOW to fit within the limit, or use SYMMETRY=""NS""." + severity failure; + -- For SSR>1, the DI delay buffer has depth LDI=TAPS-1. TAPS=1 gives LDI=0 + -- (null range), which is not supported by xsim or synthesis. + assert SSR=1 or TAPS>=2 + report "TAPS must be >= 2 when SSR > 1: TAPS=1 creates a null-range DI buffer (LDI=0). No practical use case exists for SSR>1 with a single-tap FIR." + severity failure; i1:if SSR=1 generate - signal DI:SFIXED(I'range(2)); + signal DI:SFIXED(I(I'low)'range); signal AC:AC_VECTOR(0 to TAPS); signal PC:PC_VECTOR(0 to TAPS); + signal CS:UNSIGNED_VECTOR(0 to TAPS)(CSA'range):=(others=>(others=>'0')); function DSIZE(TAPS:INTEGER;SYMMETRY:STRING) return INTEGER is begin if SYMMETRY="ES" then - return 2*TAPS-1; + return 2*TAPS; elsif SYMMETRY="OS" then - return 2*TAPS-2; + return 2*TAPS-1; elsif SYMMETRY="NS" then return 0; else @@ -121,25 +195,41 @@ begin DDR=>DDR) port map(CLK=>CLK, CLKH=>CLKH, - I=>II(I'low), + I=>I(I'low), O=>DI); -- DSPx8 Array AC(0)<=STD_LOGIC_VECTOR(TO_SIGNED(0,AC(0)'length)); PC(0)<=STD_LOGIC_VECTOR(TO_SIGNED(0,PC(0)'length)); - lj:for J in 0 to TAPS-1 generate + CS(0)<=CSA; + lj:for J in 0 to TAPS-1-BOOLEAN'pos(ANTI and SYMMETRY="OS") generate + signal ALUMODE:STD_LOGIC_VECTOR(3 downto 0); signal INMODE:STD_LOGIC_VECTOR(4 downto 0); signal OPMODE:STD_LOGIC_VECTOR(8 downto 0); - signal A,D:SFIXED(I'range(2)); - signal C,P:SFIXED(LOG2(TAPS)+I'high(2)+C_HIGH+1 downto I'low(2)+C_LOW); -- must not be longer than 48/58 bits! + signal A,D:SFIXED(I(I'low)'range); + signal B:SFIXED(C_high downto C_low); + signal C,P:SFIXED(LOG2(TAPS)+I(I'low)'high+C_HIGH+1 downto I(I'low)'low+C_LOW); -- must not be longer than 48/58 bits! signal CE:STD_LOGIC; begin - A<=II(I'low) when J=0 else (others=>'0'); - C<=TO_SFIXED(2.0**(C'low-O'low(2)-1),C) when ROUNDING and (J=0) else TO_SFIXED(0.0,C); + A<=I(I'low) when J=0 else (others=>'0'); + C<=TO_SFIXED(2.0**(C'low-O(O'low)'low-1),C) when ROUNDING and (J=0) else TO_SFIXED(0.0,C); D<=(others=>'0') when SYMMETRY="NS" else DI; - INMODE<=5x"11" when (SYMMETRY="OS") and (J=TAPS-1) else 5x"15"; -- A1*B1 when (SYMMETRY="OS") and (K=TAPS-1) else (D+A1)*B1 - OPMODE<=9x"035" when J=0 else 9x"015"; -- PCOUT=C+(D+A)*B when J=0 else PCIN+(D+A)*B + ALUMODE<=4x"3" when ANTI else 4x"0"; + INMODE<=5x"18" when ANTI and (SYMMETRY="OS") and (J=TAPS-1) else 5x"1C" when ANTI else + 5x"10" when (SYMMETRY="OS") and (J=TAPS-1) else 5x"14"; -- A2*B1 when (SYMMETRY="OS") and (J=TAPS-1) else (D+A1)*B1 + OPMODE<=9x"035" when J=0 else 9x"015"; -- PCOUT=C-(D+A)*B when J=0 else PCIN-(D+A)*B CE<='1' when VI else '0'; - + ia:if COEFFICIENT'length=1 generate + B<=TO_SFIXED(COEFFICIENT(TO_INTEGER(CS(J+1-BOOLEAN'pos(SYMMETRY="NS"))))(COEFFICIENT(COEFFICIENT'low)'low+J),C_HIGH,C_LOW); + CS(J+1)<=CS(J); + else generate + process(CLK) + begin + if rising_edge(CLK) then + B<=TO_SFIXED(COEFFICIENT(TO_INTEGER(CS(J+1-BOOLEAN'pos(SYMMETRY="NS"))))(COEFFICIENT(COEFFICIENT'low)'low+J),C_HIGH,C_LOW); + CS(J+1)<=CS(J); + end if; + end process; + end generate; jk:entity work.DSPx8_WRAPPER generic map(X=>OX, Y=>OY+J, FAMILY=>FAMILY, @@ -150,10 +240,11 @@ begin port map (CLK=>CLK, CEA2=>CE, A=>A, - B=>TO_SFIXED(COEFFICIENT(COEFFICIENT'low+J),C_HIGH,C_LOW), + B=>B, C=>C, D=>D, ACIN=>AC(J), + ALUMODE=>ALUMODE, INMODE=>INMODE, OPMODE=>OPMODE, PCIN=>PC(J), @@ -161,26 +252,20 @@ begin PCOUT=>PC(J+1), P=>P); -- Output Samples - il:if J=COEFFICIENT'high generate --- all SET calls targetting the same signal must be located in a single process! --- SET does not work in QuestaSim! + il:if J=TAPS-1-BOOLEAN'pos(ANTI and SYMMETRY="OS") generate process(P) begin --- SET(O,O'low,P); - for L in O'range(2) loop - O(O'low(1),L)<=P(L); - end loop; + O(O'low)<=RESIZE(P,O(O'low)); end process; - end generate; + end generate; end generate; end; else generate - signal DI:SFIXED_VECTOR(-LDI to -1):=(others=>(others=>'0')); - signal WI,EI:SFIXED_VECTOR(1 to TAPS-1); - signal RSW:SFIXED_MATRIX(0 to TAPS-2,0 to SSR-2):=(others=>(others=>(others=>'0'))); - signal RSE:SFIXED_MATRIX(0 to TAPS-2,1 to SSR-1):=(others=>(others=>(others=>'0'))); - type P_SFIXED_VECTOR is array(INTEGER range <>) of SFIXED(LOG2(TAPS)+I'high(2)+C_HIGH+1 downto I'low(2)+C_LOW); -- must not be longer than 48/58 bits! - signal vP:P_SFIXED_VECTOR(0 to SSR-1); + signal DI:SFIXED_VECTOR(-LDI to -1)(I(I'low)'range):=(others=>(others=>'0')); + signal WI,EI:SFIXED_VECTOR(1 to TAPS-1)(I(I'low)'range); + signal RSW:SFIXED_MATRIX(0 to TAPS-2)(0 to SSR-2)(I(I'low)'range):=(others=>(others=>(others=>'0'))); + signal RSE:SFIXED_MATRIX(0 to TAPS-2)(1 to SSR-1)(I(I'low)'range):=(others=>(others=>(others=>'0'))); + signal vP:SFIXED_VECTOR(0 to SSR-1)(LOG2(TAPS)+I(I'low)'high+C_HIGH+1 downto I(I'low)'low+C_LOW); begin -- Input Data Delay Line for SSR Input Samples process(CLK) @@ -191,7 +276,7 @@ begin if K<-SSR then DI(K)<=DI(K+SSR); else - DI(K)<=II(I'low+K+SSR); + DI(K)<=I(I'low+K+SSR); end if; end loop; end if; @@ -207,12 +292,28 @@ begin O=>WI(J)); ns:if SYMMETRY/="NS" generate - rd:entity work.SDELAY generic map(SIZE=>J, - DDR=>DDR) - port map(CLK=>CLK, - CLKH=>CLKH, - I=>DI(-LDI-1+SSR+J), - O=>EI(J)); + -- Reverse delay: DI(-LDI-1+SSR+J) accesses the symmetric + -- counterpart sample. Guard against OOB when SSR >= TAPS_internal + -- (i.e. -LDI-1+SSR+J >= 0). For the degenerate center-tap case + -- use DI(-J) so D+B=2D; with the halved center coefficient this + -- gives the correct full-coefficient result. For ANTI=TRUE the + -- center coefficient is 0 so this path has no effect. + valid_rd:if (-LDI-1+SSR+J) < 0 generate + rd:entity work.SDELAY generic map(SIZE=>J, + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>DI(-LDI-1+SSR+J), + O=>EI(J)); + end generate; + center_rd:if (-LDI-1+SSR+J) >= 0 generate + rd:entity work.SDELAY generic map(SIZE=>J, + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>DI(-J), + O=>EI(J)); + end generate; end generate; end generate; -- Pipelining Array Registers @@ -221,7 +322,7 @@ begin process(CLK) begin if rising_edge(CLK) then - RSW(J,K)<=SW(J,K); + RSW(J)(K)<=SW(J)(K); end if; end process; end generate; @@ -231,7 +332,7 @@ begin process(CLK) begin if rising_edge(CLK) then - RSE(J,K)<=SE(J,K); + RSE(J)(K)<=SE(J)(K); end if; end process; end generate; @@ -240,58 +341,71 @@ begin -- DSPx8 Array lk:for K in 0 to SSR-1 generate signal PC:PC_VECTOR(0 to TAPS); + signal CS:UNSIGNED_VECTOR(0 to TAPS)(CSA'range):=(others=>(others=>'0')); begin PC(0)<=STD_LOGIC_VECTOR(TO_SIGNED(0,PC(0)'length)); - lj:for J in 0 to TAPS-1 generate + CS(0)<=CSA; + lj:for J in 0 to TAPS-1-BOOLEAN'pos(ANTI and SYMMETRY="OS") generate + signal ALUMODE:STD_LOGIC_VECTOR(3 downto 0); signal INMODE:STD_LOGIC_VECTOR(4 downto 0); signal OPMODE:STD_LOGIC_VECTOR(8 downto 0); - signal D:SFIXED(I'range(2)); + signal D:SFIXED(I(I'low)'range); signal C,P:SFIXED(vP(vP'low)'range); + signal B:SFIXED(C_high downto C_low); begin -- MIN/MAX workaround for QuestaSim bug - SW(J,K)<=II(I'low+K) when J=0 else - WI(work.TYPES_PKG.MAX(J,1)) when K=0 else - RSW(work.TYPES_PKG.MAX(J-1,0),work.TYPES_PKG.MAX(K-1,0)); + SW(J)(K)<=I(I'low+K) when J=0 else + WI(work.cfixed_pkg.MAX(J,1)) when K=0 else + RSW(work.cfixed_pkg.MAX(J-1,0))(work.cfixed_pkg.MAX(K-1,0)); ns:if SYMMETRY/="NS" generate - SE(J,K)<=DI(-LDI+K) when J=0 else - EI(work.TYPES_PKG.MAX(J,1)) when K=SSR-1 else - RSE(work.TYPES_PKG.MAX(J-1,0),work.TYPES_PKG.MIN(K+1,SSR-1)); + SE(J)(K)<=DI(-LDI+K) when J=0 else + EI(work.cfixed_pkg.MAX(J,1)) when K=SSR-1 else + RSE(work.cfixed_pkg.MAX(J-1,0))(work.cfixed_pkg.MIN(K+1,SSR-1)); end generate; - C<=TO_SFIXED(2.0**(C'low-O'low(2)-1),C) when ROUNDING and (J=0) else TO_SFIXED(0.0,C); - D<=(others=>'0') when SYMMETRY="NS" else SE(J,K); - INMODE<=5x"11" when (SYMMETRY="OS") and (J=TAPS-1) else 5x"15"; -- A1*B1 when (SYMMETRY="OS") and (K=TAPS-1) else (D+A1)*B1 + C<=TO_SFIXED(2.0**(C'low-O(O'low)'low-1),C) when ROUNDING and (J=0) else TO_SFIXED(0.0,C); + D<=(others=>'0') when SYMMETRY="NS" else SE(J)(K); + ALUMODE<=4x"3" when ANTI else 4x"0"; + INMODE<=5x"13" when ANTI and (SYMMETRY="OS") and (J=TAPS-1) else 5x"1D" when ANTI else + 5x"11" when (SYMMETRY="OS") and (J=TAPS-1) else 5x"15"; -- A2*B1 when (SYMMETRY="OS") and (J=TAPS-1) else (D+A1)*B1 OPMODE<=9x"035" when J=0 else 9x"015"; -- PCOUT=C+(D+A)*B when J=0 else PCIN+(D+A)*B - + ia:if COEFFICIENT'length=1 generate + B<=TO_SFIXED(COEFFICIENT(TO_INTEGER(CS(J+1-BOOLEAN'pos(SYMMETRY="NS"))))(COEFFICIENT(COEFFICIENT'low)'low+J),C_HIGH,C_LOW); + CS(J+1)<=CS(J); + else generate + process(CLK) + begin + if rising_edge(CLK) then + B<=TO_SFIXED(COEFFICIENT(TO_INTEGER(CS(J)))(COEFFICIENT(COEFFICIENT'low)'low+J),C_HIGH,C_LOW); + CS(J+1)<=CS(J); + end if; + end process; + end generate; jk:entity work.DSPx8_WRAPPER generic map(X=>OX+K, Y=>OY+J, FAMILY=>FAMILY, AMULTSEL=>AMULTSEL(SYMMETRY)) --"A" for Non-Symmetric FIR else "AD" port map (CLK=>CLK, - A=>SW(J,K), - B=>TO_SFIXED(COEFFICIENT(COEFFICIENT'low+J),C_HIGH,C_LOW), + A=>SW(J)(K), + B=>B, C=>C, D=>D, + ALUMODE=>ALUMODE, INMODE=>INMODE, OPMODE=>OPMODE, PCIN=>PC(J), PCOUT=>PC(J+1), P=>P); -- SSR Output Samples - il:if J=COEFFICIENT'high generate + il:if J=TAPS-1-BOOLEAN'pos(ANTI and SYMMETRY="OS") generate vP(K)<=P; end generate; end generate; end generate; --- all SET calls targetting the same signal must be located in a single process! --- SET does not work in QuestaSim! process(vP) begin for K in vP'range loop --- SET(O,O'low+K,vP(K)); - for L in O'range(2) loop - O(O'low(1)+K,L)<=vP(K)(L); - end loop; + O(O'low+K)<=RESIZE(vP(K),O(O'low+K)); end loop; end process; end; @@ -303,4 +417,4 @@ begin CLKH=>CLKH, I=>VI, O=>VO); -end TEST; \ No newline at end of file +end TEST; diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR_TEST.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR_TEST.vhd deleted file mode 100755 index 68da4082b..000000000 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/SSR_FIR_TEST.vhd +++ /dev/null @@ -1,77 +0,0 @@ --- Copyright (C) 2024, Advanced Micro Devices, Inc. - -library IEEE; -use IEEE.STD_LOGIC_1164.ALL; -use IEEE.NUMERIC_STD.ALL; - -use work.TYPES_PKG.all; - -library UNISIM; -use UNISIM.VComponents.all; - -entity SSR_FIR_TEST is - generic(OX,OY:INTEGER:=0; -- Coordinates of the lower left corner DSPx8, use -2147483648 if you do not want floorplanning - FAMILY:INTEGER:=3; -- use 1 for 7-series, 2 for US/US+ and 3 for Versal - DDR:BOOLEAN:=TRUE; -- DDR implementation for Versal -3 clock rates over 872MHz - SSR:INTEGER:=8; -- for 8Gsps in Versal speed grade -3 --- SSR:INTEGER:=9; -- for 8Gsps in US+ speed grade -3 --- SSR:INTEGER:=11; -- for 8Gsps in 7-series speed grade -3 - COEFFICIENT:REAL_VECTOR(0 to 15):=(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0); --- COEFFICIENT:REAL_VECTOR(0 to 23):=(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0); - I_HIGH:INTEGER:=7; - I_LOW:INTEGER:=-8; - C_HIGH:INTEGER:=9; - C_LOW:INTEGER:=-8; - O_HIGH:INTEGER:=19; - O_LOW:INTEGER:=-8; - ROUNDING:BOOLEAN:=TRUE; - SYMMETRY:STRING:="OS"); -- Filter Symmetry, use "ES" for Even-Symmetric, "OS" for Odd-Symmetric and "NS" for Non-Symmetric - port(CLKIN:in STD_LOGIC; - I:in SFIXED_VECTOR(0 to SSR-1,I_HIGH downto I_LOW); - VI:in BOOLEAN; - O:out SFIXED_VECTOR(0 to SSR-1,O_HIGH downto O_LOW); - VO:out BOOLEAN); -end SSR_FIR_TEST; - -architecture TEST of SSR_FIR_TEST is - signal CLK,CLKH:STD_LOGIC; - signal ID:SFIXED_VECTOR(0 to SSR-1,I_HIGH downto I_LOW):=(others=>(others=>'0')); - signal VID:BOOLEAN:=FALSE; -begin - i1:if DDR generate - b2:MBUFGCE_DIV port map(I=>CLKIN, - CE=>'1', - CLR=>'0', - CLRB_LEAF=>'1', - O1=>CLK, - O2=>CLKH); - end; - else generate - CLK<=CLKIN; - CLKH<='0'; - end generate; - - process(CLK) - begin - if rising_edge(CLK) then - ID<=I; - VID<=VI; - end if; - end process; - - u1:entity work.SSR_FIR generic map(OX=>OX, - OY=>OY, - FAMILY=>FAMILY, - DDR=>DDR, - COEFFICIENT=>COEFFICIENT, - C_HIGH=>C_HIGH, - C_LOW=>C_LOW, - ROUNDING=>ROUNDING, - SYMMETRY=>SYMMETRY) - port map (CLK=>CLK, - CLKH=>CLKH, - I=>ID, - VI=>VID, - O=>O, - VO=>VO); -end TEST; \ No newline at end of file diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/TRANSPOSE_SSR_FIR.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/TRANSPOSE_SSR_FIR.vhd new file mode 100644 index 000000000..af39ae127 --- /dev/null +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/TRANSPOSE_SSR_FIR.vhd @@ -0,0 +1,382 @@ +-- Copyright © Advanced Micro Devices, Inc., or its affiliates. All rights reserved. +-- Portions of this content consists of AI generated content. +-- You may not use this software and documentation (if any) (collectively, the "Materials") +-- except in compliance with the terms and conditions of the Software License Agreement +-- included with the Materials or otherwise as set forth in writing and signed by you and +-- an authorized signatory of AMD. If you do not have a copy of the Software License +-- Agreement, contact your AMD representative for a copy. You agree that you will not +-- reverse engineer or decompile the Materials, in whole or in part, except as allowed by +-- applicable law. +-- THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +-- REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +use work.FIXED_PKG.all; +use work.CFIXED_PKG.all; + +-- ============================================================================= +-- TRANSPOSE_SSR_FIR_ROW +-- One J-row sub-filter for the transpose-form SSR FIR. Instantiated SSR times +-- from TRANSPOSE_SSR_FIR via for-generate. Keeping J as a generic constant +-- (not a for-generate loop variable) works around an xsim VHDL-2008 bug where +-- concurrent signal assignments and processes inside for-generate use only the +-- first iteration value of the loop parameter. Entity instantiation port maps +-- are evaluated correctly for every iteration, so the row is instantiated with +-- the right J_IDX via the outer generate and handles its own K-loop internally. +-- ============================================================================= +entity TRANSPOSE_SSR_FIR_ROW is + generic(J_IDX:INTEGER; -- row index (0..SSR-1) + OX,OY:INTEGER; -- floorplan origin; chains add K to OX + FAMILY:INTEGER; + DDR:BOOLEAN; + COEFFICIENT:REAL_MATRIX; -- full coefficient matrix; GET() selects K-slice + TAPS_G:INTEGER; -- total number of taps (passed in to size C) + C_HIGH:INTEGER; + C_LOW:INTEGER; + EXTRA_C_PIPELINING:BOOLEAN; + EXTRA_D_PIPELINING:BOOLEAN; + ROUNDING:BOOLEAN; + ANTI:BOOLEAN; + SYMMETRY:STRING); + port(CLK:in STD_LOGIC; + CLKH:in STD_LOGIC; + I:in SFIXED_VECTOR; -- full SSR-wide input + VI:in BOOLEAN; + CSA:in UNSIGNED; -- coefficient set address + O_J:out SFIXED); -- single sub-filter output (one lane) +end TRANSPOSE_SSR_FIR_ROW; + +architecture ROW of TRANSPOSE_SSR_FIR_ROW is + constant SSR:INTEGER:=I'length; + constant D_HIGH:INTEGER:=I(I'low)'high; + constant D_LOW :INTEGER:=I(I'low)'low; + constant CH:INTEGER:=LOG2(TAPS_G)+D_HIGH+C_HIGH+2; + constant CL:INTEGER:=D_LOW+C_LOW; + -- All signals at architecture level - no for-generate local signal declarations. + -- K-indexed signals are 1-D arrays (SFIXED_VECTOR or UNSIGNED_VECTOR), indexed + -- by the lk for-generate parameter K. J_IDX is a generic constant here, so + -- every expression that references it is correctly evaluated in all contexts. + signal C :SFIXED_VECTOR(0 to SSR)(CH downto CL); + signal ID,RD:SFIXED_VECTOR(I'range)(D_HIGH downto D_LOW); + signal CP_K:SFIXED_VECTOR(0 to SSR-1)(CH downto CL) + :=(others=>(others=>'0')); + signal DD_K,BB_K:SFIXED_VECTOR(0 to SSR-1)(D_HIGH downto D_LOW) + :=(others=>(others=>'0')); + signal DO_K,BO_K:SFIXED_VECTOR(0 to SSR-1)(D_HIGH downto D_LOW); + signal CS:UNSIGNED_VECTOR(0 to SSR)(CSA'range):=(others=>(others=>'0')); + + function GET(COEFFICIENT:REAL_MATRIX;K:INTEGER) return REAL_MATRIX is + constant TAPS_K:INTEGER:=COEFFICIENT(COEFFICIENT'low)'length; + variable RESULT:REAL_MATRIX(COEFFICIENT'range)(0 to (TAPS_K+SSR-1)/SSR-1); + begin + for J in RESULT'range loop + for L in RESULT(J)'range loop + if SSR*L+SSR-1-K >= TAPS_K then + RESULT(J)(L):=0.0; + elsif (SYMMETRY="OS") and (SSR*L+SSR-1-K=TAPS_K-1) then + RESULT(J)(L):=COEFFICIENT(J)(SSR*L+SSR-1-K)/2.0; + else + RESULT(J)(L):=COEFFICIENT(J)(SSR*L+SSR-1-K); + end if; + end loop; + end loop; + return RESULT; + end; + +begin + C(0)<=TO_SFIXED(0.0,C(0)); + + -- Anti+OS: center coefficient (last element of coefficient array) must be 0.0. + -- The GET() function applies /2.0 to the center tap; for anti-symmetric filters + -- the center must be zero so the pre-adder subtraction (D-B) cancels correctly. + assert not (ANTI and SYMMETRY="OS" and + COEFFICIENT(COEFFICIENT'low)(COEFFICIENT(COEFFICIENT'low)'high) /= 0.0) + report "TRANSPOSE_SSR_FIR_ROW: ANTI+OS center coefficient must be 0.0" + severity FAILURE; + + -- CS cascade: one process owns ALL elements of CS (including CS(0)) to avoid + -- any cross-driver conflict on the unresolved UNSIGNED_VECTOR composite signal. + -- Having CS(0)<=CSA as a separate concurrent assignment plus a process driving + -- CS(1..SSR) makes xsim see the composite as having two drivers and CS(0) gets + -- stuck at its initial value. Absorbing CS(0)<=CSA into the same process (with + -- CSA in the sensitivity list so it stays combinatorial) resolves this. + cs_cascade:if EXTRA_C_PIPELINING generate + process(CLK, CSA) begin + CS(0)<=CSA; -- combinatorial: all CSA changes reflected immediately + if rising_edge(CLK) then + CS(1)<=CSA; -- CS(1) registered once from CSA + for K in 1 to SSR-1 loop + CS(K+1)<=CS(K); + end loop; + end if; + end process; + else generate + process(all) begin + CS(0)<=CSA; + CS(1)<=CSA; + for K in 1 to SSR-1 loop + CS(K+1)<=CS(K); + end loop; + end process; + end generate; + + -- CP_K cascade: one process owns ALL elements of CP_K to avoid any + -- cross-driver conflict on the unresolved SFIXED_VECTOR composite signal. + -- CP_K(0)=C(0)=0 always; CP_K(1..SSR-1) registered when EXTRA_C_PIPELINING. + cp_cascade:if EXTRA_C_PIPELINING generate + process(CLK) begin + if rising_edge(CLK) then + for K in 0 to SSR-1 loop + CP_K(K)<=C(K); + end loop; + end if; + end process; + else generate + process(all) begin + for K in 0 to SSR-1 loop + CP_K(K)<=C(K); + end loop; + end process; + end generate; + + -- DD_K / BB_K: registered when EXTRA_D_PIPELINING, wired otherwise. + -- Single process drives all SSR elements to avoid multiple-driver issues. + dd_bb_pipe:if EXTRA_D_PIPELINING generate + process(CLK) begin + if rising_edge(CLK) then + for K in 0 to SSR-1 loop + DD_K(K)<=ID(K+I'low); + BB_K(K)<=RD(K+I'low); + end loop; + end if; + end process; + else generate + process(all) begin + for K in 0 to SSR-1 loop + DD_K(K)<=ID(K+I'low); + BB_K(K)<=RD(K+I'low); + end loop; + end process; + end generate; + + -- RD for NS symmetry: no reverse-input path, drive entire array to zero. + -- Handled here at architecture level (not inside the lk for-generate) so + -- there is exactly one driver on the unresolved SFIXED_VECTOR signal. + rd_ns:if SYMMETRY="NS" generate + RD<=(others=>(others=>'0')); + end generate; + + -- K-loop: ONLY entity instantiations appear here. + -- Concurrent signal assignments and processes have been moved to architecture-level + -- constructs above so xsim's for-generate multiple-driver bug cannot affect them. + lk:for K in 0 to SSR-1 generate + + -- Triangular input delay: selects and time-aligns the correct input sample + -- for this (J_IDX, K) filter chain. J_IDX is a generic constant so the + -- SIZE expression is evaluated correctly for each elaborated ROW instance. + sd_gen:if J_IDX=(SSR-1) generate + sd:entity work.SDELAY + generic map(SIZE=>2*K+K*BOOLEAN'pos(EXTRA_C_PIPELINING), + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>I((1+K+J_IDX) mod SSR+I'low), + O=>ID(K+I'low)); + else generate + sd:entity work.SDELAY + generic map(SIZE=>1+2*K-(J_IDX+K)/(SSR-1)+K*BOOLEAN'pos(EXTRA_C_PIPELINING), + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>I((1+K+J_IDX) mod SSR+I'low), + O=>ID(K+I'low)); + end generate; + + -- Reverse input delay for symmetric path (ES/OS only). + -- NS symmetry: RD is driven to zero at architecture level above. + ri:if SYMMETRY="ES" generate + dr_gen:if J_IDX=(SSR-1) generate + dr:entity work.SDELAY + generic map(SIZE=>2*(SSR-K)-1+(SSR-K-1)*BOOLEAN'pos(EXTRA_C_PIPELINING), + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>I((1+J_IDX+K) mod SSR+I'low), + O=>RD(SSR-1-K+I'low)); + else generate + dr:entity work.SDELAY + generic map(SIZE=>1+2*(SSR-K)-1-(J_IDX+K)/(SSR-1)+(SSR-K-1)*BOOLEAN'pos(EXTRA_C_PIPELINING), + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>I((1+J_IDX+K) mod SSR+I'low), + O=>RD(SSR-1-K+I'low)); + end generate; + elsif SYMMETRY="OS" generate + dr:entity work.SDELAY + generic map(SIZE=>2*(SSR-K-1)+BOOLEAN'pos(((J_IDX+K+2) mod SSR)>J_IDX)+(SSR-K-1)*BOOLEAN'pos(EXTRA_C_PIPELINING), + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>I((J_IDX+K+2) mod SSR+I'low), + O=>RD(SSR-1-K+I'low)); + end generate; + + -- Accumulator chain for this (J_IDX, K) tap group. + jk:entity work.TRANSPOSE_SSR_FIR_CHAIN + generic map(OX=>OX+K, + OY=>OY, + FAMILY=>FAMILY, + DDR=>DDR, + COEFFICIENT=>GET(COEFFICIENT,K), + C_HIGH=>C_HIGH, + C_LOW=>C_LOW, + ROUNDING=>ROUNDING and (K=0), + ANTI=>ANTI, + SYMMETRY=>SYMMETRY, + FIRST_CHAIN=>K=0) + port map(CLK=>CLK, + CLKH=>CLKH, + DI=>DD_K(K), + BI=>BB_K(K), + CI=>CP_K(K), + VI=>VI, + CSA=>CS(K), + DO=>DO_K(K), + BO=>BO_K(K), + O=>C(K+1), + VO=>open); + end generate lk; + + O_J<=RESIZE(C(SSR),O_J); + +end ROW; + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; +use work.FIXED_PKG.all; +use work.CFIXED_PKG.all; + +-- ============================================================================= +-- TRANSPOSE_SSR_FIR +-- Top-level SSR polyphase transpose-form FIR filter. +-- Instantiates SSR TRANSPOSE_SSR_FIR_ROW entities via a for-generate. +-- Only entity instantiations appear inside the for-generate (no concurrent +-- signal assignments or processes) so xsim's for-generate evaluation bug does +-- not affect correctness. J is passed as a generic constant J_IDX to each row. +-- ============================================================================= +entity TRANSPOSE_SSR_FIR is + generic(OX,OY:INTEGER:=-2147483648; -- Coordinates of the lower left corner DSPx8, use -2147483648 if you do not want floorplanning + FAMILY:INTEGER:=3; -- use 1 for 7-series, 2 for US/US+ and 3 for Versal + DDR:BOOLEAN:=FALSE; -- DDR implementation to be used only for Versal -3 and clock rates over 872MHz + COEFFICIENT:REAL_MATRIX; -- Number of Coefficient Sets=COEFFICIENT'length(1), Number of Taps=COEFFICIENT'length(2) + C_HIGH:INTEGER:=0; -- For odd anti-symmetric filters, the center tap coefficient, COEFFICIENT(:,COEFFICIENT'high(2)) must be zero, but the redundant DSPx8 is removed + C_LOW:INTEGER:=-17; + EXTRA_C_PIPELINING:BOOLEAN:=TRUE; + EXTRA_D_PIPELINING:BOOLEAN:=TRUE; + ROUNDING:BOOLEAN:=FALSE; + ANTI:BOOLEAN:=FALSE; -- If even or odd symmetric, is it anti-symmetric? + SYMMETRY:STRING:="ES"); -- Filter Symmetry, use "ES" for Even-Symmetric, "OS" for Odd-Symmetric and "NS" for Non-Symmetric + port(CLK:in STD_LOGIC; + CLKH:in STD_LOGIC:='0'; -- if DDR=FALSE can be left unconnected + I:in SFIXED_VECTOR; -- for non-symmetric filters use DI as input, tie BI to zero, for symmetric filters tie DI and BI together + VI:in BOOLEAN:=TRUE; -- WARNING: VI must not be deasserted mid-stream. + -- SDELAY chains are not gated by VI; deasserting VI + -- after data has entered the pipeline causes permanent + -- misalignment between the data path and VO delay line. + -- There is no reset port — power-cycle to recover. + --CEBI,CECI,CEDI:in BOOLEAN:=TRUE; + CSA:in UNSIGNED:="0"; -- Coefficient Set Address, CSA'length must be equal to LOG2(COEFFICIENT'length(1)) + O:out SFIXED_VECTOR; + VO:out BOOLEAN); +end TRANSPOSE_SSR_FIR; + +architecture TEST of TRANSPOSE_SSR_FIR is + constant COEFFICIENT_SETS:INTEGER:=COEFFICIENT'length; + constant TAPS:INTEGER:=COEFFICIENT(COEFFICIENT'low)'length; + constant SSR:INTEGER:=I'length; + -- DSP port widths (integer division intentional; matches DSPx8_WRAPPER slv* signals) + constant I_WIDTH :INTEGER:=I(I'low)'high-I(I'low)'low+1; -- data bit width + constant C_WIDTH :INTEGER:=C_HIGH-C_LOW+1; -- coefficient bit width + constant A_PORT_W :INTEGER:=FAMILY/3*4+30; -- A port: 34b Versal/DSP58, 30b US+/DSP48E2 + constant D_PORT_W :INTEGER:=27; -- D port: 27b on all families (FAMILY/2*2+25) + constant B_PORT_W :INTEGER:=FAMILY/3*6+18; -- B port: 24b Versal/DSP58, 18b US+/DSP48E2 +begin + assert I'length=O'length report "Inconsistent I and O port lengths!" severity warning; + assert CSA'length=MAX(LOG2(COEFFICIENT'length(1)),1) report "Inconsistent CSA length!" severity warning; + -- DSP port truncation guards ------------------------------------------- + -- Coefficient maps to DSP A port (AMULTSEL="A", PREADDINSEL="B"). + -- Exceeding A_PORT_W silently truncates coefficient MSBs -> wrong MAC results. + assert C_WIDTH<=A_PORT_W + report "TRANSPOSE_SSR_FIR: coefficient width "&INTEGER'image(C_WIDTH) + &"b exceeds DSP A port ("&INTEGER'image(A_PORT_W) + &"b on FAMILY="&INTEGER'image(FAMILY) + &") - reduce C_HIGH-C_LOW or target FAMILY=3 (Versal, A=34b)" + severity FAILURE; + -- Forward data maps to DSP D port (always, all filter modes; BMULTSEL="AD", B=0 for NS). + -- D port is 27b on all supported families. Exceeding it truncates data MSBs. + assert I_WIDTH<=D_PORT_W + report "TRANSPOSE_SSR_FIR: data width "&INTEGER'image(I_WIDTH) + &"b exceeds DSP D port (27b, all families) - reduce I_HIGH-I_LOW" + severity FAILURE; + -- Reverse data maps to DSP B port (ES/OS only; B is forced to 0 for NS so no risk). + -- B port is narrower than D: 24b Versal, 18b US+. Exceeding it truncates the + -- reverse pre-adder operand -> wrong ES/OS symmetric filter output. + assert SYMMETRY="NS" or I_WIDTH<=B_PORT_W + report "TRANSPOSE_SSR_FIR: data width "&INTEGER'image(I_WIDTH) + &"b exceeds DSP B port ("&INTEGER'image(B_PORT_W) + &"b on FAMILY="&INTEGER'image(FAMILY)&") for "&SYMMETRY + &" reverse path - reduce I_HIGH-I_LOW" + &" or switch to FAMILY=3 (Versal, B=24b vs US+ B=18b)" + severity FAILURE; + -- ----------------------------------------------------------------------- + + -- Instantiate one row sub-filter per SSR lane. Only entity instantiations + -- appear inside this for-generate so xsim evaluates J correctly for all + -- J=0..SSR-1 (xsim correctly handles for-generate for entity port maps). + lj:for J in SSR-1 downto 0 generate + jrow:entity work.TRANSPOSE_SSR_FIR_ROW + generic map(J_IDX => J, + OX => OX, + OY => OY+TAPS/SSR*J, + FAMILY => FAMILY, + DDR => DDR, + COEFFICIENT => COEFFICIENT, + TAPS_G => TAPS, + C_HIGH => C_HIGH, + C_LOW => C_LOW, + EXTRA_C_PIPELINING => EXTRA_C_PIPELINING, + EXTRA_D_PIPELINING => EXTRA_D_PIPELINING, + ROUNDING => ROUNDING, + ANTI => ANTI, + SYMMETRY => SYMMETRY) + port map(CLK => CLK, + CLKH => CLKH, + I => I, + VI => VI, + CSA => CSA, + O_J => O(J+O'low)); + end generate lj; + +-- Data Valid Delay Line +-- Latency formula (EXTRA_C=TRUE, EXTRA_D=TRUE): 2*SSR + 2 + (SSR-1) + 1 = 3*SSR + 2 +-- SSR LATENCY +-- 1 5 +-- 2 8 +-- 3 11 +-- 4 14 +-- 5 17 +-- 6 20 +-- 8 26 +-- 10 32 + bd:entity work.BDELAY generic map(SIZE=>2*SSR+2+(SSR-1)*BOOLEAN'pos(EXTRA_C_PIPELINING)+BOOLEAN'pos(EXTRA_D_PIPELINING), + DDR=>DDR) + port map(CLK=>CLK, + CLKH=>CLKH, + I=>VI, + O=>VO); +end TEST; diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/TRANSPOSE_SSR_FIR_CHAIN.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/TRANSPOSE_SSR_FIR_CHAIN.vhd new file mode 100644 index 000000000..dea715736 --- /dev/null +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/TRANSPOSE_SSR_FIR_CHAIN.vhd @@ -0,0 +1,324 @@ +-- Copyright © Advanced Micro Devices, Inc., or its affiliates. All rights reserved. +-- Portions of this content consists of AI generated content. +-- You may not use this software and documentation (if any) (collectively, the "Materials") +-- except in compliance with the terms and conditions of the Software License Agreement +-- included with the Materials or otherwise as set forth in writing and signed by you and +-- an authorized signatory of AMD. If you do not have a copy of the Software License +-- Agreement, contact your AMD representative for a copy. You agree that you will not +-- reverse engineer or decompile the Materials, in whole or in part, except as allowed by +-- applicable law. +-- THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +-- REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +use work.FIXED_PKG.all; +use work.CFIXED_PKG.all; + +-- ============================================================================= +-- TRANSPOSE_SSR_FIR_CHAIN +-- One K-column accumulator chain for the SSR polyphase transpose-form FIR. +-- Instantiated SSR times from TRANSPOSE_SSR_FIR_ROW. +-- +-- xsim workaround: concurrent signal assignments and processes inside +-- for-generate use only the first iteration value of the loop parameter. +-- Additionally, signal instances inside for-generate are SHARED across all +-- iterations, causing multiple-driver conflicts (X propagation) when per-stage +-- drivers disagree. The fix (matching what was done in TRANSPOSE_SSR_FIR_ROW) +-- is to declare ALL signals at architecture level and drive them with a single +-- architecture-level process or concurrent assignment. Only entity +-- instantiations appear inside the lj:for J generate; port maps are evaluated +-- correctly per iteration in xsim. +-- ============================================================================= +entity TRANSPOSE_SSR_FIR_CHAIN is + generic(OX,OY:INTEGER:=-2147483648; -- Coordinates of the lower left corner DSPx8, use -2147483648 if you do not want floorplanning + FAMILY:INTEGER:=3; -- use 1 for 7-series, 2 for US/US+ and 3 for Versal + DDR:BOOLEAN:=FALSE; -- DDR implementation to be used only for Versal -3 and clock rates over 872MHz + COEFFICIENT:REAL_MATRIX; -- Number of Coefficient Sets=COEFFICIENT'length(1), Number of Taps=COEFFICIENT'length(2) + C_HIGH:INTEGER:=0; -- For odd anti-symmetric filters, the center tap coefficient, COEFFICIENT(:,COEFFICIENT'high(2)) must be zero, but the redundant DSPx8 is removed + C_LOW:INTEGER:=-17; + ROUNDING:BOOLEAN:=FALSE; + ANTI:BOOLEAN:=FALSE; -- If even or odd symmetric, is it anti-symmetric? + SYMMETRY:STRING:="ES"; -- Filter Symmetry, use "ES" for Even-Symmetric, "OS" for Odd-Symmetric and "NS" for Non-Symmetric + FIRST_CHAIN: BOOLEAN := TRUE); + port(CLK:in STD_LOGIC; + CLKH:in STD_LOGIC:='0'; -- if DDR=FALSE can be left unconnected + DI,BI:in SFIXED; -- for non-symmetric filters use DI as input, tie BI to zero, for symmetric filters tie DI is forward data and BI is reverse data + CI:in SFIXED; -- C input into the last DSPx8 in the chain, used for cascading transpose sections + VI:in BOOLEAN:=TRUE; + CEBI,CECI,CEDI:in BOOLEAN:=TRUE; + CSA:in UNSIGNED:="0"; -- Coefficient Set Address, CSA'length must be equal to LOG2(COEFFICIENT'length(1)) + DO,BO:out SFIXED; -- these are not used unless you build a systolic transposed filter structure + O:out SFIXED; + VO:out BOOLEAN); +end TRANSPOSE_SSR_FIR_CHAIN; + +architecture TEST of TRANSPOSE_SSR_FIR_CHAIN is + constant COEFFICIENT_SETS:INTEGER:=COEFFICIENT'length; + constant TAPS:INTEGER:=COEFFICIENT(COEFFICIENT'low)'length; + constant JLOW:INTEGER:=0; + + -- P/C signal width (must not exceed DSP P register width of 48/58 bits) + constant P_HIGH : INTEGER := MAX(LOG2(TAPS)+BI'high+C_HIGH+1, O'high); + constant P_LOW : INTEGER := BI'low+C_LOW; + + -- ------------------------------------------------------------------------- + -- Helper functions + -- ------------------------------------------------------------------------- + function BREG(SYMMETRY:STRING;ANTI:BOOLEAN;J:INTEGER) return INTEGER is + begin + if (SYMMETRY="ES") or (SYMMETRY="OS") then + if (SYMMETRY="OS") and FIRST_CHAIN and J=JLOW then + return 1; + elsif (SYMMETRY/="OS") and J=JLOW then + return 1; + else + return 2; + end if; + elsif SYMMETRY="NS" then + return 1; + else + assert FALSE report "SYMMETRY must be either ""ES"", ""OS"" or ""NS""!" severity warning; + return 0; + end if; + end; + + function LATENCY(SYMMETRY:STRING;TAPS:INTEGER) return INTEGER is + begin + if SYMMETRY="ES" then + return 4; + elsif SYMMETRY="OS" then + return 4; + elsif SYMMETRY="NS" then + return 3; + else + assert FALSE report "SYMMETRY must be either ""ES"", ""OS"" or ""NS""!" severity warning; + return 0; + end if; + end; + + function B_INPUT(J:INTEGER) return STRING is + begin + if J=JLOW then + return "DIRECT"; + else + return "CASCADE"; + end if; + end; + + -- ------------------------------------------------------------------------- + -- Cascade signal arrays (one entry per DSP stage, plus boundary values) + -- ------------------------------------------------------------------------- + type BC_VECTOR is array(INTEGER range <>) of STD_LOGIC_VECTOR(FAMILY/3*6+17 downto 0); + type CS_VECTOR is array(INTEGER range <>) of UNSIGNED(LOG2(COEFFICIENT_SETS)-1 downto 0); + type PC_VECTOR is array(INTEGER range <>) of STD_LOGIC_VECTOR(FAMILY/3*10+47 downto 0); + subtype A_VECTOR is SFIXED_VECTOR(0 to COEFFICIENT'length-1)(C_HIGH downto C_LOW); + + signal BC : BC_VECTOR(JLOW to TAPS); + signal PC : PC_VECTOR(JLOW to TAPS); + + -- ------------------------------------------------------------------------- + -- Per-stage coefficient ROM: AV_ALL(J)(coeff_set) = coefficient at tap J + -- ALL_AV_INIT uses a local for-loop variable (not a generate variable) so J + -- evaluates correctly for every iteration -> no xsim bug here. + -- ------------------------------------------------------------------------- + function A_VALUE(COEFFICIENT:REAL_MATRIX;J:INTEGER) return A_VECTOR is + variable AV:A_VECTOR; + begin + for K in AV'range loop + AV(K):=TO_SFIXED(COEFFICIENT(COEFFICIENT'low+K)(COEFFICIENT(COEFFICIENT'low)'low+J),C_HIGH,C_LOW); + end loop; + return AV; + end; + + type AV_MATRIX_T is array(INTEGER range <>) of A_VECTOR; + + function ALL_AV_INIT(COEFFICIENT:REAL_MATRIX) return AV_MATRIX_T is + variable result : AV_MATRIX_T(JLOW to TAPS-1); + begin + for J in JLOW to TAPS-1 loop + result(J) := A_VALUE(COEFFICIENT, TAPS-1-J); -- transposed FIR: tap 0 at last DSP + end loop; + return result; + end; + -- Elaboration-time constant: correct coefficient per stage, all coeff sets + constant AV_ALL : AV_MATRIX_T(JLOW to TAPS-1) := ALL_AV_INIT(COEFFICIENT); + + -- ------------------------------------------------------------------------- + -- Static per-stage control arrays (constants, correct at elaboration time) + -- ------------------------------------------------------------------------- + type OPMODE_ARR_T is array(INTEGER range <>) of STD_LOGIC_VECTOR(8 downto 0); + type INMODE_ARR_T is array(INTEGER range <>) of STD_LOGIC_VECTOR(4 downto 0); + + function ALL_OPMODE_INIT return OPMODE_ARR_T is + variable result : OPMODE_ARR_T(JLOW to TAPS-1); + begin + for J in JLOW to TAPS-1 loop + if J = JLOW then + result(J) := 9x"035"; -- first: P = C + A*D (no PCIN cascade in) + elsif J = TAPS-1 then + result(J) := 9x"195"; -- last: P = C + PCIN + A*D (CI passes via C) + else + result(J) := 9x"015"; -- middle: P = PCIN + A*D + end if; + end loop; + return result; + end; + constant OPMODE_CONST : OPMODE_ARR_T(JLOW to TAPS-1) := ALL_OPMODE_INIT; + + function ALL_INMODE_INIT return INMODE_ARR_T is + variable result : INMODE_ARR_T(JLOW to TAPS-1); + begin + for J in JLOW to TAPS-1 loop + if SYMMETRY = "NS" then + result(J) := 5x"07"; -- A1*D (NS: no pre-adder subtraction) + elsif ANTI and not ((SYMMETRY="OS") and FIRST_CHAIN and (J=JLOW)) then + result(J) := 5x"0D"; -- A1*(D-B2) (anti-symmetric) + else + result(J) := 5x"05"; -- A1*(D+B2) (symmetric) + end if; + end loop; + return result; + end; + constant INMODE_CONST : INMODE_ARR_T(JLOW to TAPS-1) := ALL_INMODE_INIT; + + -- ------------------------------------------------------------------------- + -- Architecture-level per-stage signal arrays + -- Declared here (NOT inside for-generate) so each array element has exactly + -- ONE driver -> no xsim signal-sharing / multiple-driver X propagation. + -- ------------------------------------------------------------------------- + signal A_ARR : SFIXED_VECTOR(JLOW to TAPS-1)(C_HIGH downto C_LOW); + signal B_ARR : SFIXED_VECTOR(JLOW to TAPS-1)(BI'high downto BI'low); + signal C_ARR : SFIXED_VECTOR(JLOW to TAPS-1)(P_HIGH downto P_LOW); + signal P_ARR : SFIXED_VECTOR(JLOW to TAPS-1)(P_HIGH downto P_LOW); + signal CE_SIG, CEB1_SIG, CEC_SIG, CED_SIG : STD_LOGIC; + +begin + assert CSA'length=MAX(LOG2(COEFFICIENT'length(1)),1) report "Inconsistent CSA length!" severity warning; + + -- Cascade boundary initial values (tie chain input to zero) + BC(JLOW) <= STD_LOGIC_VECTOR(TO_SIGNED(0,BC(JLOW)'length)); + PC(JLOW) <= STD_LOGIC_VECTOR(TO_SIGNED(0,PC(JLOW)'length)); + + -- Clock enable signals (same for all DSP stages) + CE_SIG <= '1' when VI else '0'; + CEB1_SIG <= '1' when CEBI else '0'; + CEC_SIG <= '1' when CECI else '0'; + CED_SIG <= '1' when CEDI else '0'; + + -- A_ARR: select per-stage coefficient from the elaboration-time constant AV_ALL. + -- Single process owns all elements; no for-generate concurrent-assignment bug. + ia: if CSA'length=1 generate + -- Combinatorial select (single coeff set or runtime-selected with 1-bit CSA). + -- Explicit sensitivity list instead of process(all): xsim 2025.2 may not + -- auto-detect SFIXED/UNSIGNED ports read through composite array indexing. + process(CSA) begin + for J in JLOW to TAPS-1 loop + A_ARR(J) <= AV_ALL(J)(TO_INTEGER(CSA)); + end loop; + end process; + else generate + -- Registered select: pipeline CSA through one CLK stage + process(CLK) begin + if rising_edge(CLK) then + for J in JLOW to TAPS-1 loop + A_ARR(J) <= AV_ALL(J)(TO_INTEGER(CSA)); + end loop; + end if; + end process; + end generate; + + -- B_ARR: first stage and all NS stages get BI; remainder get 0. + -- Explicit sensitivity list: xsim may not auto-detect BI (SFIXED port) via process(all). + gen_b: process(BI) begin + for J in JLOW to TAPS-1 loop + if (J = JLOW) or (SYMMETRY = "NS") then + B_ARR(J) <= BI; + else + B_ARR(J) <= (others => '0'); + end if; + end loop; + end process; + + -- C_ARR: rounding constant at first stage, RESIZE(CI) at last, 0 elsewhere. + -- Explicit sensitivity list: xsim may not auto-detect CI (SFIXED port) via process(all), + -- which would cause C_ARR(TAPS-1) to be stuck at 0, breaking the inter-chain cascade. + gen_c: process(CI) begin + for J in JLOW to TAPS-1 loop + if ROUNDING and (J = JLOW) then + C_ARR(J) <= TO_SFIXED(2.0**(P_LOW+O'low-1), P_HIGH, P_LOW); + elsif J = TAPS-1 then + C_ARR(J) <= RESIZE(CI, P_HIGH, P_LOW); + else + C_ARR(J) <= TO_SFIXED(0.0, P_HIGH, P_LOW); + end if; + end loop; + end process; + + -- Output: last DSP stage P register + O <= RESIZE(P_ARR(TAPS-1), O); + + -- ------------------------------------------------------------------------- + -- DSP cascade for-generate. + -- ONLY entity instantiations appear here; xsim evaluates entity port maps + -- correctly for every for-generate iteration so J-indexed signal accesses + -- (A_ARR(J), B_ARR(J), BC(J), PC(J), etc.) connect to the right elements. + -- Generic map expressions using J (BREG, B_INPUT, Y offset) also evaluate + -- correctly since generics are set at elaboration time. + -- ------------------------------------------------------------------------- + lj: for J in JLOW to TAPS-1 generate + jk: entity work.DSPx8_WRAPPER + generic map(X => OX, + Y => OY+J-JLOW, + FAMILY => FAMILY, + BREG => BREG(SYMMETRY,ANTI,J), + BCASCREG => BREG(SYMMETRY,ANTI,J), + B_INPUT => B_INPUT(J), + PREADDINSEL => "B", + AMULTSEL => "A", + BMULTSEL => "AD") + port map(CLK => CLK, + CEB1 => CEB1_SIG, + CEB2 => CE_SIG, + CEC => CEC_SIG, + CED => CED_SIG, + A => A_ARR(J), + B => B_ARR(J), + C => C_ARR(J), + D => DI, + BCIN => BC(J), + INMODE => INMODE_CONST(J), + OPMODE => OPMODE_CONST(J), + PCIN => PC(J), + BCOUT => BC(J+1), + PCOUT => PC(J+1), + P => P_ARR(J)); + end generate lj; + + -- DO: forward-data pipeline register (used when cascading chain sections) + process(CLK) + begin + if rising_edge(CLK) then + DO <= DI; + end if; + end process; + + -- BO: reverse-data shift register for cascading symmetric-filter sections + db: entity work.SDELAY generic map(SIZE => 2*TAPS+BREG(SYMMETRY,ANTI,0)-1, + DDR => DDR) + port map(CLK => CLK, + CLKH => CLKH, + I => BI, + O => BO); + + -- Data Valid Delay Line + bd: entity work.BDELAY generic map(SIZE => LATENCY(SYMMETRY,TAPS), + DDR => DDR) + port map(CLK => CLK, + CLKH => CLKH, + I => VI, + O => VO); + +end TEST; diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/TYPES_PKG.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/TYPES_PKG.vhd deleted file mode 100755 index c8712fe76..000000000 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/TYPES_PKG.vhd +++ /dev/null @@ -1,776 +0,0 @@ --- Copyright (C) 2024, Advanced Micro Devices, Inc. - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use ieee.math_real.all; -use ieee.math_complex.all; - -package TYPES_PKG is --- constant I_SIZE:INTEGER:=16; --- constant C_SIZE:INTEGER:=16; --- constant O_SIZE:INTEGER:=36; --- type BOOLEAN_VECTOR is array(NATURAL range <>) of BOOLEAN; --- type INTEGER_VECTOR is array(INTEGER range <>) of INTEGER; --- type REAL_VECTOR is array(INTEGER range <>) of REAL; --- type COMPLEX_VECTOR is array(INTEGER range <>) of COMPLEX; --- type I_VECTOR is array(INTEGER range <>) of STD_LOGIC_VECTOR(I_SIZE-1 downto 0); --- type O_VECTOR is array(INTEGER range <>) of STD_LOGIC_VECTOR(O_SIZE-1 downto 0); --- type SI_VECTOR is array(INTEGER range <>) of SIGNED(I_SIZE-1 downto 0); --- type SO_VECTOR is array(INTEGER range <>) of SIGNED(O_SIZE-1 downto 0); --- type SI_MATRIX is array(INTEGER range <>,INTEGER range <>) of SIGNED(I_SIZE-1 downto 0); - - function LOG2(N:INTEGER) return INTEGER; - --- type REAL_VECTOR is array(INTEGER range <>) of REAL; - type COMPLEX_VECTOR is array(INTEGER range <>) of COMPLEX; - - type SFIXED is array(INTEGER range <>) of STD_LOGIC; - type SFIXED_VECTOR is array(INTEGER range <>,INTEGER range <>) of STD_LOGIC; - type SFIXED_MATRIX is array(INTEGER range <>,INTEGER range <>,INTEGER range <>) of STD_LOGIC; - - type CFIXED is array(INTEGER range <>) of STD_LOGIC; -- CFIXED'high must be odd and CFIXED'low must be even - type CFIXED_VECTOR is array(INTEGER range <>,INTEGER range <>) of STD_LOGIC; -- CFIXED'high(2) must be odd and CFIXED'low(2) must be even - type CFIXED_MATRIX is array(INTEGER range <>,INTEGER range <>,INTEGER range <>) of STD_LOGIC; -- CFIXED'high(3) must be odd and CFIXED'low(3) must be even - - function MIN(A,B:INTEGER) return INTEGER; - function MIN(A,B,C:INTEGER) return INTEGER; - function MIN(A,B,C,D:INTEGER) return INTEGER; - function MAX(A,B:INTEGER) return INTEGER; - function MAX(A,B,C:INTEGER) return INTEGER; - function MAX(A,B,C,D:INTEGER) return INTEGER; - function MED(A,B,C:INTEGER) return INTEGER; - function "+"(X,Y:SFIXED) return SFIXED; -- full precision add with SFIXED(MAX(X'high,Y'high)+1 downto MIN(X'low,Y'low)) result - function "-"(X,Y:SFIXED) return SFIXED; -- full precision subtract with SFIXED(MAX(X'high,Y'high)+1 downto MIN(X'low,Y'low)) result - function "-"(X:SFIXED) return SFIXED; -- full precision negate with SFIXED(X'high+1 downto X'low) result - function "*"(X,Y:SFIXED) return SFIXED; -- full precision multiply with SFIXED(X'high+Y'high+1 downto X'low+Y'low) result - function "*"(X:SFIXED;Y:STD_LOGIC) return SFIXED; -- multiply by 0 or 1 with SFIXED(X'high downto X'low) result - function RESIZE(X:SFIXED;H,L:INTEGER) return SFIXED; -- resizes X and returns SFIXED(H downto L) - function RESIZE(X:SFIXED;HL:SFIXED) return SFIXED; -- resizes X to match HL and returns SFIXED(HL'high downto HL'low) - function SHIFT_RIGHT(X:SFIXED;N:INTEGER) return SFIXED; -- returns SFIXED(X'high-N downto X'low-N) result - function SHIFT_LEFT(X:SFIXED;N:INTEGER) return SFIXED; -- returns SFIXED(X'high+N downto X'low+N) result - function TO_SFIXED(R:REAL;H,L:INTEGER) return SFIXED; -- returns SFIXED(H downto L) result - function TO_SFIXED(R:REAL;HL:SFIXED) return SFIXED; -- returns SFIXED(HL'high downto HL'low) result - function TO_SFIXED(I:SIGNED;H,L:INTEGER;SHIFT:INTEGER:=0) return SFIXED; -- convert SIGNED to SFIXED(H downto L) and resize - function TO_SFIXED(I:SIGNED;X:SFIXED;SHIFT:INTEGER:=0) return SFIXED; -- convert SIGNED to SFIXED(X'high downto X'low) and resize - function TO_SIGNED(I:SFIXED;X:SFIXED) return SIGNED; -- resize I to X and convert to SIGNED(I'length-1 downto 0), returns SIGNED(SHIFT_RIGHT(RESIZE(I,X),X'low)) - function TO_REAL(S:SFIXED) return REAL; -- returns REAL result - function TO_01(X:SFIXED) return SFIXED; -- get rid of Xes --- all SET calls targetting the same signal must be located in a single process! - function GET(A:SFIXED_VECTOR;I:INTEGER) return SFIXED; -- A(I) - function GET(A:SFIXED_MATRIX;I,J:INTEGER) return SFIXED; -- A(I,J) - function GET(A:SFIXED_MATRIX;I:INTEGER) return SFIXED_VECTOR; -- A(I) - procedure SET(signal A:inout SFIXED_VECTOR;constant I:INTEGER;signal V:SFIXED); -- A(I)<=V; - procedure SET(signal A:inout SFIXED_MATRIX;constant I,J:INTEGER;signal V:SFIXED); -- A(I,J)<=V; - procedure SET(signal A:inout SFIXED_MATRIX;constant I:INTEGER;signal V:SFIXED_VECTOR); -- A(I)<=V; - - function RE(A:CFIXED) return SFIXED; -- A.RE - function IM(A:CFIXED) return SFIXED; -- A.IM - procedure RE(signal A:inout CFIXED;constant V:SFIXED); -- A.RE<=RESIZE(V,A.RE); - procedure IM(signal A:inout CFIXED;constant V:SFIXED); -- A.IM<=RESIZE(V,A.IM); - function "+"(X,Y:CFIXED) return CFIXED; -- full precision add with CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result - function "-"(X,Y:CFIXED) return CFIXED; -- full precision subtract with CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result - function "*"(X,Y:CFIXED) return CFIXED; -- full precision multiply with CFIXED(X'high+Y'high+2 downto X'low+Y'low) result - function "*"(X:CFIXED;Y:SFIXED) return CFIXED; -- full precision multiply with CFIXED(X'high+Y'high downto X'low+Y'low) result - function "*"(X:SFIXED;Y:CFIXED) return CFIXED; - function RESIZE(X:CFIXED;H,L:INTEGER) return CFIXED; -- resizes X and returns CFIXED(RE(H downto L),IM(H downto L)) - function RESIZE(X:CFIXED;HL:CFIXED) return CFIXED; -- resizes X to match HL and returns CFIXED(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) - function PLUS_i_TIMES(X:CFIXED) return CFIXED; -- returns CFIXED(X'high+2 downto X'low) result - function "-"(X:CFIXED) return CFIXED; -- full precision negate with CFIXED(X'high+2 downto X'low) result - function MINUS_i_TIMES(X:CFIXED) return CFIXED; -- returns CFIXED(X'high+2 downto X'low) result - function X_PLUS_i_TIMES_Y(X,Y:CFIXED;RND:CFIXED) return CFIXED; -- returns CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result - function X_MINUS_i_TIMES_Y(X,Y:CFIXED;RND:CFIXED) return CFIXED; -- returns CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result - function SWAP(X:CFIXED) return CFIXED; -- returns CFIXED(X'high downto X'low) result - function CONJ(X:CFIXED) return CFIXED; -- returns CFIXED(X'high+2 downto X'low) result - function SHIFT_RIGHT(X:CFIXED;N:INTEGER) return CFIXED; -- returns CFIXED(X'high-N downto X'low-N) result - function SHIFT_LEFT(X:CFIXED;N:INTEGER) return CFIXED; -- returns CFIXED(X'high+N downto X'low+N) result - function TO_CFIXED(R,I:REAL;H,L:INTEGER) return CFIXED; -- returns CFIXED(RE(H downto L),IM(H downto L)) result - function TO_CFIXED(R,I:REAL;HL:CFIXED) return CFIXED; -- returns CFIXED(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) result - function TO_CFIXED(C:COMPLEX;HL:CFIXED) return CFIXED; -- returns CFIXED(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) result - function TO_CFIXED(R,I:SFIXED) return CFIXED; -- returns CFIXED(2*MAX(R'high,I'high)+1 downto 2*MIN(R'low,I'low)) result - function TO_COMPLEX(C:CFIXED) return COMPLEX; -- returns COMPLEX result -/* - function TO_CFIXED_VECTOR(C:COMPLEX_VECTOR;HL:CFIXED) return CFIXED_VECTOR; -- returns CFIXED_VECTOR(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) result - function TO_COMPLEX_VECTOR(C:CFIXED_VECTOR) return COMPLEX_VECTOR; -- returns COMPLEX_VECTOR result -*/ - function "*"(R:REAL;C:COMPLEX_VECTOR) return COMPLEX_VECTOR; -- returns R*C - function ZERO(U:UNSIGNED) return UNSIGNED; -- returns 0 with the same range as U - function ZERO(S:SFIXED) return SFIXED; -- returns 0 with the same range as S - function ZERO(C:CFIXED) return CFIXED; -- returns 0 with the same range as C - function ZERO(SV:SFIXED_VECTOR) return SFIXED_VECTOR; -- returns 0 with the same range as SV - function ZERO(CV:CFIXED_VECTOR) return CFIXED_VECTOR; -- returns 0 with the same range as CV - - function GET(A:CFIXED_VECTOR;I:INTEGER) return CFIXED; -- A(I) - function GET(A:CFIXED_MATRIX;I,J:INTEGER) return CFIXED; -- A(I,J) - function GET(A:CFIXED_MATRIX;I:INTEGER) return CFIXED_VECTOR; -- A(I) - procedure SET(signal A:inout CFIXED_VECTOR;constant I:INTEGER;signal V:CFIXED); -- A(I)<=V; - procedure SET(signal A:inout CFIXED_MATRIX;constant I,J:INTEGER;signal V:CFIXED); -- A(I,J)<=V; - procedure SET(signal A:inout CFIXED_MATRIX;constant I:INTEGER;signal V:CFIXED_VECTOR); -- A(I)<=V; -end TYPES_PKG; - -package body TYPES_PKG is - function LOG2(N:INTEGER) return INTEGER is - variable TEMP:INTEGER; - variable RESULT:INTEGER; - begin - TEMP:=N; - RESULT:=0; - while TEMP>1 loop - RESULT:=RESULT+1; - TEMP:=(TEMP+1)/2; - end loop; - return RESULT; - end; - - function MIN(A,B:INTEGER) return INTEGER is - begin - if AB then - return A; - else - return B; - end if; - end; - - function MAX(A,B,C:INTEGER) return INTEGER is - begin - return MAX(MAX(A,B),C); - end; - - function MAX(A,B,C,D:INTEGER) return INTEGER is - begin - return MAX(MAX(A,B),MAX(C,D)); - end; - - function MED(A,B,C:INTEGER) return INTEGER is - begin - return MAX(MIN(MAX(A,B),C),MIN(A,B)); - end; - - function "+"(X,Y:SFIXED) return SFIXED is - variable SX,SY,SR:SIGNED(MAX(X'high,Y'high)+1-MIN(X'low,Y'low) downto 0); - variable R:SFIXED(MAX(X'high,Y'high)+1 downto MIN(X'low,Y'low)); - begin - for K in SX'range loop - if KX'high-R'low then - SX(K):=X(X'high); -- sign extend X MSBs - else - SX(K):=X(R'low+K); - end if; - end loop; - for K in SY'range loop - if KY'high-R'low then - SY(K):=Y(Y'high); -- sign extend Y MSBs - else - SY(K):=Y(R'low+K); - end if; - end loop; - SR:=SX+SY; -- SIGNED addition - for K in SR'range loop - R(R'low+K):=SR(K); - end loop; - return R; - end; - - function "-"(X,Y:SFIXED) return SFIXED is - variable SX,SY,SR:SIGNED(MAX(X'high,Y'high)+1-MIN(X'low,Y'low) downto 0); - variable R:SFIXED(MAX(X'high,Y'high)+1 downto MIN(X'low,Y'low)); - begin - for K in SX'range loop - if KX'high-R'low then - SX(K):=X(X'high); -- sign extend X MSBs - else - SX(K):=X(R'low+K); - end if; - end loop; - for K in SY'range loop - if KY'high-R'low then - SY(K):=Y(Y'high); -- sign extend Y MSBs - else - SY(K):=Y(R'low+K); - end if; - end loop; - SR:=SX-SY; -- SIGNED subtraction - for K in SR'range loop - R(R'low+K):=SR(K); - end loop; - return R; - end; - - function "-"(X:SFIXED) return SFIXED is - variable SX:SIGNED(X'high-X'low downto 0); - variable SR:SIGNED(X'high-X'low+1 downto 0); - variable R:SFIXED(X'high+1 downto X'low); - begin - for K in SX'range loop - SX(K):=X(X'low+K); - end loop; - SR:=-RESIZE(SX,SR'length); -- SIGNED negation - for K in SR'range loop - R(R'low+K):=SR(K); - end loop; - return R; - end; - - function "*"(X,Y:SFIXED) return SFIXED is - variable SX:SIGNED(X'high-X'low downto 0); - variable SY:SIGNED(Y'high-Y'low downto 0); - variable SR:SIGNED(SX'high+SY'high+1 downto 0); - variable R:SFIXED(X'high+Y'high+1 downto X'low+Y'low); - begin - for K in SX'range loop - SX(K):=X(X'low+K); - end loop; - for K in SY'range loop - SY(K):=Y(Y'low+K); - end loop; - SR:=SX*SY; -- SIGNED multiplication - for K in SR'range loop - R(R'low+K):=SR(K); - end loop; - return R; - end; - - function "*"(X:SFIXED;Y:STD_LOGIC) return SFIXED is - begin - if Y='1' then - return X; - else - return TO_SFIXED(0.0,X); - end if; - end; - - function RESIZE(X:SFIXED;H,L:INTEGER) return SFIXED is - variable R:SFIXED(H downto L); - begin - for K in R'range loop - if KX'high then - R(K):=X(X'high); -- sign extend X MSBs - else - R(K):=X(K); - end if; - end loop; - return R; - end; - - function RESIZE(X:SFIXED;HL:SFIXED) return SFIXED is - begin - return RESIZE(X,HL'high,HL'low); - end; - - function SHIFT_RIGHT(X:SFIXED;N:INTEGER) return SFIXED is - variable R:SFIXED(X'high-N downto X'low-N); - begin - for K in R'range loop - R(K):=X(K+N); - end loop; - return R; - end; - - function SHIFT_LEFT(X:SFIXED;N:INTEGER) return SFIXED is - variable R:SFIXED(X'high+N downto X'low+N); - begin - for K in R'range loop - R(K):=X(K-N); - end loop; - return R; - end; - - function TO_SFIXED(R:REAL;H,L:INTEGER) return SFIXED is - variable RR:REAL; - variable V:SFIXED(H downto L); - begin - assert (R<2.0**H) and (R>=-2.0**H) report "TO_SFIXED vector truncation!" severity warning; - if R<0.0 then - V(V'high):='1'; - RR:=R+2.0**V'high; - else - V(V'high):='0'; - RR:=R; - end if; - for K in V'high-1 downto V'low loop - if RR>=2.0**K then - V(K):='1'; - RR:=RR-2.0**K; - else - V(K):='0'; - end if; - end loop; - return V; - end; - - function TO_SFIXED(R:REAL;HL:SFIXED) return SFIXED is - begin - return TO_SFIXED(R,HL'high,HL'low); - end; - - function TO_SFIXED(I:SIGNED;H,L:INTEGER;SHIFT:INTEGER:=0) return SFIXED is - variable O:SFIXED(H downto L); - begin - for K in O'range loop - if KV'high then - A(I,K)<=V(V'high); -- sign extend V MSBs - else - A(I,K)<=V(K); - end if; - end loop; - end; - - procedure SET(signal A:inout SFIXED_MATRIX;constant I,J:INTEGER;signal V:SFIXED) is - begin - for K in A'range(3) loop - if KV'high then - A(I,J,K)<=V(V'high); -- sign extend V MSBs - else - A(I,J,K)<=V(K); - end if; - end loop; - end; - - procedure SET(signal A:inout SFIXED_MATRIX;constant I:INTEGER;signal V:SFIXED_VECTOR) is - begin - for J in V'range(1) loop - for K in A'range(3) loop - if KV'high(2) then - A(I,J,K)<=V(J,V'high(2)); -- sign extend V(J) MSBs - else - A(I,J,K)<=V(J,K); - end if; - end loop; - end loop; - end; - - function RE(A:CFIXED) return SFIXED is - variable R:SFIXED(A'high/2 downto A'low/2); - begin - for K in R'range loop - R(K):=A(2*K); - end loop; - return R; - end; - - function IM(A:CFIXED) return SFIXED is - variable R:SFIXED(A'high/2 downto A'low/2); - begin - for K in R'range loop - R(K):=A(2*K+1); - end loop; - return R; - end; - - procedure RE(signal A:inout CFIXED;constant V:SFIXED) is - begin - for K in A'low/2 to A'high/2 loop - if KV'high then - A(2*K)<=V(V'high); -- sign extend V MSBs - else - A(2*K)<=V(K); - end if; - end loop; - end; - - procedure IM(signal A:inout CFIXED;constant V:SFIXED) is - begin - for K in A'low/2 to A'high/2 loop - if KV'high then - A(2*K+1)<=V(V'high); -- sign extend V MSBs - else - A(2*K+1)<=V(K); - end if; - end loop; - end; - - function "+"(X,Y:CFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X)+RE(Y),IM(X)+IM(Y)); - end; - - function "-"(X,Y:CFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X)-RE(Y),IM(X)-IM(Y)); - end; - - function "*"(X,Y:CFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X)*RE(Y)-IM(X)*IM(Y),RE(X)*IM(Y)+IM(X)*RE(Y)); - end; - - function "*"(X:CFIXED;Y:SFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X)*Y,IM(X)*Y); - end; - - function "*"(X:SFIXED;Y:CFIXED) return CFIXED is - begin - return TO_CFIXED(X*RE(Y),X*IM(Y)); - end; - - function RESIZE(X:CFIXED;H,L:INTEGER) return CFIXED is - begin - return TO_CFIXED(RESIZE(RE(X),H,L),RESIZE(IM(X),H,L)); - end; - - function RESIZE(X:CFIXED;HL:CFIXED) return CFIXED is - begin - return TO_CFIXED(RESIZE(RE(X),RE(HL)),RESIZE(IM(X),IM(HL))); - end; - - function PLUS_i_TIMES(X:CFIXED) return CFIXED is - begin - return TO_CFIXED(-IM(X),RE(X)); - end; - - function "-"(X:CFIXED) return CFIXED is - begin - return TO_CFIXED(-RE(X),-IM(X)); - end; - - function MINUS_i_TIMES(X:CFIXED) return CFIXED is - begin - return TO_CFIXED(IM(X),-RE(X)); - end; - - function X_PLUS_i_TIMES_Y(X,Y:CFIXED;RND:CFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X)-IM(Y)+RE(RND),IM(X)+RE(Y)+IM(RND)); - end; - - function X_MINUS_i_TIMES_Y(X,Y:CFIXED;RND:CFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X)+IM(Y)+RE(RND),IM(X)-RE(Y)+IM(RND)); - end; - - function SWAP(X:CFIXED) return CFIXED is - begin - return TO_CFIXED(IM(X),RE(X)); - end; - - function CONJ(X:CFIXED) return CFIXED is - begin - return TO_CFIXED(RE(X),-IM(X)); - end; - - function SHIFT_RIGHT(X:CFIXED;N:INTEGER) return CFIXED is - begin - return TO_CFIXED(SHIFT_RIGHT(RE(X),N),SHIFT_RIGHT(IM(X),N)); - end; - - function SHIFT_LEFT(X:CFIXED;N:INTEGER) return CFIXED is - begin - return TO_CFIXED(SHIFT_LEFT(RE(X),N),SHIFT_LEFT(IM(X),N)); - end; - - function TO_CFIXED(R,I:REAL;H,L:INTEGER) return CFIXED is - begin - return TO_CFIXED(TO_SFIXED(R,H,L),TO_SFIXED(I,H,L)); - end; - - function TO_CFIXED(R,I:REAL;HL:CFIXED) return CFIXED is - begin - return TO_CFIXED(TO_SFIXED(R,HL'high/2,HL'low/2),TO_SFIXED(I,HL'high/2,HL'low/2)); - end; - - function TO_CFIXED(C:COMPLEX;HL:CFIXED) return CFIXED is - begin - return TO_CFIXED(C.RE,C.IM,HL); - end; - - function TO_CFIXED(R,I:SFIXED) return CFIXED is - constant H:INTEGER:=MAX(R'high,I'high); - constant L:INTEGER:=MIN(R'low,I'low); - variable C:CFIXED(2*H+1 downto 2*L); - begin - for K in C'low/2 to C'high/2 loop - if KR'high then - C(2*K):=R(R'high); -- sign extend R MSBs - else - C(2*K):=R(K); - end if; - end loop; - for K in C'low/2 to C'high/2 loop - if KI'high then - C(2*K+1):=I(I'high); -- sign extend I MSBs - else - C(2*K+1):=I(K); - end if; - end loop; - return C; - end; - - function TO_COMPLEX(C:CFIXED) return COMPLEX is - variable R:COMPLEX; - begin - R.RE:=TO_REAL(RE(C)); - R.IM:=TO_REAL(IM(C)); - return R; - end; -/* - function TO_CFIXED_VECTOR(C:COMPLEX_VECTOR;HL:CFIXED) return CFIXED_VECTOR is - variable R:CFIXED_VECTOR(C'range,HL'range); - begin - for K in C'range loop - R(K):=TO_CFIXED(C(K),HL); - end loop; - return R; - end; - - function TO_COMPLEX_VECTOR(C:CFIXED_VECTOR) return COMPLEX_VECTOR is - variable R:COMPLEX_VECTOR(C'range); - begin - for K in C'range loop - R(K):=TO_COMPLEX(C(K)); - end loop; - return R; - end; -*/ - function "*"(R:REAL;C:COMPLEX_VECTOR) return COMPLEX_VECTOR is - variable X:COMPLEX_VECTOR(C'range); - begin - for K in C'range loop - X(K):=R*C(K); - end loop; - return X; - end; - - function ZERO(U:UNSIGNED) return UNSIGNED is -- returns 0 with the same range as S - begin - return TO_UNSIGNED(0,U'length); - end; - - function ZERO(S:SFIXED) return SFIXED is -- returns 0 with the same range as S - begin - return TO_SFIXED(0.0,S); - end; - - function ZERO(C:CFIXED) return CFIXED is -- returns 0 with the same range as C - begin - return TO_CFIXED(0.0,0.0,C); - end; - - function ZERO(SV:SFIXED_VECTOR) return SFIXED_VECTOR is -- returns 0 with the same range as SV - variable RESULT:SFIXED_VECTOR(SV'range(1),SV'range(2)); - begin - RESULT:=(others=>(others=>'0')); - return RESULT; - end; - - function ZERO(CV:CFIXED_VECTOR) return CFIXED_VECTOR is -- returns 0 with the same range as CV - variable RESULT:CFIXED_VECTOR(CV'range(1),CV'range(2)); - begin - RESULT:=(others=>(others=>'0')); - return RESULT; - end; - - function GET(A:CFIXED_VECTOR;I:INTEGER) return CFIXED is - variable R:CFIXED(A'range(2)); - begin - for K in R'range loop - R(K):=A(I,K); - end loop; - return R; - end; - - function GET(A:CFIXED_MATRIX;I,J:INTEGER) return CFIXED is - variable R:CFIXED(A'range(3)); - begin - for K in R'range loop - R(K):=A(I,J,K); - end loop; - return R; - end; - - function GET(A:CFIXED_MATRIX;I:INTEGER) return CFIXED_VECTOR is - variable R:CFIXED_VECTOR(A'range(1),A'range(3)); - begin - for J in R'range(1) loop - for K in R'range(2) loop - R(J,K):=A(I,J,K); - end loop; - end loop; - return R; - end; - - procedure SET(signal A:inout CFIXED_VECTOR;constant I:INTEGER;signal V:CFIXED) is - begin - for K in A'range(2) loop - if KV'high then - A(I,K)<=V(V'high+K mod 2-1); -- sign extend V.RE and V.IM MSBs - else - A(I,K)<=V(K); - end if; - end loop; - end; - - procedure SET(signal A:inout CFIXED_MATRIX;constant I,J:INTEGER;signal V:CFIXED) is - begin - for K in A'range(3) loop - if KV'high then - A(I,J,K)<=V(V'high+K mod 2-1); -- sign extend V.RE and V.IM MSBs - else - A(I,J,K)<=V(K); - end if; - end loop; - end; - - procedure SET(signal A:inout CFIXED_MATRIX;constant I:INTEGER;signal V:CFIXED_VECTOR) is - begin - for J in V'range(1) loop - for K in A'range(3) loop - if KV'high(2) then - A(I,J,K)<=V(J,V'high(2)+K mod 2-1); -- sign extend V.RE and V.IM MSBs - else - A(I,J,K)<=V(J,K); - end if; - end loop; - end loop; - end; -end TYPES_PKG; \ No newline at end of file diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR.vhd index 52d262162..23d2a2648 100755 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR.vhd +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR.vhd @@ -1,4 +1,11 @@ --- Copyright (C) 2024, Advanced Micro Devices, Inc. +-- Copyright (C) 2026, Advanced Micro Devices, Inc. +-- +-- Vitis Model Composer HDL Black Box wrapper for the Direct-Form SSR FIR +-- (SSR_FIR.vhd). This wrapper adapts the System Generator / Model Composer +-- STD_LOGIC_VECTOR black-box interface to the SFIXED_VECTOR interface of the +-- VHDL-2008 SSR_FIR core, builds the REAL_MATRIX coefficient set from the +-- string generic passed in by the config script, and ties the single +-- coefficient-set address CSA to 0. library IEEE; use IEEE.STD_LOGIC_1164.ALL; @@ -8,18 +15,18 @@ library STD; use STD.textio.all; use STD.env.all; -use work.TYPES_PKG.all; +use work.FIXED_PKG.all; -- VHDL-2008 IEEE standard SFIXED type and functions +use work.CFIXED_PKG.all; -- SFIXED_VECTOR, REAL_VECTOR, REAL_MATRIX and helpers library UNISIM; use UNISIM.VComponents.all; entity WRAPPER_SSR_FIR is - generic(OX,OY:INTEGER:=-1;---2147483648; -- Coordinates of the lower left corner DSPx8, use -2147483648 if you do not want floorplanning + generic(OX,OY:INTEGER:=-1; -- Coordinates of the lower left corner DSPx8, use -1 if you do not want floorplanning FAMILY:INTEGER:=3; -- use 1 for 7-series, 2 for US/US+ and 3 for Versal - DDR:BOOLEAN:=FALSE; -- use DDR=TRUE implementation only for Versal clock rates over 795MHz/824MHz/872MHz in speed grades -1/-2/-3 + DDR:BOOLEAN:=FALSE; -- use DDR=TRUE implementation only for Versal clock rates over 795MHz/824MHz/872MHz in speed grades -1/-2/-3 SSR:INTEGER:=4; TAPS:INTEGER:=36; --- COEFFICIENT:REAL_VECTOR(0 to TAPS-1):=(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0); C:STRING:="1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0"; I_HIGH:INTEGER:=7; I_LOW:INTEGER:=-8; @@ -27,7 +34,7 @@ entity WRAPPER_SSR_FIR is C_LOW:INTEGER:=-17; O_HIGH:INTEGER:=7; O_LOW:INTEGER:=-16; - ROUNDING:BOOLEAN:=FALSE; -- use TRUE for rounding the output result + ROUNDING:BOOLEAN:=FALSE; -- use TRUE for rounding the output result SYMMETRY:STRING:="ES"); -- Filter Symmetry, use "ES" for Even-Symmetric, "OS" for Odd-Symmetric and "NS" for Non-Symmetric port(CLK:in STD_LOGIC; CE:in STD_LOGIC:='1'; -- not used, for SysGen only @@ -38,9 +45,9 @@ entity WRAPPER_SSR_FIR is end WRAPPER_SSR_FIR; architecture WRAPPER of WRAPPER_SSR_FIR is - signal II:SFIXED_VECTOR(0 to SSR-1,I_HIGH downto I_LOW); + signal II:SFIXED_VECTOR(0 to SSR-1)(I_HIGH downto I_LOW); signal VII:BOOLEAN; - signal OO:SFIXED_VECTOR(0 to SSR-1,O_HIGH downto O_LOW); + signal OO:SFIXED_VECTOR(0 to SSR-1)(O_HIGH downto O_LOW); signal VOO:BOOLEAN; function REAL_value(S:STRING) return REAL is variable R,F:REAL; @@ -116,32 +123,32 @@ architecture WRAPPER of WRAPPER_SSR_FIR is return R*10.0**E; end if; end; - function STRING2REAL(S:STRING;N:INTEGER) return REAL_VECTOR is - variable R:REAL_VECTOR(1 to N); - variable P,C:INTEGER; + -- Parse a whitespace/comma separated coefficient string into the first N + -- coefficients, returned as a single-row REAL_MATRIX (one coefficient set). + function STRING2MATRIX(S:STRING;N:INTEGER) return REAL_MATRIX is + variable R:REAL_MATRIX(0 to 0)(0 to N-1); + variable P,CNT:INTEGER; begin P:=S'low; if (S(P)='(') or (S(P)='[') then P:=P+1; end if; - for K in R'range loop + for K in R(0)'range loop while (P0 --- REAL'value() not supported in Vivado Synthesis --- then R(K):=REAL'value(S(P to P+C-1)); - then R(K):=REAL_value(S(P to P+C-1)); - else R(K):=0.0; + if CNT>0 +-- REAL'value() not supported in Vivado Synthesis, use REAL_value instead + then R(0)(K):=REAL_value(S(P to P+CNT-1)); + else R(0)(K):=0.0; end if; - P:=P+C; + P:=P+CNT; end loop; return R; end; --- constant COEFFICIENT:REAL_VECTOR(0 to TAPS-1):=(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0); - constant COEFFICIENT:REAL_VECTOR(0 to TAPS-1):=STRING2REAL(C,TAPS); + constant COEFFICIENT:REAL_MATRIX(0 to 0)(0 to TAPS-1):=STRING2MATRIX(C,TAPS); signal CLK1,CLKH:STD_LOGIC; begin i1:if DDR generate @@ -156,9 +163,9 @@ begin CLK1<=CLK; CLKH<='0'; end generate; - ij:for J in II'range(1) generate - ik:for K in II'range(2) generate - II(J,K)<=I(J*II'length(2)+K-II'low(2)); + ij:for J in 0 to SSR-1 generate + ik:for K in I_HIGH downto I_LOW generate + II(J)(K)<=I(J*(I_HIGH-I_LOW+1)+K-I_LOW); end generate; end generate; VII<=VI='1'; @@ -170,17 +177,19 @@ begin C_HIGH=>C_HIGH, C_LOW=>C_LOW, ROUNDING=>ROUNDING, + ANTI=>FALSE, SYMMETRY=>SYMMETRY) port map (CLK=>CLK1, CLKH=>CLKH, I=>II, VI=>VII, + CSA=>"0", O=>OO, VO=>VOO); - oj:for J in OO'range(1) generate - ok:for K in OO'range(2) generate - O(J*OO'length(2)+K-OO'low(2))<=OO(J,K); + oj:for J in 0 to SSR-1 generate + ok:for K in O_HIGH downto O_LOW generate + O(J*(O_HIGH-O_LOW+1)+K-O_LOW)<=OO(J)(K); end generate; end generate; VO<='1' when VOO else '0'; -end WRAPPER; \ No newline at end of file +end WRAPPER; diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR_config.m b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR_config.m index 0e09885ca..a92eddd71 100755 --- a/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR_config.m +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_SSR_FIR_config.m @@ -157,7 +157,8 @@ function WRAPPER_SSR_FIR_config(this_block) % | this_block.addFile('a.vhd'); % |------------- - this_block.addFile('TYPES_PKG.vhd'); + this_block.addFile('fixed_pkg.vhd'); + this_block.addFile('cfixed_pkg.vhd'); this_block.addFile('BDELAY.vhd'); this_block.addFile('SDELAY.vhd'); this_block.addFile('DSPx8_WRAPPER.vhd'); diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_TRANSPOSE_SSR_FIR.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_TRANSPOSE_SSR_FIR.vhd new file mode 100644 index 000000000..8e2417cd5 --- /dev/null +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_TRANSPOSE_SSR_FIR.vhd @@ -0,0 +1,202 @@ +-- Copyright (C) 2026, Advanced Micro Devices, Inc. +-- +-- Vitis Model Composer HDL Black Box wrapper for the Transpose-Form SSR FIR +-- (TRANSPOSE_SSR_FIR.vhd). This wrapper adapts the System Generator / Model +-- Composer STD_LOGIC_VECTOR black-box interface to the SFIXED_VECTOR interface +-- of the VHDL-2008 TRANSPOSE_SSR_FIR core, builds the REAL_MATRIX coefficient +-- set from the string generic passed in by the config script, and ties the +-- single coefficient-set address CSA to 0. +-- +-- The transpose form has the same external behaviour and latency as the direct +-- form for a given filter, but its DSP PREG/PCOUT accumulation chain depth is +-- TAPS/SSR per lane (vs TAPS in the direct form), so it closes timing at high +-- SSR and large TAPS where the direct form's cascade does not. + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +library STD; +use STD.textio.all; +use STD.env.all; + +use work.FIXED_PKG.all; -- VHDL-2008 IEEE standard SFIXED type and functions +use work.CFIXED_PKG.all; -- SFIXED_VECTOR, REAL_VECTOR, REAL_MATRIX and helpers + +library UNISIM; +use UNISIM.VComponents.all; + +entity WRAPPER_TRANSPOSE_SSR_FIR is + generic(OX,OY:INTEGER:=-1; -- Coordinates of the lower left corner DSPx8, use -1 if you do not want floorplanning + FAMILY:INTEGER:=3; -- use 1 for 7-series, 2 for US/US+ and 3 for Versal + DDR:BOOLEAN:=FALSE; -- use DDR=TRUE implementation only for Versal clock rates over 795MHz/824MHz/872MHz in speed grades -1/-2/-3 + SSR:INTEGER:=4; + TAPS:INTEGER:=36; + C:STRING:="1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0"; + I_HIGH:INTEGER:=7; + I_LOW:INTEGER:=-8; + C_HIGH:INTEGER:=0; + C_LOW:INTEGER:=-17; + O_HIGH:INTEGER:=7; + O_LOW:INTEGER:=-16; + ROUNDING:BOOLEAN:=FALSE; -- use TRUE for rounding the output result + SYMMETRY:STRING:="ES"); -- Filter Symmetry, use "ES" for Even-Symmetric, "OS" for Odd-Symmetric and "NS" for Non-Symmetric + port(CLK:in STD_LOGIC; + CE:in STD_LOGIC:='1'; -- not used, for SysGen only + I:in STD_LOGIC_VECTOR(SSR*(I_HIGH-I_LOW+1)-1 downto 0); + VI:in STD_LOGIC; + O:out STD_LOGIC_VECTOR(SSR*(O_HIGH-O_LOW+1)-1 downto 0); + VO:out STD_LOGIC); +end WRAPPER_TRANSPOSE_SSR_FIR; + +architecture WRAPPER of WRAPPER_TRANSPOSE_SSR_FIR is + signal II:SFIXED_VECTOR(0 to SSR-1)(I_HIGH downto I_LOW); + signal VII:BOOLEAN; + signal OO:SFIXED_VECTOR(0 to SSR-1)(O_HIGH downto O_LOW); + signal VOO:BOOLEAN; + function REAL_value(S:STRING) return REAL is + variable R,F:REAL; + variable E:INTEGER; + variable DECIMAL,NEGATIVE,EXP,NEGEXP:BOOLEAN; + begin + R:=0.0;F:=1.0;E:=0; + DECIMAL:=FALSE; + NEGATIVE:=FALSE; + EXP:=FALSE; + NEGEXP:=FALSE; + for K in 0 to S'length-1 loop + if (S(K+S'low)='E') or (S(K+S'low)='e') then + EXP:=TRUE; + elsif K=0 and S(K+S'low)='-' then + NEGATIVE:=TRUE; + elsif EXP then + if S(K+S'low)='-' then + NEGEXP:=TRUE; + else + case S(K+S'low) is + when '0'=>E:=E*10; + when '1'=>E:=E*10+1; + when '2'=>E:=E*10+2; + when '3'=>E:=E*10+3; + when '4'=>E:=E*10+4; + when '5'=>E:=E*10+5; + when '6'=>E:=E*10+6; + when '7'=>E:=E*10+7; + when '8'=>E:=E*10+8; + when '9'=>E:=E*10+9; + when others=>assert FALSE report "REAL number format error!" severity warning; + end case; + end if; + elsif DECIMAL then + F:=F/10.0; + case S(K+S'low) is + when '0'=>null; + when '1'=>R:=R+1.0*F; + when '2'=>R:=R+2.0*F; + when '3'=>R:=R+3.0*F; + when '4'=>R:=R+4.0*F; + when '5'=>R:=R+5.0*F; + when '6'=>R:=R+6.0*F; + when '7'=>R:=R+7.0*F; + when '8'=>R:=R+8.0*F; + when '9'=>R:=R+9.0*F; + when others=>assert FALSE report "REAL number format error!" severity warning; + end case; + else + case S(K+S'low) is + when '0'=>R:=R*10.0; + when '1'=>R:=R*10.0+1.0; + when '2'=>R:=R*10.0+2.0; + when '3'=>R:=R*10.0+3.0; + when '4'=>R:=R*10.0+4.0; + when '5'=>R:=R*10.0+5.0; + when '6'=>R:=R*10.0+6.0; + when '7'=>R:=R*10.0+7.0; + when '8'=>R:=R*10.0+8.0; + when '9'=>R:=R*10.0+9.0; + when '.'=>DECIMAL:=TRUE; + when others=>assert FALSE report "REAL number format error!" severity warning; + end case; + end if; + end loop; + if NEGEXP then + E:=-E; + end if; + if NEGATIVE then + return -R*10.00**E; + else + return R*10.0**E; + end if; + end; + -- Parse a whitespace/comma separated coefficient string into the first N + -- coefficients, returned as a single-row REAL_MATRIX (one coefficient set). + function STRING2MATRIX(S:STRING;N:INTEGER) return REAL_MATRIX is + variable R:REAL_MATRIX(0 to 0)(0 to N-1); + variable P,CNT:INTEGER; + begin + P:=S'low; + if (S(P)='(') or (S(P)='[') then P:=P+1; end if; + for K in R(0)'range loop + while (P0 +-- REAL'value() not supported in Vivado Synthesis, use REAL_value instead + then R(0)(K):=REAL_value(S(P to P+CNT-1)); + else R(0)(K):=0.0; + end if; + P:=P+CNT; + end loop; + return R; + end; + constant COEFFICIENT:REAL_MATRIX(0 to 0)(0 to TAPS-1):=STRING2MATRIX(C,TAPS); + signal CLK1,CLKH:STD_LOGIC; +begin + i1:if DDR generate + b2:MBUFGCE_DIV port map(I=>CLK, + CE=>'1', + CLR=>'0', + CLRB_LEAF=>'1', + O1=>CLK1, + O2=>CLKH); + end; + else generate + CLK1<=CLK; + CLKH<='0'; + end generate; + ij:for J in 0 to SSR-1 generate + ik:for K in I_HIGH downto I_LOW generate + II(J)(K)<=I(J*(I_HIGH-I_LOW+1)+K-I_LOW); + end generate; + end generate; + VII<=VI='1'; + u1:entity work.TRANSPOSE_SSR_FIR generic map(OX=>OX, + OY=>OY, + FAMILY=>FAMILY, + DDR=>DDR, + COEFFICIENT=>COEFFICIENT, + C_HIGH=>C_HIGH, + C_LOW=>C_LOW, + EXTRA_C_PIPELINING=>TRUE, + EXTRA_D_PIPELINING=>TRUE, + ROUNDING=>ROUNDING, + ANTI=>FALSE, + SYMMETRY=>SYMMETRY) + port map (CLK=>CLK1, + CLKH=>CLKH, + I=>II, + VI=>VII, + CSA=>"0", + O=>OO, + VO=>VOO); + oj:for J in 0 to SSR-1 generate + ok:for K in O_HIGH downto O_LOW generate + O(J*(O_HIGH-O_LOW+1)+K-O_LOW)<=OO(J)(K); + end generate; + end generate; + VO<='1' when VOO else '0'; +end WRAPPER; diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_TRANSPOSE_SSR_FIR_config.m b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_TRANSPOSE_SSR_FIR_config.m new file mode 100644 index 000000000..acb65b643 --- /dev/null +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/WRAPPER_TRANSPOSE_SSR_FIR_config.m @@ -0,0 +1,196 @@ + +function WRAPPER_TRANSPOSE_SSR_FIR_config(this_block) + + % Vitis Model Composer HDL Black Box configuration script for the + % Transpose-Form SSR FIR (WRAPPER_TRANSPOSE_SSR_FIR.vhd). + % + % This is the transpose-form counterpart of WRAPPER_SSR_FIR_config.m; it + % shares the same mask parameter set and the same fixed_pkg/cfixed_pkg base + % files, and only differs in the entity name and the design source files + % added at the bottom. + + this_block.setTopLevelLanguage('Verilog'); + + this_block.setEntityName('WRAPPER_TRANSPOSE_SSR_FIR'); + + parent = get_param(this_block.blockName,'Parent'); +maskParamNameValuePairs = get_param(parent, 'MaskWSVariables'); + +% now step through each MASK to get the name and the evaluated value +for i=1:length(maskParamNameValuePairs) + if (strcmpi(maskParamNameValuePairs(i).Name, 'OX')) + OX = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'OY')) + OY = maskParamNameValuePairs(i).Value; + end +% if (strcmpi(maskParamNameValuePairs(i).Name, 'FAMILY')) +% FAMILY = maskParamNameValuePairs(i).Value; +% end + if (strcmpi(maskParamNameValuePairs(i).Name, 'DDR')) + DDR = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'SSR')) + SSR = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'TAPS')) + TAPS = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'COEFFICIENT')) + COEFFICIENT = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'I_HIGH')) + I_HIGH = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'I_LOW')) + I_LOW = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'C_HIGH')) + C_HIGH = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'C_LOW')) + C_LOW = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'O_HIGH')) + O_HIGH = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'O_LOW')) + O_LOW = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'ROUNDING')) + ROUNDING = maskParamNameValuePairs(i).Value; + end + if (strcmpi(maskParamNameValuePairs(i).Name, 'SYMMETRY')) + SYMMETRY = maskParamNameValuePairs(i).Value; + end +end + +family = this_block.getDeviceFamilyName; +if(contains(family,"7") || strcmp(family,"zynq") || strcmp(family,"qzynq") || strcmp(family,"azynq")) + FAMILY = 1; % use 1 for selecting DSP48E1 +elseif(contains(family,"versal")) + FAMILY = 3; % use 1 for selecting DSP58 +else + FAMILY = 2; % use 2 for selecting DSP48E2 +end + +% System Generator has to assume that your entity has a combinational feed through; + % if not, then comment out the following line: + %this_block.tagAsCombinational; + + this_block.addSimulinkInport('I'); + this_block.addSimulinkInport('VI'); + + this_block.addSimulinkOutport('O'); + this_block.addSimulinkOutport('VO'); + + VO_port = this_block.port('VO'); + VO_port.setType('UFix_1_0'); + VO_port.useHDLVector(false); + + % ----------------------------- + if (this_block.inputTypesKnown) + % do input type checking, dynamic output type and generic setup in this code block. + + % (!) Port 'I' appeared to have dynamic type in the HDL -- please add type checking as appropriate; + % inp_width = this_block.port('I').width; % this should be SSR*(I_HIGH-I_LOW+1) + this_block.port('I').useHDLVector(true); + + if (this_block.port('VI').width ~= 1); + this_block.setError('Input data type for port "VI" must have width=1.'); + end + + this_block.port('VI').useHDLVector(false); + + % (!) Port 'O' appeared to have dynamic type in the HDL + % --- you must add an appropriate type setting for this port + this_block.port('O').setType(['UFix_' num2str(SSR*(O_HIGH-O_LOW+1)) '_' num2str(0)]); +end % if(inputTypesKnown) + % ----------------------------- + + % ----------------------------- + if (this_block.inputRatesKnown) + setup_as_single_rate(this_block,'CLK','CE') + end % if(inputRatesKnown) + % ----------------------------- + + uniqueInputRates = unique(this_block.getInputRates); + + % (!) Custimize the following generic settings as appropriate. If any settings depend + % on input types, make the settings in the "inputTypesKnown" code block. + % The addGeneric function takes 3 parameters, generic name, type and constant value. + % Supported types are boolean, real, integer and string. + + + this_block.addGeneric('OX','INTEGER',num2str(OX)); + this_block.addGeneric('OY','INTEGER',num2str(OY)); + this_block.addGeneric('FAMILY','INTEGER',num2str(FAMILY)); + if (DDR==1) + this_block.addGeneric('DDR','BOOLEAN','TRUE'); + else + this_block.addGeneric('DDR','BOOLEAN','FALSE'); + end + this_block.addGeneric('SSR','INTEGER',num2str(SSR)); + this_block.addGeneric('TAPS','INTEGER',num2str(TAPS)); + this_block.addGeneric('C','STRING',strcat('"',num2str(COEFFICIENT,' %.16f'),'"')); + this_block.addGeneric('I_HIGH','INTEGER',num2str(I_HIGH)); + this_block.addGeneric('I_LOW','INTEGER',num2str(I_LOW)); + this_block.addGeneric('C_HIGH','INTEGER',num2str(C_HIGH)); + this_block.addGeneric('C_LOW','INTEGER',num2str(C_LOW)); + this_block.addGeneric('O_HIGH','INTEGER',num2str(O_HIGH)); + this_block.addGeneric('O_LOW','INTEGER',num2str(O_LOW)); + if (ROUNDING==1) + this_block.addGeneric('ROUNDING','BOOLEAN','TRUE'); + else + this_block.addGeneric('ROUNDING','BOOLEAN','FALSE'); + end + this_block.addGeneric('SYMMETRY','STRING',convertStringsToChars(strcat('"',SYMMETRY,'"'))); + + % Add addtional source files as needed. + % |------------- + % | Add files in the order in which they should be compiled. + % | If two files "a.vhd" and "b.vhd" contain the entities + % | entity_a and entity_b, and entity_a contains a + % | component of type entity_b, the correct sequence of + % | addFile() calls would be: + % | this_block.addFile('b.vhd'); + % | this_block.addFile('a.vhd'); + % |------------- + + this_block.addFile('fixed_pkg.vhd'); + this_block.addFile('cfixed_pkg.vhd'); + this_block.addFile('BDELAY.vhd'); + this_block.addFile('SDELAY.vhd'); + this_block.addFile('DSPx8_WRAPPER.vhd'); + this_block.addFile('TRANSPOSE_SSR_FIR_CHAIN.vhd'); + this_block.addFile('TRANSPOSE_SSR_FIR.vhd'); + this_block.addFile('WRAPPER_TRANSPOSE_SSR_FIR.vhd'); + +return; + + +% ------------------------------------------------------------ + +function setup_as_single_rate(block,clkname,cename) + inputRates = block.inputRates; + uniqueInputRates = unique(inputRates); + if (length(uniqueInputRates)==1 & uniqueInputRates(1)==Inf) + block.addError('The inputs to this block cannot all be constant.'); + return; + end + if (uniqueInputRates(end) == Inf) + hasConstantInput = true; + uniqueInputRates = uniqueInputRates(1:end-1); + end + if (length(uniqueInputRates) ~= 1) + block.addError('The inputs to this block must run at a single rate.'); + return; + end + theInputRate = uniqueInputRates(1); + for i = 1:block.numSimulinkOutports + block.outport(i).setRate(theInputRate); + end + block.addClkCEPair(clkname,cename,theInputRate); + return; + +% ------------------------------------------------------------ diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/cfixed_pkg.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/cfixed_pkg.vhd new file mode 100644 index 000000000..ffa0bc71f --- /dev/null +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/cfixed_pkg.vhd @@ -0,0 +1,407 @@ +-- Copyright © Advanced Micro Devices, Inc., or its affiliates. All rights reserved. +-- Portions of this content consists of AI generated content. +-- You may not use this software and documentation (if any) (collectively, the "Materials") +-- except in compliance with the terms and conditions of the Software License Agreement +-- included with the Materials or otherwise as set forth in writing and signed by you and +-- an authorized signatory of AMD. If you do not have a copy of the Software License +-- Agreement, contact your AMD representative for a copy. You agree that you will not +-- reverse engineer or decompile the Materials, in whole or in part, except as allowed by +-- applicable law. +-- THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +-- REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +library IEEE; +use IEEE.STD_LOGIC_1164.all; +use IEEE.NUMERIC_STD.all; +use IEEE.MATH_REAL.all; +use IEEE.MATH_COMPLEX.all; + +package cfixed_pkg is + use work.fixed_pkg.all; -- VHDL-2008 fixed_pkg with no saturation, rounding or warning messages + -- this gives us UFIXED, SFIXED, operators and conversion functions + + constant PI:REAL:=3.1415926535897932; +-- type BOOLEAN_VECTOR is array(NATURAL range <>) of BOOLEAN; + type INTEGER_VECTOR is array(INTEGER range <>) of INTEGER; + type REAL_VECTOR is array(INTEGER range <>) of REAL; + type COMPLEX_VECTOR is array(INTEGER range <>) of COMPLEX; + type UNSIGNED_VECTOR is array(INTEGER range <>) of UNSIGNED; + +-- SFIXED is now the standard VHDL-2008 type defined in fixed_generic_pkg, we do not need a user defined type anymore but this is what SFIXED really is +-- type SFIXED is array(INTEGER range <>) of STD_LOGIC; -- arbitrary precision fixed point signed number, like SIGNED but lower bound can be negative + type SFIXED_VECTOR is array(INTEGER range <>) of SFIXED; -- unconstrained array of SFIXED + type SFIXED_MATRIX is array(INTEGER range <>) of SFIXED_VECTOR; -- unconstrained 2-D array of SFIXED + type CFIXED is record RE,IM:SFIXED; end record; -- arbitrary precision fixed point complex signed number + type CFIXED_VECTOR is array(INTEGER range <>) of CFIXED; -- unconstrained array of CFIXED + type CFIXED_MATRIX is array(INTEGER range <>) of CFIXED_VECTOR; -- unconstrained array of CFIXED_VECTOR + type REAL_MATRIX is array(INTEGER range <>) of REAL_VECTOR; + type COMPLEX_MATRIX is array(INTEGER range <>) of COMPLEX_VECTOR; + + function MIN(A,B:INTEGER) return INTEGER; + function MIN(A,B,C:INTEGER) return INTEGER; + function MIN(A,B,C,D:INTEGER) return INTEGER; + function MAX(A,B:INTEGER) return INTEGER; + function MAX(A,B,C:INTEGER) return INTEGER; + function MAX(A,B,C,D:INTEGER) return INTEGER; + function MED(A,B,C:INTEGER) return INTEGER; +-- function TO_INTEGER(B:BOOLEAN) return INTEGER; + + function "*"(X:SFIXED;Y:STD_LOGIC) return SFIXED; -- multiply by 0 or 1 with SFIXED(X'high downto X'low) result + function TO_SFIXED(I:SIGNED;H,L:INTEGER;SHIFT:INTEGER:=0) return SFIXED; -- convert SIGNED to SFIXED(H downto L) and resize + function TO_SFIXED(I:SIGNED;X:SFIXED;SHIFT:INTEGER:=0) return SFIXED; -- convert SIGNED to SFIXED(X'high downto X'low) and resize +--redefine standard VHDL-2008 SFIXED shift function to make them full precision + function SHIFT_RIGHT(X:SFIXED;N:INTEGER) return SFIXED; -- returns SFIXED(X'high-N downto X'low-N) result + function SHIFT_LEFT(X:SFIXED;N:INTEGER) return SFIXED; -- returns SFIXED(X'high+N downto X'low+N) result + function TO_SFIXED_VECTOR(C:REAL_VECTOR;HL:SFIXED) return SFIXED_VECTOR; -- returns SFIXED_VECTOR(HL'high downto HL'low) result + function TO_REAL_VECTOR(C:SFIXED_VECTOR) return REAL_VECTOR; -- returns REAL_VECTOR result + + function RE(X:CFIXED) return SFIXED; -- returns SFIXED(X'high/2 downto X'low/2) result + function IM(X:CFIXED) return SFIXED; -- returns SFIXED(X'high/2 downto X'low/2) result + function "+"(X,Y:CFIXED) return CFIXED; -- full precision add with CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result + function "-"(X,Y:CFIXED) return CFIXED; -- full precision subtract with CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result + function "*"(X,Y:CFIXED) return CFIXED; -- full precision multiply with CFIXED(X'high+Y'high+2 downto X'low+Y'low) result + function "*"(X:CFIXED;Y:SFIXED) return CFIXED; -- full precision multiply with CFIXED(X'high+Y'high downto X'low+Y'low) result + function "*"(X:SFIXED;Y:CFIXED) return CFIXED; + function RESIZE(X:CFIXED;H,L:INTEGER) return CFIXED; -- resizes X and returns CFIXED(RE(H downto L),IM(H downto L)) + function RESIZE(X:CFIXED;HL:CFIXED) return CFIXED; -- resizes X to match HL and returns CFIXED(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) + function PLUS_i_TIMES(X:CFIXED) return CFIXED; -- returns CFIXED(X'high+2 downto X'low) result + function "-"(X:CFIXED) return CFIXED; -- full precision negate with CFIXED(X'high+2 downto X'low) result + function MINUS_i_TIMES(X:CFIXED) return CFIXED; -- returns CFIXED(X'high+2 downto X'low) result + function X_PLUS_i_TIMES_Y(X,Y:CFIXED;RND:CFIXED) return CFIXED; -- returns CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result + function X_MINUS_i_TIMES_Y(X,Y:CFIXED;RND:CFIXED) return CFIXED; -- returns CFIXED(MAX(X'high,Y'high)+2 downto MIN(X'low,Y'low)) result + function SWAP(X:CFIXED) return CFIXED; -- returns CFIXED(X'high downto X'low) result + function CONJ(X:CFIXED) return CFIXED; -- returns CFIXED(X'high+2 downto X'low) result + function SHIFT_RIGHT(X:CFIXED;N:INTEGER) return CFIXED; -- returns CFIXED(X'high-N downto X'low-N) result + function SHIFT_LEFT(X:CFIXED;N:INTEGER) return CFIXED; -- returns CFIXED(X'high+N downto X'low+N) result + function TO_CFIXED(R,I:REAL;H,L:INTEGER) return CFIXED; -- returns CFIXED(RE(H downto L),IM(H downto L)) result + function TO_CFIXED(R,I:REAL;HL:CFIXED) return CFIXED; -- returns CFIXED(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) result + function TO_CFIXED(C:COMPLEX;HL:CFIXED) return CFIXED; -- returns CFIXED(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) result + function TO_CFIXED(R,I:SFIXED) return CFIXED; -- returns CFIXED(2*MAX(R'high,I'high)+1 downto 2*MIN(R'low,I'low)) result + function TO_COMPLEX(C:CFIXED) return COMPLEX; -- returns COMPLEX result + function TO_CFIXED_VECTOR(C:COMPLEX_VECTOR;HL:CFIXED) return CFIXED_VECTOR; -- returns CFIXED_VECTOR(RE(HL.RE'high downto HL.RE'low),IM(RE(HL.IM'high downto HL.IM'low)) result + function TO_COMPLEX_VECTOR(C:CFIXED_VECTOR) return COMPLEX_VECTOR; -- returns COMPLEX_VECTOR result + function "*"(R:REAL;C:COMPLEX_VECTOR) return COMPLEX_VECTOR; -- returns R*C + function LOG2(N:INTEGER) return INTEGER; -- returns ceil(log2(N)) + function ZERO(U:UNSIGNED) return UNSIGNED; -- returns 0 with the same range as U + function ZERO(S:SFIXED) return SFIXED; -- returns 0 with the same range as S + function ZERO(C:CFIXED) return CFIXED; -- returns 0 with the same range as C + function ZERO(SV:SFIXED_VECTOR) return SFIXED_VECTOR; -- returns 0 with the same range as SV + function ZERO(CV:CFIXED_VECTOR) return CFIXED_VECTOR; -- returns 0 with the same range as CV +end package cfixed_pkg; + +package body cfixed_pkg is + function MIN(A,B:INTEGER) return INTEGER is + begin + if AB then + return A; + else + return B; + end if; + end; + + function MAX(A,B,C:INTEGER) return INTEGER is + begin + return MAX(MAX(A,B),C); + end; + + function MAX(A,B,C,D:INTEGER) return INTEGER is + begin + return MAX(MAX(A,B),MAX(C,D)); + end; + + function MED(A,B,C:INTEGER) return INTEGER is + begin + return MAX(MIN(MAX(A,B),C),MIN(A,B)); + end; + +-- function TO_INTEGER(B:BOOLEAN) return INTEGER is +-- begin +-- if B then +-- return 1; +-- else +-- return 0; +-- end if; +-- end; + + function "*"(X:SFIXED;Y:STD_LOGIC) return SFIXED is + begin + if Y='1' then + return X; + else + return TO_SFIXED(0.0,X); + end if; + end; + + function TO_SFIXED(I:SIGNED;H,L:INTEGER;SHIFT:INTEGER:=0) return SFIXED is + variable O:SFIXED(H downto L); + begin + for K in O'range loop + if K1 loop + RESULT:=RESULT+1; + TEMP:=(TEMP+1)/2; + end loop; + return RESULT; + end; + + function ZERO(U:UNSIGNED) return UNSIGNED is -- returns 0 with the same range as S + begin + return TO_UNSIGNED(0,U'length); + end; + + function ZERO(S:SFIXED) return SFIXED is -- returns 0 with the same range as S + begin + return TO_SFIXED(0.0,S); + end; + + function ZERO(C:CFIXED) return CFIXED is -- returns 0 with the same range as C + begin + return TO_CFIXED(0.0,0.0,C); + end; + + function ZERO(SV:SFIXED_VECTOR) return SFIXED_VECTOR is -- returns 0 with the same range as SV + variable RESULT:SFIXED_VECTOR(SV'range)(SV(SV'low)'range); + begin + for K in SV'range loop + RESULT(K):=TO_SFIXED(0.0,SV(K)); + end loop; + return RESULT; + end; + + function ZERO(CV:CFIXED_VECTOR) return CFIXED_VECTOR is -- returns 0 with the same range as CV + variable RESULT:CFIXED_VECTOR(CV'range)(RE(CV(CV'low).RE'range),IM(CV(CV'low).IM'range)); + begin + for K in CV'range loop + RESULT(K):=TO_CFIXED(0.0,0.0,CV(K)); + end loop; + return RESULT; + end; +end package body cfixed_pkg; \ No newline at end of file diff --git a/Examples/HDL/Digital_Filtering/SSR_FIR/fixed_pkg.vhd b/Examples/HDL/Digital_Filtering/SSR_FIR/fixed_pkg.vhd new file mode 100644 index 000000000..2cb1318d5 --- /dev/null +++ b/Examples/HDL/Digital_Filtering/SSR_FIR/fixed_pkg.vhd @@ -0,0 +1,20 @@ +-- Copyright © Advanced Micro Devices, Inc., or its affiliates. All rights reserved. +-- Portions of this content consists of AI generated content. +-- You may not use this software and documentation (if any) (collectively, the "Materials") +-- except in compliance with the terms and conditions of the Software License Agreement +-- included with the Materials or otherwise as set forth in writing and signed by you and +-- an authorized signatory of AMD. If you do not have a copy of the Software License +-- Agreement, contact your AMD representative for a copy. You agree that you will not +-- reverse engineer or decompile the Materials, in whole or in part, except as allowed by +-- applicable law. +-- THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +-- REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +library IEEE; +use IEEE.STD_LOGIC_1164.all; + +package fixed_pkg is new IEEE.fixed_generic_pkg + generic map(fixed_round_style=>IEEE.fixed_float_types.fixed_truncate, + fixed_overflow_style=>IEEE.fixed_float_types.fixed_wrap, + fixed_guard_bits=>2, + no_warning=>true);