From 4f0a02594cc40ea923aa1134c40a22af55b90513 Mon Sep 17 00:00:00 2001 From: Charles Papon Date: Thu, 4 Apr 2019 20:34:35 +0200 Subject: [PATCH] Change LR/SC to reserve the whole memory Fix MPP access from other plugins Got all the common configuration to compile and pass regression excepted the debugger one First synthesis results --- src/main/scala/vexriscv/TestsWorkspace.scala | 2 +- src/main/scala/vexriscv/demo/GenFull.scala | 10 ++-- src/main/scala/vexriscv/demo/Linux.scala | 48 ++++++++++--------- .../scala/vexriscv/demo/SynthesisBench.scala | 19 ++------ src/main/scala/vexriscv/ip/DataCache.scala | 31 +++++------- .../scala/vexriscv/plugin/CsrPlugin.scala | 4 +- .../vexriscv/plugin/DBusCachedPlugin.scala | 22 ++++++--- .../vexriscv/plugin/DBusSimplePlugin.scala | 34 ++++--------- .../vexriscv/plugin/IBusCachedPlugin.scala | 2 +- src/test/cpp/regression/main.cpp | 4 +- .../vexriscv/TestIndividualFeatures.scala | 2 +- 11 files changed, 80 insertions(+), 98 deletions(-) diff --git a/src/main/scala/vexriscv/TestsWorkspace.scala b/src/main/scala/vexriscv/TestsWorkspace.scala index 387691a..8db4316 100644 --- a/src/main/scala/vexriscv/TestsWorkspace.scala +++ b/src/main/scala/vexriscv/TestsWorkspace.scala @@ -81,7 +81,7 @@ object TestsWorkspace { catchAccessError = true, catchIllegal = true, catchUnaligned = true, - atomicEntriesCount = 2 + withLrSc = true ), // memoryTranslatorPortConfig = null memoryTranslatorPortConfig = MemoryTranslatorPortConfig( diff --git a/src/main/scala/vexriscv/demo/GenFull.scala b/src/main/scala/vexriscv/demo/GenFull.scala index ae75397..97ae71a 100644 --- a/src/main/scala/vexriscv/demo/GenFull.scala +++ b/src/main/scala/vexriscv/demo/GenFull.scala @@ -27,7 +27,7 @@ object GenFull extends App{ twoCycleRam = true, twoCycleCache = true ), - memoryTranslatorPortConfig = MemoryTranslatorPortConfig( + memoryTranslatorPortConfig = MmuPortConfig( portTlbSize = 4 ) ), @@ -43,14 +43,14 @@ object GenFull extends App{ catchIllegal = true, catchUnaligned = true ), - memoryTranslatorPortConfig = MemoryTranslatorPortConfig( + memoryTranslatorPortConfig = MmuPortConfig( portTlbSize = 6 ) ), - new MemoryTranslatorPlugin( - tlbSize = 32, + new MmuPlugin( virtualRange = _(31 downto 28) === 0xC, - ioRange = _(31 downto 28) === 0xF + ioRange = _(31 downto 28) === 0xF, + allowUserIo = false ), new DecoderSimplePlugin( catchIllegalInstruction = true diff --git a/src/main/scala/vexriscv/demo/Linux.scala b/src/main/scala/vexriscv/demo/Linux.scala index c9fa9e3..9213031 100644 --- a/src/main/scala/vexriscv/demo/Linux.scala +++ b/src/main/scala/vexriscv/demo/Linux.scala @@ -118,9 +118,9 @@ object LinuxGen { prediction = NONE, injectorStage = true, config = InstructionCacheConfig( - cacheSize = 4096*2, + cacheSize = 4096*1, bytePerLine = 32, - wayCount = 2, + wayCount = 1, addressWidth = 32, cpuDataWidth = 32, memDataWidth = 32, @@ -140,32 +140,33 @@ object LinuxGen { // catchAddressMisaligned = true, // catchAccessFault = true, // earlyInjection = false, -// atomicEntriesCount = 1, +// withLrSc = true, // memoryTranslatorPortConfig = withMmu generate MmuPortConfig( // portTlbSize = 4 // ) // ), new DBusCachedPlugin( + dBusCmdMasterPipe = true, + dBusCmdSlavePipe = true, + dBusRspSlavePipe = true, config = new DataCacheConfig( - cacheSize = 4096*2, + cacheSize = 4096*1, bytePerLine = 32, - wayCount = 2, + wayCount = 1, addressWidth = 32, cpuDataWidth = 32, memDataWidth = 32, catchAccessError = true, catchIllegal = true, catchUnaligned = true, - atomicEntriesCount = 1 + withLrSc = true // ) ), memoryTranslatorPortConfig = withMmu generate MmuPortConfig( portTlbSize = 4 ) ), -// new StaticMemoryTranslatorPlugin( -// ioRange = _(31 downto 28) === 0xF -// ), + // new MemoryTranslatorPlugin( // tlbSize = 32, // virtualRange = _(31 downto 28) === 0xC, @@ -177,13 +178,13 @@ object LinuxGen { ), new RegFilePlugin( regFileReadyKind = plugin.SYNC, - zeroBoot = true + zeroBoot = false //TODO ), new IntAluPlugin, new SrcPlugin( separatedAddSub = false ), - new FullBarrelShifterPlugin(earlyInjection = true), + new FullBarrelShifterPlugin(earlyInjection = false), // new LightShifterPlugin, new HazardSimplePlugin( bypassExecute = true, @@ -230,7 +231,7 @@ object LinuxGen { // )), // new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))), new BranchPlugin( - earlyBranch = true, + earlyBranch = false, catchAddressMisaligned = true, fenceiGenAsAJump = true ), @@ -239,10 +240,14 @@ object LinuxGen { ) if(withMmu) config.plugins += new MmuPlugin( virtualRange = a => True, - // virtualRange = x => x(31 downto 24) =/= 0x81, //TODO It fix the DTB kernel access (workaround) + // virtualRange = x => x(31 downto 24) =/= 0x81, ioRange = (x => if(litex) x(31 downto 28) === 0xB || x(31 downto 28) === 0xE || x(31 downto 28) === 0xF else x(31 downto 28) === 0xF), - allowUserIo = true - ) + allowUserIo = true //TODO ?? + ) else { + config.plugins += new StaticMemoryTranslatorPlugin( + ioRange = _(31 downto 28) === 0xF + ) + } config } @@ -375,14 +380,13 @@ object LinuxSyntesisBench extends App{ // val rtls = List(fullNoMmu) val targets = XilinxStdTargets( - vivadoArtix7Path = "/eda/Xilinx/Vivado/2017.2/bin" - )/* ++ AlteraStdTargets( - quartusCycloneIVPath = "/eda/intelFPGA_lite/17.0/quartus/bin", - quartusCycloneVPath = "/eda/intelFPGA_lite/17.0/quartus/bin" - ) ++ IcestormStdTargets().take(1)*/ + vivadoArtix7Path = "/media/miaou/HD/linux/Xilinx/Vivado/2018.3/bin" + ) ++ AlteraStdTargets( + quartusCycloneIVPath = "/media/miaou/HD/linux/intelFPGA_lite/18.1/quartus/bin", + quartusCycloneVPath = "/media/miaou/HD/linux/intelFPGA_lite/18.1/quartus/bin" + ) ++ IcestormStdTargets().take(1) - - Bench(rtls, targets, "/eda/tmp") + Bench(rtls, targets, "/media/miaou/HD/linux/tmp") } object LinuxSim extends App{ diff --git a/src/main/scala/vexriscv/demo/SynthesisBench.scala b/src/main/scala/vexriscv/demo/SynthesisBench.scala index 13ebde3..29ee27e 100644 --- a/src/main/scala/vexriscv/demo/SynthesisBench.scala +++ b/src/main/scala/vexriscv/demo/SynthesisBench.scala @@ -107,25 +107,14 @@ object VexRiscvSynthesisBench { // val rtls = List(fullNoMmu) val targets = XilinxStdTargets( - vivadoArtix7Path = "/eda/Xilinx/Vivado/2017.2/bin" + vivadoArtix7Path = "/media/miaou/HD/linux/Xilinx/Vivado/2018.3/bin" ) ++ AlteraStdTargets( - quartusCycloneIVPath = "/eda/intelFPGA_lite/17.0/quartus/bin", - quartusCycloneVPath = "/eda/intelFPGA_lite/17.0/quartus/bin" + quartusCycloneIVPath = "/media/miaou/HD/linux/intelFPGA_lite/18.1/quartus/bin", + quartusCycloneVPath = "/media/miaou/HD/linux/intelFPGA_lite/18.1/quartus/bin" ) ++ IcestormStdTargets().take(1) - -// val targets = XilinxStdTargets( -// vivadoArtix7Path = "/eda/Xilinx/Vivado/2017.2/bin" -// ) - -// val targets = AlteraStdTargets( -// quartusCycloneIVPath = "/eda/intelFPGA_lite/17.0/quartus/bin", -// quartusCycloneVPath = null -// ) - - // val targets = IcestormStdTargets() - Bench(rtls, targets, "/eda/tmp") + Bench(rtls, targets, "/media/miaou/HD/linux/tmp") } } diff --git a/src/main/scala/vexriscv/ip/DataCache.scala b/src/main/scala/vexriscv/ip/DataCache.scala index a43e55f..66f6999 100644 --- a/src/main/scala/vexriscv/ip/DataCache.scala +++ b/src/main/scala/vexriscv/ip/DataCache.scala @@ -22,13 +22,12 @@ case class DataCacheConfig(cacheSize : Int, earlyWaysHits : Boolean = true, earlyDataMux : Boolean = false, tagSizeShift : Int = 0, //Used to force infering ram - atomicEntriesCount : Int = 0){ + withLrSc : Boolean = false){ assert(!(earlyDataMux && !earlyWaysHits)) def burstSize = bytePerLine*8/memDataWidth val burstLength = bytePerLine/(memDataWidth/8) def catchSomething = catchUnaligned || catchIllegal || catchAccessError - def genAtomic = atomicEntriesCount != 0 def getAxi4SharedConfig() = Axi4Config( addressWidth = addressWidth, @@ -89,7 +88,7 @@ case class DataCacheCpuExecuteArgs(p : DataCacheConfig) extends Bundle{ val data = Bits(p.cpuDataWidth bit) val size = UInt(2 bits) val forceUncachedAccess = Bool - val isAtomic = ifGen(p.genAtomic){Bool} + val isAtomic = ifGen(p.withLrSc){Bool} // val all = Bool //Address should be zero when "all" is used } @@ -116,7 +115,7 @@ case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMaste val data = Bits(p.cpuDataWidth bit) val address = UInt(p.addressWidth bit) val mmuException, unalignedAccess , accessError = Bool - val clearAtomicEntries = ifGen(p.genAtomic) {Bool} + val clearAtomicEntries = ifGen(p.withLrSc) {Bool} // val exceptionBus = if(p.catchSomething) Flow(ExceptionCause()) else null @@ -467,7 +466,7 @@ class DataCache(p : DataCacheConfig) extends Component{ } - val atomic = genAtomic generate new Area{ + val atomic = withLrSc generate new Area{ case class AtomicEntry() extends Bundle{ val valid = Bool() val address = UInt(addressWidth bits) @@ -477,18 +476,12 @@ class DataCache(p : DataCacheConfig) extends Component{ this } } - val entries = Vec(Reg(AtomicEntry()).init, atomicEntriesCount) - val entriesAllocCounter = Counter(atomicEntriesCount) - val entriesHit = entries.map(e => e.valid && e.address === io.cpu.writeBack.address).orR - when(io.cpu.writeBack.isValid && request.isAtomic && !request.wr){ - entries(entriesAllocCounter).valid := True - entries(entriesAllocCounter).address := io.cpu.writeBack.address - when(!io.cpu.writeBack.isStuck){ - entriesAllocCounter.increment() - } + val reserved = RegInit(False) + when(io.cpu.writeBack.isValid && !io.cpu.writeBack.isStuck && !io.cpu.redo && request.isAtomic && !request.wr){ + reserved := True } when(io.cpu.writeBack.clearAtomicEntries){ - entries.foreach(_.valid := False) + reserved := False } } @@ -512,7 +505,7 @@ class DataCache(p : DataCacheConfig) extends Component{ io.mem.cmd.length := 0 io.mem.cmd.last := True - if(genAtomic) when(request.isAtomic && !atomic.entriesHit){ + if(withLrSc) when(request.isAtomic && !atomic.reserved){ io.mem.cmd.valid := False io.cpu.writeBack.haltIt := False } @@ -538,7 +531,7 @@ class DataCache(p : DataCacheConfig) extends Component{ //On write to read colisions io.cpu.redo := !request.wr && (colisions & waysHits) =/= 0 - if(genAtomic) when(request.isAtomic && !atomic.entriesHit){ + if(withLrSc) when(request.isAtomic && !atomic.reserved){ io.mem.cmd.valid := False dataWriteCmd.valid := False io.cpu.writeBack.haltIt := False @@ -577,9 +570,9 @@ class DataCache(p : DataCacheConfig) extends Component{ assert(!(io.cpu.writeBack.isValid && !io.cpu.writeBack.haltIt && io.cpu.writeBack.isStuck), "writeBack stuck by another plugin is not allowed") - if(genAtomic){ + if(withLrSc){ when(request.isAtomic && request.wr){ - io.cpu.writeBack.data := (!atomic.entriesHit).asBits.resized + io.cpu.writeBack.data := (!atomic.reserved).asBits.resized } } } diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 4df02b9..59410f7 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -403,6 +403,8 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception source <- privilege.sources){ source.cond = source.cond.pull() } + + pipeline.update(MPP, UInt(2 bits)) } def inhibateInterrupts() : Unit = allowInterrupts := False @@ -503,7 +505,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception ucycleAccess(CSR.UCYCLE, mcycle(31 downto 0)) ucycleAccess(CSR.UCYCLEH, mcycle(63 downto 32)) - pipeline.update(MPP, mstatus.MPP) + pipeline(MPP) := mstatus.MPP } val supervisorCsr = ifGen(supervisorGen) { diff --git a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala index defe9f3..fcf807b 100644 --- a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala @@ -20,6 +20,9 @@ class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : An class DBusCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null, + dBusCmdMasterPipe : Boolean = false, + dBusCmdSlavePipe : Boolean = false, + dBusRspSlavePipe : Boolean = false, csrInfo : Boolean = false) extends Plugin[VexRiscv] with DBusAccessService { import config._ @@ -80,7 +83,7 @@ class DBusCachedPlugin(config : DataCacheConfig, List(SB, SH, SW).map(_ -> storeActions) ) - if(genAtomic){ + if(withLrSc){ List(LB, LH, LW, LBU, LHU, LWU, SB, SH, SW).foreach(e => decoderService.add(e, Seq(MEMORY_ATOMIC -> False)) ) @@ -147,7 +150,14 @@ class DBusCachedPlugin(config : DataCacheConfig, dBus = master(DataCacheMemBus(this.config)).setName("dBus") val cache = new DataCache(this.config) - cache.io.mem <> dBus + + //Interconnect the plugin dBus with the cache dBus with some optional pipelining + def optionPipe[T](cond : Boolean, on : T)(f : T => T) : T = if(cond) f(on) else on + def cmdBuf = optionPipe(dBusCmdSlavePipe, cache.io.mem.cmd)(_.s2mPipe()) + dBus.cmd << optionPipe(dBusCmdMasterPipe, cmdBuf)(_.m2sPipe()) + cache.io.mem.rsp << optionPipe(dBusRspSlavePipe,dBus.rsp)(_.m2sPipe()) + + execute plug new Area { import execute._ @@ -167,7 +177,7 @@ class DBusCachedPlugin(config : DataCacheConfig, cache.io.cpu.flush.valid := arbitration.isValid && input(MEMORY_MANAGMENT) arbitration.haltItself setWhen(cache.io.cpu.flush.isStall) - if(genAtomic) { + if(withLrSc) { cache.io.cpu.execute.args.isAtomic := False when(input(MEMORY_ATOMIC)){ cache.io.cpu.execute.args.isAtomic := True @@ -197,7 +207,7 @@ class DBusCachedPlugin(config : DataCacheConfig, cache.io.cpu.writeBack.isStuck := arbitration.isStuck cache.io.cpu.writeBack.isUser := (if(privilegeService != null) privilegeService.isUser() else False) cache.io.cpu.writeBack.address := U(input(REGFILE_WRITE_DATA)) - if(genAtomic) cache.io.cpu.writeBack.clearAtomicEntries := service(classOf[IContextSwitching]).isContextSwitching + if(withLrSc) cache.io.cpu.writeBack.clearAtomicEntries := service(classOf[IContextSwitching]).isContextSwitching if(catchSomething) { exceptionBus.valid := False //cache.io.cpu.writeBack.mmuMiss || cache.io.cpu.writeBack.accessError || cache.io.cpu.writeBack.illegalAccess || cache.io.cpu.writeBack.unalignedAccess @@ -250,7 +260,7 @@ class DBusCachedPlugin(config : DataCacheConfig, } //Share access to the dBus (used by self refilled MMU) - val dBusSharing = (dBusAccess != null) generate pipeline plug new Area{ + if(dBusAccess != null) pipeline plug new Area{ dBusAccess.cmd.ready := False val forceDatapath = False when(dBusAccess.cmd.valid){ @@ -264,7 +274,7 @@ class DBusCachedPlugin(config : DataCacheConfig, cache.io.cpu.execute.args.data := dBusAccess.cmd.data cache.io.cpu.execute.args.size := dBusAccess.cmd.size cache.io.cpu.execute.args.forceUncachedAccess := False - if(genAtomic) cache.io.cpu.execute.args.isAtomic := False + if(withLrSc) cache.io.cpu.execute.args.isAtomic := False cache.io.cpu.execute.address := dBusAccess.cmd.address //Will only be 12 muxes forceDatapath := True } diff --git a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala index a59e79d..54b7f19 100644 --- a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala @@ -205,12 +205,11 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, earlyInjection : Boolean = false, /*, idempotentRegions : (UInt) => Bool = (x) => False*/ emitCmdInMemoryStage : Boolean = false, onlyLoadWords : Boolean = false, - atomicEntriesCount : Int = 0, + withLrSc : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] with DBusAccessService { var dBus : DBusSimpleBus = null assert(!(emitCmdInMemoryStage && earlyInjection)) - def genAtomic = atomicEntriesCount != 0 object MEMORY_ENABLE extends Stageable(Bool) object MEMORY_READ_DATA extends Stageable(Bits(32 bits)) object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits)) @@ -269,7 +268,7 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, ) - if(genAtomic){ + if(withLrSc){ List(LB, LH, LW, LBU, LHU, LWU, SB, SH, SW).foreach(e => decoderService.add(e, Seq(MEMORY_ATOMIC -> False)) ) @@ -373,29 +372,14 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, } - val atomic = genAtomic generate new Area{ - val address = input(SRC_ADD).asUInt - case class AtomicEntry() extends Bundle{ - val valid = Bool() - val address = UInt(32 bits) - - def init: this.type ={ - valid init(False) - this - } - } - val entries = Vec(Reg(AtomicEntry()).init, atomicEntriesCount) - val entriesAllocCounter = Counter(atomicEntriesCount) - insert(ATOMIC_HIT) := entries.map(e => e.valid && e.address === address).orR - when(arbitration.isValid && input(MEMORY_ENABLE) && input(MEMORY_ATOMIC) && !input(MEMORY_STORE)){ - entries(entriesAllocCounter).valid := True - entries(entriesAllocCounter).address := address - when(!arbitration.isStuck){ - entriesAllocCounter.increment() - } + val atomic = withLrSc generate new Area{ + val reserved = RegInit(False) + insert(ATOMIC_HIT) := reserved + when(arbitration.isFiring && input(MEMORY_ENABLE) && input(MEMORY_ATOMIC) && !input(MEMORY_STORE)){ + reserved := True } when(service(classOf[IContextSwitching]).isContextSwitching){ - entries.foreach(_.valid := False) + reserved := False } when(input(MEMORY_STORE) && input(MEMORY_ATOMIC) && !input(ATOMIC_HIT)){ @@ -476,7 +460,7 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, when(arbitration.isValid && input(MEMORY_ENABLE)) { output(REGFILE_WRITE_DATA) := (if(!onlyLoadWords) rspFormated else input(MEMORY_READ_DATA)) - if(genAtomic){ + if(withLrSc){ when(input(MEMORY_ATOMIC) && input(MEMORY_STORE)){ output(REGFILE_WRITE_DATA) := (!input(ATOMIC_HIT)).asBits.resized } diff --git a/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala index 2b8c89c..ec1025c 100644 --- a/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala @@ -220,9 +220,9 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, decodeExceptionPort.valid := iBusRsp.readyForError decodeExceptionPort.code := 1 } + decodeExceptionPort.valid clearWhen(fetcherHalt) } - decodeExceptionPort.valid clearWhen(fetcherHalt) cacheRspArbitration.halt setWhen (issueDetected || iBusRspOutputHalt) iBusRsp.output.valid := cacheRspArbitration.output.valid diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index 907b2c4..83a2204 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -3456,7 +3456,6 @@ int main(int argc, char **argv, char **env) { // redo(REDO,WorkspaceRegression("deleg").withRiscvRef()->loadHex("../raw/deleg/build/deleg.hex")->bootAt(0x80000000u)->run(50e3);); // return 0; - redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3);); for(int idx = 0;idx < 1;idx++){ @@ -3568,7 +3567,8 @@ int main(int argc, char **argv, char **env) { #endif #ifdef DEBUG_PLUGIN - redo(REDO,DebugPluginTest().run(1e6);); + //TODO +// redo(REDO,DebugPluginTest().run(1e6);); #endif #endif diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index 9be96b4..d57f9bd 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -366,7 +366,7 @@ class DBusDimension extends VexRiscvDimension("DBus") { catchAccessError = catchAll, catchIllegal = catchAll, catchUnaligned = catchAll, - atomicEntriesCount = 0 + withLrSc = false ), memoryTranslatorPortConfig = null )