adding option to include XICS external interrupts.
[soc.git] / src / soc / simple / issuer.py
1 """simple core issuer
2
3 not in any way intended for production use. this runs a FSM that:
4
5 * reads the Program Counter from StateRegs
6 * reads an instruction from a fixed-size Test Memory
7 * issues it to the Simple Core
8 * waits for it to complete
9 * increments the PC
10 * does it all over again
11
12 the purpose of this module is to verify the functional correctness
13 of the Function Units in the absolute simplest and clearest possible
14 way, and to at provide something that can be further incrementally
15 improved.
16 """
17
18 from nmigen import (Elaboratable, Module, Signal, ClockSignal, ResetSignal,
19 ClockDomain, DomainRenamer)
20 from nmigen.cli import rtlil
21 from nmigen.cli import main
22 import sys
23
24 from soc.decoder.power_decoder import create_pdecode
25 from soc.decoder.power_decoder2 import PowerDecode2
26 from soc.decoder.decode2execute1 import Data
27 from soc.experiment.testmem import TestMemory # test only for instructions
28 from soc.regfile.regfiles import StateRegs
29 from soc.simple.core import NonProductionCore
30 from soc.config.test.test_loadstore import TestMemPspec
31 from soc.config.ifetch import ConfigFetchUnit
32 from soc.decoder.power_enums import MicrOp
33 from soc.debug.dmi import CoreDebug, DMIInterface
34 from soc.config.state import CoreState
35 from soc.interrupts.xics import XICS_ICP, XICS_ICS
36
37 from nmutil.util import rising_edge
38
39
40 class TestIssuer(Elaboratable):
41 """TestIssuer - reads instructions from TestMemory and issues them
42
43 efficiency and speed is not the main goal here: functional correctness is.
44 """
45 def __init__(self, pspec):
46
47 # add interrupt controller?
48 self.xics = hasattr(pspec, "xics") and pspec.xics == True
49 if self.xics:
50 self.xics_icp = XICS_ICP()
51 self.xics_ics = XICS_ICS()
52
53 # main instruction core
54 self.core = core = NonProductionCore(pspec)
55
56 # instruction decoder
57 pdecode = create_pdecode()
58 self.pdecode2 = PowerDecode2(pdecode) # decoder
59
60 # Test Instruction memory
61 self.imem = ConfigFetchUnit(pspec).fu
62 # one-row cache of instruction read
63 self.iline = Signal(64) # one instruction line
64 self.iprev_adr = Signal(64) # previous address: if different, do read
65
66 # DMI interface
67 self.dbg = CoreDebug()
68
69 # instruction go/monitor
70 self.pc_o = Signal(64, reset_less=True)
71 self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me"
72 self.core_bigendian_i = Signal()
73 self.busy_o = Signal(reset_less=True)
74 self.memerr_o = Signal(reset_less=True)
75
76 # FAST regfile read /write ports for PC and MSR
77 staterf = self.core.regs.rf['state']
78 self.state_r_pc = staterf.r_ports['cia'] # PC rd
79 self.state_w_pc = staterf.w_ports['d_wr1'] # PC wr
80 self.state_r_msr = staterf.r_ports['msr'] # MSR rd
81
82 # DMI interface access
83 intrf = self.core.regs.rf['int']
84 crrf = self.core.regs.rf['cr']
85 xerrf = self.core.regs.rf['xer']
86 self.int_r = intrf.r_ports['dmi'] # INT read
87 self.cr_r = crrf.r_ports['full_cr_dbg'] # CR read
88 self.xer_r = xerrf.r_ports['full_xer'] # XER read
89
90 # hack method of keeping an eye on whether branch/trap set the PC
91 self.state_nia = self.core.regs.rf['state'].w_ports['nia']
92 self.state_nia.wen.name = 'state_nia_wen'
93
94 def elaborate(self, platform):
95 m = Module()
96 comb, sync = m.d.comb, m.d.sync
97
98 m.submodules.core = core = DomainRenamer("coresync")(self.core)
99 m.submodules.imem = imem = self.imem
100 m.submodules.dbg = dbg = self.dbg
101
102 if self.xics:
103 m.submodules.xics_icp = icp = self.xics_icp
104 m.submodules.xics_ics = ics = self.xics_ics
105 comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
106 comb += core.ext_irq_i.eq(icp.core_irq_o) # connect ICP to core
107
108 # instruction decoder
109 pdecode = create_pdecode()
110 m.submodules.dec2 = pdecode2 = self.pdecode2
111
112 # convenience
113 dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
114 intrf = self.core.regs.rf['int']
115
116 # clock delay power-on reset
117 cd_por = ClockDomain(reset_less=True)
118 cd_sync = ClockDomain()
119 core_sync = ClockDomain("coresync")
120 m.domains += cd_por, cd_sync, core_sync
121
122 delay = Signal(range(4), reset=3)
123 with m.If(delay != 0):
124 m.d.por += delay.eq(delay - 1)
125 comb += cd_por.clk.eq(ClockSignal())
126 comb += core_sync.clk.eq(ClockSignal())
127 # power-on reset delay
128 comb += core.core_reset_i.eq(delay != 0 | dbg.core_rst_o)
129
130 # busy/halted signals from core
131 comb += self.busy_o.eq(core.busy_o)
132 comb += pdecode2.dec.bigendian.eq(self.core_bigendian_i)
133
134 # current state (MSR/PC at the moment
135 cur_state = CoreState("cur")
136
137 # temporary hack: says "go" immediately for both address gen and ST
138 l0 = core.l0
139 ldst = core.fus.fus['ldst0']
140 st_go_edge = rising_edge(m, ldst.st.rel_o)
141 m.d.comb += ldst.ad.go_i.eq(ldst.ad.rel_o) # link addr-go direct to rel
142 m.d.comb += ldst.st.go_i.eq(st_go_edge) # link store-go to rising rel
143
144 # PC and instruction from I-Memory
145 pc_changed = Signal() # note write to PC
146 comb += self.pc_o.eq(cur_state.pc)
147 ilatch = Signal(32)
148
149 # next instruction (+4 on current)
150 nia = Signal(64, reset_less=True)
151 comb += nia.eq(cur_state.pc + 4)
152
153 # read the PC
154 pc = Signal(64, reset_less=True)
155 pc_ok_delay = Signal()
156 sync += pc_ok_delay.eq(~self.pc_i.ok)
157 with m.If(self.pc_i.ok):
158 # incoming override (start from pc_i)
159 comb += pc.eq(self.pc_i.data)
160 with m.Else():
161 # otherwise read StateRegs regfile for PC...
162 comb += self.state_r_pc.ren.eq(1<<StateRegs.PC)
163 # ... but on a 1-clock delay
164 with m.If(pc_ok_delay):
165 comb += pc.eq(self.state_r_pc.data_o)
166
167 # don't write pc every cycle
168 comb += self.state_w_pc.wen.eq(0)
169 comb += self.state_w_pc.data_i.eq(0)
170
171 # don't read msr every cycle
172 comb += self.state_r_msr.ren.eq(0)
173
174 # connect up debug signals
175 # TODO comb += core.icache_rst_i.eq(dbg.icache_rst_o)
176 comb += dbg.terminate_i.eq(core.core_terminate_o)
177 comb += dbg.state.pc.eq(pc)
178 #comb += dbg.state.pc.eq(cur_state.pc)
179 comb += dbg.state.msr.eq(cur_state.msr)
180
181 # temporaries
182 core_busy_o = core.busy_o # core is busy
183 core_ivalid_i = core.ivalid_i # instruction is valid
184 core_issue_i = core.issue_i # instruction is issued
185 dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
186
187 insn_type = core.e.do.insn_type
188 dec_state = pdecode2.state
189
190 # actually use a nmigen FSM for the first time (w00t)
191 # this FSM is perhaps unusual in that it detects conditions
192 # then "holds" information, combinatorially, for the core
193 # (as opposed to using sync - which would be on a clock's delay)
194 # this includes the actual opcode, valid flags and so on.
195 with m.FSM() as fsm:
196
197 # waiting (zzz)
198 with m.State("IDLE"):
199 sync += pc_changed.eq(0)
200 sync += core.e.eq(0)
201 with m.If(~dbg.core_stop_o & ~core.core_reset_i):
202 # instruction allowed to go: start by reading the PC
203 # capture the PC and also drop it into Insn Memory
204 # we have joined a pair of combinatorial memory
205 # lookups together. this is Generally Bad.
206 comb += self.imem.a_pc_i.eq(pc)
207 comb += self.imem.a_valid_i.eq(1)
208 comb += self.imem.f_valid_i.eq(1)
209 sync += cur_state.pc.eq(pc)
210
211 # initiate read of MSR
212 comb += self.state_r_msr.ren.eq(1<<StateRegs.MSR)
213
214 m.next = "INSN_READ" # move to "wait for bus" phase
215 with m.Else():
216 comb += core.core_stopped_i.eq(1)
217 comb += dbg.core_stopped_i.eq(1)
218
219 # dummy pause to find out why simulation is not keeping up
220 with m.State("INSN_READ"):
221 # one cycle later, msr read arrives
222 sync += cur_state.msr.eq(self.state_r_msr.data_o)
223 with m.If(self.imem.f_busy_o): # zzz...
224 # busy: stay in wait-read
225 comb += self.imem.a_valid_i.eq(1)
226 comb += self.imem.f_valid_i.eq(1)
227 with m.Else():
228 # not busy: instruction fetched
229 f_instr_o = self.imem.f_instr_o
230 if f_instr_o.width == 32:
231 insn = f_instr_o
232 else:
233 insn = f_instr_o.word_select(cur_state.pc[2], 32)
234 comb += dec_opcode_i.eq(insn) # actual opcode
235 comb += dec_state.eq(cur_state)
236 sync += core.e.eq(pdecode2.e)
237 sync += ilatch.eq(insn) # latch current insn
238 # also drop PC and MSR into decode "state"
239 m.next = "INSN_START" # move to "start"
240
241 # waiting for instruction bus (stays there until not busy)
242 with m.State("INSN_START"):
243 comb += core_ivalid_i.eq(1) # instruction is valid
244 comb += core_issue_i.eq(1) # and issued
245
246
247 m.next = "INSN_ACTIVE" # move to "wait completion"
248
249 # instruction started: must wait till it finishes
250 with m.State("INSN_ACTIVE"):
251 with m.If(insn_type != MicrOp.OP_NOP):
252 comb += core_ivalid_i.eq(1) # instruction is valid
253 with m.If(self.state_nia.wen):
254 sync += pc_changed.eq(1)
255 with m.If(~core_busy_o): # instruction done!
256 # ok here we are not reading the branch unit. TODO
257 # this just blithely overwrites whatever pipeline
258 # updated the PC
259 with m.If(~pc_changed):
260 comb += self.state_w_pc.wen.eq(1<<StateRegs.PC)
261 comb += self.state_w_pc.data_i.eq(nia)
262 sync += core.e.eq(0)
263 m.next = "IDLE" # back to idle
264
265 # this bit doesn't have to be in the FSM: connect up to read
266 # regfiles on demand from DMI
267 with m.If(d_reg.req): # request for regfile access being made
268 # TODO: error-check this
269 # XXX should this be combinatorial? sync better?
270 if intrf.unary:
271 comb += self.int_r.ren.eq(1<<d_reg.addr)
272 else:
273 comb += self.int_r.addr.eq(d_reg.addr)
274 comb += self.int_r.ren.eq(1)
275 d_reg_delay = Signal()
276 sync += d_reg_delay.eq(d_reg.req)
277 with m.If(d_reg_delay):
278 # data arrives one clock later
279 comb += d_reg.data.eq(self.int_r.data_o)
280 comb += d_reg.ack.eq(1)
281
282 # sigh same thing for CR debug
283 with m.If(d_cr.req): # request for regfile access being made
284 comb += self.cr_r.ren.eq(0b11111111) # enable all
285 d_cr_delay = Signal()
286 sync += d_cr_delay.eq(d_cr.req)
287 with m.If(d_cr_delay):
288 # data arrives one clock later
289 comb += d_cr.data.eq(self.cr_r.data_o)
290 comb += d_cr.ack.eq(1)
291
292 # aaand XER...
293 with m.If(d_xer.req): # request for regfile access being made
294 comb += self.xer_r.ren.eq(0b111111) # enable all
295 d_xer_delay = Signal()
296 sync += d_xer_delay.eq(d_xer.req)
297 with m.If(d_xer_delay):
298 # data arrives one clock later
299 comb += d_xer.data.eq(self.xer_r.data_o)
300 comb += d_xer.ack.eq(1)
301
302 return m
303
304 def __iter__(self):
305 yield from self.pc_i.ports()
306 yield self.pc_o
307 yield self.memerr_o
308 yield from self.core.ports()
309 yield from self.imem.ports()
310 yield self.core_bigendian_i
311 yield self.busy_o
312
313 def ports(self):
314 return list(self)
315
316 def external_ports(self):
317 ports = self.pc_i.ports()
318 ports += [self.pc_o, self.memerr_o, self.core_bigendian_i, self.busy_o,
319 ClockSignal(), ResetSignal(),
320 ]
321 ports += list(self.dbg.dmi.ports())
322 ports += list(self.imem.ibus.fields.values())
323 ports += list(self.core.l0.cmpi.lsmem.lsi.slavebus.fields.values())
324
325 if self.xics:
326 ports += list(self.xics_icp.bus.fields.values())
327 ports += list(self.xics_ics.bus.fields.values())
328
329 return ports
330
331 def ports(self):
332 return list(self)
333
334
335 if __name__ == '__main__':
336 units = {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1, 'logical': 1,
337 'spr': 1,
338 'div': 1,
339 'mul': 1,
340 'shiftrot': 1
341 }
342 pspec = TestMemPspec(ldst_ifacetype='bare_wb',
343 imem_ifacetype='bare_wb',
344 addr_wid=48,
345 mask_wid=8,
346 reg_wid=64,
347 units=units)
348 dut = TestIssuer(pspec)
349 vl = main(dut, ports=dut.ports(), name="test_issuer")
350
351 if len(sys.argv) == 1:
352 vl = rtlil.convert(dut, ports=dut.external_ports(), name="test_issuer")
353 with open("test_issuer.il", "w") as f:
354 f.write(vl)