"""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 from soc.clock.dummypll import 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): # JTAG interface. add this right at the start because if it's # added it *modifies* the pspec, by adding enable/disable signals # for parts of the rest of the core 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)) # add signals to pspec to enable/disable icache and dcache # (or data and intstruction wishbone if icache/dcache not included) # https://bugs.libre-soc.org/show_bug.cgi?id=520 # TODO: do we actually care if these are not domain-synchronised? # honestly probably not. pspec.wb_icache_en = self.jtag.wb_icache_en pspec.wb_dcache_en = self.jtag.wb_dcache_en # 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() # 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) # XXX causes litex ECP5 test to get wrong idea about input and output # (but works with verilator sim *sigh*) #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()) # 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<