Share memory between renode and kelvin_sim
The MappedMemory is passed to kelvin_sim for load/store in the Kelvin SW
program, so Renode and CantripOS can access the memory content same as the
previous KelvinRiscV32 module.
Change-Id: I73853336789f90bd755eaf9f742957010deff7a5
diff --git a/kelvin.resc b/kelvin.resc
index 68ecce9..8bae286 100644
--- a/kelvin.resc
+++ b/kelvin.resc
@@ -31,12 +31,10 @@
sysbus.cpu2 IsHalted true
sysbus.cpu2 CpuLibraryPath $cpuLibrary
-sysbus.cpu2 MemoryOffset 0
macro reset
"""
- # TODO(hcindyl): Replace it with LoadBinary `sysbus LoadBinary $bin 0x5A000000`
- sysbus.cpu2 ExecutableFile $bin
+ sysbus LoadBinary $bin 0x5A000000
# Start the vector core at address 0 of its TCM and halt / reset it.
sysbus.ml_top_controlblock WriteDoubleWord 0xc 3
"""
diff --git a/platforms/kelvin_ml_core_external_cpu.repl b/platforms/kelvin_ml_core_external_cpu.repl
index a2f3b11..a4dd79f 100644
--- a/platforms/kelvin_ml_core_external_cpu.repl
+++ b/platforms/kelvin_ml_core_external_cpu.repl
@@ -13,12 +13,13 @@
// limitations under the License.
// ***************************************************
-// ML Core that uses kelvin_sim as the external CPU library. WIP
+// ML Core that uses kelvin_sim as the external CPU library.
// ***************************************************
cpu2: CPU.KelvinCPU @ sysbus
id: 1
cpuType: "Kelvin"
+ memory: ram_ml_top_dmem
// RAM_ML_TOP_MEM [‘h5A00_0000 - ‘h5A3F_FFFF) 4MB RAM for ML core
ram_ml_top_dmem: Memory.MappedMemory @ sysbus 0x5A000000
diff --git a/shodan_infrastructure/KelvinCPU.cs b/shodan_infrastructure/KelvinCPU.cs
index d9df2ef..6653ee7 100644
--- a/shodan_infrastructure/KelvinCPU.cs
+++ b/shodan_infrastructure/KelvinCPU.cs
@@ -25,6 +25,7 @@
using Antmicro.Renode.Logging;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Peripherals.CPU;
+using Antmicro.Renode.Peripherals.Memory;
using Antmicro.Renode.Peripherals.Timers;
using Antmicro.Renode.Peripherals.CPU.Disassembler;
using Antmicro.Renode.Peripherals.CPU.Registers;
@@ -55,6 +56,7 @@
}
public KelvinCPU(uint id, string cpuType, Machine machine,
+ MappedMemory memory,
Endianess endianess = Endianess.LittleEndian,
CpuBitness bitness = CpuBitness.Bits32)
: base(id, cpuType, machine, endianess, bitness)
@@ -63,7 +65,12 @@
value_ptr = Marshal.AllocHGlobal(8);
reg_info_ptr = Marshal.AllocHGlobal(Marshal.SizeOf<RegInfo>());
string_ptr = Marshal.AllocHGlobal(maxStringLen);
- memoryOffset = 0;
+ dataMemory = memory;
+
+ // Touch all the memory segments to ensure all of segments are
+ // allocated with valid pointers.
+ dataMemory.TouchAllSegments();
+ cpuLibraryRegistered = false;
}
~KelvinCPU()
@@ -82,30 +89,20 @@
LogAndThrowRE("Failed to create kelvin sim instance");
return;
}
- Int32 result = load_image(kelvin_id, executableFile, memoryOffset);
- if (result < 0)
- {
- LogAndThrowRE("Failed to load executable image");
- }
- this.NoisyLog("Load executable image " + executableFile);
-
PC = 0;
}
public void RegisterControlBlock(MlTopControlBlock controlBlock)
{
- ControlBlock = controlBlock;
- ControlBlockRegistered = true;
+ this.controlBlock = controlBlock;
+ controlBlockRegistered = true;
}
- private MlTopControlBlock ControlBlock;
- private bool ControlBlockRegistered = false;
-
private void HandleKelvinEBreak()
{
- if (ControlBlockRegistered)
+ if (controlBlockRegistered)
{
- ControlBlock.ExecEBreak();
+ controlBlock.ExecEBreak();
}
else
{
@@ -115,9 +112,9 @@
private void HandleKelvinMPause()
{
- if (ControlBlockRegistered)
+ if (controlBlockRegistered)
{
- ControlBlock.ExecMPause();
+ controlBlock.ExecMPause();
}
else
{
@@ -143,7 +140,10 @@
{
base.Dispose();
// Cleanup: simulator and any unmanaged resources.
- destruct(kelvin_id);
+ lock(nativeLock)
+ {
+ destruct(kelvin_id);
+ }
}
public override ulong ExecutedInstructions => totalExecutedInstructions;
@@ -176,23 +176,32 @@
{
get
{
- Int32 error = read_register(kelvin_id, PC_ID, value_ptr);
- // TODO(hcindyl): Check different error types.
- if (error < 0)
+ Int64 value = 0;
+ lock(nativeLock)
{
- this.ErrorLog("Failed to read PC");
+ Int32 error = read_register(kelvin_id, PC_ID, value_ptr);
+ // TODO(hcindyl): Check different error types.
+ if (error < 0)
+ {
+ this.ErrorLog("Failed to read PC");
+ }
+ value = Marshal.ReadInt64(value_ptr);
+
}
- Int64 value = Marshal.ReadInt64(value_ptr);
return Convert.ToUInt64(value);
}
set
{
- Int32 error = write_register(kelvin_id, PC_ID, value);
- // TODO(hcindyl): Check different error types.
- if (error < 0)
+ lock(nativeLock)
{
- this.NoisyLog("Failed to write PC");
+ Int32 error = write_register(kelvin_id, PC_ID, value);
+
+ // TODO(hcindyl): Check different error types.
+ if (error < 0)
+ {
+ this.NoisyLog("Failed to write PC");
+ }
}
}
}
@@ -205,37 +214,41 @@
ExecutionResult result = ExecutionResult.Ok;
try
{
- // Invoke simulator for the number of instructions.
- instructionsExecutedThisRound = step(kelvin_id, numberOfInstructionsToExecute,
- error_ptr);
- // Parse the result.
- Int32 step_result = Marshal.ReadInt32(error_ptr);
- switch (step_result)
+ lock(nativeLock)
{
- case -1:
- result = ExecutionResult.Aborted;
- break;
- case 0:
- result = ExecutionResult.Ok;
- break;
- case 1:
- result = ExecutionResult.Interrupted;
- break;
- case 2:
- result = ExecutionResult.WaitingForInterrupt;
- break;
- case 3:
- result = ExecutionResult.StoppedAtBreakpoint;
- break;
- case 4:
- result = ExecutionResult.StoppedAtWatchpoint;
- break;
- case 5:
- result = ExecutionResult.ExternalMmuFault;
- break;
- default:
- LogAndThrowRE("Unknown return value from step - " + step_result);
- break;
+ // Invoke simulator for the number of instructions.
+ instructionsExecutedThisRound = step(kelvin_id,
+ numberOfInstructionsToExecute,
+ error_ptr);
+ // Parse the result.
+ Int32 step_result = Marshal.ReadInt32(error_ptr);
+ switch (step_result)
+ {
+ case -1:
+ result = ExecutionResult.Aborted;
+ break;
+ case 0:
+ result = ExecutionResult.Ok;
+ break;
+ case 1:
+ result = ExecutionResult.Interrupted;
+ break;
+ case 2:
+ result = ExecutionResult.WaitingForInterrupt;
+ break;
+ case 3:
+ result = ExecutionResult.StoppedAtBreakpoint;
+ break;
+ case 4:
+ result = ExecutionResult.StoppedAtWatchpoint;
+ break;
+ case 5:
+ result = ExecutionResult.ExternalMmuFault;
+ break;
+ default:
+ LogAndThrowRE("Unknown return value from step - " + step_result);
+ break;
+ }
}
}
catch (Exception)
@@ -272,21 +285,28 @@
// ICPUWithRegisters methods implementations.
public void SetRegisterUnsafe(int register, RegisterValue value)
{
- var status = write_register((Int32)kelvin_id, (Int32)register, (UInt64)value);
- if (status < 0)
+ lock(nativeLock)
{
- LogAndThrowRE("Failed to write register " + register);
+ var status = write_register((Int32)kelvin_id, (Int32)register, (UInt64)value);
+ if (status < 0)
+ {
+ LogAndThrowRE("Failed to write register " + register);
+ }
}
}
public RegisterValue GetRegisterUnsafe(int register)
{
- var status = read_register(kelvin_id, (UInt32)register, value_ptr);
- if (status < 0)
+ Int64 value = 0;
+ lock(nativeLock)
{
- LogAndThrowRE("Failed to read register " + register);
+ var status = read_register(kelvin_id, (UInt32)register, value_ptr);
+ if (status < 0)
+ {
+ LogAndThrowRE("Failed to read register " + register);
+ }
+ value = Marshal.ReadInt64(value_ptr);
}
- Int64 value = Marshal.ReadInt64(value_ptr);
return (UInt64)value;
}
@@ -294,22 +314,25 @@
{
if (registerMap.Count == 0)
{
- Int32 num_regs = get_reg_info_size(kelvin_id);
- for (Int32 i = 0; i < num_regs; i++)
+ lock(nativeLock)
{
- Int32 result = get_reg_info(kelvin_id, i, string_ptr, reg_info_ptr);
- if (result < 0)
+ Int32 num_regs = get_reg_info_size(kelvin_id);
+ for (Int32 i = 0; i < num_regs; i++)
{
- this.ErrorLog("Failed to get register info for index " + i);
- continue;
+ Int32 result = get_reg_info(kelvin_id, i, string_ptr, reg_info_ptr);
+ if (result < 0)
+ {
+ this.ErrorLog("Failed to get register info for index " + i);
+ 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 reg_name = Marshal.PtrToStringAuto(string_ptr);
+ registerMap.Add(reg_info.index, cpu_reg);
+ registerNamesMap.Add(reg_name, reg_info.index);
}
- 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 reg_name = Marshal.PtrToStringAuto(string_ptr);
- registerMap.Add(reg_info.index, cpu_reg);
- registerNamesMap.Add(reg_name, reg_info.index);
}
}
return registerMap.Values.OrderBy(x => x.Index);
@@ -324,9 +347,13 @@
var result = new Dictionary<string, ulong>();
foreach (var reg in registerNamesMap)
{
- var status = read_register(kelvin_id, (UInt32)reg.Value, value_ptr);
- if (status < 0) continue;
- Int64 value = Marshal.ReadInt64(value_ptr);
+ Int64 value = 0;
+ lock(nativeLock)
+ {
+ var status = read_register(kelvin_id, (UInt32)reg.Value, value_ptr);
+ if (status < 0) continue;
+ value = Marshal.ReadInt64(value_ptr);
+ }
result.Add(reg.Key, Convert.ToUInt64(value));
}
var table = new Table().AddRow("Name", "Value");
@@ -344,6 +371,12 @@
{
if (!String.IsNullOrWhiteSpace(value))
{
+ if (cpuLibraryRegistered)
+ {
+ this.WarningLog("cpu library already registerd and " +
+ "should not be updated again.");
+ return;
+ }
cpuLibraryPath = value;
try
{
@@ -353,9 +386,40 @@
{
LogAndThrowRE("Failed to load CPU library: " + e.Message);
}
+ cpuLibraryRegistered = true;
+
+ this.NoisyLog("Memory block size: {0}, total size: {1}",
+ dataMemory.SegmentSize, dataMemory.Size);
+ IntPtr[] memoryBlocks = new IntPtr[dataMemory.SegmentCount];
+ for (int i = 0; i < dataMemory.SegmentCount; i++)
+ {
+ this.NoisyLog("Segment {0}: pointer {1:8X}, size {2}",
+ i, dataMemory.GetSegment(i),
+ dataMemory.SegmentSize);
+ memoryBlocks[i] = dataMemory.GetSegment(i);
+ }
// Simulator initialization code goes here.
- kelvin_id = construct(maxStringLen);
+ IntPtr memoryBlocksPtr = Marshal.AllocHGlobal(
+ dataMemory.SegmentCount * Marshal.SizeOf<IntPtr>());
+ Marshal.Copy(memoryBlocks, 0, memoryBlocksPtr, memoryBlocks.Length);
+ lock(nativeLock)
+ {
+ // Initiate a kelvin_sim simulator. For memory access
+ // inside the kelvin_sim, it refers to the memory block
+ // pointers provided by the dataMemory here.
+ //
+ // Note we don't preempt the memory access from other
+ // cores (e.g., MlCoordinator) with the nativeLock. The
+ // control flow we have in CantripOS should not cause
+ // the conflict memory access.
+ kelvin_id = construct_with_memory(maxStringLen,
+ (ulong)dataMemory.SegmentSize,
+ (ulong)dataMemory.Size,
+ memoryBlocksPtr);
+ }
+ Marshal.FreeHGlobal(memoryBlocksPtr);
+
if (kelvin_id < 0)
{
LogAndThrowRE("Failed to create simulator instance");
@@ -370,63 +434,30 @@
}
}
- public string ExecutableFile
- {
- get
- {
- return executableFile;
- }
- set
- {
- if (!String.IsNullOrWhiteSpace(value))
- {
- executableFile = value;
- }
- else
- {
- return;
- }
- }
- }
-
- public UInt64 MemoryOffset
- {
- get
- {
- return memoryOffset;
- }
- set
- {
-
- memoryOffset = value;
-
- }
- }
-
private void LogAndThrowRE(string info)
{
this.Log(LogLevel.Error, info);
throw new RecoverableException(info);
}
- private Dictionary<Int32, CPURegister> registerMap = new Dictionary<Int32, CPURegister>();
- private Dictionary<string, Int32> registerNamesMap = new Dictionary<string, Int32>();
-
protected string cpuLibraryPath;
- protected string executableFile;
+ private bool cpuLibraryRegistered;
- protected UInt64 memoryOffset;
+ private readonly MappedMemory dataMemory;
private NativeBinder binder;
- private readonly object nativeLock;
+ // lock to ensure only one binding function call is running.
+ private readonly object nativeLock = new object();
private readonly Int32 maxStringLen = 32;
private IntPtr value_ptr { get; set; }
private IntPtr error_ptr { get; set; }
private IntPtr reg_info_ptr { get; set; }
private IntPtr string_ptr { get; set; }
-#pragma warning disable 649
+
+ // Functions from cpuLibrary
[Import(UseExceptionWrapper = false)]
- // Int32(Int32 max_string_lenth)
- private FuncInt32Int32 construct;
+ // Int32(Int32 max_string_lenth, UInt64 mem_block_size,
+ // UInt64 mem_size, uint8_t ** mem_block_ptr_list);
+ private FuncInt32Int32UInt64UInt64IntPtr construct_with_memory;
[Import(UseExceptionWrapper = false)]
// Int32(Int32 id);
private FuncInt32Int32 destruct;
@@ -440,9 +471,6 @@
// Int32(Int32 id, Int32 index, char *name, IntPtr *info);
private FuncInt32Int32Int32IntPtrIntPtr get_reg_info;
[Import(UseExceptionWrapper = false)]
- // Int32(Int32 id, String file_name, Int64 address);
- private FuncInt32Int32StringUInt64 load_image;
- [Import(UseExceptionWrapper = false)]
// UInt64(Int32 id, UInt64 step, IntPtr *status);
private FuncUInt64Int32UInt64IntPtr step;
[Import(UseExceptionWrapper = false)]
@@ -451,12 +479,25 @@
[Import(UseExceptionWrapper = false)]
// Int32(Int32 id, Int32 reg_id, UInt64 value);
private FuncInt32Int32Int32UInt64 write_register;
-#pragma warning restore 649
- private Int32 kelvin_id { get; set; }
+ // Register access.
+ private Dictionary<Int32, CPURegister> registerMap = new Dictionary<Int32, CPURegister>();
+ private Dictionary<string, Int32> registerNamesMap = new Dictionary<string, Int32>();
+
+ // PC magic ID. Defined in
+ // https://github.com/riscv/riscv-opcodes/blob/d752f193cec46cfd33300abba2a3a0d020c36755/constants.py#L317
+ // It is used to set/get PC by passing this register ID via `read_register`
+ // and `write_register`.
+ private const int PC_ID = 0x07b1;
+
private ulong instructionsExecutedThisRound { get; set; }
private ulong totalExecutedInstructions { get; set; }
- private const int PC_ID = 0x07b1; // PC magic ID
+
+ private Int32 kelvin_id { get; set; }
+
+ // MlTopControlBlock
+ private MlTopControlBlock controlBlock;
+ private bool controlBlockRegistered = false;
}