def COMPARE(self, arg1: intbv, arg2: intbv, rd, cond="e", signed=True): """ Comapre arg1 and arg2. Args: arg1: first argument arg2: second argument cond: comparison condition. e for equal, l for less, and g for greater, le for <=, ge for >=. signed: Boolean flag for sign. By default is True. Set to False in case inputs are unsigned """ if signed: # Check the sign arg1 = arg1.signed() arg2 = arg2.signed() if cond == "e": # Check the conditions self.regs[rd] = intbv(arg1 == arg2)[32:] return arg1 == arg2 elif cond == "l": self.regs[rd] = intbv(arg1 < arg2)[32:] return arg1 < arg2 elif cond == "g": self.regs[rd] = intbv(arg1 > arg2)[32:] return arg1 > arg2 elif cond == "le": self.regs[rd] = intbv(arg1 <= arg2)[32:] elif cond == "ge": self.regs[rd] = intbv(arg1 >= arg2)[32:]
def BRANCH(self, rs1, rs2, imm: intbv, cond='e', signed=True): """ Execute branching instructions based on comparison conditions of two values Args: rs1: register source 1 rs2: register source 2 imm: immediate value cond: e for rs1==rs2, ne for !=, lt for <, ge for >= signed: boolean flag. True by default. Set to false to enable unsigned branch """ val1 = intbv(self.regs[rs1])[32:0] val2 = intbv(self.regs[rs2])[32:0] res = False if signed: val1 = val1.signed() val2 = val2.signed() if cond == 'e': res = val1 == val2 elif cond == 'ne': res = not (val1 == val2) elif cond == 'lt': res = val1 < val2 elif cond == 'ge': res = val1 >= val2 else: print("Error: Please enter valid comparison condition") if res: self.jump_flag = True self.pc = self.pc + int(imm.signed())
def LOAD(self, rd, rs1, imm: intbv, width=1, signed=True): """ Executes load instructions. Load the value of memory address [ register rs1+imm value(offset) ] into register rd Args: rd: destination register rs1: source register imm: immediate value, represents offset width: number of bytes. can be 1, 2, or 4 for byte, half word, or word signed: boolean flag. Set to True by default. Set to False to enable unsigned immediate """ # These instructions take the form lb rd, imm(rs1) if signed: imm = imm.signed() src = intbv(self.regs[rs1])[32:0] target_address = src + imm loaded_bytes = None if width == 1: loaded_bytes = self.ram.read(target_address) elif width == 2: loaded_bytes = self.ram.readHalfWord(target_address) elif width == 4: loaded_bytes = self.ram.readWord(target_address) else: print("Error: please enter valid load width") exit(0) # Store the loaded byte as an integer in the destination reg self.regs[rd] = intbv(int.from_bytes(loaded_bytes, 'little'))[32:]
def JALR(self, rd, rs1, imm: intbv): """ Jump to address stored in rs1 + imm (immediate serves as offset, usually 0) Args: rd: saves return address rs1: contains the address to jump to imm: immediate value, serves as offset. Usually 0 """ self.regs[rd] = intbv(self.pc + 4)[32:0] self.jump_flag = True self.pc = self.regs[rs1] + imm.signed() # immediate is signed
def MUL(self, arg1: intbv, arg2: intbv, rd, sign='S'): # I considered the notation (s)(u) to mean arg1 is signed, arg2 not signed if sign.capitalize() == 'SU': self.regs[rd] = intbv(arg1.signed() * arg2.unsigned())[32:] return arg1.signed() * arg2.unsigned() elif sign.capitalize() == 'U': self.regs[rd] = intbv(arg1 * arg2)[32:] return arg1 * arg2 elif sign.capitalize() == 'S': self.regs[rd] = intbv(arg1.signed() * arg2.signed())[32:] return arg1.signed() * arg2.signed() else: print("Error: condition " + sign + " is not defined for MUL function")
def JAL(self, rd, imm: intbv): """ Jump by increasing pc by amount of imm. Save return address in rd Args: rd: saves return address imm: immediate value. The pc will be incremented by this amount * 2 Returns: """ # make immediate a signed number imm = imm.signed() self.regs[rd] = intbv(self.pc + 4)[32:0] # Save return address in rd self.jump_flag = True # update jump flag self.pc = self.pc + imm # jump
def SHIFT(self, arg1: intbv, arg2: intbv, rd, dir='r', signed=False): # needs testing """ Shifts arg1 by amount of arg2 Args: arg1: Number to be shifted arg2: Shift amount dir: direction of shift. By default is r. Set to l for shift left signed: Boolean flag for sign. By default is False. Set to True to enable sign extension """ if dir == "r": if signed: self.regs[rd] = intbv(arg1.signed() >> arg2)[32:] else: self.regs[rd] = intbv(arg1 >> arg2)[32:] elif dir == "l": self.regs[rd] = intbv(arg1 << arg2)[32:]
def REM(self, arg1: intbv, arg2: intbv, rd, signed=True): if signed: self.regs[rd] = intbv(arg1.signed() % arg2.signed())[32:] return arg1.signed() % arg2.signed() self.regs[rd] = intbv(arg1 % arg2)[32:] return arg1 % arg2
def DIV(self, arg1: intbv, arg2: intbv, rd, signed=True): if signed: self.regs[rd] = intbv(arg1.signed() // arg2.signed())[32:] return arg1.signed() // arg2.signed() self.regs[rd] = intbv(arg1 // arg2)[32:] return arg1 // arg2
def SUB(self, arg1: intbv, arg2: intbv, rd: intbv): self.regs[rd] = intbv(arg1.signed() - arg2)[32:]
def ADD(self, arg1: intbv, arg2: intbv, rd: intbv): self.regs[rd] = intbv(arg1.signed() + arg2.signed())[32:]