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