1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Linq;
9 using System.Threading;
10 using System.Collections.Generic;
11 using System.Collections.Concurrent;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Peripherals.Bus;
16 using Antmicro.Renode.Peripherals.CPU;
17 using Antmicro.Renode.Peripherals.Timers;
18 using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection;
19 using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection.Protocols;
20 using Antmicro.Renode.Peripherals.CPU.Disassembler;
21 using Antmicro.Renode.Peripherals.CPU.Registers;
22 using Antmicro.Renode.Utilities;
23 using Antmicro.Renode.Time;
24 using ELFSharp.ELF;
25 using ELFSharp.UImage;
26 using Range = Antmicro.Renode.Core.Range;
27 using Machine = Antmicro.Renode.Core.Machine;
28 
29 namespace Antmicro.Renode.Peripherals.CoSimulated
30 {
31     public abstract class CoSimulatedCPU : BaseCPU, IGPIOReceiver, ICoSimulationConnectible, ITimeSink, IDisposable
32     {
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)33         public CoSimulatedCPU(string cpuType, Machine machine, Endianess endianness, CpuBitness bitness = CpuBitness.Bits32,
34             string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null,
35             string simulationContextLinux = null, string simulationContextWindows = null, string simulationContextMacOS = null, string address = null)
36             : base(0, cpuType, machine, endianness, bitness)
37         {
38             // Multiple CoSimulatedCPUs per CoSimulationConnection are currently not supported.
39             RenodeToCosimIndex = 0;
40             CosimToRenodeIndex = 0;
41 
42             cosimConnection = new CoSimulationConnection(machine, "cpu_cosim_cosimConnection", 0,
43                         simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
44                         simulationContextLinux, simulationContextWindows, simulationContextMacOS,
45                         0, 0, address);
46             cosimConnection.AttachTo(this);
47 
48             InitializeRegisters();
49         }
50 
Reset()51         public override void Reset()
52         {
53             base.Reset();
54 
55             gotRegisterValue = false;
56             setRegisterValue = false;
57             gotSingleStepMode = false;
58             ticksProcessed = false;
59             gotStep = false;
60 
61             registerValue = 0;
62             instructionsExecutedThisRound = 0;
63             totalExecutedInstructions = 0;
64 
65             lock(cosimConnectionLock)
66             {
67                 cosimConnection.Reset();
68             }
69         }
70 
Dispose()71         public override void Dispose()
72         {
73             base.Dispose();
74             lock(cosimConnectionLock)
75             {
76                 cosimConnection.Dispose();
77             }
78         }
79 
OnGPIO(int number, bool value)80         public void OnGPIO(int number, bool value)
81         {
82             if(!cosimConnection.IsConnected)
83             {
84                 this.NoisyLog("OnGPIO for IRQ {number}, value {value} will have no effect, because co-simulation is not connected.");
85                 return;
86             }
87             this.NoisyLog("IRQ {0}, value {1}", number, value);
88             lock(cosimConnectionLock)
89             {
90                 cosimConnection.Send(this, ActionType.Interrupt, (ulong)number, (ulong)(value ? 1 : 0));
91             }
92         }
93 
SetRegisterValue32(int register, uint value)94         public virtual void SetRegisterValue32(int register, uint value)
95         {
96             lock(cosimConnectionLock)
97             {
98                 setRegisterValue = false;
99                 cosimConnection.Send(this, ActionType.RegisterSet, (ulong)register, (ulong) value);
100                 while(!setRegisterValue) // This kind of while loops are for socket communication
101                 {
102                     cosimConnection.HandleMessage();
103                 }
104             }
105         }
106 
GetRegisterValue32(int register)107         public virtual uint GetRegisterValue32(int register)
108         {
109             lock(cosimConnectionLock)
110             {
111                 gotRegisterValue = false;
112                 cosimConnection.Send(this, ActionType.RegisterGet, (ulong)register, 0);
113                 while(!gotRegisterValue)
114                 {
115                     cosimConnection.HandleMessage();
116                 }
117                 return (uint)registerValue;
118             }
119         }
120 
ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)121         public override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)
122         {
123             instructionsExecutedThisRound = 0UL;
124 
125             try
126             {
127                 lock(cosimConnectionLock)
128                 {
129                     if (IsSingleStepMode)
130                     {
131                         while(instructionsExecutedThisRound < 1)
132                         {
133                             gotStep = false;
134                             cosimConnection.Send(this, ActionType.Step, 0, 1);
135                             while(!gotStep)
136                             {
137                                cosimConnection.HandleMessage();
138                             }
139                         }
140                     }
141                     else
142                     {
143                         ticksProcessed = false;
144                         cosimConnection.Send(this, ActionType.TickClock, 0, numberOfInstructionsToExecute);
145                         while(!ticksProcessed)
146                         {
147                             cosimConnection.HandleMessage();
148                         }
149                     }
150                 }
151             }
152             catch(Exception)
153             {
154                 this.NoisyLog("CPU exception detected, halting.");
155                 InvokeHalted(new HaltArguments(HaltReason.Abort, this));
156                 return ExecutionResult.Aborted;
157             }
158             finally
159             {
160                 numberOfExecutedInstructions = instructionsExecutedThisRound;
161                 totalExecutedInstructions += instructionsExecutedThisRound;
162             }
163 
164             return ExecutionResult.Ok;
165         }
166 
OnConnectionAttached(CoSimulationConnection connection)167         public virtual void OnConnectionAttached(CoSimulationConnection connection)
168         {
169             cosimConnection.OnReceive += HandleReceived;
170         }
171 
OnConnectionDetached(CoSimulationConnection connection)172         public virtual void OnConnectionDetached(CoSimulationConnection connection)
173         {
174             cosimConnection.OnReceive -= HandleReceived;
175         }
176 
177         public override ExecutionMode ExecutionMode
178         {
179             get => executionMode;
180             set
181             {
182                 lock(singleStepSynchronizer.Guard)
183                 {
184                     if(executionMode == value)
185                     {
186                         return;
187                     }
188 
189                     executionMode = value;
190 
191                     gotSingleStepMode = false;
192                     lock(cosimConnectionLock)
193                     {
194                         switch(executionMode)
195                         {
196                             case ExecutionMode.Continuous:
197                                 cosimConnection.Send(this, ActionType.SingleStepMode, 0, 0);
198                                 break;
199                             case ExecutionMode.SingleStep:
200                                 cosimConnection.Send(this, ActionType.SingleStepMode, 0, 1);
201                                 break;
202                         }
203 
204                         while(!gotSingleStepMode)
205                         {
206                             cosimConnection.HandleMessage();
207                         }
208                     }
209 
210                     singleStepSynchronizer.Enabled = IsSingleStepMode;
211                     UpdateHaltedState();
212                 }
213             }
214         }
215 
216         public override ulong ExecutedInstructions => totalExecutedInstructions;
217 
InitializeRegisters()218         protected abstract void InitializeRegisters();
219 
HandleReceived(ProtocolMessage message)220         protected bool HandleReceived(ProtocolMessage message)
221         {
222             switch(message.ActionId)
223             {
224                 case ActionType.TickClock:
225                     ticksProcessed = true;
226                     instructionsExecutedThisRound = message.Data;
227                     return true;
228                 case ActionType.IsHalted:
229                     isHaltedRequested = message.Data > 0 ? true : false;
230                     this.NoisyLog("isHaltedRequested: {0}", isHaltedRequested);
231                     return true;
232                 case ActionType.RegisterGet:
233                     registerValue = message.Data;
234                     gotRegisterValue = true;
235                     return true;
236                 case ActionType.RegisterSet:
237                     setRegisterValue = true;
238                     return true;
239                 case ActionType.SingleStepMode:
240                     gotSingleStepMode = true;
241                     return true;
242                 case ActionType.Step:
243                     gotStep = true;
244                     instructionsExecutedThisRound = message.Data;
245                     return true;
246                 default:
247                     break;
248             }
249 
250             return false;
251         }
252 
253         public string SimulationFilePathLinux
254         {
255             get => SimulationFilePath;
256             set
257             {
258 #if PLATFORM_LINUX
259                 SimulationFilePath = value;
260 #endif
261             }
262         }
263 
264         public string SimulationFilePathWindows
265         {
266             get => SimulationFilePath;
267             set
268             {
269 #if PLATFORM_WINDOWS
270                 SimulationFilePath = value;
271 #endif
272             }
273         }
274 
275         public string SimulationFilePathMacOS
276         {
277             get => SimulationFilePath;
278             set
279             {
280 #if PLATFORM_OSX
281                 SimulationFilePath = value;
282 #endif
283             }
284         }
285 
286         public string SimulationFilePath
287         {
288             get => cosimConnection.SimulationFilePath;
289             set
290             {
291                 if(!String.IsNullOrWhiteSpace(value))
292                 {
293                     cosimConnection.SimulationFilePath = value;
294                 }
295             }
296         }
297 
298         public int RenodeToCosimIndex { get; }
299         public int CosimToRenodeIndex { get; }
300 
301         protected readonly object cosimConnectionLock = new object();
302 
303         private readonly CoSimulationConnection cosimConnection;
304 
305         private ulong registerValue;
306 
307         private bool gotRegisterValue;
308         private bool setRegisterValue;
309         private bool gotSingleStepMode;
310         private bool gotStep;
311         private ulong instructionsExecutedThisRound;
312         private ulong totalExecutedInstructions;
313         private bool ticksProcessed;
314     }
315 }
316