99512458053b5116a71667070e6e9a67aec11c72
[openpower-isa.git] / src / openpower / cyclemodel / inorder.py
1 #!/usr/bin/env python3
2 # An In-order cycle-accurate model of a Power ISA 3.0 hardware implementation
3 # LGPLv3+
4 # Funded by NLnet
5 #
6 # Bugs: https://bugs.libre-soc.org/show_bug.cgi?id=1039
7 """
8 CPU: Fetch <- log file
9 |
10 Decode <- works out read/write regs
11 |
12 Issue <- checks read-regs, sets write-regs
13 |
14 Execute -> stages (countdown) clears write-regs
15
16 """
17
18
19 class RegisterWrite:
20 """
21 RegisterWrite: contains the set of Read-after-Write Hazards.
22 Anything in this set must be a STALL at Decode phase because the
23 answer has still not popped out the end of a pipeline
24 """
25 def __init__(self):
26 self.storage = set()
27
28 def expect_write(self, regs):
29 return self.storage.update(regs)
30
31 def write_expected(self, regs):
32 return (len(self.storage.intersection(regs)) != 0)
33
34 def retire_write(self, regs):
35 return self.storage.difference_update(regs)
36
37
38 class Execute:
39 """
40 Execute Pipeline: keeps a countdown-sorted list of instructions
41 to expect at a future cycle (tick). Anything at zero is processed
42 by assuming it is completed, and wishes to write to the regfile.
43 However there are only a limited number of regfile write ports,
44 so they must be handled a few at a time. under these circumstances
45 STALL condition is returned, and the "processor" must *NOT* tick().
46 """
47 def __init__(self, cpu):
48 self.stages = []
49 self.cpu = cpu
50
51 def add_stage(self, cycles_away, stage):
52 while cycles_away > len(self.stages):
53 self.stages.append([])
54 self.stages[cycles_away].append(stage)
55
56 def add_instruction(self, insn, writeregs):
57 self.add_stage(2, {'insn': insn, 'writes': writeregs})
58
59 def tick(self):
60 self.stages.pop(0) # tick drops anything at time "zero"
61
62 def process_instructions(self, stall):
63 instructions = self.stages[0] # get list of instructions
64 to_write = set() # need to know total writes
65 for instruction in instructions:
66 to_write.update(instruction['writes'])
67 # see if all writes can be done, otherwise stall
68 writes_possible = self.cpu.writes_possible(to_write):
69 if writes_possible != to_write:
70 stall = True
71 # retire the writes that are possible in this cycle (regfile writes)
72 self.cpu.regs.retire_write(writes_possible)
73 # and now go through the instructions, removing those regs written
74 for instruction in instructions:
75 instruction['writes'].difference_update(writes_possible)
76 return stall
77
78
79 class Fetch:
80 """
81 Fetch: reads the next log-entry and puts it into the queue.
82 """
83 def __init__(self, cpu):
84 self.stages = [None] # only ever going to be 1 long but hey
85 self.cpu = cpu
86
87 def tick(self):
88 self.stages[0] = None
89
90 def process_instructions(self, stall):
91 if stall: return stall
92 insn = self.stages[0] # get current instruction
93 if insn is not None:
94 self.cpu.decode.add_instructions(insn) # pass on instruction
95 # read from log file, write into self.stages[0]
96 # XXX TODO
97 return stall
98
99
100 class Decode:
101 """
102 Decode: performs a "decode" of the instruction. identifies and records
103 read/write regs. the reads/writes possible should likely not all be here,
104 perhaps split across "Issue"?
105 """
106 def __init__(self, cpu):
107 self.stages = [None] # only ever going to be 1 long but hey
108 self.cpu = cpu
109
110 def add_instruction(self, insn):
111 # get the read and write regs
112 writeregs = get_input_regs(insn)
113 readregs = get_output_regs(insn)
114 assert self.stages[0] is None # must be empty (tick or stall)
115 self.stages[0] = (insn, writeregs, readregs)
116
117 def tick(self):
118 self.stages[0] = None
119
120 def process_instructions(self, stall):
121 if stall: return stall
122 # get current instruction
123 insn, writeregs, readregs = self.stages[0]
124 # check that the readregs are all available
125 reads_possible = self.cpu.reads_possible(readregs):
126 stall = reads_possible != readregs
127 # perform the "reads" that are possible in this cycle
128 readregs.difference_update(reads_possible)
129 # and "Reserves" the writes
130 self.cpu.expect_write(writeregs)
131 # now pass the instruction on to Issue
132 self.cpu.issue.add_instruction(insn, writeregs)
133 return stall
134
135
136 class Issue:
137 """
138 Issue phase: if not stalled will place the instruction into execute.
139 TODO: move the reading and writing of regs here.
140 """
141 def __init__(self, cpu):
142 self.stages = [None] # only ever going to be 1 long but hey
143 self.cpu = cpu
144
145 def add_instruction(self, insn, writeregs):
146 # get the read and write regs
147 assert self.stages[0] is None # must be empty (tick or stall)
148 self.stages[0] = (insn, writeregs)
149
150 def tick(self):
151 self.stages[0] = None
152
153 def process_instructions(self, stall):
154 if stall: return stall
155 self.cpu.execute.add_instructions(self.stages[0])
156 return stall
157
158
159 class CPU:
160 """
161 CPU: contains Fetch, Decode, Issue and Execute pipelines, and regs.
162 Reads "instructions" from a file, starts putting them into a pipeline,
163 and monitors hazards. first version looks only for register hazards.
164 """
165 def __init__(self):
166 self.regs = RegisterWrite()
167 self.fetch = Fetch(self)
168 self.decode = Decode(self)
169 self.issue = Issue(self)
170 self.exe = Execute(self)
171 self.stall = False
172
173 def reads_possible(self, regs):
174 # TODO: subdivide this down by GPR FPR CR-field.
175 # currently assumes total of 3 regs are readable at one time
176 possible = set()
177 r = regs.copy()
178 while len(possible) < 3 and len(r) > 0:
179 possible.add(r.pop())
180 return possible
181
182 def writess_possible(self, regs):
183 # TODO: subdivide this down by GPR FPR CR-field.
184 # currently assumes total of 1 reg is possible regardless of what it is
185 possible = set()
186 r = regs.copy()
187 while len(possible) < 1 and len(r) > 0:
188 possible.add(r.pop())
189 return possible
190
191 def process_instructions(self):
192 stall = self.stall
193 stall = self.fetch.process_instructions(stall)
194 stall = self.decode.process_instructions(stall)
195 stall = self.issue.process_instructions(stall)
196 stall = self.exe.process_instructions(stall)
197 self.stall = stall
198 if not stall:
199 self.fetch.tick()
200 self.decode.tick()
201 self.issue.tick()
202 self.exe.tick()