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>
This commit is contained in:
Robert Jordens 2015-07-01 03:18:46 -06:00 committed by Spencer Oliver
parent 3edcb94186
commit d25355473d
6 changed files with 817 additions and 0 deletions

View File

@ -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)

View File

@ -4802,6 +4802,49 @@ flash bank $_FLASHNAME cfi 0x00000000 0x02000000 2 4 $_TARGETNAME
@c "cfi part_id" disabled @c "cfi part_id" disabled
@end deffn @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 @deffn {Flash Driver} lpcspifi
@cindex NXP SPI Flash Interface @cindex NXP SPI Flash Interface
@cindex SPIFI @cindex SPIFI

View File

@ -19,6 +19,7 @@ NOR_DRIVERS = \
efm32.c \ efm32.c \
em357.c \ em357.c \
faux.c \ faux.c \
jtagspi.c \
lpc2000.c \ lpc2000.c \
lpc288x.c \ lpc288x.c \
lpc2900.c \ lpc2900.c \

View File

@ -59,6 +59,7 @@ extern struct flash_driver nrf51_flash;
extern struct flash_driver mrvlqspi_flash; extern struct flash_driver mrvlqspi_flash;
extern struct flash_driver psoc4_flash; extern struct flash_driver psoc4_flash;
extern struct flash_driver sim3x_flash; extern struct flash_driver sim3x_flash;
extern struct flash_driver jtagspi_flash;
/** /**
* The list of built-in flash drivers. * The list of built-in flash drivers.
@ -102,6 +103,7 @@ static struct flash_driver *flash_drivers[] = {
&mrvlqspi_flash, &mrvlqspi_flash,
&psoc4_flash, &psoc4_flash,
&sim3x_flash, &sim3x_flash,
&jtagspi_flash,
NULL, NULL,
}; };

411
src/flash/nor/jtagspi.c Normal file
View File

@ -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
};

43
tcl/cpld/jtagspi.cfg Normal file
View File

@ -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
}