1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using Antmicro.Renode.Core; 8 using Antmicro.Renode.Logging; 9 using Antmicro.Renode.Time; 10 using System; 11 12 namespace Antmicro.Renode.Peripherals.Timers 13 { 14 public class EFR32_RTCCCounter 15 { EFR32_RTCCCounter(IMachine machine, long frequency, IPeripheral owner, string localName, int counterWidth = 32, int preCounterWidth = 32, int numberOfCaptureCompareChannels = 3)16 public EFR32_RTCCCounter(IMachine machine, long frequency, IPeripheral owner, string localName, int counterWidth = 32, int preCounterWidth = 32, int numberOfCaptureCompareChannels = 3) 17 { 18 var counterLimit = (1UL << counterWidth) - 1; 19 var preCounterLimit = (1UL << preCounterWidth) - 1; 20 21 coreTimer = new LimitTimer(machine.ClockSource, frequency, owner, localName, counterLimit, Direction.Ascending, eventEnabled: true, autoUpdate: true); 22 coreTimerTick = new LimitTimer(machine.ClockSource, frequency, owner, $"{localName}-tick", 1, Direction.Ascending, eventEnabled: true, autoUpdate: true); 23 preTimer = new LimitTimer(machine.ClockSource, frequency, owner, $"{localName}-pre", preCounterLimit, Direction.Ascending, eventEnabled: true, autoUpdate: true); 24 25 channels = new CCChannel[numberOfCaptureCompareChannels]; 26 for(var i = 0; i < numberOfCaptureCompareChannels; ++i) 27 { 28 var channelTimer = new LimitTimer(machine.ClockSource, frequency, owner, $"{localName}-cc{i}", counterLimit, Direction.Ascending, workMode: WorkMode.OneShot, eventEnabled: true, autoUpdate: true); 29 channels[i] = new CCChannel(channelTimer, coreTimer, owner, i); 30 } 31 this.machine = machine; 32 } 33 Reset()34 public void Reset() 35 { 36 coreTimer.Reset(); 37 coreTimerTick.Reset(); 38 preTimer.Reset(); 39 foreach(var channel in channels) 40 { 41 channel.Reset(); 42 } 43 } 44 45 public ulong Counter 46 { 47 get 48 { 49 TrySyncTime(); 50 return coreTimer.Value; 51 } 52 set 53 { 54 coreTimer.Value = value; 55 foreach(var channel in channels) 56 { 57 channel.Value = value; 58 } 59 } 60 } 61 62 public ulong PreCounter 63 { 64 get 65 { 66 TrySyncTime(); 67 return preTimer.Value; 68 } 69 set => preTimer.Value = value; 70 } 71 72 public bool Enabled 73 { 74 get => coreTimer.Enabled; 75 set 76 { 77 if(value == coreTimer.Enabled) 78 { 79 return; 80 } 81 coreTimer.Enabled = value; 82 coreTimerTick.Enabled = value; 83 preTimer.Enabled = value; 84 foreach(var channel in channels) 85 { 86 channel.Refresh(); 87 } 88 } 89 } 90 91 public int Prescaler 92 { 93 get => coreTimer.Divider; 94 set 95 { 96 if(value == coreTimer.Divider) 97 { 98 return; 99 } 100 coreTimer.Divider = value; 101 coreTimerTick.Divider = value; 102 foreach(var channel in channels) 103 { 104 channel.Divider = value; 105 } 106 } 107 } 108 109 public ICCChannel[] Channels => channels; 110 111 public event Action LimitReached 112 { 113 add => coreTimer.LimitReached += value; 114 remove => coreTimer.LimitReached -= value; 115 } 116 117 public event Action CounterTicked 118 { 119 add => coreTimerTick.LimitReached += value; 120 remove => coreTimerTick.LimitReached -= value; 121 } 122 TrySyncTime()123 private bool TrySyncTime() 124 { 125 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 126 { 127 cpu.SyncTime(); 128 return true; 129 } 130 return false; 131 } 132 133 private readonly CCChannel[] channels; 134 private readonly LimitTimer coreTimer; 135 private readonly LimitTimer coreTimerTick; 136 private readonly LimitTimer preTimer; 137 private readonly IMachine machine; 138 139 public enum CCChannelMode 140 { 141 Off = 0, 142 InputCapture = 1, 143 OutputCompare = 2, 144 } 145 146 public enum CCChannelComparisonBase 147 { 148 Counter = 0x0, 149 PreCounter = 0x1, 150 } 151 152 public interface ICCChannel 153 { 154 CCChannelMode Mode { get; set; } 155 CCChannelComparisonBase ComparisonBase { get; set; } 156 ulong CompareValue { get; set; } 157 event Action CompareReached; 158 } 159 160 private class CCChannel : ICCChannel 161 { CCChannel(LimitTimer ownTimer, LimitTimer coreTimer, IPeripheral owner, int id)162 public CCChannel(LimitTimer ownTimer, LimitTimer coreTimer, IPeripheral owner, int id) 163 { 164 this.channelTimer = ownTimer; 165 this.coreTimer = coreTimer; 166 this.owner = owner; 167 this.id = id; 168 } 169 Reset()170 public void Reset() 171 { 172 channelTimer.Reset(); 173 } 174 Refresh()175 public void Refresh() 176 { 177 var isEnabled = mode != CCChannelMode.Off; 178 var isTargetInThePast = CompareValue < coreTimer.Value; 179 var isAlreadyRunning = channelTimer.Enabled; 180 var shouldEnableTimer = isEnabled && !isTargetInThePast; 181 182 if(!isAlreadyRunning && shouldEnableTimer) 183 { 184 // Reload the counter value, so the individual timer used by the channel 185 // is synchronized with the core timer 186 channelTimer.Value = coreTimer.Value; 187 } 188 channelTimer.Enabled = shouldEnableTimer; 189 } 190 191 public CCChannelMode Mode 192 { 193 get => mode; 194 set 195 { 196 if(value == CCChannelMode.InputCapture) 197 { 198 owner.Log(LogLevel.Warning, "Attempt to set mode for Channel #{0} ignored. Input capture is not supported.", id); 199 return; 200 } 201 mode = value; 202 Refresh(); 203 } 204 } 205 206 public CCChannelComparisonBase ComparisonBase 207 { 208 get => comparisonBase; 209 set 210 { 211 if(value == CCChannelComparisonBase.PreCounter) 212 { 213 owner.Log(LogLevel.Warning, "Attempt to set comparison base for Channel #{0} ignored. Pre-counter is not supported.", id); 214 return; 215 } 216 comparisonBase = value; 217 Refresh(); 218 } 219 } 220 221 public ulong CompareValue 222 { 223 get => channelTimer.Limit; 224 set 225 { 226 // When changing the limit value, the value of the LimitTimer is reset 227 // Save and restore it, so changing the limit while the timer is running 228 // doesn't break the currently set alarm. 229 var oldCounter = channelTimer.Value; 230 channelTimer.Limit = value; 231 channelTimer.Value = oldCounter; 232 Refresh(); 233 } 234 } 235 236 public int Divider 237 { 238 set 239 { 240 channelTimer.Divider = value; 241 } 242 } 243 244 public ulong Value 245 { 246 set 247 { 248 channelTimer.Value = value; 249 Refresh(); 250 } 251 } 252 253 public event Action CompareReached 254 { 255 add => channelTimer.LimitReached += value; 256 remove => channelTimer.LimitReached -= value; 257 } 258 259 private CCChannelMode mode; 260 private CCChannelComparisonBase comparisonBase; 261 private readonly LimitTimer channelTimer; 262 private readonly LimitTimer coreTimer; 263 private readonly IPeripheral owner; 264 private readonly int id; 265 } 266 } 267 }