5 from nmigen
.build
import *
6 from nmigen
.lib
.io
import *
8 from wishbone
import Wishbone
11 "PmodJTAGMasterResource",
12 "PmodJTAGMasterAResource",
13 "PmodJTAGSlaveResource",
14 "PmodJTAGSlaveAResource",
18 #TODO: Provide more documentation
20 def PmodJTAGMasterResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
21 return Resource(name
, number
,
22 Subsignal("TCK", Pins("1", dir="o", conn
=("pmod", pmod
))),
23 Subsignal("TMS", Pins("2", dir="o", conn
=("pmod", pmod
))),
24 Subsignal("TDO", Pins("3", dir="o", conn
=("pmod", pmod
))),
25 Subsignal("TDI", Pins("4", dir="i", conn
=("pmod", pmod
))),
29 def PmodJTAGMasterAResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
30 return Resource(name
, number
,
31 Subsignal("TCK", Pins("1", dir="o", conn
=("pmod", pmod
))),
32 Subsignal("TMS", Pins("2", dir="o", conn
=("pmod", pmod
))),
33 Subsignal("TDO", Pins("3", dir="o", conn
=("pmod", pmod
))),
34 Subsignal("TDI", Pins("4", dir="i", conn
=("pmod", pmod
))),
35 Subsignal("TRST", PinsN("7", dir="o", conn
=("pmod", pmod
))),
39 def PmodJTAGSlaveResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
40 return Resource(name
, number
,
41 Subsignal("TCK", Pins("1", dir="i", conn
=("pmod", pmod
))),
42 Subsignal("TMS", Pins("2", dir="i", conn
=("pmod", pmod
))),
43 Subsignal("TDI", Pins("3", dir="i", conn
=("pmod", pmod
))),
44 Subsignal("TDO", Pins("4", dir="o", conn
=("pmod", pmod
))),
48 def PmodJTAGSlaveAResource(name
, number
, *, pmod
, attrs
=Attrs(IOSTANDARD
="LVCMOS33")):
49 return Resource(name
, number
,
50 Subsignal("TCK", Pins("1", dir="i", conn
=("pmod", pmod
))),
51 Subsignal("TMS", Pins("2", dir="i", conn
=("pmod", pmod
))),
52 Subsignal("TDI", Pins("3", dir="i", conn
=("pmod", pmod
))),
53 Subsignal("TDO", Pins("4", dir="o", conn
=("pmod", pmod
))),
54 Subsignal("TRST", PinsN("7", dir="i", conn
=("pmod", pmod
))),
59 def _add_files(platform
, prefix
):
60 d
= os
.path
.realpath("{0}{1}{2}{1}vhdl".format(
61 os
.path
.dirname(__file__
), os
.path
.sep
, os
.path
.pardir
65 "c4m_jtag_idblock.vhdl",
66 "c4m_jtag_iocell.vhdl",
67 "c4m_jtag_ioblock.vhdl",
68 "c4m_jtag_irblock.vhdl",
69 "c4m_jtag_tap_fsm.vhdl",
70 "c4m_jtag_tap_controller.vhdl",
72 f
= open(d
+ fname
, "r")
73 platform
.add_file(prefix
+ fname
, f
)
77 class ShiftReg(Elaboratable
):
78 def __init__(self
, ircodes
, length
, domain
):
79 # The sr record will be returned to user code
80 self
.sr
= Record([("i", length
), ("o", length
), ("oe", len(ircodes
)), ("ack", 1)])
81 # The next attributes are for JTAG class usage only
82 self
.ir
= None # made None as width is not known yet
85 self
.tdo_en
= Signal()
86 self
.capture
= Signal()
88 self
.update
= Signal()
89 self
.jtag_cd
= None # The JTAG clock domain
93 self
._ircodes
= ircodes
96 def elaborate(self
, platform
):
97 length
= len(self
.sr
.o
)
102 m
.domains
.jtag
= self
.jtag_cd
104 sr_jtag
= Signal(length
)
106 assert isinstance(self
.ir
, Signal
)
107 isir
= Signal(len(self
._ircodes
))
112 isir
.eq(Cat(self
.ir
== ircode
for ircode
in self
._ircodes
)),
113 capture
.eq((isir
!= 0) & self
.capture
),
114 shift
.eq((isir
!= 0) & self
.shift
),
115 update
.eq((isir
!= 0) & self
.update
),
118 # On update set o, oe and wait for ack
119 # update signal is on JTAG clockdomain, latch it
120 update_core
= Signal()
121 m
.d
[domain
] += update_core
.eq(update
) # This is CDC from JTAG domain to given domain
122 with m
.FSM(domain
=domain
):
123 with m
.State("IDLE"):
124 m
.d
.comb
+= self
.sr
.oe
.eq(0)
125 with m
.If(update_core
):
126 # Latch sr_jtag cross domain but it should be stable due to latching of update_core
127 m
.d
[domain
] += self
.sr
.o
.eq(sr_jtag
)
128 # Wait one cycle to raise oe so sr.o has one more cycle to stabilize
130 with m
.State("WAIT4ACK"):
131 m
.d
.comb
+= self
.sr
.oe
.eq(isir
)
132 with m
.If(self
.sr
.ack
):
134 with m
.State("WAIT4END"):
135 m
.d
.comb
+= self
.sr
.oe
.eq(0)
136 with m
.If(~update_core
):
140 self
.tdo
.eq(sr_jtag
[0]),
141 self
.tdo_en
.eq(shift
),
145 m
.d
.jtag
+= sr_jtag
.eq(Cat(sr_jtag
[1:], self
.tdi
))
147 m
.d
.jtag
+= sr_jtag
.eq(self
.sr
.i
)
151 class JTAGWishbone(Elaboratable
):
152 def __init__(self
, sr_addr
, sr_data
, wb
, domain
):
153 self
._sr
_addr
= sr_addr
154 self
._sr
_data
= sr_data
156 self
._domain
= domain
161 def elaborate(self
, platform
):
162 sr_addr
= self
._sr
_addr
163 sr_data
= self
._sr
_data
165 domain
= self
._domain
170 if hasattr(wb
, "sel"):
172 m
.d
.comb
+= [s
.eq(1) for s
in wb
.sel
]
176 sr_addr
.ack
.eq(sr_addr
.oe
),
177 sr_data
.ack
.eq(sr_data
.oe
!= 0),
180 with m
.FSM(domain
=domain
) as fsm
:
181 with m
.State("IDLE"):
187 with m
.If(sr_addr
.oe
): # WBADDR code
188 m
.d
[domain
] += wb
.addr
.eq(sr_addr
.o
)
190 with m
.If(sr_data
.oe
[0]): # WBREAD code
191 m
.d
[domain
] += wb
.addr
.eq(wb
.addr
+ 1)
193 with m
.If(sr_data
.oe
[1]): # WBWRITE code
194 m
.d
[domain
] += wb
.dat_w
.eq(sr_data
.o
)
196 with m
.State("READ"):
202 with m
.If(~wb
.stall
):
204 with m
.State("READACK"):
211 m
.d
[domain
] += sr_data
.i
.eq(wb
.dat_r
)
213 with m
.State("WRITEREAD"):
219 with m
.If(~wb
.stall
):
220 m
.next
= "WRITEREADACK"
221 with m
.State("WRITEREADACK"):
228 m
.d
[domain
] += wb
.addr
.eq(wb
.addr
+ 1)
234 class JTAG(Elaboratable
):
237 def __init__(self
, io_count
, *, ir_width
=None, manufacturer_id
=Const(0b10001111111, 11),
238 part_number
=Const(1, 16), version
=Const(0, 4)
240 assert(isinstance(io_count
, int) and io_count
> 0)
241 assert((ir_width
is None) or (isinstance(ir_width
, int) and ir_width
>= 2))
242 assert(len(version
) == 4)
244 # TODO: Handle IOs with different directions
249 self
.core
= Array(Pin(1, "io") for _
in range(io_count
)) # Signals to use for core
250 self
.pad
= Array(Pin(1, "io") for _
in range(io_count
)) # Signals going to IO pads
252 self
.jtag_cd
= ClockDomain(name
="jtag", local
=True) # Own clock domain using TCK as clock signal
256 self
._io
_count
= io_count
257 self
._ir
_width
= ir_width
258 self
._manufacturer
_id
= manufacturer_id
259 self
._part
_number
= part_number
260 self
._version
= version
262 self
._ircodes
= [0, 1, 2] # Already taken codes, all ones added at the end
267 def elaborate(self
, platform
):
268 _add_files(platform
, "jtag" + os
.path
.sep
)
279 ir_max
= max(self
._ircodes
) + 1 # One extra code needed with all ones
280 ir_width
= len("{:b}".format(ir_max
))
281 if self
._ir
_width
is not None:
282 assert self
._ir
_width
>= ir_width
, "Specified JTAG IR width not big enough for allocated shiift registers"
283 ir_width
= self
._ir
_width
284 ir
= Signal(ir_width
)
286 core_i
= Cat(pin
.i
for pin
in self
.core
)
287 core_o
= Cat(pin
.o
for pin
in self
.core
)
288 core_oe
= Cat(pin
.oe
for pin
in self
.core
)
289 pad_i
= Cat(pin
.i
for pin
in self
.pad
)
290 pad_o
= Cat(pin
.o
for pin
in self
.pad
)
291 pad_oe
= Cat(pin
.oe
for pin
in self
.pad
)
294 "p_IOS": self
._io
_count
,
295 "p_IR_WIDTH": ir_width
,
296 "p_MANUFACTURER": self
._manufacturer
_id
,
297 "p_PART_NUMBER": self
._part
_number
,
298 "p_VERSION": self
._version
,
303 "i_TRST_N": Const(1),
305 "o_DRCAPTURE": capture
,
307 "o_DRUPDATE": update
,
310 "i_CORE_OUT": core_o
,
311 "i_CORE_EN": core_oe
,
316 m
.submodules
.tap
= Instance("c4m_jtag_tap_controller", **params
)
319 self
.jtag_cd
.clk
.eq(self
.tck
),
320 self
.jtag_cd
.rst
.eq(reset
),
323 for i
, sr
in enumerate(self
._srs
):
324 m
.submodules
["sr{}".format(i
)] = sr
328 sr
.capture
.eq(capture
),
330 sr
.update
.eq(update
),
333 if len(self
._srs
) > 0:
338 with m
.If(sr
.tdo_en
):
339 m
.d
.comb
+= self
.tdo
.eq(sr
.tdo
)
341 with m
.Elif(sr
.tdo_en
):
342 m
.d
.comb
+= self
.tdo
.eq(sr
.tdo
)
344 m
.d
.comb
+= self
.tdo
.eq(tdo_jtag
)
346 m
.d
.comb
+= self
.tdo
.eq(tdo_jtag
)
348 for i
, wb
in enumerate(self
._wbs
):
349 m
.submodules
["wb{}".format(i
)] = wb
355 def add_shiftreg(self
, ircode
, length
, domain
="sync"):
356 """Add a shift register to the JTAG interface
359 - ircode: code(s) for the IR; int or sequence of ints. In the latter case this
360 shiftreg is shared between different IR codes.
361 - length: the length of the shift register
362 - domain: the domain on which the signal will be used"""
368 ir_it
= ircodes
= (ircode
,)
369 for _ircode
in ir_it
:
370 assert(isinstance(_ircode
, int) and _ircode
> 0 and _ircode
not in self
._ircodes
)
372 sr
= ShiftReg(ircodes
, length
, domain
)
373 sr
.jtag_cd
= self
.jtag_cd
374 self
._ircodes
.extend(ircodes
)
380 def add_wishbone(self
, ircodes
, address_width
, data_width
, sel_width
=None, domain
="sync"):
381 """Add a wishbone interface
384 - ircodes: sequence of three integer for the JTAG IR codes;
385 they represent resp. WBADDR, WBREAD and WBREADWRITE. First code
386 has a shift register of length 'address_width', the two other codes
387 share a shift register of length data_width.
388 - address_width: width of the address
389 - data_width: width of the data"""
391 assert len(ircodes
) == 3
393 sr_addr
= self
.add_shiftreg(ircodes
[0], address_width
, domain
=domain
)
394 sr_data
= self
.add_shiftreg(ircodes
[1:], data_width
, domain
=domain
)
396 wb
= Wishbone(data_width
=data_width
, address_width
=address_width
, sel_width
=sel_width
, master
=True)
398 self
._wbs
.append(JTAGWishbone(sr_addr
, sr_data
, wb
, domain
))