diff --git a/boards/uniboard2c/libraries/unb2c_board/hdllib.cfg b/boards/uniboard2c/libraries/unb2c_board/hdllib.cfg index 2c8c0275e1c60cd36b8fe99c65faeb1023bafd95..15a5c8eafcbc7a61794252995bb5da8dfaee29a7 100644 --- a/boards/uniboard2c/libraries/unb2c_board/hdllib.cfg +++ b/boards/uniboard2c/libraries/unb2c_board/hdllib.cfg @@ -1,7 +1,7 @@ hdl_lib_name = unb2c_board hdl_library_clause_name = unb2c_board_lib hdl_lib_uses_synth = common dp ppsh i2c eth remu technology tech_clkbuf tech_pll tech_fractional_pll epcs fpga_sense -hdl_lib_uses_sim = +hdl_lib_uses_sim = hdl_lib_technology = ip_arria10_e2sg hdl_lib_include_ip = ip_arria10_e2sg_tse_sgmii_lvds ip_arria10_e2sg_clkbuf_global @@ -23,18 +23,20 @@ synth_files = src/vhdl/mms_unb2c_fpga_sens.vhd src/vhdl/unb2c_board_wdi_reg.vhd src/vhdl/unb2c_board_qsfp_leds.vhd + src/vhdl/unb2c_board_qsfp_leds_v2.vhd src/vhdl/ctrl_unb2c_board.vhd src/vhdl/unb2c_board_front_io.vhd src/vhdl/unb2c_board_back_io.vhd src/vhdl/unb2c_board_ring_io.vhd src/vhdl/unb2c_board_peripherals_pkg.vhd - -test_bench_files = + +test_bench_files = tb/vhdl/tb_unb2c_board_clk200_pll.vhd tb/vhdl/tb_unb2c_board_clk25_pll.vhd tb/vhdl/tb_unb2c_board_node_ctrl.vhd tb/vhdl/tb_unb2c_board_qsfp_leds.vhd - + tb/vhdl/tb_unb2c_board_qsfp_leds_v2.vhd + [modelsim_project_file] diff --git a/boards/uniboard2c/libraries/unb2c_board/src/vhdl/unb2c_board_qsfp_leds_v2.vhd b/boards/uniboard2c/libraries/unb2c_board/src/vhdl/unb2c_board_qsfp_leds_v2.vhd new file mode 100644 index 0000000000000000000000000000000000000000..81a8010a008919ad437063f36ce9f23558788c03 --- /dev/null +++ b/boards/uniboard2c/libraries/unb2c_board/src/vhdl/unb2c_board_qsfp_leds_v2.vhd @@ -0,0 +1,222 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2024 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- +-- Author: E. Kooistra +-- Purpose: Provide visual activity information via the UniBoard2 front panel QSFP LEDs. +-- Description: +-- The testio LED on UniBoard2 is not visible via the front panel. The +-- front panel does have a dual colour LED for each QSFP lane. Therefore +-- these QSFP LEDs are used to signal some application information and lane +-- status/activity information. +-- +-- Comparison unb2c_board_qsfp_leds_v2 and unb2c_board_qsfp_leds: +-- . unb2c_board_qsfp_leds_v2 does not output the internal us, ms and s pulses, +-- because they depend on the clock domain and are typically left open with +-- unb2c_board_qsfp_leds +-- . For g_factory_image = false the unb2c_board_qsfp_leds_v2 = unb2c_board_qsfp_leds +-- except use dp_clk to detect the transceiver activity strobes. +-- . For g_factory_image = true unb2c_board_qsfp_leds_v2 also shows status of dp_clk, +-- dp_pps, and ddr_I_cal_ok, ddr_II_cal_ok +-- +-- LED lights: +-- +-- 1) Default behaviour for all QSFP leds: +-- . off = no FPGA image is running +-- +-- 2) For factory image only use the red leds: +-- . green off +-- . red[0] on = factory image is running (g_factory_image=true) +-- off = no image is running +-- . red[1] toggling every 1 s = 125 MHz clock present (= mm_clk = clk) +-- . red[2] toggling every 1 s = pps present (= dp_pps) +-- . red[3] toggling every 1 s = 200 MHz clock present (= dp_clk) +-- . red[4] on = DDR4 bank I calibrated = ctlr_tech_miso.done in io_ddr +-- off = not calibrated or no DDR4 module present +-- . red[5] on = DDR4 bank II calibrated = ctlr_tech_miso.done in io_ddr +-- off = not calibrated or no DDR4 module present +-- +-- 3) For a user image only use green leds: +-- . red off +-- +-- a) without Gbps lane functionality: +-- . green toggling every 1 s = user image is running (g_factory_image=FALSE and green_on_arr(I)='0' default) +-- +-- b) with Gbps lane functionality: +-- . green toggling every 1 s when the lane status is not OK (green_on_arr(I)=xon='0') +-- . green on continously when the lane status is OK (green_on_arr(I)=xon='1') +-- . green led goes off briefly off when there is an Tx or Rx packet (green_evt_arr(I).sop='1') +-- +-- The combined colour amber (= red + green) is not used. The factory image +-- only uses the red led and the user image only uses the green led. +-- +-- Each QSFP carries c_quad = 4 lanes, therefore the green led LED can only +-- signal a combined status of the lanes. The combined status eg. be: +-- +-- 'and-status' = combined status is on when all lanes are on +-- 'or-status' = combined status is on when at least 1 lane is on +-- +-- Choose using 'or-status', because then the LED can give lane status +-- information when less than all 4 lane are connected. +-- +library IEEE, common_lib, dp_lib; +use IEEE.std_logic_1164.all; +use common_lib.common_pkg.all; +use dp_lib.dp_stream_pkg.all; + +entity unb2c_board_qsfp_leds_v2 is + generic ( + g_sim : boolean := false; -- when true speed up led toggling in simulation + g_factory_image : boolean := false; -- distinguish factory image and user images + g_nof_qsfp : natural := 6; -- number of QSFP cages each with one dual led that can light red or green (or amber = red + green) + g_mm_pulse_us : natural := 125; -- nof mm_clk cycles to get us period + g_dp_pulse_us : natural := 200 -- nof dp_clk cycles to get us period + ); + port ( + mm_rst : in std_logic; + mm_clk : in std_logic; + dp_rst : in std_logic := '0'; + dp_clk : in std_logic := '0'; + dp_pps : in std_logic := '0'; + -- ddr status (level signals) + ddr_I_cal_ok : in std_logic := '0'; + ddr_II_cal_ok : in std_logic := '0'; + -- lane status (sop strobe signals in dp_clk domain) + tx_siso_arr : in t_dp_siso_arr(g_nof_qsfp * c_quad - 1 downto 0) := (others => c_dp_siso_rst); + tx_sosi_arr : in t_dp_sosi_arr(g_nof_qsfp * c_quad - 1 downto 0) := (others => c_dp_sosi_rst); + rx_sosi_arr : in t_dp_sosi_arr(g_nof_qsfp * c_quad - 1 downto 0) := (others => c_dp_sosi_rst); + -- leds + green_led_arr : out std_logic_vector(g_nof_qsfp - 1 downto 0); + red_led_arr : out std_logic_vector(g_nof_qsfp - 1 downto 0) + ); +end unb2c_board_qsfp_leds_v2; + +architecture str of unb2c_board_qsfp_leds_v2 is + constant c_nof_ms : natural := sel_a_b(g_sim, 1, 100); -- force off for c_nof_ms and then on for at least c_nof_ms + constant c_nof_lanes : natural := g_nof_qsfp * c_quad; -- number of transceiver lanes, fixed 4 per Quad-SFP cage + + -- internal pulses + signal mm_pulse_s : std_logic; -- pulse 1 mm_clk cycle every 1 s + signal mm_toggle_s : std_logic; -- toggle every 1 s in mm_clk domain + + signal dp_pulse_ms : std_logic; -- pulse 1 dp_clk cycle every 1 ms + signal dp_pulse_s : std_logic; -- pulse 1 dp_clk cycle every 1 us + signal dp_toggle_s : std_logic; -- toggle every 1 s in dp_clk domain + + signal dp_toggle_pps : std_logic; -- pulse 1 dp_clk cycle every 1 s + + signal green_on_arr : std_logic_vector(g_nof_qsfp * c_quad - 1 downto 0); + signal green_evt_arr : std_logic_vector(g_nof_qsfp * c_quad - 1 downto 0); + + signal qsfp_on_arr : std_logic_vector(g_nof_qsfp - 1 downto 0); + signal qsfp_evt_arr : std_logic_vector(g_nof_qsfp - 1 downto 0); +begin + -- mm_clk domain pulsers + u_mm_common_pulser_us_ms_s : entity common_lib.common_pulser_us_ms_s + generic map ( + g_pulse_us => g_mm_pulse_us, -- nof mm_clk cycles to get us period + g_pulse_ms => sel_a_b(g_sim, 10, 1000), -- nof pulse_us pulses to get ms period + g_pulse_s => sel_a_b(g_sim, 10, 1000) -- nof pulse_ms pulses to get s period + ) + port map ( + rst => mm_rst, + clk => mm_clk, + pulse_s => mm_pulse_s + ); + + u_mm_common_toggle_s : entity common_lib.common_toggle + port map ( + rst => mm_rst, + clk => mm_clk, + in_dat => mm_pulse_s, + out_dat => mm_toggle_s + ); + + -- dp_clk domain pulsers + u_dp_common_pulser_us_ms_s : entity common_lib.common_pulser_us_ms_s + generic map ( + g_pulse_us => g_dp_pulse_us, -- nof dp_clk cycles to get us period + g_pulse_ms => sel_a_b(g_sim, 10, 1000), -- nof pulse_us pulses to get ms period + g_pulse_s => sel_a_b(g_sim, 10, 1000) -- nof pulse_ms pulses to get s period + ) + port map ( + rst => dp_rst, + clk => dp_clk, + pulse_ms => dp_pulse_ms, + pulse_s => dp_pulse_s + ); + + u_dp_common_toggle_s : entity common_lib.common_toggle + port map ( + rst => dp_rst, + clk => dp_clk, + in_dat => dp_pulse_s, + out_dat => dp_toggle_s + ); + + gen_factory_image : if g_factory_image = true generate + green_led_arr <= (others => '0'); + + red_led_arr(0) <= '1'; + red_led_arr(1) <= mm_toggle_s; + red_led_arr(2) <= dp_toggle_pps; + red_led_arr(3) <= dp_toggle_s; + red_led_arr(4) <= ddr_I_cal_ok; + red_led_arr(5) <= ddr_II_cal_ok; + + u_dp_common_toggle_pps : entity common_lib.common_toggle + port map ( + rst => dp_rst, + clk => dp_clk, + in_dat => dp_pps, + out_dat => dp_toggle_pps + ); + end generate; + + gen_user_image : if g_factory_image = false generate + red_led_arr <= (others => '0'); + + gen_green_ctrl_arr : for I in c_nof_lanes - 1 downto 0 generate + green_on_arr(I) <= tx_siso_arr(I).xon when rising_edge(dp_clk); + green_evt_arr(I) <= tx_sosi_arr(I).sop or rx_sosi_arr(I).sop when rising_edge(dp_clk); + end generate; + + gen_green_led_arr : for I in g_nof_qsfp - 1 downto 0 generate + qsfp_on_arr(I) <= orv(green_on_arr( (I + 1) * c_quad - 1 downto + I * c_quad)); + qsfp_evt_arr(I) <= orv(green_evt_arr((I + 1) * c_quad - 1 downto + I * c_quad)); + + u_green_led_controller : entity common_lib.common_led_controller + generic map ( + g_nof_ms => c_nof_ms + ) + port map ( + rst => dp_rst, + clk => dp_clk, + pulse_ms => dp_pulse_ms, + -- led control + ctrl_on => qsfp_on_arr(I), + ctrl_evt => qsfp_evt_arr(I), + ctrl_input => dp_toggle_s, + -- led output + led => green_led_arr(I) + ); + end generate; + end generate; +end str; diff --git a/boards/uniboard2c/libraries/unb2c_board/tb/vhdl/tb_unb2c_board_qsfp_leds_v2.vhd b/boards/uniboard2c/libraries/unb2c_board/tb/vhdl/tb_unb2c_board_qsfp_leds_v2.vhd new file mode 100644 index 0000000000000000000000000000000000000000..3f63e429586530a8e7ee0a6495a455168dc33cf0 --- /dev/null +++ b/boards/uniboard2c/libraries/unb2c_board/tb/vhdl/tb_unb2c_board_qsfp_leds_v2.vhd @@ -0,0 +1,210 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2024 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- +-- Author: E. Kooistra +-- Purpose: Test bench for unb2c_board_qsfp_leds +-- Description: +-- The test bench is self-stopping but not self-checking. Manually obeserve +-- in the wave window that: +-- 1) factory image: +-- - green led is off +-- - red led toggles +-- 2) user image +-- - red led is off +-- - green led toggles when any xon='0' +-- - green led is on continously when any xon='1' +-- - green led goes briefly off when any sop='1' +-- Usage: +-- > as 3 +-- > run -a + +library IEEE, common_lib, dp_lib; +use IEEE.std_logic_1164.all; +use common_lib.common_pkg.all; +use common_lib.tb_common_pkg.all; +use dp_lib.dp_stream_pkg.all; + +entity tb_unb2c_board_qsfp_leds_v2 is +end tb_unb2c_board_qsfp_leds_v2; + +architecture tb of tb_unb2c_board_qsfp_leds_v2 is + constant c_sim_factor : natural := 100; -- same as in unb2c_board_qsfp_leds_v2.vhd + + constant c_mm_clk_freq_hz : natural := 125 * 10**6; + constant c_mm_clk_period_ns : natural := 10**9 / c_mm_clk_freq_hz; + constant c_nof_mm_clk_per_us : natural := 1000 / c_mm_clk_period_ns; + + constant c_dp_clk_freq_hz : natural := 125 * 10**6; + constant c_dp_clk_period_ns : natural := 10**9 / c_dp_clk_freq_hz; + constant c_nof_dp_clk_per_us : natural := 1000 / c_dp_clk_period_ns; + + constant mm_clk_period : time := c_mm_clk_period_ns * 1 ns; + constant dp_clk_period : time := c_dp_clk_period_ns * 1 ns; + + constant c_nof_qsfp : natural := 6; + constant c_nof_lanes : natural := c_nof_qsfp * c_quad; + + signal tb_end : std_logic := '0'; + signal mm_rst : std_logic := '1'; + signal mm_clk : std_logic := '0'; + signal mm_pulse_ms : std_logic := '0'; + signal dp_rst : std_logic := '1'; + signal dp_clk : std_logic := '0'; + signal dp_pps : std_logic := '0'; + + signal ddr_I_cal_ok : std_logic := '0'; + signal ddr_II_cal_ok : std_logic := '0'; + + signal tx_siso_arr : t_dp_siso_arr(c_nof_lanes - 1 downto 0) := (others => c_dp_siso_rst); + signal tx_sosi_arr : t_dp_sosi_arr(c_nof_lanes - 1 downto 0) := (others => c_dp_sosi_rst); + signal rx_sosi_arr : t_dp_sosi_arr(c_nof_lanes - 1 downto 0) := (others => c_dp_sosi_rst); + + signal dbg_xon_arr : std_logic_vector(c_nof_lanes - 1 downto 0); + signal dbg_tx_sop_arr : std_logic_vector(c_nof_lanes - 1 downto 0); + signal dbg_rx_sop_arr : std_logic_vector(c_nof_lanes - 1 downto 0); + + signal factory_green_led_arr : std_logic_vector(c_nof_qsfp - 1 downto 0); + signal factory_red_led_arr : std_logic_vector(c_nof_qsfp - 1 downto 0); + + signal user_green_led_arr : std_logic_vector(c_nof_qsfp - 1 downto 0); + signal user_red_led_arr : std_logic_vector(c_nof_qsfp - 1 downto 0); + + -- Cannot use proc_common_gen_pulse() to create sop in array. + -- proc_common_gen_pulse() works for dbg_sop, dbg_sosi.sop but not for dbg_sop_slv(I) or for tx_sosi_arr(I).sop. + -- The compiler then gives Error: "(vcom-1450) Actual (indexed name) for formal "pulse" is not a static signal name" + -- It does work if the array index is from a GENERATE statement, but it does not work when it is from a LOOP statement. + signal dbg_sop : std_logic; + signal dbg_sop_slv : std_logic_vector(c_nof_lanes - 1 downto 0); + signal dbg_sosi : t_dp_sosi; +begin + mm_clk <= not mm_clk or tb_end after mm_clk_period / 2; + mm_rst <= '1', '0' after mm_clk_period * 7; + dp_clk <= not dp_clk or tb_end after dp_clk_period / 2; + dp_rst <= '1', '0' after dp_clk_period * 7; + + proc_common_gen_pulse(1, (c_nof_mm_clk_per_us * 10**3) / c_sim_factor, '1', mm_rst, mm_clk, mm_pulse_ms); + proc_common_gen_pulse(1, (c_nof_dp_clk_per_us * 10**6) / c_sim_factor**2, '1', dp_rst, dp_clk, dp_pps); + + -- Ease observation of record fields in Wave window, by mapping them to a SLV + dbg_xon_arr <= func_dp_stream_arr_get(tx_siso_arr, "XON"); + dbg_tx_sop_arr <= func_dp_stream_arr_get(tx_sosi_arr, "SOP"); + dbg_rx_sop_arr <= func_dp_stream_arr_get(rx_sosi_arr, "SOP"); + + p_stimuli : process + begin + tx_siso_arr <= (others => c_dp_siso_rst); + tx_sosi_arr <= (others => c_dp_sosi_rst); + rx_sosi_arr <= (others => c_dp_sosi_rst); + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 50); + + -- Toggle ddr calibration status + ddr_I_cal_ok <= '1'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 5); + ddr_II_cal_ok <= '1'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 50); + ddr_I_cal_ok <= '0'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 5); + ddr_II_cal_ok <= '0'; + + -- Switch on each lane + for I in 0 to c_nof_lanes - 1 loop + tx_siso_arr(I).xon <= '1'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 10); + end loop; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 50); + + -- Issue the sop of a Tx packet on each lane + for I in 0 to c_nof_lanes - 1 loop + -- Cannot use proc_common_gen_pulse(), because index I in a LOOP is not static + tx_sosi_arr(I).sop <= '1'; + wait until rising_edge(mm_clk); + tx_sosi_arr(I).sop <= '0'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 10); + end loop; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 50); + + -- Issue the sop of an Rx packet on each lane + for I in 0 to c_nof_lanes - 1 loop + -- Cannot use proc_common_gen_pulse(), because index I in a LOOP is not static + rx_sosi_arr(I).sop <= '1'; + wait until rising_edge(mm_clk); + rx_sosi_arr(I).sop <= '0'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 10); + end loop; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 50); + + -- Switch off each lane + for I in 0 to c_nof_lanes - 1 loop + tx_siso_arr(I).xon <= '0'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 10); + end loop; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 50); + + tb_end <= '1'; + proc_common_wait_some_pulses(mm_clk, mm_pulse_ms, 10); + wait; + end process; + + u_unb2c_factory_qsfp_leds_v2 : entity work.unb2c_board_qsfp_leds_v2 + generic map ( + g_sim => true, -- when true speed up led toggling in simulation + g_factory_image => true, -- distinguish factory image and user images + g_nof_qsfp => c_nof_qsfp, -- number of QSFP cages each with one dual led that can light red or green (or amber = red + green) + g_mm_pulse_us => c_nof_mm_clk_per_us, -- nof mm_clk cycles to get us period< + g_dp_pulse_us => c_nof_dp_clk_per_us -- nof dp_clk cycles to get us period< + ) + port map ( + mm_rst => mm_rst, + mm_clk => mm_clk, + dp_rst => dp_rst, + dp_clk => dp_clk, + dp_pps => dp_pps, + -- ddr status + ddr_I_cal_ok => ddr_I_cal_ok, + ddr_II_cal_ok => ddr_II_cal_ok, + -- leds + green_led_arr => factory_green_led_arr, + red_led_arr => factory_red_led_arr + ); + + u_unb2c_user_qsfp_leds_v2 : entity work.unb2c_board_qsfp_leds_v2 + generic map ( + g_sim => true, -- when true speed up led toggling in simulation + g_factory_image => false, -- distinguish factory image and user images + g_nof_qsfp => c_nof_qsfp, -- number of QSFP cages each with one dual led that can light red or green (or amber = red + green) + g_mm_pulse_us => c_nof_mm_clk_per_us, -- nof mm_clk cycles to get us period< + g_dp_pulse_us => c_nof_dp_clk_per_us -- nof dp_clk cycles to get us period< + ) + port map ( + mm_rst => mm_rst, + mm_clk => mm_clk, + dp_rst => dp_rst, + dp_clk => dp_clk, + dp_pps => dp_pps, + -- lane status + tx_siso_arr => tx_siso_arr, + tx_sosi_arr => tx_sosi_arr, + rx_sosi_arr => rx_sosi_arr, + -- leds + green_led_arr => user_green_led_arr, + red_led_arr => user_red_led_arr + ); +end tb;