sigh have to allow external clocks and reset mess even in microwatt-compat
[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, Repl, Cat)
20 from nmigen.cli import rtlil
21 from nmigen.cli import main
22 import sys
23
24 from nmutil.singlepipe import ControlBase
25 from soc.simple.core_data import FetchOutput, FetchInput
26
27 from nmigen.lib.coding import PriorityEncoder
28
29 from openpower.decoder.power_decoder import create_pdecode
30 from openpower.decoder.power_decoder2 import PowerDecode2, SVP64PrefixDecoder
31 from openpower.decoder.decode2execute1 import IssuerDecode2ToOperand
32 from openpower.decoder.decode2execute1 import Data
33 from openpower.decoder.power_enums import (MicrOp, SVP64PredInt, SVP64PredCR,
34 SVP64PredMode)
35 from openpower.state import CoreState
36 from openpower.consts import (CR, SVP64CROffs, MSR)
37 from soc.experiment.testmem import TestMemory # test only for instructions
38 from soc.regfile.regfiles import StateRegs, FastRegs
39 from soc.simple.core import NonProductionCore
40 from soc.config.test.test_loadstore import TestMemPspec
41 from soc.config.ifetch import ConfigFetchUnit
42 from soc.debug.dmi import CoreDebug, DMIInterface
43 from soc.debug.jtag import JTAG
44 from soc.config.pinouts import get_pinspecs
45 from soc.interrupts.xics import XICS_ICP, XICS_ICS
46 from soc.bus.simple_gpio import SimpleGPIO
47 from soc.bus.SPBlock512W64B8W import SPBlock512W64B8W
48 from soc.clock.select import ClockSelect
49 from soc.clock.dummypll import DummyPLL
50 from openpower.sv.svstate import SVSTATERec
51 from soc.experiment.icache import ICache
52
53 from nmutil.util import rising_edge
54
55
56 def get_insn(f_instr_o, pc):
57 if f_instr_o.width == 32:
58 return f_instr_o
59 else:
60 # 64-bit: bit 2 of pc decides which word to select
61 return f_instr_o.word_select(pc[2], 32)
62
63 # gets state input or reads from state regfile
64
65
66 def state_get(m, res, core_rst, state_i, name, regfile, regnum):
67 comb = m.d.comb
68 sync = m.d.sync
69 # read the {insert state variable here}
70 res_ok_delay = Signal(name="%s_ok_delay" % name)
71 with m.If(~core_rst):
72 sync += res_ok_delay.eq(~state_i.ok)
73 with m.If(state_i.ok):
74 # incoming override (start from pc_i)
75 comb += res.eq(state_i.data)
76 with m.Else():
77 # otherwise read StateRegs regfile for {insert state here}...
78 comb += regfile.ren.eq(1 << regnum)
79 # ... but on a 1-clock delay
80 with m.If(res_ok_delay):
81 comb += res.eq(regfile.o_data)
82
83
84 def get_predint(m, mask, name):
85 """decode SVP64 predicate integer mask field to reg number and invert
86 this is identical to the equivalent function in ISACaller except that
87 it doesn't read the INT directly, it just decodes "what needs to be done"
88 i.e. which INT reg, whether it is shifted and whether it is bit-inverted.
89
90 * all1s is set to indicate that no mask is to be applied.
91 * regread indicates the GPR register number to be read
92 * invert is set to indicate that the register value is to be inverted
93 * unary indicates that the contents of the register is to be shifted 1<<r3
94 """
95 comb = m.d.comb
96 regread = Signal(5, name=name+"regread")
97 invert = Signal(name=name+"invert")
98 unary = Signal(name=name+"unary")
99 all1s = Signal(name=name+"all1s")
100 with m.Switch(mask):
101 with m.Case(SVP64PredInt.ALWAYS.value):
102 comb += all1s.eq(1) # use 0b1111 (all ones)
103 with m.Case(SVP64PredInt.R3_UNARY.value):
104 comb += regread.eq(3)
105 comb += unary.eq(1) # 1<<r3 - shift r3 (single bit)
106 with m.Case(SVP64PredInt.R3.value):
107 comb += regread.eq(3)
108 with m.Case(SVP64PredInt.R3_N.value):
109 comb += regread.eq(3)
110 comb += invert.eq(1)
111 with m.Case(SVP64PredInt.R10.value):
112 comb += regread.eq(10)
113 with m.Case(SVP64PredInt.R10_N.value):
114 comb += regread.eq(10)
115 comb += invert.eq(1)
116 with m.Case(SVP64PredInt.R30.value):
117 comb += regread.eq(30)
118 with m.Case(SVP64PredInt.R30_N.value):
119 comb += regread.eq(30)
120 comb += invert.eq(1)
121 return regread, invert, unary, all1s
122
123
124 def get_predcr(m, mask, name):
125 """decode SVP64 predicate CR to reg number field and invert status
126 this is identical to _get_predcr in ISACaller
127 """
128 comb = m.d.comb
129 idx = Signal(2, name=name+"idx")
130 invert = Signal(name=name+"crinvert")
131 with m.Switch(mask):
132 with m.Case(SVP64PredCR.LT.value):
133 comb += idx.eq(CR.LT)
134 comb += invert.eq(0)
135 with m.Case(SVP64PredCR.GE.value):
136 comb += idx.eq(CR.LT)
137 comb += invert.eq(1)
138 with m.Case(SVP64PredCR.GT.value):
139 comb += idx.eq(CR.GT)
140 comb += invert.eq(0)
141 with m.Case(SVP64PredCR.LE.value):
142 comb += idx.eq(CR.GT)
143 comb += invert.eq(1)
144 with m.Case(SVP64PredCR.EQ.value):
145 comb += idx.eq(CR.EQ)
146 comb += invert.eq(0)
147 with m.Case(SVP64PredCR.NE.value):
148 comb += idx.eq(CR.EQ)
149 comb += invert.eq(1)
150 with m.Case(SVP64PredCR.SO.value):
151 comb += idx.eq(CR.SO)
152 comb += invert.eq(0)
153 with m.Case(SVP64PredCR.NS.value):
154 comb += idx.eq(CR.SO)
155 comb += invert.eq(1)
156 return idx, invert
157
158
159 class TestIssuerBase(Elaboratable):
160 """TestIssuerBase - common base class for Issuers
161
162 takes care of power-on reset, peripherals, debug, DEC/TB,
163 and gets PC/MSR/SVSTATE from the State Regfile etc.
164 """
165
166 def __init__(self, pspec):
167
168 # test if microwatt compatibility is to be enabled
169 self.microwatt_compat = (hasattr(pspec, "microwatt_compat") and
170 (pspec.microwatt_compat == True))
171 self.alt_reset = Signal(reset_less=True) # not connected yet (microwatt)
172
173 # test is SVP64 is to be enabled
174 self.svp64_en = hasattr(pspec, "svp64") and (pspec.svp64 == True)
175
176 # and if regfiles are reduced
177 self.regreduce_en = (hasattr(pspec, "regreduce") and
178 (pspec.regreduce == True))
179
180 # and if overlap requested
181 self.allow_overlap = (hasattr(pspec, "allow_overlap") and
182 (pspec.allow_overlap == True))
183
184 # and get the core domain
185 self.core_domain = "coresync"
186 if (hasattr(pspec, "core_domain") and
187 isinstance(pspec.core_domain, str)):
188 self.core_domain = pspec.core_domain
189
190 # JTAG interface. add this right at the start because if it's
191 # added it *modifies* the pspec, by adding enable/disable signals
192 # for parts of the rest of the core
193 self.jtag_en = hasattr(pspec, "debug") and pspec.debug == 'jtag'
194 #self.dbg_domain = "sync" # sigh "dbgsunc" too problematic
195 self.dbg_domain = "dbgsync" # domain for DMI/JTAG clock
196 if self.jtag_en:
197 # XXX MUST keep this up-to-date with litex, and
198 # soc-cocotb-sim, and err.. all needs sorting out, argh
199 subset = ['uart',
200 'mtwi',
201 'eint', 'gpio', 'mspi0',
202 # 'mspi1', - disabled for now
203 # 'pwm', 'sd0', - disabled for now
204 'sdr']
205 self.jtag = JTAG(get_pinspecs(subset=subset),
206 domain=self.dbg_domain)
207 # add signals to pspec to enable/disable icache and dcache
208 # (or data and intstruction wishbone if icache/dcache not included)
209 # https://bugs.libre-soc.org/show_bug.cgi?id=520
210 # TODO: do we actually care if these are not domain-synchronised?
211 # honestly probably not.
212 pspec.wb_icache_en = self.jtag.wb_icache_en
213 pspec.wb_dcache_en = self.jtag.wb_dcache_en
214 self.wb_sram_en = self.jtag.wb_sram_en
215 else:
216 self.wb_sram_en = Const(1)
217
218 # add 4k sram blocks?
219 self.sram4x4k = (hasattr(pspec, "sram4x4kblock") and
220 pspec.sram4x4kblock == True)
221 if self.sram4x4k:
222 self.sram4k = []
223 for i in range(4):
224 self.sram4k.append(SPBlock512W64B8W(name="sram4k_%d" % i,
225 # features={'err'}
226 ))
227
228 # add interrupt controller?
229 self.xics = hasattr(pspec, "xics") and pspec.xics == True
230 if self.xics:
231 self.xics_icp = XICS_ICP()
232 self.xics_ics = XICS_ICS()
233 self.int_level_i = self.xics_ics.int_level_i
234 else:
235 self.ext_irq = Signal()
236
237 # add GPIO peripheral?
238 self.gpio = hasattr(pspec, "gpio") and pspec.gpio == True
239 if self.gpio:
240 self.simple_gpio = SimpleGPIO()
241 self.gpio_o = self.simple_gpio.gpio_o
242
243 # main instruction core. suitable for prototyping / demo only
244 self.core = core = NonProductionCore(pspec)
245 self.core_rst = ResetSignal(self.core_domain)
246
247 # instruction decoder. goes into Trap Record
248 #pdecode = create_pdecode()
249 self.cur_state = CoreState("cur") # current state (MSR/PC/SVSTATE)
250 self.pdecode2 = PowerDecode2(None, state=self.cur_state,
251 opkls=IssuerDecode2ToOperand,
252 svp64_en=self.svp64_en,
253 regreduce_en=self.regreduce_en)
254 pdecode = self.pdecode2.dec
255
256 if self.svp64_en:
257 self.svp64 = SVP64PrefixDecoder() # for decoding SVP64 prefix
258
259 self.update_svstate = Signal() # set this if updating svstate
260 self.new_svstate = new_svstate = SVSTATERec("new_svstate")
261
262 # Test Instruction memory
263 if hasattr(core, "icache"):
264 # XXX BLECH! use pspec to transfer the I-Cache to ConfigFetchUnit
265 # truly dreadful. needs a huge reorg.
266 pspec.icache = core.icache
267 self.imem = ConfigFetchUnit(pspec).fu
268
269 # DMI interface
270 self.dbg = CoreDebug()
271 self.dbg_rst_i = Signal(reset_less=True)
272
273 # instruction go/monitor
274 self.pc_o = Signal(64, reset_less=True)
275 self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me"
276 self.msr_i = Data(64, "msr_i") # set "ok" to indicate "please change me"
277 self.svstate_i = Data(64, "svstate_i") # ditto
278 self.core_bigendian_i = Signal() # TODO: set based on MSR.LE
279 self.busy_o = Signal(reset_less=True)
280 self.memerr_o = Signal(reset_less=True)
281
282 # STATE regfile read /write ports for PC, MSR, SVSTATE
283 staterf = self.core.regs.rf['state']
284 self.state_r_msr = staterf.r_ports['msr'] # MSR rd
285 self.state_r_pc = staterf.r_ports['cia'] # PC rd
286 self.state_r_sv = staterf.r_ports['sv'] # SVSTATE rd
287
288 self.state_w_msr = staterf.w_ports['msr'] # MSR wr
289 self.state_w_pc = staterf.w_ports['d_wr1'] # PC wr
290 self.state_w_sv = staterf.w_ports['sv'] # SVSTATE wr
291
292 # DMI interface access
293 intrf = self.core.regs.rf['int']
294 crrf = self.core.regs.rf['cr']
295 xerrf = self.core.regs.rf['xer']
296 self.int_r = intrf.r_ports['dmi'] # INT read
297 self.cr_r = crrf.r_ports['full_cr_dbg'] # CR read
298 self.xer_r = xerrf.r_ports['full_xer'] # XER read
299
300 if self.svp64_en:
301 # for predication
302 self.int_pred = intrf.r_ports['pred'] # INT predicate read
303 self.cr_pred = crrf.r_ports['cr_pred'] # CR predicate read
304
305 # hack method of keeping an eye on whether branch/trap set the PC
306 self.state_nia = self.core.regs.rf['state'].w_ports['nia']
307 self.state_nia.wen.name = 'state_nia_wen'
308
309 # pulse to synchronize the simulator at instruction end
310 self.insn_done = Signal()
311
312 # indicate any instruction still outstanding, in execution
313 self.any_busy = Signal()
314
315 if self.svp64_en:
316 # store copies of predicate masks
317 self.srcmask = Signal(64)
318 self.dstmask = Signal(64)
319
320 def setup_peripherals(self, m):
321 comb, sync = m.d.comb, m.d.sync
322
323 # okaaaay so the debug module must be in coresync clock domain
324 # but NOT its reset signal. to cope with this, set every single
325 # submodule explicitly in coresync domain, debug and JTAG
326 # in their own one but using *external* reset.
327 csd = DomainRenamer(self.core_domain)
328 dbd = DomainRenamer(self.dbg_domain)
329
330 if self.microwatt_compat:
331 m.submodules.core = core = self.core
332 else:
333 m.submodules.core = core = csd(self.core)
334 # this _so_ needs sorting out. ICache is added down inside
335 # LoadStore1 and is already a submodule of LoadStore1
336 if not isinstance(self.imem, ICache):
337 m.submodules.imem = imem = csd(self.imem)
338 m.submodules.dbg = dbg = dbd(self.dbg)
339 if self.jtag_en:
340 m.submodules.jtag = jtag = dbd(self.jtag)
341 # TODO: UART2GDB mux, here, from external pin
342 # see https://bugs.libre-soc.org/show_bug.cgi?id=499
343 sync += dbg.dmi.connect_to(jtag.dmi)
344
345 # fixup the clocks in microwatt-compat mode (but leave resets alone
346 # so that microwatt soc.vhdl can pull a reset on the core or DMI
347 # can do it, just like in TestIssuer)
348 if self.microwatt_compat:
349 intclk = ClockSignal(self.core_domain)
350 dbgclk = ClockSignal(self.dbg_domain)
351 if self.core_domain != 'sync':
352 comb += intclk.eq(ClockSignal())
353 if self.dbg_domain != 'sync':
354 comb += dbgclk.eq(ClockSignal())
355
356 cur_state = self.cur_state
357
358 # 4x 4k SRAM blocks. these simply "exist", they get routed in litex
359 if self.sram4x4k:
360 for i, sram in enumerate(self.sram4k):
361 m.submodules["sram4k_%d" % i] = csd(sram)
362 comb += sram.enable.eq(self.wb_sram_en)
363
364 # XICS interrupt handler
365 if self.xics:
366 m.submodules.xics_icp = icp = csd(self.xics_icp)
367 m.submodules.xics_ics = ics = csd(self.xics_ics)
368 comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
369 sync += cur_state.eint.eq(icp.core_irq_o) # connect ICP to core
370 else:
371 sync += cur_state.eint.eq(self.ext_irq) # connect externally
372
373 # GPIO test peripheral
374 if self.gpio:
375 m.submodules.simple_gpio = simple_gpio = csd(self.simple_gpio)
376
377 # connect one GPIO output to ICS bit 15 (like in microwatt soc.vhdl)
378 # XXX causes litex ECP5 test to get wrong idea about input and output
379 # (but works with verilator sim *sigh*)
380 # if self.gpio and self.xics:
381 # comb += self.int_level_i[15].eq(simple_gpio.gpio_o[0])
382
383 # instruction decoder
384 pdecode = create_pdecode()
385 m.submodules.dec2 = pdecode2 = csd(self.pdecode2)
386 if self.svp64_en:
387 m.submodules.svp64 = svp64 = csd(self.svp64)
388
389 # convenience
390 dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
391 intrf = self.core.regs.rf['int']
392
393 # clock delay power-on reset
394 cd_por = ClockDomain(reset_less=True)
395 cd_sync = ClockDomain()
396 m.domains += cd_por, cd_sync
397 core_sync = ClockDomain(self.core_domain)
398 if self.core_domain != "sync":
399 m.domains += core_sync
400 if self.dbg_domain != "sync":
401 dbg_sync = ClockDomain(self.dbg_domain)
402 m.domains += dbg_sync
403
404 ti_rst = Signal(reset_less=True)
405 delay = Signal(range(4), reset=3)
406 with m.If(delay != 0):
407 m.d.por += delay.eq(delay - 1)
408 comb += cd_por.clk.eq(ClockSignal())
409
410 # power-on reset delay
411 core_rst = ResetSignal(self.core_domain)
412 if self.core_domain != "sync":
413 comb += ti_rst.eq(delay != 0 | dbg.core_rst_o | ResetSignal())
414 comb += core_rst.eq(ti_rst)
415 else:
416 with m.If(delay != 0 | dbg.core_rst_o):
417 comb += core_rst.eq(1)
418
419 # connect external reset signal to DMI Reset
420 if self.dbg_domain != "sync":
421 dbg_rst = ResetSignal(self.dbg_domain)
422 comb += dbg_rst.eq(self.dbg_rst_i)
423
424 # busy/halted signals from core
425 core_busy_o = ~core.p.o_ready | core.n.o_data.busy_o # core is busy
426 comb += self.busy_o.eq(core_busy_o)
427 comb += pdecode2.dec.bigendian.eq(self.core_bigendian_i)
428
429 # temporary hack: says "go" immediately for both address gen and ST
430 l0 = core.l0
431 ldst = core.fus.fus['ldst0']
432 st_go_edge = rising_edge(m, ldst.st.rel_o)
433 # link addr-go direct to rel
434 m.d.comb += ldst.ad.go_i.eq(ldst.ad.rel_o)
435 m.d.comb += ldst.st.go_i.eq(st_go_edge) # link store-go to rising rel
436
437 def do_dmi(self, m, dbg):
438 """deals with DMI debug requests
439
440 currently only provides read requests for the INT regfile, CR and XER
441 it will later also deal with *writing* to these regfiles.
442 """
443 comb = m.d.comb
444 sync = m.d.sync
445 dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
446 intrf = self.core.regs.rf['int']
447
448 with m.If(d_reg.req): # request for regfile access being made
449 # TODO: error-check this
450 # XXX should this be combinatorial? sync better?
451 if intrf.unary:
452 comb += self.int_r.ren.eq(1 << d_reg.addr)
453 else:
454 comb += self.int_r.addr.eq(d_reg.addr)
455 comb += self.int_r.ren.eq(1)
456 d_reg_delay = Signal()
457 sync += d_reg_delay.eq(d_reg.req)
458 with m.If(d_reg_delay):
459 # data arrives one clock later
460 comb += d_reg.data.eq(self.int_r.o_data)
461 comb += d_reg.ack.eq(1)
462
463 # sigh same thing for CR debug
464 with m.If(d_cr.req): # request for regfile access being made
465 comb += self.cr_r.ren.eq(0b11111111) # enable all
466 d_cr_delay = Signal()
467 sync += d_cr_delay.eq(d_cr.req)
468 with m.If(d_cr_delay):
469 # data arrives one clock later
470 comb += d_cr.data.eq(self.cr_r.o_data)
471 comb += d_cr.ack.eq(1)
472
473 # aaand XER...
474 with m.If(d_xer.req): # request for regfile access being made
475 comb += self.xer_r.ren.eq(0b111111) # enable all
476 d_xer_delay = Signal()
477 sync += d_xer_delay.eq(d_xer.req)
478 with m.If(d_xer_delay):
479 # data arrives one clock later
480 comb += d_xer.data.eq(self.xer_r.o_data)
481 comb += d_xer.ack.eq(1)
482
483 def tb_dec_fsm(self, m, spr_dec):
484 """tb_dec_fsm
485
486 this is a FSM for updating either dec or tb. it runs alternately
487 DEC, TB, DEC, TB. note that SPR pipeline could have written a new
488 value to DEC, however the regfile has "passthrough" on it so this
489 *should* be ok.
490
491 see v3.0B p1097-1099 for Timeer Resource and p1065 and p1076
492 """
493
494 comb, sync = m.d.comb, m.d.sync
495 fast_rf = self.core.regs.rf['fast']
496 fast_r_dectb = fast_rf.r_ports['issue'] # DEC/TB
497 fast_w_dectb = fast_rf.w_ports['issue'] # DEC/TB
498
499 with m.FSM() as fsm:
500
501 # initiates read of current DEC
502 with m.State("DEC_READ"):
503 comb += fast_r_dectb.addr.eq(FastRegs.DEC)
504 comb += fast_r_dectb.ren.eq(1)
505 m.next = "DEC_WRITE"
506
507 # waits for DEC read to arrive (1 cycle), updates with new value
508 with m.State("DEC_WRITE"):
509 new_dec = Signal(64)
510 # TODO: MSR.LPCR 32-bit decrement mode
511 comb += new_dec.eq(fast_r_dectb.o_data - 1)
512 comb += fast_w_dectb.addr.eq(FastRegs.DEC)
513 comb += fast_w_dectb.wen.eq(1)
514 comb += fast_w_dectb.i_data.eq(new_dec)
515 sync += spr_dec.eq(new_dec) # copy into cur_state for decoder
516 m.next = "TB_READ"
517
518 # initiates read of current TB
519 with m.State("TB_READ"):
520 comb += fast_r_dectb.addr.eq(FastRegs.TB)
521 comb += fast_r_dectb.ren.eq(1)
522 m.next = "TB_WRITE"
523
524 # waits for read TB to arrive, initiates write of current TB
525 with m.State("TB_WRITE"):
526 new_tb = Signal(64)
527 comb += new_tb.eq(fast_r_dectb.o_data + 1)
528 comb += fast_w_dectb.addr.eq(FastRegs.TB)
529 comb += fast_w_dectb.wen.eq(1)
530 comb += fast_w_dectb.i_data.eq(new_tb)
531 m.next = "DEC_READ"
532
533 return m
534
535 def elaborate(self, platform):
536 m = Module()
537 # convenience
538 comb, sync = m.d.comb, m.d.sync
539 cur_state = self.cur_state
540 pdecode2 = self.pdecode2
541 dbg = self.dbg
542
543 # set up peripherals and core
544 core_rst = self.core_rst
545 self.setup_peripherals(m)
546
547 # reset current state if core reset requested
548 with m.If(core_rst):
549 m.d.sync += self.cur_state.eq(0)
550
551 # check halted condition: requested PC to execute matches DMI stop addr
552 # and immediately stop. address of 0xffff_ffff_ffff_ffff can never
553 # match
554 halted = Signal()
555 comb += halted.eq(dbg.stop_addr_o == dbg.state.pc)
556 with m.If(halted):
557 comb += dbg.core_stopped_i.eq(1)
558 comb += dbg.terminate_i.eq(1)
559
560 # PC and instruction from I-Memory
561 comb += self.pc_o.eq(cur_state.pc)
562 self.pc_changed = Signal() # note write to PC
563 self.msr_changed = Signal() # note write to MSR
564 self.sv_changed = Signal() # note write to SVSTATE
565
566 # read state either from incoming override or from regfile
567 state = CoreState("get") # current state (MSR/PC/SVSTATE)
568 state_get(m, state.msr, core_rst, self.msr_i,
569 "msr", # read MSR
570 self.state_r_msr, StateRegs.MSR)
571 state_get(m, state.pc, core_rst, self.pc_i,
572 "pc", # read PC
573 self.state_r_pc, StateRegs.PC)
574 state_get(m, state.svstate, core_rst, self.svstate_i,
575 "svstate", # read SVSTATE
576 self.state_r_sv, StateRegs.SVSTATE)
577
578 # don't write pc every cycle
579 comb += self.state_w_pc.wen.eq(0)
580 comb += self.state_w_pc.i_data.eq(0)
581
582 # connect up debug state. note "combinatorially same" below,
583 # this is a bit naff, passing state over in the dbg class, but
584 # because it is combinatorial it achieves the desired goal
585 comb += dbg.state.eq(state)
586
587 # this bit doesn't have to be in the FSM: connect up to read
588 # regfiles on demand from DMI
589 self.do_dmi(m, dbg)
590
591 # DEC and TB inc/dec FSM. copy of DEC is put into CoreState,
592 # (which uses that in PowerDecoder2 to raise 0x900 exception)
593 self.tb_dec_fsm(m, cur_state.dec)
594
595 # while stopped, allow updating the MSR, PC and SVSTATE.
596 # these are mainly for debugging purposes (including DMI/JTAG)
597 with m.If(dbg.core_stopped_i):
598 with m.If(self.pc_i.ok):
599 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
600 comb += self.state_w_pc.i_data.eq(self.pc_i.data)
601 sync += self.pc_changed.eq(1)
602 with m.If(self.msr_i.ok):
603 comb += self.state_w_msr.wen.eq(1 << StateRegs.MSR)
604 comb += self.state_w_msr.i_data.eq(self.msr_i.data)
605 sync += self.msr_changed.eq(1)
606 with m.If(self.svstate_i.ok | self.update_svstate):
607 with m.If(self.svstate_i.ok): # over-ride from external source
608 comb += self.new_svstate.eq(self.svstate_i.data)
609 comb += self.state_w_sv.wen.eq(1 << StateRegs.SVSTATE)
610 comb += self.state_w_sv.i_data.eq(self.new_svstate)
611 sync += self.sv_changed.eq(1)
612
613 # start renaming some of the ports to match microwatt
614 if self.microwatt_compat:
615 self.core.o.core_terminate_o.name = "terminated_out"
616 # names of DMI interface
617 self.dbg.dmi.addr_i.name = 'dmi_addr'
618 self.dbg.dmi.din.name = 'dmi_din'
619 self.dbg.dmi.dout.name = 'dmi_dout'
620 self.dbg.dmi.req_i.name = 'dmi_req'
621 self.dbg.dmi.we_i.name = 'dmi_wr'
622 self.dbg.dmi.ack_o.name = 'dmi_ack'
623 # wishbone instruction bus
624 ibus = self.imem.ibus
625 ibus.adr.name = 'wishbone_insn_out.adr'
626 ibus.dat_w.name = 'wishbone_insn_out.dat'
627 ibus.sel.name = 'wishbone_insn_out.sel'
628 ibus.cyc.name = 'wishbone_insn_out.cyc'
629 ibus.stb.name = 'wishbone_insn_out.stb'
630 ibus.we.name = 'wishbone_insn_out.we'
631 ibus.dat_r.name = 'wishbone_insn_in.dat'
632 ibus.ack.name = 'wishbone_insn_in.ack'
633 ibus.stall.name = 'wishbone_insn_in.stall'
634 # wishbone data bus
635 dbus = self.core.l0.cmpi.wb_bus()
636 dbus.adr.name = 'wishbone_data_out.adr'
637 dbus.dat_w.name = 'wishbone_data_out.dat'
638 dbus.sel.name = 'wishbone_data_out.sel'
639 dbus.cyc.name = 'wishbone_data_out.cyc'
640 dbus.stb.name = 'wishbone_data_out.stb'
641 dbus.we.name = 'wishbone_data_out.we'
642 dbus.dat_r.name = 'wishbone_data_in.dat'
643 dbus.ack.name = 'wishbone_data_in.ack'
644 dbus.stall.name = 'wishbone_data_in.stall'
645
646 return m
647
648 def __iter__(self):
649 yield from self.pc_i.ports()
650 yield from self.msr_i.ports()
651 yield self.pc_o
652 yield self.memerr_o
653 yield from self.core.ports()
654 yield from self.imem.ports()
655 yield self.core_bigendian_i
656 yield self.busy_o
657
658 def ports(self):
659 return list(self)
660
661 def external_ports(self):
662 if self.microwatt_compat:
663 ports = [self.core.o.core_terminate_o,
664 self.ext_irq,
665 self.alt_reset, # not connected yet
666 ClockSignal(),
667 ResetSignal(),
668 ]
669 ports += list(self.dbg.dmi.ports())
670 # for dbus/ibus microwatt, exclude err btw and cti
671 for name, sig in self.imem.ibus.fields.items():
672 if name not in ['err', 'bte', 'cti']:
673 ports.append(sig)
674 for name, sig in self.core.l0.cmpi.wb_bus().fields.items():
675 if name not in ['err', 'bte', 'cti']:
676 ports.append(sig)
677 return ports
678
679 ports = self.pc_i.ports()
680 ports = self.msr_i.ports()
681 ports += [self.pc_o, self.memerr_o, self.core_bigendian_i, self.busy_o,
682 ]
683
684 if self.jtag_en:
685 ports += list(self.jtag.external_ports())
686 else:
687 # don't add DMI if JTAG is enabled
688 ports += list(self.dbg.dmi.ports())
689
690 ports += list(self.imem.ibus.fields.values())
691 ports += list(self.core.l0.cmpi.wb_bus().fields.values())
692
693 if self.sram4x4k:
694 for sram in self.sram4k:
695 ports += list(sram.bus.fields.values())
696
697 if self.xics:
698 ports += list(self.xics_icp.bus.fields.values())
699 ports += list(self.xics_ics.bus.fields.values())
700 ports.append(self.int_level_i)
701 else:
702 ports.append(self.ext_irq)
703
704 if self.gpio:
705 ports += list(self.simple_gpio.bus.fields.values())
706 ports.append(self.gpio_o)
707
708 return ports
709
710 def ports(self):
711 return list(self)
712
713
714
715 # Fetch Finite State Machine.
716 # WARNING: there are currently DriverConflicts but it's actually working.
717 # TODO, here: everything that is global in nature, information from the
718 # main TestIssuerInternal, needs to move to either ispec() or ospec().
719 # not only that: TestIssuerInternal.imem can entirely move into here
720 # because imem is only ever accessed inside the FetchFSM.
721 class FetchFSM(ControlBase):
722 def __init__(self, allow_overlap, svp64_en, imem, core_rst,
723 pdecode2, cur_state,
724 dbg, core, svstate, nia, is_svp64_mode):
725 self.allow_overlap = allow_overlap
726 self.svp64_en = svp64_en
727 self.imem = imem
728 self.core_rst = core_rst
729 self.pdecode2 = pdecode2
730 self.cur_state = cur_state
731 self.dbg = dbg
732 self.core = core
733 self.svstate = svstate
734 self.nia = nia
735 self.is_svp64_mode = is_svp64_mode
736
737 # set up pipeline ControlBase and allocate i/o specs
738 # (unusual: normally done by the Pipeline API)
739 super().__init__(stage=self)
740 self.p.i_data, self.n.o_data = self.new_specs(None)
741 self.i, self.o = self.p.i_data, self.n.o_data
742
743 # next 3 functions are Stage API Compliance
744 def setup(self, m, i):
745 pass
746
747 def ispec(self):
748 return FetchInput()
749
750 def ospec(self):
751 return FetchOutput()
752
753 def elaborate(self, platform):
754 """fetch FSM
755
756 this FSM performs fetch of raw instruction data, partial-decodes
757 it 32-bit at a time to detect SVP64 prefixes, and will optionally
758 read a 2nd 32-bit quantity if that occurs.
759 """
760 m = super().elaborate(platform)
761
762 dbg = self.dbg
763 core = self.core
764 pc = self.i.pc
765 msr = self.i.msr
766 svstate = self.svstate
767 nia = self.nia
768 is_svp64_mode = self.is_svp64_mode
769 fetch_pc_o_ready = self.p.o_ready
770 fetch_pc_i_valid = self.p.i_valid
771 fetch_insn_o_valid = self.n.o_valid
772 fetch_insn_i_ready = self.n.i_ready
773
774 comb = m.d.comb
775 sync = m.d.sync
776 pdecode2 = self.pdecode2
777 cur_state = self.cur_state
778 dec_opcode_o = pdecode2.dec.raw_opcode_in # raw opcode
779
780 # also note instruction fetch failed
781 if hasattr(core, "icache"):
782 fetch_failed = core.icache.i_out.fetch_failed
783 flush_needed = True
784 else:
785 fetch_failed = Const(0, 1)
786 flush_needed = False
787
788 # set priv / virt mode on I-Cache, sigh
789 if isinstance(self.imem, ICache):
790 comb += self.imem.i_in.priv_mode.eq(~msr[MSR.PR])
791 comb += self.imem.i_in.virt_mode.eq(msr[MSR.IR]) # Instr. Redir (VM)
792
793 with m.FSM(name='fetch_fsm'):
794
795 # waiting (zzz)
796 with m.State("IDLE"):
797 # fetch allowed if not failed and stopped but not stepping
798 # (see dmi.py for how core_stop_o is generated)
799 with m.If(~fetch_failed & ~dbg.core_stop_o):
800 comb += fetch_pc_o_ready.eq(1)
801 with m.If(fetch_pc_i_valid & ~pdecode2.instr_fault
802 & ~dbg.core_stop_o):
803 # instruction allowed to go: start by reading the PC
804 # capture the PC and also drop it into Insn Memory
805 # we have joined a pair of combinatorial memory
806 # lookups together. this is Generally Bad.
807 comb += self.imem.a_pc_i.eq(pc)
808 comb += self.imem.a_i_valid.eq(1)
809 comb += self.imem.f_i_valid.eq(1)
810 # transfer state to output
811 sync += cur_state.pc.eq(pc)
812 sync += cur_state.svstate.eq(svstate) # and svstate
813 sync += cur_state.msr.eq(msr) # and msr
814
815 m.next = "INSN_READ" # move to "wait for bus" phase
816
817 # dummy pause to find out why simulation is not keeping up
818 with m.State("INSN_READ"):
819 # when using "single-step" mode, checking dbg.stopping_o
820 # prevents progress. allow fetch to proceed once started
821 stopping = Const(0)
822 #if self.allow_overlap:
823 # stopping = dbg.stopping_o
824 with m.If(stopping):
825 # stopping: jump back to idle
826 m.next = "IDLE"
827 with m.Else():
828 with m.If(self.imem.f_busy_o &
829 ~pdecode2.instr_fault): # zzz...
830 # busy but not fetch failed: stay in wait-read
831 comb += self.imem.a_pc_i.eq(pc)
832 comb += self.imem.a_i_valid.eq(1)
833 comb += self.imem.f_i_valid.eq(1)
834 with m.Else():
835 # not busy (or fetch failed!): instruction fetched
836 # when fetch failed, the instruction gets ignored
837 # by the decoder
838 if hasattr(core, "icache"):
839 # blech, icache returns actual instruction
840 insn = self.imem.f_instr_o
841 else:
842 # but these return raw memory
843 insn = get_insn(self.imem.f_instr_o, cur_state.pc)
844 if self.svp64_en:
845 svp64 = self.svp64
846 # decode the SVP64 prefix, if any
847 comb += svp64.raw_opcode_in.eq(insn)
848 comb += svp64.bigendian.eq(self.core_bigendian_i)
849 # pass the decoded prefix (if any) to PowerDecoder2
850 sync += pdecode2.sv_rm.eq(svp64.svp64_rm)
851 sync += pdecode2.is_svp64_mode.eq(is_svp64_mode)
852 # remember whether this is a prefixed instruction,
853 # so the FSM can readily loop when VL==0
854 sync += is_svp64_mode.eq(svp64.is_svp64_mode)
855 # calculate the address of the following instruction
856 insn_size = Mux(svp64.is_svp64_mode, 8, 4)
857 sync += nia.eq(cur_state.pc + insn_size)
858 with m.If(~svp64.is_svp64_mode):
859 # with no prefix, store the instruction
860 # and hand it directly to the next FSM
861 sync += dec_opcode_o.eq(insn)
862 m.next = "INSN_READY"
863 with m.Else():
864 # fetch the rest of the instruction from memory
865 comb += self.imem.a_pc_i.eq(cur_state.pc + 4)
866 comb += self.imem.a_i_valid.eq(1)
867 comb += self.imem.f_i_valid.eq(1)
868 m.next = "INSN_READ2"
869 else:
870 # not SVP64 - 32-bit only
871 sync += nia.eq(cur_state.pc + 4)
872 sync += dec_opcode_o.eq(insn)
873 m.next = "INSN_READY"
874
875 with m.State("INSN_READ2"):
876 with m.If(self.imem.f_busy_o): # zzz...
877 # busy: stay in wait-read
878 comb += self.imem.a_i_valid.eq(1)
879 comb += self.imem.f_i_valid.eq(1)
880 with m.Else():
881 # not busy: instruction fetched
882 if hasattr(core, "icache"):
883 # blech, icache returns actual instruction
884 insn = self.imem.f_instr_o
885 else:
886 insn = get_insn(self.imem.f_instr_o, cur_state.pc+4)
887 sync += dec_opcode_o.eq(insn)
888 m.next = "INSN_READY"
889 # TODO: probably can start looking at pdecode2.rm_dec
890 # here or maybe even in INSN_READ state, if svp64_mode
891 # detected, in order to trigger - and wait for - the
892 # predicate reading.
893 if self.svp64_en:
894 pmode = pdecode2.rm_dec.predmode
895 """
896 if pmode != SVP64PredMode.ALWAYS.value:
897 fire predicate loading FSM and wait before
898 moving to INSN_READY
899 else:
900 sync += self.srcmask.eq(-1) # set to all 1s
901 sync += self.dstmask.eq(-1) # set to all 1s
902 m.next = "INSN_READY"
903 """
904
905 with m.State("INSN_READY"):
906 # hand over the instruction, to be decoded
907 comb += fetch_insn_o_valid.eq(1)
908 with m.If(fetch_insn_i_ready):
909 m.next = "IDLE"
910
911 # whatever was done above, over-ride it if core reset is held
912 with m.If(self.core_rst):
913 sync += nia.eq(0)
914
915 return m
916
917
918 class TestIssuerInternal(TestIssuerBase):
919 """TestIssuer - reads instructions from TestMemory and issues them
920
921 efficiency and speed is not the main goal here: functional correctness
922 and code clarity is. optimisations (which almost 100% interfere with
923 easy understanding) come later.
924 """
925
926 def fetch_predicate_fsm(self, m,
927 pred_insn_i_valid, pred_insn_o_ready,
928 pred_mask_o_valid, pred_mask_i_ready):
929 """fetch_predicate_fsm - obtains (constructs in the case of CR)
930 src/dest predicate masks
931
932 https://bugs.libre-soc.org/show_bug.cgi?id=617
933 the predicates can be read here, by using IntRegs r_ports['pred']
934 or CRRegs r_ports['pred']. in the case of CRs it will have to
935 be done through multiple reads, extracting one relevant at a time.
936 later, a faster way would be to use the 32-bit-wide CR port but
937 this is more complex decoding, here. equivalent code used in
938 ISACaller is "from openpower.decoder.isa.caller import get_predcr"
939
940 note: this ENTIRE FSM is not to be called when svp64 is disabled
941 """
942 comb = m.d.comb
943 sync = m.d.sync
944 pdecode2 = self.pdecode2
945 rm_dec = pdecode2.rm_dec # SVP64RMModeDecode
946 predmode = rm_dec.predmode
947 srcpred, dstpred = rm_dec.srcpred, rm_dec.dstpred
948 cr_pred, int_pred = self.cr_pred, self.int_pred # read regfiles
949 # get src/dst step, so we can skip already used mask bits
950 cur_state = self.cur_state
951 srcstep = cur_state.svstate.srcstep
952 dststep = cur_state.svstate.dststep
953 cur_vl = cur_state.svstate.vl
954
955 # decode predicates
956 sregread, sinvert, sunary, sall1s = get_predint(m, srcpred, 's')
957 dregread, dinvert, dunary, dall1s = get_predint(m, dstpred, 'd')
958 sidx, scrinvert = get_predcr(m, srcpred, 's')
959 didx, dcrinvert = get_predcr(m, dstpred, 'd')
960
961 # store fetched masks, for either intpred or crpred
962 # when src/dst step is not zero, the skipped mask bits need to be
963 # shifted-out, before actually storing them in src/dest mask
964 new_srcmask = Signal(64, reset_less=True)
965 new_dstmask = Signal(64, reset_less=True)
966
967 with m.FSM(name="fetch_predicate"):
968
969 with m.State("FETCH_PRED_IDLE"):
970 comb += pred_insn_o_ready.eq(1)
971 with m.If(pred_insn_i_valid):
972 with m.If(predmode == SVP64PredMode.INT):
973 # skip fetching destination mask register, when zero
974 with m.If(dall1s):
975 sync += new_dstmask.eq(-1)
976 # directly go to fetch source mask register
977 # guaranteed not to be zero (otherwise predmode
978 # would be SVP64PredMode.ALWAYS, not INT)
979 comb += int_pred.addr.eq(sregread)
980 comb += int_pred.ren.eq(1)
981 m.next = "INT_SRC_READ"
982 # fetch destination predicate register
983 with m.Else():
984 comb += int_pred.addr.eq(dregread)
985 comb += int_pred.ren.eq(1)
986 m.next = "INT_DST_READ"
987 with m.Elif(predmode == SVP64PredMode.CR):
988 # go fetch masks from the CR register file
989 sync += new_srcmask.eq(0)
990 sync += new_dstmask.eq(0)
991 m.next = "CR_READ"
992 with m.Else():
993 sync += self.srcmask.eq(-1)
994 sync += self.dstmask.eq(-1)
995 m.next = "FETCH_PRED_DONE"
996
997 with m.State("INT_DST_READ"):
998 # store destination mask
999 inv = Repl(dinvert, 64)
1000 with m.If(dunary):
1001 # set selected mask bit for 1<<r3 mode
1002 dst_shift = Signal(range(64))
1003 comb += dst_shift.eq(self.int_pred.o_data & 0b111111)
1004 sync += new_dstmask.eq(1 << dst_shift)
1005 with m.Else():
1006 # invert mask if requested
1007 sync += new_dstmask.eq(self.int_pred.o_data ^ inv)
1008 # skip fetching source mask register, when zero
1009 with m.If(sall1s):
1010 sync += new_srcmask.eq(-1)
1011 m.next = "FETCH_PRED_SHIFT_MASK"
1012 # fetch source predicate register
1013 with m.Else():
1014 comb += int_pred.addr.eq(sregread)
1015 comb += int_pred.ren.eq(1)
1016 m.next = "INT_SRC_READ"
1017
1018 with m.State("INT_SRC_READ"):
1019 # store source mask
1020 inv = Repl(sinvert, 64)
1021 with m.If(sunary):
1022 # set selected mask bit for 1<<r3 mode
1023 src_shift = Signal(range(64))
1024 comb += src_shift.eq(self.int_pred.o_data & 0b111111)
1025 sync += new_srcmask.eq(1 << src_shift)
1026 with m.Else():
1027 # invert mask if requested
1028 sync += new_srcmask.eq(self.int_pred.o_data ^ inv)
1029 m.next = "FETCH_PRED_SHIFT_MASK"
1030
1031 # fetch masks from the CR register file
1032 # implements the following loop:
1033 # idx, inv = get_predcr(mask)
1034 # mask = 0
1035 # for cr_idx in range(vl):
1036 # cr = crl[cr_idx + SVP64CROffs.CRPred] # takes one cycle
1037 # if cr[idx] ^ inv:
1038 # mask |= 1 << cr_idx
1039 # return mask
1040 with m.State("CR_READ"):
1041 # CR index to be read, which will be ready by the next cycle
1042 cr_idx = Signal.like(cur_vl, reset_less=True)
1043 # submit the read operation to the regfile
1044 with m.If(cr_idx != cur_vl):
1045 # the CR read port is unary ...
1046 # ren = 1 << cr_idx
1047 # ... in MSB0 convention ...
1048 # ren = 1 << (7 - cr_idx)
1049 # ... and with an offset:
1050 # ren = 1 << (7 - off - cr_idx)
1051 idx = SVP64CROffs.CRPred + cr_idx
1052 comb += cr_pred.ren.eq(1 << (7 - idx))
1053 # signal data valid in the next cycle
1054 cr_read = Signal(reset_less=True)
1055 sync += cr_read.eq(1)
1056 # load the next index
1057 sync += cr_idx.eq(cr_idx + 1)
1058 with m.Else():
1059 # exit on loop end
1060 sync += cr_read.eq(0)
1061 sync += cr_idx.eq(0)
1062 m.next = "FETCH_PRED_SHIFT_MASK"
1063 with m.If(cr_read):
1064 # compensate for the one cycle delay on the regfile
1065 cur_cr_idx = Signal.like(cur_vl)
1066 comb += cur_cr_idx.eq(cr_idx - 1)
1067 # read the CR field, select the appropriate bit
1068 cr_field = Signal(4)
1069 scr_bit = Signal()
1070 dcr_bit = Signal()
1071 comb += cr_field.eq(cr_pred.o_data)
1072 comb += scr_bit.eq(cr_field.bit_select(sidx, 1)
1073 ^ scrinvert)
1074 comb += dcr_bit.eq(cr_field.bit_select(didx, 1)
1075 ^ dcrinvert)
1076 # set the corresponding mask bit
1077 bit_to_set = Signal.like(self.srcmask)
1078 comb += bit_to_set.eq(1 << cur_cr_idx)
1079 with m.If(scr_bit):
1080 sync += new_srcmask.eq(new_srcmask | bit_to_set)
1081 with m.If(dcr_bit):
1082 sync += new_dstmask.eq(new_dstmask | bit_to_set)
1083
1084 with m.State("FETCH_PRED_SHIFT_MASK"):
1085 # shift-out skipped mask bits
1086 sync += self.srcmask.eq(new_srcmask >> srcstep)
1087 sync += self.dstmask.eq(new_dstmask >> dststep)
1088 m.next = "FETCH_PRED_DONE"
1089
1090 with m.State("FETCH_PRED_DONE"):
1091 comb += pred_mask_o_valid.eq(1)
1092 with m.If(pred_mask_i_ready):
1093 m.next = "FETCH_PRED_IDLE"
1094
1095 def issue_fsm(self, m, core, nia,
1096 dbg, core_rst, is_svp64_mode,
1097 fetch_pc_o_ready, fetch_pc_i_valid,
1098 fetch_insn_o_valid, fetch_insn_i_ready,
1099 pred_insn_i_valid, pred_insn_o_ready,
1100 pred_mask_o_valid, pred_mask_i_ready,
1101 exec_insn_i_valid, exec_insn_o_ready,
1102 exec_pc_o_valid, exec_pc_i_ready):
1103 """issue FSM
1104
1105 decode / issue FSM. this interacts with the "fetch" FSM
1106 through fetch_insn_ready/valid (incoming) and fetch_pc_ready/valid
1107 (outgoing). also interacts with the "execute" FSM
1108 through exec_insn_ready/valid (outgoing) and exec_pc_ready/valid
1109 (incoming).
1110 SVP64 RM prefixes have already been set up by the
1111 "fetch" phase, so execute is fairly straightforward.
1112 """
1113
1114 comb = m.d.comb
1115 sync = m.d.sync
1116 pdecode2 = self.pdecode2
1117 cur_state = self.cur_state
1118 new_svstate = self.new_svstate
1119
1120 # temporaries
1121 dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
1122
1123 # for updating svstate (things like srcstep etc.)
1124 comb += new_svstate.eq(cur_state.svstate)
1125
1126 # precalculate srcstep+1 and dststep+1
1127 cur_srcstep = cur_state.svstate.srcstep
1128 cur_dststep = cur_state.svstate.dststep
1129 next_srcstep = Signal.like(cur_srcstep)
1130 next_dststep = Signal.like(cur_dststep)
1131 comb += next_srcstep.eq(cur_state.svstate.srcstep+1)
1132 comb += next_dststep.eq(cur_state.svstate.dststep+1)
1133
1134 # note if an exception happened. in a pipelined or OoO design
1135 # this needs to be accompanied by "shadowing" (or stalling)
1136 exc_happened = self.core.o.exc_happened
1137 # also note instruction fetch failed
1138 if hasattr(core, "icache"):
1139 fetch_failed = core.icache.i_out.fetch_failed
1140 flush_needed = True
1141 # set to fault in decoder
1142 # update (highest priority) instruction fault
1143 rising_fetch_failed = rising_edge(m, fetch_failed)
1144 with m.If(rising_fetch_failed):
1145 sync += pdecode2.instr_fault.eq(1)
1146 else:
1147 fetch_failed = Const(0, 1)
1148 flush_needed = False
1149
1150 with m.FSM(name="issue_fsm"):
1151
1152 # sync with the "fetch" phase which is reading the instruction
1153 # at this point, there is no instruction running, that
1154 # could inadvertently update the PC.
1155 with m.State("ISSUE_START"):
1156 # reset instruction fault
1157 sync += pdecode2.instr_fault.eq(0)
1158 # wait on "core stop" release, before next fetch
1159 # need to do this here, in case we are in a VL==0 loop
1160 with m.If(~dbg.core_stop_o & ~core_rst):
1161 comb += fetch_pc_i_valid.eq(1) # tell fetch to start
1162 with m.If(fetch_pc_o_ready): # fetch acknowledged us
1163 m.next = "INSN_WAIT"
1164 with m.Else():
1165 # tell core it's stopped, and acknowledge debug handshake
1166 comb += dbg.core_stopped_i.eq(1)
1167 # while stopped, allow updating SVSTATE
1168 with m.If(self.svstate_i.ok):
1169 comb += new_svstate.eq(self.svstate_i.data)
1170 comb += self.update_svstate.eq(1)
1171 sync += self.sv_changed.eq(1)
1172
1173 # wait for an instruction to arrive from Fetch
1174 with m.State("INSN_WAIT"):
1175 # when using "single-step" mode, checking dbg.stopping_o
1176 # prevents progress. allow issue to proceed once started
1177 stopping = Const(0)
1178 #if self.allow_overlap:
1179 # stopping = dbg.stopping_o
1180 with m.If(stopping):
1181 # stopping: jump back to idle
1182 m.next = "ISSUE_START"
1183 if flush_needed:
1184 # request the icache to stop asserting "failed"
1185 comb += core.icache.flush_in.eq(1)
1186 # stop instruction fault
1187 sync += pdecode2.instr_fault.eq(0)
1188 with m.Else():
1189 comb += fetch_insn_i_ready.eq(1)
1190 with m.If(fetch_insn_o_valid):
1191 # loop into ISSUE_START if it's a SVP64 instruction
1192 # and VL == 0. this because VL==0 is a for-loop
1193 # from 0 to 0 i.e. always, always a NOP.
1194 cur_vl = cur_state.svstate.vl
1195 with m.If(is_svp64_mode & (cur_vl == 0)):
1196 # update the PC before fetching the next instruction
1197 # since we are in a VL==0 loop, no instruction was
1198 # executed that we could be overwriting
1199 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
1200 comb += self.state_w_pc.i_data.eq(nia)
1201 comb += self.insn_done.eq(1)
1202 m.next = "ISSUE_START"
1203 with m.Else():
1204 if self.svp64_en:
1205 m.next = "PRED_START" # fetching predicate
1206 else:
1207 m.next = "DECODE_SV" # skip predication
1208
1209 with m.State("PRED_START"):
1210 comb += pred_insn_i_valid.eq(1) # tell fetch_pred to start
1211 with m.If(pred_insn_o_ready): # fetch_pred acknowledged us
1212 m.next = "MASK_WAIT"
1213
1214 with m.State("MASK_WAIT"):
1215 comb += pred_mask_i_ready.eq(1) # ready to receive the masks
1216 with m.If(pred_mask_o_valid): # predication masks are ready
1217 m.next = "PRED_SKIP"
1218
1219 # skip zeros in predicate
1220 with m.State("PRED_SKIP"):
1221 with m.If(~is_svp64_mode):
1222 m.next = "DECODE_SV" # nothing to do
1223 with m.Else():
1224 if self.svp64_en:
1225 pred_src_zero = pdecode2.rm_dec.pred_sz
1226 pred_dst_zero = pdecode2.rm_dec.pred_dz
1227
1228 # new srcstep, after skipping zeros
1229 skip_srcstep = Signal.like(cur_srcstep)
1230 # value to be added to the current srcstep
1231 src_delta = Signal.like(cur_srcstep)
1232 # add leading zeros to srcstep, if not in zero mode
1233 with m.If(~pred_src_zero):
1234 # priority encoder (count leading zeros)
1235 # append guard bit, in case the mask is all zeros
1236 pri_enc_src = PriorityEncoder(65)
1237 m.submodules.pri_enc_src = pri_enc_src
1238 comb += pri_enc_src.i.eq(Cat(self.srcmask,
1239 Const(1, 1)))
1240 comb += src_delta.eq(pri_enc_src.o)
1241 # apply delta to srcstep
1242 comb += skip_srcstep.eq(cur_srcstep + src_delta)
1243 # shift-out all leading zeros from the mask
1244 # plus the leading "one" bit
1245 # TODO count leading zeros and shift-out the zero
1246 # bits, in the same step, in hardware
1247 sync += self.srcmask.eq(self.srcmask >> (src_delta+1))
1248
1249 # same as above, but for dststep
1250 skip_dststep = Signal.like(cur_dststep)
1251 dst_delta = Signal.like(cur_dststep)
1252 with m.If(~pred_dst_zero):
1253 pri_enc_dst = PriorityEncoder(65)
1254 m.submodules.pri_enc_dst = pri_enc_dst
1255 comb += pri_enc_dst.i.eq(Cat(self.dstmask,
1256 Const(1, 1)))
1257 comb += dst_delta.eq(pri_enc_dst.o)
1258 comb += skip_dststep.eq(cur_dststep + dst_delta)
1259 sync += self.dstmask.eq(self.dstmask >> (dst_delta+1))
1260
1261 # TODO: initialize mask[VL]=1 to avoid passing past VL
1262 with m.If((skip_srcstep >= cur_vl) |
1263 (skip_dststep >= cur_vl)):
1264 # end of VL loop. Update PC and reset src/dst step
1265 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
1266 comb += self.state_w_pc.i_data.eq(nia)
1267 comb += new_svstate.srcstep.eq(0)
1268 comb += new_svstate.dststep.eq(0)
1269 comb += self.update_svstate.eq(1)
1270 # synchronize with the simulator
1271 comb += self.insn_done.eq(1)
1272 # go back to Issue
1273 m.next = "ISSUE_START"
1274 with m.Else():
1275 # update new src/dst step
1276 comb += new_svstate.srcstep.eq(skip_srcstep)
1277 comb += new_svstate.dststep.eq(skip_dststep)
1278 comb += self.update_svstate.eq(1)
1279 # proceed to Decode
1280 m.next = "DECODE_SV"
1281
1282 # pass predicate mask bits through to satellite decoders
1283 # TODO: for SIMD this will be *multiple* bits
1284 sync += core.i.sv_pred_sm.eq(self.srcmask[0])
1285 sync += core.i.sv_pred_dm.eq(self.dstmask[0])
1286
1287 # after src/dst step have been updated, we are ready
1288 # to decode the instruction
1289 with m.State("DECODE_SV"):
1290 # decode the instruction
1291 with m.If(~fetch_failed):
1292 sync += pdecode2.instr_fault.eq(0)
1293 sync += core.i.e.eq(pdecode2.e)
1294 sync += core.i.state.eq(cur_state)
1295 sync += core.i.raw_insn_i.eq(dec_opcode_i)
1296 sync += core.i.bigendian_i.eq(self.core_bigendian_i)
1297 if self.svp64_en:
1298 sync += core.i.sv_rm.eq(pdecode2.sv_rm)
1299 # set RA_OR_ZERO detection in satellite decoders
1300 sync += core.i.sv_a_nz.eq(pdecode2.sv_a_nz)
1301 # and svp64 detection
1302 sync += core.i.is_svp64_mode.eq(is_svp64_mode)
1303 # and svp64 bit-rev'd ldst mode
1304 ldst_dec = pdecode2.use_svp64_ldst_dec
1305 sync += core.i.use_svp64_ldst_dec.eq(ldst_dec)
1306 # after decoding, reset any previous exception condition,
1307 # allowing it to be set again during the next execution
1308 sync += pdecode2.ldst_exc.eq(0)
1309
1310 m.next = "INSN_EXECUTE" # move to "execute"
1311
1312 # handshake with execution FSM, move to "wait" once acknowledged
1313 with m.State("INSN_EXECUTE"):
1314 # when using "single-step" mode, checking dbg.stopping_o
1315 # prevents progress. allow execute to proceed once started
1316 stopping = Const(0)
1317 #if self.allow_overlap:
1318 # stopping = dbg.stopping_o
1319 with m.If(stopping):
1320 # stopping: jump back to idle
1321 m.next = "ISSUE_START"
1322 if flush_needed:
1323 # request the icache to stop asserting "failed"
1324 comb += core.icache.flush_in.eq(1)
1325 # stop instruction fault
1326 sync += pdecode2.instr_fault.eq(0)
1327 with m.Else():
1328 comb += exec_insn_i_valid.eq(1) # trigger execute
1329 with m.If(exec_insn_o_ready): # execute acknowledged us
1330 m.next = "EXECUTE_WAIT"
1331
1332 with m.State("EXECUTE_WAIT"):
1333 comb += exec_pc_i_ready.eq(1)
1334 # see https://bugs.libre-soc.org/show_bug.cgi?id=636
1335 # the exception info needs to be blatted into
1336 # pdecode.ldst_exc, and the instruction "re-run".
1337 # when ldst_exc.happened is set, the PowerDecoder2
1338 # reacts very differently: it re-writes the instruction
1339 # with a "trap" (calls PowerDecoder2.trap()) which
1340 # will *overwrite* whatever was requested and jump the
1341 # PC to the exception address, as well as alter MSR.
1342 # nothing else needs to be done other than to note
1343 # the change of PC and MSR (and, later, SVSTATE)
1344 with m.If(exc_happened):
1345 mmu = core.fus.get_exc("mmu0")
1346 ldst = core.fus.get_exc("ldst0")
1347 if mmu is not None:
1348 with m.If(fetch_failed):
1349 # instruction fetch: exception is from MMU
1350 # reset instr_fault (highest priority)
1351 sync += pdecode2.ldst_exc.eq(mmu)
1352 sync += pdecode2.instr_fault.eq(0)
1353 if flush_needed:
1354 # request icache to stop asserting "failed"
1355 comb += core.icache.flush_in.eq(1)
1356 with m.If(~fetch_failed):
1357 # otherwise assume it was a LDST exception
1358 sync += pdecode2.ldst_exc.eq(ldst)
1359
1360 with m.If(exec_pc_o_valid):
1361
1362 # was this the last loop iteration?
1363 is_last = Signal()
1364 cur_vl = cur_state.svstate.vl
1365 comb += is_last.eq(next_srcstep == cur_vl)
1366
1367 with m.If(pdecode2.instr_fault):
1368 # reset instruction fault, try again
1369 sync += pdecode2.instr_fault.eq(0)
1370 m.next = "ISSUE_START"
1371
1372 # return directly to Decode if Execute generated an
1373 # exception.
1374 with m.Elif(pdecode2.ldst_exc.happened):
1375 m.next = "DECODE_SV"
1376
1377 # if MSR, PC or SVSTATE were changed by the previous
1378 # instruction, go directly back to Fetch, without
1379 # updating either MSR PC or SVSTATE
1380 with m.Elif(self.msr_changed | self.pc_changed |
1381 self.sv_changed):
1382 m.next = "ISSUE_START"
1383
1384 # also return to Fetch, when no output was a vector
1385 # (regardless of SRCSTEP and VL), or when the last
1386 # instruction was really the last one of the VL loop
1387 with m.Elif((~pdecode2.loop_continue) | is_last):
1388 # before going back to fetch, update the PC state
1389 # register with the NIA.
1390 # ok here we are not reading the branch unit.
1391 # TODO: this just blithely overwrites whatever
1392 # pipeline updated the PC
1393 comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
1394 comb += self.state_w_pc.i_data.eq(nia)
1395 # reset SRCSTEP before returning to Fetch
1396 if self.svp64_en:
1397 with m.If(pdecode2.loop_continue):
1398 comb += new_svstate.srcstep.eq(0)
1399 comb += new_svstate.dststep.eq(0)
1400 comb += self.update_svstate.eq(1)
1401 else:
1402 comb += new_svstate.srcstep.eq(0)
1403 comb += new_svstate.dststep.eq(0)
1404 comb += self.update_svstate.eq(1)
1405 m.next = "ISSUE_START"
1406
1407 # returning to Execute? then, first update SRCSTEP
1408 with m.Else():
1409 comb += new_svstate.srcstep.eq(next_srcstep)
1410 comb += new_svstate.dststep.eq(next_dststep)
1411 comb += self.update_svstate.eq(1)
1412 # return to mask skip loop
1413 m.next = "PRED_SKIP"
1414
1415
1416 # check if svstate needs updating: if so, write it to State Regfile
1417 with m.If(self.update_svstate):
1418 sync += cur_state.svstate.eq(self.new_svstate) # for next clock
1419
1420 def execute_fsm(self, m, core,
1421 exec_insn_i_valid, exec_insn_o_ready,
1422 exec_pc_o_valid, exec_pc_i_ready):
1423 """execute FSM
1424
1425 execute FSM. this interacts with the "issue" FSM
1426 through exec_insn_ready/valid (incoming) and exec_pc_ready/valid
1427 (outgoing). SVP64 RM prefixes have already been set up by the
1428 "issue" phase, so execute is fairly straightforward.
1429 """
1430
1431 comb = m.d.comb
1432 sync = m.d.sync
1433 dbg = self.dbg
1434 pdecode2 = self.pdecode2
1435
1436 # temporaries
1437 core_busy_o = core.n.o_data.busy_o # core is busy
1438 core_ivalid_i = core.p.i_valid # instruction is valid
1439
1440 if hasattr(core, "icache"):
1441 fetch_failed = core.icache.i_out.fetch_failed
1442 else:
1443 fetch_failed = Const(0, 1)
1444
1445 with m.FSM(name="exec_fsm"):
1446
1447 # waiting for instruction bus (stays there until not busy)
1448 with m.State("INSN_START"):
1449 comb += exec_insn_o_ready.eq(1)
1450 with m.If(exec_insn_i_valid):
1451 comb += core_ivalid_i.eq(1) # instruction is valid/issued
1452 sync += self.sv_changed.eq(0)
1453 sync += self.pc_changed.eq(0)
1454 sync += self.msr_changed.eq(0)
1455 with m.If(core.p.o_ready): # only move if accepted
1456 m.next = "INSN_ACTIVE" # move to "wait completion"
1457
1458 # instruction started: must wait till it finishes
1459 with m.State("INSN_ACTIVE"):
1460 # note changes to MSR, PC and SVSTATE
1461 # XXX oops, really must monitor *all* State Regfile write
1462 # ports looking for changes!
1463 with m.If(self.state_nia.wen & (1 << StateRegs.SVSTATE)):
1464 sync += self.sv_changed.eq(1)
1465 with m.If(self.state_nia.wen & (1 << StateRegs.MSR)):
1466 sync += self.msr_changed.eq(1)
1467 with m.If(self.state_nia.wen & (1 << StateRegs.PC)):
1468 sync += self.pc_changed.eq(1)
1469 with m.If(~core_busy_o): # instruction done!
1470 comb += exec_pc_o_valid.eq(1)
1471 with m.If(exec_pc_i_ready):
1472 # when finished, indicate "done".
1473 # however, if there was an exception, the instruction
1474 # is *not* yet done. this is an implementation
1475 # detail: we choose to implement exceptions by
1476 # taking the exception information from the LDST
1477 # unit, putting that *back* into the PowerDecoder2,
1478 # and *re-running the entire instruction*.
1479 # if we erroneously indicate "done" here, it is as if
1480 # there were *TWO* instructions:
1481 # 1) the failed LDST 2) a TRAP.
1482 with m.If(~pdecode2.ldst_exc.happened &
1483 ~pdecode2.instr_fault):
1484 comb += self.insn_done.eq(1)
1485 m.next = "INSN_START" # back to fetch
1486 # terminate returns directly to INSN_START
1487 with m.If(dbg.terminate_i):
1488 # comb += self.insn_done.eq(1) - no because it's not
1489 m.next = "INSN_START" # back to fetch
1490
1491 def elaborate(self, platform):
1492 m = super().elaborate(platform)
1493 # convenience
1494 comb, sync = m.d.comb, m.d.sync
1495 cur_state = self.cur_state
1496 pdecode2 = self.pdecode2
1497 dbg = self.dbg
1498 core = self.core
1499
1500 # set up peripherals and core
1501 core_rst = self.core_rst
1502
1503 # indicate to outside world if any FU is still executing
1504 comb += self.any_busy.eq(core.n.o_data.any_busy_o) # any FU executing
1505
1506 # address of the next instruction, in the absence of a branch
1507 # depends on the instruction size
1508 nia = Signal(64)
1509
1510 # connect up debug signals
1511 with m.If(core.o.core_terminate_o):
1512 comb += dbg.terminate_i.eq(1)
1513
1514 # pass the prefix mode from Fetch to Issue, so the latter can loop
1515 # on VL==0
1516 is_svp64_mode = Signal()
1517
1518 # there are *THREE^WFOUR-if-SVP64-enabled* FSMs, fetch (32/64-bit)
1519 # issue, decode/execute, now joined by "Predicate fetch/calculate".
1520 # these are the handshake signals between each
1521
1522 # fetch FSM can run as soon as the PC is valid
1523 fetch_pc_i_valid = Signal() # Execute tells Fetch "start next read"
1524 fetch_pc_o_ready = Signal() # Fetch Tells SVSTATE "proceed"
1525
1526 # fetch FSM hands over the instruction to be decoded / issued
1527 fetch_insn_o_valid = Signal()
1528 fetch_insn_i_ready = Signal()
1529
1530 # predicate fetch FSM decodes and fetches the predicate
1531 pred_insn_i_valid = Signal()
1532 pred_insn_o_ready = Signal()
1533
1534 # predicate fetch FSM delivers the masks
1535 pred_mask_o_valid = Signal()
1536 pred_mask_i_ready = Signal()
1537
1538 # issue FSM delivers the instruction to the be executed
1539 exec_insn_i_valid = Signal()
1540 exec_insn_o_ready = Signal()
1541
1542 # execute FSM, hands over the PC/SVSTATE back to the issue FSM
1543 exec_pc_o_valid = Signal()
1544 exec_pc_i_ready = Signal()
1545
1546 # the FSMs here are perhaps unusual in that they detect conditions
1547 # then "hold" information, combinatorially, for the core
1548 # (as opposed to using sync - which would be on a clock's delay)
1549 # this includes the actual opcode, valid flags and so on.
1550
1551 # Fetch, then predicate fetch, then Issue, then Execute.
1552 # Issue is where the VL for-loop # lives. the ready/valid
1553 # signalling is used to communicate between the four.
1554
1555 # set up Fetch FSM
1556 fetch = FetchFSM(self.allow_overlap, self.svp64_en,
1557 self.imem, core_rst, pdecode2, cur_state,
1558 dbg, core,
1559 dbg.state.svstate, # combinatorially same
1560 nia, is_svp64_mode)
1561 m.submodules.fetch = fetch
1562 # connect up in/out data to existing Signals
1563 comb += fetch.p.i_data.pc.eq(dbg.state.pc) # combinatorially same
1564 comb += fetch.p.i_data.msr.eq(dbg.state.msr) # combinatorially same
1565 # and the ready/valid signalling
1566 comb += fetch_pc_o_ready.eq(fetch.p.o_ready)
1567 comb += fetch.p.i_valid.eq(fetch_pc_i_valid)
1568 comb += fetch_insn_o_valid.eq(fetch.n.o_valid)
1569 comb += fetch.n.i_ready.eq(fetch_insn_i_ready)
1570
1571 self.issue_fsm(m, core, nia,
1572 dbg, core_rst, is_svp64_mode,
1573 fetch_pc_o_ready, fetch_pc_i_valid,
1574 fetch_insn_o_valid, fetch_insn_i_ready,
1575 pred_insn_i_valid, pred_insn_o_ready,
1576 pred_mask_o_valid, pred_mask_i_ready,
1577 exec_insn_i_valid, exec_insn_o_ready,
1578 exec_pc_o_valid, exec_pc_i_ready)
1579
1580 if self.svp64_en:
1581 self.fetch_predicate_fsm(m,
1582 pred_insn_i_valid, pred_insn_o_ready,
1583 pred_mask_o_valid, pred_mask_i_ready)
1584
1585 self.execute_fsm(m, core,
1586 exec_insn_i_valid, exec_insn_o_ready,
1587 exec_pc_o_valid, exec_pc_i_ready)
1588
1589 return m
1590
1591
1592 class TestIssuer(Elaboratable):
1593 def __init__(self, pspec):
1594 self.ti = TestIssuerInternal(pspec)
1595 self.pll = DummyPLL(instance=True)
1596
1597 self.dbg_rst_i = Signal(reset_less=True)
1598
1599 # PLL direct clock or not
1600 self.pll_en = hasattr(pspec, "use_pll") and pspec.use_pll
1601 if self.pll_en:
1602 self.pll_test_o = Signal(reset_less=True)
1603 self.pll_vco_o = Signal(reset_less=True)
1604 self.clk_sel_i = Signal(2, reset_less=True)
1605 self.ref_clk = ClockSignal() # can't rename it but that's ok
1606 self.pllclk_clk = ClockSignal("pllclk")
1607
1608 def elaborate(self, platform):
1609 m = Module()
1610 comb = m.d.comb
1611
1612 # TestIssuer nominally runs at main clock, actually it is
1613 # all combinatorial internally except for coresync'd components
1614 m.submodules.ti = ti = self.ti
1615
1616 if self.pll_en:
1617 # ClockSelect runs at PLL output internal clock rate
1618 m.submodules.wrappll = pll = self.pll
1619
1620 # add clock domains from PLL
1621 cd_pll = ClockDomain("pllclk")
1622 m.domains += cd_pll
1623
1624 # PLL clock established. has the side-effect of running clklsel
1625 # at the PLL's speed (see DomainRenamer("pllclk") above)
1626 pllclk = self.pllclk_clk
1627 comb += pllclk.eq(pll.clk_pll_o)
1628
1629 # wire up external 24mhz to PLL
1630 #comb += pll.clk_24_i.eq(self.ref_clk)
1631 # output 18 mhz PLL test signal, and analog oscillator out
1632 comb += self.pll_test_o.eq(pll.pll_test_o)
1633 comb += self.pll_vco_o.eq(pll.pll_vco_o)
1634
1635 # input to pll clock selection
1636 comb += pll.clk_sel_i.eq(self.clk_sel_i)
1637
1638 # now wire up ResetSignals. don't mind them being in this domain
1639 pll_rst = ResetSignal("pllclk")
1640 comb += pll_rst.eq(ResetSignal())
1641
1642 # internal clock is set to selector clock-out. has the side-effect of
1643 # running TestIssuer at this speed (see DomainRenamer("intclk") above)
1644 # debug clock runs at coresync internal clock
1645 if self.ti.dbg_domain != 'sync':
1646 cd_dbgsync = ClockDomain("dbgsync")
1647 intclk = ClockSignal(self.ti.core_domain)
1648 dbgclk = ClockSignal(self.ti.dbg_domain)
1649 # XXX BYPASS PLL XXX
1650 # XXX BYPASS PLL XXX
1651 # XXX BYPASS PLL XXX
1652 if self.pll_en:
1653 comb += intclk.eq(self.ref_clk)
1654 assert self.ti.core_domain != 'sync', \
1655 "cannot set core_domain to sync and use pll at the same time"
1656 else:
1657 if self.ti.core_domain != 'sync':
1658 comb += intclk.eq(ClockSignal())
1659 if self.ti.dbg_domain != 'sync':
1660 dbgclk = ClockSignal(self.ti.dbg_domain)
1661 comb += dbgclk.eq(intclk)
1662 comb += self.ti.dbg_rst_i.eq(self.dbg_rst_i)
1663
1664 return m
1665
1666 def ports(self):
1667 return list(self.ti.ports()) + list(self.pll.ports()) + \
1668 [ClockSignal(), ResetSignal()]
1669
1670 def external_ports(self):
1671 ports = self.ti.external_ports()
1672 ports.append(ClockSignal())
1673 ports.append(ResetSignal())
1674 if self.pll_en:
1675 ports.append(self.clk_sel_i)
1676 ports.append(self.pll.clk_24_i)
1677 ports.append(self.pll_test_o)
1678 ports.append(self.pll_vco_o)
1679 ports.append(self.pllclk_clk)
1680 ports.append(self.ref_clk)
1681 return ports
1682
1683
1684 if __name__ == '__main__':
1685 units = {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1, 'logical': 1,
1686 'spr': 1,
1687 'div': 1,
1688 'mul': 1,
1689 'shiftrot': 1
1690 }
1691 pspec = TestMemPspec(ldst_ifacetype='bare_wb',
1692 imem_ifacetype='bare_wb',
1693 addr_wid=48,
1694 mask_wid=8,
1695 reg_wid=64,
1696 units=units)
1697 dut = TestIssuer(pspec)
1698 vl = main(dut, ports=dut.ports(), name="test_issuer")
1699
1700 if len(sys.argv) == 1:
1701 vl = rtlil.convert(dut, ports=dut.external_ports(), name="test_issuer")
1702 with open("test_issuer.il", "w") as f:
1703 f.write(vl)