[U-Boot-Users] [PATCH 4/8] Support LATTICE FPGA parts using JTAG programming. Support functions.

Pantelis Antoniou pantelis at embeddedalley.com
Sat Dec 2 23:16:16 CET 2006


Add support for Lattice FPGA parts programmed using JTAG.

---
Signed-off-by: Pantelis Antoniou <pantelis at embeddedalley.com>
---

 common/lattice_ivm_supp.c | 1426 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1426 insertions(+), 0 deletions(-)

diff --git a/common/lattice_ivm_supp.c b/common/lattice_ivm_supp.c
new file mode 100644
index 0000000..797dff1
--- /dev/null
+++ b/common/lattice_ivm_supp.c
@@ -0,0 +1,1426 @@
+/*
+ * (C) Copyright 2006 - Embedded Alley Solutions Inc.
+ * by Pantelis Antoniou, pantelis at embeddedalley.com
+ *
+ * Based on redboot's lattice_ivm_core.c
+ *
+ * This file was based on ASP8347DB's redboot sources
+ * with the same name. The file was not carrying any copyright
+ * markings while RedBoot is clearly GPL licensed.
+
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <malloc.h>
+#include <lattice.h>
+#include <lattice_ec.h>
+
+#include <lattice_ivm_core.h>
+#include <lattice_vmopcode.h>
+
+#if (CONFIG_FPGA & CFG_FPGA_LATTICE)
+
+/* Enable/Disable debug console messages */
+#ifdef FPGA_DEBUG
+#define	PRINTF(fmt,args...)	printf (fmt ,##args)
+#else
+#define	PRINTF(fmt,args...)
+#endif
+
+/*
+ * Returns a VME-encoded number, usually used to indicate the
+ * bit length of an SIR/SDR command.
+ */
+int ispvm_data_size(struct ispvm_desc *d)
+{
+	int i, j, val;
+
+	i = j = 0;
+	while ((val = ispvm_get(d)) != -1 && (val & 0x80)) {
+		j |= (val & 0x7F) << i;
+		i += 7;
+	}
+	if (val == -1)
+		return -1;
+	j |= (val & 0x7F) << i;
+	return j;
+}
+
+/* Processes the TDI/TDO/MASK/DMASK etc of an SIR/SDR command. */
+int ispvm_data_code(struct ispvm_desc *d)
+{
+	int ret, data;
+	int data_source = 0;	/* source file by default */
+
+	if (d->data_type & HEAP_IN)
+		data_source = 1;	/* source from memory */
+
+	/* Clear the data type register. */
+	d->data_type &= ~(MASK_DATA + TDI_DATA + TDO_DATA + DMASK_DATA);
+
+	/*
+	 * Iterate through SIR/SDR command and look
+	 * for TDI, TDO, MASK, etc.
+	 */
+	while ((data = ispvm_get(d)) >= 0) {
+
+		ret = ispvm_alloc(d, data, d->max_size);
+		if (ret != 0)
+			return VME_OUT_OF_MEMORY;
+
+		switch (data) {
+
+		case TDI:
+			/*
+			 * Store the maximum size of the TDI buffer.
+			 * Used to convert VME to HEX.
+			 */
+			if (d->data_size > d->TDI_size)
+				d->TDI_size = d->data_size;
+
+			/*
+			 * Updated data type register to indicate that TDI data
+			 * is currently being used. Process the data in the VME
+			 * file into the TDI buffer.
+			 */
+			d->data_type |= TDI_DATA;
+			ret = ispvm_data(d, d->in_TDI_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+			break;
+
+		case XTDO:
+			/*
+			 * Store the maximum size of the TDO buffer.
+			 * Used to convert VME to HEX.
+			 */
+			if (d->data_size > d->TDO_size)
+				d->TDO_size = d->data_size;
+
+			/*
+			 * Updated data type register to indicate that TDO data
+			 * is currently being used.
+			 */
+			d->data_type |= TDO_DATA;
+			break;
+
+		case TDO:
+			/*
+			 * Store the maximum size of the TDO buffer.
+			 * Used to convert VME to HEX.
+			 */
+			if (d->data_size > d->TDO_size)
+				d->TDO_size = d->data_size;
+
+			/*
+			 * Updated data type register to indicate that TDO data
+			 * is currently being used. Process the data in the VME
+			 * file into the TDO buffer.
+			 */
+			d->data_type |= TDO_DATA;
+			ret = ispvm_data(d, d->out_TDO_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+			break;
+
+		case MASK:
+			/*
+			 * Store the maximum size of the MASK buffer.
+			 * Used to convert VME to HEX.
+			 */
+			if (d->data_size > d->MASK_size)
+				d->MASK_size = d->data_size;
+
+			/*
+			 * Updated data type register to indicate that MASK data
+			 * is currently being used. Process the data in the VME
+			 * file into the TDO buffer.
+			 */
+			d->data_type |= MASK_DATA;
+			ret = ispvm_data(d, d->out_MASK_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+			break;
+
+		case DMASK:
+			/*
+			 * Store the maximum size of the DMASK buffer.
+			 * Used to convert VME to HEX.
+			 */
+			if (d->data_size > d->DMASK_size)
+				d->DMASK_size = d->data_size;
+
+			/*
+			 * Updated data type register to indicate that DMASK
+			 * data is currently being used. Process the data in
+			 * the VME file into the TDO buffer.
+			 */
+
+			d->data_type |= DMASK_DATA;
+			ret = ispvm_data(d, d->out_DMASK_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+			break;
+
+		case CONTINUE:
+			return 0;
+
+			/* Encountered invalid opcode. */
+		default:
+			return VME_INVALID_FILE;
+		}
+
+		if (data == TDI) {
+			/*
+			 * Left bit shift. Used when performing
+			 * algorithm looping.
+			 */
+			if (d->flow_control & SHIFTLEFT) {
+				ispvm_bit_shift(d, SHL, d->shift_value);
+				d->flow_control &= ~SHIFTLEFT;
+			}
+
+			/*
+			 * Right bit shift. Used when performing
+			 * algorithm looping.
+			 */
+			if (d->flow_control & SHIFTRIGHT) {
+				ispvm_bit_shift(d, SHR, d->shift_value);
+				d->flow_control &= ~SHIFTRIGHT;
+			}
+		}
+
+		if (data_source)
+			d->data_type |= HEAP_IN; /* restore data from memory */
+	}
+
+	if (data_source)	/* fetch data from heap memory upon return */
+		d->data_type |= HEAP_IN;
+
+	if (data < 0)
+		return VME_INVALID_FILE;	/* invalid opcode */
+
+	return 0;
+}
+
+/*
+ * Extract one row of data operand from the current data type opcode. Perform
+ * the decompression if necessary. Extra RAM is not required for the
+ * decompression process. The decompression scheme employed in this module
+ * is on row by row basis. The format of the data stream:
+ * [compression code][compressed data stream]
+ * 0x00    --No compression
+ * 0x01    --Compress by 0x00.
+ *           Example:
+ *           Original stream:   0x000000000000000000000001
+ *           Compressed stream: 0x01000901
+ *           Detail:            0x01 is the code, 0x00 is the key,
+ *                              0x09 is the count of 0x00 bytes,
+ *                              0x01 is the uncompressed byte.
+ * 0x02    --Compress by 0xFF.
+ *           Example:
+ *           Original stream:   0xFFFFFFFFFFFFFFFFFFFFFF01
+ *           Compressed stream: 0x02FF0901
+ *           Detail:            0x02 is the code, 0xFF is the key,
+ *                              0x09 is the count of 0xFF bytes,
+ *                              0x01 is the uncompressed byte.
+ * 0x03
+ * : :
+ * 0xFE   -- Compress by nibble blocks.
+ *           Example:
+ *           Original stream:   0x84210842108421084210
+ *           Compressed stream: 0x0584210
+ *           Detail:            0x05 is the code, means 5 nibbles block.
+ *                              0x84210 is the 5 nibble blocks.
+ *                              The whole row is 80 bits given by d->data_size
+ *                              The number of times the block repeat itself
+ *                              is found by d->data_size/(4*0x05) which is 4.
+ * 0xFF   -- Compress by the most frequently happen byte.
+ *           Example:
+ *           Original stream:   0x04020401030904040404
+ *           Compressed stream: 0xFF04(0,1,0x02,0,1,0x01,1,0x03,1,0x09,0,0,0)
+ *                          or: 0xFF044090181C240
+ *           Detail:            0xFF is the code, 0x04 is the key.
+ *                              a bit of 0 represent the key shall be put into
+ *                              the current bit position and a bit of 1
+ *                              represent copying the next of 8 bits of data
+ *                              in.
+ */
+
+int ispvm_data(struct ispvm_desc *d, u8 *ptr)
+{
+	int i, j, k, l, size, gotdata, FFcount, compress, data;
+	u8 compr_char, val;
+	int compression = 0;
+
+	gotdata = 0;
+	FFcount = 0;
+	compress = 0;
+	data = 0;
+	compr_char = 0xff;
+
+	/*convert number in bits to bytes */
+	if (d->data_size % 8 > 0)
+		size = d->data_size / 8 + 1;
+	else
+		size = d->data_size / 8;
+
+	/*
+	 * If there is compression, then check if compress by
+	 * key of 0x00 or 0xFF
+	 * or by other keys or by nibble blocks
+	 */
+	if (d->data_type & COMPRESS) {
+		compression = 1;
+
+		compress = ispvm_get(d);
+		if (compress == -1)
+			return VME_INVALID_FILE;
+
+		if (compress == VAR && (d->data_type & HEAP_IN)) {
+			gotdata = 1;
+			d->data_type &= ~HEAP_IN;
+			compress = ispvm_get(d);
+			if (compress == -1)
+				return VME_INVALID_FILE;
+		}
+
+		switch (compress) {
+
+			/* No compression */
+		case 0x00:
+			compression = 0;
+			break;
+
+			/* Compress by byte 0x00 */
+		case 0x01:
+			compr_char = 0x00;
+			break;
+
+			/* Compress by byte 0xFF */
+		case 0x02:
+			compr_char = 0xFF;
+			break;
+
+			/* Huffman encoding */
+		case 0xFF:
+			j = ispvm_get(d);
+			if (j == -1)
+				return VME_INVALID_FILE;
+
+			compr_char = j;
+
+			i = 8;
+			for (l = 0; l < size; l++) {
+				ptr[l] = 0x00;
+				if (i > 7) {
+					data = ispvm_get(d);
+					if (data == -1)
+						return VME_INVALID_FILE;
+					i = 0;
+				}
+				if ((data << i++) & 0x80) {
+					k = 8;
+				} else {
+					ptr[l] = compr_char;
+					k = 0;
+				}
+
+				for (j = 0; j < k; j++) {
+					if (i > 7) {
+						data = ispvm_get(d);
+						if (data == -1)
+							return VME_INVALID_FILE;
+						i = 0;
+					}
+					ptr[l] |= ((data << i++) & 0x80) >> j;
+				}
+			}
+			size = 0;
+			break;
+
+		default:
+			for (l = 0; l < size; l++)
+				ptr[l] = 0x00;
+
+			for (l = 0; l < compress; l++) {
+				if ((l % 2) == 0) {
+					data = ispvm_get(d);
+					if (data == -1)
+						return VME_INVALID_FILE;
+				}
+				for (i = 0; i < size * 2 / compress; i++) {
+					j = l + i * compress;
+					/* clear the nibble to zero first */
+					if (j % 2) {
+						if (l % 2)
+							val = data & 0x0F;
+						else
+							val = data >> 4;
+					} else {
+						if (l % 2)
+							val = data << 4;
+						else
+							val = data & 0xF0;
+					}
+
+					ptr[j / 2] |= val;
+				}
+			}
+			size = 0;
+			break;
+		}
+	}
+
+	FFcount = 0;
+
+	/* Decompress by byte 0x00 or 0xFF */
+	for (l = 0; l < size; l++) {
+		if (FFcount <= 0) {
+			data = ispvm_get(d);
+			if (data == -1)
+				return VME_INVALID_FILE;
+
+			if (data == VAR && (d->data_type & HEAP_IN) &&
+				!gotdata && !(d->data_type & COMPRESS)) {
+				gotdata = 1;
+				d->data_type &= ~HEAP_IN;
+				data = ispvm_get(d);
+				if (data == -1)
+					return VME_INVALID_FILE;
+			}
+			ptr[l] = data & 0xff;
+
+			/* decompression is on? set num. of 0xff/0x00 bytes */
+			if (compression && data == compr_char) {
+				FFcount = ispvm_data_size(d);
+				if (FFcount == -1)
+					return VME_INVALID_FILE;
+			}
+		} else {
+			FFcount--;	/* Use up the 0xFF chain first */
+			ptr[l] = compr_char & 0xff;
+		}
+	}
+
+	if (gotdata) {
+		d->data_type |= HEAP_IN;
+		gotdata = 0;
+	}
+
+	return 0;
+}
+
+/* Processes the SDR/XSDR/SIR commands. */
+int ispvm_shift(struct ispvm_desc *d, int code)
+{
+	int i, j;
+	int ret;
+
+	ret = 0;
+	j = ispvm_data_size(d);
+	if (j == -1)
+		return VME_INVALID_FILE;
+
+	d->data_size = j; 
+
+	/* clear the flags first */
+	d->data_type &= ~(SIR_DATA + EXPRESS + SDR_DATA);
+
+	switch (code) {
+
+	case SIR:
+		d->data_type |= SIR_DATA;
+		/*
+		 * 1/15/04 If performing cascading, then go
+		 * directly to SHIFTIR.
+		 * Else, go to IRPAUSE before going to SHIFTIR
+		 */
+		if (d->flow_control & CASCADE) {
+			ispvm_state_machine(d, SHIFTIR);
+			break;
+		}
+		ispvm_state_machine(d, IRPAUSE);
+		ispvm_state_machine(d, SHIFTIR);
+		if (d->head_IR > 0) {
+			ispvm_bypass(d, HIR, d->head_IR);
+			ispvm_clock(d);
+		}
+		break;
+
+	case XSDR:
+		/* mark simultaneous in and out */
+		d->data_type |= EXPRESS;
+		/* fall-through */
+
+	case SDR:
+		d->data_type |= SDR_DATA;
+
+		/*
+		 * 1/15/04 If already in SHIFTDR, then do not move
+		 * state or shift in header. This would imply that the
+		 * previously shifted frame was a cascaded frame.
+		 */
+		if (d->current_jtag_state == SHIFTDR)
+			break;
+
+		/*
+		 * 1/15/04 If performing cascading, then go directly
+		 * to SHIFTDR. Else, go to DRPAUSE before going to SHIFTDR
+		 */
+		if (d->flow_control & CASCADE) {
+			if (d->current_jtag_state == DRPAUSE) {
+				ispvm_state_machine(d, SHIFTDR);
+				/*
+				 * 1/15/04 If cascade flag has been set and
+				 * the current state is DRPAUSE, this implies
+				 * that the first cascaded frame is about to be
+				 * shifted in. The header must be shifted prior
+				 * to shifting the first cascaded frame.
+				 */
+				if (d->head_DR > 0) {
+					ispvm_bypass(d, HDR, d->head_DR);
+					ispvm_clock(d);
+				}
+			} else
+				ispvm_state_machine(d, SHIFTDR);
+		} else {
+			ispvm_state_machine(d, DRPAUSE);
+			ispvm_state_machine(d, SHIFTDR);
+			if (d->head_DR > 0) {
+				ispvm_bypass(d, HDR, d->head_DR);
+				ispvm_clock(d);
+			}
+		}
+		break;
+
+	default:
+		return VME_INVALID_FILE;
+	}
+
+	ret = ispvm_data_code(d);
+	if (ret != 0)
+		return VME_INVALID_FILE;
+
+	if (d->data_type & DMASK_DATA) {
+		ret = ispvm_read_and_save(d, d->data_size);
+		if (!ret) {
+			if (d->tail_DR > 0) {
+				ispvm_clock(d);
+				ispvm_bypass(d, TDR, d->tail_DR);
+			}
+			ispvm_state_machine(d, DRPAUSE);
+			ispvm_state_machine(d, SHIFTDR);
+			if (d->head_DR > 0) {
+				ispvm_bypass(d, HDR, d->head_DR);
+				ispvm_clock(d);
+			}
+			for (i = 0; i < d->data_size / 8 + 1; i++)
+				d->in_TDI_data[i] = d->out_TDO_data[i];
+			d->data_type &= ~(TDO_DATA + DMASK_DATA);
+			ret = ispvm_send(d, d->data_size);
+		}
+	} else if (d->data_type & TDO_DATA) {
+		ret = ispvm_read(d, d->data_size);
+		if (ret == -1 && d->vendor == XILINX) {
+			for (j = 0; j < 30; j++) {
+				ret = ispvm_read(d, d->data_size);
+				if (!ret)
+					break;
+
+				/* Always DRPAUSE */
+				ispvm_state_machine(d, DRPAUSE);
+
+				/* Bypass other devices when appropriate */
+				ispvm_bypass(d, TDR, d->tail_DR);
+				ispvm_state_machine(d, d->end_DR);
+				ispvm_state_machine(d, IDLE);
+				udelay(1000);
+			}
+		}
+	} else
+		ret = ispvm_send(d, d->data_size);	/*TDI only */
+
+	/*transfer the input data to the output buffer for the next verify */
+	if ((d->data_type & EXPRESS) ||
+			(code == SDR && d->out_TDO_data != NULL))
+		for (i = 0; i < d->data_size / 8 + 1; i++)
+			d->out_TDO_data[i] = d->in_TDI_data[i];
+
+	switch (code) {
+
+	case SIR:
+		/* 1/15/04 If not performing cascading, then shift ENDIR */
+		if ((d->flow_control & CASCADE))
+			break;
+
+		if (d->tail_IR > 0) {
+			ispvm_clock(d);
+			ispvm_bypass(d, TIR, d->tail_IR);
+		}
+		ispvm_state_machine(d, d->end_IR);
+		break;
+
+	case XSDR:
+	case SDR:
+		/* 1/15/04 If not performing cascading, then shift ENDDR */
+		if ((d->flow_control & CASCADE))
+			break;
+
+		if (d->tail_DR > 0) {
+			ispvm_clock(d);
+			ispvm_bypass(d, TDR, d->tail_DR);
+		}
+		ispvm_state_machine(d, d->end_DR);
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/* This routine is to extract Header and Trailer parameter for SIR and
+ * SDR operations.
+ *
+ * The Header and Trailer parameter are the pre-amble and post-amble bit
+ * stream need to be shifted into TDI or out of TDO of the devices. Mostly
+ * is for the purpose of bypassing the leading or trailing devices. ispVM
+ * supports only shifting data into TDI to bypass the devices.
+ *
+ * For a single device, the header and trailer parameters are all set to 0
+ * as default by ispVM. If it is for multiple devices, the header and trailer
+ * value will change as specified by the VME file.
+ */
+
+int ispvm_amble(struct ispvm_desc *d, int code)
+{
+	int j, ret;
+	int compress = 0;
+
+	j = ispvm_data_size(d);
+	if (j == -1)
+		return VME_INVALID_FILE;
+
+	d->data_size = j;
+
+	if (d->data_size) {
+		/*
+		 * Discard the TDI byte and set the compression bit in the
+		 * data type register to false if compression is set
+		 * because TDI data after HIR/HDR/TIR/TDR is not compressed.
+		 */
+		j = ispvm_get(d);
+		if (j == -1)
+			return VME_INVALID_FILE;
+
+		if (d->data_type & COMPRESS) {
+			d->data_type &= ~(COMPRESS);
+			compress = 1;
+		}
+	}
+
+	switch (code) {
+	case HIR:
+		/*
+		 * Store the maximum size of the HIR buffer.
+		 * Used to convert VME to HEX.
+		 */
+		if (d->data_size > d->HIR_size)
+			d->HIR_size = d->data_size;
+
+		/* Assign the HIR value and allocate memory. */
+		d->head_IR = d->data_size;
+		if (d->head_IR) {
+			ret = ispvm_alloc(d, HIR, d->head_IR);
+			if (ret != 0)
+				return VME_OUT_OF_MEMORY;
+			ret = ispvm_data(d, d->HIR_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+
+		}
+		break;
+
+	case TIR:
+		/*
+		 * Store the maximum size of the TIR buffer.
+		 * Used to convert VME to HEX.
+		 */
+		if (d->data_size > d->TIR_size)
+			d->TIR_size = d->data_size;
+
+		/* Assign the TIR value and allocate memory. */
+		d->tail_IR = d->data_size;
+		if (d->tail_IR) {
+			ret = ispvm_alloc(d, TIR, d->tail_IR);
+			if (ret != 0)
+				return VME_OUT_OF_MEMORY;
+			ret = ispvm_data(d, d->TIR_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+		}
+		break;
+
+	case HDR:
+		/*
+		 * Store the maximum size of the HDR buffer.
+		 * Used to convert VME to HEX.
+		 */
+		if (d->data_size > d->HDR_size)
+			d->HDR_size = d->data_size;
+
+		/* Assign the HDR value and allocate memory. */
+		d->head_DR = d->data_size;
+		if (d->head_DR) {
+			ret = ispvm_alloc(d, HDR, d->head_DR);
+			if (ret != 0)
+				return VME_OUT_OF_MEMORY;
+			ret = ispvm_data(d, d->HDR_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+		}
+		break;
+
+	case TDR:
+
+		/*
+		 * Store the maximum size of the TDR buffer.
+		 * Used to convert VME to HEX.
+		 */
+		if (d->data_size > d->TDR_size)
+			d->TDR_size = d->data_size;
+
+		/* Assign the TDR value and allocate memory. */
+		d->tail_DR = d->data_size;
+		if (d->tail_DR) {
+			ret = ispvm_alloc(d, TDR, d->tail_DR);
+			if (ret != 0)
+				return VME_OUT_OF_MEMORY;
+			ret = ispvm_data(d, d->TDR_data);
+			if (ret != 0)
+				return VME_INVALID_FILE;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	/* Re-enable compression if it was previously set. */
+	if (compress)
+		d->data_type |= COMPRESS;
+
+	if (d->data_size > 0 && ispvm_get(d) != CONTINUE)
+		return VME_INVALID_FILE; /* invalid opcode */
+
+	return 0;
+}
+
+/*
+ * Perform the function call upon by the REPEAT opcode.
+ * Memory is to be allocated to store the entire loop from REPEAT to ENDLOOP.
+ * After the loop is stored then execution begin. The REPEATLOOP flag is set
+ * on the d->flow_control register to indicate the repeat loop is in session
+ * and therefore fetch opcode from the memory instead of from the file.
+ */
+int ispvm_loop(struct ispvm_desc *d, int repeats)
+{
+	int i, j;
+	int ret = 0;
+
+	d->shift_value = 0;
+	for (i = 0; i < d->heap_repeat_size; i++) {
+		j = ispvm_get(d);
+		if (j == -1)
+			return VME_INVALID_FILE;
+		d->heap_memory[i] = j;
+	}
+
+	if (d->heap_memory[i - 1] != ENDLOOP)
+		return VME_INVALID_FILE;
+
+	d->flow_control |= REPEATLOOP;
+	d->data_type |= HEAP_IN;
+
+	for (i = 0; i < repeats; i++) {
+		d->heap_counter = 0;
+		ret = ispvm_code(d);
+		d->repeat_loops++;
+		if (ret < 0)
+			break;
+	}
+
+	d->data_type &= ~HEAP_IN;
+	d->flow_control &= ~REPEATLOOP;
+	return ret;
+}
+
+/*
+ * Shift the TDI stream left or right by the number of bits. The data in
+ * d->in_TDI_data is of the VME format, so the actual shifting is the reverse of
+ * IEEE 1532 or SVF format.
+ */
+int ispvm_bit_shift(struct ispvm_desc *d, int mode, u16 bits)
+{
+	u16 i, size, j;
+
+	if (d->data_size % 8 > 0)
+		size = d->data_size / 8 + 1;
+	else
+		size = d->data_size / 8;
+
+	switch (mode) {
+
+	case SHR:
+		for (i = 0; i < size; i++) {
+			if (d->in_TDI_data[i] == 0)
+				continue;
+			j = bits;
+			while (j > 0) {
+				d->in_TDI_data[i] <<= 1;
+				if (d->in_TDI_data[i] == 0) {
+					i--;
+					d->in_TDI_data[i] = 1;
+				}
+				j--;
+			}
+		}
+		break;
+
+	case SHL:
+		for (i = 0; i < size; i++) {
+			if (d->in_TDI_data[i] == 0)
+				continue;
+			j = bits;
+			while (j > 0) {
+				d->in_TDI_data[i] >>= 1;
+				if (d->in_TDI_data[i] == 0) {
+					i--;
+					d->in_TDI_data[i] = 8;
+				}
+				j--;
+			}
+		}
+		break;
+
+	default:
+		return VME_INVALID_FILE;
+	}
+
+	return 0;
+}
+
+/* Displays the SVF comments. */
+int ispvm_comment(struct ispvm_desc *d, int size)
+{
+	int i, j;
+
+	/* Print character to the terminal. */
+	printf("VM-Comment: ");
+	for (i = 0; i < size; i++) {
+		j = ispvm_get(d);
+		if (j == -1)
+			return VME_INVALID_FILE;
+		printf("%c", j);
+	}
+	printf("\n");
+
+	return 0;
+}
+
+/* Iterate the length of the header and discard it. */
+int ispvm_header(struct ispvm_desc *d, int size)
+{
+	int i, j;
+
+	for (i = 0; i < size; i++) {
+		j = ispvm_get(d);
+		if (j == -1)
+			return VME_INVALID_FILE;
+	}
+	return 0;
+}
+
+/* Process the intelligent programming loops. */
+int ispvm_lcount(struct ispvm_desc *d, int size)
+{
+	int i, j, k;
+	int ret = 0;
+	int repheap = 0;
+
+	j = ispvm_data_size(d);
+	if (j == -1)
+		return VME_INVALID_FILE;
+
+	d->intel_buffer_size = j;
+
+	/* Allocate memory for intel buffer. */
+	ret = ispvm_alloc(d, LHEAP, d->intel_buffer_size);
+	if (ret != 0)
+		return VME_OUT_OF_MEMORY;
+
+	/*
+	 * Store the maximum size of the intelligent buffer.
+	 * Used to convert VME to HEX.
+	 */
+	if (d->intel_buffer_size > d->LCOUNT_size)
+		d->LCOUNT_size = d->intel_buffer_size;
+
+	/* Copy intel data to the buffer */
+	for (i = 0; i < d->intel_buffer_size; i++) {
+		k = ispvm_get(d);
+		if (k == -1)
+			return VME_INVALID_FILE;
+		d->intel_buffer[i] = k;
+	}
+
+	/*
+	 * Set the data type register to get data from the
+	 * intelligent data buffer.
+	 */
+	d->data_type |= LHEAP_IN;
+
+	/*
+	 * If the HEAP_IN flag is set, temporarily unset the flag
+	 * so data will be retrieved from the status buffer.
+	 */
+	if (d->data_type & HEAP_IN) {
+		d->data_type &= ~HEAP_IN;
+		repheap = 1;
+	}
+
+	/* Iterate through the intelligent programming command. */
+	for (i = 0; i < size; i++) {
+		d->intel_data_idx = 0;
+
+		/*
+		 * Make recursive call to process the intelligent
+		 * programming commands.
+		 */
+		ret = ispvm_code(d);
+		if (ret >= 0)
+			break;	/* success? */
+	}
+
+	/*
+	 * If HEAP_IN flag was temporarily disabled,
+	 * re-enable it before exiting.
+	 */
+	if (repheap)
+		d->data_type |= HEAP_IN;
+
+	/*
+	 * Set the data type register to not get data from
+	 * the intelligent data buffer.
+	 */
+	d->data_type &= ~LHEAP_IN;
+	return ret;
+}
+
+/* Applies a single pulse to TCK. */
+void ispvm_clock(struct ispvm_desc *d)
+{
+	int idle;
+
+	idle = 1000 / d->frequency + !!(1000 % d->frequency);
+
+	/* NOTE: we assume that at least one usec will pass
+	 * until getting the port toggled
+	 */
+	idle--;
+
+	lattice_ec_write_port(d->cookie, LATTICE_JTAG_TCK, 0x01);
+	if (idle > 0)
+		udelay(idle);
+
+	lattice_ec_write_port(d->cookie, LATTICE_JTAG_TCK, 0x00);
+	if (idle > 0)
+		udelay(idle);
+}
+
+/*
+ * This procedure takes care of the HIR, HDR, TIR, TDR for the
+ * purpose of putting the other devices into Bypass mode. The
+ * current state is checked to find out if it is at DRPAUSE or
+ * IRPAUSE. If it is at DRPAUSE, perform bypass register scan.
+ * If it is at IRPAUSE, scan into instruction registers the bypass
+ * instruction.
+ */
+void ispvm_bypass(struct ispvm_desc *d, int scan_type, u16 bits)
+{
+	int i, j, bit;
+	u8 val = 0;
+	u8 *pcSource = 0;
+
+	if (bits <= 0)
+		return;
+
+	switch (scan_type) {
+	case HIR:
+		pcSource = d->HIR_data;
+		break;
+	case TIR:
+		pcSource = d->TIR_data;
+		break;
+	case HDR:
+		pcSource = d->HDR_data;
+		break;
+	case TDR:
+		pcSource = d->TDR_data;
+		break;
+	default:
+		break;
+	}
+
+	j = 0;
+	for (i = 0; i < bits - 1; i++) {
+		/* Scan instruction or bypass register */
+		if (i % 8 == 0)
+			val = pcSource[j++];
+		bit = !!((val << (i % 8)) & 0x80);
+		lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI, bit);
+		ispvm_clock(d);
+	}
+
+	if ((i % 8) == 0)
+		val = pcSource[j++];
+
+	bit = !!((val << (i % 8)) & 0x80);
+	lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI, bit);
+}
+
+/*
+ * This procedure steps all devices in the daisy chain from a given
+ * JTAG state to the next desirable state. If the next state is TLR,
+ * the JTAG state machine is brute forced into TLR by driving TMS
+ * high and pulse TCK 6 times.
+ */
+void ispvm_state_machine(struct ispvm_desc *d, int next)
+{
+	/*
+	 * JTAG state machine transition trajectory table
+	 */
+	static const struct {
+		u8 cur_state;		/* From this state    */
+		u8 next_state;		/* Step to this state */
+		u8 pattern;		/* Tragectory of TMS  */
+		u8 pulses;		/* Number of steps    */
+	} jtag_transitions[24] = {
+		/* Transitions from RESET */
+		{ RESET,     RESET,     0xFC, 6 },
+		{ RESET,     IDLE,      0x00, 1 },
+		{ RESET,     DRPAUSE,   0x50, 5 },
+		{ RESET,     IRPAUSE,   0x68, 6 },
+
+		/* Transitions from IDLE */
+		{ IDLE,      RESET,     0xE0, 3 },
+		{ IDLE,      DRPAUSE,   0xA0, 4 },
+		{ IDLE,      IRPAUSE,   0xD0, 5 },
+
+		/* Transitions from DRPAUSE */
+		{ DRPAUSE,   RESET,     0xF8, 5 },
+		{ DRPAUSE,   IDLE,      0xC0, 3 },
+		{ DRPAUSE,   IRPAUSE,   0xF4, 7 },
+
+		/* Transitions from IRPAUSE */
+		{ IRPAUSE,   RESET,     0xF8, 5 },
+		{ IRPAUSE,   IDLE,      0xC0, 3 },
+		{ IRPAUSE,   DRPAUSE,   0xE8, 6 },
+
+		/* Extra transitions using SHIFTDR */
+		{ DRPAUSE,   SHIFTDR,   0x80, 2 },
+		{ IRPAUSE,   SHIFTDR,   0xE0, 5 },
+		{ SHIFTDR,   DRPAUSE,   0x80, 2 },
+		{ SHIFTDR,   IDLE,      0xC0, 3 },
+
+		/* Extra transitions using SHIFTIR */
+		{ IRPAUSE,   SHIFTIR,   0x80, 2 }, 
+		{ SHIFTIR,   IRPAUSE,   0x80, 2 },
+		{ SHIFTIR,   IDLE,      0xC0, 3 },
+
+		/* 11/15/05 Nguyen changed to support DRCAPTURE */
+		{ DRPAUSE,   DRCAPTURE, 0xE0, 4 },
+		{ DRCAPTURE, DRPAUSE,   0x80, 2 },
+		{ IDLE,      DRCAPTURE, 0x80, 2 },
+		{ IRPAUSE,   DRCAPTURE, 0xE0, 4}
+	};
+
+	int i, j, maxstate;
+
+	/* already there? */
+	if (d->current_jtag_state == next && next != RESET)
+		return;
+
+	maxstate = sizeof(jtag_transitions) / sizeof(jtag_transitions[0]);
+	for (j = 0; j < maxstate; j++)
+		if (d->current_jtag_state == jtag_transitions[j].cur_state &&
+			next == jtag_transitions[j].next_state)
+			break;
+
+	d->current_jtag_state = next;
+	for (i = 0; i < jtag_transitions[j].pulses; i++) {
+		lattice_ec_write_port(d->cookie, LATTICE_JTAG_TMS,
+				(jtag_transitions[j].pattern << i) & 0x80);
+		ispvm_clock(d);
+	}
+
+	lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI, 0);
+	lattice_ec_write_port(d->cookie, LATTICE_JTAG_TMS, 0);
+}
+
+/*
+ * Send the TDI data stream to devices. The data stream can be
+ * instructions or data.
+ */
+int ispvm_send(struct ispvm_desc *d, u16 size)
+{
+	int i, j, bit;
+	u8 val = 0;
+
+	for (i = 0, j = 0; i < size - 1; i++) {
+		if ((i % 8) == 0)
+			val = d->in_TDI_data[j++];
+		bit = !!((val << (i % 8)) & 0x80);
+		lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI, bit);
+		ispvm_clock(d);
+	}
+
+	/* Take care of the last bit */
+	if ((i % 8) == 0)
+		val = d->in_TDI_data[j];
+
+	bit = !!((val << (i % 8)) & 0x80);
+	lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI, bit);
+
+	/* 1/15/04 Clock in last bit for the first n-1 cascaded frames */
+	if (d->flow_control & CASCADE)
+		ispvm_clock(d);
+
+	return 0;
+}
+
+/* Read the data stream from devices and verify. */
+int ispvm_read(struct ispvm_desc *d, u16 size)
+{
+	int i, j, k, bit, lasti, errors, dflg;
+	u8 data, mask, indata, display;
+
+	errors = 0;
+	lasti = size - 1;
+	j = 0;
+	k = 0;
+	dflg = 1;
+	data = 0;
+	mask = 0;
+	indata = 0;
+	display = 0;
+
+	/*
+	 * If mask is not all zeros, then set the display flag to 0x00,
+	 * otherwise it shall be set to 0x01 to indicate that data read from
+	 * the device shall be displayed. If VME_DEBUG is defined, always
+	 * display data.
+	 */
+	for (i = 0; i < (size + 7) / 8; i++) {
+		if (d->data_type & MASK_DATA) {
+			if (d->out_MASK_data[i] != 0x00) {
+				dflg = 0;
+				break;
+			}
+		} else {
+			dflg = 0x00;
+			break;
+		}
+	}
+
+	/* Begin shifting data in and out of the device. */
+	for (i = 0; i < size; i++) {
+		if (k == 0) {
+			/* Grab byte from TDO buffer. */
+			if (d->data_type & TDO_DATA)
+				data = d->out_TDO_data[j];
+
+			/* Grab byte from MASK buffer. */
+			if (d->data_type & MASK_DATA)
+				mask = d->out_MASK_data[j];
+			else
+				mask = 0xFF;
+
+			/* Grab byte from TDI buffer. */
+			if (d->data_type & TDI_DATA)
+				indata = d->in_TDI_data[j];
+
+			j++;
+		}
+
+		bit = lattice_ec_read_port(d->cookie);
+
+		if (dflg) {
+			display <<= 1;
+			display |= bit;
+		}
+
+		/* Check if data read from port matches with expected TDO. */
+		if (d->data_type & TDO_DATA) {
+			if ((mask << k) & 0x80) {
+				if (bit != !!((data << k) & 0x80)) {
+					errors++;
+					dflg = 1;
+				}
+			}
+		}
+
+		/* Write TDI data to the port. */
+		lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI,
+				(((indata << k) & 0x80) ? 0x01 : 0x00));
+
+		/* Clock data out from the data shift register. */
+		if (i < lasti || (d->flow_control & CASCADE))
+			ispvm_clock(d);
+
+		k++;
+		if (k >= 8) {
+
+			if (dflg) {
+				/*
+				 * Store displayed data in the TDO buffer.
+				 * By reusing the TDO buffer to store displayed
+				 * data, there is no need to allocate a buffer
+				 * simply to hold display data. This will not
+				 * cause any false verification errors because
+				 * the true TDO byte has already been consumed.
+				 */
+				d->out_TDO_data[j - 1] = display;
+				display = 0;
+			}
+			k = 0;
+		}
+	}
+
+	if (dflg) {
+		/* Display data read from the device. */
+		printf("\nDisplay Data: 0x");
+
+		for (i = ((size + 7) / 8); i > 0; i--) {
+			mask = d->out_TDO_data[i - 1];
+			data = 0;
+
+			/* Flip mask and store it in data. */
+			for (j = 0; j < 8; j++) {
+				data <<= 1;
+				if (mask & 0x01)
+					data |= 0x01;
+				mask >>= 1;
+			}
+
+			printf(" %02X", ((unsigned int)data) & 0xff);
+		}
+		printf("\n\n");
+	}
+
+	if (errors > 0) {
+		if (d->flow_control & VERIFYUES) {
+			printf("USERCODE verification failed."
+				"  Continue programming......\n\n");
+			d->flow_control &= ~VERIFYUES;
+			return 0;
+		}
+
+		return VME_VERIFICATION_FAILURE;
+
+	}
+
+	if (d->flow_control & VERIFYUES) {
+		printf("USERCODE verification passed."
+				"  Programming aborted. \n\n");
+		d->flow_control &= ~VERIFYUES;
+		return 1;
+	}
+	return 0;
+}
+
+/* Support dynamic I/O. */
+int ispvm_read_and_save(struct ispvm_desc *d, u16 size)
+{
+	int i, j, k, l, m, lasti, bit, outbit;
+	u8 data, dmask, indata, outdata;
+
+	lasti = size - 1;
+	j = 0;
+	k = 0;
+	l = 0;
+	m = 0;
+	data = 0;
+	dmask = 0;
+	indata = 0;
+
+	/* Iterate through the data bits. */
+	for (i = 0; i < size; i++) {
+		if (k == 0) {
+			/* Grab byte from DMASK buffer. */
+			if (d->data_type & DMASK_DATA)
+				dmask = d->out_DMASK_data[j];
+			else
+				dmask = 0x00;
+
+			/* Grab byte from TDI buffer. */
+			if (d->data_type & TDI_DATA)
+				indata = d->in_TDI_data[j];
+
+			j++;
+		}
+
+		bit = lattice_ec_read_port(d->cookie);
+		data = !!((indata << k) & 0x80);
+
+		/* Initialize the byte to be zero. */
+		if (l % 8 == 0)
+			d->out_TDO_data[l / 8] = 0x00;
+
+		/*
+		 * Use TDI, DMASK, and device TDO to create new TDI (actually
+		 * stored in d->out_TDO_data).
+		 */
+		if ((dmask << k) & 0x80) {
+
+			if (d->LVDS_list) {
+				for (m = 0; m < d->LVDS_pair_count; m++) {
+					if (d->LVDS_list[m].negative_idx
+							== i) {
+						d->LVDS_list[m].update = 0x01;
+						break;
+					}
+				}
+			}
+
+			/* DMASK bit is 1, use TDI. */
+			outbit = data & 1;
+		} else
+			outbit = bit;
+
+		/* DMASK bit is 0, use device TDO. */
+		outdata = outbit << (7 - l % 8);
+		d->out_TDO_data[l / 8] |= outdata;
+
+		/* Shift in TDI in order to get TDO out. */
+		l++;
+		lattice_ec_write_port(d->cookie, LATTICE_JTAG_TDI, data);
+		if (i < lasti)
+			ispvm_clock(d);
+
+		k++;
+		if (k >= 8)
+			k = 0;
+	}
+
+	/*
+	 * If d->LVDS_list exists and pairs need updating, then update
+	 * the negative-pair to receive the flipped positive-pair value.
+	 */
+	if (!d->LVDS_list)
+		return 0;
+
+	for (m = 0; m < d->LVDS_pair_count; m++)
+		if (d->LVDS_list[m].update)
+			break;
+
+	/* update not found? */
+	if (m >= d->LVDS_pair_count)
+		return 0;
+
+	/* Read the positive value and flip it. */
+	outdata = d->out_TDO_data[d->LVDS_list[m].positive_idx / 8];
+	data = !((outdata << (d->LVDS_list[m].positive_idx % 8)) & 0x80);
+
+	/* Get the byte that needs modification. */
+	indata = d->out_TDO_data[d->LVDS_list[m].negative_idx / 8];
+
+	if (data) {
+		/* Copy over the current byte and set the negative bit to 1 */
+		data = 0x00;
+		for (i = 7; i >= 0; i--) {
+			data <<= 1;
+			if (7 - (d->LVDS_list[m].negative_idx % 8) == i)
+				/* Set negative bit to 1. */
+				data |= 0x01;
+			else if (indata & 0x80)
+				data |= 0x01;
+
+			indata <<= 1;
+		}
+
+
+	} else {
+		/* Copy over the current byte and set the negative bit to 0. */
+		data = 0x00;
+		for (i = 7; i >= 0; i--) {
+			data <<= 1;
+			if (7 - (d->LVDS_list[m].negative_idx % 8) == i)
+				/* Set negative bit to 0. */
+				data |= 0x00;
+			else if (indata & 0x80)
+				data |= 0x01;
+
+			indata <<= 1;
+		}
+	}
+
+	/* Store the modified byte. */
+	d->out_TDO_data[d->LVDS_list[m].negative_idx / 8] = data;
+
+	return 0;
+}
+
+int ispvm_process_lvds(struct ispvm_desc *d, int count)
+{
+	int i, j;
+	/* LVDSPair * pLVDSPair = NULL; */
+
+	/* Allocate memory to hold LVDS pairs. */
+	j = ispvm_alloc(d, LVDS, count);
+	if (j != 0)
+		return VME_OUT_OF_MEMORY;
+	d->LVDS_pair_count = count;
+
+	/* Iterate through each given LVDS pair. */
+	for (i = 0; i < count; i++) {
+		/*
+		 * Assign the positive and negative indices
+		 * of the LVDS pair.
+		 */
+		j = ispvm_data_size(d);
+		if (j == -1)
+			return VME_INVALID_FILE;
+
+		d->LVDS_list[i].positive_idx = j;
+
+		j = ispvm_data_size(d);
+		if (j == -1)
+			return VME_INVALID_FILE;
+		d->LVDS_list[i].negative_idx = j;
+	}
+
+	return 0;
+}
+
+#endif




More information about the U-Boot mailing list