
/*--------------------------------------------------------------------
 * 	Xilinx Virtex and Spartan JTAG programmer, based on
  	reference doc: XAPP 139 V1.6
	Table 7

	Rene van Leuken, April 2004

--------------------------------------------------------------------*/

/*--------------------------------------------------------------------

  ljp.c: Linux JTAG Xilinx Programmer - version 0.1

  Programs Xilinx FPGAs in JTAG mode through a parallel port,
  using a Xilinx Parallel Cable III/IV compatible interface by default.
  Input bitstream files must be in .bit (binary) format.  The parallel
  port is used only in 'compatible' mode, so any old hardware will
  do.  Requires a Linux kernel with ppdev/parport drivers, i.e. 2.4+
  or 2.2.17 with the ppdev patch; for more information see
  http://people.redhat.com/twaugh/parport/.

  You must have r/w permissions for the parport device you want to
  use.  Typical use:
    $ ljp bitstream.bit /dev/parport0
  or:
    $ gunzip < bitstream.bit.gz | ljp - /dev/parport0

  To compile this code use something like: gcc -O2 -o ljp ljp.c

  Rudolf Usselmann	ASICS World Services	rudi(at)asics(dot)ws

  Based on original work by:
  Reinoud <reinoud@remove.et.tudelft.nl>

  --------------------------------------------------------------------

  (c) 2003/2004 Rudolf Usselmann
  (c) 2001 Reinoud Lamberts

  This program 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.

  This program 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; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
  USA

--------------------------------------------------------------------*/


#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/ppdev.h>
#include <linux/parport.h>


/*--- interface configuration ---*/

/* Configure the interface/cable with the defines below.  Xilinx
   parallel cable compatible is the default.  If your interface
   doesn't use a signal, simply make the corresponding mask all zero.

   Note that the mask defines below are named after FPGA signals they
   control, and not after names used in the Xilinx parallel cable
   schematic.  This is because the Xilinx cable is not the only
   target, and the names it uses are confusing. */

// select data or control output lines: PPWDATA or PPWCONTROL 
#define O_WRITECMD PPWDATA


// output signal masks 

#define O_ENABLE 0x10		// Enable TDI, TCK and TSM outputs
#define O_START  0x10		// Pulses TDO (DONE Input to Par. Port) (UNUSED)

#define O_TDI_0	0x00		// TDI Low
#define O_TDI_1	0x01		// TDI High

#define O_TCK_0	0x00		// TCK Low
#define	O_TCK_1	0x02		// TCK High

#define O_TMS_0	0x00		// TMS Low
#define O_TMS_1	0x04		// TMS High

// input signal masks; for *_OK give masked expected OK value 
#define I_SENSE   	 PARPORT_STATUS_ERROR
#define I_SENSE_OK	 I_SENSE
#define I_DONE   	  PARPORT_STATUS_SELECT
#define I_DONE_OK 	 I_DONE

// JTAG Commands
#define CMD_CFG		0x05
#define CMD_JSTART	0x0c
#define CMD_IDCODE	0x09

static int d;			// descriptor for parport device 
unsigned char byte;

// --- print error and clean up if needed ---

void
err (int d, char *s)
{
  unsigned char o;
  perror (s);

  if (d != -1)
    {
      o = 0x00;
      ioctl (d, O_WRITECMD, &o);
      ioctl (d, PPRELEASE);
    }
  exit (1);
}

// Read TDO pin
unsigned char
read_tdo ()
{
  unsigned char stat;		/* status (input) data from parport */
  if (ioctl (d, PPRSTATUS, &stat))
    err (d, "read status parport");
  if ((stat & I_DONE) == I_DONE_OK)
    return (1);
  else
    return (0);
}

// Read Vcc sense pin
unsigned char
read_vcc ()
{
  unsigned char stat;		/* status (input) data from parport */
  if (ioctl (d, PPRSTATUS, &stat))
    err (d, "read status parport");
  if ((stat & I_SENSE) == I_SENSE_OK)
    return (1);
  else
    return (0);
}

// Send a single bit to the device (1 clock cycle). 'val' is the
// value for TDI and tms is the value for the TMS pin.
void
send_bit (unsigned char val, unsigned char tms)
{
  unsigned char out;
  out = O_TCK_0 | O_ENABLE;
  out |= (tms == 0) ? O_TMS_0 : O_TMS_1;
  out |= (val == 0) ? O_TDI_0 : O_TDI_1;
  ioctl (d, O_WRITECMD, &out);

  out = O_TCK_1 | O_ENABLE;
  out |= (tms == 0) ? O_TMS_0 : O_TMS_1;
  out |= (val == 0) ? O_TDI_0 : O_TDI_1;
  ioctl (d, O_WRITECMD, &out);
}

// Send an enitre byte to the device. If last is '1', TMS will
// be high for the last bit transmitteda (e.g. force trasnition
// to EXIT1 state).
void
send_data_byte (unsigned char val, unsigned char last)
{
  int n;
  for (n = 7; n >= 0; n--)
    {
      send_bit ((val >> n) & 0x01, (n == 0 & last));
    }
}

// Identical to send_data_byte, but also reads TDO. Created seperate
// subroutines for write only and write & read mainly for speed-up
// reasons.
void
read_data_byte (unsigned char val, unsigned char last)
{
  int n;
  unsigned char bit;
  byte = 0;
  for (n = 7; n >= 0; n--)
    {
      send_bit ((val >> n) & 0x01, (n == 0 & last));
      bit = read_tdo ();
      byte |= bit << (7 - n);
    }
}

void
decode_id (int id)
{
  int manuf, size, family, rev;

  if ((id & 0x01) != 1)
    {
      printf ("Warning: Bit 0 (LSB) of devcice id is not '1' !\n");
    }

  manuf = (id >> 01) & 0x07ff;
  size = (id >> 12) & 0x01ff;
  family = (id >> 21) & 0x007f;
  rev = (id >> 28) & 0x000f;

  printf ("Device ID: %x\n", id);
  printf ("Manuf: %x, Part Size: %x, Family Code: %x, Revision: %0d\n", manuf,
	  size, family, rev);

  if (manuf != 0x49)
    {
      printf ("I can only program Xilinx Virtex and Spartan devices\n");
    }
}

int
main (int argc, char *argv[])
{
  char *rbname;			/* rbt file name */
  char *ppname;			/* parport device file name */

  FILE *f;			/* rbt file ptr */
  char c, next;			/* char from f */
  unsigned char uc;
  char *mem;
  char mem2[256];

  unsigned char o;		/* output data for parport */
  unsigned char stat;		/* status (input) data from parport */
  long bitcount;		/* bits transferred */
  int n, x, len, len2, token;
  int devid;

// ====================================================
// ===== get arguments

  if (argc != 3)
    {
      fprintf (stderr,
	       "usage: %s <BIT file, or - for stdin> <parport device>\n",
	       argv[0]);
      exit (2);
    }

  rbname = argv[1];
  ppname = argv[2];

// ====================================================
// ===== get input file

  if (strcmp (rbname, "-"))
    f = fopen (rbname, "r");
  else
    f = stdin;
  if (f == NULL)
    err (-1, "open bitstream file");


// ====================================================
// =====        Parse binary Header             PLEASE REWRITE THIS !

  uc = getc (f);
  len = uc << 8;
  uc = getc (f);
  len += uc;
  for (x = 0; x < len; x++)
    c = getc (f);

  uc = getc (f);
  len = uc << 8;
  uc = getc (f);
  len += uc;
  //printf("Length: %0d\n",len);

  uc = getc (f);
  token = uc;
  //printf("Token: %x\n",token);

  uc = getc (f);
  len = uc << 8;
  uc = getc (f);
  len += uc;
  //printf("Length: %0d\n",len);

  printf ("Design Name: ");

  for (x = 0; x < len; x++)
    {
      uc = getc (f);
      printf ("%c", uc);
    }
  printf ("\n");

  uc = getc (f);
  token = uc;
  //printf("Token: %x\n",token);

  uc = getc (f);
  len = uc << 8;
  uc = getc (f);
  len += uc;
  //printf("Length: %0d\n",len);

  printf ("Device: ");

  for (x = 0; x < len; x++)
    {
      uc = getc (f);
      printf ("%c", uc);
    }
  printf ("\n");


  uc = getc (f);
  token = uc;
  //printf("Token: %x\n",token);

  uc = getc (f);
  len = uc << 8;
  uc = getc (f);
  len += uc;
  //printf("Length: %0d\n",len);

  printf ("Date: ");

  for (x = 0; x < len; x++)
    {
      uc = getc (f);
      printf ("%c", uc);
    }
  printf ("\n");


  uc = getc (f);
  token = uc;
  //printf("Token: %x\n",token);

  uc = getc (f);
  len = uc << 8;
  uc = getc (f);
  len += uc;
  //printf("Length: %0d\n",len);

  printf ("Time: ");

  for (x = 0; x < len; x++)
    {
      uc = getc (f);
      printf ("%c", uc);
    }
  printf ("\n");

  uc = getc (f);
  token = uc;
  //printf("Token: %x\n",token);

  uc = getc (f);
  len = uc << 24;
  uc = getc (f);
  len += uc << 16;
  uc = getc (f);
  len += uc << 8;
  uc = getc (f);
  len += uc;
  printf ("Bitstream Length: %0d bits\n", len * 8);


// ====================================================
// ===== get parport

  d = open (ppname, O_RDWR);
  if (d == -1)
    err (-1, "open parport");
  if (ioctl (d, PPCLAIM))
    err (-1, "claim parport");

// ====================================================
// ===== check power 
  if (!read_vcc ())
    {
      fprintf (stderr, "ERROR: no power on interface\n");
      o = 0x00;			// Turn off driver
      if (ioctl (d, O_WRITECMD, &o))
	err (d, "write parport");
      if (ioctl (d, PPRELEASE))
	err (-1, "release parport");
      exit (-1);
    }

// ====================================================
// ##########  Start of JTAG Programming sequence

// ====================================================
// ##########  RESET Test Logic

  for (n = 0; n < 6; n++)
    send_bit (0, 1);		// Test Logic Reset  // 1
//       tdi tms
  send_bit (0, 0);		// 2

// State = IDLE

  send_bit (0, 1);		// 3
  send_bit (0, 1);		// 3
  send_bit (0, 0);		// 4
  send_bit (0, 0);		// 4

// ====================================================
// ##########  IDCODE Instruction
  send_bit (1, 0);		// 
  send_bit (0, 0);		// 
  send_bit (0, 0);		// 
  send_bit (1, 0);		// 
  send_bit (0, 1);		// 


  send_bit (0, 1);		//
  send_bit (0, 1);		//

  send_bit (0, 0);		//
  send_bit (0, 0);		//

  read_data_byte (0x00, 0);
  devid = byte;

  read_data_byte (0x00, 0);
  devid += byte << 8;

  read_data_byte (0x00, 0);
  devid += byte << 16;

  read_data_byte (0x00, 1);
  devid += byte << 24;

  decode_id (devid);

  send_bit (0, 1);		//

  send_bit (0, 1);		//
  send_bit (0, 1);		//
  send_bit (0, 0);		//
  send_bit (0, 0);		//


// END IDCODE



// ====================================================
// ##########  CONFIGURE
// CFG_IN 0101
  send_bit (1, 0);		// 5
  send_bit (0, 0);		// 5
  send_bit (1, 0);		// 5
  send_bit (0, 0);		// 5

  send_bit (0, 1);		// 6

  send_bit (0, 1);		// 7
  send_bit (0, 1);		// 7
  send_bit (0, 0);		// 8
  send_bit (0, 0);		// 8

// ====================================================
// SEND DATA

  bitcount = 0;
  fprintf (stderr, "\nProgramming ... ");

  for (n = 0; n < len; n++)
    {
      uc = getc (f);
      send_data_byte (uc, n == (len - 1));
      bitcount += 8;
      if ((bitcount % (256 * 1024)) == 0)
	putc ('*', stderr);
    }

  fprintf (stderr, "\nProgrammed %0d bits", bitcount);

  send_bit (0, 1);		// 11
  send_bit (0, 1);		// 12
  send_bit (0, 1);		// 12
  send_bit (0, 0);		// 13
  send_bit (0, 0);		// 13

// ====================================================
// ##########  JSTART 1100

  send_bit (0, 0);		// 14
  send_bit (0, 0);		// 14
  send_bit (1, 0);		// 14
  send_bit (1, 0);		// 14

  send_bit (0, 1);		// 15
  send_bit (0, 1);		// 16
  send_bit (0, 1);		// 16

  for (n = 0; n < 17; n++)
    send_bit (0, 0);		// Clock-in RUN-TEST sequence

  send_bit (0, 1);		// 18
  send_bit (0, 1);		// 18
  send_bit (0, 0);		// 19
  send_bit (0, 0);		// 19

// DONE !
// ====================================================

  printf ("\n");

  o = 0x00;			// Turn off driver
  if (ioctl (d, O_WRITECMD, &o))
    err (d, "write parport");
  if (ioctl (d, PPRELEASE))
    err (-1, "release parport");
  return 0;
}

