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