1 // 2 // Copyright (c) 2010-2021 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 Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Miscellaneous; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class NRF52840_Timer : BasicDoubleWordPeripheral, IKnownSize, INRFEventProvider 17 { NRF52840_Timer(IMachine machine, int numberOfEvents)18 public NRF52840_Timer(IMachine machine, int numberOfEvents) : base(machine) 19 { 20 IRQ = new GPIO(); 21 22 if(numberOfEvents > MaxNumberOfEvents) 23 { 24 throw new ConstructionException($"Cannot create {nameof(NRF52840_Timer)} with {numberOfEvents} events (must be less than {MaxNumberOfEvents})"); 25 } 26 this.numberOfEvents = numberOfEvents; 27 28 this.eventCompareEnabled = new IFlagRegisterField[numberOfEvents]; 29 innerTimers = new ComparingTimer[numberOfEvents]; 30 for(var i = 0u; i < innerTimers.Length; i++) 31 { 32 var j = i; 33 innerTimers[j] = new ComparingTimer(machine.ClockSource, InitialFrequency, this, $"compare{j}", eventEnabled: true); 34 innerTimers[j].CompareReached += () => 35 { 36 this.Log(LogLevel.Noisy, "Compare Reached on CC{0} is {1}", j, innerTimers[j].Compare); 37 eventCompareEnabled[j].Value = true; 38 EventTriggered?.Invoke((uint)Register.Compare0EventPending + 0x4u * j); 39 UpdateInterrupts(); 40 }; 41 } 42 43 DefineRegisters(); 44 } 45 Reset()46 public override void Reset() 47 { 48 base.Reset(); 49 50 foreach(var timer in innerTimers) 51 { 52 timer.Reset(); 53 } 54 55 IRQ.Unset(); 56 } 57 58 public GPIO IRQ { get; } 59 60 public long Size => 0x1000; 61 62 public event Action<uint> EventTriggered; 63 DefineRegisters()64 private void DefineRegisters() 65 { 66 Register.Start.Define(this) 67 .WithFlag(0, FieldMode.Write, name: "TASKS_START", writeCallback: (_, value) => 68 { 69 if(value) 70 { 71 timerRunning = true; 72 if(mode.Value == Mode.Timer) 73 { 74 foreach(var timer in innerTimers) 75 { 76 timer.Enabled = true; 77 } 78 } 79 } 80 }) 81 .WithReservedBits(1, 31) 82 ; 83 84 Register.Stop.Define(this) 85 .WithFlag(0, FieldMode.Write, name: "TASKS_STOP", writeCallback: (_, value) => 86 { 87 if(value) 88 { 89 timerRunning = false; 90 foreach(var timer in innerTimers) 91 { 92 timer.Enabled = false; 93 } 94 } 95 }) 96 .WithReservedBits(1, 31) 97 ; 98 99 Register.Count.Define(this) 100 .WithFlag(0, FieldMode.Write, name: "TASK_COUNT", writeCallback: (_, value) => 101 { 102 if(value) 103 { 104 if(!timerRunning) 105 { 106 this.Log(LogLevel.Warning, "Triggered TASK_COUNT before issuing TASK_START, ignoring..."); 107 return; 108 } 109 if(mode.Value == Mode.Timer) 110 { 111 this.Log(LogLevel.Warning, "Triggered TASK_COUNT in TIMER mode, ignoring..."); 112 return; 113 } 114 var i = 0; 115 foreach(var timer in innerTimers) 116 { 117 timer.Value++; 118 if(timer.Compare == timer.Value) 119 { 120 eventCompareEnabled[i].Value = true; 121 UpdateInterrupts(); 122 } 123 i++; 124 } 125 } 126 }) 127 .WithReservedBits(1, 31) 128 ; 129 130 Register.Clear.Define(this) 131 .WithFlag(0, FieldMode.Write, name: "TASK_CLEAR", writeCallback: (_, value) => 132 { 133 if(value) 134 { 135 foreach(var timer in innerTimers) 136 { 137 timer.Value = 0; 138 } 139 } 140 }) 141 .WithReservedBits(1, 31) 142 ; 143 144 Register.Capture0.DefineMany(this, (uint)numberOfEvents, setup: (register, idx) => 145 { 146 register 147 .WithFlag(0, FieldMode.Write, name: "TASKS_CAPTURE", writeCallback: (_,__) => 148 { 149 SetCompare(idx, innerTimers[idx].Value); 150 }) 151 .WithReservedBits(1, 31); 152 }); 153 154 Register.Compare0EventPending.DefineMany(this, (uint)numberOfEvents, setup: (register, idx) => 155 { 156 register 157 .WithFlag(0, out eventCompareEnabled[idx], name: $"EVENTS_COMPARE[{idx}]", writeCallback: (_,__) => 158 { 159 UpdateInterrupts(); 160 }) 161 .WithReservedBits(1, 31); 162 }); 163 164 Register.InterruptEnableSet.Define(this) 165 .WithReservedBits(0, 16) 166 .WithFlags(16, numberOfEvents, out eventCompareInterruptEnabled, FieldMode.Set | FieldMode.Read, name: "COMPARE") 167 .WithReservedBits(22 - MaxNumberOfEvents + numberOfEvents, 10 + MaxNumberOfEvents - numberOfEvents) 168 .WithChangeCallback((_, __) => 169 { 170 UpdateInterrupts(); 171 }) 172 ; 173 174 Register.InterruptEnableClear.Define(this) 175 .WithReservedBits(0, 16) 176 .WithFlags(16, numberOfEvents, name: "COMPARE", writeCallback: (i, _, value) => { if(value) eventCompareInterruptEnabled[i].Value = false; }) 177 .WithReservedBits(22 - MaxNumberOfEvents + numberOfEvents, 10 + MaxNumberOfEvents - numberOfEvents) 178 .WithChangeCallback((_, __) => 179 { 180 UpdateInterrupts(); 181 }) 182 ; 183 184 Register.Mode.Define(this) 185 .WithEnumField(0, 2, out mode, name: "MODE", changeCallback: (_, value) => 186 { 187 if(value != Mode.Timer && innerTimers[0].Enabled) 188 { 189 this.Log(LogLevel.Error, "Switching timer to COUNTER mode while the timer is running"); 190 } 191 }) 192 ; 193 194 Register.Prescaler.Define(this) 195 .WithValueField(0, 4, out prescaler, name: "PRESCALER", writeCallback: (_, value) => 196 { 197 foreach(var timer in innerTimers) 198 { 199 timer.Divider = (uint)(1 << (int)value); 200 } 201 }) 202 .WithReservedBits(12, 20) 203 ; 204 205 Register.Compare0.DefineMany(this, (uint)numberOfEvents, setup: (register, idx) => 206 { 207 register 208 .WithValueField(0, 32, name: "CAPTURE_COMPARE", writeCallback: (_, value) => 209 { 210 SetCompare(idx, value); 211 }, 212 valueProviderCallback: _ => 213 { 214 return (uint)innerTimers[idx].Compare; 215 }); 216 }); 217 } 218 SetCompare(int idx, ulong value)219 private void SetCompare(int idx, ulong value) 220 { 221 eventCompareEnabled[idx].Value = false; 222 UpdateInterrupts(); 223 innerTimers[idx].Compare = value; 224 } 225 UpdateInterrupts()226 private void UpdateInterrupts() 227 { 228 var flag = false; 229 230 for(var i = 0; i < numberOfEvents; i++) 231 { 232 flag |= eventCompareInterruptEnabled[i].Value && eventCompareEnabled[i].Value; 233 } 234 235 IRQ.Set(flag); 236 } 237 238 private IFlagRegisterField[] eventCompareEnabled; 239 private IFlagRegisterField[] eventCompareInterruptEnabled; 240 private IValueRegisterField prescaler; 241 private IEnumRegisterField<Mode> mode; 242 private bool timerRunning; 243 244 private readonly ComparingTimer[] innerTimers; 245 246 private readonly int numberOfEvents; 247 private const int InitialFrequency = 16000000; 248 private const int MaxNumberOfEvents = 6; 249 250 private enum Mode 251 { 252 Timer, 253 Counter, 254 LowPowerCounter 255 } 256 257 private enum Register : long 258 { 259 Start = 0x000, 260 Stop = 0x004, 261 Count = 0x008, 262 Clear = 0x00C, 263 Shutdown = 0x010, 264 265 Capture0 = 0x040, 266 Capture1 = 0x044, 267 Capture2 = 0x048, 268 Capture3 = 0x04C, 269 Capture4 = 0x050, 270 Capture5 = 0x054, 271 272 Compare0EventPending = 0x140, 273 Compare1EventPending = 0x144, 274 Compare2EventPending = 0x148, 275 Compare3EventPending = 0x14C, 276 Compare4EventPending = 0x150, 277 Compare5EventPending = 0x154, 278 279 Shortcuts = 0x200, 280 281 InterruptEnableSet = 0x304, 282 InterruptEnableClear = 0x308, 283 284 Mode = 0x504, 285 BitMode = 0x508, 286 Prescaler = 0x510, 287 288 Compare0 = 0x540, 289 Compare1 = 0x544, 290 Compare2 = 0x548, 291 Compare3 = 0x54C, 292 Compare4 = 0x550, 293 Compare5 = 0x554 294 } 295 } 296 } 297