summaryrefslogtreecommitdiff
path: root/drivers/fpga/lattice.c
diff options
context:
space:
mode:
authorStefano Babic <sbabic@denx.de>2010-06-29 11:47:48 +0200
committerStefano Babic <sbabic@denx.de>2010-10-13 18:09:31 +0200
commit3b8ac464f25581a7d22b303a9154c05e0c7f40a8 (patch)
tree47f8c1789a5667ef0d31daed228b9b70386138ed /drivers/fpga/lattice.c
parentf8f8acd7249d224ed5f9704a5738c469b137bc8e (diff)
FPGA: add support for downloading Lattice bitstream
The patch adds support to load a Lattice's bitstream image (called VME file) into a Lattice FPGA. The code containing the state machine delivered as part of Lattice's ispVMtools is integrated. The FPGA is programmed using the JTAG interface. The board maintainer must provide accessors to drive the JTAG signals TCK, TMS, TDI and to get the value of the input signal TDO. Signed-off-by: Stefano Babic <sbabic@denx.de>
Diffstat (limited to 'drivers/fpga/lattice.c')
-rw-r--r--drivers/fpga/lattice.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/drivers/fpga/lattice.c b/drivers/fpga/lattice.c
new file mode 100644
index 0000000000..a0e7823102
--- /dev/null
+++ b/drivers/fpga/lattice.c
@@ -0,0 +1,399 @@
+/*
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * (C) Copyright 2002
+ * Rich Ireland, Enterasys Networks, rireland@enterasys.com.
+ *
+ * ispVM functions adapted from Lattice's ispmVMEmbedded code:
+ * Copyright 2009 Lattice Semiconductor Corp.
+ *
+ * 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 <fpga.h>
+#include <lattice.h>
+
+static lattice_board_specific_func *pfns;
+static char *fpga_image;
+static unsigned long read_bytes;
+static unsigned long bufsize;
+static unsigned short expectedCRC;
+
+/*
+ * External variables and functions declared in ivm_core.c module.
+ */
+extern unsigned short g_usCalculatedCRC;
+extern unsigned short g_usDataType;
+extern unsigned char *g_pucIntelBuffer;
+extern unsigned char *g_pucHeapMemory;
+extern unsigned short g_iHeapCounter;
+extern unsigned short g_iHEAPSize;
+extern unsigned short g_usIntelDataIndex;
+extern unsigned short g_usIntelBufferSize;
+extern char *const g_szSupportedVersions[];
+
+
+/*
+ * ispVMDelay
+ *
+ * Users must implement a delay to observe a_usTimeDelay, where
+ * bit 15 of the a_usTimeDelay defines the unit.
+ * 1 = milliseconds
+ * 0 = microseconds
+ * Example:
+ * a_usTimeDelay = 0x0001 = 1 microsecond delay.
+ * a_usTimeDelay = 0x8001 = 1 millisecond delay.
+ *
+ * This subroutine is called upon to provide a delay from 1 millisecond to a few
+ * hundreds milliseconds each time.
+ * It is understood that due to a_usTimeDelay is defined as unsigned short, a 16
+ * bits integer, this function is restricted to produce a delay to 64000
+ * micro-seconds or 32000 milli-second maximum. The VME file will never pass on
+ * to this function a delay time > those maximum number. If it needs more than
+ * those maximum, the VME file will launch the delay function several times to
+ * realize a larger delay time cummulatively.
+ * It is perfectly alright to provide a longer delay than required. It is not
+ * acceptable if the delay is shorter.
+ */
+void ispVMDelay(unsigned short delay)
+{
+ if (delay & 0x8000)
+ delay = (delay & ~0x8000) * 1000;
+ udelay(delay);
+}
+
+void writePort(unsigned char a_ucPins, unsigned char a_ucValue)
+{
+ a_ucValue = a_ucValue ? 1 : 0;
+
+ switch (a_ucPins) {
+ case g_ucPinTDI:
+ pfns->jtag_set_tdi(a_ucValue);
+ break;
+ case g_ucPinTCK:
+ pfns->jtag_set_tck(a_ucValue);
+ break;
+ case g_ucPinTMS:
+ pfns->jtag_set_tms(a_ucValue);
+ break;
+ default:
+ printf("%s: requested unknown pin\n", __func__);
+ }
+}
+
+unsigned char readPort(void)
+{
+ return pfns->jtag_get_tdo();
+}
+
+void sclock(void)
+{
+ writePort(g_ucPinTCK, 0x01);
+ writePort(g_ucPinTCK, 0x00);
+}
+
+void calibration(void)
+{
+ /* Apply 2 pulses to TCK. */
+ writePort(g_ucPinTCK, 0x00);
+ writePort(g_ucPinTCK, 0x01);
+ writePort(g_ucPinTCK, 0x00);
+ writePort(g_ucPinTCK, 0x01);
+ writePort(g_ucPinTCK, 0x00);
+
+ ispVMDelay(0x8001);
+
+ /* Apply 2 pulses to TCK. */
+ writePort(g_ucPinTCK, 0x01);
+ writePort(g_ucPinTCK, 0x00);
+ writePort(g_ucPinTCK, 0x01);
+ writePort(g_ucPinTCK, 0x00);
+}
+
+/*
+ * GetByte
+ *
+ * Returns a byte to the caller. The returned byte depends on the
+ * g_usDataType register. If the HEAP_IN bit is set, then the byte
+ * is returned from the HEAP. If the LHEAP_IN bit is set, then
+ * the byte is returned from the intelligent buffer. Otherwise,
+ * the byte is returned directly from the VME file.
+ */
+unsigned char GetByte(void)
+{
+ unsigned char ucData;
+ unsigned int block_size = 4 * 1024;
+
+ if (g_usDataType & HEAP_IN) {
+
+ /*
+ * Get data from repeat buffer.
+ */
+
+ if (g_iHeapCounter > g_iHEAPSize) {
+
+ /*
+ * Data over-run.
+ */
+
+ return 0xFF;
+ }
+
+ ucData = g_pucHeapMemory[g_iHeapCounter++];
+ } else if (g_usDataType & LHEAP_IN) {
+
+ /*
+ * Get data from intel buffer.
+ */
+
+ if (g_usIntelDataIndex >= g_usIntelBufferSize) {
+ return 0xFF;
+ }
+
+ ucData = g_pucIntelBuffer[g_usIntelDataIndex++];
+ } else {
+ if (read_bytes == bufsize) {
+ return 0xFF;
+ }
+ ucData = *fpga_image++;
+ read_bytes++;
+
+ if (!(read_bytes % block_size)) {
+ printf("Downloading FPGA %ld/%ld completed\r",
+ read_bytes,
+ bufsize);
+ }
+
+ if (expectedCRC != 0) {
+ ispVMCalculateCRC32(ucData);
+ }
+ }
+
+ return ucData;
+}
+
+signed char ispVM(void)
+{
+ char szFileVersion[9] = { 0 };
+ signed char cRetCode = 0;
+ signed char cIndex = 0;
+ signed char cVersionIndex = 0;
+ unsigned char ucReadByte = 0;
+ unsigned short crc;
+
+ g_pucHeapMemory = NULL;
+ g_iHeapCounter = 0;
+ g_iHEAPSize = 0;
+ g_usIntelDataIndex = 0;
+ g_usIntelBufferSize = 0;
+ g_usCalculatedCRC = 0;
+ expectedCRC = 0;
+ ucReadByte = GetByte();
+ switch (ucReadByte) {
+ case FILE_CRC:
+ crc = (unsigned char)GetByte();
+ crc <<= 8;
+ crc |= GetByte();
+ expectedCRC = crc;
+
+ for (cIndex = 0; cIndex < 8; cIndex++)
+ szFileVersion[cIndex] = GetByte();
+
+ break;
+ default:
+ szFileVersion[0] = (signed char) ucReadByte;
+ for (cIndex = 1; cIndex < 8; cIndex++)
+ szFileVersion[cIndex] = GetByte();
+
+ break;
+ }
+
+ /*
+ *
+ * Compare the VME file version against the supported version.
+ *
+ */
+
+ for (cVersionIndex = 0; g_szSupportedVersions[cVersionIndex] != 0;
+ cVersionIndex++) {
+ for (cIndex = 0; cIndex < 8; cIndex++) {
+ if (szFileVersion[cIndex] !=
+ g_szSupportedVersions[cVersionIndex][cIndex]) {
+ cRetCode = VME_VERSION_FAILURE;
+ break;
+ }
+ cRetCode = 0;
+ }
+
+ if (cRetCode == 0) {
+ break;
+ }
+ }
+
+ if (cRetCode < 0) {
+ return VME_VERSION_FAILURE;
+ }
+
+ printf("VME file checked: starting downloading to FPGA\n");
+
+ ispVMStart();
+
+ cRetCode = ispVMCode();
+
+ ispVMEnd();
+ ispVMFreeMem();
+ puts("\n");
+
+ if (cRetCode == 0 && expectedCRC != 0 &&
+ (expectedCRC != g_usCalculatedCRC)) {
+ printf("Expected CRC: 0x%.4X\n", expectedCRC);
+ printf("Calculated CRC: 0x%.4X\n", g_usCalculatedCRC);
+ return VME_CRC_FAILURE;
+ }
+ return cRetCode;
+}
+
+static int lattice_validate(Lattice_desc *desc, const char *fn)
+{
+ int ret_val = FALSE;
+
+ if (desc) {
+ if ((desc->family > min_lattice_type) &&
+ (desc->family < max_lattice_type)) {
+ if ((desc->iface > min_lattice_iface_type) &&
+ (desc->iface < max_lattice_iface_type)) {
+ if (desc->size) {
+ ret_val = TRUE;
+ } else {
+ printf("%s: NULL part size\n", fn);
+ }
+ } else {
+ printf("%s: Invalid Interface type, %d\n",
+ fn, desc->iface);
+ }
+ } else {
+ printf("%s: Invalid family type, %d\n",
+ fn, desc->family);
+ }
+ } else {
+ printf("%s: NULL descriptor!\n", fn);
+ }
+
+ return ret_val;
+}
+
+int lattice_load(Lattice_desc *desc, void *buf, size_t bsize)
+{
+ int ret_val = FPGA_FAIL;
+
+ if (!lattice_validate(desc, (char *)__func__)) {
+ printf("%s: Invalid device descriptor\n", __func__);
+ } else {
+ pfns = desc->iface_fns;
+
+ switch (desc->family) {
+ case Lattice_XP2:
+ fpga_image = buf;
+ read_bytes = 0;
+ bufsize = bsize;
+ debug("%s: Launching the Lattice ISPVME Loader:"
+ " addr 0x%x size 0x%x...\n",
+ __func__, fpga_image, bufsize);
+ ret_val = ispVM();
+ if (ret_val)
+ printf("%s: error %d downloading FPGA image\n",
+ __func__, ret_val);
+ else
+ puts("FPGA downloaded successfully\n");
+ break;
+ default:
+ printf("%s: Unsupported family type, %d\n",
+ __func__, desc->family);
+ }
+ }
+
+ return ret_val;
+}
+
+int lattice_dump(Lattice_desc *desc, void *buf, size_t bsize)
+{
+ puts("Dump not supported for Lattice FPGA\n");
+
+ return FPGA_FAIL;
+
+}
+
+int lattice_info(Lattice_desc *desc)
+{
+ int ret_val = FPGA_FAIL;
+
+ if (lattice_validate(desc, (char *)__func__)) {
+ printf("Family: \t");
+ switch (desc->family) {
+ case Lattice_XP2:
+ puts("XP2\n");
+ break;
+ /* Add new family types here */
+ default:
+ printf("Unknown family type, %d\n", desc->family);
+ }
+
+ puts("Interface type:\t");
+ switch (desc->iface) {
+ case lattice_jtag_mode:
+ puts("JTAG Mode\n");
+ break;
+ /* Add new interface types here */
+ default:
+ printf("Unsupported interface type, %d\n", desc->iface);
+ }
+
+ printf("Device Size: \t%d bytes\n",
+ desc->size);
+
+ if (desc->iface_fns) {
+ printf("Device Function Table @ 0x%p\n",
+ desc->iface_fns);
+ switch (desc->family) {
+ case Lattice_XP2:
+ break;
+ /* Add new family types here */
+ default:
+ break;
+ }
+ } else {
+ puts("No Device Function Table.\n");
+ }
+
+ if (desc->desc)
+ printf("Model: \t%s\n", desc->desc);
+
+ ret_val = FPGA_SUCCESS;
+ } else {
+ printf("%s: Invalid device descriptor\n", __func__);
+ }
+
+ return ret_val;
+}
+
+