Commit 34c2dd8c authored by Khoi Lam's avatar Khoi Lam 💬
Browse files

Finished Lab4

parent aac9e2b1
def alu(op1, op2, alu_fun):
if alu_fun == 0:
if alu_fun == 0: # NOP
return 0
elif alu_fun == 1: #xor
return op1^op2
......@@ -27,6 +27,9 @@ def alu(op1, op2, alu_fun):
else:
return "Invalid alu_fun"
def alufunName(num):
return {0: "NOP", 1:"xor", 2:"cp", 3:"sltu", 4:"and", 5:"add", 6:"slt", 7:"sra", 8:"sub", 9:"srl", 10:"sll", 11:"or"}[num]
if __name__ == "__main__":
op1 = 2
op2 = 1
......
......@@ -3,7 +3,7 @@ import itertools
from pydigital.memory import readmemh
from pydigital.register import Register
from riscv_isa import Instruction
from mux import make_mux2
from mux import make_mux
from riscv_isa.decoder import control
# the PC register
......@@ -11,10 +11,10 @@ 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')
imem = readmemh('riscv_isa/programs/fetch_test.hex',
word_size = 4, byteorder = 'big')
op1_mux = make_mux2(lambda: instr.rs1, lambda: None)
#op1_mux = make_mux(lambda: instr.rs1, lambda: None)
def display():
if pc_val == None:
......@@ -39,7 +39,7 @@ for t in itertools.count():
# access instruction memory
instr = Instruction(imem[pc_val], pc_val)
alu_in1 = op1_mux(control[instr.instr_name].op1_sel)
#alu_in1 = op1_mux(control[instr.instr_name].op1_sel)
# print one line at the end of the clock cycle
......
# filename: mux.py
def make_mux2(*args):
def make_mux(*args):
# returns a function with arguments args
return lambda i: (args)[i]()
if __name__=="__main__":
# mux here is a variable that contains a function with argument i
mux = make_mux2(lambda: 1, lambda: 2, lambda:3, lambda:4, lambda:5)
mux = make_mux(lambda: 1, lambda: 2, lambda:3, lambda:4, lambda:5)
for i in range(5):
print(f"mux({i}) == {mux(i)}") # mux function is called here and i is passed in as parameter
......@@ -5,7 +5,10 @@ from pydigital.memory import readmemh
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.utils import sextend
# the PC register
PC = Register()
......@@ -16,64 +19,97 @@ PC = Register()
# 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
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():
def run_cpu(imem):
startup = True
imem = readmemh('riscv_isa/programs/fetch_test.hex',
word_size = 4, byteorder = 'big')
#create the regfile
# create the regfile
my_reg = RegFile()
# 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()
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(imem.begin_addr)
startup = False
print(f"{t:20d}:", display(pc_val, instr))
continue
# access instruction memory
instr = Instruction(imem[pc_val], pc_val)
# print one line at the end of the clock cycle
print(f"{t:20d}:", display(pc_val, instr))
# RESET the PC register
if startup:
PC.reset(imem.begin_addr)
startup = False
print(f"{t:20d}:", display(pc_val, instr))
continue
# clock logic blocks, PC is the only clocked module!
# here the next pc value is always +4
PC.clock(4 + pc_val)
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))
# print one line at the end of the clock cycle
print(f"{t:20d}:", display(pc_val, instr))
#print(hex(int(instr.immI,2)))
# clock logic blocks, PC is the only clocked module!
# here the next pc value is always +4
PC.clock(4 + pc_val)
op1_mux = make_mux(lambda: rs1_value, lambda : None, lambda: instr.immU)
op2_mux = make_mux(lambda : rs2_value, lambda : instr.immI, lambda :None, lambda: pc_val)
#display the register file
alu_fun = control[instr.instr_name].ALU_fun
op1_sel = control[instr.instr_name].op1_sel
op2_sel = control[instr.instr_name].op2_sel
# check stopping conditions on NEXT instruction
if PC.out() > 0x1100:
print("STOP -- PC is large! Is something wrong?")
break
if imem[PC.out()] == 0:
print("Done -- end of program.")
break
result = alu(op1_mux(op1_sel), op2_mux(op2_sel), alu_fun)
my_reg.clock(int(instr.rd,2), result, 1)
rd_value = my_reg.read(int(instr.rd, 2))
# 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)))
# check stopping conditions on NEXT instruction
if PC.out() > 0x1100:
print("STOP -- PC is large! Is something wrong?")
break
if imem[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/fetch_test.hex" #default instruction file
program_file = "riscv_isa/programs/fetch_test.hex" # 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)
imem = readmemh(program_file, word_size = 4, byteorder = 'big')
program_file = sys.argv[1] # or choose another file
#dmem = Memory(MemorySegment(0xE0000, count = 0x10000)) #for lab 5
# construct a memory segment for the instruction memory
# load the contents from the 32-bit fetch_test hex file (big endian)
imem = readmemh(program_file, word_size=4, byteorder='big')
# dmem = Memory(MemorySegment(0xE0000, count = 0x10000)) #for lab 5
sys.exit(run_cpu())
sys.exit(run_cpu(imem))
......@@ -80,8 +80,8 @@ if __name__=="__main__":
print (f'{sextend(0x80000000):08x}')
print(f'{as_twos_comp(-1)<<1:08x}')
exit()
for a in [0, 1, -1, 0x7fffffff, -0x80000000, 0x80000000]:
for a in [0, 1, -1, 0x7fffffff, -0x80000000, 0x80000000, -4096]:
twos = as_twos_comp(a)
fromtwos = sextend(twos)
print(f"a = {a:+09x} as twos = {twos:08x} from twos {fromtwos:+09x}")
......
from .csr_list import csrs
from pydigital.utils import sextend
class BadInstruction(Exception):
pass
# build csr lookup
csrd = {k:v for k,v in csrs}
r_type ={
"0000000": {
"000": "add",
"001": "sll",
"010": "slt",
"011": "sltu",
"100": "xor",
"101": "srl",
"110": "or",
"111": "and"
},
"0100000": {
"000": "sub",
"101": "sra"
}
}
csrd = {k: v for k, v in csrs}
r_type = {
"0000000": {
"000": "add",
"001": "sll",
"010": "slt",
"011": "sltu",
"100": "xor",
"101": "srl",
"110": "or",
"111": "and"
},
"0100000": {
"000": "sub",
"101": "sra",
}
}
# There is no func7 for i types, only func3. Set default "func7" to be "NA" literal
i_type ={
"NA": {
"000": "addi",
"010": "slti",
#to be added
}
}
i_type = {
"NA": {
"000": "addi",
"010": "slti",
"110": "ori",
"001": "slli",
"111": "andi"
# to be added
}
}
u_type = {} # not needed right now
def regNumToName(num):
if type(num) != int or num < 0 or num > 31:
raise BadInstruction()
return ['zero','ra','sp','gp', # 0..3
'tp', 't0', 't1', 't2', # 4..7
's0', 's1', 'a0', 'a1', # 8..11
'a2', 'a3', 'a4', 'a5', # 12..15
'a6', 'a7', 's2', 's3', # 16..19
's4', 's5', 's6', 's7', # 20..23
's8', 's9', 's10', 's11', # 24..27]
't3', 't4', 't5', 't6'][num] # 28..31
return ['zero', 'ra', 'sp', 'gp', # 0..3
'tp', 't0', 't1', 't2', # 4..7
's0', 's1', 'a0', 'a1', # 8..11
'a2', 'a3', 'a4', 'a5', # 12..15
'a6', 'a7', 's2', 's3', # 16..19
's4', 's5', 's6', 's7', # 20..23
's8', 's9', 's10', 's11', # 24..27]
't3', 't4', 't5', 't6'][num] # 28..31
def convert_to_string(int_val):
......@@ -52,71 +60,90 @@ def convert_to_string(int_val):
bin_value = bin(int_val)
return str(bin_value)[2:].zfill(32)
def decode_type(opcode):
'''
function that determines what type the instruction is
return type
'''
return {"0110011": "r", "0010011": "i", "1110011": "sys"}[opcode]
return {"0110011": "r", "0010011": "i", "1110011": "sys", "0110111": "u"}[opcode]
def decode_r_instruction(func3, func7, rd, rs1, rs2):
def decode_r_instruction(func3, func7, rd, rs1, rs2, instr_name):
"""
return a full string that represents the r instruction
"""
assert (len(func3) == 3 and len(func7) == 7 and len(rd) == 5 and len(rs1) == 5 and len(rs2) == 5), "one of the sizes is incorrect"
instr_name = r_type[func7][func3]
assert (len(func3) == 3 and len(func7) == 7 and len(rd) == 5 and len(rs1) == 5 and len(
rs2) == 5), "one of the sizes is incorrect"
# instr_name = r_type[func7][func3]
rd_name = regNumToName(int(rd, 2))
rs1_name = regNumToName(int(rs1, 2))
rs2_name = regNumToName(int(rs2, 2))
return instr_name + " " + rd_name + " " + rs1_name + " " + rs2_name
def decode_i_instruction(func3, rd, rs1, imm):
def decode_i_instruction(func3, rd, rs1, immI, instr_name):
"""
return a full string that represents the i instruction
"""
assert (len(func3) == 3 and len(imm) == 12 and len(rd) == 5 and len(rs1) == 5), "one of the sizes is incorrect"
instr_name = i_type["NA"][func3]
# instr_name = i_type["NA"][func3]
rd_name = regNumToName(int(rd, 2))
rs1_name = regNumToName(int(rs1, 2))
imm_value = str(int(imm,2))
#imm_value = str(int(imm, 2))
if rs1_name == "zero":
return "li" + " " + rd_name + " " + imm_value
return instr_name + " " + rd_name + " " + rs1_name + " " + imm_value
return "li" + " " + rd_name + " " + str(immI)
return instr_name + " " + rd_name + " " + rs1_name + " " + str(immI)
def decode_u_instruction(rd, immU, instr_name):
rd_name = regNumToName(int(rd,2))
#imm_value = str(int(immU,2))
return instr_name + " " + rd_name + " " + str(immU)
class Instruction():
"represents/decodes RISCV instructions"
def __init__ (self, val, pc, symbols = {}):
def __init__(self, val, pc, symbols={}):
"""
Decodes a risc-v instruction word
val is the machine code word
pc is the pc value used to format pc-relative assembly instructions
symbols is an optional symbol table to decode addresses in assembly output
"""
self.val = val #machine coded word
self.pc = pc # pc relative instrs need pc to compute targets for display
if val == None:
return
self.val = val
self.pc = pc # pc relative instr need pc to compute targets for display
self.symbols = symbols
self.bin_val = convert_to_string(val)
self.rs1 = self.bin_val[12:17] # 5 bits
self.rs2 = self.bin_val[7:12] # 5 bits
self.func3 = self.bin_val[17:20] # 3 bits
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.immI = self.bin_val[:12] # 12 bits
self.immU = sextend((val & 0xFFFFF000))
self.immI = sextend((val & 0xFFF00000) >> 20)
self.type = decode_type(self.opcode) # return type string
if self.type == "r":
self.instr_name = r_type[self.func7][self.func3]
elif self.type == "i":
self.instr_name = i_type["NA"][self.func3]
elif self.type == "u":
self.instr_name = "lui"
else:
self.instr_name = "ecall"
def __str__(self):
bin_val = convert_to_string(self.val)
rs1= bin_val[12:17] # 5 bits
func3 = bin_val[17:20] # 3 bits
rd= bin_val[20:25] # 5 bits
opcode = bin_val[25:] # 7 bits
type = decode_type(opcode)
if type == "r":
#strip the needed args for r type here
func7 = bin_val[0:7] # 7 bits
rs2 = bin_val[7:12] # 5 bits
return decode_r_instruction(func3, func7, rd, rs1, rs2)
elif type == "i":
#strip the needed args here
imm = bin_val[:12]
return decode_i_instruction(func3, rd, rs1, imm)
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":
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)
else:
return "ecall"
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