diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-05-25 15:14:24 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-05-25 15:14:24 +0000 |
| commit | 661a79240c8bafb3c99f3080c546689d025738f0 (patch) | |
| tree | 3168b7acd14a8d627ca941bdd5a81c1d17268078 | |
| parent | close to working AUIPC+JALR jump (diff) | |
| download | subv-661a79240c8bafb3c99f3080c546689d025738f0.tar.gz subv-661a79240c8bafb3c99f3080c546689d025738f0.zip | |
apply "black" formatting
| -rw-r--r-- | bits.py | 108 | ||||
| -rwxr-xr-x | elf.py | 87 | ||||
| -rwxr-xr-x | format.py | 117 | ||||
| -rwxr-xr-x | pack.py | 24 | ||||
| -rw-r--r-- | subv.py | 153 | ||||
| -rwxr-xr-x | survey.py | 58 |
6 files changed, 318 insertions, 229 deletions
@@ -2,6 +2,7 @@ import re from operator import __and__ from functools import reduce + class WordBase(object): def slice_allowempty(self, range): if not isinstance(range, slice): @@ -18,15 +19,16 @@ class WordBase(object): 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)) + return "BitsUnion({})".format(", ".join(repr(part) for part in self.parts)) def as_value(self, **kwargs): - """ reduce down to a single Bitfield value. + """reduce down to a single Bitfield value. >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value() Bitfield(0x1234, 16) @@ -37,7 +39,7 @@ class BitsUnion(WordBase): @property def size(self): - """ get the total size. + """get the total size. >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size 16 @@ -47,7 +49,7 @@ class BitsUnion(WordBase): return sum(part.size for part in self.parts) def __getitem__(self, range): - """ slice a union given hi and lo bit indices. + """slice a union given hi and lo bit indices. >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4] Bitfield(0x23, 8) @@ -62,7 +64,9 @@ class BitsUnion(WordBase): 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)) + raise ValueError( + "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) + ) i = size result = Bitfield(0, 0) @@ -91,6 +95,7 @@ class BitsUnion(WordBase): return BitsUnion(*self.parts, other) raise NotImplementedError() + class Bitfield(WordBase): def __init__(self, val, size, extra=()): if val < 0: @@ -98,7 +103,9 @@ class Bitfield(WordBase): max = -1 - min if val > max or val < min: - raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max)) + raise ValueError( + "value {} out of i{} range [{};{}]".format(val, size, min, max) + ) if val < 0: val = (1 << size) + val @@ -108,7 +115,7 @@ class Bitfield(WordBase): self.extra = extra def __repr__(self): - """ format for debugging. + """format for debugging. >>> Bitfield(0, 8) Bitfield(0x0, 8) >>> Bitfield(16, 8) @@ -120,13 +127,13 @@ class Bitfield(WordBase): >>> Bitfield(3, 4, extra=('hello',)) Bitfield(0x3, 4, extra=('hello',)) """ - extra = '' + extra = "" if self.extra: extra = ", extra={!r}".format(self.extra) return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra) def __str__(self): - """ format as a word. + """format as a word. >>> str(Bitfield(0, 8)) '00/8' >>> str(Bitfield(16, 8)) @@ -142,14 +149,14 @@ class Bitfield(WordBase): """ digits = 1 + ((self.size - 1) // 4) base = "{:x}".format(self.val) - base = (digits - len(base)) * '0' + base - return '/'.join([base, *self.extra, str(self.size)]) + 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. + """slice a bitfield given hi and lo bit indices. >>> Bitfield(0x7f, 8)[7:4] Bitfield(0x7, 4) @@ -182,14 +189,16 @@ class Bitfield(WordBase): 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)) + raise ValueError( + "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) + ) size = hi - lo + 1 val = (self.val >> lo) & ((1 << size) - 1) return Bitfield(val, size) def __and__(self, other): - """ concatenate multiple bitfields. + """concatenate multiple bitfields. >>> Bitfield(0b00, 2) & Bitfield(0b10, 2) Bitfield(0x2, 4) @@ -202,9 +211,12 @@ class Bitfield(WordBase): return BitsUnion(self, other) raise NotImplementedError() + global_slice = slice + + class LabelRef(WordBase): - mode_re = re.compile(r'^(imm|off)(\d+)?$') + mode_re = re.compile(r"^(imm|off)(\d+)?$") def __init__(self, label, offset, mode, slice=None): if isinstance(mode, str): @@ -214,11 +226,11 @@ class LabelRef(WordBase): self.label = label self.mode = mode - self.slice = slice or global_slice(self.mode[1]-1, 0) + self.slice = slice or global_slice(self.mode[1] - 1, 0) self.offset = offset def __repr__(self): - """ format for debugging. + """format for debugging. >>> LabelRef("label", 0, ("imm", 32), slice(31,31)) LabelRef('label', 0, ('imm', 32), 31:31) @@ -229,10 +241,12 @@ class LabelRef(WordBase): >>> 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) + 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. + """format as a word. >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31))) 'label/imm32/[31:31]' @@ -243,18 +257,20 @@ class LabelRef(WordBase): >>> str(LabelRef("test", -12, "off")) 'test-12/off32/[31:0]' """ - offset = '' + 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) + 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. + """resolve the reference. >>> labels = {'test': 0xabcd1234, 'main': 0x8000} >>> LabelRef("test", 0, "imm").as_value(labels=labels) @@ -287,15 +303,17 @@ class LabelRef(WordBase): 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'] + 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. + """slice a label using hi and lo bit indices. >>> label = LabelRef("label", 0, "imm32", slice(31,0)) >>> label.size @@ -327,15 +345,19 @@ class LabelRef(WordBase): 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)) + 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) + def u(num, bits): - """ parse an unsigned integer into a bitfield. + """parse an unsigned integer into a bitfield. >>> u(0x08, 8) Bitfield(0x8, 8) @@ -355,13 +377,15 @@ def u(num, bits): raise ValueError("negative value not allowed: {}".format(num)) if num.bit_length() > bits: - raise ValueError("value {} (u{}) too large for u{} field" - .format(num, num.bit_length(), bits)) + raise ValueError( + "value {} (u{}) too large for u{} field".format(num, num.bit_length(), bits) + ) return Bitfield(num, bits) + def i(num, bits): - """ parse a signed integer into a bitfield. + """parse a signed integer into a bitfield. >>> i(8, 8) Bitfield(0x8, 8) @@ -385,16 +409,20 @@ def i(num, bits): max = -1 - min if num > max or num < min: - raise ValueError("value {} (i{}) too large for i{} field [{};{}]" - .format(num, num.bit_length()+1, bits, min, max)) + raise ValueError( + "value {} (i{}) too large for i{} field [{};{}]".format( + num, num.bit_length() + 1, bits, min, max + ) + ) if num < 0: num = (1 << bits) + num return u(num, bits) + def from_part(part): - """ parse a size-tagged subv part into a bit. + """parse a size-tagged subv part into a bit. >>> from_part((0x12, 2)) Bitfield(0x12, 2) @@ -405,12 +433,14 @@ def from_part(part): size = part[1] return Bitfield(val, int(size)) + global_slice = slice -ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?$') -slice_re = re.compile(r'^\[(\d+):(\d+)]$') +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. + """add a default slice spec to labels if missing. >>> ref(('label', 'imm12')) LabelRef('label', 0, ('imm', 12), 11:0) @@ -451,6 +481,8 @@ def ref(val, default_slice=None): if default_slice: expected = default_slice.start - default_slice.stop + 1 - assert expected == ref.size, ValueError("expected {} bit slice, got {}".format(expected, ref)) + assert expected == ref.size, ValueError( + "expected {} bit slice, got {}".format(expected, ref) + ) return ref @@ -18,82 +18,88 @@ See full documentation here: https://sifive.cdn.prismic.io/sifive/4d063bf8-3ae6-4db6-9843-ee9076ebadf7_fe310-g000.pdf """ + def w(b): sys.stdout.buffer.write(b) return len(b) + def wi(num, size=2): - sys.stdout.buffer.write(num.to_bytes(size, byteorder='little')) + sys.stdout.buffer.write(num.to_bytes(size, byteorder="little")) return size + def padto(offset, cursor): missing = offset - cursor if missing < 0: raise ValueError("cursor {} already past offset {}!".format(cursor, offset)) - w(b'\x00' * missing) + w(b"\x00" * missing) return offset + def write_elf_header(segments): c = 0 - code = next(s for s in segments if s['name'] == 'code') - entrypoint = code['addr'] - - c += w(b"\x7fELF") # ELF magic - c += wi(0x010101, 4) # 32bit, little endian - c += wi(0, 8) # reserved - c += wi(0x02) # e_type - c += wi(0xf3) # e_machine - c += wi(0x01, 4) # e_version - c += wi(entrypoint, 4) # e_entry - c += wi(0x34, 4) # e_phoff (Program Header offset) - c += wi(0x00, 4) # e_shoff (Section Header offset, unused) - c += wi(0x0004, 4) # e_flags - c += wi(0x34) # e_ehsize - c += wi(0x20) # e_phentsize - c += wi(len(segments)) # e_phnum - c += wi(0x28) # e_shentsize - c += wi(0x0) # e_shnum - c += wi(0x0) # e_shstrndx + code = next(s for s in segments if s["name"] == "code") + entrypoint = code["addr"] + + c += w(b"\x7fELF") # ELF magic + c += wi(0x010101, 4) # 32bit, little endian + c += wi(0, 8) # reserved + c += wi(0x02) # e_type + c += wi(0xF3) # e_machine + c += wi(0x01, 4) # e_version + c += wi(entrypoint, 4) # e_entry + c += wi(0x34, 4) # e_phoff (Program Header offset) + c += wi(0x00, 4) # e_shoff (Section Header offset, unused) + c += wi(0x0004, 4) # e_flags + c += wi(0x34) # e_ehsize + c += wi(0x20) # e_phentsize + c += wi(len(segments)) # e_phnum + c += wi(0x28) # e_shentsize + c += wi(0x0) # e_shnum + c += wi(0x0) # e_shstrndx return c + def write_program_header(segment, c, start): align = 0x1000 offset = (1 + start // align) * align - segment['offset'] = offset - start = segment['addr'] - size = len(segment['content']) - flags = 5 if segment['name'] == "code" else 6 + segment["offset"] = offset + start = segment["addr"] + size = len(segment["content"]) + 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") - c += wi(0x1, 4) # p_type - c += wi(offset, 4) # p_offset + c += wi(0x1, 4) # p_type + c += wi(offset, 4) # p_offset c += wi(start, 4) # p_vaddr c += wi(start, 4) # p_paddr - c += wi(size, 4) # p_filesz - c += wi(size, 4) # p_memsz - c += wi(flags, 4) # p_flags, 0x5=rx, 0x6=rw + c += wi(size, 4) # p_filesz + c += wi(size, 4) # p_memsz + c += wi(flags, 4) # p_flags, 0x5=rx, 0x6=rw c += wi(align, 4) # p_align return c + @subv.with_parsed_lines def elf(iter): segments = [] segment = None for _, line in iter: - if line['type'] == 'segment': - (name, addr) = line['segment'] - segment = { 'name': name, 'addr': addr, 'content': [] } + if line["type"] == "segment": + (name, addr) = line["segment"] + segment = {"name": name, "addr": addr, "content": []} segments.append(segment) - elif line['type'] == 'instr': + elif line["type"] == "instr": if segment == None: - raise ValueError("label or code outside of segment!") + raise ValueError("label or code outside of segment!") - segment['content'] += line['instr'] + segment["content"] += line["instr"] else: raise ValueError("elf input should contain only segments and data!") @@ -101,15 +107,16 @@ def elf(iter): segment_start = 0x1000 for seg in segments: c = write_program_header(seg, c, segment_start) - segment_start = seg['offset'] + len(seg['content']) + segment_start = seg["offset"] + len(seg["content"]) for seg in segments: - c = padto(seg['offset'], c) - for part in seg['content']: + c = padto(seg["offset"], c) + for part in seg["content"]: c += wi(part[0], 1) yield -if __name__ == '__main__': + +if __name__ == "__main__": for line in elf(sys.stdin): pass @@ -66,24 +66,29 @@ main: import subv import bits + def _test_format(words): - return ' '.join(map(str, words)) + return " ".join(map(str, words)) + def ref_slice(ref, hi, lo): if hi < lo: raise ValueError("cant slice reverse range") - elif hi >= ref['size']: - raise ValueError("cant slice [{}:{}] from {} bit value".format(hi, lo, ref['size'])) + elif hi >= ref["size"]: + raise ValueError( + "cant slice [{}:{}] from {} bit value".format(hi, lo, ref["size"]) + ) return { **ref, - 'hi': hi, - 'lo': lo, - 'size': hi - lo + 1, + "hi": hi, + "lo": lo, + "size": hi - lo + 1, } + def sign_trunc(val, size): - """ truncate and sign-shrink a number literal. + """truncate and sign-shrink a number literal. >>> sign_trunc(bits.i(4, 32), 8) Bitfield(0x4, 8) @@ -94,12 +99,13 @@ def sign_trunc(val, size): >>> sign_trunc(bits.i(-0x11ff, 32), 8) Bitfield(0x81, 8) """ - sign = val[val.size-1] - rest = val[size-2:0] + sign = val[val.size - 1] + rest = val[size - 2 : 0] return sign & rest + def pack_u(instr): - """ verify & pack U-type instructions. + """verify & pack U-type instructions. >>> _test_format(pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')])) '37/7 05/5 10010/20' @@ -117,16 +123,17 @@ def pack_u(instr): """ (op, rd, imm) = instr op = bits.u(subv.untag(op), 7) - rd = bits.u(subv.untag(rd, 'rd'), 5) + rd = bits.u(subv.untag(rd, "rd"), 5) if subv.is_reference(imm): imm = bits.ref(imm, slice(31, 12)) else: - imm = bits.i(subv.untag(imm, 'imm20'), 20) + imm = bits.i(subv.untag(imm, "imm20"), 20) return [op, rd, imm] + def pack_i(instr): - """ verify & pack I-type instructions. + """verify & pack I-type instructions. >>> _test_format(pack_i([(0x13, 'opi'), (0, 'subop', 'add'), (6, 'rd', 't1'), (0, 'rs', 'x0'), (0x65, 'imm12')])) '13/7 06/5 0/3 00/5 065/12' @@ -136,22 +143,23 @@ def pack_i(instr): """ (op, sub, rd, rs, imm) = instr op_tag = op[1] - op = bits.u(subv.untag(op), 7) - sub = bits.u(subv.untag(sub, 'subop'), 3) - rd = bits.u(subv.untag(rd, 'rd'), 5) - rs = bits.u(subv.untag(rs, 'rs'), 5) + op = bits.u(subv.untag(op), 7) + sub = bits.u(subv.untag(sub, "subop"), 3) + rd = bits.u(subv.untag(rd, "rd"), 5) + rs = bits.u(subv.untag(rs, "rs"), 5) if subv.is_reference(imm): - if op_tag == 'jalr': + if op_tag == "jalr": imm = bits.ref(imm, slice(12, 1)) else: imm = bits.ref(imm, slice(11, 0)) else: - imm = bits.i(subv.untag(imm, 'imm12'), 12) + imm = bits.i(subv.untag(imm, "imm12"), 12) return [op, rd, sub, rs, imm] + def pack_s(instr): - """ verify & pack S-type instructions. + """verify & pack S-type instructions. >>> _test_format(pack_s([(0x23, 'store'), (2, 'subop', 'word'), (5, 'rs', 't0'), (6, 'rs', 't1'), (0, 'off12')])) '23/7 00/5 2/3 05/5 06/5 00/7' @@ -160,22 +168,23 @@ def pack_s(instr): '23/7 home/off12/[4:0] 2/3 05/5 06/5 home/off12/[11:5]' """ (op, sub, rs1, rs2, imm) = instr - op = bits.u(subv.untag(op), 7) - sub = bits.u(subv.untag(sub, 'subop'), 3) - rs1 = bits.u(subv.untag(rs1, 'rs'), 5) - rs2 = bits.u(subv.untag(rs2, 'rs'), 5) + op = bits.u(subv.untag(op), 7) + sub = bits.u(subv.untag(sub, "subop"), 3) + rs1 = bits.u(subv.untag(rs1, "rs"), 5) + rs2 = bits.u(subv.untag(rs2, "rs"), 5) if subv.is_reference(imm): imm = bits.ref(imm, slice(11, 0)) else: - imm = bits.i(subv.untag(imm, 'off12'), 12) + imm = bits.i(subv.untag(imm, "off12"), 12) imm_lo = imm[4:0] imm_hi = imm[11:5] return [op, imm_lo, sub, rs1, rs2, imm_hi] + def pack_j(instr): - """ verify & pack J-type instructions. + """verify & pack J-type instructions. >>> _test_format(pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (0, 'off20')])) '6f/7 00/5 00/8 0/1 000/10 0/1' @@ -188,12 +197,12 @@ def pack_j(instr): """ (op, rd, imm) = instr op = bits.u(subv.untag(op), 7) - rd = bits.u(subv.untag(rd, 'rd'), 5) + rd = bits.u(subv.untag(rd, "rd"), 5) if subv.is_reference(imm): imm = bits.ref(imm, slice(20, 1)) else: - imm = bits.i(subv.untag(imm, 'off20'), 20) + imm = bits.i(subv.untag(imm, "off20"), 20) imm_lo = imm[9:0] imm_11 = imm[10] @@ -202,8 +211,9 @@ def pack_j(instr): return [op, rd, imm_hi, imm_11, imm_lo, imm_20] + def pack_b(instr): - """ verify & pack B-type instructions. + """verify & pack B-type instructions. >>> _test_format(pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), (0, 'off12')])) '63/7 0/1 0/4 0/3 06/5 00/5 00/6 0/1' @@ -215,15 +225,15 @@ def pack_b(instr): '63/7 home/off12/[11:11] home/off12/[4:1] 0/3 06/5 00/5 home/off12/[10:5] home/off12/[12:12]' """ (op, sub, rs1, rs2, imm) = instr - op = bits.u(subv.untag(op), 7) - sub = bits.u(subv.untag(sub, 'subop'), 3) - rs1 = bits.u(subv.untag(rs1, 'rs'), 5) - rs2 = bits.u(subv.untag(rs2, 'rs'), 5) + op = bits.u(subv.untag(op), 7) + sub = bits.u(subv.untag(sub, "subop"), 3) + rs1 = bits.u(subv.untag(rs1, "rs"), 5) + rs2 = bits.u(subv.untag(rs2, "rs"), 5) if subv.is_reference(imm): imm = bits.ref(imm, slice(12, 1)) else: - imm = bits.i(subv.untag(imm, 'off12'), 12) + imm = bits.i(subv.untag(imm, "off12"), 12) imm_lo = imm[3:0] imm_md = imm[9:4] @@ -232,39 +242,46 @@ def pack_b(instr): return [op, imm_11, imm_lo, sub, rs1, rs2, imm_md, imm_12] + instr_map = { # 'opr': (pack_r, 0x33), - 'load': (pack_i, 0x03), - 'opi': (pack_i, 0x13), - 'jalr': (pack_i, 0x67), - 'store': (pack_s, 0x23), - 'branch': (pack_b, 0x63), - 'lui': (pack_u, 0x37), - 'auipc': (pack_u, 0x17), - 'jal': (pack_j, 0x6f), + "load": (pack_i, 0x03), + "opi": (pack_i, 0x13), + "jalr": (pack_i, 0x67), + "store": (pack_s, 0x23), + "branch": (pack_b, 0x63), + "lui": (pack_u, 0x37), + "auipc": (pack_u, 0x17), + "jal": (pack_j, 0x6F), } + @subv.with_parsed_lines def format(iter): for segment, line in iter: - if line['type'] == 'instr' and segment == 'code': - op = line['instr'][0] - assert len(op) == 2, 'instruction without op label: {}'.format(op) + if line["type"] == "instr" and segment == "code": + op = line["instr"][0] + assert len(op) == 2, "instruction without op label: {}".format(op) (op, label) = op if label not in instr_map: raise ValueError("unknown instruction label: {}".format(label)) (formatter, expected) = instr_map[label] if op != expected: - raise ValueError("opcode {} doesn't match label {} (expected {})" - .format(op, label, expected)) + raise ValueError( + "opcode {} doesn't match label {} (expected {})".format( + op, label, expected + ) + ) - line['instr'] = formatter(line['instr']) + line["instr"] = formatter(line["instr"]) line = yield subv.format(line) else: - line = yield line['raw'] + line = yield line["raw"] + -if __name__ == '__main__': +if __name__ == "__main__": import sys + for line in format(sys.stdin): print(line) @@ -41,8 +41,9 @@ b7 02 01 10 import subv import bits + def byteify(word): - """ split longer bitfield into bytes. + """split longer bitfield into bytes. >>> byteify(bits.Bitfield(0x12345678, 32)) [(120,), (86,), (52,), (18,)] @@ -57,10 +58,11 @@ def byteify(word): out = [] for i in range(0, word.size, 8): - byte = word[i+7:i] + byte = word[i + 7 : i] out.append((byte.val,)) return out + @subv.with_parsed_lines def pack(iter): out = [] @@ -70,9 +72,9 @@ def pack(iter): tmp = out[:] out.clear() return { - 'type': 'instr', - 'instr': tmp, - 'comment': None, + "type": "instr", + "instr": tmp, + "comment": None, } last_segment = None @@ -82,15 +84,15 @@ def pack(iter): if out: yield subv.format(flush_bytes()) - if line['type'] == 'instr': - for part in line['instr']: + if line["type"] == "instr": + for part in line["instr"]: buf = bits.from_part(part) & buf else: - yield line['raw'] + yield line["raw"] while buf.size >= 8: byte = buf[7:0] - buf = buf.slice_allowempty(slice(buf.size-1, 8)) + buf = buf.slice_allowempty(slice(buf.size - 1, 8)) out.append((byte.val,)) if len(out) == 4: @@ -100,7 +102,9 @@ def pack(iter): assert buf.size == 0, "segment '{}' end isn't byte-aligned".format(segment) -if __name__ == '__main__': + +if __name__ == "__main__": import sys + for line in pack(sys.stdin): print(line) @@ -1,14 +1,15 @@ import re import bits -white = re.compile(r'[ \t\.\n]+') -hex = re.compile(r'^\-?(0x)?[0-9a-f]+$') -num = re.compile(r'^\d+$') -ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?(?:\[(\d+):(\d+)\])?$') -field_re = re.compile(r'^(imm|off)(\d+)$') + +white = re.compile(r"[ \t\.\n]+") +hex = re.compile(r"^\-?(0x)?[0-9a-f]+$") +num = re.compile(r"^\d+$") +ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?(?:\[(\d+):(\d+)\])?$") +field_re = re.compile(r"^(imm|off)(\d+)$") # parsing def parse_part(part): - """ parse a literal with attached metadata. + """parse a literal with attached metadata. >>> parse_part('0') (0,) @@ -45,23 +46,25 @@ def parse_part(part): >>> parse_part('$label:suff[11:0]/tag*') ('$label:suff[11:0]', 'tag*') """ - part = part.split('/') + part = part.split("/") if hex.match(part[0]): part[0] = int(part[0], 16) return tuple(part) + def parse_instr(line): - """ parse an instruction-line. + """parse an instruction-line. >>> parse_instr('ff/op 0/subop/add 1/rd/x1 label[11:0]/imm12') [(255, 'op'), (0, 'subop', 'add'), (1, 'rd', 'x1'), ('label[11:0]', 'imm12')] """ parts = white.split(line) - parts = [parse_part(part) for part in parts if part != ''] + parts = [parse_part(part) for part in parts if part != ""] return parts + def parse_segment(line): - """ parse a segment-line. + """parse a segment-line. >>> parse_segment('== code 0x8000') ('code', 32768) @@ -70,14 +73,15 @@ def parse_segment(line): """ parts = white.split(line) if len(parts) == 3: - return (parts[1], int(parts[2], 16)) + return (parts[1], int(parts[2], 16)) elif len(parts) == 2: - return (parts[1],) + return (parts[1],) else: - raise ValueError("invalid segment line") + raise ValueError("invalid segment line") + def parse_label(line): - """ parse a label-line. + """parse a label-line. >>> parse_label('some_label:') 'some_label' @@ -86,8 +90,9 @@ def parse_label(line): """ return line[:-1] + def parse_reference(part): - """ parse a sliced-label part. + """parse a sliced-label part. >>> parse_reference(('lbl', 'imm32')) {'label': 'lbl', 'mode': 'imm', 'size': 32, 'meta': ()} @@ -109,25 +114,26 @@ def parse_reference(part): label, off, hi, lo = ref_re.match(ref).groups() mode, size = field_re.match(field).groups() - + ref = { - 'label': label, - 'mode': mode, - 'size': int(size), - 'meta': part[2:], + "label": label, + "mode": mode, + "size": int(size), + "meta": part[2:], } if off is not None: - ref['offset'] = int(off) + ref["offset"] = int(off) if hi is not None: - ref['hi'] = int(hi) - ref['lo'] = int(lo) + ref["hi"] = int(hi) + ref["lo"] = int(lo) return ref + def classify(line): - """ classify cleaned lines. + """classify cleaned lines. >>> classify('') 'empty' @@ -144,20 +150,20 @@ def classify(line): >>> classify('ff/8 0/3 2/5') 'instr' """ - if line == '': - return 'empty' - elif line.startswith('=='): # segment - return 'segment' - elif line.endswith(':'): # label - return 'label' + if line == "": + return "empty" + elif line.startswith("=="): # segment + return "segment" + elif line.endswith(":"): # label + return "label" else: - return 'instr' + return "instr" + def parse(line): - """ clean, classify and parse lines. - """ + """clean, classify and parse lines.""" raw = line.strip() - split = raw.split('#', 1) + split = raw.split("#", 1) if len(split) == 1: clean = raw comment = None @@ -166,25 +172,26 @@ def parse(line): type = classify(clean) - if type == 'segment': + if type == "segment": parsed = parse_segment(clean) - elif type == 'label': + elif type == "label": parsed = parse_label(clean) - elif type == 'instr': + elif type == "instr": parsed = parse_instr(clean) else: parsed = None return { - 'type': type, - 'raw': raw, - 'line': clean, - 'comment': comment, + "type": type, + "raw": raw, + "line": clean, + "comment": comment, type: parsed, } + def is_reference(part): - """ check whether a part is a label reference. + """check whether a part is a label reference. >>> is_reference(('hello',)) True @@ -216,8 +223,9 @@ def is_reference(part): """ return isinstance(part[0], str) + def untag(part, expect=None): - """ returns the value of a part and optionally verifies the first tag. + """returns the value of a part and optionally verifies the first tag. >>> untag((2, 'num')) 2 @@ -255,8 +263,9 @@ def untag(part, expect=None): raise ValueError("expected {} to be labelled one of {}".format(part, expect)) return part[0] + def format_reference(ref): - """ opposite of parse_reference.. + """opposite of parse_reference.. >>> format_reference({'label': 'lbl', 'mode': 'imm', 'size': 32, 'meta': ()}) ('lbl', 'imm32') @@ -271,15 +280,16 @@ def format_reference(ref): >>> 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']) + 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_part(part): - """ opposite of parse_part. + """opposite of parse_part. >>> format_part((0,)) '00' @@ -302,12 +312,13 @@ def format_part(part): if isinstance(part, bits.WordBase): return str(part) elif not is_reference(part): - first = '{:02x}'.format(part[0]) + first = "{:02x}".format(part[0]) part = (first, *part[1:]) - return '/'.join([str(p) for p in part]) + return "/".join([str(p) for p in part]) + def format(line): - """ opposite of parse. + """opposite of parse. >>> format({ ... 'type': 'instr', @@ -316,17 +327,18 @@ def format(line): ... }) 'ff/op 00/subop/add 01/rd/x1 label[11:0]/imm12 # this does things.' """ - type = line['type'] - if type == 'instr' or type == 'data': - packed = ' '.join(format_part(part) for part in line[type]) - if line['comment']: - packed = packed + ' # ' + line['comment'] + type = line["type"] + if type == "instr" or type == "data": + packed = " ".join(format_part(part) for part in line[type]) + if line["comment"]: + packed = packed + " # " + line["comment"] return packed else: raise NotImplementedError("type {}".format(type)) + def dump(line): - """ debug-friendly string representation of parsed lines. + """debug-friendly string representation of parsed lines. >>> dump({ ... 'type': 'instr', @@ -336,16 +348,18 @@ def dump(line): "instr[(255, 'op'), (0, 'subop', 'add'), (1, 'rd', 'x1'), ('label[11:0]', 'imm12')]" """ - return '{}{}'.format(line['type'], line[line['type']]) + return "{}{}".format(line["type"], line[line["type"]]) + def join_all(gen): - res = '\n'.join(gen) + res = "\n".join(gen) return res class SubVException(Exception): pass + class LineIterator(object): def __init__(self, stream): self.stream = stream @@ -361,21 +375,24 @@ class LineIterator(object): self.line = None try: self.line = parse(self.raw_line) - if self.line['type'] == 'segment': - self.segment = self.line['segment'][0] + if self.line["type"] == "segment": + self.segment = self.line["segment"][0] except Exception as e: raise self.exception("failed to parse line") from e return (self.segment, self.line) def exception(self, msg): - stream_name = getattr(self.stream, 'name', '(unnamed)') + stream_name = getattr(self.stream, "name", "(unnamed)") if self.line: msg = msg + "\n{}:{}: {}".format(stream_name, self.i, format(self.line)) msg = msg + "\nparsed as {}".format(dump(self.line)) elif self.raw_line: - msg = msg + "\n{}:{}: {}".format(stream_name, self.i, self.raw_line.strip()) + msg = msg + "\n{}:{}: {}".format( + stream_name, self.i, self.raw_line.strip() + ) return SubVException(msg) + def with_parsed_lines(process_fn): def _wrapped(iter): iterator = LineIterator(iter) @@ -384,6 +401,8 @@ def with_parsed_lines(process_fn): except SubVException: raise except Exception as e: - raise iterator.exception("failed to {} line".format(process_fn.__name__)) from e + raise iterator.exception( + "failed to {} line".format(process_fn.__name__) + ) from e return _wrapped @@ -50,11 +50,12 @@ import subv import bits import re -slice_re = re.compile(r'^([^\[]+)(?:\[(\d+):(\d+)\])?$') -field_re = re.compile(r'^(imm|off)(\d+)$') +slice_re = re.compile(r"^([^\[]+)(?:\[(\d+):(\d+)\])?$") +field_re = re.compile(r"^(imm|off)(\d+)$") + def observe(word, pc, map): - """ resolve a label reference. + """resolve a label reference. >>> observe((3, '3'), 0, {}) (3, '3') @@ -86,43 +87,50 @@ def observe(word, pc, map): word = bits.ref(word) return word.as_value(pc=pc, labels=map) + @subv.with_parsed_lines def survey(iter): queue = [] map = {} addr, bitcount = -1, 0 for segment, line in iter: - line['addr'] = addr + line["addr"] = addr queue.append(line) # step forward addr - type = line['type'] - if type == 'segment': - addr, bitcount = line['segment'][1], 0 - elif type == 'label': + type = line["type"] + if type == "segment": + addr, bitcount = line["segment"][1], 0 + elif type == "label": assert bitcount == 0, "label isn't byte aligned" - map[line['label']] = addr - elif type == 'instr': - if segment == 'data': - for part in line['instr']: + map[line["label"]] = addr + elif type == "instr": + if segment == "data": + for part in line["instr"]: bitcount += int(part[1]) if bitcount >= 8: addr += bitcount // 8 bitcount = bitcount % 8 - elif segment == 'code': - assert bitcount == 0, "instruction isn't byte aligned ({} bitcount left)".format(8 - bitcount) + elif segment == "code": + assert ( + bitcount == 0 + ), "instruction isn't byte aligned ({} bitcount left)".format( + 8 - bitcount + ) assert addr % 2 == 0, "instruction isn't 2-byte aligned" bitcount = 0 - for part in line['instr']: + for part in line["instr"]: if subv.is_reference(part): ref = bits.ref(part) bitcount += ref.size else: bitcount += int(part[1]) - assert bitcount % 8 == 0, "instruction size not multiple of 8 bitcount: {}".format(bitcount) + assert ( + bitcount % 8 == 0 + ), "instruction size not multiple of 8 bitcount: {}".format(bitcount) addr += bitcount // 8 bitcount = 0 else: @@ -130,28 +138,30 @@ def survey(iter): for i, line in enumerate(queue, start=1): try: - type = line['type'] - if type == 'instr': + type = line["type"] + if type == "instr": instr = [] - if type == 'instr': + if type == "instr": # strip label from opcode op = line[type].pop(0) instr.append(op[:2]) for part in line[type]: - observed = observe(part, line['addr'], map) + observed = observe(part, line["addr"], map) instr.append(observed) line[type] = instr yield subv.format(line) - elif type == 'segment': - yield line['raw'] + elif type == "segment": + yield line["raw"] except Exception as e: msg = "failed to survey line" - msg = msg + "\n{}:{}: {}".format('(unknown)', i, subv.format(line)) + msg = msg + "\n{}:{}: {}".format("(unknown)", i, subv.format(line)) msg = msg + "\nparsed as {}".format(subv.dump(line)) raise subv.SubVException(msg) from e -if __name__ == '__main__': + +if __name__ == "__main__": import sys + for line in survey(sys.stdin): print(line) |
