mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
New bidirectional-capable Record API
This commit is contained in:
parent
c4f4143591
commit
6a3c413717
3 changed files with 132 additions and 119 deletions
24
examples/basic/record.py
Normal file
24
examples/basic/record.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from migen.fhdl.structure import *
|
||||||
|
from migen.fhdl.module import Module
|
||||||
|
from migen.fhdl import verilog
|
||||||
|
from migen.genlib.record import *
|
||||||
|
|
||||||
|
L = [
|
||||||
|
("position", [
|
||||||
|
("x", 10, DIR_M_TO_S),
|
||||||
|
("y", 10, DIR_M_TO_S),
|
||||||
|
]),
|
||||||
|
("color", 32, DIR_M_TO_S),
|
||||||
|
("stb", 1, DIR_M_TO_S),
|
||||||
|
("ack", 1, DIR_S_TO_M)
|
||||||
|
]
|
||||||
|
|
||||||
|
class Test(Module):
|
||||||
|
def __init__(self):
|
||||||
|
master = Record(L)
|
||||||
|
slave = Record(L)
|
||||||
|
self.comb += master.connect(slave)
|
||||||
|
|
||||||
|
print(verilog.convert(Test()))
|
||||||
|
print(layout_len(L))
|
||||||
|
print(layout_partial(L, "position/x", "color"))
|
|
@ -1,20 +0,0 @@
|
||||||
from migen.fhdl.structure import *
|
|
||||||
from migen.genlib.record import *
|
|
||||||
|
|
||||||
L = [
|
|
||||||
("x", 10, 8),
|
|
||||||
("y", 10, 8),
|
|
||||||
("level2", [
|
|
||||||
("a", 5, 32),
|
|
||||||
("b", 5, 16)
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
myrec = Record(L)
|
|
||||||
print(myrec.flatten())
|
|
||||||
print(myrec.flatten(True))
|
|
||||||
s = myrec.subrecord("level2/a", "x")
|
|
||||||
print(s.flatten())
|
|
||||||
print(s.level2.layout())
|
|
||||||
myrec2 = myrec.copy()
|
|
||||||
print(myrec2.flatten())
|
|
|
@ -1,112 +1,121 @@
|
||||||
from migen.fhdl.structure import *
|
from migen.fhdl.structure import *
|
||||||
from migen.fhdl.tools import value_bits_sign
|
from migen.fhdl.tracer import get_obj_var_name
|
||||||
|
from migen.genlib.misc import optree
|
||||||
|
|
||||||
|
(DIR_NONE, DIR_S_TO_M, DIR_M_TO_S) = range(3)
|
||||||
|
|
||||||
|
# Possible layout elements:
|
||||||
|
# 1. (name, size)
|
||||||
|
# 2. (name, size, direction)
|
||||||
|
# 3. (name, sublayout)
|
||||||
|
# size can be an int, or a (int, bool) tuple for signed numbers
|
||||||
|
# sublayout must be a list
|
||||||
|
|
||||||
|
def layout_len(layout):
|
||||||
|
r = 0
|
||||||
|
for f in layout:
|
||||||
|
if isinstance(f[1], (int, tuple)): # cases 1/2
|
||||||
|
if(len(f) == 3):
|
||||||
|
fname, fsize, fdirection = f
|
||||||
|
else:
|
||||||
|
fname, fsize = f
|
||||||
|
elif isinstance(f[1], list): # case 3
|
||||||
|
fname, fsublayout = f
|
||||||
|
fsize = layout_len(fsublayout)
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
if isinstance(fsize, tuple):
|
||||||
|
r += fsize[0]
|
||||||
|
else:
|
||||||
|
r += fsize
|
||||||
|
return r
|
||||||
|
|
||||||
|
def layout_get(layout, name):
|
||||||
|
for f in layout:
|
||||||
|
if f[0] == name:
|
||||||
|
return f
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
|
def layout_partial(layout, *elements):
|
||||||
|
r = []
|
||||||
|
for path in elements:
|
||||||
|
path_s = path.split("/")
|
||||||
|
last = path_s.pop()
|
||||||
|
copy_ref = layout
|
||||||
|
insert_ref = r
|
||||||
|
for hop in path_s:
|
||||||
|
name, copy_ref = layout_get(copy_ref, hop)
|
||||||
|
try:
|
||||||
|
name, insert_ref = layout_get(insert_ref, hop)
|
||||||
|
except KeyError:
|
||||||
|
new_insert_ref = []
|
||||||
|
insert_ref.append((hop, new_insert_ref))
|
||||||
|
insert_ref = new_insert_ref
|
||||||
|
insert_ref.append(layout_get(copy_ref, last))
|
||||||
|
return r
|
||||||
|
|
||||||
class Record:
|
class Record:
|
||||||
def __init__(self, layout, name=""):
|
def __init__(self, layout, name=None):
|
||||||
self.name = name
|
self.name = get_obj_var_name(name, "")
|
||||||
self.field_order = []
|
self.layout = layout
|
||||||
|
|
||||||
if self.name:
|
if self.name:
|
||||||
prefix = self.name + "_"
|
prefix = self.name + "_"
|
||||||
else:
|
else:
|
||||||
prefix = ""
|
prefix = ""
|
||||||
for f in layout:
|
for f in self.layout:
|
||||||
if isinstance(f, tuple):
|
if isinstance(f[1], (int, tuple)): # cases 1/2
|
||||||
if isinstance(f[1], (int, tuple)):
|
if(len(f) == 3):
|
||||||
setattr(self, f[0], Signal(f[1], prefix + f[0]))
|
fname, fsize, fdirection = f
|
||||||
elif isinstance(f[1], Signal) or isinstance(f[1], Record):
|
|
||||||
setattr(self, f[0], f[1])
|
|
||||||
elif isinstance(f[1], list):
|
|
||||||
setattr(self, f[0], Record(f[1], prefix + f[0]))
|
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
fname, fsize = f
|
||||||
if len(f) == 3:
|
finst = Signal(fsize, name=prefix + fname)
|
||||||
self.field_order.append((f[0], f[2]))
|
elif isinstance(f[1], list): # case 3
|
||||||
else:
|
fname, fsublayout = f
|
||||||
self.field_order.append((f[0], 1))
|
finst = Record(fsublayout, prefix + fname)
|
||||||
else:
|
|
||||||
setattr(self, f, Signal(1, prefix + f))
|
|
||||||
self.field_order.append((f, 1))
|
|
||||||
|
|
||||||
def eq(self, other):
|
|
||||||
return [getattr(self, key).eq(getattr(other, key))
|
|
||||||
for key, a in self.field_order]
|
|
||||||
|
|
||||||
def layout(self):
|
|
||||||
l = []
|
|
||||||
for key, alignment in self.field_order:
|
|
||||||
e = getattr(self, key)
|
|
||||||
if isinstance(e, Signal):
|
|
||||||
l.append((key, (e.nbits, e.signed), alignment))
|
|
||||||
elif isinstance(e, Record):
|
|
||||||
l.append((key, e.layout(), alignment))
|
|
||||||
return l
|
|
||||||
|
|
||||||
def copy(self, name=None):
|
|
||||||
return Record(self.layout(), name)
|
|
||||||
|
|
||||||
def get_alignment(self, name):
|
|
||||||
return list(filter(lambda x: x[0] == name, self.field_order))[0][1]
|
|
||||||
|
|
||||||
def subrecord(self, *descr):
|
|
||||||
fields = []
|
|
||||||
for item in descr:
|
|
||||||
path = item.split("/")
|
|
||||||
last = path.pop()
|
|
||||||
pos_self = self
|
|
||||||
pos_fields = fields
|
|
||||||
for hop in path:
|
|
||||||
pos_self = getattr(pos_self, hop)
|
|
||||||
lu = list(filter(lambda x: x[0] == hop, pos_fields))
|
|
||||||
try:
|
|
||||||
pos_fields = lu[0][1]
|
|
||||||
except IndexError:
|
|
||||||
n = []
|
|
||||||
pos_fields.append((hop, n))
|
|
||||||
pos_fields = n
|
|
||||||
if not isinstance(pos_fields, list):
|
|
||||||
raise ValueError
|
|
||||||
if len(list(filter(lambda x: x[0] == last, pos_fields))) > 0:
|
|
||||||
raise ValueError
|
|
||||||
pos_fields.append((last, getattr(pos_self, last), pos_self.get_alignment(last)))
|
|
||||||
return Record(fields, "subrecord")
|
|
||||||
|
|
||||||
def compatible(self, other):
|
|
||||||
tpl1 = self.flatten()
|
|
||||||
tpl2 = other.flatten()
|
|
||||||
return len(tpl1) == len(tpl2)
|
|
||||||
|
|
||||||
def flatten(self, align=False, offset=0, return_offset=False):
|
|
||||||
l = []
|
|
||||||
for key, alignment in self.field_order:
|
|
||||||
if align:
|
|
||||||
pad_size = alignment - (offset % alignment)
|
|
||||||
if pad_size < alignment:
|
|
||||||
l.append(Replicate(0, pad_size))
|
|
||||||
offset += pad_size
|
|
||||||
|
|
||||||
e = getattr(self, key)
|
|
||||||
if isinstance(e, Signal):
|
|
||||||
added = [e]
|
|
||||||
elif isinstance(e, Record):
|
|
||||||
added = e.flatten(align, offset)
|
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
for x in added:
|
setattr(self, fname, finst)
|
||||||
offset += value_bits_sign(x)[0]
|
|
||||||
l += added
|
def eq(self, other):
|
||||||
if return_offset:
|
return [getattr(self, f[0]).eq(getattr(other, f[0]))
|
||||||
return (l, offset)
|
for f in self.layout if hasattr(other, f[0])]
|
||||||
else:
|
|
||||||
return l
|
|
||||||
|
|
||||||
def to_signal(self, assignment_list, sig_out, align=False):
|
def flatten(self):
|
||||||
flattened, length = self.flatten(align, return_offset=True)
|
r = []
|
||||||
raw = Signal(length)
|
for f in self.layout:
|
||||||
if sig_out:
|
e = getattr(self, f[0])
|
||||||
assignment_list.append(raw.eq(Cat(*flattened)))
|
if isinstance(e, Signal):
|
||||||
else:
|
r.append(e)
|
||||||
assignment_list.append(Cat(*flattened).eq(raw))
|
elif isinstance(e, Record):
|
||||||
return raw
|
r += e.flatten()
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
return r
|
||||||
|
|
||||||
|
def raw_bits(self):
|
||||||
|
return Cat(*self.flatten())
|
||||||
|
|
||||||
|
def connect(self, *slaves):
|
||||||
|
r = []
|
||||||
|
for f in self.layout:
|
||||||
|
field = f[0]
|
||||||
|
self_e = getattr(self, field)
|
||||||
|
if isinstance(self_e, Signal):
|
||||||
|
direction = f[2]
|
||||||
|
if direction == DIR_M_TO_S:
|
||||||
|
r += [getattr(slave, field).eq(self_e) for slave in slaves]
|
||||||
|
elif direction == DIR_S_TO_M:
|
||||||
|
r.append(self_e.eq(optree("|", [getattr(slave, field) for slave in slaves])))
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
else:
|
||||||
|
for slave in slaves:
|
||||||
|
r += self_e.connect(getattr(slave, field))
|
||||||
|
return r
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return layout_len(self.layout)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Record " + repr(self.layout()) + ">"
|
return "<Record " + ":".join(f[0] for f in self.layout) + " at " + hex(id(self)) + ">"
|
||||||
|
|
Loading…
Reference in a new issue