3 # Copyright (C) 2015 Robert Jordens <jordens@gmail.com>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
19 import migen.build.generic_platform as mb
20 from migen.genlib import io
21 from migen.build import xilinx
25 This migen script produces proxy bitstreams to allow programming SPI flashes
28 Bitstream binaries built with this script are available at:
29 https://github.com/jordens/bscan_spi_bitstreams
31 A JTAG2SPI transfer consists of:
33 1. an arbitrary number of 0 bits (from BYPASS registers in front of the
35 2. a marker bit (1) indicating the start of the JTAG2SPI transaction
36 3. 32 bits (big endian) describing the length of the SPI transaction
37 4. a number of SPI clock cycles (corresponding to 3.) with CS_N asserted
38 5. an arbitrary number of cycles (to shift MISO/TDO data through subsequent
43 * The JTAG2SPI DR is 1 bit long (due to different sampling edges of
44 {MISO,MOSI}/{TDO,TDI}).
45 * MOSI is TDI with half a cycle delay.
46 * TDO is MISO with half a cycle delay.
47 * CAPTURE-DR needs to be performed before SHIFT-DR on the BYPASSed TAPs in
48 JTAG chain to clear the BYPASS registers to 0.
50 https://github.com/m-labs/migen
54 class JTAG2SPI(mg.Module):
55 def __init__(self, spi=None, bits=32):
56 self.jtag = mg.Record([
64 self.cs_n = mg.TSTriple()
65 self.clk = mg.TSTriple()
66 self.mosi = mg.TSTriple()
67 self.miso = mg.TSTriple()
71 self.cs_n.o.reset = mg.Constant(1)
72 self.mosi.o.reset_less = True
73 bits = mg.Signal(bits, reset_less=True)
74 head = mg.Signal(max=len(bits), reset=len(bits) - 1)
75 self.clock_domains.cd_sys = mg.ClockDomain()
76 self.submodules.fsm = mg.FSM("IDLE")
79 self.cs_n.get_tristate(spi.cs_n),
80 self.mosi.get_tristate(spi.mosi),
81 self.miso.get_tristate(spi.miso),
83 if hasattr(spi, "clk"): # 7 Series drive it fixed
84 self.specials += self.clk.get_tristate(spi.clk)
85 # self.specials += io.DDROutput(1, 0, spi.clk, self.clk.o)
87 self.cd_sys.rst.eq(self.jtag.sel & self.jtag.capture),
88 self.cd_sys.clk.eq(self.jtag.tck),
89 self.cs_n.oe.eq(self.jtag.sel),
90 self.clk.oe.eq(self.jtag.sel),
91 self.mosi.oe.eq(self.jtag.sel),
93 # Do not suppress CLK toggles outside CS_N asserted.
94 # Xilinx USRCCLK0 requires three dummy cycles to do anything
95 # https://www.xilinx.com/support/answers/52626.html
96 # This is fine since CS_N changes only on falling CLK.
97 self.clk.o.eq(~self.jtag.tck),
98 self.jtag.tdo.eq(self.miso.i),
100 # Latency calculation (in half cycles):
101 # 0 (falling TCK, rising CLK):
102 # JTAG adapter: set TDI
103 # 1 (rising TCK, falling CLK):
104 # JTAG2SPI: sample TDI -> set MOSI
106 # 2 (falling TCK, rising CLK):
108 # JTAG2SPI (BSCAN primitive): sample MISO -> set TDO
109 # 3 (rising TCK, falling CLK):
110 # JTAG adapter: sample TDO
112 mg.If(self.jtag.tdi & self.jtag.sel & self.jtag.shift,
127 self.mosi.o.eq(self.jtag.tdi),
128 self.cs_n.o.eq(~self.fsm.ongoing("XFER")),
129 mg.If(self.fsm.ongoing("HEAD"),
130 bits.eq(mg.Cat(self.jtag.tdi, bits)),
133 mg.If(self.fsm.ongoing("XFER"),
139 class JTAG2SPITest(unittest.TestCase):
142 self.dut = JTAG2SPI(bits=self.bits)
144 def test_instantiate(self):
147 def test_initial_conditions(self):
150 self.assertEqual((yield self.dut.cs_n.oe), 0)
151 self.assertEqual((yield self.dut.mosi.oe), 0)
152 self.assertEqual((yield self.dut.miso.oe), 0)
153 self.assertEqual((yield self.dut.clk.oe), 0)
154 mg.run_simulation(self.dut, check())
156 def test_enable(self):
158 yield self.dut.jtag.sel.eq(1)
159 yield self.dut.jtag.shift.eq(1)
161 self.assertEqual((yield self.dut.cs_n.oe), 1)
162 self.assertEqual((yield self.dut.mosi.oe), 1)
163 self.assertEqual((yield self.dut.miso.oe), 0)
164 self.assertEqual((yield self.dut.clk.oe), 1)
165 mg.run_simulation(self.dut, check())
167 def run_seq(self, tdi, tdo, spi=None):
168 yield self.dut.jtag.sel.eq(1)
170 yield self.dut.jtag.shift.eq(1)
172 yield self.dut.jtag.tdi.eq(di)
174 tdo.append((yield self.dut.jtag.tdo))
177 for k in "cs_n clk mosi miso".split():
178 t = getattr(self.dut, k)
179 v.append("{}>".format((yield t.o)) if (yield t.oe)
180 else "<{}".format((yield t.i)))
181 spi.append(" ".join(v))
182 yield self.dut.jtag.sel.eq(0)
184 yield self.dut.jtag.shift.eq(0)
187 def test_shift(self):
190 tdi = [0, 0, 1] # dummy from BYPASS TAPs and marker
191 tdi += [((bits - 1) >> j) & 1 for j in range(self.bits - 1, -1, -1)]
192 tdi += [(data >> j) & 1 for j in range(bits)]
193 tdi += [0, 0, 0, 0] # dummy from BYPASS TAPs
196 mg.run_simulation(self.dut, self.run_seq(tdi, tdo, spi))
202 class Spartan3(mg.Module):
203 macro = "BSCAN_SPARTAN3"
206 def __init__(self, platform):
207 platform.toolchain.bitgen_opt += " -g compress -g UnusedPin:Pullup"
208 self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
212 o_SHIFT=j2s.jtag.shift, o_SEL1=j2s.jtag.sel,
213 o_CAPTURE=j2s.jtag.capture,
214 o_DRCK1=j2s.jtag.tck,
215 o_TDI=j2s.jtag.tdi, i_TDO1=j2s.jtag.tdo,
218 platform.add_period_constraint(j2s.jtag.tck, 6)
221 class Spartan3A(Spartan3):
222 macro = "BSCAN_SPARTAN3A"
225 class Spartan6(mg.Module):
228 def __init__(self, platform):
229 platform.toolchain.bitgen_opt += " -g compress -g UnusedPin:Pullup"
230 self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
234 "BSCAN_SPARTAN6", p_JTAG_CHAIN=1,
235 o_SHIFT=j2s.jtag.shift, o_SEL=j2s.jtag.sel,
236 o_CAPTURE=j2s.jtag.capture,
238 o_TDI=j2s.jtag.tdi, i_TDO=j2s.jtag.tdo),
239 # mg.Instance("BUFG", i_I=clk, o_O=j2s.jtag.tck)
241 platform.add_period_constraint(j2s.jtag.tck, 6)
244 class Series7(mg.Module):
247 def __init__(self, platform):
248 platform.toolchain.bitstream_commands.extend([
249 "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
250 "set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]"
252 self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
256 "BSCANE2", p_JTAG_CHAIN=1,
257 o_SHIFT=j2s.jtag.shift, o_SEL=j2s.jtag.sel,
258 o_CAPTURE=j2s.jtag.capture,
260 o_TDI=j2s.jtag.tdi, i_TDO=j2s.jtag.tdo),
262 "STARTUPE2", i_CLK=0, i_GSR=0, i_GTS=0,
263 i_KEYCLEARB=0, i_PACK=1,
264 i_USRCCLKO=j2s.clk.o, i_USRCCLKTS=~j2s.clk.oe,
265 i_USRDONEO=1, i_USRDONETS=1),
266 # mg.Instance("BUFG", i_I=clk, o_O=j2s.jtag.tck)
268 platform.add_period_constraint(j2s.jtag.tck, 6)
271 platform.request("user_sma_gpio_p").eq(j2s.cs_n.i),
272 platform.request("user_sma_gpio_n").eq(j2s.clk.o),
273 platform.request("user_sma_clock_p").eq(j2s.mosi.o),
274 platform.request("user_sma_clock_n").eq(j2s.miso.i),
276 except mb.ConstraintError:
280 class Ultrascale(mg.Module):
283 def __init__(self, platform):
284 platform.toolchain.bitstream_commands.extend([
285 "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
286 "set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]",
288 self.submodules.j2s0 = j2s0 = JTAG2SPI()
289 self.submodules.j2s1 = j2s1 = JTAG2SPI(platform.request("spiflash"))
291 self.comb += mg.Cat(j2s0.mosi.i, j2s0.miso.i).eq(di)
293 mg.Instance("BSCANE2", p_JTAG_CHAIN=1,
294 o_SHIFT=j2s0.jtag.shift, o_SEL=j2s0.jtag.sel,
295 o_CAPTURE=j2s0.jtag.capture,
296 o_DRCK=j2s0.jtag.tck,
297 o_TDI=j2s0.jtag.tdi, i_TDO=j2s0.jtag.tdo),
298 mg.Instance("BSCANE2", p_JTAG_CHAIN=2,
299 o_SHIFT=j2s1.jtag.shift, o_SEL=j2s1.jtag.sel,
300 o_CAPTURE=j2s1.jtag.capture,
301 o_DRCK=j2s1.jtag.tck,
302 o_TDI=j2s1.jtag.tdi, i_TDO=j2s1.jtag.tdo),
303 mg.Instance("STARTUPE3", i_GSR=0, i_GTS=0,
304 i_KEYCLEARB=0, i_PACK=1,
305 i_USRDONEO=1, i_USRDONETS=1,
306 i_USRCCLKO=mg.Mux(j2s0.clk.oe, j2s0.clk.o, j2s1.clk.o),
307 i_USRCCLKTS=~(j2s0.clk.oe | j2s1.clk.oe),
308 i_FCSBO=j2s0.cs_n.o, i_FCSBTS=~j2s0.cs_n.oe,
310 i_DO=mg.Cat(j2s0.mosi.o, j2s0.miso.o, 0, 0),
311 i_DTS=mg.Cat(~j2s0.mosi.oe, ~j2s0.miso.oe, 1, 1))
313 platform.add_period_constraint(j2s0.jtag.tck, 6)
314 platform.add_period_constraint(j2s1.jtag.tck, 6)
317 class XilinxBscanSpi(xilinx.XilinxPlatform):
319 # (package-speedgrade, id): [cs_n, clk, mosi, miso, *pullups]
320 ("cp132", 1): ["M2", "N12", "N2", "N8"],
321 ("fg320", 1): ["U3", "U16", "T4", "N10"],
322 ("fg320", 2): ["V3", "U16", "T11", "V16"],
323 ("fg484", 1): ["Y4", "AA20", "AB14", "AB20"],
324 ("fgg484", 1): ["Y4", "AA20", "AB14", "AB20"],
325 ("fgg400", 1): ["Y2", "Y19", "W12", "W18"],
326 ("ftg256", 1): ["T2", "R14", "P10", "T14"],
327 ("ft256", 1): ["T2", "R14", "P10", "T14"],
328 ("fg400", 1): ["Y2", "Y19", "W12", "W18"],
329 ("cs484", 1): ["U7", "V17", "V13", "W17"],
330 ("qg144-2", 1): ["P38", "P70", "P64", "P65", "P62", "P61"],
331 ("cpg196-2", 1): ["P2", "N13", "P11", "N11", "N10", "P10"],
332 ("cpg236-1", 1): ["K19", None, "D18", "D19", "G18", "F18"],
333 ("csg484-2", 1): ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
334 ("csg324-2", 1): ["V3", "R15", "T13", "R13", "T14", "V14"],
335 ("csg324-1", 1): ["L13", None, "K17", "K18", "L14", "M14"],
336 ("fbg484-1", 1): ["T19", None, "P22", "R22", "P21", "R21"],
337 ("fbg484-1", 2): ["L16", None, "H18", "H19", "G18", "F19"],
338 ("fbg676-1", 1): ["C23", None, "B24", "A25", "B22", "A22"],
339 ("ffg901-1", 1): ["V26", None, "R30", "T30", "R28", "T28"],
340 ("ffg900-1", 1): ["U19", None, "P24", "R25", "R20", "R21"],
341 ("ffg1156-1", 1): ["V30", None, "AA33", "AA34", "Y33", "Y34"],
342 ("ffg1157-1", 1): ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
343 ("ffg1158-1", 1): ["C24", None, "A23", "A24", "B26", "A26"],
344 ("ffg1926-1", 1): ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
345 ("fhg1761-1", 1): ["AL36", None, "AM36", "AN36", "AJ36", "AJ37"],
346 ("flg1155-1", 1): ["AL28", None, "AE28", "AF28", "AJ29", "AJ30"],
347 ("flg1932-1", 1): ["V32", None, "T33", "R33", "U31", "T31"],
348 ("flg1926-1", 1): ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
350 ("ffva1156-2-e", 1): ["G26", None, "M20", "L20", "R21", "R22"],
351 ("ffva1156-2-e", "sayma"): ["K21", None, "M20", "L20", "R21", "R22"],
355 # bitstreams are named by die, package does not matter, speed grade
358 # chip: (package, id, standard, class)
359 "xc3s100e": ("cp132", 1, "LVCMOS33", Spartan3),
360 "xc3s1200e": ("fg320", 1, "LVCMOS33", Spartan3),
361 "xc3s1400a": ("fg484", 1, "LVCMOS33", Spartan3A),
362 "xc3s1400an": ("fgg484", 1, "LVCMOS33", Spartan3A),
363 "xc3s1600e": ("fg320", 1, "LVCMOS33", Spartan3),
364 "xc3s200a": ("fg320", 2, "LVCMOS33", Spartan3A),
365 "xc3s200an": ("ftg256", 1, "LVCMOS33", Spartan3A),
366 "xc3s250e": ("cp132", 1, "LVCMOS33", Spartan3),
367 "xc3s400a": ("fg320", 2, "LVCMOS33", Spartan3A),
368 "xc3s400an": ("fgg400", 1, "LVCMOS33", Spartan3A),
369 "xc3s500e": ("cp132", 1, "LVCMOS33", Spartan3),
370 "xc3s50a": ("ft256", 1, "LVCMOS33", Spartan3A),
371 "xc3s50an": ("ftg256", 1, "LVCMOS33", Spartan3A),
372 "xc3s700a": ("fg400", 1, "LVCMOS33", Spartan3A),
373 "xc3s700an": ("fgg484", 1, "LVCMOS33", Spartan3A),
374 "xc3sd1800a": ("cs484", 1, "LVCMOS33", Spartan3A),
375 "xc3sd3400a": ("cs484", 1, "LVCMOS33", Spartan3A),
377 "xc6slx100": ("csg484-2", 1, "LVCMOS33", Spartan6),
378 "xc6slx100t": ("csg484-2", 1, "LVCMOS33", Spartan6),
379 "xc6slx150": ("csg484-2", 1, "LVCMOS33", Spartan6),
380 "xc6slx150t": ("csg484-2", 1, "LVCMOS33", Spartan6),
381 "xc6slx16": ("cpg196-2", 1, "LVCMOS33", Spartan6),
382 "xc6slx25": ("csg324-2", 1, "LVCMOS33", Spartan6),
383 "xc6slx25t": ("csg324-2", 1, "LVCMOS33", Spartan6),
384 "xc6slx45": ("csg324-2", 1, "LVCMOS33", Spartan6),
385 "xc6slx45t": ("csg324-2", 1, "LVCMOS33", Spartan6),
386 "xc6slx4": ("cpg196-2", 1, "LVCMOS33", Spartan6),
387 "xc6slx4t": ("qg144-2", 1, "LVCMOS33", Spartan6),
388 "xc6slx75": ("csg484-2", 1, "LVCMOS33", Spartan6),
389 "xc6slx75t": ("csg484-2", 1, "LVCMOS33", Spartan6),
390 "xc6slx9": ("cpg196-2", 1, "LVCMOS33", Spartan6),
391 "xc6slx9t": ("qg144-2", 1, "LVCMOS33", Spartan6),
393 "xc7a100t": ("csg324-1", 1, "LVCMOS25", Series7),
394 "xc7a15t": ("cpg236-1", 1, "LVCMOS25", Series7),
395 "xc7a200t": ("fbg484-1", 1, "LVCMOS25", Series7),
396 "xc7a35t": ("cpg236-1", 1, "LVCMOS25", Series7),
397 "xc7a50t": ("cpg236-1", 1, "LVCMOS25", Series7),
398 "xc7a75t": ("csg324-1", 1, "LVCMOS25", Series7),
399 "xc7k160t": ("fbg484-1", 2, "LVCMOS25", Series7),
400 "xc7k325t": ("fbg676-1", 1, "LVCMOS25", Series7),
401 "xc7k325t-debug": ("ffg900-1", 1, "LVCMOS25", Series7),
402 "xc7k355t": ("ffg901-1", 1, "LVCMOS25", Series7),
403 "xc7k410t": ("fbg676-1", 1, "LVCMOS25", Series7),
404 "xc7k420t": ("ffg1156-1", 1, "LVCMOS25", Series7),
405 "xc7k480t": ("ffg1156-1", 1, "LVCMOS25", Series7),
406 "xc7k70t": ("fbg484-1", 2, "LVCMOS25", Series7),
407 "xc7v2000t": ("fhg1761-1", 1, "LVCMOS18", Series7),
408 "xc7v585t": ("ffg1157-1", 1, "LVCMOS18", Series7),
409 "xc7vh580t": ("flg1155-1", 1, "LVCMOS18", Series7),
410 "xc7vh870t": ("flg1932-1", 1, "LVCMOS18", Series7),
411 "xc7vx1140t": ("flg1926-1", 1, "LVCMOS18", Series7),
412 "xc7vx330t": ("ffg1157-1", 1, "LVCMOS18", Series7),
413 "xc7vx415t": ("ffg1157-1", 1, "LVCMOS18", Series7),
414 "xc7vx485t": ("ffg1157-1", 1, "LVCMOS18", Series7),
415 "xc7vx550t": ("ffg1158-1", 1, "LVCMOS18", Series7),
416 "xc7vx690t": ("ffg1157-1", 1, "LVCMOS18", Series7),
417 "xc7vx980t": ("ffg1926-1", 1, "LVCMOS18", Series7),
419 "xcku040": ("ffva1156-2-e", 1, "LVCMOS18", Ultrascale),
420 "xcku040-sayma": ("ffva1156-2-e", "sayma", "LVCMOS18", Ultrascale),
423 def __init__(self, device, pins, std, toolchain="ise"):
424 ios = [self.make_spi(0, pins, std, toolchain)]
425 if device == "xc7k325t-ffg900-1": # debug
427 ("user_sma_clock_p", 0, mb.Pins("L25"), mb.IOStandard("LVCMOS25")),
428 ("user_sma_clock_n", 0, mb.Pins("K25"), mb.IOStandard("LVCMOS25")),
429 ("user_sma_gpio_p", 0, mb.Pins("Y23"), mb.IOStandard("LVCMOS25")),
430 ("user_sma_gpio_n", 0, mb.Pins("Y24"), mb.IOStandard("LVCMOS25")),
432 xilinx.XilinxPlatform.__init__(self, device, ios, toolchain=toolchain)
435 def make_spi(i, pins, std, toolchain):
436 pu = "PULLUP" if toolchain == "ise" else "PULLUP TRUE"
437 pd = "PULLDOWN" if toolchain == "ise" else "PULLDOWN TRUE"
438 cs_n, clk, mosi, miso = pins[:4]
440 mb.Subsignal("cs_n", mb.Pins(cs_n), mb.Misc(pu)),
441 mb.Subsignal("mosi", mb.Pins(mosi), mb.Misc(pu)),
442 mb.Subsignal("miso", mb.Pins(miso), mb.Misc(pu)),
446 io.append(mb.Subsignal("clk", mb.Pins(clk), mb.Misc(pd)))
447 for i, p in enumerate(pins[4:]):
448 io.append(mb.Subsignal("pullup{}".format(i), mb.Pins(p),
453 def make(cls, target, errors=False):
454 pkg, id, std, Top = cls.pinouts[target]
455 pins = cls.packages[(pkg, id)]
456 device = target.split("-", 1)[0]
457 platform = cls("{}-{}".format(device, pkg), pins, std, Top.toolchain)
459 name = "bscan_spi_{}".format(target)
461 platform.build(top, build_name=name)
462 except Exception as e:
463 print(("ERROR: xilinx_bscan_spi build failed "
464 "for {}: {}").format(target, e))
469 if __name__ == "__main__":
471 import multiprocessing
472 p = argparse.ArgumentParser(description="build bscan_spi bitstreams "
473 "for openocd jtagspi flash driver")
474 p.add_argument("device", nargs="*",
475 default=sorted(list(XilinxBscanSpi.pinouts)),
476 help="build for these devices (default: %(default)s)")
477 p.add_argument("-p", "--parallel", default=1, type=int,
478 help="number of parallel builds (default: %(default)s)")
479 args = p.parse_args()
480 pool = multiprocessing.Pool(args.parallel)
481 pool.map(XilinxBscanSpi.make, args.device, chunksize=1)