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;
9 using Antmicro.Renode.Peripherals;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Utilities;
12 using System.Linq;
13 using Antmicro.Renode.Logging;
14 
15 namespace Antmicro.Renode.Core.Structure.Registers
16 {
17     /// <summary>
18     /// 64 bit <see cref="PeripheralRegister"/>.
19     /// </summary>
20     public sealed class QuadWordRegister : PeripheralRegister, IPeripheralRegister<ulong>
21     {
22         /// <summary>
23         /// Creates a register with one field, serving a purpose of read and write register.
24         /// </summary>
25         /// <returns>A new register.</returns>
26         /// <param name="resetValue">Reset value.</param>
27         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
CreateRWRegister(ulong resetValue = 0, string name = null, bool softResettable = true)28         public static QuadWordRegister CreateRWRegister(ulong resetValue = 0, string name = null, bool softResettable = true)
29         {
30             //null because parent is used for logging purposes only - this will never happen in this case.
31             var register = new QuadWordRegister(null, resetValue, softResettable);
32             register.DefineValueField(0, register.RegisterWidth);
33             return register;
34         }
35 
QuadWordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true)36         public QuadWordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, QuadWordWidth)
37         {
38         }
39 
40         /// <summary>
41         /// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
42         /// </summary>
Read()43         public ulong Read()
44         {
45             return ReadInner();
46         }
47 
48         /// <summary>
49         /// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
50         /// </summary>
Write(long offset, ulong value)51         public void Write(long offset, ulong value)
52         {
53             WriteInner(offset, value);
54         }
55 
56         /// <summary>
57         /// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
58         /// Note that it will also be called for unreadable registers.
59         /// </summary>
60         /// <param name="readCallback">Method to be called whenever this register is read. The first parameter is the value of this register before read,
61         /// the second parameter is the value after read.</param>
DefineReadCallback(Action<ulong, ulong> readCallback)62         public void DefineReadCallback(Action<ulong, ulong> readCallback)
63         {
64             readCallbacks.Add(readCallback);
65         }
66 
67         /// <summary>
68         /// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
69         /// Note that it will also be called for unwrittable registers.
70         /// </summary>
71         /// <param name="writeCallback">Method to be called whenever this register is written to. The first parameter is the value of this register before write,
72         /// the second parameter is the value written (without any modification).</param>
DefineWriteCallback(Action<ulong, ulong> writeCallback)73         public void DefineWriteCallback(Action<ulong, ulong> writeCallback)
74         {
75             writeCallbacks.Add(writeCallback);
76         }
77 
78         /// <summary>
79         /// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
80         /// Note that it will also be called for unwrittable registers.
81         /// </summary>
82         /// <param name="changeCallback">Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
83         /// the second parameter is the value after change.</param>
DefineChangeCallback(Action<ulong, ulong> changeCallback)84         public void DefineChangeCallback(Action<ulong, ulong> changeCallback)
85         {
86             changeCallbacks.Add(changeCallback);
87         }
88 
89         /// <summary>
90         /// Gets or sets the underlying value without any modification or reaction.
91         /// </summary>
92         public ulong Value
93         {
94             get
95             {
96                 return UnderlyingValue;
97             }
98             set
99             {
100                 UnderlyingValue = value;
101             }
102         }
103 
104         public const int QuadWordWidth = 64;
105 
CallChangeHandlers(ulong oldValue, ulong newValue)106         protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
107         {
108             CallHandlers(changeCallbacks, oldValue, newValue);
109         }
110 
CallReadHandlers(ulong oldValue, ulong newValue)111         protected override void CallReadHandlers(ulong oldValue, ulong newValue)
112         {
113             CallHandlers(readCallbacks, oldValue, newValue);
114         }
115 
CallWriteHandlers(ulong oldValue, ulong newValue)116         protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
117         {
118             CallHandlers(writeCallbacks, oldValue, newValue);
119         }
120 
121         private List<Action<ulong, ulong>> readCallbacks = new List<Action<ulong, ulong>>();
122         private List<Action<ulong, ulong>> writeCallbacks = new List<Action<ulong, ulong>>();
123         private List<Action<ulong, ulong>> changeCallbacks = new List<Action<ulong, ulong>>();
124     }
125 
126     /// <summary>
127     /// 32 bit <see cref="PeripheralRegister"/>.
128     /// </summary>
129     public sealed class DoubleWordRegister : PeripheralRegister, IPeripheralRegister<uint>
130     {
131         /// <summary>
132         /// Creates a register with one field, serving a purpose of read and write register.
133         /// </summary>
134         /// <returns>A new register.</returns>
135         /// <param name="resetValue">Reset value.</param>
136         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)137         public static DoubleWordRegister CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)
138         {
139             //null because parent is used for logging purposes only - this will never happen in this case.
140             var register = new DoubleWordRegister(null, resetValue, softResettable);
141             register.DefineValueField(0, register.RegisterWidth);
142             return register;
143         }
144 
DoubleWordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true)145         public DoubleWordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, DoubleWordWidth)
146         {
147         }
148 
149         /// <summary>
150         /// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
151         /// </summary>
Read()152         public uint Read()
153         {
154             return (uint)ReadInner();
155         }
156 
157         /// <summary>
158         /// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
159         /// </summary>
Write(long offset, uint value)160         public void Write(long offset, uint value)
161         {
162             WriteInner(offset, value);
163         }
164 
165         /// <summary>
166         /// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
167         /// Note that it will also be called for unreadable registers.
168         /// </summary>
169         /// <param name="readCallback">Method to be called whenever this register is read. The first parameter is the value of this register before read,
170         /// the second parameter is the value after read.</param>
DefineReadCallback(Action<uint, uint> readCallback)171         public void DefineReadCallback(Action<uint, uint> readCallback)
172         {
173             readCallbacks.Add(readCallback);
174         }
175 
176         /// <summary>
177         /// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
178         /// Note that it will also be called for unwrittable registers.
179         /// </summary>
180         /// <param name="writeCallback">Method to be called whenever this register is written to. The first parameter is the value of this register before write,
181         /// the second parameter is the value written (without any modification).</param>
DefineWriteCallback(Action<uint, uint> writeCallback)182         public void DefineWriteCallback(Action<uint, uint> writeCallback)
183         {
184             writeCallbacks.Add(writeCallback);
185         }
186 
187         /// <summary>
188         /// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
189         /// Note that it will also be called for unwrittable registers.
190         /// </summary>
191         /// <param name="changeCallback">Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
192         /// the second parameter is the value after change.</param>
DefineChangeCallback(Action<uint, uint> changeCallback)193         public void DefineChangeCallback(Action<uint, uint> changeCallback)
194         {
195             changeCallbacks.Add(changeCallback);
196         }
197 
198         /// <summary>
199         /// Gets or sets the underlying value without any modification or reaction.
200         /// </summary>
201         public uint Value
202         {
203             get
204             {
205                 return (uint)UnderlyingValue;
206             }
207             set
208             {
209                 UnderlyingValue = value;
210             }
211         }
212 
213         public const int DoubleWordWidth = 32;
214 
CallChangeHandlers(ulong oldValue, ulong newValue)215         protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
216         {
217             CallHandlers(changeCallbacks, (uint)oldValue, (uint)newValue);
218         }
219 
CallReadHandlers(ulong oldValue, ulong newValue)220         protected override void CallReadHandlers(ulong oldValue, ulong newValue)
221         {
222             CallHandlers(readCallbacks, (uint)oldValue, (uint)newValue);
223         }
224 
CallWriteHandlers(ulong oldValue, ulong newValue)225         protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
226         {
227             CallHandlers(writeCallbacks, (uint)oldValue, (uint)newValue);
228         }
229 
230         private List<Action<uint, uint>> readCallbacks = new List<Action<uint, uint>>();
231         private List<Action<uint, uint>> writeCallbacks = new List<Action<uint, uint>>();
232         private List<Action<uint, uint>> changeCallbacks = new List<Action<uint, uint>>();
233     }
234 
235     /// <summary>
236     /// 16 bit <see cref="PeripheralRegister"/>.
237     /// </summary>
238     public sealed class WordRegister : PeripheralRegister, IPeripheralRegister<ushort>
239     {
240         /// <summary>
241         /// Creates a register with one field, serving a purpose of read and write register.
242         /// </summary>
243         /// <returns>A new register.</returns>
244         /// <param name="resetValue">Reset value.</param>
245         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)246         public static WordRegister CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)
247         {
248             //null because parent is used for logging purposes only - this will never happen in this case.
249             var register = new WordRegister(null, resetValue, softResettable);
250             register.DefineValueField(0, register.RegisterWidth);
251             return register;
252         }
253 
WordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true)254         public WordRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, WordWidth)
255         {
256         }
257 
258         /// <summary>
259         /// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
260         /// </summary>
Read()261         public ushort Read()
262         {
263             return (ushort)ReadInner();
264         }
265 
266         /// <summary>
267         /// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
268         /// </summary>
Write(long offset, ushort value)269         public void Write(long offset, ushort value)
270         {
271             WriteInner(offset, value);
272         }
273 
274         /// <summary>
275         /// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
276         /// Note that it will also be called for unreadable registers.
277         /// </summary>
278         /// <param name="readCallback">Method to be called whenever this register is read. The first parameter is the value of this register before read,
279         /// the second parameter is the value after read.</param>
DefineReadCallback(Action<ushort, ushort> readCallback)280         public void DefineReadCallback(Action<ushort, ushort> readCallback)
281         {
282             readCallbacks.Add(readCallback);
283         }
284 
285         /// <summary>
286         /// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
287         /// Note that it will also be called for unwrittable registers.
288         /// </summary>
289         /// <param name="writeCallback">Method to be called whenever this register is written to. The first parameter is the value of this register before write,
290         /// the second parameter is the value written (without any modification).</param>
DefineWriteCallback(Action<ushort, ushort> writeCallback)291         public void DefineWriteCallback(Action<ushort, ushort> writeCallback)
292         {
293             writeCallbacks.Add(writeCallback);
294         }
295 
296         /// <summary>
297         /// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
298         /// Note that it will also be called for unwrittable registers.
299         /// </summary>
300         /// <param name="changeCallback">Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
301         /// the second parameter is the value after change.</param>
DefineChangeCallback(Action<ushort, ushort> changeCallback)302         public void DefineChangeCallback(Action<ushort, ushort> changeCallback)
303         {
304             changeCallbacks.Add(changeCallback);
305         }
306 
307         /// <summary>
308         /// Gets or sets the underlying value without any modification or reaction.
309         /// </summary>
310         public ushort Value
311         {
312             get
313             {
314                 return (ushort)UnderlyingValue;
315             }
316             set
317             {
318                 UnderlyingValue = value;
319             }
320         }
321 
322         public const int WordWidth = 16;
323 
CallChangeHandlers(ulong oldValue, ulong newValue)324         protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
325         {
326             CallHandlers(changeCallbacks, (ushort)oldValue, (ushort)newValue);
327         }
328 
CallReadHandlers(ulong oldValue, ulong newValue)329         protected override void CallReadHandlers(ulong oldValue, ulong newValue)
330         {
331             CallHandlers(readCallbacks, (ushort)oldValue, (ushort)newValue);
332         }
333 
CallWriteHandlers(ulong oldValue, ulong newValue)334         protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
335         {
336             CallHandlers(writeCallbacks, (ushort)oldValue, (ushort)newValue);
337         }
338 
339         private List<Action<ushort, ushort>> readCallbacks = new List<Action<ushort, ushort>>();
340         private List<Action<ushort, ushort>> writeCallbacks = new List<Action<ushort, ushort>>();
341         private List<Action<ushort, ushort>> changeCallbacks = new List<Action<ushort, ushort>>();
342     }
343 
344     /// <summary>
345     /// 8 bit <see cref="PeripheralRegister"/>.
346     /// </summary>
347     public sealed class ByteRegister : PeripheralRegister, IPeripheralRegister<byte>
348     {
349         /// <summary>
350         /// Creates a register with one field, serving a purpose of read and write register.
351         /// </summary>
352         /// <returns>A new register.</returns>
353         /// <param name="resetValue">Reset value.</param>
354         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)355         public static ByteRegister CreateRWRegister(uint resetValue = 0, string name = null, bool softResettable = true)
356         {
357             //null because parent is used for logging purposes only - this will never happen in this case.
358             var register = new ByteRegister(null, resetValue, softResettable);
359             register.DefineValueField(0, register.RegisterWidth);
360             return register;
361         }
362 
ByteRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true)363         public ByteRegister(IPeripheral parent, ulong resetValue = 0, bool softResettable = true) : base(parent, resetValue, softResettable, ByteWidth)
364         {
365         }
366 
367         /// <summary>
368         /// Retrieves the current value of readable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
369         /// </summary>
Read()370         public byte Read()
371         {
372             return (byte)ReadInner();
373         }
374 
375         /// <summary>
376         /// Writes the given value to writeable fields. All FieldMode values are interpreted and callbacks are executed where applicable.
377         /// </summary>
Write(long offset, byte value)378         public void Write(long offset, byte value)
379         {
380             WriteInner(offset, value);
381         }
382 
383         /// <summary>
384         /// Defines the read callback that is called once on each read, regardles of the number of defined register fields.
385         /// Note that it will also be called for unreadable registers.
386         /// </summary>
387         /// <param name="readCallback">Method to be called whenever this register is read. The first parameter is the value of this register before read,
388         /// the second parameter is the value after read.</param>
DefineReadCallback(Action<byte, byte> readCallback)389         public void DefineReadCallback(Action<byte, byte> readCallback)
390         {
391             readCallbacks.Add(readCallback);
392         }
393 
394         /// <summary>
395         /// Defines the write callback that is called once on each write, regardles of the number of defined register fields.
396         /// Note that it will also be called for unwrittable registers.
397         /// </summary>
398         /// <param name="writeCallback">Method to be called whenever this register is written to. The first parameter is the value of this register before write,
399         /// the second parameter is the value written (without any modification).</param>
DefineWriteCallback(Action<byte, byte> writeCallback)400         public void DefineWriteCallback(Action<byte, byte> writeCallback)
401         {
402             writeCallbacks.Add(writeCallback);
403         }
404 
405         /// <summary>
406         /// Defines the change callback that is called once on each change, regardles of the number of defined register fields.
407         /// Note that it will also be called for unwrittable registers.
408         /// </summary>
409         /// <param name="changeCallback">Method to be called whenever this register's value is changed, either due to read or write. The first parameter is the value of this register before change,
410         /// the second parameter is the value after change.</param>
DefineChangeCallback(Action<byte, byte> changeCallback)411         public void DefineChangeCallback(Action<byte, byte> changeCallback)
412         {
413             changeCallbacks.Add(changeCallback);
414         }
415 
416         /// <summary>
417         /// Gets or sets the underlying value without any modification or reaction.
418         /// </summary>
419         public byte Value
420         {
421             get
422             {
423                 return (byte)UnderlyingValue;
424             }
425             set
426             {
427                 UnderlyingValue = value;
428             }
429         }
430 
431         public const int ByteWidth = 8;
432 
CallChangeHandlers(ulong oldValue, ulong newValue)433         protected override void CallChangeHandlers(ulong oldValue, ulong newValue)
434         {
435             CallHandlers(changeCallbacks, (byte)oldValue, (byte)newValue);
436         }
437 
CallReadHandlers(ulong oldValue, ulong newValue)438         protected override void CallReadHandlers(ulong oldValue, ulong newValue)
439         {
440             CallHandlers(readCallbacks, (byte)oldValue, (byte)newValue);
441         }
442 
CallWriteHandlers(ulong oldValue, ulong newValue)443         protected override void CallWriteHandlers(ulong oldValue, ulong newValue)
444         {
445             CallHandlers(writeCallbacks, (byte)oldValue, (byte)newValue);
446         }
447 
448         private List<Action<byte, byte>> readCallbacks = new List<Action<byte, byte>>();
449         private List<Action<byte, byte>> writeCallbacks = new List<Action<byte, byte>>();
450         private List<Action<byte, byte>> changeCallbacks = new List<Action<byte, byte>>();
451 
452     }
453 
454     public interface IPeripheralRegister<T>
455     {
Read()456         T Read();
Write(long offset, T value)457         void Write(long offset, T value);
Reset()458         void Reset();
459     }
460 
461     /// <summary>
462     /// Represents a register of a given width, containing defined fields.
463     /// Fields may not exceed this register's width, nor may they overlap each other.
464     /// Fields that are not handled (e.g. left for future implementation or unimportant) have to be tagged.
465     /// Otherwise, they will not be logged.
466     /// </summary>
467     public abstract partial class PeripheralRegister
468     {
469         /// <summary>
470         /// Restores this register's value to its reset value, defined on per-field basis.
471         /// </summary>
Reset()472         public void Reset()
473         {
474             BitHelper.UpdateWithMasked(ref UnderlyingValue, resetValue, resettableMask);
475         }
476 
477         /// <summary>
478         /// Wrapper for <see cref="Tag"/> method, tagging bits as "RESERVED".
479         /// </summary>
480         /// <param name="position">Offset in the register.</param>
481         /// <param name="width">Width of field.</param>
482         /// <param name="allowedValue">Value allowed to be written.<\param>
Reserved(int position, int width, ulong? allowedValue = null)483         public void Reserved(int position, int width, ulong? allowedValue = null)
484         {
485             Tag("RESERVED", position, width, allowedValue);
486         }
487 
488         /// <summary>
489         /// Mark an unhandled field, so it is logged with its name.
490         /// </summary>
491         /// <param name="name">Name of the unhandled field.</param>
492         /// <param name="position">Offset in the register.</param>
493         /// <param name="width">Width of field.</param>
494         /// <param name="allowedValue">Value allowed to be written.<\param>
Tag(string name, int position, int width, ulong? allowedValue = null)495         public void Tag(string name, int position, int width, ulong? allowedValue = null)
496         {
497             ThrowIfRangeIllegal(position, width, name);
498 
499             if(allowedValue != null)
500             {
501                 ThrowIfAllowedValueDoesNotFitInWidth(width, allowedValue.Value, name);
502             }
503 
504             tags.Add(new Tag
505             {
506                 Name = name,
507                 Position = position,
508                 Width = width,
509                 AllowedValue = allowedValue
510             });
511         }
512 
513         /// <summary>
514         /// Defines the flag field. Its width is always 1 and is interpreted as boolean value.
515         /// </summary>
516         /// <param name="position">Offset in the register.</param>
517         /// <param name="mode">Access modifiers of this field.</param>
518         /// <param name="readCallback">Method to be called whenever the containing register is read. The first parameter is the value of this field before read,
519         /// the second parameter is the value after read. Note that it will also be called for unreadable fields.</param>
520         /// <param name="writeCallback">Method to be called whenever the containing register is written to. The first parameter is the value of this field before write,
521         /// the second parameter is the value written (without any modification). Note that it will also be called for unwrittable fields.</param>
522         /// <param name="changeCallback">Method to be called whenever this field's value is changed, either due to read or write. The first parameter is the value of this field before change,
523         /// the second parameter is the value after change. Note that it will also be called for unwrittable fields.</param>
524         /// <param name="valueProviderCallback">Method to be called whenever this field is read. The value passed is the current field's value, that will be overwritten by
525         /// the value returned from it. This returned value is eventually passed as the first parameter of <paramref name="readCallback"/>.</param>
526         /// <param name="softResettable">Indicates whether the field should be cleared by soft reset.</param>
527         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
DefineFlagField(int position, FieldMode mode = FieldMode.Read | FieldMode.Write, Action<bool, bool> readCallback = null, Action<bool, bool> writeCallback = null, Action<bool, bool> changeCallback = null, Func<bool, bool> valueProviderCallback = null, bool softResettable = true, string name = null)528         public IFlagRegisterField DefineFlagField(int position, FieldMode mode = FieldMode.Read | FieldMode.Write, Action<bool, bool> readCallback = null,
529             Action<bool, bool> writeCallback = null, Action<bool, bool> changeCallback = null, Func<bool, bool> valueProviderCallback = null, bool softResettable = true,
530             string name = null)
531         {
532             ThrowIfRangeIllegal(position, 1, name);
533             var field = new FlagRegisterField(this, position, mode, readCallback, writeCallback, changeCallback, valueProviderCallback, name);
534             registerFields.Add(field);
535             if(!softResettable)
536             {
537                 MarkNonResettable(position, 1);
538             }
539             RecalculateFieldMask();
540             return field;
541         }
542 
543         /// <summary>
544         /// Defines the value field. Its value is interpreted as a regular number.
545         /// </summary>
546         /// <param name="position">Offset in the register.</param>
547         /// <param name="width">Maximum width of the value, in terms of binary representation.</param>
548         /// <param name="mode">Access modifiers of this field.</param>
549         /// <param name="readCallback">Method to be called whenever the containing register is read. The first parameter is the value of this field before read,
550         /// the second parameter is the value after read. Note that it will also be called for unreadable fields.</param>
551         /// <param name="writeCallback">Method to be called whenever the containing register is written to. The first parameter is the value of this field before write,
552         /// the second parameter is the value written (without any modification). Note that it will also be called for unwrittable fields.</param>
553         /// <param name="changeCallback">Method to be called whenever this field's value is changed, either due to read or write. The first parameter is the value of this field before change,
554         /// the second parameter is the value after change. Note that it will also be called for unwrittable fields.</param>
555         /// <param name="valueProviderCallback">Method to be called whenever this field is read. The value passed is the current field's value, that will be overwritten by
556         /// the value returned from it. This returned value is eventually passed as the first parameter of <paramref name="readCallback"/>.</param>
557         /// <param name="softResettable">Indicates whether the field should be cleared by soft reset.</param>
558         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
DefineValueField(int position, int width, FieldMode mode = FieldMode.Read | FieldMode.Write, Action<ulong, ulong> readCallback = null, Action<ulong, ulong> writeCallback = null, Action<ulong, ulong> changeCallback = null, Func<ulong, ulong> valueProviderCallback = null, bool softResettable = true, string name = null)559         public IValueRegisterField DefineValueField(int position, int width, FieldMode mode = FieldMode.Read | FieldMode.Write, Action<ulong, ulong> readCallback = null,
560             Action<ulong, ulong> writeCallback = null, Action<ulong, ulong> changeCallback = null, Func<ulong, ulong> valueProviderCallback = null, bool softResettable = true,
561             string name = null)
562         {
563             ThrowIfRangeIllegal(position, width, name);
564             ThrowIfZeroWidth(position, width, name);
565             var field = new ValueRegisterField(this, position, width, mode, readCallback, writeCallback, changeCallback, valueProviderCallback, name);
566             registerFields.Add(field);
567             if(!softResettable)
568             {
569                 MarkNonResettable(position, width);
570             }
571             RecalculateFieldMask();
572             return field;
573         }
574 
575         /// <summary>
576         /// Defines the enum field. Its value is interpreted as an enumeration
577         /// </summary>
578         /// <param name="position">Offset in the register.</param>
579         /// <param name="width">Maximum width of the value, in terms of binary representation.</param>
580         /// <param name="mode">Access modifiers of this field.</param>
581         /// <param name="readCallback">Method to be called whenever the containing register is read. The first parameter is the value of this field before read,
582         /// the second parameter is the value after read. Note that it will also be called for unreadable fields.</param>
583         /// <param name="writeCallback">Method to be called whenever the containing register is written to. The first parameter is the value of this field before write,
584         /// the second parameter is the value written (without any modification). Note that it will also be called for unwrittable fields.</param>
585         /// <param name="changeCallback">Method to be called whenever this field's value is changed, either due to read or write. The first parameter is the value of this field before change,
586         /// the second parameter is the value after change. Note that it will also be called for unwrittable fields.</param>
587         /// <param name="valueProviderCallback">Method to be called whenever this field is read. The value passed is the current field's value, that will be overwritten by
588         /// the value returned from it. This returned value is eventually passed as the first parameter of <paramref name="readCallback"/>.</param>
589         /// <param name="softResettable">Indicates whether the field should be cleared by soft reset.</param>
590         /// <param name="name">Ignored parameter, for convenience. Treat it as a comment.</param>
591         public IEnumRegisterField<TEnum> DefineEnumField<TEnum>(int position, int width, FieldMode mode = FieldMode.Read | FieldMode.Write, Action<TEnum, TEnum> readCallback = null,
592             Action<TEnum, TEnum> writeCallback = null, Action<TEnum, TEnum> changeCallback = null, Func<TEnum, TEnum> valueProviderCallback = null, bool softResettable = true,
593             string name = null)
594             where TEnum : struct, IConvertible
595         {
ThrowIfRangeIllegalAntmicro.Renode.Core.Structure.Registers.PeripheralRegister.IConvertible596             ThrowIfRangeIllegal(position, width, name);
ThrowIfZeroWidthAntmicro.Renode.Core.Structure.Registers.PeripheralRegister.IConvertible597             ThrowIfZeroWidth(position, width, name);
598             var field = new EnumRegisterField<TEnum>(this, position, width, mode, readCallback, writeCallback, changeCallback, valueProviderCallback, name);
registerFields.AddAntmicro.Renode.Core.Structure.Registers.PeripheralRegister.IConvertible599             registerFields.Add(field);
600             if(!softResettable)
601             {
602                 MarkNonResettable(position, width);
603             }
RecalculateFieldMaskAntmicro.Renode.Core.Structure.Registers.PeripheralRegister.IConvertible604             RecalculateFieldMask();
605             return field;
606         }
607 
608         public int RegisterWidth { get; }
609 
PeripheralRegister(IPeripheral parent, ulong resetValue, bool softResettable, int width)610         protected PeripheralRegister(IPeripheral parent, ulong resetValue, bool softResettable, int width)
611         {
612             this.parent = parent;
613             RegisterWidth = width;
614             this.resetValue = resetValue;
615             // We want to reset the register before setting the resettableMask. If we don't do that then
616             // the register will not be initialized to the resetValue, instead it will hold the default value of 0
617             Reset();
618             if(!softResettable)
619             {
620                 resettableMask = 0;
621             }
622         }
623 
ReadInner()624         protected ulong ReadInner()
625         {
626             foreach(var registerField in registerFields)
627             {
628                 UnderlyingValue = registerField.CallValueProviderHandler(UnderlyingValue);
629             }
630             var baseValue = UnderlyingValue;
631             var valueToRead = UnderlyingValue;
632             var changedFields = new List<RegisterField>();
633             foreach(var registerField in registerFields)
634             {
635                 if(!registerField.fieldMode.IsReadable())
636                 {
637                     BitHelper.ClearBits(ref valueToRead, registerField.position, registerField.width);
638                 }
639 
640                 if(registerField.fieldMode.IsFlagSet(FieldMode.ReadToClear)
641                    && BitHelper.AreAnyBitsSet(UnderlyingValue, registerField.position, registerField.width))
642                 {
643                     BitHelper.ClearBits(ref UnderlyingValue, registerField.position, registerField.width);
644                     changedFields.Add(registerField);
645                 }
646                 if(registerField.fieldMode.IsFlagSet(FieldMode.ReadToSet)
647                    && !BitHelper.AreAllBitsSet(UnderlyingValue, registerField.position, registerField.width))
648                 {
649                     BitHelper.SetBits(ref UnderlyingValue, registerField.position, registerField.width);
650                     changedFields.Add(registerField);
651                 }
652             }
653             foreach(var registerField in registerFields)
654             {
655                 registerField.CallReadHandler(baseValue, UnderlyingValue);
656             }
657             foreach(var changedRegister in changedFields.Distinct())
658             {
659                 changedRegister.CallChangeHandler(baseValue, UnderlyingValue);
660             }
661 
662             CallReadHandlers(baseValue, UnderlyingValue);
663             if(changedFields.Any())
664             {
665                 CallChangeHandlers(baseValue, UnderlyingValue);
666             }
667 
668             return valueToRead;
669         }
670 
WriteInner(long offset, ulong value)671         protected void WriteInner(long offset, ulong value)
672         {
673             var baseValue = UnderlyingValue;
674             var difference = UnderlyingValue ^ value;
675             var changedRegisters = new List<RegisterField>();
676             foreach(var registerField in registerFields)
677             {
678                 //switch is OK, because write modes are exclusive.
679                 switch(registerField.fieldMode.WriteBits())
680                 {
681                     case FieldMode.Write:
682                         if(BitHelper.AreAnyBitsSet(difference, registerField.position, registerField.width))
683                         {
684                             BitHelper.UpdateWith(ref UnderlyingValue, value, registerField.position, registerField.width);
685                             changedRegisters.Add(registerField);
686                         }
687                         break;
688                     case FieldMode.Set:
689                         var setRegisters = value & (~UnderlyingValue);
690                         if(BitHelper.AreAnyBitsSet(setRegisters, registerField.position, registerField.width))
691                         {
692                             BitHelper.OrWith(ref UnderlyingValue, setRegisters, registerField.position, registerField.width);
693                             changedRegisters.Add(registerField);
694                         }
695                         break;
696                     case FieldMode.Toggle:
697                         if(BitHelper.AreAnyBitsSet(value, registerField.position, registerField.width))
698                         {
699                             BitHelper.XorWith(ref UnderlyingValue, value, registerField.position, registerField.width);
700                             changedRegisters.Add(registerField);
701                         }
702                         break;
703                     case FieldMode.WriteOneToClear:
704                         if(BitHelper.AreAnyBitsSet((~difference & value), registerField.position, registerField.width))
705                         {
706                             BitHelper.AndWithNot(ref UnderlyingValue, value, registerField.position, registerField.width);
707                             changedRegisters.Add(registerField);
708                         }
709                         break;
710                     case FieldMode.WriteZeroToClear:
711                         if(BitHelper.AreAnyBitsSet((difference & UnderlyingValue), registerField.position, registerField.width))
712                         {
713                             BitHelper.AndWithNot(ref UnderlyingValue, ~value, registerField.position, registerField.width);
714                             changedRegisters.Add(registerField);
715                         }
716                         break;
717                     case FieldMode.WriteZeroToSet:
718                         var negSetRegisters = ~value & (~UnderlyingValue);
719                         if(BitHelper.AreAnyBitsSet(negSetRegisters, registerField.position, registerField.width))
720                         {
721                             BitHelper.OrWith(ref UnderlyingValue, negSetRegisters, registerField.position, registerField.width);
722                             changedRegisters.Add(registerField);
723                         }
724                         break;
725                     case FieldMode.WriteZeroToToggle:
726                         if(BitHelper.AreAnyBitsSet(~value, registerField.position, registerField.width))
727                         {
728                             BitHelper.XorWith(ref UnderlyingValue, ~value, registerField.position, registerField.width);
729                             changedRegisters.Add(registerField);
730                         }
731                         break;
732                     case FieldMode.WriteToClear:
733                         if(BitHelper.AreAnyBitsSet(UnderlyingValue, registerField.position, registerField.width))
734                         {
735                             BitHelper.ClearBits(ref UnderlyingValue, registerField.position, registerField.width);
736                             changedRegisters.Add(registerField);
737                         }
738                         break;
739                 }
740             }
741             foreach(var registerField in registerFields)
742             {
743                 registerField.CallWriteHandler(baseValue, value);
744             }
745             foreach(var changedRegister in changedRegisters.Distinct())
746             {
747                 changedRegister.CallChangeHandler(baseValue, UnderlyingValue);
748             }
749 
750             CallWriteHandlers(baseValue, value);
751             if(changedRegisters.Any())
752             {
753                 CallChangeHandlers(baseValue, UnderlyingValue);
754             }
755 
756             var unhandledWrites = difference & ~definedFieldsMask;
757             if(unhandledWrites != 0)
758             {
759                 parent.Log(LogLevel.Warning, TagLogger(offset, unhandledWrites, value));
760             }
761 
762             if(InvalidTagValues(offset, value, out var invalidValueLog))
763             {
764                 parent.Log(LogLevel.Error, invalidValueLog);
765             }
766         }
767 
CallHandlers(List<Action<T, T>> handlers, T oldValue, T newValue)768         protected void CallHandlers<T>(List<Action<T, T>> handlers, T oldValue, T newValue)
769         {
770             foreach(var handler in handlers)
771             {
772                 handler(oldValue, newValue);
773             }
774         }
775 
CallWriteHandlers(ulong oldValue, ulong newValue)776         protected abstract void CallWriteHandlers(ulong oldValue, ulong newValue);
CallReadHandlers(ulong oldValue, ulong newValue)777         protected abstract void CallReadHandlers(ulong oldValue, ulong newValue);
CallChangeHandlers(ulong oldValue, ulong newValue)778         protected abstract void CallChangeHandlers(ulong oldValue, ulong newValue);
779 
780 
781         protected ulong UnderlyingValue;
782 
783         /// <summary>
784         /// Returns information about tag writes. Extracted as a method to allow future lazy evaluation.
785         /// </summary>
786         /// <param name="offset">The offset of the affected register.</param>
787         /// <param name="unhandledMask">Unhandled bits mask.</param>
788         /// <param name="originalValue">The whole value written to the register.</param>
TagLogger(long offset, ulong unhandledMask, ulong originalValue)789         private string TagLogger(long offset, ulong unhandledMask, ulong originalValue)
790         {
791             var tagsAffected = tags.Where(x => BitHelper.AreAnyBitsSet(unhandledMask, x.Position, x.Width))
792                 .Select(x =>  new { x.Name, Value = BitHelper.GetValue(originalValue, x.Position, x.Width) });
793             return "Unhandled write to offset 0x{2:X}. Unhandled bits: [{1}] when writing value 0x{3:X}.{0}"
794                 .FormatWith(tagsAffected.Any() ? " Tags: {0}.".FormatWith(
795                     tagsAffected.Select(x => "{0} (0x{1:X})".FormatWith(x.Name, x.Value)).Stringify(", ")) : String.Empty,
796                     BitHelper.GetSetBitsPretty(unhandledMask),
797                     offset,
798                     originalValue);
799         }
800 
InvalidTagValues(long offset, ulong originalValue, out string log)801         private bool InvalidTagValues(long offset, ulong originalValue, out string log)
802         {
803             ulong allowedValuesMask = 0;
804             ulong allowedValues = 0;
805 
806             foreach(var tag in tags.Where(x => x.AllowedValue != null))
807             {
808                 allowedValuesMask |= BitHelper.CalculateQuadWordMask(tag.Width, tag.Position);
809                 allowedValues |= tag.AllowedValue.Value << tag.Position;
810             }
811 
812             var invalidBits = (allowedValues ^ originalValue) & allowedValuesMask;
813             if(invalidBits == 0)
814             {
815                 log = "";
816                 return false;
817             }
818 
819             var writtenValue = "0b" + Convert.ToString((long)originalValue, 2).PadLeft(RegisterWidth, '0');
820             var desiredValues = "0b";
821 
822             for(int i = RegisterWidth - 1; i >= 0; i--)
823             {
824                 if(((allowedValuesMask >> i) & 1u) == 1)
825                 {
826                     desiredValues += ((allowedValues >> i) & 1) == 0 ? "0" : "1";
827                 }
828                 else
829                 {
830                     desiredValues += "x";
831                 }
832             }
833             log = $"Invalid value written to offset 0x{offset:X} reserved bits. Allowed values = {desiredValues}, Value written = {writtenValue}";
834             return true;
835         }
836 
837 
ThrowIfRangeIllegal(int position, int width, string name)838         private void ThrowIfRangeIllegal(int position, int width, string name)
839         {
840             if(width < 0)
841             {
842                 throw new ArgumentException("Field {0} has to have a size larger than or equal to 0.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
843             }
844             if(position + width > RegisterWidth)
845             {
846                 throw new ArgumentException("Field {0} does not fit in the register size.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
847             }
848             foreach(var field in registerFields.Select(x => new { x.position, x.width }).Concat(tags.Select(x => new { position = x.Position, width = x.Width })))
849             {
850                 var minEnd = Math.Min(position + width, field.position + field.width);
851                 var maxStart = Math.Max(position, field.position);
852                 if(minEnd > maxStart)
853                 {
854                     throw new ArgumentException("Field {0} intersects with another range.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
855                 }
856             }
857         }
858 
ThrowIfZeroWidth(int position, int width, string name)859         private void ThrowIfZeroWidth(int position, int width, string name)
860         {
861             if(width == 0)
862             {
863                 throw new ArgumentException("Field {0} has to have a size not equal to 0.".FormatWith(name ?? "at {0} of {1} bits".FormatWith(position, width)));
864             }
865         }
866 
ThrowIfAllowedValueDoesNotFitInWidth(int width, ulong allowedValue, string name)867         private void ThrowIfAllowedValueDoesNotFitInWidth(int width, ulong allowedValue, string name)
868         {
869             if((allowedValue >> width) != 0)
870             {
871                 throw new ArgumentException($"Fields {name} allowedValue does not fit in its width");
872             }
873         }
874 
RecalculateFieldMask()875         private void RecalculateFieldMask()
876         {
877             var mask = 0UL;
878             foreach(var field in registerFields)
879             {
880                 mask |= BitHelper.CalculateQuadWordMask(field.width, field.position);
881             }
882             definedFieldsMask = mask;
883         }
884 
MarkNonResettable(int position, int width)885         private void MarkNonResettable(int position, int width)
886         {
887             BitHelper.ClearBits(ref resettableMask, position, width);
888         }
889 
890         private List<RegisterField> registerFields = new List<RegisterField>();
891 
892         private List<Tag> tags = new List<Tag>();
893 
894         private IPeripheral parent;
895 
896         private ulong definedFieldsMask;
897 
898         private ulong resettableMask = ulong.MaxValue;
899         private readonly ulong resetValue;
900     }
901 }
902