diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-28 16:28:07 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-05-28 16:28:07 +0000 |
| commit | d298144e6a5d6a0c52338100b73eadf4b33c5f09 (patch) | |
| tree | 8b595d54677bfdcf58f0ec606f25c154712d6d71 | |
| parent | Add ex.* (diff) | |
| download | subv-d298144e6a5d6a0c52338100b73eadf4b33c5f09.tar.gz subv-d298144e6a5d6a0c52338100b73eadf4b33c5f09.zip | |
cleanup
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | bits.py | 80 | ||||
| -rwxr-xr-x | elf.py | 168 | ||||
| -rw-r--r-- | ex.format | 6 | ||||
| -rw-r--r-- | format.py | 113 | ||||
| -rw-r--r-- | pack.py | 76 | ||||
| -rw-r--r-- | riscv.py | 203 | ||||
| -rw-r--r-- | subv.py (renamed from subx.py) | 61 | ||||
| -rw-r--r-- | survey.py | 47 | ||||
| -rwxr-xr-x | test.py | 22 | ||||
| -rw-r--r-- | test_riscv.py | 3 |
11 files changed, 341 insertions, 445 deletions
@@ -46,17 +46,22 @@ and now front to back with a little example: `ex.format`: arguments sliced and diced and put into the ISA order. == code 0x80000000 + # repeatedly print "Hi\n" 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 0x69 (i) in UART0+0 13/7 06/5 00/3 00/5 69/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 - `ex.survey`: labels resolved, ready to be packed into hex format == code 0x80000000 @@ -0,0 +1,80 @@ +def u(num, bits): + if num < 0: + raise ValueError("negative value not allowed: {}".format(num)) + + if num.bit_length() > bits: + raise ValueError("value too large for u{} field: {} ({} bits)", + bits, num, num.bit_length()) + + return (num, bits) + +def i(num, bits): + if num < 0: + num = (1 << bits) + num + + return u(num, bits) + +def from_part(part): + val, size = part + return (val, int(size)) + +def concat(*parts): + val, size = 0, 0 + for (pval, psize) in parts: + val = val | pval << size + size += psize + return (val, size) + +def slice(bits, top, bottom): + (val, size) = bits + + if top < bottom: + raise ValueError("cant slice reverse range") + elif bottom < 0: + raise ValueError("negative slice index") + elif top >= size: + raise ValueError("cant slice [{}:{}] from {} bit value".format(top, bottom, size)) + + width = top - bottom + 1 + val = (val >> bottom) & ((1 << width) - 1) + return (val, width) + +import unittest +class TestHelpers(unittest.TestCase): + def test_concat(self): + self.assertEqual( + concat((0b10, 2), (0b00, 2)), + (0b0010, 4) + ) + self.assertEqual( + concat((0b1, 1), (0b0110, 4), (0b110, 3)), + (0b11001101, 8) + ) + + def test_slice(self): + self.assertEqual( + slice((0x7f, 8), 7, 4), + (0x7, 4) + ) + self.assertEqual( + slice((0b100, 3), 2, 2), + (0b1, 1) + ) + with self.assertRaises(ValueError): + slice((0xf, 4), 4, 0) + with self.assertRaises(ValueError): + slice((0xf, 4), 2, 3) + with self.assertRaises(ValueError): + slice((0xf, 4), 3, -1) + self.assertEqual( + slice((0x12345678, 32), 7, 0), + (0x78, 8) + ) + self.assertEqual( + slice((0x12345678, 32), 15, 8), + (0x56, 8) + ) + self.assertEqual( + slice((0x12345678, 32), 23, 16), + (0x34, 8) + ) @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import sys -from subx import clean, classify, parse_segment, parse_instr +import subv """ Takes a packed & surveyed hex stream (with section headers) and packs it into a @@ -18,86 +18,92 @@ See full documentation here: https://sifive.cdn.prismic.io/sifive/4d063bf8-3ae6-4db6-9843-ee9076ebadf7_fe310-g000.pdf """ -segments = [] -segment = None -for line in sys.stdin: - line = clean(line) - type = classify(line) - - if type == 'segment': - (name, addr) = parse_segment(line) - segment = { 'name': name, 'addr': addr, 'content': [] } - segments.append(segment) - elif type == 'instr': - if segment == None: - raise ValueError("label or code outside of segment!") - - instr = parse_instr(line) - segment['content'] += instr - else: - raise ValueError("elf input should contain only segments and instructions!") -entrypoint = 0x80000000 -cursor = 0 - def w(b): - global cursor - cursor += len(b) - sys.stdout.buffer.write(b) + sys.stdout.buffer.write(b) + return len(b) def wi(num, size=2): - global cursor - cursor += size - sys.stdout.buffer.write(num.to_bytes(size, byteorder='little')) - -def padto(offset): - missing = offset - cursor - if missing < 0: - raise ValueError("already past offset!") - w(b'\x00' * missing) - -def write_elf_header(): - w(b"\x7fELF") # ELF magic - wi(0x010101, 4) # 32bit, little endian - wi(0, 8) # reserved - wi(0x02) # e_type - wi(0xf3) # e_machine - wi(0x01, 4) # e_version - wi(entrypoint, 4) # e_entry - wi(0x34, 4) # e_phoff (Program Header offset) - wi(0x00, 4) # e_shoff (Section Header offset, unused) - wi(0x0004, 4) # e_flags - wi(0x34) # e_ehsize - wi(0x20) # e_phentsize - wi(len(segments)) # e_phnum - wi(0x28) # e_shentsize - wi(0x0) # e_shnum - wi(0x0) # e_shstrndx - -def write_program_header(segment): - align = 0x1000 - offset = (1 + cursor // align) * align - start = segment['addr'] - size = len(segment['content']) - segment['offset'] = offset - - if offset % align != start % align: - print("{:02x} {:02x}".format(offset, offset % align)) - print("{:02x} {:02x}".format(start, start % align)) - raise ValueError("improper alignment") - - wi(0x1, 4) # p_type - wi(offset, 4) # p_offset - wi(start, 4) # p_vaddr - wi(start, 4) # p_paddr - wi(size, 4) # p_filesz - wi(size, 4) # p_memsz - wi(0x5, 4) # p_flags, 0x5=rx, 0x6=rw - wi(align, 4) # p_align - -write_elf_header() -for seg in segments: - write_program_header(seg) -for seg in segments: - padto(seg['offset']) - for part in seg['content']: - wi(part[0], 1) + sys.stdout.buffer.write(num.to_bytes(size, byteorder='little')) + return size + +def padto(offset, cursor): + missing = offset - cursor + if missing < 0: + raise ValueError("already past offset!") + 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 + return c + +def write_program_header(segment, c): + align = 0x1000 + offset = (1 + c // align) * align + start = segment['addr'] + size = len(segment['content']) + segment['offset'] = offset + + 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(start, 4) # p_vaddr + c += wi(start, 4) # p_paddr + c += wi(size, 4) # p_filesz + c += wi(size, 4) # p_memsz + c += wi(0x5, 4) # p_flags, 0x5=rx, 0x6=rw + c += wi(align, 4) # p_align + return c + +if __name__ == "__main__": + segments = [] + segment = None + for line in sys.stdin: + line = subv.parse(line) + + if line['type'] == 'segment': + (name, addr) = line['segment'] + segment = { 'name': name, 'addr': addr, 'content': [] } + segments.append(segment) + elif line['type'] == 'instr': + if segment == None: + raise ValueError("label or code outside of segment!") + + segment['content'] += line['instr'] + else: + raise ValueError("elf input should contain only segments and instructions!") + + cursor = 0 + + c = write_elf_header(segments) + for seg in segments: + c = write_program_header(seg, c) + + for seg in segments: + c = padto(seg['offset'], c) + for part in seg['content']: + c += wi(part[0], 1) @@ -1,10 +1,16 @@ == code 0x80000000 +# repeatedly print "Hi\n" 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 0x69 (i) in UART0+0 13/7 06/5 00/3 00/5 69/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 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from subx import clean, classify, parse_segment, parse_instr, is_lref, untag, format_instr -from riscv import u, i, bit_concat, bit_slice +import subv +import bits instr_map = { 'opr': ('r', 0x33), @@ -16,55 +16,55 @@ instr_map = { def pack_u(instr): (op, rd, imm) = instr - op = u(untag(op), 7) - rd = u(untag(rd, 'rd'), 5) - if not is_lref(imm): - imm = i(untag(imm, 'imm20'), 20) + 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, 'imm20'), 20) return [op, rd, imm] def pack_i(instr): (op, sub, rd, rs, imm) = instr - op = u(untag(op), 7) - sub = u(untag(sub, 'subop'), 3) - rd = u(untag(rd, 'rd'), 5) - rs = u(untag(rs, 'rs'), 5) - if not is_lref(imm): - imm = i(untag(imm, 'imm12'), 12) + 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): + imm = bits.i(subv.untag(imm, 'imm12'), 12) return [op, rd, sub, rs, imm] def pack_s(instr): (op, sub, rs1, rs2, imm) = instr - op = u(untag(op), 7) - sub = u(untag(sub, 'subop'), 3) - rs1 = u(untag(rs1, 'rs'), 5) - rs2 = u(untag(rs2, 'rs'), 5) - if not is_lref(imm): - imm = i(untag(imm, 'off12'), 12) - imm_lo = bit_slice(imm, 4, 0) - imm_hi = bit_slice(imm, 11, 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 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) else: - label = untag(imm, 'off12') + label = subv.untag(imm, 'off12') imm_lo = (label, 5, 'off12') imm_hi = (label, 7, 'off12>>5') return [op, imm_lo, sub, rs1, rs2, imm_hi] def pack_j(instr): (op, rd, imm) = instr - op = u(untag(op), 7) - rd = u(untag(rd, 'rd'), 5) + op = bits.u(subv.untag(op), 7) + rd = bits.u(subv.untag(rd, 'rd'), 5) - if not is_lref(imm): - imm = i(untag(imm, 'off21'), 21) + 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 = bit_slice(imm, 10, 1) - imm_11 = bit_slice(imm, 11, 11) - imm_hi = bit_slice(imm, 19, 12) - imm_20 = bit_slice(imm, 20, 20) + 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) else: - label = untag(imm, 'off21') + label = subv.untag(imm, 'off21') imm_lo = (label, 10, 'off21>>1') imm_11 = (label, 1, 'off21>>11') imm_hi = (label, 8, 'off21>>12') @@ -74,20 +74,20 @@ def pack_j(instr): def pack_b(instr): (op, sub, rs1, rs2, imm) = instr - op = u(untag(op), 7) - sub = u(untag(sub, 'subop'), 3) - rs1 = u(untag(rs1, 'rs'), 5) - rs2 = u(untag(rs2, 'rs'), 5) - if not is_lref(imm): - imm = i(untag(imm, 'off13'), 13) + 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 = bit_slice(imm, 4, 1) - imm_md = bit_slice(imm, 10, 5) - imm_11 = bit_slice(imm, 11, 11) - imm_12 = bit_slice(imm, 12, 12) + 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) else: - label = untag(imm, 'off13') + label = subv.untag(imm, 'off13') imm_lo = (label, 4, 'off13>>1') imm_md = (label, 6, 'off13>>5') imm_11 = (label, 1, 'off13>>11') @@ -97,16 +97,10 @@ def pack_b(instr): def format(iter): for line in iter: - line = clean(line) - if line == '': - continue + line = subv.parse(line) - type = classify(line) - - if type == 'segment' or type == 'label': - yield line - else: - instr = parse_instr(line) + if line['type'] == 'instr': + instr = line['instr'] op = instr[0] if len(op) != 2: raise ValueError("instruction without op label") @@ -130,7 +124,9 @@ def format(iter): else: raise NotImplementedError() - yield format_instr(out) + yield subv.format_instr(out) + else: + yield line['raw'] if __name__ == '__main__': import sys @@ -145,7 +141,7 @@ class TestPackers(unittest.TestCase): final, [(0x37, 7), (0x5, 5), (0x10010, 20)] ) - self.assertEqual(bit_concat(*final)[1], 32) + self.assertEqual(bits.concat(*final)[1], 32) label = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')]) self.assertEqual( @@ -165,7 +161,7 @@ class TestPackers(unittest.TestCase): final, [(0x13, 7), (6, 5), (0, 3), (0, 5), (0x65, 12)] ) - self.assertEqual(bit_concat(*final)[1], 32) + self.assertEqual(bits.concat(*final)[1], 32) def test_pack_s(self): final = pack_s([ @@ -179,7 +175,7 @@ class TestPackers(unittest.TestCase): final, [(0x23, 7), (0, 5), (2, 3), (5, 5), (6, 5), (0, 7)] ) - self.assertEqual(bit_concat(*final)[1], 32) + self.assertEqual(bits.concat(*final)[1], 32) label = pack_s([ (0x23, 'store'), @@ -231,19 +227,27 @@ class TestE2E(unittest.TestCase): 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 ''') @@ -251,4 +255,5 @@ class TestE2E(unittest.TestCase): for line in format(StringIO(inp)): got += line + '\n' + self.maxDiff = None self.assertEqual(got, out) @@ -1,25 +1,31 @@ #!/usr/bin/env python3 -from riscv import byteify, bit_concat -from subx import clean, classify, parse_bits, format_instr +import subv +import bits -def pack(iter): - for line in iter: - line = clean(line) - if line == '': - continue +def byteify(word): + (val, size) = word + if size != 32: + raise ValueError("Expected 32-bit word") - type = classify(line) + b0 = bits.slice(word, 7, 0) + b1 = bits.slice(word, 15, 8) + b2 = bits.slice(word, 23, 16) + b3 = bits.slice(word, 31, 24) + return [b0[:1], b1[:1], b2[:1], b3[:1]] - if type == 'segment' or type == 'label': - yield line - else: - fields = parse_bits(line) - total = bit_concat(*fields) +def pack(iter): + for line in iter: + line = subv.parse(line) + if line['type'] == 'instr': + fields = [bits.from_part(p) for p in line['instr']] + total = bits.concat(*fields) if total[1] != 32: raise ValueError("instruction parts do not add up to 32 bit!") - yield format_instr(byteify(total)) + yield subv.format_instr(byteify(total)) + else: + yield line['raw'] if __name__ == '__main__': import sys @@ -27,29 +33,41 @@ if __name__ == '__main__': print(line) import unittest -class TestE2E(unittest.TestCase): +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 + 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 diff --git a/riscv.py b/riscv.py deleted file mode 100644 index 238a06e..0000000 --- a/riscv.py +++ /dev/null @@ -1,203 +0,0 @@ -def u(num, bits): - if num < 0: - raise ValueError("negative value not allowed: {}".format(num)) - - if num.bit_length() > bits: - raise ValueError("value too large for u{} field: {} ({} bits)", - bits, num, num.bit_length()) - - return (num, bits) - -def i(num, bits): - if num < 0: - num = (1 << bits) + num - - return u(num, bits) - -def bit_concat(*parts): - val, size = 0, 0 - for (pval, psize) in parts: - val = val | pval << size - size += psize - return (val, size) - -def bit_slice(bits, top, bottom): - (val, size) = bits - - if top < bottom: - raise ValueError("cant slice reverse range") - elif bottom < 0: - raise ValueError("negative slice index") - elif top >= size: - raise ValueError("cant slice [{}:{}] from {} bit value".format(top, bottom, size)) - - width = top - bottom + 1 - val = (val >> bottom) & ((1 << width) - 1) - return (val, width) - -def byteify(word): - (val, size) = word - if size != 32: - raise ValueError("Expected 32-bit word") - - b0 = bit_slice(word, 7, 0) - b1 = bit_slice(word, 15, 8) - b2 = bit_slice(word, 23, 16) - b3 = bit_slice(word, 31, 24) - return [b0[:1], b1[:1], b2[:1], b3[:1]] - -def format_r(op, rd, r1, r2, funct3, funct7): - # HH funct7 rs2 rs1 funct3 rd opcode LL - return byteify(bit_concat( - u(op, 7), - u(rd, 5), - u(funct3, 3), - u(r1, 5), - u(r2, 5), - u(funct7, 7), - )) - -def format_i(op, rd, r1, imm12, funct3): - # HH imm[11:0] rs1 funct3 rd opcode LL - return byteify(bit_concat( - u(op, 7), - u(rd, 5), - u(funct3, 3), - u(r1, 5), - i(imm12, 12), - )) - -def format_s(op, r1, r2, imm12, funct3): - # HH imm[11:5] rs2 rs1 funct3 imm[4:0] opcode LL - imm = i(imm12, 12) - imm_lo = bit_slice(imm, 4, 0) - imm_hi = bit_slice(imm, 11, 5) - return byteify(bit_concat( - u(op, 7), - imm_lo, - u(funct3, 3), - u(r1, 5), - u(r2, 5), - imm_hi - )) - - # imm12 = i(imm12, 12) - # r1 = i(r1, 5, neg=False) - # r2 = i(r2, 5, neg=False) - # - # b0 = (imm12 & 0x1) << 7 | op - # b1 = (r1 & 0x1) << 7 | funct3 << 4 | (imm12 & 0x1f) >> 1 - # b2 = (r2 & 0xf) << 4 | r1 >> 1 - # b3 = (imm12 & 0xf0) | r2 >> 4 - # return [(b0,), (b1,), (b2,), (b3,)] - -def format_u(op, rd, imm20): - # HH imm[31:12] rd opcode LL - return byteify(bit_concat( - u(op, 7), - u(rd, 5), - i(imm20, 20) - )) - -def format_j(op, rd, imm20): - # HH imm[20] imm[10:1] imm[11] imm[19:12] rd opcode LL - imm = i(imm20, 20) - imm_lo = bit_slice(imm, 9, 0) - imm_10 = bit_slice(imm, 10, 10) - imm_hi = bit_slice(imm, 18, 11) - imm_19 = bit_slice(imm, 19, 19) - return byteify(bit_concat( - u(op, 7), - u(rd, 5), - imm_hi, - imm_10, - imm_lo, - imm_19 - )) - -import unittest -class TestHelpers(unittest.TestCase): - def test_bit_concat(self): - self.assertEqual( - bit_concat((0b10, 2), (0b00, 2)), - (0b0010, 4) - ) - self.assertEqual( - bit_concat((0b1, 1), (0b0110, 4), (0b110, 3)), - (0b11001101, 8) - ) - - def test_bit_slice(self): - self.assertEqual( - bit_slice((0x7f, 8), 7, 4), - (0x7, 4) - ) - self.assertEqual( - bit_slice((0b100, 3), 2, 2), - (0b1, 1) - ) - with self.assertRaises(ValueError): - bit_slice((0xf, 4), 4, 0) - with self.assertRaises(ValueError): - bit_slice((0xf, 4), 2, 3) - with self.assertRaises(ValueError): - bit_slice((0xf, 4), 3, -1) - self.assertEqual( - bit_slice((0x12345678, 32), 7, 0), - (0x78, 8) - ) - self.assertEqual( - bit_slice((0x12345678, 32), 15, 8), - (0x56, 8) - ) - self.assertEqual( - bit_slice((0x12345678, 32), 23, 16), - (0x34, 8) - ) - - def test_byteify(self): - self.assertEqual( - byteify((0x12345678, 32)), - [(0x78,), (0x56,), (0x34,), (0x12,)] - ) - self.assertEqual( - byteify((0x4801813, 32)), - [(0x13,), (0x18,), (0x80,), (0x04,)] - ) - -class TestFormats(unittest.TestCase): - def test_format_u(self): - self.assertEqual( - format_u(0x37, 0x5, 0x10010), - [(183,), (2,), (1,), (16,)] - ) - - def test_format_i(self): - self.assertEqual( - format_i(0x13, 5, 0, 72, 0x0), - [(147,), (2,), (128,), (4,)] - ) - self.assertEqual( - format_i(0x13, 9, 2, 72, 0x3), - [(147,), (52,), (129,), (4,)] - ) - - def test_format_s(self): - self.assertEqual( - format_s(0x23, 3, 4, 0, 0x2), - [(35,), (160,), (65,), (0,)] - ) - self.assertEqual( - format_s(0x23, 2, 0, -4, 0x2), - [(35,), (46,), (1,), (254,)] - ) - - def test_format_j(self): - self.assertEqual( - format_j(0x6f, 0, -26), - [(111,), (240,), (223,), (252,)] - ) - self.assertEqual( - format_j(0x6f, 9, 0), - [(239,), (4,), (0,), (0,)] - ) @@ -2,6 +2,8 @@ import re white = re.compile('[ \t\.\n]+') hex = re.compile(r'^\-?(0x)?[0-9a-f]+$') +# parsing + def parse_part(part): part = part.split('/') if hex.match(part[0]): @@ -13,16 +15,6 @@ def parse_instr(line): parts = [parse_part(part) for part in parts if part != ''] return parts -def parse_bits(line): - parts = [] - for part in white.split(line): - if part == '': - continue - - val, size = parse_part(part) - parts.append((val, int(size))) - return parts - def parse_segment(line): parts = white.split(line) return (parts[1], int(parts[2], 16)) @@ -30,6 +22,44 @@ def parse_segment(line): def parse_label(line): return line[:-1] +def classify(line): + if line == '': + return 'empty' + elif line.startswith('=='): # segment + return 'segment' + elif line.endswith(':'): # label + return 'label' + else: + return 'instr' + +def parse(line): + raw = line.strip() + split = raw.split('#', 1) + if len(split) == 1: + clean = raw + comment = None + else: + clean, comment = split + + type = classify(clean) + + if type == 'segment': + parsed = parse_segment(clean) + elif type == 'label': + parsed = parse_label(clean) + elif type == 'instr': + parsed = parse_instr(clean) + else: + parsed = None + + return { + 'type': type, + 'raw': raw, + 'line': clean, + 'comment': comment, + type: parsed, + } + def is_lref(part): return isinstance(part[0], str) @@ -50,17 +80,6 @@ def format_instr(inst, comment=None): packed = packed + ' # ' + comment return packed -def clean(line): - return line.strip().split('#')[0].rstrip() - -def classify(line): - if line.startswith('=='): # segment - return 'segment' - elif line.endswith(':'): # label - return 'label' - else: - return 'instr' - import unittest class TestParsing(unittest.TestCase): def test_parse_part(self): @@ -1,8 +1,10 @@ #!/usr/bin/env python3 -from subx import clean, classify, parse_segment, parse_label, parse_instr, format_instr, is_lref -from riscv import u, i, bit_concat, bit_slice +import subv +import bits import re +tag_re = re.compile(r'^(imm|off)(\d+)(?:>>(\d+))?$') + def scan(iter): addr = None map = {} @@ -11,9 +13,8 @@ def scan(iter): map[line['label']] = line['addr'] return map -tag_re = re.compile(r'^(imm|off)(\d+)(?:>>(\d+))?$') def observe(part, rel_addr, map): - if is_lref(part): + if subv.is_lref(part): label, trim, tag = part trim = int(trim) @@ -26,11 +27,11 @@ def observe(part, rel_addr, map): shift = int(shift) if mode == 'imm': - addr = u(addr, size) + addr = bits.u(addr, size) else: - addr = i(addr - rel_addr, size) + addr = bits.i(addr - rel_addr, size) - part = bit_slice(addr, trim+shift-1, shift) + part = bits.slice(addr, trim+shift-1, shift) return part @@ -39,32 +40,16 @@ def survey(iter): queue = [] addr = -1 for line in iter: - # parse the line - line = clean(line) - if line == '': - continue - - type = classify(line) - if type == 'segment': - parsed = parse_segment(line) - elif type == 'label': - parsed = parse_label(line) - elif type == 'instr': - parsed = parse_instr(line) - - queue.append({ - 'type': type, - 'raw': line, - 'addr': addr, - type: parsed, - }) + line = subv.parse(line) + line['addr'] = addr + queue.append(line) # step forward addr - if type == 'segment': - segment, addr = parsed - elif type == 'instr': + if line['type'] == 'segment': + segment, addr = line['segment'] + elif line['type'] == 'instr': bits = 0 - for part in parsed: + for part in line['instr']: bits += int(part[1]) if bits % 8 != 0: @@ -79,7 +64,7 @@ def survey(iter): for part in line['instr']: observed = observe(part, line['addr'], map) instr.append(observed) - yield format_instr(instr) + yield subv.format_instr(instr) elif line['type'] == 'segment': yield line['raw'] diff --git a/test.py b/test.py deleted file mode 100755 index 9934f7a..0000000 --- a/test.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python3 -from riscv import format_u, format_i, format_s, format_j -from subx import format_instr - -if __name__ == "__main__": - t0 = 0x5 - t1 = 0x6 - print("== code 0x80000000") - print(format_instr(format_u(0x37, t0, 0x10010), "lui t0, 0x10010")) - print(format_instr(format_i(0x13, t1, 0, 72, 0x0), "addi t1, x0, 72")) - print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)")) - print(format_instr(format_i(0x13, t1, 0, 101, 0x0), "addi t1, x0, 101")) - print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)")) - print(format_instr(format_i(0x13, t1, 0, 108, 0x0), "addi t1, x0, 108")) - print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)")) - print(format_instr(format_i(0x13, t1, 0, 108, 0x0), "addi t1, x0, 108")) - print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)")) - print(format_instr(format_i(0x13, t1, 0, 111, 0x0), "addi t1, x0, 111")) - print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)")) - print(format_instr(format_i(0x13, t1, 0, 10, 0x0), "addi t1, x0, 10")) - print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)")) - print(format_instr(format_j(0x6f, 0, -26), "jal x0, -26")) diff --git a/test_riscv.py b/test_riscv.py deleted file mode 100644 index b945b36..0000000 --- a/test_riscv.py +++ /dev/null @@ -1,3 +0,0 @@ -import unittest -from riscv import bit_concat, bit_slice, byteify -from riscv import format_u, format_i, format_s, format_j |
