---------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 3/27/2014 -- Design Name: -- Module Name: LaunchCaptureEngine - Behavioral -- Project Name: -- Target Devices: -- Tool versions: -- Description: -- -- Dependencies: -- -- Revision: -- Revision 0.01 - File Created -- Additional Comments: -- ---------------------------------------------------------------------------------- -- =================================================================================================== -- =================================================================================================== library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.all; -- This state machine responsible for handling the ClkEn signal for the Launch row(s). entity L_Control is Port ( Clk_Launch: in std_logic; RESET: in std_logic; L_go: in std_logic; DisableClkGating: in std_logic; ready: out std_logic; C_go: out std_logic; ClkEn_Launch: out std_logic); end L_Control; architecture beh of L_Control is type state_type is (idle, fire_clk); signal state_reg, state_next: state_type; signal ready_reg, ready_next: std_logic; signal GenEdge: std_logic; begin -- State and register logic process(Clk_Launch, RESET) begin if (RESET = '1') then state_reg <= idle; ready_reg <= '1'; elsif (Clk_Launch'event and Clk_Launch = '1') then state_reg <= state_next; ready_reg <= ready_next; end if; end process; -- Combo logic for state machine process (state_reg, L_go, ready_reg) begin ready_next <= ready_reg; state_next <= state_reg; GenEdge <= '0'; case state_reg is -- ===================== when idle => ready_next <= '1'; if ( L_go = '1' ) then state_next <= fire_clk; ready_next <= '0'; end if; -- ===================== -- Hold GenEdge high for 1 clock cycle when fire_clk => -- This guarantees that the capture engine defined below doesn't set ClkEn_Capture high before the launch engine sets -- ClkEn_Launch high. If the LC interval gets too small, this will add a FULL cycle delay to the capture engine. -- This is typically not a problem because the core logic delay is large enough (> couple ns) forcing the -- LC interval to be skewed by enough for this to operate properly GenEdge <= '1'; state_next <= idle; end case; end process; -- ClkEn MUST be high BEFORE the rising edge of clk and then immediately turn it off (when using it for REBEL -- launch/capture). Need to keep an eye on this -- particularly if ClkEn starts driving a lot of launch/capture FFs. -- Carried out post-place-and-route simulations on this module to confirm operation. DisableClkGating signal is used for scan ClkEn_Launch <= '1' when (GenEdge = '1') else '1' when (DisableClkGating = '1' ) else '0'; ready <= ready_reg; C_go <= GenEdge; end beh; -- =================================================================================================== -- =================================================================================================== library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.all; -- This state machine is very similar to the L_Control above. Needed to create a separate instance because the ClkEn signal -- can be handled differently here (more freedom) and needs to be in order to allow the delay chain to be sensitive as long -- as possible to the PUT. With a 100 MHz clock, the largest amount of time the delay chain is sensitive is 5 ns (as determined -- by the length of the interval in which the capture clock is low). In order to get all 5 ns, I assert the ClkEn earlier in -- this state machine (then in the L_Control above). The only timing constraint here is that ClkEn MUST be de-asserted BEFORE -- the falling edge of the capture clock that FOLLOWS the capturing rising edge. Just the opposite to the requirements above. -- This is to prevent REBEL from going back into flush delay mode. entity C_Control is Port ( Clk_Capture: in std_logic; RESET: in std_logic; C_go: in std_logic; DisableClkGating: in std_logic; ready: out std_logic; ClkEn_Capture: out std_logic); end C_Control; architecture beh of C_Control is type state_type is (idle, fire_clk); signal state_reg, state_next: state_type; signal ready_reg, ready_next: std_logic; signal GenEdge: std_logic; begin -- State and register logic process(Clk_Capture, RESET) begin if (RESET = '1') then state_reg <= idle; ready_reg <= '1'; elsif (Clk_Capture'event and Clk_Capture = '1') then state_reg <= state_next; ready_reg <= ready_next; end if; end process; -- Combo logic for state machine process (state_reg, C_go, ready_reg) begin ready_next <= ready_reg; state_next <= state_reg; GenEdge <= '0'; case state_reg is -- ===================== when idle => ready_next <= '1'; -- Wait for the launch engine (L_Control above) to enable the capture event. Immediately assert ClkEn (with GenEdge -- going high). Note, this is a difference between the Launch and Capture instance -- see note above. Since GenEdge is -- combinational, it is subject to glitching (which fortunately, doesn't appear to happen). Probably a better way to do this. if ( C_go = '1' ) then GenEdge <= '1'; state_next <= fire_clk; ready_next <= '0'; end if; -- ===================== -- Continue to hold GenEdge high when fire_clk => state_next <= idle; GenEdge <= '1'; end case; end process; -- ClkEn MUST be high for as long as possible BEFORE the rising edge of clk and then -- immediately turn it off. See requirements above ClkEn_Capture <= '1' when (GenEdge = '1') else '1' when (DisableClkGating = '1') else '0'; ready <= ready_reg; end beh; -- =================================================================================================== -- =================================================================================================== library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.all; -- The module carries out a launch capture test. It takes a requested phase tunes the phase shift of the capture -- clock (waiting as appropriate for PhaseAdjDone for each tick of phase), and initiates two timed LC events -- in L_Control and C_control. -- LC_start starts the whole process, DisableClkGating_xxx allow other modules to enable clocking of the launch -- and capture FFs (for scan configuration data). REBEL_FDMode is controlled within this module to activate the -- delay chain. FPA_PhaseAdjEn is the activating signal to the DCM (controlled within this module), FPA_IncDecCtrl -- controls signal direction -- positive shifts (1) the capture clock forward (later) in time. FPA_PhaseAdjDone -- is a feedback signal FROM the DCM -- must wait for a pulse on this signal before carrying out the next tick. -- ClkEn_Launch/ClkEn_Capture are the main output control signals. The model is to gate the clocks using the gate -- controls on the Xilinx primitives instead of sticking a gate in series with the launch/capture clocks. L_go_out and -- C_go_out are NOT used in the parent -- have it to allow observation of these signal in simulations. -- Main limiting factor is the speed at which ClkEn can be changed. entity LaunchCaptureEngine is Generic( PHASE_SHIFT_LEN_NB: integer := 11; PHASE_SHIFT_RANGE: integer := 1120; PHASE_SHIFT_ZEROPHASE: integer := 0; MAX_PUT_WAIT_CYCLES_NB: integer := 2; MAX_PUT_WAIT_CYCLES: integer := 3); Port ( Clk_Launch: in std_logic; Clk_Capture: in std_logic; RESET: in std_logic; LC_start: in std_logic; requested_phase: in std_logic_vector(PHASE_SHIFT_LEN_NB-1 downto 0); DisableClkGating_Launch: in std_logic; DisableClkGating_Capture: in std_logic; Clk_Capture_Sel: out std_logic; ready: out std_logic; REBEL_FDMode: out std_logic; FPA_PhaseAdjEn: out std_logic; FPA_IncDecCtrl: out std_logic; FPA_PhaseAdjDone: in std_logic; ClkEn_Launch: out std_logic; ClkEn_Capture: out std_logic; REBEL_QMOutRow: in std_logic; PUT_init_val: out std_logic; -- DEBUG L_go_out: out std_logic; C_go_out: out std_logic); -- fire_clks1: out std_logic; -- fire_clks2: out std_logic); end LaunchCaptureEngine; architecture beh of LaunchCaptureEngine is type state_type is (idle, adjust_phase, adjust_done1, adjust_done2, change_capture_phase, fire_LC_clks, wait_PUT_init_value, L_go_hold, complete_launch, return_launch_clk, LC_complete); signal state_reg, state_next: state_type; -- NOTE: delta_phase is SIGNED because it can indicate an increase or decrease below. We make it one -- bit larger to allow for the sign bit. signal current_phase_reg, current_phase_next: unsigned(PHASE_SHIFT_LEN_NB-1 downto 0); signal delta_phase_reg, delta_phase_next: signed(PHASE_SHIFT_LEN_NB downto 0); signal PUT_init_val_reg, PUT_init_val_next: std_logic; signal wait_PUT_cycles_reg, wait_PUT_cycles_next: unsigned(MAX_PUT_WAIT_CYCLES_NB-1 downto 0); signal ready_reg, ready_next: std_logic; signal ready_Launch, ready_Capture: std_logic; -- These need to be glitch free -- make them registers signal Clk_Capture_Sel_reg, Clk_Capture_Sel_next: std_logic; signal L_go_reg, L_go_next: std_logic; signal REBEL_FDMode_reg, REBEL_FDMode_next: std_logic; signal allow_REBEL_FDMode_reg, allow_REBEL_FDMode_next: std_logic; signal PhaseAdjEn_reg, PhaseAdjEn_next: std_logic; signal IncDecCtrl_reg, IncDecCtrl_next: std_logic; signal allow_scan_REBEL_FD : std_logic; signal C_go: std_logic; begin -- L_Control/C_Control incorporates a state machine that implements the timing to implement the launch -- capture event. Each copy runs on their own clock. L_go is made a register to prevent glitching. -- C_go is combinational to allow it to change quickly. Forces capture state machine to always -- lag the launch state machine. From simulations, looks like it takes some time for this signal to be -- recognized as high, so if the launch and capture clocks are less than 1.5ns (ZED board), the capture -- event will have a glitch and will be delayed a full clock cycle behind the capture -- DOES NOT WORK -- properly. Stay away from this region. LaunchControl: entity work.L_Control(beh) port map (Clk_Launch=>Clk_Launch, RESET=>RESET, L_go=>L_go_reg, DisableClkGating=>DisableClkGating_Launch, ready=>ready_Launch, C_go=>C_go, ClkEn_Launch=>ClkEn_Launch); CaptureControl: entity work.C_Control(beh) port map (Clk_Capture=>Clk_Capture, RESET=>RESET, C_go=>C_go, DisableClkGating=>allow_scan_REBEL_FD, ready=>ready_Capture, ClkEn_Capture=>ClkEn_Capture); -- Enable the capture latches to respond to the capture clock when in scan mode OR after we enter REBEL_FDMode to allow -- the scan chain to flush out. allow_scan_REBEL_FD <= DisableClkGating_Capture or allow_REBEL_FDMode_reg; -- State and register logic -- use un-phaseshifted launch clk process(Clk_Launch, RESET) begin if (RESET = '1') then state_reg <= idle; Clk_Capture_Sel_reg <= '0'; -- Initialize the current phase to the default value after reset. current_phase_reg <= to_unsigned(PHASE_SHIFT_ZEROPHASE, PHASE_SHIFT_LEN_NB); delta_phase_reg <= to_signed(0, PHASE_SHIFT_LEN_NB+1); L_go_reg <= '0'; REBEL_FDMode_reg <= '0'; allow_REBEL_FDMode_reg <= '0'; PhaseAdjEn_reg <= '0'; IncDecCtrl_reg <= '0'; PUT_init_val_reg <= '0'; wait_PUT_cycles_reg <= (others=>'0'); ready_reg <= '1'; elsif (Clk_Launch'event and Clk_Launch = '1') then state_reg <= state_next; Clk_Capture_Sel_reg <= Clk_Capture_Sel_next; current_phase_reg <= current_phase_next; delta_phase_reg <= delta_phase_next; L_go_reg <= L_go_next; REBEL_FDMode_reg <= REBEL_FDMode_next; allow_REBEL_FDMode_reg <= allow_REBEL_FDMode_next; PhaseAdjEn_reg <= PhaseAdjEn_next; IncDecCtrl_reg <= IncDecCtrl_next; PUT_init_val_reg <= PUT_init_val_next; wait_PUT_cycles_reg <= wait_PUT_cycles_next; ready_reg <= ready_next; end if; end process; -- =================================================================================================== -- =================================================================================================== process (state_reg, LC_start, requested_phase, ready_Launch, ready_Capture, current_phase_reg, delta_phase_reg, FPA_PhaseAdjDone, L_go_reg, Clk_Capture_Sel_reg, REBEL_FDMode_reg, allow_REBEL_FDMode_reg, PhaseAdjEn_reg, IncDecCtrl_reg, REBEL_QMOutRow, PUT_init_val_reg, wait_PUT_cycles_reg, ready_reg, delta_phase_next) begin -- Launch engine go signal -- allows the capture engine to start. L_go_next <= L_go_reg; -- Flush delay mode signals REBEL_FDMode_next <= REBEL_FDMode_reg; allow_REBEL_FDMode_next <= allow_REBEL_FDMode_reg; -- Current phase shift -- we need to keep track of it since we don't reset the DCM any longer back to 0 phase when we finish one -- iteration of this engine. Instead, we leave the current phase and adjust from this point when a new requested value comes in. current_phase_next <= current_phase_reg; delta_phase_next <= delta_phase_reg; -- These are the DCM control signals (as registers) PhaseAdjEn_next <= PhaseAdjEn_reg; IncDecCtrl_next <= IncDecCtrl_reg; -- After entering FDMode and waiting sufficiently long, save the initial value on the output of the PUT PUT_init_val_next <= PUT_init_val_reg; wait_PUT_cycles_next <= wait_PUT_cycles_reg; state_next <= state_reg; ready_next <= ready_reg; -- This signal switches from the launch clock for the REBEL row to the phase-shifted capture clock for the REBEL row. Clk_Capture_Sel_next <= Clk_Capture_Sel_reg; -- DEBUG -- fire_clks1 <= '0'; -- fire_clks2 <= '0'; -- ========================================================= case state_reg is when idle => ready_next <= '1'; -- Go to phase adjust state if command is given to launch edge. If the requested phase is equal to the current phase, be sure to -- go through the adjust phase states even though we are not adjusting phase -- this allows REBEL to flush out. if ( LC_start = '1' ) then ready_next <= '0'; wait_PUT_cycles_next <= (others => '0'); state_next <= adjust_phase; end if; -- =============== -- Adjust the fine phase shift of the capture clock when adjust_phase => -- This puts the REBEL row(s) into flush delay mode. NOTE: WE MUST LEAVE ENOUGH TIME for the existing values to flush out BEFORE -- firing the clks in the states below. REBEL_FDMode_next <= '1'; allow_REBEL_FDMode_next <= '1'; -- Enable phase adjustment if delta is not 0. HOLD PhaseAdjEn high FOR EXACTLY ONE CLOCK CYCLE. If the requested phase and the current -- phase are equal, do NOT adjust phase. delta_phase_next <= signed('0' & requested_phase) - signed('0' & std_logic_vector(current_phase_reg)); if ( delta_phase_next /= 0 ) then PhaseAdjEn_next <= '1'; end if; -- Direction is defined as 'to the left' for '0' and 'to the right' for '1'. If the requested phase is larger than the current, than move -- phase to the right (larger phase shift), otherwise decreases it. if ( delta_phase_next >= 0 ) then IncDecCtrl_next <= '1'; else IncDecCtrl_next <= '0'; end if; state_next <= adjust_done1; -- =============== -- Wait for operation to complete when adjust_done1 => PhaseAdjEn_next <= '0'; -- Since we are in FD mode, count these cycles toward the wait required. Don't allow the counter to exceed the -- maximum value. if ( wait_PUT_cycles_reg < MAX_PUT_WAIT_CYCLES - 1 ) then wait_PUT_cycles_next <= wait_PUT_cycles_reg + 1; end if; -- Don't wait if we did NOT adjust phase. if ( delta_phase_reg = 0 ) then state_next <= adjust_done2; -- Else wait until the phase adjust done signal goes high -- takes about 15 clocks for the Zynq chip elsif ( FPA_PhaseAdjDone = '1' ) then state_next <= adjust_done2; end if; -- =============== when adjust_done2 => -- Since we are in FD mode, count these cycles toward the wait required. Don't allow the counter to exceed the -- maximum value. if ( wait_PUT_cycles_reg < MAX_PUT_WAIT_CYCLES - 1 ) then wait_PUT_cycles_next <= wait_PUT_cycles_reg + 1; end if; -- Switch from Launch to Capture DCM for capture row. This is done to account for the propagation delay in the core -- logic. BUFGMUX needs a cycle to begin operation. For example, if the capture current clock input is high, then -- the switch does NOT happen. Once this clock goes low, if the phase shifted clock is low (which should be the case), -- then the switch is made if ( delta_phase_reg = 0 ) then Clk_Capture_Sel_next <= '1'; state_next <= change_capture_phase; -- Adjust the current phase toward the requested phase by 1 in cases where they are NOT equal elsif ( delta_phase_reg > 0 ) then current_phase_next <= current_phase_reg + 1; state_next <= adjust_phase; else current_phase_next <= current_phase_reg - 1; state_next <= adjust_phase; end if; -- =============== -- Wait for the capture clock to change before the launch/capture event (typically 1 clock cycle). Since I'm sending -- both the launch and capture clock through the BUFGMUX, both are halted for 1 clock so this state not needed -- any longer. when change_capture_phase => state_next <= wait_PUT_init_value; -- =============== -- Save the initial value of the PUT in a register. Since we have been in FD mode for at least 4 clock cycles at this -- point (when there is no fine phase shift), we can use the value on the output of QMNext wire. when wait_PUT_init_value => PUT_init_val_next <= REBEL_QMOutRow; if ( wait_PUT_cycles_reg = MAX_PUT_WAIT_CYCLES - 1 ) then state_next <= fire_LC_clks; else wait_PUT_cycles_next <= wait_PUT_cycles_reg + 1; end if; -- =============== -- FIRE CLKS -- Start the launch capture state machines. when fire_LC_clks => -- Turn off the forcing of the ClkEn high -- the L & C state machines will handle these signals from here on out allow_REBEL_FDMode_next <= '0'; -- When both state machines are ready, fire the LC clocks. if ( ready_Launch = '1' and ready_Capture = '1' ) then L_go_next <= '1'; state_next <= L_go_hold; -- DEBUG -- fire_clks1 <= '1'; end if; -- =============== -- Enter 'fire_clk' in L_Control. Allow a cycle the state machines to see L_go_reg at '1' when L_go_hold => state_next <= complete_launch; -- DEBUG -- fire_clks2 <= '1'; -- =============== -- Wait for completion when complete_launch => L_go_next <= '0'; -- Probably don't finish at the same time. if ( ready_Launch = '1' and ready_Capture = '1' ) then state_next <= return_launch_clk; end if; -- =============== -- Reset to the launch clock as the source for the capture row. See note above, takes a clock or two. when return_launch_clk => Clk_Capture_Sel_next <= '0'; state_next <= LC_complete; -- =============== -- Return to idle, or if a phase fine adjustment was made, undo them. when LC_complete => ready_next <= '1'; -- Before returning to idle, turn off FD mode. REBEL_FDMode_next <= '0'; state_next <= idle; end case; end process; -- DEBUG L_go_out <= L_go_reg; C_go_out <= C_go; REBEL_FDMode <= REBEL_FDMode_reg; FPA_PhaseAdjEn <= PhaseAdjEn_reg; FPA_IncDecCtrl <= IncDecCtrl_reg; Clk_Capture_Sel <= Clk_Capture_Sel_reg; ready <= ready_reg; PUT_init_val <= PUT_init_val_reg; end beh;