comments TestIssuer, add a stub FSM
[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, Mux, Const)
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, SVP64PrefixDecoder
26 from soc.decoder.decode2execute1 import IssuerDecode2ToOperand
27 from soc.decoder.decode2execute1 import Data
28 from soc.experiment.testmem import TestMemory # test only for instructions
29 from soc.regfile.regfiles import StateRegs, FastRegs
30 from soc.simple.core import NonProductionCore
31 from soc.config.test.test_loadstore import TestMemPspec
32 from soc.config.ifetch import ConfigFetchUnit
33 from soc.decoder.power_enums import MicrOp
34 from soc.debug.dmi import CoreDebug, DMIInterface
35 from soc.debug.jtag import JTAG
36 from soc.config.pinouts import get_pinspecs
37 from soc.config.state import CoreState
38 from soc.interrupts.xics import XICS_ICP, XICS_ICS
39 from soc.bus.simple_gpio import SimpleGPIO
40 from soc.bus.SPBlock512W64B8W import SPBlock512W64B8W
41 from soc.clock.select import ClockSelect
42 from soc.clock.dummypll import DummyPLL
43 from soc.sv.svstate import SVSTATERec
44
45
46 from nmutil.util import rising_edge
47
48 def get_insn(f_instr_o, pc):
49 if f_instr_o.width == 32:
50 return f_instr_o
51 else:
52 # 64-bit: bit 2 of pc decides which word to select
53 return f_instr_o.word_select(pc[2], 32)
54
55
56 class TestIssuerInternal(Elaboratable):
57 """TestIssuer - reads instructions from TestMemory and issues them
58
59 efficiency and speed is not the main goal here: functional correctness is.
60 """
61 def __init__(self, pspec):
62
63 # test is SVP64 is to be enabled
64 self.svp64_en = hasattr(pspec, "svp64") and (pspec.svp64 == True)
65
66 # JTAG interface. add this right at the start because if it's
67 # added it *modifies* the pspec, by adding enable/disable signals
68 # for parts of the rest of the core
69 self.jtag_en = hasattr(pspec, "debug") and pspec.debug == 'jtag'
70 if self.jtag_en:
71 subset = {'uart', 'mtwi', 'eint', 'gpio', 'mspi0', 'mspi1',
72 'pwm', 'sd0', 'sdr'}
73 self.jtag = JTAG(get_pinspecs(subset=subset))
74 # add signals to pspec to enable/disable icache and dcache
75 # (or data and intstruction wishbone if icache/dcache not included)
76 # https://bugs.libre-soc.org/show_bug.cgi?id=520
77 # TODO: do we actually care if these are not domain-synchronised?
78 # honestly probably not.
79 pspec.wb_icache_en = self.jtag.wb_icache_en
80 pspec.wb_dcache_en = self.jtag.wb_dcache_en
81 self.wb_sram_en = self.jtag.wb_sram_en
82 else:
83 self.wb_sram_en = Const(1)
84
85 # add 4k sram blocks?
86 self.sram4x4k = (hasattr(pspec, "sram4x4kblock") and
87 pspec.sram4x4kblock == True)
88 if self.sram4x4k:
89 self.sram4k = []
90 for i in range(4):
91 self.sram4k.append(SPBlock512W64B8W(name="sram4k_%d" % i,
92 features={'err'}))
93
94 # add interrupt controller?
95 self.xics = hasattr(pspec, "xics") and pspec.xics == True
96 if self.xics:
97 self.xics_icp = XICS_ICP()
98 self.xics_ics = XICS_ICS()
99 self.int_level_i = self.xics_ics.int_level_i
100
101 # add GPIO peripheral?
102 self.gpio = hasattr(pspec, "gpio") and pspec.gpio == True
103 if self.gpio:
104 self.simple_gpio = SimpleGPIO()
105 self.gpio_o = self.simple_gpio.gpio_o
106
107 # main instruction core25
108 self.core = core = NonProductionCore(pspec)
109
110 # instruction decoder. goes into Trap Record
111 pdecode = create_pdecode()
112 self.cur_state = CoreState("cur") # current state (MSR/PC/EINT/SVSTATE)
113 self.pdecode2 = PowerDecode2(pdecode, state=self.cur_state,
114 opkls=IssuerDecode2ToOperand,
115 svp64_en=self.svp64_en)
116 if self.svp64_en:
117 self.svp64 = SVP64PrefixDecoder() # for decoding SVP64 prefix
118
119 # Test Instruction memory
120 self.imem = ConfigFetchUnit(pspec).fu
121 # one-row cache of instruction read
122 self.iline = Signal(64) # one instruction line
123 self.iprev_adr = Signal(64) # previous address: if different, do read
124
125 # DMI interface
126 self.dbg = CoreDebug()
127
128 # instruction go/monitor
129 self.pc_o = Signal(64, reset_less=True)
130 self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me"
131 self.svstate_i = Data(32, "svstate_i") # ditto
132 self.core_bigendian_i = Signal()
133 self.busy_o = Signal(reset_less=True)
134 self.memerr_o = Signal(reset_less=True)
135
136 # STATE regfile read /write ports for PC, MSR, SVSTATE
137 staterf = self.core.regs.rf['state']
138 self.state_r_pc = staterf.r_ports['cia'] # PC rd
139 self.state_w_pc = staterf.w_ports['d_wr1'] # PC wr
140 self.state_r_msr = staterf.r_ports['msr'] # MSR rd
141 self.state_r_sv = staterf.r_ports['sv'] # SVSTATE rd
142 self.state_w_sv = staterf.w_ports['sv'] # SVSTATE wr
143
144 # DMI interface access
145 intrf = self.core.regs.rf['int']
146 crrf = self.core.regs.rf['cr']
147 xerrf = self.core.regs.rf['xer']
148 self.int_r = intrf.r_ports['dmi'] # INT read
149 self.cr_r = crrf.r_ports['full_cr_dbg'] # CR read
150 self.xer_r = xerrf.r_ports['full_xer'] # XER read
151
152 # hack method of keeping an eye on whether branch/trap set the PC
153 self.state_nia = self.core.regs.rf['state'].w_ports['nia']
154 self.state_nia.wen.name = 'state_nia_wen'
155
156 # pulse to synchronize the simulator at instruction end
157 self.insn_done = Signal()
158
159 def fetch_fsm(self, m, core, pc, svstate, nia, is_svp64_mode,
160 fetch_pc_ready_o, fetch_pc_valid_i,
161 fetch_insn_valid_o, fetch_insn_ready_i):
162 """fetch FSM
163 this FSM performs fetch of raw instruction data, partial-decodes
164 it 32-bit at a time to detect SVP64 prefixes, and will optionally
165 read a 2nd 32-bit quantity if that occurs.
166 """
167 comb = m.d.comb
168 sync = m.d.sync
169 pdecode2 = self.pdecode2
170 cur_state = self.cur_state
171 dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
172
173 msr_read = Signal(reset=1)
174
175 with m.FSM(name='fetch_fsm'):
176
177 # waiting (zzz)
178 with m.State("IDLE"):
179 comb += fetch_pc_ready_o.eq(1)
180 with m.If(fetch_pc_valid_i):
181 # instruction allowed to go: start by reading the PC
182 # capture the PC and also drop it into Insn Memory
183 # we have joined a pair of combinatorial memory
184 # lookups together. this is Generally Bad.
185 comb += self.imem.a_pc_i.eq(pc)
186 comb += self.imem.a_valid_i.eq(1)
187 comb += self.imem.f_valid_i.eq(1)
188 sync += cur_state.pc.eq(pc)
189 sync += cur_state.svstate.eq(svstate) # and svstate
190
191 # initiate read of MSR. arrives one clock later
192 comb += self.state_r_msr.ren.eq(1 << StateRegs.MSR)
193 sync += msr_read.eq(0)
194
195 m.next = "INSN_READ" # move to "wait for bus" phase
196
197 # dummy pause to find out why simulation is not keeping up
198 with m.State("INSN_READ"):
199 # one cycle later, msr/sv read arrives. valid only once.
200 with m.If(~msr_read):
201 sync += msr_read.eq(1) # yeah don't read it again
202 sync += cur_state.msr.eq(self.state_r_msr.data_o)
203 with m.If(self.imem.f_busy_o): # zzz...
204 # busy: stay in wait-read
205 comb += self.imem.a_valid_i.eq(1)
206 comb += self.imem.f_valid_i.eq(1)
207 with m.Else():
208 # not busy: instruction fetched
209 insn = get_insn(self.imem.f_instr_o, cur_state.pc)
210 if self.svp64_en:
211 svp64 = self.svp64
212 # decode the SVP64 prefix, if any
213 comb += svp64.raw_opcode_in.eq(insn)
214 comb += svp64.bigendian.eq(self.core_bigendian_i)
215 # pass the decoded prefix (if any) to PowerDecoder2
216 sync += pdecode2.sv_rm.eq(svp64.svp64_rm)
217 # remember whether this is a prefixed instruction, so
218 # the FSM can readily loop when VL==0
219 sync += is_svp64_mode.eq(svp64.is_svp64_mode)
220 # calculate the address of the following instruction
221 insn_size = Mux(svp64.is_svp64_mode, 8, 4)
222 sync += nia.eq(cur_state.pc + insn_size)
223 with m.If(~svp64.is_svp64_mode):
224 # with no prefix, store the instruction
225 # and hand it directly to the next FSM
226 sync += dec_opcode_i.eq(insn)
227 m.next = "INSN_READY"
228 with m.Else():
229 # fetch the rest of the instruction from memory
230 comb += self.imem.a_pc_i.eq(cur_state.pc + 4)
231 comb += self.imem.a_valid_i.eq(1)
232 comb += self.imem.f_valid_i.eq(1)
233 m.next = "INSN_READ2"
234 else:
235 # not SVP64 - 32-bit only
236 sync += nia.eq(cur_state.pc + 4)
237 sync += dec_opcode_i.eq(insn)
238 m.next = "INSN_READY"
239
240 with m.State("INSN_READ2"):
241 with m.If(self.imem.f_busy_o): # zzz...
242 # busy: stay in wait-read
243 comb += self.imem.a_valid_i.eq(1)
244 comb += self.imem.f_valid_i.eq(1)
245 with m.Else():
246 # not busy: instruction fetched
247 insn = get_insn(self.imem.f_instr_o, cur_state.pc+4)
248 sync += dec_opcode_i.eq(insn)
249 m.next = "INSN_READY"
250
251 with m.State("INSN_READY"):
252 # hand over the instruction, to be decoded
253 comb += fetch_insn_valid_o.eq(1)
254 with m.If(fetch_insn_ready_i):
255 m.next = "IDLE"
256
257 def fetch_predicate_fsm(self, m, core, TODO):
258 """fetch_predicate_fsm - obtains (constructs in the case of CR)
259 src/dest predicate masks
260
261 https://bugs.libre-soc.org/show_bug.cgi?id=617
262 the predicates can be read here, by using IntRegs r_ports['pred']
263 or CRRegs r_ports['pred']. in the case of CRs it will have to
264 be done through multiple reads, extracting one relevant at a time.
265 later, a faster way would be to use the 32-bit-wide CR port but
266 this is more complex decoding, here.
267 """
268 comb = m.d.comb
269 sync = m.d.sync
270 pdecode2 = self.pdecode2
271 rm_dec = pdecode2.rm_dec # SVP64RMModeDecode
272 predmode = rm_dec.predmode
273 srcpred, dstpred = rm_dec.srcpred, rm_dec.dstpred
274
275 def issue_fsm(self, m, core, pc_changed, sv_changed, nia,
276 dbg, core_rst, is_svp64_mode,
277 fetch_pc_ready_o, fetch_pc_valid_i,
278 fetch_insn_valid_o, fetch_insn_ready_i,
279 exec_insn_valid_i, exec_insn_ready_o,
280 exec_pc_valid_o, exec_pc_ready_i):
281 """issue FSM
282
283 decode / issue FSM. this interacts with the "fetch" FSM
284 through fetch_insn_ready/valid (incoming) and fetch_pc_ready/valid
285 (outgoing). also interacts with the "execute" FSM
286 through exec_insn_ready/valid (outgoing) and exec_pc_ready/valid
287 (incoming).
288 SVP64 RM prefixes have already been set up by the
289 "fetch" phase, so execute is fairly straightforward.
290 """
291
292 comb = m.d.comb
293 sync = m.d.sync
294 pdecode2 = self.pdecode2
295 cur_state = self.cur_state
296
297 # temporaries
298 dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
299
300 # for updating svstate (things like srcstep etc.)
301 update_svstate = Signal() # set this (below) if updating
302 new_svstate = SVSTATERec("new_svstate")
303 comb += new_svstate.eq(cur_state.svstate)
304
305 with m.FSM(name="issue_fsm"):
306
307 # sync with the "fetch" phase which is reading the instruction
308 # at this point, there is no instruction running, that
309 # could inadvertently update the PC.
310 with m.State("ISSUE_START"):
311 # wait on "core stop" release, before next fetch
312 # need to do this here, in case we are in a VL==0 loop
313 with m.If(~dbg.core_stop_o & ~core_rst):
314 comb += fetch_pc_valid_i.eq(1) # tell fetch to start
315 with m.If(fetch_pc_ready_o): # fetch acknowledged us
316 m.next = "INSN_WAIT"
317 with m.Else():
318 # tell core it's stopped, and acknowledge debug handshake
319 comb += core.core_stopped_i.eq(1)
320 comb += dbg.core_stopped_i.eq(1)
321 # while stopped, allow updating the PC and SVSTATE
322 with m.If(self.pc_i.ok):
323 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
324 comb += self.state_w_pc.data_i.eq(self.pc_i.data)
325 sync += pc_changed.eq(1)
326 with m.If(self.svstate_i.ok):
327 comb += new_svstate.eq(self.svstate_i.data)
328 comb += update_svstate.eq(1)
329 sync += sv_changed.eq(1)
330
331 # decode the instruction when it arrives
332 with m.State("INSN_WAIT"):
333 comb += fetch_insn_ready_i.eq(1)
334 with m.If(fetch_insn_valid_o):
335 # decode the instruction
336 sync += core.e.eq(pdecode2.e)
337 sync += core.state.eq(cur_state)
338 sync += core.raw_insn_i.eq(dec_opcode_i)
339 sync += core.bigendian_i.eq(self.core_bigendian_i)
340 # set RA_OR_ZERO detection in satellite decoders
341 sync += core.sv_a_nz.eq(pdecode2.sv_a_nz)
342 # loop into ISSUE_START if it's a SVP64 instruction
343 # and VL == 0. this because VL==0 is a for-loop
344 # from 0 to 0 i.e. always, always a NOP.
345 cur_vl = cur_state.svstate.vl
346 with m.If(is_svp64_mode & (cur_vl == 0)):
347 # update the PC before fetching the next instruction
348 # since we are in a VL==0 loop, no instruction was
349 # executed that we could be overwriting
350 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
351 comb += self.state_w_pc.data_i.eq(nia)
352 comb += self.insn_done.eq(1)
353 m.next = "ISSUE_START"
354 with m.Else():
355 m.next = "INSN_EXECUTE" # move to "execute"
356
357 # handshake with execution FSM, move to "wait" once acknowledged
358 with m.State("INSN_EXECUTE"):
359 comb += exec_insn_valid_i.eq(1) # trigger execute
360 with m.If(exec_insn_ready_o): # execute acknowledged us
361 m.next = "EXECUTE_WAIT"
362
363 with m.State("EXECUTE_WAIT"):
364 # wait on "core stop" release, at instruction end
365 # need to do this here, in case we are in a VL>1 loop
366 with m.If(~dbg.core_stop_o & ~core_rst):
367 comb += exec_pc_ready_i.eq(1)
368 with m.If(exec_pc_valid_o):
369 # precalculate srcstep+1 and dststep+1
370 # TODO these need to "skip" over predicated-out src/dst
371 # https://bugs.libre-soc.org/show_bug.cgi?id=617#c3
372 # but still without exceeding VL in either case
373 next_srcstep = Signal.like(cur_state.svstate.srcstep)
374 next_dststep = Signal.like(cur_state.svstate.dststep)
375 comb += next_srcstep.eq(cur_state.svstate.srcstep+1)
376 comb += next_dststep.eq(cur_state.svstate.dststep+1)
377
378 # was this the last loop iteration?
379 is_last = Signal()
380 cur_vl = cur_state.svstate.vl
381 comb += is_last.eq(next_srcstep == cur_vl)
382
383 # if either PC or SVSTATE were changed by the previous
384 # instruction, go directly back to Fetch, without
385 # updating either PC or SVSTATE
386 with m.If(pc_changed | sv_changed):
387 m.next = "ISSUE_START"
388
389 # also return to Fetch, when no output was a vector
390 # (regardless of SRCSTEP and VL), or when the last
391 # instruction was really the last one of the VL loop
392 with m.Elif((~pdecode2.loop_continue) | is_last):
393 # before going back to fetch, update the PC state
394 # register with the NIA.
395 # ok here we are not reading the branch unit.
396 # TODO: this just blithely overwrites whatever
397 # pipeline updated the PC
398 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
399 comb += self.state_w_pc.data_i.eq(nia)
400 # reset SRCSTEP before returning to Fetch
401 with m.If(pdecode2.loop_continue):
402 comb += new_svstate.srcstep.eq(0)
403 comb += new_svstate.dststep.eq(0)
404 comb += update_svstate.eq(1)
405 m.next = "ISSUE_START"
406
407 # returning to Execute? then, first update SRCSTEP
408 with m.Else():
409 comb += new_svstate.srcstep.eq(next_srcstep)
410 comb += new_svstate.dststep.eq(next_dststep)
411 comb += update_svstate.eq(1)
412 m.next = "DECODE_SV"
413
414 with m.Else():
415 comb += core.core_stopped_i.eq(1)
416 comb += dbg.core_stopped_i.eq(1)
417 # while stopped, allow updating the PC and SVSTATE
418 with m.If(self.pc_i.ok):
419 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
420 comb += self.state_w_pc.data_i.eq(self.pc_i.data)
421 sync += pc_changed.eq(1)
422 with m.If(self.svstate_i.ok):
423 comb += new_svstate.eq(self.svstate_i.data)
424 comb += update_svstate.eq(1)
425 sync += sv_changed.eq(1)
426
427 # need to decode the instruction again, after updating SRCSTEP
428 # in the previous state.
429 # mostly a copy of INSN_WAIT, but without the actual wait
430 with m.State("DECODE_SV"):
431 # decode the instruction
432 sync += core.e.eq(pdecode2.e)
433 sync += core.state.eq(cur_state)
434 sync += core.bigendian_i.eq(self.core_bigendian_i)
435 sync += core.sv_a_nz.eq(pdecode2.sv_a_nz)
436 m.next = "INSN_EXECUTE" # move to "execute"
437
438 # check if svstate needs updating: if so, write it to State Regfile
439 with m.If(update_svstate):
440 comb += self.state_w_sv.wen.eq(1<<StateRegs.SVSTATE)
441 comb += self.state_w_sv.data_i.eq(new_svstate)
442 sync += cur_state.svstate.eq(new_svstate) # for next clock
443
444 def execute_fsm(self, m, core, pc_changed, sv_changed,
445 exec_insn_valid_i, exec_insn_ready_o,
446 exec_pc_valid_o, exec_pc_ready_i):
447 """execute FSM
448
449 execute FSM. this interacts with the "issue" FSM
450 through exec_insn_ready/valid (incoming) and exec_pc_ready/valid
451 (outgoing). SVP64 RM prefixes have already been set up by the
452 "issue" phase, so execute is fairly straightforward.
453 """
454
455 comb = m.d.comb
456 sync = m.d.sync
457 pdecode2 = self.pdecode2
458
459 # temporaries
460 core_busy_o = core.busy_o # core is busy
461 core_ivalid_i = core.ivalid_i # instruction is valid
462 core_issue_i = core.issue_i # instruction is issued
463 insn_type = core.e.do.insn_type # instruction MicroOp type
464
465 with m.FSM(name="exec_fsm"):
466
467 # waiting for instruction bus (stays there until not busy)
468 with m.State("INSN_START"):
469 comb += exec_insn_ready_o.eq(1)
470 with m.If(exec_insn_valid_i):
471 comb += core_ivalid_i.eq(1) # instruction is valid
472 comb += core_issue_i.eq(1) # and issued
473 sync += sv_changed.eq(0)
474 sync += pc_changed.eq(0)
475 m.next = "INSN_ACTIVE" # move to "wait completion"
476
477 # instruction started: must wait till it finishes
478 with m.State("INSN_ACTIVE"):
479 with m.If(insn_type != MicrOp.OP_NOP):
480 comb += core_ivalid_i.eq(1) # instruction is valid
481 # note changes to PC and SVSTATE
482 with m.If(self.state_nia.wen & (1<<StateRegs.SVSTATE)):
483 sync += sv_changed.eq(1)
484 with m.If(self.state_nia.wen & (1<<StateRegs.PC)):
485 sync += pc_changed.eq(1)
486 with m.If(~core_busy_o): # instruction done!
487 comb += exec_pc_valid_o.eq(1)
488 with m.If(exec_pc_ready_i):
489 comb += self.insn_done.eq(1)
490 m.next = "INSN_START" # back to fetch
491
492 def elaborate(self, platform):
493 m = Module()
494 comb, sync = m.d.comb, m.d.sync
495
496 m.submodules.core = core = DomainRenamer("coresync")(self.core)
497 m.submodules.imem = imem = self.imem
498 m.submodules.dbg = dbg = self.dbg
499 if self.jtag_en:
500 m.submodules.jtag = jtag = self.jtag
501 # TODO: UART2GDB mux, here, from external pin
502 # see https://bugs.libre-soc.org/show_bug.cgi?id=499
503 sync += dbg.dmi.connect_to(jtag.dmi)
504
505 cur_state = self.cur_state
506
507 # 4x 4k SRAM blocks. these simply "exist", they get routed in litex
508 if self.sram4x4k:
509 for i, sram in enumerate(self.sram4k):
510 m.submodules["sram4k_%d" % i] = sram
511 comb += sram.enable.eq(self.wb_sram_en)
512
513 # XICS interrupt handler
514 if self.xics:
515 m.submodules.xics_icp = icp = self.xics_icp
516 m.submodules.xics_ics = ics = self.xics_ics
517 comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
518 sync += cur_state.eint.eq(icp.core_irq_o) # connect ICP to core
519
520 # GPIO test peripheral
521 if self.gpio:
522 m.submodules.simple_gpio = simple_gpio = self.simple_gpio
523
524 # connect one GPIO output to ICS bit 15 (like in microwatt soc.vhdl)
525 # XXX causes litex ECP5 test to get wrong idea about input and output
526 # (but works with verilator sim *sigh*)
527 #if self.gpio and self.xics:
528 # comb += self.int_level_i[15].eq(simple_gpio.gpio_o[0])
529
530 # instruction decoder
531 pdecode = create_pdecode()
532 m.submodules.dec2 = pdecode2 = self.pdecode2
533 if self.svp64_en:
534 m.submodules.svp64 = svp64 = self.svp64
535
536 # convenience
537 dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
538 intrf = self.core.regs.rf['int']
539
540 # clock delay power-on reset
541 cd_por = ClockDomain(reset_less=True)
542 cd_sync = ClockDomain()
543 core_sync = ClockDomain("coresync")
544 m.domains += cd_por, cd_sync, core_sync
545
546 ti_rst = Signal(reset_less=True)
547 delay = Signal(range(4), reset=3)
548 with m.If(delay != 0):
549 m.d.por += delay.eq(delay - 1)
550 comb += cd_por.clk.eq(ClockSignal())
551
552 # power-on reset delay
553 core_rst = ResetSignal("coresync")
554 comb += ti_rst.eq(delay != 0 | dbg.core_rst_o | ResetSignal())
555 comb += core_rst.eq(ti_rst)
556
557 # busy/halted signals from core
558 comb += self.busy_o.eq(core.busy_o)
559 comb += pdecode2.dec.bigendian.eq(self.core_bigendian_i)
560
561 # temporary hack: says "go" immediately for both address gen and ST
562 l0 = core.l0
563 ldst = core.fus.fus['ldst0']
564 st_go_edge = rising_edge(m, ldst.st.rel_o)
565 m.d.comb += ldst.ad.go_i.eq(ldst.ad.rel_o) # link addr-go direct to rel
566 m.d.comb += ldst.st.go_i.eq(st_go_edge) # link store-go to rising rel
567
568 # PC and instruction from I-Memory
569 comb += self.pc_o.eq(cur_state.pc)
570 pc_changed = Signal() # note write to PC
571 sv_changed = Signal() # note write to SVSTATE
572
573 # read the PC
574 pc = Signal(64, reset_less=True)
575 pc_ok_delay = Signal()
576 sync += pc_ok_delay.eq(~self.pc_i.ok)
577 with m.If(self.pc_i.ok):
578 # incoming override (start from pc_i)
579 comb += pc.eq(self.pc_i.data)
580 with m.Else():
581 # otherwise read StateRegs regfile for PC...
582 comb += self.state_r_pc.ren.eq(1<<StateRegs.PC)
583 # ... but on a 1-clock delay
584 with m.If(pc_ok_delay):
585 comb += pc.eq(self.state_r_pc.data_o)
586
587 # read svstate
588 svstate = Signal(64, reset_less=True)
589 svstate_ok_delay = Signal()
590 sync += svstate_ok_delay.eq(~self.svstate_i.ok)
591 with m.If(self.svstate_i.ok):
592 # incoming override (start from svstate__i)
593 comb += svstate.eq(self.svstate_i.data)
594 with m.Else():
595 # otherwise read StateRegs regfile for SVSTATE...
596 comb += self.state_r_sv.ren.eq(1 << StateRegs.SVSTATE)
597 # ... but on a 1-clock delay
598 with m.If(svstate_ok_delay):
599 comb += svstate.eq(self.state_r_sv.data_o)
600
601 # don't write pc every cycle
602 comb += self.state_w_pc.wen.eq(0)
603 comb += self.state_w_pc.data_i.eq(0)
604
605 # don't read msr every cycle
606 comb += self.state_r_msr.ren.eq(0)
607
608 # address of the next instruction, in the absence of a branch
609 # depends on the instruction size
610 nia = Signal(64, reset_less=True)
611
612 # connect up debug signals
613 # TODO comb += core.icache_rst_i.eq(dbg.icache_rst_o)
614 comb += dbg.terminate_i.eq(core.core_terminate_o)
615 comb += dbg.state.pc.eq(pc)
616 comb += dbg.state.svstate.eq(svstate)
617 comb += dbg.state.msr.eq(cur_state.msr)
618
619 # pass the prefix mode from Fetch to Issue, so the latter can loop
620 # on VL==0
621 is_svp64_mode = Signal()
622
623 # there are *THREE* FSMs, fetch (32/64-bit) issue, decode/execute.
624 # these are the handshake signals between fetch and decode/execute
625
626 # fetch FSM can run as soon as the PC is valid
627 fetch_pc_valid_i = Signal() # Execute tells Fetch "start next read"
628 fetch_pc_ready_o = Signal() # Fetch Tells SVSTATE "proceed"
629
630 # fetch FSM hands over the instruction to be decoded / issued
631 fetch_insn_valid_o = Signal()
632 fetch_insn_ready_i = Signal()
633
634 # issue FSM delivers the instruction to the be executed
635 exec_insn_valid_i = Signal()
636 exec_insn_ready_o = Signal()
637
638 # execute FSM, hands over the PC/SVSTATE back to the issue FSM
639 exec_pc_valid_o = Signal()
640 exec_pc_ready_i = Signal()
641
642 # the FSMs here are perhaps unusual in that they detect conditions
643 # then "hold" information, combinatorially, for the core
644 # (as opposed to using sync - which would be on a clock's delay)
645 # this includes the actual opcode, valid flags and so on.
646
647 # Fetch, then Issue, then Execute. Issue is where the VL for-loop
648 # lives. the ready/valid signalling is used to communicate between
649 # the three.
650
651 self.fetch_fsm(m, core, pc, svstate, nia, is_svp64_mode,
652 fetch_pc_ready_o, fetch_pc_valid_i,
653 fetch_insn_valid_o, fetch_insn_ready_i)
654
655 self.issue_fsm(m, core, pc_changed, sv_changed, nia,
656 dbg, core_rst, is_svp64_mode,
657 fetch_pc_ready_o, fetch_pc_valid_i,
658 fetch_insn_valid_o, fetch_insn_ready_i,
659 exec_insn_valid_i, exec_insn_ready_o,
660 exec_pc_valid_o, exec_pc_ready_i)
661
662 self.execute_fsm(m, core, pc_changed, sv_changed,
663 exec_insn_valid_i, exec_insn_ready_o,
664 exec_pc_valid_o, exec_pc_ready_i)
665
666 # this bit doesn't have to be in the FSM: connect up to read
667 # regfiles on demand from DMI
668 self.do_dmi(m, dbg)
669
670 # DEC and TB inc/dec FSM. copy of DEC is put into CoreState,
671 # (which uses that in PowerDecoder2 to raise 0x900 exception)
672 self.tb_dec_fsm(m, cur_state.dec)
673
674 return m
675
676 def do_dmi(self, m, dbg):
677 comb = m.d.comb
678 sync = m.d.sync
679 dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
680 intrf = self.core.regs.rf['int']
681
682 with m.If(d_reg.req): # request for regfile access being made
683 # TODO: error-check this
684 # XXX should this be combinatorial? sync better?
685 if intrf.unary:
686 comb += self.int_r.ren.eq(1<<d_reg.addr)
687 else:
688 comb += self.int_r.addr.eq(d_reg.addr)
689 comb += self.int_r.ren.eq(1)
690 d_reg_delay = Signal()
691 sync += d_reg_delay.eq(d_reg.req)
692 with m.If(d_reg_delay):
693 # data arrives one clock later
694 comb += d_reg.data.eq(self.int_r.data_o)
695 comb += d_reg.ack.eq(1)
696
697 # sigh same thing for CR debug
698 with m.If(d_cr.req): # request for regfile access being made
699 comb += self.cr_r.ren.eq(0b11111111) # enable all
700 d_cr_delay = Signal()
701 sync += d_cr_delay.eq(d_cr.req)
702 with m.If(d_cr_delay):
703 # data arrives one clock later
704 comb += d_cr.data.eq(self.cr_r.data_o)
705 comb += d_cr.ack.eq(1)
706
707 # aaand XER...
708 with m.If(d_xer.req): # request for regfile access being made
709 comb += self.xer_r.ren.eq(0b111111) # enable all
710 d_xer_delay = Signal()
711 sync += d_xer_delay.eq(d_xer.req)
712 with m.If(d_xer_delay):
713 # data arrives one clock later
714 comb += d_xer.data.eq(self.xer_r.data_o)
715 comb += d_xer.ack.eq(1)
716
717 def tb_dec_fsm(self, m, spr_dec):
718 """tb_dec_fsm
719
720 this is a FSM for updating either dec or tb. it runs alternately
721 DEC, TB, DEC, TB. note that SPR pipeline could have written a new
722 value to DEC, however the regfile has "passthrough" on it so this
723 *should* be ok.
724
725 see v3.0B p1097-1099 for Timeer Resource and p1065 and p1076
726 """
727
728 comb, sync = m.d.comb, m.d.sync
729 fast_rf = self.core.regs.rf['fast']
730 fast_r_dectb = fast_rf.r_ports['issue'] # DEC/TB
731 fast_w_dectb = fast_rf.w_ports['issue'] # DEC/TB
732
733 with m.FSM() as fsm:
734
735 # initiates read of current DEC
736 with m.State("DEC_READ"):
737 comb += fast_r_dectb.addr.eq(FastRegs.DEC)
738 comb += fast_r_dectb.ren.eq(1)
739 m.next = "DEC_WRITE"
740
741 # waits for DEC read to arrive (1 cycle), updates with new value
742 with m.State("DEC_WRITE"):
743 new_dec = Signal(64)
744 # TODO: MSR.LPCR 32-bit decrement mode
745 comb += new_dec.eq(fast_r_dectb.data_o - 1)
746 comb += fast_w_dectb.addr.eq(FastRegs.DEC)
747 comb += fast_w_dectb.wen.eq(1)
748 comb += fast_w_dectb.data_i.eq(new_dec)
749 sync += spr_dec.eq(new_dec) # copy into cur_state for decoder
750 m.next = "TB_READ"
751
752 # initiates read of current TB
753 with m.State("TB_READ"):
754 comb += fast_r_dectb.addr.eq(FastRegs.TB)
755 comb += fast_r_dectb.ren.eq(1)
756 m.next = "TB_WRITE"
757
758 # waits for read TB to arrive, initiates write of current TB
759 with m.State("TB_WRITE"):
760 new_tb = Signal(64)
761 comb += new_tb.eq(fast_r_dectb.data_o + 1)
762 comb += fast_w_dectb.addr.eq(FastRegs.TB)
763 comb += fast_w_dectb.wen.eq(1)
764 comb += fast_w_dectb.data_i.eq(new_tb)
765 m.next = "DEC_READ"
766
767 return m
768
769 def __iter__(self):
770 yield from self.pc_i.ports()
771 yield self.pc_o
772 yield self.memerr_o
773 yield from self.core.ports()
774 yield from self.imem.ports()
775 yield self.core_bigendian_i
776 yield self.busy_o
777
778 def ports(self):
779 return list(self)
780
781 def external_ports(self):
782 ports = self.pc_i.ports()
783 ports += [self.pc_o, self.memerr_o, self.core_bigendian_i, self.busy_o,
784 ]
785
786 if self.jtag_en:
787 ports += list(self.jtag.external_ports())
788 else:
789 # don't add DMI if JTAG is enabled
790 ports += list(self.dbg.dmi.ports())
791
792 ports += list(self.imem.ibus.fields.values())
793 ports += list(self.core.l0.cmpi.lsmem.lsi.slavebus.fields.values())
794
795 if self.sram4x4k:
796 for sram in self.sram4k:
797 ports += list(sram.bus.fields.values())
798
799 if self.xics:
800 ports += list(self.xics_icp.bus.fields.values())
801 ports += list(self.xics_ics.bus.fields.values())
802 ports.append(self.int_level_i)
803
804 if self.gpio:
805 ports += list(self.simple_gpio.bus.fields.values())
806 ports.append(self.gpio_o)
807
808 return ports
809
810 def ports(self):
811 return list(self)
812
813
814 class TestIssuer(Elaboratable):
815 def __init__(self, pspec):
816 self.ti = TestIssuerInternal(pspec)
817
818 self.pll = DummyPLL()
819
820 # PLL direct clock or not
821 self.pll_en = hasattr(pspec, "use_pll") and pspec.use_pll
822 if self.pll_en:
823 self.pll_18_o = Signal(reset_less=True)
824
825 def elaborate(self, platform):
826 m = Module()
827 comb = m.d.comb
828
829 # TestIssuer runs at direct clock
830 m.submodules.ti = ti = self.ti
831 cd_int = ClockDomain("coresync")
832
833 if self.pll_en:
834 # ClockSelect runs at PLL output internal clock rate
835 m.submodules.pll = pll = self.pll
836
837 # add clock domains from PLL
838 cd_pll = ClockDomain("pllclk")
839 m.domains += cd_pll
840
841 # PLL clock established. has the side-effect of running clklsel
842 # at the PLL's speed (see DomainRenamer("pllclk") above)
843 pllclk = ClockSignal("pllclk")
844 comb += pllclk.eq(pll.clk_pll_o)
845
846 # wire up external 24mhz to PLL
847 comb += pll.clk_24_i.eq(ClockSignal())
848
849 # output 18 mhz PLL test signal
850 comb += self.pll_18_o.eq(pll.pll_18_o)
851
852 # now wire up ResetSignals. don't mind them being in this domain
853 pll_rst = ResetSignal("pllclk")
854 comb += pll_rst.eq(ResetSignal())
855
856 # internal clock is set to selector clock-out. has the side-effect of
857 # running TestIssuer at this speed (see DomainRenamer("intclk") above)
858 intclk = ClockSignal("coresync")
859 if self.pll_en:
860 comb += intclk.eq(pll.clk_pll_o)
861 else:
862 comb += intclk.eq(ClockSignal())
863
864 return m
865
866 def ports(self):
867 return list(self.ti.ports()) + list(self.pll.ports()) + \
868 [ClockSignal(), ResetSignal()]
869
870 def external_ports(self):
871 ports = self.ti.external_ports()
872 ports.append(ClockSignal())
873 ports.append(ResetSignal())
874 if self.pll_en:
875 ports.append(self.pll.clk_sel_i)
876 ports.append(self.pll_18_o)
877 ports.append(self.pll.pll_lck_o)
878 return ports
879
880
881 if __name__ == '__main__':
882 units = {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1, 'logical': 1,
883 'spr': 1,
884 'div': 1,
885 'mul': 1,
886 'shiftrot': 1
887 }
888 pspec = TestMemPspec(ldst_ifacetype='bare_wb',
889 imem_ifacetype='bare_wb',
890 addr_wid=48,
891 mask_wid=8,
892 reg_wid=64,
893 units=units)
894 dut = TestIssuer(pspec)
895 vl = main(dut, ports=dut.ports(), name="test_issuer")
896
897 if len(sys.argv) == 1:
898 vl = rtlil.convert(dut, ports=dut.external_ports(), name="test_issuer")
899 with open("test_issuer.il", "w") as f:
900 f.write(vl)