flash/nor/jtagspi: add JTAGSPI driver
authorRobert Jordens <jordens@gmail.com>
Wed, 1 Jul 2015 09:18:46 +0000 (03:18 -0600)
committerSpencer Oliver <spen@spen-soft.co.uk>
Thu, 6 Aug 2015 12:14:08 +0000 (13:14 +0100)
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>
contrib/loaders/flash/fpga/xilinx_bscan_spi.py [new file with mode: 0755]
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/jtagspi.c [new file with mode: 0644]
tcl/cpld/jtagspi.cfg [new file with mode: 0644]

diff --git a/contrib/loaders/flash/fpga/xilinx_bscan_spi.py b/contrib/loaders/flash/fpga/xilinx_bscan_spi.py
new file mode 100755 (executable)
index 0000000..a107a6a
--- /dev/null
@@ -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)
index 4430050eece3e94a6ab479b3e033cda53e3324a2..63ab5deb5dc29bc00ea612adcdc1e97387891295 100644 (file)
@@ -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
index 878fc26ba7a392476aeb049b209b8cc8b2d198b2..19ae90ee26330e9252840759d83e18222978fa01 100644 (file)
@@ -19,6 +19,7 @@ NOR_DRIVERS = \
        efm32.c \
        em357.c \
        faux.c \
+       jtagspi.c \
        lpc2000.c \
        lpc288x.c \
        lpc2900.c \
index fead7974fb3d205778f82e49603403631f0df51a..6ae0859dd41f1ee687306a3f6f9da387ced34d23 100644 (file)
@@ -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 (file)
index 0000000..6ca5c3c
--- /dev/null
@@ -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 (file)
index 0000000..60c3cb1
--- /dev/null
@@ -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
+}