-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSimulator.py
More file actions
176 lines (152 loc) · 6.69 KB
/
Copy pathSimulator.py
File metadata and controls
176 lines (152 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import sys
OPCODE_R_TYPE = 0x33
OPCODE_I_TYPE = 0x13
OPCODE_LW = 0x03
OPCODE_JALR = 0x67
OPCODE_S_TYPE = 0x23
OPCODE_B_TYPE = 0x63
OPCODE_LUI = 0x37
OPCODE_AUIPC = 0x17
OPCODE_JAL = 0x6F
VIRTUAL_HALT = 0x00000063
PROG_MEM_START = 0x00000000
PROG_MEM_END = 0x000000FC
STACK_MEM_START = 0x00000100
STACK_MEM_END = 0x0000017C
DATA_MEM_START = 0x00010000
DATA_MEM_END = 0x0001007C
MASK_32BIT = 0xFFFFFFFF
def sign_extend(val, bits):
if val & (1 << (bits - 1)):
val -= (1 << bits)
return val
def unsigned_32(val):
return val & MASK_32BIT
class Simulator:
def __init__(self):
self.regs = [0] * 32
self.regs[2] = STACK_MEM_END
self.pc = 0
self.prog_mem = [0] * 64
self.stack_mem = [0] * 32
self.data_mem = [0] * 32
def read_mem(self, addr):
if addr % 4 != 0:
raise Exception("Unaligned Memory Access")
if PROG_MEM_START <= addr <= PROG_MEM_END:
return self.prog_mem[(addr - PROG_MEM_START) >> 2]
elif STACK_MEM_START <= addr <= STACK_MEM_END:
return self.stack_mem[(addr - STACK_MEM_START) >> 2]
elif DATA_MEM_START <= addr <= DATA_MEM_END:
return self.data_mem[(addr - DATA_MEM_START) >> 2]
else:
raise Exception(f"Invalid Memory Access at {hex(addr)}")
def write_mem(self, addr, val):
if addr % 4 != 0:
raise Exception("Unaligned Memory Access")
if STACK_MEM_START <= addr <= STACK_MEM_END:
self.stack_mem[(addr - STACK_MEM_START) >> 2] = unsigned_32(val)
elif DATA_MEM_START <= addr <= DATA_MEM_END:
self.data_mem[(addr - DATA_MEM_START) >> 2] = unsigned_32(val)
else:
raise Exception(f"Invalid Memory Access at {hex(addr)}")
def write_reg(self, idx, val):
if idx != 0:
self.regs[idx] = unsigned_32(val)
def execute_r_type(self, rd, rs1, rs2, funct3, funct7):
v1, v2 = self.regs[rs1], self.regs[rs2]
if funct3 == 0x0 and funct7 == 0x00: self.write_reg(rd, v1 + v2)
elif funct3 == 0x0 and funct7 == 0x20: self.write_reg(rd, v1 - v2)
elif funct3 == 0x1: self.write_reg(rd, v1<<(v2 & 0x1F))
elif funct3 == 0x2: self.write_reg(rd, 1 if sign_extend(v1, 32)<sign_extend(v2, 32) else 0)
elif funct3 == 0x3: self.write_reg(rd, 1 if v1 < v2 else 0)
elif funct3 == 0x4: self.write_reg(rd, v1 ^ v2)
elif funct3 == 0x5: self.write_reg(rd, v1>>(v2 & 0x1F))
elif funct3 == 0x6: self.write_reg(rd, v1 | v2)
elif funct3 == 0x7: self.write_reg(rd, v1 & v2)
def execute_b_type(self, inst, rs1, rs2, funct3, default_next_pc):
b_imm = sign_extend(
((inst>>31) & 1)<<12 | ((inst>>7) & 1)<<11 | ((inst>>25) & 0x3F)<<5 | ((inst >> 8) & 0xF) << 1, 13
)
v1, v2 = self.regs[rs1], self.regs[rs2]
sv1, sv2 = sign_extend(v1, 32), sign_extend(v2, 32)
taken = ((funct3 ==0x0 and v1 == v2) or (funct3 == 0x1 and v1 != v2) or (funct3 ==0x4 and sv1 < sv2) or (funct3== 0x5 and sv1 >= sv2) or (funct3 == 0x6 and v1 < v2) or (funct3== 0x7 and v1 >= v2))
return (self.pc + b_imm) if taken else default_next_pc
def execute_jal(self, inst, rd):
j_imm = sign_extend(
((inst>>31) & 1)<<20 | ((inst>>12) & 0xFF)<<12 | ((inst>>20) & 1)<<11 | ((inst>>21) & 0x3FF)<<1, 21
)
self.write_reg(rd, self.pc + 4)
return self.pc + j_imm
def execute_jalr(self, inst, rd, rs1, funct3):
imm = sign_extend((inst>>20) & 0xFFF, 12)
if funct3 == 0x0:
self.write_reg(rd, self.pc + 4)
return (self.regs[rs1] + imm) & ~1
return self.pc + 4
def execute_lw(self, inst, rd, rs1, funct3):
imm = sign_extend((inst>>20)& 0xFFF,12)
if funct3 == 0x2:
self.write_reg(rd, self.read_mem(self.regs[rs1] + imm))
def execute_i_type(self, inst, rd, rs1, funct3):
imm = sign_extend((inst >> 20) & 0xFFF, 12)
v1 = self.regs[rs1]
if funct3 == 0x0: self.write_reg(rd, v1 + imm)
elif funct3 == 0x3: self.write_reg(rd, 1 if v1 < unsigned_32(imm) else 0)
def execute_s_type(self, inst, rs1, rs2, funct3):
imm = sign_extend(((inst >> 25) & 0x7F) << 5 | ((inst >> 7) & 0x1F), 12)
if funct3 == 0x2:
self.write_mem(self.regs[rs1] + imm, self.regs[rs2])
def load_program(self, input_file):
with open(input_file, 'r') as f:
for i, line in enumerate(f):
line = line.strip()
if line:
self.prog_mem[i] = int(line, 2)
def dump_trace(self, out_f):
parts = [f"0b{self.pc:032b}"] + [f"0b{unsigned_32(v):032b}" for v in self.regs]
out_f.write(" ".join(parts) + "\n")
def dump_memory(self, out_f):
for i in range(32):
addr = DATA_MEM_START + i * 4
val = unsigned_32(self.data_mem[i])
out_f.write(f"0x{addr:08X}:0b{val:032b}\n")
def execute_instruction(self, inst):
opcode = inst & 0x7F
rd = (inst >> 7) & 0x1F
funct3 = (inst >> 12) & 0x7
rs1 = (inst >> 15) & 0x1F
rs2 = (inst >> 20) & 0x1F
funct7 = (inst >> 25) & 0x7F
next_pc = self.pc + 4
if opcode == OPCODE_R_TYPE: self.execute_r_type(rd, rs1, rs2, funct3, funct7)
elif opcode == OPCODE_I_TYPE: self.execute_i_type(inst, rd, rs1, funct3)
elif opcode == OPCODE_LW: self.execute_lw(inst, rd, rs1, funct3)
elif opcode == OPCODE_JALR: next_pc = self.execute_jalr(inst, rd, rs1, funct3)
elif opcode == OPCODE_S_TYPE: self.execute_s_type(inst, rs1, rs2, funct3)
elif opcode == OPCODE_B_TYPE: next_pc = self.execute_b_type(inst, rs1, rs2, funct3, next_pc)
elif opcode == OPCODE_LUI: self.write_reg(rd, inst & 0xFFFFF000)
elif opcode == OPCODE_AUIPC: self.write_reg(rd, self.pc + (inst & 0xFFFFF000))
elif opcode == OPCODE_JAL: next_pc = self.execute_jal(inst, rd)
return next_pc
def run(self, input_file, output_file):
self.load_program(input_file)
out_f = open(output_file, 'w')
try:
while True:
inst = self.read_mem(self.pc)
if inst == VIRTUAL_HALT:
self.dump_trace(out_f)
break
next_pc = self.execute_instruction(inst)
self.pc = unsigned_32(next_pc)
self.dump_trace(out_f)
self.dump_memory(out_f)
except Exception as e:
print(e)
finally:
out_f.close()
if __name__ == '__main__':
sim = Simulator()
if len(sys.argv) >= 3:
sim.run(sys.argv[1], sys.argv[2])