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 System; 8 using System.Linq; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Time; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class ARM_SP804_Timer : BasicDoubleWordPeripheral, IKnownSize 17 { ARM_SP804_Timer(IMachine machine, long frequency = 1000000)18 public ARM_SP804_Timer(IMachine machine, long frequency = 1000000) : base(machine) 19 { 20 DefineRegisters(); 21 for(var i = 0; i < NumberOfTimers; i++) 22 { 23 var j = i; 24 innerTimers[j] = new LimitTimer(machine.ClockSource, frequency, this, ((Timer)j).ToString(), limit: uint.MaxValue, eventEnabled: true, autoUpdate: true); 25 innerTimers[j].LimitReached += delegate 26 { 27 this.Log(LogLevel.Noisy, "{0}: limit reached", (Timer)j); 28 29 if(backgroundLimitSet[j]) 30 { 31 innerTimers[j].Limit = backgroundLimit[j]; 32 backgroundLimitSet[j] = false; 33 } 34 35 interruptPending[j].Value = true; 36 UpdateInterrupts(); 37 }; 38 } 39 } 40 Reset()41 public override void Reset() 42 { 43 base.Reset(); 44 for(var i = 0; i < NumberOfTimers; i++) 45 { 46 innerTimers[i].Reset(); 47 innerTimers[i].Limit = TimerLimitResetValue; 48 backgroundLimit[i] = TimerLimitResetValue; 49 backgroundLimitSet[i] = false; 50 } 51 UpdateInterrupts(); 52 } 53 54 public long Size => 0x1000; 55 56 public GPIO IRQ { get; } = new GPIO(); 57 DefineRegisters()58 private void DefineRegisters() 59 { 60 Registers.Timer1Load.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) => 61 { 62 reg.WithValueField(0, 32, name: "Load", 63 valueProviderCallback: _ => innerTimers[idx].Limit, 64 writeCallback: (_, value) => 65 { 66 innerTimers[idx].Limit = value; 67 } 68 ); 69 }); 70 71 Registers.Timer1Value.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, resetValue: 0xFFFFFFFF, setup: (reg, idx) => 72 { 73 reg.WithValueField(0, 32, FieldMode.Read, name: "Value", 74 valueProviderCallback: _ => 75 { 76 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 77 { 78 // being here means we are on the CPU thread 79 cpu.SyncTime(); 80 } 81 return (uint)innerTimers[idx].Value; 82 } 83 ); 84 }); 85 86 Registers.Timer1Control.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, resetValue: 0x20, setup: (reg, idx) => 87 { 88 reg.WithFlag(0, name: "One Shot", 89 writeCallback: (_, val) => 90 { 91 innerTimers[idx].Mode = val ? WorkMode.OneShot : WorkMode.Periodic; 92 }); 93 reg.WithTaggedFlag("Timer Size", 1); 94 reg.WithEnumField<DoubleWordRegister, PrescalerMode>(2, 2, name: "Timer Prescaler", 95 writeCallback: (_, val) => 96 { 97 switch(val) 98 { 99 case PrescalerMode.NoPrescaler: 100 innerTimers[idx].Divider = 1; 101 break; 102 case PrescalerMode.Prescaler16: 103 innerTimers[idx].Divider = 16; 104 break; 105 case PrescalerMode.Prescaler256: 106 innerTimers[idx].Divider = 256; 107 break; 108 case PrescalerMode.Undefined: 109 this.Log(LogLevel.Error, "Timer{0} prescaler set to an undefined value!", idx); 110 break; 111 } 112 }); 113 reg.WithReservedBits(4, 1); 114 reg.WithFlag(5, out interruptEnable[idx], name: "Interrupt Enable", 115 writeCallback: (_, val) => 116 { 117 UpdateInterrupts(); 118 }); 119 reg.WithTaggedFlag("Timer Mode", 6); 120 reg.WithFlag(7, name: "Timer Enable", 121 writeCallback: (_, val) => 122 { 123 innerTimers[idx].Enabled = val; 124 }); 125 reg.WithReservedBits(8, 24); 126 reg.WithWriteCallback((_, __) => 127 { 128 if(innerTimers[idx].Enabled) 129 { 130 this.Log(LogLevel.Warning, "Timer{0}: Writing to the Control register while the timer is running!", idx); 131 } 132 }); 133 }); 134 135 Registers.Timer1InterruptClear.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) => 136 { 137 reg.WithFlag(0, out interruptPending[idx], FieldMode.WriteOneToClear, name: "Interrupt Clear Mask", 138 writeCallback: (_, val) => 139 { 140 UpdateInterrupts(); 141 }); 142 reg.WithReservedBits(1, 31); 143 }); 144 145 Registers.Timer1RawInterruptStatus.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) => 146 { 147 reg.WithFlag(0, FieldMode.Read, name: "Raw Interrupt Status", 148 valueProviderCallback: _ => interruptPending[idx].Value); 149 reg.WithReservedBits(1, 31); 150 }); 151 152 Registers.Timer1MaskedInterruptStatus.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) => 153 { 154 reg.WithFlag(0, FieldMode.Read, name: "Masked Interrupt Status", 155 valueProviderCallback: _ => interruptEnable[idx].Value && interruptPending[idx].Value); 156 reg.WithReservedBits(1, 31); 157 }); 158 159 Registers.Timer1BackgroundLoad.DefineMany(this, NumberOfTimers, stepInBytes: TimersRegistersOffset, setup: (reg, idx) => 160 { 161 reg.WithValueField(0, 32, name: "Background Load", 162 // As per the SP804 TRM, reading from Background Load returns the same value as the Load register 163 valueProviderCallback: _ => (uint)innerTimers[idx].Limit, 164 writeCallback: (_, value) => 165 { 166 backgroundLimit[idx] = value; 167 backgroundLimitSet[idx] = true; 168 } 169 ); 170 }); 171 172 Registers.IntegrationTestControl.Define(this) 173 .WithTaggedFlag("Integration Test Enable", 0) 174 .WithReservedBits(1, 31); 175 176 Registers.IntegrationTestOutputSet.Define(this) 177 .WithTaggedFlag("Timer Interrupt 1", 0) 178 .WithReservedBits(1, 31); 179 } 180 UpdateInterrupts()181 private void UpdateInterrupts() 182 { 183 var anyPending = interruptEnable.Select(x => x.Value) 184 .Zip(interruptPending.Select(x => x.Value), (enabled, pending) => enabled && pending) 185 .Any(x => x); 186 187 this.Log(LogLevel.Noisy, "Setting IRQ to: {0}", anyPending); 188 IRQ.Set(anyPending); 189 } 190 191 private readonly IFlagRegisterField[] interruptEnable = new IFlagRegisterField[NumberOfTimers]; 192 private readonly IFlagRegisterField[] interruptPending = new IFlagRegisterField[NumberOfTimers]; 193 private readonly LimitTimer[] innerTimers = new LimitTimer[NumberOfTimers]; 194 private readonly ulong[] backgroundLimit = new ulong[NumberOfTimers]; 195 private readonly bool[] backgroundLimitSet = new bool[NumberOfTimers]; 196 197 private const int NumberOfTimers = 2; 198 private const uint TimersRegistersOffset = 0x20; 199 private const uint TimerLimitResetValue = 0xFFFFFFFF; 200 201 private enum PrescalerMode 202 { 203 NoPrescaler, 204 Prescaler16, 205 Prescaler256, 206 Undefined 207 } 208 209 private enum Timer 210 { 211 Timer1, 212 Timer2 213 } 214 215 private enum Registers 216 { 217 Timer1Load = 0x00, 218 Timer1Value = 0x04, 219 Timer1Control = 0x08, 220 Timer1InterruptClear = 0x0C, 221 Timer1RawInterruptStatus = 0x10, 222 Timer1MaskedInterruptStatus = 0x14, 223 Timer1BackgroundLoad = 0x18, 224 225 Timer2Load = 0x20, 226 Timer2Value = 0x24, 227 Timer2Control = 0x28, 228 Timer2InterruptClear = 0x2C, 229 Timer2RawInterruptStatus = 0x30, 230 Timer2MaskedInterruptStatus = 0x34, 231 Timer2BackgroundLoad = 0x38, 232 233 IntegrationTestControl = 0xF00, 234 IntegrationTestOutputSet = 0xF04, 235 } 236 } 237 } 238