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.Runtime.InteropServices;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.CPU;
15 using Antmicro.Renode.Peripherals.CFU;
16 using Antmicro.Renode.Peripherals.Timers;
17 using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection;
18 using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection.Protocols;
19 using Antmicro.Renode.Utilities;
20 using Antmicro.Renode.Utilities.Binding;
21 using Antmicro.Renode.Core.Structure;
22 
23 namespace Antmicro.Renode.Peripherals.CoSimulated
24 {
25     public class CoSimulatedCFU : ICFU, ICoSimulationConnectible, IDisposable
26     {
CoSimulatedCFU(Machine machine, long frequency = 0, ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null)27         public CoSimulatedCFU(Machine machine, long frequency = 0, ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null)
28         {
29             // Multiple CoSimulatedCFUs per CoSimulationConnection are currently not supported.
30             RenodeToCosimIndex = 0;
31             CosimToRenodeIndex = 0;
32 
33             connection = new CoSimulationConnection(machine, "cosimulation_connection", frequency,
34                     simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
35                     null, null, null,
36                     limitBuffer, timeout, null);
37             connection.AttachTo(this);
38             errorPointer = IntPtr.Zero;
39         }
40 
OnConnectionAttached(CoSimulationConnection connection)41         public virtual void OnConnectionAttached(CoSimulationConnection connection)
42         {
43         }
44 
OnConnectionDetached(CoSimulationConnection connection)45         public virtual void OnConnectionDetached(CoSimulationConnection connection)
46         {
47         }
48 
Reset()49         public void Reset()
50         {
51             connection?.Reset();
52         }
53 
Dispose()54         public void Dispose()
55         {
56             connection.Dispose();
57             executeBinder?.Dispose();
58             Marshal.FreeHGlobal(errorPointer);
59         }
60 
61         public string SimulationContextLinux
62         {
63             get => connection.SimulationContextLinux;
64             set
65             {
66                 AssureIsConnected();
67                 connection.SimulationContextLinux = value;
68             }
69         }
70 
71         public string SimulationContextWindows
72         {
73             get => connection.SimulationContextWindows;
74             set
75             {
76                 AssureIsConnected();
77                 connection.SimulationContextWindows = value;
78             }
79         }
80 
81         public string SimulationContextMacOS
82         {
83             get => connection.SimulationContextMacOS;
84             set
85             {
86                 AssureIsConnected();
87                 connection.SimulationContextMacOS = value;
88             }
89         }
90 
91         public string SimulationContext
92         {
93             get => connection.SimulationContext;
94             set
95             {
96                 AssureIsConnected();
97                 connection.SimulationContext = value;
98             }
99         }
100 
101         public string SimulationFilePathLinux
102         {
103             get => connection.SimulationFilePathLinux;
104             set
105             {
106                 AssureIsConnected();
107                 connection.SimulationFilePathLinux = value;
108             }
109         }
110 
111         public string SimulationFilePathWindows
112         {
113             get => connection.SimulationFilePathWindows;
114             set
115             {
116                 AssureIsConnected();
117                 connection.SimulationFilePathWindows = value;
118             }
119         }
120 
121         public string SimulationFilePathMacOS
122         {
123             get => connection.SimulationFilePathMacOS;
124             set
125             {
126                 AssureIsConnected();
127                 connection.SimulationFilePathMacOS = value;
128             }
129         }
130 
131         public string SimulationFilePath
132         {
133             get => connection.SimulationFilePath;
134             set
135             {
136                 AssureIsConnected();
137 
138                 if(String.IsNullOrWhiteSpace(value))
139                 {
140                     this.Log(LogLevel.Warning, "SimulationFilePath not set!");
141                     return;
142                 }
143                 else if(!String.IsNullOrWhiteSpace(SimulationFilePath))
144                 {
145                     LogAndThrowRE("CoSimulatedCFU already connected, cannot change the file name!");
146                 }
147                 else if(connectedCpu.Children.Any(child => child.Peripheral.SimulationFilePath == value))
148                 {
149                     LogAndThrowRE("Another CFU already connected to provided library!");
150                 }
151                 else
152                 {
153                     try
154                     {
155                         errorPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));
156                         executeBinder = new NativeBinder(this, value);
157                         connection.SimulationFilePath = value;
158                     }
159                     catch(Exception e)
160                     {
161                         LogAndThrowRE(e.Message);
162                     }
163                 }
164             }
165         }
166 
167         public ICPU ConnectedCpu
168         {
169             get
170             {
171                 return connectedCpu;
172             }
173             set
174             {
175                 if(ConnectedCpu != null)
176                 {
177                     LogAndThrowRE("CFU already connected to CPU, cannot change CPU!");
178                 }
179                 else
180                 {
181                     connectedCpu = value as BaseRiscV;
182                     if(connectedCpu == null)
183                     {
184                         LogAndThrowRE("CFU is supported for RISCV-V CPUs only!");
185                     }
186                     RegisterCFU();
187                 }
188             }
189         }
190 
191         // This function is not used here but it is required to properly bind with libVtop.so
192         // so it is left empty on purpose.
193         [Export]
HandleSenderMessage(IntPtr received)194         private void HandleSenderMessage(IntPtr received)
195         {
196 
197         }
198 
LogAndThrowRE(string info)199         private void LogAndThrowRE(string info)
200         {
201             this.Log(LogLevel.Error, info);
202             throw new RecoverableException(info);
203         }
204 
RegisterCFU()205         private void RegisterCFU()
206         {
207             string opcodePattern = "";
208             int connectedCfus = connectedCpu.ChildCollection.Count;
209             switch(connectedCfus)
210             {
211                 case 1:
212                     opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC0001011";
213                     break;
214                 case 2:
215                     opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC0101011";
216                     break;
217                 case 3:
218                     opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC1001011";
219                     break;
220                 case 4:
221                     opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC1101011";
222                     break;
223                 default:
224                     this.LogAndThrowRE("Can't handle more than 4 CFUs!");
225                     break;
226             }
227             connectedCpu.InstallCustomInstruction(pattern: opcodePattern, handler: opcode => HandleCustomInstruction(opcode));
228             this.Log(LogLevel.Noisy, "CFU {0} registered", connectedCfus);
229         }
230 
HandleCustomInstruction(ulong opcode)231         private void HandleCustomInstruction(ulong opcode)
232         {
233             int rD = (int)BitHelper.GetValue(opcode, 7, 5);
234             int rs1 = (int)BitHelper.GetValue(opcode, 15, 5);
235             UInt32 rs1Value = Convert.ToUInt32(connectedCpu.GetRegister(rs1).RawValue);
236             int rs2 = (int)BitHelper.GetValue(opcode, 20, 5);
237             UInt32 rs2Value = Convert.ToUInt32(connectedCpu.GetRegister(rs2).RawValue);
238             UInt32 funct3 = Convert.ToUInt32(BitHelper.GetValue(opcode, 12, 3));
239             UInt32 funct7 = Convert.ToUInt32(BitHelper.GetValue(opcode, 25, 7));
240             UInt32 functionID = (funct7 << 3) + funct3;
241 
242             UInt64 result = 0UL;
243             result = execute(functionID, rs1Value, rs2Value, errorPointer);
244 
245             CfuStatus status = (CfuStatus)Marshal.ReadInt32(errorPointer);
246 
247             switch(status)
248             {
249                 case CfuStatus.CfuOk:
250                     connectedCpu.SetRegister(rD, result);
251                     break;
252                 case CfuStatus.CfuFail:
253                     this.Log(LogLevel.Error, "CFU custom instruction error, opcode: 0x{0:x}, error: {1}", opcode, status);
254                     break;
255                 case CfuStatus.CfuTimeout:
256                     this.Log(LogLevel.Error, "CFU operation timeout, opcode: 0x{0:x}, error: {1}", opcode, status);
257                     break;
258                 default:
259                     this.Log(LogLevel.Error, "CFU unknown error, opcode: 0x{0:x}, error: {1}", opcode, status);
260                     break;
261             }
262         }
263 
AssureIsConnected(string message = null)264         private void AssureIsConnected(string message = null)
265         {
266             if(connection == null)
267             {
268                 throw new RecoverableException("CoSimulatedPeripheral is not attached to a CoSimulationConnection.");
269             }
270         }
271 
272         public int RenodeToCosimIndex { get; }
273         public int CosimToRenodeIndex { get; }
274 
275         protected const ulong LimitBuffer = 100000;
276         protected const int DefaultTimeout = 3000;
277         private NativeBinder executeBinder;
278         private BaseRiscV connectedCpu;
279         private IntPtr errorPointer;
280 
281 #pragma warning disable 649
282         [Import(UseExceptionWrapper = false)]
283         private Func<uint, uint, uint, IntPtr, ulong> execute;
284 #pragma warning restore 649
285 
286         private enum CfuStatus
287         {
288             CfuOk = 0,
289             CfuFail = 1,
290             CfuTimeout = 2
291         }
292 
293         protected CoSimulationConnection connection;
294     }
295 }
296