aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2021-05-25 15:14:24 +0000
committers-ol <s+removethis@s-ol.nu>2021-05-25 15:14:24 +0000
commit661a79240c8bafb3c99f3080c546689d025738f0 (patch)
tree3168b7acd14a8d627ca941bdd5a81c1d17268078
parentclose to working AUIPC+JALR jump (diff)
downloadsubv-661a79240c8bafb3c99f3080c546689d025738f0.tar.gz
subv-661a79240c8bafb3c99f3080c546689d025738f0.zip
apply "black" formatting
-rw-r--r--bits.py108
-rwxr-xr-xelf.py87
-rwxr-xr-xformat.py117
-rwxr-xr-xpack.py24
-rw-r--r--subv.py153
-rwxr-xr-xsurvey.py58
6 files changed, 318 insertions, 229 deletions
diff --git a/bits.py b/bits.py
index dd49665..a40c363 100644
--- a/bits.py
+++ b/bits.py
@@ -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
diff --git a/elf.py b/elf.py
index a785f79..f0cc94e 100755
--- a/elf.py
+++ b/elf.py
@@ -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
diff --git a/format.py b/format.py
index 5776da8..62c35b2 100755
--- a/format.py
+++ b/format.py
@@ -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)
diff --git a/pack.py b/pack.py
index cf531af..4a21ab5 100755
--- a/pack.py
+++ b/pack.py
@@ -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)
diff --git a/subv.py b/subv.py
index c8e6048..e0285cb 100644
--- a/subv.py
+++ b/subv.py
@@ -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
diff --git a/survey.py b/survey.py
index 6d6aae1..3aceda7 100755
--- a/survey.py
+++ b/survey.py
@@ -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)