"""simple core issuer not in any way intended for production use. this runs a FSM that: * reads the Program Counter from StateRegs * reads an instruction from a fixed-size Test Memory * issues it to the Simple Core * waits for it to complete * increments the PC * does it all over again the purpose of this module is to verify the functional correctness of the Function Units in the absolute simplest and clearest possible way, and to at provide something that can be further incrementally improved. """ from nmigen import (Elaboratable, Module, Signal, ClockSignal, ResetSignal, ClockDomain, DomainRenamer) from nmigen.cli import rtlil from nmigen.cli import main import sys from soc.decoder.power_decoder import create_pdecode from soc.decoder.power_decoder2 import PowerDecode2 from soc.decoder.decode2execute1 import IssuerDecode2ToOperand from soc.decoder.decode2execute1 import Data from soc.experiment.testmem import TestMemory # test only for instructions from soc.regfile.regfiles import StateRegs, FastRegs from soc.simple.core import NonProductionCore from soc.config.test.test_loadstore import TestMemPspec from soc.config.ifetch import ConfigFetchUnit from soc.decoder.power_enums import MicrOp from soc.debug.dmi import CoreDebug, DMIInterface from soc.debug.jtag import JTAG from soc.config.pinouts import get_pinspecs from soc.config.state import CoreState from soc.interrupts.xics import XICS_ICP, XICS_ICS from soc.bus.simple_gpio import SimpleGPIO from soc.clock.select import ClockSelect, DummyPLL from nmutil.util import rising_edge class TestIssuerInternal(Elaboratable): """TestIssuer - reads instructions from TestMemory and issues them efficiency and speed is not the main goal here: functional correctness is. """ def __init__(self, pspec): # add interrupt controller? self.xics = hasattr(pspec, "xics") and pspec.xics == True if self.xics: self.xics_icp = XICS_ICP() self.xics_ics = XICS_ICS() self.int_level_i = self.xics_ics.int_level_i # add GPIO peripheral? self.gpio = hasattr(pspec, "gpio") and pspec.gpio == True if self.gpio: self.simple_gpio = SimpleGPIO() self.gpio_o = self.simple_gpio.gpio_o # main instruction core25 self.core = core = NonProductionCore(pspec) # instruction decoder. goes into Trap Record pdecode = create_pdecode() self.cur_state = CoreState("cur") # current state (MSR/PC/EINT) self.pdecode2 = PowerDecode2(pdecode, state=self.cur_state, opkls=IssuerDecode2ToOperand) # Test Instruction memory self.imem = ConfigFetchUnit(pspec).fu # one-row cache of instruction read self.iline = Signal(64) # one instruction line self.iprev_adr = Signal(64) # previous address: if different, do read # DMI interface self.dbg = CoreDebug() # JTAG interface self.jtag_en = hasattr(pspec, "debug") and pspec.debug == 'jtag' if self.jtag_en: subset = {'uart', 'mtwi', 'eint', 'gpio', 'mspi0', 'mspi1', 'pwm', 'sd0', 'sdr'} self.jtag = JTAG(get_pinspecs(subset=subset)) # instruction go/monitor self.pc_o = Signal(64, reset_less=True) self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me" self.core_bigendian_i = Signal() self.busy_o = Signal(reset_less=True) self.memerr_o = Signal(reset_less=True) # FAST regfile read /write ports for PC, MSR, DEC/TB staterf = self.core.regs.rf['state'] self.state_r_pc = staterf.r_ports['cia'] # PC rd self.state_w_pc = staterf.w_ports['d_wr1'] # PC wr self.state_r_msr = staterf.r_ports['msr'] # MSR rd # DMI interface access intrf = self.core.regs.rf['int'] crrf = self.core.regs.rf['cr'] xerrf = self.core.regs.rf['xer'] self.int_r = intrf.r_ports['dmi'] # INT read self.cr_r = crrf.r_ports['full_cr_dbg'] # CR read self.xer_r = xerrf.r_ports['full_xer'] # XER read # hack method of keeping an eye on whether branch/trap set the PC self.state_nia = self.core.regs.rf['state'].w_ports['nia'] self.state_nia.wen.name = 'state_nia_wen' def elaborate(self, platform): m = Module() comb, sync = m.d.comb, m.d.sync m.submodules.core = core = DomainRenamer("coresync")(self.core) m.submodules.imem = imem = self.imem m.submodules.dbg = dbg = self.dbg if self.jtag_en: m.submodules.jtag = jtag = self.jtag # TODO: UART2GDB mux, here, from external pin # see https://bugs.libre-soc.org/show_bug.cgi?id=499 sync += dbg.dmi.connect_to(jtag.dmi) cur_state = self.cur_state # XICS interrupt handler if self.xics: m.submodules.xics_icp = icp = self.xics_icp m.submodules.xics_ics = ics = self.xics_ics comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP sync += cur_state.eint.eq(icp.core_irq_o) # connect ICP to core # GPIO test peripheral if self.gpio: m.submodules.simple_gpio = simple_gpio = self.simple_gpio # connect one GPIO output to ICS bit 15 (like in microwatt soc.vhdl) if self.gpio and self.xics: comb += self.int_level_i[15].eq(simple_gpio.gpio_o[0]) # instruction decoder pdecode = create_pdecode() m.submodules.dec2 = pdecode2 = self.pdecode2 # convenience dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer intrf = self.core.regs.rf['int'] # clock delay power-on reset cd_por = ClockDomain(reset_less=True) cd_sync = ClockDomain() core_sync = ClockDomain("coresync") m.domains += cd_por, cd_sync, core_sync ti_rst = Signal(reset_less=True) delay = Signal(range(4), reset=3) with m.If(delay != 0): m.d.por += delay.eq(delay - 1) comb += cd_por.clk.eq(ClockSignal()) comb += core_sync.clk.eq(ClockSignal()) # power-on reset delay core_rst = ResetSignal("coresync") comb += ti_rst.eq(delay != 0 | dbg.core_rst_o | ResetSignal()) comb += core_rst.eq(ti_rst) # busy/halted signals from core comb += self.busy_o.eq(core.busy_o) comb += pdecode2.dec.bigendian.eq(self.core_bigendian_i) # temporary hack: says "go" immediately for both address gen and ST l0 = core.l0 ldst = core.fus.fus['ldst0'] st_go_edge = rising_edge(m, ldst.st.rel_o) m.d.comb += ldst.ad.go_i.eq(ldst.ad.rel_o) # link addr-go direct to rel m.d.comb += ldst.st.go_i.eq(st_go_edge) # link store-go to rising rel # PC and instruction from I-Memory pc_changed = Signal() # note write to PC comb += self.pc_o.eq(cur_state.pc) ilatch = Signal(32) # next instruction (+4 on current) nia = Signal(64, reset_less=True) comb += nia.eq(cur_state.pc + 4) # read the PC pc = Signal(64, reset_less=True) pc_ok_delay = Signal() sync += pc_ok_delay.eq(~self.pc_i.ok) with m.If(self.pc_i.ok): # incoming override (start from pc_i) comb += pc.eq(self.pc_i.data) with m.Else(): # otherwise read StateRegs regfile for PC... comb += self.state_r_pc.ren.eq(1<