def test_lui(): imm = "00000000000010101010" rd = "0" * 5 instruction = "$imm$rd0110111" instruction = instruction.replace("$imm", imm) instruction = instruction.replace("$rd", rd) exp = "10101010000000000000" instr = ISA().instruction_from_bin(instruction, 0) assert int(exp, 2) == instr.execute(None)
def test_auipc(): imm = "00000000000010101010" rd = "0" * 5 instruction = "$imm$rd0010111" instruction = instruction.replace("$imm", imm) instruction = instruction.replace("$rd", rd) exp_par = "10101010000000000000" PC = 24 exp = int(exp_par, 2) + PC instr = ISA().instruction_from_bin(instruction, 0) assert exp == instr.execute(PC)
def test_beq(): rd = "0" * 5 instruction = "0000000$rs2$rs1000100001100011" instruction = instruction.replace("$rs1", rd) instruction = instruction.replace("$rs2", rd) rs1 = 0b00000000000000000000000000001101 # 13 rs2 = 0b00000000000000000000000000001011 # 11 exp = 16 instr = ISA().instruction_from_bin(instruction, 0) assert exp == instr.execute(rs1, rs1) assert 4 == instr.execute(rs1, rs2)
def test_i_instruction_from_binary(): instruction = "00000000110000011000001000010011" res = ISA().instruction_from_bin(instruction, 0) assert res.rd == 4 assert res.rs == 3 assert res.imm == 12
def test_i_instruction_from_string(): instruction = "addi r4, r3, 12" res = ISA().instruction_from_str(instruction, None, None) assert res.rd == 4 assert res.rs == 3 assert res.imm == 12
def test_sym_reg_names(): instruction = "add sp, ra, gp" res = ISA().instruction_from_str(instruction, None, None) assert res.rd == 2 assert res.rs1 == 1 assert res.rs2 == 3
def test_r_instruction_from_binary(): instruction = "00000000001000011001001000110011" res = ISA().instruction_from_bin(instruction, 0) assert res.rd == 4 assert res.rs2 == 2 assert res.rs1 == 3
def test_r_instruction_from_string(): instruction = "add r4, r3, r2" res = ISA().instruction_from_str(instruction, None, None) assert res.rd == 4 assert res.rs1 == 3 assert res.rs2 == 2
def test_MW_step(): w = MainWindow() w.emulators[0].IFQ.put(ISA().instruction_from_str("mul x1, x0, x0", None, 0)) s1 = w.emulators[0].emulator_instance.get_steps() w.emulator_step() assert w.emulators[0].emulator_instance.get_steps() > s1, "Emulator did not perform a step"
def create_tab_isa(self): isa = ISA().ISA toolbox = QtWidgets.QToolBox() # Lambdas do not preserve context when updating ISA from event signal def get_isa_updater(t, i, f): def _(v): print(t, i, f, v) isa[t][i][f] = v return _ for type in isa.keys(): for inst in isa[type]: f = QtWidgets.QFrame() f.setLayout(QtWidgets.QFormLayout()) for inst in isa[type]: f.layout().addRow(inst.upper(), None) op_txt = QtWidgets.QLineEdit() op_txt.setText(isa[type][inst]['exec']) op_txt.textChanged.connect( get_isa_updater(type, inst, 'exec')) f.layout().addRow("Expression:", op_txt) cyc = QtWidgets.QSpinBox() cyc.setMinimum(1) cyc.setValue(isa[type][inst]['clockNeeded']) cyc.valueChanged.connect( get_isa_updater(type, inst, 'clockNeeded')) f.layout().addRow("Clock cycles:", cyc) f.layout().addRow(" ", None) scroll = QtWidgets.QScrollArea() scroll.setWidgetResizable(True) scroll.setWidget(f) toolbox.addItem(f, type.upper()) return toolbox
def __init__(self, DM): self.DM = DM self.ISA = ISA() self.syntax_errors = [] # Line numbers for text self.unknown_instructions = [] # Instruction indexes for ELF self.IM = [] # Instruction Memory self.sections = {'.text': 0} self.symbol_table = {} self.alignment = 4 self.directives = { '.byte': 1, '.2byte': 2, '.4byte': 4, '.8byte': 8, '.half': 2, '.word': 4, '.dword': 8, # '.asciz': None, # '.string': None, # '.zero': None }
class Program: def __init__(self, DM): self.DM = DM self.ISA = ISA() self.syntax_errors = [] # Line numbers for text self.unknown_instructions = [] # Instruction indexes for ELF self.IM = [] # Instruction Memory self.sections = {'.text': 0} self.symbol_table = {} self.alignment = 4 self.directives = { '.byte': 1, '.2byte': 2, '.4byte': 4, '.8byte': 8, '.half': 2, '.word': 4, '.dword': 8, # '.asciz': None, # '.string': None, # '.zero': None } def get_entry_point(self): if '_start' in self.symbol_table: return self.symbol_table['_start']['value'] elif 'main' in self.symbol_table: return self.symbol_table['main']['value'] else: return self.sections['.text'] def load_text(self, text): pc = 0 for l_n, line in enumerate(text.split('\n')): line = line.split(';')[0].strip() line = line.replace('\t', ' ') if line != '': if re.match(r'.+:', line): # Label label = re.match(r'.+:', line).group(0) label = label[:-1].lower() self.symbol_table[label] = { 'name': label, 'value': pc, 'size': 0, 'section': '.text' } self.last_symbol = label elif re.match(r'\.[a-zA-Z0-9]+', line): # Directive try: pc = self.__parse_directive__(line, pc) except SyntaxError as s: self.syntax_errors.append((l_n, line, s)) else: # Instruction try: inst = self.ISA.instruction_from_str( line, self.symbol_table, pc) self.IM.append(inst) # Load 32 bits into memory for i in range(4): self.DM.store( pc + i, int(inst.to_binary()[8 * i:(8 * i) + 8], 2)) except NotImplementedError: self.syntax_errors.append( (l_n, line, "Instruction not yet implemented")) except SyntaxError: self.syntax_errors.append( (l_n, line, "Unknown instruction")) pc += 4 def __parse_directive__(self, line, pc): line = line.split(' ') sections = ['.text', '.data', '.rodata', '.bss'] if line[0] in sections or (line[0] == '.section' and line[1]) in sections: section = line[0] if line[0] in sections else line[1] self.sections[section] = pc elif line[0] == '.align' or line[0] == '.p2align': self.alignment = 2**int(line[1]) elif line[0] == '.balign': self.alignment = int(line[1]) elif line[0] in self.directives: # TODO: store value byte per byte dir = self.directives[line[0]] val = line[1] for offset in range(self.alignment): self.DM.store(pc, val) elif line[0] == '.zero': addr = pc while addr < int(line[1]): self.DM.store(addr, 0) addr += 1 pc += addr elif line[0] == '.string' or line[0] == '.asciz': s = line[1][1:-1] # Remove quotes addr = pc for c in s: self.DM.store(addr, ord(c)) addr += 1 pc += addr else: raise SyntaxError("Unknown directive") return pc def __iter__(self): return iter(self.IM) def load_machine_code(self, filename): file = ELF(filename) file.load_program(self) self.unknown_instructions = file.unknown_instructions def to_code(self): lines = [] done_sections = [] for idx, inst in enumerate(self.IM): for s in self.sections: if self.sections[s] == idx * 4: if s != '.text': # If not the first line lines.append('') lines.append(s) done_sections.append(s) for s in self.symbol_table: if self.symbol_table[s][ 'value'] == idx * 4 and self.symbol_table[s][ 'section'] == done_sections[-1]: lines.append('') lines.append(s + ':') lines.append(' ' + str(inst)) if idx in self.unknown_instructions: self.syntax_errors.append( (len(lines) - 1, str(inst), "Unknown Instruction")) for s in self.sections: if s not in done_sections: lines.append('') lines.append(s) for sym in self.symbol_table.values(): if sym['section'] == s: lines.append(sym['name'] + ":") if sym['size'] == 1: dim = '.byte' elif sym['size'] == 2: dim = '.2byte' elif sym['size'] == 4: dim = '.4byte' elif sym['size'] == 8: dim = '.8byte' else: lines.append(' <no_info>') continue data = [] for i in range(sym['size']): addr = self.sections[sym['section']] + sym['value'] data.append('{:x}'.format(self.DM.load(addr))) lines.append(" {} 0x{}".format(dim, ''.join(data))) return '\n'.join(lines)
def test_to_bin(): inst = ISA().instruction_from_str( 'beq x5, x0, 4', None, 0) # BType_Instruction(1100011, 5, '000', 1, 2) assert inst.to_binary() == '00000000010100000000010001100011'
def load_program(self, prog): print("\nLoading program into memory...") next_addr = 0 # If file is relocatable, need to store sections one after the other for s in self.sections.values(): if s.sh_type == 0: # SHT_NULL continue print(" {}:".format(s.sh_name)) # Load in memory if s.sh_flags & 0x02 != 0x02: print(" ! SHF_ALLOC flag not set, skipping") continue if s.sh_addr == 0 and self.eheader['e_type'] != 1: # Not ET_REL (relocatable) print(" ! sh_addr is set to 0 and file is not relocatable, skipping") continue if s.sh_addr == 0: s.sh_addr = next_addr if s.sh_type == 8: # SHT_NOBITS, like .bss, doesn't have content print(" - sh_type is set to SHT_NOBITS, setting {} bytes to 0x00".format(s.sh_size)) for offset in range(s.sh_size): prog.DM.store(s.sh_addr + offset, 0x00) else: self.file.seek(s.sh_offset) s.content = self.file.read(s.sh_size) for offset, byte in enumerate(s.content): prog.DM.store(s.sh_addr + offset, byte) print(" > Loaded {} bytes from {} to {} excluded. (Read from offset {})".format( s.sh_size, s.sh_addr, s.sh_addr + s.sh_size, s.sh_offset )) next_addr += s.sh_size # Insert start address in program's section array prog.sections[s.sh_name] = s.sh_addr # Dump DM print("\nData Memory dump (first 128 bytes):") bin, hex, ascii = [], [], [] for addr, val in enumerate(prog.DM): if addr >= 128: break bin.append('{:08b}'.format(val)) hex.append('{:02x}'.format(val)) ascii.append(chr(val) if chr(val).isprintable() and chr(val) != '\n' else '.') bin_idx, hex_idx, ascii_idx = 0, 0, 0 while bin_idx < len(bin): print(' ', ' '.join(bin[bin_idx:bin_idx+8]), end=' │ ') bin_idx += 8 print(' '.join(hex[hex_idx:hex_idx+8]), end=' │ ') hex_idx += 8 print(''.join(ascii[ascii_idx:ascii_idx+8])) ascii_idx += 8 # Make relocation of symbols symbol_bindings = self.parse_rs() # Relocation section # Fill program's symbol table for s in self.symbols: if s.st_shndx < len(self.ordered_sections): prog.symbol_table[s.st_name] = { 'name': s.st_name, 'value': s.st_value, 'size': s.st_size, 'section': self.ordered_sections[s.st_shndx].sh_name } # Parse .text section to instructions print("\nParsing .text section into ISA.Instruction objects...") isa = ISA() pc = prog.sections['.text'] self.file.seek(self.sections['.text'].sh_offset) while self.file.tell() < self.sections['.text'].sh_offset + self.sections['.text'].sh_size: bin = ''.join(["{:08b}".format(b) for b in reversed(self.file.read(4))]) try: inst = isa.instruction_from_bin(bin, pc) except NotImplementedError: inst = None if symbol_bindings is not None and len(prog.IM) in symbol_bindings: # Relocate symbol in the instruction sym = symbol_bindings[len(prog.IM)] print(" (relocating a symbol: {})".format(sym)) if isinstance(inst, (IType_Instruction, UType_Instruction, UJType_Instruction)): inst.imm = sym print(' ', bin, '->', inst if inst is not None else "! [error]") prog.IM.append(inst) if inst is None: self.unknown_instructions.append(len(prog.IM)-1) pc += 4