aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Jordens <jordens@gmail.com>2015-07-01 03:18:46 -0600
committerSpencer Oliver <spen@spen-soft.co.uk>2015-08-06 13:14:08 +0100
commitd25355473da9a925a696183a9947aac292cd2f60 (patch)
treefa695378980ccf9cf0b326a2df4c7dc53709b471
parent3edcb941864d677e30d36ad77c22d86ec7ac8eb3 (diff)
downloadriscv-openocd-d25355473da9a925a696183a9947aac292cd2f60.zip
riscv-openocd-d25355473da9a925a696183a9947aac292cd2f60.tar.gz
riscv-openocd-d25355473da9a925a696183a9947aac292cd2f60.tar.bz2
flash/nor/jtagspi: add JTAGSPI driver
Many FPGA board speak JTAG and have a SPI flash for their bitstream attached to them. The SPI flash is programmed by first uploading a proxy bitstream to the FPGA that connects the JTAG interface to the SPI interface if the IR contains a certain USER instruction. Then the SPI flash can be erase, written, read directly through the JTAG DR. The JTAG and SPI signaling is compatible. Such a proxy bitstream only needs to connect TDO-MISO, TDI-MOSI, TCK-CLK, and the activate the chip select when the IR contains the special instruction and the JTAG state machine is in the DR-SHIFT state. Change-Id: Ibc21d793a83b36fa37e2704966aa5c837c4dd0d2 Signed-off-by: Robert Jordens <jordens@gmail.com> Reviewed-on: http://openocd.zylin.com/2844 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
-rwxr-xr-xcontrib/loaders/flash/fpga/xilinx_bscan_spi.py317
-rw-r--r--doc/openocd.texi43
-rw-r--r--src/flash/nor/Makefile.am1
-rw-r--r--src/flash/nor/drivers.c2
-rw-r--r--src/flash/nor/jtagspi.c411
-rw-r--r--tcl/cpld/jtagspi.cfg43
6 files changed, 817 insertions, 0 deletions
diff --git a/contrib/loaders/flash/fpga/xilinx_bscan_spi.py b/contrib/loaders/flash/fpga/xilinx_bscan_spi.py
new file mode 100755
index 0000000..a107a6a
--- /dev/null
+++ b/contrib/loaders/flash/fpga/xilinx_bscan_spi.py
@@ -0,0 +1,317 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2015 Robert Jordens <jordens@gmail.com>
+#
+# 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.
+#
+
+from migen.fhdl.std import *
+from mibuild.generic_platform import *
+from mibuild.xilinx import XilinxPlatform
+from mibuild.xilinx.vivado import XilinxVivadoToolchain
+from mibuild.xilinx.ise import XilinxISEToolchain
+
+
+"""
+This migen script produces proxy bitstreams to allow programming SPI flashes
+behind FPGAs. JTAG signalling is connected directly to SPI signalling. CS_N is
+asserted when the JTAG IR contains the USER1 instruction and the state is
+SHIFT-DR.
+
+Xilinx bscan cells sample TDO on falling TCK and forward it.
+MISO requires sampling on rising CLK and leads to one cycle of latency.
+
+https://github.com/m-labs/migen
+"""
+
+
+class Spartan3(Module):
+ macro = "BSCAN_SPARTAN3"
+
+ def __init__(self, platform):
+ self.clock_domains.cd_jtag = ClockDomain(reset_less=True)
+ spi = platform.request("spiflash")
+ shift = Signal()
+ tdo = Signal()
+ sel1 = Signal()
+ self.comb += [
+ self.cd_jtag.clk.eq(spi.clk),
+ spi.cs_n.eq(~shift | ~sel1),
+ ]
+ self.sync.jtag += tdo.eq(spi.miso)
+ self.specials += Instance(self.macro,
+ o_DRCK1=spi.clk, o_SHIFT=shift,
+ o_TDI=spi.mosi, i_TDO1=tdo, i_TDO2=0,
+ o_SEL1=sel1)
+
+
+class Spartan3A(Spartan3):
+ macro = "BSCAN_SPARTAN3A"
+
+
+class Spartan6(Module):
+ def __init__(self, platform):
+ self.clock_domains.cd_jtag = ClockDomain(reset_less=True)
+ spi = platform.request("spiflash")
+ shift = Signal()
+ tdo = Signal()
+ sel = Signal()
+ self.comb += self.cd_jtag.clk.eq(spi.clk), spi.cs_n.eq(~shift | ~sel)
+ self.sync.jtag += tdo.eq(spi.miso)
+ self.specials += Instance("BSCAN_SPARTAN6", p_JTAG_CHAIN=1,
+ o_TCK=spi.clk, o_SHIFT=shift, o_SEL=sel,
+ o_TDI=spi.mosi, i_TDO=tdo)
+
+
+class Series7(Module):
+ def __init__(self, platform):
+ self.clock_domains.cd_jtag = ClockDomain(reset_less=True)
+ spi = platform.request("spiflash")
+ clk = Signal()
+ shift = Signal()
+ tdo = Signal()
+ sel = Signal()
+ self.comb += self.cd_jtag.clk.eq(clk), spi.cs_n.eq(~shift | ~sel)
+ self.sync.jtag += tdo.eq(spi.miso)
+ self.specials += Instance("BSCANE2", p_JTAG_CHAIN=1,
+ o_SHIFT=shift, o_TCK=clk, o_SEL=sel,
+ o_TDI=spi.mosi, i_TDO=tdo)
+ self.specials += Instance("STARTUPE2", i_CLK=0, i_GSR=0, i_GTS=0,
+ i_KEYCLEARB=0, i_PACK=1, i_USRCCLKO=clk,
+ i_USRCCLKTS=0, i_USRDONEO=1, i_USRDONETS=1)
+
+
+class XilinxBscanSpi(XilinxPlatform):
+ pinouts = {
+ # bitstreams are named by die, package does not matter, speed grade
+ # should not matter.
+ # cs_n, clk, mosi, miso, *pullups
+ "xc3s100e": ("cp132",
+ ["M2", "N12", "N2", "N8"],
+ "LVCMOS33", Spartan3),
+ "xc3s1200e": ("fg320",
+ ["U3", "U16", "T4", "N10"],
+ "LVCMOS33", Spartan3),
+ "xc3s1400a": ("fg484",
+ ["Y4", "AA20", "AB14", "AB20"],
+ "LVCMOS33", Spartan3A),
+ "xc3s1400an": ("fgg484",
+ ["Y4", "AA20", "AB14", "AB20"],
+ "LVCMOS33", Spartan3A),
+ "xc3s1600e": ("fg320",
+ ["U3", "U16", "T4", "N10"],
+ "LVCMOS33", Spartan3),
+ "xc3s200a": ("fg320",
+ ["V3", "U16", "T11", "V16"],
+ "LVCMOS33", Spartan3A),
+ "xc3s200an": ("ftg256",
+ ["T2", "R14", "P10", "T14"],
+ "LVCMOS33", Spartan3A),
+ "xc3s250e": ("cp132",
+ ["M2", "N12", "N2", "N8"],
+ "LVCMOS33", Spartan3),
+ "xc3s400a": ("fg320",
+ ["V3", "U16", "T11", "V16"],
+ "LVCMOS33", Spartan3A),
+ "xc3s400an": ("fgg400",
+ ["Y2", "Y19", "W12", "W18"],
+ "LVCMOS33", Spartan3A),
+ "xc3s500e": ("cp132",
+ ["M2", "N12", "N2", "N8"],
+ "LVCMOS33", Spartan3),
+ "xc3s50a": ("ft256",
+ ["T2", "R14", "P10", "T14"],
+ "LVCMOS33", Spartan3A),
+ "xc3s50an": ("ftg256",
+ ["T2", "R14", "P10", "T14"],
+ "LVCMOS33", Spartan3A),
+ "xc3s700a": ("fg400",
+ ["Y2", "Y19", "W12", "W18"],
+ "LVCMOS33", Spartan3A),
+ "xc3s700an": ("fgg484",
+ ["Y4", "AA20", "AB14", "AB20"],
+ "LVCMOS33", Spartan3A),
+ "xc3sd1800a": ("cs484",
+ ["U7", "V17", "V13", "W17"],
+ "LVCMOS33", Spartan3A),
+ "xc3sd3400a": ("cs484",
+ ["U7", "V17", "V13", "W17"],
+ "LVCMOS33", Spartan3A),
+
+ "xc6slx100": ("csg484-2",
+ ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+ "LVCMOS33", Spartan6),
+ "xc6slx100t": ("csg484-2",
+ ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+ "LVCMOS33", Spartan6),
+ "xc6slx150": ("csg484-2",
+ ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+ "LVCMOS33", Spartan6),
+ "xc6slx150t": ("csg484-2",
+ ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+ "LVCMOS33", Spartan6),
+ "xc6slx16": ("cpg196-2",
+ ["P2", "N13", "P11", "N11", "N10", "P10"],
+ "LVCMOS33", Spartan6),
+ "xc6slx25": ("csg324-2",
+ ["V3", "R15", "T13", "R13", "T14", "V14"],
+ "LVCMOS33", Spartan6),
+ "xc6slx25t": ("csg324-2",
+ ["V3", "R15", "T13", "R13", "T14", "V14"],
+ "LVCMOS33", Spartan6),
+ "xc6slx45": ("csg324-2",
+ ["V3", "R15", "T13", "R13", "T14", "V14"],
+ "LVCMOS33", Spartan6),
+ "xc6slx45t": ("csg324-2",
+ ["V3", "R15", "T13", "R13", "T14", "V14"],
+ "LVCMOS33", Spartan6),
+ "xc6slx4": ("cpg196-2",
+ ["P2", "N13", "P11", "N11", "N10", "P10"],
+ "LVCMOS33", Spartan6),
+ "xc6slx4t": ("qg144-2",
+ ["P38", "P70", "P64", "P65", "P62", "P61"],
+ "LVCMOS33", Spartan6),
+ "xc6slx75": ("csg484-2",
+ ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+ "LVCMOS33", Spartan6),
+ "xc6slx75t": ("csg484-2",
+ ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+ "LVCMOS33", Spartan6),
+ "xc6slx9": ("cpg196-2",
+ ["P2", "N13", "P11", "N11", "N10", "P10"],
+ "LVCMOS33", Spartan6),
+ "xc6slx9t": ("qg144-2",
+ ["P38", "P70", "P64", "P65", "P62", "P61"],
+ "LVCMOS33", Spartan6),
+
+ "xc7a100t": ("csg324-1",
+ ["L13", None, "K17", "K18", "L14", "M14"],
+ "LVCMOS25", Series7),
+ "xc7a15t": ("cpg236-1",
+ ["K19", None, "D18", "D19", "G18", "F18"],
+ "LVCMOS25", Series7),
+ "xc7a200t": ("fbg484-1",
+ ["T19", None, "P22", "R22", "P21", "R21"],
+ "LVCMOS25", Series7),
+ "xc7a35t": ("cpg236-1",
+ ["K19", None, "D18", "D19", "G18", "F18"],
+ "LVCMOS25", Series7),
+ "xc7a50t": ("cpg236-1",
+ ["K19", None, "D18", "D19", "G18", "F18"],
+ "LVCMOS25", Series7),
+ "xc7a75t": ("csg324-1",
+ ["L13", None, "K17", "K18", "L14", "M14"],
+ "LVCMOS25", Series7),
+ "xc7k160t": ("fbg484-1",
+ ["L16", None, "H18", "H19", "G18", "F19"],
+ "LVCMOS25", Series7),
+ "xc7k325t": ("fbg676-1",
+ ["C23", None, "B24", "A25", "B22", "A22"],
+ "LVCMOS25", Series7),
+ "xc7k355t": ("ffg901-1",
+ ["V26", None, "R30", "T30", "R28", "T28"],
+ "LVCMOS25", Series7),
+ "xc7k410t": ("fbg676-1",
+ ["C23", None, "B24", "A25", "B22", "A22"],
+ "LVCMOS25", Series7),
+ "xc7k420t": ("ffg1156-1",
+ ["V30", None, "AA33", "AA34", "Y33", "Y34"],
+ "LVCMOS25", Series7),
+ "xc7k480t": ("ffg1156-1",
+ ["V30", None, "AA33", "AA34", "Y33", "Y34"],
+ "LVCMOS25", Series7),
+ "xc7k70t": ("fbg484-1",
+ ["L16", None, "H18", "H19", "G18", "F19"],
+ "LVCMOS25", Series7),
+ "xc7v2000t": ("fhg1761-1",
+ ["AL36", None, "AM36", "AN36", "AJ36", "AJ37"],
+ "LVCMOS18", Series7),
+ "xc7v585t": ("ffg1157-1",
+ ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+ "LVCMOS18", Series7),
+ "xc7vh580t": ("flg1155-1",
+ ["AL28", None, "AE28", "AF28", "AJ29", "AJ30"],
+ "LVCMOS18", Series7),
+ "xc7vh870t": ("flg1932-1",
+ ["V32", None, "T33", "R33", "U31", "T31"],
+ "LVCMOS18", Series7),
+ "xc7vx1140t": ("flg1926-1",
+ ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
+ "LVCMOS18", Series7),
+ "xc7vx330t": ("ffg1157-1",
+ ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+ "LVCMOS18", Series7),
+ "xc7vx415t": ("ffg1157-1",
+ ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+ "LVCMOS18", Series7),
+ "xc7vx485t": ("ffg1157-1",
+ ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+ "LVCMOS18", Series7),
+ "xc7vx550t": ("ffg1158-1",
+ ["C24", None, "A23", "A24", "B26", "A26"],
+ "LVCMOS18", Series7),
+ "xc7vx690t": ("ffg1157-1",
+ ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+ "LVCMOS18", Series7),
+ "xc7vx980t": ("ffg1926-1",
+ ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
+ "LVCMOS18", Series7),
+ }
+
+ def __init__(self, device, pins, std):
+ cs_n, clk, mosi, miso = pins[:4]
+ io = ["spiflash", 0,
+ Subsignal("cs_n", Pins(cs_n)),
+ Subsignal("mosi", Pins(mosi)),
+ Subsignal("miso", Pins(miso), Misc("PULLUP")),
+ IOStandard(std),
+ ]
+ if clk:
+ io.append(Subsignal("clk", Pins(clk)))
+ for i, p in enumerate(pins[4:]):
+ io.append(Subsignal("pullup{}".format(i), Pins(p), Misc("PULLUP")))
+
+ XilinxPlatform.__init__(self, device, [io])
+ if isinstance(self.toolchain, XilinxVivadoToolchain):
+ self.toolchain.bitstream_commands.append(
+ "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]"
+ )
+ elif isinstance(self.toolchain, XilinxISEToolchain):
+ self.toolchain.bitgen_opt += " -g compress"
+
+ @classmethod
+ def make(cls, device, errors=False):
+ pkg, pins, std, Top = cls.pinouts[device]
+ platform = cls("{}-{}".format(device, pkg), pins, std)
+ top = Top(platform)
+ name = "bscan_spi_{}".format(device)
+ dir = "build_{}".format(device)
+ try:
+ platform.build(top, build_name=name, build_dir=dir)
+ except Exception as e:
+ print("ERROR: build failed for {}: {}".format(device, e))
+ if errors:
+ raise
+
+
+if __name__ == "__main__":
+ import argparse
+ import multiprocessing
+ p = argparse.ArgumentParser(description="build bscan_spi bitstreams "
+ "for openocd jtagspi flash driver")
+ p.add_argument("device", nargs="*",
+ default=sorted(list(XilinxBscanSpi.pinouts)),
+ help="build for these devices (default: %(default)s)")
+ p.add_argument("-p", "--parallel", default=1, type=int,
+ help="number of parallel builds (default: %(default)s)")
+ args = p.parse_args()
+ pool = multiprocessing.Pool(args.parallel)
+ pool.map(XilinxBscanSpi.make, args.device, chunksize=1)
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 4430050..63ab5de 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -4802,6 +4802,49 @@ flash bank $_FLASHNAME cfi 0x00000000 0x02000000 2 4 $_TARGETNAME
@c "cfi part_id" disabled
@end deffn
+@deffn {Flash Driver} jtagspi
+@cindex Generic JTAG2SPI driver
+@cindex SPI
+@cindex jtagspi
+@cindex bscan_spi
+Several FPGAs and CPLDs can retrieve their configuration (bitstream) from a
+SPI flash connected to them. To access this flash from the host, the device
+is first programmed with a special proxy bitstream that
+exposes the SPI flash on the device's JTAG interface. The flash can then be
+accessed through JTAG.
+
+Since signaling between JTAG and SPI is compatible, all that is required for
+a proxy bitstream is to connect TDI-MOSI, TDO-MISO, TCK-CLK and activate
+the flash chip select when the JTAG state machine is in SHIFT-DR. Such
+a bitstream for several Xilinx FPGAs can be found in
+@file{contrib/loaders/flash/fpga/xilinx_bscan_spi.py}. It requires migen
+(@url{http://github.com/m-labs/migen}) and a Xilinx toolchain to build.
+
+This flash bank driver requires a target on a JTAG tap and will access that
+tap directly. Since no support from the target is needed, the target can be a
+"testee" dummy. Since the target does not expose the flash memory
+mapping, target commands that would otherwise be expected to access the flash
+will not work. These include all @command{*_image} and
+@command{$target_name m*} commands as well as @command{program}. Equivalent
+functionality is available through the @command{flash write_bank},
+@command{flash read_bank}, and @command{flash verify_bank} commands.
+
+@itemize
+@item @var{ir} ... is loaded into the JTAG IR to map the flash as the JTAG DR.
+For the bitstreams generated from @file{xilinx_bscan_spi.py} this is the
+@var{USER1} instruction.
+@item @var{dr_length} ... is the length of the DR register. This will be 1 for
+@file{xilinx_bscan_spi.py} bitstreams and most other cases.
+@end itemize
+
+@example
+target create $_TARGETNAME testee -chain-position $_CHIPNAME.fpga
+set _XILINX_USER1 0x02
+set _DR_LENGTH 1
+flash bank $_FLASHNAME spi 0x0 0 0 0 $_TARGETNAME $_XILINX_USER1 $_DR_LENGTH
+@end example
+@end deffn
+
@deffn {Flash Driver} lpcspifi
@cindex NXP SPI Flash Interface
@cindex SPIFI
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 878fc26..19ae90e 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -19,6 +19,7 @@ NOR_DRIVERS = \
efm32.c \
em357.c \
faux.c \
+ jtagspi.c \
lpc2000.c \
lpc288x.c \
lpc2900.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index fead797..6ae0859 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -59,6 +59,7 @@ extern struct flash_driver nrf51_flash;
extern struct flash_driver mrvlqspi_flash;
extern struct flash_driver psoc4_flash;
extern struct flash_driver sim3x_flash;
+extern struct flash_driver jtagspi_flash;
/**
* The list of built-in flash drivers.
@@ -102,6 +103,7 @@ static struct flash_driver *flash_drivers[] = {
&mrvlqspi_flash,
&psoc4_flash,
&sim3x_flash,
+ &jtagspi_flash,
NULL,
};
diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c
new file mode 100644
index 0000000..6ca5c3c
--- /dev/null
+++ b/src/flash/nor/jtagspi.c
@@ -0,0 +1,411 @@
+/***************************************************************************
+ * Copyright (C) 2015 Robert Jordens <jordens@gmail.com> *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <jtag/jtag.h>
+#include <flash/nor/spi.h>
+#include <helper/time_support.h>
+
+#define JTAGSPI_MAX_TIMEOUT 3000
+
+
+struct jtagspi_flash_bank {
+ struct jtag_tap *tap;
+ const struct flash_device *dev;
+ int probed;
+ uint32_t ir;
+ uint32_t dr_len;
+};
+
+FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command)
+{
+ struct jtagspi_flash_bank *info;
+
+ if (CMD_ARGC < 8)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ info = malloc(sizeof(struct jtagspi_flash_bank));
+ if (info == NULL) {
+ LOG_ERROR("no memory for flash bank info");
+ return ERROR_FAIL;
+ }
+ bank->driver_priv = info;
+
+ info->tap = NULL;
+ info->probed = 0;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->dr_len);
+
+ return ERROR_OK;
+}
+
+static void jtagspi_set_ir(struct flash_bank *bank)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ struct scan_field field;
+ uint8_t buf[4];
+
+ if (buf_get_u32(info->tap->cur_instr, 0, info->tap->ir_length) == info->ir)
+ return;
+
+ LOG_DEBUG("loading jtagspi ir");
+ buf_set_u32(buf, 0, info->tap->ir_length, info->ir);
+ field.num_bits = info->tap->ir_length;
+ field.out_value = buf;
+ field.in_value = NULL;
+ jtag_add_ir_scan(info->tap, &field, TAP_IDLE);
+}
+
+static void flip_u8(uint8_t *in, uint8_t *out, int len)
+{
+ for (int i = 0; i < len; i++)
+ out[i] = flip_u32(in[i], 8);
+}
+
+static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd,
+ uint32_t *addr, uint8_t *data, int len)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ struct scan_field fields[3];
+ uint8_t cmd_buf[4];
+ uint8_t *data_buf;
+ int is_read, lenb, n;
+
+ /* LOG_DEBUG("cmd=0x%02x len=%i", cmd, len); */
+
+ n = 0;
+ fields[n].num_bits = 8;
+ cmd_buf[0] = cmd;
+ if (addr) {
+ h_u24_to_be(cmd_buf + 1, *addr);
+ fields[n].num_bits += 24;
+ }
+ flip_u8(cmd_buf, cmd_buf, 4);
+ fields[n].out_value = cmd_buf;
+ fields[n].in_value = NULL;
+ n++;
+
+ is_read = (len < 0);
+ if (is_read)
+ len = -len;
+ lenb = DIV_ROUND_UP(len, 8);
+ data_buf = malloc(lenb);
+ if (lenb > 0) {
+ if (data_buf == NULL) {
+ LOG_ERROR("no memory for spi buffer");
+ return ERROR_FAIL;
+ }
+ if (is_read) {
+ fields[n].num_bits = info->dr_len;
+ fields[n].out_value = NULL;
+ fields[n].in_value = NULL;
+ n++;
+ fields[n].out_value = NULL;
+ fields[n].in_value = data_buf;
+ } else {
+ flip_u8(data, data_buf, lenb);
+ fields[n].out_value = data_buf;
+ fields[n].in_value = NULL;
+ }
+ fields[n].num_bits = len;
+ n++;
+ }
+
+ jtagspi_set_ir(bank);
+ jtag_add_dr_scan(info->tap, n, fields, TAP_IDLE);
+ jtag_execute_queue();
+
+ if (is_read)
+ flip_u8(data_buf, data, lenb);
+ free(data_buf);
+ return ERROR_OK;
+}
+
+static int jtagspi_probe(struct flash_bank *bank)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ struct flash_sector *sectors;
+ uint8_t in_buf[3];
+ uint32_t id;
+
+ if (info->probed)
+ free(bank->sectors);
+ info->probed = 0;
+
+ if (bank->target->tap == NULL) {
+ LOG_ERROR("Target has no JTAG tap");
+ return ERROR_FAIL;
+ }
+ info->tap = bank->target->tap;
+
+ jtagspi_cmd(bank, SPIFLASH_READ_ID, NULL, in_buf, -24);
+ /* the table in spi.c has the manufacturer byte (first) as the lsb */
+ id = le_to_h_u24(in_buf);
+
+ info->dev = NULL;
+ for (const struct flash_device *p = flash_devices; p->name ; p++)
+ if (p->device_id == id) {
+ info->dev = p;
+ break;
+ }
+
+ if (!(info->dev)) {
+ LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
+ return ERROR_FAIL;
+ }
+
+ LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
+ info->dev->name, info->dev->device_id);
+
+ /* Set correct size value */
+ bank->size = info->dev->size_in_bytes;
+
+ /* create and fill sectors array */
+ bank->num_sectors =
+ info->dev->size_in_bytes / info->dev->sectorsize;
+ sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+ if (sectors == NULL) {
+ LOG_ERROR("not enough memory");
+ return ERROR_FAIL;
+ }
+
+ for (int sector = 0; sector < bank->num_sectors; sector++) {
+ sectors[sector].offset = sector * info->dev->sectorsize;
+ sectors[sector].size = info->dev->sectorsize;
+ sectors[sector].is_erased = -1;
+ sectors[sector].is_protected = 0;
+ }
+
+ bank->sectors = sectors;
+ info->probed = 1;
+ return ERROR_OK;
+}
+
+static void jtagspi_read_status(struct flash_bank *bank, uint32_t *status)
+{
+ uint8_t buf;
+ jtagspi_cmd(bank, SPIFLASH_READ_STATUS, NULL, &buf, -8);
+ *status = buf;
+ /* LOG_DEBUG("status=0x%08" PRIx32, *status); */
+}
+
+static int jtagspi_wait(struct flash_bank *bank, int timeout_ms)
+{
+ uint32_t status;
+ long long t0 = timeval_ms();
+ long long dt;
+
+ do {
+ dt = timeval_ms() - t0;
+ jtagspi_read_status(bank, &status);
+ if ((status & SPIFLASH_BSY_BIT) == 0) {
+ LOG_DEBUG("waited %lld ms", dt);
+ return ERROR_OK;
+ }
+ alive_sleep(1);
+ } while (dt <= timeout_ms);
+
+ LOG_ERROR("timeout, device still busy");
+ return ERROR_FAIL;
+}
+
+static int jtagspi_write_enable(struct flash_bank *bank)
+{
+ uint32_t status;
+
+ jtagspi_cmd(bank, SPIFLASH_WRITE_ENABLE, NULL, NULL, 0);
+ jtagspi_read_status(bank, &status);
+ if ((status & SPIFLASH_WE_BIT) == 0) {
+ LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+static int jtagspi_bulk_erase(struct flash_bank *bank)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ int retval;
+ long long t0 = timeval_ms();
+
+ retval = jtagspi_write_enable(bank);
+ if (retval != ERROR_OK)
+ return retval;
+ jtagspi_cmd(bank, info->dev->chip_erase_cmd, NULL, NULL, 0);
+ retval = jtagspi_wait(bank, bank->num_sectors*JTAGSPI_MAX_TIMEOUT);
+ LOG_INFO("took %lld ms", timeval_ms() - t0);
+ return retval;
+}
+
+static int jtagspi_sector_erase(struct flash_bank *bank, int sector)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ int retval;
+ long long t0 = timeval_ms();
+
+ retval = jtagspi_write_enable(bank);
+ if (retval != ERROR_OK)
+ return retval;
+ jtagspi_cmd(bank, info->dev->erase_cmd, &bank->sectors[sector].offset, NULL, 0);
+ retval = jtagspi_wait(bank, JTAGSPI_MAX_TIMEOUT);
+ LOG_INFO("sector %d took %lld ms", sector, timeval_ms() - t0);
+ return retval;
+}
+
+static int jtagspi_erase(struct flash_bank *bank, int first, int last)
+{
+ int sector;
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ int retval;
+
+ LOG_DEBUG("erase from sector %d to sector %d", first, last);
+
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+ LOG_ERROR("Flash sector invalid");
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ if (!(info->probed)) {
+ LOG_ERROR("Flash bank not probed");
+ return ERROR_FLASH_BANK_NOT_PROBED;
+ }
+
+ for (sector = first; sector <= last; sector++) {
+ if (bank->sectors[sector].is_protected) {
+ LOG_ERROR("Flash sector %d protected", sector);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (first == 0 && last == (bank->num_sectors - 1)
+ && info->dev->chip_erase_cmd != info->dev->erase_cmd) {
+ LOG_DEBUG("Trying bulk erase.");
+ retval = jtagspi_bulk_erase(bank);
+ if (retval == ERROR_OK)
+ return retval;
+ else
+ LOG_WARNING("Bulk flash erase failed. Falling back to sector erase.");
+ }
+
+ for (sector = first; sector <= last; sector++) {
+ retval = jtagspi_sector_erase(bank, sector);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Sector erase failed.");
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static int jtagspi_protect(struct flash_bank *bank, int set, int first, int last)
+{
+ int sector;
+
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+ LOG_ERROR("Flash sector invalid");
+ return ERROR_FLASH_SECTOR_INVALID;
+ }
+
+ for (sector = first; sector <= last; sector++)
+ bank->sectors[sector].is_protected = set;
+ return ERROR_OK;
+}
+
+static int jtagspi_protect_check(struct flash_bank *bank)
+{
+ return ERROR_OK;
+}
+
+static int jtagspi_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+
+ if (!(info->probed)) {
+ LOG_ERROR("Flash bank not yet probed.");
+ return ERROR_FLASH_BANK_NOT_PROBED;
+ }
+
+ jtagspi_cmd(bank, SPIFLASH_READ, &offset, buffer, -count*8);
+ return ERROR_OK;
+}
+
+static int jtagspi_page_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ int retval;
+
+ retval = jtagspi_write_enable(bank);
+ if (retval != ERROR_OK)
+ return retval;
+ jtagspi_cmd(bank, SPIFLASH_PAGE_PROGRAM, &offset, (uint8_t *) buffer, count*8);
+ return jtagspi_wait(bank, JTAGSPI_MAX_TIMEOUT);
+}
+
+static int jtagspi_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+ int retval;
+ uint32_t n;
+
+ if (!(info->probed)) {
+ LOG_ERROR("Flash bank not yet probed.");
+ return ERROR_FLASH_BANK_NOT_PROBED;
+ }
+
+ for (n = 0; n < count; n += info->dev->pagesize) {
+ retval = jtagspi_page_write(bank, buffer + n, offset + n,
+ MIN(count - n, info->dev->pagesize));
+ if (retval != ERROR_OK) {
+ LOG_ERROR("page write error");
+ return retval;
+ }
+ LOG_DEBUG("wrote page at 0x%08" PRIx32, offset + n);
+ }
+ return ERROR_OK;
+}
+
+static int jtagspi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ struct jtagspi_flash_bank *info = bank->driver_priv;
+
+ if (!(info->probed)) {
+ snprintf(buf, buf_size, "\nJTAGSPI flash bank not probed yet\n");
+ return ERROR_OK;
+ }
+
+ snprintf(buf, buf_size, "\nSPIFI flash information:\n"
+ " Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+ info->dev->name, info->dev->device_id);
+
+ return ERROR_OK;
+}
+
+struct flash_driver jtagspi_flash = {
+ .name = "jtagspi",
+ .flash_bank_command = jtagspi_flash_bank_command,
+ .erase = jtagspi_erase,
+ .protect = jtagspi_protect,
+ .write = jtagspi_write,
+ .read = jtagspi_read,
+ .probe = jtagspi_probe,
+ .auto_probe = jtagspi_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = jtagspi_protect_check,
+ .info = jtagspi_info
+};
diff --git a/tcl/cpld/jtagspi.cfg b/tcl/cpld/jtagspi.cfg
new file mode 100644
index 0000000..60c3cb1
--- /dev/null
+++ b/tcl/cpld/jtagspi.cfg
@@ -0,0 +1,43 @@
+set _USER1 0x02
+
+if { [info exists JTAGSPI_IR] } {
+ set _JTAGSPI_IR $JTAGSPI_IR
+} else {
+ set _JTAGSPI_IR $_USER1
+}
+
+if { [info exists DR_LENGTH] } {
+ set _DR_LENGTH $DR_LENGTH
+} else {
+ set _DR_LENGTH 1
+}
+
+if { [info exists TARGETNAME] } {
+ set _TARGETNAME $TARGETNAME
+} else {
+ set _TARGETNAME $_CHIPNAME.proxy
+}
+
+if { [info exists FLASHNAME] } {
+ set _FLASHNAME $FLASHNAME
+} else {
+ set _FLASHNAME $_CHIPNAME.spi
+}
+
+target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap
+flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME $_JTAGSPI_IR $_DR_LENGTH
+
+proc jtagspi_init {chain_id proxy_bit} {
+ # load proxy bitstream $proxy_bit and probe spi flash
+ global _FLASHNAME
+ pld load $chain_id $proxy_bit
+ reset halt
+ flash probe $_FLASHNAME
+}
+
+proc jtagspi_program {bin addr} {
+ # write and verify binary file $bin at offset $addr
+ global _FLASHNAME
+ flash write_image erase $bin $addr
+ flash verify_bank $_FLASHNAME $bin $addr
+}