---------------------------------------------------------------------------------- -- 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( PHASE_SHIFT_LEN_NB: integer := 11; PHASE_SHIFT_RANGE: integer := 1120; PHASE_SHIFT_ZEROPHASE: integer := 0; XOR_INSPECTION_NUM_FFS: integer := 16; XOR_INSPECTION_NUM_FFS_NB: integer := 5); Port ( Clk: in std_logic; RESET: in std_logic; phase_shift_lower_bound: in std_logic_vector(PHASE_SHIFT_LEN_NB-1 downto 0); phase_shift_upper_bound: in std_logic_vector(PHASE_SHIFT_LEN_NB-1 downto 0); REBEL_num_result_trans: out std_logic_vector(XOR_INSPECTION_NUM_FFS_NB-1 downto 0); LC_start: out std_logic; LC_ready: in std_logic; start: in std_logic; ready: out std_logic; DEBUG_print_snapshots: in std_logic; DEBUG_snapshot_ready: out std_logic; DEBUG_processor_done_reading: in std_logic; PUT_init_val: in std_logic; REBEL_QMOuts_Seg: in std_logic_vector(XOR_INSPECTION_NUM_FFS downto 0); target_index: in std_logic_vector(XOR_INSPECTION_NUM_FFS_NB-1 downto 0); back_or_forward_search: in std_logic; LC_phaseshift_request: out std_logic_vector(PHASE_SHIFT_LEN_NB-1 downto 0); phase_shift_inc_dec: in std_logic_vector(PHASE_SHIFT_LEN_NB-1 downto 0); success_flag: out std_logic; TOP_authentication: in std_logic; TOP_enrollment: in std_logic); end LCSweep; architecture beh of LCSweep is -- Specifies the number of times the termination pattern must appear consecutitively in the target FF. IT MUST BE 1 or LARGER -- (DO NOT SPECIFY 0). For original behavior, use 1 which indicates that as soon as the edge is pushed back just before the -- target (termination pattern), we return control to the parent with success. Make sure _NB constant is large enough in bits to -- hold the termination constant. NOTE: this parameter is sensitive to the phase_shift_inc_dec value specified by the user. -- For this parameter, I do NOT adjust this based on the phase_shift_inc_dec value, unlike the WOBBLE_MIN_FF_STAY_FPA parameter -- below constant NUM_TERMINATIONS: integer := 4; constant NUM_TERMINATIONS_NB: integer := 3; -- The minimim number of FPAs that a transition must remain in a given FF in the delay chain (other than the first FF) -- before it moves to the next. Any number of PFAs less than this is considered a violation. Be careful setting this -- See 'with select' statement below. constant WOBBLE_MIN_FF_STAY_FPA: integer := 20; type state_type is (idle, go_LC, wait_LC, xor_segment, compute_num_trans, check_wobble, debug_dump_data, check_term_cond, 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_num_result_trans_reg, REBEL_num_result_trans_next: unsigned(XOR_INSPECTION_NUM_FFS_NB-1 downto 0); -- A new 'path stability' criteria -- ensures there is an orderly progression through the FFs as the edge -- is pushed back (or forward) into the target. signal wobble_pos_reg, wobble_pos_next: unsigned(XOR_INSPECTION_NUM_FFS_NB-1 downto 0); signal trans_pos: unsigned(XOR_INSPECTION_NUM_FFS_NB-1 downto 0); signal wobble_violation_reg, wobble_violation_next: std_logic; signal wobble_FFcyclecnt_reg, wobble_FFcyclecnt_next: unsigned(PHASE_SHIFT_LEN_NB-1 downto 0); signal wobble_firstFF_reg, wobble_firstFF_next: std_logic; signal wobble_min_FF_stay_cycles: unsigned(PHASE_SHIFT_LEN_NB-1 downto 0); signal flip_dir_reg, flip_dir_next: std_logic; signal success_reg, success_next: std_logic; -- Trials to solve jump problem signal term_cond_cnt_reg, term_cond_cnt_next: unsigned(NUM_TERMINATIONS_NB-1 downto 0); -- This will be used as an index into REBEL_QMouts_Seg, which has XOR_INSPECTION_NUM_FFS bits. subtype index_type is integer range 0 to XOR_INSPECTION_NUM_FFS; signal index: index_type; signal Segment_XOR_vals_reg, Segment_XOR_vals_next: std_logic_vector(XOR_INSPECTION_NUM_FFS-1 downto 0); signal lev1: std_logic_vector(XOR_INSPECTION_NUM_FFS-1 downto 0); signal lev2: std_logic_vector((XOR_INSPECTION_NUM_FFS - XOR_INSPECTION_NUM_FFS/4)-1 downto 0); signal lev3: std_logic_vector(XOR_INSPECTION_NUM_FFS/2-1 downto 0); signal lev4: std_logic_vector(XOR_INSPECTION_NUM_FFS_NB-1 downto 0); begin -- Adder tree -- connected to the 16 XOR gates and used add up the number of transitions that occurred across all 17 FFs -- in the delay chain. GEN_LEV1: for I in 0 to XOR_INSPECTION_NUM_FFS/2-1 generate begin lev1(2*I+1 downto 2*I) <= std_logic_vector(resize(unsigned(Segment_XOR_vals_reg(2*I+1 downto 2*I+1)), 2) + resize(unsigned(Segment_XOR_vals_reg(2*I downto 2*I)), 2)); end generate GEN_LEV1; GEN_LEV2: for I in 0 to XOR_INSPECTION_NUM_FFS/4-1 generate begin lev2(3*I+2 downto 3*I) <= std_logic_vector(resize(unsigned(lev1(4*I+3 downto 4*I+2)), 3) + resize(unsigned(lev1(4*I+1 downto 4*I)), 3)); end generate GEN_LEV2; GEN_LEV3: for I in 0 to XOR_INSPECTION_NUM_FFS/8-1 generate begin lev3(4*I+3 downto 4*I) <= std_logic_vector(resize(unsigned(lev2(6*I+5 downto 6*I+3)), 4) + resize(unsigned(lev2(6*I+2 downto 6*I)), 4)); end generate GEN_LEV3; lev4(XOR_INSPECTION_NUM_FFS_NB-1 downto 0) <= std_logic_vector(resize(unsigned(lev3(7 downto 4)), XOR_INSPECTION_NUM_FFS_NB) + resize(unsigned(lev3(3 downto 0)), XOR_INSPECTION_NUM_FFS_NB)); -- Decoder which specifies where the transition occurs in the REBEL delay chain segment of 17 elements. If between delay -- chain elements on the far right (elements 1 and 0), then record a 1. If between elements 15 and 16, record 16. No transitions -- or more than one transition is invalid and produces 31. Code in state machine ignores 'trans_pos' in these circumstances. with ( Segment_XOR_vals_reg ) select trans_pos <= to_unsigned(1, XOR_INSPECTION_NUM_FFS_NB) when "0000000000000001", to_unsigned(2, XOR_INSPECTION_NUM_FFS_NB) when "0000000000000010", to_unsigned(3, XOR_INSPECTION_NUM_FFS_NB) when "0000000000000100", to_unsigned(4, XOR_INSPECTION_NUM_FFS_NB) when "0000000000001000", to_unsigned(5, XOR_INSPECTION_NUM_FFS_NB) when "0000000000010000", to_unsigned(6, XOR_INSPECTION_NUM_FFS_NB) when "0000000000100000", to_unsigned(7, XOR_INSPECTION_NUM_FFS_NB) when "0000000001000000", to_unsigned(8, XOR_INSPECTION_NUM_FFS_NB) when "0000000010000000", to_unsigned(9, XOR_INSPECTION_NUM_FFS_NB) when "0000000100000000", to_unsigned(10, XOR_INSPECTION_NUM_FFS_NB) when "0000001000000000", to_unsigned(11, XOR_INSPECTION_NUM_FFS_NB) when "0000010000000000", to_unsigned(12, XOR_INSPECTION_NUM_FFS_NB) when "0000100000000000", to_unsigned(13, XOR_INSPECTION_NUM_FFS_NB) when "0001000000000000", to_unsigned(14, XOR_INSPECTION_NUM_FFS_NB) when "0010000000000000", to_unsigned(15, XOR_INSPECTION_NUM_FFS_NB) when "0100000000000000", to_unsigned(16, XOR_INSPECTION_NUM_FFS_NB) when "1000000000000000", to_unsigned(31, XOR_INSPECTION_NUM_FFS_NB) when others; -- ***** WARNING ****** DEPENDENCY ON phase_shift_inc_dec. Assumes that I don't set phase_shift_inc_dec higher than 5. with unsigned(phase_shift_inc_dec) select wobble_min_FF_stay_cycles <= to_unsigned(WOBBLE_MIN_FF_STAY_FPA, PHASE_SHIFT_LEN_NB) when to_unsigned(1, PHASE_SHIFT_LEN_NB), to_unsigned(WOBBLE_MIN_FF_STAY_FPA/2, PHASE_SHIFT_LEN_NB) when to_unsigned(2, PHASE_SHIFT_LEN_NB), to_unsigned(WOBBLE_MIN_FF_STAY_FPA/3, PHASE_SHIFT_LEN_NB) when to_unsigned(3, PHASE_SHIFT_LEN_NB), to_unsigned(WOBBLE_MIN_FF_STAY_FPA/4, PHASE_SHIFT_LEN_NB) when to_unsigned(4, PHASE_SHIFT_LEN_NB), to_unsigned(WOBBLE_MIN_FF_STAY_FPA/5, PHASE_SHIFT_LEN_NB) when to_unsigned(5, PHASE_SHIFT_LEN_NB), to_unsigned(3, PHASE_SHIFT_LEN_NB) when others; -- 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_num_result_trans_reg <= (others => '0'); wobble_pos_reg <= (others => '0'); wobble_violation_reg <= '0'; wobble_FFcyclecnt_reg <= (others => '0'); wobble_firstFF_reg <= '1'; flip_dir_reg <= '0'; success_reg <= '0'; term_cond_cnt_reg <= (others => '0'); Segment_XOR_vals_reg <= (others => '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_num_result_trans_reg <= REBEL_num_result_trans_next; wobble_pos_reg <= wobble_pos_next; wobble_violation_reg <= wobble_violation_next; wobble_FFcyclecnt_reg <= wobble_FFcyclecnt_next; wobble_firstFF_reg <= wobble_firstFF_next; flip_dir_reg <= flip_dir_next; success_reg <= success_next; term_cond_cnt_reg <= term_cond_cnt_next; Segment_XOR_vals_reg <= Segment_XOR_vals_next; end if; end process; -- Combo logic for state machine process (state_reg, start, ready_reg, phase_shift_lower_bound, phase_shift_upper_bound, REBEL_num_result_trans_reg, wobble_pos_reg, wobble_violation_reg, wobble_FFcyclecnt_reg, wobble_firstFF_reg, wobble_min_FF_stay_cycles, LC_ready, LC_phaseshift_request_reg, DEBUG_print_snapshots, DEBUG_processor_done_reading, PUT_init_val, REBEL_QMOuts_Seg, target_index, index, trans_pos, back_or_forward_search, flip_dir_reg, phase_shift_inc_dec, success_reg, term_cond_cnt_reg, Segment_XOR_vals_reg, lev4, TOP_authentication, TOP_enrollment) begin ready_next <= ready_reg; state_next <= state_reg; LC_start <= '0'; DEBUG_snapshot_ready <= '0'; LC_phaseshift_request_next <= LC_phaseshift_request_reg; REBEL_num_result_trans_next <= REBEL_num_result_trans_reg; wobble_pos_next <= wobble_pos_reg; wobble_violation_next <= wobble_violation_reg; wobble_FFcyclecnt_next <= wobble_FFcyclecnt_reg; wobble_firstFF_next <= wobble_firstFF_reg; index <= 0; flip_dir_next <= flip_dir_reg; success_next <= success_reg; term_cond_cnt_next <= term_cond_cnt_reg; Segment_XOR_vals_next <= Segment_XOR_vals_reg; case state_reg is -- ===================== when idle => ready_next <= '1'; if ( start = '1' ) then ready_next <= '0'; -- Indicate failure to find a valid path initially. success_next <= '0'; term_cond_cnt_next <= (others => '0'); REBEL_num_result_trans_next <= (others => '0'); -- Initialize the wobble analysis variable to invalid (0) wobble_pos_next <= (others => '0'); wobble_violation_next <= '0'; wobble_FFcyclecnt_next <= (others => '0'); wobble_firstFF_next <= '1'; -- 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 then 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 <= unsigned(phase_shift_upper_bound); flip_dir_next <= '0'; else LC_phaseshift_request_next <= unsigned(phase_shift_lower_bound); 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; -- ===================== -- Technically, if we are doing regeneration, we do NOT need to save the results of the XOR op over the REBEL delay -- chain segment (xor_segment) nor do we need to compute the number of transitions (compute_num_trans) -- we can -- go directly to debug_dump_data. We only do these two states if we want information about the number of transitions -- for printouts during regeneration. NOTE: authentication DEPENDS on the number of transitions being set in -- LCTest_Base. when wait_LC => if ( LC_ready = '1' ) then -- if ( TOP_enrollment = '1' or TOP_authentication = '1' ) then state_next <= xor_segment; -- else -- state_next <= debug_dump_data; -- end if; end if; -- ===================== when xor_segment => -- Instead of iterating over the REBEL_QMOuts_Seg, I now doing this in parallel to save cycles. We want to first count up -- the number of transitions that occurred over the 17 FFs (currently) in the REBEL_QMOuts_Seg segment. Create a sequence -- of 16 XORs for the 17 QMOouts. Do this on the first cycle. Segment_XOR_vals_next <= REBEL_QMOuts_Seg(XOR_INSPECTION_NUM_FFS downto 1) xor REBEL_QMOuts_Seg(XOR_INSPECTION_NUM_FFS-1 downto 0); state_next <= compute_num_trans; -- ===================== -- This state adds up the values on the outputs of the 16 XOR gates using a tree-adder. when compute_num_trans => -- Save the number of transitions that occurred in the delay chain elements of the current snapshot. The number of transitions recorded -- in the last LC test in the sweep will be the number returned. Note that during enrollment, if this ever becomes > 1, we exit below -- with 'invalid'. REBEL_num_result_trans_next <= unsigned(lev4); -- If the number of transitions is = 1 and we are doing enrollment, then check wobble criteria, otherwise skip the wobble check -- since the path will be deemed 'unstable' and invalid and we will stop the LC tests on it and return to the caller. if ( unsigned(lev4) = 1 and TOP_enrollment = '1' ) then state_next <= check_wobble; else state_next <= debug_dump_data; end if; -- ===================== -- The wobble criteria ensures that a transition over consecutive LC tests moves in an orderly fashion from FF with smaller -- indexes to FFs labeled with larger indexes (as the edge is 'pushed back' in the target FF for backward search) and from -- large to small for forward search. Any movement in the opposite direction, even for one snapshot, sets the violation -- flag and causes the current path to be marked as invalid if we are doing enrollment (see below). We check this ONLY if -- there is ONLY one transition and we are carrying out enrollment. If number of transitions is NOT 1, then 'trans_pos' is -- set to 31 (is NOT valid) and this code will NOT work properly. when check_wobble => -- Our new wobble criteria. If wobble_pos is 0, then this is the first LC test. ASSUMES trans_pos can NEVER be zero. if ( wobble_pos_reg = 0 ) then wobble_pos_next <= trans_pos; else -- For LC test iterations other than the first, we allow the transition to remain where it was or to increase (decrese) by 1. -- If it decreases (increases) by 1 or more, set violation flag. if ( trans_pos = wobble_pos_reg ) then -- Another criteria is how long the transition remains in each FF. This counter is used to flag violations in cases where -- the transition does not remain long enough in a master latch of the delay chain. Since the delay through master -- latches is fixed between 500ps and 1ns, it is not possible for a transistion to remain for example for only 200ps -- (10 cycles with phase_shift_inc_dec set to 2). It must be the case, when this occurs, that there is a glitch propagating -- with the actual edge that causes this short duration condition. Add one to the cycle counter while the transition -- remains in a given latch. wobble_FFcyclecnt_next <= wobble_FFcyclecnt_reg + 1; -- If the transition was pushed back into the next latch to the left (backward search), check that we remained for sufficient -- time in the previous FF. Do NOT check this for the first FF. elsif ( (back_or_forward_search = '0' and trans_pos = wobble_pos_reg+1) or (back_or_forward_search = '1' and trans_pos = wobble_pos_reg-1) ) then if ( wobble_FFcyclecnt_reg < wobble_min_FF_stay_cycles and wobble_firstFF_reg = '0' ) then wobble_violation_next <= '1'; end if; wobble_pos_next <= trans_pos; wobble_FFcyclecnt_next <= (others => '0'); wobble_firstFF_next <= '0'; -- If neither of the above cases are true, then we moved in the wrong direction and/or by more than one latch. else wobble_violation_next <= '1'; end if; end if; state_next <= debug_dump_data; -- ===================== -- DEBUG: Allow processor to fetch digital snapshot. Signal processor that data is ready to be read with DEBUG_snapshot_ready. -- Signal and wait for processor to indicate that it has read the data with DEBUG_processor_done_reading. when debug_dump_data => if ( DEBUG_print_snapshots = '1' ) then DEBUG_snapshot_ready <= '1'; if ( DEBUG_processor_done_reading = '1' ) then state_next <= check_term_cond; end if; else state_next <= check_term_cond; end if; -- ===================== 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. 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)); -- Fail case: Current LC test generated more than 1 transition (forward or backward) or a wobble violation occurred and we -- are in enrollment mode. Also fail if doing a backward search or the backward component of a forward/backward search and -- the LOWER BOUND is reached, or the UPPER BOUND is reached in the forward component. NOTE: REGEN must tolerate multiple -- transitions. Also note that we do NOT bother doing the forward component during REGEN 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. YOU CANNOT filter NO -- transitions here because some of the individual LC tests during the sweep won't have any. Added inequalities on phase -- shift xxx bounds 5/19/2014 because increment/decrement can be by more than 1. if ( ((REBEL_num_result_trans_reg > 1 or wobble_violation_reg = '1') and TOP_enrollment = '1') or (flip_dir_reg = '0' and LC_phaseshift_request_reg <= unsigned(phase_shift_lower_bound)) or (flip_dir_reg = '1' and LC_phaseshift_request_reg >= unsigned(phase_shift_upper_bound)) ) then -- Indicate failure and give control back to the LCTest engine. success_next <= '0'; ready_next <= '1'; state_next <= idle; -- Possible 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 REBEL_QMOuts_Seg(index) = PUT_init_val ) then -- If no transition occurred, and we met the criteria to exit, then this is definitely a case where there is NO -- transition. Added this recently 4/18/2014. Let's also consider this to fail if we have a single transition BUT it has -- NOT reached the target FF even with the longest launch-capture interval (THIS IS OVERFLOW). Added this on 4/24/2014. -- Made this 'TOP_enrollment' dependent on 5/14/2014 if ( (REBEL_num_result_trans_reg = 0 or LC_phaseshift_request_reg >= unsigned(phase_shift_upper_bound)) and TOP_enrollment = '1' ) then success_next <= '0'; ready_next <= '1'; state_next <= idle; else -- Do not consider that we have completed the timing of this path until the termination condition is met for n consecutive FPAs. -- Added 5/13/2014. Works well for helping to make good decisions on which paths are stable during enrollment. NOTE: this exit -- criteria is enforced during enrollment, regen and authen. if ( term_cond_cnt_reg < to_unsigned(NUM_TERMINATIONS - 1, NUM_TERMINATIONS_NB) ) then term_cond_cnt_next <= term_cond_cnt_reg + 1; state_next <= incdec_phase_request; else success_next <= '1'; ready_next <= '1'; state_next <= idle; end if; end if; -- 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 just before the target. elsif (flip_dir_reg = '1' and REBEL_QMOuts_Seg(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 -- If we revert out of the termination condition during enrollment, consider the path unstable. This else is only executed -- if the termination pattern does NOT exist in the current snapshot. If we processed any snapshots on previous FPAs that -- DID have the termination pattern, then term_cond_cnt would not be zero. if ( term_cond_cnt_reg /= 0 and TOP_enrollment = '1' ) then success_next <= '0'; ready_next <= '1'; state_next <= idle; -- If we revert during regen/authen, then continue to search but start the counter over. else term_cond_cnt_next <= (others => '0'); state_next <= incdec_phase_request; end if; 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 <= unsigned(phase_shift_upper_bound); 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 - unsigned(phase_shift_inc_dec); else LC_phaseshift_request_next <= LC_phaseshift_request_reg + unsigned(phase_shift_inc_dec); 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); success_flag <= success_reg; end beh;