From 6e3e5e6aaf1bc9b8f2d787fdf5f9bbd714776b5d Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Sat, 10 Apr 2021 19:38:40 +0200 Subject: reorder everything --- .gitignore | 1 + instructions.py | 145 --------------------------------- memory.py | 112 ------------------------- pysc-v/Frontend/__init__.py | 0 pysc-v/Frontend/lexer.py | 4 + pysc-v/Frontend/reader.py | 34 ++++++++ pysc-v/InstructionSets/RV32C.py | 29 +++++++ pysc-v/InstructionSets/RV32D.py | 0 pysc-v/InstructionSets/RV32F.py | 0 pysc-v/InstructionSets/RV32I.py | 71 ++++++++++++++++ pysc-v/InstructionSets/__init__.py | 0 pysc-v/InstructionSets/instructions.py | 30 +++++++ pysc-v/main.py | 5 ++ pysc-v/memory.py | 112 +++++++++++++++++++++++++ pysc-v/registers/RV32I.py | 116 ++++++++++++++++++++++++++ pysc-v/registers/__init__.py | 0 reader.py | 34 -------- registersRV32I.py | 116 -------------------------- 18 files changed, 402 insertions(+), 407 deletions(-) create mode 100644 .gitignore delete mode 100644 instructions.py delete mode 100644 memory.py create mode 100644 pysc-v/Frontend/__init__.py create mode 100644 pysc-v/Frontend/lexer.py create mode 100644 pysc-v/Frontend/reader.py create mode 100644 pysc-v/InstructionSets/RV32C.py create mode 100644 pysc-v/InstructionSets/RV32D.py create mode 100644 pysc-v/InstructionSets/RV32F.py create mode 100644 pysc-v/InstructionSets/RV32I.py create mode 100644 pysc-v/InstructionSets/__init__.py create mode 100644 pysc-v/InstructionSets/instructions.py create mode 100644 pysc-v/main.py create mode 100644 pysc-v/memory.py create mode 100644 pysc-v/registers/RV32I.py create mode 100644 pysc-v/registers/__init__.py delete mode 100644 reader.py delete mode 100644 registersRV32I.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/instructions.py b/instructions.py deleted file mode 100644 index 0bfda4c..0000000 --- a/instructions.py +++ /dev/null @@ -1,145 +0,0 @@ -# Full list of instructions -""" -U lui -U auipc -J jal -I jalr -B beq -B bne -B blt -B bge -B bltu -B bgeu -I lb -I lh -I lw -I lbu -I lhu -S sb -S sh -S sw -I addi -I slti -I sltiu -I xori -I ori -I andi -I slli -I srli -I srai -R add -R sub -R sll -R slt -R sltu -R xor -R srl -R sra -R or -R and -I fence -I fence.i -I ecall -I ebreak -I csrrw -I csrrs -I csrrc -I csrrwi -I csrrsi -I csrrci -""" - - -from ctypes import c_uint32 - -class Instruction: - - self.memory = None - self.regs = None - - def __init__(self): - pass - - def compile(self): - # return the binstream of the instruction in a c_uint32 - pass - - def execute(self): - # executes the instruction and returns the next program counter - return - -class R(Instruction): - funct3 = None - funct7 = None - opcode = None - def __init__(self, rd, rs1, rs2): - self.rd = rd - self.rs1 = rs1 - self.rs2 = rs2 - - def compile(self): - # TODO: ensure sizes and convert register names to number... - return c_uint32( - (self.funct7 << 25) +\ - (self.rs2 << 20) +\ - (self.rs1 << 15) +\ - (self.funct3 << 12) +\ - (self.rd << 7) +\ - self.opcode - ) - -class I(Instruction): - funct3 = None - opcode = None - - def __init__(self, rd, rs, imm): - self.rd = rd - self.rs = rs - self.imm = imm - - def compile(self): - return c_uint32( - (self.imm << 20) +\ - (self.rs << 15) +\ - (self.funct3 << 12) +\ - (self.rd << 7) +\ - self.opcode - ) - -class S(Instruction): - pass - -class B(Instruction): - pass - -class U(Instruction): - pass - -class J(Instruction): - pass - - - -class add(R): - name = "add" - opcode = 0b0110011 - funct3 = 0b000 - funct7 = 0b0000000 - - def execute(self): - pass - - -class addi(I): - name = "addi" - opcode = 0b0010011 - funct3 = 0b000 - - def execute(self): - pass - -if __name__ == "__main__": - r = Regs() - # This is the interface i'd love to have - addins = add("x5","x2","zero") - j("labelName") diff --git a/memory.py b/memory.py deleted file mode 100644 index 84bf0bd..0000000 --- a/memory.py +++ /dev/null @@ -1,112 +0,0 @@ -from ctypes import c_uint8, c_uint16, c_uint32 - -class Addressable: - def __init__(self, start=0, end=0): - self.start = start - self.end = end - def addr_to_pos(self, addr): - if not (self.start <= addr < self.end): - raise KeyError("Address out of space") - return addr - self.start - -def merge_bytes (byte_iter): - val = 0 - for i,v in enumerate(byte_ouput): - val += v.value << 8 * i - return val -def split_bytes (val, byte_count): - return tuple( c_uint8(val >> 8 * i) for i in range(byte_count) ) - -class Memory32 (Addressable): - """ - This is a raw 32-bit word memory addressable at a byte level. - Internally it is defined as c_uint8 list. - Special functions are needed to access halfs (c_uint16) and words - (c_uint32). - """ - - def __init__(self, start=0, end=0, bigEndian=False): - super().__init__(start, end) - self.bigEndian = bigEndian - self.lastChange = None - # Pre-allocate or allocate or allocate per write? - self.data = [None] * (end - start) - - def get_byte(self, addr): - return self.data[ self.addr_to_pos(addr) ] - def set_byte(self, addr, val): - if not isinstance(val, c_uint8): - if val > 0xFF: - raise ValueError("Value is larger than a byte") - self.data[ self.addr_to_pos(addr) ] = val if isinstance(val, c_uint8) else c_uint8(val) - self.lastChange = range(addr, addr+1) - - - def get_half(self,addr): - byte_output = (get_byte(addr), get_byte(addr+1)) - if self.bigEndian: - byte_output = reversed(byte_output) - return c_uint16( merge_bytes(byte_output) ) - - def set_half(self, addr, val): - if not isinstance(val, c_uint16): - if val > 0xFFFF: - raise ValueError("Value is larger than a half") - bytes = split_bytes(val, 2) - if self.bigEndian: - bytes = reversed(bytes) - for i,v in enumerate(bytes): - self.set_byte(addr+i, v) - self.lastChange = range(addr,addr+2) - - def get_word(self, pos): - byte_output = tuple(get_byte(addr+i) for i in range(4)) - if self.bigEndian: - byte_output = reversed(byte_output) - return c_uint32( merge_bytes(byte_output) ) - - def set_word(self, addr, val): - if not isinstance(val, c_uint32): - if val > 0xFFFFFFFF: - raise ValueError("Value is larger than a word") - bytes = split_bytes(val, 4) - if self.bigEndian: - bytes = reversed(bytes) - for i,v in enumerate(bytes): - self.set_byte(addr+i, v) - self.lastChange = range(addr,addr+4) - - def __str__(self): - out = " " - out += "-" * 50 - out += "\n" - for i,d in enumerate(self.data): - if d is not None: - addr = i+self.start - out += "->" if addr in self.lastChange else " " - out += f"| {addr:#20x} | {d.value:#5} | {d.value:#04x} | {d.value:#010b} |" - out += "\n" - out += " " - out += "-" * 50 - out += "\n" - return out - - -class Memory32RO (Memory32): - def set_byte(self, addr, val): - raise NotImplementedError("Trying to write in a read-only memory") - def set_half(self, addr, val): - raise NotImplementedError("Trying to write in a read-only memory") - def set_word(self, addr, val): - raise NotImplementedError("Trying to write in a read-only memory") - -class CodeMemory32 (Memory32RO): - def __init__(self, start=0, end=0): - super().__init__(writable=False,start=start,end=end) - - -if __name__ == "__main__": - m = Memory32(start=100,end=200) - print(m.start, m.end) - m.set_byte(100, 246) - print(m) diff --git a/pysc-v/Frontend/__init__.py b/pysc-v/Frontend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pysc-v/Frontend/lexer.py b/pysc-v/Frontend/lexer.py new file mode 100644 index 0000000..5175097 --- /dev/null +++ b/pysc-v/Frontend/lexer.py @@ -0,0 +1,4 @@ +# Returns: +# - label +# - instruction (with args) +# - directive (with args) diff --git a/pysc-v/Frontend/reader.py b/pysc-v/Frontend/reader.py new file mode 100644 index 0000000..45d155c --- /dev/null +++ b/pysc-v/Frontend/reader.py @@ -0,0 +1,34 @@ +class Reader: + def __init__(self, filename): + self.filename = filename + self._file = None + self.lineno = 0 + self.charno = 0 + + def __enter__(self): + self._file = open(self.filename, "r") + return self + + def __exit__(self, type, value, tb): + self._file.close() + + def char(self): + ch = self._file.read(1) + self.charno += 1 + if ch == "\n": + self.lineno += 1 + self.charno = 0 + return ch + + def peek(self): + pos = self._file.tell() + ch = self._file.read(1) + self._file.seek(pos) + return ch + + +if __name__ == "__main__": + with Reader("reader.py") as src: + print(src.peek()) + while src.peek() != "": + print(src.char()) diff --git a/pysc-v/InstructionSets/RV32C.py b/pysc-v/InstructionSets/RV32C.py new file mode 100644 index 0000000..e740ac4 --- /dev/null +++ b/pysc-v/InstructionSets/RV32C.py @@ -0,0 +1,29 @@ +from instructions import Instruction, InstructionSet + +class Compressed(Instruction): + size = 2 + + +class CR(Compressed): + pass + +class CI(Compressed): + pass + +class CSS(Compressed): + pass + +class CIW(Compressed): + pass + +class CJ(Compressed): + pass + +class CB(Compressed): + pass + +class CL(Compressed): + pass + +class CS(Compressed): + pass diff --git a/pysc-v/InstructionSets/RV32D.py b/pysc-v/InstructionSets/RV32D.py new file mode 100644 index 0000000..e69de29 diff --git a/pysc-v/InstructionSets/RV32F.py b/pysc-v/InstructionSets/RV32F.py new file mode 100644 index 0000000..e69de29 diff --git a/pysc-v/InstructionSets/RV32I.py b/pysc-v/InstructionSets/RV32I.py new file mode 100644 index 0000000..967b24d --- /dev/null +++ b/pysc-v/InstructionSets/RV32I.py @@ -0,0 +1,71 @@ +from instructions import Instruction, InstructionSet + +RV32I = InstructionSet() + +class R(Instruction): + funct3 = None + funct7 = None + opcode = None + def __init__(self, rd, rs1, rs2): + self.rd = rd + self.rs1 = rs1 + self.rs2 = rs2 + + def compile(self): + # TODO: ensure sizes and convert register names to number... + return c_uint32( + (self.funct7 << 25) +\ + (self.rs2 << 20) +\ + (self.rs1 << 15) +\ + (self.funct3 << 12) +\ + (self.rd << 7) +\ + self.opcode + ) + +class I(Instruction): + funct3 = None + opcode = None + + def __init__(self, rd, rs, imm): + self.rd = rd + self.rs = rs + self.imm = imm + + def compile(self): + return c_uint32( + (self.imm << 20) +\ + (self.rs << 15) +\ + (self.funct3 << 12) +\ + (self.rd << 7) +\ + self.opcode + ) + +class S(Instruction): + pass + +class B(Instruction): + pass + +class U(Instruction): + pass + +class J(Instruction): + pass + + +class add(R): + name = "add" + opcode = 0b0110011 + funct3 = 0b000 + funct7 = 0b0000000 + + def execute(self): + return pc + self.size + +class addi(I): + name = "addi" + opcode = 0b0010011 + funct3 = 0b000 + + def execute(self): + pass diff --git a/pysc-v/InstructionSets/__init__.py b/pysc-v/InstructionSets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pysc-v/InstructionSets/instructions.py b/pysc-v/InstructionSets/instructions.py new file mode 100644 index 0000000..5587f2e --- /dev/null +++ b/pysc-v/InstructionSets/instructions.py @@ -0,0 +1,30 @@ +from ctypes import c_uint32 + + +class Instruction: + size = 4 # Instruction size in bytes + + def __init__(self): + pass + + def compile(self): + # return the binstream of the instruction in a c_uint32 + pass + + def execute(self): + # executes the instruction and returns the next program counter + return + +class InstructionSet: + def __init__(self, init=None): + self.data = dict() + + def add_instruction(self, ins): + if ins.name not in self.data: + self.data[ins.name] = ins + + +if __name__ == "__main__": + # TODO This is the interface i'd love to have + addins = add("x5","x2","zero") + j("labelName") diff --git a/pysc-v/main.py b/pysc-v/main.py new file mode 100644 index 0000000..2d6f6a5 --- /dev/null +++ b/pysc-v/main.py @@ -0,0 +1,5 @@ +# Use a generator for the execution flow +# -> PC has to be a global variable, updated by each instruction to the next val +# So user can set the PC by hand and call next(run) and make the code jump! + + diff --git a/pysc-v/memory.py b/pysc-v/memory.py new file mode 100644 index 0000000..84bf0bd --- /dev/null +++ b/pysc-v/memory.py @@ -0,0 +1,112 @@ +from ctypes import c_uint8, c_uint16, c_uint32 + +class Addressable: + def __init__(self, start=0, end=0): + self.start = start + self.end = end + def addr_to_pos(self, addr): + if not (self.start <= addr < self.end): + raise KeyError("Address out of space") + return addr - self.start + +def merge_bytes (byte_iter): + val = 0 + for i,v in enumerate(byte_ouput): + val += v.value << 8 * i + return val +def split_bytes (val, byte_count): + return tuple( c_uint8(val >> 8 * i) for i in range(byte_count) ) + +class Memory32 (Addressable): + """ + This is a raw 32-bit word memory addressable at a byte level. + Internally it is defined as c_uint8 list. + Special functions are needed to access halfs (c_uint16) and words + (c_uint32). + """ + + def __init__(self, start=0, end=0, bigEndian=False): + super().__init__(start, end) + self.bigEndian = bigEndian + self.lastChange = None + # Pre-allocate or allocate or allocate per write? + self.data = [None] * (end - start) + + def get_byte(self, addr): + return self.data[ self.addr_to_pos(addr) ] + def set_byte(self, addr, val): + if not isinstance(val, c_uint8): + if val > 0xFF: + raise ValueError("Value is larger than a byte") + self.data[ self.addr_to_pos(addr) ] = val if isinstance(val, c_uint8) else c_uint8(val) + self.lastChange = range(addr, addr+1) + + + def get_half(self,addr): + byte_output = (get_byte(addr), get_byte(addr+1)) + if self.bigEndian: + byte_output = reversed(byte_output) + return c_uint16( merge_bytes(byte_output) ) + + def set_half(self, addr, val): + if not isinstance(val, c_uint16): + if val > 0xFFFF: + raise ValueError("Value is larger than a half") + bytes = split_bytes(val, 2) + if self.bigEndian: + bytes = reversed(bytes) + for i,v in enumerate(bytes): + self.set_byte(addr+i, v) + self.lastChange = range(addr,addr+2) + + def get_word(self, pos): + byte_output = tuple(get_byte(addr+i) for i in range(4)) + if self.bigEndian: + byte_output = reversed(byte_output) + return c_uint32( merge_bytes(byte_output) ) + + def set_word(self, addr, val): + if not isinstance(val, c_uint32): + if val > 0xFFFFFFFF: + raise ValueError("Value is larger than a word") + bytes = split_bytes(val, 4) + if self.bigEndian: + bytes = reversed(bytes) + for i,v in enumerate(bytes): + self.set_byte(addr+i, v) + self.lastChange = range(addr,addr+4) + + def __str__(self): + out = " " + out += "-" * 50 + out += "\n" + for i,d in enumerate(self.data): + if d is not None: + addr = i+self.start + out += "->" if addr in self.lastChange else " " + out += f"| {addr:#20x} | {d.value:#5} | {d.value:#04x} | {d.value:#010b} |" + out += "\n" + out += " " + out += "-" * 50 + out += "\n" + return out + + +class Memory32RO (Memory32): + def set_byte(self, addr, val): + raise NotImplementedError("Trying to write in a read-only memory") + def set_half(self, addr, val): + raise NotImplementedError("Trying to write in a read-only memory") + def set_word(self, addr, val): + raise NotImplementedError("Trying to write in a read-only memory") + +class CodeMemory32 (Memory32RO): + def __init__(self, start=0, end=0): + super().__init__(writable=False,start=start,end=end) + + +if __name__ == "__main__": + m = Memory32(start=100,end=200) + print(m.start, m.end) + m.set_byte(100, 246) + print(m) diff --git a/pysc-v/registers/RV32I.py b/pysc-v/registers/RV32I.py new file mode 100644 index 0000000..4652bca --- /dev/null +++ b/pysc-v/registers/RV32I.py @@ -0,0 +1,116 @@ +# Register block of RV32I +# Set and get using [] with the name of the register (lowercase) or the +# position +from ctypes import c_uint32 + +def defaultGetter(self, pos): + return self.data[pos] + +def defaultSetter(self, pos, val): + self.data[pos] = val + +def zeroSetter(self, pos, val): + # hardwired to zero + self.data[pos] = c_uint32(0) + + +class RegistersRV32I: + + def __init__(self): + self.names = {} + self.data = [] + + self.setter = [] + self.getter = [] + + self.lastChange = None + + self.addRegister(("x0","zero"), setter = zeroSetter) + self.addRegister(("x1","ra")) + self.addRegister(("x2","sp")) + self.addRegister(("x3","gp")) + self.addRegister(("x4","tp")) + self.addRegister(("x5","t0")) + self.addRegister(("x6","t1")) + self.addRegister(("x7","t2")) + self.addRegister(("x8","s0","fp")) + self.addRegister(("x9","s1")) + self.addRegister(("x10","a0")) + self.addRegister(("x11","a1")) + self.addRegister(("x12","a2")) + self.addRegister(("x13","a3")) + self.addRegister(("x14","a4")) + self.addRegister(("x15","a5")) + self.addRegister(("x16","a6")) + self.addRegister(("x17","a7")) + self.addRegister(("x18","s2")) + self.addRegister(("x19","s3")) + self.addRegister(("x20","s4")) + self.addRegister(("x21","s5")) + self.addRegister(("x22","s6")) + self.addRegister(("x23","s7")) + self.addRegister(("x24","s8")) + self.addRegister(("x25","s9")) + self.addRegister(("x26","s10")) + self.addRegister(("x27","s11")) + self.addRegister(("x28","t3")) + self.addRegister(("x29","t4")) + self.addRegister(("x30","t5")) + self.addRegister(("x31","t6")) + + + def addRegister(self, names, getter=defaultGetter, setter=defaultSetter): + for name in names: + if name in self.names: + raise KeyError("Register name already in use") + + currentpos = len(self.data) + for name in names: + self.names[name] = currentpos + self.data.append(c_uint32(0)) + + self.getter.append(getter) + self.setter.append(setter) + + def getPos(self, el): + if isinstance(el, str): + pos = self.names[el] + elif isinstance(el, int): + pos = el + else: + raise ValueError("Wrong type of register id. Must be str or int") + return pos + + def __getitem__(self, el): + return self.getter[self.getPos(el)](self, pos) + + def __setitem__(self, el, val): + pos = self.getPos(el) + + if val > 0xFFFFFFFF: + raise ValueError("Value is larger than size of the register") + val = c_uint32(val) + self.setter[pos](self, pos, val) + self.lastChange = pos + + + def __str__(self): + + out = " " + out += "-" * 67 + out += "\n" + for i,d in enumerate(self.data): + out += "->" if i == self.lastChange else " " + out += f" | {d.value:#13} | {d.value:#010x} | {d.value:#034b} |" + out += "\n" + out += " " + out += "-" * 67 + out += "\n" + return out + + +if __name__ == "__main__": + Regs = RegistersRV32I() + Regs[0] = 1 + Regs["x1"] = 10 + print(Regs) diff --git a/pysc-v/registers/__init__.py b/pysc-v/registers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/reader.py b/reader.py deleted file mode 100644 index 45d155c..0000000 --- a/reader.py +++ /dev/null @@ -1,34 +0,0 @@ -class Reader: - def __init__(self, filename): - self.filename = filename - self._file = None - self.lineno = 0 - self.charno = 0 - - def __enter__(self): - self._file = open(self.filename, "r") - return self - - def __exit__(self, type, value, tb): - self._file.close() - - def char(self): - ch = self._file.read(1) - self.charno += 1 - if ch == "\n": - self.lineno += 1 - self.charno = 0 - return ch - - def peek(self): - pos = self._file.tell() - ch = self._file.read(1) - self._file.seek(pos) - return ch - - -if __name__ == "__main__": - with Reader("reader.py") as src: - print(src.peek()) - while src.peek() != "": - print(src.char()) diff --git a/registersRV32I.py b/registersRV32I.py deleted file mode 100644 index 4652bca..0000000 --- a/registersRV32I.py +++ /dev/null @@ -1,116 +0,0 @@ -# Register block of RV32I -# Set and get using [] with the name of the register (lowercase) or the -# position -from ctypes import c_uint32 - -def defaultGetter(self, pos): - return self.data[pos] - -def defaultSetter(self, pos, val): - self.data[pos] = val - -def zeroSetter(self, pos, val): - # hardwired to zero - self.data[pos] = c_uint32(0) - - -class RegistersRV32I: - - def __init__(self): - self.names = {} - self.data = [] - - self.setter = [] - self.getter = [] - - self.lastChange = None - - self.addRegister(("x0","zero"), setter = zeroSetter) - self.addRegister(("x1","ra")) - self.addRegister(("x2","sp")) - self.addRegister(("x3","gp")) - self.addRegister(("x4","tp")) - self.addRegister(("x5","t0")) - self.addRegister(("x6","t1")) - self.addRegister(("x7","t2")) - self.addRegister(("x8","s0","fp")) - self.addRegister(("x9","s1")) - self.addRegister(("x10","a0")) - self.addRegister(("x11","a1")) - self.addRegister(("x12","a2")) - self.addRegister(("x13","a3")) - self.addRegister(("x14","a4")) - self.addRegister(("x15","a5")) - self.addRegister(("x16","a6")) - self.addRegister(("x17","a7")) - self.addRegister(("x18","s2")) - self.addRegister(("x19","s3")) - self.addRegister(("x20","s4")) - self.addRegister(("x21","s5")) - self.addRegister(("x22","s6")) - self.addRegister(("x23","s7")) - self.addRegister(("x24","s8")) - self.addRegister(("x25","s9")) - self.addRegister(("x26","s10")) - self.addRegister(("x27","s11")) - self.addRegister(("x28","t3")) - self.addRegister(("x29","t4")) - self.addRegister(("x30","t5")) - self.addRegister(("x31","t6")) - - - def addRegister(self, names, getter=defaultGetter, setter=defaultSetter): - for name in names: - if name in self.names: - raise KeyError("Register name already in use") - - currentpos = len(self.data) - for name in names: - self.names[name] = currentpos - self.data.append(c_uint32(0)) - - self.getter.append(getter) - self.setter.append(setter) - - def getPos(self, el): - if isinstance(el, str): - pos = self.names[el] - elif isinstance(el, int): - pos = el - else: - raise ValueError("Wrong type of register id. Must be str or int") - return pos - - def __getitem__(self, el): - return self.getter[self.getPos(el)](self, pos) - - def __setitem__(self, el, val): - pos = self.getPos(el) - - if val > 0xFFFFFFFF: - raise ValueError("Value is larger than size of the register") - val = c_uint32(val) - self.setter[pos](self, pos, val) - self.lastChange = pos - - - def __str__(self): - - out = " " - out += "-" * 67 - out += "\n" - for i,d in enumerate(self.data): - out += "->" if i == self.lastChange else " " - out += f" | {d.value:#13} | {d.value:#010x} | {d.value:#034b} |" - out += "\n" - out += " " - out += "-" * 67 - out += "\n" - return out - - -if __name__ == "__main__": - Regs = RegistersRV32I() - Regs[0] = 1 - Regs["x1"] = 10 - print(Regs) -- cgit v1.2.3