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
112 lines
3.3 KiB
Python
2 years ago
|
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
|