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.Threading; 9 using System.Collections.Generic; 10 using System.Collections.ObjectModel; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Peripherals.Timers; 16 using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection; 17 using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection.Protocols; 18 using Range = Antmicro.Renode.Core.Range; 19 20 namespace Antmicro.Renode.Peripherals.CoSimulated 21 { 22 public class CoSimulatedPeripheral : ICoSimulationConnectible, IQuadWordPeripheral, IDoubleWordPeripheral, IWordPeripheral, IBytePeripheral, IBusPeripheral, IDisposable, INumberedGPIOOutput, IGPIOReceiver, IAbsoluteAddressAware 23 { CoSimulatedPeripheral(Machine machine, int maxWidth = 64, bool useAbsoluteAddress = false, long frequency = VerilogTimeunitFrequency, string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null, string simulationContextLinux = null, string simulationContextWindows = null, string simulationContextMacOS = null, ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string address = null, bool createConnection = true, ulong renodeToCosimSignalsOffset = 0, Range? cosimToRenodeSignalRange = null, int renodeToCosimIndex = 0, int cosimToRenodeIndex = 0)24 public CoSimulatedPeripheral(Machine machine, int maxWidth = 64, bool useAbsoluteAddress = false, long frequency = VerilogTimeunitFrequency, 25 string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null, 26 string simulationContextLinux = null, string simulationContextWindows = null, string simulationContextMacOS = null, 27 ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string address = null, bool createConnection = true, 28 ulong renodeToCosimSignalsOffset = 0, Range? cosimToRenodeSignalRange = null, int renodeToCosimIndex = 0, int cosimToRenodeIndex = 0) 29 { 30 UseAbsoluteAddress = useAbsoluteAddress; 31 this.maxWidth = maxWidth; 32 this.renodeToCosimSignalsOffset = renodeToCosimSignalsOffset; 33 this.cosimToRenodeSignalRange = cosimToRenodeSignalRange; 34 RenodeToCosimIndex = renodeToCosimIndex; 35 CosimToRenodeIndex = cosimToRenodeIndex; 36 37 if(createConnection) 38 { 39 connection = new CoSimulationConnection(machine, "cosimulation_connection", frequency, 40 simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS, 41 simulationContextLinux, simulationContextWindows, simulationContextMacOS, 42 limitBuffer, timeout, address); 43 connection.AttachTo(this); 44 } 45 else 46 { 47 CheckNoEffectConstructorParam(nameof(frequency), frequency, VerilogTimeunitFrequency); 48 CheckNoEffectConstructorParam(nameof(limitBuffer), limitBuffer, LimitBuffer); 49 CheckNoEffectConstructorParam(nameof(timeout), timeout, DefaultTimeout); 50 CheckNoEffectConstructorParam(nameof(address), address, null); 51 CheckNoEffectConstructorParam(nameof(simulationFilePathLinux), simulationFilePathLinux, null); 52 CheckNoEffectConstructorParam(nameof(simulationFilePathWindows), simulationFilePathWindows, null); 53 CheckNoEffectConstructorParam(nameof(simulationFilePathMacOS), simulationFilePathMacOS, null); 54 CheckNoEffectConstructorParam(nameof(simulationContextLinux), simulationContextLinux, null); 55 CheckNoEffectConstructorParam(nameof(simulationContextWindows), simulationContextWindows, null); 56 CheckNoEffectConstructorParam(nameof(simulationContextMacOS), simulationContextMacOS, null); 57 } 58 59 var innerGPIOConnections = new Dictionary<int, IGPIO>(); 60 if(this.cosimToRenodeSignalRange.HasValue) 61 { 62 for(int i = 0; i < (int)this.cosimToRenodeSignalRange.Value.Size; i++) 63 { 64 innerGPIOConnections[i] = new GPIO(); 65 } 66 } 67 68 Connections = new ReadOnlyDictionary<int, IGPIO>(innerGPIOConnections); 69 } 70 Reset()71 public void Reset() 72 { 73 connection?.Reset(); 74 } 75 OnGPIO(int number, bool value)76 public void OnGPIO(int number, bool value) 77 { 78 // Connection can be null here because OnGPIO is called during initialization 79 // for each input connection 80 connection?.SendGPIO((int)renodeToCosimSignalsOffset + number, value); 81 } 82 OnConnectionAttached(CoSimulationConnection connection)83 public virtual void OnConnectionAttached(CoSimulationConnection connection) 84 { 85 this.connection = connection; 86 if(cosimToRenodeSignalRange.HasValue) 87 { 88 this.connection.RegisterOnGPIOReceive(ReceiveGPIOChange, cosimToRenodeSignalRange.Value); 89 } 90 } 91 OnConnectionDetached(CoSimulationConnection connection)92 public virtual void OnConnectionDetached(CoSimulationConnection connection) 93 { 94 if(cosimToRenodeSignalRange.HasValue) 95 { 96 this.connection.UnregisterOnGPIOReceive(cosimToRenodeSignalRange.Value); 97 } 98 this.connection = null; 99 } 100 ReadByte(long offset)101 public byte ReadByte(long offset) 102 { 103 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 104 if(!VerifyLength(8, offset)) 105 { 106 return 0; 107 } 108 return (byte)connection.Read(this, ActionType.ReadFromBusByte, offset); 109 } 110 ReadWord(long offset)111 public ushort ReadWord(long offset) 112 { 113 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 114 if(!VerifyLength(16, offset)) 115 { 116 return 0; 117 } 118 return (ushort)connection.Read(this, ActionType.ReadFromBusWord, offset); 119 } 120 ReadDoubleWord(long offset)121 public uint ReadDoubleWord(long offset) 122 { 123 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 124 if(!VerifyLength(32, offset)) 125 { 126 return 0; 127 } 128 return (uint)connection.Read(this, ActionType.ReadFromBusDoubleWord, offset); 129 } 130 ReadQuadWord(long offset)131 public ulong ReadQuadWord(long offset) 132 { 133 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 134 if(!VerifyLength(64, offset)) 135 { 136 return 0; 137 } 138 return connection.Read(this, ActionType.ReadFromBusQuadWord, offset); 139 } 140 WriteByte(long offset, byte value)141 public void WriteByte(long offset, byte value) 142 { 143 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 144 if(VerifyLength(8, offset, value)) 145 { 146 connection.Write(this, ActionType.WriteToBusByte, offset, value); 147 } 148 } 149 WriteWord(long offset, ushort value)150 public void WriteWord(long offset, ushort value) 151 { 152 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 153 if(VerifyLength(16, offset, value)) 154 { 155 connection.Write(this, ActionType.WriteToBusWord, offset, value); 156 } 157 } 158 WriteDoubleWord(long offset, uint value)159 public void WriteDoubleWord(long offset, uint value) 160 { 161 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 162 if(VerifyLength(32, offset, value)) 163 { 164 connection.Write(this, ActionType.WriteToBusDoubleWord, offset, value); 165 } 166 } 167 WriteQuadWord(long offset, ulong value)168 public void WriteQuadWord(long offset, ulong value) 169 { 170 offset = UseAbsoluteAddress ? (long)absoluteAddress : offset; 171 if(VerifyLength(64, offset, value)) 172 { 173 connection.Write(this, ActionType.WriteToBusQuadWord, offset, value); 174 } 175 } 176 ReceiveGPIOChange(int coSimNumber, bool value)177 public virtual void ReceiveGPIOChange(int coSimNumber, bool value) 178 { 179 if(!cosimToRenodeSignalRange.HasValue) 180 { 181 this.Log(LogLevel.Warning, $"Received GPIO change from co-simulation, but no cosimToRenodeSignalRange is defined."); 182 return; 183 } 184 185 var localNumber = coSimNumber - (int)cosimToRenodeSignalRange.Value.StartAddress; 186 if (!Connections.TryGetValue(localNumber, out var gpioConnection)) 187 { 188 this.Log(LogLevel.Warning, "Unhandled interrupt: '{0}'", localNumber); 189 return; 190 } 191 192 gpioConnection.Set(value); 193 } 194 Dispose()195 public void Dispose() 196 { 197 connection?.DetachFrom(this); 198 } 199 200 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 201 202 public string ConnectionParameters => connection?.ConnectionParameters ?? ""; Connect()203 public void Connect() 204 { 205 AssureIsConnected(); 206 connection.Connect(); 207 } 208 SetAbsoluteAddress(ulong address)209 public void SetAbsoluteAddress(ulong address) 210 { 211 absoluteAddress = address; 212 } 213 214 public string SimulationContextLinux 215 { 216 get => connection.SimulationContextLinux; 217 set 218 { 219 AssureIsConnected(); 220 connection.SimulationContextLinux = value; 221 } 222 } 223 224 public string SimulationContextWindows 225 { 226 get => connection.SimulationContextWindows; 227 set 228 { 229 AssureIsConnected(); 230 connection.SimulationContextWindows = value; 231 } 232 } 233 234 public string SimulationContextMacOS 235 { 236 get => connection.SimulationContextMacOS; 237 set 238 { 239 AssureIsConnected(); 240 connection.SimulationContextMacOS = value; 241 } 242 } 243 244 public string SimulationContext 245 { 246 get => connection.SimulationContext; 247 set 248 { 249 AssureIsConnected(); 250 connection.SimulationContext = value; 251 } 252 } 253 254 public string SimulationFilePathLinux 255 { 256 get => connection.SimulationFilePathLinux; 257 set 258 { 259 AssureIsConnected(); 260 connection.SimulationFilePathLinux = value; 261 } 262 } 263 264 public string SimulationFilePathWindows 265 { 266 get => connection.SimulationFilePathWindows; 267 set 268 { 269 AssureIsConnected(); 270 connection.SimulationFilePathWindows = value; 271 } 272 } 273 274 public string SimulationFilePathMacOS 275 { 276 get => connection.SimulationFilePathMacOS; 277 set 278 { 279 AssureIsConnected(); 280 connection.SimulationFilePathMacOS = value; 281 } 282 } 283 284 // The following constant should be in sync with a time unit defined in the `renode` SystemVerilog module. 285 // It allows using simulation time instead of a number of clock ticks. 286 public const long VerilogTimeunitFrequency = 1000000000; 287 public bool UseAbsoluteAddress { get; set; } 288 VerifyLength(int length, long offset, ulong? value = null)289 private bool VerifyLength(int length, long offset, ulong? value = null) 290 { 291 if(length > maxWidth) 292 { 293 this.Log(LogLevel.Warning, "Trying to {0} {1} bits at offset 0x{2:X}{3}, but maximum length is {4}", 294 value.HasValue ? "write" : "read", 295 length, 296 offset, 297 value.HasValue ? $" (value 0x{value})" : String.Empty, 298 maxWidth 299 ); 300 return false; 301 } 302 return true; 303 } 304 AssureIsConnected(string message = null)305 private void AssureIsConnected(string message = null) 306 { 307 if(connection == null) 308 { 309 throw new RecoverableException("CoSimulatedPeripheral is not attached to a CoSimulationConnection."); 310 } 311 } 312 CheckNoEffectConstructorParam(string name, T value, T defaultValue)313 private void CheckNoEffectConstructorParam<T>(string name, T value, T defaultValue) 314 { 315 if(EqualityComparer<T>.Default.Equals(defaultValue, value) == false) 316 { 317 this.Log(LogLevel.Error, "CoSimulatedPeripheral: Parameter \"{0}\" set to {1} will be ignored, because this peripheral uses an external CoSimulationConnection (\"createConnection\" is set to false). Change the property in the relevant CoSimulationConnection instead.", name, value); 318 } 319 } 320 321 public int RenodeToCosimIndex { get; } 322 public int CosimToRenodeIndex { get; } 323 324 protected CoSimulationConnection connection; 325 protected const ulong LimitBuffer = 1000000; 326 protected const int DefaultTimeout = 3000; 327 readonly protected Range? cosimToRenodeSignalRange; 328 329 private int maxWidth; 330 private ulong renodeToCosimSignalsOffset; 331 private ulong absoluteAddress = 0; 332 private const string LimitTimerName = "CoSimulationIntegrationClock"; 333 } 334 } 335