2 # SPDX-License-Identifier: GPL-2.0-or-later
4 # Copyright (C) 2015 Robert Jordens <jordens@gmail.com>
9 import migen.build.generic_platform as mb
10 from migen.genlib import io
11 from migen.build import xilinx
15 This migen script produces proxy bitstreams to allow programming SPI flashes
18 Bitstream binaries built with this script are available at:
19 https://github.com/jordens/bscan_spi_bitstreams
21 A JTAG2SPI transfer consists of:
23 1. an arbitrary number of 0 bits (from BYPASS registers in front of the
25 2. a marker bit (1) indicating the start of the JTAG2SPI transaction
26 3. 32 bits (big endian) describing the length of the SPI transaction
27 4. a number of SPI clock cycles (corresponding to 3.) with CS_N asserted
28 5. an arbitrary number of cycles (to shift MISO/TDO data through subsequent
33 * The JTAG2SPI DR is 1 bit long (due to different sampling edges of
34 {MISO,MOSI}/{TDO,TDI}).
35 * MOSI is TDI with half a cycle delay.
36 * TDO is MISO with half a cycle delay.
37 * CAPTURE-DR needs to be performed before SHIFT-DR on the BYPASSed TAPs in
38 JTAG chain to clear the BYPASS registers to 0.
40 https://github.com/m-labs/migen
44 class JTAG2SPI(mg.Module):
45 def __init__(self, spi=None, bits=32):
46 self.jtag = mg.Record([
54 self.cs_n = mg.TSTriple()
55 self.clk = mg.TSTriple()
56 self.mosi = mg.TSTriple()
57 self.miso = mg.TSTriple()
61 self.cs_n.o.reset = mg.Constant(1)
62 self.mosi.o.reset_less = True
63 bits = mg.Signal(bits, reset_less=True)
64 head = mg.Signal(max=len(bits), reset=len(bits) - 1)
65 self.clock_domains.cd_sys = mg.ClockDomain()
66 self.submodules.fsm = mg.FSM("IDLE")
69 self.cs_n.get_tristate(spi.cs_n),
70 self.mosi.get_tristate(spi.mosi),
71 self.miso.get_tristate(spi.miso),
73 if hasattr(spi, "clk"): # 7 Series drive it fixed
74 self.specials += self.clk.get_tristate(spi.clk)
75 # self.specials += io.DDROutput(1, 0, spi.clk, self.clk.o)
77 self.cd_sys.rst.eq(self.jtag.sel & self.jtag.capture),
78 self.cd_sys.clk.eq(self.jtag.tck),
79 self.cs_n.oe.eq(self.jtag.sel),
80 self.clk.oe.eq(self.jtag.sel),
81 self.mosi.oe.eq(self.jtag.sel),
83 # Do not suppress CLK toggles outside CS_N asserted.
84 # Xilinx USRCCLK0 requires three dummy cycles to do anything
85 # https://www.xilinx.com/support/answers/52626.html
86 # This is fine since CS_N changes only on falling CLK.
87 self.clk.o.eq(~self.jtag.tck),
88 self.jtag.tdo.eq(self.miso.i),
90 # Latency calculation (in half cycles):
91 # 0 (falling TCK, rising CLK):
92 # JTAG adapter: set TDI
93 # 1 (rising TCK, falling CLK):
94 # JTAG2SPI: sample TDI -> set MOSI
96 # 2 (falling TCK, rising CLK):
98 # JTAG2SPI (BSCAN primitive): sample MISO -> set TDO
99 # 3 (rising TCK, falling CLK):
100 # JTAG adapter: sample TDO
102 mg.If(self.jtag.tdi & self.jtag.sel & self.jtag.shift,
117 self.mosi.o.eq(self.jtag.tdi),
118 self.cs_n.o.eq(~self.fsm.ongoing("XFER")),
119 mg.If(self.fsm.ongoing("HEAD"),
120 bits.eq(mg.Cat(self.jtag.tdi, bits)),
123 mg.If(self.fsm.ongoing("XFER"),
129 class JTAG2SPITest(unittest.TestCase):
132 self.dut = JTAG2SPI(bits=self.bits)
134 def test_instantiate(self):
137 def test_initial_conditions(self):
140 self.assertEqual((yield self.dut.cs_n.oe), 0)
141 self.assertEqual((yield self.dut.mosi.oe), 0)
142 self.assertEqual((yield self.dut.miso.oe), 0)
143 self.assertEqual((yield self.dut.clk.oe), 0)
144 mg.run_simulation(self.dut, check())
146 def test_enable(self):
148 yield self.dut.jtag.sel.eq(1)
149 yield self.dut.jtag.shift.eq(1)
151 self.assertEqual((yield self.dut.cs_n.oe), 1)
152 self.assertEqual((yield self.dut.mosi.oe), 1)
153 self.assertEqual((yield self.dut.miso.oe), 0)
154 self.assertEqual((yield self.dut.clk.oe), 1)
155 mg.run_simulation(self.dut, check())
157 def run_seq(self, tdi, tdo, spi=None):
158 yield self.dut.jtag.sel.eq(1)
160 yield self.dut.jtag.shift.eq(1)
162 yield self.dut.jtag.tdi.eq(di)
164 tdo.append((yield self.dut.jtag.tdo))
167 for k in "cs_n clk mosi miso".split():
168 t = getattr(self.dut, k)
169 v.append("{}>".format((yield t.o)) if (yield t.oe)
170 else "<{}".format((yield t.i)))
171 spi.append(" ".join(v))
172 yield self.dut.jtag.sel.eq(0)
174 yield self.dut.jtag.shift.eq(0)
177 def test_shift(self):
180 tdi = [0, 0, 1] # dummy from BYPASS TAPs and marker
181 tdi += [((bits - 1) >> j) & 1 for j in range(self.bits - 1, -1, -1)]
182 tdi += [(data >> j) & 1 for j in range(bits)]
183 tdi += [0, 0, 0, 0] # dummy from BYPASS TAPs
186 mg.run_simulation(self.dut, self.run_seq(tdi, tdo, spi))
192 class Spartan3(mg.Module):
193 macro = "BSCAN_SPARTAN3"
196 def __init__(self, platform):
197 platform.toolchain.bitgen_opt += " -g compress -g UnusedPin:Pullup"
198 self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
202 o_SHIFT=j2s.jtag.shift, o_SEL1=j2s.jtag.sel,
203 o_CAPTURE=j2s.jtag.capture,
204 o_DRCK1=j2s.jtag.tck,
205 o_TDI=j2s.jtag.tdi, i_TDO1=j2s.jtag.tdo,
208 platform.add_period_constraint(j2s.jtag.tck, 6)
211 class Spartan3A(Spartan3):
212 macro = "BSCAN_SPARTAN3A"
215 class Spartan6(mg.Module):
218 def __init__(self, platform):
219 platform.toolchain.bitgen_opt += " -g compress -g UnusedPin:Pullup"
220 self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
224 "BSCAN_SPARTAN6", p_JTAG_CHAIN=1,
225 o_SHIFT=j2s.jtag.shift, o_SEL=j2s.jtag.sel,
226 o_CAPTURE=j2s.jtag.capture,
228 o_TDI=j2s.jtag.tdi, i_TDO=j2s.jtag.tdo),
229 # mg.Instance("BUFG", i_I=clk, o_O=j2s.jtag.tck)
231 platform.add_period_constraint(j2s.jtag.tck, 6)
234 class Series7(mg.Module):
237 def __init__(self, platform):
238 platform.toolchain.bitstream_commands.extend([
239 "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
240 "set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]"
242 self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
246 "BSCANE2", p_JTAG_CHAIN=1,
247 o_SHIFT=j2s.jtag.shift, o_SEL=j2s.jtag.sel,
248 o_CAPTURE=j2s.jtag.capture,
250 o_TDI=j2s.jtag.tdi, i_TDO=j2s.jtag.tdo),
252 "STARTUPE2", i_CLK=0, i_GSR=0, i_GTS=0,
253 i_KEYCLEARB=0, i_PACK=1,
254 i_USRCCLKO=j2s.clk.o, i_USRCCLKTS=~j2s.clk.oe,
255 i_USRDONEO=1, i_USRDONETS=1),
256 # mg.Instance("BUFG", i_I=clk, o_O=j2s.jtag.tck)
258 platform.add_period_constraint(j2s.jtag.tck, 6)
261 platform.request("user_sma_gpio_p").eq(j2s.cs_n.i),
262 platform.request("user_sma_gpio_n").eq(j2s.clk.o),
263 platform.request("user_sma_clock_p").eq(j2s.mosi.o),
264 platform.request("user_sma_clock_n").eq(j2s.miso.i),
266 except mb.ConstraintError:
270 class Ultrascale(mg.Module):
273 def __init__(self, platform):
274 platform.toolchain.bitstream_commands.extend([
275 "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
276 "set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]",
278 self.submodules.j2s0 = j2s0 = JTAG2SPI()
279 self.submodules.j2s1 = j2s1 = JTAG2SPI(platform.request("spiflash"))
281 self.comb += mg.Cat(j2s0.mosi.i, j2s0.miso.i).eq(di)
283 mg.Instance("BSCANE2", p_JTAG_CHAIN=1,
284 o_SHIFT=j2s0.jtag.shift, o_SEL=j2s0.jtag.sel,
285 o_CAPTURE=j2s0.jtag.capture,
286 o_DRCK=j2s0.jtag.tck,
287 o_TDI=j2s0.jtag.tdi, i_TDO=j2s0.jtag.tdo),
288 mg.Instance("BSCANE2", p_JTAG_CHAIN=2,
289 o_SHIFT=j2s1.jtag.shift, o_SEL=j2s1.jtag.sel,
290 o_CAPTURE=j2s1.jtag.capture,
291 o_DRCK=j2s1.jtag.tck,
292 o_TDI=j2s1.jtag.tdi, i_TDO=j2s1.jtag.tdo),
293 mg.Instance("STARTUPE3", i_GSR=0, i_GTS=0,
294 i_KEYCLEARB=0, i_PACK=1,
295 i_USRDONEO=1, i_USRDONETS=1,
296 i_USRCCLKO=mg.Mux(j2s0.clk.oe, j2s0.clk.o, j2s1.clk.o),
297 i_USRCCLKTS=~(j2s0.clk.oe | j2s1.clk.oe),
298 i_FCSBO=j2s0.cs_n.o, i_FCSBTS=~j2s0.cs_n.oe,
300 i_DO=mg.Cat(j2s0.mosi.o, j2s0.miso.o, 0, 0),
301 i_DTS=mg.Cat(~j2s0.mosi.oe, ~j2s0.miso.oe, 1, 1))
303 platform.add_period_constraint(j2s0.jtag.tck, 6)
304 platform.add_period_constraint(j2s1.jtag.tck, 6)
307 class XilinxBscanSpi(xilinx.XilinxPlatform):
309 # (package-speedgrade, id): [cs_n, clk, mosi, miso, *pullups]
310 ("cp132", 1): ["M2", "N12", "N2", "N8"],
311 ("fg320", 1): ["U3", "U16", "T4", "N10"],
312 ("fg320", 2): ["V3", "U16", "T11", "V16"],
313 ("fg484", 1): ["Y4", "AA20", "AB14", "AB20"],
314 ("fgg484", 1): ["Y4", "AA20", "AB14", "AB20"],
315 ("fgg400", 1): ["Y2", "Y19", "W12", "W18"],
316 ("ftg256", 1): ["T2", "R14", "P10", "T14"],
317 ("ft256", 1): ["T2", "R14", "P10", "T14"],
318 ("fg400", 1): ["Y2", "Y19", "W12", "W18"],
319 ("cs484", 1): ["U7", "V17", "V13", "W17"],
320 ("qg144-2", 1): ["P38", "P70", "P64", "P65", "P62", "P61"],
321 ("cpg196-2", 1): ["P2", "N13", "P11", "N11", "N10", "P10"],
322 ("cpg236-1", 1): ["K19", None, "D18", "D19", "G18", "F18"],
323 ("csg484-2", 1): ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
324 ("csg324-2", 1): ["V3", "R15", "T13", "R13", "T14", "V14"],
325 ("csg324-1", 1): ["L13", None, "K17", "K18", "L14", "M14"],
326 ("fbg484-1", 1): ["T19", None, "P22", "R22", "P21", "R21"],
327 ("fbg484-1", 2): ["L16", None, "H18", "H19", "G18", "F19"],
328 ("fbg676-1", 1): ["C23", None, "B24", "A25", "B22", "A22"],
329 ("ffg901-1", 1): ["V26", None, "R30", "T30", "R28", "T28"],
330 ("ffg900-1", 1): ["U19", None, "P24", "R25", "R20", "R21"],
331 ("ffg1156-1", 1): ["V30", None, "AA33", "AA34", "Y33", "Y34"],
332 ("ffg1157-1", 1): ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
333 ("ffg1158-1", 1): ["C24", None, "A23", "A24", "B26", "A26"],
334 ("ffg1926-1", 1): ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
335 ("fhg1761-1", 1): ["AL36", None, "AM36", "AN36", "AJ36", "AJ37"],
336 ("flg1155-1", 1): ["AL28", None, "AE28", "AF28", "AJ29", "AJ30"],
337 ("flg1932-1", 1): ["V32", None, "T33", "R33", "U31", "T31"],
338 ("flg1926-1", 1): ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
340 ("ffva1156-2-e", 1): ["G26", None, "M20", "L20", "R21", "R22"],
341 ("ffva1156-2-e", "sayma"): ["K21", None, "M20", "L20", "R21", "R22"],
345 # bitstreams are named by die, package does not matter, speed grade
348 # chip: (package, id, standard, class)
349 "xc3s100e": ("cp132", 1, "LVCMOS33", Spartan3),
350 "xc3s1200e": ("fg320", 1, "LVCMOS33", Spartan3),
351 "xc3s1400a": ("fg484", 1, "LVCMOS33", Spartan3A),
352 "xc3s1400an": ("fgg484", 1, "LVCMOS33", Spartan3A),
353 "xc3s1600e": ("fg320", 1, "LVCMOS33", Spartan3),
354 "xc3s200a": ("fg320", 2, "LVCMOS33", Spartan3A),
355 "xc3s200an": ("ftg256", 1, "LVCMOS33", Spartan3A),
356 "xc3s250e": ("cp132", 1, "LVCMOS33", Spartan3),
357 "xc3s400a": ("fg320", 2, "LVCMOS33", Spartan3A),
358 "xc3s400an": ("fgg400", 1, "LVCMOS33", Spartan3A),
359 "xc3s500e": ("cp132", 1, "LVCMOS33", Spartan3),
360 "xc3s50a": ("ft256", 1, "LVCMOS33", Spartan3A),
361 "xc3s50an": ("ftg256", 1, "LVCMOS33", Spartan3A),
362 "xc3s700a": ("fg400", 1, "LVCMOS33", Spartan3A),
363 "xc3s700an": ("fgg484", 1, "LVCMOS33", Spartan3A),
364 "xc3sd1800a": ("cs484", 1, "LVCMOS33", Spartan3A),
365 "xc3sd3400a": ("cs484", 1, "LVCMOS33", Spartan3A),
367 "xc6slx100": ("csg484-2", 1, "LVCMOS33", Spartan6),
368 "xc6slx100t": ("csg484-2", 1, "LVCMOS33", Spartan6),
369 "xc6slx150": ("csg484-2", 1, "LVCMOS33", Spartan6),
370 "xc6slx150t": ("csg484-2", 1, "LVCMOS33", Spartan6),
371 "xc6slx16": ("cpg196-2", 1, "LVCMOS33", Spartan6),
372 "xc6slx25": ("csg324-2", 1, "LVCMOS33", Spartan6),
373 "xc6slx25t": ("csg324-2", 1, "LVCMOS33", Spartan6),
374 "xc6slx45": ("csg324-2", 1, "LVCMOS33", Spartan6),
375 "xc6slx45t": ("csg324-2", 1, "LVCMOS33", Spartan6),
376 "xc6slx4": ("cpg196-2", 1, "LVCMOS33", Spartan6),
377 "xc6slx4t": ("qg144-2", 1, "LVCMOS33", Spartan6),
378 "xc6slx75": ("csg484-2", 1, "LVCMOS33", Spartan6),
379 "xc6slx75t": ("csg484-2", 1, "LVCMOS33", Spartan6),
380 "xc6slx9": ("cpg196-2", 1, "LVCMOS33", Spartan6),
381 "xc6slx9t": ("qg144-2", 1, "LVCMOS33", Spartan6),
383 "xc7a100t": ("csg324-1", 1, "LVCMOS25", Series7),
384 "xc7a15t": ("cpg236-1", 1, "LVCMOS25", Series7),
385 "xc7a200t": ("fbg484-1", 1, "LVCMOS25", Series7),
386 "xc7a35t": ("cpg236-1", 1, "LVCMOS25", Series7),
387 "xc7a50t": ("cpg236-1", 1, "LVCMOS25", Series7),
388 "xc7a75t": ("csg324-1", 1, "LVCMOS25", Series7),
389 "xc7k160t": ("fbg484-1", 2, "LVCMOS25", Series7),
390 "xc7k325t": ("fbg676-1", 1, "LVCMOS25", Series7),
391 "xc7k325t-debug": ("ffg900-1", 1, "LVCMOS25", Series7),
392 "xc7k355t": ("ffg901-1", 1, "LVCMOS25", Series7),
393 "xc7k410t": ("fbg676-1", 1, "LVCMOS25", Series7),
394 "xc7k420t": ("ffg1156-1", 1, "LVCMOS25", Series7),
395 "xc7k480t": ("ffg1156-1", 1, "LVCMOS25", Series7),
396 "xc7k70t": ("fbg484-1", 2, "LVCMOS25", Series7),
397 "xc7v2000t": ("fhg1761-1", 1, "LVCMOS18", Series7),
398 "xc7v585t": ("ffg1157-1", 1, "LVCMOS18", Series7),
399 "xc7vh580t": ("flg1155-1", 1, "LVCMOS18", Series7),
400 "xc7vh870t": ("flg1932-1", 1, "LVCMOS18", Series7),
401 "xc7vx1140t": ("flg1926-1", 1, "LVCMOS18", Series7),
402 "xc7vx330t": ("ffg1157-1", 1, "LVCMOS18", Series7),
403 "xc7vx415t": ("ffg1157-1", 1, "LVCMOS18", Series7),
404 "xc7vx485t": ("ffg1157-1", 1, "LVCMOS18", Series7),
405 "xc7vx550t": ("ffg1158-1", 1, "LVCMOS18", Series7),
406 "xc7vx690t": ("ffg1157-1", 1, "LVCMOS18", Series7),
407 "xc7vx980t": ("ffg1926-1", 1, "LVCMOS18", Series7),
409 "xcku040": ("ffva1156-2-e", 1, "LVCMOS18", Ultrascale),
410 "xcku040-sayma": ("ffva1156-2-e", "sayma", "LVCMOS18", Ultrascale),
413 def __init__(self, device, pins, std, toolchain="ise"):
414 ios = [self.make_spi(0, pins, std, toolchain)]
415 if device == "xc7k325t-ffg900-1": # debug
417 ("user_sma_clock_p", 0, mb.Pins("L25"), mb.IOStandard("LVCMOS25")),
418 ("user_sma_clock_n", 0, mb.Pins("K25"), mb.IOStandard("LVCMOS25")),
419 ("user_sma_gpio_p", 0, mb.Pins("Y23"), mb.IOStandard("LVCMOS25")),
420 ("user_sma_gpio_n", 0, mb.Pins("Y24"), mb.IOStandard("LVCMOS25")),
422 xilinx.XilinxPlatform.__init__(self, device, ios, toolchain=toolchain)
425 def make_spi(i, pins, std, toolchain):
426 pu = "PULLUP" if toolchain == "ise" else "PULLUP TRUE"
427 pd = "PULLDOWN" if toolchain == "ise" else "PULLDOWN TRUE"
428 cs_n, clk, mosi, miso = pins[:4]
430 mb.Subsignal("cs_n", mb.Pins(cs_n), mb.Misc(pu)),
431 mb.Subsignal("mosi", mb.Pins(mosi), mb.Misc(pu)),
432 mb.Subsignal("miso", mb.Pins(miso), mb.Misc(pu)),
436 io.append(mb.Subsignal("clk", mb.Pins(clk), mb.Misc(pd)))
437 for i, p in enumerate(pins[4:]):
438 io.append(mb.Subsignal("pullup{}".format(i), mb.Pins(p),
443 def make(cls, target, errors=False):
444 pkg, id, std, Top = cls.pinouts[target]
445 pins = cls.packages[(pkg, id)]
446 device = target.split("-", 1)[0]
447 platform = cls("{}-{}".format(device, pkg), pins, std, Top.toolchain)
449 name = "bscan_spi_{}".format(target)
451 platform.build(top, build_name=name)
452 except Exception as e:
453 print(("ERROR: xilinx_bscan_spi build failed "
454 "for {}: {}").format(target, e))
459 if __name__ == "__main__":
461 import multiprocessing
462 p = argparse.ArgumentParser(description="build bscan_spi bitstreams "
463 "for openocd jtagspi flash driver")
464 p.add_argument("device", nargs="*",
465 default=sorted(list(XilinxBscanSpi.pinouts)),
466 help="build for these devices (default: %(default)s)")
467 p.add_argument("-p", "--parallel", default=1, type=int,
468 help="number of parallel builds (default: %(default)s)")
469 args = p.parse_args()
470 pool = multiprocessing.Pool(args.parallel)
471 pool.map(XilinxBscanSpi.make, args.device, chunksize=1)