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")),
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
,
129 self
.ir
= Signal(ir_width
, reset
=cmd_idcode
)
133 self
._capture
= capture
135 self
._update
= update
137 def elaborate(self
, platform
):
140 shift_ir
= Signal(len(self
.ir
), reset_less
=True)
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
151 m
.d
.posjtag
+= self
.ir
.eq(shift_ir
)
163 class IOConn(Record
):
171 """TAP subblock representing the interface for an JTAG IO cell.
172 It contains signal to connect to the core and to the pad
174 This object is normally only allocated and returned from ``TAP.add_io``
175 It is a Record subclass.
179 core: subrecord with signals for the core
180 i: Signal(1), present only for IOType.In and IOType.InTriOut.
181 Signal input to core with pad input value.
182 o: Signal(1), present only for IOType.Out, IOType.TriOut and
184 Signal output from core with the pad output value.
185 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
186 Signal output from core with the pad output enable value.
187 pad: subrecord with for the pad
188 i: Signal(1), present only for IOType.In and IOType.InTriOut
189 Output from pad with pad input value for core.
190 o: Signal(1), present only for IOType.Out, IOType.TriOut and
192 Input to pad with pad output value.
193 oe: Signal(1), present only for IOType.TriOut and IOType.InTriOut.
194 Input to pad with pad output enable value.
199 if iotype
in (IOType
.In
, IOType
.InTriOut
):
200 sigs
.append(("i", 1))
201 if iotype
in (IOType
.Out
, IOType
.TriOut
, IOType
.InTriOut
):
202 sigs
.append(("o", 1))
203 if iotype
in (IOType
.TriOut
, IOType
.InTriOut
):
204 sigs
.append(("oe", 1))
206 return Layout((("core", sigs
), ("pad", sigs
)))
208 def __init__(self
, *, iotype
, name
=None, src_loc_at
=0):
209 super().__init
__(self
.__class
__.layout(iotype
), name
=name
,
210 src_loc_at
=src_loc_at
+1)
212 self
._iotype
= iotype
215 class _IDBypassBlock(Elaboratable
):
216 """TAP subblock for the ID shift register"""
217 def __init__(self
, *, manufacturer_id
, part_number
, version
,
218 tdi
, capture
, shift
, update
, bypass
,
221 if (not isinstance(manufacturer_id
, Const
) and
222 len(manufacturer_id
) != 11):
223 raise ValueError("manufacturer_id has to be Const of length 11")
224 if not isinstance(part_number
, Const
) and len(manufacturer_id
) != 16:
225 raise ValueError("part_number has to be Const of length 16")
226 if not isinstance(version
, Const
) and len(version
) != 4:
227 raise ValueError("version has to be Const of length 4")
228 self
._id
= Cat(Const(1,1), manufacturer_id
, part_number
, version
)
230 self
.tdo
= Signal(name
=name
+"_tdo")
233 self
._capture
= capture
235 self
._update
= update
236 self
._bypass
= bypass
238 def elaborate(self
, platform
):
241 sr
= Signal(32, reset_less
=True, name
=self
.name
+"_sr")
243 # Local signals for the module
252 _capture
.eq(self
._capture
),
253 _shift
.eq(self
._shift
),
254 _update
.eq(self
._update
),
255 _bypass
.eq(self
._bypass
),
260 m
.d
.posjtag
+= sr
.eq(self
._id
)
263 m
.d
.posjtag
+= sr
[0].eq(_tdi
)
265 m
.d
.posjtag
+= sr
.eq(Cat(sr
[1:], _tdi
))
270 class ShiftReg(Record
):
271 """Object with interface for extra shift registers on a TAP.
276 cmds : int, default=1
277 The number of corresponding JTAG instructions
279 This object is normally only allocated and returned from ``TAP.add_shiftreg``
280 It is a Record subclass.
284 i: length=sr_length, FANIN
285 The input data sampled during capture state of the TAP
286 ie: length=cmds, FANOUT
287 Indicates that data is to be sampled by the JTAG TAP and
288 should be held stable. The bit indicates the corresponding
289 instruction for which data is asked.
290 This signal is kept high for a whole JTAG TAP clock cycle
291 and may thus be kept higher for more than one clock cycle
292 on the domain where ShiftReg is used.
293 The JTAG protocol does not allow insertion of wait states
294 so data need to be provided before ie goes down. The speed
295 of the response will determine the max. frequency for the
297 o: length=sr_length, FANOUT
298 The value of the shift register.
299 oe: length=cmds, FANOUT
300 Indicates that output is stable and can be sampled downstream because
301 JTAG TAP is in the Update state. The bit indicates the corresponding
302 instruction. The bit is only kept high for one clock cycle.
304 def __init__(self
, *, sr_length
, cmds
=1, name
=None, src_loc_at
=0):
306 ("i", sr_length
, Direction
.FANIN
),
307 ("ie", cmds
, Direction
.FANOUT
),
308 ("o", sr_length
, Direction
.FANOUT
),
309 ("oe", cmds
, Direction
.FANOUT
),
311 super().__init
__(layout
, name
=name
, src_loc_at
=src_loc_at
+1)
314 class TAP(Elaboratable
):
316 def __init__(self
, *, with_reset
=False, ir_width
=None,
317 manufacturer_id
=Const(0b10001111111, 11),
318 part_number
=Const(1, 16),
320 name
=None, src_loc_at
=0):
321 assert((ir_width
is None) or (isinstance(ir_width
, int) and
323 assert(len(version
) == 4)
326 name
= get_var_name(depth
=src_loc_at
+2, default
="TAP")
328 self
.bus
= Interface(with_reset
=with_reset
, name
=self
.name
+"_bus",
329 src_loc_at
=src_loc_at
+1)
333 self
._ir
_width
= ir_width
334 self
._manufacturer
_id
= manufacturer_id
335 self
._part
_number
= part_number
336 self
._version
= version
338 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at end
345 def elaborate(self
, platform
):
348 # Determine ir_width if not fixed.
349 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
350 ir_width
= len("{:b}".format(ir_max
))
351 if self
._ir
_width
is not None:
352 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width " \
353 "not big enough for allocated shiift registers"
354 ir_width
= self
._ir
_width
356 # TODO: Make commands numbers configurable
362 cmd_bypass
= 2**ir_width
- 1 # All ones
364 m
.submodules
.fsm
= fsm
= _FSM(bus
=self
.bus
)
365 m
.domains
.posjtag
= fsm
.posjtag
366 m
.domains
.negjtag
= fsm
.negjtag
370 m
.submodules
.irblock
= irblock
= _IRBlock(
371 ir_width
=ir_width
, cmd_idcode
=cmd_idcode
, tdi
=self
.bus
.tdi
,
372 capture
=(fsm
.isir
& fsm
.capture
),
373 shift
=(fsm
.isir
& fsm
.shift
),
374 update
=(fsm
.isir
& fsm
.update
),
375 name
=self
.name
+"_ir",
382 m
.d
.comb
+= select_id
.eq(fsm
.isdr
&
383 ((ir
== cmd_idcode
) |
(ir
== cmd_bypass
)))
384 m
.d
.comb
+= id_bypass
.eq(ir
== cmd_bypass
)
385 m
.submodules
.idblock
= idblock
= _IDBypassBlock(
386 manufacturer_id
=self
._manufacturer
_id
,
387 part_number
=self
._part
_number
,
388 version
=self
._version
, tdi
=self
.bus
.tdi
,
389 capture
=(select_id
& fsm
.capture
),
390 shift
=(select_id
& fsm
.shift
),
391 update
=(select_id
& fsm
.update
),
393 name
=self
.name
+"_id",
396 # IO (Boundary scan) block
397 io_capture
= Signal()
401 io_bd2core
= Signal()
402 sample
= (ir
== cmd_extest
) |
(ir
== cmd_sample
)
403 preload
= (ir
== cmd_preload
)
404 select_io
= fsm
.isdr
& (sample | preload
)
406 io_capture
.eq(sample
& fsm
.capture
), # Don't capture if not sample
408 io_shift
.eq(select_io
& fsm
.shift
),
409 io_update
.eq(select_io
& fsm
.update
),
410 io_bd2io
.eq(ir
== cmd_extest
),
411 io_bd2core
.eq(ir
== cmd_intest
),
413 io_tdo
= self
._elaborate
_ios
(
415 capture
=io_capture
, shift
=io_shift
, update
=io_update
,
416 bd2io
=io_bd2io
, bd2core
=io_bd2core
,
419 # chain tdo: select as appropriate, to go into into shiftregs
420 tdo
= Signal(name
=self
.name
+"_tdo")
421 with m
.If(select_ir
):
422 m
.d
.comb
+= tdo
.eq(irblock
.tdo
)
423 with m
.Elif(select_id
):
424 m
.d
.comb
+= tdo
.eq(idblock
.tdo
)
425 if io_tdo
is not None:
426 with m
.Elif(select_io
):
427 m
.d
.comb
+= tdo
.eq(io_tdo
)
430 self
._elaborate
_shiftregs
(
431 m
, capture
=fsm
.capture
, shift
=fsm
.shift
, update
=fsm
.update
,
432 ir
=irblock
.ir
, tdo_jtag
=tdo
436 self
._elaborate
_wishbones
(m
)
438 # DMI (Debug Memory Interface)
439 self
._elaborate
_dmis
(m
)
443 def add_dmi(self
, *, ircodes
, address_width
=8, data_width
=64,
444 domain
="sync", name
=None):
445 """Add a DMI interface
447 * writing to DMIADDR will automatically trigger a DMI READ.
448 the DMI address does not alter (so writes can be done at that addr)
449 * reading from DMIREAD triggers a DMI READ at the current DMI addr
450 the address is automatically incremented by 1 after.
451 * writing to DMIWRITE triggers a DMI WRITE at the current DMI addr
452 the address is automatically incremented by 1 after.
456 ircodes: sequence of three integer for the JTAG IR codes;
457 they represent resp. DMIADDR, DMIREAD and DMIWRITE.
458 First code has a shift register of length 'address_width',
459 the two other codes share a shift register of length
462 address_width: width of the address
463 data_width: width of the data
466 dmi: soc.debug.dmi.DMIInterface
469 if len(ircodes
) != 3:
470 raise ValueError("3 IR Codes have to be provided")
473 name
= "dmi" + str(len(self
._dmis
))
475 # add 2 shift registers: one for addr, one for data.
476 sr_addr
= self
.add_shiftreg(ircode
=ircodes
[0], length
=address_width
,
477 domain
=domain
, name
=name
+"_addrsr")
478 sr_data
= self
.add_shiftreg(ircode
=ircodes
[1:], length
=data_width
,
479 domain
=domain
, name
=name
+"_datasr")
481 dmi
= DMIInterface(name
=name
)
482 self
._dmis
.append((sr_addr
, sr_data
, dmi
, domain
))
486 def _elaborate_dmis(self
, m
):
487 for sr_addr
, sr_data
, dmi
, domain
in self
._dmis
:
489 m
.d
.comb
+= sr_addr
.i
.eq(dmi
.addr_i
)
491 with m
.FSM(domain
=domain
) as ds
:
493 # detect mode based on whether jtag addr or data read/written
494 with m
.State("IDLE"):
495 with m
.If(sr_addr
.oe
): # DMIADDR code
496 cd
+= dmi
.addr_i
.eq(sr_addr
.o
)
498 with m
.Elif(sr_data
.oe
[0]): # DMIREAD code
500 cd
+= dmi
.addr_i
.eq(dmi
.addr_i
+ 1)
502 with m
.Elif(sr_data
.oe
[1]): # DMIWRITE code
503 cd
+= dmi
.din
.eq(sr_data
.o
)
506 # req_i raises for 1 clock
507 with m
.State("READ"):
511 with m
.State("READACK"):
512 with m
.If(dmi
.ack_o
):
513 # Store read data in sr_data.i hold till next read
514 # Note: could use FFSynchroniser
515 cd
+= sr_data
.i
.eq(dmi
.dout
)
518 # req_i raises for 1 clock
519 with m
.State("WRRD"):
523 with m
.State("WRRDACK"):
524 with m
.If(dmi
.ack_o
):
525 cd
+= dmi
.addr_i
.eq(dmi
.addr_i
+ 1)
526 m
.next
= "READ" # for readwrite
528 # set DMI req and write-enable based on ongoing FSM states
530 dmi
.req_i
.eq(ds
.ongoing("READ") | ds
.ongoing("WRRD")),
531 dmi
.we_i
.eq(ds
.ongoing("WRRD")),
534 def add_io(self
, *, iotype
, name
=None, src_loc_at
=0):
535 """Add a io cell to the boundary scan chain
538 - iotype: :class:`IOType` enum.
544 name
= "ioconn" + str(len(self
._ios
))
546 ioconn
= IOConn(iotype
=iotype
, name
=name
, src_loc_at
=src_loc_at
+1)
547 self
._ios
.append(ioconn
)
550 def _elaborate_ios(self
, *, m
, capture
, shift
, update
, bd2io
, bd2core
):
551 length
= sum(IOConn
.lengths
[conn
._iotype
] for conn
in self
._ios
)
555 io_sr
= Signal(length
)
556 io_bd
= Signal(length
)
558 # Boundary scan "capture" mode. makes I/O status available via SR
562 for conn
in self
._ios
:
563 # in appropriate sequence: In/TriOut has pad.i,
564 # Out.InTriOut has everything, Out and TriOut have core.o
565 if conn
._iotype
in [IOType
.In
, IOType
.InTriOut
]:
566 iol
.append(conn
.pad
.i
)
567 if conn
._iotype
in [IOType
.Out
, IOType
.InTriOut
]:
568 iol
.append(conn
.core
.o
)
569 if conn
._iotype
in [IOType
.TriOut
, IOType
.InTriOut
]:
570 iol
.append(conn
.core
.oe
)
571 # length double-check
572 idx
+= IOConn
.lengths
[conn
._iotype
] # fails if wrong type
573 assert idx
== length
, "Internal error"
574 m
.d
.posjtag
+= io_sr
.eq(Cat(*iol
)) # assigns all io_sr in one hit
576 # "Shift" mode (sends out captured data on tdo, sets incoming from tdi)
578 m
.d
.posjtag
+= io_sr
.eq(Cat(self
.bus
.tdi
, io_sr
[:-1]))
582 m
.d
.negjtag
+= io_bd
.eq(io_sr
)
584 # sets up IO (pad<->core) or in testing mode depending on requested
585 # mode, via Muxes controlled by bd2core and bd2io
587 for conn
in self
._ios
:
588 if conn
._iotype
== IOType
.In
:
589 m
.d
.comb
+= conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
))
591 elif conn
._iotype
== IOType
.Out
:
592 m
.d
.comb
+= conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
))
594 elif conn
._iotype
== IOType
.TriOut
:
596 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
], conn
.core
.o
)),
597 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.oe
)),
600 elif conn
._iotype
== IOType
.InTriOut
:
602 conn
.core
.i
.eq(Mux(bd2core
, io_bd
[idx
], conn
.pad
.i
)),
603 conn
.pad
.o
.eq(Mux(bd2io
, io_bd
[idx
+1], conn
.core
.o
)),
604 conn
.pad
.oe
.eq(Mux(bd2io
, io_bd
[idx
+2], conn
.core
.oe
)),
608 raise("Internal error")
609 assert idx
== length
, "Internal error"
613 def add_shiftreg(self
, *, ircode
, length
, domain
="sync", name
=None,
615 """Add a shift register to the JTAG interface
618 - ircode: code(s) for the IR; int or sequence of ints. In the latter
619 case this shiftreg is shared between different IR codes.
620 - length: the length of the shift register
621 - domain: the domain on which the signal will be used"""
627 ir_it
= ircodes
= (ircode
,)
628 for _ircode
in ir_it
:
629 if not isinstance(_ircode
, int) or _ircode
<= 0:
630 raise ValueError("IR code '{}' is not an int "
631 "greater than 0".format(_ircode
))
632 if _ircode
in self
._ircodes
:
633 raise ValueError("IR code '{}' already taken".format(_ircode
))
635 self
._ircodes
.extend(ircodes
)
638 name
= "sr{}".format(len(self
._srs
))
639 sr
= ShiftReg(sr_length
=length
, cmds
=len(ircodes
), name
=name
,
640 src_loc_at
=src_loc_at
+1)
641 self
._srs
.append((ircodes
, domain
, sr
))
645 def _elaborate_shiftregs(self
, m
, capture
, shift
, update
, ir
, tdo_jtag
):
646 # tdos is tuple of (tdo, tdo_en) for each shiftreg
648 for ircodes
, domain
, sr
in self
._srs
:
649 reg
= Signal(len(sr
.o
), name
=sr
.name
+"_reg")
650 m
.d
.comb
+= sr
.o
.eq(reg
)
652 isir
= Signal(len(ircodes
), name
=sr
.name
+"_isir")
653 sr_capture
= Signal(name
=sr
.name
+"_capture")
654 sr_shift
= Signal(name
=sr
.name
+"_shift")
655 sr_update
= Signal(name
=sr
.name
+"_update")
657 isir
.eq(Cat(ir
== ircode
for ircode
in ircodes
)),
658 sr_capture
.eq((isir
!= 0) & capture
),
659 sr_shift
.eq((isir
!= 0) & shift
),
660 sr_update
.eq((isir
!= 0) & update
),
663 # update signal is on the JTAG clockdomain, sr.oe is on `domain`
664 # clockdomain latch update in `domain` clockdomain and see when
665 # it has falling edge.
666 # At that edge put isir in sr.oe for one `domain` clockdomain
667 # Note: could use FFSynchroniser instead
668 update_core
= Signal(name
=sr
.name
+"_update_core")
669 update_core_prev
= Signal(name
=sr
.name
+"_update_core_prev")
671 update_core
.eq(sr_update
), # This is CDC from JTAG domain
673 update_core_prev
.eq(update_core
)
675 with m
.If(update_core_prev
& ~update_core
):
676 # Falling edge of update
677 m
.d
[domain
] += sr
.oe
.eq(isir
)
679 m
.d
[domain
] += sr
.oe
.eq(0)
682 m
.d
.posjtag
+= reg
.eq(Cat(reg
[1:], self
.bus
.tdi
))
683 with m
.If(sr_capture
):
684 # could also use FFSynchroniser here too
685 m
.d
.posjtag
+= reg
.eq(sr
.i
)
687 # tdo = reg[0], tdo_en = shift
688 tdos
.append((reg
[0], sr_shift
))
691 # Assign the right tdo to the bus tdo
692 for i
, (tdo
, tdo_en
) in enumerate(tdos
):
695 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
698 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo
)
702 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
704 # Always connect tdo_jtag to
705 m
.d
.comb
+= self
.bus
.tdo
.eq(tdo_jtag
)
708 def add_wishbone(self
, *, ircodes
, address_width
, data_width
,
709 granularity
=None, domain
="sync", features
=None,
710 name
=None, src_loc_at
=0):
711 """Add a wishbone interface
713 In order to allow high JTAG clock speed, data will be cached.
714 This means that if data is output the value of the next address
715 will be read automatically.
719 ircodes: sequence of three integer for the JTAG IR codes;
720 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
721 has a shift register of length 'address_width', the two other codes
722 share a shift register of length data_width.
723 address_width: width of the address
724 data_width: width of the data
725 features: features required. defaults to stall, lock, err, rty
728 wb: nmigen_soc.wishbone.bus.Interface
729 The Wishbone interface, is pipelined and has stall field.
731 if len(ircodes
) != 3:
732 raise ValueError("3 IR Codes have to be provided")
735 features
={"stall", "lock", "err", "rty"}
737 name
= "wb" + str(len(self
._wbs
))
738 sr_addr
= self
.add_shiftreg(
739 ircode
=ircodes
[0], length
=address_width
, domain
=domain
,
742 sr_data
= self
.add_shiftreg(
743 ircode
=ircodes
[1:], length
=data_width
, domain
=domain
,
747 wb
= WishboneInterface(data_width
=data_width
, addr_width
=address_width
,
748 granularity
=granularity
, features
=features
,
749 name
=name
, src_loc_at
=src_loc_at
+1)
751 self
._wbs
.append((sr_addr
, sr_data
, wb
, domain
))
755 def _elaborate_wishbones(self
, m
):
756 for sr_addr
, sr_data
, wb
, domain
in self
._wbs
:
757 m
.d
.comb
+= sr_addr
.i
.eq(wb
.adr
)
759 if hasattr(wb
, "sel"):
761 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
763 with m
.FSM(domain
=domain
) as fsm
:
764 with m
.State("IDLE"):
765 with m
.If(sr_addr
.oe
): # WBADDR code
766 m
.d
[domain
] += wb
.adr
.eq(sr_addr
.o
)
768 with m
.Elif(sr_data
.oe
[0]): # WBREAD code
770 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
772 with m
.Elif(sr_data
.oe
[1]): # WBWRITE code
773 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
775 with m
.State("READ"):
776 if not hasattr(wb
, "stall"):
779 with m
.If(~wb
.stall
):
781 with m
.State("READACK"):
783 # Store read data in sr_data.i and keep it there
784 # til next read. could use FFSynchroniser (see above)
785 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
787 with m
.State("WRITEREAD"):
788 if not hasattr(wb
, "stall"):
789 m
.next
= "WRITEREADACK"
791 with m
.If(~wb
.stall
):
792 m
.next
= "WRITEREADACK"
793 with m
.State("WRITEREADACK"):
795 m
.d
[domain
] += wb
.adr
.eq(wb
.adr
+ 1)
798 if hasattr(wb
, "stall"):
799 m
.d
.comb
+= wb
.stb
.eq(fsm
.ongoing("READ") |
800 fsm
.ongoing("WRITEREAD"))
801 m
.d
.comb
+= wb
.we
.eq(fsm
.ongoing("WRITEREAD"))
803 # non-stall is single-cycle (litex), must assert stb
805 m
.d
.comb
+= wb
.stb
.eq(fsm
.ongoing("READ") |
806 fsm
.ongoing("WRITEREAD") |
807 fsm
.ongoing("READACK") |
808 fsm
.ongoing("WRITEREADACK"))
809 m
.d
.comb
+= wb
.we
.eq(fsm
.ongoing("WRITEREAD") |
810 fsm
.ongoing("WRITEREADACK"))
811 m
.d
.comb
+= wb
.cyc
.eq(~fsm
.ongoing("IDLE"))