add IOConn options banksel, pullup and pulldown into TAP._elaborate_ios
[c4m-jtag.git] / c4m / nmigen / jtag / tap.py
1 #!/usr/bin/env python3
2 #!/bin/env python3
3 # Copyright (C) 2019,2020,2021 Staf Verhaegen <staf@fibraservi.eu>
4 # Copyright (C) 2021,2022 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
5 # Funded by NLnet and NGI POINTER under EU Grants 871528 and 957073
6
7 import os, textwrap
8 from enum import Enum, auto
9
10 from nmigen import (Elaboratable, Signal, Module, ClockDomain, Cat, Record,
11 Const, Mux)
12 from nmigen.hdl.rec import Direction, Layout
13 from nmigen.tracer import get_var_name
14
15 from nmigen_soc.wishbone import Interface as WishboneInterface
16
17 from .bus import Interface, DMIInterface
18
19 __all__ = [
20 "TAP", "ShiftReg", "IOType", "IOConn",
21 ]
22
23
24 class _FSM(Elaboratable):
25 """TAP subblock for the FSM"""
26 def __init__(self, *, bus):
27 self.isir = Signal()
28 self.isdr = Signal()
29 self.capture = Signal()
30 self.shift = Signal()
31 self.update = Signal()
32
33 # JTAG uses both edges of the incoming clock (TCK). set them up here
34 self.posjtag = ClockDomain("posjtag", local=True)
35 self.negjtag = ClockDomain("negjtag", local=True, clk_edge="neg")
36
37 self._bus = bus
38
39 def elaborate(self, platform):
40 m = Module()
41
42 rst = Signal()
43 m.d.comb += [
44 self.posjtag.clk.eq(self._bus.tck),
45 self.posjtag.rst.eq(rst),
46 self.negjtag.clk.eq(self._bus.tck),
47 self.negjtag.rst.eq(rst),
48 ]
49
50 # Make local clock domain optionally using trst of JTAG bus as reset
51 if hasattr(self._bus, "trst"):
52 m.domains.local = local = ClockDomain(local=True)
53 m.d.comb += local.rst.eq(self._bus.trst)
54 else:
55 m.domains.local = local = ClockDomain(local=True, reset_less=True)
56 m.d.comb += local.clk.eq(self._bus.tck)
57
58 with m.FSM(domain="local") as fsm:
59 with m.State("TestLogicReset"):
60 # Be sure to reset isir, isdr
61 m.d.local += [
62 self.isir.eq(0),
63 self.isdr.eq(0),
64 ]
65 with m.If(self._bus.tms == 0):
66 m.next = "RunTestIdle"
67 with m.State("RunTestIdle"):
68 # Be sure to reset isir, isdr
69 m.d.local += [
70 self.isir.eq(0),
71 self.isdr.eq(0),
72 ]
73 with m.If(self._bus.tms == 1):
74 m.next = "SelectDRScan"
75 with m.State("SelectDRScan"):
76 with m.If(self._bus.tms == 0):
77 m.d.local += self.isdr.eq(1)
78 m.next = "CaptureState"
79 with m.Else():
80 m.next = "SelectIRScan"
81 with m.State("SelectIRScan"):
82 with m.If(self._bus.tms == 0):
83 m.d.local += self.isir.eq(1)
84 m.next = "CaptureState"
85 with m.Else():
86 m.next = "TestLogicReset"
87 with m.State("CaptureState"):
88 with m.If(self._bus.tms == 0):
89 m.next = "ShiftState"
90 with m.Else():
91 m.next = "Exit1"
92 with m.State("ShiftState"):
93 with m.If(self._bus.tms == 1):
94 m.next = "Exit1"
95 with m.State("Exit1"):
96 with m.If(self._bus.tms == 0):
97 m.next = "Pause"
98 with m.Else():
99 m.next = "UpdateState"
100 with m.State("Pause"):
101 with m.If(self._bus.tms == 1):
102 m.next = "Exit2"
103 with m.State("Exit2"):
104 with m.If(self._bus.tms == 0):
105 m.next = "ShiftState"
106 with m.Else():
107 m.next = "UpdateState"
108 with m.State("UpdateState"):
109 m.d.local += [
110 self.isir.eq(0),
111 self.isdr.eq(0),
112 ]
113 with m.If(self._bus.tms == 0):
114 m.next = "RunTestIdle"
115 with m.Else():
116 m.next = "SelectDRScan"
117
118 m.d.comb += [
119 rst.eq(fsm.ongoing("TestLogicReset")),
120 self.capture.eq(fsm.ongoing("CaptureState")),
121 self.shift.eq(fsm.ongoing("ShiftState")),
122 self.update.eq(fsm.ongoing("UpdateState")),
123 ]
124
125 return m
126
127
128 class _IRBlock(Elaboratable):
129 """TAP subblock for handling the IR shift register"""
130 def __init__(self, *, ir_width, cmd_idcode,
131 tdi, capture, shift, update,
132 name):
133 self.name = name
134 self.ir = Signal(ir_width, reset=cmd_idcode)
135 self.tdo = Signal()
136
137 self._tdi = tdi
138 self._capture = capture
139 self._shift = shift
140 self._update = update
141
142 def elaborate(self, platform):
143 m = Module()
144
145 shift_ir = Signal(len(self.ir), reset_less=True)
146
147 m.d.comb += self.tdo.eq(self.ir[0])
148 with m.If(self._capture):
149 m.d.posjtag += shift_ir.eq(self.ir)
150 with m.Elif(self._shift):
151 m.d.posjtag += shift_ir.eq(Cat(shift_ir[1:], self._tdi))
152 with m.Elif(self._update):
153 # For ir we only update it on the rising edge of clock
154 # to avoid that we already have the new ir value when still in
155 # Update state
156 m.d.posjtag += self.ir.eq(shift_ir)
157
158 return m
159
160
161 class IOType(Enum):
162 In = auto()
163 Out = auto()
164 TriOut = auto()
165 InTriOut = auto()
166
167
168 class IOConn(Record):
169 lengths = {
170 IOType.In: 1,
171 IOType.Out: 1,
172 IOType.TriOut: 2,
173 IOType.InTriOut: 3,
174 }
175
176 """TAP subblock representing the interface for an JTAG IO cell.
177 It contains signals to connect to the core and to the pad
178
179 This object is normally only allocated and returned from ``TAP.add_io``
180 It is a Record subclass.
181
182 Attributes
183 ----------
184 core: subrecord with signals for the core
185 i: Signal(1), present only for IOType.In and IOType.InTriOut.
186 Signal input to core with pad input value.
187 o: Signal(1), present only for IOType.Out, IOType.TriOut and
188 IOType.InTriOut.
189 Signal output from core with the pad output value.
190 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
191 Signal output from core with the pad output enable value.
192 pad: subrecord with for the pad
193 i: Signal(1), present only for IOType.In and IOType.InTriOut
194 Output from pad with pad input value for core.
195 o: Signal(1), present only for IOType.Out, IOType.TriOut and
196 IOType.InTriOut.
197 Input to pad with pad output value.
198 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
199 Input to pad with pad output enable value.
200
201 bank select, pullup and pulldown may also be optionally added
202 """
203 @staticmethod
204 def layout(iotype, banksel=0, pullup=False, pulldown=False):
205 sigs = []
206 if iotype in (IOType.In, IOType.InTriOut):
207 sigs.append(("i", 1))
208 if iotype in (IOType.Out, IOType.TriOut, IOType.InTriOut):
209 sigs.append(("o", 1))
210 if iotype in (IOType.TriOut, IOType.InTriOut):
211 sigs.append(("oe", 1))
212 if banksel > 0:
213 sigs.append(("sel", banksel))
214 if pullup:
215 sigs.append(("pu", 1))
216 if pulldown:
217 sigs.append(("pd", 1))
218
219 return Layout((("core", sigs), ("pad", sigs)))
220
221 def __init__(self, *, iotype, name=None, banksel=0,
222 pullup=False, pulldown=False,
223 src_loc_at=0):
224 layout = self.__class__.layout(iotype, banksel, pullup, pulldown)
225 super().__init__(layout, name=name, src_loc_at=src_loc_at+1)
226
227 self._iotype = iotype
228 self._banksel = banksel
229 self._pullup = pullup
230 self._pulldown = pulldown
231
232
233 class _IDBypassBlock(Elaboratable):
234 """TAP subblock for the ID shift register"""
235 def __init__(self, *, manufacturer_id, part_number, version,
236 tdi, capture, shift, update, bypass,
237 name):
238 self.name = name
239 if (not isinstance(manufacturer_id, Const) and
240 len(manufacturer_id) != 11):
241 raise ValueError("manufacturer_id has to be Const of length 11")
242 if not isinstance(part_number, Const) and len(manufacturer_id) != 16:
243 raise ValueError("part_number has to be Const of length 16")
244 if not isinstance(version, Const) and len(version) != 4:
245 raise ValueError("version has to be Const of length 4")
246 self._id = Cat(Const(1,1), manufacturer_id, part_number, version)
247
248 self.tdo = Signal(name=name+"_tdo")
249
250 self._tdi = tdi
251 self._capture = capture
252 self._shift = shift
253 self._update = update
254 self._bypass = bypass
255
256 def elaborate(self, platform):
257 m = Module()
258
259 sr = Signal(32, reset_less=True, name=self.name+"_sr")
260
261 # Local signals for the module
262 _tdi = Signal()
263 _capture = Signal()
264 _shift = Signal()
265 _update = Signal()
266 _bypass = Signal()
267
268 m.d.comb += [
269 _tdi.eq(self._tdi),
270 _capture.eq(self._capture),
271 _shift.eq(self._shift),
272 _update.eq(self._update),
273 _bypass.eq(self._bypass),
274 self.tdo.eq(sr[0]),
275 ]
276
277 with m.If(_capture):
278 m.d.posjtag += sr.eq(self._id)
279 with m.Elif(_shift):
280 with m.If(_bypass):
281 m.d.posjtag += sr[0].eq(_tdi)
282 with m.Else():
283 m.d.posjtag += sr.eq(Cat(sr[1:], _tdi))
284
285 return m
286
287
288 class ShiftReg(Record):
289 """Object with interface for extra shift registers on a TAP.
290
291 Parameters
292 ----------
293 sr_length : int
294 cmds : int, default=1
295 The number of corresponding JTAG instructions
296
297 This object is normally only allocated and returned from ``TAP.add_shiftreg``
298 It is a Record subclass.
299
300 Attributes
301 ----------
302 i: length=sr_length, FANIN
303 The input data sampled during capture state of the TAP
304 ie: length=cmds, FANOUT
305 Indicates that data is to be sampled by the JTAG TAP and
306 should be held stable. The bit indicates the corresponding
307 instruction for which data is asked.
308 This signal is kept high for a whole JTAG TAP clock cycle
309 and may thus be kept higher for more than one clock cycle
310 on the domain where ShiftReg is used.
311 The JTAG protocol does not allow insertion of wait states
312 so data need to be provided before ie goes down. The speed
313 of the response will determine the max. frequency for the
314 JTAG interface.
315 o: length=sr_length, FANOUT
316 The value of the shift register.
317 oe: length=cmds, FANOUT
318 Indicates that output is stable and can be sampled downstream because
319 JTAG TAP is in the Update state. The bit indicates the corresponding
320 instruction. The bit is only kept high for one clock cycle.
321 """
322 def __init__(self, *, sr_length, cmds=1, name=None, src_loc_at=0):
323 layout = [
324 ("i", sr_length, Direction.FANIN),
325 ("ie", cmds, Direction.FANOUT),
326 ("o", sr_length, Direction.FANOUT),
327 ("oe", cmds, Direction.FANOUT),
328 ]
329 super().__init__(layout, name=name, src_loc_at=src_loc_at+1)
330
331
332 class TAP(Elaboratable):
333 #TODO: Document TAP
334 def __init__(self, *, with_reset=False, ir_width=None,
335 manufacturer_id=Const(0b10001111111, 11),
336 part_number=Const(1, 16),
337 version=Const(0, 4),
338 name=None, src_loc_at=0):
339 assert((ir_width is None) or (isinstance(ir_width, int) and
340 ir_width >= 2))
341 assert(len(version) == 4)
342
343 if name is None:
344 name = get_var_name(depth=src_loc_at+2, default="TAP")
345 self.name = name
346 self.bus = Interface(with_reset=with_reset, name=self.name+"_bus",
347 src_loc_at=src_loc_at+1)
348
349 ##
350
351 self._ir_width = ir_width
352 self._manufacturer_id = manufacturer_id
353 self._part_number = part_number
354 self._version = version
355
356 self._ircodes = [0, 1, 2] # Already taken codes, all ones added at end
357
358 self._ios = []
359 self._srs = []
360 self._wbs = []
361 self._dmis = []
362
363 def elaborate(self, platform):
364 m = Module()
365
366 # Determine ir_width if not fixed.
367 ir_max = max(self._ircodes) + 1 # One extra code needed with all ones
368 ir_width = len("{:b}".format(ir_max))
369 if self._ir_width is not None:
370 assert self._ir_width >= ir_width, "Specified JTAG IR width " \
371 "not big enough for allocated shiift registers"
372 ir_width = self._ir_width
373
374 # TODO: Make commands numbers configurable
375 cmd_extest = 0
376 cmd_intest = 0
377 cmd_idcode = 1
378 cmd_sample = 2
379 cmd_preload = 2
380 cmd_bypass = 2**ir_width - 1 # All ones
381
382 m.submodules.fsm = fsm = _FSM(bus=self.bus)
383 m.domains.posjtag = fsm.posjtag
384 m.domains.negjtag = fsm.negjtag
385
386 # IR block
387 select_ir = fsm.isir
388 m.submodules.irblock = irblock = _IRBlock(
389 ir_width=ir_width, cmd_idcode=cmd_idcode, tdi=self.bus.tdi,
390 capture=(fsm.isir & fsm.capture),
391 shift=(fsm.isir & fsm.shift),
392 update=(fsm.isir & fsm.update),
393 name=self.name+"_ir",
394 )
395 ir = irblock.ir
396
397 # ID block
398 select_id = Signal()
399 id_bypass = Signal()
400 m.d.comb += select_id.eq(fsm.isdr &
401 ((ir == cmd_idcode) | (ir == cmd_bypass)))
402 m.d.comb += id_bypass.eq(ir == cmd_bypass)
403 m.submodules.idblock = idblock = _IDBypassBlock(
404 manufacturer_id=self._manufacturer_id,
405 part_number=self._part_number,
406 version=self._version, tdi=self.bus.tdi,
407 capture=(select_id & fsm.capture),
408 shift=(select_id & fsm.shift),
409 update=(select_id & fsm.update),
410 bypass=id_bypass,
411 name=self.name+"_id",
412 )
413
414 # IO (Boundary scan) block
415 io_capture = Signal()
416 io_shift = Signal()
417 io_update = Signal()
418 io_bd2io = Signal()
419 io_bd2core = Signal()
420 sample = (ir == cmd_extest) | (ir == cmd_sample)
421 preload = (ir == cmd_preload)
422 select_io = fsm.isdr & (sample | preload)
423 m.d.comb += [
424 io_capture.eq(sample & fsm.capture), # Don't capture if not sample
425 # (like for PRELOAD)
426 io_shift.eq(select_io & fsm.shift),
427 io_update.eq(select_io & fsm.update),
428 io_bd2io.eq(ir == cmd_extest),
429 io_bd2core.eq(ir == cmd_intest),
430 ]
431 io_tdo = self._elaborate_ios(
432 m=m,
433 capture=io_capture, shift=io_shift, update=io_update,
434 bd2io=io_bd2io, bd2core=io_bd2core,
435 )
436
437 # chain tdo: select as appropriate, to go into into shiftregs
438 tdo = Signal(name=self.name+"_tdo")
439 with m.If(select_ir):
440 m.d.comb += tdo.eq(irblock.tdo)
441 with m.Elif(select_id):
442 m.d.comb += tdo.eq(idblock.tdo)
443 with m.Elif(select_io):
444 m.d.comb += tdo.eq(io_tdo)
445
446 # shiftregs block
447 self._elaborate_shiftregs(
448 m, capture=fsm.capture, shift=fsm.shift, update=fsm.update,
449 ir=irblock.ir, tdo_jtag=tdo
450 )
451
452 # wishbone
453 self._elaborate_wishbones(m)
454
455 # DMI (Debug Memory Interface)
456 self._elaborate_dmis(m)
457
458 return m
459
460 def add_dmi(self, *, ircodes, address_width=8, data_width=64,
461 domain="sync", name=None):
462 """Add a DMI interface
463
464 * writing to DMIADDR will automatically trigger a DMI READ.
465 the DMI address does not alter (so writes can be done at that addr)
466 * reading from DMIREAD triggers a DMI READ at the current DMI addr
467 the address is automatically incremented by 1 after.
468 * writing to DMIWRITE triggers a DMI WRITE at the current DMI addr
469 the address is automatically incremented by 1 after.
470
471 Parameters:
472 -----------
473 ircodes: sequence of three integer for the JTAG IR codes;
474 they represent resp. DMIADDR, DMIREAD and DMIWRITE.
475 First code has a shift register of length 'address_width',
476 the two other codes share a shift register of length
477 data_width.
478
479 address_width: width of the address
480 data_width: width of the data
481
482 Returns:
483 dmi: soc.debug.dmi.DMIInterface
484 The DMI interface
485 """
486 if len(ircodes) != 3:
487 raise ValueError("3 IR Codes have to be provided")
488
489 if name is None:
490 name = "dmi" + str(len(self._dmis))
491
492 # add 2 shift registers: one for addr, one for data.
493 sr_addr = self.add_shiftreg(ircode=ircodes[0], length=address_width,
494 domain=domain, name=name+"_addrsr")
495 sr_data = self.add_shiftreg(ircode=ircodes[1:], length=data_width,
496 domain=domain, name=name+"_datasr")
497
498 dmi = DMIInterface(name=name)
499 self._dmis.append((sr_addr, sr_data, dmi, domain))
500
501 return dmi
502
503 def _elaborate_dmis(self, m):
504 for sr_addr, sr_data, dmi, domain in self._dmis:
505 cd = m.d[domain]
506 m.d.comb += sr_addr.i.eq(dmi.addr_i)
507
508 with m.FSM(domain=domain) as ds:
509
510 # detect mode based on whether jtag addr or data read/written
511 with m.State("IDLE"):
512 with m.If(sr_addr.oe): # DMIADDR code
513 cd += dmi.addr_i.eq(sr_addr.o)
514 m.next = "READ"
515 with m.Elif(sr_data.oe[0]): # DMIREAD code
516 # If data is
517 cd += dmi.addr_i.eq(dmi.addr_i + 1)
518 m.next = "READ"
519 with m.Elif(sr_data.oe[1]): # DMIWRITE code
520 cd += dmi.din.eq(sr_data.o)
521 m.next = "WRRD"
522
523 # req_i raises for 1 clock
524 with m.State("READ"):
525 m.next = "READACK"
526
527 # wait for read ack
528 with m.State("READACK"):
529 with m.If(dmi.ack_o):
530 # Store read data in sr_data.i hold till next read
531 cd += sr_data.i.eq(dmi.dout)
532 m.next = "IDLE"
533
534 # req_i raises for 1 clock
535 with m.State("WRRD"):
536 m.next = "WRRDACK"
537
538 # wait for write ack
539 with m.State("WRRDACK"):
540 with m.If(dmi.ack_o):
541 cd += dmi.addr_i.eq(dmi.addr_i + 1)
542 m.next = "READ" # for readwrite
543
544 # set DMI req and write-enable based on ongoing FSM states
545 m.d.comb += [
546 dmi.req_i.eq(ds.ongoing("READ") | ds.ongoing("WRRD")),
547 dmi.we_i.eq(ds.ongoing("WRRD")),
548 ]
549
550 def add_io(self, *, iotype, name=None, src_loc_at=0,
551 banksel=0, pullup=False, pulldown=False):
552 """Add a io cell to the boundary scan chain
553
554 Parameters:
555 - iotype: :class:`IOType` enum.
556
557 Returns:
558 - :class:`IOConn`
559 """
560 if name is None:
561 name = "ioconn" + str(len(self._ios))
562
563 ioconn = IOConn(iotype=iotype, banksel=banksel,
564 pullup=pullup, pulldown=pulldown,
565 name=name, src_loc_at=src_loc_at+1)
566 self._ios.append(ioconn)
567 return ioconn
568
569 def _elaborate_ios(self, *, m, capture, shift, update, bd2io, bd2core):
570 length = sum(IOConn.lengths[conn._iotype] for conn in self._ios)
571 # note: the starting points where each IOConn is placed into
572 # the Shift Register depends *specifically* on the type (parameters)
573 # of each IOConn, and therefore on all IOConn(s) that came before it
574 # [prior calls to add_io]. this function consistently follows
575 # the exact same pattern in the exact same sequence every time,
576 # to compute consistent offsets. developers must do the same:
577 # note that each length depends on *all* parameters:
578 # IOtype, banksel, pullup *and* pulldown.
579
580 # pre-compute the length of the IO shift registers needed.
581 # relies on Record.len() returning the total bit-width including
582 # all Signals
583 length = sum(len(conn) for conn in self._ios)
584 if length == 0:
585 return self.bus.tdi
586
587 io_sr = Signal(length)
588 io_bd = Signal(length)
589
590 # Boundary scan "capture" mode. makes I/O status available via SR
591 with m.If(capture):
592 iol = []
593 idx = 0
594 for conn in self._ios:
595 # in appropriate sequence: In/TriOut has pad.i,
596 # Out.InTriOut has everything, Out and TriOut have core.o
597 if conn._iotype in [IOType.In, IOType.InTriOut]:
598 iol.append(conn.pad.i)
599 if conn._iotype in [IOType.Out, IOType.InTriOut]:
600 iol.append(conn.core.o)
601 if conn._iotype in [IOType.TriOut, IOType.InTriOut]:
602 iol.append(conn.core.oe)
603 # now also banksel, pullup and pulldown from core are added
604 if conn._banksel != 0:
605 iol.append(conn.core.sel)
606 idx += 1
607 if conn._pullup:
608 iol.append(conn.core.pu)
609 idx += 1
610 if conn._pulldown:
611 iol.append(conn.core.pd)
612 idx += 1
613 # help with length double-check
614 idx += IOConn.lengths[conn._iotype] # fails if wrong type
615 assert idx == length, "Internal error, length mismatch"
616 m.d.posjtag += io_sr.eq(Cat(*iol)) # assigns all io_sr in one hit
617
618 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
619 with m.Elif(shift):
620 m.d.posjtag += io_sr.eq(Cat(self.bus.tdi, io_sr[:-1]))
621
622 # "Update" mode
623 with m.Elif(update):
624 m.d.negjtag += io_bd.eq(io_sr)
625
626 # sets up IO (pad<->core) or in testing mode depending on requested
627 # mode, via Muxes controlled by bd2core and bd2io
628 # for each IOConn, the number of bits needed from io_bd will vary
629 # and is computed on-the-fly, here. it is up to the developer to
630 # keep track of where each IO pad configuration starts and ends
631 # in the Shift Register (TODO: provide a dictionary of starting points)
632 idx = 0
633 for conn in self._ios:
634 # mux the I/O/OE depending on IOType
635 if conn._iotype == IOType.In:
636 m.d.comb += conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i))
637 idx += 1
638 elif conn._iotype == IOType.Out:
639 m.d.comb += conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o))
640 idx += 1
641 elif conn._iotype == IOType.TriOut:
642 m.d.comb += [
643 conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o)),
644 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+1], conn.core.oe)),
645 ]
646 idx += 2
647 elif conn._iotype == IOType.InTriOut:
648 m.d.comb += [
649 conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i)),
650 conn.pad.o.eq(Mux(bd2io, io_bd[idx+1], conn.core.o)),
651 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+2], conn.core.oe)),
652 ]
653 idx += 3
654 else:
655 raise("Internal error")
656 # optional mux of banksel, pullup and pulldown. note that idx
657 # advances each time, so that io_bd[idx] comes from the right point
658 comb = m.d.comb
659 if conn._banksel != 0:
660 s, e = (idx, idx+conn._banksel) # banksel can be multi-bit
661 comb += conn.pad.sel.eq(Mux(bd2io, io_bd[s:e], conn.core.sel))
662 idx = e
663 if conn._pullup:
664 comb += conn.pad.pu.eq(Mux(bd2io, io_bd[idx], conn.core.pu))
665 idx += 1
666 if conn._pulldown:
667 comb += conn.pad.pd.eq(Mux(bd2io, io_bd[idx], conn.core.pd))
668 idx += 1
669 assert idx == length, "Internal error"
670
671 # return the last bit of the shift register, for output on tdo
672 return io_sr[-1]
673
674 def add_shiftreg(self, *, ircode, length, domain="sync", name=None,
675 src_loc_at=0):
676 """Add a shift register to the JTAG interface
677
678 Parameters:
679 - ircode: code(s) for the IR; int or sequence of ints. In the latter
680 case this shiftreg is shared between different IR codes.
681 - length: the length of the shift register
682 - domain: the domain on which the signal will be used"""
683
684 try:
685 ir_it = iter(ircode)
686 ircodes = ircode
687 except TypeError:
688 ir_it = ircodes = (ircode,)
689 for _ircode in ir_it:
690 if not isinstance(_ircode, int) or _ircode <= 0:
691 raise ValueError("IR code '{}' is not an int "
692 "greater than 0".format(_ircode))
693 if _ircode in self._ircodes:
694 raise ValueError("IR code '{}' already taken".format(_ircode))
695
696 self._ircodes.extend(ircodes)
697
698 if name is None:
699 name = "sr{}".format(len(self._srs))
700 sr = ShiftReg(sr_length=length, cmds=len(ircodes), name=name,
701 src_loc_at=src_loc_at+1)
702 self._srs.append((ircodes, domain, sr))
703
704 return sr
705
706 def _elaborate_shiftregs(self, m, capture, shift, update, ir, tdo_jtag):
707 # tdos is tuple of (tdo, tdo_en) for each shiftreg
708 tdos = []
709 for ircodes, domain, sr in self._srs:
710 reg = Signal(len(sr.o), name=sr.name+"_reg")
711 m.d.comb += sr.o.eq(reg)
712
713 isir = Signal(len(ircodes), name=sr.name+"_isir")
714 sr_capture = Signal(name=sr.name+"_capture")
715 sr_shift = Signal(name=sr.name+"_shift")
716 sr_update = Signal(name=sr.name+"_update")
717 m.d.comb += [
718 isir.eq(Cat(ir == ircode for ircode in ircodes)),
719 sr_capture.eq((isir != 0) & capture),
720 sr_shift.eq((isir != 0) & shift),
721 sr_update.eq((isir != 0) & update),
722 ]
723
724 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
725 # clockdomain latch update in `domain` clockdomain and see when
726 # it has falling edge.
727 # At that edge put isir in sr.oe for one `domain` clockdomain
728 # Using this custom sync <> JTAG domain synchronization avoids
729 # the use of more generic but also higher latency CDC solutions
730 # like FFSynchronizer.
731 update_core = Signal(name=sr.name+"_update_core")
732 update_core_prev = Signal(name=sr.name+"_update_core_prev")
733 m.d[domain] += [
734 update_core.eq(sr_update), # This is CDC from JTAG domain
735 # to given domain
736 update_core_prev.eq(update_core)
737 ]
738 with m.If(update_core_prev & ~update_core):
739 # Falling edge of update
740 m.d[domain] += sr.oe.eq(isir)
741 with m.Else():
742 m.d[domain] += sr.oe.eq(0)
743
744 with m.If(sr_shift):
745 m.d.posjtag += reg.eq(Cat(reg[1:], self.bus.tdi))
746 with m.If(sr_capture):
747 m.d.posjtag += reg.eq(sr.i)
748
749 # tdo = reg[0], tdo_en = shift
750 tdos.append((reg[0], sr_shift))
751
752
753 # Assign the right tdo to the bus tdo
754 for i, (tdo, tdo_en) in enumerate(tdos):
755 if i == 0:
756 with m.If(tdo_en):
757 m.d.comb += self.bus.tdo.eq(tdo)
758 else:
759 with m.Elif(tdo_en):
760 m.d.comb += self.bus.tdo.eq(tdo)
761
762 if len(tdos) > 0:
763 with m.Else():
764 m.d.comb += self.bus.tdo.eq(tdo_jtag)
765 else:
766 # Always connect tdo_jtag to
767 m.d.comb += self.bus.tdo.eq(tdo_jtag)
768
769
770 def add_wishbone(self, *, ircodes, address_width, data_width,
771 granularity=None, domain="sync", features=None,
772 name=None, src_loc_at=0):
773 """Add a wishbone interface
774
775 In order to allow high JTAG clock speed, data will be cached.
776 This means that if data is output the value of the next address
777 will be read automatically.
778
779 Parameters:
780 -----------
781 ircodes: sequence of three integer for the JTAG IR codes;
782 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
783 has a shift register of length 'address_width', the two other codes
784 share a shift register of length data_width.
785 address_width: width of the address
786 data_width: width of the data
787 features: features required. defaults to stall, lock, err, rty
788
789 Returns:
790 wb: nmigen_soc.wishbone.bus.Interface
791 The Wishbone interface, is pipelined and has stall field.
792 """
793 if len(ircodes) != 3:
794 raise ValueError("3 IR Codes have to be provided")
795
796 if features is None:
797 features={"stall", "lock", "err", "rty"}
798 if name is None:
799 name = "wb" + str(len(self._wbs))
800 sr_addr = self.add_shiftreg(
801 ircode=ircodes[0], length=address_width, domain=domain,
802 name=name+"_addrsr"
803 )
804 sr_data = self.add_shiftreg(
805 ircode=ircodes[1:], length=data_width, domain=domain,
806 name=name+"_datasr"
807 )
808
809 wb = WishboneInterface(data_width=data_width, addr_width=address_width,
810 granularity=granularity, features=features,
811 name=name, src_loc_at=src_loc_at+1)
812
813 self._wbs.append((sr_addr, sr_data, wb, domain))
814
815 return wb
816
817 def _elaborate_wishbones(self, m):
818 for sr_addr, sr_data, wb, domain in self._wbs:
819 m.d.comb += sr_addr.i.eq(wb.adr)
820
821 if hasattr(wb, "sel"):
822 # Always selected
823 m.d.comb += [s.eq(1) for s in wb.sel]
824
825 with m.FSM(domain=domain) as fsm:
826 with m.State("IDLE"):
827 with m.If(sr_addr.oe): # WBADDR code
828 m.d[domain] += wb.adr.eq(sr_addr.o)
829 m.next = "READ"
830 with m.Elif(sr_data.oe[0]): # WBREAD code
831 # If data is
832 m.d[domain] += wb.adr.eq(wb.adr + 1)
833 m.next = "READ"
834 with m.Elif(sr_data.oe[1]): # WBWRITE code
835 m.d[domain] += wb.dat_w.eq(sr_data.o)
836 m.next = "WRITEREAD"
837 with m.State("READ"):
838 if not hasattr(wb, "stall"):
839 m.next = "READACK"
840 else:
841 with m.If(~wb.stall):
842 m.next = "READACK"
843 with m.State("READACK"):
844 with m.If(wb.ack):
845 # Store read data in sr_data.i
846 # and keep it there til next read.
847 # This is enough to synchronize between sync and JTAG
848 # clock domain and no higher latency solutions like
849 # FFSynchronizer is needed.
850 m.d[domain] += sr_data.i.eq(wb.dat_r)
851 m.next = "IDLE"
852 with m.State("WRITEREAD"):
853 if not hasattr(wb, "stall"):
854 m.next = "WRITEREADACK"
855 else:
856 with m.If(~wb.stall):
857 m.next = "WRITEREADACK"
858 with m.State("WRITEREADACK"):
859 with m.If(wb.ack):
860 m.d[domain] += wb.adr.eq(wb.adr + 1)
861 m.next = "READ"
862
863 if hasattr(wb, "stall"):
864 m.d.comb += wb.stb.eq(fsm.ongoing("READ") |
865 fsm.ongoing("WRITEREAD"))
866 m.d.comb += wb.we.eq(fsm.ongoing("WRITEREAD"))
867 else:
868 # non-stall is single-cycle (litex), must assert stb
869 # until ack is sent
870 m.d.comb += wb.stb.eq(fsm.ongoing("READ") |
871 fsm.ongoing("WRITEREAD") |
872 fsm.ongoing("READACK") |
873 fsm.ongoing("WRITEREADACK"))
874 m.d.comb += wb.we.eq(fsm.ongoing("WRITEREAD") |
875 fsm.ongoing("WRITEREADACK"))
876 m.d.comb += wb.cyc.eq(~fsm.ongoing("IDLE"))