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