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