diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-27 21:10:29 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-05-27 21:10:29 +0000 |
| commit | 132db77501f0399c413886c885b580f0070b54c0 (patch) | |
| tree | 37ce9a89e2ebce87be93b05727c8e06bee793aca | |
| parent | add test.py (diff) | |
| download | subv-132db77501f0399c413886c885b580f0070b54c0.tar.gz subv-132db77501f0399c413886c885b580f0070b54c0.zip | |
output emulatable .elf
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | README.md | 22 | ||||
| -rwxr-xr-x | elf.py | 105 | ||||
| -rwxr-xr-x | qemu.sh | 2 | ||||
| -rw-r--r-- | riscv.py | 31 | ||||
| -rw-r--r-- | subx.py | 40 | ||||
| -rwxr-xr-x[-rw-r--r--] | test.py | 74 |
7 files changed, 220 insertions, 56 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6c512b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.elf +__pycache__ diff --git a/README.md b/README.md new file mode 100644 index 0000000..df03318 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +SubV +==== + +This is a wip clone of [SubX][mu] for the RISC-V RV31I base ISA. + + $ ./test.py | ./elf.py > out.elf + $ ./qemu.sh out.elf + +Debugging +--------- + +You can hook gdb into `qemu` using the `-s` flag, and make it halt on start +using the `-S` flag. `gdb` can be attached using `target remote localhost:1234`: + + $ ./qemu.sh out.elf -S -s + # in another terminal + $ riscv32-elf-gdb -iex "target remote localhost:1234" + layout asm # show assembly trace + nexti # step forward one instruction + c # free-run forward + +[mu]: https://github.com/akkartik/mu @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +import sys +from subx import clean, classify, parse_segment, parse_instr, format_instr + +""" +Takes a packed & surveyed hex stream (with section headers) and packs it into a +simulatable ELF file that can be run as a kernel: + + qemu-system-riscv32 -nographic -machine sifive_u -nographic -kernel out.elf + +sifive-u has lots of memory mapped peripherals, see +https://github.com/qemu/qemu/blob/master/hw/riscv/sifive_u.c#L66-L83 + +Among them are two memory-mapped virtual UARTs at 0x10010000 and 0x10011000. +Writing to UART0+0x0 writes to qemu's output TTY, and reading from UART0+0x4 +reads from it. The MSB in UART0+0x4 is set if there is nothing to read. +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) + else: + if segment == None: + raise ValueError("label or code outside of segment!") + + if type == 'label': + raise NotImplementedError() + else: + instr = parse_instr(line) + segment['content'] += instr + +entrypoint = 0x80000000 +cursor = 0 + +def w(b): + global cursor + cursor += len(b) + sys.stdout.buffer.write(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) @@ -0,0 +1,2 @@ +#!/bin/sh +qemu-system-riscv32 -nographic -machine sifive_u -nographic -kernel "$@" diff --git a/riscv.py b/riscv.py new file mode 100644 index 0000000..b2af4cc --- /dev/null +++ b/riscv.py @@ -0,0 +1,31 @@ +def format_r(op, rd, r1, r2, funct3, funct7): + # opcode[7] rd[5] funct3[3] rs1[5] rs2[5] funct7[7] + b0 = (rd & 0x1) << 7 | op + b1 = (r1 & 0x1) << 7 | funct3 << 4 | rd >> 1 + b2 = (r2 & 0xf) << 4 | r1 >> 1 + b3 = funct7 << 1 | r2 >> 4 + return [(b0,), (b1,), (b2,), (b3,)] + +def format_i(op, rd, r1, imm12, funct3): + # opcode[7] rd[5] funct3[3] rs1[5] imm[12] + b0 = (rd & 0x1) << 7 | op + b1 = (r1 & 0x1) << 7 | funct3 << 4 | rd >> 1 + b2 = (imm12 & 0xf) << 4 | r1 >> 1 + b3 = imm12 >> 4 + return [(b0,), (b1,), (b2,), (b3,)] + +def format_s(op, r1, r2, imm12, funct3): + # opcode[7] rd[5] funct3[3] rs1[5] rs2[5] funct7[7] + 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): + # opcode[7] rd[5] imm31:12[20] + b0 = (rd & 0x1) << 7 | op + b1 = (imm20 & 0xf) << 4 | rd >> 1 + b2 = (imm20 >> 4) & 0xff + b3 = imm20 >> 12 + return [(b0,), (b1,), (b2,), (b3,)] @@ -0,0 +1,40 @@ +import re +white = re.compile('[ \t\.\n]+') +hex = re.compile('^(0x)?[0-9a-f]+$') + +def parse_part(part): + part = part.split('/') + if hex.match(part[0]): + part[0] = int(part[0], 16) + return tuple(part) + +def parse_instr(line): + parts = white.split(line) + parts = [parse_part(part) for part in parts if part != ''] + return parts + +def parse_segment(line): + parts = white.split(line) + return (parts[1], int(parts[2], 16)) + +def format_part(part): + if not isinstance(part[0], str): + part = ('{:02x}'.format(part[0]),) + part[1:] + return '/'.join(part) + +def format_instr(inst, comment=None): + packed = ' '.join(format_part(part) for part in inst) + if comment: + packed = packed + ' # ' + comment + return packed + +def clean(line): + return line.strip().split('#')[0] + +def classify(line): + if line.startswith('=='): # segment + return 'segment' + elif line.endswith(':'): # label + return 'label' + else: + return 'instr' @@ -1,59 +1,21 @@ #!/usr/bin/env python3 - -import re -white = re.compile('[ \t\.\n]+') -hex = re.compile('^(0x)?[0-9a-f]+$') - -def format_r(op, rd, r1, r2, funct3, funct7): - # opcode[7] rd[5] funct3[3] rs1[5] rs2[5] funct7[7] - b0 = (rd & 0x1) << 7 | op - b1 = (r1 & 0x1) << 7 | funct3 << 4 | rd >> 1 - b2 = (r2 & 0xf) << 4 | r1 >> 1 - b3 = funct7 << 1 | r2 >> 4 - return [(b3,), (b2,), (b1,), (b0,)] - -def parse_part(part): - part = part.split('/') - if hex.match(part[0]): - part[0] = int(part[0], 16) - return tuple(part) - -def parse_instr(line): - parts = white.split(line) - parts = [parse_part(part) for part in parts if part != ''] - return parts - -def translate_instr(orig): - return orig - -def format_part(part): - if not isinstance(part[0], str): - part = ('0x{:02x}'.format(part[0]),) + part[1:] - return '/'.join(part) - -def format_instr(inst, comment=None): - packed = ' '.join(format_part(part) for part in inst) - if comment: - packed = packed + ' # ' + comment - return packed +from riscv import format_u, format_i, format_s +from subx import format_instr if __name__ == "__main__": - import sys - - seg = 'code' - for line in sys.stdin: - line = line.rstrip() - if line.startswith('=='): # segment - print(line) - seg = line.split(' ')[1] - elif line.endswith(':'): # label - print(line) - elif seg != 'code': - print(line) - else: - parts = parse_instr(line) - risc = translate_instr(parts) - print(format_instr(risc, line)) - - # emit_instr(format_r(0x33, 0x5, 0x6, 0x7, 0, 0)) - # emit_instr(format_r(0x33, 0x5, 0x6, 0x7, 0, 0), "add eax, 2") + 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)")) |
