d1c05a6d8cf3e3d58d5ef533177f7404ea10fdf1
3 using Staf Verhaegen (Chips4Makers) wishbone TAP
6 from nmigen
.build
.res
import ResourceManager
7 from nmigen
.hdl
.rec
import Layout
8 from collections
import OrderedDict
9 from nmigen
.cli
import rtlil
11 from nmigen
import (Module
, Signal
, Elaboratable
, Cat
)
12 from c4m
.nmigen
.jtag
.tap
import IOType
, TAP
14 # map from pinmux to c4m jtag iotypes
15 iotypes
= {'-': IOType
.In
,
22 # nmigen Resources has a different encoding for direction: "i", "o", "io", "oe"
23 resiotypes
= {'i': IOType
.In
,
26 'io': IOType
.InTriOut
,
29 # How many bits in each signal type
30 scanlens
= {IOType
.In
: 1,
38 # sigh this needs to come from pinmux.
41 gpios
.append("%d*" % i
)
42 return {'uart': ['tx+', 'rx-'],
44 'i2c': ['sda*', 'scl+']}
47 # TODO: move to suitable location
49 """declare a list of pins, including name and direction. grouped by fn
50 the pin dictionary needs to be in a reliable order so that the JTAG
51 Boundary Scan is also in a reliable order
53 def __init__(self
, pindict
=None):
56 self
.io_names
= OrderedDict()
57 if isinstance(pindict
, OrderedDict
):
58 self
.io_names
.update(pindict
)
60 keys
= list(pindict
.keys())
63 self
.io_names
[k
] = pindict
[k
]
66 # start parsing io_names and enumerate them to return pin specs
68 for fn
, pins
in self
.io_names
.items():
70 # decode the pin name and determine the c4m jtag io type
71 name
, pin_type
= pin
[:-1], pin
[-1]
72 iotype
= iotypes
[pin_type
]
73 pin_name
= "%s_%s" % (fn
, name
)
74 yield (fn
, name
, iotype
, pin_name
, scan_idx
)
75 scan_idx
+= scanlens
[iotype
] # inc boundary reg scan offset
78 def recurse_down(asicpad
, jtagpad
):
79 """recurse_down: messy ASIC-to-JTAG pad matcher which expects
80 at some point some Records named i, o and oe, and wires them
81 up in the right direction according to those names. "i" for
82 input must come *from* the ASIC pad and connect *to* the JTAG pad
85 for asiclayout
, jtaglayout
in zip(asicpad
.layout
, jtagpad
.layout
):
86 apad
= getattr(asicpad
, asiclayout
[0])
87 jpad
= getattr(jtagpad
, jtaglayout
[0])
88 print ("recurse_down", asiclayout
, jtaglayout
, apad
, jpad
)
89 if isinstance(asiclayout
[1], Layout
):
90 eqs
+= recurse_down(apad
, jpad
)
91 elif asiclayout
[0] == 'i':
92 eqs
.append(jpad
.eq(apad
))
93 elif asiclayout
[0] in ['o', 'oe']:
94 eqs
.append(apad
.eq(jpad
))
98 class JTAG(TAP
, Pins
):
99 # 32-bit data width here. use None to not add a wishbone interface
100 def __init__(self
, pinset
, domain
, wb_data_wid
=32, resources
=None):
101 if resources
is None:
104 TAP
.__init
__(self
, ir_width
=4)
105 Pins
.__init
__(self
, pinset
)
107 # enumerate pin specs and create IOConn Records.
108 # we store the boundary scan register offset in the IOConn record
109 self
.ios
= {} # these are enumerated in external_ports
111 self
.add_pins(list(self
))
113 # this is redundant. or maybe part of testing, i don't know.
114 self
.sr
= self
.add_shiftreg(ircode
=4, length
=3,
117 # create and connect wishbone
118 if wb_data_wid
is not None:
119 self
.wb
= self
.add_wishbone(ircodes
=[5, 6, 7], features
={'err'},
120 address_width
=30, data_width
=wb_data_wid
,
121 granularity
=8, # 8-bit wide
125 # create DMI2JTAG (goes through to dmi_sim())
126 self
.dmi
= self
.add_dmi(ircodes
=[8, 9, 10],
129 # use this for enable/disable of parts of the ASIC.
130 # XXX make sure to add the _en sig to en_sigs list
131 self
.wb_icache_en
= Signal(reset
=1)
132 self
.wb_dcache_en
= Signal(reset
=1)
133 self
.wb_sram_en
= Signal(reset
=1)
134 self
.en_sigs
= en_sigs
= Cat(self
.wb_icache_en
, self
.wb_dcache_en
,
136 self
.sr_en
= self
.add_shiftreg(ircode
=11, length
=len(en_sigs
),
139 # Platform Resource Mirror: enumerated by boundary_elaborate()
140 # in order to make a transparent/auto wire-up of what would
141 # normally be directly connected to IO Pads, to go instead
142 # first through a JTAG Boundary Scan... *and then* get auto-
143 # connected on ultimately to the IO Pads. to do that, the best
144 # API is one that reflects that of Platforms... and that means
145 # using duplicate ResourceManagers so that the user may use
146 # the exact same resource-requesting function, "request", and
147 # may also use the exact same Resource list
149 self
.pad_mgr
= ResourceManager([], [])
150 self
.core_mgr
= ResourceManager([], [])
151 self
.pad_mgr
.add_resources(resources
)
152 self
.core_mgr
.add_resources(resources
)
154 # record resource lookup between core IO names and pads
156 self
.requests_made
= []
157 self
.boundary_scan_pads
= []
158 self
.resource_table
= {}
159 self
.resource_table_pads
= {}
160 self
.eqs
= [] # list of BS to core/pad connections
162 # allocate all resources in advance in pad/core ResourceManagers
163 # this is because whilst a completely new (different) platform is
164 # passed in to elaborate()s every time, that cannot happen with
165 # JTAG Boundary scanning: the resources are allocated *prior*
166 # to elaborate() being called [from Simulation(), Platform.build(),
167 # and many other sources, multiple times]
169 for resource
in resources
:
170 print ("JTAG resource", resource
)
171 if resource
.name
in ['clk', 'rst']: # hack
173 self
.add_jtag_resource(resource
.name
, resource
.number
)
175 def add_pins(self
, pinlist
):
176 for fn
, pin
, iotype
, pin_name
, scan_idx
in pinlist
:
177 io
= self
.add_io(iotype
=iotype
, name
=pin_name
)
178 io
._scan
_idx
= scan_idx
# hmm shouldn't really do this
179 self
.scan_len
+= scan_idx
# record full length of boundary scan
180 self
.ios
[pin_name
] = io
182 def elaborate(self
, platform
):
183 m
= super().elaborate(platform
)
184 m
.d
.comb
+= self
.sr
.i
.eq(self
.sr
.o
) # loopback as part of test?
186 # provide way to enable/disable wishbone caches and SRAM
187 # just in case of issues
188 # see https://bugs.libre-soc.org/show_bug.cgi?id=520
189 with m
.If(self
.sr_en
.oe
):
190 m
.d
.sync
+= self
.en_sigs
.eq(self
.sr_en
.o
)
191 # also make it possible to read the enable/disable current state
192 with m
.If(self
.sr_en
.ie
):
193 m
.d
.comb
+= self
.sr_en
.i
.eq(self
.en_sigs
)
195 # create a fake "stall"
197 #m.d.comb += wb.stall.eq(wb.cyc & ~wb.ack) # No burst support
201 def boundary_elaborate(self
, m
, platform
):
202 jtag_resources
= self
.pad_mgr
.resources
203 core_resources
= self
.core_mgr
.resources
205 # platform requested: make the exact same requests,
206 # then add JTAG afterwards
207 if platform
is not None:
208 for (name
, number
, dir, xdr
) in self
.requests_made
:
209 asicpad
= platform
.request(name
, number
, dir=dir, xdr
=xdr
)
210 jtagpad
= self
.resource_table_pads
[(name
, number
)]
211 print ("jtagpad", jtagpad
, jtagpad
.layout
)
212 m
.d
.comb
+= recurse_down(asicpad
, jtagpad
)
214 # wire up JTAG otherwise we are in trouble
215 jtag
= platform
.request('jtag')
216 m
.d
.comb
+= self
.bus
.tdi
.eq(jtag
.tdi
)
217 m
.d
.comb
+= self
.bus
.tck
.eq(jtag
.tck
)
218 m
.d
.comb
+= self
.bus
.tms
.eq(jtag
.tms
)
219 m
.d
.comb
+= jtag
.tdo
.eq(self
.bus
.tdo
)
221 # add the eq assignments connecting up JTAG boundary scan to core
225 def external_ports(self
):
226 """create a list of ports that goes into the top level il (or verilog)
228 ports
= super().external_ports() # gets JTAG signal names
229 ports
+= list(self
.wb
.fields
.values()) # wishbone signals
230 for io
in self
.ios
.values():
231 ports
+= list(io
.core
.fields
.values()) # io "core" signals
232 ports
+= list(io
.pad
.fields
.values()) # io "pad" signals"
236 return list(self
.iter_ports())
238 def iter_ports(self
):
243 yield from self
.boundary_scan_pads
245 def request(self
, name
, number
=0, *, dir=None, xdr
=None):
246 """looks like ResourceManager.request but can be called multiple times.
248 return self
.resource_table
[(name
, number
)]
250 def add_jtag_resource(self
, name
, number
=0, *, dir=None, xdr
=None):
251 """request a Resource (e.g. name="uart", number=0) which will
252 return a data structure containing Records of all the pins.
254 this override will also - automatically - create a JTAG Boundary Scan
255 connection *without* any change to the actual Platform.request() API
257 pad_mgr
= self
.pad_mgr
258 core_mgr
= self
.core_mgr
259 padlookup
= self
.padlookup
260 # okaaaay, bit of shenanigens going on: the important data structure
261 # here is Resourcemanager._ports. requests add to _ports, which is
262 # what needs redirecting. therefore what has to happen is to
263 # capture the number of ports *before* the request. sigh.
264 start_ports
= len(core_mgr
._ports
)
265 value
= core_mgr
.request(name
, number
, dir=dir, xdr
=xdr
)
266 end_ports
= len(core_mgr
._ports
)
268 # take a copy of the requests made
269 self
.requests_made
.append((name
, number
, dir, xdr
))
271 # now make a corresponding (duplicate) request to the pad manager
272 # BUT, if it doesn't exist, don't sweat it: all it means is, the
273 # application did not request Boundary Scan for that resource.
274 pad_start_ports
= len(pad_mgr
._ports
)
275 pvalue
= pad_mgr
.request(name
, number
, dir=dir, xdr
=xdr
)
276 pad_end_ports
= len(pad_mgr
._ports
)
278 # ok now we have the lengths: now create a lookup between the pad
279 # and the core, so that JTAG boundary scan can be inserted in between
280 core
= core_mgr
._ports
[start_ports
:end_ports
]
281 pads
= pad_mgr
._ports
[pad_start_ports
:pad_end_ports
]
282 # oops if not the same numbers added. it's a duplicate. shouldn't happen
283 assert len(core
) == len(pads
), "argh, resource manager error"
287 # pad/core each return a list of tuples of (res, pin, port, attrs)
288 for pad
, core
in zip(pads
, core
):
289 # create a lookup on pin name to get at the hidden pad instance
290 # this pin name will be handed to get_input, get_output etc.
291 # and without the padlookup you can't find the (duplicate) pad.
292 # note that self.padlookup and self.ios use the *exact* same
296 if padpin
is None: continue # skip when pin is None
297 assert corepin
is not None # if pad was None, core should be too
298 print ("iter", pad
, padpin
.name
)
299 print ("existing pads", padlookup
.keys())
300 assert padpin
.name
not in padlookup
# no overwrites allowed!
301 assert padpin
.name
== corepin
.name
# has to be the same!
302 padlookup
[padpin
.name
] = pad
# store pad by pin name
304 # now add the IO Shift Register. first identify the type
305 # then request a JTAG IOConn. we can't wire it up (yet) because
306 # we don't have a Module() instance. doh. that comes in get_input
307 # and get_output etc. etc.
308 iotype
= resiotypes
[padpin
.dir] # look up the C4M-JTAG IOType
309 io
= self
.add_io(iotype
=iotype
, name
=padpin
.name
) # IOConn
310 self
.ios
[padpin
.name
] = io
# store IOConn Record by pin name
312 # and connect up core to pads based on type. could create
313 # Modules here just like in Platform.get_input/output but
314 # in some ways it is clearer by being simpler to wire them globally
316 if padpin
.dir == 'i':
317 print ("jtag_request add input pin", padpin
)
318 print (" corepin", corepin
)
319 print (" jtag io core", io
.core
)
320 print (" jtag io pad", io
.pad
)
321 # corepin is to be returned, here. so, connect jtag corein to it
322 self
.eqs
+= [corepin
.i
.eq(io
.core
.i
)]
323 # and padpin to JTAG pad
324 self
.eqs
+= [io
.pad
.i
.eq(padpin
.i
)]
325 self
.boundary_scan_pads
.append(padpin
.i
)
326 elif padpin
.dir == 'o':
327 print ("jtag_request add output pin", padpin
)
328 print (" corepin", corepin
)
329 print (" jtag io core", io
.core
)
330 print (" jtag io pad", io
.pad
)
331 # corepin is to be returned, here. connect it to jtag core out
332 self
.eqs
+= [io
.core
.o
.eq(corepin
.o
)]
333 # and JTAG pad to padpin
334 self
.eqs
+= [padpin
.o
.eq(io
.pad
.o
)]
335 self
.boundary_scan_pads
.append(padpin
.o
)
336 elif padpin
.dir == 'io':
337 print ("jtag_request add io pin", padpin
)
338 print (" corepin", corepin
)
339 print (" jtag io core", io
.core
)
340 print (" jtag io pad", io
.pad
)
341 # corepin is to be returned, here. so, connect jtag corein to it
342 self
.eqs
+= [corepin
.i
.eq(io
.core
.i
)]
343 # and padpin to JTAG pad
344 self
.eqs
+= [io
.pad
.i
.eq(padpin
.i
)]
345 # corepin is to be returned, here. connect it to jtag core out
346 self
.eqs
+= [io
.core
.o
.eq(corepin
.o
)]
347 # and JTAG pad to padpin
348 self
.eqs
+= [padpin
.o
.eq(io
.pad
.o
)]
349 # corepin is to be returned, here. connect it to jtag core out
350 self
.eqs
+= [io
.core
.oe
.eq(corepin
.oe
)]
351 # and JTAG pad to padpin
352 self
.eqs
+= [padpin
.oe
.eq(io
.pad
.oe
)]
354 self
.boundary_scan_pads
.append(padpin
.i
)
355 self
.boundary_scan_pads
.append(padpin
.o
)
356 self
.boundary_scan_pads
.append(padpin
.oe
)
358 # finally record the *CORE* value just like ResourceManager.request()
359 # so that the module using this can connect to *CORE* i/o to the
360 # resource. pads are taken care of
361 self
.resource_table
[(name
, number
)] = value
363 # and the *PAD* value so that it can be wired up externally as well
364 self
.resource_table_pads
[(name
, number
)] = pvalue
366 if __name__
== '__main__':
367 pinset
= dummy_pinset()
368 dut
= JTAG(pinset
, "sync")
370 vl
= rtlil
.convert(dut
)
371 with
open("test_jtag.il", "w") as f
: