Commit 7c07ec61 authored by Khoi Lam's avatar Khoi Lam 💬
Browse files

Dealing with a bug on a ret instruction

parent 59d224cc
# filename: fetch_decode.py
import itertools
import sys
from pydigital.memory import readmemh
from pydigital.memory import Memory
from pydigital.memory import MemorySegment
from pydigital.register import Register
from riscv_isa import Instruction
from regfile import RegFile
from mux import make_mux
from riscv_isa.decoder import control
from alu import alu
from pydigital.elfloader import load_elf
from pydigital.utils import as_twos_comp
from pydigital.utils import sextend
# the PC register
PC = Register()
# construct a memory segment for instruction memory
# load the contents from the 32-bit fetch_test hex file (big endian)
# imem = readmemh('riscv_isa/programs/fetch_test.hex',
# word_size = 4, byteorder = 'big')
pc_val = PC.out()
def display(pc_val, instr):
if pc_val == None:
return "PC: xxxxxxxx, IR: xxxxxxxx"
else:
return f"PC: {pc_val:08x}, IR: {instr.val:08x}, {instr}" # {instr} just returns the representation of the instruction that we overloaded with __str__ in isa.py
# run cpu
def run_cpu(imem, dmem, symbols, debug):
startup = True
# create the regfile
my_reg = RegFile()
word_size = 4
# generate system clocks until we reach a stopping condition
# this is basically the run function from the last lab
for t in itertools.count(): # t represents clock
# sample inputs
pc_val = PC.out()
# access instruction memory
instr = Instruction(imem[pc_val], pc_val)
# RESET the PC register
if startup:
PC.reset(symbols["_start"])
startup = False
print(f"{t:20d}:", display(pc_val, instr))
continue
# print one line at the end of the clock cycle
print(f"{t:20d}:", display(pc_val, instr))
# clock logic blocks, PC is the only clocked module!
# decide next PC based on what instruction is run
ecall_val = my_reg.read(10) #ecall argument is the value in a0
# Get the values in rs1 rs2 and rd
rs1_value = my_reg.read(int(instr.rs1, 2))
rs2_value = my_reg.read(int(instr.rs2, 2))
# decide if there is a branch instruction and if the branch was taken for that instruction
isTaken = False
if ( (instr.instr_name == "beq" or instr.instr_name == "beqz") and (rs1_value == rs2_value) ): isTaken = True
if (instr.instr_name == "bne" and (rs1_value != rs2_value) ): isTaken = True
if (instr.instr_name == "blt" and (rs1_value < rs2_value) ): isTaken = True
if (instr.instr_name == "bge" and (rs1_value >= rs2_value) ): isTaken = True
if (instr.instr_name == "bltu" and (as_twos_comp(rs1_value) < as_twos_comp(rs2_value)) ): isTaken = True
if (instr.instr_name == "bgeu" and (as_twos_comp(rs1_value) >= as_twos_comp(rs2_value)) ): isTaken = True
# Handle all of the CSSRs as NOP, then PC = PC + 4, continue
if instr.instr_name == "CSRRW" or instr.instr_name == "CSRRC" or instr.instr_name == "CSRRS" or instr.instr_name == "CSRRWI" or instr.instr_name == "CSRRCI" or instr.instr_name == "CSRRSI":
PC.clock(pc_val + 4)
continue
op1_mux = make_mux(lambda: rs1_value, lambda : None, lambda: instr.immU)
op2_mux = make_mux(lambda : rs2_value, lambda : instr.immI, lambda :instr.immS, lambda: pc_val)
# getting all control signals from the decoder
alu_fun = control[instr.instr_name].ALU_fun
op1_sel = control[instr.instr_name].op1_sel
op2_sel = control[instr.instr_name].op2_sel
rf_wen = control[instr.instr_name].rf_wen
wb_sel = control[instr.instr_name].wb_sel
mem_val = control[instr.instr_name].mask_type
mem_wr = control[instr.instr_name].mem_wr
mem_em = control[instr.instr_name].mem_em
result = alu(op1_mux(op1_sel), op2_mux(op2_sel), alu_fun)
sextend_res = sextend(result)
if debug:
print("ALURes = " + str(result))
print("Sextended_ALURes = " + str(sextend_res))
print("ALU_fun = " + str(alu_fun))
# Data Memory Read
if mem_wr == 0 and mem_em == 1:
rdata = dmem.out(result)
# Data Memory Write
dmem.clock(result, rs2_value, mem_wr, mem_val)
if instr.instr_name == "slli":
writeback_mux = make_mux(lambda : 4 + pc_val, lambda: sextend_res, lambda: rdata, lambda: 0) # replace first None with dmem.out() and 2nd None with csr
else:
writeback_mux = make_mux(lambda : 4 + pc_val, lambda: result, lambda: rdata, lambda: 0) # replace first None with dmem.out() and 2nd None with csr
my_reg.clock(int(instr.rd,2), writeback_mux(wb_sel), rf_wen)
if instr.instr_name == "jal":
PC.clock(instr.immUJ + pc_val)
elif instr.instr_name == "jalr":
PC.clock(instr.immI + rs1_value)
elif isTaken:
PC.clock(instr.immSB + pc_val)
else:
PC.clock(pc_val + 4)
rd_value = my_reg.read(int(instr.rd, 2))
print('rd: ' + str(rd_value) + ' rs1: ' + str(rs1_value) + ' rs2: ' + str(rs2_value))
if debug != 0:
print('rd: ' + str(rd_value) + ' rs1: ' + str(rs1_value) + ' rs2: ' + str(rs2_value))
my_reg.display()
print("------------------------------------------------------------------")
# the default values for instr are strings, if you want them as ints you will need to do int(,2)
if instr.instr_name == "ecall":
if ecall_val == 0:
print('ECALL(' + str(format(ecall_val)) + '): ' + 'HALT')
elif ecall_val == 10:
print('ECALL(' +str(format(ecall_val)) +'): ' + 'EXIT')
else:
print('ECALL(' +str(format(ecall_val)) +'): ' + str(my_reg.read(11)))
if mem_em == 1 and mem_wr == 1 and 'tohost' in symbols:
if (word_size == 4 and result == symbols['tohost']+4) or (word_size == 8 and result == symbols['tohost']):
# syscall detected, tohost is a 64 bit reg, so wait for addr of second word on 32 bit
val = dmem.mem[symbols['tohost']]
if val & 0b1 == 0b1:
# exit
print (f"SYSCALL: exit ({val>>1})")
sys.exit(val>>1)
if val & 0b0 == 0b0:
which = dmem.mem[dmem.mem[symbols['tohost']]]
arg0 = dmem.mem[dmem.mem[symbols['tohost']] + 8]
arg1 = dmem.mem[dmem.mem[symbols['tohost']] + 16]
arg2 = dmem.mem[dmem.mem[symbols['tohost']] + 24]
if which == 64:
print(dmem.mem[arg1:arg1+arg2].decode("ASCII"), end = "")
dmem.mem[symbols['fromhost']] = 1
# check stopping conditions on NEXT instruction
if imem[as_twos_comp(PC.out())] == 0:
print("Done -- end of program.")
break
# display the regfile after all instructions are run
print("--------------------Final register values-----------------------")
my_reg.display()
if __name__ == "__main__":
if len(sys.argv) == 1:
program_file = "riscv_isa/programs/hello" # default instruction file
else:
program_file = sys.argv[1] # or choose another file
# construct a memory segment for the instruction memory
# load the contents from the 32-bit fetch_test hex file (big endian)
sys_mem, symbols = load_elf(program_file)
imem = sys_mem
dmem = Memory(sys_mem) #for lab 5
sys.exit(run_cpu(imem, dmem, symbols, debug = 0))
......@@ -34,10 +34,18 @@ i_type = {
"010": "slti",
"110": "ori",
"001": "slli",
"111": "andi"
# to be added
"111": "andi",
"101": "srai"
}
}
i_type_1 = {
"NA": {
"000": "fence",
"001": "fence.i",
}
}
# similar to i type, sb type doesnt have func7 and only func 3
sb_type = {
"NA": {
......@@ -54,6 +62,16 @@ u_type = {} # not needed right now
s_type = {} # not needed right now
#sys is indexed by immI (as funct7) and funct3
sys = { "NA": {
"000": "ecall",
"101": "CSRRWI",
"110": "CSRRSI",
"001": "CSRRW",
"111": "CSRRCI",
"010": "CSRRS",
"011": "CSRRC"
}}
def regNumToName(num):
if type(num) != int or num < 0 or num > 31:
......@@ -83,7 +101,7 @@ def decode_type(opcode):
function that determines what type the instruction is
return type
'''
return {"0110011": "r", "0010011": "i", "0000011": "i" , "1110011": "sys", "0110111": "u", "1101111": "uj", "1100011":"sb", "0100011":"s"}[opcode]
return {"0110011": "r", "0010011": "i", "0000011": "i" ,"0001111": "i1" , "1110011": "sys", "0110111": "lui", "0010111": "auipc" , "1101111": "uj", "1100011":"sb", "0100011":"s", "1100111": "jalr"}[opcode]
def decode_r_instruction(func3, func7, rd, rs1, rs2, instr_name):
......@@ -121,19 +139,24 @@ def decode_u_instruction(rd, immU, instr_name):
#imm_value = str(int(immU,2))
return instr_name + " " + rd_name + " " + format(immU,'x')
def decode_sb_instruction(rs1, rs2, immSB, instr_name):
def decode_sb_instruction(rs1, rs2, immSB, instr_name, pc):
rs1_name = regNumToName(int(rs1, 2))
rs2_name = regNumToName(int(rs2, 2))
return instr_name+ " LOL"
return instr_name + str(immSB) + " " + format(pc + immSB, "x")
def decode_s_instruction(rs1, rs2, immS, instr_name):
rs1_name = regNumToName(int(rs1, 2))
rs2_name = regNumToName(int(rs2, 2))
return instr_name + " LMAO"
def decode_uj_instruction(immUJ, instr_name):
def decode_uj_instruction(immUJ, instr_name, pc):
return instr_name + " " + str(immUJ)
return instr_name + " " + format(pc + immUJ, "x")
def decode_jalr_instruction(rd, rs1, instr_name):
rd_name = regNumToName(int(rd,2))
rs1_name = regNumToName(int(rs1,2))
return instr_name + " " + rd_name + " " +rs1_name
......@@ -159,16 +182,16 @@ class Instruction():
self.func7 = self.bin_val[0:7] # 7 bits
self.rd = self.bin_val[20:25] # 5 bits
self.opcode = self.bin_val[25:] # 7 bits
self.immU = sextend((val & 0xFFFFF000), 20)
self.immU = sextend((val & 0xFFFFF000), 32)
self.immI = sextend((val & 0xFFF00000) >> 20, 12)
self.immS = sextend(((val & 0xFE000000) >> 25) | ((val & 0xF80) >> 7), 12)
self.immS = sextend(0xfff & ((0x1f & (val >> 7)) | ((0x7f & (val >> 25)) << 5)), 12)
self.immSB = sextend(
0x1ffffff & (
0x1fffff & (
((0xf & (val >> 8)) << 1) |
((0x3f & (val >> 25)) << 5) |
((0x1 & (val >> 7)) << 11) |
((0x80000000 & val) >> 19)
), 12)
), 13)
self.immUJ = sextend(
0xfffff & (
((0x3ff & (val >> 21)) << 1) |
......@@ -187,31 +210,43 @@ class Instruction():
self.instr_name = i_type["NA"][self.func3]
else:
self.instr_name = "lw" # temporary fix for the fact that I types have 2 different opcodes
elif self.type == "u":
#TODO there are other U type instructions to handle!!!
elif self.type == "i1":
self.instr_name = i_type_1["NA"][self.func3]
elif self.type == "lui":
self.instr_name = "lui"
elif self.type == "auipc":
self.instr_name = "auipc"
elif self.type == "s":
self.instr_name = "sw" #TODO make a real dictionary for all the s types
elif self.type == "uj":
self.instr_name = "jal" #this is fine because there is only 1 uj instruction which is jal
elif self.type == "sb":
self.instr_name = sb_type["NA"][self.func3]
else:
self.instr_name = "ecall"
elif self.type == "sys":
self.instr_name = sys["NA"][self.func3]
elif self.type == "jalr":
self.instr_name = "jalr"
def __str__(self):
if self.type == "r":
return decode_r_instruction(self.func3, self.func7, self.rd, self.rs1, self.rs2, self.instr_name)
elif self.type == "i":
elif self.type == "i" or self.type == "i1":
return decode_i_instruction(self.func3, self.rd, self.rs1, self.immI, self.instr_name)
elif self.type == "u":
return decode_u_instruction(self.rd, self.immU, self.instr_name)
elif self.type == "lui" or self.type == "auipc":
return decode_u_instruction(self.rd, self.immU, self.instr_name) # this is since u type only has 2 instructions : lui and auipc
elif self.type == "sb":
return decode_sb_instruction(self.rs1, self.rs2, self.immSB, self.instr_name)
return decode_sb_instruction(self.rs1, self.rs2, self.immSB, self.instr_name, self.pc)
elif self.type == "uj":
return decode_uj_instruction(self.immUJ, self.instr_name)
return decode_uj_instruction(self.immUJ, self.instr_name, self.pc)
elif self.type == "s":
return decode_s_instruction(self.rs1, self.rs2, self.immS, self.instr_name)
else:
return "ecall"
elif self.type == "sys":
if self.instr_name == "ecall":
return "ecall"
else:
# The other CSRRs
return "NOP"
elif self.type == "jalr":
return decode_jalr_instruction(self.rd, self.rs1, self.instr_name)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment