9d7d0d1f6e55ee3c2bf43da65d67fa53b0539c54
[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
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
337 def elaborate(self, platform):
338 m = Module()
339
340 # Determine ir_width if not fixed.
341 ir_max = max(self._ircodes) + 1 # One extra code needed with all ones
342 ir_width = len("{:b}".format(ir_max))
343 if self._ir_width is not None:
344 assert self._ir_width >= ir_width, "Specified JTAG IR width " \
345 "not big enough for allocated shiift registers"
346 ir_width = self._ir_width
347
348 # TODO: Make commands numbers configurable
349 cmd_extest = 0
350 cmd_intest = 0
351 cmd_idcode = 1
352 cmd_sample = 2
353 cmd_preload = 2
354 cmd_bypass = 2**ir_width - 1 # All ones
355
356 m.submodules._fsm = fsm = _FSM(bus=self.bus)
357 m.domains.posjtag = fsm.posjtag
358 m.domains.negjtag = fsm.negjtag
359
360 # IR block
361 select_ir = fsm.isir
362 m.submodules._irblock = irblock = _IRBlock(
363 ir_width=ir_width, cmd_idcode=cmd_idcode, tdi=self.bus.tdi,
364 capture=(fsm.isir & fsm.capture),
365 shift=(fsm.isir & fsm.shift),
366 update=(fsm.isir & fsm.update),
367 name=self.name+"_ir",
368 )
369 ir = irblock.ir
370
371 # ID block
372 select_id = fsm.isdr & ((ir == cmd_idcode) | (ir == cmd_bypass))
373 m.submodules._idblock = idblock = _IDBypassBlock(
374 manufacturer_id=self._manufacturer_id,
375 part_number=self._part_number,
376 version=self._version, tdi=self.bus.tdi,
377 capture=(select_id & fsm.capture),
378 shift=(select_id & fsm.shift),
379 update=(select_id & fsm.update),
380 bypass=(ir == cmd_bypass),
381 name=self.name+"_id",
382 )
383
384 # IO (Boundary scan) block
385 io_capture = Signal()
386 io_shift = Signal()
387 io_update = Signal()
388 io_bd2io = Signal()
389 io_bd2core = Signal()
390 sample = (ir == cmd_extest) | (ir == cmd_sample)
391 preload = (ir == cmd_preload)
392 select_io = fsm.isdr & (sample | preload)
393 m.d.comb += [
394 io_capture.eq(sample & fsm.capture), # Don't capture if not sample
395 # (like for PRELOAD)
396 io_shift.eq(select_io & fsm.shift),
397 io_update.eq(select_io & fsm.update),
398 io_bd2io.eq(ir == cmd_extest),
399 io_bd2core.eq(ir == cmd_intest),
400 ]
401 io_tdo = self._elaborate_ios(
402 m=m,
403 capture=io_capture, shift=io_shift, update=io_update,
404 bd2io=io_bd2io, bd2core=io_bd2core,
405 )
406
407 # chain tdo: select as appropriate, to go into into shiftregs
408 tdo = Signal(name=self.name+"_tdo")
409 with m.If(select_ir):
410 m.d.comb += tdo.eq(irblock.tdo)
411 with m.Elif(select_id):
412 m.d.comb += tdo.eq(idblock.tdo)
413 with m.Elif(select_io):
414 m.d.comb += tdo.eq(io_tdo)
415
416 # shiftregs block
417 self._elaborate_shiftregs(
418 m, capture=fsm.capture, shift=fsm.shift, update=fsm.update,
419 ir=irblock.ir, tdo_jtag=tdo
420 )
421
422 # wishbone
423 self._elaborate_wishbones(m)
424
425 return m
426
427
428 def add_io(self, *, iotype, name=None, src_loc_at=0):
429 """Add a io cell to the boundary scan chain
430
431 Parameters:
432 - iotype: :class:`IOType` enum.
433
434 Returns:
435 - :class:`IOConn`
436 """
437 if name is None:
438 name = "ioconn" + str(len(self._ios))
439
440 ioconn = IOConn(iotype=iotype, name=name, src_loc_at=src_loc_at+1)
441 self._ios.append(ioconn)
442 return ioconn
443
444 def _elaborate_ios(self, *, m, capture, shift, update, bd2io, bd2core):
445 connlength = {
446 IOType.In: 1,
447 IOType.Out: 1,
448 IOType.TriOut: 2,
449 IOType.InTriOut: 3,
450 }
451 length = sum(connlength[conn._iotype] for conn in self._ios)
452 if length == 0:
453 return self.bus.tdi
454
455 io_sr = Signal(length)
456 io_bd = Signal(length)
457
458 # Boundary scan "capture" mode. makes I/O status available via SR
459 with m.If(capture):
460 idx = 0
461 for conn in self._ios:
462 if conn._iotype == IOType.In:
463 m.d.posjtag += io_sr[idx].eq(conn.pad.i)
464 idx += 1
465 elif conn._iotype == IOType.Out:
466 m.d.posjtag += io_sr[idx].eq(conn.core.o)
467 idx += 1
468 elif conn._iotype == IOType.TriOut:
469 m.d.posjtag += [
470 io_sr[idx].eq(conn.core.o),
471 io_sr[idx+1].eq(conn.core.oe),
472 ]
473 idx += 2
474 elif conn._iotype == IOType.InTriOut:
475 m.d.posjtag += [
476 io_sr[idx].eq(conn.pad.i),
477 io_sr[idx+1].eq(conn.core.o),
478 io_sr[idx+2].eq(conn.core.oe),
479 ]
480 idx += 3
481 else:
482 raise("Internal error")
483 assert idx == length, "Internal error"
484
485 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
486 with m.Elif(shift):
487 m.d.posjtag += io_sr.eq(Cat(self.bus.tdi, io_sr[:-1]))
488
489 # "Update" mode
490 with m.Elif(update):
491 m.d.negjtag += io_bd.eq(io_sr)
492
493 # sets up IO (pad<->core) or in testing mode depending on requested
494 # mode, via Muxes controlled by bd2core and bd2io
495 idx = 0
496 for conn in self._ios:
497 if conn._iotype == IOType.In:
498 m.d.comb += conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i))
499 idx += 1
500 elif conn._iotype == IOType.Out:
501 m.d.comb += conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o))
502 idx += 1
503 elif conn._iotype == IOType.TriOut:
504 m.d.comb += [
505 conn.pad.o.eq(Mux(bd2io, io_bd[idx], conn.core.o)),
506 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+1], conn.core.oe)),
507 ]
508 idx += 2
509 elif conn._iotype == IOType.InTriOut:
510 m.d.comb += [
511 conn.core.i.eq(Mux(bd2core, io_bd[idx], conn.pad.i)),
512 conn.pad.o.eq(Mux(bd2io, io_bd[idx+1], conn.core.o)),
513 conn.pad.oe.eq(Mux(bd2io, io_bd[idx+2], conn.core.oe)),
514 ]
515 idx += 3
516 else:
517 raise("Internal error")
518 assert idx == length, "Internal error"
519
520 return io_sr[-1]
521
522 def add_shiftreg(self, *, ircode, length, domain="sync", name=None,
523 src_loc_at=0):
524 """Add a shift register to the JTAG interface
525
526 Parameters:
527 - ircode: code(s) for the IR; int or sequence of ints. In the latter
528 case this shiftreg is shared between different IR codes.
529 - length: the length of the shift register
530 - domain: the domain on which the signal will be used"""
531
532 try:
533 ir_it = iter(ircode)
534 ircodes = ircode
535 except TypeError:
536 ir_it = ircodes = (ircode,)
537 for _ircode in ir_it:
538 if not isinstance(_ircode, int) or _ircode <= 0:
539 raise ValueError("IR code '{}' is not an int "
540 "greater than 0".format(_ircode))
541 if _ircode in self._ircodes:
542 raise ValueError("IR code '{}' already taken".format(_ircode))
543
544 self._ircodes.extend(ircodes)
545
546 if name is None:
547 name = "sr{}".format(len(self._srs))
548 sr = ShiftReg(sr_length=length, cmds=len(ircodes), name=name,
549 src_loc_at=src_loc_at+1)
550 self._srs.append((ircodes, domain, sr))
551
552 return sr
553
554 def _elaborate_shiftregs(self, m, capture, shift, update, ir, tdo_jtag):
555 # tdos is tuple of (tdo, tdo_en) for each shiftreg
556 tdos = []
557 for ircodes, domain, sr in self._srs:
558 reg = Signal(len(sr.o), name=sr.name+"_reg")
559 m.d.comb += sr.o.eq(reg)
560
561 isir = Signal(len(ircodes), name=sr.name+"_isir")
562 sr_capture = Signal(name=sr.name+"_capture")
563 sr_shift = Signal(name=sr.name+"_shift")
564 sr_update = Signal(name=sr.name+"_update")
565 m.d.comb += [
566 isir.eq(Cat(ir == ircode for ircode in ircodes)),
567 sr_capture.eq((isir != 0) & capture),
568 sr_shift.eq((isir != 0) & shift),
569 sr_update.eq((isir != 0) & update),
570 ]
571
572 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
573 # clockdomain latch update in `domain` clockdomain and see when
574 # it has falling edge.
575 # At that edge put isir in sr.oe for one `domain` clockdomain
576 update_core = Signal(name=sr.name+"_update_core")
577 update_core_prev = Signal(name=sr.name+"_update_core_prev")
578 m.d[domain] += [
579 update_core.eq(sr_update), # This is CDC from JTAG domain
580 # to given domain
581 update_core_prev.eq(update_core)
582 ]
583 with m.If(update_core_prev & ~update_core):
584 # Falling edge of update
585 m.d[domain] += sr.oe.eq(isir)
586 with m.Else():
587 m.d[domain] += sr.oe.eq(0)
588
589 with m.If(sr_shift):
590 m.d.posjtag += reg.eq(Cat(reg[1:], self.bus.tdi))
591 with m.If(sr_capture):
592 m.d.posjtag += reg.eq(sr.i)
593
594 # tdo = reg[0], tdo_en = shift
595 tdos.append((reg[0], sr_shift))
596
597
598 # Assign the right tdo to the bus tdo
599 for i, (tdo, tdo_en) in enumerate(tdos):
600 if i == 0:
601 with m.If(tdo_en):
602 m.d.comb += self.bus.tdo.eq(tdo)
603 else:
604 with m.Elif(tdo_en):
605 m.d.comb += self.bus.tdo.eq(tdo)
606
607 if len(tdos) > 0:
608 with m.Else():
609 m.d.comb += self.bus.tdo.eq(tdo_jtag)
610 else:
611 # Always connect tdo_jtag to
612 m.d.comb += self.bus.tdo.eq(tdo_jtag)
613
614
615 def add_wishbone(self, *, ircodes, address_width, data_width,
616 granularity=None, domain="sync", features=None,
617 name=None, src_loc_at=0):
618 """Add a wishbone interface
619
620 In order to allow high JTAG clock speed, data will be cached.
621 This means that if data is output the value of the next address
622 will be read automatically.
623
624 Parameters:
625 -----------
626 ircodes: sequence of three integer for the JTAG IR codes;
627 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
628 has a shift register of length 'address_width', the two other codes
629 share a shift register of length data_width.
630 address_width: width of the address
631 data_width: width of the data
632 features: features required. defaults to stall, lock, err, rty
633
634 Returns:
635 wb: nmigen_soc.wishbone.bus.Interface
636 The Wishbone interface, is pipelined and has stall field.
637 """
638 if len(ircodes) != 3:
639 raise ValueError("3 IR Codes have to be provided")
640
641 if features is None:
642 features={"stall", "lock", "err", "rty"}
643 if name is None:
644 name = "wb" + str(len(self._wbs))
645 sr_addr = self.add_shiftreg(
646 ircode=ircodes[0], length=address_width, domain=domain,
647 name=name+"_addrsr"
648 )
649 sr_data = self.add_shiftreg(
650 ircode=ircodes[1:], length=data_width, domain=domain,
651 name=name+"_datasr"
652 )
653
654 wb = WishboneInterface(data_width=data_width, addr_width=address_width,
655 granularity=granularity, features=features,
656 name=name, src_loc_at=src_loc_at+1)
657
658 self._wbs.append((sr_addr, sr_data, wb, domain))
659
660 return wb
661
662 def _elaborate_wishbones(self, m):
663 for sr_addr, sr_data, wb, domain in self._wbs:
664 m.d.comb += sr_addr.i.eq(wb.adr)
665
666 if hasattr(wb, "sel"):
667 # Always selected
668 m.d.comb += [s.eq(1) for s in wb.sel]
669
670 with m.FSM(domain=domain) as fsm:
671 with m.State("IDLE"):
672 with m.If(sr_addr.oe): # WBADDR code
673 m.d[domain] += wb.adr.eq(sr_addr.o)
674 m.next = "READ"
675 with m.Elif(sr_data.oe[0]): # WBREAD code
676 # If data is
677 m.d[domain] += wb.adr.eq(wb.adr + 1)
678 m.next = "READ"
679 with m.Elif(sr_data.oe[1]): # WBWRITE code
680 m.d[domain] += wb.dat_w.eq(sr_data.o)
681 m.next = "WRITEREAD"
682 with m.State("READ"):
683 with m.If(~wb.stall):
684 m.next = "READACK"
685 with m.State("READACK"):
686 with m.If(wb.ack):
687 # Store read data in sr_data.i
688 # and keep it there til next read
689 m.d[domain] += sr_data.i.eq(wb.dat_r)
690 m.next = "IDLE"
691 with m.State("WRITEREAD"):
692 with m.If(~wb.stall):
693 m.next = "WRITEREADACK"
694 with m.State("WRITEREADACK"):
695 with m.If(wb.ack):
696 m.d[domain] += wb.adr.eq(wb.adr + 1)
697 m.next = "READ"
698
699 m.d.comb += [
700 wb.cyc.eq(~fsm.ongoing("IDLE")),
701 wb.stb.eq(fsm.ongoing("READ") | fsm.ongoing("WRITEREAD")),
702 wb.we.eq(fsm.ongoing("WRITEREAD")),
703 ]