blob: 57feec1d51be20106a5be001a16cf5575a3c9b80 [file]
//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.Linq;
using System.Threading;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Antmicro.Renode.Core;
using Antmicro.Renode.Exceptions;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Peripherals.Bus;
using Antmicro.Renode.Peripherals.CPU;
using Antmicro.Renode.Peripherals.Timers;
using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection;
using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection.Protocols;
using Antmicro.Renode.Peripherals.CPU.Disassembler;
using Antmicro.Renode.Peripherals.CPU.Registers;
using Antmicro.Renode.Utilities;
using Antmicro.Renode.Time;
using ELFSharp.ELF;
using ELFSharp.UImage;
using Range = Antmicro.Renode.Core.Range;
using Machine = Antmicro.Renode.Core.Machine;
namespace Antmicro.Renode.Peripherals.CoSimulated
{
public abstract class CoSimulatedCPU : BaseCPU, IGPIOReceiver, ICoSimulationConnectible, ITimeSink, IDisposable
{
public CoSimulatedCPU(string cpuType, Machine machine, Endianess endianness, CpuBitness bitness = CpuBitness.Bits32,
string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null,
string simulationContextLinux = null, string simulationContextWindows = null, string simulationContextMacOS = null, string address = null)
: base(0, cpuType, machine, endianness, bitness)
{
// Multiple CoSimulatedCPUs per CoSimulationConnection are currently not supported.
RenodeToCosimIndex = 0;
CosimToRenodeIndex = 0;
cosimConnection = new CoSimulationConnection(machine, "cpu_cosim_cosimConnection", 0,
simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
simulationContextLinux, simulationContextWindows, simulationContextMacOS,
0, 0, address);
cosimConnection.AttachTo(this);
InitializeRegisters();
}
public override void Reset()
{
base.Reset();
gotRegisterValue = false;
setRegisterValue = false;
gotSingleStepMode = false;
ticksProcessed = false;
gotStep = false;
registerValue = 0;
instructionsExecutedThisRound = 0;
totalExecutedInstructions = 0;
lock(cosimConnectionLock)
{
cosimConnection.Reset();
}
}
public override void Dispose()
{
base.Dispose();
lock(cosimConnectionLock)
{
cosimConnection.Dispose();
}
}
public void OnGPIO(int number, bool value)
{
if(!cosimConnection.IsConnected)
{
this.NoisyLog("OnGPIO for IRQ {number}, value {value} will have no effect, because co-simulation is not connected.");
return;
}
this.NoisyLog("IRQ {0}, value {1}", number, value);
lock(cosimConnectionLock)
{
cosimConnection.Send(this, ActionType.Interrupt, (ulong)number, (ulong)(value ? 1 : 0));
}
}
public virtual void SetRegisterValue32(int register, uint value)
{
lock(cosimConnectionLock)
{
setRegisterValue = false;
cosimConnection.Send(this, ActionType.RegisterSet, (ulong)register, (ulong) value);
while(!setRegisterValue) // This kind of while loops are for socket communication
{
cosimConnection.HandleMessage();
}
}
}
public virtual uint GetRegisterValue32(int register)
{
lock(cosimConnectionLock)
{
gotRegisterValue = false;
cosimConnection.Send(this, ActionType.RegisterGet, (ulong)register, 0);
while(!gotRegisterValue)
{
cosimConnection.HandleMessage();
}
return (uint)registerValue;
}
}
public override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)
{
instructionsExecutedThisRound = 0UL;
try
{
lock(cosimConnectionLock)
{
if (IsSingleStepMode)
{
while(instructionsExecutedThisRound < 1)
{
gotStep = false;
cosimConnection.Send(this, ActionType.Step, 0, 1);
while(!gotStep)
{
cosimConnection.HandleMessage();
}
}
}
else
{
ticksProcessed = false;
cosimConnection.Send(this, ActionType.TickClock, 0, numberOfInstructionsToExecute);
while(!ticksProcessed)
{
cosimConnection.HandleMessage();
}
}
}
}
catch(Exception)
{
this.NoisyLog("CPU exception detected, halting.");
InvokeHalted(new HaltArguments(HaltReason.Abort, this));
return ExecutionResult.Aborted;
}
finally
{
numberOfExecutedInstructions = instructionsExecutedThisRound;
totalExecutedInstructions += instructionsExecutedThisRound;
}
return ExecutionResult.Ok;
}
public virtual void OnConnectionAttached(CoSimulationConnection connection)
{
cosimConnection.OnReceive += HandleReceived;
}
public virtual void OnConnectionDetached(CoSimulationConnection connection)
{
cosimConnection.OnReceive -= HandleReceived;
}
public override ExecutionMode ExecutionMode
{
get => executionMode;
set
{
lock(singleStepSynchronizer.Guard)
{
if(executionMode == value)
{
return;
}
executionMode = value;
gotSingleStepMode = false;
lock(cosimConnectionLock)
{
switch(executionMode)
{
case ExecutionMode.Continuous:
cosimConnection.Send(this, ActionType.SingleStepMode, 0, 0);
break;
case ExecutionMode.SingleStep:
cosimConnection.Send(this, ActionType.SingleStepMode, 0, 1);
break;
}
while(!gotSingleStepMode)
{
cosimConnection.HandleMessage();
}
}
singleStepSynchronizer.Enabled = IsSingleStepMode;
UpdateHaltedState();
}
}
}
public override ulong ExecutedInstructions => totalExecutedInstructions;
protected abstract void InitializeRegisters();
protected bool HandleReceived(ProtocolMessage message)
{
switch(message.ActionId)
{
case ActionType.TickClock:
ticksProcessed = true;
instructionsExecutedThisRound = message.Data;
return true;
case ActionType.IsHalted:
isHaltedRequested = message.Data > 0 ? true : false;
this.NoisyLog("isHaltedRequested: {0}", isHaltedRequested);
return true;
case ActionType.RegisterGet:
registerValue = message.Data;
gotRegisterValue = true;
return true;
case ActionType.RegisterSet:
setRegisterValue = true;
return true;
case ActionType.SingleStepMode:
gotSingleStepMode = true;
return true;
case ActionType.Step:
gotStep = true;
instructionsExecutedThisRound = message.Data;
return true;
default:
break;
}
return false;
}
public string SimulationFilePathLinux
{
get => SimulationFilePath;
set
{
#if PLATFORM_LINUX
SimulationFilePath = value;
#endif
}
}
public string SimulationFilePathWindows
{
get => SimulationFilePath;
set
{
#if PLATFORM_WINDOWS
SimulationFilePath = value;
#endif
}
}
public string SimulationFilePathMacOS
{
get => SimulationFilePath;
set
{
#if PLATFORM_OSX
SimulationFilePath = value;
#endif
}
}
public string SimulationFilePath
{
get => cosimConnection.SimulationFilePath;
set
{
if(!String.IsNullOrWhiteSpace(value))
{
cosimConnection.SimulationFilePath = value;
}
}
}
public int RenodeToCosimIndex { get; }
public int CosimToRenodeIndex { get; }
protected readonly object cosimConnectionLock = new object();
private readonly CoSimulationConnection cosimConnection;
private ulong registerValue;
private bool gotRegisterValue;
private bool setRegisterValue;
private bool gotSingleStepMode;
private bool gotStep;
private ulong instructionsExecutedThisRound;
private ulong totalExecutedInstructions;
private bool ticksProcessed;
}
}