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 using System.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Time; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Exceptions; 17 18 namespace Antmicro.Renode.Peripherals.Timers 19 { 20 // This class does not implement advanced-control timers interrupts 21 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 22 public class STM32_Timer : LimitTimer, IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput, IPeripheralRegister<IGPIOReceiver, NumberRegistrationPoint<int>>, IPeripheralRegister<IGPIOReceiver, NullRegistrationPoint> 23 { STM32_Timer(IMachine machine, long frequency, uint initialLimit)24 public STM32_Timer(IMachine machine, long frequency, uint initialLimit) : base(machine.ClockSource, frequency, limit: initialLimit, direction: Direction.Ascending, enabled: false, autoUpdate: false) 25 { 26 this.machine = machine; 27 sysbus = machine.GetSystemBus(this); 28 IRQ = new GPIO(); 29 connections = Enumerable.Range(0, NumberOfCCChannels).ToDictionary(i => i, _ => (IGPIO)new GPIO()); 30 this.initialLimit = initialLimit; 31 // If initialLimit is 0, throw an error - this is an invalid state for us, since we would not be able to infer the counter's width 32 if(initialLimit == 0) 33 { 34 throw new ConstructionException($"{nameof(initialLimit)} has to be greater than zero"); 35 } 36 // We need to ensure that the counter is at least as wide as the position of MSB in initialLimit 37 // but since we count from 0 (log_2 (1) = 0 ) - add 1 38 this.timerCounterLengthInBits = (int)Math.Floor(Math.Log(initialLimit, 2)) + 1; 39 if(this.timerCounterLengthInBits > 32) 40 { 41 throw new ConstructionException($"Timer's width cannot be more than 32 bits - requested {this.timerCounterLengthInBits} bits (inferred from {nameof(initialLimit)})"); 42 } 43 44 LimitReached += delegate 45 { 46 if(updateDisable.Value) 47 { 48 return; 49 } 50 Limit = autoReloadValue; 51 52 for(var i = 0; i < NumberOfCCChannels; ++i) 53 { 54 UpdateCaptureCompareTimer(i); 55 if(!ccTimers[i].Enabled) 56 { 57 continue; 58 } 59 60 switch(outputCompareModes[i].Value) 61 { 62 case OutputCompareMode.PwmMode1: 63 Connections[i].Set(); 64 break; 65 case OutputCompareMode.PwmMode2: 66 Connections[i].Unset(); 67 break; 68 } 69 } 70 71 if(updateInterruptEnable.Value && repetitionsLeft == 0) 72 { 73 // 2 of central-aligned modes should raise IRQ only on overflow/underflow, hence it happens 2 times less often 74 var centerAlignedUnbalancedMode = (centerAlignedMode.Value == CenterAlignedMode.CenterAligned1) || (centerAlignedMode.Value == CenterAlignedMode.CenterAligned2); 75 this.Log(LogLevel.Noisy, "IRQ pending"); 76 updateInterruptFlag = true; 77 repetitionsLeft = 1u + (uint)repetitionCounter.Value * (centerAlignedUnbalancedMode ? 2u : 1u); 78 UpdateInterrupts(); 79 } 80 81 if(repetitionsLeft > 0) 82 { 83 repetitionsLeft--; 84 } 85 }; 86 87 for(var i = 0; i < NumberOfCCChannels; ++i) 88 { 89 var j = i; 90 ccTimers[j] = new LimitTimer(machine.ClockSource, frequency, this, String.Format("cctimer{0}", j + 1), limit: initialLimit, eventEnabled: true, direction: Direction.Ascending, enabled: false, autoUpdate: false, workMode: WorkMode.OneShot); 91 ccTimers[j].LimitReached += delegate 92 { 93 switch(outputCompareModes[j].Value) 94 { 95 case OutputCompareMode.SetActiveOnMatch: 96 Connections[j].Blink(); // high pulse 97 break; 98 case OutputCompareMode.SetInactiveOnMatch: 99 Connections[j].Unset(); 100 Connections[j].Set(); // low pulse 101 break; 102 case OutputCompareMode.ToggleOnMatch: 103 Connections[j].Toggle(); 104 break; 105 case OutputCompareMode.PwmMode1: 106 Connections[j].Unset(); 107 break; 108 case OutputCompareMode.PwmMode2: 109 Connections[j].Set(); 110 break; 111 } 112 113 if(ccInterruptEnable[j]) 114 { 115 ccInterruptFlag[j] = true; 116 this.Log(LogLevel.Noisy, "cctimer{0}: Compare IRQ pending", j + 1); 117 UpdateInterrupts(); 118 } 119 }; 120 } 121 122 var registersMap = new Dictionary<long, DoubleWordRegister> 123 { 124 {(long)Registers.Control1, new DoubleWordRegister(this) 125 .WithFlag(0, writeCallback: (_, val) => 126 { 127 enableRequested = val; 128 Enabled = enableRequested && autoReloadValue > 0; 129 }, valueProviderCallback: _ => enableRequested, name: "Counter enable (CEN)") 130 .WithFlag(1, out updateDisable, name: "Update disable (UDIS)") 131 .WithFlag(2, out updateRequestSource, name: "Update request source (URS)") 132 .WithFlag(3, writeCallback: (_, val) => Mode = val ? WorkMode.OneShot : WorkMode.Periodic, valueProviderCallback: _ => Mode == WorkMode.OneShot, name: "One-pulse mode (OPM)") 133 .WithFlag(4, writeCallback: (_, val) => Direction = val ? Direction.Descending : Direction.Ascending, valueProviderCallback: _ => Direction == Direction.Descending, name: "Direction (DIR)") 134 .WithEnumField(5, 2, out centerAlignedMode, name: "Center-aligned mode selection (CMS)") 135 .WithFlag(7, out autoReloadPreloadEnable, name: "Auto-reload preload enable (APRE)") 136 .WithTag("Clock Division (CKD)", 8, 2) 137 .WithReservedBits(10, 22) 138 .WithWriteCallback((_, __) => { UpdateCaptureCompareTimers(); UpdateInterrupts(); }) 139 }, 140 141 {(long)Registers.Control2, new DoubleWordRegister(this) 142 .WithTaggedFlag("CCPC", 0) 143 .WithReservedBits(1, 1) 144 .WithTaggedFlag("CCUS", 2) 145 .WithTaggedFlag("CCDS", 3) 146 .WithTag("MMS", 4, 2) 147 .WithTaggedFlag("TI1S", 7) 148 .WithTaggedFlag("OIS1", 8) 149 .WithTaggedFlag("OIS1N", 9) 150 .WithTaggedFlag("OIS2", 10) 151 .WithTaggedFlag("OIS2N", 11) 152 .WithTaggedFlag("OIS3", 12) 153 .WithTaggedFlag("OIS3N", 13) 154 .WithTaggedFlag("OIS4", 14) 155 .WithReservedBits(15, 17) 156 }, 157 158 {(long)Registers.SlaveModeControl, new DoubleWordRegister(this) 159 .WithTag("SMS", 0, 3) 160 .WithTaggedFlag("OCCS", 3) 161 .WithTag("TS", 4, 2) 162 .WithTaggedFlag("MSM", 7) 163 .WithTag("ETF", 8, 3) 164 .WithTag("ETPS", 12, 2) 165 .WithTaggedFlag("ECE", 14) 166 .WithTaggedFlag("ETP", 15) 167 .WithReservedBits(16, 16) 168 }, 169 170 {(long)Registers.DmaOrInterruptEnable, new DoubleWordRegister(this) 171 .WithFlag(0, out updateInterruptEnable, name: "Update interrupt enable (UIE)") 172 .WithFlag(1, valueProviderCallback: _ => ccInterruptEnable[0], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(0, val), name: "Capture/Compare 1 interrupt enable (CC1IE)") 173 .WithFlag(2, valueProviderCallback: _ => ccInterruptEnable[1], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(1, val), name: "Capture/Compare 2 interrupt enable (CC2IE)") 174 .WithFlag(3, valueProviderCallback: _ => ccInterruptEnable[2], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(2, val), name: "Capture/Compare 3 interrupt enable (CC3IE)") 175 .WithFlag(4, valueProviderCallback: _ => ccInterruptEnable[3], writeCallback: (_, val) => WriteCaptureCompareInterruptEnable(3, val), name: "Capture/Compare 4 interrupt enable (CC4IE)") 176 .WithReservedBits(5, 1) 177 .WithTag("Trigger interrupt enable (TIE)", 6, 1) 178 .WithReservedBits(7, 1) 179 .WithTag("Update DMA request enable (UDE)", 8, 1) 180 .WithTag("Capture/Compare 1 DMA request enable (CC1DE)", 9, 1) 181 .WithTag("Capture/Compare 2 DMA request enable (CC2DE)", 10, 1) 182 .WithTag("Capture/Compare 3 DMA request enable (CC3DE)", 11, 1) 183 .WithTag("Capture/Compare 4 DMA request enable (CC4DE)", 12, 1) 184 .WithReservedBits(13, 1) 185 .WithTag("Trigger DMA request enable (TDE)", 14, 1) 186 .WithReservedBits(15, 17) 187 .WithWriteCallback((_, __) => UpdateInterrupts()) 188 }, 189 190 {(long)Registers.Status, new DoubleWordRegister(this) 191 .WithFlag(0, FieldMode.Read | FieldMode.WriteZeroToClear, 192 writeCallback: (_, val) => 193 { 194 if(!val) 195 { 196 updateInterruptFlag = false; 197 this.Log(LogLevel.Noisy, "IRQ claimed"); 198 } 199 }, 200 valueProviderCallback: (_) => 201 { 202 return updateInterruptFlag; 203 }, 204 name: "Update interrupt flag (UIF)") 205 .WithFlag(1, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(0, val), valueProviderCallback: _ => ccInterruptFlag[0], name: "Capture/Compare 1 interrupt flag (CC1IF)") 206 .WithFlag(2, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(1, val), valueProviderCallback: _ => ccInterruptFlag[1], name: "Capture/Compare 2 interrupt flag (CC2IF)") 207 .WithFlag(3, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(2, val), valueProviderCallback: _ => ccInterruptFlag[2], name: "Capture/Compare 3 interrupt flag (CC3IF)") 208 .WithFlag(4, FieldMode.Read | FieldMode.WriteZeroToClear, writeCallback: (_, val) => ClaimCaptureCompareInterrupt(3, val), valueProviderCallback: _ => ccInterruptFlag[3], name: "Capture/Compare 4 interrupt flag (CC4IF)") 209 // Reserved fields were changed to flags to prevent from very frequent logging 210 .WithFlag(5, name: "Reserved1") 211 // These write callbacks are here only to prevent from very frequent logging. 212 .WithValueField(6, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Trigger interrupt flag (TIE)") 213 .WithFlag(7, name: "Reserved2") 214 .WithFlag(8, name: "Reserved3") 215 .WithValueField(9, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 1 overcapture flag (CC1OF)") 216 .WithValueField(10, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 2 overcapture flag (CC2OF)") 217 .WithValueField(11, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 3 overcapture flag (CC3OF)") 218 .WithValueField(12, 1, FieldMode.WriteZeroToClear, writeCallback: (_, __) => {}, name: "Capture/Compare 4 overcapture flag (CC4OF)") 219 .WithValueField(13, 19, name: "Reserved4") 220 .WithWriteCallback((_, __) => UpdateInterrupts()) 221 }, 222 223 {(long)Registers.EventGeneration, new DoubleWordRegister(this) 224 .WithFlag(0, FieldMode.WriteOneToClear, writeCallback: (_, val) => 225 { 226 if(updateDisable.Value) 227 { 228 return; 229 } 230 if(Direction == Direction.Ascending) 231 { 232 Value = 0; 233 } 234 else if(Direction == Direction.Descending) 235 { 236 Value = autoReloadValue; 237 } 238 239 repetitionsLeft = (uint)repetitionCounter.Value; 240 241 if(!updateRequestSource.Value && updateInterruptEnable.Value) 242 { 243 this.Log(LogLevel.Noisy, "IRQ pending"); 244 updateInterruptFlag = true; 245 } 246 for(var i = 0; i < NumberOfCCChannels; ++i) 247 { 248 if(ccTimers[i].Enabled) 249 { 250 ccTimers[i].Value = Value; 251 } 252 } 253 }, name: "Update generation (UG)") 254 .WithTag("Capture/compare 1 generation (CC1G)", 1, 1) 255 .WithTag("Capture/compare 2 generation (CC2G)", 2, 1) 256 .WithTag("Capture/compare 3 generation (CC3G)", 3, 1) 257 .WithTag("Capture/compare 4 generation (CC4G)", 4, 1) 258 .WithTaggedFlag("Capture/compare update generation (COMG)", 5) 259 .WithTag("Trigger generation (TG)", 6, 1) 260 .WithReservedBits(7, 25) 261 .WithWriteCallback((_, __) => UpdateInterrupts()) 262 }, 263 264 {(long)Registers.CaptureOrCompareMode1, new DoubleWordRegister(this) 265 // Fields of this register vary between 'Output compare'/'Input capture' mode 266 // Only fields for output compare mode are defined 267 .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(0, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(0, val), name: "CC1S") 268 // Input mode: 269 // "IC1PSC", 2, 2 270 // "IC1F", 4, 4 271 .WithTaggedFlag("OC1FE", 2) 272 .WithTaggedFlag("OC1PE", 3) 273 .WithEnumField(4, 3, out outputCompareModes[0], writeCallback: (_, val) => WriteOutputCompareMode(0, val), name: "OC1M") 274 .WithTaggedFlag("OC1CE", 7) 275 .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(8, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(1, val), name: "CC2S") 276 .WithTaggedFlag("OC2FE", 10) 277 .WithTaggedFlag("OC2PE", 11) 278 .WithEnumField(12, 3, out outputCompareModes[1], writeCallback: (_, val) => WriteOutputCompareMode(1, val), name: "OC2M") 279 .WithTaggedFlag("OC2CE", 15) 280 // Input mode: 281 // "IC2PSC", 10, 2 282 // "IC2F", 12, 4 283 .WithReservedBits(16, 16) 284 }, 285 286 {(long)Registers.CaptureOrCompareMode2, new DoubleWordRegister(this) 287 // Fields of this register vary between 'Output compare'/'Input capture' mode 288 // Only fields for output compare mode are defined 289 .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(0, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(2, val), name: "CC3S") 290 .WithTaggedFlag("OC3FE", 2) 291 .WithTaggedFlag("OC3PE", 3) 292 .WithEnumField(4, 3, out outputCompareModes[2], writeCallback: (_, val) => WriteOutputCompareMode(2, val), name: "OC3M") 293 .WithTaggedFlag("OC3CE", 7) 294 // Input mode: 295 // "IC3PSC", 2, 2 296 // "IC3F", 4, 4 297 .WithEnumField<DoubleWordRegister, CaptureCompareSelection>(8, 2, writeCallback: (_, val) => WriteCaptureCompareSelection(3, val), name: "CC4S") 298 .WithTaggedFlag("OC4FE", 10) 299 .WithTaggedFlag("OC4PE", 11) 300 .WithEnumField(12, 3, out outputCompareModes[3], writeCallback: (_, val) => WriteOutputCompareMode(3, val), name: "OC4M") 301 .WithTaggedFlag("OC4CE", 15) 302 // Input mode: 303 // "IC4PSC", 10, 2 304 // "IC4F", 12, 4 305 .WithReservedBits(16, 16) 306 }, 307 308 {(long)Registers.CaptureOrCompareEnable, new DoubleWordRegister(this) 309 .WithFlag(0, valueProviderCallback: _ => ccOutputEnable[0], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(0, val), name: "Capture/Compare 1 enable (CC1E)") 310 .WithTaggedFlag("CC1P", 1) 311 .WithTaggedFlag("CC1NE", 2) 312 .WithTaggedFlag("CC1NP", 3) 313 .WithFlag(4, valueProviderCallback: _ => ccOutputEnable[1], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(1, val), name: "Capture/Compare 2 enable (CC2E)") 314 .WithTaggedFlag("CC2P", 5) 315 .WithTaggedFlag("CC2NE", 6) 316 .WithTaggedFlag("CC2NP", 7) 317 .WithFlag(8, valueProviderCallback: _ => ccOutputEnable[2], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(2, val), name: "Capture/Compare 3 enable (CC3E)") 318 .WithTaggedFlag("CC3P", 9) 319 .WithTaggedFlag("CC3NE", 10) 320 .WithTaggedFlag("CC3NP", 11) 321 .WithFlag(12, valueProviderCallback: _ => ccOutputEnable[3], writeCallback: (_, val) => WriteCaptureCompareOutputEnable(3, val), name: "Capture/Compare 4 enable (CC4E)") 322 .WithTaggedFlag("CC4P", 13) 323 .WithReservedBits(14, 18) 324 }, 325 326 {(long)Registers.Counter, new DoubleWordRegister(this) 327 .WithValueField(0, timerCounterLengthInBits, 328 writeCallback: (_, val) => Value = val, 329 valueProviderCallback: _ => 330 { 331 if(sysbus.TryGetCurrentCPU(out var cpu)) 332 { 333 cpu.SyncTime(); 334 } 335 return (uint)Value; 336 }, name: "Counter value (CNT)") 337 .WithReservedBits(timerCounterLengthInBits, 32 - timerCounterLengthInBits) 338 .WithWriteCallback((_, val) => 339 { 340 for(var i = 0; i < NumberOfCCChannels; ++i) 341 { 342 if(val < ccTimers[i].Limit) 343 { 344 ccTimers[i].Value = val; 345 } 346 } 347 UpdateInterrupts(); 348 }) 349 }, 350 351 {(long)Registers.Prescaler, new DoubleWordRegister(this) 352 .WithValueField(0, 16, writeCallback: (_, val) => Divider = (int)val + 1, valueProviderCallback: _ => (uint)Divider - 1, name: "Prescaler value (PSC)") 353 .WithReservedBits(16, 16) 354 .WithWriteCallback((_, __) => 355 { 356 for(var i = 0; i < NumberOfCCChannels; ++i) 357 { 358 ccTimers[i].Divider = Divider; 359 } 360 UpdateInterrupts(); 361 }) 362 }, 363 364 {(long)Registers.AutoReload, new DoubleWordRegister(this) 365 .WithValueField(0, timerCounterLengthInBits, writeCallback: (_, val) => 366 { 367 autoReloadValue = (uint)val; 368 Enabled = enableRequested && autoReloadValue > 0; 369 if(!autoReloadPreloadEnable.Value) 370 { 371 Limit = autoReloadValue; 372 } 373 }, valueProviderCallback: _ => autoReloadValue, name: "Auto-reload value (ARR)") 374 .WithReservedBits(timerCounterLengthInBits, 32 - timerCounterLengthInBits) 375 .WithWriteCallback((_, __) => UpdateInterrupts()) 376 }, 377 {(long)Registers.RepetitionCounter, new DoubleWordRegister(this) 378 .WithValueField(0, 8, out repetitionCounter, name: "Repetition counter (TIM1_RCR)") 379 .WithReservedBits(8, 24) 380 }, 381 {(long)Registers.BreakAndDeadTime, new DoubleWordRegister(this) 382 .WithTag("Dead Time Generator (DTG)", 0, 8) 383 .WithTag("LOCK", 8, 2) 384 .WithTaggedFlag("Off-state selection idle mode (OSSI)", 10) 385 .WithTaggedFlag("Off-state selection run mode (OSSR)", 11) 386 .WithTaggedFlag("Break enable (BKE)", 12) 387 .WithTaggedFlag("Break polarity (BKP)", 13) 388 .WithTaggedFlag("Automatic output enable (AOE)", 14) 389 .WithTaggedFlag("Main Output Enable (MOE)", 15) 390 .WithReservedBits(16, 16) 391 }, 392 }; 393 394 for(var i = 0; i < NumberOfCCChannels; ++i) 395 { 396 var j = i; 397 registersMap.Add((long)Registers.CaptureOrCompare1 + (j * 0x4), new DoubleWordRegister(this) 398 .WithValueField(0, timerCounterLengthInBits, valueProviderCallback: _ => (uint)ccTimers[j].Limit, writeCallback: (_, val) => 399 { 400 if(val == 0) 401 { 402 ccTimers[j].Enabled = false; 403 } 404 ccTimers[j].Limit = val; 405 }, name: String.Format("Capture/compare value {0} (CCR{0})", j + 1)) 406 .WithReservedBits(timerCounterLengthInBits, 32 - timerCounterLengthInBits) 407 .WithWriteCallback((_, __) => { UpdateCaptureCompareTimer(j); UpdateInterrupts(); }) 408 ); 409 } 410 411 registers = new DoubleWordRegisterCollection(this, registersMap); 412 Reset(); 413 414 EventEnabled = true; 415 } 416 ReadDoubleWord(long offset)417 public uint ReadDoubleWord(long offset) 418 { 419 return registers.Read(offset); 420 } 421 WriteDoubleWord(long offset, uint value)422 public void WriteDoubleWord(long offset, uint value) 423 { 424 registers.Write(offset, value); 425 } 426 Reset()427 public override void Reset() 428 { 429 base.Reset(); 430 registers.Reset(); 431 autoReloadValue = initialLimit; 432 enableRequested = false; 433 Limit = initialLimit; 434 repetitionsLeft = 0; 435 updateInterruptFlag = false; 436 for(var i = 0; i < NumberOfCCChannels; ++i) 437 { 438 ccTimers[i].Reset(); 439 ccInterruptFlag[i] = false; 440 ccInterruptEnable[i] = false; 441 ccOutputEnable[i] = false; 442 Connections[i].Unset(); 443 } 444 UpdateInterrupts(); 445 } 446 447 public GPIO IRQ { get; private set; } 448 public IReadOnlyDictionary<int, IGPIO> Connections => connections; 449 450 public long Size => 0x400; 451 Register(IGPIOReceiver peripheral, NumberRegistrationPoint<int> registrationPoint)452 public void Register(IGPIOReceiver peripheral, NumberRegistrationPoint<int> registrationPoint) 453 { 454 machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 455 } 456 Register(IGPIOReceiver peripheral, NullRegistrationPoint registrationPoint)457 public void Register(IGPIOReceiver peripheral, NullRegistrationPoint registrationPoint) 458 { 459 machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 460 } 461 Unregister(IGPIOReceiver peripheral)462 public void Unregister(IGPIOReceiver peripheral) 463 { 464 machine.UnregisterAsAChildOf(this, peripheral); 465 } 466 UpdateCaptureCompareTimer(int i)467 private void UpdateCaptureCompareTimer(int i) 468 { 469 ccTimers[i].Enabled = Enabled && IsInterruptOrOutputEnabled(i) && Value < ccTimers[i].Limit; 470 if(ccTimers[i].Enabled) 471 { 472 ccTimers[i].Value = Value; 473 } 474 ccTimers[i].Direction = Direction; 475 } 476 UpdateCaptureCompareTimers()477 private void UpdateCaptureCompareTimers() 478 { 479 for(var i = 0; i < NumberOfCCChannels; ++i) 480 { 481 UpdateCaptureCompareTimer(i); 482 } 483 } 484 WriteCaptureCompareOutputEnable(int i, bool value)485 private void WriteCaptureCompareOutputEnable(int i, bool value) 486 { 487 ccOutputEnable[i] = value; 488 UpdateCaptureCompareTimer(i); 489 if(!value) 490 { 491 Connections[i].Unset(); 492 } 493 this.Log(LogLevel.Noisy, "cctimer{0}: Output Enable set to {1}", i + 1, value); 494 } 495 WriteCaptureCompareInterruptEnable(int i, bool value)496 private void WriteCaptureCompareInterruptEnable(int i, bool value) 497 { 498 ccInterruptEnable[i] = value; 499 UpdateCaptureCompareTimer(i); 500 this.Log(LogLevel.Noisy, "cctimer{0}: Interrupt Enable set to {1}", i + 1, value); 501 } 502 IsInterruptOrOutputEnabled(int i)503 private bool IsInterruptOrOutputEnabled(int i) 504 { 505 return ccInterruptEnable[i] || ccOutputEnable[i]; 506 } 507 WriteCaptureCompareSelection(int i, CaptureCompareSelection value)508 private void WriteCaptureCompareSelection(int i, CaptureCompareSelection value) 509 { 510 if(value != CaptureCompareSelection.Output) 511 { 512 this.Log(LogLevel.Warning, "Channel {0}: input capture mode is not supported", i + 1); 513 } 514 } 515 WriteOutputCompareMode(int i, OutputCompareMode value)516 private void WriteOutputCompareMode(int i, OutputCompareMode value) 517 { 518 this.Log(LogLevel.Noisy, "cctimer{0}: output compare mode set to {1}", i + 1, value); 519 switch(value) 520 { 521 case OutputCompareMode.ForceInactive: 522 case OutputCompareMode.SetActiveOnMatch: 523 Connections[i].Unset(); 524 break; 525 case OutputCompareMode.SetInactiveOnMatch: 526 case OutputCompareMode.ForceActive: 527 Connections[i].Set(); 528 break; 529 } 530 } 531 ClaimCaptureCompareInterrupt(int i, bool value)532 private void ClaimCaptureCompareInterrupt(int i, bool value) 533 { 534 if(!value) 535 { 536 ccInterruptFlag[i] = false; 537 this.Log(LogLevel.Noisy, "cctimer{0}: Compare IRQ claimed", i + 1); 538 } 539 } 540 UpdateInterrupts()541 private void UpdateInterrupts() 542 { 543 var value = false; 544 value |= updateInterruptFlag & updateInterruptEnable.Value; 545 for(var i = 0; i < NumberOfCCChannels; ++i) 546 { 547 value |= ccInterruptFlag[i] & ccInterruptEnable[i]; 548 } 549 550 IRQ.Set(value); 551 } 552 553 private readonly uint initialLimit; 554 private readonly int timerCounterLengthInBits; 555 private uint autoReloadValue; 556 private uint repetitionsLeft; 557 private bool updateInterruptFlag; 558 private bool enableRequested; 559 private bool[] ccInterruptFlag = new bool[NumberOfCCChannels]; 560 private bool[] ccInterruptEnable = new bool[NumberOfCCChannels]; 561 private bool[] ccOutputEnable = new bool[NumberOfCCChannels]; 562 private readonly IFlagRegisterField updateDisable; 563 private readonly IFlagRegisterField updateRequestSource; 564 private readonly IFlagRegisterField updateInterruptEnable; 565 private readonly IFlagRegisterField autoReloadPreloadEnable; 566 private readonly IEnumRegisterField<CenterAlignedMode> centerAlignedMode; 567 private readonly IValueRegisterField repetitionCounter; 568 private readonly DoubleWordRegisterCollection registers; 569 private readonly IEnumRegisterField<OutputCompareMode>[] outputCompareModes = new IEnumRegisterField<OutputCompareMode>[NumberOfCCChannels]; 570 private readonly LimitTimer[] ccTimers = new LimitTimer[NumberOfCCChannels]; 571 private readonly IMachine machine; 572 private readonly IBusController sysbus; 573 private readonly Dictionary<int, IGPIO> connections; 574 575 private const int NumberOfCCChannels = 4; 576 577 private enum CenterAlignedMode 578 { 579 EdgeAligned = 0, // Direction depending on direction bit (TIMx_CR1::BIT) 580 CenterAligned1 = 1, // Up and down alternatively, compare interrupt flag set only when counting down 581 CenterAligned2 = 2, // Up and down alternatively, compare interrupt flag set only when counting up 582 CenterAligned3 = 3, // Up and down alternatively, compare interrupt flag set on both up/down counting 583 } 584 585 private enum OutputCompareMode 586 { 587 Frozen = 0, // Comparison between CNT and CCR has no effect on the outputs 588 SetActiveOnMatch = 1, // Output is high when CNT = CCR 589 SetInactiveOnMatch = 2, // Output is low when CNT = CCR 590 ToggleOnMatch = 3, // Output is toggled when CNT = CCR 591 ForceInactive = 4, // Output is forced low 592 ForceActive = 5, // Output is forced high 593 PwmMode1 = 6, // Ascending: output is high when CNT < CCR 594 // Descending: output is high when CNT ≤ CCR 595 PwmMode2 = 7, // Ascending: output is high when CNT ≥ CCR 596 // Descending: output is high when CNT > CCR 597 } 598 599 private enum CaptureCompareSelection 600 { 601 Output = 0, // Channel is configured as an output 602 InputTi1 = 1, // Channel is configured as an input, mapped on TI1 603 InputTi2 = 2, // Channel is configured as an input, mapped on TI2 604 InputTrc = 3, // Channel is configured as an input, mapped on TRC 605 } 606 607 private enum Registers : long 608 { 609 Control1 = 0x0, 610 Control2 = 0x04, 611 SlaveModeControl = 0x08, 612 DmaOrInterruptEnable = 0x0C, 613 Status = 0x10, 614 EventGeneration = 0x14, 615 CaptureOrCompareMode1 = 0x18, 616 CaptureOrCompareMode2 = 0x1C, 617 CaptureOrCompareEnable = 0x20, 618 Counter = 0x24, 619 Prescaler = 0x28, 620 AutoReload = 0x2C, 621 // gap intended 622 RepetitionCounter = 0x30, 623 // gap intended 624 CaptureOrCompare1 = 0x34, 625 CaptureOrCompare2 = 0x38, 626 CaptureOrCompare3 = 0x3C, 627 CaptureOrCompare4 = 0x40, 628 BreakAndDeadTime = 0x44, 629 // gap intended 630 DmaControl = 0x48, 631 DmaAddressForFullTransfer = 0x4C, 632 Option = 0x50 633 } 634 } 635 } 636 637