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,
+ };
+ }
+
}