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 Antmicro.Renode.Core; 8 using Antmicro.Renode.Core.Structure.Registers; 9 using Antmicro.Renode.Utilities; 10 using Antmicro.Renode.Time; 11 12 namespace Antmicro.Renode.Peripherals.Timers 13 { 14 public class EFR32xG24_SYSRTC : BasicDoubleWordPeripheral, IKnownSize 15 { EFR32xG24_SYSRTC(Machine machine, long frequency = 32768)16 public EFR32xG24_SYSRTC(Machine machine, long frequency = 32768) : base(machine) 17 { 18 AppIRQ = new GPIO(); 19 interruptManager = new InterruptManager<Interrupt>(this, AppIRQ); 20 21 limitTimer = new LimitTimer(machine.ClockSource, frequency, this, "limitTimer", uint.MaxValue, direction: Direction.Ascending, autoUpdate: true, eventEnabled: true); 22 limitTimer.LimitReached += () => interruptManager.SetInterrupt(Interrupt.Overflow); 23 24 compare0Timer = new ComparingTimer(machine.ClockSource, frequency, this, "compare0Timer", 25 limit: uint.MaxValue, compare: uint.MaxValue, workMode: WorkMode.Periodic, eventEnabled: true); 26 compare0Timer.CompareReached += HandleCompare0TimerCompareReached; 27 28 compare1Timer = new ComparingTimer(machine.ClockSource, frequency, this, "compare1Timer", 29 limit: uint.MaxValue, compare: uint.MaxValue, workMode: WorkMode.Periodic, eventEnabled: true); 30 compare1Timer.CompareReached += HandleCompare1TimerCompareReached; 31 32 capture0Timer = new ComparingTimer(machine.ClockSource, frequency, this, "capture0Timer", 33 limit: uint.MaxValue, compare: uint.MaxValue, workMode: WorkMode.Periodic, eventEnabled: true); 34 capture0Timer.CompareReached += HandleCapture0TimerCompareReached; 35 36 DefineRegisters(); 37 } 38 Reset()39 public override void Reset() 40 { 41 base.Reset(); 42 limitTimer.Reset(); 43 compare0Timer.Reset(); 44 compare1Timer.Reset(); 45 capture0Timer.Reset(); 46 interruptManager.Reset(); 47 AppIRQ.Unset(); 48 } 49 50 public long Size => 0x4000; 51 52 public GPIO AppIRQ { get; } 53 DefineRegisters()54 private void DefineRegisters() 55 { 56 Register.IpVersion.Define(this, 0x1) 57 .WithTag("IPVERSION", 0, 32); 58 59 Register.Enable.Define(this) 60 .WithTaggedFlag("EN", 0) 61 .WithTaggedFlag("DISABLING", 1) 62 .WithReservedBits(2, 30); 63 64 Register.SoftwareReset.Define(this) 65 .WithTaggedFlag("SWRST", 0) 66 .WithTaggedFlag("RESETTING", 1) 67 .WithReservedBits(2, 30); 68 69 Register.Config.Define(this) 70 .WithTaggedFlag("DEBUGRUN", 0) 71 .WithReservedBits(1, 31); 72 73 Register.Command.Define(this) 74 .WithFlag(0, FieldMode.Write, 75 writeCallback: (_, value) => 76 { 77 if(!value) 78 { 79 return; 80 } 81 limitTimer.Enabled = true; 82 compare0Timer.Enabled = true; 83 compare1Timer.Enabled = true; 84 capture0Timer.Enabled = true; 85 }, 86 name: "START") 87 .WithFlag(1, FieldMode.Write, 88 writeCallback: (_, value) => 89 { 90 if(!value) 91 { 92 return; 93 } 94 limitTimer.Enabled = false; 95 compare0Timer.Enabled = false; 96 compare1Timer.Enabled = false; 97 capture0Timer.Enabled = false; 98 }, 99 name: "STOP") 100 .WithReservedBits(2, 30); 101 102 Register.Status.Define(this) 103 .WithFlag(0, FieldMode.Read, 104 valueProviderCallback: _ => compare0Timer.Enabled, 105 name: "RUNNING") 106 .WithTaggedFlag("LOCKSTATUS", 1) 107 .WithReservedBits(2, 30); 108 109 Register.Counter.Define(this) 110 .WithValueField(0, 32, 111 valueProviderCallback: _ => compare0Timer.Value, 112 writeCallback: (_, value) => 113 { 114 limitTimer.Value = value; 115 compare0Timer.Value = value; 116 compare1Timer.Value = value; 117 capture0Timer.Value = value; 118 }, 119 name: "CNT"); 120 121 Register.SyncBusy.Define(this) 122 .WithTaggedFlag("START", 0) 123 .WithTaggedFlag("STOP", 1) 124 .WithTaggedFlag("CNT", 2) 125 .WithReservedBits(3, 29); 126 127 Register.Lock.Define(this) 128 .WithTag("LOCKKEY", 0, 16) 129 .WithReservedBits(16, 16); 130 131 Register.Group0Control.Define(this) 132 .WithFlag(0, out group0Compare0Enable, name: "CMP0EN") 133 .WithFlag(1, out group0Compare1Enable, name: "CMP1EN") 134 .WithFlag(2, out group0Capture0Enable, name: "CAP0EN") 135 .WithTag("CMP0CMOA", 3, 3) 136 .WithTag("CMP1CMOA", 6, 3) 137 .WithTag("CAP0EDGE", 9, 2) 138 .WithReservedBits(11, 21) 139 .WithChangeCallback((_, __) => 140 { 141 compare0Timer.Enabled = group0Compare0Enable.Value; 142 compare1Timer.Enabled = group0Compare1Enable.Value; 143 capture0Timer.Enabled = group0Capture0Enable.Value; 144 limitTimer.Enabled = compare0Timer.Enabled | compare1Timer.Enabled | capture0Timer.Enabled; 145 }); 146 147 Register.Group0Compare0.Define(this) 148 .WithValueField(0, 32, out group0Compare0Value, 149 writeCallback: (_, val) => compare0Timer.Compare = group0Compare0Value.Value, 150 name: "CMP0VALUE"); 151 152 Register.Group0Compare1.Define(this) 153 .WithValueField(0, 32, out group0Compare1Value, 154 writeCallback: (_, val) => compare1Timer.Compare = group0Compare1Value.Value, 155 name: "CMP1VALUE"); 156 157 Register.Group0Capture0.Define(this) 158 .WithValueField(0, 32, out group0Capture0Value, 159 writeCallback: (_, val) => capture0Timer.Compare = group0Capture0Value.Value, 160 name: "CAP0VALUE"); 161 162 Register.Group0SyncBusy.Define(this) 163 .WithTaggedFlag("CTRL", 0) 164 .WithTaggedFlag("CMP0VALUE", 1) 165 .WithTaggedFlag("CMP1VALUE", 2) 166 .WithReservedBits(3, 29); 167 168 RegistersCollection.AddRegister((long)Register.Group0InterruptFlags, interruptManager.GetRegister<DoubleWordRegister>( 169 valueProviderCallback: (irq, _) => interruptManager.IsSet(irq), 170 writeCallback: (irq, _, newValue) => interruptManager.SetInterrupt(irq, newValue))); 171 172 RegistersCollection.AddRegister((long)Register.Group0InterruptEnable, interruptManager.GetInterruptEnableRegister<DoubleWordRegister>()); 173 174 RegistersCollection.AddRegister((long)Register.Group0InterruptEnable_Set, interruptManager.GetInterruptEnableSetRegister<DoubleWordRegister>()); 175 176 RegistersCollection.AddRegister((long)Register.Group0InterruptFlags_Clear, interruptManager.GetInterruptClearRegister<DoubleWordRegister>()); 177 178 RegistersCollection.AddRegister((long)Register.Group0InterruptEnable_Clear, interruptManager.GetInterruptEnableClearRegister<DoubleWordRegister>()); 179 } 180 HandleCompare0TimerCompareReached()181 private void HandleCompare0TimerCompareReached() 182 { 183 if(group0Compare0Enable.Value) 184 { 185 interruptManager.SetInterrupt(Interrupt.Compare0Match); 186 } 187 } 188 HandleCompare1TimerCompareReached()189 private void HandleCompare1TimerCompareReached() 190 { 191 if(group0Compare1Enable.Value) 192 { 193 interruptManager.SetInterrupt(Interrupt.Compare1Match); 194 } 195 } 196 HandleCapture0TimerCompareReached()197 private void HandleCapture0TimerCompareReached() 198 { 199 if(group0Capture0Enable.Value) 200 { 201 interruptManager.SetInterrupt(Interrupt.Capture0); 202 } 203 } 204 205 private readonly InterruptManager<Interrupt> interruptManager; 206 private readonly LimitTimer limitTimer; 207 private readonly ComparingTimer compare0Timer; 208 private readonly ComparingTimer compare1Timer; 209 private readonly ComparingTimer capture0Timer; 210 211 private IFlagRegisterField group0Compare0Enable; 212 private IFlagRegisterField group0Compare1Enable; 213 private IFlagRegisterField group0Capture0Enable; 214 private IValueRegisterField group0Compare0Value; 215 private IValueRegisterField group0Compare1Value; 216 private IValueRegisterField group0Capture0Value; 217 218 private enum Interrupt 219 { 220 Overflow, 221 Compare0Match, 222 Compare1Match, 223 Capture0 224 } 225 226 private enum Register 227 { 228 IpVersion = 0x0000, 229 Enable = 0x0004, 230 SoftwareReset = 0x0008, 231 Config = 0x000C, 232 Command = 0x0010, 233 Status = 0x0014, 234 Counter = 0x0018, 235 SyncBusy = 0x001C, 236 Lock = 0x0020, 237 Group0InterruptFlags = 0x0040, 238 Group0InterruptEnable = 0x0044, 239 Group0Control = 0x0048, 240 Group0Compare0 = 0x004C, 241 Group0Compare1 = 0x0050, 242 Group0Capture0 = 0x0054, 243 Group0SyncBusy = 0x0058, 244 245 IpVersion_Set = 0x1000, 246 Enable_Set = 0x1004, 247 SoftwareReset_Set = 0x1008, 248 Config_Set = 0x100C, 249 Command_Set = 0x1010, 250 Status_Set = 0x1014, 251 Counter_Set = 0x1018, 252 SyncBusy_Set = 0x101C, 253 Lock_Set = 0x1020, 254 Group0InterruptFlags_Set = 0x1040, 255 Group0InterruptEnable_Set = 0x1044, 256 Group0Control_Set = 0x1048, 257 Group0Compare0_Set = 0x104C, 258 Group0Compare1_Set = 0x1050, 259 Group0Capture0_Set = 0x1054, 260 Group0SyncBusy_Set = 0x1058, 261 262 IpVersion_Clear = 0x2000, 263 Enable_Clear = 0x2004, 264 SoftwareReset_Clear = 0x2008, 265 Config_Clear = 0x200C, 266 Command_Clear = 0x2010, 267 Status_Clear = 0x2014, 268 Counter_Clear = 0x2018, 269 SyncBusy_Clear = 0x201C, 270 Lock_Clear = 0x2020, 271 Group0InterruptFlags_Clear = 0x2040, 272 Group0InterruptEnable_Clear = 0x2044, 273 Group0Control_Clear = 0x2048, 274 Group0Compare0_Clear = 0x204C, 275 Group0Compare1_Clear = 0x2050, 276 Group0Capture0_Clear = 0x2054, 277 Group0SyncBusy_Clear = 0x2058, 278 279 IpVersion_Toggle = 0x3000, 280 Enable_Toggle = 0x3004, 281 SoftwareReset_Toggle = 0x3008, 282 Config_Toggle = 0x300C, 283 Command_Toggle = 0x3010, 284 Status_Toggle = 0x3014, 285 Counter_Toggle = 0x3018, 286 SyncBusy_Toggle = 0x301C, 287 Lock_Toggle = 0x3020, 288 Group0InterruptFlags_Toggle = 0x3040, 289 Group0InterruptEnable_Toggle = 0x3044, 290 Group0Control_Toggle = 0x3048, 291 Group0Compare0_Toggle = 0x304C, 292 Group0Compare1_Toggle = 0x3050, 293 Group0Capture0_Toggle = 0x3054, 294 Group0SyncBusy_Toggle = 0x3058, 295 } 296 } 297 } 298