525b5311748973a4b287516154e8759b4e3f0710
3 from enum
import Enum
, auto
5 from nmigen
import (Elaboratable
, Signal
, Module
, ClockDomain
, Cat
, Record
,
7 from nmigen
.hdl
.rec
import Direction
, Layout
8 from nmigen
.tracer
import get_var_name
10 from nmigen_soc
.wishbone
import Interface
as WishboneInterface
12 from .bus
import Interface
, DMIInterface
15 "TAP", "ShiftReg", "IOType", "IOConn",
19 class _FSM(Elaboratable
):
20 """TAP subblock for the FSM"""
21 def __init__(self
, *, bus
):
24 self
.capture
= Signal()
26 self
.update
= Signal()
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")
34 def elaborate(self
, platform
):
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
),
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
)
50 m
.domains
.local
= local
= ClockDomain(local
=True, reset_less
=True)
51 m
.d
.comb
+= local
.clk
.eq(self
._bus
.tck
)
53 with m
.FSM(domain
="local") as fsm
:
54 with m
.State("TestLogicReset"):
55 # Be sure to reset isir, isdr
60 with m
.If(self
._bus
.tms
== 0):
61 m
.next
= "RunTestIdle"
62 with m
.State("RunTestIdle"):
63 # Be sure to reset isir, isdr
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"
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"
81 m
.next
= "TestLogicReset"
82 with m
.State("CaptureState"):
83 with m
.If(self
._bus
.tms
== 0):
87 with m
.State("ShiftState"):
88 with m
.If(self
._bus
.tms
== 1):
90 with m
.State("Exit1"):
91 with m
.If(self
._bus
.tms
== 0):
94 m
.next
= "UpdateState"
95 with m
.State("Pause"):
96 with m
.If(self
._bus
.tms
== 1):
98 with m
.State("Exit2"):
99 with m
.If(self
._bus
.tms
== 0):
100 m
.next
= "ShiftState"
102 m
.next
= "UpdateState"
103 with m
.State("UpdateState"):
108 with m
.If(self
._bus
.tms
== 0):
109 m
.next
= "RunTestIdle"
111 m
.next
= "SelectDRScan"
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")),
122 class _IRBlock(Elaboratable
):
123 """TAP subblock for handling the IR shift register"""
124 def __init__(self
, *, ir_width
, cmd_idcode
,
125 tdi
, capture
, shift
, update
,
128 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
132 self
._capture
= capture
134 self
._update
= update
136 def elaborate(self
, platform
):
139 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
141 m
.d
.comb
+= self
.tdo
.eq(self
.ir
[0])
142 with m
.If(self
._capture
):
143 m
.d
.posjtag
+= shift_ir
.eq(self
.ir
)
144 with m
.Elif(self
._shift
):
145 m
.d
.posjtag
+= shift_ir
.eq(Cat(shift_ir
[1:], self
._tdi
))
146 with m
.Elif(self
._update
):
147 # For ir we only update it on the rising edge of clock
148 # to avoid that we already have the new ir value when still in
150 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
160 class IOConn(Record
):
168 """TAP subblock representing the interface for an JTAG IO cell.
169 It contains signal to connect to the core and to the pad
171 This object is normally only allocated and returned from ``TAP.add_io``
172 It is a Record subclass.
176 core: subrecord with signals for the core
177 i: Signal(1), present only for IOType.In and IOType.InTriOut.
178 Signal input to core with pad input value.
179 o: Signal(1), present only for IOType.Out, IOType.TriOut and
181 Signal output from core with the pad output value.
182 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
183 Signal output from core with the pad output enable value.
184 pad: subrecord with for the pad
185 i: Signal(1), present only for IOType.In and IOType.InTriOut
186 Output from pad with pad input value for core.
187 o: Signal(1), present only for IOType.Out, IOType.TriOut and
189 Input to pad with pad output value.
190 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
191 Input to pad with pad output enable value.
196 if iotype
in (IOType
.In
, IOType
.InTriOut
):
197 sigs
.append(("i", 1))
198 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
199 sigs
.append(("o", 1))
200 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
201 sigs
.append(("oe", 1))
203 return Layout((("core", sigs
), ("pad", sigs
)))
205 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
206 super().__init
__(self
.__class
__.layout(iotype
), name
=name
,
207 src_loc_at
=src_loc_at
+1)
209 self
._iotype
= iotype
211 class _IDBypassBlock(Elaboratable
):
212 """TAP subblock for the ID shift register"""
213 def __init__(self
, *, manufacturer_id
, part_number
, version
,
214 tdi
, capture
, shift
, update
, bypass
,
217 if (not isinstance(manufacturer_id
, Const
) and
218 len(manufacturer_id
) != 11):
219 raise ValueError("manufacturer_id has to be Const of length 11")
220 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
221 raise ValueError("part_number has to be Const of length 16")
222 if not isinstance(version
, Const
) and len(version
) != 4:
223 raise ValueError("version has to be Const of length 4")
224 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
226 self
.tdo
= Signal(name
=name
+"_tdo")
229 self
._capture
= capture
231 self
._update
= update
232 self
._bypass
= bypass
234 def elaborate(self
, platform
):
237 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
239 # Local signals for the module
248 _capture
.eq(self
._capture
),
249 _shift
.eq(self
._shift
),
250 _update
.eq(self
._update
),
251 _bypass
.eq(self
._bypass
),
256 m
.d
.posjtag
+= sr
.eq(self
._id
)
259 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
261 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
266 class ShiftReg(Record
):
267 """Object with interface for extra shift registers on a TAP.
272 cmds : int, default=1
273 The number of corresponding JTAG instructions
275 This object is normally only allocated and returned from ``TAP.add_shiftreg``
276 It is a Record subclass.
280 i: length=sr_length, FANIN
281 The input data sampled during capture state of the TAP
282 ie: length=cmds, FANOUT
283 Indicates that data is to be sampled by the JTAG TAP and
284 should be held stable. The bit indicates the corresponding
285 instruction for which data is asked.
286 This signal is kept high for a whole JTAG TAP clock cycle
287 and may thus be kept higher for more than one clock cycle
288 on the domain where ShiftReg is used.
289 The JTAG protocol does not allow insertion of wait states
290 so data need to be provided before ie goes down. The speed
291 of the response will determine the max. frequency for the
293 o: length=sr_length, FANOUT
294 The value of the shift register.
295 oe: length=cmds, FANOUT
296 Indicates that output is stable and can be sampled downstream because
297 JTAG TAP is in the Update state. The bit indicates the corresponding
298 instruction. The bit is only kept high for one clock cycle.
300 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
302 ("i", sr_length
, Direction
.FANIN
),
303 ("ie", cmds
, Direction
.FANOUT
),
304 ("o", sr_length
, Direction
.FANOUT
),
305 ("oe", cmds
, Direction
.FANOUT
),
307 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
309 class TAP(Elaboratable
):
311 def __init__(self
, *, with_reset
=False, ir_width
=None,
312 manufacturer_id
=Const(0b10001111111, 11),
313 part_number
=Const(1, 16),
315 name
=None, src_loc_at
=0):
316 assert((ir_width
is None) or (isinstance(ir_width
, int) and
318 assert(len(version
) == 4)
321 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
323 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
324 src_loc_at
=src_loc_at
+1)
328 self
._ir
_width
= ir_width
329 self
._manufacturer
_id
= manufacturer_id
330 self
._part
_number
= part_number
331 self
._version
= version
333 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at end
340 def elaborate(self
, platform
):
343 # Determine ir_width if not fixed.
344 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
345 ir_width
= len("{:b}".format(ir_max
))
346 if self
._ir
_width
is not None:
347 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width " \
348 "not big enough for allocated shiift registers"
349 ir_width
= self
._ir
_width
351 # TODO: Make commands numbers configurable
357 cmd_bypass
= 2**ir_width
- 1 # All ones
359 m
.submodules
.fsm
= fsm
= _FSM(bus
=self
.bus
)
360 m
.domains
.posjtag
= fsm
.posjtag
361 m
.domains
.negjtag
= fsm
.negjtag
365 m
.submodules
.irblock
= irblock
= _IRBlock(
366 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
367 capture
=(fsm
.isir
& fsm
.capture
),
368 shift
=(fsm
.isir
& fsm
.shift
),
369 update
=(fsm
.isir
& fsm
.update
),
370 name
=self
.name
+"_ir",
377 m
.d
.comb
+= select_id
.eq(fsm
.isdr
&
378 ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
)))
379 m
.d
.comb
+= id_bypass
.eq(ir
== cmd_bypass
)
380 m
.submodules
.idblock
= idblock
= _IDBypassBlock(
381 manufacturer_id
=self
._manufacturer
_id
,
382 part_number
=self
._part
_number
,
383 version
=self
._version
, tdi
=self
.bus
.tdi
,
384 capture
=(select_id
& fsm
.capture
),
385 shift
=(select_id
& fsm
.shift
),
386 update
=(select_id
& fsm
.update
),
388 name
=self
.name
+"_id",
391 # IO (Boundary scan) block
392 io_capture
= Signal()
396 io_bd2core
= Signal()
397 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
398 preload
= (ir
== cmd_preload
)
399 select_io
= fsm
.isdr
& (sample | preload
)
401 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample
403 io_shift
.eq(select_io
& fsm
.shift
),
404 io_update
.eq(select_io
& fsm
.update
),
405 io_bd2io
.eq(ir
== cmd_extest
),
406 io_bd2core
.eq(ir
== cmd_intest
),
408 io_tdo
= self
._elaborate
_ios
(
410 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
411 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
414 # chain tdo: select as appropriate, to go into into shiftregs
415 tdo
= Signal(name
=self
.name
+"_tdo")
416 with m
.If(select_ir
):
417 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
418 with m
.Elif(select_id
):
419 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
420 if io_tdo
is not None:
421 with m
.Elif(select_io
):
422 m
.d
.comb
+= tdo
.eq(io_tdo
)
425 self
._elaborate
_shiftregs
(
426 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
427 ir
=irblock
.ir
, tdo_jtag
=tdo
431 self
._elaborate
_wishbones
(m
)
433 # DMI (Debug Memory Interface)
434 self
._elaborate
_dmis
(m
)
438 def add_dmi(self
, *, ircodes
, address_width
=8, data_width
=64,
439 domain
="sync", name
=None):
440 """Add a DMI interface
442 * writing to DMIADDR will automatically trigger a DMI READ.
443 the DMI address does not alter (so writes can be done at that addr)
444 * reading from DMIREAD triggers a DMI READ at the current DMI addr
445 the address is automatically incremented by 1 after.
446 * writing to DMIWRITE triggers a DMI WRITE at the current DMI addr
447 the address is automatically incremented by 1 after.
451 ircodes: sequence of three integer for the JTAG IR codes;
452 they represent resp. DMIADDR, DMIREAD and DMIWRITE.
453 First code has a shift register of length 'address_width',
454 the two other codes share a shift register of length
457 address_width: width of the address
458 data_width: width of the data
461 dmi: soc.debug.dmi.DMIInterface
464 if len(ircodes
) != 3:
465 raise ValueError("3 IR Codes have to be provided")
468 name
= "dmi" + str(len(self
._dmis
))
470 # add 2 shift registers: one for addr, one for data.
471 sr_addr
= self
.add_shiftreg(ircode
=ircodes
[0], length
=address_width
,
472 domain
=domain
, name
=name
+"_addrsr")
473 sr_data
= self
.add_shiftreg(ircode
=ircodes
[1:], length
=data_width
,
474 domain
=domain
, name
=name
+"_datasr")
476 dmi
= DMIInterface(name
=name
)
477 self
._dmis
.append((sr_addr
, sr_data
, dmi
, domain
))
481 def _elaborate_dmis(self
, m
):
482 for sr_addr
, sr_data
, dmi
, domain
in self
._dmis
:
484 m
.d
.comb
+= sr_addr
.i
.eq(dmi
.addr_i
)
486 with m
.FSM(domain
=domain
) as ds
:
488 # detect mode based on whether jtag addr or data read/written
489 with m
.State("IDLE"):
490 with m
.If(sr_addr
.oe
): # DMIADDR code
491 cd
+= dmi
.addr_i
.eq(sr_addr
.o
)
493 with m
.Elif(sr_data
.oe
[0]): # DMIREAD code
495 cd
+= dmi
.addr_i
.eq(dmi
.addr_i
+ 1)
497 with m
.Elif(sr_data
.oe
[1]): # DMIWRITE code
498 cd
+= dmi
.din
.eq(sr_data
.o
)
501 # req_i raises for 1 clock
502 with m
.State("READ"):
506 with m
.State("READACK"):
507 with m
.If(dmi
.ack_o
):
508 # Store read data in sr_data.i hold till next read
509 # Note: could use FFSynchroniser
510 cd
+= sr_data
.i
.eq(dmi
.dout
)
513 # req_i raises for 1 clock
514 with m
.State("WRRD"):
518 with m
.State("WRRDACK"):
519 with m
.If(dmi
.ack_o
):
520 cd
+= dmi
.addr_i
.eq(dmi
.addr_i
+ 1)
521 m
.next
= "READ" # for readwrite
523 # set DMI req and write-enable based on ongoing FSM states
525 dmi
.req_i
.eq(ds
.ongoing("READ") | ds
.ongoing("WRRD")),
526 dmi
.we_i
.eq(ds
.ongoing("WRRD")),
529 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
530 """Add a io cell to the boundary scan chain
533 - iotype: :class:`IOType` enum.
539 name
= "ioconn" + str(len(self
._ios
))
541 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
542 self
._ios
.append(ioconn
)
545 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
546 length
= sum(IOConn
.lengths
[conn
._iotype
] for conn
in self
._ios
)
550 io_sr
= Signal(length
)
551 io_bd
= Signal(length
)
553 # Boundary scan "capture" mode. makes I/O status available via SR
557 for conn
in self
._ios
:
558 # in appropriate sequence: In/TriOut has pad.i,
559 # Out.InTriOut has everything, Out and TriOut have core.o
560 if conn
._iotype
in [IOType
.In
, IOType
.InTriOut
]:
561 iol
.append(conn
.pad
.i
)
562 if conn
._iotype
in [IOType
.Out
, IOType
.InTriOut
]:
563 iol
.append(conn
.core
.o
)
564 if conn
._iotype
in [IOType
.TriOut
, IOType
.InTriOut
]:
565 iol
.append(conn
.core
.oe
)
566 # length double-check
567 idx
+= IOConn
.lengths
[conn
._iotype
] # fails if wrong type
568 assert idx
== length
, "Internal error"
569 m
.d
.posjtag
+= io_sr
.eq(Cat(*iol
)) # assigns all io_sr in one hit
571 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
573 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
577 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
579 # sets up IO (pad<->core) or in testing mode depending on requested
580 # mode, via Muxes controlled by bd2core and bd2io
582 for conn
in self
._ios
:
583 if conn
._iotype
== IOType
.In
:
584 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
586 elif conn
._iotype
== IOType
.Out
:
587 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
589 elif conn
._iotype
== IOType
.TriOut
:
591 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
592 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
595 elif conn
._iotype
== IOType
.InTriOut
:
597 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
598 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
599 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
603 raise("Internal error")
604 assert idx
== length
, "Internal error"
608 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None,
610 """Add a shift register to the JTAG interface
613 - ircode: code(s) for the IR; int or sequence of ints. In the latter
614 case this shiftreg is shared between different IR codes.
615 - length: the length of the shift register
616 - domain: the domain on which the signal will be used"""
622 ir_it
= ircodes
= (ircode
,)
623 for _ircode
in ir_it
:
624 if not isinstance(_ircode
, int) or _ircode
<= 0:
625 raise ValueError("IR code '{}' is not an int "
626 "greater than 0".format(_ircode
))
627 if _ircode
in self
._ircodes
:
628 raise ValueError("IR code '{}' already taken".format(_ircode
))
630 self
._ircodes
.extend(ircodes
)
633 name
= "sr{}".format(len(self
._srs
))
634 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
,
635 src_loc_at
=src_loc_at
+1)
636 self
._srs
.append((ircodes
, domain
, sr
))
640 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
641 # tdos is tuple of (tdo, tdo_en) for each shiftreg
643 for ircodes
, domain
, sr
in self
._srs
:
644 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
645 m
.d
.comb
+= sr
.o
.eq(reg
)
647 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
648 sr_capture
= Signal(name
=sr
.name
+"_capture")
649 sr_shift
= Signal(name
=sr
.name
+"_shift")
650 sr_update
= Signal(name
=sr
.name
+"_update")
652 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
653 sr_capture
.eq((isir
!= 0) & capture
),
654 sr_shift
.eq((isir
!= 0) & shift
),
655 sr_update
.eq((isir
!= 0) & update
),
658 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
659 # clockdomain latch update in `domain` clockdomain and see when
660 # it has falling edge.
661 # At that edge put isir in sr.oe for one `domain` clockdomain
662 # Note: could use FFSynchroniser instead
663 update_core
= Signal(name
=sr
.name
+"_update_core")
664 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
666 update_core
.eq(sr_update
), # This is CDC from JTAG domain
668 update_core_prev
.eq(update_core
)
670 with m
.If(update_core_prev
& ~update_core
):
671 # Falling edge of update
672 m
.d
[domain
] += sr
.oe
.eq(isir
)
674 m
.d
[domain
] += sr
.oe
.eq(0)
677 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
678 with m
.If(sr_capture
):
679 # could also use FFSynchroniser here too
680 m
.d
.posjtag
+= reg
.eq(sr
.i
)
682 # tdo = reg[0], tdo_en = shift
683 tdos
.append((reg
[0], sr_shift
))
686 # Assign the right tdo to the bus tdo
687 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
690 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
693 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
697 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
699 # Always connect tdo_jtag to
700 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
703 def add_wishbone(self
, *, ircodes
, address_width
, data_width
,
704 granularity
=None, domain
="sync", features
=None,
705 name
=None, src_loc_at
=0):
706 """Add a wishbone interface
708 In order to allow high JTAG clock speed, data will be cached.
709 This means that if data is output the value of the next address
710 will be read automatically.
714 ircodes: sequence of three integer for the JTAG IR codes;
715 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
716 has a shift register of length 'address_width', the two other codes
717 share a shift register of length data_width.
718 address_width: width of the address
719 data_width: width of the data
720 features: features required. defaults to stall, lock, err, rty
723 wb: nmigen_soc.wishbone.bus.Interface
724 The Wishbone interface, is pipelined and has stall field.
726 if len(ircodes
) != 3:
727 raise ValueError("3 IR Codes have to be provided")
730 features
={"stall", "lock", "err", "rty"}
732 name
= "wb" + str(len(self
._wbs
))
733 sr_addr
= self
.add_shiftreg(
734 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
737 sr_data
= self
.add_shiftreg(
738 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
742 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
743 granularity
=granularity
, features
=features
,
744 name
=name
, src_loc_at
=src_loc_at
+1)
746 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
750 def _elaborate_wishbones(self
, m
):
751 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
752 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
754 if hasattr(wb
, "sel"):
756 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
758 with m
.FSM(domain
=domain
) as fsm
:
759 with m
.State("IDLE"):
760 with m
.If(sr_addr
.oe
): # WBADDR code
761 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
763 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
765 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
767 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
768 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
770 with m
.State("READ"):
771 if not hasattr(wb
, "stall"):
774 with m
.If(~wb
.stall
):
776 with m
.State("READACK"):
778 # Store read data in sr_data.i and keep it there
779 # til next read. could use FFSynchroniser (see above)
780 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
782 with m
.State("WRITEREAD"):
783 if not hasattr(wb
, "stall"):
784 m
.next
= "WRITEREADACK"
786 with m
.If(~wb
.stall
):
787 m
.next
= "WRITEREADACK"
788 with m
.State("WRITEREADACK"):
790 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
793 if hasattr(wb
, "stall"):
794 m
.d
.comb
+= wb
.stb
.eq(fsm
.ongoing("READ") |
795 fsm
.ongoing("WRITEREAD"))
796 m
.d
.comb
+= wb
.we
.eq(fsm
.ongoing("WRITEREAD"))
798 # non-stall is single-cycle (litex), must assert stb
800 m
.d
.comb
+= wb
.stb
.eq(fsm
.ongoing("READ") |
801 fsm
.ongoing("WRITEREAD") |
802 fsm
.ongoing("READACK") |
803 fsm
.ongoing("WRITEREADACK"))
804 m
.d
.comb
+= wb
.we
.eq(fsm
.ongoing("WRITEREAD") |
805 fsm
.ongoing("WRITEREADACK"))
806 m
.d
.comb
+= wb
.cyc
.eq(~fsm
.ongoing("IDLE"))