diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-29 10:41:25 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-05-29 11:49:42 +0000 |
| commit | 212ea358c73cd5f3c70d143987472327cf15f653 (patch) | |
| tree | b8ee641d14ae68c5558341a58e6a825930c7b83d | |
| parent | fix permissions (diff) | |
| download | subv-212ea358c73cd5f3c70d143987472327cf15f653.tar.gz subv-212ea358c73cd5f3c70d143987472327cf15f653.zip | |
new label-slice syntax in format.py; switch to doctest
| -rwxr-xr-x | format.py | 369 | ||||
| -rw-r--r-- | subv.py | 38 | ||||
| -rwxr-xr-x | survey.py | 3 | ||||
| -rw-r--r-- | test.subv | 10 |
4 files changed, 251 insertions, 169 deletions
@@ -1,4 +1,59 @@ #!/usr/bin/env python3 +""" +>>> from io import StringIO +>>> # doctest: +REPORT_NDIFF +... print(subv.join_all(format(StringIO(''' +... == code 0x80000000 +... main: +... # load 0x10010000 (UART0) into t0 +... 37/lui 5/rd/t0 0x10010/imm20 +... # store 0x48 (H) in UART0+0 +... 13/opi 0/subop/add 6/rd/t1 0/rs/x0 48/imm12 +... 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 +... # store 0x65 (e) in UART0+0 +... 13/opi 0/subop/add 6/rd/t1 0/rs/x0 65/imm12 +... 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 +... # store 0x6c (l) in UART0+0 +... 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12 +... 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 +... # store 0x6c (l) in UART0+0 +... 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12 +... 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 +... # store 0x6f (o) in UART0+0 +... 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6f/imm12 +... 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 +... # store 0x0a (\\\\n) in UART0+0 +... 13/opi 0/subop/add 6/rd/t1 0/rs/x0 0a/imm12 +... 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 +... # jump back up to the top +... 6f/jal 0/rd/x0 main/off20 +... '''[1:-1])))) +== 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 +""" + import subv import bits @@ -14,85 +69,201 @@ instr_map = { 'jal': ('j', 0x6f), } +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'])) + + return { + **ref, + 'hi': hi, + 'lo': lo, + 'size': hi - lo + 1, + } + +def default_slice(val, hi, lo): + """ ... + >>> default_slice(('label', 'imm12'), 12, 1) + ('label[12:1]', 'imm12') + >>> default_slice(('label[14:3]', 'imm12'), 12, 1) + ('label[14:3]', 'imm12') + >>> default_slice(('label[14:3]', 'imm12', 'extra'), 12, 1) + ('label[14:3]', 'imm12', 'extra') + >>> default_slice(('label[31:0]', 'imm12'), 11, 0) + Traceback (most recent call last): + ... + AssertionError: slice doesnt match metadata + """ + parsed = subv.parse_reference(val) + if 'hi' not in parsed: + parsed['hi'] = hi + parsed['lo'] = lo + + p_size = parsed['hi'] - parsed['lo'] + 1 + assert parsed['size'] == p_size, "slice doesnt match metadata" + + ref = '{label}[{hi}:{lo}]'.format(**parsed) + return (ref, *val[1:]) + +def slice_bit_or_ref(val, hi, lo): + """ ... + >>> slice_bit_or_ref(('label[31:0]', 'imm32'), 12, 1) + ('label[12:1]', 'imm12') + >>> slice_bit_or_ref(('label[31:0]', 'off32'), 11, 0) + ('label[11:0]', 'off12') + >>> slice_bit_or_ref(('label[31:0]', 'off32'), 31, 12) + ('label[31:12]', 'off20') + >>> slice_bit_or_ref(('label[11:0]', 'imm12', 'extra'), 15, 4) + Traceback (most recent call last): + ... + AssertionError: cant slice outside of bounds + >>> slice_bit_or_ref(('label', 'imm12'), 12, 1) + Traceback (most recent call last): + ... + AssertionError: cant slice label w/o bounds + """ + if not isinstance(val[0], str): + return bits.slice(val, hi, lo) + + ref = subv.parse_reference(val) + assert 'hi' in ref, "cant slice label w/o bounds" + + sz = hi - lo + lo = ref['lo'] + lo + hi = lo + sz + assert hi <= ref['hi'], "cant slice outside of bounds" + + ref['hi'] = hi + ref['lo'] = lo + ref['size'] = sz + 1 + main = '{label}[{hi}:{lo}]'.format(**ref) + field = '{mode}{size}'.format(**ref) + return (main, field, *val[2:]) + def pack_u(instr): + """ verify & pack U-type instructions. + + >>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')]) + [(55, 7), (5, 5), (65552, 20)] + + >>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')]) + [(55, 7), (5, 5), ('pos[31:12]', 'imm20')] + + >>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos[19:0]', 'imm20')]) + [(55, 7), (5, 5), ('pos[19:0]', 'imm20')] + + >>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos[31:0]', 'imm20')]) + Traceback (most recent call last): + ... + AssertionError: slice doesnt match metadata + """ (op, rd, imm) = instr op = bits.u(subv.untag(op), 7) rd = bits.u(subv.untag(rd, 'rd'), 5) - if not subv.is_lref(imm): + if subv.is_lref(imm): + imm = default_slice(imm, 31, 12) + else: imm = bits.i(subv.untag(imm, 'imm20'), 20) return [op, rd, imm] def pack_i(instr): + """ verify & pack I-type instructions. + >>> pack_i([(0x13, 'opi'), (0, 'subop', 'add'), (6, 'rd', 't1'), (0, 'rs', 'x0'), (101, 'imm12')]) + [(19, 7), (6, 5), (0, 3), (0, 5), (101, 12)] + + >>> pack_i([(0x13, 'opi'), (0, 'subop', 'add'), (6, 'rd', 't1'), (0, 'rs', 'x0'), ('label', 'imm12')]) + [(19, 7), (6, 5), (0, 3), (0, 5), ('label[11:0]', 'imm12')] + """ (op, sub, rd, rs, imm) = instr 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 not subv.is_lref(imm): + if subv.is_lref(imm): + imm = default_slice(imm, 11, 0) + else: imm = bits.i(subv.untag(imm, 'imm12'), 12) return [op, rd, sub, rs, imm] def pack_s(instr): + """ + >>> pack_s([(0x23, 'store'), (2, 'subop', 'word'), (5, 'rs', 't0'), (6, 'rs', 't1'), (0, 'off12')]) + [(35, 7), (0, 5), (2, 3), (5, 5), (6, 5), (0, 7)] + + >>> pack_s([(0x23, 'store'), (2, 'subop', 'word'), (5, 'rs', 't0'), (6, 'rs', 't1'), ('home', 'off12')]) + [(35, 7), ('home[4:0]', 'off5'), (2, 3), (5, 5), (6, 5), ('home[11:5]', 'off7')] + """ (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) - if not subv.is_lref(imm): - imm = bits.i(subv.untag(imm, 'off12'), 12) - imm_lo = bits.slice(imm, 4, 0) - imm_hi = bits.slice(imm, 11, 5) + + if subv.is_lref(imm): + imm = default_slice(imm, 11, 0) else: - label = subv.untag(imm, 'off12') - imm_lo = (label, 5, 'off12') - imm_hi = (label, 7, 'off12>>5') + imm = bits.i(subv.untag(imm, 'off12'), 12) + + imm_lo = slice_bit_or_ref(imm, 4, 0) # + ('off[4:0]',) + imm_hi = slice_bit_or_ref(imm, 11, 5) # + ('off[11:5]',) return [op, imm_lo, sub, rs1, rs2, imm_hi] def pack_j(instr): + """ + >>> pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (0, 'off20')]) + [(111, 7), (0, 5), (0, 8), (0, 1), (0, 10), (0, 1)] + + >>> pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (-2, 'off20')]) + [(111, 7), (0, 5), (255, 8), (1, 1), (1022, 10), (1, 1)] + + >>> pack_j([(0x6f, 'jal'), (2, 'rd', 'x2'), ('home', 'off20')]) + [(111, 7), (2, 5), ('home[19:12]', 'off8'), ('home[11:11]', 'off1'), ('home[10:1]', 'off10'), ('home[20:20]', 'off1')] + """ (op, rd, imm) = instr op = bits.u(subv.untag(op), 7) rd = bits.u(subv.untag(rd, 'rd'), 5) - if not subv.is_lref(imm): - imm = bits.i(subv.untag(imm, 'off21'), 21) - if imm & 0b1 == 1: - raise ArgumentError("J-type offsets have to be halfword-aligned") - imm_lo = bits.slice(imm, 10, 1) - imm_11 = bits.slice(imm, 11, 11) - imm_hi = bits.slice(imm, 19, 12) - imm_20 = bits.slice(imm, 20, 20) + if subv.is_lref(imm): + imm = default_slice(imm, 20, 1) else: - label = subv.untag(imm, 'off21') - imm_lo = (label, 10, 'off21>>1') - imm_11 = (label, 1, 'off21>>11') - imm_hi = (label, 8, 'off21>>12') - imm_20 = (label, 1, 'off21>>20') + imm = bits.i(subv.untag(imm, 'off20'), 20) + + imm_lo = slice_bit_or_ref(imm, 9, 0) # + ('off[10:1]',) + imm_11 = slice_bit_or_ref(imm, 10, 10) # + ('off[11]',) + imm_hi = slice_bit_or_ref(imm, 18, 11) # + ('off[19:12]',) + imm_20 = slice_bit_or_ref(imm, 19, 19) # + ('off[20]',) return [op, rd, imm_hi, imm_11, imm_lo, imm_20] def pack_b(instr): + """ + >>> pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), (0, 'off12')]) + [(99, 7), (0, 1), (0, 4), (0, 3), (6, 5), (0, 5), (0, 6), (0, 1)] + + >>> pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), (-2, 'off12')]) + [(99, 7), (1, 1), (14, 4), (0, 3), (6, 5), (0, 5), (63, 6), (1, 1)] + + >>> pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), ('home', 'off12')]) + [(99, 7), ('home[11:11]', 'off1'), ('home[4:1]', 'off4'), (0, 3), (6, 5), (0, 5), ('home[10:5]', 'off6'), ('home[12:12]', 'off1')] + """ (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) - if not subv.is_lref(imm): - imm = bits.i(subv.untag(imm, 'off13'), 13) - if imm & 0b1 == 1: - raise ArgumentError("B-type offsets have to be halfword-aligned") - imm_lo = bits.slice(imm, 4, 1) - imm_md = bits.slice(imm, 10, 5) - imm_11 = bits.slice(imm, 11, 11) - imm_12 = bits.lice(imm, 12, 12) + + if subv.is_lref(imm): + imm = default_slice(imm, 12, 1) else: - label = subv.untag(imm, 'off13') - imm_lo = (label, 4, 'off13>>1') - imm_md = (label, 6, 'off13>>5') - imm_11 = (label, 1, 'off13>>11') - imm_12 = (label, 1, 'off13>>12') + imm = bits.i(subv.untag(imm, 'off12'), 12) + imm_lo = slice_bit_or_ref(imm, 3, 0) # + ('off[4:1]',) + imm_md = slice_bit_or_ref(imm, 9, 4) # + ('off[10:5]',) + imm_11 = slice_bit_or_ref(imm, 10, 10) # + ('off[11]',) + imm_12 = slice_bit_or_ref(imm, 11, 11) # + ('off[12]',) return [op, imm_11, imm_lo, sub, rs1, rs2, imm_md, imm_12] def format(iter): @@ -103,8 +274,7 @@ def format(iter): if segment == 'code' and line['type'] == 'instr': instr = line['instr'] op = instr[0] - if len(op) != 2: - raise ValueError("instruction without op label") + assert len(op) == 2, 'instruction without op label: {}'.format(instr) (op, label) = op if label not in instr_map: @@ -137,128 +307,3 @@ if __name__ == '__main__': import sys for line in format(sys.stdin): print(line) - -import unittest -class TestPackers(unittest.TestCase): - def test_pack_u(self): - final = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')]) - self.assertEqual( - final, - [(0x37, 7), (0x5, 5), (0x10010, 20)] - ) - self.assertEqual(bits.concat(*final)[1], 32) - - label = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')]) - self.assertEqual( - label, - [(0x37, 7), (0x5, 5), ('pos', 'imm20')] - ) - - def test_pack_i(self): - final = pack_i([ - (0x13, 'opi'), - (0, 'subop', 'add'), - (6, 'rd', 't1'), - (0, 'rs', 'x0'), - (0x65, 'imm12'), - ]) - self.assertEqual( - final, - [(0x13, 7), (6, 5), (0, 3), (0, 5), (0x65, 12)] - ) - self.assertEqual(bits.concat(*final)[1], 32) - - def test_pack_s(self): - final = pack_s([ - (0x23, 'store'), - (2, 'subop', 'word'), - (5, 'rs', 't0'), - (6, 'rs', 't1'), - (0, 'off12'), - ]) - self.assertEqual( - final, - [(0x23, 7), (0, 5), (2, 3), (5, 5), (6, 5), (0, 7)] - ) - self.assertEqual(bits.concat(*final)[1], 32) - - label = pack_s([ - (0x23, 'store'), - (2, 'subop', 'word'), - (5, 'rs', 't0'), - (6, 'rs', 't1'), - ('home', 'off12'), - ]) - self.assertEqual(label, [ - (0x23, 7), - ('home', 5, 'off12'), - (2, 3), - (5, 5), - (6, 5), - ('home', 7, 'off12>>5'), - ]) - -class TestE2E(unittest.TestCase): - def test_e2e(self): - from io import StringIO - from textwrap import dedent - inp = dedent('''\ - == code 0x80000000 - main: - # load 0x10010000 (UART0) into t0 - 37/lui 5/rd/t0 0x10010/imm20 - # store 0x48 (H) in UART0+0 - 13/opi 0/subop/add 6/rd/t1 0/rs/x0 48/imm12 - 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 - # store 0x65 (e) in UART0+0 - 13/opi 0/subop/add 6/rd/t1 0/rs/x0 65/imm12 - 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 - # store 0x6c (l) in UART0+0 - 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12 - 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 - # store 0x6c (l) in UART0+0 - 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12 - 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 - # store 0x6f (o) in UART0+0 - 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6f/imm12 - 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 - # store 0x0a (\\n) in UART0+0 - 13/opi 0/subop/add 6/rd/t1 0/rs/x0 0a/imm12 - 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 - # jump back up to the top - 6f/jal 0/rd/x0 main/off21 - ''') - - out = dedent('''\ - == 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/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20 - ''') - - got = '' - for line in format(StringIO(inp)): - got += line + '\n' - - self.maxDiff = None - self.assertEqual(got, out) @@ -1,6 +1,8 @@ import re white = re.compile('[ \t\.\n]+') hex = re.compile(r'^\-?(0x)?[0-9a-f]+$') +slice_re = re.compile(r'^([^\[]+)(?:\[(\d+):(\d+)\])?$') +field_re = re.compile(r'^(imm|off)(\d+)$') # parsing @@ -22,6 +24,26 @@ def parse_segment(line): def parse_label(line): return line[:-1] +def parse_reference(part): + ref = part[0] + field = part[1] + + label, hi, lo = slice_re.match(ref).groups() + mode, size = field_re.match(field).groups() + + ref = { + 'label': label, + 'mode': mode, + 'size': int(size), + 'meta': part[2:], + } + + if hi is not None: + ref['hi'] = int(hi) + ref['lo'] = int(lo) + + return ref + def classify(line): if line == '': return 'empty' @@ -71,7 +93,7 @@ def untag(part, expect=None): def format_part(part): if not is_lref(part): first = '{:02x}'.format(part[0]) - part = (first,) + part[1:] + part = (first, *part[1:]) return '/'.join([str(p) for p in part]) def format_instr(inst, comment=None): @@ -80,6 +102,10 @@ def format_instr(inst, comment=None): packed = packed + ' # ' + comment return packed +def join_all(gen): + res = '\n'.join(gen) + return res + import unittest class TestParsing(unittest.TestCase): def test_parse_part(self): @@ -101,6 +127,16 @@ class TestParsing(unittest.TestCase): self.assertEqual(parse_part('label:suff/tag*'), ('label:suff', 'tag*')) self.assertEqual(parse_part('$label:suff/tag*'), ('$label:suff', 'tag*')) + def test_parse_reference(self): + self.assertEqual( + parse_reference(('lbl', 'imm32')), + { 'label': 'lbl', 'hi': None, 'lo': None, 'mode': 'imm', 'size': 32, 'meta': () } + ) + self.assertEqual( + parse_reference(('x[11:0]', 'off12')), + { 'label': 'x', 'hi': 11, 'lo': 0, 'mode': 'off', 'size': 12, 'meta': () } + ) + class TestChecks(unittest.TestCase): def test_is_lref(self): self.assertTrue(is_lref(('hello',))) @@ -3,7 +3,8 @@ import subv import bits import re -tag_re = re.compile(r'^(imm|off)(\d+)(?:>>(\d+))?$') +slice_re = re.compile(r'^([^\[]+)(?:\[(\d+):(\d+)\])?$') +field_re = re.compile(r'^(imm|off)(\d+)$') def scan(iter): addr = None @@ -5,25 +5,25 @@ main: # x4 = &message # . load high bits - 37/lui 4/rd/x4 Message/20/imm32>>12 + 37/lui 4/rd/x4 Message/imm20 # . add low bits - 13/opi 0/subop/add 4/rd/x4 4/rs/x4 Message/12/imm32 + 13/opi 0/subop/add 4/rd/x4 4/rs/x4 Message/imm12 print:loop: # load unsigned byte at x4 03/load 4/subop/lbu 6/rd/t1 4/rs/x4 0/imm12 # break loop if zero - 63/branch 0/subop/== 6/rs/t1 0/rs/x0 print:break/off13 + 63/branch 0/subop/== 6/rs/t1 0/rs/x0 print:break/off12 # print char 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12 # increment x4 13/opi 0/subop/add 4/rd/x4 4/rs/x4 1/imm12 # jump back up - 6f/jal 0/rd/x0 print:loop/off21 + 6f/jal 0/rd/x0 print:loop/off20 print:break: loop: # infinite loop - 6f/jal 0/rd/x0 loop/off21 + 6f/jal 0/rd/x0 loop/off20 == data 0x80001000 Message: |
