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
)
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
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
37 from nmutil
.util
import rising_edge
40 class TestIssuer(Elaboratable
):
41 """TestIssuer - reads instructions from TestMemory and issues them
43 efficiency and speed is not the main goal here: functional correctness is.
45 def __init__(self
, pspec
):
47 # add interrupt controller?
48 self
.xics
= hasattr(pspec
, "xics") and pspec
.xics
== True
50 self
.xics_icp
= XICS_ICP()
51 self
.xics_ics
= XICS_ICS()
53 # main instruction core
54 self
.core
= core
= NonProductionCore(pspec
)
57 pdecode
= create_pdecode()
58 self
.pdecode2
= PowerDecode2(pdecode
) # decoder
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
67 self
.dbg
= CoreDebug()
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)
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
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
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'
94 def elaborate(self
, platform
):
96 comb
, sync
= m
.d
.comb
, m
.d
.sync
98 m
.submodules
.core
= core
= DomainRenamer("coresync")(self
.core
)
99 m
.submodules
.imem
= imem
= self
.imem
100 m
.submodules
.dbg
= dbg
= self
.dbg
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
108 # instruction decoder
109 pdecode
= create_pdecode()
110 m
.submodules
.dec2
= pdecode2
= self
.pdecode2
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']
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
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
)
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
)
134 # current state (MSR/PC at the moment
135 cur_state
= CoreState("cur")
137 # temporary hack: says "go" immediately for both address gen and ST
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
144 # PC and instruction from I-Memory
145 pc_changed
= Signal() # note write to PC
146 comb
+= self
.pc_o
.eq(cur_state
.pc
)
149 # next instruction (+4 on current)
150 nia
= Signal(64, reset_less
=True)
151 comb
+= nia
.eq(cur_state
.pc
+ 4)
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
)
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
)
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)
171 # don't read msr every cycle
172 comb
+= self
.state_r_msr
.ren
.eq(0)
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
)
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
187 insn_type
= core
.e
.do
.insn_type
188 dec_state
= pdecode2
.state
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.
198 with m
.State("IDLE"):
199 sync
+= pc_changed
.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
)
211 # initiate read of MSR
212 comb
+= self
.state_r_msr
.ren
.eq(1<<StateRegs
.MSR
)
214 m
.next
= "INSN_READ" # move to "wait for bus" phase
216 comb
+= core
.core_stopped_i
.eq(1)
217 comb
+= dbg
.core_stopped_i
.eq(1)
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)
228 # not busy: instruction fetched
229 f_instr_o
= self
.imem
.f_instr_o
230 if f_instr_o
.width
== 32:
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"
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
247 m
.next
= "INSN_ACTIVE" # move to "wait completion"
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
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
)
263 m
.next
= "IDLE" # back to idle
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?
271 comb
+= self
.int_r
.ren
.eq(1<<d_reg
.addr
)
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)
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)
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)
305 yield from self
.pc_i
.ports()
308 yield from self
.core
.ports()
309 yield from self
.imem
.ports()
310 yield self
.core_bigendian_i
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(),
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())
326 ports
+= list(self
.xics_icp
.bus
.fields
.values())
327 ports
+= list(self
.xics_ics
.bus
.fields
.values())
335 if __name__
== '__main__':
336 units
= {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1, 'logical': 1,
342 pspec
= TestMemPspec(ldst_ifacetype
='bare_wb',
343 imem_ifacetype
='bare_wb',
348 dut
= TestIssuer(pspec
)
349 vl
= main(dut
, ports
=dut
.ports(), name
="test_issuer")
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
: