fix(iomux): Fix port signal length (given mux size non-power of 2)
[pinmux.git] / src / bsv / actual_pinmux.py
1 import math
2 from string import digits
3 try:
4 from string import maketrans
5 except ImportError:
6 maketrans = str.maketrans
7
8
9 # ============== common bsv templates ============ #
10 # first argument is the io-cell number being assigned.
11 # second argument is the mux value.
12 # Third argument is the signal from the pinmap file
13 mux_wire = '''\
14 rule assign_{2}_on_cell{0}(wrcell{0}_mux=={1});
15 {2}<=cell{0}_mux_in;
16 endrule
17 '''
18 dedicated_wire = '''
19 rule assign_{1}_on_cell{0};
20 {1}<=cell{0}_mux_in;
21 endrule
22 '''
23 # ============================================================
24 digits = maketrans('0123456789', ' ' * 10) # delete space later
25
26
27 def cn(idx): # idx is an integer
28 return "cell%s_mux" % str(idx)
29
30
31 def transfn(temp):
32 """ removes the number from the string of signal name.
33 """
34 temp = temp.split('_')
35 if len(temp) == 2:
36 temp[0] = temp[0].translate(digits)
37 temp[0] = temp[0] .replace(' ', '')
38 return '_'.join(temp)
39
40
41 # XXX this needs to move into interface_decl.py
42 # and made to use ifaceoutfmtfn and ifaceinfmtfn
43 def fmt(ifaces, cells, idx, suffix=None):
44 """ blank entries need to output a 0 to the pin (it could just as
45 well be a 1 but we choose 0). reason: blank entries in
46 the pinmap.txt file indicate that there's nothing to choose
47 from. however the user may still set the muxer to that value,
48 and rather than throw an exception we choose to output... zero.
49
50 NOTE: IMPORTANT. when a function is an output-only there
51 is a special-case assumption that:
52 * (a) GPIO is always the first mux entry
53 * (b) GPIO's outen is also used to set the pad
54 the reason for this is that it is assumed better that
55 multiple pads be switched simutaneously to outputs
56 by setting the GPIO direction rather than having them
57 set arbitrarily by changing the muxer registers.
58
59 The exception to this rule is when the muxer width is 1
60 (i.e. it is a dedicated line). Then, a "1" is outputted
61 and the pin is PERMANENTly enabled as an output.
62 """
63 idx += 1
64 if idx < len(cells):
65 cell = cells[idx]
66 else:
67 cell = ''
68 #print "fmt", idx, cells, cell
69 if not cell:
70 return 'val0'
71 temp = transfn(cell)
72 x = ifaces.getifacetype(temp)
73 if x == 'input':
74 return 'val0' # inputs don't get passed through to the out mux
75 if suffix == '_outen' and x == 'out':
76 if len(cells) == 2:
77 return "val1"
78 return "wr%s%s" % (cells[1], suffix or '') # USE GPIO FOR SELECTION
79 if x == 'out': # sigh hack, should be using interface_decl
80 suffix = ''
81 return "wr%s%s" % (cell, suffix or '')
82
83 # XXX this needs to move into interface_decl.py
84
85
86 def mkcomment(ifaces, cell, idx, outenmode=False):
87 """ returns a comment string for the cell when muxed
88 """
89 idx += 1
90 if idx >= len(cell):
91 return ' // unused'
92 cname = cell[idx]
93 if not cname:
94 return ' // unused'
95 temp = transfn(cname)
96 x = ifaces.getifacetype(temp)
97 #print (cname, x)
98 if x == 'input':
99 return ' // %s is an input' % cname
100 if outenmode and x == 'inout':
101 return ' // bi-directional'
102 if outenmode and x == 'out':
103 return ' // %s is an output' % cname
104
105 return ""
106
107
108 def mkmux(p, ifaces, cell, suffix, outenmode):
109 """ creates a straight many-to-one muxer that accepts
110 multiple inputs and, based on an "address" routes
111 a given indexed input through to the (one) output
112 """
113 cellnum = cell[0]
114 comment = 'outen' if outenmode else 'output'
115 fmtstr = "\t\t\twr%s==%d?%s:%s\n" # mux-selector format
116 ret = ''
117 ret += " // %s muxer for cell idx %s\n" % (comment, cellnum)
118 ret += " %s%s=\n" % (cn(cellnum), suffix)
119 i = 0
120 mwid = p.get_muxwidth(cellnum)
121 if mwid > 1:
122 for i in range(0, mwid - 1): # full mux range (minus 1)
123 comment = mkcomment(ifaces, cell, i, outenmode)
124 cf = fmt(ifaces, cell, i, suffix)
125 ret += fmtstr % (cn(cell[0]), i, cf, comment)
126 i += 1
127 comment = mkcomment(ifaces, cell, i, outenmode)
128 ret += "\t\t\t" + fmt(ifaces, cell, i, suffix) # last line
129 ret += ";%s\n" % comment
130
131 return ret
132
133
134 def init(p, ifaces):
135 """ generates the actual output pinmux for each io-cell. blank lines
136 need to output "0" to the iopad, if there is no entry in
137 that column.
138
139 text is outputted in the format:
140
141 x_out =
142 muxer_sel==0 ? a :
143 muxer_sel==1 ? b :
144 muxer_sel==2 ? 0 :
145 d
146
147 last line doesn't need selector-logic, obviously.
148
149 note that it's *important* that all muxer options be covered
150 (hence going up to 1<<cell_bitwidth) even if the muxer cells
151 are blank (no entries), because muxer selection could be to
152 the last one, and we do not want the "default" (last line)
153 to be the output.
154 """
155 p.pinmux = ' '
156 global dedicated_wire
157 for cell in p.muxed_cells:
158
159 cellidx = cell[0]
160
161 p.pinmux += " // --------------------\n"
162 p.pinmux += " // ----- cell %s -----\n\n" % (cellidx)
163
164 # first do the outputs
165 p.pinmux += mkmux(p, ifaces, cell, '_out', False)
166 p.pinmux += "\n"
167
168 # now do the output enablers (outens)
169 p.pinmux += mkmux(p, ifaces, cell, '_outen', True)
170
171 # ======================================================== #
172
173 # check each cell if "peripheral input/inout" then assign its wire
174 # Here we check the direction of each signal in the dictionary.
175 # We choose to keep the dictionary within the code and not user-input
176 # since the interfaces are always standard and cannot change from
177 # user-to-user. Plus this also reduces human-error as well :)
178 p.pinmux += "\n"
179 p.pinmux += " // priority-in-muxer for cell idx %s\n" % (cellidx)
180 for i in range(0, len(cell) - 1):
181 cname = cell[i + 1]
182 if not cname: # skip blank entries, no need to test
183 continue
184 temp = transfn(cname)
185 x = ifaces.getifacetype(temp)
186 print (cname, temp, x)
187 assert x is not None, "ERROR: The signal : " + \
188 str(cname) + \
189 " of pinmap.txt isn't present \nin the current" + \
190 " dictionary. Update dictionary or fix-typo."
191 bwid = p.get_muxbitwidth(cellidx)
192 if bwid > 0:
193 muxcell(p, cname, x, cell, i)
194 else:
195 dedcell(p, x, cell)
196
197 # ================== Logic for dedicated pins ========= #
198 p.pinmux += "\n /*=========================================*/\n"
199 p.pinmux += " // dedicated cells\n\n"
200 for cell in p.dedicated_cells:
201 p.pinmux += " // dedicated cell idx %s\n" % (cell[0])
202 p.pinmux += " %s_out=%s_io;\n" % (cn(cell[0]), cell[1])
203 temp = transfn(cell[1])
204 x = ifaces.getifacetype(temp)
205 #print cell, temp, x
206 dedcell(p, x, cell)
207
208
209 def muxcell(p, cname, x, cell, i):
210 if x == "input":
211 p.pinmux += \
212 mux_wire.format(cell[0], i, "wr" + cname) + "\n"
213 elif x == "inout":
214 p.pinmux += \
215 mux_wire.format(cell[0], i, "wr" + cname +
216 "_in") + "\n"
217
218
219 def dedcell(p, x, cell):
220 if x == "input":
221 p.pinmux += \
222 dedicated_wire.format(cell[0], "wr" + cell[1]) + "\n"
223 elif x == "inout":
224 p.pinmux += \
225 dedicated_wire.format(cell[0], "wr" + cell[1] + "_in") + "\n"