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