// // 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; } }