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 System.Collections.ObjectModel; 10 using System.Linq; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Time; 16 17 namespace Antmicro.Renode.Peripherals.Timers 18 { 19 public class Cadence_TTC : IDoubleWordPeripheral, INumberedGPIOOutput, IKnownSize 20 { Cadence_TTC(IMachine machine, long frequency = DefaultFrequency)21 public Cadence_TTC(IMachine machine, long frequency = DefaultFrequency) 22 { 23 var irqs = new Dictionary<int, IGPIO>(TimerUnitsCount); 24 var registersMap = new Dictionary<long, DoubleWordRegister>(); 25 26 for(var index = 0; index < timerUnits.Length; index++) 27 { 28 var timer = new TimerUnit(machine.ClockSource, this, frequency, $"Timer{index + 1}"); 29 foreach(var register in BuildTimerUnitRegisters(timer)) 30 { 31 registersMap[register.Key + index * RegisterSize] = register.Value; 32 } 33 34 timerUnits[index] = timer; 35 irqs[index] = timer.irq; 36 } 37 38 Connections = new ReadOnlyDictionary<int, IGPIO>(irqs); 39 registers = new DoubleWordRegisterCollection(this, registersMap); 40 } 41 WriteDoubleWord(long offset, uint value)42 public void WriteDoubleWord(long offset, uint value) 43 { 44 registers.Write(offset, value); 45 } 46 ReadDoubleWord(long offset)47 public uint ReadDoubleWord(long offset) 48 { 49 return registers.Read(offset); 50 } 51 Reset()52 public void Reset() 53 { 54 registers.Reset(); 55 // Registers values depend only on a timer object (not on registers reset) 56 foreach(var timer in timerUnits) 57 { 58 timer.Reset(); 59 } 60 } 61 SetCounterValue(int timerIndex, uint value)62 public void SetCounterValue(int timerIndex, uint value) 63 { 64 if(timerIndex < 0 || timerIndex >= TimerUnitsCount) 65 { 66 throw new RecoverableException($"Invalid timer index: TTC contains {TimerUnitsCount} timers."); 67 } 68 timerUnits[timerIndex].Value = value; 69 } 70 71 public long Size => 0x100; 72 73 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 74 75 public long Frequency 76 { 77 get => timerUnits[0].Frequency; 78 set 79 { 80 foreach(var timer in timerUnits) 81 { 82 timer.Frequency = value; 83 } 84 } 85 } 86 BuildTimerUnitRegisters(TimerUnit timer)87 private Dictionary<long, DoubleWordRegister> BuildTimerUnitRegisters(TimerUnit timer) 88 { 89 return new Dictionary<long, DoubleWordRegister> 90 { 91 {(long)Registers.ClockControl1, new DoubleWordRegister(this) 92 .WithReservedBits(7, 25) 93 .WithTaggedFlag("ExternalClockEdge", 6) 94 .WithTaggedFlag("ClockSource", 5) 95 .WithValueField(1, 4, name: "PrescalerValue", 96 writeCallback: (_, val) => timer.Prescaler = (int)val, 97 valueProviderCallback: (_) => (uint)timer.Prescaler 98 ) 99 .WithFlag(0, name: "PrescalerEnable", 100 writeCallback: (_, val) => timer.PrescalerEnabled = val, 101 valueProviderCallback: (_) => timer.PrescalerEnabled 102 ) 103 }, 104 {(long)Registers.CounterControl1, new DoubleWordRegister(this) 105 .WithReservedBits(7, 25) 106 .WithTaggedFlag("WaveformPolarity", 6) 107 .WithTaggedFlag("WaveformOutputDisable", 5) 108 .WithFlag(4, name: "Reset", 109 writeCallback: (_, val) => { if(val) timer.ResetValue(); }, 110 valueProviderCallback: (_) => false 111 ) 112 .WithFlag(3, name: "MatchEnable", 113 writeCallback: (_, val) => timer.MatchEnabled = val, 114 valueProviderCallback: (_) => timer.MatchEnabled 115 ) 116 .WithFlag(2, name: "CounterDecrement", 117 writeCallback: (_, val) => timer.Direction = (val ? Direction.Descending : Direction.Ascending), 118 valueProviderCallback: (_) => timer.Direction == Direction.Descending 119 ) 120 .WithEnumField<DoubleWordRegister, CounterMode>(1, 1, name: "CounterMode", 121 writeCallback: (_, val) => timer.Mode = val, 122 valueProviderCallback: (_) => timer.Mode 123 ) 124 .WithFlag(0, name: "Disable", 125 writeCallback: (_, val) => timer.Enabled = !val, 126 valueProviderCallback: (_) => !timer.Enabled 127 ) 128 }, 129 {(long)Registers.CounterValue1, new DoubleWordRegister(this) 130 .WithValueField(0, 32, FieldMode.Read, name: "CounterValue", 131 valueProviderCallback: (_) => (uint)timer.Value 132 ) 133 }, 134 {(long)Registers.CounterInterval1, new DoubleWordRegister(this) 135 .WithValueField(0, 32, name: "IntervalCounter", 136 writeCallback: (_, val) => timer.Interval = (uint)val, 137 valueProviderCallback: (_) => timer.Interval 138 ) 139 }, 140 {(long)Registers.Match1Counter1, new DoubleWordRegister(this) 141 .WithValueField(0, 32, name: "Match1Value", 142 writeCallback: (_, val) => timer.Match[0].MatchValue = (uint)val, 143 valueProviderCallback: (_) => timer.Match[0].MatchValue 144 ) 145 }, 146 {(long)Registers.Match2Counter1, new DoubleWordRegister(this) 147 .WithValueField(0, 32, name: "Match1Value", 148 writeCallback: (_, val) => timer.Match[1].MatchValue = (uint)val, 149 valueProviderCallback: (_) => timer.Match[1].MatchValue 150 ) 151 }, 152 {(long)Registers.Match3Counter1, new DoubleWordRegister(this) 153 .WithValueField(0, 32, name: "Match1Value", 154 writeCallback: (_, val) => timer.Match[2].MatchValue = (uint)val, 155 valueProviderCallback: (_) => timer.Match[2].MatchValue 156 ) 157 }, 158 {(long)Registers.InterruptStatus1, new DoubleWordRegister(this) 159 .WithReservedBits(6, 26) 160 .WithTaggedFlag("EventTimerOverflowInterrupt", 5) 161 .WithFlag(4, FieldMode.ReadToClear, name: "CounterInterrupt", 162 readCallback: (_, __) => timer.OverflowInterruptFlag = false, 163 valueProviderCallback: (_) => timer.OverflowInterruptFlag 164 ) 165 .WithFlag(3, FieldMode.ReadToClear, name: "Match3Interrupt", 166 readCallback: (_, __) => timer.Match[2].Interrupt = false, 167 valueProviderCallback: (_) => timer.Match[2].Interrupt 168 ) 169 .WithFlag(2, FieldMode.ReadToClear, name: "Match2Interrupt", 170 readCallback: (_, __) => timer.Match[1].Interrupt = false, 171 valueProviderCallback: (_) => timer.Match[1].Interrupt 172 ) 173 .WithFlag(1, FieldMode.ReadToClear, name: "Match1Interrupt", 174 readCallback: (_, __) => timer.Match[0].Interrupt = false, 175 valueProviderCallback: (_) => timer.Match[0].Interrupt 176 ) 177 .WithFlag(0, FieldMode.ReadToClear, name: "IntervalInterrupt", 178 readCallback: (_, __) => timer.IntervalInterruptFlag = false, 179 valueProviderCallback: (_) => timer.IntervalInterruptFlag 180 ) 181 .WithReadCallback((_, __) => timer.UpdateInterrupts()) 182 }, 183 {(long)Registers.InterruptEnable1, new DoubleWordRegister(this) 184 .WithReservedBits(6, 26) 185 .WithTaggedFlag("EventTimerOverflowInterruptEnable", 5) 186 .WithFlag(4, name: "CounterInterruptEnable", 187 writeCallback: (_, val) => timer.OverflowInterruptEnabled = val, 188 valueProviderCallback: (_) => timer.OverflowInterruptEnabled 189 ) 190 .WithFlag(3, name: "Match3InterruptEnable", 191 writeCallback: (_, val) => timer.Match[2].InterruptEnable = val, 192 valueProviderCallback: (_) => timer.Match[2].InterruptEnable 193 ) 194 .WithFlag(2, name: "Match2InterruptEnable", 195 writeCallback: (_, val) => timer.Match[1].InterruptEnable = val, 196 valueProviderCallback: (_) => timer.Match[1].InterruptEnable 197 ) 198 .WithFlag(1, name: "Match1InterruptEnable", 199 writeCallback: (_, val) => timer.Match[0].InterruptEnable = val, 200 valueProviderCallback: (_) => timer.Match[0].InterruptEnable 201 ) 202 .WithFlag(0, name: "IntervalInterruptEnable", 203 writeCallback: (_, val) => timer.IntervalInterruptEnabled = val, 204 valueProviderCallback: (_) => timer.IntervalInterruptEnabled 205 ) 206 .WithWriteCallback((_, __) => timer.UpdateInterrupts()) 207 } 208 }; 209 } 210 211 private readonly TimerUnit[] timerUnits = new TimerUnit[TimerUnitsCount]; 212 private readonly DoubleWordRegisterCollection registers; 213 214 private const long DefaultFrequency = 33330000; 215 private const int RegisterSize = 4; 216 private const int TimerUnitsCount = 3; 217 private const int MatchTimerUnitsCount = 3; 218 219 private class TimerUnit : ITimer 220 { TimerUnit(IClockSource clockSource, IPeripheral parent, long frequency, string localName)221 public TimerUnit(IClockSource clockSource, IPeripheral parent, long frequency, string localName) 222 { 223 timer = new LimitTimer(clockSource, frequency, parent, localName, limit: OverflowLimit, direction: Direction.Ascending, eventEnabled: true); 224 timer.LimitReached += OnLimitReached; 225 226 Match = new MatchTimerUnit[MatchTimerUnitsCount]; 227 for(var i = 0; i < MatchTimerUnitsCount; i++) 228 { 229 Match[i] = new MatchTimerUnit(clockSource, parent, this, frequency, $"{localName}-match{i}"); 230 } 231 } 232 Reset()233 public void Reset() 234 { 235 timer.Reset(); 236 MatchEnabled = false; 237 Mode = CounterMode.Overflow; 238 Interval = 0; 239 PrescalerEnabled = false; 240 Prescaler = 0; 241 Array.ForEach(Match, m => m.Reset()); 242 243 OverflowInterruptEnabled = false; 244 IntervalInterruptEnabled = false; 245 ResetFlags(); 246 } 247 ResetFlags()248 public void ResetFlags() 249 { 250 OverflowInterruptFlag = false; 251 IntervalInterruptFlag = false; 252 UpdateInterrupts(); 253 } 254 ResetValue()255 public void ResetValue() 256 { 257 timer.ResetValue(); 258 Array.ForEach(Match, m => m.Update()); 259 } 260 UpdateInterrupts()261 public void UpdateInterrupts() 262 { 263 irq.Set((OverflowInterruptFlag && OverflowInterruptEnabled) 264 || (IntervalInterruptFlag && IntervalInterruptEnabled) 265 || Match.Any(m => m.IRQ)); 266 } 267 268 public bool Enabled 269 { 270 get => timer.Enabled; 271 set 272 { 273 timer.Enabled = value; 274 Array.ForEach(Match, m => m.Update()); 275 } 276 } 277 278 public bool MatchEnabled 279 { 280 get => matchEnabled; 281 set 282 { 283 matchEnabled = value; 284 Array.ForEach(Match, m => m.Enabled = value); 285 } 286 } 287 288 public CounterMode Mode 289 { 290 get => mode; 291 set 292 { 293 mode = value; 294 UpdateLimit(); 295 } 296 } 297 298 public uint Interval 299 { 300 get => interval; 301 set 302 { 303 interval = value; 304 UpdateLimit(); 305 } 306 } 307 308 public bool PrescalerEnabled 309 { 310 get => prescalerEnabled; 311 set 312 { 313 prescalerEnabled = value; 314 UpdateDivider(); 315 } 316 } 317 318 public int Prescaler 319 { 320 get => prescaler; 321 set 322 { 323 prescaler = value; 324 UpdateDivider(); 325 } 326 } 327 328 public Direction Direction 329 { 330 get => timer.Direction; 331 set 332 { 333 timer.Direction = value; 334 Array.ForEach(Match, m => m.Update()); 335 } 336 } 337 338 public ulong Value 339 { 340 get => timer.Value; 341 set 342 { 343 timer.Value = value; 344 Array.ForEach(Match, m => m.Update()); 345 } 346 } 347 348 public long Frequency 349 { 350 get => timer.Frequency; 351 set 352 { 353 timer.Frequency = value; 354 Array.ForEach(Match, m => m.Frequency = value); 355 } 356 } 357 358 public bool OverflowInterruptFlag { get; set; } 359 public bool OverflowInterruptEnabled { get; set; } 360 public bool IntervalInterruptFlag { get; set; } 361 public bool IntervalInterruptEnabled { get; set; } 362 public MatchTimerUnit[] Match { get; } 363 364 public readonly IGPIO irq = new GPIO(); 365 OnLimitReached()366 private void OnLimitReached() 367 { 368 if(Mode == CounterMode.Interval) 369 { 370 IntervalInterruptFlag = true; 371 } 372 else 373 { 374 OverflowInterruptFlag = true; 375 } 376 Array.ForEach(Match, m => m.Update()); 377 UpdateInterrupts(); 378 } 379 UpdateDivider()380 private void UpdateDivider() 381 { 382 if(PrescalerEnabled) 383 { 384 timer.Divider = 1 << (Prescaler + 1); 385 } 386 else 387 { 388 timer.Divider = 1; 389 } 390 Array.ForEach(Match, m => m.Divider = timer.Divider); 391 } 392 UpdateLimit()393 private void UpdateLimit() 394 { 395 if(Mode == CounterMode.Interval) 396 { 397 timer.Limit = Interval; 398 } 399 else 400 { 401 timer.Limit = OverflowLimit; 402 } 403 Array.ForEach(Match, m => m.Limit = timer.Limit); 404 } 405 406 private CounterMode mode; 407 private uint interval; 408 private bool prescalerEnabled; 409 private bool matchEnabled; 410 private int prescaler; 411 412 private readonly LimitTimer timer; 413 414 private const uint OverflowLimit = UInt32.MaxValue; 415 416 public class MatchTimerUnit 417 { MatchTimerUnit(IClockSource clockSource, IPeripheral parent, TimerUnit owner, long frequency, string localName)418 public MatchTimerUnit(IClockSource clockSource, IPeripheral parent, TimerUnit owner, long frequency, string localName) 419 { 420 this.owner = owner; 421 timer = new LimitTimer(clockSource, frequency, parent, localName, limit: OverflowLimit, direction: Direction.Ascending, workMode: WorkMode.OneShot); 422 timer.LimitReached += owner.UpdateInterrupts; 423 limit = OverflowLimit; 424 } 425 Reset()426 public void Reset() 427 { 428 timer.Reset(); 429 matchValue = 0; 430 // `limit` and `matchEnabled` are reset by `owner` 431 } 432 Update()433 public void Update() 434 { 435 if(!enabled || matchValue > limit || !owner.Enabled || (IsAscending ? matchValue < owner.Value : owner.Value < matchValue)) 436 { 437 timer.Enabled = false; 438 return; 439 } 440 TimerMatchValue = matchValue; 441 TimerValue = owner.Value; 442 timer.Enabled = true; 443 } 444 445 public uint MatchValue 446 { 447 get => matchValue; 448 set 449 { 450 matchValue = value; 451 Update(); 452 } 453 } 454 455 public bool Enabled 456 { 457 get => enabled; 458 set 459 { 460 enabled = value; 461 Update(); 462 } 463 } 464 465 public bool Interrupt 466 { 467 get => timer.RawInterrupt; 468 set 469 { 470 if(!value) 471 { 472 timer.ClearInterrupt(); 473 } 474 } 475 } 476 477 public bool IRQ => timer.Interrupt; 478 479 public bool InterruptEnable 480 { 481 get => timer.EventEnabled; 482 set => timer.EventEnabled = value; 483 } 484 485 public int Divider 486 { 487 set => timer.Divider = value; 488 } 489 490 public ulong Limit 491 { 492 set 493 { 494 limit = value; 495 Update(); 496 } 497 } 498 499 public long Frequency 500 { 501 set => timer.Frequency = value; 502 } 503 TranslateValueForInternalTimer(ulong value)504 private ulong TranslateValueForInternalTimer(ulong value) 505 { 506 // For descending this class flips direction, thus counting in ascending 507 // direction, changing sign and using values congruent modulo `limit` 508 509 // NOTE: ComparingTimer doesn't support descending direction so this 510 // translation and usage of LimitTimer is a workaround 511 return IsAscending ? value : limit - value; 512 } 513 514 private ulong TimerMatchValue 515 { 516 get => TranslateValueForInternalTimer(timer.Limit); 517 set => timer.Limit = TranslateValueForInternalTimer(value); 518 } 519 520 private ulong TimerValue 521 { 522 get => TranslateValueForInternalTimer(timer.Value); 523 set => timer.Value = TranslateValueForInternalTimer(value); 524 } 525 526 private bool IsAscending => owner.Direction == Direction.Ascending; 527 528 private bool enabled; 529 private ulong limit; 530 private uint matchValue; 531 532 private readonly TimerUnit owner; 533 private readonly LimitTimer timer; 534 } 535 } 536 537 private enum CounterMode 538 { 539 Overflow = 0x0, 540 Interval = 0x1, 541 } 542 543 private enum Registers : long 544 { 545 ClockControl1 = 0x00, 546 ClockControl2 = 0x04, 547 ClockControl3 = 0x08, 548 CounterControl1 = 0x0C, 549 CounterControl2 = 0x10, 550 CounterControl3 = 0x14, 551 CounterValue1 = 0x18, 552 CounterValue2 = 0x1C, 553 CounterValue3 = 0x20, 554 CounterInterval1 = 0x24, 555 CounterInterval2 = 0x28, 556 CounterInterval3 = 0x2C, 557 Match1Counter1 = 0x30, 558 Match1Counter2 = 0x34, 559 Match1Counter3 = 0x38, 560 Match2Counter1 = 0x3C, 561 Match2Counter2 = 0x40, 562 Match2Counter3 = 0x44, 563 Match3Counter1 = 0x48, 564 Match3Counter2 = 0x4C, 565 Match3Counter3 = 0x50, 566 InterruptStatus1 = 0x54, 567 InterruptStatus2 = 0x58, 568 InterruptStatus3 = 0x5C, 569 InterruptEnable1 = 0x60, 570 InterruptEnable2 = 0x64, 571 InterruptEnable3 = 0x68, 572 EventControlTimer1 = 0x6C, 573 EventControlTimer2 = 0x70, 574 EventControlTimer3 = 0x74, 575 EventRegister1 = 0x78, 576 EventRegister2 = 0x7C, 577 EventRegister3 = 0x80 578 } 579 } 580 } 581