Add ml_top controlblock on single-core kelvin resc and repl Change-Id: I26951636601f14975c5515169a62351c44169f54
diff --git a/kelvin.resc b/kelvin.resc index 52c043a..68ecce9 100644 --- a/kelvin.resc +++ b/kelvin.resc
@@ -12,35 +12,36 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Renode script for testing the Kelvin Vector Core +# Renode script for testing the Kelvin core using kelvin_sim external CPU library + mach create "kelvin" -EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.RiscV32" -include @sim/config/shodan_infrastructure/KelvinRiscV32.cs -EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.KelvinRiscV32" -EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.KelvinRiscV32_ControlBlock" -$platformfile?=@sim/config/platforms/kelvin.repl +include @sim/config/shodan_infrastructure/KelvinCPU.cs +EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.KelvinCPU" +EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.MlTopControlBlock" + +$platformfile?=@sim/config/platforms/kelvin_ml_core_external_cpu.repl machine LoadPlatformDescription $platformfile $bin?=@out/kelvin/sw/bazel_out/hello_world.bin +$cpuLibrary?=@out/kelvin/sim/librenode_kelvin.so -sysbus.cpu2 EnableRiscvOpcodesCounting +sysbus.cpu2 IsHalted true + +sysbus.cpu2 CpuLibraryPath $cpuLibrary +sysbus.cpu2 MemoryOffset 0 macro reset """ - sysbus LoadBinary $bin 0x5A000000 + # TODO(hcindyl): Replace it with LoadBinary `sysbus LoadBinary $bin 0x5A000000` + sysbus.cpu2 ExecutableFile $bin # Start the vector core at address 0 of its TCM and halt / reset it. - sysbus.vec_controlblock WriteDoubleWord 12 3 + sysbus.ml_top_controlblock WriteDoubleWord 0xc 3 """ runMacro $reset -# Note: GDB doesn't seem to like the way we're setting the program counter in -# the vec_controlblock. Breakpoints set to the exact reset address don't seem to -# get reliably triggered. We should probably root cause this later when we have -# time, but as a workaround setting the breakpoint to reset_addr+4 instead seems -# to work fine. -# Enable vec_controlblock verbose logging -logLevel -1 sysbus.vec_controlblock +logLevel 0 sysbus.cpu2 +logLevel -1 sysbus.ml_top_controlblock
diff --git a/kelvin_external_cpu.resc b/kelvin_external_cpu.resc deleted file mode 100644 index 0f78c60..0000000 --- a/kelvin_external_cpu.resc +++ /dev/null
@@ -1,34 +0,0 @@ -# Copyright 2023 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Renode script for testing the Kelvin core using kelvin_sim external CPU library - - -mach create "kelvin" - -include @sim/config/shodan_infrastructure/KelvinCPU.cs -EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.KelvinCPU" - -$platformfile?=@sim/config/platforms/kelvin_ml_core_external_cpu.repl - -machine LoadPlatformDescription $platformfile - -$bin?=@out/kelvin/sw/bazel_out/hello_world.bin -$cpuLibrary?=@out/kelvin/sim/librenode_kelvin.so - -sysbus.cpu2 CpuLibraryPath $cpuLibrary -sysbus.cpu2 ExecutableFile $bin -sysbus.cpu2 MemoryOffset 0 - -logLevel -1 sysbus.cpu2
diff --git a/platforms/kelvin_ml_core_external_cpu.repl b/platforms/kelvin_ml_core_external_cpu.repl index f83ecdd..a2f3b11 100644 --- a/platforms/kelvin_ml_core_external_cpu.repl +++ b/platforms/kelvin_ml_core_external_cpu.repl
@@ -19,3 +19,11 @@ cpu2: CPU.KelvinCPU @ sysbus id: 1 cpuType: "Kelvin" + +// RAM_ML_TOP_MEM [‘h5A00_0000 - ‘h5A3F_FFFF) 4MB RAM for ML core +ram_ml_top_dmem: Memory.MappedMemory @ sysbus 0x5A000000 + size: 0x00400000 + + +ml_top_controlblock : CPU.MlTopControlBlock @ sysbus 0x5C000000 + core: cpu2
diff --git a/shodan_infrastructure/KelvinCPU.cs b/shodan_infrastructure/KelvinCPU.cs index b136900..d9df2ef 100644 --- a/shodan_infrastructure/KelvinCPU.cs +++ b/shodan_infrastructure/KelvinCPU.cs
@@ -20,6 +20,7 @@ using System.Collections.Generic; using System.Collections.Concurrent; using Antmicro.Renode.Core; +using Antmicro.Renode.Core.Structure.Registers; using Antmicro.Renode.Exceptions; using Antmicro.Renode.Logging; using Antmicro.Renode.Peripherals.Bus; @@ -88,11 +89,42 @@ } this.NoisyLog("Load executable image " + executableFile); - // Start with halted CPU - IsHalted = true; PC = 0; } + public void RegisterControlBlock(MlTopControlBlock controlBlock) + { + ControlBlock = controlBlock; + ControlBlockRegistered = true; + } + + private MlTopControlBlock ControlBlock; + private bool ControlBlockRegistered = false; + + private void HandleKelvinEBreak() + { + if (ControlBlockRegistered) + { + ControlBlock.ExecEBreak(); + } + else + { + IsHalted = true; + } + } + + private void HandleKelvinMPause() + { + if (ControlBlockRegistered) + { + ControlBlock.ExecMPause(); + } + else + { + IsHalted = true; + } + } + public override void Reset() { base.Reset(); @@ -165,7 +197,8 @@ } } - protected override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, + protected override ExecutionResult ExecuteInstructions( + ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions) { UInt64 instructionsExecutedThisRound = 0UL; @@ -173,7 +206,8 @@ try { // Invoke simulator for the number of instructions. - instructionsExecutedThisRound = step(kelvin_id, numberOfInstructionsToExecute, error_ptr); + instructionsExecutedThisRound = step(kelvin_id, numberOfInstructionsToExecute, + error_ptr); // Parse the result. Int32 step_result = Marshal.ReadInt32(error_ptr); switch (step_result) @@ -219,11 +253,17 @@ if (numberOfInstructionsToExecute > instructionsExecutedThisRound && result == ExecutionResult.Ok) { - // TODO(hcindyl): Change to control block callbacks. - this.NoisyLog("CPU finish excution."); - this.NoisyLog("Total instruction count: {0}", + this.DebugLog("CPU finish execution."); + this.DebugLog("Total instruction count: {0}", totalExecutedInstructions); - IsHalted = true; + HandleKelvinMPause(); + } + + if (numberOfInstructionsToExecute > instructionsExecutedThisRound && + result == ExecutionResult.Aborted) + { + this.DebugLog("CPU finish execution."); + HandleKelvinEBreak(); } return result; @@ -264,7 +304,9 @@ continue; } var reg_info = Marshal.PtrToStructure<RegInfo>(reg_info_ptr); - var cpu_reg = new CPURegister(reg_info.index, reg_info.width, reg_info.isGeneral, reg_info.isReadOnly); + var cpu_reg = new CPURegister(reg_info.index, reg_info.width, + reg_info.isGeneral, + reg_info.isReadOnly); var reg_name = Marshal.PtrToStringAuto(string_ptr); registerMap.Add(reg_info.index, cpu_reg); registerNamesMap.Add(reg_name, reg_info.index); @@ -383,23 +425,32 @@ private IntPtr string_ptr { get; set; } #pragma warning disable 649 [Import(UseExceptionWrapper = false)] - private FuncInt32Int32 construct; // Int32(Int32 max_string_lenth) + // Int32(Int32 max_string_lenth) + private FuncInt32Int32 construct; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32 destruct; // Int32(Int32 id); + // Int32(Int32 id); + private FuncInt32Int32 destruct; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32 reset; // Int32(Int32 id); + // Int32(Int32 id); + private FuncInt32Int32 reset; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32 get_reg_info_size; // Int32(Int32 id) + // Int32(Int32 id) + private FuncInt32Int32 get_reg_info_size; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32Int32IntPtrIntPtr get_reg_info; // Int32(Int32 id, Int32 index, char *name, IntPtr *info); + // Int32(Int32 id, Int32 index, char *name, IntPtr *info); + private FuncInt32Int32Int32IntPtrIntPtr get_reg_info; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32StringUInt64 load_image; // Int32(Int32 id, String file_name, Int64 address); + // Int32(Int32 id, String file_name, Int64 address); + private FuncInt32Int32StringUInt64 load_image; [Import(UseExceptionWrapper = false)] - private FuncUInt64Int32UInt64IntPtr step; // UInt64(Int32 id, UInt64 step, IntPtr *status); + // UInt64(Int32 id, UInt64 step, IntPtr *status); + private FuncUInt64Int32UInt64IntPtr step; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32UInt32IntPtr read_register; // Int32(Int32 id, UInt32 reg_id, IntPtr *value); + // Int32(Int32 id, UInt32 reg_id, IntPtr *value); + private FuncInt32Int32UInt32IntPtr read_register; [Import(UseExceptionWrapper = false)] - private FuncInt32Int32Int32UInt64 write_register; // Int32(Int32 id, Int32 reg_id, UInt64 value); + // Int32(Int32 id, Int32 reg_id, UInt64 value); + private FuncInt32Int32Int32UInt64 write_register; #pragma warning restore 649 private Int32 kelvin_id { get; set; } @@ -407,4 +458,252 @@ private ulong totalExecutedInstructions { get; set; } private const int PC_ID = 0x07b1; // PC magic ID } + + + public class MlTopControlBlock : + IDoubleWordPeripheral, + IProvidesRegisterCollection<DoubleWordRegisterCollection>, + IKnownSize + { + + public MlTopControlBlock(Machine machine, + KelvinCPU core) + { + Machine = machine; + Core = core; + + HostReqIRQ = new GPIO(); + FinishIRQ = new GPIO(); + InstructionFaultIRQ = new GPIO(); + + Core.RegisterControlBlock(this); + + RegistersCollection = new DoubleWordRegisterCollection(this); + DefineRegisters(); + + Reset(); + } + + public void Reset() + { + mode = Mode.Freeze | Mode.SwReset; + RegistersCollection.Reset(); + } + + private void DefineRegisters() + { + Registers.IntrState.Define32(this) + .WithValueField(0, 4, + writeCallback: (_, value) => + { + this.NoisyLog("Got {0} to clear IRQ pending bits", value); + irqsPending = irqsPending & ~(InterruptBits)value; + IrqUpdate(); + }, + valueProviderCallback: (_) => + { + return (uint)irqsPending; + }); + + Registers.IntrEnable.Define32(this) + .WithValueField(0, 4, + writeCallback: (_, value) => + { + this.NoisyLog("Got {0} to write IRQ enable bits", value); + irqsEnabled = (InterruptBits)value & InterruptBits.Mask; + IrqUpdate(); + }, + valueProviderCallback: (_) => + { + return (uint)irqsEnabled; + }); + + Registers.IntrTest.Define32(this) + .WithValueField(0, 4, + writeCallback: (_, value) => + { + this.NoisyLog("Got {0} to set IRQ pending bits", value); + irqsPending |= ((InterruptBits)value & InterruptBits.Mask); + IrqUpdate(); + }); + + Registers.Control.Define32(this, resetValue: 0x00000002) + .WithValueField(0, 24, name: "FREEZE_RESET_PC", + writeCallback: (_, val) => + { + Mode newMode = (Mode)val & Mode.Mask; + // Pause the core when either freeze or swreset is asserted. + if ((mode == Mode.Run) && (newMode != Mode.Run)) + { + this.NoisyLog("Pausing core."); + Core.IsHalted = true; + } + + // Trigger the core's reset when SwReset is deasserted. + if (((mode & Mode.SwReset) != 0) && + ((newMode & Mode.SwReset) == 0)) + { + this.NoisyLog("Resetting core."); + // Reset PC + ulong startAddress = (val >> (int)Mode.NumBits); + if (Core.PC != startAddress) + { + this.DebugLog("Setting PC to 0x{0:X8}.", + startAddress); + Core.PC = startAddress; + } + Core.Reset(); + } + + // Raise reset bit, but the core will not be + // reset until SwReset is deasserted. + if ((mode & Mode.SwReset) == 0 && + ((newMode & Mode.SwReset) != 0)) + { + this.NoisyLog("Raise reset bit"); + } + + // Unpause the core when both freeze and + // SwReset are deasserted. + if ((mode != Mode.Run) && (newMode == Mode.Run)) + { + this.NoisyLog("Resuming core."); + Core.IsHalted = false; + + Core.Resume(); + } + + this.mode = newMode; + }) + .WithTaggedFlag("VOLT_SEL", 24) + .WithIgnoredBits(25, 32 - 25); + + // TODO: No implementation to memory bank control. + Registers.MemoryBankControl.Define32(this) + .WithTag("MEM_ENABLE", 0, 16) + .WithIgnoredBits(16, 32 - 16); + + // TODO: No implementation of memory access error. + Registers.ErrorStatus.Define32(this) + .WithTaggedFlag("MEM_OUT_OF_RANGE", 0) + .WithTag("MEM_DISABLE_ACCESS", 1, 8) + .WithIgnoredBits(9, 32 - 9); + + Registers.InitStart.Define32(this) + .WithTag("ADDRESS", 0, 22) + .WithIgnoredBits(22, 32 - 22); + + Registers.InitEnd.Define32(this) + .WithTag("ADDRESS", 0, 22) + .WithTaggedFlag("VALID", 22) + .WithIgnoredBits(23, 32 - 23); + + Registers.InitStatus.Define32(this) + .WithTaggedFlag("INIT_PENDING", 0) + .WithTaggedFlag("INIT_DONE", 1) + .WithIgnoredBits(2, 32 - 2); + + } + + public virtual uint ReadDoubleWord(long offset) + { + return RegistersCollection.Read(offset); + } + + public virtual void WriteDoubleWord(long offset, uint value) + { + RegistersCollection.Write(offset, value); + } + + public void ExecMPause() + { + // Pause and trigger a host interrupt indicating completion + if (mode == Mode.Run) + { + this.NoisyLog("Pausing the core for host completion notification."); + } + else + { + this.ErrorLog("Pausing the core for host completion notification, " + + "but core was not expected to be running. Did you " + + "clear IsHalted manually?"); + } + Core.IsHalted = true; + mode = Mode.Freeze; + irqsPending |= InterruptBits.Finish; + IrqUpdate(); + } + + public void ExecEBreak() + { + // Pause and trigger a host interrupt indicating completion with + // fault + if (mode == Mode.Run) + { + this.NoisyLog("Core executed ebreak."); + } + else + { + this.ErrorLog("Core executed ebreak, but core was not expected " + + "to be running. Did you clear IsHalted manually?"); + } + Core.IsHalted = true; + mode = Mode.Freeze; + irqsPending |= InterruptBits.Finish | InterruptBits.InstructionFault; + IrqUpdate(); + } + + public DoubleWordRegisterCollection RegistersCollection { get; private set; } + + public GPIO HostReqIRQ { get; } + public GPIO FinishIRQ { get; } + public GPIO InstructionFaultIRQ { get; } + + private InterruptBits irqsEnabled; + private InterruptBits irqsPending; + + private void IrqUpdate() + { + InterruptBits irqsPassed = irqsEnabled & irqsPending; + HostReqIRQ.Set((irqsPassed & InterruptBits.HostReq) != 0); + FinishIRQ.Set((irqsPassed & InterruptBits.Finish) != 0); + InstructionFaultIRQ.Set((irqsPassed & InterruptBits.InstructionFault) != 0); + } + + private Mode mode; + private readonly Machine Machine; + private readonly KelvinCPU Core; + // Length of register space. Required by IKnowSize. + public long Size => 0x1000; + private enum Registers + { + IntrState = 0x00, + IntrEnable = 0x04, + IntrTest = 0x08, + Control = 0x0C, + MemoryBankControl = 0x10, + ErrorStatus = 0x14, + InitStart = 0x18, + InitEnd = 0x1C, + InitStatus = 0x20, + }; + [Flags] + private enum Mode + { + Run = 0x00, + Freeze = 0x01, + SwReset = 0x02, + Mask = 0x03, + NumBits = 2, + }; + [Flags] + private enum InterruptBits + { + HostReq = 1, + Finish = 2, + InstructionFault = 4, + Mask = 7, + }; + } + }