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 NUnit.Framework;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using System.Collections.Generic;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.UnitTests
16 {
17     [TestFixture]
18     public class RegistersTests
19     {
20         [Test]
ShouldNotAcceptOutOfBoundsValues()21         public void ShouldNotAcceptOutOfBoundsValues()
22         {
23             Assert.Catch<ConstructionException>(() => enumRWField.Value = (TwoBitEnum)(1 << 2));
24             Assert.Catch<ConstructionException>(() => valueRWField.Value = (1 << 4));
25         }
26 
27         [Test]
ShouldNotAcceptNegativeFields()28         public void ShouldNotAcceptNegativeFields()
29         {
30             var localRegister = new DoubleWordRegister(null);
31             Assert.Catch<ArgumentException>(() => localRegister.DefineEnumField<TwoBitEnum>(0, -1));
32             Assert.Catch<ArgumentException>(() => localRegister.DefineValueField(0, -1));
33         }
34 
35         [Test]
ShouldNotExceedRegisterSize()36         public void ShouldNotExceedRegisterSize()
37         {
38             var registersAndPositions = new Dictionary<PeripheralRegister, int>
39             {
40                 { new QuadWordRegister(null), 63 },
41                 { new DoubleWordRegister(null), 31 },
42                 { new WordRegister(null), 15 },
43                 { new ByteRegister(null), 7 }
44             };
45             foreach(var registerAndPosition in registersAndPositions)
46             {
47                 var localRegister = registerAndPosition.Key;
48                 Assert.Catch<ArgumentException>(() => localRegister.DefineEnumField<TwoBitEnum>(registerAndPosition.Value, 2));
49             }
50         }
51 
52         [Test]
ShouldNotAllowIntersectingFields()53         public void ShouldNotAllowIntersectingFields()
54         {
55             var localRegister = new QuadWordRegister(null);
56             localRegister.DefineValueField(1, 5);
57             Assert.Catch<ArgumentException>(() => localRegister.DefineValueField(0, 2));
58         }
59 
60         [Test]
ShouldWriteFieldsWithMaxLength()61         public void ShouldWriteFieldsWithMaxLength()
62         {
63             var localRegister = new DoubleWordRegister(null);
64             localRegister.DefineValueField(0, 32);
65             localRegister.Write(0, uint.MaxValue);
66             Assert.AreEqual(uint.MaxValue, localRegister.Read());
67         }
68 
69         [Test]
ShouldReadBoolField()70         public void ShouldReadBoolField()
71         {
72             register.Write(0, 1 << 2);
73             Assert.AreEqual(true, flagRWField.Value);
74         }
75 
76         [Test]
ShouldReadEnumField()77         public void ShouldReadEnumField()
78         {
79             register.Write(0, 3);
80             Assert.AreEqual(TwoBitEnum.D, enumRWField.Value);
81         }
82 
83         [Test]
ShouldRead64BitWideEnum()84         public void ShouldRead64BitWideEnum()
85         {
86             var localRegister = new QuadWordRegister(null);
87             var localEnumField = localRegister.DefineEnumField<SixtyFourBitEnum>(0, 64);
88 
89             localRegister.Write(0, ulong.MaxValue);
90             Assert.AreEqual(SixtyFourBitEnum.B, localEnumField.Value);
91         }
92 
93         [Test]
ShouldReadValueField()94         public void ShouldReadValueField()
95         {
96             register.Write(0, 88); //1011000
97             Assert.AreEqual(11, valueRWField.Value); //1011
98         }
99 
100         [Test]
ShouldWriteBoolField()101         public void ShouldWriteBoolField()
102         {
103             flagRWField.Value = true;
104             Assert.AreEqual(1 << 2 | RegisterResetValue, register.Read());
105         }
106 
107         [Test]
ShouldWriteEnumField()108         public void ShouldWriteEnumField()
109         {
110             enumRWField.Value = TwoBitEnum.D;
111             Assert.AreEqual((uint)TwoBitEnum.D | RegisterResetValue, register.Read());
112         }
113 
114         [Test]
ShouldWrite64BitWideEnum()115         public void ShouldWrite64BitWideEnum()
116         {
117             var localRegister = new QuadWordRegister(null);
118             var localEnumField = localRegister.DefineEnumField<SixtyFourBitEnum>(0, 64);
119 
120             localEnumField.Value = SixtyFourBitEnum.A;
121             Assert.AreEqual((ulong)SixtyFourBitEnum.A, localRegister.Read());
122         }
123 
124         [Test]
ShouldWriteValueField()125         public void ShouldWriteValueField()
126         {
127             valueRWField.Value = 11;
128             Assert.AreEqual(88 | RegisterResetValue, register.Read());
129         }
130 
131         [Test]
ShouldResetComplexRegister()132         public void ShouldResetComplexRegister()
133         {
134             register.Reset();
135             Assert.AreEqual(0x3780, register.Read());
136         }
137 
138         [Test]
ShouldNotWriteUnwritableField()139         public void ShouldNotWriteUnwritableField()
140         {
141             register.Write(0, 1 << 21);
142             Assert.AreEqual(false, flagRField.Value);
143         }
144 
145         [Test]
ShouldNotReadUnreadableField()146         public void ShouldNotReadUnreadableField()
147         {
148             flagWField.Value = true;
149             Assert.AreEqual(RegisterResetValue, register.Read());
150         }
151 
152         [Test]
ShouldWriteZeroToClear()153         public void ShouldWriteZeroToClear()
154         {
155             flagW0CField.Value = true;
156             Assert.AreEqual(1 << 18 | RegisterResetValue, register.Read());
157             register.Write(0, 0);
158             Assert.AreEqual(false, flagW0CField.Value);
159             Assert.AreEqual(0, register.Read() & readMask);
160         }
161 
162         [Test]
ShouldWriteOneToClear()163         public void ShouldWriteOneToClear()
164         {
165             flagW1CField.Value = true;
166             Assert.AreEqual(1 << 17 | RegisterResetValue, register.Read() & readMask);
167             register.Write(0, 1 << 17);
168             Assert.AreEqual(false, flagW0CField.Value);
169             Assert.AreEqual(0, register.Read() & readMask);
170         }
171 
172         [Test]
ShouldWriteToClear()173         public void ShouldWriteToClear()
174         {
175             flagWTCField.Value = true;
176             Assert.AreEqual(1u << 31 | RegisterResetValue, register.Read());
177             register.Write(0, 1u << 31);
178             Assert.IsFalse(flagWTCField.Value);
179 
180             flagWTCField.Value = true;
181             register.Write(0, 0);
182             Assert.IsFalse(flagWTCField.Value);
183         }
184 
185         [Test]
ShouldReadToClear()186         public void ShouldReadToClear()
187         {
188             flagWRTCField.Value = true;
189             Assert.AreEqual(1 << 19 | RegisterResetValue, register.Read());
190             Assert.AreEqual(false, flagWRTCField.Value);
191         }
192 
193         [Test]
ShouldWriteZeroToSet()194         public void ShouldWriteZeroToSet()
195         {
196             flagWZTSField.Value = false;
197 
198             register.Write(0, 1 << 0x1c);
199             Assert.IsFalse(flagWZTSField.Value);
200 
201             register.Write(0, 0);
202             Assert.IsTrue(flagWZTSField.Value);
203 
204             register.Write(0, 0);
205             Assert.IsTrue(flagWZTSField.Value);
206 
207             register.Write(0, 1 << 0x1c);
208             Assert.IsTrue(flagWZTSField.Value);
209         }
210 
211         [Test]
ShouldWriteZeroToToggle()212         public void ShouldWriteZeroToToggle()
213         {
214             flagWZTTField.Value = false;
215 
216             register.Write(0, 1 << 0x1d);
217             Assert.IsFalse(flagWZTTField.Value);
218 
219             register.Write(0, 0);
220             Assert.IsTrue(flagWZTTField.Value);
221 
222             register.Write(0, 1 << 0x1d);
223             Assert.IsTrue(flagWZTTField.Value);
224 
225             register.Write(0, 0);
226             Assert.IsFalse(flagWZTTField.Value);
227         }
228 
229         [Test]
ShouldReadToSet()230         public void ShouldReadToSet()
231         {
232             flagRTSField.Value = false;
233             Assert.AreEqual(~(1UL << 0x1e) & RegisterResetValue, register.Read());
234             Assert.IsTrue(flagRTSField.Value);
235         }
236 
237         [Test]
ShouldCallReadHandler()238         public void ShouldCallReadHandler()
239         {
240             //for the sake of sanity
241             Assert.AreEqual(0, enumCallbacks);
242             Assert.AreEqual(0, boolCallbacks);
243             Assert.AreEqual(0, numberCallbacks);
244             register.Read();
245             Assert.AreEqual(1, enumCallbacks);
246             Assert.AreEqual(1, boolCallbacks);
247             Assert.AreEqual(1, numberCallbacks);
248 
249             Assert.IsTrue(oldBoolValue == newBoolValue);
250             Assert.IsTrue(oldEnumValue == newEnumValue);
251             Assert.IsTrue(oldNumberValue == newNumberValue);
252         }
253 
254         [Test]
ShouldRetrieveValueFromHandler()255         public void ShouldRetrieveValueFromHandler()
256         {
257             enableValueProviders = true;
258             register.Write(0, 3 << 0x16 | 1 << 0x19);
259             Assert.AreEqual(4 << 0x16 | 1 << 0x1A, register.Read());
260         }
261 
262         [Test]
ShouldCallWriteAndChangeHandler()263         public void ShouldCallWriteAndChangeHandler()
264         {
265             Assert.AreEqual(0, enumCallbacks);
266             Assert.AreEqual(0, boolCallbacks);
267             Assert.AreEqual(0, numberCallbacks);
268             register.Write(0, 0x2A80);
269             //Two calls for changed registers, 1 call for unchanged register
270             Assert.AreEqual(2, enumCallbacks);
271             Assert.AreEqual(1, boolCallbacks);
272             Assert.AreEqual(2, numberCallbacks);
273 
274             Assert.IsTrue(oldBoolValue == newBoolValue);
275             Assert.IsTrue(oldEnumValue == TwoBitEnum.D && newEnumValue == TwoBitEnum.B);
276             Assert.IsTrue(oldNumberValue == 13);
277             Assert.IsTrue(newNumberValue == 10);
278         }
279 
280         [Test]
ShouldCallGlobalReadHandler()281         public void ShouldCallGlobalReadHandler()
282         {
283             flagRTSField.Value = true; // prevent change callback from being fired
284             Assert.AreEqual(0, globalCallbacks);
285             register.Read();
286             Assert.AreEqual(1, globalCallbacks);
287             Assert.AreEqual(RegisterResetValue & readMask, oldGlobalValue & readMask);
288             Assert.AreEqual(RegisterResetValue & readMask, newGlobalValue & readMask);
289         }
290 
291         [Test]
ShouldCallGlobalWriteAndChangeHandler()292         public void ShouldCallGlobalWriteAndChangeHandler()
293         {
294             register.Reset();
295             Assert.AreEqual(0, globalCallbacks);
296             register.Write(0, 0x2A80 & writeMask);
297             //1 for write, 1 for change
298             Assert.AreEqual(2, globalCallbacks);
299 
300             Assert.AreEqual(RegisterResetValue, oldGlobalValue);
301             Assert.AreEqual(0x2A80 & writeMask, newGlobalValue & writeMask);
302         }
303 
304         [Test]
ShouldWorkWithUndefinedEnumValue()305         public void ShouldWorkWithUndefinedEnumValue()
306         {
307             register.Write(0, 2);
308             Assert.AreEqual((TwoBitEnum)2, enumRWField.Value);
309         }
310 
311         [Test]
ShouldToggleField()312         public void ShouldToggleField()
313         {
314             register.Write(0, 1 << 15);
315             Assert.AreEqual(true, flagTRField.Value);
316             register.Write(0, 1 << 15);
317             Assert.AreEqual(false, flagTRField.Value);
318             register.Write(0, 1 << 15);
319             Assert.AreEqual(true, flagTRField.Value);
320         }
321 
322         [Test]
ShouldSetField()323         public void ShouldSetField()
324         {
325             register.Write(0, 1 << 16);
326             Assert.AreEqual(true, flagSRField.Value);
327             register.Write(0, 0);
328             Assert.AreEqual(true, flagSRField.Value);
329         }
330 
331         [Test]
ShouldHandle64BitWideRegistersProperly()332         public void ShouldHandle64BitWideRegistersProperly()
333         {
334             ulong test = 0;
335             new QuadWordRegister(null, 0).WithValueField(0, 64, writeCallback: (oldValue, newValue) => test = newValue).Write(0x0, 0xDEADBEEF01234567UL);
336             Assert.AreEqual(0xDEADBEEF01234567UL, test);
337         }
338 
339         [Test]
ShouldHandle32BitWideRegistersProperly()340         public void ShouldHandle32BitWideRegistersProperly()
341         {
342             uint test = 0;
343             new DoubleWordRegister(null, 0).WithValueField(0, 32, writeCallback: (oldValue, newValue) => test = (uint)newValue).Write(0x0, 0xDEADBEEF);
344             Assert.AreEqual(0xDEADBEEF, test);
345         }
346 
347         [SetUp]
SetUp()348         public void SetUp()
349         {
350             register = new QuadWordRegister(null, RegisterResetValue);
351             enumRWField = register.DefineEnumField<TwoBitEnum>(0, 2);
352             flagRWField = register.DefineFlagField(2);
353             valueRWField = register.DefineValueField(3, 4);
354             register.DefineEnumField<TwoBitEnum>(7, 2, readCallback: EnumCallback, writeCallback: EnumCallback, changeCallback: EnumCallback);
355             register.DefineFlagField(9, readCallback: BoolCallback, writeCallback: BoolCallback, changeCallback: BoolCallback);
356             register.DefineValueField(10, 4, readCallback: NumberCallback, writeCallback: NumberCallback, changeCallback: NumberCallback);
357             flagTRField = register.DefineFlagField(15, FieldMode.Read | FieldMode.Toggle);
358             flagSRField = register.DefineFlagField(16, FieldMode.Read | FieldMode.Set);
359             flagW1CField = register.DefineFlagField(17, FieldMode.Read | FieldMode.WriteOneToClear);
360             flagW0CField = register.DefineFlagField(18, FieldMode.Read | FieldMode.WriteZeroToClear);
361             flagWRTCField = register.DefineFlagField(19, FieldMode.ReadToClear | FieldMode.Write);
362             flagWField = register.DefineFlagField(20, FieldMode.Write);
363             flagRField = register.DefineFlagField(21, FieldMode.Read);
364             register.DefineValueField(22, 3, valueProviderCallback: ModifyingValueCallback);
365             register.DefineFlagField(25, valueProviderCallback: ModifyingFlagCallback);
366             register.DefineEnumField<TwoBitEnum>(26, 2, valueProviderCallback: ModifyingEnumCallback);
367             flagWZTSField = register.DefineFlagField(28, FieldMode.WriteZeroToSet);
368             flagWZTTField = register.DefineFlagField(29, FieldMode.WriteZeroToToggle);
369             flagRTSField = register.DefineFlagField(30, FieldMode.ReadToSet);
370             flagWTCField = register.DefineFlagField(31, FieldMode.Read | FieldMode.WriteToClear);
371 
372             register.WithReadCallback(GlobalCallback).WithWriteCallback(GlobalCallback).WithChangeCallback(GlobalCallback);
373 
374             enableValueProviders = false;
375 
376             enumCallbacks = 0;
377             boolCallbacks = 0;
378             numberCallbacks = 0;
379             globalCallbacks = 0;
380             oldBoolValue = false;
381             newBoolValue = false;
382             oldEnumValue = TwoBitEnum.A;
383             newEnumValue = TwoBitEnum.A;
384             oldNumberValue = 0;
385             newNumberValue = 0;
386             oldGlobalValue = 0;
387             newGlobalValue = 0;
388         }
389 
GlobalCallback(ulong oldValue, ulong newValue)390         private void GlobalCallback(ulong oldValue, ulong newValue)
391         {
392             globalCallbacks++;
393             oldGlobalValue = oldValue;
394             newGlobalValue = newValue;
395         }
396 
EnumCallback(TwoBitEnum oldValue, TwoBitEnum newValue)397         private void EnumCallback(TwoBitEnum oldValue, TwoBitEnum newValue)
398         {
399             enumCallbacks++;
400             oldEnumValue = oldValue;
401             newEnumValue = newValue;
402         }
403 
BoolCallback(bool oldValue, bool newValue)404         private void BoolCallback(bool oldValue, bool newValue)
405         {
406             boolCallbacks++;
407             oldBoolValue = oldValue;
408             newBoolValue = newValue;
409         }
410 
NumberCallback(ulong oldValue, ulong newValue)411         private void NumberCallback(ulong oldValue, ulong newValue)
412         {
413             numberCallbacks++;
414             oldNumberValue = oldValue;
415             newNumberValue = newValue;
416         }
417 
ModifyingValueCallback(ulong currentValue)418         private ulong ModifyingValueCallback(ulong currentValue)
419         {
420             if(enableValueProviders)
421             {
422                 return currentValue + 1;
423             }
424             return currentValue;
425         }
426 
ModifyingFlagCallback(bool currentValue)427         private bool ModifyingFlagCallback(bool currentValue)
428         {
429             if(enableValueProviders)
430             {
431                 return !currentValue;
432             }
433             return currentValue;
434         }
435 
ModifyingEnumCallback(TwoBitEnum currentValue)436         private TwoBitEnum ModifyingEnumCallback(TwoBitEnum currentValue)
437         {
438             if(enableValueProviders)
439             {
440                 return currentValue + 1;
441             }
442             return currentValue;
443         }
444 
445         /*Offset  |   Field
446           --------------------
447           0       |   Enum rw
448           1       |
449           --------------------
450           2       |   Bool rw
451           --------------------
452           3       |   Value rw
453           4       |
454           5       |
455           6       |
456           --------------------
457           7       |   Enum rw w/callbacks, reset value 3
458           8       |
459           --------------------
460           9       |   Bool rw w/callbacks, reset value 1
461           --------------------
462           A       |   Value rw w/callbacks, reset value 13
463           B       |
464           C       |
465           D       |
466           --------------------
467           F       |   Bool tr
468           --------------------
469           10      |   Bool sr
470           --------------------
471           11      |   Bool w1c
472           --------------------
473           12      |   Bool w0c
474           --------------------
475           13      |   Bool wrtc
476           --------------------
477           14      |   Bool w
478           --------------------
479           15      |   Bool r
480           --------------------
481           16      |   Value rw w/changing r callback
482           17      |
483           18      |
484           --------------------
485           19      |   Bool rw w/changing r callback
486           --------------------
487           1A      |   Enum rw w/changing r callback
488           1B      |
489           --------------------
490           1C      |   Bool wzts
491           --------------------
492           1D      |   Bool wztt
493           --------------------
494           1E      |   Bool rts
495           --------------------
496           1F      |   Bool wtc
497         */
498         private QuadWordRegister register;
499 
500         private int enumCallbacks;
501         private int boolCallbacks;
502         private int numberCallbacks;
503 
504         private int globalCallbacks;
505 
506         private TwoBitEnum oldEnumValue;
507         private TwoBitEnum newEnumValue;
508         private bool oldBoolValue;
509         private bool newBoolValue;
510         private ulong oldNumberValue;
511         private ulong newNumberValue;
512         private ulong oldGlobalValue;
513         private ulong newGlobalValue;
514 
515         private bool enableValueProviders;
516 
517         private IEnumRegisterField<TwoBitEnum> enumRWField;
518         private IFlagRegisterField flagRWField;
519         private IValueRegisterField valueRWField;
520         private IFlagRegisterField flagTRField;
521         private IFlagRegisterField flagSRField;
522         private IFlagRegisterField flagW1CField;
523         private IFlagRegisterField flagW0CField;
524         private IFlagRegisterField flagWRTCField;
525         private IFlagRegisterField flagWField;
526         private IFlagRegisterField flagRField;
527         private IFlagRegisterField flagWZTSField;
528         private IFlagRegisterField flagWZTTField;
529         private IFlagRegisterField flagRTSField;
530         private IFlagRegisterField flagWTCField;
531 
532         // Bits that store written value
533         private ulong writeMask = 0b0000_1111_1101_1000__0111_1111_1111_1111;
534         // Bits that provide stored value
535         private ulong readMask =  0b0000_1111_1110_0111__1111_1111_1111_1111;
536         private const ulong RegisterResetValue = 0x3780u;
537 
538         private enum TwoBitEnum
539         {
540             A = 0,
541             B = 1,
542             D = 3
543         }
544 
545         private enum SixtyFourBitEnum : ulong
546         {
547             A = 0x0123456789123456,
548             B = 0xFFFFFFFFFFFFFFFF
549         }
550     }
551 }
552