diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-05-25 13:42:02 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-05-25 13:42:02 +0000 |
| commit | 12ca589a55376687c13d2298392ca5ddb344a219 (patch) | |
| tree | d53f03557db0072eeeb8c0433fbe5444be4510e7 | |
| parent | use new bits in format.py (diff) | |
| download | subv-12ca589a55376687c13d2298392ca5ddb344a219.tar.gz subv-12ca589a55376687c13d2298392ca5ddb344a219.zip | |
update all stages to work
| -rw-r--r-- | bits.py | 52 | ||||
| -rwxr-xr-x | format.py | 58 | ||||
| -rwxr-xr-x | pack.py | 4 | ||||
| -rw-r--r-- | subv.py | 5 | ||||
| -rwxr-xr-x | survey.py | 137 | ||||
| -rw-r--r-- | test.subv | 10 |
6 files changed, 131 insertions, 135 deletions
@@ -283,7 +283,7 @@ class LabelRef(WordBase): Bitfield(0xa, 8) """ if self.label not in labels: - raise ValueError("label '{}' unresolved".format(self.label)) + raise ValueError("undefined label '{}'".format(self.label)) value = labels[self.label] + self.offset @@ -404,3 +404,53 @@ 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 @@ -98,54 +98,6 @@ def sign_trunc(val, size): rest = val[size-2:0] return sign & rest -global_slice = slice -def default_slice(val, hi, lo): - """ add a default slice spec to labels if missing. - - >>> default_slice(('label', 'imm12'), 12, 1) - LabelRef('label', 0, ('imm', 12), 12:1) - >>> default_slice(('label-4', 'imm12'), 12, 1) - LabelRef('label', -4, ('imm', 12), 12:1) - >>> default_slice(('label', 'imm12', '[14:3]'), 12, 1) - LabelRef('label', 0, ('imm', 12), 14:3) - >>> default_slice(('label+4', 'imm12', '[14:3]'), 12, 1) - LabelRef('label', 4, ('imm', 12), 14:3) - >>> default_slice(('label', 'imm12', '[14:3]'), 12, 1) - LabelRef('label', 0, ('imm', 12), 14:3) - >>> default_slice(('label', 'imm12', '[31:0]'), 11, 0) - Traceback (most recent call last): - ... - AssertionError: expected 12 bit slice, got label/imm12/[31:0] - """ - if len(val) == 3: - label_offset, mode, slice = val - elif len(val) == 2: - label_offset, mode = val - slice = global_slice(hi, lo) - else: - raise ValueError("expected label reference") - - import re - - ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?$') - slice_re = re.compile(r'^\[(\d+):(\d+)]$') - - 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 = bits.LabelRef(label, offset, mode, slice) - - expected = hi - lo + 1 - assert expected == ref.size, ValueError("expected {} bit slice, got {}".format(expected, ref)) - return ref - def pack_u(instr): """ verify & pack U-type instructions. @@ -167,7 +119,7 @@ def pack_u(instr): op = bits.u(subv.untag(op), 7) rd = bits.u(subv.untag(rd, 'rd'), 5) if subv.is_reference(imm): - imm = default_slice(imm, 31, 12) + imm = bits.ref(imm, slice(31, 12)) else: imm = bits.i(subv.untag(imm, 'imm20'), 20) @@ -188,7 +140,7 @@ def pack_i(instr): rd = bits.u(subv.untag(rd, 'rd'), 5) rs = bits.u(subv.untag(rs, 'rs'), 5) if subv.is_reference(imm): - imm = default_slice(imm, 11, 0) + imm = bits.ref(imm, slice(11, 0)) else: imm = bits.i(subv.untag(imm, 'imm12'), 12) @@ -210,7 +162,7 @@ def pack_s(instr): rs2 = bits.u(subv.untag(rs2, 'rs'), 5) if subv.is_reference(imm): - imm = default_slice(imm, 11, 0) + imm = bits.ref(imm, slice(11, 0)) else: imm = bits.i(subv.untag(imm, 'off12'), 12) @@ -235,7 +187,7 @@ def pack_j(instr): rd = bits.u(subv.untag(rd, 'rd'), 5) if subv.is_reference(imm): - imm = default_slice(imm, 20, 1) + imm = bits.ref(imm, slice(20, 1)) else: imm = bits.i(subv.untag(imm, 'off20'), 20) @@ -265,7 +217,7 @@ def pack_b(instr): rs2 = bits.u(subv.untag(rs2, 'rs'), 5) if subv.is_reference(imm): - imm = default_slice(imm, 12, 1) + imm = bits.ref(imm, slice(12, 1)) else: imm = bits.i(subv.untag(imm, 'off12'), 12) @@ -19,7 +19,7 @@ Packs bitfields tagged with their size into untagged bytes. ... 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 +... 6f/7 00/5 ff/8 1/1 3e6/10 1/1 ... '''[1:-1])))) == code 0x80000000 b7 02 01 10 @@ -51,7 +51,7 @@ def byteify(word): >>> byteify(bits.Bitfield(0x13, 9)), Traceback (most recent call last): ... - AssertionError: not byte aligned: 0x13'9 + AssertionError: not byte aligned: 013/9 """ assert word.size % 8 == 0, "not byte aligned: {}".format(word) @@ -368,11 +368,12 @@ class LineIterator(object): return (self.segment, self.line) def exception(self, msg): + stream_name = getattr(self.stream, 'name', '(unnamed)') if self.line: - msg = msg + "\n{}:{}: {}".format(self.stream.name, self.i, format(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(self.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): @@ -10,25 +10,25 @@ Resolves label references and substitutes them with literal values. ... # 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 +... 13/7 06/5 0/3 00/5 048/12 +... 23/7 00/5 2/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 +... 13/7 06/5 0/3 00/5 065/12 +... 23/7 00/5 2/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 +... 13/7 06/5 0/3 00/5 06c/12 +... 23/7 00/5 2/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 +... 13/7 06/5 0/3 00/5 06c/12 +... 23/7 00/5 2/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 +... 13/7 06/5 0/3 00/5 06f/12 +... 23/7 00/5 2/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 +... 13/7 06/5 0/3 00/5 00a/12 +... 23/7 00/5 2/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 +... 6f/7 00/5 main/off21/[19:12] main/off21/[11:11] main/off21/[10:1] main/off21/[20:20] ... '''[1:-1])))) == code 0x80000000 37/7 05/5 10010/20 @@ -44,7 +44,7 @@ Resolves label references and substitutes them with literal values. 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 +6f/7 00/5 ff/8 1/1 3e6/10 1/1 """ import subv import bits @@ -53,57 +53,44 @@ import re slice_re = re.compile(r'^([^\[]+)(?:\[(\d+):(\d+)\])?$') field_re = re.compile(r'^(imm|off)(\d+)$') -def observe(part, rel_addr, map): +def observe(word, pc, map): """ resolve a label reference. >>> observe((3, '3'), 0, {}) (3, '3') - >>> observe(('label[31:0]', 'imm32'), 0, {'label': 0x1234}) + >>> observe(('label', 'imm32', '[31:0]'), 0, {'label': 0x1234}) Bitfield(0x1234, 32) - >>> observe(('label-2[31:0]', 'imm32'), 0, {'label': 0x1234}) + >>> observe(('label-2', 'imm32', '[31:0]'), 0, {'label': 0x1234}) Bitfield(0x1232, 32) - >>> observe(('label[1:0]', 'imm32'), 0, {'label': 0x1236}) + >>> observe(('label', 'imm32', '[1:0]'), 0, {'label': 0x1236}) Bitfield(0x2, 2) - >>> observe(('label[31:0]', 'off32'), 0x1000, {'label': 0x1234}) + >>> observe(('label', 'off32', '[31:0]'), 0x1000, {'label': 0x1234}) Bitfield(0x234, 32) - >>> observe(('label[31:12]', 'off32'), 0x1000, {'label': 0x10fff}) + >>> observe(('label', 'off32', '[31:12]'), 0x1000, {'label': 0x10fff}) Bitfield(0xf, 20) - >>> observe(('label[31:0]', 'off32'), 0x2000, {'label': 0x1000}) + >>> observe(('label', 'off32', '[31:0]'), 0x2000, {'label': 0x1000}) Bitfield(0xfffff000, 32) - >>> observe(('label+4[7:0]', 'off32'), 0x1000, {'label': 0x1234}) + >>> observe(('label+4', 'off32', '[7:0]'), 0x1000, {'label': 0x1234}) Bitfield(0x38, 8) - >>> observe(('label+4[15:8]', 'off32'), 0x1000, {'label': 0x1234}) + >>> observe(('label+4', 'off32', '[15:8]'), 0x1000, {'label': 0x1234}) Bitfield(0x2, 8) - >>> observe(('label[32:0]', 'imm32'), 0, {}) + >>> observe(('label', 'imm32', '[32:0]'), 0, {}) Traceback (most recent call last): ... - AssertionError: undefined label 'label' + ValueError: undefined label 'label' """ - if not subv.is_reference(part): - return part + if not subv.is_reference(word): + return word - ref = subv.parse_reference(part) - assert ref['label'] in map, "undefined label '{}'".format(ref['label']) - addr = map[ref['label']] - - if 'offset' in ref: - addr += ref['offset'] - - # @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 addr[ref['hi']:ref['lo']] + word = bits.ref(word) + return word.as_value(pc=pc, labels=map) @subv.with_parsed_lines def survey(iter): queue = [] map = {} - addr, bits = -1, 0 + addr, bitcount = -1, 0 for segment, line in iter: line['addr'] = addr queue.append(line) @@ -111,52 +98,58 @@ def survey(iter): # step forward addr type = line['type'] if type == 'segment': - addr, bits = line['segment'][1], 0 + addr, bitcount = line['segment'][1], 0 elif type == 'label': - assert bits == 0, "label isn't byte aligned" + assert bitcount == 0, "label isn't byte aligned" map[line['label']] = addr elif type == 'instr': if segment == 'data': for part in line['instr']: - bits += int(part[1]) + bitcount += int(part[1]) - if bits >= 8: - addr += bits // 8 - bits = bits % 8 + if bitcount >= 8: + addr += bitcount // 8 + bitcount = bitcount % 8 elif segment == 'code': - assert bits == 0, "instruction isn't byte aligned ({} bits left)".format(8 - bits) + assert bitcount == 0, "instruction isn't byte aligned ({} bitcount left)".format(8 - bitcount) assert addr % 2 == 0, "instruction isn't 2-byte aligned" - bits = 0 + bitcount = 0 for part in line['instr']: if subv.is_reference(part): - ref = subv.parse_reference(part) - bits += ref['size'] + ref = bits.ref(part) + bitcount += ref.size else: - bits += int(part[1]) + bitcount += int(part[1]) - assert bits % 8 == 0, "instruction size not multiple of 8 bits: {}".format(bits) - addr += bits // 8 - bits = 0 + assert bitcount % 8 == 0, "instruction size not multiple of 8 bitcount: {}".format(bitcount) + addr += bitcount // 8 + bitcount = 0 else: raise ValueError("unknown segment '{}'".format(segment)) - for line in queue: - type = line['type'] - if type == 'instr': - instr = [] + for i, line in enumerate(queue, start=1): + try: + type = line['type'] 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) - instr.append(observed) - line[type] = instr - yield subv.format(line) - elif type == 'segment': - yield line['raw'] + 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) + instr.append(observed) + line[type] = instr + yield subv.format(line) + 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 + "\nparsed as {}".format(subv.dump(line)) + raise subv.SubVException(msg) from e if __name__ == '__main__': import sys @@ -5,25 +5,25 @@ main: # x4 = &message # . load high bits - 37/lui 4/rd/x4 Message/imm20 + 37/lui 4/rd/x4 Message/imm # . add low bits - 13/opi 0/subop/add 4/rd/x4 4/rs/x4 Message/imm12 + 13/opi 0/subop/add 4/rd/x4 4/rs/x4 Message/imm 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/off12 + 63/branch 0/subop/== 6/rs/t1 0/rs/x0 print:break/off13 # 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/off20 + 6f/jal 0/rd/x0 print:loop/off21 print:break: loop: # infinite loop - 6f/jal 0/rd/x0 loop/off20 + 6f/jal 0/rd/x0 loop/off21 == data 0x80001000 Message: |
