1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using Antmicro.Renode.Core;
8 using Antmicro.Renode.Exceptions;
9 using Antmicro.Renode.Logging;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Peripherals.CPU;
12 using Antmicro.Renode.Peripherals.Timers;
13 using Antmicro.Renode.Time;
14 using Antmicro.Renode.Utilities;
15 using System;
16 using System.Collections.Generic;
17 using System.Collections.ObjectModel;
18 using System.Diagnostics;
19 using System.Linq;
20 using System.Net;
21 using System.Net.Sockets;
22 using System.Runtime.InteropServices;
23 using System.Text;
24 using System.Threading;
25 #if !PLATFORM_WINDOWS
26 using Mono.Unix.Native;
27 #endif
28 
29 namespace Antmicro.Renode.Peripherals.SystemC
30 {
31     public enum RenodeAction : byte
32     {
33         Init = 0,
34         Read = 1,
35         Write = 2,
36         Timesync = 3,
37         GPIOWrite = 4,
38         Reset = 5,
39         DMIReq = 6,
40         InvalidateTBs = 7,
41         ReadRegister = 8,
42         WriteRegister = 9,
43     }
44 
45     [StructLayout(LayoutKind.Sequential, Pack = 1)]
46     public struct RenodeMessage
47     {
RenodeMessageAntmicro.Renode.Peripherals.SystemC.RenodeMessage48         public RenodeMessage(RenodeAction actionId, byte dataLength, byte connectionIndex, ulong address, ulong payload)
49         {
50             ActionId = actionId;
51             DataLength = dataLength;
52             ConnectionIndex = connectionIndex;
53             Address = address;
54             Payload = payload;
55         }
56 
SerializeAntmicro.Renode.Peripherals.SystemC.RenodeMessage57         public byte[] Serialize()
58         {
59             var size = Marshal.SizeOf(this);
60             var result = new byte[size];
61             var handler = default(GCHandle);
62 
63             try
64             {
65                 handler = GCHandle.Alloc(result, GCHandleType.Pinned);
66                 Marshal.StructureToPtr(this, handler.AddrOfPinnedObject(), false);
67             }
68             finally
69             {
70                 if(handler.IsAllocated)
71                 {
72                     handler.Free();
73                 }
74             }
75 
76             return result;
77         }
78 
DeserializeAntmicro.Renode.Peripherals.SystemC.RenodeMessage79         public void Deserialize(byte[] message)
80         {
81             var handler = default(GCHandle);
82             try
83             {
84                 handler = GCHandle.Alloc(message, GCHandleType.Pinned);
85                 this = (RenodeMessage)Marshal.PtrToStructure(handler.AddrOfPinnedObject(), typeof(RenodeMessage));
86             }
87             finally
88             {
89                 if(handler.IsAllocated)
90                 {
91                     handler.Free();
92                 }
93             }
94         }
95 
ToStringAntmicro.Renode.Peripherals.SystemC.RenodeMessage96         public override string ToString()
97         {
98             return $"RenodeMessage [{ActionId}@{ConnectionIndex}:{Address}] {Payload}";
99         }
100 
IsSystemBusConnectionAntmicro.Renode.Peripherals.SystemC.RenodeMessage101         public bool IsSystemBusConnection() => ConnectionIndex == MainSystemBusConnectionIndex;
IsDirectConnectionAntmicro.Renode.Peripherals.SystemC.RenodeMessage102         public bool IsDirectConnection() => !IsSystemBusConnection();
103 
GetDirectConnectionIndexAntmicro.Renode.Peripherals.SystemC.RenodeMessage104         public byte GetDirectConnectionIndex()
105         {
106             if(!IsDirectConnection())
107             {
108                 Logger.Log(LogLevel.Error, "Message for main system bus connection does not have a direct connection index.");
109                 return 0xff;
110             }
111             return (byte)(ConnectionIndex - 1);
112         }
113 
114         public const int DMIAllowed = 1;
115         public const int DMINotAllowed = 0;
116 
117         private const byte MainSystemBusConnectionIndex = 0;
118 
119         public readonly RenodeAction ActionId;
120         public readonly byte DataLength;
121         public readonly byte ConnectionIndex;
122         public readonly ulong Address;
123         public readonly ulong Payload;
124     }
125 
126     [StructLayout(LayoutKind.Sequential, Pack = 1)]
127     public struct DMIMessage
128     {
DMIMessageAntmicro.Renode.Peripherals.SystemC.DMIMessage129         public DMIMessage(RenodeAction actionId, byte allowed, ulong startAddress, ulong endAddress, ulong mmfOffset, string mmfPath)
130         {
131             ActionId = actionId;
132             Allowed = allowed;
133             StartAddress = startAddress;
134             EndAddress = endAddress;
135             MMFOffset = mmfOffset;
136             MMFPath = new byte[256];
137             byte[] mmfPathBytes = Encoding.ASCII.GetBytes(mmfPath);
138             if(mmfPathBytes.Length > 256)
139             {
140                 Logger.Log(LogLevel.Error, "MMF path name is too long");
141             }
142             else
143             {
144                 Array.Copy(mmfPathBytes, MMFPath, Math.Min(mmfPathBytes.Length, MMFPath.Length));
145             }
146         }
147 
SerializeAntmicro.Renode.Peripherals.SystemC.DMIMessage148         public byte[] Serialize()
149         {
150             var size = Marshal.SizeOf(this);
151             var result = new byte[size];
152             var handler = default(GCHandle);
153 
154             try
155             {
156                 handler = GCHandle.Alloc(result, GCHandleType.Pinned);
157                 Marshal.StructureToPtr(this, handler.AddrOfPinnedObject(), false);
158             }
159             finally
160             {
161                 if(handler.IsAllocated)
162                 {
163                     handler.Free();
164                 }
165             }
166 
167             return result;
168         }
169 
DeserializeAntmicro.Renode.Peripherals.SystemC.DMIMessage170         public void Deserialize(byte[] message)
171         {
172             var handler = default(GCHandle);
173             try
174             {
175                 handler = GCHandle.Alloc(message, GCHandleType.Pinned);
176                 this = (DMIMessage)Marshal.PtrToStructure(handler.AddrOfPinnedObject(), typeof(DMIMessage));
177             }
178             finally
179             {
180                 if(handler.IsAllocated)
181                 {
182                     handler.Free();
183                 }
184             }
185         }
186 
ToStringAntmicro.Renode.Peripherals.SystemC.DMIMessage187         public override string ToString()
188         {
189             return $"DMIMessage [{ActionId}@{StartAddress}:{EndAddress}]";
190         }
191 
192         public readonly RenodeAction ActionId;
193         public readonly byte Allowed;
194         public readonly ulong StartAddress;
195         public readonly ulong EndAddress;
196         public readonly ulong MMFOffset;
197 
198         // TODO: 256 should be a named constant
199         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
200         public byte[] MMFPath;
201     }
202 
203     public interface IDirectAccessPeripheral : IPeripheral
204     {
ReadDirect(byte dataLength, long offset, byte connectionIndex)205         ulong ReadDirect(byte dataLength, long offset, byte connectionIndex);
WriteDirect(byte dataLength, long offset, ulong value, byte connectionIndex)206         void WriteDirect(byte dataLength, long offset, ulong value, byte connectionIndex);
207     };
208 
209     public class SystemCPeripheral : IQuadWordPeripheral, IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral, INumberedGPIOOutput, IGPIOReceiver, IDirectAccessPeripheral, IDisposable
210     {
SystemCPeripheral( IMachine machine, string address, int port = 0, int timeSyncPeriodUS = 1000, bool disableTimeoutCheck = false )211         public SystemCPeripheral(
212                 IMachine machine,
213                 string address,
214                 int port = 0,
215                 int timeSyncPeriodUS = 1000,
216                 bool disableTimeoutCheck = false
217         )
218         {
219             this.address = address;
220             this.requestedPort = port;
221             this.machine = machine;
222             this.timeSyncPeriodUS = timeSyncPeriodUS;
223             this.disableTimeoutCheck = disableTimeoutCheck;
224             sysbus = machine.GetSystemBus(this);
225 
226             directAccessPeripherals = new Dictionary<int, IDirectAccessPeripheral>();
227 
228             messageLock = new object();
229 
230             backwardThread = new Thread(BackwardConnectionLoop)
231             {
232                 IsBackground = true,
233                 Name = "SystemC.BackwardThread"
234             };
235 
236             var innerConnections = new Dictionary<int, IGPIO>();
237             for(int i = 0; i < NumberOfGPIOPins; i++)
238             {
239                 innerConnections[i] = new GPIO();
240             }
241             Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections);
242         }
243 
AddDirectConnection(byte connectionIndex, IDirectAccessPeripheral target)244         public void AddDirectConnection(byte connectionIndex, IDirectAccessPeripheral target)
245         {
246             if(directAccessPeripherals.ContainsKey(connectionIndex))
247             {
248                 this.Log(LogLevel.Error, "Failed to add Direct Connection #{0} - connection with this index is already present", connectionIndex);
249                 return;
250             }
251 
252             directAccessPeripherals.Add(connectionIndex, target);
253         }
254 
255         public string SystemCExecutablePath
256         {
257             get => systemcExecutablePath;
258             set
259             {
260                 try
261                 {
262                     systemcExecutablePath = value;
263                     var listenerSocket = CreateListenerSocket(requestedPort);
264                     var assignedPort = ((IPEndPoint)listenerSocket.LocalEndPoint).Port;
265                     this.Log(LogLevel.Info, "SystemCPeripheral waiting for forward SystemC connection on {0}:{1}", address, assignedPort);
266                     var connectionParams = $"{address} {assignedPort}";
267                     StartSystemCProcess(systemcExecutablePath, connectionParams);
268                     SetupConnection(listenerSocket);
269                     SetupTimesync();
270                 }
271                 catch(Exception e)
272                 {
273                     throw new RecoverableException($"Failed to start SystemC process: {e.Message}");
274                 }
275             }
276         }
277 
ReadQuadWord(long offset)278         public ulong ReadQuadWord(long offset)
279         {
280             return Read(8, offset);
281         }
282 
WriteQuadWord(long offset, ulong value)283         public void WriteQuadWord(long offset, ulong value)
284         {
285             Write(8, offset, value);
286         }
287 
ReadDoubleWord(long offset)288         public uint ReadDoubleWord(long offset)
289         {
290             return (uint)Read(4, offset);
291         }
292 
WriteDoubleWord(long offset, uint value)293         public void WriteDoubleWord(long offset, uint value)
294         {
295             Write(4, offset, value);
296         }
297 
ReadWord(long offset)298         public ushort ReadWord(long offset)
299         {
300             return (ushort)Read(2, offset);
301         }
302 
WriteWord(long offset, ushort value)303         public void WriteWord(long offset, ushort value)
304         {
305             Write(2, offset, value);
306         }
307 
ReadByte(long offset)308         public byte ReadByte(long offset)
309         {
310             return (byte)Read(1, offset);
311         }
312 
WriteByte(long offset, byte value)313         public void WriteByte(long offset, byte value)
314         {
315             Write(1, offset, value);
316         }
317 
ReadDirect(byte dataLength, long offset, byte connectionIndex)318         public ulong ReadDirect(byte dataLength, long offset, byte connectionIndex)
319         {
320             return Read(dataLength, offset, connectionIndex);
321         }
322 
WriteDirect(byte dataLength, long offset, ulong value, byte connectionIndex)323         public void WriteDirect(byte dataLength, long offset, ulong value, byte connectionIndex)
324         {
325             Write(dataLength, offset, value, connectionIndex);
326         }
327 
OnGPIO(int number, bool value)328         public void OnGPIO(int number, bool value)
329         {
330             // When GPIO connections are initialized, OnGPIO is called with
331             // false value. The socket is not yet initialized in that case. We
332             // can safely return, no special initialization is required.
333             if(forwardSocket == null)
334             {
335                 return;
336             }
337 
338             BitHelper.SetBit(ref outGPIOState, (byte)number, value);
339             var request = new RenodeMessage(RenodeAction.GPIOWrite, 0, 0, 0, outGPIOState);
340             SendRequest(request, out var response);
341         }
342 
Reset()343         public void Reset()
344         {
345             outGPIOState = 0;
346             var request = new RenodeMessage(RenodeAction.Reset, 0, 0, 0, 0);
347             SendRequest(request, out var response);
348         }
349 
Dispose()350         public void Dispose()
351         {
352             if(systemcProcess != null && !systemcProcess.HasExited)
353             {
354                 // Init message sent after connection has been established signifies Renode terminated and SystemC process
355                 // should exit.
356                 var request = new RenodeMessage(RenodeAction.Init, 0, 0, 0, 0);
357                 SendRequest(request, out var response);
358 
359                 if(!systemcProcess.WaitForExit(500)) {
360                     this.Log(LogLevel.Info, "SystemC process failed to exit gracefully - killing it.");
361                     systemcProcess.Kill();
362                 }
363             }
364 
365             forwardSocket?.Close();
366             backwardSocket?.Close();
367         }
368 
369         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
370 
Read(byte dataLength, long offset, byte connectionIndex = 0)371         private ulong Read(byte dataLength, long offset, byte connectionIndex = 0)
372         {
373             return ReadInternal(RenodeAction.Read, dataLength, offset, connectionIndex);
374         }
375 
ReadRegister(byte dataLength, long offset, byte connectionIndex = 0)376         public ulong ReadRegister(byte dataLength, long offset, byte connectionIndex = 0)
377         {
378             return ReadInternal(RenodeAction.ReadRegister, dataLength, offset, connectionIndex);
379         }
380 
ReadInternal(RenodeAction action, byte dataLength, long offset, byte connectionIndex)381         private ulong ReadInternal(RenodeAction action, byte dataLength, long offset, byte connectionIndex)
382         {
383             var request = new RenodeMessage(action, dataLength, connectionIndex, (ulong)offset, 0);
384             if(!SendRequest(request, out var response))
385             {
386                 this.Log(LogLevel.Error, "Request to SystemCPeripheral failed, Read will return 0.");
387                 return 0;
388             }
389 
390             TryToSkipTransactionTime(response.Address);
391 
392             return response.Payload;
393         }
394 
Write(byte dataLength, long offset, ulong value, byte connectionIndex = 0)395         private void Write(byte dataLength, long offset, ulong value, byte connectionIndex = 0)
396         {
397             WriteInternal(RenodeAction.Write, dataLength, offset, value, connectionIndex);
398         }
399 
WriteRegister(byte dataLength, long offset, ulong value, byte connectionIndex = 0)400         public void WriteRegister(byte dataLength, long offset, ulong value, byte connectionIndex = 0)
401         {
402             WriteInternal(RenodeAction.WriteRegister, dataLength, offset, value, connectionIndex);
403         }
404 
WriteInternal(RenodeAction action, byte dataLength, long offset, ulong value, byte connectionIndex)405         private void WriteInternal(RenodeAction action, byte dataLength, long offset, ulong value, byte connectionIndex)
406         {
407             var request = new RenodeMessage(action, dataLength, connectionIndex, (ulong)offset, value);
408             if(!SendRequest(request, out var response))
409             {
410                 this.Log(LogLevel.Error, "Request to SystemCPeripheral failed, Write will have no effect.");
411                 return;
412             }
413 
414             TryToSkipTransactionTime(response.Address);
415         }
416 
GetCurrentVirtualTimeUS()417         private ulong GetCurrentVirtualTimeUS()
418         {
419             // Truncate, as the SystemC integration uses microsecond resolution
420             return (ulong)machine.LocalTimeSource.ElapsedVirtualTime.TotalMicroseconds;
421         }
422 
StartSystemCProcess(string systemcExecutablePath, string connectionParams)423         private void StartSystemCProcess(string systemcExecutablePath, string connectionParams)
424         {
425             try
426             {
427 #if !PLATFORM_WINDOWS
428                 Mono.Unix.Native.Syscall.chmod(systemcExecutablePath, FilePermissions.S_IRWXU);
429 #endif
430                 systemcProcess = new Process
431                 {
432                     StartInfo = new ProcessStartInfo(systemcExecutablePath)
433                     {
434                         UseShellExecute = false,
435                         Arguments = connectionParams
436                     }
437                 };
438 
439                 systemcProcess.Start();
440             }
441             catch(Exception e)
442             {
443                 throw new RecoverableException(e.Message);
444             }
445         }
446 
CreateListenerSocket(int requestedPort)447         private Socket CreateListenerSocket(int requestedPort)
448         {
449             var listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
450             listenerSocket.Bind(new IPEndPoint(IPAddress.Parse(address), requestedPort));
451             listenerSocket.Listen(2);
452             return listenerSocket;
453         }
454 
SetupConnection(Socket listenerSocket)455         private void SetupConnection(Socket listenerSocket)
456         {
457 
458             forwardSocket = listenerSocket.Accept();
459             forwardSocket.SendTimeout = 1000;
460             // No ReceiveTimeout for forwardSocket if the disableTimeoutCheck constructor argument is set - so if a debugger halts the SystemC process, Renode will wait for the process to restart
461             if(!disableTimeoutCheck)
462             {
463                 forwardSocket.ReceiveTimeout = 1000;
464             }
465 
466             backwardSocket = listenerSocket.Accept();
467             backwardSocket.SendTimeout = 1000;
468             // No ReceiveTimeout for backwardSocket - it runs on a dedicated thread and by design blocks on Receive until a message arrives from SystemC process.
469 
470             listenerSocket.Close();
471 
472             SendRequest(new RenodeMessage(RenodeAction.Init, 0, 0, 0, (ulong)timeSyncPeriodUS), out var response);
473 
474             backwardThread.Start();
475         }
476 
SetupTimesync()477         private void SetupTimesync()
478         {
479             // Timer unit is microseconds
480             var timerName = "RenodeSystemCTimesyncTimer";
481             var timesyncFrequency = 1000000;
482             var timesyncLimit = (ulong)timeSyncPeriodUS;
483 
484             var timesyncTimer = new LimitTimer(machine.ClockSource, timesyncFrequency, this, timerName, limit: timesyncLimit, enabled: true, eventEnabled: true, autoUpdate: true);
485 
486             Action<TimeInterval, TimeInterval> adjustTimesyncToQuantum = ((_, newQuantum) => {
487                 if(TimeInterval.FromMicroseconds(timesyncTimer.Limit) < newQuantum)
488                 {
489                     var newLimit = (ulong)newQuantum.TotalMicroseconds;
490                     this.Log(LogLevel.Warning, $"Requested time synchronization period of {timesyncTimer.Limit}us is smaller than local time source quantum - synchronization time will be changed to {newLimit}us to match it.");
491                     timesyncTimer.Limit = newLimit;
492                 }
493             });
494             var currentQuantum = machine.LocalTimeSource.Quantum;
495             adjustTimesyncToQuantum(currentQuantum, currentQuantum);
496             machine.LocalTimeSource.QuantumChanged += adjustTimesyncToQuantum;
497 
498             timesyncTimer.LimitReached += () =>
499             {
500                 machine.LocalTimeSource.ExecuteInNearestSyncedState(_ =>
501                 {
502                     var request = new RenodeMessage(RenodeAction.Timesync, 0, 0, 0, GetCurrentVirtualTimeUS());
503                     SendRequest(request, out var response);
504                 });
505             };
506         }
507 
SendRequest(RenodeMessage request, out RenodeMessage responseMessage)508         private bool SendRequest(RenodeMessage request, out RenodeMessage responseMessage)
509         {
510             lock (messageLock)
511             {
512                 var messageSize = Marshal.SizeOf(typeof(RenodeMessage));
513                 var recvBytes = new byte[messageSize];
514                 if(forwardSocket != null)
515                 {
516                     forwardSocket.Send(request.Serialize(), SocketFlags.None);
517                     forwardSocket.Receive(recvBytes, 0, messageSize, SocketFlags.None);
518 
519                     responseMessage = new RenodeMessage();
520                     responseMessage.Deserialize(recvBytes);
521 
522                     return true;
523                 }
524                 else
525                 {
526                     this.Log(LogLevel.Error, "Unable to communicate with SystemC peripheral. Try setting SystemCExecutablePath first.");
527                     responseMessage = new RenodeMessage();
528                     return false;
529                 }
530             }
531         }
532 
BackwardConnectionLoop()533         private void BackwardConnectionLoop()
534         {
535             while(true)
536             {
537                 var messageSize = Marshal.SizeOf(typeof(RenodeMessage));
538                 var recvBytes = new byte[messageSize];
539 
540                 var nbytes = backwardSocket.Receive(recvBytes, 0, messageSize, SocketFlags.None);
541                 if(nbytes == 0) {
542                     this.Log(LogLevel.Info, "Backward connection to SystemC process closed.");
543                     return;
544                 }
545 
546                 var message = new RenodeMessage();
547                 message.Deserialize(recvBytes);
548 
549                 ulong payload = 0;
550                 switch(message.ActionId)
551                 {
552                     case RenodeAction.GPIOWrite:
553                         // We have to respond before GPIO state is changed, because SystemC is blocked until
554                         // it receives the response. Setting the GPIO may require it to respond, e. g. when it
555                         // is interracted with from an interrupt handler.
556                         backwardSocket.Send(message.Serialize(), SocketFlags.None);
557                         for(int pin = 0; pin < NumberOfGPIOPins; pin++)
558                         {
559                             bool irqval = (message.Payload & (1UL << pin)) != 0;
560                             Connections[pin].Set(irqval);
561                         }
562                         break;
563                     case RenodeAction.Write:
564                         bool writeToSharedMem = false;
565                         if(message.IsSystemBusConnection())
566                         {
567                             var targetMem = sysbus.FindMemory(message.Address);
568                             if(targetMem != null)
569                             {
570                                 writeToSharedMem = targetMem.Peripheral.UsingSharedMemory;
571                             }
572                             sysbus.TryGetCurrentCPU(out var icpu);
573                             switch(message.DataLength)
574                             {
575                                 case 1:
576                                     sysbus.WriteByte(message.Address, (byte)message.Payload, context: icpu);
577                                     break;
578                                 case 2:
579                                     sysbus.WriteWord(message.Address, (ushort)message.Payload, context: icpu);
580                                     break;
581                                 case 4:
582                                     sysbus.WriteDoubleWord(message.Address, (uint)message.Payload, context: icpu);
583                                     break;
584                                 case 8:
585                                     sysbus.WriteQuadWord(message.Address, message.Payload, context: icpu);
586                                     break;
587                                 default:
588                                     this.Log(LogLevel.Error, "SystemC integration error - invalid data length {0} sent through backward connection from the SystemC process.", message.DataLength);
589                                     break;
590                             }
591                         }
592                         else
593                         {
594                             directAccessPeripherals[message.GetDirectConnectionIndex()].WriteDirect(
595                                     message.DataLength, (long)message.Address, message.Payload, message.ConnectionIndex);
596                         }
597                         var writeResponseMessage = new RenodeMessage(message.ActionId, message.DataLength,
598                             writeToSharedMem ? (byte)RenodeMessage.DMIAllowed : (byte)RenodeMessage.DMINotAllowed, message.Address, message.Payload);
599                         backwardSocket.Send(writeResponseMessage.Serialize(), SocketFlags.None);
600                         break;
601                     case RenodeAction.Read:
602                         bool readFromSharedMem = false;
603                         if(message.IsSystemBusConnection())
604                         {
605                             var targetMem = sysbus.FindMemory(message.Address);
606                             if(targetMem != null)
607                             {
608                                 readFromSharedMem = targetMem.Peripheral.UsingSharedMemory;
609                             }
610                             sysbus.TryGetCurrentCPU(out var icpu);
611                             switch(message.DataLength)
612                             {
613                                 case 1:
614                                     payload = (ulong)sysbus.ReadByte(message.Address, context: icpu);
615                                     break;
616                                 case 2:
617                                     payload = (ulong)sysbus.ReadWord(message.Address, context: icpu);
618                                     break;
619                                 case 4:
620                                     payload = (ulong)sysbus.ReadDoubleWord(message.Address, context: icpu);
621                                     break;
622                                 case 8:
623                                     payload = (ulong)sysbus.ReadQuadWord(message.Address, context: icpu);
624                                     break;
625                                 default:
626                                     this.Log(LogLevel.Error, "SystemC integration error - invalid data length {0} sent through backward connection from the SystemC process.", message.DataLength);
627                                     break;
628                             }
629                         }
630                         else
631                         {
632                             payload = directAccessPeripherals[message.GetDirectConnectionIndex()].ReadDirect(message.DataLength, (long)message.Address, message.ConnectionIndex);
633                         }
634                         var readResponseMessage = new RenodeMessage(message.ActionId, message.DataLength,
635                             readFromSharedMem ? (byte)RenodeMessage.DMIAllowed : (byte)RenodeMessage.DMINotAllowed, message.Address, payload);
636 
637                         backwardSocket.Send(readResponseMessage.Serialize(), SocketFlags.None);
638                         break;
639                     case RenodeAction.DMIReq:
640                         bool allowDMI = false;
641                         var targetMemory = sysbus.FindMemory(message.Address);
642                         if(targetMemory != null)
643                         {
644                             allowDMI = targetMemory.Peripheral.UsingSharedMemory;
645                         }
646                         long memBase = (long)(targetMemory.RegistrationPoint.Range.StartAddress + targetMemory.RegistrationPoint.Offset);
647                         long memOffset = (long)message.Address - memBase;
648                         ulong segmentStart = (ulong)memBase + (ulong)(memOffset - (memOffset % targetMemory.Peripheral.SegmentSize));
649                         int segmentNo = 0;
650                         if(allowDMI)
651                         {
652                             segmentNo = (int)(memOffset / targetMemory.Peripheral.SegmentSize);
653                             allowDMI = segmentNo >= 0 && segmentNo < targetMemory.Peripheral.SegmentCount;
654                         }
655                         if(allowDMI)
656                         {
657                             // DMI allowed
658                             var responseDMIMessage = new DMIMessage(
659                                 message.ActionId,
660                                 RenodeMessage.DMIAllowed,
661                                 segmentStart,
662                                 segmentStart + (ulong)targetMemory.Peripheral.SegmentSize - 1UL, // segment end
663                                 targetMemory.Peripheral.GetSegmentAlignmentOffset(segmentNo), // offset into the MMF corresponding to the segment start address
664                                 targetMemory.Peripheral.GetSegmentPath(segmentNo) // MMF path
665                             );
666                             backwardSocket.Send(responseDMIMessage.Serialize(), SocketFlags.None);
667                         }
668                         else
669                         {
670                             // DMI rejected
671                             var responseDMIMessage = new DMIMessage(
672                                 message.ActionId,
673                                 RenodeMessage.DMINotAllowed,
674                                 0UL,
675                                 0UL,
676                                 0UL,
677                                 ""
678                             );
679                             backwardSocket.Send(responseDMIMessage.Serialize(), SocketFlags.None);
680                         }
681                         break;
682                     case RenodeAction.InvalidateTBs:
683                         TryToInvalidateTBs(message.Address, message.Payload);
684                         backwardSocket.Send(message.Serialize(), SocketFlags.None);
685                         break;
686                     default:
687                         this.Log(LogLevel.Error, "SystemC integration error - invalid message type {0} sent through backward connection from the SystemC process.", message.ActionId);
688                         break;
689                 }
690             }
691         }
692 
TryToInvalidateTBs(ulong startAddress, ulong endAddress)693         private void TryToInvalidateTBs(ulong startAddress, ulong endAddress)
694         {
695             foreach(var cpu in machine.SystemBus.GetCPUs().OfType<CPU.ICPU>())
696             {
697                 var translationCPU = cpu as TranslationCPU;
698                 if(translationCPU != null)
699                 {
700                     translationCPU.InvalidateTranslationBlocks(new IntPtr((int)startAddress), new IntPtr((int)endAddress));
701                 }
702             }
703         }
704 
TryToSkipTransactionTime(ulong timeUS)705         private void TryToSkipTransactionTime(ulong timeUS)
706         {
707             if(machine.SystemBus.TryGetCurrentCPU(out var icpu))
708             {
709                 var baseCPU = icpu as BaseCPU;
710                 if(baseCPU != null)
711                 {
712                      baseCPU.SkipTime(TimeInterval.FromMicroseconds(timeUS));
713                 }
714                 else
715                 {
716                     this.Log(LogLevel.Error, "Failed to get CPU, all SystemC transactions processed as if they have no duration. This can desynchronize Renode and SystemC simulations.");
717                 }
718             }
719         }
720 
721         private readonly IBusController sysbus;
722         private readonly IMachine machine;
723 
724         // NumberOfGPIOPins must be equal to renode_bridge.h:NUM_GPIO
725         private const int NumberOfGPIOPins = 64;
726 
727         private readonly string address;
728         private readonly int requestedPort;
729         private readonly int timeSyncPeriodUS;
730         private readonly bool disableTimeoutCheck;
731         private readonly object messageLock;
732 
733         private readonly Thread backwardThread;
734 
735         private Dictionary<int, IDirectAccessPeripheral> directAccessPeripherals;
736         private string systemcExecutablePath;
737         private Process systemcProcess;
738 
739         private ulong outGPIOState;
740 
741         private Socket forwardSocket;
742         private Socket backwardSocket;
743     }
744 }
745