JTAG boundary scan test 1st attempt
[soc.git] / src / soc / debug / jtag.py
1 """JTAG interface
2
3 using Staf Verhaegen (Chips4Makers) wishbone TAP
4 """
5
6 from collections import OrderedDict
7 from nmigen import (Module, Signal, Elaboratable)
8 from nmigen.cli import rtlil
9 from c4m.nmigen.jtag.tap import IOType
10 from soc.debug.dmi import DMIInterface, DBGCore
11 from soc.debug.dmi2jtag import DMITAP
12
13 # map from pinmux to c4m jtag iotypes
14 iotypes = {'-': IOType.In,
15 '+': IOType.Out,
16 '>': IOType.TriOut,
17 '*': IOType.InTriOut,
18 }
19
20 scanlens = {IOType.In: 1,
21 IOType.Out: 1,
22 IOType.TriOut: 2,
23 IOType.InTriOut: 3,
24 }
25
26 def dummy_pinset():
27 # sigh this needs to come from pinmux.
28 gpios = []
29 for i in range(16):
30 gpios.append("%d*" % i)
31 return {'uart': ['tx+', 'rx-'],
32 'gpio': gpios,
33 'i2c': ['sda*', 'scl+']}
34
35 # TODO: move to suitable location
36 class Pins:
37 """declare a list of pins, including name and direction. grouped by fn
38 the pin dictionary needs to be in a reliable order so that the JTAG
39 Boundary Scan is also in a reliable order
40 """
41 def __init__(self, pindict):
42 self.io_names = OrderedDict()
43 if isinstance(pindict, OrderedDict):
44 self.io_names.update(pindict)
45 else:
46 keys = list(pindict.keys())
47 keys.sort()
48 for k in keys:
49 self.io_names[k] = pindict[k]
50
51 def __iter__(self):
52 # start parsing io_names and enumerate them to return pin specs
53 scan_idx = 0
54 for fn, pins in self.io_names.items():
55 for pin in pins:
56 # decode the pin name and determine the c4m jtag io type
57 name, pin_type = pin[:-1], pin[-1]
58 iotype = iotypes[pin_type]
59 pin_name = "%s_%s" % (fn, name)
60 yield (fn, name, iotype, pin_name, scan_idx)
61 scan_idx += scanlens[iotype] # inc boundary reg scan offset
62
63
64 class JTAG(DMITAP, Pins):
65 def __init__(self, pinset, wb_data_wid=64):
66 DMITAP.__init__(self, ir_width=4)
67 Pins.__init__(self, pinset)
68
69 # enumerate pin specs and create IOConn Records.
70 # we store the boundary scan register offset in the IOConn record
71 self.ios = [] # these are enumerated in external_ports
72 self.scan_len = 0
73 for fn, pin, iotype, pin_name, scan_idx in list(self):
74 io = self.add_io(iotype=iotype, name=pin_name)
75 io._scan_idx = scan_idx # hmm shouldn't really do this
76 self.scan_len += scan_idx # record full length of boundary scan
77 self.ios.append(io)
78
79 # this is redundant. or maybe part of testing, i don't know.
80 self.sr = self.add_shiftreg(ircode=4, length=3)
81
82 # create and connect wishbone
83 self.wb = self.add_wishbone(ircodes=[5, 6, 7],
84 address_width=29, data_width=wb_data_wid,
85 name="jtag_wb")
86
87 # create DMI2JTAG (goes through to dmi_sim())
88 self.dmi = self.add_dmi(ircodes=[8, 9, 10])
89
90 def elaborate(self, platform):
91 m = super().elaborate(platform)
92 m.d.comb += self.sr.i.eq(self.sr.o) # loopback as part of test?
93 return m
94
95 def external_ports(self):
96 """create a list of ports that goes into the top level il (or verilog)
97 """
98 ports = super().external_ports() # gets JTAG signal names
99 ports += list(self.wb.fields.values()) # wishbone signals
100 for io in self.ios:
101 ports += list(io.core.fields.values()) # io "core" signals
102 ports += list(io.pad.fields.values()) # io "pad" signals"
103 return ports
104
105
106 if __name__ == '__main__':
107 pinset = dummy_pinset()
108 dut = JTAG(pinset)
109
110 vl = rtlil.convert(dut)
111 with open("test_jtag.il", "w") as f:
112 f.write(vl)
113