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 Antmicro.Renode.Core; 8 using Antmicro.Renode.Core.Structure.Registers; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Utilities; 12 using System; 13 using System.Linq; 14 15 namespace Antmicro.Renode.Peripherals.Timers 16 { 17 public class EFR32xG2_RTCC : BasicDoubleWordPeripheral, IKnownSize 18 { EFR32xG2_RTCC(IMachine machine, long frequency)19 public EFR32xG2_RTCC(IMachine machine, long frequency) : base(machine) 20 { 21 IRQ = new GPIO(); 22 interruptManager = new InterruptManager<Interrupt>(this); 23 24 innerTimer = new EFR32_RTCCCounter(machine, frequency, this, "rtcc", CounterWidth, PreCounterWidth, NumberOfCaptureCompareChannels); 25 innerTimer.LimitReached += delegate 26 { 27 interruptManager.SetInterrupt(Interrupt.Overflow); 28 }; 29 innerTimer.CounterTicked += delegate 30 { 31 interruptManager.SetInterrupt(Interrupt.CounterTick); 32 }; 33 34 for(var idx = 0; idx < NumberOfCaptureCompareChannels; ++idx) 35 { 36 var i = idx; 37 innerTimer.Channels[i].CompareReached += delegate 38 { 39 interruptManager.SetInterrupt(InterruptForChannel(i)); 40 }; 41 } 42 43 DefineRegisters(); 44 } 45 Reset()46 public override void Reset() 47 { 48 base.Reset(); 49 innerTimer.Reset(); 50 interruptManager.Reset(); 51 } 52 53 [ConnectionRegionAttribute("set")] ReadDoubleWordSet(long offset)54 public uint ReadDoubleWordSet(long offset) 55 { 56 return ReadDoubleWord(offset); 57 } 58 59 [ConnectionRegionAttribute("set")] WriteDoubleWordSet(long offset, uint mask)60 public void WriteDoubleWordSet(long offset, uint mask) 61 { 62 var value = ReadDoubleWord(offset); 63 value &= ~(uint)mask; 64 WriteDoubleWord(offset, value); 65 } 66 67 [ConnectionRegionAttribute("clear")] ReadDoubleWordClear(long offset)68 public uint ReadDoubleWordClear(long offset) 69 { 70 return ReadDoubleWord(offset); 71 } 72 73 [ConnectionRegionAttribute("clear")] WriteDoubleWordClear(long offset, uint mask)74 public void WriteDoubleWordClear(long offset, uint mask) 75 { 76 var value = ReadDoubleWord(offset); 77 value &= ~(uint)mask; 78 WriteDoubleWord(offset, value); 79 } 80 81 [ConnectionRegionAttribute("toggle")] ReadDoubleWordToggle(long offset)82 public uint ReadDoubleWordToggle(long offset) 83 { 84 return ReadDoubleWord(offset); 85 } 86 87 [ConnectionRegionAttribute("toggle")] WriteDoubleWordToggle(long offset, uint mask)88 public void WriteDoubleWordToggle(long offset, uint mask) 89 { 90 var value = ReadDoubleWord(offset); 91 value ^= (uint)mask; 92 WriteDoubleWord(offset, value); 93 } 94 95 public long Size => 0x1000; 96 97 [IrqProvider] 98 public GPIO IRQ { get; } 99 DefineRegisters()100 private void DefineRegisters() 101 { 102 Register.IpVersion.Define(this) 103 .WithTag("IPVERSION", 0, 32); 104 105 Register.Enable.Define(this) 106 .WithTaggedFlag("EN", 0) 107 .WithReservedBits(1, 31); 108 109 Register.Configuration.Define(this) 110 .WithTaggedFlag("DEBUGRUN", 0) 111 .WithTaggedFlag("PRECNTCCV0TOP", 1) 112 .WithTaggedFlag("CNTCCV1TOP", 2) 113 .WithTaggedFlag("CNTTICK", 3) 114 .WithValueField(4, 4, 115 writeCallback: (_, value) => innerTimer.Prescaler = (int)Math.Pow(2, value), 116 valueProviderCallback: _ => (uint)Math.Log(innerTimer.Prescaler, 2), 117 name: "CNTPRESC") 118 .WithReservedBits(8, 24); 119 120 Register.Command.Define(this) 121 .WithEnumField<DoubleWordRegister, Command>(0, 2, FieldMode.Write, writeCallback: (_, value) => 122 { 123 switch(value) 124 { 125 case Command.None: 126 break; 127 case Command.Start: 128 innerTimer.Enabled = true; 129 break; 130 case Command.Stop: 131 innerTimer.Enabled = false; 132 break; 133 default: 134 this.Log(LogLevel.Warning, "Unsupported command combination: {0}", value); 135 break; 136 } 137 }, name: "START/STOP") 138 .WithReservedBits(2, 30); 139 140 Register.Status.Define(this) 141 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => innerTimer.Enabled, name: "RUNNING") 142 .WithTaggedFlag("RTCCLOCKSTATUS", 1) 143 .WithReservedBits(2, 30); 144 145 RegistersCollection.AddRegister((long)Register.InterruptFlags, interruptManager.GetRegister<DoubleWordRegister>( 146 valueProviderCallback: (irq, _) => interruptManager.IsSet(irq), 147 writeCallback: (irq, _, newValue) => interruptManager.SetInterrupt(irq, newValue))); 148 RegistersCollection.AddRegister((long)Register.InterruptEnable, interruptManager.GetInterruptEnableRegister<DoubleWordRegister>()); 149 150 Register.PreCounter.Define(this) 151 .WithValueField(0, 15, 152 writeCallback: (_, value) => innerTimer.PreCounter = value, 153 valueProviderCallback: _ => innerTimer.PreCounter, 154 name: "PRECNT") 155 .WithReservedBits(15, 17); 156 157 Register.CounterValue.Define(this) 158 .WithValueField(0, 32, 159 writeCallback: (_, value) => innerTimer.Counter = value, 160 valueProviderCallback: _ => innerTimer.Counter, 161 name: "CNT"); 162 163 Register.CombinedPreCounterValue.Define(this) 164 .WithValueField(0, 15, FieldMode.Read, 165 valueProviderCallback: _ => innerTimer.PreCounter, 166 name: "PRECNT") 167 .WithValueField(15, 17, FieldMode.Read, 168 valueProviderCallback: _ => innerTimer.Counter, 169 name: "CNTLSB"); 170 171 Register.SyncBusy.Define(this) 172 .WithReservedBits(0, 5) 173 .WithTaggedFlag("CMD", 5) 174 .WithReservedBits(6, 26); 175 176 Register.Lock.Define(this) 177 .WithTag("LOCK", 0, 15) 178 .WithReservedBits(15, 17); 179 180 Register.ChanelControlC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) => 181 register 182 .WithEnumField<DoubleWordRegister, EFR32_RTCCCounter.CCChannelMode>(0, 2, 183 writeCallback: (_, value) => innerTimer.Channels[idx].Mode = value, 184 valueProviderCallback: _ => innerTimer.Channels[idx].Mode, 185 name: "MODE") 186 .WithTag("CMOA", 2, 2) 187 .WithEnumField<DoubleWordRegister, EFR32_RTCCCounter.CCChannelComparisonBase>(4, 1, 188 writeCallback: (_, value) => innerTimer.Channels[idx].ComparisonBase = value, 189 valueProviderCallback: _ => innerTimer.Channels[idx].ComparisonBase, 190 name: "COMPBASE") 191 .WithTag("ICEDGE", 5, 2) 192 .WithReservedBits(7, 25) 193 , stepInBytes: StepBetweenCaptureCompareRegisters); 194 195 Register.OutputCompareValueC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) => 196 register 197 .WithValueField(0, 32, 198 writeCallback: (_, value) => innerTimer.Channels[idx].CompareValue = value, 199 valueProviderCallback: _ => (uint)innerTimer.Channels[idx].CompareValue, 200 name: $"OC[{idx}]") 201 , stepInBytes: StepBetweenCaptureCompareRegisters); 202 203 Register.InputCaptureValueC0.DefineMany(this, NumberOfCaptureCompareChannels, (register, idx) => 204 register 205 .WithTag($"IC[{idx}]", 0, 32) 206 , stepInBytes: StepBetweenCaptureCompareRegisters); 207 } 208 InterruptForChannel(int channel)209 private Interrupt InterruptForChannel(int channel) 210 { 211 switch(channel) 212 { 213 case 0: return Interrupt.Channel0; 214 case 1: return Interrupt.Channel1; 215 case 2: return Interrupt.Channel2; 216 default: throw new System.NotImplementedException($"Channel {channel} does not have an IRQ set up"); 217 } 218 } 219 220 private readonly InterruptManager<Interrupt> interruptManager; 221 private readonly EFR32_RTCCCounter innerTimer; 222 private const int NumberOfCaptureCompareChannels = 3; 223 private const int StepBetweenCaptureCompareRegisters = Register.ChanelControlC1 - Register.ChanelControlC0; 224 private const int CounterWidth = 32; 225 private const int PreCounterWidth = 15; 226 227 private enum Interrupt 228 { 229 Overflow, 230 CounterTick, 231 [NotSettable] 232 Reserved0, 233 [NotSettable] 234 Reserved1, 235 Channel0, 236 [NotSettable] 237 Reserved2, 238 Channel1, 239 [NotSettable] 240 Reserved3, 241 Channel2, 242 } 243 244 private enum Command 245 { 246 None = 0b00, 247 Start = 0b01, 248 Stop = 0b10, 249 } 250 251 private enum Register 252 { 253 IpVersion = 0x0, 254 Enable = 0x4, 255 Configuration = 0x8, 256 Command = 0xC, 257 Status = 0x10, 258 InterruptFlags = 0x14, 259 InterruptEnable = 0x18, 260 PreCounter = 0x1C, 261 CounterValue = 0x20, 262 CombinedPreCounterValue = 0x24, 263 SyncBusy = 0x28, 264 Lock = 0x2C, 265 ChanelControlC0 = 0x30, 266 OutputCompareValueC0 = 0x34, 267 InputCaptureValueC0 = 0x38, 268 ChanelControlC1 = 0x3C, 269 OutputCompareValueC1 = 0x40, 270 InputCaptureValueC1 = 0x44, 271 ChanelControlC2 = 0x48, 272 OutputCompareValueC2 = 0x4C, 273 InputCaptureValueC3 = 0x50, 274 } 275 } 276 } 277