You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
3.3 KiB
Python

from operator import attrgetter
from amaranth import *
from amaranth.asserts import AnyConst
from amaranth.hdl.ast import ValueCastable, Value
__all__ = ["InsnField", "WordInsn"]
class InsnField:
def __init__(self, value=None):
self.value = value
@property
def shape(self):
return self._shape
@property
def offset(self):
return self._offset
@property
def name(self):
return self._name
@property
def value(self):
return self._value
@value.setter
def value(self, value):
if value is not None and not isinstance(value, int):
raise TypeError("Field value must be an integer, not {!r}"
.format(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(ValueCastable):
SIZE = 32
def __init__(self):
value_map = {}
field_map = {}
curr_offset = 0
for field in sorted(self.fields, key=attrgetter('offset')):
if not isinstance(field, InsnField):
raise TypeError("Field must be an instance of InsnField, not {!r}"
.format(field))
if field.name in field_map:
raise ValueError("Duplicate field name '{}'".format(field.name))
if curr_offset > field.offset:
raise ValueError("Field '{}' at offset {} overlaps with its predecessor"
.format(field.name, field.offset))
# Add undefined bits located between this field and its predecessor.
if curr_offset < field.offset:
undef_bits = AnyConst(unsigned(field.offset - curr_offset))
value_map[curr_offset] = undef_bits
value_map[field.offset] = field.as_const()
field_map[field.name ] = field
curr_offset = field.offset + field.shape.width
if curr_offset > self.SIZE:
raise ValueError
# Add undefined bits located after the last field.
if curr_offset < self.SIZE:
undef_bits = AnyConst(unsigned(self.SIZE - curr_offset))
value_map[curr_offset] = undef_bits
self.field_map = field_map
self.value_map = value_map
@property
def fields(self):
return self._fields
def __getattr__(self, name):
return self[name]
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