git.s-ol.nu subv / 661a792
apply "black" formatting s-ol 28 days ago
6 changed file(s) with 320 addition(s) and 231 deletion(s). Raw diff Collapse all Expand all
00 import re
11 from operator import __and__
22 from functools import reduce
3
34
45 class WordBase(object):
56 def slice_allowempty(self, range):
1718 return BitsUnion(self, other)
1819 raise NotImplementedError()
1920
21
2022 class BitsUnion(WordBase):
2123 def __init__(self, *parts):
2224 self.parts = parts
2325
2426 def __repr__(self):
25 return "BitsUnion({})".format(', '.join(repr(part) for part in self.parts))
27 return "BitsUnion({})".format(", ".join(repr(part) for part in self.parts))
2628
2729 def as_value(self, **kwargs):
28 """ reduce down to a single Bitfield value.
30 """reduce down to a single Bitfield value.
2931
3032 >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value()
3133 Bitfield(0x1234, 16)
3638
3739 @property
3840 def size(self):
39 """ get the total size.
41 """get the total size.
4042
4143 >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size
4244 16
4648 return sum(part.size for part in self.parts)
4749
4850 def __getitem__(self, range):
49 """ slice a union given hi and lo bit indices.
51 """slice a union given hi and lo bit indices.
5052
5153 >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4]
5254 Bitfield(0x23, 8)
6163 if hi < lo:
6264 raise ValueError("cant slice reverse range")
6365 elif lo < 0 or hi >= size:
64 raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
66 raise ValueError(
67 "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
68 )
6569
6670 i = size
6771 result = Bitfield(0, 0)
9094 return BitsUnion(*self.parts, other)
9195 raise NotImplementedError()
9296
97
9398 class Bitfield(WordBase):
9499 def __init__(self, val, size, extra=()):
95100 if val < 0:
97102 max = -1 - min
98103
99104 if val > max or val < min:
100 raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max))
105 raise ValueError(
106 "value {} out of i{} range [{};{}]".format(val, size, min, max)
107 )
101108
102109 if val < 0:
103110 val = (1 << size) + val
107114 self.extra = extra
108115
109116 def __repr__(self):
110 """ format for debugging.
117 """format for debugging.
111118 >>> Bitfield(0, 8)
112119 Bitfield(0x0, 8)
113120 >>> Bitfield(16, 8)
119126 >>> Bitfield(3, 4, extra=('hello',))
120127 Bitfield(0x3, 4, extra=('hello',))
121128 """
122 extra = ''
129 extra = ""
123130 if self.extra:
124131 extra = ", extra={!r}".format(self.extra)
125132 return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra)
126133
127134 def __str__(self):
128 """ format as a word.
135 """format as a word.
129136 >>> str(Bitfield(0, 8))
130137 '00/8'
131138 >>> str(Bitfield(16, 8))
141148 """
142149 digits = 1 + ((self.size - 1) // 4)
143150 base = "{:x}".format(self.val)
144 base = (digits - len(base)) * '0' + base
145 return '/'.join([base, *self.extra, str(self.size)])
151 base = (digits - len(base)) * "0" + base
152 return "/".join([base, *self.extra, str(self.size)])
146153
147154 def as_value(self, **kwargs):
148155 return self
149156
150157 def __getitem__(self, range):
151 """ slice a bitfield given hi and lo bit indices.
158 """slice a bitfield given hi and lo bit indices.
152159
153160 >>> Bitfield(0x7f, 8)[7:4]
154161 Bitfield(0x7, 4)
181188 if hi < lo:
182189 raise ValueError("cant slice reverse range")
183190 elif lo < 0 or hi >= self.size:
184 raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
191 raise ValueError(
192 "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
193 )
185194
186195 size = hi - lo + 1
187196 val = (self.val >> lo) & ((1 << size) - 1)
188197 return Bitfield(val, size)
189198
190199 def __and__(self, other):
191 """ concatenate multiple bitfields.
200 """concatenate multiple bitfields.
192201
193202 >>> Bitfield(0b00, 2) & Bitfield(0b10, 2)
194203 Bitfield(0x2, 4)
201210 return BitsUnion(self, other)
202211 raise NotImplementedError()
203212
213
204214 global_slice = slice
215
216
205217 class LabelRef(WordBase):
206 mode_re = re.compile(r'^(imm|off)(\d+)?$')
218 mode_re = re.compile(r"^(imm|off)(\d+)?$")
207219
208220 def __init__(self, label, offset, mode, slice=None):
209221 if isinstance(mode, str):
213225
214226 self.label = label
215227 self.mode = mode
216 self.slice = slice or global_slice(self.mode[1]-1, 0)
228 self.slice = slice or global_slice(self.mode[1] - 1, 0)
217229 self.offset = offset
218230
219231 def __repr__(self):
220 """ format for debugging.
232 """format for debugging.
221233
222234 >>> LabelRef("label", 0, ("imm", 32), slice(31,31))
223235 LabelRef('label', 0, ('imm', 32), 31:31)
228240 >>> LabelRef("test", 0, "off")
229241 LabelRef('test', 0, ('off', 32), 31:0)
230242 """
231 return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(self.label, self.offset, self.mode, self.slice)
243 return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(
244 self.label, self.offset, self.mode, self.slice
245 )
232246
233247 def __str__(self):
234 """ format as a word.
248 """format as a word.
235249
236250 >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31)))
237251 'label/imm32/[31:31]'
242256 >>> str(LabelRef("test", -12, "off"))
243257 'test-12/off32/[31:0]'
244258 """
245 offset = ''
259 offset = ""
246260 if self.offset != 0:
247261 offset = "{:+}".format(self.offset)
248262
249 return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(self.label, offset, self.mode, self.slice)
263 return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(
264 self.label, offset, self.mode, self.slice
265 )
250266
251267 @property
252268 def size(self):
253269 return self.slice.start - self.slice.stop + 1
254270
255271 def as_value(self, labels={}, **kwargs):
256 """ resolve the reference.
272 """resolve the reference.
257273
258274 >>> labels = {'test': 0xabcd1234, 'main': 0x8000}
259275 >>> LabelRef("test", 0, "imm").as_value(labels=labels)
286302
287303 value = labels[self.label] + self.offset
288304
289 if self.mode[0] == 'off':
290 assert 'pc' in kwargs, TypeError("'pc' is required to resolve offset-references")
291 value = value - kwargs['pc']
305 if self.mode[0] == "off":
306 assert "pc" in kwargs, TypeError(
307 "'pc' is required to resolve offset-references"
308 )
309 value = value - kwargs["pc"]
292310
293311 field = Bitfield(value, self.mode[1])
294312 return field[self.slice]
295313
296314 def __getitem__(self, range):
297 """ slice a label using hi and lo bit indices.
315 """slice a label using hi and lo bit indices.
298316
299317 >>> label = LabelRef("label", 0, "imm32", slice(31,0))
300318 >>> label.size
326344 if hi < lo:
327345 raise ValueError("cant slice reverse range")
328346 elif lo < 0 or hi >= self.size:
329 raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
347 raise ValueError(
348 "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
349 )
330350
331351 new_slice = slice(self.slice.stop + hi, self.slice.stop + lo)
332352 return LabelRef(self.label, self.offset, self.mode, new_slice)
333353
354
334355 empty = Bitfield(0, 0)
335356
357
336358 def u(num, bits):
337 """ parse an unsigned integer into a bitfield.
359 """parse an unsigned integer into a bitfield.
338360
339361 >>> u(0x08, 8)
340362 Bitfield(0x8, 8)
354376 raise ValueError("negative value not allowed: {}".format(num))
355377
356378 if num.bit_length() > bits:
357 raise ValueError("value {} (u{}) too large for u{} field"
358 .format(num, num.bit_length(), bits))
379 raise ValueError(
380 "value {} (u{}) too large for u{} field".format(num, num.bit_length(), bits)
381 )
359382
360383 return Bitfield(num, bits)
361384
385
362386 def i(num, bits):
363 """ parse a signed integer into a bitfield.
387 """parse a signed integer into a bitfield.
364388
365389 >>> i(8, 8)
366390 Bitfield(0x8, 8)
384408 max = -1 - min
385409
386410 if num > max or num < min:
387 raise ValueError("value {} (i{}) too large for i{} field [{};{}]"
388 .format(num, num.bit_length()+1, bits, min, max))
411 raise ValueError(
412 "value {} (i{}) too large for i{} field [{};{}]".format(
413 num, num.bit_length() + 1, bits, min, max
414 )
415 )
389416
390417 if num < 0:
391418 num = (1 << bits) + num
392419
393420 return u(num, bits)
394421
422
395423 def from_part(part):
396 """ parse a size-tagged subv part into a bit.
424 """parse a size-tagged subv part into a bit.
397425
398426 >>> from_part((0x12, 2))
399427 Bitfield(0x12, 2)
404432 size = part[1]
405433 return Bitfield(val, int(size))
406434
435
407436 global_slice = slice
408 ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?$')
409 slice_re = re.compile(r'^\[(\d+):(\d+)]$')
437 ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$")
438 slice_re = re.compile(r"^\[(\d+):(\d+)]$")
439
410440
411441 def ref(val, default_slice=None):
412 """ add a default slice spec to labels if missing.
442 """add a default slice spec to labels if missing.
413443
414444 >>> ref(('label', 'imm12'))
415445 LabelRef('label', 0, ('imm', 12), 11:0)
450480
451481 if default_slice:
452482 expected = default_slice.start - default_slice.stop + 1
453 assert expected == ref.size, ValueError("expected {} bit slice, got {}".format(expected, ref))
483 assert expected == ref.size, ValueError(
484 "expected {} bit slice, got {}".format(expected, ref)
485 )
454486
455487 return ref
1717 https://sifive.cdn.prismic.io/sifive/4d063bf8-3ae6-4db6-9843-ee9076ebadf7_fe310-g000.pdf
1818 """
1919
20
2021 def w(b):
2122 sys.stdout.buffer.write(b)
2223 return len(b)
2324
25
2426 def wi(num, size=2):
25 sys.stdout.buffer.write(num.to_bytes(size, byteorder='little'))
27 sys.stdout.buffer.write(num.to_bytes(size, byteorder="little"))
2628 return size
29
2730
2831 def padto(offset, cursor):
2932 missing = offset - cursor
3033 if missing < 0:
3134 raise ValueError("cursor {} already past offset {}!".format(cursor, offset))
32 w(b'\x00' * missing)
35 w(b"\x00" * missing)
3336 return offset
37
3438
3539 def write_elf_header(segments):
3640 c = 0
3741
38 code = next(s for s in segments if s['name'] == 'code')
39 entrypoint = code['addr']
42 code = next(s for s in segments if s["name"] == "code")
43 entrypoint = code["addr"]
4044
41 c += w(b"\x7fELF") # ELF magic
42 c += wi(0x010101, 4) # 32bit, little endian
43 c += wi(0, 8) # reserved
44 c += wi(0x02) # e_type
45 c += wi(0xf3) # e_machine
46 c += wi(0x01, 4) # e_version
47 c += wi(entrypoint, 4) # e_entry
48 c += wi(0x34, 4) # e_phoff (Program Header offset)
49 c += wi(0x00, 4) # e_shoff (Section Header offset, unused)
50 c += wi(0x0004, 4) # e_flags
51 c += wi(0x34) # e_ehsize
52 c += wi(0x20) # e_phentsize
53 c += wi(len(segments)) # e_phnum
54 c += wi(0x28) # e_shentsize
55 c += wi(0x0) # e_shnum
56 c += wi(0x0) # e_shstrndx
45 c += w(b"\x7fELF") # ELF magic
46 c += wi(0x010101, 4) # 32bit, little endian
47 c += wi(0, 8) # reserved
48 c += wi(0x02) # e_type
49 c += wi(0xF3) # e_machine
50 c += wi(0x01, 4) # e_version
51 c += wi(entrypoint, 4) # e_entry
52 c += wi(0x34, 4) # e_phoff (Program Header offset)
53 c += wi(0x00, 4) # e_shoff (Section Header offset, unused)
54 c += wi(0x0004, 4) # e_flags
55 c += wi(0x34) # e_ehsize
56 c += wi(0x20) # e_phentsize
57 c += wi(len(segments)) # e_phnum
58 c += wi(0x28) # e_shentsize
59 c += wi(0x0) # e_shnum
60 c += wi(0x0) # e_shstrndx
5761 return c
62
5863
5964 def write_program_header(segment, c, start):
6065 align = 0x1000
6166 offset = (1 + start // align) * align
62 segment['offset'] = offset
63 start = segment['addr']
64 size = len(segment['content'])
65 flags = 5 if segment['name'] == "code" else 6
67 segment["offset"] = offset
68 start = segment["addr"]
69 size = len(segment["content"])
70 flags = 5 if segment["name"] == "code" else 6
6671
6772 if offset % align != start % align:
6873 print("{:02x} {:02x}".format(offset, offset % align))
6974 print("{:02x} {:02x}".format(start, start % align))
7075 raise ValueError("improper alignment")
7176
72 c += wi(0x1, 4) # p_type
73 c += wi(offset, 4) # p_offset
77 c += wi(0x1, 4) # p_type
78 c += wi(offset, 4) # p_offset
7479 c += wi(start, 4) # p_vaddr
7580 c += wi(start, 4) # p_paddr
76 c += wi(size, 4) # p_filesz
77 c += wi(size, 4) # p_memsz
78 c += wi(flags, 4) # p_flags, 0x5=rx, 0x6=rw
81 c += wi(size, 4) # p_filesz
82 c += wi(size, 4) # p_memsz
83 c += wi(flags, 4) # p_flags, 0x5=rx, 0x6=rw
7984 c += wi(align, 4) # p_align
8085 return c
86
8187
8288 @subv.with_parsed_lines
8389 def elf(iter):
8490 segments = []
8591 segment = None
8692 for _, line in iter:
87 if line['type'] == 'segment':
88 (name, addr) = line['segment']
89 segment = { 'name': name, 'addr': addr, 'content': [] }
93 if line["type"] == "segment":
94 (name, addr) = line["segment"]
95 segment = {"name": name, "addr": addr, "content": []}
9096 segments.append(segment)
91 elif line['type'] == 'instr':
97 elif line["type"] == "instr":
9298 if segment == None:
93 raise ValueError("label or code outside of segment!")
99 raise ValueError("label or code outside of segment!")
94100
95 segment['content'] += line['instr']
101 segment["content"] += line["instr"]
96102 else:
97103 raise ValueError("elf input should contain only segments and data!")
98104
100106 segment_start = 0x1000
101107 for seg in segments:
102108 c = write_program_header(seg, c, segment_start)
103 segment_start = seg['offset'] + len(seg['content'])
109 segment_start = seg["offset"] + len(seg["content"])
104110
105111 for seg in segments:
106 c = padto(seg['offset'], c)
107 for part in seg['content']:
112 c = padto(seg["offset"], c)
113 for part in seg["content"]:
108114 c += wi(part[0], 1)
109115
110116 yield
111117
112 if __name__ == '__main__':
118
119 if __name__ == "__main__":
113120 for line in elf(sys.stdin):
114121 pass
6565 import subv
6666 import bits
6767
68
6869 def _test_format(words):
69 return ' '.join(map(str, words))
70 return " ".join(map(str, words))
71
7072
7173 def ref_slice(ref, hi, lo):
7274 if hi < lo:
7375 raise ValueError("cant slice reverse range")
74 elif hi >= ref['size']:
75 raise ValueError("cant slice [{}:{}] from {} bit value".format(hi, lo, ref['size']))
76 elif hi >= ref["size"]:
77 raise ValueError(
78 "cant slice [{}:{}] from {} bit value".format(hi, lo, ref["size"])
79 )
7680
7781 return {
7882 **ref,
79 'hi': hi,
80 'lo': lo,
81 'size': hi - lo + 1,
83 "hi": hi,
84 "lo": lo,
85 "size": hi - lo + 1,
8286 }
8387
88
8489 def sign_trunc(val, size):
85 """ truncate and sign-shrink a number literal.
90 """truncate and sign-shrink a number literal.
8691
8792 >>> sign_trunc(bits.i(4, 32), 8)
8893 Bitfield(0x4, 8)
9398 >>> sign_trunc(bits.i(-0x11ff, 32), 8)
9499 Bitfield(0x81, 8)
95100 """
96 sign = val[val.size-1]
97 rest = val[size-2:0]
101 sign = val[val.size - 1]
102 rest = val[size - 2 : 0]
98103 return sign & rest
99104
105
100106 def pack_u(instr):
101 """ verify & pack U-type instructions.
107 """verify & pack U-type instructions.
102108
103109 >>> _test_format(pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')]))
104110 '37/7 05/5 10010/20'
116122 """
117123 (op, rd, imm) = instr
118124 op = bits.u(subv.untag(op), 7)
119 rd = bits.u(subv.untag(rd, 'rd'), 5)
125 rd = bits.u(subv.untag(rd, "rd"), 5)
120126 if subv.is_reference(imm):
121127 imm = bits.ref(imm, slice(31, 12))
122128 else:
123 imm = bits.i(subv.untag(imm, 'imm20'), 20)
129 imm = bits.i(subv.untag(imm, "imm20"), 20)
124130
125131 return [op, rd, imm]
126132
133
127134 def pack_i(instr):
128 """ verify & pack I-type instructions.
135 """verify & pack I-type instructions.
129136
130137 >>> _test_format(pack_i([(0x13, 'opi'), (0, 'subop', 'add'), (6, 'rd', 't1'), (0, 'rs', 'x0'), (0x65, 'imm12')]))
131138 '13/7 06/5 0/3 00/5 065/12'
135142 """
136143 (op, sub, rd, rs, imm) = instr
137144 op_tag = op[1]
138 op = bits.u(subv.untag(op), 7)
139 sub = bits.u(subv.untag(sub, 'subop'), 3)
140 rd = bits.u(subv.untag(rd, 'rd'), 5)
141 rs = bits.u(subv.untag(rs, 'rs'), 5)
142 if subv.is_reference(imm):
143 if op_tag == 'jalr':
145 op = bits.u(subv.untag(op), 7)
146 sub = bits.u(subv.untag(sub, "subop"), 3)
147 rd = bits.u(subv.untag(rd, "rd"), 5)
148 rs = bits.u(subv.untag(rs, "rs"), 5)
149 if subv.is_reference(imm):
150 if op_tag == "jalr":
144151 imm = bits.ref(imm, slice(12, 1))
145152 else:
146153 imm = bits.ref(imm, slice(11, 0))
147154 else:
148 imm = bits.i(subv.untag(imm, 'imm12'), 12)
155 imm = bits.i(subv.untag(imm, "imm12"), 12)
149156
150157 return [op, rd, sub, rs, imm]
151158
159
152160 def pack_s(instr):
153 """ verify & pack S-type instructions.
161 """verify & pack S-type instructions.
154162
155163 >>> _test_format(pack_s([(0x23, 'store'), (2, 'subop', 'word'), (5, 'rs', 't0'), (6, 'rs', 't1'), (0, 'off12')]))
156164 '23/7 00/5 2/3 05/5 06/5 00/7'
159167 '23/7 home/off12/[4:0] 2/3 05/5 06/5 home/off12/[11:5]'
160168 """
161169 (op, sub, rs1, rs2, imm) = instr
162 op = bits.u(subv.untag(op), 7)
163 sub = bits.u(subv.untag(sub, 'subop'), 3)
164 rs1 = bits.u(subv.untag(rs1, 'rs'), 5)
165 rs2 = bits.u(subv.untag(rs2, 'rs'), 5)
170 op = bits.u(subv.untag(op), 7)
171 sub = bits.u(subv.untag(sub, "subop"), 3)
172 rs1 = bits.u(subv.untag(rs1, "rs"), 5)
173 rs2 = bits.u(subv.untag(rs2, "rs"), 5)
166174
167175 if subv.is_reference(imm):
168176 imm = bits.ref(imm, slice(11, 0))
169177 else:
170 imm = bits.i(subv.untag(imm, 'off12'), 12)
178 imm = bits.i(subv.untag(imm, "off12"), 12)
171179
172180 imm_lo = imm[4:0]
173181 imm_hi = imm[11:5]
174182 return [op, imm_lo, sub, rs1, rs2, imm_hi]
175183
184
176185 def pack_j(instr):
177 """ verify & pack J-type instructions.
186 """verify & pack J-type instructions.
178187
179188 >>> _test_format(pack_j([(0x6f, 'jal'), (0, 'rd', 'x0'), (0, 'off20')]))
180189 '6f/7 00/5 00/8 0/1 000/10 0/1'
187196 """
188197 (op, rd, imm) = instr
189198 op = bits.u(subv.untag(op), 7)
190 rd = bits.u(subv.untag(rd, 'rd'), 5)
199 rd = bits.u(subv.untag(rd, "rd"), 5)
191200
192201 if subv.is_reference(imm):
193202 imm = bits.ref(imm, slice(20, 1))
194203 else:
195 imm = bits.i(subv.untag(imm, 'off20'), 20)
204 imm = bits.i(subv.untag(imm, "off20"), 20)
196205
197206 imm_lo = imm[9:0]
198207 imm_11 = imm[10]
201210
202211 return [op, rd, imm_hi, imm_11, imm_lo, imm_20]
203212
213
204214 def pack_b(instr):
205 """ verify & pack B-type instructions.
215 """verify & pack B-type instructions.
206216
207217 >>> _test_format(pack_b([(0x63, 'branch'), (0, 'subop', '=='), (6, 'rs'), (0, 'rs'), (0, 'off12')]))
208218 '63/7 0/1 0/4 0/3 06/5 00/5 00/6 0/1'
214224 '63/7 home/off12/[11:11] home/off12/[4:1] 0/3 06/5 00/5 home/off12/[10:5] home/off12/[12:12]'
215225 """
216226 (op, sub, rs1, rs2, imm) = instr
217 op = bits.u(subv.untag(op), 7)
218 sub = bits.u(subv.untag(sub, 'subop'), 3)
219 rs1 = bits.u(subv.untag(rs1, 'rs'), 5)
220 rs2 = bits.u(subv.untag(rs2, 'rs'), 5)
227 op = bits.u(subv.untag(op), 7)
228 sub = bits.u(subv.untag(sub, "subop"), 3)
229 rs1 = bits.u(subv.untag(rs1, "rs"), 5)
230 rs2 = bits.u(subv.untag(rs2, "rs"), 5)
221231
222232 if subv.is_reference(imm):
223233 imm = bits.ref(imm, slice(12, 1))
224234 else:
225 imm = bits.i(subv.untag(imm, 'off12'), 12)
235 imm = bits.i(subv.untag(imm, "off12"), 12)
226236
227237 imm_lo = imm[3:0]
228238 imm_md = imm[9:4]
231241
232242 return [op, imm_11, imm_lo, sub, rs1, rs2, imm_md, imm_12]
233243
244
234245 instr_map = {
235246 # 'opr': (pack_r, 0x33),
236 'load': (pack_i, 0x03),
237 'opi': (pack_i, 0x13),
238 'jalr': (pack_i, 0x67),
239 'store': (pack_s, 0x23),
240 'branch': (pack_b, 0x63),
241 'lui': (pack_u, 0x37),
242 'auipc': (pack_u, 0x17),
243 'jal': (pack_j, 0x6f),
247 "load": (pack_i, 0x03),
248 "opi": (pack_i, 0x13),
249 "jalr": (pack_i, 0x67),
250 "store": (pack_s, 0x23),
251 "branch": (pack_b, 0x63),
252 "lui": (pack_u, 0x37),
253 "auipc": (pack_u, 0x17),
254 "jal": (pack_j, 0x6F),
244255 }
256
245257
246258 @subv.with_parsed_lines
247259 def format(iter):
248260 for segment, line in iter:
249 if line['type'] == 'instr' and segment == 'code':
250 op = line['instr'][0]
251 assert len(op) == 2, 'instruction without op label: {}'.format(op)
261 if line["type"] == "instr" and segment == "code":
262 op = line["instr"][0]
263 assert len(op) == 2, "instruction without op label: {}".format(op)
252264
253265 (op, label) = op
254266 if label not in instr_map:
255267 raise ValueError("unknown instruction label: {}".format(label))
256268 (formatter, expected) = instr_map[label]
257269 if op != expected:
258 raise ValueError("opcode {} doesn't match label {} (expected {})"
259 .format(op, label, expected))
260
261 line['instr'] = formatter(line['instr'])
270 raise ValueError(
271 "opcode {} doesn't match label {} (expected {})".format(
272 op, label, expected
273 )
274 )
275
276 line["instr"] = formatter(line["instr"])
262277 line = yield subv.format(line)
263278 else:
264 line = yield line['raw']
265
266 if __name__ == '__main__':
279 line = yield line["raw"]
280
281
282 if __name__ == "__main__":
267283 import sys
284
268285 for line in format(sys.stdin):
269286 print(line)
4040 import subv
4141 import bits
4242
43
4344 def byteify(word):
44 """ split longer bitfield into bytes.
45 """split longer bitfield into bytes.
4546
4647 >>> byteify(bits.Bitfield(0x12345678, 32))
4748 [(120,), (86,), (52,), (18,)]
5657
5758 out = []
5859 for i in range(0, word.size, 8):
59 byte = word[i+7:i]
60 byte = word[i + 7 : i]
6061 out.append((byte.val,))
6162 return out
63
6264
6365 @subv.with_parsed_lines
6466 def pack(iter):
6971 tmp = out[:]
7072 out.clear()
7173 return {
72 'type': 'instr',
73 'instr': tmp,
74 'comment': None,
74 "type": "instr",
75 "instr": tmp,
76 "comment": None,
7577 }
7678
7779 last_segment = None
8183 if out:
8284 yield subv.format(flush_bytes())
8385
84 if line['type'] == 'instr':
85 for part in line['instr']:
86 if line["type"] == "instr":
87 for part in line["instr"]:
8688 buf = bits.from_part(part) & buf
8789 else:
88 yield line['raw']
90 yield line["raw"]
8991
9092 while buf.size >= 8:
9193 byte = buf[7:0]
92 buf = buf.slice_allowempty(slice(buf.size-1, 8))
94 buf = buf.slice_allowempty(slice(buf.size - 1, 8))
9395 out.append((byte.val,))
9496
9597 if len(out) == 4:
99101
100102 assert buf.size == 0, "segment '{}' end isn't byte-aligned".format(segment)
101103
102 if __name__ == '__main__':
104
105 if __name__ == "__main__":
103106 import sys
107
104108 for line in pack(sys.stdin):
105109 print(line)
00 import re
11 import bits
2 white = re.compile(r'[ \t\.\n]+')
3 hex = re.compile(r'^\-?(0x)?[0-9a-f]+$')
4 num = re.compile(r'^\d+$')
5 ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?(?:\[(\d+):(\d+)\])?$')
6 field_re = re.compile(r'^(imm|off)(\d+)$')
2
3 white = re.compile(r"[ \t\.\n]+")
4 hex = re.compile(r"^\-?(0x)?[0-9a-f]+$")
5 num = re.compile(r"^\d+$")
6 ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?(?:\[(\d+):(\d+)\])?$")
7 field_re = re.compile(r"^(imm|off)(\d+)$")
78
89 # parsing
910 def parse_part(part):
10 """ parse a literal with attached metadata.
11 """parse a literal with attached metadata.
1112
1213 >>> parse_part('0')
1314 (0,)
4445 >>> parse_part('$label:suff[11:0]/tag*')
4546 ('$label:suff[11:0]', 'tag*')
4647 """
47 part = part.split('/')
48 part = part.split("/")
4849 if hex.match(part[0]):
4950 part[0] = int(part[0], 16)
5051 return tuple(part)
5152
53
5254 def parse_instr(line):
53 """ parse an instruction-line.
55 """parse an instruction-line.
5456
5557 >>> parse_instr('ff/op 0/subop/add 1/rd/x1 label[11:0]/imm12')
5658 [(255, 'op'), (0, 'subop', 'add'), (1, 'rd', 'x1'), ('label[11:0]', 'imm12')]
5759 """
5860 parts = white.split(line)
59 parts = [parse_part(part) for part in parts if part != '']
61 parts = [parse_part(part) for part in parts if part != ""]
6062 return parts
6163
64
6265 def parse_segment(line):
63 """ parse a segment-line.
66 """parse a segment-line.
6467
6568 >>> parse_segment('== code 0x8000')
6669 ('code', 32768)
6972 """
7073 parts = white.split(line)
7174 if len(parts) == 3:
72 return (parts[1], int(parts[2], 16))
75 return (parts[1], int(parts[2], 16))
7376 elif len(parts) == 2:
74 return (parts[1],)
77 return (parts[1],)
7578 else:
76 raise ValueError("invalid segment line")
79 raise ValueError("invalid segment line")
80
7781
7882 def parse_label(line):
79 """ parse a label-line.
83 """parse a label-line.
8084
8185 >>> parse_label('some_label:')
8286 'some_label'
8589 """
8690 return line[:-1]
8791
92
8893 def parse_reference(part):
89 """ parse a sliced-label part.
94 """parse a sliced-label part.
9095
9196 >>> parse_reference(('lbl', 'imm32'))
9297 {'label': 'lbl', 'mode': 'imm', 'size': 32, 'meta': ()}
108113
109114 label, off, hi, lo = ref_re.match(ref).groups()
110115 mode, size = field_re.match(field).groups()
111
116
112117 ref = {
113 'label': label,
114 'mode': mode,
115 'size': int(size),
116 'meta': part[2:],
118 "label": label,
119 "mode": mode,
120 "size": int(size),
121 "meta": part[2:],
117122 }
118123
119124 if off is not None:
120 ref['offset'] = int(off)
125 ref["offset"] = int(off)
121126
122127 if hi is not None:
123 ref['hi'] = int(hi)
124 ref['lo'] = int(lo)
128 ref["hi"] = int(hi)
129 ref["lo"] = int(lo)
125130
126131 return ref
127132
133
128134 def classify(line):
129 """ classify cleaned lines.
135 """classify cleaned lines.
130136
131137 >>> classify('')
132138 'empty'
143149 >>> classify('ff/8 0/3 2/5')
144150 'instr'
145151 """
146 if line == '':
147 return 'empty'
148 elif line.startswith('=='): # segment
149 return 'segment'
150 elif line.endswith(':'): # label
151 return 'label'
152 if line == "":
153 return "empty"
154 elif line.startswith("=="): # segment
155 return "segment"
156 elif line.endswith(":"): # label
157 return "label"
152158 else:
153 return 'instr'
159 return "instr"
160
154161
155162 def parse(line):
156 """ clean, classify and parse lines.
157 """
163 """clean, classify and parse lines."""
158164 raw = line.strip()
159 split = raw.split('#', 1)
165 split = raw.split("#", 1)
160166 if len(split) == 1:
161167 clean = raw
162168 comment = None
165171
166172 type = classify(clean)
167173
168 if type == 'segment':
174 if type == "segment":
169175 parsed = parse_segment(clean)
170 elif type == 'label':
176 elif type == "label":
171177 parsed = parse_label(clean)
172 elif type == 'instr':
178 elif type == "instr":
173179 parsed = parse_instr(clean)
174180 else:
175181 parsed = None
176182
177183 return {
178 'type': type,
179 'raw': raw,
180 'line': clean,
181 'comment': comment,
184 "type": type,
185 "raw": raw,
186 "line": clean,
187 "comment": comment,
182188 type: parsed,
183189 }
184190
191
185192 def is_reference(part):
186 """ check whether a part is a label reference.
193 """check whether a part is a label reference.
187194
188195 >>> is_reference(('hello',))
189196 True
215222 """
216223 return isinstance(part[0], str)
217224
225
218226 def untag(part, expect=None):
219 """ returns the value of a part and optionally verifies the first tag.
227 """returns the value of a part and optionally verifies the first tag.
220228
221229 >>> untag((2, 'num'))
222230 2
254262 raise ValueError("expected {} to be labelled one of {}".format(part, expect))
255263 return part[0]
256264
265
257266 def format_reference(ref):
258 """ opposite of parse_reference..
267 """opposite of parse_reference..
259268
260269 >>> format_reference({'label': 'lbl', 'mode': 'imm', 'size': 32, 'meta': ()})
261270 ('lbl', 'imm32')
270279 >>> format_reference({'label': 'x', 'offset': -16, 'mode': 'off', 'size': 12, 'meta': ('extra', 'stuff'), 'hi': 11, 'lo': 0})
271280 ('x-16[11:0]', 'off12', 'extra', 'stuff')
272281 """
273 label = ref['label']
274 if 'offset' in ref:
275 label += '{:+}'.format(ref['offset'])
276 if 'hi' in ref:
277 label += '[{hi}:{lo}]'.format(**ref)
278 return (label, '{mode}{size}'.format(**ref), *ref['meta'])
282 label = ref["label"]
283 if "offset" in ref:
284 label += "{:+}".format(ref["offset"])
285 if "hi" in ref:
286 label += "[{hi}:{lo}]".format(**ref)
287 return (label, "{mode}{size}".format(**ref), *ref["meta"])
288
279289
280290 def format_part(part):
281 """ opposite of parse_part.
291 """opposite of parse_part.
282292
283293 >>> format_part((0,))
284294 '00'
301311 if isinstance(part, bits.WordBase):
302312 return str(part)
303313 elif not is_reference(part):
304 first = '{:02x}'.format(part[0])
314 first = "{:02x}".format(part[0])
305315 part = (first, *part[1:])
306 return '/'.join([str(p) for p in part])
316 return "/".join([str(p) for p in part])
317
307318
308319 def format(line):
309 """ opposite of parse.
320 """opposite of parse.
310321
311322 >>> format({
312323 ... 'type': 'instr',
315326 ... })
316327 'ff/op 00/subop/add 01/rd/x1 label[11:0]/imm12 # this does things.'
317328 """
318 type = line['type']
319 if type == 'instr' or type == 'data':
320 packed = ' '.join(format_part(part) for part in line[type])
321 if line['comment']:
322 packed = packed + ' # ' + line['comment']
329 type = line["type"]
330 if type == "instr" or type == "data":
331 packed = " ".join(format_part(part) for part in line[type])
332 if line["comment"]:
333 packed = packed + " # " + line["comment"]
323334 return packed
324335 else:
325336 raise NotImplementedError("type {}".format(type))
326337
338
327339 def dump(line):
328 """ debug-friendly string representation of parsed lines.
340 """debug-friendly string representation of parsed lines.
329341
330342 >>> dump({
331343 ... 'type': 'instr',
335347 "instr[(255, 'op'), (0, 'subop', 'add'), (1, 'rd', 'x1'), ('label[11:0]', 'imm12')]"
336348 """
337349
338 return '{}{}'.format(line['type'], line[line['type']])
350 return "{}{}".format(line["type"], line[line["type"]])
351
339352
340353 def join_all(gen):
341 res = '\n'.join(gen)
354 res = "\n".join(gen)
342355 return res
343356
344357
345358 class SubVException(Exception):
346359 pass
360
347361
348362 class LineIterator(object):
349363 def __init__(self, stream):
360374 self.line = None
361375 try:
362376 self.line = parse(self.raw_line)
363 if self.line['type'] == 'segment':
364 self.segment = self.line['segment'][0]
377 if self.line["type"] == "segment":
378 self.segment = self.line["segment"][0]
365379 except Exception as e:
366380 raise self.exception("failed to parse line") from e
367381 return (self.segment, self.line)
368382
369383 def exception(self, msg):
370 stream_name = getattr(self.stream, 'name', '(unnamed)')
384 stream_name = getattr(self.stream, "name", "(unnamed)")
371385 if self.line:
372386 msg = msg + "\n{}:{}: {}".format(stream_name, self.i, format(self.line))
373387 msg = msg + "\nparsed as {}".format(dump(self.line))
374388 elif self.raw_line:
375 msg = msg + "\n{}:{}: {}".format(stream_name, self.i, self.raw_line.strip())
389 msg = msg + "\n{}:{}: {}".format(
390 stream_name, self.i, self.raw_line.strip()
391 )
376392 return SubVException(msg)
393
377394
378395 def with_parsed_lines(process_fn):
379396 def _wrapped(iter):
383400 except SubVException:
384401 raise
385402 except Exception as e:
386 raise iterator.exception("failed to {} line".format(process_fn.__name__)) from e
403 raise iterator.exception(
404 "failed to {} line".format(process_fn.__name__)
405 ) from e
387406
388407 return _wrapped
4949 import bits
5050 import re
5151
52 slice_re = re.compile(r'^([^\[]+)(?:\[(\d+):(\d+)\])?$')
53 field_re = re.compile(r'^(imm|off)(\d+)$')
52 slice_re = re.compile(r"^([^\[]+)(?:\[(\d+):(\d+)\])?$")
53 field_re = re.compile(r"^(imm|off)(\d+)$")
54
5455
5556 def observe(word, pc, map):
56 """ resolve a label reference.
57 """resolve a label reference.
5758
5859 >>> observe((3, '3'), 0, {})
5960 (3, '3')
8586 word = bits.ref(word)
8687 return word.as_value(pc=pc, labels=map)
8788
89
8890 @subv.with_parsed_lines
8991 def survey(iter):
9092 queue = []
9193 map = {}
9294 addr, bitcount = -1, 0
9395 for segment, line in iter:
94 line['addr'] = addr
96 line["addr"] = addr
9597 queue.append(line)
9698
9799 # step forward addr
98 type = line['type']
99 if type == 'segment':
100 addr, bitcount = line['segment'][1], 0
101 elif type == 'label':
100 type = line["type"]
101 if type == "segment":
102 addr, bitcount = line["segment"][1], 0
103 elif type == "label":
102104 assert bitcount == 0, "label isn't byte aligned"
103 map[line['label']] = addr
104 elif type == 'instr':
105 if segment == 'data':
106 for part in line['instr']:
105 map[line["label"]] = addr
106 elif type == "instr":
107 if segment == "data":
108 for part in line["instr"]:
107109 bitcount += int(part[1])
108110
109111 if bitcount >= 8:
110112 addr += bitcount // 8
111113 bitcount = bitcount % 8
112 elif segment == 'code':
113 assert bitcount == 0, "instruction isn't byte aligned ({} bitcount left)".format(8 - bitcount)
114 elif segment == "code":
115 assert (
116 bitcount == 0
117 ), "instruction isn't byte aligned ({} bitcount left)".format(
118 8 - bitcount
119 )
114120 assert addr % 2 == 0, "instruction isn't 2-byte aligned"
115121
116122 bitcount = 0
117 for part in line['instr']:
123 for part in line["instr"]:
118124 if subv.is_reference(part):
119125 ref = bits.ref(part)
120126 bitcount += ref.size
121127 else:
122128 bitcount += int(part[1])
123129
124 assert bitcount % 8 == 0, "instruction size not multiple of 8 bitcount: {}".format(bitcount)
130 assert (
131 bitcount % 8 == 0
132 ), "instruction size not multiple of 8 bitcount: {}".format(bitcount)
125133 addr += bitcount // 8
126134 bitcount = 0
127135 else:
129137
130138 for i, line in enumerate(queue, start=1):
131139 try:
132 type = line['type']
133 if type == 'instr':
140 type = line["type"]
141 if type == "instr":
134142 instr = []
135 if type == 'instr':
143 if type == "instr":
136144 # strip label from opcode
137145 op = line[type].pop(0)
138146 instr.append(op[:2])
139147
140148 for part in line[type]:
141 observed = observe(part, line['addr'], map)
149 observed = observe(part, line["addr"], map)
142150 instr.append(observed)
143151 line[type] = instr
144152 yield subv.format(line)
145 elif type == 'segment':
146 yield line['raw']
153 elif type == "segment":
154 yield line["raw"]
147155 except Exception as e:
148156 msg = "failed to survey line"
149 msg = msg + "\n{}:{}: {}".format('(unknown)', i, subv.format(line))
157 msg = msg + "\n{}:{}: {}".format("(unknown)", i, subv.format(line))
150158 msg = msg + "\nparsed as {}".format(subv.dump(line))
151159 raise subv.SubVException(msg) from e
152160
153 if __name__ == '__main__':
161
162 if __name__ == "__main__":
154163 import sys
164
155165 for line in survey(sys.stdin):
156166 print(line)