3 not in any way intended for production use. this runs a FSM that:
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
10 * does it all over again
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
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
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
46 from nmutil
.util
import rising_edge
48 def get_insn(f_instr_o
, pc
):
49 if f_instr_o
.width
== 32:
52 # 64-bit: bit 2 of pc decides which word to select
53 return f_instr_o
.word_select(pc
[2], 32)
56 class TestIssuerInternal(Elaboratable
):
57 """TestIssuer - reads instructions from TestMemory and issues them
59 efficiency and speed is not the main goal here: functional correctness is.
61 def __init__(self
, pspec
):
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'
68 subset
= {'uart', 'mtwi', 'eint', 'gpio', 'mspi0', 'mspi1',
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
80 self
.wb_sram_en
= Const(1)
83 self
.sram4x4k
= (hasattr(pspec
, "sram4x4kblock") and
84 pspec
.sram4x4kblock
== True)
88 self
.sram4k
.append(SPBlock512W64B8W(name
="sram4k_%d" % i
))
90 # add interrupt controller?
91 self
.xics
= hasattr(pspec
, "xics") and pspec
.xics
== True
93 self
.xics_icp
= XICS_ICP()
94 self
.xics_ics
= XICS_ICS()
95 self
.int_level_i
= self
.xics_ics
.int_level_i
97 # add GPIO peripheral?
98 self
.gpio
= hasattr(pspec
, "gpio") and pspec
.gpio
== True
100 self
.simple_gpio
= SimpleGPIO()
101 self
.gpio_o
= self
.simple_gpio
.gpio_o
103 # main instruction core25
104 self
.core
= core
= NonProductionCore(pspec
)
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
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
120 self
.dbg
= CoreDebug()
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)
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
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
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'
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
):
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.
160 pdecode2
= self
.pdecode2
162 cur_state
= self
.cur_state
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
169 msr_read
= Signal(reset
=1)
171 with m
.FSM(name
='fetch_fsm'):
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
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)
191 m
.next
= "INSN_READ" # move to "wait for bus" phase
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)
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"
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"
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)
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"
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
):
243 def issue_fsm(self
, m
, core
, pc_changed
, sv_changed
, nia
,
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
):
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
256 SVP64 RM prefixes have already been set up by the
257 "fetch" phase, so execute is fairly straightforward.
262 pdecode2
= self
.pdecode2
263 cur_state
= self
.cur_state
266 dec_opcode_i
= pdecode2
.dec
.raw_opcode_in
# raw opcode
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
)
273 with m
.FSM(name
="issue_fsm"):
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"
280 comb
+= core
.core_stopped_i
.eq(1)
281 comb
+= dbg
.core_stopped_i
.eq(1)
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
):
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"
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"
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.
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"
336 comb
+= core
.core_stopped_i
.eq(1)
337 comb
+= dbg
.core_stopped_i
.eq(1)
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
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
):
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.
358 pdecode2
= self
.pdecode2
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
367 with m
.FSM(name
="exec_fsm"):
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"
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)
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
397 def elaborate(self
, platform
):
399 comb
, sync
= m
.d
.comb
, m
.d
.sync
401 m
.submodules
.core
= core
= DomainRenamer("coresync")(self
.core
)
402 m
.submodules
.imem
= imem
= self
.imem
403 m
.submodules
.dbg
= dbg
= self
.dbg
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
)
410 cur_state
= self
.cur_state
412 # 4x 4k SRAM blocks. these simply "exist", they get routed in litex
414 for i
, sram
in enumerate(self
.sram4k
):
415 m
.submodules
["sram4k_%d" % i
] = sram
416 comb
+= sram
.enable
.eq(self
.wb_sram_en
)
418 # XICS interrupt handler
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
425 # GPIO test peripheral
427 m
.submodules
.simple_gpio
= simple_gpio
= self
.simple_gpio
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])
435 # instruction decoder
436 pdecode
= create_pdecode()
437 m
.submodules
.dec2
= pdecode2
= self
.pdecode2
438 m
.submodules
.svp64
= svp64
= self
.svp64
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']
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
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())
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
)
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
)
465 # temporary hack: says "go" immediately for both address gen and ST
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
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
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
)
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
)
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
)
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
)
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)
510 # don't read msr every cycle
511 comb
+= self
.state_r_msr
.ren
.eq(0)
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)
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
)
524 # there are *TWO* FSMs, one fetch (32/64-bit) one decode/execute.
525 # these are the handshake signals between fetch and decode/execute
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"
531 # fetch FSM hands over the instruction to be decoded / issued
532 fetch_insn_valid_o
= Signal()
533 fetch_insn_ready_i
= Signal()
535 # issue FSM delivers the instruction to the be executed
536 exec_insn_valid_i
= Signal()
537 exec_insn_ready_o
= Signal()
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()
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.
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
)
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
,
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
)
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
)
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?
573 comb
+= self
.int_r
.ren
.eq(1<<d_reg
.addr
)
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)
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)
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)
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
)
610 def tb_dec_fsm(self
, m
, spr_dec
):
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
618 see v3.0B p1097-1099 for Timeer Resource and p1065 and p1076
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
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)
634 # waits for DEC read to arrive (1 cycle), updates with new value
635 with m
.State("DEC_WRITE"):
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
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)
651 # waits for read TB to arrive, initiates write of current TB
652 with m
.State("TB_WRITE"):
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
)
663 yield from self
.pc_i
.ports()
666 yield from self
.core
.ports()
667 yield from self
.imem
.ports()
668 yield self
.core_bigendian_i
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
,
680 ports
+= list(self
.jtag
.external_ports())
682 # don't add DMI if JTAG is enabled
683 ports
+= list(self
.dbg
.dmi
.ports())
685 ports
+= list(self
.imem
.ibus
.fields
.values())
686 ports
+= list(self
.core
.l0
.cmpi
.lsmem
.lsi
.slavebus
.fields
.values())
689 for sram
in self
.sram4k
:
690 ports
+= list(sram
.bus
.fields
.values())
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
)
698 ports
+= list(self
.simple_gpio
.bus
.fields
.values())
699 ports
.append(self
.gpio_o
)
707 class TestIssuer(Elaboratable
):
708 def __init__(self
, pspec
):
709 self
.ti
= TestIssuerInternal(pspec
)
711 self
.pll
= DummyPLL()
713 # PLL direct clock or not
714 self
.pll_en
= hasattr(pspec
, "use_pll") and pspec
.use_pll
716 self
.pll_18_o
= Signal(reset_less
=True)
718 def elaborate(self
, platform
):
722 # TestIssuer runs at direct clock
723 m
.submodules
.ti
= ti
= self
.ti
724 cd_int
= ClockDomain("coresync")
727 # ClockSelect runs at PLL output internal clock rate
728 m
.submodules
.pll
= pll
= self
.pll
730 # add clock domains from PLL
731 cd_pll
= ClockDomain("pllclk")
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
)
739 # wire up external 24mhz to PLL
740 comb
+= pll
.clk_24_i
.eq(ClockSignal())
742 # output 18 mhz PLL test signal
743 comb
+= self
.pll_18_o
.eq(pll
.pll_18_o
)
745 # now wire up ResetSignals. don't mind them being in this domain
746 pll_rst
= ResetSignal("pllclk")
747 comb
+= pll_rst
.eq(ResetSignal())
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")
753 comb
+= intclk
.eq(pll
.clk_pll_o
)
755 comb
+= intclk
.eq(ClockSignal())
760 return list(self
.ti
.ports()) + list(self
.pll
.ports()) + \
761 [ClockSignal(), ResetSignal()]
763 def external_ports(self
):
764 ports
= self
.ti
.external_ports()
765 ports
.append(ClockSignal())
766 ports
.append(ResetSignal())
768 ports
.append(self
.pll
.clk_sel_i
)
769 ports
.append(self
.pll_18_o
)
770 ports
.append(self
.pll
.pll_lck_o
)
774 if __name__
== '__main__':
775 units
= {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1, 'logical': 1,
781 pspec
= TestMemPspec(ldst_ifacetype
='bare_wb',
782 imem_ifacetype
='bare_wb',
787 dut
= TestIssuer(pspec
)
788 vl
= main(dut
, ports
=dut
.ports(), name
="test_issuer")
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
: