import migen in litex/gen
[litex.git] / litex / gen / migen / genlib / record.py
1 from migen.fhdl.structure import *
2 from migen.fhdl.tracer import get_obj_var_name
3
4 from functools import reduce
5 from operator import or_
6
7
8 (DIR_NONE, DIR_S_TO_M, DIR_M_TO_S) = range(3)
9
10 # Possible layout elements:
11 # 1. (name, size)
12 # 2. (name, size, direction)
13 # 3. (name, sublayout)
14 # size can be an int, or a (int, bool) tuple for signed numbers
15 # sublayout must be a list
16
17
18 def set_layout_parameters(layout, **layout_dict):
19 def resolve(p):
20 if isinstance(p, str):
21 try:
22 return layout_dict[p]
23 except KeyError:
24 return p
25 else:
26 return p
27
28 r = []
29 for f in layout:
30 if isinstance(f[1], (int, tuple, str)): # cases 1/2
31 if len(f) == 3:
32 r.append((f[0], resolve(f[1]), f[2]))
33 else:
34 r.append((f[0], resolve(f[1])))
35 elif isinstance(f[1], list): # case 3
36 r.append((f[0], set_layout_parameters(f[1], **layout_dict)))
37 else:
38 raise TypeError
39 return r
40
41
42 def layout_len(layout):
43 r = 0
44 for f in layout:
45 if isinstance(f[1], (int, tuple)): # cases 1/2
46 if len(f) == 3:
47 fname, fsize, fdirection = f
48 else:
49 fname, fsize = f
50 elif isinstance(f[1], list): # case 3
51 fname, fsublayout = f
52 fsize = layout_len(fsublayout)
53 else:
54 raise TypeError
55 if isinstance(fsize, tuple):
56 r += fsize[0]
57 else:
58 r += fsize
59 return r
60
61
62 def layout_get(layout, name):
63 for f in layout:
64 if f[0] == name:
65 return f
66 raise KeyError(name)
67
68
69 def layout_partial(layout, *elements):
70 r = []
71 for path in elements:
72 path_s = path.split("/")
73 last = path_s.pop()
74 copy_ref = layout
75 insert_ref = r
76 for hop in path_s:
77 name, copy_ref = layout_get(copy_ref, hop)
78 try:
79 name, insert_ref = layout_get(insert_ref, hop)
80 except KeyError:
81 new_insert_ref = []
82 insert_ref.append((hop, new_insert_ref))
83 insert_ref = new_insert_ref
84 insert_ref.append(layout_get(copy_ref, last))
85 return r
86
87
88 class Record:
89 def __init__(self, layout, name=None):
90 self.name = get_obj_var_name(name, "")
91 self.layout = layout
92
93 if self.name:
94 prefix = self.name + "_"
95 else:
96 prefix = ""
97 for f in self.layout:
98 if isinstance(f[1], (int, tuple)): # cases 1/2
99 if(len(f) == 3):
100 fname, fsize, fdirection = f
101 else:
102 fname, fsize = f
103 finst = Signal(fsize, name=prefix + fname)
104 elif isinstance(f[1], list): # case 3
105 fname, fsublayout = f
106 finst = Record(fsublayout, prefix + fname)
107 else:
108 raise TypeError
109 setattr(self, fname, finst)
110
111 def eq(self, other):
112 return [getattr(self, f[0]).eq(getattr(other, f[0]))
113 for f in self.layout if hasattr(other, f[0])]
114
115 def iter_flat(self):
116 for f in self.layout:
117 e = getattr(self, f[0])
118 if isinstance(e, Signal):
119 if len(f) == 3:
120 yield e, f[2]
121 else:
122 yield e, DIR_NONE
123 elif isinstance(e, Record):
124 yield from e.iter_flat()
125 else:
126 raise TypeError
127
128 def flatten(self):
129 return [signal for signal, direction in self.iter_flat()]
130
131 def raw_bits(self):
132 return Cat(*self.flatten())
133
134 def connect(self, *slaves, leave_out=set()):
135 if isinstance(leave_out, str):
136 leave_out = {leave_out}
137 r = []
138 for f in self.layout:
139 field = f[0]
140 if field not in leave_out:
141 self_e = getattr(self, field)
142 if isinstance(self_e, Signal):
143 direction = f[2]
144 if direction == DIR_M_TO_S:
145 r += [getattr(slave, field).eq(self_e) for slave in slaves]
146 elif direction == DIR_S_TO_M:
147 r.append(self_e.eq(reduce(or_, [getattr(slave, field) for slave in slaves])))
148 else:
149 raise TypeError
150 else:
151 for slave in slaves:
152 r += self_e.connect(getattr(slave, field), leave_out=leave_out)
153 return r
154
155 def connect_flat(self, *slaves):
156 r = []
157 iter_slaves = [slave.iter_flat() for slave in slaves]
158 for m_signal, m_direction in self.iter_flat():
159 if m_direction == DIR_M_TO_S:
160 for iter_slave in iter_slaves:
161 s_signal, s_direction = next(iter_slave)
162 assert(s_direction == DIR_M_TO_S)
163 r.append(s_signal.eq(m_signal))
164 elif m_direction == DIR_S_TO_M:
165 s_signals = []
166 for iter_slave in iter_slaves:
167 s_signal, s_direction = next(iter_slave)
168 assert(s_direction == DIR_S_TO_M)
169 s_signals.append(s_signal)
170 r.append(m_signal.eq(reduce(or_, s_signals)))
171 else:
172 raise TypeError
173 return r
174
175 def __len__(self):
176 return layout_len(self.layout)
177
178 def __repr__(self):
179 return "<Record " + ":".join(f[0] for f in self.layout) + " at " + hex(id(self)) + ">"