[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