diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | instructions.py | 145 | ||||
-rw-r--r-- | memory.py | 112 | ||||
-rw-r--r-- | reader.py | 34 | ||||
-rw-r--r-- | registersRV32I.py | 116 |
5 files changed, 410 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..9583ad1 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# PYSC-V + +PYSC-V is a WIP RISC-V assembly language interpreter and assembler. diff --git a/instructions.py b/instructions.py new file mode 100644 index 0000000..0bfda4c --- /dev/null +++ b/instructions.py @@ -0,0 +1,145 @@ +# 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 new file mode 100644 index 0000000..84bf0bd --- /dev/null +++ b/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/reader.py b/reader.py new file mode 100644 index 0000000..45d155c --- /dev/null +++ b/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/registersRV32I.py b/registersRV32I.py new file mode 100644 index 0000000..4652bca --- /dev/null +++ b/registersRV32I.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) |