move pin-adding into separate function in JTAG class
[pinmux.git] / src / spec / 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, Cat)
8 from nmigen.cli import rtlil
9 from c4m.nmigen.jtag.tap import IOType, TAP
10
11 # map from pinmux to c4m jtag iotypes
12 iotypes = {'-': IOType.In,
13 '+': IOType.Out,
14 '>': IOType.TriOut,
15 '*': IOType.InTriOut,
16 }
17 # Resources
18 # nmigen Resources has a different encoding for direction: "i", "o", "io", "oe"
19 resiotypes = {'i': IOType.In,
20 'o': IOType.Out,
21 'oe': IOType.TriOut,
22 'io': IOType.InTriOut,
23 }
24 # How many bits in each signal type
25 scanlens = {IOType.In: 1,
26 IOType.Out: 1,
27 IOType.TriOut: 2,
28 IOType.InTriOut: 3,
29 }
30
31 def dummy_pinset():
32 # sigh this needs to come from pinmux.
33 gpios = []
34 for i in range(16):
35 gpios.append("%d*" % i)
36 return {'uart': ['tx+', 'rx-'],
37 'gpio': gpios,
38 'i2c': ['sda*', 'scl+']}
39
40
41 # TODO: move to suitable location
42 class Pins:
43 """declare a list of pins, including name and direction. grouped by fn
44 the pin dictionary needs to be in a reliable order so that the JTAG
45 Boundary Scan is also in a reliable order
46 """
47 def __init__(self, pindict=None):
48 if pindict is None:
49 pindict = {}
50 self.io_names = OrderedDict()
51 if isinstance(pindict, OrderedDict):
52 self.io_names.update(pindict)
53 else:
54 keys = list(pindict.keys())
55 keys.sort()
56 for k in keys:
57 self.io_names[k] = pindict[k]
58
59 def __iter__(self):
60 # start parsing io_names and enumerate them to return pin specs
61 scan_idx = 0
62 for fn, pins in self.io_names.items():
63 for pin in pins:
64 # decode the pin name and determine the c4m jtag io type
65 name, pin_type = pin[:-1], pin[-1]
66 iotype = iotypes[pin_type]
67 pin_name = "%s_%s" % (fn, name)
68 yield (fn, name, iotype, pin_name, scan_idx)
69 scan_idx += scanlens[iotype] # inc boundary reg scan offset
70
71
72 class JTAG(TAP, Pins):
73 # 32-bit data width here so that it matches with litex
74 def __init__(self, pinset, domain, wb_data_wid=32):
75 self.domain = domain
76 TAP.__init__(self, ir_width=4)
77 Pins.__init__(self, pinset)
78
79 # enumerate pin specs and create IOConn Records.
80 # we store the boundary scan register offset in the IOConn record
81 self.ios = {} # these are enumerated in external_ports
82 self.scan_len = 0
83 self.add_pins(list(self))
84
85 # this is redundant. or maybe part of testing, i don't know.
86 self.sr = self.add_shiftreg(ircode=4, length=3,
87 domain=domain)
88
89 # create and connect wishbone
90 self.wb = self.add_wishbone(ircodes=[5, 6, 7], features={'err'},
91 address_width=30, data_width=wb_data_wid,
92 granularity=8, # 8-bit wide
93 name="jtag_wb",
94 domain=domain)
95
96 # create DMI2JTAG (goes through to dmi_sim())
97 self.dmi = self.add_dmi(ircodes=[8, 9, 10],
98 domain=domain)
99
100 # use this for enable/disable of parts of the ASIC.
101 # XXX make sure to add the _en sig to en_sigs list
102 self.wb_icache_en = Signal(reset=1)
103 self.wb_dcache_en = Signal(reset=1)
104 self.wb_sram_en = Signal(reset=1)
105 self.en_sigs = en_sigs = Cat(self.wb_icache_en, self.wb_dcache_en,
106 self.wb_sram_en)
107 self.sr_en = self.add_shiftreg(ircode=11, length=len(en_sigs),
108 domain=domain)
109
110 def add_pins(self, pinlist):
111 for fn, pin, iotype, pin_name, scan_idx in pinlist:
112 io = self.add_io(iotype=iotype, name=pin_name)
113 io._scan_idx = scan_idx # hmm shouldn't really do this
114 self.scan_len += scan_idx # record full length of boundary scan
115 self.ios[pin_name] = io
116
117 def elaborate(self, platform):
118 m = super().elaborate(platform)
119 m.d.comb += self.sr.i.eq(self.sr.o) # loopback as part of test?
120
121 # provide way to enable/disable wishbone caches and SRAM
122 # just in case of issues
123 # see https://bugs.libre-soc.org/show_bug.cgi?id=520
124 with m.If(self.sr_en.oe):
125 m.d.sync += self.en_sigs.eq(self.sr_en.o)
126 # also make it possible to read the enable/disable current state
127 with m.If(self.sr_en.ie):
128 m.d.comb += self.sr_en.i.eq(self.en_sigs)
129
130 # create a fake "stall"
131 #wb = self.wb
132 #m.d.comb += wb.stall.eq(wb.cyc & ~wb.ack) # No burst support
133
134 return m
135
136 def external_ports(self):
137 """create a list of ports that goes into the top level il (or verilog)
138 """
139 ports = super().external_ports() # gets JTAG signal names
140 ports += list(self.wb.fields.values()) # wishbone signals
141 for io in self.ios.values():
142 ports += list(io.core.fields.values()) # io "core" signals
143 ports += list(io.pad.fields.values()) # io "pad" signals"
144 return ports
145
146
147 if __name__ == '__main__':
148 pinset = dummy_pinset()
149 dut = JTAG(pinset, "sync")
150
151 vl = rtlil.convert(dut)
152 with open("test_jtag.il", "w") as f:
153 f.write(vl)
154