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.Collections.Generic; 8 using System.Collections.ObjectModel; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Time; 14 15 namespace Antmicro.Renode.Peripherals.Timers 16 { 17 public class S32K_LPTMR : BasicDoubleWordPeripheral, IKnownSize 18 { S32K_LPTMR(IMachine machine, long frequency)19 public S32K_LPTMR(IMachine machine, long frequency) : base(machine) 20 { 21 innerTimer = new ComparingTimer(machine.ClockSource, frequency, this, "lptmr", limit: 0xFFFF, direction: Direction.Ascending, 22 enabled: false, eventEnabled: true, workMode: WorkMode.Periodic, compare: 0xFFFF, divider: 2); 23 24 innerTimer.CompareReached += CompareReached; 25 26 IRQ = new GPIO(); 27 28 DefineRegisters(); 29 } 30 Reset()31 public override void Reset() 32 { 33 base.Reset(); 34 innerTimer.Reset(); 35 prescaleValue = 0; 36 prescalerBypass = false; 37 compare = 0; 38 latchedTimerValue = 0; 39 UpdateInterrupt(); 40 } 41 42 public GPIO IRQ { get; } 43 44 public long Size => 0x1000; 45 UpdateInterrupt()46 private void UpdateInterrupt() 47 { 48 var value = true; 49 value &= compareFlag.Value; 50 value &= interruptEnable.Value; 51 IRQ.Set(value); 52 } 53 CompareReached()54 private void CompareReached() 55 { 56 compareFlag.Value = true; 57 if(!freeRunningCounter.Value) 58 { 59 innerTimer.Value = 0; 60 } 61 UpdateInterrupt(); 62 } 63 UpdateDivider()64 private void UpdateDivider() 65 { 66 if(!prescalerBypass) 67 { 68 innerTimer.Divider = (uint)System.Math.Pow(2, prescaleValue + 1); 69 } 70 } 71 DefineRegisters()72 private void DefineRegisters() 73 { 74 Registers.ControlStatus.Define(this) 75 .WithFlag(0, out enabled, name: "TEN", changeCallback: (_, value) => 76 { 77 innerTimer.Enabled = value; 78 if(!value) 79 { 80 innerTimer.Value = 0; 81 compareFlag.Value = false; 82 } 83 }) 84 .WithTaggedFlag("TMS", 1) 85 .WithFlag(2, out freeRunningCounter, name: "TFC") 86 .WithTaggedFlag("TPP", 3) 87 .WithTag("TPS", 4, 2) 88 .WithFlag(6, out interruptEnable, name: "TIE") 89 .WithFlag(7, out compareFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "TCF") 90 .WithTaggedFlag("TDRE", 8) 91 .WithReservedBits(9, 23) 92 .WithWriteCallback((_, __) => UpdateInterrupt()) 93 ; 94 95 Registers.Prescale.Define(this) 96 .WithTag("PCS", 0, 2) 97 .WithFlag(2, name: "PBYP", changeCallback: (_, value) => 98 { 99 if(enabled.Value) 100 { 101 this.Log(LogLevel.Warning, "Trying to update the prescaler bypass value with LPTIMR enabled - ignoring..."); 102 return; 103 } 104 prescalerBypass = value; 105 }) 106 .WithValueField(3, 4, name: "PRESCALE", changeCallback: (_, value) => 107 { 108 if(enabled.Value) 109 { 110 this.Log(LogLevel.Warning, "Trying to update the prescale value with LPTIMR enabled - ignoring..."); 111 return; 112 } 113 prescaleValue = (uint)value; 114 }, valueProviderCallback: _ => prescaleValue) // we keep the prescaleValue not to have to calculate logarithms 115 .WithReservedBits(7, 25) 116 .WithWriteCallback((_, __) => UpdateDivider()) 117 ; 118 119 Registers.Compare.Define(this) 120 .WithValueField(0, 16, name: "CMR", writeCallback: (_, value) => 121 { 122 if(enabled.Value && !compareFlag.Value) 123 { 124 this.Log(LogLevel.Warning, "Trying to update the compare value while the timer is enabled and TCF is not set, ignoring..."); 125 return; 126 } 127 if(value == 0) 128 { 129 //set tcf until disabled. May be unhandled, because setting compare to 0 can be difficult 130 this.Log(LogLevel.Warning, "Trying to set CMR to 0, this is currently not handled"); 131 } 132 compare = (uint)value; 133 134 }) 135 .WithReservedBits(16, 16) 136 ; 137 138 // value written is not relevant - used only to latch the current value 139 Registers.Counter.Define(this) 140 .WithValueField(0, 16, writeCallback: (_, __) => latchedTimerValue = (uint)innerTimer.Value, valueProviderCallback: _ => latchedTimerValue, name: "CNR") 141 .WithReservedBits(16, 16) 142 ; 143 } 144 145 private IFlagRegisterField enabled; 146 private IFlagRegisterField compareFlag; 147 private IFlagRegisterField interruptEnable; 148 private IFlagRegisterField freeRunningCounter; 149 150 private uint compare; 151 private uint latchedTimerValue; 152 private bool prescalerBypass; 153 private uint prescaleValue; 154 155 private readonly ComparingTimer innerTimer; 156 157 private enum Registers 158 { 159 ControlStatus = 0x0, 160 Prescale = 0x4, 161 Compare = 0x8, 162 Counter = 0xC, 163 } 164 } 165 } 166