1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Linq; 10 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Peripherals.CPU; 15 using Antmicro.Renode.Peripherals.Bus; 16 using Antmicro.Renode.Logging; 17 using Antmicro.Renode.Time; 18 19 namespace Antmicro.Renode.Peripherals.Timers 20 { 21 public class MSP430_Timer : BasicWordPeripheral 22 { MSP430_Timer(IMachine machine, MSP430X cpu, int acknowledgeInterrupt, long baseFrequency = 32768, int captureCompareCount = 7)23 public MSP430_Timer(IMachine machine, MSP430X cpu, int acknowledgeInterrupt, long baseFrequency = 32768, int captureCompareCount = 7) : base(machine) 24 { 25 if(captureCompareCount <= 0 || captureCompareCount > 7) 26 { 27 throw new ConstructionException("captureCompareCount should be between 1 and 7"); 28 } 29 30 cpu.InterruptAcknowledged += (interruptIndex) => 31 { 32 if(interruptIndex == acknowledgeInterrupt) 33 { 34 timerInterruptPending[0].Value = false; 35 UpdateInterrupts(); 36 } 37 }; 38 39 TimersCount = captureCompareCount; 40 41 mainTimer = new LimitTimer(machine.ClockSource, baseFrequency, this, "clk", limit: ushort.MaxValue, workMode: WorkMode.OneShot, eventEnabled: true); 42 internalTimers = Enumerable.Range(0, TimersCount) 43 .Select(idx => 44 { 45 var timer = new LimitTimer(machine.ClockSource, baseFrequency, this, $"compare {idx}", limit: ushort.MaxValue, workMode: WorkMode.OneShot, eventEnabled: true, direction: Direction.Ascending); 46 var index = idx; 47 timer.LimitReached += delegate 48 { 49 timerInterruptPending[index].Value = true; 50 UpdateInterrupts(); 51 }; 52 return timer; 53 }) 54 .ToArray() 55 ; 56 57 timerInterruptEnabled = new IFlagRegisterField[TimersCount]; 58 timerInterruptPending = new IFlagRegisterField[TimersCount]; 59 timerCompare = new IValueRegisterField[TimersCount]; 60 61 mainTimer.LimitReached += LimitReached; 62 63 DefineRegisters(); 64 } 65 66 [ConnectionRegionAttribute("interruptVector")] WriteWordToInterruptVector(long offset, ushort value)67 public void WriteWordToInterruptVector(long offset, ushort value) 68 { 69 if(offset != 0) 70 { 71 this.Log(LogLevel.Warning, "Illegal write access at non-zero offset (0x{0:X}) to interruptVector region", offset); 72 } 73 // NOTE: This region is single word wide, so we are ignoring offset argument 74 WriteWord((long)Registers.InterruptVector, value); 75 } 76 77 [ConnectionRegionAttribute("interruptVector")] ReadWordFromInterruptVector(long offset)78 public ushort ReadWordFromInterruptVector(long offset) 79 { 80 if(offset != 0) 81 { 82 this.Log(LogLevel.Warning, "Illegal read access at non-zero offset (0x{0:X}) to interruptVector region", offset); 83 } 84 // NOTE: This region is single word wide, so we are ignoring offset argument 85 return ReadWord((long)Registers.InterruptVector); 86 } 87 88 public int TimersCount { get; } 89 90 public GPIO IRQ0 { get; } = new GPIO(); 91 public GPIO IRQ_IV { get; } = new GPIO(); 92 LimitReached()93 private void LimitReached() 94 { 95 if(timerMode.Value == Mode.UpDown) 96 { 97 mainTimer.Direction = mainTimer.Direction == Direction.Ascending ? Direction.Descending : Direction.Ascending; 98 mainTimer.Value = mainTimer.Direction == Direction.Ascending ? 0 : mainTimer.Limit; 99 } 100 101 if(timerMode.Value != Mode.Stop) 102 { 103 mainTimer.Enabled = true; 104 RecalculateCompareTimers(); 105 } 106 107 interruptOverflowPending.Value |= timerMode.Value == Mode.Up; 108 UpdateInterrupts(); 109 } 110 RecalculateCompareTimers()111 private void RecalculateCompareTimers() 112 { 113 var currentCount = mainTimer.Direction == Direction.Ascending ? mainTimer.Value : mainTimer.Limit - mainTimer.Value; 114 foreach(var entry in internalTimers.Select((Timer, Index) => new { Timer, Index })) 115 { 116 var newLimit = mainTimer.Direction == Direction.Ascending ? timerCompare[entry.Index].Value : mainTimer.Limit - timerCompare[entry.Index].Value; 117 entry.Timer.Value = currentCount; 118 entry.Timer.Limit = newLimit; 119 entry.Timer.Enabled |= mainTimer.Enabled && entry.Timer.Value <= entry.Timer.Limit; 120 } 121 } 122 UpdateInterrupts()123 private void UpdateInterrupts() 124 { 125 var interrupt = timerInterruptPending[0].Value && timerInterruptEnabled[0].Value; 126 this.Log(LogLevel.Debug, "IRQ0: {0}", interrupt); 127 IRQ0.Set(interrupt); 128 129 var interruptVectorIndex = Enumerable 130 .Range(1, internalTimers.Length - 1) 131 .FirstOrDefault(index => timerInterruptPending[index].Value && timerInterruptEnabled[index].Value); 132 133 var interruptVector = interruptVectorIndex > 0; 134 interruptVector |= interruptOverflowEnabled.Value && interruptOverflowPending.Value; 135 this.Log(LogLevel.Debug, "IRQ_IV: {0}", interruptVector); 136 IRQ_IV.Set(interruptVector); 137 } 138 UpdateDivider()139 private void UpdateDivider() 140 { 141 Divider = (1 << (int)clockDivider.Value) * ((int)clockDividerExtended.Value + 1); 142 } 143 UpdateMode()144 private void UpdateMode() 145 { 146 switch(timerMode.Value) 147 { 148 case Mode.Stop: 149 Enabled = false; 150 return; 151 case Mode.Up: 152 mainTimer.Direction = Direction.Ascending; 153 mainTimer.Limit = timerCompare[0].Value; 154 break; 155 case Mode.Continuous: 156 mainTimer.Direction = Direction.Ascending; 157 var bits = timerWidth.Value == 0 ? 16 : 16 - 2 * ((int)timerWidth.Value + 1); 158 mainTimer.Limit = (1UL << bits) - 1; 159 break; 160 case Mode.UpDown: 161 mainTimer.Limit = timerCompare[0].Value; 162 break; 163 default: 164 throw new Exception("unreachable"); 165 } 166 167 mainTimer.Enabled = true; 168 } 169 DefineRegisters()170 private void DefineRegisters() 171 { 172 Registers.Control.Define(this) 173 // NOTE: Interrupt flag 174 .WithFlag(0, out interruptOverflowPending, name: "TBIFG") 175 // NOTE: Interrupt enable 176 .WithFlag(1, out interruptOverflowEnabled, name: "TBIE") 177 // NOTE: Interrupt clear 178 .WithFlag(2, FieldMode.WriteOneToClear, name: "TBCLR", 179 writeCallback: (_, value) => 180 { 181 clockDivider.Value = 0; 182 UpdateDivider(); 183 184 if(timerMode.Value == Mode.UpDown) 185 { 186 mainTimer.Direction = Direction.Ascending; 187 } 188 mainTimer.Value = mainTimer.Direction == Direction.Ascending ? 0 : mainTimer.Limit; 189 }) 190 .WithReservedBits(3, 1) 191 // NOTE: Mode 192 .WithEnumField(4, 2, out timerMode, name: "MC", 193 changeCallback: (_, __) => UpdateMode()) 194 // NOTE: Divider, (1 << value) 195 .WithValueField(6, 2, out clockDivider, name: "ID", 196 changeCallback: (_, __) => UpdateDivider()) 197 // NOTE: Clock select 198 .WithTag("TBSSEL", 8, 2) 199 .WithReservedBits(10, 1) 200 // NOTE: Counter length, 201 // 00b=16, 01b=12, 202 // 10b=10, 11b= 8 203 .WithValueField(11, 2, out timerWidth, name: "CNTL", 204 changeCallback: (_, __) => UpdateMode()) 205 // NOTE: Group select 206 .WithTag("TBCLGRP", 13, 2) 207 .WithReservedBits(15, 1) 208 .WithWriteCallback((_, __) => UpdateInterrupts()) 209 ; 210 211 Registers.CaptureCompareControl0.DefineMany(this, (uint)TimersCount, (register, index) => 212 { 213 register 214 .WithFlag(0, out timerInterruptPending[index], name: "CCIFG") 215 .WithTaggedFlag("COV", 1) 216 .WithTaggedFlag("OUT", 2) 217 .WithTaggedFlag("CCI", 3) 218 .WithFlag(4, out timerInterruptEnabled[index], name: "CCIE") 219 .WithTag("OUTMOD", 5, 3) 220 .WithTaggedFlag("CAP", 8) 221 .WithReservedBits(9, 1) 222 .WithTaggedFlag("SCCI", 10) 223 .WithTaggedFlag("SCS", 11) 224 .WithTag("CCIS", 12, 2) 225 .WithTag("CM", 14, 2) 226 .WithWriteCallback((_, __) => UpdateInterrupts()) 227 ; 228 }); 229 230 Registers.Counter.Define(this) 231 .WithValueField(0, 16, name: "TxAR", 232 valueProviderCallback: _ => mainTimer.Value, 233 writeCallback: (_, value) => mainTimer.Value = value) 234 .WithWriteCallback((_, __) => 235 { 236 RecalculateCompareTimers(); 237 UpdateInterrupts(); 238 }) 239 ; 240 241 Registers.CaptureCompare0.DefineMany(this, (uint)TimersCount, (register, index) => 242 { 243 register 244 .WithValueField(0, 16, out timerCompare[index], name: $"TAxCCR{index}", 245 changeCallback: (_, __) => 246 { 247 UpdateMode(); 248 RecalculateCompareTimers(); 249 }) 250 ; 251 }); 252 253 Registers.InterruptVector.Define(this) 254 .WithValueField(0, 16, name: "TAIV", 255 valueProviderCallback: _ => 256 { 257 int? index = Enumerable.Range(0, internalTimers.Length - 1).FirstOrDefault(idx => timerInterruptPending[idx].Value); 258 if(!index.HasValue && interruptOverflowPending.Value) 259 { 260 index = internalTimers.Length + 1; 261 interruptOverflowPending.Value = false; 262 } 263 else 264 { 265 timerInterruptPending[index.Value].Value = false; 266 } 267 268 UpdateInterrupts(); 269 return (ulong)(index << 1); 270 }, 271 writeCallback: (_, value) => 272 { 273 if(value != 0) 274 { 275 // NOTE: Writes other than zero are no-op 276 return; 277 } 278 279 int? firstIndex = Enumerable.Range(0, internalTimers.Length - 1).FirstOrDefault(index => timerInterruptPending[index].Value); 280 if(firstIndex.HasValue) 281 { 282 timerInterruptPending[firstIndex.Value].Value = false; 283 } 284 else 285 { 286 interruptOverflowEnabled.Value = false; 287 } 288 UpdateInterrupts(); 289 }) 290 ; 291 292 Registers.Expansion0.Define(this) 293 .WithValueField(0, 3, out clockDividerExtended, name: "TAIDEX", 294 changeCallback: (_, __) => UpdateDivider()) 295 .WithReservedBits(3, 13) 296 ; 297 } 298 299 private bool Enabled 300 { 301 get => mainTimer.Enabled; 302 set 303 { 304 mainTimer.Enabled = value; 305 foreach(var timer in internalTimers) 306 { 307 timer.Enabled = value; 308 } 309 } 310 } 311 312 private long Frequency 313 { 314 get => mainTimer.Frequency; 315 set 316 { 317 mainTimer.Frequency = value; 318 foreach(var timer in internalTimers) 319 { 320 timer.Frequency = value; 321 } 322 } 323 } 324 325 private int Divider 326 { 327 get => mainTimer.Divider; 328 set 329 { 330 mainTimer.Divider = value; 331 foreach(var timer in internalTimers) 332 { 333 timer.Divider = value; 334 } 335 } 336 } 337 338 private IFlagRegisterField interruptOverflowEnabled; 339 private IFlagRegisterField interruptOverflowPending; 340 341 private IFlagRegisterField[] timerInterruptEnabled; 342 private IFlagRegisterField[] timerInterruptPending; 343 private IValueRegisterField[] timerCompare; 344 345 private IValueRegisterField clockDivider; 346 private IValueRegisterField clockDividerExtended; 347 348 private IEnumRegisterField<Mode> timerMode; 349 private IValueRegisterField timerWidth; 350 351 private readonly LimitTimer mainTimer; 352 private readonly LimitTimer[] internalTimers; 353 354 private enum Mode 355 { 356 Stop, 357 Up, 358 Continuous, 359 UpDown, 360 } 361 362 private enum Registers 363 { 364 Control = 0x00, 365 CaptureCompareControl0 = 0x02, 366 Counter = 0x10, 367 CaptureCompare0 = 0x12, 368 InterruptVector = 0x2E, 369 Expansion0 = 0x20 370 } 371 } 372 } 373