diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-29 12:44:04 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-05-29 12:45:24 +0000 |
| commit | 18870fe5ae2ad9295724c9d0de5624710ba327f8 (patch) | |
| tree | bbfe26245f9dd4b569dde4f5fcf4d71224373830 | |
| parent | switch bit.py, subv.py to doctest (diff) | |
| download | subv-18870fe5ae2ad9295724c9d0de5624710ba327f8.tar.gz subv-18870fe5ae2ad9295724c9d0de5624710ba327f8.zip | |
fix pack, survey and move to doctest
| -rwxr-xr-x | pack.py | 118 | ||||
| -rwxr-xr-x | survey.py | 176 |
2 files changed, 146 insertions, 148 deletions
@@ -1,11 +1,60 @@ #!/usr/bin/env python3 +""" pack.py +Packs bitfields tagged with their size into untagged bytes. + +>>> from io import StringIO +>>> # doctest: +REPORT_NDIFF +... print(subv.join_all(pack(StringIO(''' +... == code 0x80000000 +... 37/7 05/5 10010/20 +... 13/7 06/5 00/3 00/5 48/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... 13/7 06/5 00/3 00/5 65/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... 13/7 06/5 00/3 00/5 6c/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... 13/7 06/5 00/3 00/5 6c/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... 13/7 06/5 00/3 00/5 6f/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... 13/7 06/5 00/3 00/5 0a/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... 6f/7 00/5 ff/8 01/1 3e6/10 01/1 +... '''[1:-1])))) +== code 0x80000000 +b7 02 01 10 +13 03 80 04 +23 a0 62 00 +13 03 50 06 +23 a0 62 00 +13 03 c0 06 +23 a0 62 00 +13 03 c0 06 +23 a0 62 00 +13 03 f0 06 +23 a0 62 00 +13 03 a0 00 +23 a0 62 00 +6f f0 df fc +""" + import subv import bits def byteify(word): + """ split longer bitfield into bytes. + + >>> byteify((0x12345678, 32)) + [(120,), (86,), (52,), (18,)] + >>> byteify((0x4801813, 32)) + [(19,), (24,), (128,), (4,)] + >>> byteify((0x13, 9)), + Traceback (most recent call last): + ... + AssertionError: not byte aligned: 9 bits + """ (val, size) = word - if size % 8 != 0: - raise ValueError("Not byte aligned: {} bits".format(size)) + assert size % 8 == 0, "not byte aligned: {} bits".format(size) out = [] for i in range(0, size, 8): @@ -21,10 +70,8 @@ def pack(iter): fields = [bits.from_part(p) for p in line['instr']] total = bits.concat(*fields) - if segment == 'code' and total[1] != 32: - raise ValueError("instruction parts do not add up to 32 bit!") - - yield subv.format_instr(byteify(total)) + line['instr'] = byteify(total) + yield subv.format(line) else: if line['type'] == 'segment': segment = line['segment'][0] @@ -34,62 +81,3 @@ if __name__ == '__main__': import sys for line in pack(sys.stdin): print(line) - -import unittest -class TestPack(unittest.TestCase): - def test_byteify(self): - self.assertEqual( - byteify((0x12345678, 32)), - [(0x78,), (0x56,), (0x34,), (0x12,)] - ) - self.assertEqual( - byteify((0x4801813, 32)), - [(0x13,), (0x18,), (0x80,), (0x04,)] - ) - - def test_e2e(self): - from io import StringIO - from textwrap import dedent - inp = dedent('''\ - # do some things - == code 0x80000000 - 37/7 05/5 10010/20 - 13/7 06/5 00/3 00/5 48/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 65/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6c/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6c/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6f/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 0a/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 6f/7 00/5 ff/8 01/1 3e6/10 1/1 - ''') - - out = dedent('''\ - # do some things - == code 0x80000000 - b7 02 01 10 - 13 03 80 04 - 23 a0 62 00 - 13 03 50 06 - 23 a0 62 00 - 13 03 c0 06 - 23 a0 62 00 - 13 03 c0 06 - 23 a0 62 00 - 13 03 f0 06 - 23 a0 62 00 - 13 03 a0 00 - 23 a0 62 00 - 6f f0 df fc - ''') - - got = '' - for line in pack(StringIO(inp)): - got += line + '\n' - - self.assertEqual(got, out) @@ -1,4 +1,51 @@ #!/usr/bin/env python3 +""" survey.py +Resolves label references and substitutes them with literal values. + +>>> from io import StringIO +>>> # doctest: +REPORT_NDIFF +... print(subv.join_all(survey(StringIO(''' +... == code 0x80000000 +... main: +... # load 0x10010000 (UART0) into t0 +... 37/7 05/5 10010/20 +... # store 0x48 (H) in UART0+0 +... 13/7 06/5 00/3 00/5 48/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... # store 0x65 (e) in UART0+0 +... 13/7 06/5 00/3 00/5 65/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... # store 0x6c (l) in UART0+0 +... 13/7 06/5 00/3 00/5 6c/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... # store 0x6c (l) in UART0+0 +... 13/7 06/5 00/3 00/5 6c/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... # store 0x6f (o) in UART0+0 +... 13/7 06/5 00/3 00/5 6f/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... # store 0x0a (\\\\n) in UART0+0 +... 13/7 06/5 00/3 00/5 0a/12 +... 23/7 00/5 02/3 05/5 06/5 00/7 +... # jump back up to the top +... 6f/7 00/5 main[19:12]/off8 main[11:11]/off1 main[10:1]/off10 main[20:20]/off1 +... '''[1:-1])))) +== code 0x80000000 +37/7 05/5 10010/20 +13/7 06/5 00/3 00/5 48/12 +23/7 00/5 02/3 05/5 06/5 00/7 +13/7 06/5 00/3 00/5 65/12 +23/7 00/5 02/3 05/5 06/5 00/7 +13/7 06/5 00/3 00/5 6c/12 +23/7 00/5 02/3 05/5 06/5 00/7 +13/7 06/5 00/3 00/5 6c/12 +23/7 00/5 02/3 05/5 06/5 00/7 +13/7 06/5 00/3 00/5 6f/12 +23/7 00/5 02/3 05/5 06/5 00/7 +13/7 06/5 00/3 00/5 0a/12 +23/7 00/5 02/3 05/5 06/5 00/7 +6f/7 00/5 ff/8 01/1 3e6/10 01/1 +""" import subv import bits import re @@ -6,39 +53,46 @@ import re slice_re = re.compile(r'^([^\[]+)(?:\[(\d+):(\d+)\])?$') field_re = re.compile(r'^(imm|off)(\d+)$') -def scan(iter): - addr = None - map = {} - for line in iter: - if line['type'] == 'label': - map[line['label']] = line['addr'] - return map - def observe(part, rel_addr, map): - if subv.is_reference(part): - label, trim, tag = part - trim = int(trim) - - if label not in map: - raise ValueError("label not found: {} ({})".format(label, part)) - addr = map[label] - - mode, size, shift = tag_re.match(tag).groups() - size = int(size) - shift = int(shift) if shift else 0 - - if mode == 'imm': - addr = bits.u(addr, size) - else: - addr = bits.i(addr - rel_addr, size) - - part = bits.slice(addr, trim+shift-1, shift) - - return part + """ resolve a label reference. + + >>> observe((3, '3'), 0, {}) + (3, '3') + >>> observe(('label[31:0]', 'imm32'), 0, {'label': 1234}) + (1234, 32) + >>> observe(('label[1:0]', 'imm32'), 0, {'label': 1234}) + (2, 2) + >>> observe(('label[31:0]', 'off32'), 1000, {'label': 1234}) + (234, 32) + >>> observe(('label[31:12]', 'off32'), 1000, {'label': 1234}) + (0, 20) + >>> observe(('label[31:0]', 'off32'), 2000, {'label': 1000}) + (4294966296, 32) + + >>> observe(('label[32:0]', 'imm32'), 0, {}) + Traceback (most recent call last): + ... + AssertionError: undefined label 'label' + """ + if not subv.is_reference(part): + return part + + ref = subv.parse_reference(part) + assert ref['label'] in map, "undefined label '{}'".format(ref['label']) + addr = map[ref['label']] + + # @TODO: the hardcoded 32 here is not right, this is going to blow up + # in some circumstances (e.g. a backwards branch 2<<18 bytes away) + if ref['mode'] == 'imm': + addr = bits.u(addr, 32) + else: + addr = bits.i(addr - rel_addr, 32) + + return bits.slice(addr, ref['hi'], ref['lo']) def survey(iter): - # buffer in everything first, we need two passes queue = [] + map = {} addr = -1 for line in iter: line = subv.parse(line) @@ -48,24 +102,28 @@ def survey(iter): # step forward addr if line['type'] == 'segment': segment, addr = line['segment'] + elif line['type'] == 'label': + map[line['label']] = addr elif line['type'] == 'instr': bits = 0 for part in line['instr']: - bits += int(part[1]) - - if bits % 8 != 0: - raise ValueError("SubV lines should be byte aligned!") + if subv.is_reference(part): + ref = subv.parse_reference(part) + bits += ref['size'] + else: + bits += int(part[1]) + assert bits % 8 == 0, "line not byte-aligned" addr += bits // 8 - map = scan(queue) for line in queue: if line['type'] == 'instr': instr = [] for part in line['instr']: observed = observe(part, line['addr'], map) instr.append(observed) - yield subv.format_instr(instr) + line['instr'] = instr + yield subv.format(line) elif line['type'] == 'segment': yield line['raw'] @@ -73,51 +131,3 @@ if __name__ == '__main__': import sys for line in survey(sys.stdin): print(line) - -import unittest -class TestE2E(unittest.TestCase): - def test_e2e(self): - from io import StringIO - from textwrap import dedent - inp = dedent('''\ - == code 0x80000000 - main: - 37/7 05/5 10010/20 - 13/7 06/5 00/3 00/5 48/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 65/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6c/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6c/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6f/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 0a/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 6f/7 00/5 main/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20 - ''') - - out = dedent('''\ - == code 0x80000000 - 37/7 05/5 10010/20 - 13/7 06/5 00/3 00/5 48/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 65/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6c/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6c/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 6f/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 13/7 06/5 00/3 00/5 0a/12 - 23/7 00/5 02/3 05/5 06/5 00/7 - 6f/7 00/5 ff/8 01/1 3e6/10 01/1 - ''') - - got = '' - for line in survey(StringIO(inp)): - got += line + '\n' - - self.assertEqual(got, out) |
