---------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 17:06:55 10/21/2011 -- Design Name: -- Module Name: LCTest - 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 sequencing the configuration steps for the launch registers -- and the REBEL capture row across the set of fine phase shifts available from the DCM. The -- resulting LC interval (that results in the edge just arriving or disappearing in a target FF) is -- stored in LC_phaseshift_request register. It effectively initiates the LaunchCaptureEngine -- for each of the possible fine phase shifts after scanning in the configuration data. entity LCSweep is Generic( DESIGN_WIDTH_LB: integer := 8; DESIGN_WIDTH_NB: integer := 256; PHASE_SHIFT_LEN_NB: integer := 10; PHASE_SHIFT_RANGE: integer := 740; PHASE_SHIFT_ZEROPHASE: integer := 0; REBEL_LENGTH_LB: integer := 9; REBEL_LENGTH_NB: integer := 264; MAX_SWEEP_TRANSITIONS_NB: integer := 8); Port ( Clk : in std_logic; RESET: in std_logic; REBEL_Config_ins_point: in std_logic_vector(REBEL_LENGTH_LB-1 downto 0); REBEL_num_result_trans: out std_logic_vector(MAX_SWEEP_TRANSITIONS_NB-1 downto 0); REBEL_num_sweep_trans: out std_logic_vector(MAX_SWEEP_TRANSITIONS_NB-1 downto 0); LC_start: out std_logic; LC_ready: in std_logic; start: in std_logic; ready: out std_logic; DEBUG_dump_data : out std_logic; DEBUG_out_ready: in std_logic; DEBUG_print_all_strobes: in std_logic; PUT_init_val: in std_logic; TOP_REBEL_results: in std_logic_vector(REBEL_LENGTH_NB-1 downto 0); target_index: in std_logic_vector(REBEL_LENGTH_LB-1 downto 0); back_or_forward_search: in std_logic; LC_phaseshift_request: out std_logic_vector(PHASE_SHIFT_LEN_NB-1 downto 0); success_flag: out std_logic; TOP_enrollment: in std_logic); end LCSweep; architecture beh of LCSweep is -- We don't want to run the phase shift all the way out to 560, nor do we want to reduce it to 0. I'll probably tune this -- according to the path delays I'm seeing. These represent the lower and upper bounds on the phase shift. Takes 15 -- clocks to move phase by 1. Each shift is 10ns/560 = 17.86 ps, so setting these to 56 makes the range 1 ns to 9 ns. -- This is 4 ns to 9 ns constant PHASE_SHIFT_LOWER_BOUND: integer := 224; constant PHASE_SHIFT_UPPER_BOUND: integer := PHASE_SHIFT_RANGE - 30; -- At 75 MHz, there are 740 fine phase shifts. If we test each one, then consecutive phase shifts are about 20 ps apart. -- Although this is really great for high resolution, it takes too long. Setting this to 5 causes 5 increments to occur -- between each consecutive LC test in the sweep, which is about 100 ps of resolution. This can be tuned as needed. constant PHASE_SHIFT_INCREMENT: integer := 5; -- This constant determines HOW MANY FFs in the REBEL row are inspected for MULTIPLE transitions (from the inspection -- point). -- THIS IS BASED ON THE CURRENT RESTRICTION that the propagation delay allowed along the delay chain is restricted to 5 ns. -- NOTE: I only extend the delay chain by 8 elements (at the end of REBEL) so making this larger (to be safe) doesn't help the -- insertion points on the far right in the delay chain. THIS ONLY impacts the evaluation of the number of transitions. constant XOR_INSPECTION_NUM_FFS: integer := 20; type state_type is (idle, go_LC, wait_LC, prepare_eval, eval_result, dump_data, check_term_cond, wait_dump, flip_direction, incdec_phase_request); signal state_reg, state_next: state_type; signal ready_reg, ready_next: std_logic; signal LC_phaseshift_request_reg, LC_phaseshift_request_next: unsigned(PHASE_SHIFT_LEN_NB-1 downto 0); signal REBEL_position_reg, REBEL_position_next: unsigned(REBEL_LENGTH_LB-1 downto 0); signal REBEL_term_pos_reg, REBEL_term_pos_next: unsigned(REBEL_LENGTH_LB-1 downto 0); signal REBEL_num_result_trans_reg, REBEL_num_result_trans_next: unsigned(MAX_SWEEP_TRANSITIONS_NB-1 downto 0); signal REBEL_num_sweep_trans_reg, REBEL_num_sweep_trans_next: unsigned(MAX_SWEEP_TRANSITIONS_NB-1 downto 0); signal flip_dir_reg, flip_dir_next: std_logic; signal success_reg, success_next: std_logic; -- This will be used as an index into TOP_REBEL_results, which has REBEL_LENGTH_NB bits. Since it is -- an index, it needs to be restricted to 0 to REBEL_LENGTH_NB-1. Technically, it can be -2 because the -- index value is always 1 less than the first bits to xor (bits index+1 and index are xored). This is -- to satisfy the synthesis engine -- otherwise it complains about a 'simulation mismatch' problem. subtype index_type is integer range 0 to REBEL_LENGTH_NB-1; signal index: index_type; begin -- State and register logic process(Clk, RESET) begin if (RESET = '1') then state_reg <= idle; ready_reg <= '1'; -- After reset, we are at zero phase LC_phaseshift_request_reg <= to_unsigned(PHASE_SHIFT_ZEROPHASE, PHASE_SHIFT_LEN_NB); REBEL_position_reg <= (others => '0'); REBEL_term_pos_reg <= (others => '0'); REBEL_num_result_trans_reg <= (others => '0'); REBEL_num_sweep_trans_reg <= (others => '0'); flip_dir_reg <= '0'; success_reg <= '0'; elsif (Clk'event and Clk = '1') then state_reg <= state_next; ready_reg <= ready_next; LC_phaseshift_request_reg <= LC_phaseshift_request_next; REBEL_position_reg <= REBEL_position_next; REBEL_term_pos_reg <= REBEL_term_pos_next; REBEL_num_result_trans_reg <= REBEL_num_result_trans_next; REBEL_num_sweep_trans_reg <= REBEL_num_sweep_trans_next; flip_dir_reg <= flip_dir_next; success_reg <= success_next; end if; end process; -- Combo logic for state machine process (state_reg, start, ready_reg, REBEL_position_reg, REBEL_position_next, REBEL_term_pos_reg, REBEL_num_result_trans_reg, REBEL_num_sweep_trans_reg, REBEL_Config_ins_point, LC_ready, LC_phaseshift_request_reg, DEBUG_out_ready, PUT_init_val, TOP_REBEL_results, target_index, index, DEBUG_print_all_strobes, back_or_forward_search, flip_dir_reg, success_reg, TOP_enrollment) begin ready_next <= ready_reg; state_next <= state_reg; LC_start <= '0'; DEBUG_dump_data <= '0'; LC_phaseshift_request_next <= LC_phaseshift_request_reg; REBEL_position_next <= REBEL_position_reg; REBEL_term_pos_next <= REBEL_term_pos_reg; REBEL_num_result_trans_next <= REBEL_num_result_trans_reg; REBEL_num_sweep_trans_next <= REBEL_num_sweep_trans_reg; index <= 0; flip_dir_next <= flip_dir_reg; success_next <= success_reg; case state_reg is -- ===================== when idle => ready_next <= '1'; if ( start = '1' ) then ready_next <= '0'; -- Initialize the transition counters, one that counts them between the insertion point and the target -- and one that counts all of them. NOTE: These sums are computed across ALL phase shift trials. REBEL_num_sweep_trans_next <= (others => '0'); -- Indicate failure to find a valid path initially. success_next <= '0'; -- Start with phase shift all the way to the left (FORWARD sweep) or right (BACKWARD sweep). Set the flip_dir_reg if -- the back_or_forward_search is '1' (which indicates forward and backward search). Do NOT do the forward search -- component during REGEN -- forward search is only there to detect additional hazards and to exit early (see below) -- if they occur. We ALWAYS use the PN from the backward search as the result. if ( back_or_forward_search = '0' or TOP_enrollment = '0' ) then LC_phaseshift_request_next <= to_unsigned(PHASE_SHIFT_UPPER_BOUND, PHASE_SHIFT_LEN_NB); flip_dir_next <= '0'; else LC_phaseshift_request_next <= to_unsigned(PHASE_SHIFT_LOWER_BOUND, PHASE_SHIFT_LEN_NB); flip_dir_next <= '1'; end if; state_next <= go_LC; end if; -- ===================== -- Enter from above (on first iteration) or from below (on subsequent iterations). Fire the LC clocks to carry out the test. when go_LC => if ( LC_ready = '1' ) then LC_start <= '1'; state_next <= wait_LC; end if; when wait_LC => if ( LC_ready = '1' ) then state_next <= prepare_eval; end if; -- ===================== -- For subsequent iterations, repeat the above by scanning out the previous result and configuring for the next application. when prepare_eval => -- Start examining the REBEL_result row from the insertion point forward (toward 0). The insertion point can be a number -- 255 downto 0. If REBEL_LENGTH_NB is 264, then 264-256-1+255 = 262 when the insertion point 255. So we start comparing -- bit positions 263 and 262, etc. We subtract 1 to make the math easier in the next step. REBEL_position_next <= (REBEL_LENGTH_NB - DESIGN_WIDTH_NB - 1) + unsigned(REBEL_Config_ins_point); -- No sense in looking at the whole row, especially for insertion points that are on the far left of the REBEL row, -- since even the longest LC interval won't allow the edge to propagate more than about n FFs. We just waste cycles -- examining elements further then say twice this amount. if ( REBEL_position_next >= XOR_INSPECTION_NUM_FFS ) then REBEL_term_pos_next <= REBEL_position_next - to_unsigned(XOR_INSPECTION_NUM_FFS, REBEL_LENGTH_LB); else REBEL_term_pos_next <= (others => '0'); end if; -- For the result transitions, re-initialize on each LC test. The number of transition recorded in the last LC test in the sweep -- will be the number returned. REBEL_num_result_trans_next <= (others => '0'); state_next <= eval_result; -- ===================== -- Stay here and count down toward 0 across the REBEL row carrying out xor ops on bitwise neighbors. If xor generates 1, -- then add 1 to the counters. when eval_result => REBEL_position_next <= REBEL_position_reg - 1; index <= to_integer(REBEL_position_reg); -- XOR the current bit position with the one to the right. If different, add 1 to the sum. if ( (TOP_REBEL_results(index+1) xor TOP_REBEL_results(index)) = '1' ) then REBEL_num_result_trans_next <= REBEL_num_result_trans_reg + 1; -- Don't allow this to overflow (and wrap back around) -- hold at max value if we have too many transitions if ( REBEL_num_sweep_trans_reg /= (MAX_SWEEP_TRANSITIONS_NB-1 downto 0 => '1') ) then REBEL_num_sweep_trans_next <= REBEL_num_sweep_trans_reg + 1; end if; end if; -- Exit after we XOR'ed the relevant adjacent pairs of FFs in the results row. if ( REBEL_position_reg = REBEL_term_pos_reg ) then state_next <= dump_data; end if; -- ===================== -- Dump strobe data. Allowing parallelism here is of very little benefit. Whole process without printing (for 5,000 -- paths and 20 samples/path) takes about 7 seconds. With TOP_print_final_strobe turned on, it takes about 10 minutes! when dump_data => if ( DEBUG_print_all_strobes = '1' ) then if ( DEBUG_out_ready = '1' ) then DEBUG_dump_data <= '1'; state_next <= wait_dump; end if; else state_next <= check_term_cond; end if; when wait_dump => if ( DEBUG_out_ready = '1' ) then state_next <= check_term_cond; end if; -- ===================== -- If we ever see more than one transition in the LC test, exit with failure. when check_term_cond => -- For backward search, if we are successful in 'pushing' back the edge to just before the target FF, i.e., the target -- stores the initial value, we're done. We are also done if we reach the end of the sweep (0). If we are carrying out -- a forward AND backward search, then flip_dir_reg will be '0' during the second (backward) phase. Fail if we are in -- the first phase of a forward/backward search, i.e., the forward search component, and we reach the MAX phase shift. -- This indicates that we were NOT successful in pushing the edge forward to the target FF, i.e., the path is too long. index <= to_integer(unsigned(target_index)); -- First the fail case -- current LC test generated more than 1 transition (forward or backward) OR backward search or -- backward component forward/backward search produces LOWER BOUND for the fine phase shift, or forward component of -- forward/backward search produces UPPER BOUND. NOTE: We can NOT exit early because we detect MULTIPLE transitions -- -- this is true ONLY for enrollment -- REGEN must tolerate multiple transitions. Also note that we do NOT bother doing -- the forward component when back_or_forward_search is set to 1 since the only purpose of the forward search is to exit -- early if multiple transitions occur (glitching). During regen, we MUST test the path so do NOT bail out here. if ( REBEL_num_result_trans_reg > 1 or (flip_dir_reg = '0' and LC_phaseshift_request_reg = PHASE_SHIFT_LOWER_BOUND) or (flip_dir_reg = '1' and LC_phaseshift_request_reg = PHASE_SHIFT_UPPER_BOUND) ) then -- Indicate failure and give control back to the LCTest engine. success_next <= '0'; ready_next <= '1'; state_next <= idle; -- Success case -- backward search or backward component of forward/backward search and edge is pushed back JUST before -- target FF -- OR the LC capture interval is NOT wide enough and the transition has NOT yet reached the target FF. elsif ( flip_dir_reg = '0' and TOP_REBEL_results(index) = PUT_init_val ) then success_next <= '1'; ready_next <= '1'; state_next <= idle; -- If we were successful with the forward search, start the backward search. It could happen that the path is really short -- and therefore, the edge has already propagated past the target FF even at the shortest LC interval. In this case, even though -- this succeeds, we will fail in the backward search b/c we will NOT be able to push the edge back to the target. elsif (flip_dir_reg = '1' and TOP_REBEL_results(index) = not PUT_init_val ) then state_next <= flip_direction; -- Haven't achieved the goal but haven't yet reached the end points -- decrement/increment fine phase shift and try again. else state_next <= incdec_phase_request; end if; -- ===================== -- Flip direction and search backwards if we succeed in the forward direction. Jump immediately to the launch/capture state since we -- already configured the scan chains for the next test. when flip_direction => LC_phaseshift_request_next <= to_unsigned(PHASE_SHIFT_UPPER_BOUND, PHASE_SHIFT_LEN_NB); flip_dir_next <= '0'; state_next <= go_LC; -- ===================== -- Update the phaseshift_request register and repeat. Separated this from 'check_term_cond' to help timing. when incdec_phase_request => if ( flip_dir_reg = '0' ) then LC_phaseshift_request_next <= LC_phaseshift_request_reg - PHASE_SHIFT_INCREMENT; else LC_phaseshift_request_next <= LC_phaseshift_request_reg + PHASE_SHIFT_INCREMENT; end if; state_next <= go_LC; end case; end process; ready <= ready_reg; LC_phaseshift_request <= std_logic_vector(LC_phaseshift_request_reg); REBEL_num_result_trans <= std_logic_vector(REBEL_num_result_trans_reg); REBEL_num_sweep_trans <= std_logic_vector(REBEL_num_sweep_trans_reg); success_flag <= success_reg; end beh;