1 // 2 // Copyright (c) 2010-2019 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.Time; 11 using Antmicro.Renode.Utilities; 12 using System; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class EFR32_Timer : BasicDoubleWordPeripheral, IKnownSize 17 { EFR32_Timer(IMachine machine, long frequency, TimerWidth width)18 public EFR32_Timer(IMachine machine, long frequency, TimerWidth width) : base(machine) 19 { 20 IRQ = new GPIO(); 21 this.width = width; 22 interruptManager = new InterruptManager<Interrupt>(this); 23 24 innerTimer = new LimitTimer(machine.ClockSource, frequency, this, "timer", limit: (1UL << (int)width) - 1, direction: Direction.Ascending, eventEnabled: true, autoUpdate: true); 25 innerTimer.LimitReached += LimitReached; 26 DefineRegisters(); 27 } 28 Reset()29 public override void Reset() 30 { 31 base.Reset(); 32 interruptManager.Reset(); 33 innerTimer.Reset(); 34 } 35 36 public long Size => 0x400; 37 38 [IrqProvider] 39 public GPIO IRQ { get; } 40 DefineRegisters()41 private void DefineRegisters() 42 { 43 Registers.Control.Define(this) 44 .WithEnumField(0, 2, changeCallback: (Mode _, Mode value) => SetMode(value), name: "MODE") 45 .WithReservedBits(2, 1) 46 .WithTaggedFlag("SYNC", 3) 47 .WithFlag(4, changeCallback: (_, value) => innerTimer.Mode = value ? WorkMode.OneShot : WorkMode.Periodic, name: "OSMEN") 48 .WithTaggedFlag("QDM", 5) 49 .WithTaggedFlag("DEBUGRUN", 6) 50 .WithTaggedFlag("DMACLRACT", 7) 51 .WithTag("RISEA", 8, 2) 52 .WithTag("FALLA", 10, 2) 53 .WithReservedBits(12, 1) 54 .WithTaggedFlag("X2CNT", 13) 55 .WithTaggedFlag("DISSYNCOUT", 14) 56 .WithReservedBits(15, 1) 57 .WithTag("CLKSEL", 16, 2) 58 .WithReservedBits(18, 6) 59 .WithValueField(24, 4, changeCallback: (_, value) => { 60 if(value <= 10) 61 { 62 innerTimer.Divider = 2 << (int)value; 63 } 64 else 65 { 66 this.Log(LogLevel.Warning, "Trying to set the prescaler to an invalid value: {0}, ignoring.", 1 << (int)value); 67 } 68 }, name: "PRESC") 69 .WithTaggedFlag("ATI", 28) 70 .WithTaggedFlag("RSSCOIST", 29) 71 .WithReservedBits(30, 2) 72 ; 73 74 Registers.Command.Define(this) 75 .WithFlag(0, FieldMode.Set, changeCallback: (_, __) => innerTimer.Enabled = false) 76 .WithFlag(1, FieldMode.Set, changeCallback: (_, __) => innerTimer.Enabled = true) 77 ; 78 79 Registers.Status.Define(this) 80 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => innerTimer.Enabled) 81 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => innerTimer.Direction == Direction.Descending) 82 .WithTaggedFlag("TOPBV", 2) 83 .WithReservedBits(3, 5) 84 .WithTaggedFlag("CCVBV0", 8) 85 .WithTaggedFlag("CCVBV1", 9) 86 .WithTaggedFlag("CCVBV2", 10) 87 .WithTaggedFlag("CCVBV2", 11) 88 .WithReservedBits(12, 4) 89 .WithTaggedFlag("ICV0", 16) 90 .WithTaggedFlag("ICV1", 17) 91 .WithTaggedFlag("ICV2", 18) 92 .WithTaggedFlag("ICV3", 19) 93 .WithReservedBits(20, 4) 94 .WithTaggedFlag("CCPOL0", 24) 95 .WithTaggedFlag("CCPOL1", 25) 96 .WithTaggedFlag("CCPOL2", 26) 97 .WithTaggedFlag("CCPOL3", 27) 98 .WithReservedBits(28, 4) 99 ; 100 101 RegistersCollection.AddRegister((long)Registers.InterruptFlag, interruptManager.GetMaskedInterruptFlagRegister<DoubleWordRegister>()); 102 RegistersCollection.AddRegister((long)Registers.InterruptFlagSet, interruptManager.GetInterruptSetRegister<DoubleWordRegister>()); 103 RegistersCollection.AddRegister((long)Registers.InterruptFlagClear, interruptManager.GetInterruptClearRegister<DoubleWordRegister>()); 104 RegistersCollection.AddRegister((long)Registers.InterruptEnable, interruptManager.GetInterruptEnableRegister<DoubleWordRegister>()); 105 106 Registers.CounterTopValue.Define(this) 107 .WithValueField(0, (int)width, writeCallback: (_, value) => innerTimer.Limit = value, valueProviderCallback: _ => (uint)innerTimer.Limit, name: "TOP") 108 ; 109 110 Registers.CounterValue.Define(this) 111 .WithValueField(0, (int)width, writeCallback: (_, value) => innerTimer.Value = value, valueProviderCallback: _ => (uint)innerTimer.Value, name: "CNT") 112 ; 113 114 } 115 LimitReached()116 private void LimitReached() 117 { 118 if(innerTimer.Direction == Direction.Descending) 119 { 120 interruptManager.SetInterrupt(Interrupt.Underflow); 121 } 122 else 123 { 124 interruptManager.SetInterrupt(Interrupt.Overflow); 125 } 126 } 127 SetMode(Mode value)128 private void SetMode(Mode value) 129 { 130 if(mode == Mode.QuadratureDecoder || mode == Mode.UpDown) 131 { 132 this.Log(LogLevel.Warning, "Unsupported mode {0}", mode); 133 return; 134 } 135 mode = value; 136 innerTimer.Direction = mode == Mode.Up ? Direction.Ascending : Direction.Descending; 137 } 138 139 private LimitTimer innerTimer; 140 private Mode mode; 141 private TimerWidth width; 142 private InterruptManager<Interrupt> interruptManager; 143 144 public enum TimerWidth 145 { 146 Bit16 = 16, 147 Bit32 = 32, 148 } 149 150 private enum Mode 151 { 152 Up, 153 Down, 154 UpDown, 155 QuadratureDecoder, 156 } 157 158 private enum Interrupt 159 { 160 Overflow, 161 Underflow, 162 DirectionChanged, 163 [NotSettable] 164 Reserved, 165 CompareCaptureChannel0, 166 CompareCaptureChannel1, 167 CompareCaptureChannel2, 168 CompareCaptureChannel3, 169 InputCaptureBufferOverflow0, 170 InputCaptureBufferOverflow1, 171 InputCaptureBufferOverflow2, 172 InputCaptureBufferOverflow3, 173 } 174 175 private enum Registers 176 { 177 Control = 0x0, 178 Command = 0x4, 179 Status = 0x8, 180 InterruptFlag = 0xC, 181 InterruptFlagSet = 0x10, 182 InterruptFlagClear = 0x14, 183 InterruptEnable = 0x18, 184 CounterTopValue = 0x1C, 185 CounterTopValueBuffer = 0x20, 186 CounterValue = 0x24, 187 ConfigurationLock = 0x2C, 188 IORoutingPinEnable = 0x30, 189 IORoutingLocation0 = 0x34, 190 IORoutingLocation2 = 0x3C, 191 CCChannelControl0 = 0x60, 192 CCChannelValue0 = 0x64, 193 CCChannelValuePeek0 = 0x68, 194 CCChannelBuffer0 = 0x6C, 195 CCChannelControl1 = 0x70, 196 CCChannelValue1 = 0x74, 197 CCChannelValuePeek1 = 0x78, 198 CCChannelBuffer1 = 0x7C, 199 CCChannelControl2 = 0x80, 200 CCChannelValue2 = 0x84, 201 CCChannelValuePeek2 = 0x88, 202 CCChannelBuffer2 = 0x8C, 203 CCChannelControl3 = 0x90, 204 CCChannelValue3 = 0x94, 205 CCChannelValuePeek3 = 0x98, 206 CCChannelBuffer3 = 0x9C, 207 DTIControl = 0xA0, 208 DTITimeControl = 0xA4, 209 DTIFaultConfiguration = 0xA8, 210 DTIOutputGenerationEnable = 0xAC, 211 DTIFault = 0xB0, 212 DTIFaultClear = 0xB4, 213 DTIConfigurationLock = 0xB8, 214 } 215 } 216 } 217