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 Antmicro.Renode.Core; 8 using Antmicro.Renode.Core.Structure.Registers; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Peripherals; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Time; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.Timers 16 { 17 public class RenesasRA_AGT : IBytePeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IWordPeripheral, IProvidesRegisterCollection<WordRegisterCollection>, IKnownSize 18 { RenesasRA_AGT(IMachine machine, long lowSpeedOnChipOscillatorFrequency, long subClockOscillatorFrequency, long peripheralClockBFrequency)19 public RenesasRA_AGT(IMachine machine, long lowSpeedOnChipOscillatorFrequency, long subClockOscillatorFrequency, long peripheralClockBFrequency) 20 { 21 this.lowSpeedOnChipOscillatorFrequency = lowSpeedOnChipOscillatorFrequency; 22 this.subClockOscillatorFrequency = subClockOscillatorFrequency; 23 this.peripheralClockBFrequency = peripheralClockBFrequency; 24 this.machine = machine; 25 IRQ = new GPIO(); 26 timer = new LimitTimer(machine.ClockSource, peripheralClockBFrequency, this, "agt", ushort.MaxValue, eventEnabled: true, autoUpdate: true); 27 timer.LimitReached += HandleLimitReached; 28 29 channels = new CompareChannel[2]; 30 for(var i = 0; i < channels.Length; ++i) 31 { 32 channels[i] = new CompareChannel(machine, peripheralClockBFrequency, this, $"agt-channel{(char)((int)'A' + i)}"); 33 } 34 35 ByteRegisterCollection = new ByteRegisterCollection(this); 36 WordRegisterCollection = new WordRegisterCollection(this); 37 DefineRegisters(); 38 } 39 Reset()40 public void Reset() 41 { 42 timer.Reset(); 43 foreach(var channel in channels) 44 { 45 channel.Reset(); 46 } 47 WordRegisterCollection.Reset(); 48 WordRegisterCollection.Reset(); 49 } 50 ReadByte(long offset)51 public byte ReadByte(long offset) 52 { 53 return ByteRegisterCollection.Read(offset); 54 } 55 WriteByte(long offset, byte value)56 public void WriteByte(long offset, byte value) 57 { 58 ByteRegisterCollection.Write(offset, value); 59 } 60 ReadWord(long offset)61 public ushort ReadWord(long offset) 62 { 63 return WordRegisterCollection.Read(offset); 64 } 65 WriteWord(long offset, ushort value)66 public void WriteWord(long offset, ushort value) 67 { 68 WordRegisterCollection.Write(offset, value); 69 } 70 71 ByteRegisterCollection IProvidesRegisterCollection<ByteRegisterCollection>.RegistersCollection => ByteRegisterCollection; 72 WordRegisterCollection IProvidesRegisterCollection<WordRegisterCollection>.RegistersCollection => WordRegisterCollection; 73 74 public long Size => 0x100; 75 public long PeripheralClockBFrequency 76 { 77 get => peripheralClockBFrequency; 78 set 79 { 80 peripheralClockBFrequency = value; 81 switch(countSource.Value) 82 { 83 case CountSource.PeripheralClockB_Div1: 84 case CountSource.PeripheralClockB_Div8: 85 case CountSource.PeripheralClockB_Div2: 86 Frequency = peripheralClockBFrequency; 87 break; 88 default: 89 break; 90 } 91 } 92 } 93 94 public GPIO IRQ { get; } 95 public GPIO CompareMatchA => channels[0].IRQ; 96 public GPIO CompareMatchB => channels[1].IRQ; 97 DefineRegisters()98 private void DefineRegisters() 99 { 100 IProvidesRegisterCollection<WordRegisterCollection> thisWithWordRegisters = this; 101 IProvidesRegisterCollection<ByteRegisterCollection> thisWithByteRegisters = this; 102 103 Registers.Counter.Define(thisWithWordRegisters, 0xFFFF) 104 .WithValueField(0, 16, name: "Counter And Reload", 105 valueProviderCallback: _ => Value, 106 writeCallback: (_, value) => Limit = (ushort)value 107 ) 108 ; 109 110 Registers.CompareMatchA.Define(thisWithWordRegisters, 0xFFFF) 111 .WithValueField(0, 16, name: "Compare Match A", 112 valueProviderCallback: _ => channels[0].CompareValue, 113 writeCallback: (_, value) => channels[0].CompareValue = (ushort)value 114 ) 115 ; 116 117 Registers.CompareMatchB.Define(thisWithWordRegisters, 0xFFFF) 118 .WithValueField(0, 16, name: "Compare Match B", 119 valueProviderCallback: _ => channels[1].CompareValue, 120 writeCallback: (_, value) => channels[1].CompareValue = (ushort)value 121 ) 122 ; 123 124 Registers.Control.Define(thisWithByteRegisters) 125 .WithFlag(0, name: "Count Start (TSTART)", 126 valueProviderCallback: _ => Enabled, 127 writeCallback: (_, value) => 128 { 129 if(value && !Enabled) 130 { 131 Value = Limit; 132 } 133 Enabled = value; 134 } 135 ) 136 .WithFlag(1, FieldMode.Read, name: "Count Status Flag (TCSTF)", 137 valueProviderCallback: _ => Enabled 138 ) 139 .WithFlag(2, FieldMode.Write, name: "Count Forces Stop (TSTOP)", 140 writeCallback: (_, value) => 141 { 142 if(value) 143 { 144 Enabled = false; 145 Value = ushort.MaxValue; 146 } 147 } 148 ) 149 .WithReservedBits(3, 1) 150 .WithTaggedFlag("Active Edge Judgment Flag (TEDGF)", 4) 151 .WithFlag(5, out underflowFlag, FieldMode.Read | FieldMode.WriteZeroToClear, name: "Underflow Flag (TUNDF)") 152 .WithFlag(6, FieldMode.Read | FieldMode.WriteZeroToClear, name: "Compare Match A Flag (TCMAF)", 153 valueProviderCallback: _ => channels[0].MatchFlag, 154 writeCallback: (_, value) => channels[0].MatchFlag &= value 155 ) 156 .WithFlag(7, FieldMode.Read | FieldMode.WriteZeroToClear, name: "Compare Match B Flag (TCMBF)", 157 valueProviderCallback: _ => channels[1].MatchFlag, 158 writeCallback: (_, value) => channels[1].MatchFlag &= value 159 ) 160 ; 161 162 Registers.Mode1.Define(thisWithByteRegisters) 163 .WithEnumField<ByteRegister, OperatingMode>(0, 3, out operatingMode, name: "Operating Mode (TMOD)", 164 writeCallback: (_, __) => 165 { 166 switch(operatingMode.Value) 167 { 168 case OperatingMode.Timer: 169 case OperatingMode.PulseOutput: 170 Limit = ushort.MaxValue; 171 break; 172 case OperatingMode.EventCounter: 173 case OperatingMode.PuleWidthMeasurement: 174 case OperatingMode.PulsePeriodMeasurement: 175 this.Log(LogLevel.Error, "Unimplemented operating mode ({0}). Ignoring write.", operatingMode.Value); 176 break; 177 default: 178 this.Log(LogLevel.Error, "Illegal operating mode (0x{0:X}). Ignoring write.", operatingMode.Value); 179 break; 180 } 181 } 182 ) 183 .WithTaggedFlag("Edge Polarity (TEDGPL)", 3) 184 .WithEnumField<ByteRegister, CountSource>(4, 3, out countSource, name: "Count Source (TCK)", 185 writeCallback: (previousValue, _) => 186 { 187 switch(countSource.Value) 188 { 189 case CountSource.PeripheralClockB_Div1: 190 Frequency = peripheralClockBFrequency; 191 Divider = 1; 192 break; 193 case CountSource.PeripheralClockB_Div8: 194 Frequency = peripheralClockBFrequency; 195 Divider = 8; 196 break; 197 case CountSource.PeripheralClockB_Div2: 198 Frequency = peripheralClockBFrequency; 199 Divider = 2; 200 break; 201 case CountSource.LowSpeedOnChipOscillator: 202 Frequency = lowSpeedOnChipOscillatorFrequency; 203 Divider = 1 << (int)divider.Value; 204 break; 205 case CountSource.UnderflowEventFromAGT: 206 this.Log(LogLevel.Error, "Unimplemented count source selected ({0}). Ignoring write.", countSource.Value); 207 countSource.Value = previousValue; 208 break; 209 case CountSource.SubClockOscillator: 210 Frequency = subClockOscillatorFrequency; 211 Divider = 1 << (int)divider.Value; 212 break; 213 default: 214 this.Log(LogLevel.Error, "Illegal count source selected (0x{0:X}). Ignoring write.", countSource.Value); 215 countSource.Value = previousValue; 216 break; 217 } 218 } 219 ) 220 .WithReservedBits(7, 1) 221 ; 222 223 Registers.Mode2.Define(thisWithByteRegisters) 224 .WithValueField(0, 3, out divider, name: "Source Clock Frequency Division Ratio (CKS)", 225 writeCallback: (_, __) => 226 { 227 switch(countSource.Value) 228 { 229 case CountSource.LowSpeedOnChipOscillator: 230 case CountSource.SubClockOscillator: 231 Divider = 1 << (int)divider.Value; 232 break; 233 default: 234 return; 235 } 236 } 237 ) 238 .WithReservedBits(3, 4) 239 .WithTaggedFlag("Low Power Mode (LPM)", 7) 240 ; 241 242 Registers.IOControl.Define(thisWithByteRegisters) 243 .WithTaggedFlag("I/O Polarity Switch (TEDGSEL)", 0) 244 .WithReservedBits(1, 1) 245 .WithTaggedFlag("AGTOn pin Output Enable (TOE)", 2) 246 .WithReservedBits(3, 1) 247 .WithTag("Input Filter (TIPF)", 4, 2) 248 .WithTag("Count Control (TIOGT)", 6, 2) 249 ; 250 251 Registers.EventPinSelect.Define(thisWithByteRegisters) 252 .WithReservedBits(0, 2) 253 .WithTaggedFlag("AGTEEn Polarity Selection (EEPS)", 2) 254 .WithReservedBits(3, 5) 255 ; 256 257 Registers.CompareMatchFunctionSelect.Define(thisWithByteRegisters) 258 .WithFlag(0, name: "Compare Match A Register Enable (TCMEA)", 259 valueProviderCallback: _ => channels[0].CompareMatchEnabled, 260 writeCallback: (_, value) => channels[0].CompareMatchEnabled = value 261 ) 262 .WithTaggedFlag("AGTOAn Pin Output Enable (TOEA)", 1) 263 .WithTaggedFlag("AGTOAn Pin Polarity Select (TOPOLA)", 2) 264 .WithReservedBits(3, 1) 265 .WithFlag(4, name: "Compare Match B Register Enable (TCMEB)", 266 valueProviderCallback: _ => channels[1].CompareMatchEnabled, 267 writeCallback: (_, value) => channels[1].CompareMatchEnabled = value 268 ) 269 .WithTaggedFlag("AGTOBn Pin Output Enable (TOEB)", 5) 270 .WithTaggedFlag("AGTOBn Pin Polarity Select (TOPOLB)", 6) 271 .WithReservedBits(7, 1) 272 ; 273 274 Registers.PinSelect.Define(thisWithByteRegisters) 275 .WithTag("AGTIOn Pin Select (SEL)", 0, 2) 276 .WithReservedBits(2, 2) 277 .WithTaggedFlag("AGTIOn Pin Input Enable (TIES)", 4) 278 .WithReservedBits(5, 3) 279 ; 280 } 281 HandleLimitReached()282 private void HandleLimitReached() 283 { 284 foreach(var channel in channels) 285 { 286 channel.Restart(); 287 } 288 underflowFlag.Value = true; 289 IRQ.Blink(); 290 this.Log(LogLevel.Debug, "IRQ blinked"); 291 } 292 TrySyncTime()293 private bool TrySyncTime() 294 { 295 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 296 { 297 cpu.SyncTime(); 298 return true; 299 } 300 return false; 301 } 302 303 private ushort Limit 304 { 305 get => (ushort)timer.Limit; 306 set 307 { 308 timer.Limit = value; 309 foreach(var channel in channels) 310 { 311 channel.Limit = value; 312 } 313 } 314 } 315 316 private ushort Value 317 { 318 get 319 { 320 TrySyncTime(); 321 return (ushort)timer.Value; 322 } 323 set 324 { 325 timer.Value = value; 326 foreach(var channel in channels) 327 { 328 channel.Value = value; 329 } 330 } 331 } 332 333 private bool Enabled 334 { 335 get => timer.Enabled; 336 set 337 { 338 timer.Enabled = value; 339 foreach(var channel in channels) 340 { 341 channel.Enabled = value; 342 } 343 } 344 } 345 346 private long Frequency 347 { 348 set 349 { 350 timer.Frequency = value; 351 foreach(var channel in channels) 352 { 353 channel.Frequency = value; 354 } 355 } 356 } 357 358 private int Divider 359 { 360 set 361 { 362 timer.Divider = value; 363 foreach(var channel in channels) 364 { 365 channel.Divider = value; 366 } 367 } 368 } 369 370 private ByteRegisterCollection ByteRegisterCollection { get; } 371 private WordRegisterCollection WordRegisterCollection { get; } 372 373 private IFlagRegisterField underflowFlag; 374 private IEnumRegisterField<OperatingMode> operatingMode; 375 private IEnumRegisterField<CountSource> countSource; 376 private IValueRegisterField divider; 377 378 private long peripheralClockBFrequency; 379 380 private readonly long lowSpeedOnChipOscillatorFrequency; 381 private readonly long subClockOscillatorFrequency; 382 383 private readonly IMachine machine; 384 private readonly LimitTimer timer; 385 private readonly CompareChannel[] channels; 386 387 public enum Registers 388 { 389 Counter = 0x00, 390 CompareMatchA = 0x02, 391 CompareMatchB = 0x04, 392 Control = 0x08, 393 Mode1 = 0x09, 394 Mode2 = 0x0A, 395 IOControl = 0x0C, 396 EventPinSelect = 0x0D, 397 CompareMatchFunctionSelect = 0x0E, 398 PinSelect = 0x0F, 399 } 400 401 private enum OperatingMode 402 { 403 Timer = 0b000, 404 PulseOutput = 0b001, 405 EventCounter = 0b010, 406 PuleWidthMeasurement = 0b011, 407 PulsePeriodMeasurement = 0b100, 408 } 409 410 private enum CountSource 411 { 412 PeripheralClockB_Div1 = 0b000, 413 PeripheralClockB_Div8 = 0b001, 414 PeripheralClockB_Div2 = 0b011, 415 LowSpeedOnChipOscillator = 0b100, 416 UnderflowEventFromAGT = 0b101, 417 SubClockOscillator = 0b111, 418 } 419 420 private class CompareChannel 421 { CompareChannel(IMachine machine, long frequency, IPeripheral parent, string localName)422 public CompareChannel(IMachine machine, long frequency, IPeripheral parent, string localName) 423 { 424 this.parent = parent; 425 this.name = localName; 426 IRQ = new GPIO(); 427 innerTimer = new LimitTimer(machine.ClockSource, frequency, parent, localName, ushort.MaxValue, workMode: WorkMode.OneShot, eventEnabled: true); 428 innerTimer.LimitReached += () => 429 { 430 MatchFlag = true; 431 Running = false; 432 IRQ.Blink(); 433 parent.Log(LogLevel.Debug, "{0}.IRQ blinked ", name); 434 }; 435 } 436 Reset()437 public void Reset() 438 { 439 innerTimer.Reset(); 440 compareMatchEnabled = false; 441 enabled = false; 442 running = false; 443 compareValue = ushort.MaxValue; 444 Limit = ushort.MaxValue; 445 } 446 Restart()447 public void Restart() 448 { 449 Running = Limit < CompareValue; 450 if(!Running) 451 { 452 return; 453 } 454 innerTimer.Limit = (ulong)(Limit - CompareValue); 455 innerTimer.ResetValue(); 456 } 457 458 public GPIO IRQ { get; } 459 460 public ushort CompareValue 461 { 462 get => compareValue; 463 set 464 { 465 var counterValue = Value; 466 compareValue = value; 467 if(Limit < CompareValue) 468 { 469 Running = false; 470 return; 471 } 472 innerTimer.Limit = (ulong)(Limit - CompareValue); 473 if(CompareValue > counterValue) 474 { 475 Running = false; 476 return; 477 } 478 Value = counterValue; 479 Running = true; 480 } 481 } 482 483 public ushort Limit 484 { 485 private get => limit; 486 set 487 { 488 limit = value; 489 if(Limit < CompareValue) 490 { 491 return; 492 } 493 innerTimer.Limit = (ulong)(Limit - CompareValue); 494 } 495 } 496 497 public ushort Value 498 { 499 private get => (ushort)((ushort)innerTimer.Value + CompareValue); 500 set 501 { 502 Running = value >= CompareValue; 503 if(!Running) 504 { 505 return; 506 } 507 innerTimer.Value = (ulong)(value - CompareValue); 508 } 509 } 510 511 public bool CompareMatchEnabled 512 { 513 get => compareMatchEnabled; 514 set 515 { 516 compareMatchEnabled = value; 517 Refresh(); 518 } 519 } 520 521 public bool Enabled 522 { 523 private get => enabled; 524 set 525 { 526 enabled = value; 527 Refresh(); 528 } 529 } 530 531 public long Frequency 532 { 533 set => innerTimer.Frequency = value; 534 } 535 536 public int Divider 537 { 538 set => innerTimer.Divider = value; 539 } 540 541 public bool MatchFlag { get; set; } 542 Refresh()543 private void Refresh() 544 { 545 innerTimer.Enabled = Enabled && Running && CompareMatchEnabled; 546 } 547 548 private bool Running 549 { 550 get => running; 551 set 552 { 553 running = value; 554 Refresh(); 555 } 556 } 557 558 private ushort compareValue; 559 private ushort limit; 560 private bool compareMatchEnabled; 561 private bool enabled; 562 private bool running; 563 564 private readonly string name; 565 566 private readonly IPeripheral parent; 567 private readonly LimitTimer innerTimer; 568 } 569 } 570 } 571