@ -1,8 +1,6 @@
from operator import attrgetter
from operator import attrgetter
from amaranth import *
from amaranth import *
from amaranth.asserts import AnyConst
from amaranth.hdl.ast import ValueCastable, Value
__all__ = ["InsnField", "WordInsn"]
__all__ = ["InsnField", "WordInsn"]
@ -35,38 +33,34 @@ class InsnField:
.format(value))
.format(value))
self._value = value
self._value = value
def as_const(self):
if self.value is not None:
return Const(self.value, self.shape)
else:
return AnyConst(self.shape)
class WordInsn(Record):
class WordInsn(ValueCastable):
SIZE = 32
SIZE = 32
def __init__(self):
def __init__(self, name=None, src_loc_at=0):
value_map = {}
field_map = {}
curr_offset = 0
curr_offset = 0
layout = []
pattern = ""
for field in sorted(self.fields, key=attrgetter('offset')):
for field in sorted(self._fields, key=attrgetter("offset")):
if not isinstance(field, InsnField):
if not isinstance(field, InsnField):
raise TypeError("Field must be an instance of InsnField, not {!r}"
raise TypeError("Field must be an instance of InsnField, not {!r}"
.format(field))
.format(field))
if field.name in field_map:
raise ValueError("Duplicate field name '{}'".format(field.name))
if curr_offset > field.offset:
if curr_offset > field.offset:
raise ValueError("Field '{}' at offset {} overlaps with its predecessor"
raise ValueError("Field '{}' at offset {} overlaps with its predecessor"
.format(field.name, field.offset))
.format(field.name, field.offset))
# Add undefined bits located between this field and its predecessor.
# Add undefined bits located between this field and its predecessor.
if curr_offset < field.offset:
if curr_offset < field.offset:
undef_bits = AnyConst(unsigned(field.offset - curr_offset))
undef_bits = field.offset - curr_offset
value_map[curr_offset] = undef_bits
layout.append((f"_{curr_offset}", unsigned(undef_bits)))
pattern += "-" * undef_bits
value_map[field.offset] = field.as_const()
layout.append((field.name, field.shape))
field_map[field.name ] = field
if field.value is not None:
pattern += "{:0{}b}".format(field.value, field.shape.width)
else:
pattern += "-" * field.shape.width
curr_offset = field.offset + field.shape.width
curr_offset = field.offset + field.shape.width
@ -75,37 +69,16 @@ class WordInsn(ValueCastable):
# Add undefined bits located after the last field.
# Add undefined bits located after the last field.
if curr_offset < self.SIZE:
if curr_offset < self.SIZE:
undef_bits = AnyConst(unsigned(self.SIZE - curr_offset))
undef_bits = self.SIZE - curr_offset
value_map[curr_offset] = undef_bits
layout.append((f"_{curr_offset}", unsigned(undef_bits)))
pattern += "-" * undef_bits
self.field_map = field_map
self._pattern = pattern
self.value_map = value_map
super().__init__(reversed(layout), name=name, src_loc_at=1 + src_loc_at)
@property
@property
def fields(self):
def pattern(self):
return self._fields
return self._pattern
def __getattr__(self, name):
def is_valid(self):
return self[name]
return self.as_value().matches(self.pattern)
def __getitem__(self, item):
if isinstance(item, str):
try:
field = self.field_map[item]
except KeyError:
raise AttributeError("WordInsn {!r} does not have a field '{}'"
.format(self, item))
value = self.value_map[field.offset]
return value
else:
try:
return Value.__getitem__(self, item)
except KeyError:
raise AttributeError("WordInsn {!r} does not have a field '{}'"
.format(self, item))
@ValueCastable.lowermethod
def as_value(self):
value = Cat(v for o, v in sorted(self.value_map.items(), reverse=True))
assert len(value) == self.SIZE
return value