1775 lines
90 KiB
VHDL
Executable File
1775 lines
90 KiB
VHDL
Executable File
--*****************************************************************************
|
|
-- (c) Copyright 2009 Xilinx, Inc. All rights reserved.
|
|
--
|
|
-- This file contains confidential and proprietary information
|
|
-- of Xilinx, Inc. and is protected under U.S. and
|
|
-- international copyright and other intellectual property
|
|
-- laws.
|
|
--
|
|
-- DISCLAIMER
|
|
-- This disclaimer is not a license and does not grant any
|
|
-- rights to the materials distributed herewith. Except as
|
|
-- otherwise provided in a valid license issued to you by
|
|
-- Xilinx, and to the maximum extent permitted by applicable
|
|
-- law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
|
|
-- WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
|
|
-- AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
|
|
-- BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
|
|
-- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
|
|
-- (2) Xilinx shall not be liable (whether in contract or tort,
|
|
-- including negligence, or under any other theory of
|
|
-- liability) for any loss or damage of any kind or nature
|
|
-- related to, arising under or in connection with these
|
|
-- materials, including for any direct, or any indirect,
|
|
-- special, incidental, or consequential loss or damage
|
|
-- (including loss of data, profits, goodwill, or any type of
|
|
-- loss or damage suffered as a result of any action brought
|
|
-- by a third party) even if such damage or loss was
|
|
-- reasonably foreseeable or Xilinx had been advised of the
|
|
-- possibility of the same.
|
|
--
|
|
-- CRITICAL APPLICATIONS
|
|
-- Xilinx products are not designed or intended to be fail-
|
|
-- safe, or for use in any application requiring fail-safe
|
|
-- performance, such as life-support or safety devices or
|
|
-- systems, Class III medical devices, nuclear facilities,
|
|
-- applications related to the deployment of airbags, or any
|
|
-- other applications that could lead to death, personal
|
|
-- injury, or severe property or environmental damage
|
|
-- (individually and collectively, "Critical
|
|
-- Applications"). Customer assumes the sole risk and
|
|
-- liability of any use of Xilinx products in Critical
|
|
-- Applications, subject only to applicable laws and
|
|
-- regulations governing limitations on product liability.
|
|
--
|
|
-- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
|
|
-- PART OF THIS FILE AT ALL TIMES.
|
|
--
|
|
--*****************************************************************************
|
|
-- ____ ____
|
|
-- / /\/ /
|
|
-- /___/ \ / Vendor: Xilinx
|
|
-- \ \ \/ Version: %version
|
|
-- \ \ Application: MIG
|
|
-- / / Filename: mcb_soft_calibration.vhd
|
|
-- /___/ /\ Date Last Modified: $Date: 2011/06/02 07:17:26 $
|
|
-- \ \ / \ Date Created: Mon Feb 9 2009
|
|
-- \___\/\___\
|
|
--
|
|
--Device: Spartan6
|
|
--Design Name: DDR/DDR2/DDR3/LPDDR
|
|
--Purpose: Xilinx reference design for MCB Soft
|
|
-- Calibration
|
|
--Reference:
|
|
--
|
|
-- Revision: Date: Comment
|
|
-- 1.0: 2/06/09: Initial version for MIG wrapper.
|
|
-- 1.1: 2/09/09: moved Max_Value_Previous assignments to be completely inside CASE statement for next-state logic (needed to get it working
|
|
-- correctly)
|
|
-- 1.2: 2/12/09: Many other changes.
|
|
-- 1.3: 2/26/09: Removed section with Max_Value_pre and DQS_COUNT_PREVIOUS_pre, and instead added PREVIOUS_STATE reg and moved assignment to within
|
|
-- STATE
|
|
-- 1.4: 3/02/09: Removed comments out of sensitivity list of always block to mux SDI, SDO, CS, and ADD.Also added reg declaration for PREVIOUS_STATE
|
|
-- 1.5: 3/16/09: Added pll_lock port, and using it to gate reset. Changing RST (except input port) to RST_reg and gating it with pll_lock.
|
|
-- 1.6: 6/05/09: Added START_DYN_CAL_PRE with pulse on SYSRST; removed MCB_UIDQCOUNT.
|
|
-- 1.7: 6/24/09: Gave RZQ and ZIO each their own unique ADD and SDI nets
|
|
-- 2.6: 12/15/09: Changed STATE from 7-bit to 6-bit. Dropped (* FSM_ENCODING="BINARY" *) for STATE. Moved MCB_UICMDEN = 0 from OFF_RZQ_PTERM to
|
|
-- RST_DELAY.
|
|
-- Changed the "reset" always block so that RST_reg is always set to 1 when the PLL loses lock, and is now held in reset for at least
|
|
-- 16 clocks. Added PNSKEW option.
|
|
-- 2.7: 12/23/09: Added new states "SKEW" and "MULTIPLY_DIVIDE" to help with timing.
|
|
-- 2.8: 01/14/10: Added functionality to allow for SUSPEND. Changed MCB_SYSRST port from wire to reg.
|
|
-- 2.9: 02/01/10: More changes to SUSPEND and Reset logic to handle SUSPEND properly. Also - eliminated 2's comp DQS_COUNT_VIRTUAL, and replaced
|
|
-- with 8bit TARGET_DQS_DELAY which
|
|
-- will track most recnet Max_Value. Eliminated DQS_COUNT_PREVIOUS. Combined DQS_COUNT_INITIAL and DQS_DELAY into DQS_DELAY_INITIAL.
|
|
-- Changed DQS_COUNT* to DQS_DELAY*.
|
|
-- Changed MCB_SYSRST port back to wire (from reg).
|
|
-- 3.0: 02/10/10: Added count_inc and count_dec to add few (4) UI_CLK cycles latency to the INC and DEC signals(to deal with latency on UOREFRSHFLAG)
|
|
-- 3.1: 02/23/10: Registered the DONE_SOFTANDHARD_CAL for timing.
|
|
-- 3.2: 02/28/10: Corrected the WAIT_SELFREFRESH_EXIT_DQS_CAL logic;
|
|
-- 3.3: 03/02/10: Changed PNSKEW to default on (1'b1)
|
|
-- 3.4: 03/04/10: Recoded the RST_Reg logic.
|
|
-- 3.5: 03/05/10: Changed Result register to be 16-bits. Changed DQS_NUMERATOR/DENOMINATOR values to 3/8 (from 6/16)
|
|
-- 3.6 03/10/10: Improvements to Reset logic.
|
|
-- 3.7: 04/26/10: Added DDR2 Initialization fix to meet 400 ns wait as outlined in step d) of JEDEC DDR2 spec .
|
|
-- 3.8: 05/05/10: Added fixes for the CR# 559092 (updated Mult_Divide function) and 555416 (added IOB attribute to DONE_SOFTANDHARD_CAL).
|
|
-- 3.9: 05/24/10: Added 200us Wait logic to control CKE_Train. The 200us Wait counter assumes UI_CLK freq not higher than 100 MHz.
|
|
-- 3.10 10/22/10: Fixed PERFORM_START_DYN_CAL_AFTER_SELFREFRESH logic.
|
|
-- 3.11 2/14/11: Apply a different skkew for the P and N inputs for the differential LDQS and UDQS signals to provide more noise immunity.
|
|
-- 4.1 03/08/12: Fixed SELFREFRESH_MCB_REQ logic. It should not need depend on the SM STATE so that
|
|
-- MCB can come out of selfresh mode. SM requires refresh cycle to update the DQS value.
|
|
-- 4.2 05/10/12: All P/N terms of input and bidir memory pins are initialized with value of ZERO. TZQINIT_MAXCNT
|
|
-- are set to 8 for LPDDR,DDR and DDR2 interface .
|
|
-- Keep the UICMDEN in assertion state when SM is in RST_DELAY state so that MCB will not start doing
|
|
-- Premable detection until the second deassertion of MCB_SYSRST.
|
|
|
|
|
|
-- End Revision
|
|
--**********************************************************************************
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
USE ieee.numeric_std.all;
|
|
|
|
entity mcb_soft_calibration is
|
|
generic (
|
|
C_MEM_TZQINIT_MAXCNT : std_logic_vector(9 downto 0) := "1000000000"; -- DDR3 Minimum delay between resets
|
|
SKIP_IN_TERM_CAL : integer := 0; -- provides option to skip the input termination calibration
|
|
SKIP_DYNAMIC_CAL : integer := 0; -- provides option to skip the dynamic delay calibration
|
|
SKIP_DYN_IN_TERM : integer := 1; -- provides option to skip the input termination calibration
|
|
C_MC_CALIBRATION_MODE : string := "CALIBRATION"; -- if set to CALIBRATION will reset DQS IDELAY to DQS_NUMERATOR/DQS_DENOMINATOR local_param value
|
|
-- if set to NOCALIBRATION then defaults to hard cal blocks setting of C_MC_CALBRATION_DELAY
|
|
-- (Quarter, etc)
|
|
C_SIMULATION : string := "FALSE"; -- Tells us whether the design is being simulated or implemented
|
|
C_MEM_TYPE : string := "DDR"
|
|
|
|
|
|
);
|
|
port (
|
|
UI_CLK : in std_logic; -- main clock input for logic and IODRP CLK pins. At top level, this should also connect to IODRP2_MCB
|
|
-- CLK pins
|
|
RST : in std_logic; -- main system reset for both the Soft Calibration block - also will act as a passthrough to MCB's SYSRST
|
|
DONE_SOFTANDHARD_CAL : out std_logic; -- active high flag signals soft calibration of input delays is complete and MCB_UODONECAL is high (MCB
|
|
-- hard calib complete)
|
|
PLL_LOCK : in std_logic; -- Lock signal from PLL
|
|
SELFREFRESH_REQ : in std_logic;
|
|
SELFREFRESH_MCB_MODE : in std_logic;
|
|
SELFREFRESH_MCB_REQ : out std_logic;
|
|
SELFREFRESH_MODE : out std_logic;
|
|
IODRP_ADD : out std_logic; -- IODRP ADD port
|
|
IODRP_SDI : out std_logic; -- IODRP SDI port
|
|
RZQ_IN : in std_logic; -- RZQ pin from board - expected to have a 2*R resistor to ground
|
|
RZQ_IODRP_SDO : in std_logic; -- RZQ IODRP's SDO port
|
|
RZQ_IODRP_CS : out std_logic := '0'; -- RZQ IODRP's CS port
|
|
ZIO_IN : in std_logic; -- Z-stated IO pin - garanteed not to be driven externally
|
|
ZIO_IODRP_SDO : in std_logic; -- ZIO IODRP's SDO port
|
|
ZIO_IODRP_CS : out std_logic := '0'; -- ZIO IODRP's CS port
|
|
MCB_UIADD : out std_logic; -- to MCB's UIADD port
|
|
MCB_UISDI : out std_logic; -- to MCB's UISDI port
|
|
MCB_UOSDO : in std_logic; -- from MCB's UOSDO port (User output SDO)
|
|
MCB_UODONECAL : in std_logic; -- indicates when MCB hard calibration process is complete
|
|
MCB_UOREFRSHFLAG : in std_logic; -- high during refresh cycle and time when MCB is innactive
|
|
MCB_UICS : out std_logic; -- to MCB's UICS port (User Input CS)
|
|
MCB_UIDRPUPDATE : out std_logic := '1'; -- MCB's UIDRPUPDATE port (gets passed to IODRP2_MCB's MEMUPDATE port: this controls shadow latch used
|
|
-- during IODRP2_MCB writes). Currently just trasnparent
|
|
MCB_UIBROADCAST : out std_logic; -- only to MCB's UIBROADCAST port (User Input BROADCAST - gets passed to IODRP2_MCB's BKST port)
|
|
MCB_UIADDR : out std_logic_vector(4 downto 0) := "00000"; -- to MCB's UIADDR port (gets passed to IODRP2_MCB's AUXADDR port
|
|
MCB_UICMDEN : out std_logic := '1'; -- set to 1 to take control of UI interface - removes control from internal calib block
|
|
MCB_UIDONECAL : out std_logic := '0'; -- set to 0 to "tell" controller that it's still in a calibrate state
|
|
MCB_UIDQLOWERDEC : out std_logic ;
|
|
MCB_UIDQLOWERINC : out std_logic ;
|
|
MCB_UIDQUPPERDEC : out std_logic ;
|
|
MCB_UIDQUPPERINC : out std_logic ;
|
|
MCB_UILDQSDEC : out std_logic := '0';
|
|
MCB_UILDQSINC : out std_logic := '0';
|
|
MCB_UIREAD : out std_logic; -- enables read w/o writing by turning on a SDO->SDI loopback inside the IODRP2_MCBs (doesn't exist in
|
|
-- regular IODRP2). IODRPCTRLR_R_WB becomes don't-care.
|
|
MCB_UIUDQSDEC : out std_logic := '0';
|
|
MCB_UIUDQSINC : out std_logic := '0';
|
|
MCB_RECAL : out std_logic ; -- future hook to drive MCB's RECAL pin - initiates a hard re-calibration sequence when high
|
|
MCB_UICMD : out std_logic;
|
|
MCB_UICMDIN : out std_logic;
|
|
MCB_UIDQCOUNT : out std_logic_vector(3 downto 0);
|
|
MCB_UODATA : in std_logic_vector(7 downto 0);
|
|
MCB_UODATAVALID : in std_logic;
|
|
MCB_UOCMDREADY : in std_logic;
|
|
MCB_UO_CAL_START : in std_logic;
|
|
MCB_SYSRST : out std_logic; -- drives the MCB's SYSRST pin - the main reset for MCB
|
|
Max_Value : out std_logic_vector(7 downto 0);
|
|
CKE_Train : out std_logic
|
|
);
|
|
end entity mcb_soft_calibration;
|
|
|
|
architecture trans of mcb_soft_calibration is
|
|
|
|
constant IOI_DQ0 : std_logic_vector(4 downto 0) := ("0000" & '1');
|
|
constant IOI_DQ1 : std_logic_vector(4 downto 0) := ("0000" & '0');
|
|
constant IOI_DQ2 : std_logic_vector(4 downto 0) := ("0001" & '1');
|
|
constant IOI_DQ3 : std_logic_vector(4 downto 0) := ("0001" & '0');
|
|
constant IOI_DQ4 : std_logic_vector(4 downto 0) := ("0010" & '1');
|
|
constant IOI_DQ5 : std_logic_vector(4 downto 0) := ("0010" & '0');
|
|
constant IOI_DQ6 : std_logic_vector(4 downto 0) := ("0011" & '1');
|
|
constant IOI_DQ7 : std_logic_vector(4 downto 0) := ("0011" & '0');
|
|
constant IOI_DQ8 : std_logic_vector(4 downto 0) := ("0100" & '1');
|
|
constant IOI_DQ9 : std_logic_vector(4 downto 0) := ("0100" & '0');
|
|
constant IOI_DQ10 : std_logic_vector(4 downto 0) := ("0101" & '1');
|
|
constant IOI_DQ11 : std_logic_vector(4 downto 0) := ("0101" & '0');
|
|
constant IOI_DQ12 : std_logic_vector(4 downto 0) := ("0110" & '1');
|
|
constant IOI_DQ13 : std_logic_vector(4 downto 0) := ("0110" & '0');
|
|
constant IOI_DQ14 : std_logic_vector(4 downto 0) := ("0111" & '1');
|
|
constant IOI_DQ15 : std_logic_vector(4 downto 0) := ("0111" & '0');
|
|
constant IOI_UDM : std_logic_vector(4 downto 0) := ("1000" & '1');
|
|
constant IOI_LDM : std_logic_vector(4 downto 0) := ("1000" & '0');
|
|
constant IOI_CK_P : std_logic_vector(4 downto 0) := ("1001" & '1');
|
|
constant IOI_CK_N : std_logic_vector(4 downto 0) := ("1001" & '0');
|
|
constant IOI_RESET : std_logic_vector(4 downto 0) := ("1010" & '1');
|
|
constant IOI_A11 : std_logic_vector(4 downto 0) := ("1010" & '0');
|
|
constant IOI_WE : std_logic_vector(4 downto 0) := ("1011" & '1');
|
|
constant IOI_BA2 : std_logic_vector(4 downto 0) := ("1011" & '0');
|
|
constant IOI_BA0 : std_logic_vector(4 downto 0) := ("1100" & '1');
|
|
constant IOI_BA1 : std_logic_vector(4 downto 0) := ("1100" & '0');
|
|
constant IOI_RASN : std_logic_vector(4 downto 0) := ("1101" & '1');
|
|
constant IOI_CASN : std_logic_vector(4 downto 0) := ("1101" & '0');
|
|
constant IOI_UDQS_CLK : std_logic_vector(4 downto 0) := ("1110" & '1');
|
|
constant IOI_UDQS_PIN : std_logic_vector(4 downto 0) := ("1110" & '0');
|
|
constant IOI_LDQS_CLK : std_logic_vector(4 downto 0) := ("1111" & '1');
|
|
constant IOI_LDQS_PIN : std_logic_vector(4 downto 0) := ("1111" & '0');
|
|
|
|
constant START : std_logic_vector(5 downto 0) := "000000";
|
|
constant LOAD_RZQ_NTERM : std_logic_vector(5 downto 0) := "000001";
|
|
constant WAIT1 : std_logic_vector(5 downto 0) := "000010";
|
|
constant LOAD_RZQ_PTERM : std_logic_vector(5 downto 0) := "000011";
|
|
constant WAIT2 : std_logic_vector(5 downto 0) := "000100";
|
|
constant INC_PTERM : std_logic_vector(5 downto 0) := "000101";
|
|
constant MULTIPLY_DIVIDE : std_logic_vector(5 downto 0) := "000110";
|
|
constant LOAD_ZIO_PTERM : std_logic_vector(5 downto 0) := "000111";
|
|
constant WAIT3 : std_logic_vector(5 downto 0) := "001000";
|
|
constant LOAD_ZIO_NTERM : std_logic_vector(5 downto 0) := "001001";
|
|
constant WAIT4 : std_logic_vector(5 downto 0) := "001010";
|
|
constant INC_NTERM : std_logic_vector(5 downto 0) := "001011";
|
|
constant SKEW : std_logic_vector(5 downto 0) := "001100";
|
|
constant WAIT_FOR_START_BROADCAST : std_logic_vector(5 downto 0) := "001101";
|
|
constant BROADCAST_PTERM : std_logic_vector(5 downto 0) := "001110";
|
|
constant WAIT5 : std_logic_vector(5 downto 0) := "001111";
|
|
constant BROADCAST_NTERM : std_logic_vector(5 downto 0) := "010000";
|
|
constant WAIT6 : std_logic_vector(5 downto 0) := "010001";
|
|
|
|
constant LDQS_CLK_WRITE_P_TERM : std_logic_vector(5 downto 0) := "010010";
|
|
constant LDQS_CLK_P_TERM_WAIT : std_logic_vector(5 downto 0) := "010011";
|
|
constant LDQS_CLK_WRITE_N_TERM : std_logic_vector(5 downto 0) := "010100";
|
|
constant LDQS_CLK_N_TERM_WAIT : std_logic_vector(5 downto 0) := "010101";
|
|
constant LDQS_PIN_WRITE_P_TERM : std_logic_vector(5 downto 0) := "010110";
|
|
constant LDQS_PIN_P_TERM_WAIT : std_logic_vector(5 downto 0) := "010111";
|
|
constant LDQS_PIN_WRITE_N_TERM : std_logic_vector(5 downto 0) := "011000";
|
|
constant LDQS_PIN_N_TERM_WAIT : std_logic_vector(5 downto 0) := "011001";
|
|
constant UDQS_CLK_WRITE_P_TERM : std_logic_vector(5 downto 0) := "011010";
|
|
constant UDQS_CLK_P_TERM_WAIT : std_logic_vector(5 downto 0) := "011011";
|
|
constant UDQS_CLK_WRITE_N_TERM : std_logic_vector(5 downto 0) := "011100";
|
|
constant UDQS_CLK_N_TERM_WAIT : std_logic_vector(5 downto 0) := "011101";
|
|
constant UDQS_PIN_WRITE_P_TERM : std_logic_vector(5 downto 0) := "011110";
|
|
constant UDQS_PIN_P_TERM_WAIT : std_logic_vector(5 downto 0) := "011111";
|
|
constant UDQS_PIN_WRITE_N_TERM : std_logic_vector(5 downto 0) := "100000";
|
|
constant UDQS_PIN_N_TERM_WAIT : std_logic_vector(5 downto 0) := "100001";
|
|
|
|
constant OFF_RZQ_PTERM : std_logic_vector(5 downto 0) := "100010";
|
|
constant WAIT7 : std_logic_vector(5 downto 0) := "100011";
|
|
constant OFF_ZIO_NTERM : std_logic_vector(5 downto 0) := "100100";
|
|
constant WAIT8 : std_logic_vector(5 downto 0) := "100101";
|
|
constant RST_DELAY : std_logic_vector(5 downto 0) := "100110";
|
|
constant START_DYN_CAL_PRE : std_logic_vector(5 downto 0) := "100111";
|
|
constant WAIT_FOR_UODONE : std_logic_vector(5 downto 0) := "101000";
|
|
constant LDQS_WRITE_POS_INDELAY : std_logic_vector(5 downto 0) := "101001";
|
|
constant LDQS_WAIT1 : std_logic_vector(5 downto 0) := "101010";
|
|
constant LDQS_WRITE_NEG_INDELAY : std_logic_vector(5 downto 0) := "101011";
|
|
constant LDQS_WAIT2 : std_logic_vector(5 downto 0) := "101100";
|
|
constant UDQS_WRITE_POS_INDELAY : std_logic_vector(5 downto 0) := "101101";
|
|
constant UDQS_WAIT1 : std_logic_vector(5 downto 0) := "101110";
|
|
constant UDQS_WRITE_NEG_INDELAY : std_logic_vector(5 downto 0) := "101111";
|
|
constant UDQS_WAIT2 : std_logic_vector(5 downto 0) := "110000";
|
|
constant START_DYN_CAL : std_logic_vector(5 downto 0) := "110001";
|
|
constant WRITE_CALIBRATE : std_logic_vector(5 downto 0) := "110010";
|
|
constant WAIT9 : std_logic_vector(5 downto 0) := "110011";
|
|
constant READ_MAX_VALUE : std_logic_vector(5 downto 0) := "110100";
|
|
constant WAIT10 : std_logic_vector(5 downto 0) := "110101";
|
|
constant ANALYZE_MAX_VALUE : std_logic_vector(5 downto 0) := "110110";
|
|
constant FIRST_DYN_CAL : std_logic_vector(5 downto 0) := "110111";
|
|
constant INCREMENT : std_logic_vector(5 downto 0) := "111000";
|
|
constant DECREMENT : std_logic_vector(5 downto 0) := "111001";
|
|
constant DONE : std_logic_vector(5 downto 0) := "111010";
|
|
--constant INCREMENT_TA : std_logic_vector(5 downto 0) := "111011";
|
|
|
|
constant RZQ : std_logic_vector(1 downto 0) := "00";
|
|
constant ZIO : std_logic_vector(1 downto 0) := "01";
|
|
constant MCB_PORT : std_logic_vector(1 downto 0) := "11";
|
|
constant WRITE_MODE : std_logic := '0';
|
|
constant READ_MODE : std_logic := '1';
|
|
|
|
-- IOI Registers
|
|
constant NoOp : std_logic_vector(7 downto 0) := "00000000";
|
|
constant DelayControl : std_logic_vector(7 downto 0) := "00000001";
|
|
constant PosEdgeInDly : std_logic_vector(7 downto 0) := "00000010";
|
|
constant NegEdgeInDly : std_logic_vector(7 downto 0) := "00000011";
|
|
constant PosEdgeOutDly : std_logic_vector(7 downto 0) := "00000100";
|
|
constant NegEdgeOutDly : std_logic_vector(7 downto 0) := "00000101";
|
|
constant MiscCtl1 : std_logic_vector(7 downto 0) := "00000110";
|
|
constant MiscCtl2 : std_logic_vector(7 downto 0) := "00000111";
|
|
constant MaxValue : std_logic_vector(7 downto 0) := "00001000";
|
|
|
|
-- IOB Registers
|
|
constant PDrive : std_logic_vector(7 downto 0) := "10000000";
|
|
constant PTerm : std_logic_vector(7 downto 0) := "10000001";
|
|
constant NDrive : std_logic_vector(7 downto 0) := "10000010";
|
|
constant NTerm : std_logic_vector(7 downto 0) := "10000011";
|
|
constant SlewRateCtl : std_logic_vector(7 downto 0) := "10000100";
|
|
constant LVDSControl : std_logic_vector(7 downto 0) := "10000101";
|
|
constant MiscControl : std_logic_vector(7 downto 0) := "10000110";
|
|
constant InputControl : std_logic_vector(7 downto 0) := "10000111";
|
|
constant TestReadback : std_logic_vector(7 downto 0) := "10001000";
|
|
|
|
-- No multi/divide is required when a 55 ohm resister is used on RZQ
|
|
-- localparam MULT = 1;
|
|
-- localparam DIV = 1;
|
|
-- use 7/4 scaling factor when the 100 ohm RZQ is used
|
|
constant MULT : integer := 7;
|
|
constant DIV : integer := 4;
|
|
|
|
constant PNSKEW : std_logic := '1'; -- Default is 1'b1. Change to 1'b0 if PSKEW and NSKEW are not required
|
|
constant PNSKEWDQS : std_logic := '1';
|
|
|
|
constant MULT_S : integer := 9;
|
|
constant DIV_S : integer := 8;
|
|
constant MULT_W : integer := 7;
|
|
constant DIV_W : integer := 8;
|
|
|
|
constant DQS_NUMERATOR : integer := 3;
|
|
constant DQS_DENOMINATOR : integer := 8;
|
|
constant INCDEC_THRESHOLD : std_logic_vector(7 downto 0) := X"03";
|
|
-- parameter for the threshold which triggers an inc/dec to occur. 2 for half, 4 for quarter,
|
|
-- 3 for three eighths
|
|
|
|
constant RST_CNT : std_logic_vector(9 downto 0) := "0000010000";
|
|
|
|
constant IN_TERM_PASS : std_logic := '0';
|
|
constant DYN_CAL_PASS : std_logic := '1';
|
|
|
|
function TZQINIT_MAXCNT_W return std_logic_vector is
|
|
variable temp : std_logic_vector(9 downto 0) := (others=>'0');
|
|
begin
|
|
if (C_MEM_TYPE = "DDR3") then
|
|
temp := C_MEM_TZQINIT_MAXCNT + RST_CNT;
|
|
else
|
|
temp := 8 + RST_CNT;
|
|
end if;
|
|
return temp(9 downto 0);
|
|
end function;
|
|
|
|
constant TZQINIT_MAXCNT : std_logic_vector(9 downto 0) := TZQINIT_MAXCNT_W;
|
|
|
|
component iodrp_mcb_controller is
|
|
port (
|
|
memcell_address : in std_logic_vector(7 downto 0);
|
|
write_data : in std_logic_vector(7 downto 0);
|
|
read_data : out std_logic_vector(7 downto 0);
|
|
rd_not_write : in std_logic;
|
|
cmd_valid : in std_logic;
|
|
rdy_busy_n : out std_logic;
|
|
use_broadcast : in std_logic;
|
|
drp_ioi_addr : in std_logic_vector(4 downto 0);
|
|
sync_rst : in std_logic;
|
|
DRP_CLK : in std_logic;
|
|
DRP_CS : out std_logic;
|
|
DRP_SDI : out std_logic;
|
|
DRP_ADD : out std_logic;
|
|
DRP_BKST : out std_logic;
|
|
DRP_SDO : in std_logic;
|
|
MCB_UIREAD : out std_logic
|
|
);
|
|
end component;
|
|
|
|
component iodrp_controller is
|
|
port (
|
|
memcell_address : in std_logic_vector(7 downto 0);
|
|
write_data : in std_logic_vector(7 downto 0);
|
|
read_data : out std_logic_vector(7 downto 0);
|
|
rd_not_write : in std_logic;
|
|
cmd_valid : in std_logic;
|
|
rdy_busy_n : out std_logic;
|
|
use_broadcast : in std_logic;
|
|
sync_rst : in std_logic;
|
|
DRP_CLK : in std_logic;
|
|
DRP_CS : out std_logic;
|
|
DRP_SDI : out std_logic;
|
|
DRP_ADD : out std_logic;
|
|
DRP_BKST : out std_logic;
|
|
DRP_SDO : in std_logic
|
|
);
|
|
end component;
|
|
|
|
signal P_Term : std_logic_vector(5 downto 0) := "000000";
|
|
signal N_Term : std_logic_vector(6 downto 0) := "0000000";
|
|
signal P_Term_s : std_logic_vector(5 downto 0) := "000000";
|
|
signal N_Term_s : std_logic_vector(6 downto 0) := "0000000";
|
|
signal P_Term_w : std_logic_vector(5 downto 0) := "000000";
|
|
signal N_Term_w : std_logic_vector(6 downto 0) := "0000000";
|
|
signal P_Term_Prev : std_logic_vector(5 downto 0) := "000000";
|
|
signal N_Term_Prev : std_logic_vector(6 downto 0) := "0000000";
|
|
|
|
signal STATE : std_logic_vector(5 downto 0);
|
|
signal IODRPCTRLR_MEMCELL_ADDR : std_logic_vector(7 downto 0);
|
|
signal IODRPCTRLR_WRITE_DATA : std_logic_vector(7 downto 0);
|
|
signal Active_IODRP : std_logic_vector(1 downto 0);
|
|
signal IODRPCTRLR_R_WB : std_logic := '0';
|
|
signal IODRPCTRLR_CMD_VALID : std_logic := '0';
|
|
signal IODRPCTRLR_USE_BKST : std_logic := '0';
|
|
signal MCB_CMD_VALID : std_logic := '0';
|
|
signal MCB_USE_BKST : std_logic := '0';
|
|
signal Pre_SYSRST : std_logic := '1'; -- internally generated reset which will OR with RST input to drive MCB's
|
|
-- SYSRST pin (MCB_SYSRST)
|
|
signal IODRP_SDO : std_logic;
|
|
signal Max_Value_Previous : std_logic_vector(7 downto 0) := "00000000";
|
|
signal count : std_logic_vector(5 downto 0) := "000000"; -- counter for adding 18 extra clock cycles after setting Calibrate bit
|
|
signal counter_en : std_logic := '0'; -- counter enable for "count"
|
|
signal First_Dyn_Cal_Done : std_logic := '0'; -- flag - high after the very first dynamic calibration is done
|
|
signal START_BROADCAST : std_logic ; -- Trigger to start Broadcast to IODRP2_MCBs to set Input Impedance -
|
|
-- state machine will wait for this to be high
|
|
signal DQS_DELAY_INITIAL : std_logic_vector(7 downto 0) := "00000000";
|
|
signal DQS_DELAY : std_logic_vector(7 downto 0); -- contains the latest values written to LDQS and UDQS Input Delays
|
|
signal TARGET_DQS_DELAY : std_logic_vector(7 downto 0); -- used to track the target for DQS input delays - only gets updated if
|
|
-- the Max Value changes by more than the threshold
|
|
signal counter_inc : std_logic_vector(7 downto 0); -- used to delay Inc signal by several ui_clk cycles (to deal with
|
|
-- latency on UOREFRSHFLAG)
|
|
signal counter_dec : std_logic_vector(7 downto 0); -- used to delay Dec signal by several ui_clk cycles (to deal with
|
|
-- latency on UOREFRSHFLAG)
|
|
signal IODRPCTRLR_READ_DATA : std_logic_vector(7 downto 0);
|
|
signal IODRPCTRLR_RDY_BUSY_N : std_logic;
|
|
signal IODRP_CS : std_logic;
|
|
signal MCB_READ_DATA : std_logic_vector(7 downto 0);
|
|
signal RST_reg : std_logic;
|
|
signal Block_Reset : std_logic;
|
|
signal MCB_UODATAVALID_U : std_logic;
|
|
|
|
signal Inc_Dec_REFRSH_Flag : std_logic_vector(2 downto 0); -- 3-bit flag to show:Inc is needed, Dec needed, refresh cycle taking place
|
|
signal Max_Value_Delta_Up : std_logic_vector(7 downto 0); -- tracks amount latest Max Value has gone up from previous Max Value read
|
|
signal Half_MV_DU : std_logic_vector(7 downto 0); -- half of Max_Value_Delta_Up
|
|
signal Max_Value_Delta_Dn : std_logic_vector(7 downto 0); -- tracks amount latest Max Value has gone down from previous Max Value read
|
|
signal Half_MV_DD : std_logic_vector(7 downto 0); -- half of Max_Value_Delta_Dn
|
|
|
|
signal RstCounter : std_logic_vector(9 downto 0) := (others => '0');
|
|
signal rst_tmp : std_logic;
|
|
signal LastPass_DynCal : std_logic;
|
|
signal First_In_Term_Done : std_logic;
|
|
signal Inc_Flag : std_logic; -- flag to increment Dynamic Delay
|
|
signal Dec_Flag : std_logic; -- flag to decrement Dynamic Delay
|
|
|
|
signal CALMODE_EQ_CALIBRATION : std_logic; -- will calculate and set the DQS input delays if C_MC_CALIBRATION_MODE
|
|
-- parameter = "CALIBRATION"
|
|
signal DQS_DELAY_LOWER_LIMIT : std_logic_vector(7 downto 0); -- Lower limit for DQS input delays
|
|
signal DQS_DELAY_UPPER_LIMIT : std_logic_vector(7 downto 0); -- Upper limit for DQS input delays
|
|
signal SKIP_DYN_IN_TERMINATION : std_logic; -- wire to allow skipping dynamic input termination if either the
|
|
-- one-time or dynamic parameters are 1
|
|
signal SKIP_DYNAMIC_DQS_CAL : std_logic; -- wire allowing skipping dynamic DQS delay calibration if either
|
|
-- SKIP_DYNIMIC_CAL=1, or if C_MC_CALIBRATION_MODE=NOCALIBRATION
|
|
signal Quarter_Max_Value : std_logic_vector(7 downto 0);
|
|
signal Half_Max_Value : std_logic_vector(7 downto 0);
|
|
signal PLL_LOCK_R1 : std_logic;
|
|
signal PLL_LOCK_R2 : std_logic;
|
|
signal MCB_RDY_BUSY_N : std_logic;
|
|
|
|
signal SELFREFRESH_REQ_R1 : std_logic;
|
|
signal SELFREFRESH_REQ_R2 : std_logic;
|
|
signal SELFREFRESH_REQ_R3 : std_logic;
|
|
signal SELFREFRESH_MCB_MODE_R1 : std_logic;
|
|
signal SELFREFRESH_MCB_MODE_R2 : std_logic;
|
|
signal SELFREFRESH_MCB_MODE_R3 : std_logic;
|
|
signal WAIT_SELFREFRESH_EXIT_DQS_CAL : std_logic;
|
|
signal PERFORM_START_DYN_CAL_AFTER_SELFREFRESH : std_logic;
|
|
signal START_DYN_CAL_STATE_R1 : std_logic;
|
|
signal PERFORM_START_DYN_CAL_AFTER_SELFREFRESH_R1 : std_logic;
|
|
|
|
-- Declare intermediate signals for referenced outputs
|
|
signal IODRP_ADD_xilinx0 : std_logic;
|
|
signal IODRP_SDI_xilinx1 : std_logic;
|
|
signal MCB_UIADD_xilinx2 : std_logic;
|
|
signal MCB_UISDI_xilinx11 : std_logic;
|
|
signal MCB_UICS_xilinx6 : std_logic;
|
|
signal MCB_UIBROADCAST_xilinx4 : std_logic;
|
|
signal MCB_UIADDR_int : std_logic_vector(4 downto 0);
|
|
signal MCB_UIDONECAL_xilinx7 : std_logic;
|
|
signal MCB_UIREAD_xilinx10 : std_logic;
|
|
signal SELFREFRESH_MODE_xilinx11 : std_logic;
|
|
signal Max_Value_int : std_logic_vector(7 downto 0);
|
|
signal Rst_condition1 : std_logic;
|
|
--signal Rst_condition2 : std_logic;
|
|
signal non_violating_rst : std_logic;
|
|
signal WAIT_200us_COUNTER : std_logic_vector(15 downto 0);
|
|
signal WaitTimer : std_logic_vector(7 downto 0);
|
|
signal WarmEnough : std_logic;
|
|
signal WaitCountEnable : std_logic;
|
|
signal State_Start_DynCal_R1 : std_logic;
|
|
signal State_Start_DynCal : std_logic;
|
|
|
|
signal pre_sysrst_minpulse_width_ok : std_logic;
|
|
signal pre_sysrst_cnt : std_logic_vector(3 downto 0);
|
|
|
|
-- This function multiplies by a constant MULT and then divides by the DIV constant
|
|
function Mult_Divide (Input : std_logic_vector(7 downto 0); MULT : integer ; DIV : integer ) return std_logic_vector is
|
|
variable Result : integer := 0;
|
|
variable temp : std_logic_vector(14 downto 0) := "000000000000000";
|
|
begin
|
|
for count in 0 to (MULT-1) loop
|
|
temp := temp + ("0000000" & Input);
|
|
end loop;
|
|
Result := (to_integer(unsigned(temp))) / (DIV);
|
|
temp := std_logic_vector(to_unsigned(Result,15));
|
|
return temp(7 downto 0);
|
|
end function Mult_Divide;
|
|
|
|
|
|
|
|
attribute syn_preserve : boolean;
|
|
attribute syn_preserve of P_Term : signal is TRUE;
|
|
attribute syn_preserve of N_Term : signal is TRUE;
|
|
attribute syn_preserve of P_Term_s : signal is TRUE;
|
|
attribute syn_preserve of N_Term_s : signal is TRUE;
|
|
attribute syn_preserve of P_Term_w : signal is TRUE;
|
|
attribute syn_preserve of N_Term_w : signal is TRUE;
|
|
|
|
|
|
|
|
|
|
attribute syn_preserve of P_Term_Prev : signal is TRUE;
|
|
attribute syn_preserve of N_Term_Prev : signal is TRUE;
|
|
attribute syn_preserve of IODRPCTRLR_MEMCELL_ADDR : signal is TRUE;
|
|
attribute syn_preserve of IODRPCTRLR_WRITE_DATA : signal is TRUE;
|
|
attribute syn_preserve of Max_Value_Previous : signal is TRUE;
|
|
attribute syn_preserve of DQS_DELAY_INITIAL : signal is TRUE;
|
|
|
|
attribute iob : string;
|
|
attribute iob of DONE_SOFTANDHARD_CAL : signal is "FALSE";
|
|
|
|
begin
|
|
|
|
|
|
-- move the default assignment here to make FORMALITY happy.
|
|
|
|
START_BROADCAST <= '1';
|
|
MCB_RECAL <= '0';
|
|
MCB_UIDQLOWERDEC <= '0';
|
|
MCB_UIADDR <= MCB_UIADDR_int;
|
|
MCB_UIDQLOWERINC <= '0';
|
|
MCB_UIDQUPPERDEC <= '0';
|
|
MCB_UIDQUPPERINC <= '0';
|
|
|
|
|
|
Max_Value <= Max_Value_int;
|
|
-- Drive referenced outputs
|
|
IODRP_ADD <= IODRP_ADD_xilinx0;
|
|
IODRP_SDI <= IODRP_SDI_xilinx1;
|
|
MCB_UIADD <= MCB_UIADD_xilinx2;
|
|
MCB_UISDI <= MCB_UISDI_xilinx11;
|
|
MCB_UICS <= MCB_UICS_xilinx6;
|
|
MCB_UIBROADCAST <= MCB_UIBROADCAST_xilinx4;
|
|
MCB_UIDONECAL <= MCB_UIDONECAL_xilinx7;
|
|
MCB_UIREAD <= MCB_UIREAD_xilinx10;
|
|
SELFREFRESH_MODE <= SELFREFRESH_MODE_xilinx11;
|
|
|
|
Inc_Dec_REFRSH_Flag <= (Inc_Flag & Dec_Flag & MCB_UOREFRSHFLAG);
|
|
Max_Value_Delta_Up <= Max_Value_int - Max_Value_Previous;
|
|
Half_MV_DU <= ('0' & Max_Value_Delta_Up(7 downto 1));
|
|
Max_Value_Delta_Dn <= Max_Value_Previous - Max_Value_int;
|
|
Half_MV_DD <= ('0' & Max_Value_Delta_Dn(7 downto 1));
|
|
CALMODE_EQ_CALIBRATION <= '1' when (C_MC_CALIBRATION_MODE = "CALIBRATION") else '0'; -- will calculate and set the DQS input delays if = 1'b1
|
|
Half_Max_Value <= ('0' & Max_Value_int(7 downto 1));
|
|
Quarter_Max_Value <= ("00" & Max_Value_int(7 downto 2));
|
|
DQS_DELAY_LOWER_LIMIT <= Quarter_Max_Value; -- limit for DQS_DELAY for decrements; could optionally be assigned to any 8-bit hex value here
|
|
DQS_DELAY_UPPER_LIMIT <= Half_Max_Value; -- limit for DQS_DELAY for increments; could optionally be assigned to any 8-bit hex value here
|
|
SKIP_DYN_IN_TERMINATION <= '1' when ((SKIP_DYN_IN_TERM = 1) or (SKIP_IN_TERM_CAL = 1)) else '0';
|
|
-- skip dynamic input termination if either the one-time or dynamic parameters are 1
|
|
SKIP_DYNAMIC_DQS_CAL <= '1' when ((CALMODE_EQ_CALIBRATION = '0') or (SKIP_DYNAMIC_CAL = 1)) else '0';
|
|
-- skip dynamic DQS delay calibration if either SKIP_DYNAMIC_CAL=1, or if C_MC_CALIBRATION_MODE=NOCALIBRATION
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if ((DQS_DELAY_INITIAL /= X"00") or (STATE = DONE)) then
|
|
DONE_SOFTANDHARD_CAL <= MCB_UODONECAL; -- high when either DQS input delays initialized, or STATE=DONE and UODONECAL high
|
|
else
|
|
DONE_SOFTANDHARD_CAL <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
iodrp_controller_inst : iodrp_controller
|
|
port map (
|
|
memcell_address => IODRPCTRLR_MEMCELL_ADDR,
|
|
write_data => IODRPCTRLR_WRITE_DATA,
|
|
read_data => IODRPCTRLR_READ_DATA,
|
|
rd_not_write => IODRPCTRLR_R_WB,
|
|
cmd_valid => IODRPCTRLR_CMD_VALID,
|
|
rdy_busy_n => IODRPCTRLR_RDY_BUSY_N,
|
|
use_broadcast => '0',
|
|
sync_rst => RST_reg,
|
|
DRP_CLK => UI_CLK,
|
|
DRP_CS => IODRP_CS,
|
|
DRP_SDI => IODRP_SDI_xilinx1,
|
|
DRP_ADD => IODRP_ADD_xilinx0,
|
|
DRP_SDO => IODRP_SDO,
|
|
DRP_BKST => open
|
|
);
|
|
|
|
iodrp_mcb_controller_inst : iodrp_mcb_controller
|
|
port map (
|
|
memcell_address => IODRPCTRLR_MEMCELL_ADDR,
|
|
write_data => IODRPCTRLR_WRITE_DATA,
|
|
read_data => MCB_READ_DATA,
|
|
rd_not_write => IODRPCTRLR_R_WB,
|
|
cmd_valid => MCB_CMD_VALID,
|
|
rdy_busy_n => MCB_RDY_BUSY_N,
|
|
use_broadcast => MCB_USE_BKST,
|
|
drp_ioi_addr => MCB_UIADDR_int,
|
|
sync_rst => RST_reg,
|
|
DRP_CLK => UI_CLK,
|
|
DRP_CS => MCB_UICS_xilinx6,
|
|
DRP_SDI => MCB_UISDI_xilinx11,
|
|
DRP_ADD => MCB_UIADD_xilinx2,
|
|
DRP_BKST => MCB_UIBROADCAST_xilinx4,
|
|
DRP_SDO => MCB_UOSDO,
|
|
MCB_UIREAD => MCB_UIREAD_xilinx10
|
|
);
|
|
|
|
process (UI_CLK, RST) begin
|
|
if (RST = '1') then
|
|
if (C_SIMULATION = "TRUE") then
|
|
WAIT_200us_COUNTER <= X"7FF0";
|
|
else
|
|
WAIT_200us_COUNTER <= (others => '0');
|
|
end if;
|
|
elsif (UI_CLK'event and UI_CLK = '1') then
|
|
if (WAIT_200us_COUNTER(15) = '1') then
|
|
WAIT_200us_COUNTER <= WAIT_200us_COUNTER;
|
|
else
|
|
WAIT_200us_COUNTER <= WAIT_200us_COUNTER + '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- init_sequence_skip: if (C_SIMULATION = "TRUE") generate
|
|
-- WAIT_200us_COUNTER <= X"FFFF";
|
|
-- process
|
|
-- begin
|
|
-- report "The 200 us wait period required before CKE goes active has been skipped in Simulation";
|
|
-- wait;
|
|
-- end process;
|
|
-- end generate;
|
|
|
|
|
|
gen_CKE_Train_a: if (C_MEM_TYPE = "DDR2") generate
|
|
process (UI_CLK, RST) begin
|
|
if (RST = '1') then
|
|
CKE_Train <= '0';
|
|
elsif (UI_CLK'event and UI_CLK = '1') then
|
|
if (STATE = WAIT_FOR_UODONE and MCB_UODONECAL = '1') then
|
|
CKE_Train <= '0';
|
|
elsif (WAIT_200us_COUNTER(15) = '1' and MCB_UODONECAL = '0') then
|
|
CKE_Train <= '1';
|
|
else
|
|
CKE_Train <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
end generate ;
|
|
|
|
gen_CKE_Train_b: if (not(C_MEM_TYPE = "DDR2")) generate
|
|
process (UI_CLK) begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
CKE_Train <= '0';
|
|
end if;
|
|
end process;
|
|
end generate ;
|
|
|
|
--********************************************
|
|
-- PLL_LOCK and RST signals
|
|
--********************************************
|
|
--MCB_SYSRST <= Pre_SYSRST or RST_reg; -- Pre_SYSRST is generated from the STATE state machine, and is OR'd with RST_reg input to drive MCB's
|
|
-- SYSRST pin (MCB_SYSRST)
|
|
|
|
rst_tmp <= not(SELFREFRESH_MODE_xilinx11) and not(PLL_LOCK_R2); -- rst_tmp becomes 1 if you lose Lock and the device is not in SUSPEND
|
|
|
|
process (UI_CLK, RST) begin
|
|
if (RST = '1') then
|
|
--Block_Reset <= '0';
|
|
--RstCounter <= (others => '0');
|
|
--elsif (UI_CLK'event and UI_CLK = '1') then
|
|
-- if (rst_tmp = '1') then -- this is to deal with not allowing the user-reset "RST" to violate TZQINIT_MAXCNT (min time between resets to DDR3)
|
|
Block_Reset <= '0';
|
|
RstCounter <= (others => '0');
|
|
elsif (UI_CLK'event and UI_CLK = '1') then
|
|
Block_Reset <= '0'; -- default to allow STATE to move out of RST_DELAY state
|
|
if (Pre_SYSRST = '1') then
|
|
RstCounter <= RST_CNT; -- whenever STATE wants to reset the MCB, set RstCounter to h10
|
|
else
|
|
if (RstCounter < TZQINIT_MAXCNT) then -- if RstCounter is less than d512 than this will execute
|
|
Block_Reset <= '1'; -- STATE won't exit RST_DELAY state
|
|
RstCounter <= RstCounter + "1"; -- and Rst_Counter increments
|
|
end if;
|
|
end if;
|
|
end if;
|
|
--end if;
|
|
end process;
|
|
|
|
-- Rst_contidtion1 is to make sure RESET will not happen again within TZQINIT_MAXCNT
|
|
non_violating_rst <= RST and Rst_condition1;
|
|
MCB_SYSRST <= Pre_SYSRST;
|
|
|
|
process (UI_CLK) begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RstCounter >= TZQINIT_MAXCNT) then
|
|
Rst_condition1 <= '1';
|
|
else
|
|
Rst_condition1 <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- -- non_violating_rst asserts whenever (system-level reset) RST is asserted but must be after TZQINIT_MAXCNT is reached (min-time between resets for DDR3)
|
|
-- -- After power stablizes, we will hold MCB in reset state for at least 200us before beginning initialization process.
|
|
-- -- If the PLL loses lock during normal operation, no ui_clk will be present because mcb_drp_clk is from a BUFGCE which
|
|
-- is gated by pll's lock signal. When the PLL locks again, the RST_reg stays asserted for at least 200 us which
|
|
-- will cause MCB to reset and reinitialize the memory afterwards.
|
|
-- -- During SUSPEND operation, the PLL will lose lock but non_violating_rst remains low (de-asserted) and WAIT_200us_COUNTER stays at
|
|
-- its terminal count. The PLL_LOCK input does not come direct from PLL, rather it is driven by gated_pll_lock from mcb_raw_wrapper module
|
|
-- The gated_pll_lock in the mcb_raw_wrapper does not de-assert during SUSPEND operation, hence PLL_LOCK will not de-assert, and the soft calibration
|
|
-- state machine will not reset during SUSPEND.
|
|
-- -- RST_reg is the control signal that resets the mcb_soft_calibration's State Machine. The MCB_SYSRST is now equal to
|
|
-- Pre_SYSRST. When State Machine is performing "INPUT Termination Calibration", it holds the MCB in reset by assertign MCB_SYSRST.
|
|
-- It will deassert the MCB_SYSRST so that it can grab the bus to broadcast the P and N term value to all of the DQ pins. Once the calibrated INPUT
|
|
-- termination is set, the State Machine will issue another short MCB_SYSRST so that MCB will use the tuned input termination during DQS preamble calibration.
|
|
|
|
|
|
--process (UI_CLK) begin
|
|
-- if (UI_CLK'event and UI_CLK = '1') then
|
|
--
|
|
-- if (RstCounter < RST_CNT) then
|
|
-- Rst_condition2 <= '1';
|
|
-- else
|
|
-- Rst_condition2 <= '0';
|
|
-- end if;
|
|
-- end if;
|
|
--end process;
|
|
|
|
process (UI_CLK, non_violating_rst) begin
|
|
if (non_violating_rst = '1') then
|
|
RST_reg <= '1'; -- STATE and MCB_SYSRST will both be reset if you lose lock when the device is not in SUSPEND
|
|
elsif (UI_CLK'event and UI_CLK = '1') then
|
|
if (WAIT_200us_COUNTER(15) = '0') then
|
|
RST_reg <= '1';
|
|
else
|
|
--RST_reg <= Rst_condition2 or rst_tmp; -- insures RST_reg is at least h10 pulses long
|
|
RST_reg <= rst_tmp; -- insures RST_reg is at least h10 pulses long
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
--*************************************************************
|
|
-- Stretching the pre_sysrst to satisfy the minimum pulse width
|
|
--*************************************************************
|
|
|
|
process (UI_CLK) begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (STATE = START_DYN_CAL_PRE) then
|
|
pre_sysrst_cnt <= pre_sysrst_cnt + '1';
|
|
else
|
|
pre_sysrst_cnt <= (others=>'0');
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
pre_sysrst_minpulse_width_ok <= pre_sysrst_cnt(3);
|
|
|
|
--********************************************
|
|
-- SUSPEND Logic
|
|
--********************************************
|
|
process (UI_CLK,RST)
|
|
begin
|
|
if (RST = '1') then
|
|
|
|
SELFREFRESH_MCB_MODE_R1 <= '0';
|
|
SELFREFRESH_MCB_MODE_R2 <= '0';
|
|
SELFREFRESH_MCB_MODE_R3 <= '0';
|
|
|
|
SELFREFRESH_REQ_R1 <= '0';
|
|
SELFREFRESH_REQ_R2 <= '0';
|
|
SELFREFRESH_REQ_R3 <= '0';
|
|
|
|
PLL_LOCK_R1 <= '0';
|
|
PLL_LOCK_R2 <= '0';
|
|
|
|
elsif (UI_CLK'event and UI_CLK = '1') then
|
|
-- SELFREFRESH_MCB_MODE is clocked by sysclk_2x_180
|
|
SELFREFRESH_MCB_MODE_R1 <= SELFREFRESH_MCB_MODE;
|
|
SELFREFRESH_MCB_MODE_R2 <= SELFREFRESH_MCB_MODE_R1;
|
|
SELFREFRESH_MCB_MODE_R3 <= SELFREFRESH_MCB_MODE_R2;
|
|
|
|
-- SELFREFRESH_REQ is clocked by user's application clock
|
|
SELFREFRESH_REQ_R1 <= SELFREFRESH_REQ;
|
|
SELFREFRESH_REQ_R2 <= SELFREFRESH_REQ_R1;
|
|
SELFREFRESH_REQ_R3 <= SELFREFRESH_REQ_R2;
|
|
|
|
PLL_LOCK_R1 <= PLL_LOCK;
|
|
PLL_LOCK_R2 <= PLL_LOCK_R1;
|
|
|
|
end if;
|
|
end process;
|
|
|
|
-- SELFREFRESH should only be deasserted after PLL_LOCK is asserted.
|
|
-- This is to make sure MCB get a locked sys_2x_clk before exiting
|
|
-- SELFREFRESH mode.
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
SELFREFRESH_MCB_REQ <= '0';
|
|
--elsif ((PLL_LOCK_R2 = '1') and (SELFREFRESH_REQ_R3 = '0') and (STATE = START_DYN_CAL)) then
|
|
elsif ((PLL_LOCK_R2 = '1') and (SELFREFRESH_REQ_R3 = '0')) then
|
|
SELFREFRESH_MCB_REQ <= '0';
|
|
elsif ((STATE = START_DYN_CAL) and (SELFREFRESH_REQ_R3 = '1')) then
|
|
SELFREFRESH_MCB_REQ <= '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
WAIT_SELFREFRESH_EXIT_DQS_CAL <= '0';
|
|
elsif ((SELFREFRESH_MCB_MODE_R2 = '1') and (SELFREFRESH_MCB_MODE_R3 = '0')) then
|
|
WAIT_SELFREFRESH_EXIT_DQS_CAL <= '1';
|
|
elsif ((WAIT_SELFREFRESH_EXIT_DQS_CAL = '1') and (SELFREFRESH_REQ_R3 = '0') and (PERFORM_START_DYN_CAL_AFTER_SELFREFRESH = '1')) then
|
|
-- START_DYN_CAL is next state
|
|
WAIT_SELFREFRESH_EXIT_DQS_CAL <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Need to detect when SM entering START_DYN_CAL
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
PERFORM_START_DYN_CAL_AFTER_SELFREFRESH <= '0';
|
|
START_DYN_CAL_STATE_R1 <= '0';
|
|
else
|
|
-- register PERFORM_START_DYN_CAL_AFTER_SELFREFRESH to detect end of cycle
|
|
PERFORM_START_DYN_CAL_AFTER_SELFREFRESH_R1 <= PERFORM_START_DYN_CAL_AFTER_SELFREFRESH;
|
|
if (STATE = START_DYN_CAL) then
|
|
START_DYN_CAL_STATE_R1 <= '1';
|
|
else
|
|
START_DYN_CAL_STATE_R1 <= '0';
|
|
end if;
|
|
if ((WAIT_SELFREFRESH_EXIT_DQS_CAL = '1') and (STATE /= START_DYN_CAL) and (START_DYN_CAL_STATE_R1 = '1')) then
|
|
PERFORM_START_DYN_CAL_AFTER_SELFREFRESH <= '1';
|
|
elsif ((STATE = START_DYN_CAL) and (SELFREFRESH_MCB_MODE_R3 = '0')) then
|
|
PERFORM_START_DYN_CAL_AFTER_SELFREFRESH <= '0';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- SELFREFRESH_MCB_MODE deasserted status is hold off
|
|
-- until Soft_Calib has at least done one loop of DQS update.
|
|
-- New logic WarmeEnough is added to make sure PLL_Lock is lockec and all IOs stable before
|
|
-- deassert the status of MCB's SELFREFRESH_MODE. This is to ensure all IOs are stable before
|
|
-- user logic sending new commands to MCB.
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
SELFREFRESH_MODE_xilinx11 <= '0';
|
|
elsif (SELFREFRESH_MCB_MODE_R2 = '1') then
|
|
SELFREFRESH_MODE_xilinx11 <= '1';
|
|
elsif (WarmEnough = '1') then
|
|
SELFREFRESH_MODE_xilinx11 <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
WaitCountEnable <= '0';
|
|
elsif (SELFREFRESH_REQ_R2 = '0' and SELFREFRESH_REQ_R1 = '1') then
|
|
WaitCountEnable <= '0';
|
|
elsif ((PERFORM_START_DYN_CAL_AFTER_SELFREFRESH = '0') and (PERFORM_START_DYN_CAL_AFTER_SELFREFRESH_R1 = '1')) then
|
|
WaitCountEnable <= '1';
|
|
else
|
|
WaitCountEnable <= WaitCountEnable;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
State_Start_DynCal <= '0';
|
|
elsif (STATE = START_DYN_CAL) then
|
|
State_Start_DynCal <= '1';
|
|
else
|
|
State_Start_DynCal <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
State_Start_DynCal_R1 <= '0';
|
|
else
|
|
State_Start_DynCal_R1 <= State_Start_DynCal;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST = '1') then
|
|
WaitTimer <= (others => '0');
|
|
WarmEnough <= '1';
|
|
elsif ((SELFREFRESH_REQ_R2 = '0') and (SELFREFRESH_REQ_R1 = '1')) then
|
|
WaitTimer <= (others => '0');
|
|
WarmEnough <= '0';
|
|
elsif (WaitTimer = X"04") then
|
|
WaitTimer <= WaitTimer ;
|
|
WarmEnough <= '1';
|
|
elsif (WaitCountEnable = '1') then
|
|
WaitTimer <= WaitTimer + '1';
|
|
else
|
|
WaitTimer <= WaitTimer ;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
--********************************************
|
|
--Comparitor for Dynamic Calibration circuit
|
|
--********************************************
|
|
Dec_Flag <= '1' when (TARGET_DQS_DELAY < DQS_DELAY) else '0';
|
|
Inc_Flag <= '1' when (TARGET_DQS_DELAY > DQS_DELAY) else '0';
|
|
|
|
--*********************************************************************************************
|
|
--Counter for extra clock cycles injected after setting Calibrate bit in IODRP2 for Dynamic Cal
|
|
--*********************************************************************************************
|
|
process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST_reg = '1') then
|
|
count <= "000000";
|
|
elsif (counter_en = '1') then
|
|
count <= count + "000001";
|
|
else
|
|
count <= "000000";
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
--*********************************************************************************************
|
|
-- Capture narrow MCB_UODATAVALID pulse - only one sysclk90 cycle wide
|
|
--*********************************************************************************************
|
|
process (UI_CLK, MCB_UODATAVALID)
|
|
begin
|
|
if(MCB_UODATAVALID = '1') then
|
|
MCB_UODATAVALID_U <= '1';
|
|
elsif(UI_CLK'event and UI_CLK = '1') then
|
|
MCB_UODATAVALID_U <= MCB_UODATAVALID;
|
|
end if;
|
|
end process;
|
|
|
|
--**************************************************************************************************************
|
|
--Always block to mux SDI, SDO, CS, and ADD depending on which IODRP is active: RZQ, ZIO or MCB's UI port (to IODRP2_MCBs)
|
|
--**************************************************************************************************************
|
|
process (Active_IODRP, IODRP_CS, RZQ_IODRP_SDO, ZIO_IODRP_SDO)
|
|
begin
|
|
case Active_IODRP is
|
|
when RZQ =>
|
|
RZQ_IODRP_CS <= IODRP_CS;
|
|
ZIO_IODRP_CS <= '0';
|
|
IODRP_SDO <= RZQ_IODRP_SDO;
|
|
when ZIO =>
|
|
RZQ_IODRP_CS <= '0';
|
|
ZIO_IODRP_CS <= IODRP_CS;
|
|
IODRP_SDO <= ZIO_IODRP_SDO;
|
|
when MCB_PORT =>
|
|
RZQ_IODRP_CS <= '0';
|
|
ZIO_IODRP_CS <= '0';
|
|
IODRP_SDO <= '0';
|
|
when others =>
|
|
RZQ_IODRP_CS <= '0';
|
|
ZIO_IODRP_CS <= '0';
|
|
IODRP_SDO <= '0';
|
|
end case;
|
|
end process;
|
|
|
|
--******************************************************************
|
|
--State Machine's Always block / Case statement for Next State Logic
|
|
--
|
|
--The WAIT1,2,etc states were required after every state where the
|
|
--DRP controller was used to do a write to the IODRPs - this is because
|
|
--there's a clock cycle latency on IODRPCTRLR_RDY_BUSY_N whenever the DRP controller
|
|
--sees IODRPCTRLR_CMD_VALID go high. OFF_RZQ_PTERM and OFF_ZIO_NTERM were added
|
|
--soley for the purpose of reducing power, particularly on RZQ as
|
|
--that pin is expected to have a permanent external resistor to gnd.
|
|
--******************************************************************
|
|
NEXT_STATE_LOGIC: process (UI_CLK)
|
|
begin
|
|
if (UI_CLK'event and UI_CLK = '1') then
|
|
if (RST_reg = '1') then -- Synchronous reset
|
|
MCB_CMD_VALID <= '0';
|
|
MCB_UIADDR_int <= "00000"; -- take control of UI/UO port
|
|
MCB_UICMDEN <= '1'; -- tells MCB that it is in Soft Cal.
|
|
MCB_UIDONECAL_xilinx7 <= '0';
|
|
MCB_USE_BKST <= '0';
|
|
MCB_UIDRPUPDATE <= '1';
|
|
Pre_SYSRST <= '1'; -- keeps MCB in reset
|
|
IODRPCTRLR_CMD_VALID <= '0';
|
|
IODRPCTRLR_MEMCELL_ADDR <= NoOp;
|
|
IODRPCTRLR_WRITE_DATA <= "00000000";
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_USE_BKST <= '0';
|
|
P_Term <= "000000";
|
|
N_Term <= "0000000";
|
|
P_Term_s <= "000000";
|
|
N_Term_w <= "0000000";
|
|
P_Term_w <= "000000";
|
|
N_Term_s <= "0000000";
|
|
|
|
P_Term_Prev <= "000000";
|
|
N_Term_Prev <= "0000000";
|
|
Active_IODRP <= RZQ;
|
|
MCB_UILDQSINC <= '0'; --no inc or dec
|
|
MCB_UIUDQSINC <= '0'; --no inc or dec
|
|
MCB_UILDQSDEC <= '0'; --no inc or dec
|
|
MCB_UIUDQSDEC <= '0';
|
|
counter_en <= '0'; --flag that the First Dynamic Calibration completed
|
|
First_Dyn_Cal_Done <= '0';
|
|
Max_Value_int <= "00000000";
|
|
Max_Value_Previous <= "00000000";
|
|
STATE <= START;
|
|
DQS_DELAY <= "00000000";
|
|
DQS_DELAY_INITIAL <= "00000000";
|
|
TARGET_DQS_DELAY <= "00000000";
|
|
LastPass_DynCal <= IN_TERM_PASS;
|
|
First_In_Term_Done <= '0';
|
|
MCB_UICMD <= '0';
|
|
MCB_UICMDIN <= '0';
|
|
MCB_UIDQCOUNT <= "0000";
|
|
counter_inc <= "00000000";
|
|
counter_dec <= "00000000";
|
|
else
|
|
counter_en <= '0';
|
|
IODRPCTRLR_CMD_VALID <= '0';
|
|
IODRPCTRLR_MEMCELL_ADDR <= NoOp;
|
|
IODRPCTRLR_R_WB <= READ_MODE;
|
|
IODRPCTRLR_USE_BKST <= '0';
|
|
MCB_CMD_VALID <= '0'; --no inc or dec
|
|
MCB_UILDQSINC <= '0'; --no inc or dec
|
|
MCB_UIUDQSINC <= '0'; --no inc or dec
|
|
MCB_UILDQSDEC <= '0'; --no inc or dec
|
|
MCB_UIUDQSDEC <= '0';
|
|
MCB_USE_BKST <= '0';
|
|
MCB_UICMDIN <= '0';
|
|
DQS_DELAY <= DQS_DELAY;
|
|
TARGET_DQS_DELAY <= TARGET_DQS_DELAY;
|
|
|
|
case STATE is
|
|
when START => --h00
|
|
MCB_UICMDEN <= '1'; -- take control of UI/UO port
|
|
MCB_UIDONECAL_xilinx7 <= '0'; -- tells MCB that it is in Soft Cal.
|
|
P_Term <= "000000";
|
|
N_Term <= "0000000";
|
|
Pre_SYSRST <= '1'; -- keeps MCB in reset
|
|
LastPass_DynCal <= IN_TERM_PASS;
|
|
if (SKIP_IN_TERM_CAL = 1) then
|
|
--STATE <= WRITE_CALIBRATE;
|
|
STATE <= WAIT_FOR_START_BROADCAST;
|
|
P_Term <= "000000";
|
|
N_Term <= "0000000";
|
|
elsif (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= LOAD_RZQ_NTERM;
|
|
else
|
|
STATE <= START;
|
|
end if;
|
|
--***************************
|
|
-- IOB INPUT TERMINATION CAL
|
|
--***************************
|
|
when LOAD_RZQ_NTERM => --h01
|
|
Active_IODRP <= RZQ;
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_WRITE_DATA <= ('0' & N_Term);
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= LOAD_RZQ_NTERM;
|
|
else
|
|
STATE <= WAIT1;
|
|
end if;
|
|
|
|
when WAIT1 => --h02
|
|
if (IODRPCTRLR_RDY_BUSY_N = '0') then
|
|
STATE <= WAIT1;
|
|
else
|
|
STATE <= LOAD_RZQ_PTERM;
|
|
end if;
|
|
|
|
when LOAD_RZQ_PTERM => --h03
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_WRITE_DATA <= ("00" & P_Term);
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= LOAD_RZQ_PTERM;
|
|
else
|
|
STATE <= WAIT2;
|
|
end if;
|
|
|
|
when WAIT2 => --h04
|
|
if (IODRPCTRLR_RDY_BUSY_N = '0') then
|
|
STATE <= WAIT2;
|
|
elsif ((RZQ_IN = '1') or (P_Term = "111111")) then
|
|
STATE <= MULTIPLY_DIVIDE; -- LOAD_ZIO_PTERM
|
|
else
|
|
STATE <= INC_PTERM;
|
|
end if;
|
|
|
|
when INC_PTERM => --h05
|
|
P_Term <= P_Term + "000001";
|
|
STATE <= LOAD_RZQ_PTERM;
|
|
|
|
when MULTIPLY_DIVIDE => -- h06
|
|
-- 13/4/2011 compensate the added sync FF
|
|
P_Term <= Mult_Divide(("00" & (P_Term - '1')),MULT,DIV)(5 downto 0);
|
|
STATE <= LOAD_ZIO_PTERM;
|
|
|
|
when LOAD_ZIO_PTERM => --h07
|
|
Active_IODRP <= ZIO;
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_WRITE_DATA <= ("00" & P_Term);
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= LOAD_ZIO_PTERM;
|
|
else
|
|
STATE <= WAIT3;
|
|
end if;
|
|
|
|
when WAIT3 => --h08
|
|
if ((not(IODRPCTRLR_RDY_BUSY_N)) = '1') then
|
|
STATE <= WAIT3;
|
|
else
|
|
STATE <= LOAD_ZIO_NTERM;
|
|
end if;
|
|
|
|
when LOAD_ZIO_NTERM => --h09
|
|
Active_IODRP <= ZIO;
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_WRITE_DATA <= ('0' & N_Term);
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= LOAD_ZIO_NTERM;
|
|
else
|
|
STATE <= WAIT4;
|
|
end if;
|
|
|
|
when WAIT4 => --h0A
|
|
if ((not(IODRPCTRLR_RDY_BUSY_N)) = '1') then
|
|
STATE <= WAIT4;
|
|
elsif (((not(ZIO_IN))) = '1' or (N_Term = "1111111")) then
|
|
if (PNSKEW = '1') then
|
|
STATE <= SKEW;
|
|
else
|
|
STATE <= WAIT_FOR_START_BROADCAST;
|
|
end if;
|
|
else
|
|
STATE <= INC_NTERM;
|
|
end if;
|
|
|
|
when INC_NTERM => --h0B
|
|
N_Term <= N_Term + "0000001";
|
|
STATE <= LOAD_ZIO_NTERM;
|
|
|
|
when SKEW => -- h0C
|
|
|
|
P_Term_s <= Mult_Divide(("00" & P_Term), MULT_S, DIV_S)(5 downto 0);
|
|
N_Term_w <= Mult_Divide(('0' & (N_Term-'1')), MULT_W, DIV_W)(6 downto 0);
|
|
P_Term_w <= Mult_Divide(("00" & P_Term), MULT_W, DIV_W)(5 downto 0);
|
|
N_Term_s <= Mult_Divide(('0' & (N_Term-'1')), MULT_S, DIV_S)(6 downto 0);
|
|
P_Term <= Mult_Divide(("00" & P_Term), MULT_S, DIV_S)(5 downto 0);
|
|
N_Term <= Mult_Divide(('0' & (N_Term-'1')), MULT_W, DIV_W)(6 downto 0);
|
|
STATE <= WAIT_FOR_START_BROADCAST;
|
|
|
|
when WAIT_FOR_START_BROADCAST => --h0D
|
|
Pre_SYSRST <= '0'; -- release SYSRST, but keep UICMDEN=1 and UIDONECAL=0. This is needed to do Broadcast through UI interface, while
|
|
-- keeping the MCB in calibration mode
|
|
Active_IODRP <= MCB_PORT;
|
|
if ((START_BROADCAST and IODRPCTRLR_RDY_BUSY_N) = '1') then
|
|
if ((P_Term /= P_Term_Prev) or (SKIP_IN_TERM_CAL = 1)) then
|
|
STATE <= BROADCAST_PTERM;
|
|
P_Term_Prev <= P_Term;
|
|
elsif (N_Term /= N_Term_Prev) then
|
|
N_Term_Prev <= N_Term;
|
|
STATE <= BROADCAST_NTERM;
|
|
else
|
|
STATE <= OFF_RZQ_PTERM;
|
|
end if;
|
|
else
|
|
STATE <= WAIT_FOR_START_BROADCAST;
|
|
end if;
|
|
|
|
when BROADCAST_PTERM => --h0E
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_WRITE_DATA <= ("00" & P_Term);
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
MCB_CMD_VALID <= '1';
|
|
MCB_UIDRPUPDATE <= not First_In_Term_Done; -- Set the update flag if this is the first time through
|
|
MCB_USE_BKST <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= BROADCAST_PTERM;
|
|
else
|
|
STATE <= WAIT5;
|
|
end if;
|
|
|
|
when WAIT5 => --h0F
|
|
if ((not(MCB_RDY_BUSY_N)) = '1') then
|
|
STATE <= WAIT5;
|
|
elsif (First_In_Term_Done = '1') then -- If first time through is already set, then this must be dynamic in term
|
|
if (MCB_UOREFRSHFLAG = '1')then
|
|
MCB_UIDRPUPDATE <= '1';
|
|
if (N_Term /= N_Term_Prev) then
|
|
N_Term_Prev <= N_Term;
|
|
STATE <= BROADCAST_NTERM;
|
|
else
|
|
STATE <= OFF_RZQ_PTERM;
|
|
end if;
|
|
else
|
|
STATE <= WAIT5; -- wait for a Refresh cycle
|
|
end if;
|
|
else
|
|
N_Term_Prev <= N_Term;
|
|
STATE <= BROADCAST_NTERM;
|
|
end if;
|
|
|
|
when BROADCAST_NTERM => -- h10
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_WRITE_DATA <= ("0" & N_Term);
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
MCB_CMD_VALID <= '1';
|
|
MCB_USE_BKST <= '1';
|
|
MCB_UIDRPUPDATE <= not(First_In_Term_Done); -- Set the update flag if this is the first time through
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= BROADCAST_NTERM;
|
|
else
|
|
STATE <= WAIT6;
|
|
end if;
|
|
|
|
when WAIT6 => -- h11
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= WAIT6;
|
|
elsif (First_In_Term_Done = '1') then -- If first time through is already set, then this must be dynamic in term
|
|
if (MCB_UOREFRSHFLAG = '1')then
|
|
MCB_UIDRPUPDATE <= '1';
|
|
STATE <= OFF_RZQ_PTERM;
|
|
else
|
|
STATE <= WAIT6; -- wait for a Refresh cycle
|
|
end if;
|
|
else
|
|
-- if (PNSKEWDQS = '1') then
|
|
STATE <= LDQS_CLK_WRITE_P_TERM;
|
|
-- else
|
|
-- STATE <= OFF_RZQ_PTERM;
|
|
-- end if;
|
|
end if;
|
|
|
|
-- *********************
|
|
when LDQS_CLK_WRITE_P_TERM => -- h12
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= "00" & P_Term_w;
|
|
MCB_UIADDR_int <= IOI_LDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= LDQS_CLK_WRITE_P_TERM;
|
|
else
|
|
STATE <= LDQS_CLK_P_TERM_WAIT;
|
|
end if;
|
|
|
|
|
|
when LDQS_CLK_P_TERM_WAIT => --7'h13
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= LDQS_CLK_P_TERM_WAIT;
|
|
else
|
|
STATE <= LDQS_CLK_WRITE_N_TERM;
|
|
end if;
|
|
|
|
when LDQS_CLK_WRITE_N_TERM => --7'h14
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= '0' & N_Term_s;
|
|
MCB_UIADDR_int <= IOI_LDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= LDQS_CLK_WRITE_N_TERM;
|
|
else
|
|
STATE <= LDQS_CLK_N_TERM_WAIT;
|
|
end if;
|
|
|
|
--**
|
|
when LDQS_CLK_N_TERM_WAIT => --7'h15
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= LDQS_CLK_N_TERM_WAIT;
|
|
else
|
|
STATE <= LDQS_PIN_WRITE_P_TERM;
|
|
end if;
|
|
|
|
|
|
when LDQS_PIN_WRITE_P_TERM => --7'h16
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= "00" & P_Term_s;
|
|
MCB_UIADDR_int <= IOI_LDQS_PIN;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= LDQS_PIN_WRITE_P_TERM;
|
|
else
|
|
STATE <= LDQS_PIN_P_TERM_WAIT;
|
|
end if;
|
|
|
|
|
|
when LDQS_PIN_P_TERM_WAIT => --7'h17
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= LDQS_PIN_P_TERM_WAIT;
|
|
else
|
|
STATE <= LDQS_PIN_WRITE_N_TERM;
|
|
end if;
|
|
|
|
|
|
when LDQS_PIN_WRITE_N_TERM => --7'h18
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= '0' & N_Term_w;
|
|
MCB_UIADDR_int <= IOI_LDQS_PIN;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= LDQS_PIN_WRITE_N_TERM;
|
|
else
|
|
STATE <= LDQS_PIN_N_TERM_WAIT;
|
|
end if;
|
|
|
|
|
|
when LDQS_PIN_N_TERM_WAIT => --7'h19
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= LDQS_PIN_N_TERM_WAIT;
|
|
else
|
|
STATE <= UDQS_CLK_WRITE_P_TERM;
|
|
end if;
|
|
|
|
|
|
|
|
when UDQS_CLK_WRITE_P_TERM => --7'h1A
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= "00" & P_Term_w;
|
|
MCB_UIADDR_int <= IOI_UDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= UDQS_CLK_WRITE_P_TERM;
|
|
else
|
|
STATE <= UDQS_CLK_P_TERM_WAIT;
|
|
end if;
|
|
|
|
|
|
when UDQS_CLK_P_TERM_WAIT => --7'h1B
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= UDQS_CLK_P_TERM_WAIT;
|
|
else
|
|
STATE <= UDQS_CLK_WRITE_N_TERM;
|
|
end if;
|
|
|
|
|
|
when UDQS_CLK_WRITE_N_TERM => --7'h1C
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= '0' & N_Term_s;
|
|
MCB_UIADDR_int <= IOI_UDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= UDQS_CLK_WRITE_N_TERM;
|
|
else
|
|
STATE <= UDQS_CLK_N_TERM_WAIT;
|
|
end if;
|
|
|
|
when UDQS_CLK_N_TERM_WAIT => --7'h1D
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= UDQS_CLK_N_TERM_WAIT;
|
|
else
|
|
STATE <= UDQS_PIN_WRITE_P_TERM;
|
|
end if;
|
|
|
|
|
|
|
|
when UDQS_PIN_WRITE_P_TERM => --7'h1E
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= "00" & P_Term_s;
|
|
MCB_UIADDR_int <= IOI_UDQS_PIN;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= UDQS_PIN_WRITE_P_TERM;
|
|
else
|
|
STATE <= UDQS_PIN_P_TERM_WAIT;
|
|
end if;
|
|
|
|
|
|
when UDQS_PIN_P_TERM_WAIT => --7'h1F
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= UDQS_PIN_P_TERM_WAIT;
|
|
else
|
|
STATE <= UDQS_PIN_WRITE_N_TERM;
|
|
end if;
|
|
|
|
when UDQS_PIN_WRITE_N_TERM => --7'h20
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= '0' & N_Term_w;
|
|
MCB_UIADDR_int <= IOI_UDQS_PIN;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= UDQS_PIN_WRITE_N_TERM;
|
|
else
|
|
STATE <= UDQS_PIN_N_TERM_WAIT;
|
|
end if;
|
|
|
|
|
|
when UDQS_PIN_N_TERM_WAIT => --7'h21
|
|
if (MCB_RDY_BUSY_N = '0') then
|
|
STATE <= UDQS_PIN_N_TERM_WAIT;
|
|
else
|
|
STATE <= OFF_RZQ_PTERM;
|
|
end if;
|
|
|
|
-- *********************
|
|
|
|
|
|
when OFF_RZQ_PTERM => -- h22
|
|
Active_IODRP <= RZQ;
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= PTerm;
|
|
IODRPCTRLR_WRITE_DATA <= "00000000";
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
P_Term <= "000000";
|
|
N_Term <= "0000000";
|
|
MCB_UIDRPUPDATE <= not(First_In_Term_Done); -- Set the update flag if this is the first time through
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= OFF_RZQ_PTERM;
|
|
else
|
|
STATE <= WAIT7;
|
|
end if;
|
|
|
|
when WAIT7 => -- h23
|
|
if ((not(IODRPCTRLR_RDY_BUSY_N)) = '1') then
|
|
STATE <= WAIT7;
|
|
else
|
|
STATE <= OFF_ZIO_NTERM;
|
|
end if;
|
|
|
|
when OFF_ZIO_NTERM => -- h24
|
|
Active_IODRP <= ZIO;
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= NTerm;
|
|
IODRPCTRLR_WRITE_DATA <= "00000000";
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= OFF_ZIO_NTERM;
|
|
else
|
|
STATE <= WAIT8;
|
|
end if;
|
|
|
|
when WAIT8 => -- h25
|
|
if (IODRPCTRLR_RDY_BUSY_N = '0') then
|
|
STATE <= WAIT8;
|
|
else
|
|
if (First_In_Term_Done = '1') then
|
|
STATE <= START_DYN_CAL; -- No need to reset the MCB if we are in InTerm tuning
|
|
else
|
|
STATE <= WRITE_CALIBRATE; -- go read the first Max_Value_int from RZQ
|
|
end if;
|
|
end if;
|
|
|
|
when RST_DELAY => -- h26
|
|
--MCB_UICMDEN <= '0'; -- release control of UI/UO port
|
|
if (Block_Reset = '1') then -- this ensures that more than 512 clock cycles occur since the last reset after MCB_WRITE_CALIBRATE ???
|
|
STATE <= RST_DELAY;
|
|
else
|
|
STATE <= START_DYN_CAL_PRE;
|
|
end if;
|
|
|
|
--***************************
|
|
--DYNAMIC CALIBRATION PORTION
|
|
--***************************
|
|
when START_DYN_CAL_PRE => -- h27
|
|
LastPass_DynCal <= IN_TERM_PASS;
|
|
MCB_UICMDEN <= '0'; -- release UICMDEN
|
|
MCB_UIDONECAL_xilinx7 <= '1'; -- release UIDONECAL - MCB will now initialize.
|
|
Pre_SYSRST <= '1'; -- SYSRST pulse
|
|
if (CALMODE_EQ_CALIBRATION = '0') then -- if C_MC_CALIBRATION_MODE is set to NOCALIBRATION
|
|
STATE <= START_DYN_CAL; -- we'll skip setting the DQS delays manually
|
|
elsif (pre_sysrst_minpulse_width_ok = '1') then
|
|
STATE <= WAIT_FOR_UODONE;
|
|
end if;
|
|
|
|
when WAIT_FOR_UODONE => -- h28
|
|
Pre_SYSRST <= '0'; -- SYSRST pulse
|
|
if ((IODRPCTRLR_RDY_BUSY_N and MCB_UODONECAL) = '1')then --IODRP Controller needs to be ready, & MCB needs to be done with hard calibration
|
|
MCB_UICMDEN <= '1'; -- grab UICMDEN
|
|
DQS_DELAY_INITIAL <= Mult_Divide(Max_Value_int, DQS_NUMERATOR, DQS_DENOMINATOR);
|
|
STATE <= LDQS_WRITE_POS_INDELAY;
|
|
else
|
|
STATE <= WAIT_FOR_UODONE;
|
|
end if;
|
|
|
|
when LDQS_WRITE_POS_INDELAY => -- h29
|
|
IODRPCTRLR_MEMCELL_ADDR <= PosEdgeInDly;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= DQS_DELAY_INITIAL;
|
|
MCB_UIADDR_int <= IOI_LDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1') then
|
|
STATE <= LDQS_WRITE_POS_INDELAY;
|
|
else
|
|
STATE <= LDQS_WAIT1;
|
|
end if;
|
|
|
|
when LDQS_WAIT1 => -- h2A
|
|
if (MCB_RDY_BUSY_N = '0')then
|
|
STATE <= LDQS_WAIT1;
|
|
else
|
|
STATE <= LDQS_WRITE_NEG_INDELAY;
|
|
end if;
|
|
|
|
when LDQS_WRITE_NEG_INDELAY => -- h2B
|
|
IODRPCTRLR_MEMCELL_ADDR <= NegEdgeInDly;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= DQS_DELAY_INITIAL;
|
|
MCB_UIADDR_int <= IOI_LDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1')then
|
|
STATE <= LDQS_WRITE_NEG_INDELAY;
|
|
else
|
|
STATE <= LDQS_WAIT2;
|
|
end if;
|
|
|
|
when LDQS_WAIT2 => -- 7'h2C
|
|
if(MCB_RDY_BUSY_N = '0')then
|
|
STATE <= LDQS_WAIT2;
|
|
else
|
|
STATE <= UDQS_WRITE_POS_INDELAY;
|
|
end if;
|
|
|
|
when UDQS_WRITE_POS_INDELAY => -- 7'h2D
|
|
IODRPCTRLR_MEMCELL_ADDR <= PosEdgeInDly;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= DQS_DELAY_INITIAL;
|
|
MCB_UIADDR_int <= IOI_UDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1')then
|
|
STATE <= UDQS_WRITE_POS_INDELAY;
|
|
else
|
|
STATE <= UDQS_WAIT1;
|
|
end if;
|
|
|
|
when UDQS_WAIT1 => -- 7'h2E
|
|
if (MCB_RDY_BUSY_N = '0')then
|
|
STATE <= UDQS_WAIT1;
|
|
else
|
|
STATE <= UDQS_WRITE_NEG_INDELAY;
|
|
end if;
|
|
|
|
when UDQS_WRITE_NEG_INDELAY => -- 7'h2F
|
|
IODRPCTRLR_MEMCELL_ADDR <= NegEdgeInDly;
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
IODRPCTRLR_WRITE_DATA <= DQS_DELAY_INITIAL;
|
|
MCB_UIADDR_int <= IOI_UDQS_CLK;
|
|
MCB_CMD_VALID <= '1';
|
|
if (MCB_RDY_BUSY_N = '1')then
|
|
STATE <= UDQS_WRITE_NEG_INDELAY;
|
|
else
|
|
STATE <= UDQS_WAIT2;
|
|
end if;
|
|
|
|
when UDQS_WAIT2 => -- 7'h30
|
|
if (MCB_RDY_BUSY_N = '0')then
|
|
STATE <= UDQS_WAIT2;
|
|
else
|
|
DQS_DELAY <= DQS_DELAY_INITIAL;
|
|
TARGET_DQS_DELAY <= DQS_DELAY_INITIAL;
|
|
STATE <= START_DYN_CAL;
|
|
end if;
|
|
|
|
when START_DYN_CAL => -- h31
|
|
Pre_SYSRST <= '0'; -- SYSRST not driven
|
|
counter_inc <= (others => '0');
|
|
counter_dec <= (others => '0');
|
|
if (SKIP_DYNAMIC_DQS_CAL = '1' and SKIP_DYN_IN_TERMINATION = '1')then
|
|
STATE <= DONE; --if we're skipping both dynamic algorythms, go directly to DONE
|
|
elsif ((IODRPCTRLR_RDY_BUSY_N = '1') and (MCB_UODONECAL = '1') and (SELFREFRESH_REQ_R1 = '0')) then
|
|
--IODRP Controller needs to be ready, & MCB needs to be done with hard calibration
|
|
-- Alternate between Dynamic Input Termination and Dynamic Tuning routines
|
|
if ((SKIP_DYN_IN_TERMINATION = '0') and (LastPass_DynCal = DYN_CAL_PASS)) then
|
|
LastPass_DynCal <= IN_TERM_PASS;
|
|
STATE <= LOAD_RZQ_NTERM;
|
|
else
|
|
LastPass_DynCal <= DYN_CAL_PASS;
|
|
STATE <= WRITE_CALIBRATE;
|
|
end if;
|
|
else
|
|
STATE <= START_DYN_CAL;
|
|
end if;
|
|
|
|
when WRITE_CALIBRATE => -- h32
|
|
Pre_SYSRST <= '0';
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= DelayControl;
|
|
IODRPCTRLR_WRITE_DATA <= "00100000";
|
|
IODRPCTRLR_R_WB <= WRITE_MODE;
|
|
Active_IODRP <= RZQ;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= WRITE_CALIBRATE;
|
|
else
|
|
STATE <= WAIT9;
|
|
end if;
|
|
|
|
when WAIT9 => -- h33
|
|
counter_en <= '1';
|
|
if (count < "100110") then -- this adds approximately 22 extra clock cycles after WRITE_CALIBRATE
|
|
STATE <= WAIT9;
|
|
else
|
|
STATE <= READ_MAX_VALUE;
|
|
end if;
|
|
|
|
when READ_MAX_VALUE => -- h34
|
|
IODRPCTRLR_CMD_VALID <= '1';
|
|
IODRPCTRLR_MEMCELL_ADDR <= MaxValue;
|
|
IODRPCTRLR_R_WB <= READ_MODE;
|
|
Max_Value_Previous <= Max_Value_int;
|
|
if (IODRPCTRLR_RDY_BUSY_N = '1') then
|
|
STATE <= READ_MAX_VALUE;
|
|
else
|
|
STATE <= WAIT10;
|
|
end if;
|
|
|
|
when WAIT10 => -- h35
|
|
if (IODRPCTRLR_RDY_BUSY_N = '0') then
|
|
STATE <= WAIT10;
|
|
else
|
|
Max_Value_int <= IODRPCTRLR_READ_DATA; --record the Max_Value_int from the IODRP controller
|
|
if (First_In_Term_Done = '0') then
|
|
STATE <= RST_DELAY;
|
|
First_In_Term_Done <= '1';
|
|
else
|
|
STATE <= ANALYZE_MAX_VALUE;
|
|
end if;
|
|
end if;
|
|
|
|
when ANALYZE_MAX_VALUE => -- h36 only do a Inc or Dec during a REFRESH cycle.
|
|
if (First_Dyn_Cal_Done = '0')then
|
|
STATE <= FIRST_DYN_CAL;
|
|
elsif ((Max_Value_int < Max_Value_Previous) and (Max_Value_Delta_Dn >= INCDEC_THRESHOLD)) then
|
|
STATE <= DECREMENT; -- May need to Decrement
|
|
TARGET_DQS_DELAY <= Mult_Divide(Max_Value_int, DQS_NUMERATOR, DQS_DENOMINATOR);
|
|
-- DQS_COUNT_VIRTUAL updated (could be negative value)
|
|
elsif ((Max_Value_int > Max_Value_Previous) and (Max_Value_Delta_Up >= INCDEC_THRESHOLD)) then
|
|
STATE <= INCREMENT; -- May need to Increment
|
|
TARGET_DQS_DELAY <= Mult_Divide(Max_Value_int, DQS_NUMERATOR, DQS_DENOMINATOR);
|
|
else
|
|
Max_Value_int <= Max_Value_Previous;
|
|
STATE <= START_DYN_CAL;
|
|
end if;
|
|
|
|
when FIRST_DYN_CAL => -- h37
|
|
First_Dyn_Cal_Done <= '1'; -- set flag that the First Dynamic Calibration has been completed
|
|
STATE <= START_DYN_CAL;
|
|
|
|
when INCREMENT => -- h38
|
|
STATE <= START_DYN_CAL; -- Default case: Inc is not high or no longer in REFRSH
|
|
MCB_UILDQSINC <= '0'; -- Default case: no inc or dec
|
|
MCB_UIUDQSINC <= '0'; -- Default case: no inc or dec
|
|
MCB_UILDQSDEC <= '0'; -- Default case: no inc or dec
|
|
MCB_UIUDQSDEC <= '0'; -- Default case: no inc or dec
|
|
case Inc_Dec_REFRSH_Flag is -- {Increment_Flag,Decrement_Flag,MCB_UOREFRSHFLAG},
|
|
when "101" =>
|
|
counter_inc <= counter_inc + '1';
|
|
STATE <= INCREMENT; -- Increment is still high, still in REFRSH cycle
|
|
if ((DQS_DELAY < DQS_DELAY_UPPER_LIMIT) and (counter_inc >= X"04")) then
|
|
-- if not at the upper limit yet, and you've waited 4 clks, increment
|
|
MCB_UILDQSINC <= '1';
|
|
MCB_UIUDQSINC <= '1';
|
|
DQS_DELAY <= DQS_DELAY + '1';
|
|
end if;
|
|
when "100" =>
|
|
if (DQS_DELAY < DQS_DELAY_UPPER_LIMIT) then
|
|
STATE <= INCREMENT; -- Increment is still high, REFRESH ended - wait for next REFRESH
|
|
end if;
|
|
when others =>
|
|
STATE <= START_DYN_CAL;
|
|
end case;
|
|
|
|
when DECREMENT => -- h39
|
|
STATE <= START_DYN_CAL; -- Default case: Dec is not high or no longer in REFRSH
|
|
MCB_UILDQSINC <= '0'; -- Default case: no inc or dec
|
|
MCB_UIUDQSINC <= '0'; -- Default case: no inc or dec
|
|
MCB_UILDQSDEC <= '0'; -- Default case: no inc or dec
|
|
MCB_UIUDQSDEC <= '0'; -- Default case: no inc or dec
|
|
if (DQS_DELAY /= "00000000") then
|
|
case Inc_Dec_REFRSH_Flag is -- {Increment_Flag,Decrement_Flag,MCB_UOREFRSHFLAG},
|
|
when "011" =>
|
|
counter_dec <= counter_dec + '1';
|
|
STATE <= DECREMENT; -- Decrement is still high, still in REFRSH cycle
|
|
if ((DQS_DELAY > DQS_DELAY_LOWER_LIMIT) and (counter_dec >= X"04")) then
|
|
-- if not at the lower limit, and you've waited 4 clks, decrement
|
|
MCB_UILDQSDEC <= '1'; -- decrement
|
|
MCB_UIUDQSDEC <= '1'; -- decrement
|
|
DQS_DELAY <= DQS_DELAY - '1'; -- SBS
|
|
end if;
|
|
when "010" =>
|
|
if (DQS_DELAY > DQS_DELAY_LOWER_LIMIT) then --if not at the lower limit, decrement
|
|
STATE <= DECREMENT; --Decrement is still high, REFRESH ended - wait for next REFRESH
|
|
end if;
|
|
when others =>
|
|
STATE <= START_DYN_CAL;
|
|
end case;
|
|
end if;
|
|
|
|
when DONE => -- h3A
|
|
Pre_SYSRST <= '0'; -- SYSRST cleared
|
|
MCB_UICMDEN <= '0'; -- release UICMDEN
|
|
STATE <= DONE;
|
|
|
|
when others =>
|
|
MCB_UICMDEN <= '0'; -- release UICMDEN
|
|
MCB_UIDONECAL_xilinx7 <= '1'; -- release UIDONECAL - MCB will now initialize.
|
|
Pre_SYSRST <= '0'; -- SYSRST not driven
|
|
IODRPCTRLR_CMD_VALID <= '0';
|
|
IODRPCTRLR_MEMCELL_ADDR <= "00000000";
|
|
IODRPCTRLR_WRITE_DATA <= "00000000";
|
|
IODRPCTRLR_R_WB <= '0';
|
|
IODRPCTRLR_USE_BKST <= '0';
|
|
P_Term <= "000000";
|
|
N_Term <= "0000000";
|
|
Active_IODRP <= ZIO;
|
|
Max_Value_Previous <= "00000000";
|
|
MCB_UILDQSINC <= '0'; -- no inc or dec
|
|
MCB_UIUDQSINC <= '0'; -- no inc or dec
|
|
MCB_UILDQSDEC <= '0'; -- no inc or dec
|
|
MCB_UIUDQSDEC <= '0'; -- no inc or dec
|
|
counter_en <= '0';
|
|
First_Dyn_Cal_Done <= '0'; -- flag that the First Dynamic Calibration completed
|
|
Max_Value_int <= Max_Value_int;
|
|
STATE <= START;
|
|
end case;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end architecture trans;
|
|
|
|
|