1 //
2 // Copyright (c) 2010-2022 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Threading;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Testing
16 {
17     public static class SysbusTesterExtensions
18     {
CreateSysbusTester(this IMachine machine, string name)19         public static void CreateSysbusTester(this IMachine machine, string name)
20         {
21             var tester = new SysbusTester(machine);
22             EmulationManager.Instance.CurrentEmulation.ExternalsManager.AddExternal(tester, name);
23         }
24     }
25 
26     public class SysbusTester : IExternal
27     {
SysbusTester(IMachine machine)28         public SysbusTester(IMachine machine)
29         {
30             this.machine = machine;
31         }
32 
ObserveAddress(ulong address)33         public void ObserveAddress(ulong address)
34         {
35             if(observedAddresses.Contains(address))
36             {
37                 return;
38             }
39 
40             machine.SystemBus.AddWatchpointHook(address, SysbusAccessWidth.Byte, Access.Write, (cpu, encounteredAddress, width, encounteredValue) =>
41             {
42                 this.Log(LogLevel.Noisy, "Registering byte write of 0x{0:X} at address 0x{1:X}", encounteredValue, encounteredAddress);
43                 InnerRegisterWrite(byteWrites, encounteredAddress, encounteredValue);
44             });
45 
46             machine.SystemBus.AddWatchpointHook(address, SysbusAccessWidth.Word, Access.Write, (cpu, encounteredAddress, width, encounteredValue) =>
47             {
48                 this.Log(LogLevel.Noisy, "Registering word write of 0x{0:X} at address 0x{1:X}", encounteredValue, encounteredAddress);
49                 InnerRegisterWrite(wordWrites, encounteredAddress, encounteredValue);
50             });
51 
52             machine.SystemBus.AddWatchpointHook(address, SysbusAccessWidth.DoubleWord, Access.Write, (cpu, encounteredAddress, width, encounteredValue) =>
53             {
54                 this.Log(LogLevel.Noisy, "Registering double word write of 0x{0:X} at address 0x{1:X}", encounteredValue, encounteredAddress);
55                 InnerRegisterWrite(doubleWordWrites, encounteredAddress, encounteredValue);
56             });
57 
58             machine.SystemBus.AddWatchpointHook(address, SysbusAccessWidth.QuadWord, Access.Write, (cpu, encounteredAddress, width, encounteredValue) =>
59             {
60                 this.Log(LogLevel.Noisy, "Registering quad word write of 0x{0:X} at address 0x{1:X}", encounteredValue, encounteredAddress);
61                 InnerRegisterWrite(quadWordWrites, encounteredAddress, encounteredValue);
62             });
63 
64             observedAddresses.Add(address);
65         }
66 
WaitForByteWrite(ulong address, byte expectedValue, float timeout)67         public void WaitForByteWrite(ulong address, byte expectedValue, float timeout)
68         {
69             InnerWaitForWrite(byteWrites, address, expectedValue, timeout);
70         }
71 
WaitForWordWrite(ulong address, uint expectedValue, float timeout)72         public void WaitForWordWrite(ulong address, uint expectedValue, float timeout)
73         {
74             InnerWaitForWrite(wordWrites, address, expectedValue, timeout);
75         }
76 
WaitForDoubleWordWrite(ulong address, uint expectedValue, float timeout)77         public void WaitForDoubleWordWrite(ulong address, uint expectedValue, float timeout)
78         {
79             InnerWaitForWrite(doubleWordWrites, address, expectedValue, timeout);
80         }
81 
WaitForQuadWordWrite(ulong address, uint expectedValue, float timeout)82         public void WaitForQuadWordWrite(ulong address, uint expectedValue, float timeout)
83         {
84             InnerWaitForWrite(quadWordWrites, address, expectedValue, timeout);
85         }
86 
InnerRegisterWrite(List<Tuple<ulong, ulong>> list, ulong offset, ulong value)87         private void InnerRegisterWrite(List<Tuple<ulong, ulong>> list, ulong offset, ulong value)
88         {
89             lock(list)
90             {
91                 list.Add(Tuple.Create(offset, value));
92             }
93             newWriteEvent.Set();
94         }
95 
InnerWaitForWrite(List<Tuple<ulong, ulong>> list, ulong offset, ulong value, float timeout)96         private void InnerWaitForWrite(List<Tuple<ulong, ulong>> list, ulong offset, ulong value, float timeout)
97         {
98             if(!observedAddresses.Contains(offset))
99             {
100                 throw new ArgumentException($"Address 0x{offset:X} is not currenlty observed. Did you forget to call 'ObserveAddress' before starting the emulation?");
101             }
102 
103             var timeoutEvent = machine.LocalTimeSource.EnqueueTimeoutEvent((ulong)(timeout * 1000));
104 
105             do
106             {
107                 lock(list)
108                 {
109                     for(var i = 0; i < list.Count; i++)
110                     {
111                         if(list[i].Item1 == offset && list[i].Item2 == value)
112                         {
113                             list.RemoveRange(0, i);
114                             return;
115                         }
116                     }
117                 }
118 
119                 WaitHandle.WaitAny(new [] { timeoutEvent.WaitHandle, newWriteEvent });
120             }
121             while(!timeoutEvent.IsTriggered);
122 
123             throw new InvalidOperationException("Sysbus write assertion not met.");
124         }
125 
126         private readonly IMachine machine;
127 
128         private readonly AutoResetEvent newWriteEvent = new AutoResetEvent(false);
129         private readonly HashSet<ulong> observedAddresses = new HashSet<ulong>();
130         private readonly List<Tuple<ulong, ulong>> byteWrites = new List<Tuple<ulong, ulong>>();
131         private readonly List<Tuple<ulong, ulong>> wordWrites = new List<Tuple<ulong, ulong>>();
132         private readonly List<Tuple<ulong, ulong>> doubleWordWrites = new List<Tuple<ulong, ulong>>();
133         private readonly List<Tuple<ulong, ulong>> quadWordWrites = new List<Tuple<ulong, ulong>>();
134     }
135 }
136