
Add support for Lattice FPGA parts programmed using JTAG.
--- Signed-off-by: Pantelis Antoniou pantelis@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@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