1 // 2 // Copyright (c) 2010-2024 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 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Extensions; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Time; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Timers 18 { 19 [AllowedTranslations(AllowedTranslation.WordToByte)] 20 public class SAMD21_Timer : IBytePeripheral, IDoubleWordPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IKnownSize 21 { SAMD21_Timer(IMachine machine, long baseFrequency)22 public SAMD21_Timer(IMachine machine, long baseFrequency) 23 { 24 RegistersCollection = new ByteRegisterCollection(this); 25 26 mainTimer = new LimitTimer(machine.ClockSource, baseFrequency, this, "clock", eventEnabled: true, direction: Direction.Ascending); 27 captureTimer0 = new LimitTimer(machine.ClockSource, baseFrequency, this, "capture0", eventEnabled: true, workMode: WorkMode.OneShot, direction: Direction.Ascending); 28 captureTimer1 = new LimitTimer(machine.ClockSource, baseFrequency, this, "capture1", eventEnabled: true, workMode: WorkMode.OneShot, direction: Direction.Ascending); 29 30 mainTimer.LimitReached += () => 31 { 32 interruptOverflowPending.Value = true; 33 34 UpdateInterrupts(); 35 StartChannels(); 36 }; 37 38 captureTimer0.LimitReached += () => 39 { 40 interruptChannel0Pending.Value = true; 41 UpdateInterrupts(); 42 }; 43 44 captureTimer1.LimitReached += () => 45 { 46 interruptChannel1Pending.Value = true; 47 UpdateInterrupts(); 48 }; 49 50 DefineRegisters(); 51 Reset(); 52 } 53 WriteDoubleWord(long offset, uint value)54 public void WriteDoubleWord(long offset, uint value) 55 { 56 if(offset < (long)Registers.Counter0) 57 { 58 this.WriteDoubleWordUsingByte(offset, value); 59 return; 60 } 61 62 long width = CounterWidth; 63 if(offset == (long)Registers.Counter0) 64 { 65 // NOTE: Counter 66 mainTimer.Value = value; 67 return; 68 } 69 70 if(offset == (long)Registers.ChannelCompareCaptureValue0_0) 71 { 72 // NOTE: Channel Compare Capture 0 value 73 compare0Value = value; 74 // NOTE: If correct mode is selected, we should update TOP value of the main timer 75 mainTimer.Limit = CounterTopValue; 76 StartChannels(); 77 return; 78 } 79 80 if(offset == (long)Registers.ChannelCompareCaptureValue0_0 + width) 81 { 82 // NOTE: Channel Compare Capture 1 value 83 compare1Value = value; 84 StartChannels(); 85 return; 86 } 87 88 this.Log(LogLevel.Warning, "Unhandled write on offset 0x{0:X}", offset); 89 } 90 ReadDoubleWord(long offset)91 public uint ReadDoubleWord(long offset) 92 { 93 if(offset < (long)Registers.Counter0) 94 { 95 return this.ReadDoubleWordUsingByte(offset); 96 } 97 98 long width = CounterWidth; 99 if(offset == (long)Registers.Counter0) 100 { 101 // NOTE: Counter 102 return (uint)mainTimer.Value; 103 } 104 105 if(offset == (long)Registers.ChannelCompareCaptureValue0_0) 106 { 107 // NOTE: Channel Compare Capture 0 value 108 return (uint)compare0Value; 109 } 110 111 if(offset == (long)Registers.ChannelCompareCaptureValue0_0 + width) 112 { 113 // NOTE: Channel Compare Capture 1 value 114 return (uint)compare1Value; 115 } 116 117 this.Log(LogLevel.Warning, "Unhandled read on offset 0x{0:X}", offset); 118 return 0; 119 } 120 WriteByte(long offset, byte value)121 public void WriteByte(long offset, byte value) 122 { 123 RegistersCollection.Write(offset, value); 124 } 125 ReadByte(long offset)126 public byte ReadByte(long offset) 127 { 128 return RegistersCollection.Read(offset); 129 } 130 Reset()131 public void Reset() 132 { 133 RegistersCollection.Reset(); 134 135 interruptOverflowEnabled = false; 136 interruptChannel0Enabled = false; 137 interruptChannel1Enabled = false; 138 139 UpdateInterrupts(); 140 141 Enabled = false; 142 Divider = 1; 143 144 compare0Value = 0; 145 compare1Value = 0; 146 } 147 148 public ByteRegisterCollection RegistersCollection { get; } 149 150 public GPIO IRQ { get; } = new GPIO(); 151 152 public long Size => 0x20; 153 154 public bool Enabled 155 { 156 get => mainTimer.Enabled; 157 set 158 { 159 mainTimer.Enabled = value; 160 captureTimer0.Enabled = value; 161 captureTimer1.Enabled = value; 162 } 163 } 164 165 public int Divider 166 { 167 get => mainTimer.Divider; 168 set 169 { 170 mainTimer.Divider = value; 171 captureTimer0.Divider = value; 172 captureTimer1.Divider = value; 173 } 174 } 175 UpdateInterrupts()176 private void UpdateInterrupts() 177 { 178 var interrupt = false; 179 interrupt |= interruptOverflowEnabled && interruptOverflowPending.Value; 180 interrupt |= interruptChannel0Enabled && interruptChannel0Pending.Value; 181 interrupt |= interruptChannel1Enabled && interruptChannel1Pending.Value; 182 183 this.Log(LogLevel.Debug, "Changed IRQ to {0}", IRQ.IsSet); 184 IRQ.Set(interrupt); 185 } 186 ReconfigureCounter()187 private void ReconfigureCounter() 188 { 189 mainTimer.Limit = CounterTopValue; 190 mainTimer.Value = mainTimer.Direction == Direction.Ascending ? 0 : CounterTopValue; 191 } 192 StartChannels()193 private void StartChannels() 194 { 195 captureTimer0.Value = mainTimer.Value; 196 captureTimer1.Value = mainTimer.Value; 197 198 if(mainTimer.Direction == Direction.Ascending) 199 { 200 captureTimer0.Limit = compare0Value; 201 captureTimer1.Limit = compare1Value; 202 } 203 else 204 { 205 var topValue = CounterTopValue; 206 207 captureTimer0.Limit = topValue - compare0Value; 208 captureTimer1.Limit = topValue - compare1Value; 209 } 210 211 captureTimer0.Enabled = captureTimer0.Limit > 0 && captureTimer0.Value <= captureTimer0.Limit; 212 captureTimer1.Enabled = captureTimer1.Limit > 0 && captureTimer1.Value <= captureTimer1.Limit; 213 } 214 DefineRegisters()215 private void DefineRegisters() 216 { 217 Registers.ControlA0.Define(this) 218 .WithFlag(0, FieldMode.WriteOneToClear, name: "SWRST", 219 writeCallback: (_, value) => { if(value) Reset(); }) 220 .WithFlag(1, name: "ENABLE", 221 valueProviderCallback: _ => Enabled, 222 writeCallback: (_, value) => Enabled = value) 223 .WithEnumField<ByteRegister, CounterMode>(2, 2, out counterMode, name: "MODE", 224 changeCallback: (_, __) => 225 { 226 if(counterMode.Value == CounterMode.Reserved) 227 { 228 this.Log(LogLevel.Warning, "MODE field has been set to {0} (Reserved); this will be treated as default (0)", (uint)counterMode.Value); 229 } 230 231 ReconfigureCounter(); 232 }) 233 .WithReservedBits(4, 1) 234 .WithEnumField(5, 2, out waveformGenerationOperation, name: "WAVEGEN", 235 changeCallback: (_, __) => ReconfigureCounter()) 236 .WithReservedBits(7, 1) 237 ; 238 239 Registers.ControlA1.Define(this) 240 .WithEnumField<ByteRegister, Prescaler>(0, 3, name: "PRESCALER", 241 writeCallback: (_, value) => Divider = GetPrescalerValue(value)) 242 .WithTaggedFlag("RUNSTDBY", 3) 243 .WithTag("PRESCSYNC", 4, 2) 244 .WithReservedBits(6, 2) 245 ; 246 247 Registers.ControlBSet.Define(this) 248 .WithFlag(0, name: "DIR", 249 valueProviderCallback: _ => mainTimer.Direction == Direction.Descending, 250 writeCallback: (_, value) => { if(value) mainTimer.Direction = Direction.Descending; }) 251 .WithFlag(2, name: "ONESHOT", 252 valueProviderCallback: _ => mainTimer.Mode == WorkMode.OneShot, 253 writeCallback: (_, value) => { if(value) mainTimer.Mode = WorkMode.OneShot; }) 254 .WithReservedBits(3, 3) 255 .WithEnumField<ByteRegister, Command>(6, 2, name: "CMD", 256 writeCallback: (_, value) => 257 { 258 switch(value) 259 { 260 case Command.Retrigger: 261 Enabled = true; 262 ReconfigureCounter(); 263 StartChannels(); 264 break; 265 case Command.Stop: 266 Enabled = false; 267 break; 268 default: 269 break; 270 } 271 }) 272 ; 273 274 Registers.ControlBClear.Define(this) 275 .WithFlag(0, name: "DIR", 276 valueProviderCallback: _ => mainTimer.Direction == Direction.Descending, 277 writeCallback: (_, value) => { if(value) mainTimer.Direction = Direction.Ascending; }) 278 .WithReservedBits(1, 1) 279 .WithFlag(2, name: "ONESHOT", 280 valueProviderCallback: _ => mainTimer.Mode == WorkMode.OneShot, 281 writeCallback: (_, value) => { if(value) mainTimer.Mode = WorkMode.Periodic; }) 282 .WithReservedBits(3, 3) 283 .WithTag("CMD", 6, 2) 284 ; 285 286 Registers.Status.Define(this) 287 .WithReservedBits(0, 3) 288 .WithFlag(3, FieldMode.Read, name: "STOP", 289 valueProviderCallback: _ => !Enabled) 290 .WithReservedBits(4, 3) 291 .WithTaggedFlag("SYNCBUSY", 7) 292 ; 293 294 Registers.InterruptEnableClear.Define(this) 295 .WithFlag(0, name: "OVF", 296 valueProviderCallback: _ => interruptOverflowEnabled, 297 writeCallback: (_, value) => { if(value) interruptOverflowEnabled = false; }) 298 .WithTaggedFlag("ERR", 1) 299 .WithReservedBits(2, 1) 300 .WithTaggedFlag("SYNCRDY", 3) 301 .WithFlag(4, name: "MC0", 302 valueProviderCallback: _ => interruptChannel0Enabled, 303 writeCallback: (_, value) => { if(value) interruptChannel0Enabled = false; }) 304 .WithFlag(5, name: "MC1", 305 valueProviderCallback: _ => interruptChannel1Enabled, 306 writeCallback: (_, value) => { if(value) interruptChannel1Enabled = false; }) 307 .WithReservedBits(6, 2) 308 .WithWriteCallback((_, __) => UpdateInterrupts()) 309 ; 310 311 Registers.InterruptEnableSet.Define(this) 312 .WithFlag(0, name: "OVF", 313 valueProviderCallback: _ => interruptOverflowEnabled, 314 writeCallback: (_, value) => { if(value) interruptOverflowEnabled = true; }) 315 .WithTaggedFlag("ERR", 1) 316 .WithReservedBits(2, 1) 317 .WithTaggedFlag("SYNCRDY", 3) 318 .WithFlag(4, name: "MC0", 319 valueProviderCallback: _ => interruptChannel0Enabled, 320 writeCallback: (_, value) => { if(value) interruptChannel0Enabled = true; }) 321 .WithFlag(5, name: "MC1", 322 valueProviderCallback: _ => interruptChannel1Enabled, 323 writeCallback: (_, value) => { if(value) interruptChannel1Enabled = true; }) 324 .WithReservedBits(6, 2) 325 .WithWriteCallback((_, __) => UpdateInterrupts()) 326 ; 327 328 Registers.InterruptFlags.Define(this) 329 .WithFlag(0, out interruptOverflowPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "OVF") 330 .WithTaggedFlag("ERR", 1) 331 .WithReservedBits(2, 1) 332 .WithTaggedFlag("SYNCRDY", 3) 333 .WithFlag(4, out interruptChannel0Pending, FieldMode.Read | FieldMode.WriteOneToClear, name: "MC0") 334 .WithFlag(5, out interruptChannel1Pending, FieldMode.Read | FieldMode.WriteOneToClear, name: "MC1") 335 .WithReservedBits(6, 2) 336 .WithWriteCallback((_, __) => UpdateInterrupts()) 337 ; 338 } 339 GetPrescalerValue(Prescaler prescaler)340 private int GetPrescalerValue(Prescaler prescaler) 341 { 342 switch(prescaler) 343 { 344 case Prescaler.Div1: 345 return 1; 346 case Prescaler.Div2: 347 return 2; 348 case Prescaler.Div4: 349 return 4; 350 case Prescaler.Div8: 351 return 8; 352 case Prescaler.Div16: 353 return 16; 354 case Prescaler.Div64: 355 return 64; 356 case Prescaler.Div256: 357 return 256; 358 case Prescaler.Div1024: 359 return 1024; 360 default: 361 throw new Exception("unreachable"); 362 } 363 } 364 365 private uint CounterWidth 366 { 367 get 368 { 369 switch(counterMode.Value) 370 { 371 case CounterMode.Count8: 372 return 1; 373 case CounterMode.Count32: 374 return 4; 375 case CounterMode.Count16: 376 default: 377 return 2; 378 } 379 } 380 } 381 382 private uint CounterTopValue 383 { 384 get 385 { 386 if(compare0Value > 0 && 387 (waveformGenerationOperation.Value == WaveformGenerationOperation.MatchFrequency || 388 waveformGenerationOperation.Value == WaveformGenerationOperation.MatchPWM)) 389 { 390 return (uint)compare0Value; 391 } 392 393 switch(counterMode.Value) 394 { 395 case CounterMode.Count8: 396 return 0xFF; 397 case CounterMode.Count32: 398 return 0xFFFFFFFF; 399 case CounterMode.Count16: 400 default: 401 return 0xFFFF; 402 } 403 } 404 } 405 406 private readonly LimitTimer mainTimer; 407 private readonly LimitTimer captureTimer0; 408 private readonly LimitTimer captureTimer1; 409 410 private bool interruptOverflowEnabled; 411 private bool interruptChannel0Enabled; 412 private bool interruptChannel1Enabled; 413 414 private ulong compare0Value; 415 private ulong compare1Value; 416 417 private IEnumRegisterField<CounterMode> counterMode; 418 private IEnumRegisterField<WaveformGenerationOperation> waveformGenerationOperation; 419 420 private IFlagRegisterField interruptOverflowPending; 421 private IFlagRegisterField interruptChannel0Pending; 422 private IFlagRegisterField interruptChannel1Pending; 423 424 private enum Command 425 { 426 Nothing, 427 Retrigger, 428 Stop, 429 Reserved, 430 } 431 432 private enum WaveformGenerationOperation 433 { 434 NormalFrequency, 435 MatchFrequency, 436 NormalPWM, 437 MatchPWM, 438 } 439 440 private enum CounterMode 441 { 442 Count16, 443 Count8, 444 Count32, 445 Reserved, 446 } 447 448 private enum Prescaler 449 { 450 Div1, 451 Div2, 452 Div4, 453 Div8, 454 Div16, 455 Div64, 456 Div256, 457 Div1024 458 } 459 460 private enum Registers 461 { 462 ControlA0 = 0x00, 463 ControlA1 = 0x01, 464 ReadRequest0 = 0x02, 465 ReadRequest1 = 0x03, 466 ControlBClear = 0x04, 467 ControlBSet = 0x05, 468 ControlC = 0x06, 469 470 Reserved0 = 0x07, 471 472 DebugControl = 0x08, 473 474 Reserved1 = 0x09, 475 476 EventControl = 0x0A, 477 InterruptEnableClear = 0x0C, 478 InterruptEnableSet = 0x0D, 479 InterruptFlags = 0x0E, 480 Status = 0x0F, 481 482 // NOTE: This is 8-bit, 16-bit or 32-bit register 483 Counter0 = 0x10, 484 Counter1 = 0x11, 485 Counter2 = 0x12, 486 Counter3 = 0x13, 487 488 // NOTE: This is 8-bit, 16-bit or 32-bit register 489 ChannelCompareCaptureValue0_0 = 0x18, 490 ChannelCompareCaptureValue0_1 = 0x19, 491 ChannelCompareCaptureValue0_2 = 0x1A, 492 ChannelCompareCaptureValue0_3 = 0x1B, 493 494 // NOTE: This is 8-bit, 16-bit or 32-bit register 495 ChannelCompareCaptureValue1_0 = 0x1C, 496 ChannelCompareCaptureValue1_1 = 0x1D, 497 ChannelCompareCaptureValue1_2 = 0x1E, 498 ChannelCompareCaptureValue1_3 = 0x1F, 499 } 500 } 501 } 502