aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-27 21:10:29 +0000
committers-ol <s-ol@users.noreply.github.com>2020-05-27 21:10:29 +0000
commit132db77501f0399c413886c885b580f0070b54c0 (patch)
tree37ce9a89e2ebce87be93b05727c8e06bee793aca
parentadd test.py (diff)
downloadsubv-132db77501f0399c413886c885b580f0070b54c0.tar.gz
subv-132db77501f0399c413886c885b580f0070b54c0.zip
output emulatable .elf
-rw-r--r--.gitignore2
-rw-r--r--README.md22
-rwxr-xr-xelf.py105
-rwxr-xr-xqemu.sh2
-rw-r--r--riscv.py31
-rw-r--r--subx.py40
-rwxr-xr-x[-rw-r--r--]test.py74
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
diff --git a/elf.py b/elf.py
new file mode 100755
index 0000000..8990c88
--- /dev/null
+++ b/elf.py
@@ -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)
diff --git a/qemu.sh b/qemu.sh
new file mode 100755
index 0000000..7cc3bde
--- /dev/null
+++ b/qemu.sh
@@ -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,)]
diff --git a/subx.py b/subx.py
new file mode 100644
index 0000000..53f72ce
--- /dev/null
+++ b/subx.py
@@ -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'
diff --git a/test.py b/test.py
index 525e9ca..ba6ffd6 100644..100755
--- a/test.py
+++ b/test.py
@@ -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)"))