aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bits.py158
-rwxr-xr-xformat.py50
-rwxr-xr-xpack.py31
-rw-r--r--subv.py18
-rwxr-xr-xsurvey.py30
5 files changed, 160 insertions, 127 deletions
diff --git a/bits.py b/bits.py
index b8cf441..02c24df 100644
--- a/bits.py
+++ b/bits.py
@@ -1,12 +1,86 @@
-empty = (0, 0)
+class Bitfield(object):
+ def __init__(self, val, size):
+ self.val = val
+ self.size = size
+
+ def __repr__(self):
+ return "Bitfield(0x{:x}, {})".format(self.val, self.size)
+
+ def __str__(self):
+ return "0x{:x}'{}".format(self.val, self.size)
+
+ def __getitem__(self, range):
+ """ slice a bitfield given hi and lo bit indices.
+
+ >>> Bitfield(0x7f, 8)[7:4]
+ Bitfield(0x7, 4)
+ >>> Bitfield(0b100, 3)[2:2]
+ Bitfield(0x1, 1)
+ >>> Bitfield(0x12345678, 32)[7:0]
+ Bitfield(0x78, 8)
+ >>> Bitfield(0x12345678, 32)[15:8]
+ Bitfield(0x56, 8)
+ >>> Bitfield(0x12345678, 32)[23:16]
+ Bitfield(0x34, 8)
+
+ >>> Bitfield(0xf, 4)[4:0]
+ Traceback (most recent call last):
+ ...
+ ValueError: slice [4:0] out of range of 0xf'4
+ >>> Bitfield(0xf, 4)[2:3]
+ Traceback (most recent call last):
+ ...
+ ValueError: cant slice reverse range
+ >>> Bitfield(0xf, 4)[3:-1]
+ Traceback (most recent call last):
+ ...
+ ValueError: slice [3:-1] out of range of 0xf'4
+ """
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ 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))
+
+ size = hi - lo + 1
+ val = (self.val >> lo) & ((1 << size) - 1)
+ return Bitfield(val, size)
+
+ def slice_allowempty(self, range):
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ if hi < lo:
+ return empty
+
+ return self[range]
+
+ def __and__(self, other):
+ """ concatenate multiple bitfields.
+
+ >>> Bitfield(0b00, 2) & Bitfield(0b10, 2)
+ Bitfield(0x2, 4)
+ >>> Bitfield(0b110, 3) & Bitfield(0b0110, 4) & Bitfield(0b1, 1)
+ Bitfield(0xcd, 8)
+ """
+ if not isinstance(other, Bitfield):
+ raise NotImplementedError()
+
+ return Bitfield(self.val << other.size | other.val, self.size + other.size)
+
+empty = Bitfield(0, 0)
def u(num, bits):
""" parse an unsigned integer into a bitfield.
>>> u(0x08, 8)
- (8, 8)
+ Bitfield(0x8, 8)
>>> u(0xff, 8)
- (255, 8)
+ Bitfield(0xff, 8)
>>> u(0xf0, 7)
Traceback (most recent call last):
@@ -24,19 +98,19 @@ def u(num, bits):
raise ValueError("value {} (u{}) too large for u{} field"
.format(num, num.bit_length(), bits))
- return (num, bits)
+ return Bitfield(num, bits)
def i(num, bits):
""" parse a signed integer into a bitfield.
>>> i(8, 8)
- (8, 8)
+ Bitfield(0x8, 8)
>>> i(-4, 8)
- (252, 8)
- >>> i(127, 8)
- (127, 8)
+ Bitfield(0xfc, 8)
+ >>> i(0x7f, 8)
+ Bitfield(0x7f, 8)
>>> i(-128, 8)
- (128, 8)
+ Bitfield(0x80, 8)
>>> i(128, 8)
Traceback (most recent call last):
@@ -62,67 +136,11 @@ def i(num, bits):
def from_part(part):
""" parse a size-tagged subv part into a bit.
- >>> from_part((34, 2))
- (34, 2)
- >>> from_part((34, 2, 'extra'))
- (34, 2)
+ >>> from_part((0x12, 2))
+ Bitfield(0x12, 2)
+ >>> from_part((0x12, 2, 'extra'))
+ Bitfield(0x12, 2)
"""
val = int(part[0])
size = part[1]
- return (val, int(size))
-
-def concat(*parts):
- """ concatenate multiple bitfields.
-
- >>> concat((0b10, 2), (0b00, 2))
- (2, 4)
- >>> concat((0b1, 1), (0b0110, 4), (0b110, 3))
- (205, 8)
- """
- val, size = 0, 0
- for (pval, psize) in parts:
- val = val | pval << size
- size += psize
- return (val, size)
-
-def slice(bits, hi, lo, allow_empty=False):
- """ slice a bitfield given hi and lo bit indices.
-
- >>> slice((0x7f, 8), 7, 4)
- (7, 4)
- >>> slice((0b100, 3), 2, 2)
- (1, 1)
- >>> slice((0x12345678, 32), 7, 0)
- (120, 8)
- >>> slice((0x12345678, 32), 15, 8)
- (86, 8)
- >>> slice((0x12345678, 32), 23, 16)
- (52, 8)
-
- >>> slice((0xf, 4), 4, 0)
- Traceback (most recent call last):
- ...
- ValueError: slice [4:0] out of range (4 bit value)
- >>> slice((0xf, 4), 2, 3)
- Traceback (most recent call last):
- ...
- ValueError: cant slice reverse range
- >>> slice((0xf, 4), 2, 3, allow_empty=True)
- (0, 0)
- >>> slice((0xf, 4), 3, -1)
- Traceback (most recent call last):
- ...
- ValueError: slice [3:-1] out of range (4 bit value)
- """
- (val, size) = bits
-
- if hi < lo:
- if allow_empty:
- return empty
- raise ValueError("cant slice reverse range")
- elif lo < 0 or hi >= size:
- raise ValueError("slice [{}:{}] out of range ({} bit value)".format(hi, lo, size))
-
- size = hi - lo + 1
- val = (val >> lo) & ((1 << size) - 1)
- return (val, size)
+ return Bitfield(val, int(size))
diff --git a/format.py b/format.py
index 9e0ead0..58b7708 100755
--- a/format.py
+++ b/format.py
@@ -63,6 +63,8 @@ main:
6f/7 00/5 main[19:12]/off8 main[11:11]/off1 main[10:1]/off10 main[20:20]/off1
"""
+from operator import __and__
+from functools import reduce
import subv
import bits
@@ -80,7 +82,7 @@ def ref_slice(ref, hi, lo):
}
def default_slice(val, hi, lo):
- """ add a default slice spec to labels if missing,
+ """ add a default slice spec to labels if missing.
>>> default_slice(('label', 'imm12'), 12, 1)
('label[12:1]', 'imm12')
@@ -99,8 +101,8 @@ def default_slice(val, hi, lo):
"""
parsed = subv.parse_reference(val)
if 'hi' not in parsed:
- parsed['hi'] = hi
- parsed['lo'] = lo
+ parsed['hi'] = hi
+ parsed['lo'] = lo
ref = subv.format_reference(parsed)
p_size = parsed['hi'] - parsed['lo'] + 1
@@ -126,8 +128,8 @@ def slice_bit_or_ref(val, hi, lo):
...
AssertionError: cant slice label w/o bounds
"""
- if not isinstance(val[0], str):
- return bits.slice(val, hi, lo)
+ if isinstance(val, bits.Bitfield):
+ return val[hi:lo]
ref = subv.parse_reference(val)
assert 'hi' in ref, "cant slice label w/o bounds"
@@ -148,13 +150,13 @@ def pack_u(instr):
""" verify & pack U-type instructions.
>>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')])
- [(55, 7), (5, 5), (65552, 20)]
+ [Bitfield(0x37, 7), Bitfield(0x5, 5), Bitfield(0x10010, 20)]
>>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')])
- [(55, 7), (5, 5), ('pos[31:12]', 'imm20')]
+ [Bitfield(0x37, 7), Bitfield(0x5, 5), ('pos[31:12]', 'imm20')]
>>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos[19:0]', 'imm20')])
- [(55, 7), (5, 5), ('pos[19:0]', 'imm20')]
+ [Bitfield(0x37, 7), Bitfield(0x5, 5), ('pos[19:0]', 'imm20')]
>>> pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos[31:0]', 'imm20')])
Traceback (most recent call last):
@@ -174,11 +176,11 @@ def pack_u(instr):
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)]
+ >>> reduce(__and__, pack_i([(0x13, 'opi'), (0, 'subop', 'add'), (6, 'rd', 't1'), (0, 'rs', 'x0'), (101, 'imm12')]))
+ Bitfield(0x26600065, 32)
>>> 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')]
+ [Bitfield(0x13, 7), Bitfield(0x6, 5), Bitfield(0x0, 3), Bitfield(0x0, 5), ('label[11:0]', 'imm12')]
"""
(op, sub, rd, rs, imm) = instr
op = bits.u(subv.untag(op), 7)
@@ -195,11 +197,11 @@ def pack_i(instr):
def pack_s(instr):
""" verify & pack S-type instructions.
- >>> 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)]
+ >>> reduce(__and__, pack_s([(0x23, 'store'), (2, 'subop', 'word'), (5, 'rs', 't0'), (6, 'rs', 't1'), (0, 'off12')]))
+ Bitfield(0x46045300, 32)
>>> 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')]
+ [Bitfield(0x23, 7), ('home[4:0]', 'off5'), Bitfield(0x2, 3), Bitfield(0x5, 5), Bitfield(0x6, 5), ('home[11:5]', 'off7')]
"""
(op, sub, rs1, rs2, imm) = instr
op = bits.u(subv.untag(op), 7)
@@ -219,14 +221,14 @@ def pack_s(instr):
def pack_j(instr):
""" verify & pack J-type instructions.
- >>> pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (0, 'off20')])
- [(111, 7), (0, 5), (0, 8), (0, 1), (0, 10), (0, 1)]
+ >>> reduce(__and__, pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (0, 'off20')]))
+ Bitfield(0xde000000, 32)
- >>> pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (-2, 'off20')])
- [(111, 7), (0, 5), (255, 8), (1, 1), (1022, 10), (1, 1)]
+ >>> reduce(__and__, pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (-2, 'off20')]))
+ Bitfield(0xde0ffffd, 32)
>>> 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')]
+ [Bitfield(0x6f, 7), Bitfield(0x2, 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)
@@ -247,14 +249,14 @@ def pack_j(instr):
def pack_b(instr):
""" verify & pack B-type instructions.
- >>> 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)]
+ >>> reduce(__and__, pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), (0, 'off12')]))
+ Bitfield(0xc6006000, 32)
- >>> 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)]
+ >>> reduce(__and__, pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), (-2, 'off12')]))
+ Bitfield(0xc7e0607f, 32)
>>> 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')]
+ [Bitfield(0x63, 7), ('home[11:11]', 'off1'), ('home[4:1]', 'off4'), Bitfield(0x0, 3), Bitfield(0x6, 5), Bitfield(0x0, 5), ('home[10:5]', 'off6'), ('home[12:12]', 'off1')]
"""
(op, sub, rs1, rs2, imm) = instr
op = bits.u(subv.untag(op), 7)
diff --git a/pack.py b/pack.py
index f717df4..5dbaf95 100755
--- a/pack.py
+++ b/pack.py
@@ -44,22 +44,21 @@ import bits
def byteify(word):
""" split longer bitfield into bytes.
- >>> byteify((0x12345678, 32))
+ >>> byteify(bits.Bitfield(0x12345678, 32))
[(120,), (86,), (52,), (18,)]
- >>> byteify((0x4801813, 32))
+ >>> byteify(bits.Bitfield(0x4801813, 32))
[(19,), (24,), (128,), (4,)]
- >>> byteify((0x13, 9)),
+ >>> byteify(bits.Bitfield(0x13, 9)),
Traceback (most recent call last):
...
- AssertionError: not byte aligned: 9 bits
+ AssertionError: not byte aligned: 0x13'9
"""
- (val, size) = word
- assert size % 8 == 0, "not byte aligned: {} bits".format(size)
+ assert word.size % 8 == 0, "not byte aligned: {}".format(word)
out = []
- for i in range(0, size, 8):
- byte = bits.slice(word, i+7, i)
- out.append(byte[:1])
+ for i in range(0, word.size, 8):
+ byte = word[i+7:i]
+ out.append((byte.val,))
return out
@subv.with_parsed_lines
@@ -79,27 +78,27 @@ def pack(iter):
last_segment = None
for segment, line in iter:
if last_segment != segment:
- assert buf[1] == 0, "segment '{}' end isn't byte-aligned".format(segment)
+ assert buf.size == 0, "segment '{}' end isn't byte-aligned".format(segment)
if out:
yield subv.format(flush_bytes())
if line['type'] == 'instr':
for part in line['instr']:
- buf = bits.concat(buf, bits.from_part(part))
+ buf = bits.from_part(part) & buf
else:
yield line['raw']
- while buf[1] >= 8:
- byte = bits.slice(buf, 7, 0)
- buf = bits.slice(buf, buf[1] - 1, 8, allow_empty=True)
- out.append(byte[:1])
+ while buf.size >= 8:
+ byte = buf[7:0]
+ buf = buf.slice_allowempty(slice(buf.size-1, 8))
+ out.append((byte.val,))
if len(out) == 4:
yield subv.format(flush_bytes())
last_segment = segment
- assert buf[1] == 0, "segment '{}' end isn't byte-aligned".format(segment)
+ assert buf.size == 0, "segment '{}' end isn't byte-aligned".format(segment)
if __name__ == '__main__':
import sys
diff --git a/subv.py b/subv.py
index 4f38988..dd45b16 100644
--- a/subv.py
+++ b/subv.py
@@ -1,4 +1,5 @@
import re
+import bits
white = re.compile(r'[ \t\.\n]+')
hex = re.compile(r'^\-?(0x)?[0-9a-f]+$')
num = re.compile(r'^\d+$')
@@ -230,6 +231,11 @@ def untag(part, expect=None):
>>> untag(('$label', 'imm20'), expect='imm20')
'$label'
+ >>> untag((2, 'num'), expect=['num', 'imm20'])
+ 2
+ >>> untag(('$label', 'imm20'), expect=['num', 'imm20'])
+ '$label'
+
>>> untag((2, 'imm12'), expect='imm20')
Traceback (most recent call last):
...
@@ -238,9 +244,15 @@ def untag(part, expect=None):
Traceback (most recent call last):
...
ValueError: expected ('$label', 'imm20') to be labelled off12
+ >>> untag((2, 'imm12'), expect=['num', 'off12'])
+ Traceback (most recent call last):
+ ...
+ ValueError: expected (2, 'imm12') to be labelled one of ['num', 'off12']
"""
- if expect and part[1] != expect:
+ if isinstance(expect, str) and part[1] != expect:
raise ValueError("expected {} to be labelled {}".format(part, expect))
+ elif isinstance(expect, list) and part[1] not in expect:
+ raise ValueError("expected {} to be labelled one of {}".format(part, expect))
return part[0]
def format_reference(ref):
@@ -287,7 +299,9 @@ def format_part(part):
>>> format_part(('$label:suff', 'tag'))
'$label:suff/tag'
"""
- if not is_reference(part):
+ if isinstance(part, bits.Bitfield):
+ part = ('{:02x}'.format(part.val), part.size)
+ elif not is_reference(part):
first = '{:02x}'.format(part[0])
part = (first, *part[1:])
return '/'.join([str(p) for p in part])
diff --git a/survey.py b/survey.py
index 55a8ab0..bfd9313 100755
--- a/survey.py
+++ b/survey.py
@@ -58,22 +58,22 @@ def observe(part, rel_addr, map):
>>> observe((3, '3'), 0, {})
(3, '3')
- >>> observe(('label[31:0]', 'imm32'), 0, {'label': 1234})
- (1234, 32)
- >>> observe(('label-2[31:0]', 'imm32'), 0, {'label': 1234})
- (1232, 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': 0x10fff})
- (16, 20)
- >>> observe(('label[31:0]', 'off32'), 2000, {'label': 1000})
- (4294966296, 32)
+ >>> observe(('label[31:0]', 'imm32'), 0, {'label': 0x1234})
+ Bitfield(0x1234, 32)
+ >>> observe(('label-2[31:0]', 'imm32'), 0, {'label': 0x1234})
+ Bitfield(0x1232, 32)
+ >>> observe(('label[1:0]', 'imm32'), 0, {'label': 0x1236})
+ Bitfield(0x2, 2)
+ >>> observe(('label[31:0]', 'off32'), 0x1000, {'label': 0x1234})
+ Bitfield(0x234, 32)
+ >>> observe(('label[31:12]', 'off32'), 0x1000, {'label': 0x10fff})
+ Bitfield(0xf, 20)
+ >>> observe(('label[31:0]', 'off32'), 0x2000, {'label': 0x1000})
+ Bitfield(0xfffff000, 32)
>>> observe(('label+4[7:0]', 'off32'), 0x1000, {'label': 0x1234})
- (56, 8)
+ Bitfield(0x38, 8)
>>> observe(('label+4[15:8]', 'off32'), 0x1000, {'label': 0x1234})
- (2, 8)
+ Bitfield(0x2, 8)
>>> observe(('label[32:0]', 'imm32'), 0, {})
Traceback (most recent call last):
@@ -97,7 +97,7 @@ def observe(part, rel_addr, map):
else:
addr = bits.i(addr - rel_addr, 32)
- return bits.slice(addr, ref['hi'], ref['lo'])
+ return addr[ref['hi']:ref['lo']]
@subv.with_parsed_lines
def survey(iter):