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