aboutsummaryrefslogtreecommitdiffstats
path: root/testsuite
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2023-02-03 19:11:42 +0100
committerTristan Gingold <tgingold@free.fr>2023-02-03 19:11:42 +0100
commite760a611da905b740f50ab02a58ed6b845e78af3 (patch)
tree2df075599edce7a2aa9ab3a691b0dc560df04c1a /testsuite
parent647546bd637e2d2b55dd1db00e277dff56ad36c1 (diff)
downloadghdl-e760a611da905b740f50ab02a58ed6b845e78af3.tar.gz
ghdl-e760a611da905b740f50ab02a58ed6b845e78af3.tar.bz2
ghdl-e760a611da905b740f50ab02a58ed6b845e78af3.zip
testsuite/synth: add a test for #2336
Diffstat (limited to 'testsuite')
-rw-r--r--testsuite/synth/issue2336/ecp5pll.vhd427
-rwxr-xr-xtestsuite/synth/issue2336/testsuite.sh8
-rw-r--r--testsuite/synth/issue2336/top_ecp5pll.vhd51
3 files changed, 486 insertions, 0 deletions
diff --git a/testsuite/synth/issue2336/ecp5pll.vhd b/testsuite/synth/issue2336/ecp5pll.vhd
new file mode 100644
index 000000000..9fc305449
--- /dev/null
+++ b/testsuite/synth/issue2336/ecp5pll.vhd
@@ -0,0 +1,427 @@
+-- (c)EMARD
+-- License=BSD
+
+-- parametric ECP5 PLL generator in VHDL
+-- to see actual frequencies and phase shifts
+-- trellis log/stdout : search for "MHz", "Derived", "frequency"
+-- diamond log/*.mrp : search for "MHz", "Frequency", "Phase", "Desired"
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity ecp5pll is
+ generic
+ (
+ in_hz : natural := 25000000;
+ out0_hz : natural := 25000000;
+ out0_deg : natural := 0; -- keep 0
+ out0_tol_hz : natural := 0; -- Hz tolerance
+ out1_hz : natural := 0; -- 0 to disable
+ out1_deg : natural := 0;
+ out1_tol_hz : natural := 0;
+ out2_hz : natural := 0;
+ out2_deg : natural := 0;
+ out2_tol_hz : natural := 0;
+ out3_hz : natural := 0;
+ out3_deg : natural := 0;
+ out3_tol_hz : natural := 0;
+ reset_en : natural := 0;
+ standby_en : natural := 0;
+ dynamic_en : natural := 0
+ );
+ port
+ (
+ clk_i : in std_logic;
+ clk_o : out std_logic_vector(3 downto 0);
+ reset : in std_logic := '0';
+ standby : in std_logic := '0';
+ phasesel : in std_logic_vector(1 downto 0) := "00";
+ phasedir,
+ phasestep,
+ phaseloadreg : std_logic := '0';
+ locked : out std_logic
+ );
+end;
+
+architecture mix of ecp5pll is
+ type T_clocks is record
+ in_hz : natural;
+ out0_hz : natural;
+ out0_deg : natural;
+ out1_hz : natural;
+ out1_deg : natural;
+ out2_hz : natural;
+ out2_deg : natural;
+ out3_hz : natural;
+ out3_deg : natural;
+ end record T_clocks;
+
+ type T_secondary is record
+ div : natural;
+ freq_string : string(0 to 9);
+ freq : natural;
+ phase : natural;
+ cphase : natural;
+ fphase : natural;
+ end record T_secondary;
+ type A_secondary is array(1 to 3) of T_secondary;
+
+ type A_srequest is array(1 to 3) of natural;
+
+ type T_params is record
+ result : T_clocks;
+ refclk_div : natural;
+ feedback_div : natural;
+ output_div : natural;
+ fin_string : string(0 to 9);
+ fout_string : string(0 to 9);
+ fout : natural;
+ fvco : natural;
+ primary_cphase : natural;
+ primary_fphase : natural;
+ secondary : A_secondary;
+ end record T_params;
+
+ function enabled_str(en: integer)
+ return string is
+ begin
+ if en = 0 then
+ return "DISABLED";
+ else
+ return "ENABLED";
+ end if;
+ end enabled_str;
+
+ function Hz2MHz_str(int: integer)
+ return string is
+ constant base : natural := 10;
+ constant digit : string(0 to 9) := "0123456789";
+ variable temp : string(0 to 8) := (others => '0');
+ variable power : natural := 1;
+ begin
+ -- convert integer to string
+ for i in temp'high downto 0 loop
+ temp(i) := digit(int/power mod base);
+ power := power * base;
+ end loop;
+ -- insert decimal point "123.456789"
+ return temp(0 to 2) & "." & temp(3 to temp'high);
+ end Hz2MHz_str;
+
+ function F_ecp5pll(request: T_clocks)
+ return T_params is
+ constant PFD_MIN: natural := 3125000;
+ constant PFD_MAX: natural := 400000000;
+ constant VCO_MIN: natural := 400000000;
+ constant VCO_MAX: natural := 800000000;
+ constant VCO_OPTIMAL: natural := (VCO_MIN+VCO_MAX)/2;
+ variable params: T_params;
+
+ variable input_div, input_div_min, input_div_max : natural;
+ variable feedback_div, feedback_div_min, feedback_div_max : natural;
+ variable output_div, output_div_min, output_div_max : natural;
+ variable fout: natural;
+ variable fvco: natural := 0;
+ variable error, error_prev: natural := 999999999;
+ variable result: T_clocks;
+ variable sfreq, sphase: A_srequest;
+ variable div: natural;
+ variable freq: natural;
+ variable phase_compensation: natural;
+ variable phase_count_x8: natural;
+ variable phase_shift: natural;
+ begin
+ params.fvco := 0;
+ sfreq(1) := request.out1_hz;
+ sphase(1) := request.out1_deg;
+ sfreq(2) := request.out2_hz;
+ sphase(2) := request.out2_deg;
+ sfreq(3) := request.out3_hz;
+ sphase(3) := request.out3_deg;
+ -- generate primary output
+ -- search 128*80*128=1.3e6 combination space to find the best match
+ -- loop over allowed MIN/MAX range only
+ input_div_min := in_hz/PFD_MAX;
+ if input_div_min < 1 then
+ input_div_min := 1;
+ end if;
+ input_div_max := in_hz/PFD_MIN;
+ if input_div_max > 128 then
+ input_div_max := 128;
+ end if;
+ for input_div in input_div_min to input_div_max loop
+ if out0_hz / 1000000 * input_div < 2000 then
+ feedback_div := out0_hz * input_div / in_hz;
+ else
+ feedback_div := out0_hz / in_hz * input_div;
+ end if;
+ if feedback_div < 2 then
+ feedback_div_min := 1;
+ else
+ feedback_div_min := feedback_div-1;
+ end if;
+ if feedback_div > 79 then
+ feedback_div_max := 80;
+ else
+ feedback_div_max := feedback_div+1;
+ end if;
+ for feedback_div in feedback_div_min to feedback_div_max loop
+ output_div_min := (VCO_MIN/feedback_div) / (in_hz/input_div);
+ if output_div_min < 1 then
+ output_div_min := 1;
+ end if;
+ output_div_max := (VCO_MAX/feedback_div) / (in_hz/input_div);
+ if output_div_max > 128 then
+ output_div_max := 128;
+ end if;
+ fout := in_hz * feedback_div / input_div;
+ for output_div in output_div_min to output_div_max loop
+ fvco := fout * output_div;
+ error := abs(fout-out0_hz);
+ for channel in 1 to 3 loop
+ if sfreq(channel) > 0 then
+ div := 1;
+ if fvco >= sfreq(channel) then
+ div := fvco/sfreq(channel);
+ end if;
+ freq := fvco/div;
+ error := error + abs(freq-sfreq(channel));
+ end if;
+ end loop;
+ if error < error_prev -- prefer least error
+ or (error=error_prev and abs(fvco-VCO_OPTIMAL) < abs(params.fvco-VCO_OPTIMAL)) -- or if 0 error prefer closest to optimal VCO frequency
+ then
+ error_prev := error;
+ phase_compensation := (output_div+1)/2*8-8+output_div/2*8; -- output_div/2*8 = 180 deg shift
+ phase_count_x8 := phase_compensation + 8*output_div*out0_deg/360;
+ if phase_count_x8 > 1023 then
+ phase_count_x8 := phase_count_x8 mod (output_div*8); -- wraparound 360 deg
+ end if;
+ params.refclk_div := input_div;
+ params.feedback_div := feedback_div;
+ params.output_div := output_div;
+ params.fin_string := Hz2MHz_str(in_hz);
+ params.fout_string := Hz2MHz_str(fout);
+ params.fout := in_hz * feedback_div / input_div;
+ params.fvco := fvco;
+ params.primary_cphase := phase_count_x8 / 8;
+ params.primary_fphase := phase_count_x8 mod 8;
+ end if;
+ end loop;
+ end loop;
+ end loop;
+ -- generate secondary outputs
+ for channel in 1 to 3 loop
+ if sfreq(channel) > 0 then
+ div := 1;
+ if params.fvco >= sfreq(channel) then
+ div := params.fvco/sfreq(channel);
+ end if;
+ freq := params.fvco/div;
+ phase_compensation := div*8-8;
+ phase_count_x8 := phase_compensation + 8*div*sphase(channel)/360;
+ if phase_count_x8 > 1023 then
+ phase_count_x8 := phase_count_x8 mod (div*8); -- wraparound 360 deg
+ end if;
+ phase_shift := 8*div*sphase(channel)/360 * 360 / (8*div); -- reported phase shift
+ params.secondary(channel).div := div;
+ params.secondary(channel).freq_string := Hz2MHz_str(freq);
+ params.secondary(channel).freq := freq;
+ params.secondary(channel).phase := phase_shift;
+ params.secondary(channel).cphase := phase_count_x8 / 8;
+ params.secondary(channel).fphase := phase_count_x8 mod 8;
+ else
+ params.secondary(channel).div := 1;
+ end if;
+ end loop;
+ params.result.in_hz := request.in_hz;
+ params.result.out0_hz := natural(params.fout);
+ params.result.out0_deg := 0; -- FIXME
+ params.result.out1_hz := natural(params.secondary(1).freq);
+ params.result.out1_deg := natural(params.secondary(1).phase);
+ params.result.out2_hz := natural(params.secondary(2).freq);
+ params.result.out2_deg := natural(params.secondary(2).phase);
+ params.result.out3_hz := natural(params.secondary(3).freq);
+ params.result.out3_deg := natural(params.secondary(3).phase);
+ return params;
+ end F_ecp5pll;
+
+ constant request : T_clocks :=
+ (
+ in_hz => in_hz,
+ out0_hz => out0_hz,
+ out0_deg => out0_deg,
+ out1_hz => out1_hz,
+ out1_deg => out1_deg,
+ out2_hz => out2_hz,
+ out2_deg => out2_deg,
+ out3_hz => out3_hz,
+ out3_deg => out3_deg
+ );
+ constant params : T_params := F_ecp5pll(request);
+
+ component EHXPLLL
+ generic
+ (
+ CLKI_DIV : integer := 1;
+ CLKFB_DIV : integer := 1;
+ CLKOP_DIV : integer := 8;
+ CLKOS_DIV : integer := 8;
+ CLKOS2_DIV : integer := 8;
+ CLKOS3_DIV : integer := 8;
+ CLKOP_ENABLE : string := "ENABLED";
+ CLKOS_ENABLE : string := "DISABLED";
+ CLKOS2_ENABLE : string := "DISABLED";
+ CLKOS3_ENABLE : string := "DISABLED";
+ CLKOP_CPHASE : integer := 0;
+ CLKOS_CPHASE : integer := 0;
+ CLKOS2_CPHASE : integer := 0;
+ CLKOS3_CPHASE : integer := 0;
+ CLKOP_FPHASE : integer := 0;
+ CLKOS_FPHASE : integer := 0;
+ CLKOS2_FPHASE : integer := 0;
+ CLKOS3_FPHASE : integer := 0;
+ FEEDBK_PATH : string := "CLKOP";
+ CLKOP_TRIM_POL : string := "RISING";
+ CLKOP_TRIM_DELAY : integer := 0;
+ CLKOS_TRIM_POL : string := "RISING";
+ CLKOS_TRIM_DELAY : integer := 0;
+ OUTDIVIDER_MUXA : string := "DIVA";
+ OUTDIVIDER_MUXB : string := "DIVB";
+ OUTDIVIDER_MUXC : string := "DIVC";
+ OUTDIVIDER_MUXD : string := "DIVD";
+ PLL_LOCK_MODE : integer := 0;
+ PLL_LOCK_DELAY : integer := 200;
+ STDBY_ENABLE : string := "DISABLED";
+ REFIN_RESET : string := "DISABLED";
+ SYNC_ENABLE : string := "DISABLED";
+ INT_LOCK_STICKY : string := "ENABLED";
+ DPHASE_SOURCE : string := "DISABLED";
+ PLLRST_ENA : string := "DISABLED";
+ INTFB_WAKE : string := "DISABLED"
+ );
+ port
+ (
+ CLKI, CLKFB,
+ RST, STDBY, PLLWAKESYNC,
+ PHASESEL1, PHASESEL0, PHASEDIR, PHASESTEP, PHASELOADREG,
+ ENCLKOP, ENCLKOS, ENCLKOS2, ENCLKOS3 : IN std_logic := 'X';
+ CLKOP, CLKOS, CLKOS2, CLKOS3, LOCK, INTLOCK,
+ REFCLK, CLKINTFB : OUT std_logic := 'X'
+ );
+ end component;
+
+ -- internal signal declarations
+ signal CLKOP_t : std_logic;
+ signal REFCLK : std_logic;
+ signal PHASESEL_HW : std_logic_vector(1 downto 0);
+
+ attribute FREQUENCY_PIN_CLKI : string;
+ attribute FREQUENCY_PIN_CLKOP : string;
+ attribute FREQUENCY_PIN_CLKOS : string;
+ attribute FREQUENCY_PIN_CLKOS2 : string;
+ attribute FREQUENCY_PIN_CLKOS3 : string;
+ attribute FREQUENCY_PIN_CLKI of PLL_inst : label is params.fin_string;
+ attribute FREQUENCY_PIN_CLKOP of PLL_inst : label is params.fout_string;
+ attribute FREQUENCY_PIN_CLKOS of PLL_inst : label is params.secondary(1).freq_string;
+ attribute FREQUENCY_PIN_CLKOS2 of PLL_inst : label is params.secondary(2).freq_string;
+ attribute FREQUENCY_PIN_CLKOS3 of PLL_inst : label is params.secondary(3).freq_string;
+
+ attribute ICP_CURRENT : string;
+ attribute LPF_RESISTOR : string;
+ attribute ICP_CURRENT of PLL_inst : label is "12";
+ attribute LPF_RESISTOR of PLL_inst : label is "8";
+
+ attribute syn_keep : boolean;
+ attribute NGD_DRC_MASK : integer;
+ attribute NGD_DRC_MASK of mix : architecture is 1;
+begin
+ assert false
+ report "clk_o(0) " & Hz2MHz_str(params.result.out0_hz) & " MHz " & Hz2MHz_str(params.result.out0_deg*1000000) & " deg"
+ severity note;
+ assert false
+ report "clk_o(1) " & Hz2MHz_str(params.result.out1_hz) & " MHz " & Hz2MHz_str(params.result.out1_deg*1000000) & " deg"
+ severity note;
+ assert false
+ report "clk_o(2) " & Hz2MHz_str(params.result.out2_hz) & " MHz " & Hz2MHz_str(params.result.out2_deg*1000000) & " deg"
+ severity note;
+ assert false
+ report "clk_o(3) " & Hz2MHz_str(params.result.out3_hz) & " MHz " & Hz2MHz_str(params.result.out3_deg*1000000) & " deg"
+ severity note;
+ assert abs(out0_hz - params.result.out0_hz) <= out0_tol_hz
+ report "clk_o(0) " & Hz2MHz_str(params.result.out0_hz) & " MHz exceeds " & Hz2MHz_str(out0_hz) & "+-" & Hz2MHz_str(out0_tol_hz) & " MHz out0_tol_hz"
+ severity failure;
+ assert abs(out1_hz - params.result.out1_hz) <= out1_tol_hz
+ report "clk_o(1) " & Hz2MHz_str(params.result.out1_hz) & " MHz exceeds " & Hz2MHz_str(out1_hz) & "+-" & Hz2MHz_str(out1_tol_hz) & " MHz out1_tol_hz"
+ severity failure;
+ assert abs(out2_hz - params.result.out2_hz) <= out2_tol_hz
+ report "clk_o(2) " & Hz2MHz_str(params.result.out2_hz) & " MHz exceeds " & Hz2MHz_str(out2_hz) & "+-" & Hz2MHz_str(out2_tol_hz) & " MHz out2_tol_hz"
+ severity failure;
+ assert abs(out3_hz - params.result.out3_hz) <= out3_tol_hz
+ report "clk_o(3) " & Hz2MHz_str(params.result.out3_hz) & " MHz exceeds " & Hz2MHz_str(out3_hz) & "+-" & Hz2MHz_str(out3_tol_hz) & " MHz out3_tol_hz"
+ severity failure;
+
+ G_dynamic: if dynamic_en /= 0 generate
+ PHASESEL_HW <= std_logic_vector(unsigned(phasesel)-1);
+ end generate;
+ PLL_inst: EHXPLLL
+ generic map
+ (
+ CLKI_DIV => params.refclk_div,
+ CLKFB_DIV => params.feedback_div,
+ FEEDBK_PATH => "CLKOP",
+
+ OUTDIVIDER_MUXA => "DIVA",
+ CLKOP_ENABLE => enabled_str(out0_hz),
+ CLKOP_DIV => params.output_div,
+ CLKOP_CPHASE => params.primary_cphase,
+ CLKOP_FPHASE => params.primary_fphase,
+-- CLKOP_TRIM_DELAY=> 0, CLKOP_TRIM_POL=> "FALLING",
+
+ OUTDIVIDER_MUXB => "DIVB",
+ CLKOS_ENABLE => enabled_str(out1_hz),
+ CLKOS_DIV => params.secondary(1).div,
+ CLKOS_CPHASE => params.secondary(1).cphase,
+ CLKOS_FPHASE => params.secondary(1).fphase,
+-- CLKOS_TRIM_DELAY=> 0, CLKOS_TRIM_POL=> "FALLING",
+
+ OUTDIVIDER_MUXC => "DIVC",
+ CLKOS2_ENABLE => enabled_str(out2_hz),
+ CLKOS2_DIV => params.secondary(2).div,
+ CLKOS2_CPHASE => params.secondary(2).cphase,
+ CLKOS2_FPHASE => params.secondary(2).fphase,
+
+ OUTDIVIDER_MUXD => "DIVD",
+ CLKOS3_ENABLE => enabled_str(out3_hz),
+ CLKOS3_DIV => params.secondary(3).div,
+ CLKOS3_CPHASE => params.secondary(3).cphase,
+ CLKOS3_FPHASE => params.secondary(3).fphase,
+
+ INTFB_WAKE => "DISABLED",
+ STDBY_ENABLE => enabled_str(standby_en),
+ PLLRST_ENA => enabled_str(reset_en),
+ DPHASE_SOURCE => enabled_str(dynamic_en),
+ PLL_LOCK_MODE => 0
+ )
+ port map
+ (
+ CLKI => clk_i, CLKFB => CLKOP_t,
+ RST => reset, STDBY => standby, PLLWAKESYNC => '0',
+ PHASESEL1 => PHASESEL_HW(1),
+ PHASESEL0 => PHASESEL_HW(0),
+ PHASEDIR => phasedir,
+ PHASESTEP => phasestep,
+ PHASELOADREG => phaseloadreg,
+ ENCLKOP =>'0', ENCLKOS => '0', ENCLKOS2 => '0', ENCLKOS3 => '0',
+ CLKOP => CLKOP_t,
+ CLKOS => clk_o(1),
+ CLKOS2 => clk_o(2),
+ CLKOS3 => clk_o(3),
+ INTLOCK => open, REFCLK => REFCLK, CLKINTFB => open,
+ LOCK => locked
+ );
+
+ clk_o(0) <= CLKOP_t;
+
+end mix;
diff --git a/testsuite/synth/issue2336/testsuite.sh b/testsuite/synth/issue2336/testsuite.sh
new file mode 100755
index 000000000..d4aa0cfa4
--- /dev/null
+++ b/testsuite/synth/issue2336/testsuite.sh
@@ -0,0 +1,8 @@
+#! /bin/sh
+
+. ../../testenv.sh
+
+GHDL_STD_FLAGS=--std=08
+synth_failure top_ecp5pll.vhd ecp5pll.vhd -e top_ecp5pll
+
+echo "Test successful"
diff --git a/testsuite/synth/issue2336/top_ecp5pll.vhd b/testsuite/synth/issue2336/top_ecp5pll.vhd
new file mode 100644
index 000000000..a64aa187c
--- /dev/null
+++ b/testsuite/synth/issue2336/top_ecp5pll.vhd
@@ -0,0 +1,51 @@
+-- (c)EMARD
+-- License=BSD
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity top_ecp5pll is
+ generic
+ (
+ bits: integer := 26
+ );
+ port
+ (
+ clk_25mhz : in std_logic; -- main clock input from 25MHz clock source
+ led : out std_logic_vector(7 downto 0)
+ );
+end;
+
+architecture mix of top_ecp5pll is
+ type T_blink is array (0 to 3) of unsigned(bits-1 downto 0);
+ signal R_blink: T_blink;
+ signal clocks: std_logic_vector(3 downto 0);
+begin
+ clkgen_inst: entity work.ecp5pll
+ generic map
+ (
+ in_Hz => natural( 25.0e6),
+ out0_Hz => natural( 40.0e6), out0_tol_hz => 0,
+ out1_Hz => natural( 50.0e6), out1_deg => 90, out1_tol_hz => 0,
+ out2_Hz => natural( 60.0e6), out2_deg => 180, out2_tol_hz => 0,
+ out3_Hz => natural( 6.0e6), out3_deg => 300, out3_tol_hz => 10
+ )
+ port map
+ (
+ clk_i => clk_25mhz,
+ clk_o => clocks
+ );
+
+ G_blinks: for i in 0 to 3
+ generate
+ process(clocks(i))
+ begin
+ if rising_edge(clocks(i)) then
+ R_blink(i) <= R_blink(i)+1;
+ end if;
+ led(2*i+1 downto 2*i) <= std_logic_vector(R_blink(i)(bits-1 downto bits-2));
+ end process;
+ end generate;
+
+end mix;