diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-07-06 10:03:57 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-07-06 10:04:36 +0000 |
| commit | 912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2 (patch) | |
| tree | 3c62b2a7db782bd6c142a2fdb37d232f31f16eb1 /bits.py | |
| parent | update ex.subv to work with new pipeline (diff) | |
| download | subv-912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2.tar.gz subv-912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2.zip | |
fix and finish new pipeline
Diffstat (limited to 'bits.py')
| -rw-r--r-- | bits.py | 321 |
1 files changed, 17 insertions, 304 deletions
@@ -1,102 +1,4 @@ -import re -from operator import __and__ -from functools import reduce - - -class WordBase(object): - def slice_allowempty(self, range): - if not isinstance(range, slice): - range = slice(range, range) - hi, lo = range.start, range.stop - - if hi < lo: - return empty - - return self[range] - - def __and__(self, other): - if isinstance(other, WordBase): - return BitsUnion(self, other) - raise NotImplementedError() - - -class BitsUnion(WordBase): - def __init__(self, *parts): - self.parts = parts - - def __repr__(self): - return "BitsUnion({})".format(", ".join(repr(part) for part in self.parts)) - - def as_value(self, **kwargs): - """reduce down to a single Bitfield value. - - >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value() - Bitfield(0x1234, 16) - >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).as_value(labels={'test': 0xf34f}) - Bitfield(0x1234, 16) - """ - return reduce(__and__, (part.as_value(**kwargs) for part in self.parts)) - - @property - def size(self): - """get the total size. - - >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size - 16 - >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).size - 16 - """ - return sum(part.size for part in self.parts) - - def __getitem__(self, range): - """slice a union given hi and lo bit indices. - - >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4] - Bitfield(0x23, 8) - >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4)))[11:4] - BitsUnion(Bitfield(0x2, 4), LabelRef('test', 0, ('imm', 32), 11:8)) - """ - if not isinstance(range, slice): - range = slice(range, range) - hi, lo = range.start, range.stop - - size = self.size - if hi < lo: - raise ValueError("cant slice reverse range") - elif lo < 0 or hi >= size: - raise ValueError( - "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) - ) - - i = size - result = Bitfield(0, 0) - for part in self.parts: - p_size = part.size - i -= p_size - if i > hi: - # still above hi - continue - - r_hi = min(p_size - 1, hi - i) - r_lo = max(0, lo - i) - - result = result & part[r_hi:r_lo] - - if i <= lo: - # past lo - break - - return result - - def __and__(self, other): - if isinstance(other, BitsUnion): - return BitsUnion(*self.parts, *other.parts) - elif isinstance(other, WordBase): - return BitsUnion(*self.parts, other) - raise NotImplementedError() - - -class Bitfield(WordBase): +class Bitfield(object): def __init__(self, val, size, extra=()): if val < 0: min = -(1 << (size - 1)) @@ -152,9 +54,6 @@ class Bitfield(WordBase): base = (digits - len(base)) * "0" + base return "/".join([base, *self.extra, str(self.size)]) - def as_value(self, **kwargs): - return self - def __getitem__(self, range): """slice a bitfield given hi and lo bit indices. @@ -197,6 +96,16 @@ class Bitfield(WordBase): val = (self.val >> lo) & ((1 << size) - 1) return Bitfield(val, size) + def slice_allowempty(self, range): + if not isinstance(range, slice): + range = slice(range, range) + hi, lo = range.start, range.stop + + if hi < lo: + return empty + + return self[range] + def __and__(self, other): """concatenate multiple bitfields. @@ -207,152 +116,11 @@ class Bitfield(WordBase): """ if isinstance(other, Bitfield): return Bitfield(self.val << other.size | other.val, self.size + other.size) - elif isinstance(other, WordBase): - return BitsUnion(self, other) raise NotImplementedError() global_slice = slice - -class LabelRef(WordBase): - mode_re = re.compile(r"^(imm|off)(\d+)?$") - - def __init__(self, label, offset, mode, slice=None): - if isinstance(mode, str): - match = LabelRef.mode_re.match(mode) - assert match, ValueError("invalid label reference tag") - mode = (match.group(1), int(match.group(2) or 32)) - - self.label = label - self.mode = mode - self.slice = slice or global_slice(self.mode[1] - 1, 0) - self.offset = offset - - def __repr__(self): - """format for debugging. - - >>> LabelRef("label", 0, ("imm", 32), slice(31,31)) - LabelRef('label', 0, ('imm', 32), 31:31) - >>> LabelRef("label", 4, ("imm", 32), slice(11,0)) - LabelRef('label', 4, ('imm', 32), 11:0) - >>> LabelRef("test", -4, "off32") - LabelRef('test', -4, ('off', 32), 31:0) - >>> LabelRef("test", 0, "off") - LabelRef('test', 0, ('off', 32), 31:0) - """ - return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format( - self.label, self.offset, self.mode, self.slice - ) - - def __str__(self): - """format as a word. - - >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31))) - 'label/imm32/[31:31]' - >>> str(LabelRef("label", 0, ("imm", 32), slice(11,0))) - 'label/imm32/[11:0]' - >>> str(LabelRef("test", 4, "off32")) - 'test+4/off32/[31:0]' - >>> str(LabelRef("test", -12, "off")) - 'test-12/off32/[31:0]' - """ - offset = "" - if self.offset != 0: - offset = "{:+}".format(self.offset) - - return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format( - self.label, offset, self.mode, self.slice - ) - - @property - def size(self): - return self.slice.start - self.slice.stop + 1 - - def as_value(self, labels={}, **kwargs): - """resolve the reference. - - >>> labels = {'test': 0xabcd1234, 'main': 0x8000} - >>> LabelRef("test", 0, "imm").as_value(labels=labels) - Bitfield(0xabcd1234, 32) - >>> LabelRef("test", 0, "imm", slice(15, 0)).as_value(labels=labels) - Bitfield(0x1234, 16) - >>> LabelRef("test", 0, "imm", slice(31, 12)).as_value(labels=labels) - Bitfield(0xabcd1, 20) - >>> LabelRef("test", 4, "imm", slice(15, 0)).as_value(labels=labels) - Bitfield(0x1238, 16) - >>> LabelRef("test", 4, "imm", slice(15, 4)).as_value(labels=labels) - Bitfield(0x123, 12) - >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x6000) - Bitfield(0x2000, 32) - >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x800a) - Bitfield(0xfffffff6, 32) - >>> LabelRef("main", 0, "off", slice(7, 0)).as_value(labels=labels, pc=0x8012) - Bitfield(0xee, 8) - >>> LabelRef("main", 0, "off8").as_value(labels=labels, pc=0x8012) - Bitfield(0xee, 8) - >>> LabelRef("main", 0, "off4").as_value(labels=labels, pc=0x8012) - Traceback (most recent call last): - ... - ValueError: value -18 out of i4 range [-8;7] - >>> LabelRef("main", 4, "off", slice(7, 0)).as_value(labels=labels, pc=0x70fa) - Bitfield(0xa, 8) - """ - if self.label not in labels: - raise ValueError("undefined label '{}'".format(self.label)) - - value = labels[self.label] + self.offset - - if self.mode[0] == "off": - assert "pc" in kwargs, TypeError( - "'pc' is required to resolve offset-references" - ) - value = value - kwargs["pc"] - - field = Bitfield(value, self.mode[1]) - return field[self.slice] - - def __getitem__(self, range): - """slice a label using hi and lo bit indices. - - >>> label = LabelRef("label", 0, "imm32", slice(31,0)) - >>> label.size - 32 - >>> label[31] - LabelRef('label', 0, ('imm', 32), 31:31) - >>> label[11:0] - LabelRef('label', 0, ('imm', 32), 11:0) - >>> label[11:0].size - 12 - >>> label[12:1] - LabelRef('label', 0, ('imm', 32), 12:1) - >>> label = LabelRef("abc", 0, "off32", slice(31,12)) - >>> label.size - 20 - >>> label[9:0] - LabelRef('abc', 0, ('off', 32), 21:12) - >>> label[9:2] - LabelRef('abc', 0, ('off', 32), 21:14) - >>> label[20:8] - Traceback (most recent call last): - ... - ValueError: slice [20:8] out of range of abc/off32/[31:12] - """ - if not isinstance(range, slice): - range = slice(range, range) - hi, lo = range.start, range.stop - - if hi < lo: - raise ValueError("cant slice reverse range") - elif lo < 0 or hi >= self.size: - raise ValueError( - "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) - ) - - new_slice = slice(self.slice.stop + hi, self.slice.stop + lo) - return LabelRef(self.label, self.offset, self.mode, new_slice) - - empty = Bitfield(0, 0) @@ -396,22 +164,21 @@ def i(num, bits): >>> i(-128, 8) Bitfield(0x80, 8) - >>> i(128, 8) + >>> i(256, 8) Traceback (most recent call last): ... - ValueError: value 128 (i9) too large for i8 field [-128;127] + ValueError: value 256 (u9) too large for u8 field >>> i(-129, 8) Traceback (most recent call last): ... - ValueError: value -129 (i9) too large for i8 field [-128;127] + ValueError: value -129 (i9) too small for i8 field (min -128) """ min = -(1 << (bits - 1)) - max = -1 - min - if num > max or num < min: + if num < min: raise ValueError( - "value {} (i{}) too large for i{} field [{};{}]".format( - num, num.bit_length() + 1, bits, min, max + "value {} (i{}) too small for i{} field (min {})".format( + num, num.bit_length() + 1, bits, min ) ) @@ -432,57 +199,3 @@ def from_part(part): val = int(part[0]) size = part[1] return Bitfield(val, int(size)) - - -global_slice = slice -ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$") -slice_re = re.compile(r"^\[(\d+):(\d+)]$") - - -def ref(val, default_slice=None): - """add a default slice spec to labels if missing. - - >>> ref(('label', 'imm12')) - LabelRef('label', 0, ('imm', 12), 11:0) - >>> ref(('label', 'imm12'), slice(12, 1)) - LabelRef('label', 0, ('imm', 12), 12:1) - >>> ref(('label-4', 'imm12'), slice(12, 1)) - LabelRef('label', -4, ('imm', 12), 12:1) - >>> ref(('label', 'imm12', '[14:3]'), slice(12, 1)) - LabelRef('label', 0, ('imm', 12), 14:3) - >>> ref(('label+4', 'imm12', '[14:3]'), slice(12, 1)) - LabelRef('label', 4, ('imm', 12), 14:3) - >>> ref(('label', 'imm12', '[14:3]'), slice(12, 1)) - LabelRef('label', 0, ('imm', 12), 14:3) - >>> ref(('label', 'imm12', '[31:0]'), slice(11, 0)) - Traceback (most recent call last): - ... - AssertionError: expected 12 bit slice, got label/imm12/[31:0] - """ - slice = None - if len(val) == 3: - label_offset, mode, slice = val - elif len(val) == 2: - label_offset, mode = val - else: - raise ValueError("expected label reference, got {}".format(val)) - - match = ref_re.match(label_offset) - assert match, ValueError("invalid label reference") - label, offset = match.group(1), int(match.group(2) or 0) - - if isinstance(slice, str): - match = slice_re.match(slice) - assert match, ValueError("invalid slice syntax") - a, b = match.groups() - slice = global_slice(int(a), int(b)) - - ref = LabelRef(label, offset, mode, slice or default_slice) - - if default_slice: - expected = default_slice.start - default_slice.stop + 1 - assert expected == ref.size, ValueError( - "expected {} bit slice, got {}".format(expected, ref) - ) - - return ref |
