-- -*- Mode: VHDL -*- ------------------------------------------------------------------------------- -- Title : JTAG Master Controller for JTAG over GBT -- Project : ------------------------------------------------------------------------------- -- File : jtagMaster.vhd -- Author : Stephen Goadhouse -- Company : Univ. of Virginia, Physics Dept. -- Created : 2012-04-17 -- Last update: 2012-06-22 -- Platform : -- Standard : VHDL'93 ------------------------------------------------------------------------------- -- Description: This is a JTAG Master state machine for accessing a device's JTAG -- port. Initially, Andreas Weschenfelder's jtag_master project on -- OpenCores.org was being used but had to hack it so badly to work, -- decided to start over and create it myself. The original project: -- link: http://opencores.org/project,jtag_master ------------------------------------------------------------------------------- -- Copyright (c) 2012 ------------------------------------------------------------------------------- -- Revisions : -- Date Ver Author Description -- 2012-04-17 0.1 GOADHOUSE Created from Andreas Weschenfelder's jtag_master -- 2012-06-22 1.0 GOADHOUSE Released ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- This file started as part of JTAG_Master by Andreas Weschenfelder, -- revision 0.01. However, very little of the original code exists. -- Regardless, its copyright notice follows: -- -- JTAG_Master is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- JTAG_Master is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with JTAG_Master. If not, see . ------------------------------------------------------------------------------- LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; Library xpm; use xpm.vcomponents.all; ENTITY JTAGMaster IS GENERIC ( gADDR_BITS : NATURAL := 10; -- BRAM Address Bit Size gDATA_BITS : NATURAL := 8 -- BRAM Data Bit Size ); PORT ( RST : IN STD_LOGIC; -- active high reset CLK : IN STD_LOGIC; Enable : IN STD_LOGIC; -- Hold at '1' to enable the JTAG port IgnoreTDO : IN STD_LOGIC; -- don't wait for TDO from GBT link ClkDiv : IN STD_LOGIC_VECTOR(3 DOWNTO 0); ctrl_clk_o : OUT STD_LOGIC; TCK_in_rise : OUT STD_LOGIC; -- JTAG Part BitCount : IN STD_LOGIC_VECTOR (15 DOWNTO 0); Go : IN STD_LOGIC; TDO : IN STD_LOGIC; -- not necessarily sync'ed to CLK TCK_in : IN STD_LOGIC; -- not necessarily sync'ed to CLK TCK : OUT STD_LOGIC; TMS : OUT STD_LOGIC; TDI : OUT STD_LOGIC; TRst : OUT STD_LOGIC; Busy : OUT STD_LOGIC; TimeoutError : OUT STD_LOGIC; StateReset : IN STD_LOGIC; StateEnd : IN STD_LOGIC_VECTOR(3 DOWNTO 0); StateCurrent : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); -- Ram Part A : IN STD_LOGIC_VECTOR (gADDR_BITS-1 DOWNTO 0); WR : IN STD_LOGIC; Din : IN STD_LOGIC_VECTOR (gDATA_BITS-1 DOWNTO 0); Dout : OUT STD_LOGIC_VECTOR (gDATA_BITS-1 DOWNTO 0) ); END JTAGMaster; ARCHITECTURE rtl OF JTAGMaster IS CONSTANT TEST_LOGIC_RESET : UNSIGNED(StateEnd'range) := x"0"; CONSTANT RUN_TEST_IDLE : UNSIGNED(StateEnd'range) := x"1"; CONSTANT SELECT_DR : UNSIGNED(StateEnd'range) := x"2"; CONSTANT CAPTURE_DR : UNSIGNED(StateEnd'range) := x"3"; CONSTANT SHIFT_DR : UNSIGNED(StateEnd'range) := x"4"; CONSTANT EXIT1_DR : UNSIGNED(StateEnd'range) := x"5"; CONSTANT PAUSE_DR : UNSIGNED(StateEnd'range) := x"6"; CONSTANT EXIT2_DR : UNSIGNED(StateEnd'range) := x"7"; CONSTANT UPDATE_DR : UNSIGNED(StateEnd'range) := x"8"; CONSTANT SELECT_IR : UNSIGNED(StateEnd'range) := x"9"; CONSTANT CAPTURE_IR : UNSIGNED(StateEnd'range) := x"A"; CONSTANT SHIFT_IR : UNSIGNED(StateEnd'range) := x"B"; CONSTANT EXIT1_IR : UNSIGNED(StateEnd'range) := x"C"; CONSTANT PAUSE_IR : UNSIGNED(StateEnd'range) := x"D"; CONSTANT EXIT2_IR : UNSIGNED(StateEnd'range) := x"E"; CONSTANT UPDATE_IR : UNSIGNED(StateEnd'range) := x"F"; -- This structure shows, for each JTAG state, which state is reached after -- a single TCK clock cycle with TMS high or TMS low, respectively. This -- describes all possible state transitions in the JTAG state machine. -- NOTE: This comes loosely from the released Altera STAPL player C code (v2.2) -- that Actel has modified and released for use with their devices. TYPE stateTrans_typ IS ARRAY(NATURAL RANGE <>) OF UNSIGNED(StateEnd'range); CONSTANT stateTrans : stateTrans_typ := ( -- TMS: 0 TMS: 1 Current State RUN_TEST_IDLE, TEST_LOGIC_RESET, -- TEST_LOGIC_RESET RUN_TEST_IDLE, SELECT_DR, -- RUN_TEST_IDLE CAPTURE_DR, SELECT_IR, -- SELECT_DR SHIFT_DR, EXIT1_DR, -- CAPTURE_DR SHIFT_DR, EXIT1_DR, -- SHIFT_DR PAUSE_DR, UPDATE_DR, -- EXIT1_DR PAUSE_DR, EXIT2_DR, -- PAUSE_DR SHIFT_DR, UPDATE_DR, -- EXIT2_DR RUN_TEST_IDLE, SELECT_DR, -- UPDATE_DR CAPTURE_IR, TEST_LOGIC_RESET, -- SELECT_IR SHIFT_IR, EXIT1_IR, -- CAPTURE_IR SHIFT_IR, EXIT1_IR, -- SHIFT_IR PAUSE_IR, UPDATE_IR, -- EXIT1_IR PAUSE_IR, EXIT2_IR, -- PAUSE_IR SHIFT_IR, UPDATE_IR, -- EXIT2_IR RUN_TEST_IDLE, SELECT_DR -- UPDATE_IR ); -- This table contains the TMS value to be used to take the NEXT STEP on -- the path to the desired state. The array index is the current state, -- and the bit position is the desired endstate. To find out which state -- is used as the intermediate state, look up the TMS value in the -- stateTrans[] table above. -- -- NOTE: This comes loosely from the released Altera STAPL player C code (v2.2) -- that Actel has modified and released for use with their devices. -- -- NOTE: When upgraded to Altera's v2.5 STAPL player code base (tried TO -- see if source of problems was the STAPL plyaer code), discovered that -- they had changed this table so that the TMS = '0' branch from EXIT2_DR -- or EXIT2_IR is never taken. No documentation is given as to why this -- change occurred, but it clearly did. The same change is below, with the -- old settings saved in the comments just in case ever want to go back. -- The old way is more efficient in terms of state steps but perhaps the -- new way is more compatible with other devices. Since this VHDL logic -- handles the state transistions, it only adds a couple of more TCK -- transistions which are relatively fast, however, it does slow down -- the process. Perhaps this allows the state machine to be compatible -- with more devices? TYPE stateTMSMap_typ IS ARRAY(NATURAL RANGE <>) OF UNSIGNED(15 DOWNTO 0); CONSTANT stateTMSMap : stateTMSMap_typ := ( -- Current State x"0001", -- TEST_LOGIC_RESET x"FFFD", -- RUN_TEST_IDLE x"FE01", -- SELECT_DR x"FFE7", -- CAPTURE_DR x"FFEF", -- SHIFT_DR x"FF0F", -- EXIT1_DR x"FFBF", -- PAUSE_DR x"FFFF", -- EXIT2_DR was: x"FF0F" x"FEFD", -- UPDATE_DR x"0001", -- SELECT_IR x"F3FF", -- CAPTURE_IR x"F7FF", -- SHIFT_IR x"87FF", -- EXIT1_IR x"DFFF", -- PAUSE_IR x"FFFF", -- EXIT2_IR was: x"87FF" x"7FFD" -- UPDATE_IR ); -- purpose: Determines what value of TMS should be sent out FUNCTION tmsFromMap ( CONSTANT StateReset : IN STD_LOGIC; CONSTANT rdBitCountTerm : IN STD_LOGIC; CONSTANT lastRdBitCountTerm : IN STD_LOGIC; CONSTANT StateCurrent : IN UNSIGNED(StateEnd'range); CONSTANT StateEnd : IN UNSIGNED(StateEnd'range); CONSTANT tmsMap : IN stateTMSMap_typ) RETURN STD_LOGIC IS VARIABLE tmsMapOut : UNSIGNED(15 DOWNTO 0); VARIABLE tmsOut : STD_LOGIC; BEGIN -- FUNCTION tmsFromMap -- If a JTAG Reset is requested, BitCount is ignored and TMS is forced -- to a '1' for 5 JTAG Clock cycles. rdBitCount is compared to 5 to -- create rdBitCountTerm in this case. Once the reset is complete, the -- JTAG "state machine" is advanced until it reaches StateEnd. IF (StateReset = '1' AND (rdBitCountTerm = '0' OR lastRdBitCountTerm = '0')) THEN tmsOut := '1'; ELSE -- Get TMS value from tables to take a step toward desired state tmsMapOut := tmsMap(to_integer(StateCurrent)); IF rdBitCountTerm = '1' THEN -- If the BitCount has been reached, move toward the desired end state tmsOut := tmsMapOut(to_integer(UNSIGNED(StateEnd))); ELSE -- The BitCount has not yet been reached in the starting state, so stay -- in this state until the BitCount has been reached. tmsOut := tmsMapOut(to_integer(UNSIGNED(StateCurrent))); END IF; END IF; RETURN tmsOut; END FUNCTION tmsFromMap; SIGNAL rdBitCount : UNSIGNED(BitCount'range); SIGNAL rdBitCount_next : UNSIGNED(BitCount'range); SIGNAL rdBitCountTerm : STD_LOGIC := '0'; SIGNAL lastRdBitCountTerm : STD_LOGIC := '1'; SIGNAL wrBitCount : UNSIGNED(BitCount'range); SIGNAL wrBitCount_next : UNSIGNED(BitCount'range); SIGNAL wrBitCountTerm : STD_LOGIC := '0'; --Signals for Main Controller State Machine TYPE TypeStateJTAGCtrl IS (JTAG_IDLE, JTAG_GO, JTAG_WAIT); SIGNAL StateJTAGCtrl : TypeStateJTAGCtrl := JTAG_IDLE; ATTRIBUTE fsm_encoding : STRING; ATTRIBUTE fsm_encoding OF StateJTAGCtrl : SIGNAL IS "one-hot"; --Signals for I/O State Machine TYPE TypeStateJTAGIO IS (IDLE, FALL, PRERISE, RISE, PREFALL, NEXT_WORD_RD, NEXT_WORD_LD); SIGNAL StateJTAGIO : TypeStateJTAGIO := IDLE; ATTRIBUTE fsm_encoding OF StateJTAGIO : SIGNAL IS "one-hot"; --Signals for TDOi State Machine TYPE TypeStateJTAGTDO IS (IDLE, CAPTURE, NEXT_BIT, WAIT_TCK); SIGNAL StateJTAGTDO : TypeStateJTAGTDO := IDLE; ATTRIBUTE fsm_encoding OF StateJTAGTDO : SIGNAL IS "one-hot"; -- RAM storage for TDI/TDO COMPONENT RAM IS GENERIC ( gADDR_BITS : NATURAL := gADDR_BITS; gDATA_BITS : NATURAL := gDATA_BITS ); PORT (clk : IN STD_LOGIC; Write : IN STD_LOGIC; Awr : IN STD_LOGIC_VECTOR (gADDR_BITS-1 DOWNTO 0); Ard : IN STD_LOGIC_VECTOR (gADDR_BITS-1 DOWNTO 0); Din : IN STD_LOGIC_VECTOR (gDATA_BITS-1 DOWNTO 0); Dout : OUT STD_LOGIC_VECTOR (gDATA_BITS-1 DOWNTO 0) ); END COMPONENT; SIGNAL BRAM_wr_adr : STD_LOGIC_VECTOR(gADDR_BITS-1 DOWNTO 0); SIGNAL BRAM_rd_adr : STD_LOGIC_VECTOR(gADDR_BITS-1 DOWNTO 0); SIGNAL BRAM_Din : STD_LOGIC_VECTOR(gDATA_BITS-1 DOWNTO 0); SIGNAL BRAM_Dout : STD_LOGIC_VECTOR(gDATA_BITS-1 DOWNTO 0); SIGNAL BRAM_WR : STD_LOGIC; SIGNAL TDIBits : STD_LOGIC_VECTOR(gDATA_BITS-1 DOWNTO 0); SIGNAL TDOBits : STD_LOGIC_VECTOR(gDATA_BITS-1 DOWNTO 0); SIGNAL FSM_WR : STD_LOGIC := '0'; SIGNAL ClkCnt : UNSIGNED(StateEnd'range); SIGNAL noTDOWait : STD_LOGIC; SIGNAL clk_en : STD_LOGIC; SIGNAL jtag_clk_4x : STD_LOGIC; SIGNAL gotoState_Start : STD_LOGIC; SIGNAL gotoState_Done : STD_LOGIC; SIGNAL gotoState_DoneTDO : STD_LOGIC; SIGNAL TMS_StateCurr : UNSIGNED(StateEnd'range) := TEST_LOGIC_RESET; SIGNAL tmsStateCntr : UNSIGNED(StateEnd'range); SIGNAL TMSo : STD_LOGIC; SIGNAL TCKi_sync : STD_LOGIC_VECTOR(2 DOWNTO 0); SIGNAL TDOi_sync : STD_LOGIC_VECTOR(4 DOWNTO 0); -- Timeout counter in case loss of communications for when reading in TDO bits. -- Set timeout to ~0.8 ms if clk_local is the LHC Clock (~40.0789 MHz) CONSTANT kTIMEOUT_TERM : UNSIGNED(14 DOWNTO 0) := (OTHERS => '0'); SIGNAL timeoutCntr : UNSIGNED(kTIMEOUT_TERM'range); SIGNAL timeout : STD_LOGIC; SIGNAL tclk_cnt : UNSIGNED(7 downto 0); attribute keep : string; attribute keep of tclk_cnt : signal is "true"; --attribute mark_debug : string; --attribute mark_debug of TDO, TCK_IN, TCKi_sync, TDOi_sync, tclk_cnt : signal is "true"; --attribute mark_debug of TMSo, TCK, TDI : signal is "true"; --attribute mark_debug of StateJTAGCtrl, StateJTAGIO, StateJTAGTDO, noTDOWait : signal is "true"; BEGIN -- For now, simple control of noTDOWait noTDOWait <= IgnoreTDO; -- Use CLK_En to only gate FSM_WR from the state machine and not WR from the user. BRAM_WR <= WR WHEN StateJTAGCtrl = JTAG_IDLE ELSE FSM_WR; BRAM_Din <= Din WHEN StateJTAGCtrl = JTAG_IDLE ELSE TDOBits; BRAM_wr_adr <= A WHEN StateJTAGCtrl = JTAG_IDLE ELSE STD_LOGIC_VECTOR(to_unsigned(to_INTEGER(UNSIGNED(wrBitCount)) / gDATA_BITS, gADDR_BITS)); BRAM_rd_adr <= A WHEN StateJTAGCtrl = JTAG_IDLE ELSE STD_LOGIC_VECTOR(to_unsigned(to_INTEGER(UNSIGNED(rdBitCount)) / gDATA_BITS, gADDR_BITS)); JTAG_BRAM : RAM PORT MAP ( clk => CLK, Write => BRAM_WR, Awr => BRAM_wr_adr, Ard => BRAM_rd_adr, Din => BRAM_Din, Dout => BRAM_Dout ); Dout <= BRAM_Dout; -- purpose: Main JTAG Control State Machine -- type : sequential jtagCtrlState_proc : PROCESS (CLK, RST) IS BEGIN -- PROCESS jtagCtrlState_proc IF RST = '1' THEN -- asynchronous reset (active high) Busy <= '0'; gotoState_Start <= '0'; StateJTAGCtrl <= JTAG_IDLE; ELSIF rising_edge(CLK) THEN -- rising clock edge CASE StateJTAGCtrl IS WHEN JTAG_IDLE => Busy <= '0'; IF Go = '1' THEN Busy <= '1'; gotoState_Start <= '1'; StateJTAGCtrl <= JTAG_GO; END IF; WHEN JTAG_GO => Busy <= '1'; -- Wait for gotoState_Start to be seen and acknowledged by -- deasserting gotoState_Done. IF gotoState_Done = '0' THEN gotoState_Start <= '0'; StateJTAGCtrl <= JTAG_WAIT; ELSE gotoState_Start <= '1'; StateJTAGCtrl <= JTAG_GO; END IF; WHEN JTAG_WAIT => Busy <= '1'; -- wait for both JTAGIO and JTAGTDO to be done before returning to -- IDLE. If IgnoreTDO is asserted, then gotoState_DoneTDO will -- remain a '1' and only gotoState_Done will decide when to go back -- to JTAG_IDLE. IF (gotoState_Done = '1' AND gotoState_DoneTDO = '1') THEN Busy <= '0'; StateJTAGCtrl <= JTAG_IDLE; END IF; END CASE; END IF; END PROCESS jtagCtrlState_proc; -- Determine the next value of rdBitCount. rdBitCount_next <= rdBitCount + 1; -- Determine the Bit Counter termination. rdBitCountTerm is a '1' when -- rdBitCount has been incremented BitCount times, unless StateReset is -- '1'. In that case, rdBitCountTerm is a '1' when rdBitCount reaches 5, -- which is the minimum number of clocked in TMS = '1' that are needed to -- put the JTAG controller in the target device in Reset. Since rdBitCount -- must be 0-based since it is a pointer into BRAM, but BitCount is 1-based -- since it is a count of bits, use rdBitCount_next for the comparison so -- that the comparison is done with rdBitCount+1 rdBitCountTerm <= '1' WHEN (rdBitCount_next >= UNSIGNED(BitCount)) AND StateReset = '0' ELSE '1' WHEN (rdBitCount_next >= 5) AND StateReset = '1' ELSE '0'; StateCurrent <= STD_LOGIC_VECTOR(TMS_StateCurr); -- generate clk enable signal gen_clken : PROCESS(CLK) BEGIN IF rising_edge(CLK) THEN -- If JTAG is not enabled, keep clk_en low and keep ClkCnt from -- counting. Also, when get the Go signal asserted, load in the new -- ClkCnt but still keep clk_en '0'. IF (Enable = '0') THEN -- Set ClkCnt the same as when ClkCnt = 0 so that only two pre-CLK values. ClkCnt <= UNSIGNED(ClkDiv); clk_en <= '0'; ELSIF (ClkCnt = 0) THEN -- OR Go = '1') THEN ClkCnt <= UNSIGNED(ClkDiv); clk_en <= '1'; ELSE ClkCnt <= ClkCnt - 1; clk_en <= '0'; END IF; END IF; END PROCESS gen_clken; -- generate jtag_clk_4x debug signal directly from clk_en. Use clk_en -- so can use jtag_clk_4x in chip scope to clock in the direct result -- when clk_en is asserted. gen_clk4x : PROCESS(CLK, RST) BEGIN IF (RST = '1') THEN jtag_clk_4x <= '1'; ELSIF rising_edge(CLK) THEN IF (clk_en = '1') THEN jtag_clk_4x <= NOT jtag_clk_4x; -- toggle jtag_clk_4x when clk_en is asserted END IF; END IF; END PROCESS gen_clk4x; ctrl_clk_o <= jtag_clk_4x; -- purpose: Handle the JTAG I/O -- type : sequential io_proc : PROCESS (CLK, RST) IS VARIABLE tmsState : UNSIGNED(StateEnd'range); BEGIN -- PROCESS io_proc IF RST = '1' THEN -- asynchronous reset (active high) StateJTAGIO <= IDLE; TCK <= '1'; TDI <= '1'; TRst <= '0'; gotoState_Done <= '1'; TMS_StateCurr <= TEST_LOGIC_RESET; TMSo <= '1'; -- must default to '1' so JTAG remains in reset tmsStateCntr <= "0000"; rdBitCount <= (OTHERS => '0'); ELSIF rising_edge(CLK) THEN -- rising clock edge IF Enable = '0' THEN TRst <= '0'; -- put JTAG in reset if this controller is not enabled TMSo <= '1'; -- put TMS in safe state when controller not enabled StateJTAGIO <= IDLE; -- make sure that the state machine goes directly to IDLE state when enabled gotoState_Done <= '1'; ELSE TRst <= '1'; -- allow JTAG to operate (out of reset) -- Meter the speed of the signals based on the desired TCK frequency CASE StateJTAGIO IS WHEN IDLE => -- Use clk_en to gate the clearing of TCK. This is so that at the -- end of a cycle, when jumped here from PREFALL, TCK stays high -- long enough before being cleared. However, if by some wierd -- situation, the states went from PREFALL to IDLE and then -- immediately to FALL, it would still be okay with TCK just being -- a '1' for one clock longer. Holding it longer is not the -- issue. Not holding it long enough is (ie. minimum pulse width -- is the most important metric to provide). IF clk_en = '1' THEN TCK <= '0'; END IF; -- set lastRdBitCountTerm to a '1' in IDLE so that if BitCount is 0, -- when reach PREFALL, it will not appear as if BitCount just rose -- (lastRdBitCountTerm = '0' and rdBitCountTerm = '1'). Instead, -- rdBitCountTerm must start out as a '0' and then go to '1' before -- lastRdBitCountTerm = '0' and rdBitCountTerm = '1', which is what we -- want. lastRdBitCountTerm <= '1'; IF (gotoState_Start = '1') THEN gotoState_Done <= '0'; -- Initialize TDIBits, the bits being shifted out and it is -- also used to capture the data bits shifted in. -- Need to get TDIBits from BRAM. Also, need the extra clock so -- that the BRAM_adr updates so that TDIBits is the correct -- word. StateJTAGIO <= NEXT_WORD_LD; ELSE gotoState_Done <= '1'; tmsStateCntr <= "0000"; rdBitCount <= (OTHERS => '0'); StateJTAGIO <= IDLE; END IF; WHEN FALL => IF clk_en = '1' THEN TCK <= '0'; IF (StateReset = '0' AND (lastRdBitCountTerm = '0' OR rdBitCountTerm = '0')) THEN -- Shift LSb out first, if BitCount has not terminated. Need -- to also look at lastRdBitCountTerm since rdBitCountTerm is a -- '1' in this state when about to shift out the last bit. TDI <= TDIBits(0); ELSE -- otherwise, always keep it low (not a necessity but -- keeps signals from changing unnecessarily). TDI <= '0'; END IF; TMSo <= tmsFromMap(StateReset, rdBitCountTerm, lastRdBitCountTerm, TMS_StateCurr, UNSIGNED(StateEnd), stateTMSMap); StateJTAGIO <= PRERISE; END IF; WHEN PRERISE => IF (clk_en = '1') THEN TCK <= '0'; StateJTAGIO <= RISE; END IF; WHEN RISE => IF clk_en = '1' THEN TCK <= '1'; StateJTAGIO <= PREFALL; END IF; WHEN PREFALL => IF (clk_en = '1') THEN -- Wait for the falling edge of TCK to be seen in the received -- data from the GBT link. This makes sure that the TDOi input, -- which is coherent with the TCK input, is updated before it is -- clocked into FSM_Din. NOTE: Could just keep ShiftState from -- going to shifting4. This would be slightly less logic. The BRAM -- could be written every clock while in the state and it would -- not hurt the logic. It does save a little power by doing it -- this way and only writing to BRAM when TDOi is expected to be -- updated. (NOTE: not sure if this comment is congruent with the -- current state of the code) TCK <= '1'; -- Save rdBitCountTerm so can tell when it rises. lastRdBitCountTerm <= rdBitCountTerm; -- Perform the right bit shift TDIBits <= '0' & TDIBits(TDIBits'high DOWNTO TDIBits'low+1); IF rdBitCountTerm = '1' THEN -- Only incr tmsStateCntr after BitCount has been reached and are moving to StateEnd. tmsStateCntr <= tmsStateCntr + 1; END IF; tmsState := stateTrans(to_integer(TMS_StateCurr & TMSo)); TMS_StateCurr <= tmsState; -- If find the End State or the state counter reaches its terminal, -- end the search. StateCurrent can always be read when -- gotoState_Done is asserted to see if made it to the desired state. IF ((tmsState = UNSIGNED(StateEnd) AND rdBitCountTerm = '1') OR tmsStateCntr > 8) THEN gotoState_Done <= '1'; StateJTAGIO <= IDLE; ELSIF (rdBitCountTerm = '0' AND (to_INTEGER(rdBitCount) MOD gDATA_BITS = gDATA_BITS-1)) THEN -- Have shifted out the entire word, so advance -- rdBitCount and read out the next word from the BRAM into -- TDIBits. rdBitCount <= rdBitCount_next; StateJTAGIO <= NEXT_WORD_RD; ELSE IF rdBitCountTerm = '0' THEN -- BitCount has not been reached, so increment rdBitCount before next cycle rdBitCount <= rdBitCount_next; END IF; StateJTAGIO <= FALL; END IF; END IF; WHEN NEXT_WORD_RD => -- set TCK since it is always a '1' when going into FALL TCK <= '1'; StateJTAGIO <= NEXT_WORD_LD; WHEN NEXT_WORD_LD => -- NOTE: NOT setting TCK for a reason. This state is entered from -- IDLE, where TCK = '0', and NEXT_WORD_RD, where TCK = '1'. In -- both cases, do not want to change TCK state. Allow FALL to do -- that on clk_en. -- -- Load the new word into TDIBits for shifting TDIBits <= BRAM_Dout; -- continue on the next JTAG cycle StateJTAGIO <= FALL; END CASE; END IF; END IF; END PROCESS io_proc; TMS <= TMSo; -- SDG: For syncing the JTAG inputs (TCK_in and TDO), should we try to use a -- falling edge/rising edge scheme to minimize latency? Will need to add to -- UCF file to handle rising edge to falling edge delays. -- Determine the next value of wrBitCount. wrBitCount_next <= wrBitCount + 1; -- Determine the Bit Counter termination. wrBitCountTerm is a '1' when -- wrBitCount has been incremented BitCount times. Since wrBitCount -- must be 0-based since it is a pointer into BRAM, but BitCount is 1-based -- since it is a count of bits, use wrBitCount_next for the comparison so -- that the comparison is done with wrBitCount+1 wrBitCountTerm <= '1' WHEN (wrBitCount_next >= UNSIGNED(BitCount)) ELSE '0'; -- Watch for the timeoutCntr to reach its terminal value (zero) timeout <= '1' WHEN (timeoutCntr = kTIMEOUT_TERM) ELSE '0'; -- purpose: Handles clocking of TDO coming in -- @@@@ Need a timeout counter to prevent getting stuck if miss a clock edge due to comm problems or the like. -- @@@@ May need something to prevent this from seeing the TCK edge that resulted from the previous transfer. -- @@@@ May make this state machine run every time StateJTAGIO is run so that it stays busy until all TCKi edges have -- been seen. Also, may need to then save its own copy of BitCount so that it can be overwritten while this state machine -- is waiting for the remaining TCKi edges. Could even count the TCKo edges as they go out and always know how many to -- expect back. Still use BitCount to know when to stop clocking into BRAM. The same could be done for the other -- signals as a way to verify them. Count number of edges out and match to number of edges returned! Brilliant! tck_in_Sync_inst: xpm_cdc_single generic map ( DEST_SYNC_FF => 4, -- integer; range: 2-10 INIT_SYNC_FF => 0, -- integer; 0=disable simulation init values, 1=enable simulation init values SIM_ASSERT_CHK => 0, -- integer; 0=disable simulation messages, 1=enable simulation messages SRC_INPUT_REG => 0 -- integer; 0=do not register input, 1=register input ) port map ( src_clk => '0', -- optional; required when SRC_INPUT_REG = 1 src_in => tck_in, dest_clk => clk, dest_out => tcki_Sync(1) ); tdo_proc : PROCESS (CLK, RST) IS BEGIN -- PROCESS tdo_proc IF RST = '1' THEN -- asynchronous reset (active high) StateJTAGTDO <= IDLE; TCKi_sync(2) <= '0'; TCK_in_rise <= '0'; TDOi_sync <= (OTHERS => '0'); FSM_WR <= '0'; gotoState_DoneTDO <= '1'; timeoutCntr <= (OTHERS => '1'); TimeoutError <= '0'; -- only way to reset TimeoutError is with a total reset ELSIF rising_edge(CLK) THEN -- rising clock edge FSM_WR <= '0'; -- default is deasserted TCKi_sync(2) <= TCKi_sync(1); TCK_in_rise <= not TCKi_sync(2) and TCKi_sync(1); TDOi_sync <= TDOi_sync(3 DOWNTO 0) & TDO; TimeoutError <= timeout; CASE StateJTAGTDO IS WHEN IDLE => wrBitCount <= (OTHERS => '0'); -- Reset the Timeout Counter timeoutCntr <= (OTHERS => '1'); -- IF (gotoState_Start = '1' AND noTDOWait = '0') THEN -- gotoState_DoneTDO <= '0'; -- StateJTAGTDO <= CAPTURE; -- ELSE -- gotoState_DoneTDO <= '1'; -- END IF; IF (gotoState_Start = '1')then gotoState_DoneTDO <= '0'; if(noTDOWait = '0') THEN StateJTAGTDO <= CAPTURE; end if; elsif(gotoState_Done = '1' and gotoState_DoneTDO = '0')then StateJTAGTDO <= WAIT_TCK; END IF; WHEN CAPTURE => -- On the rising edge of TCKi, strobe in TDOi into TDOBits and increment wr_BitCount if ((TCKi_sync(1) = '1' AND TCKi_sync(2) = '0') or timeout = '1') THEN TDOBits <= TDOi_sync(4) & TDOBits(TDOBits'high DOWNTO TDOBits'low+1); IF (timeout = '1') THEN -- If reach the timeout terminal count, simply clock in a '0' as -- data so that the machine moves along and works toward -- completion even if the data in incorrect. This should prevent -- the JTAG controller from getting stuck if there is a loss of -- communications or it is bad communications. This does rely on -- the high level process handling incorrect data -- properly. Likely, it is doing a VERIFY which will be easily -- caught. There will also be a TimeoutError status bit that can -- be checked. It can only be cleared by completely resetting -- jtagMaster. TDOBits <= '0' & TDOBits(TDOBits'high DOWNTO TDOBits'low+1); end if; -- If reached the end of BitCount, write the last TDOBits word & return to IDLE IF (wrBitCountTerm = '1') THEN -- NOTE: This last word will not be LSb aligned unless -- BitCount is evenly divisable by gDATA_BITS. So the -- software must always bit align the last word based on -- BitCount MOD gDATA_BITS. FSM_WR <= '1'; -- set FSM_WR to write TDOBits into BRAM gotoState_DoneTDO <= '1'; -- asserting it here saves a clock, but it may not matter StateJTAGTDO <= IDLE; ELSE -- Reached the end of a BRAM data word, so write TDOBits into the BRAM IF ((to_INTEGER(wrBitCount) MOD gDATA_BITS = gDATA_BITS-1)) THEN FSM_WR <= '1'; END IF; StateJTAGTDO <= NEXT_BIT; END IF; END IF; -- Count down timeout counter timeoutCntr <= timeoutCntr - 1; WHEN NEXT_BIT => -- incr to next bit wrBitCount <= wrBitCount_next; -- Reset the Timeout Counter timeoutCntr <= (OTHERS => '1'); -- wait for the next TCKi rising edge StateJTAGTDO <= CAPTURE; WHEN WAIT_TCK => if(tclk_cnt = x"00" or timeout = '1')then gotoState_DoneTDO <= '1'; StateJTAGTDO <= IDLE; else timeoutCntr <= timeoutCntr - 1; end if; END CASE; END IF; END PROCESS tdo_proc; process(clk,rst) begin if(rst = '1')then tclk_cnt <= (others => '0'); elsif(clk'event and clk = '1')then if(gotoState_Start = '1')then tclk_cnt <= (others => '0'); elsif(TCKi_sync(2 downto 1) /= "01" and StateJTAGIO = RISE and clk_en = '1')then tclk_cnt <= tclk_cnt + 1; elsif(TCKi_sync(2 downto 1) = "01" and (StateJTAGIO /= RISE or clk_en = '0'))then tclk_cnt <= tclk_cnt - 1; end if; end if; end process; END rtl;