4246aa2f3af3f98fa2a6f8e9233d368492fa3d77
[fw/openocd] / contrib / loaders / flash / fpga / xilinx_bscan_spi.py
1 #!/usr/bin/python3
2 #
3 #  Copyright (C) 2015 Robert Jordens <jordens@gmail.com>
4 #
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.
9 #
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.
14 #
15
16 import unittest
17
18 import migen as mg
19 import migen.build.generic_platform as mb
20 from migen.genlib import io
21 from migen.build import xilinx
22
23
24 """
25 This migen script produces proxy bitstreams to allow programming SPI flashes
26 behind FPGAs.
27
28 Bitstream binaries built with this script are available at:
29 https://github.com/jordens/bscan_spi_bitstreams
30
31 A JTAG2SPI transfer consists of:
32
33 1. an arbitrary number of 0 bits (from BYPASS registers in front of the
34    JTAG2SPI DR)
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
39    BYPASS registers)
40
41 Notes:
42
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.
49
50 https://github.com/m-labs/migen
51 """
52
53
54 class JTAG2SPI(mg.Module):
55     def __init__(self, spi=None, bits=32):
56         self.jtag = mg.Record([
57             ("sel", 1),
58             ("shift", 1),
59             ("capture", 1),
60             ("tck", 1),
61             ("tdi", 1),
62             ("tdo", 1),
63         ])
64         self.cs_n = mg.TSTriple()
65         self.clk = mg.TSTriple()
66         self.mosi = mg.TSTriple()
67         self.miso = mg.TSTriple()
68
69         # # #
70
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")
77         if spi is not None:
78             self.specials += [
79                     self.cs_n.get_tristate(spi.cs_n),
80                     self.mosi.get_tristate(spi.mosi),
81                     self.miso.get_tristate(spi.miso),
82             ]
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)
86         self.comb += [
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),
92                 self.miso.oe.eq(0),
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),
99         ]
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
105         #   SPI: set MISO
106         # 2 (falling TCK, rising CLK):
107         #   SPI: sample MOSI
108         #   JTAG2SPI (BSCAN primitive): sample MISO -> set TDO
109         # 3 (rising TCK, falling CLK):
110         #   JTAG adapter: sample TDO
111         self.fsm.act("IDLE",
112                 mg.If(self.jtag.tdi & self.jtag.sel & self.jtag.shift,
113                     mg.NextState("HEAD")
114                 )
115         )
116         self.fsm.act("HEAD",
117                 mg.If(head == 0,
118                     mg.NextState("XFER")
119                 )
120         )
121         self.fsm.act("XFER",
122                 mg.If(bits == 0,
123                     mg.NextState("IDLE")
124                 ),
125         )
126         self.sync += [
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)),
131                     head.eq(head - 1)
132                 ),
133                 mg.If(self.fsm.ongoing("XFER"),
134                     bits.eq(bits - 1)
135                 )
136         ]
137
138
139 class JTAG2SPITest(unittest.TestCase):
140     def setUp(self):
141         self.bits = 8
142         self.dut = JTAG2SPI(bits=self.bits)
143
144     def test_instantiate(self):
145         pass
146
147     def test_initial_conditions(self):
148         def check():
149             yield
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())
155
156     def test_enable(self):
157         def check():
158             yield self.dut.jtag.sel.eq(1)
159             yield self.dut.jtag.shift.eq(1)
160             yield
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())
166
167     def run_seq(self, tdi, tdo, spi=None):
168         yield self.dut.jtag.sel.eq(1)
169         yield
170         yield self.dut.jtag.shift.eq(1)
171         for di in tdi:
172             yield self.dut.jtag.tdi.eq(di)
173             yield
174             tdo.append((yield self.dut.jtag.tdo))
175             if spi is not None:
176                 v = []
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)
183         yield
184         yield self.dut.jtag.shift.eq(0)
185         yield
186
187     def test_shift(self):
188         bits = 8
189         data = 0x81
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
194         tdo = []
195         spi = []
196         mg.run_simulation(self.dut, self.run_seq(tdi, tdo, spi))
197         # print(tdo)
198         for l in spi:
199             print(l)
200
201
202 class Spartan3(mg.Module):
203     macro = "BSCAN_SPARTAN3"
204     toolchain = "ise"
205
206     def __init__(self, platform):
207         platform.toolchain.bitgen_opt += " -g compress -g UnusedPin:Pullup"
208         self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
209         self.specials += [
210                 mg.Instance(
211                     self.macro,
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,
216                     i_TDO2=0),
217         ]
218         platform.add_period_constraint(j2s.jtag.tck, 6)
219
220
221 class Spartan3A(Spartan3):
222     macro = "BSCAN_SPARTAN3A"
223
224
225 class Spartan6(mg.Module):
226     toolchain = "ise"
227
228     def __init__(self, platform):
229         platform.toolchain.bitgen_opt += " -g compress -g UnusedPin:Pullup"
230         self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
231         # clk = mg.Signal()
232         self.specials += [
233                 mg.Instance(
234                     "BSCAN_SPARTAN6", p_JTAG_CHAIN=1,
235                     o_SHIFT=j2s.jtag.shift, o_SEL=j2s.jtag.sel,
236                     o_CAPTURE=j2s.jtag.capture,
237                     o_DRCK=j2s.jtag.tck,
238                     o_TDI=j2s.jtag.tdi, i_TDO=j2s.jtag.tdo),
239                 # mg.Instance("BUFG", i_I=clk, o_O=j2s.jtag.tck)
240         ]
241         platform.add_period_constraint(j2s.jtag.tck, 6)
242
243
244 class Series7(mg.Module):
245     toolchain = "vivado"
246
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]"
251         ])
252         self.submodules.j2s = j2s = JTAG2SPI(platform.request("spiflash"))
253         # clk = mg.Signal()
254         self.specials += [
255                 mg.Instance(
256                     "BSCANE2", p_JTAG_CHAIN=1,
257                     o_SHIFT=j2s.jtag.shift, o_SEL=j2s.jtag.sel,
258                     o_CAPTURE=j2s.jtag.capture,
259                     o_DRCK=j2s.jtag.tck,
260                     o_TDI=j2s.jtag.tdi, i_TDO=j2s.jtag.tdo),
261                 mg.Instance(
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)
267         ]
268         platform.add_period_constraint(j2s.jtag.tck, 6)
269         try:
270             self.comb += [
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),
275             ]
276         except mb.ConstraintError:
277             pass
278
279
280 class Ultrascale(mg.Module):
281     toolchain = "vivado"
282
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]",
287         ])
288         self.submodules.j2s0 = j2s0 = JTAG2SPI()
289         self.submodules.j2s1 = j2s1 = JTAG2SPI(platform.request("spiflash"))
290         di = mg.Signal(4)
291         self.comb += mg.Cat(j2s0.mosi.i, j2s0.miso.i).eq(di)
292         self.specials += [
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,
309                     o_DI=di,
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))
312         ]
313         platform.add_period_constraint(j2s0.jtag.tck, 6)
314         platform.add_period_constraint(j2s1.jtag.tck, 6)
315
316
317 class XilinxBscanSpi(xilinx.XilinxPlatform):
318     packages = {
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"],
349
350         ("ffva1156-2-e", 1): ["G26", None, "M20", "L20", "R21", "R22"],
351         ("ffva1156-2-e", "sayma"): ["K21", None, "M20", "L20", "R21", "R22"],
352     }
353
354     pinouts = {
355         # bitstreams are named by die, package does not matter, speed grade
356         # should not matter.
357         #
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),
376
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),
392
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),
418
419         "xcku040": ("ffva1156-2-e", 1, "LVCMOS18", Ultrascale),
420         "xcku040-sayma": ("ffva1156-2-e", "sayma", "LVCMOS18", Ultrascale),
421     }
422
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
426             ios += [
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")),
431             ]
432         xilinx.XilinxPlatform.__init__(self, device, ios, toolchain=toolchain)
433
434     @staticmethod
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]
439         io = ["spiflash", i,
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)),
443             mb.IOStandard(std),
444             ]
445         if clk:
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),
449                                 mb.Misc(pu)))
450         return io
451
452     @classmethod
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)
458         top = Top(platform)
459         name = "bscan_spi_{}".format(target)
460         try:
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))
465             if errors:
466                 raise
467
468
469 if __name__ == "__main__":
470     import argparse
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)