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 System; 8 using System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Peripherals.Miscellaneous; 15 16 namespace Antmicro.Renode.Peripherals.Timers 17 { 18 public class NRF52840_RTC : IDoubleWordPeripheral, IKnownSize, INRFEventProvider 19 { NRF52840_RTC(IMachine machine, int numberOfEvents)20 public NRF52840_RTC(IMachine machine, int numberOfEvents) 21 { 22 IRQ = new GPIO(); 23 24 if(numberOfEvents > MaxNumberOfEvents) 25 { 26 throw new ConstructionException($"Cannot create {nameof(NRF52840_Timer)} with {numberOfEvents} events (must be less than {MaxNumberOfEvents})"); 27 } 28 this.numberOfEvents = numberOfEvents; 29 compareEventEnabled = new IFlagRegisterField[numberOfEvents]; 30 compareReached = new IFlagRegisterField[numberOfEvents]; 31 compareInterruptEnabled = new IFlagRegisterField[numberOfEvents]; 32 33 innerTimers = new ComparingTimer[numberOfEvents]; 34 for(var i = 0u; i < innerTimers.Length; i++) 35 { 36 var j = i; 37 // counters are 24-bits 38 innerTimers[i] = new ComparingTimer(machine.ClockSource, InitialFrequency, this, $"compare{i}", eventEnabled: true, limit: 0xFFFFFF, compare: 0xFFFFFF); 39 innerTimers[i].CompareReached += () => 40 { 41 this.Log(LogLevel.Noisy, "IRQ #{0} triggered", j); 42 compareReached[j].Value = true; 43 if(compareEventEnabled[j].Value) 44 { 45 EventTriggered?.Invoke((uint)Register.Compare0EventPending + j * 4); 46 } 47 UpdateInterrupts(); 48 }; 49 } 50 51 tickTimer = new LimitTimer(machine.ClockSource, InitialFrequency, this, "tick", eventEnabled: true, limit: 0x1); 52 tickTimer.LimitReached += () => 53 { 54 tickEvent.Value = true; 55 UpdateInterrupts(); 56 }; 57 58 DefineRegisters(); 59 } 60 ReadDoubleWord(long offset)61 public uint ReadDoubleWord(long offset) 62 { 63 return registers.Read(offset); 64 } 65 WriteDoubleWord(long offset, uint value)66 public void WriteDoubleWord(long offset, uint value) 67 { 68 registers.Write(offset, value); 69 } 70 Reset()71 public void Reset() 72 { 73 registers.Reset(); 74 foreach(var timer in innerTimers) 75 { 76 timer.Reset(); 77 } 78 tickTimer.Reset(); 79 IRQ.Unset(); 80 } 81 82 public GPIO IRQ { get; } 83 84 public long Size => 0x1000; 85 86 public event Action<uint> EventTriggered; 87 88 private void UpdateTimersEnable(bool? global = null, bool? tick = null) 89 { 90 if(global.HasValue) 91 { 92 foreach(var timer in innerTimers) 93 { 94 timer.Enabled = global.Value; 95 } 96 } 97 98 // due to optimization reasons we try to keep 99 // the tick timer disabled as long as possible 100 // - we enable it only when the global timer is 101 // enabled and the tick event is unmasked 102 if(tick.HasValue || (global.HasValue && !global.Value)) 103 { 104 tickTimer.Enabled = innerTimers[0].Enabled && tick.Value; 105 } 106 } 107 DefineRegisters()108 private void DefineRegisters() 109 { 110 var registersMap = new Dictionary<long, DoubleWordRegister> 111 { 112 {(long)Register.Start, new DoubleWordRegister(this) 113 .WithFlag(0, FieldMode.Write, name: "TASKS_START", writeCallback: (_, value) => 114 { 115 if(value) 116 { 117 UpdateTimersEnable(global: true); 118 } 119 }) 120 .WithReservedBits(1, 31) 121 }, 122 {(long)Register.Stop, new DoubleWordRegister(this) 123 .WithFlag(0, FieldMode.Write, name: "TASKS_STOP", writeCallback: (_, value) => 124 { 125 if(value) 126 { 127 UpdateTimersEnable(global: false); 128 } 129 }) 130 .WithReservedBits(1, 31) 131 }, 132 {(long)Register.Clear, new DoubleWordRegister(this) 133 .WithFlag(0, FieldMode.Write, name: "TASKS_CLEAR", writeCallback: (_, value) => 134 { 135 if(value) 136 { 137 foreach(var timer in innerTimers) 138 { 139 timer.Value = 0; 140 } 141 tickTimer.Value = 0; 142 } 143 }) 144 .WithReservedBits(1, 31) 145 }, 146 {(long)Register.InterruptEnableSet, new DoubleWordRegister(this) 147 .WithFlag(0, out tickInterruptEnabled, FieldMode.Set | FieldMode.Read, name: "TICK") 148 .WithTaggedFlag("OVRFLW", 1) 149 .WithReservedBits(2, 14) 150 .WithFlags(16, numberOfEvents, out compareInterruptEnabled, FieldMode.Set | FieldMode.Read, name: "COMPARE") 151 .WithChangeCallback((_, __) => 152 { 153 UpdateTimersEnable(tick: tickInterruptEnabled.Value); 154 UpdateInterrupts(); 155 }) 156 }, 157 {(long)Register.InterruptEnableClear, new DoubleWordRegister(this) 158 .WithFlag(0, name: "TICK", 159 writeCallback: (_, value) => tickInterruptEnabled.Value &= !value, 160 valueProviderCallback: _ => tickInterruptEnabled.Value) 161 .WithTaggedFlag("OVRFLW", 1) 162 .WithReservedBits(2, 14) 163 .WithFlags(16, numberOfEvents, name: "COMPARE", 164 writeCallback: (j, _, value) => compareInterruptEnabled[j].Value &= !value, 165 valueProviderCallback: (j, value) => compareInterruptEnabled[j].Value) 166 //missing register fields defined below 167 .WithChangeCallback((_, __) => 168 { 169 UpdateTimersEnable(tick: tickInterruptEnabled.Value); 170 UpdateInterrupts(); 171 }) 172 }, 173 {(long)Register.Counter, new DoubleWordRegister(this) 174 .WithValueField(0, 24, FieldMode.Read, name: "COUNTER", valueProviderCallback: _ => 175 { 176 // all timers have the same value, so let's just pick the first one 177 return (uint)innerTimers[0].Value; 178 }) 179 .WithReservedBits(24, 8) 180 }, 181 {(long)Register.Prescaler, new DoubleWordRegister(this) 182 .WithValueField(0, 12, out prescaler, name: "PRESCALER", writeCallback: (_, value) => 183 { 184 foreach(var timer in innerTimers) 185 { 186 timer.Divider = (uint)(value + 1u); 187 } 188 tickTimer.Divider = (int)(value + 1); 189 }) 190 .WithReservedBits(12, 20) 191 }, 192 {(long)Register.EventEnable, new DoubleWordRegister(this) 193 .WithFlags(16, numberOfEvents, out compareEventEnabled, name: "COMPARE") 194 }, 195 {(long)Register.EventSet, new DoubleWordRegister(this) 196 .WithTaggedFlag("TICK", 0) 197 .WithTaggedFlag("OVRFLW", 1) 198 .WithReservedBits(2, 14) 199 .WithFlags(16, numberOfEvents, 200 writeCallback: (i, _, val) => compareEventEnabled[i].Value |= val, 201 valueProviderCallback: (i, _) => compareEventEnabled[i].Value) 202 }, 203 {(long)Register.EventClear, new DoubleWordRegister(this) 204 .WithFlags(16, numberOfEvents, 205 writeCallback: (i, _, val) => compareEventEnabled[i].Value &= !val, 206 valueProviderCallback: (i, _) => compareEventEnabled[i].Value) 207 }, 208 {(long)Register.Tick, new DoubleWordRegister(this) 209 .WithFlag(0, out tickEvent, name: "EVENTS_TICK") 210 .WithReservedBits(1, 31) 211 .WithWriteCallback((_, __) => UpdateInterrupts()) 212 } 213 }; 214 215 for(var i = 0; i < numberOfEvents; i++) 216 { 217 var j = i; 218 registersMap.Add((long)Register.Compare0 + j * 4, new DoubleWordRegister(this) 219 .WithValueField(0, 24, name: $"COMPARE[{j}]", writeCallback: (_, value) => 220 { 221 compareReached[j].Value = false; 222 UpdateInterrupts(); 223 innerTimers[j].Compare = value; 224 }, 225 valueProviderCallback: _ => 226 { 227 return (uint)innerTimers[j].Compare; 228 }) 229 .WithReservedBits(24, 8) 230 ); 231 232 registersMap.Add((long)Register.Compare0EventPending + j * 4, new DoubleWordRegister(this) 233 .WithFlag(0, out compareReached[j], name: $"EVENTS_COMPARE[{j}]", writeCallback: (_, __) => 234 { 235 UpdateInterrupts(); 236 }) 237 .WithReservedBits(1, 31) 238 ); 239 } 240 241 registers = new DoubleWordRegisterCollection(this, registersMap); 242 } 243 UpdateInterrupts()244 private void UpdateInterrupts() 245 { 246 var flag = false; 247 248 for(var i = 0; i < numberOfEvents; i++) 249 { 250 var thisEventSet = compareInterruptEnabled[i].Value && compareReached[i].Value; 251 if(thisEventSet) 252 { 253 this.Log(LogLevel.Noisy, "Interrupt set by CC{0} interruptEnable={1} compareSet={2} compareEventEnable={3}", 254 i, compareInterruptEnabled[i].Value, compareReached[i].Value, compareEventEnabled[i].Value); 255 } 256 flag |= thisEventSet; 257 } 258 259 flag |= tickEvent.Value && tickInterruptEnabled.Value; 260 IRQ.Set(flag); 261 } 262 263 private DoubleWordRegisterCollection registers; 264 private IFlagRegisterField[] compareEventEnabled; 265 private IFlagRegisterField[] compareReached; 266 private IFlagRegisterField[] compareInterruptEnabled; 267 private IValueRegisterField prescaler; 268 private IFlagRegisterField tickInterruptEnabled; 269 private IFlagRegisterField tickEvent; 270 271 private readonly LimitTimer tickTimer; 272 private readonly ComparingTimer[] innerTimers; 273 274 private readonly int numberOfEvents; 275 private const int InitialFrequency = 32768; 276 private const int MaxNumberOfEvents = 4; 277 278 private enum Register : long 279 { 280 Start = 0x000, 281 Stop = 0x004, 282 Clear = 0x008, 283 TriggerOverflow = 0x00C, 284 Tick = 0x100, 285 Overflow = 0x104, 286 Compare0EventPending = 0x140, 287 Compare1EventPending = 0x144, 288 Compare2EventPending = 0x148, 289 Compare3EventPending = 0x14C, 290 InterruptEnableSet = 0x304, 291 InterruptEnableClear = 0x308, 292 EventEnable = 0x340, 293 EventSet = 0x344, 294 EventClear = 0x348, 295 Counter = 0x504, 296 Prescaler = 0x508, 297 Compare0 = 0x540, 298 Compare1 = 0x544, 299 Compare2 = 0x548, 300 Compare3 = 0x54C 301 } 302 } 303 } 304