# Memory # 32-bit address # 8-bit cell # Register File # ยท32 32-bit registers, with 2 read ports and 1 write portTest bench # / Add the number in memory address 0 and 1 to address 3 # Load r1, #0 # Load r2,#1 # Add r3, r1, r2 # Store r3, #3 import numpy as np import re import random class register_file: def __init__(self , size) -> None: self.regs = np.array([0] * size , dtype=np.int32) self.size = size def read(self , idx1 : int , idx2 : int) -> tuple:# 2 read port if idx1 < 0 or idx1 >= self.size or idx2 < 0 or idx2 >= self.size: raise IndexError(f"The index is out of range") return (self.regs[idx1] , self.regs[idx2]) def write(self , idx : int , value : np.int32) -> None:# 1 write port if idx < 0 or idx >= self.size: raise IndexError("The index is out of range") if value < -2**31 or value > 2**31 - 1: raise ValueError("The value is out of range") self.regs[idx] = value def info(self) -> None: print(f"register file : {self.regs}") class memory: def __init__(self , size): self.size = size self.mem = {key : np.int8(0) for key in range(10)}#8-bit cell # use dictionary to simulate 2**32 byte-memory def reset(self): self.mem = {key : np.int8(0) for key in self.mem} def info(self) -> None: print(f"memory : {self.mem}") def read(self , idx : int) -> np.int8: if idx < 0 or idx >= self.size: raise IndexError("The index is out of range") if idx not in self.mem: self.mem[idx] = np.int8(0) return self.mem[idx] def write(self , idx , value : np.int8) -> None: if idx < 0 or idx >= self.size: raise IndexError("The index is out of range") if(value < -128 or value > 127): raise ValueError("The value is out of range") self.mem[idx] = value class instruction: @staticmethod def Load(ins : list , immediate: bool = False) -> None: mem_val : np.int32 = np.int32(mem.read(ins[2])) | np.int32(mem.read(ins[2] + 1)) << 8 + np.int32(mem.read(ins[2] + 2)) << 16 + np.int32(mem.read(ins[2] + 3)) << 24 regfile.write(ins[1] , mem_val) @staticmethod def Loadi(ins : list):#load immediate to register , just for test , style of instruction : Loadi r1 , i100 regfile.write(ins[1] , ins[2]) #to fix : merge into Load instruction by adding flag immediate , maybe @staticmethod def Store(ins : list) -> None: reg_val : np.int32 = regfile.read(ins[1] , 0)[0]#just use one read port of regfile mem.write(ins[2] , np.int8(reg_val & 0xff)) mem.write(ins[2] + 1 , np.int8((reg_val >> 8) & 0xff)) mem.write(ins[2] + 2 , np.int8((reg_val >> 16) & 0xff)) mem.write(ins[2] + 3 , np.int8((reg_val >> 24) & 0xff)) @staticmethod def Add(ins : list) -> None: r1 , r2 = regfile.read(ins[2] , ins[3]) regfile.write(ins[1] , r1 + r2) def exec_ins(self , ins : list) -> None: target_ins = getattr(type(self) , ins[0]) if target_ins is None: raise RuntimeError("the error instruction {}".format(ins[0])) target_ins(ins) @staticmethod def parse_instruction(instruc : str) -> list: #parse the instruction _ins = re.split(r'[ ,]+' , instruc)# split the string by space and comma ins = [_ins[0]] for elem in _ins: if elem[0] == 'r' or elem[0] == '#' or elem[0] == 'i':#get the bias of address ins.append(int(elem[1:])) return ins class testbench: @staticmethod def memory_random_flip(): mem.mem = {key : random.randint(-10,10) for key in mem.mem} @staticmethod def test(): testbench.memory_random_flip() mem.info() test_instr = ['Load r1, #0' , 'Load r2,#1' , 'Add r3, r1, r2' , 'Store r3, #3'] for ins in test_instr: _ins = instruction.parse_instruction(ins) instruc.exec_ins(_ins) mem.info() if __name__ == '__main__': global regfile , mem , instruc regfile = register_file(32)#32 32-bit registers mem = memory(2 ** 32)# 32-bit address memory instruc = instruction() testbench.test() while True: _inst = input('>') inst = instruc.parse_instruction(_inst) instruc.exec_ins(inst) mem.info() regfile.info()