1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System.Collections.Generic; 9 using Antmicro.Renode.Peripherals; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Utilities; 13 using System; 14 15 namespace Antmicro.Renode.Core.Structure.Registers 16 { 17 public sealed class QuadWordRegisterCollection : BaseRegisterCollection<ulong, QuadWordRegister>, IRegisterCollection<ulong> 18 { QuadWordRegisterCollection(IPeripheral parent, IDictionary<long, QuadWordRegister> registersMap = null)19 public QuadWordRegisterCollection(IPeripheral parent, IDictionary<long, QuadWordRegister> registersMap = null) : base(parent, registersMap) 20 { 21 } 22 } 23 24 public sealed class DoubleWordRegisterCollection : BaseRegisterCollection<uint, DoubleWordRegister>, IRegisterCollection<uint> 25 { DoubleWordRegisterCollection(IPeripheral parent, IDictionary<long, DoubleWordRegister> registersMap = null)26 public DoubleWordRegisterCollection(IPeripheral parent, IDictionary<long, DoubleWordRegister> registersMap = null) : base(parent, registersMap) 27 { 28 } 29 } 30 31 public sealed class WordRegisterCollection : BaseRegisterCollection<ushort, WordRegister>, IRegisterCollection<ushort> 32 { WordRegisterCollection(IPeripheral parent, IDictionary<long, WordRegister> registersMap = null)33 public WordRegisterCollection(IPeripheral parent, IDictionary<long, WordRegister> registersMap = null) : base(parent, registersMap) 34 { 35 } 36 } 37 38 public sealed class ByteRegisterCollection : BaseRegisterCollection<byte, ByteRegister>, IRegisterCollection<byte> 39 { ByteRegisterCollection(IPeripheral parent, IDictionary<long, ByteRegister> registersMap = null)40 public ByteRegisterCollection(IPeripheral parent, IDictionary<long, ByteRegister> registersMap = null) : base(parent, registersMap) 41 { 42 } 43 } 44 45 public static class RegisterCollectionHookExtensions 46 { 47 public static void AddBeforeReadHook<T, R>(this IProvidesRegisterCollection<R> @this, long offset, Func<long, T?> func) 48 where T: struct 49 where R: IRegisterCollection 50 { 51 if(!(@this.RegistersCollection is IRegisterCollection<T> registerCollection)) 52 { 53 throw new ArgumentException($"{@this} is not valid argument", "this"); 54 } registerCollection.AddBeforeReadHookAntmicro.Renode.Core.Structure.Registers.RegisterCollectionHookExtensions.R55 registerCollection.AddBeforeReadHook(offset, func); 56 } 57 58 public static void AddAfterReadHook<T, R>(this IProvidesRegisterCollection<R> @this, long offset, Func<long, T, T?> func) 59 where T: struct 60 where R: IRegisterCollection 61 { 62 if(!(@this.RegistersCollection is IRegisterCollection<T> registerCollection)) 63 { 64 throw new ArgumentException($"{@this} is not valid argument", "this"); 65 } registerCollection.AddAfterReadHookAntmicro.Renode.Core.Structure.Registers.RegisterCollectionHookExtensions.R66 registerCollection.AddAfterReadHook(offset, func); 67 } 68 69 public static void AddBeforeWriteHook<T, R>(this IProvidesRegisterCollection<R> @this, long offset, Func<long, T, T?> func) 70 where T: struct 71 where R: IRegisterCollection 72 { 73 if(!(@this.RegistersCollection is IRegisterCollection<T> registerCollection)) 74 { 75 throw new ArgumentException($"{@this} is not valid argument", "this"); 76 } registerCollection.AddBeforeWriteHookAntmicro.Renode.Core.Structure.Registers.RegisterCollectionHookExtensions.R77 registerCollection.AddBeforeWriteHook(offset, func); 78 } 79 80 public static void AddAfterWriteHook<T, R>(this IProvidesRegisterCollection<R> @this, long offset, Action<long, T> func) 81 where T: struct 82 where R: IRegisterCollection 83 { 84 if(!(@this.RegistersCollection is IRegisterCollection<T> registerCollection)) 85 { 86 throw new ArgumentException($"{@this} is not valid argument", "this"); 87 } registerCollection.AddAfterWriteHookAntmicro.Renode.Core.Structure.Registers.RegisterCollectionHookExtensions.R88 registerCollection.AddAfterWriteHook(offset, func); 89 } 90 91 public static void RemoveBeforeReadHook<R>(this IProvidesRegisterCollection<R> @this, long offset) 92 where R: IRegisterCollection 93 { 94 @this.RegistersCollection.RemoveBeforeReadHook(offset); 95 } 96 97 public static void RemoveAfterReadHook<R>(this IProvidesRegisterCollection<R> @this, long offset) 98 where R: IRegisterCollection 99 { 100 @this.RegistersCollection.RemoveAfterReadHook(offset); 101 } 102 103 public static void RemoveBeforeWriteHook<R>(this IProvidesRegisterCollection<R> @this, long offset) 104 where R: IRegisterCollection 105 { 106 @this.RegistersCollection.RemoveBeforeWriteHook(offset); 107 } 108 109 public static void RemoveAfterWriteHook<R>(this IProvidesRegisterCollection<R> @this, long offset) 110 where R: IRegisterCollection 111 { 112 @this.RegistersCollection.RemoveAfterWriteHook(offset); 113 } 114 } 115 116 public abstract class BaseRegisterCollection<T, R> : IRegisterCollection where R: PeripheralRegister, IPeripheralRegister<T> where T: struct 117 { 118 /// <summary> 119 /// Initializes a new instance of the <see cref="Antmicro.Renode.Core.Structure.Registers.BaseRegisterCollection"/> class. 120 /// </summary> 121 /// <param name="parent">Parent peripheral (for logging purposes).</param> 122 /// <param name="registersMap">Map of register offsets and registers.</param> BaseRegisterCollection(IPeripheral parent, IDictionary<long, R> registersMap = null)123 public BaseRegisterCollection(IPeripheral parent, IDictionary<long, R> registersMap = null) 124 { 125 this.parent = parent; 126 this.beforeReadHooks = new Dictionary<long, Func<long, T?>>(); 127 this.afterReadHooks = new Dictionary<long, Func<long, T, T?>>(); 128 this.beforeWriteHooks = new Dictionary<long, Func<long, T, T?>>(); 129 this.afterWriteHooks = new Dictionary<long, Action<long, T>>(); 130 131 this.registers = new Dictionary<long, RegisterSelector<T>>(); 132 if(registersMap != null) 133 { 134 foreach(KeyValuePair<long, R> pair in registersMap) 135 { 136 AddRegisterInner(pair.Key, pair.Value); 137 } 138 } 139 } 140 141 /// <summary> 142 /// Returns the value of a register in a specified offset. If no such register is found, a logger message is issued. 143 /// </summary> 144 /// <param name="offset">Register offset.</param> Read(long offset)145 public T Read(long offset) 146 { 147 T result; 148 if(TryRead(offset, out result)) 149 { 150 return result; 151 } 152 parent.LogUnhandledRead(offset); 153 return default(T); 154 } 155 156 /// <summary> 157 /// Tries to read from a register in a specified offset. 158 /// </summary> 159 /// <returns><c>true</c>, if register was found, <c>false</c> otherwise.</returns> 160 /// <param name="offset">Register offset.</param> 161 /// <param name="result">Read value.</param> TryRead(long offset, out T result)162 public bool TryRead(long offset, out T result) 163 { 164 if(beforeReadHooks.TryGetValue(offset, out var beforeReadHook)) 165 { 166 var hookOutput = beforeReadHook(offset); 167 if(hookOutput != null) 168 { 169 result = (T)hookOutput; 170 return true; 171 } 172 } 173 174 T? output = null; 175 if(registers.TryGetValue(offset, out var register)) 176 { 177 output = register.Read(); 178 } 179 180 if(afterReadHooks.TryGetValue(offset, out var afterReadHook)) 181 { 182 var hookOutput = afterReadHook(offset, output ?? default(T)); 183 if(hookOutput != null) 184 { 185 output = hookOutput; 186 } 187 } 188 189 result = output ?? default(T); 190 return output != null; 191 } 192 193 /// <summary> 194 /// Writes to a register in a specified offset. If no such register is found, a logger message is issued. 195 /// </summary> 196 /// <param name="offset">Register offset.</param> 197 /// <param name="value">Value to write.</param> Write(long offset, T value)198 public void Write(long offset, T value) 199 { 200 if(!TryWrite(offset, value)) 201 { 202 parent.LogUnhandledWrite(offset, Misc.CastToULong(value)); 203 } 204 } 205 206 /// <summary> 207 /// Tries to write to a register in a specified offset. 208 /// </summary> 209 /// <returns><c>true</c>, if register was found, <c>false</c> otherwise.</returns> 210 /// <param name="offset">Register offset.</param> 211 /// <param name="value">Value to write.</param> TryWrite(long offset, T value)212 public bool TryWrite(long offset, T value) 213 { 214 if(registers.TryGetValue(offset, out var register)) 215 { 216 if(beforeWriteHooks.TryGetValue(offset, out var beforeWriteHook)) 217 { 218 var hookOutput = beforeWriteHook(offset, value); 219 value = hookOutput ?? value; 220 } 221 222 register.Write(offset, value); 223 224 if(afterWriteHooks.TryGetValue(offset, out var afterWriteHook)) 225 { 226 afterWriteHook(offset, value); 227 } 228 229 return true; 230 } 231 return false; 232 } 233 234 /// <summary> 235 /// Check if collection has register defined at given offset. 236 /// </summary> 237 /// <param name="offset">Register offset.</param> HasRegisterAtOffset(long offset)238 public bool HasRegisterAtOffset(long offset) 239 { 240 return registers.TryGetValue(offset, out var selector) && selector.HasRegister(); 241 } 242 243 /// <summary> 244 /// Resets all registers in this collection. 245 /// </summary> Reset()246 public void Reset() 247 { 248 foreach(var register in registers.Values) 249 { 250 register.Reset(); 251 } 252 } 253 254 /// <summary> 255 /// Adds hook which will be executed before any value has been read from the register. 256 /// First argument of the callback is the same as the provided offset. 257 /// Return value can be a number, in which case the underlying read to register will be bypassed, 258 /// or null to continue normal execution. 259 /// </summary> 260 /// <param name="offset">Register offset.</param> 261 /// <param name="hook">Callback which will be run.</param> AddBeforeReadHook(long offset, Func<long, T?> hook)262 public void AddBeforeReadHook(long offset, Func<long, T?> hook) 263 { 264 if(beforeReadHooks.ContainsKey(offset)) 265 { 266 throw new RecoverableException($"Before-read hook for 0x{offset:X} is already registered"); 267 } 268 beforeReadHooks.Add(offset, hook); 269 } 270 271 /// <summary> 272 /// Adds hook which will be executed after the value has been read from the register. 273 /// First argument of the callback is the same as the provided offset. 274 /// Second argument of the callback is the read value. 275 /// Return value can be a number, in which case the read value will be overridden, 276 /// or null to continue normal execution. 277 /// </summary> 278 /// <param name="offset">Register offset.</param> 279 /// <param name="hook">Callback which will be run.</param> AddAfterReadHook(long offset, Func<long, T, T?> hook)280 public void AddAfterReadHook(long offset, Func<long, T, T?> hook) 281 { 282 if(afterReadHooks.ContainsKey(offset)) 283 { 284 throw new RecoverableException($"After-read hook for 0x{offset:X} is already registered"); 285 } 286 afterReadHooks.Add(offset, hook); 287 } 288 289 /// <summary> 290 /// Adds hook which will be executed before any value has been written to the register. 291 /// First argument of the callback is the same as the provided offset. 292 /// Second argument of the callback is a value that is going to be written to the register. 293 /// Return value can be a number, in which case the value will be overridden, 294 /// or null to continue normal execution. 295 /// </summary> 296 /// <param name="offset">Register offset.</param> 297 /// <param name="hook">Callback which will be run.</param> AddBeforeWriteHook(long offset, Func<long, T, T?> hook)298 public void AddBeforeWriteHook(long offset, Func<long, T, T?> hook) 299 { 300 if(beforeWriteHooks.ContainsKey(offset)) 301 { 302 throw new RecoverableException($"Before-write hook for 0x{offset:X} is already registered"); 303 } 304 beforeWriteHooks.Add(offset, hook); 305 } 306 307 /// <summary> 308 /// Adds hook which will be executed before any value has been written to the register. 309 /// First argument of the callback is the same as the provided offset. 310 /// Second argument of the callback is the value that has been written to the register. 311 /// </summary> 312 /// <param name="offset">Register offset.</param> 313 /// <param name="hook">Callback which will be run.</param> AddAfterWriteHook(long offset, Action<long, T> hook)314 public void AddAfterWriteHook(long offset, Action<long, T> hook) 315 { 316 if(afterWriteHooks.ContainsKey(offset)) 317 { 318 throw new RecoverableException($"After-write hook for 0x{offset:X} is already registered"); 319 } 320 afterWriteHooks.Add(offset, hook); 321 } 322 323 /// <summary> 324 /// Removes before-read hook at given offset. 325 /// </summary> 326 /// <param name="offset">Register offset.</param> RemoveBeforeReadHook(long offset)327 public void RemoveBeforeReadHook(long offset) 328 { 329 if(!beforeReadHooks.ContainsKey(offset)) 330 { 331 throw new RecoverableException("Before-read hook for 0x{0:X} doesn't exist"); 332 } 333 beforeReadHooks.Remove(offset); 334 } 335 336 /// <summary> 337 /// Removes after-read hook at given offset. 338 /// </summary> 339 /// <param name="offset">Register offset.</param> RemoveAfterReadHook(long offset)340 public void RemoveAfterReadHook(long offset) 341 { 342 if(!afterReadHooks.ContainsKey(offset)) 343 { 344 throw new RecoverableException("After-read hook for 0x{0:X} doesn't exist"); 345 } 346 afterReadHooks.Remove(offset); 347 } 348 349 /// <summary> 350 /// Removes before-write hook at given offset. 351 /// </summary> 352 /// <param name="offset">Register offset.</param> RemoveBeforeWriteHook(long offset)353 public void RemoveBeforeWriteHook(long offset) 354 { 355 if(!beforeWriteHooks.ContainsKey(offset)) 356 { 357 throw new RecoverableException("Before-write hook for 0x{0:X} doesn't exist"); 358 } 359 beforeWriteHooks.Remove(offset); 360 } 361 362 /// <summary> 363 /// Removes after-write hook at given offset. 364 /// </summary> 365 /// <param name="offset">Register offset.</param> RemoveAfterWriteHook(long offset)366 public void RemoveAfterWriteHook(long offset) 367 { 368 if(!afterWriteHooks.ContainsKey(offset)) 369 { 370 throw new RecoverableException("After-write hook for 0x{0:X} doesn't exist"); 371 } 372 afterWriteHooks.Remove(offset); 373 } 374 375 /// <summary> 376 /// Defines a new register and adds it to the collection. 377 /// </summary> 378 /// <param name="offset">Register offset.</param> 379 /// <param name="resetValue">Register reset value.</param> 380 /// <param name="softResettable">Indicates if the register is cleared on soft reset.</param> 381 /// <returns>Newly added register.</returns> DefineRegister(long offset, T resetValue = default(T), bool softResettable = true)382 public R DefineRegister(long offset, T resetValue = default(T), bool softResettable = true) 383 { 384 return DefineConditionalRegister(offset, null, resetValue, softResettable); 385 } 386 387 /// <summary> 388 /// Defines a new conditional register and adds it to the collection. 389 /// </summary> 390 /// <param name="offset">Register offset.</param> 391 /// <param name="resetValue">Register reset value.</param> 392 /// <param name="condition">Condition based on which a register is selected.</param> 393 /// <param name="softResettable">Indicates if the register is cleared on soft reset.</param> 394 /// <returns>Newly added register.</returns> DefineConditionalRegister(long offset, Func<bool> condition, T resetValue = default(T), bool softResettable = true)395 public R DefineConditionalRegister(long offset, Func<bool> condition, T resetValue = default(T), bool softResettable = true) 396 { 397 var constructor = typeof(R).GetConstructor(new Type[] { typeof(IPeripheral), typeof(ulong), typeof(bool) }); 398 var reg = (R)constructor.Invoke(new object[] { parent, Misc.CastToULong(resetValue), softResettable }); 399 AddRegisterInner(offset, reg, condition); 400 return reg; 401 } 402 403 /// <summary> 404 /// Adds an existing register to the collection. 405 /// </summary> 406 /// <param name="offset">Register offset.</param> 407 /// <param name="register">Register to add.</param> 408 /// <returns>Added register (the same passed in <see cref="register"> argument).</returns> AddRegister(long offset, R register)409 public R AddRegister(long offset, R register) 410 { 411 AddRegisterInner(offset, register); 412 return register; 413 } 414 415 /// <summary> 416 /// Adds an existing register with a condition to the collection. 417 /// </summary> 418 /// <param name="offset">Register offset.</param> 419 /// <param name="register">Register to add.</param> 420 /// <param name="condition">Condition based on which a register is selected.</param> 421 /// <returns>Added register (the same passed in <see cref="register"> argument).</returns> AddConditionalRegister(long offset, R register, Func<bool> condition)422 public R AddConditionalRegister(long offset, R register, Func<bool> condition) 423 { 424 AddRegisterInner(offset, register, condition); 425 return register; 426 } 427 428 /// <summary> 429 /// Adds a register and condition to a new or existing selector. 430 /// </summary> 431 /// <param name="offset">Register offset.</param> 432 /// <param name="register">Rgister to add.</param> 433 /// <param name="condition">Condition based on which a register is selected.</param> AddRegisterInner(long offset, R register, Func<bool> condition = null)434 private void AddRegisterInner(long offset, R register, Func<bool> condition = null) 435 { 436 if(!registers.ContainsKey(offset)) 437 { 438 registers.Add(offset, new RegisterSelector<T>()); 439 } 440 try 441 { 442 registers[offset].AddRegister(register, condition); 443 } 444 catch(Exception e) 445 { 446 throw new ConstructionException($"At offset 0x{offset:x}: {e.Message}", e); 447 } 448 } 449 450 private readonly IPeripheral parent; 451 private readonly IDictionary<long, RegisterSelector<T>> registers; 452 453 private readonly IDictionary<long, Func<long, T?>> beforeReadHooks; 454 private readonly IDictionary<long, Func<long, T, T?>> afterReadHooks; 455 private readonly IDictionary<long, Func<long, T, T?>> beforeWriteHooks; 456 private readonly IDictionary<long, Action<long, T>> afterWriteHooks; 457 } 458 459 public interface IRegisterCollection 460 { Reset()461 void Reset(); 462 RemoveBeforeReadHook(long offset)463 void RemoveBeforeReadHook(long offset); RemoveAfterReadHook(long offset)464 void RemoveAfterReadHook(long offset); RemoveBeforeWriteHook(long offset)465 void RemoveBeforeWriteHook(long offset); RemoveAfterWriteHook(long offset)466 void RemoveAfterWriteHook(long offset); 467 } 468 469 public interface IRegisterCollection<T> : IRegisterCollection where T: struct 470 { AddBeforeReadHook(long offset, Func<long, T?> hook)471 void AddBeforeReadHook(long offset, Func<long, T?> hook); AddAfterReadHook(long offset, Func<long, T, T?> hook)472 void AddAfterReadHook(long offset, Func<long, T, T?> hook); AddBeforeWriteHook(long offset, Func<long, T, T?> hook)473 void AddBeforeWriteHook(long offset, Func<long, T, T?> hook); AddAfterWriteHook(long offset, Action<long, T> hook)474 void AddAfterWriteHook(long offset, Action<long, T> hook); 475 } 476 477 public interface IProvidesRegisterCollection<T> where T : IRegisterCollection 478 { 479 T RegistersCollection { get; } 480 } 481 } 482