Shop OBEX P1 Docs P2 Docs Learn Events
VHDL jtag possible in C, or needs PASM ? — Parallax Forums

VHDL jtag possible in C, or needs PASM ?

jmgjmg Posts: 15,173
edited 2015-02-16 14:48 in Propeller 1
As a challenge for the C/Prop experts, here is a small VHDL module that does FIFO-JTAG, and that should fit into one COG.

Q: Can this be coded in C, and fit into one COG, with tolerable performance ? or is PASM the only option ?

This VHDL is coded for an external FIFO, but in a P1 would access a HUB FIFO managed from another COG.
(FIFO HW is FT245 or FT240 with 512 bytes each way )
It has about 120 active lines of code, some of which are jump-to-next style and can be trimmed.
-------------------------------------------------------------------------------
-- Serial/Parallel converter, interfacing JTAG chain with FTDI FT245BM
-------------------------------------------------------------------------------
-- Copyright (C) 2005-2007 Kolja Waschk, ixo.de
-------------------------------------------------------------------------------
-- This code is part of usbjtag. usbjtag 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 2 of the License,
-- or (at your option) any later version. usbjtag 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 this program in the file
-- COPYING; if not, write to the Free Software Foundation, Inc., 51 Franklin
-- St, Fifth Floor, Boston, MA  02110-1301  USA
-------------------------------------------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY jtag_logic IS
	PORT
	(
		CLK : IN STD_LOGIC;        -- external 24/25 MHz oscillator
		nRXF : IN STD_LOGIC;       -- FT245BM nRXF
		nTXE : IN STD_LOGIC;       -- FT245BM nTXE
		B_TDO  : IN STD_LOGIC;     -- JTAG input: TDO, AS/PS input: CONF_DONE
		B_ASDO : IN STD_LOGIC;     -- AS input: DATAOUT, PS input: nSTATUS
		B_TCK  : out STD_LOGIC; -- JTAG output: TCK to chain, AS/PS DCLK
		B_TMS  : out STD_LOGIC; -- JTAG output: TMS to chain, AS/PS nCONFIG
		B_NCE  : out STD_LOGIC; -- AS output: nCE
		B_NCS  : out STD_LOGIC; -- AS output: nCS
		B_TDI  : out STD_LOGIC; -- JTAG output: TDI to chain, AS: ASDI, PS: DATA0
		B_OE   : out STD_LOGIC; -- LED output/output driver enable 
		nRD : OUT STD_LOGIC;       -- FT245BM nRD
		WR : OUT STD_LOGIC;        -- FT245BM WR
		D : INOUT STD_LOGIC_VECTOR(7 downto 0); -- FT245BM D[7..0]
		led : out std_logic := '1'
	);
END jtag_logic;

ARCHITECTURE spec OF jtag_logic IS

	-- There are exactly 16 states. If this is encoded using 4 bits, there will
	-- be no unknown/undefined state. The host will send us 64 times "0" to move
	-- the state machine to a known state. We don't need a power-on reset.
	
	TYPE states IS
	(
		wait_for_nRXF_low,
		set_nRD_low,
		keep_nRD_low,
		latch_data_from_host,
		set_nRD_high,
		bits_set_pins_from_data,
		bytes_set_bitcount,
		bytes_get_tdo_set_tdi,
		bytes_clock_high_and_shift,
		bytes_keep_clock_high,
		bytes_clock_finish,
		wait_for_nTXE_low,
		set_WR_high,
		output_enable,
		set_WR_low,
		output_disable
	);
	
	ATTRIBUTE ENUM_ENCODING: STRING;
	ATTRIBUTE ENUM_ENCODING OF states: TYPE IS 
	  "0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111";
	
	SIGNAL carry: STD_LOGIC;
	SIGNAL do_output: STD_LOGIC;
	SIGNAL ioshifter: STD_LOGIC_VECTOR(7 DOWNTO 0);
	SIGNAL bitcount: STD_LOGIC_VECTOR(8 DOWNTO 0);
	SIGNAL state, next_state: states;
	SIGNAL ncs: std_logic;
	
BEGIN
	sm: PROCESS(nRXF, nTXE, state, bitcount, ioshifter, do_output)

	BEGIN
		CASE state IS
		
			-- ============================ INPUT
		
			WHEN wait_for_nRXF_low =>
				IF nRXF='0' THEN
					next_state <= set_nRD_low;
				ELSE
					next_state <= wait_for_nRXF_low;
				END IF;
				
			WHEN set_nRD_low =>
				next_state <= keep_nRD_low;
			
			WHEN keep_nRD_low => 
				next_state <= latch_data_from_host;
				
			WHEN latch_data_from_host =>
				next_state <= set_nRD_high;
			
			WHEN set_nRD_high =>
				IF NOT (bitcount(8 DOWNTO 3) = "000000") THEN
					next_state <= bytes_get_tdo_set_tdi;
				ELSIF ioshifter(7) = '1' THEN
					next_state <= bytes_set_bitcount;
				ELSE
					next_state <= bits_set_pins_from_data;
				END IF;
			
			WHEN bytes_set_bitcount =>
				next_state <= wait_for_nRXF_low;
			
			-- ============================ BIT BANGING
				
			WHEN bits_set_pins_from_data =>
				IF ioshifter(6) = '0' THEN
					next_state <= wait_for_nRXF_low; -- read next byte from host
				ELSE
					next_state <= wait_for_nTXE_low; -- output byte to host
				END IF;
				
			-- ============================ BYTE OUTPUT (SHIFT OUT 8 BITS)
			
			WHEN bytes_get_tdo_set_tdi =>
				next_state <= bytes_clock_high_and_shift;
			
			WHEN bytes_clock_high_and_shift =>
				next_state <= bytes_keep_clock_high;
				
			WHEN bytes_keep_clock_high =>
				next_state <= bytes_clock_finish;
				
			WHEN bytes_clock_finish =>
				IF NOT (bitcount(2 DOWNTO 0) = "111") THEN
					next_state <= bytes_get_tdo_set_tdi; -- clock next bit
				ELSIF do_output = '1' THEN
					next_state <= wait_for_nTXE_low; -- output byte to host
				ELSE
					next_state <= wait_for_nRXF_low; -- read next byte from host
				END IF;
			
			-- ============================ OUTPUT BYTE TO HOST
			
			WHEN wait_for_nTXE_low =>
				IF nTXE = '0' THEN
					next_state <= set_WR_high;
				ELSE
					next_state <= wait_for_nTXE_low;
				END IF;
				
			WHEN set_WR_high =>
				next_state <= output_enable;
				
			WHEN output_enable =>
				next_state <= set_WR_low;
				
			WHEN set_WR_low =>
				next_state <= output_disable;
			
			WHEN output_disable =>
				next_state <= wait_for_nRXF_low; -- read next byte from host
				
			WHEN OTHERS => 
				next_state <= wait_for_nRXF_low;
				
		END CASE;
	END PROCESS sm;

	out_sm: PROCESS(CLK)

	BEGIN
		IF CLK = '1' AND CLK'event THEN
			
			IF state = set_nRD_low OR state = keep_nRD_low OR state = latch_data_from_host THEN
				nRD <= '0';
			ELSE
				nRD <= '1';
			END IF;
			
			IF state = latch_data_from_host THEN
				led <= '1';
				ioshifter(7 DOWNTO 0) <= D;
			END IF;
			
			IF state = set_WR_high OR state = output_enable THEN
				WR <= '1';
			ELSE
				WR <= '0';
			END IF;
			
			IF state = output_enable OR state = set_WR_low THEN
				led <= '0';
				D <= ioshifter(7 DOWNTO 0);
			ELSE
				D <= "ZZZZZZZZ";	
			END IF;
			
			IF state = bits_set_pins_from_data THEN
				B_TCK <= ioshifter(0);
				B_TMS <= ioshifter(1);
				B_NCE <= ioshifter(2);
				B_NCS <= ioshifter(3);
				B_TDI <= ioshifter(4);
				B_OE  <= ioshifter(5);
				ncs <= ioshifter(3);
				ioshifter <= "000000" & B_ASDO & B_TDO;
			END IF;
			
			IF state = bytes_set_bitcount THEN
				bitcount <= ioshifter(5 DOWNTO 0) & "111";
				do_output <= ioshifter(6);
			END IF;
			
			IF state = bytes_get_tdo_set_tdi THEN
				IF ncs = '1' THEN
					carry <= B_TDO; -- JTAG mode (nCS=1)
				ELSE
					carry <= B_ASDO; -- Active Serial mode (nCS=0)
				END IF;
				B_TDI <= ioshifter(0);
				bitcount <= bitcount - 1;
			END IF;
			
			IF state = bytes_clock_high_and_shift OR state = bytes_keep_clock_high THEN
				B_TCK <= '1';
			END IF;
			
			IF state = bytes_clock_high_and_shift THEN
				ioshifter <= carry & ioshifter(7 DOWNTO 1);
			END IF;
			
			IF state = bytes_clock_finish THEN
				B_TCK <= '0';
			END IF;
		
			state <= next_state;
			
		END IF;
	END PROCESS out_sm;
	
END spec;

Comments

  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-02-16 07:20
    I'm not too familiar with VHDL, but the case statement followed by the if-else statements are all pretty straight forward. I would recommend programming them in a cogc module - the performance-to-size ratio is excellent, since it gets compiled straight to assembly with no loading of instructions from hub memory. If you then find performance hiccups, you can use inline assembly in specific sections to achieve your desired results.
  • jmgjmg Posts: 15,173
    edited 2015-02-16 14:48
    I also found code that writes on the PC end, here
    http://openocd.sourceforge.net/doc/doxygen/html/usb__blaster_8c_source.html

    and that confirms my notes of the VHDL command structure, worked from the same source (above) they used
    There are 2 decision bits, and a Counter/shifter
    //
    // Shifter - USB-Blaster compatible ??
    // Command Byte Send > 64 bytes 00H, to ensure are back in BitBang/command mode
    // s7 = bitbang/Bytes
    //   s6 = nRD/WR,   WR is get reply, nRD is quiet command reading  ie simplex/duplex flag 
    //     [s5.s4.s3.s2.s1.s0]  Pin Bit pattern or Count Value  eg [B_OE.B_TDI.B_NCS.B_NCE.B_TMS.B_TCK]
    
    // BitBang, used for < 8 bits 
    // [0.0.b5.b4.b3.b2.b1.b0 ] 	Write b5..b0 to up to 6 IO Pins, Simplex (no echo)
    // [0.1.b5.b4.b3.b2.b1.b0 ] 	Write b5..b0 to up to 6 IO Pins, Duplex, echo [0.0.0.0.0.0.B_ASDO.B_TDO] 
    // Byte, used for 8N bits 1..63 - Send > 64 bytes 00H, to ensure are back in BitBang/command mode
    // [1.0.n5.n4.n3.n2.n1.n0 ][.N.] Write n5..n0 to Counter, then Data.N. Simplex (no echo)
    // [1.1.n5.n4.n3.n2.n1.n0 ][.N.] Write n5..n0 to Counter, then Data.N. Duplex, echo TDO Byte group replies 
    //
    // Expansion / Upgrade: to allow PC end Sw to confirm Firmware attach, without impacting command simplicity
    // Using this, send some wiggles in BitBang.Duplex mode and check replies  IF b3..b0 found swapped in upper nibble == correct HW 
    // Suggest change of echo from [0.0.0.0.0.0.B_ASDO.B_TDO] to echo of  [ b3.b2.b1.b0.b5.b4.B_ASDO.B_TDO];
    // Simple read-back-shifted mapping of :
    // Bit group b3.b2.b1.b0 (B_NCS.B_NCE.B_TMS.B_TCK) echo Nibble swapped (<< 4)
    // Bit group b5.b4 (B_OE.B_TDI) echo >> 2
    // Otherwise, can externally connect TDI-TDO and B_OE to B_ASDO, to see if sent(b5.b4) echo in (b1.b0)
    
    // Find this comment http://www.openjtag.org/index.php?option=com_agora&task=topic&id=6&Itemid=57
    // Q:"When i plug USB-Blaster emulation to computer,USB-Blaster emulation must have device connected (fpga/cpld) to 
    // be recognised by quartus ? "
    // A: No, the Quartus II recognize the board as "USB-Blaster" only if the USB VID/PID are VID: 09FBh and PID: 6001h. 
    // Use the FTDI tool FTProg.exe to change the VID/PID 
    //
    //
    
    
    
Sign In or Register to comment.