From 912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2 Mon Sep 17 00:00:00 2001 From: s-ol Date: Tue, 6 Jul 2021 12:03:57 +0200 Subject: fix and finish new pipeline --- bits.py | 321 ++++-------------------------------------------------------- elf.py | 8 +- pack.py | 15 ++- subv.py | 31 +----- validate.py | 5 +- 5 files changed, 36 insertions(+), 344 deletions(-) diff --git a/bits.py b/bits.py index a40c363..bba325c 100644 --- a/bits.py +++ b/bits.py @@ -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 diff --git a/elf.py b/elf.py index 0db1138..776dd91 100755 --- a/elf.py +++ b/elf.py @@ -6,7 +6,7 @@ import subv Takes a packed & surveyed hex stream (with section headers) and packs it into a simulatable ELF file that can be run as a kernel: - qemu-system-riscv32 -nographic -machine sifive_u -nographic -kernel out.elf + qemu-system-riscv32 -nographic -machine sifive_u -bios none -kernel out.elf sifive-u has lots of memory mapped peripherals, see https://github.com/qemu/qemu/blob/master/hw/riscv/sifive_u.c#L66-L83 @@ -71,9 +71,9 @@ def write_program_header(segment, c, start): flags = 5 if segment["name"] == "code" else 6 if offset % align != start % align: - print("{:02x} {:02x}".format(offset, offset % align)) - print("{:02x} {:02x}".format(start, start % align)) - raise ValueError("improper alignment") + extra = "\n{:02x} {:02x}".format(offset, offset % align) + extra += "\n{:02x} {:02x}".format(start, start % align) + raise ValueError("improper alignment:" + extra) c += wi(0x1, 4) # p_type c += wi(offset, 4) # p_offset diff --git a/pack.py b/pack.py index 4a21ab5..930c120 100755 --- a/pack.py +++ b/pack.py @@ -69,13 +69,9 @@ def pack(iter): buf = bits.empty def flush_bytes(): - tmp = out[:] + line = " ".join("{:02x}".format(b.val) for b in out) out.clear() - return { - "type": "instr", - "instr": tmp, - "comment": None, - } + return line last_segment = None for segment, line in iter: @@ -93,15 +89,18 @@ def pack(iter): while buf.size >= 8: byte = buf[7:0] buf = buf.slice_allowempty(slice(buf.size - 1, 8)) - out.append((byte.val,)) + out.append(byte) if len(out) == 4: - yield subv.format(flush_bytes()) + yield flush_bytes() last_segment = segment assert buf.size == 0, "segment '{}' end isn't byte-aligned".format(segment) + if len(out) > 0: + yield flush_bytes() + if __name__ == "__main__": import sys diff --git a/subv.py b/subv.py index 8871625..c514e34 100644 --- a/subv.py +++ b/subv.py @@ -7,6 +7,7 @@ num = re.compile(r"^\d+$") ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$") immediate_re = re.compile(r"^(imm|off)(\d+)(hi|lo)?$") + # parsing def parse_part(part): """parse a literal with attached metadata. @@ -113,7 +114,7 @@ def parse_reference(part): """parse a sliced-label part. >>> parse_reference(('lbl', 'imm32')) - {'label': 'lbl', 'mode': 'imm', 'size': 32, offset: 0} + {'label': 'lbl', 'mode': 'imm', 'size': 32, 'offset': 0} >>> parse_reference(('lbl+0', 'off32')) {'label': 'lbl', 'mode': 'off', 'size': 32, 'offset': 0} >>> parse_reference(('lbl+2', 'imm32')) @@ -274,30 +275,6 @@ def untag(part, expect=None): return part[0] -def format_reference(ref): - """opposite of parse_reference.. - - >>> format_reference({'label': 'lbl', 'mode': 'imm', 'size': 32, 'meta': ()}) - ('lbl', 'imm32') - >>> format_reference({'label': 'lbl', 'offset': 0, 'mode': 'imm', 'size': 32, 'meta': ()}) - ('lbl+0', 'imm32') - >>> format_reference({'label': 'lbl', 'offset': 2, 'mode': 'imm', 'size': 32, 'meta': ()}) - ('lbl+2', 'imm32') - >>> format_reference({'label': 'x', 'mode': 'off', 'size': 12, 'meta': (), 'hi': 11, 'lo': 0}) - ('x[11:0]', 'off12') - >>> format_reference({'label': 'x', 'offset': -16, 'mode': 'off', 'size': 12, 'meta': (), 'hi': 11, 'lo': 0}) - ('x-16[11:0]', 'off12') - >>> format_reference({'label': 'x', 'offset': -16, 'mode': 'off', 'size': 12, 'meta': ('extra', 'stuff'), 'hi': 11, 'lo': 0}) - ('x-16[11:0]', 'off12', 'extra', 'stuff') - """ - label = ref["label"] - if "offset" in ref: - label += "{:+}".format(ref["offset"]) - if "hi" in ref: - label += "[{hi}:{lo}]".format(**ref) - return (label, "{mode}{size}".format(**ref), *ref["meta"]) - - def format_immediate(imm): tag = "{mode}{size}".format(**imm) if "split" in imm: @@ -327,7 +304,7 @@ def format_part(part): >>> format_part(('$label:suff', 'tag')) '$label:suff/tag' """ - if isinstance(part, bits.WordBase): + if isinstance(part, bits.Bitfield): return str(part) elif not is_reference(part): first = "{:x}".format(part[0]) @@ -351,6 +328,8 @@ def format(line): if line["comment"]: packed = packed + " # " + line["comment"] return packed + elif type == "empty": + return line["raw"] else: raise NotImplementedError("type {}".format(type)) diff --git a/validate.py b/validate.py index 84bb036..09c37a5 100755 --- a/validate.py +++ b/validate.py @@ -193,8 +193,8 @@ def validate_b(inputs): return [ (op[0], "b"), funct, - src1, - src2, + rs1, + rs2, offset, ] @@ -233,6 +233,7 @@ instr_map = { "opi": (validate_i, 0x13), "load": (validate_i, 0x03), "jalr": (validate_i, 0x67), + "system": (validate_i, 0x73), "store": (validate_s, 0x23), "branch": (validate_b, 0x63), "lui": (validate_u, 0x37), -- cgit v1.2.3