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.Exceptions;
10 using Antmicro.Renode.Utilities;
11 
12 namespace Antmicro.Renode.Core.Structure.Registers
13 {
14     public interface IRegisterField<T>
15     {
16         /// <summary>
17         /// Gets or sets the field's value. Access to this property does not invoke verification procedures in terms of FieldMode checking.
18         /// Also, it does not invoke callbacks.
19         /// </summary>
20         T Value { get; set; }
21 
22         /// <summary>
23         /// Gets the field's width in bits. It should be used to verify if the value assigned to <cref="Value"> is valid, as exceeding
24         /// the field's limits causes an ArgumentException.
25         /// </summary>
26         int Width { get; }
27 
28         Action<T, T> ReadCallback { get; set; }
29         Action<T, T> WriteCallback { get; set; }
30         Action<T, T> ChangeCallback { get; set; }
31         Func<T, T> ValueProviderCallback { get; set; }
32     }
33 
34     public partial class PeripheralRegister
35     {
36         private sealed class ValueRegisterField : RegisterField<ulong>, IValueRegisterField
37         {
ValueRegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action<ulong, ulong> readCallback, Action<ulong, ulong> writeCallback, Action<ulong, ulong> changeCallback, Func<ulong, ulong> valueProviderCallback, string name)38             public ValueRegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action<ulong, ulong> readCallback,
39                 Action<ulong, ulong> writeCallback, Action<ulong, ulong> changeCallback, Func<ulong, ulong> valueProviderCallback, string name)
40                 : base(parent, position, width, fieldMode, readCallback, writeCallback, changeCallback, valueProviderCallback, name)
41             {
42             }
43 
FromBinary(ulong value)44             protected override ulong FromBinary(ulong value)
45             {
46                 return value;
47             }
48 
ToBinary(ulong value)49             protected override ulong ToBinary(ulong value)
50             {
51                 return value;
52             }
53         }
54 
55         private sealed class EnumRegisterField<TEnum> : RegisterField<TEnum>, IEnumRegisterField<TEnum> where TEnum : struct, IConvertible
56         {
EnumRegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action<TEnum, TEnum> readCallback, Action<TEnum, TEnum> writeCallback, Action<TEnum, TEnum> changeCallback, Func<TEnum, TEnum> valueProviderCallback, string name)57             public EnumRegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action<TEnum, TEnum> readCallback,
58                 Action<TEnum, TEnum> writeCallback, Action<TEnum, TEnum> changeCallback, Func<TEnum, TEnum> valueProviderCallback, string name)
59                 : base(parent, position, width, fieldMode, readCallback, writeCallback, changeCallback, valueProviderCallback, name)
60             {
61             }
62 
FromBinary(ulong value)63             protected override TEnum FromBinary(ulong value)
64             {
65                 return EnumConverter<TEnum>.ToEnum(value);
66             }
67 
ToBinary(TEnum value)68             protected override ulong ToBinary(TEnum value)
69             {
70                 return EnumConverter<TEnum>.ToUInt64(value);
71             }
72         }
73 
74         private sealed class FlagRegisterField : RegisterField<bool>, IFlagRegisterField
75         {
FlagRegisterField(PeripheralRegister parent, int position, FieldMode fieldMode, Action<bool, bool> readCallback, Action<bool, bool> writeCallback, Action<bool, bool> changeCallback, Func<bool, bool> valueProviderCallback, string name)76             public FlagRegisterField(PeripheralRegister parent, int position, FieldMode fieldMode, Action<bool, bool> readCallback,
77                 Action<bool, bool> writeCallback, Action<bool, bool> changeCallback, Func<bool, bool> valueProviderCallback, string name)
78                 : base(parent, position, 1, fieldMode, readCallback, writeCallback, changeCallback, valueProviderCallback, name)
79             {
80             }
81 
FromBinary(ulong value)82             protected override bool FromBinary(ulong value)
83             {
84                 return value != 0;
85             }
86 
ToBinary(bool value)87             protected override ulong ToBinary(bool value)
88             {
89                 return value ? 1u : 0;
90             }
91         }
92 
93         private abstract class RegisterField<T> : RegisterField, IRegisterField<T>
94         {
95             public T Value
96             {
97                 get
98                 {
99                     return FromBinary(FilterValue(parent.UnderlyingValue));
100                 }
101                 set
102                 {
103                     ulong binary = ToBinary(value);
104                     if((binary >> width) > 0 && width < 64)
105                     {
106                         throw new ConstructionException("Value exceeds the size of the field.");
107                     }
108                     WriteFiltered(binary);
109                 }
110             }
111 
112             public int Width => width;
113 
114             public Action<T, T> ReadCallback { get; set; }
115             public Action<T, T> WriteCallback { get; set; }
116             public Action<T, T> ChangeCallback { get; set; }
117             public Func<T, T> ValueProviderCallback { get; set; }
118 
CallReadHandler(ulong oldValue, ulong newValue)119             public override void CallReadHandler(ulong oldValue, ulong newValue)
120             {
121                 if(ReadCallback != null)
122                 {
123                     var oldValueFiltered = FilterValue(oldValue);
124                     var newValueFiltered = FilterValue(newValue);
125                     ReadCallback(FromBinary(oldValueFiltered), FromBinary(newValueFiltered));
126                 }
127             }
128 
CallWriteHandler(ulong oldValue, ulong newValue)129             public override void CallWriteHandler(ulong oldValue, ulong newValue)
130             {
131                 if(WriteCallback != null)
132                 {
133                     var oldValueFiltered = FilterValue(oldValue);
134                     var newValueFiltered = FilterValue(newValue);
135                     WriteCallback(FromBinary(oldValueFiltered), FromBinary(newValueFiltered));
136                 }
137             }
138 
CallChangeHandler(ulong oldValue, ulong newValue)139             public override void CallChangeHandler(ulong oldValue, ulong newValue)
140             {
141                 if(ChangeCallback != null)
142                 {
143                     var oldValueFiltered = FilterValue(oldValue);
144                     var newValueFiltered = FilterValue(newValue);
145                     ChangeCallback(FromBinary(oldValueFiltered), FromBinary(newValueFiltered));
146                 }
147             }
148 
CallValueProviderHandler(ulong currentValue)149             public override ulong CallValueProviderHandler(ulong currentValue)
150             {
151                 if(ValueProviderCallback != null)
152                 {
153                     var currentValueFiltered = FilterValue(currentValue);
154                     return UnfilterValue(currentValue, ToBinary(ValueProviderCallback(FromBinary(currentValueFiltered))));
155                 }
156                 return currentValue;
157             }
158 
ToString()159             public override string ToString()
160             {
161                 return $"[RegisterType<{typeof(T).Name}> Value={Value} Width={Width}]";
162             }
163 
RegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action<T, T> readCallback, Action<T, T> writeCallback, Action<T, T> changeCallback, Func<T, T> valueProviderCallback, string name)164             protected RegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, Action<T, T> readCallback,
165                 Action<T, T> writeCallback, Action<T, T> changeCallback, Func<T, T> valueProviderCallback, string name) : base(parent, position, width, fieldMode, name)
166             {
167                 if(!fieldMode.IsReadable() && valueProviderCallback != null)
168                 {
169                     throw new ConstructionException($"A write-only field cannot provide a value callback.");
170                 }
171 
172                 ReadCallback = readCallback;
173                 WriteCallback = writeCallback;
174                 ChangeCallback = changeCallback;
175                 ValueProviderCallback = valueProviderCallback;
176             }
177 
FromBinary(ulong value)178             protected abstract T FromBinary(ulong value);
179 
ToBinary(T value)180             protected abstract ulong ToBinary(T value);
181         }
182 
183         private abstract class RegisterField
184         {
CallReadHandler(ulong oldValue, ulong newValue)185             public abstract void CallReadHandler(ulong oldValue, ulong newValue);
186 
CallWriteHandler(ulong oldValue, ulong newValue)187             public abstract void CallWriteHandler(ulong oldValue, ulong newValue);
188 
CallChangeHandler(ulong oldValue, ulong newValue)189             public abstract void CallChangeHandler(ulong oldValue, ulong newValue);
190 
CallValueProviderHandler(ulong currentValue)191             public abstract ulong CallValueProviderHandler(ulong currentValue);
192 
193             public readonly int position;
194             public readonly int width;
195             public readonly string name;
196             public readonly FieldMode fieldMode;
197 
RegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, string name)198             protected RegisterField(PeripheralRegister parent, int position, int width, FieldMode fieldMode, string name)
199             {
200                 if(!fieldMode.IsValid())
201                 {
202                     throw new ConstructionException("Invalid {0} flags for register field: {1}.".FormatWith(fieldMode.GetType().Name, fieldMode.ToString()));
203                 }
204                 this.parent = parent;
205                 this.position = position;
206                 this.fieldMode = fieldMode;
207                 this.width = width;
208                 this.name = name;
209             }
210 
FilterValue(ulong value)211             protected ulong FilterValue(ulong value)
212             {
213                 return BitHelper.GetValue(value, position, width);
214             }
215 
UnfilterValue(ulong baseValue, ulong fieldValue)216             protected ulong UnfilterValue(ulong baseValue, ulong fieldValue)
217             {
218                 BitHelper.UpdateWithShifted(ref baseValue, fieldValue, position, width);
219                 return baseValue;
220             }
221 
WriteFiltered(ulong value)222             protected void WriteFiltered(ulong value)
223             {
224                 BitHelper.UpdateWithShifted(ref parent.UnderlyingValue, value, position, width);
225             }
226 
227             protected readonly PeripheralRegister parent;
228         }
229     }
230 }
231