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