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.Utilities; 10 using Antmicro.Renode.Logging; 11 12 namespace Antmicro.Renode.Peripherals.Timers 13 { 14 public class EOSS3_SimplePeriodicTimer : BasicDoubleWordPeripheral, IKnownSize, IGPIOReceiver 15 { EOSS3_SimplePeriodicTimer(IMachine machine)16 public EOSS3_SimplePeriodicTimer(IMachine machine) : base(machine) 17 { 18 interruptTimestamps = new ushort[NumberOfInterrupts]; 19 FFEKickOff = new GPIO(); 20 21 timerSoftware30Bit = new LimitTimer(machine.ClockSource, 1000 /*count every 1ms*/, this, "Software Use Timer", limit: 0x3FFFFFFF, enabled: false, eventEnabled: false, direction: Time.Direction.Ascending); 22 timerFFEKickOffUpCounter = new LimitTimer(machine.ClockSource, 1000 /*count every 1ms*/, this, "FFE Kick-Off", limit: 0x4 /* the minimal legal limit, as LimitTimer does not accept 0 */, 23 enabled: false, eventEnabled: true, direction: Time.Direction.Ascending); 24 25 timerFFEKickOffUpCounter.LimitReached += delegate 26 { 27 if(sleepMode.Value) 28 { 29 this.Log(LogLevel.Noisy, "FFE Kick-Off is masked"); 30 } 31 else 32 { 33 FFEKickOff.Blink(); 34 this.Log(LogLevel.Noisy, "FFE Kick-Off!"); 35 } 36 }; 37 DefineRegisters(); 38 } 39 Reset()40 public override void Reset() 41 { 42 enabled = false; 43 ffeKickOffPeriod = 0; 44 FFEKickOff.Unset(); 45 interruptTimestamps = new ushort[NumberOfInterrupts]; 46 47 timerFFEKickOffUpCounter.Reset(); 48 timerSoftware30Bit.Reset(); 49 50 base.Reset(); 51 } 52 53 /* 54 Saving timestamps for FFE. 55 */ OnGPIO(int number, bool value)56 public void OnGPIO(int number, bool value) 57 { 58 if(number >= 8) 59 { 60 this.Log(LogLevel.Warning, "Invalid interrupt number: {0} set to {1}", number, value); 61 return; 62 } 63 if(!value) 64 { 65 //we don't have to filter out repeated calls with `true`, as the GPIO class does it by itself 66 return; 67 } 68 69 if(((int)interruptMask.Value & (1 << number)) != 0x0) //interrupt is not masked 70 { 71 sleepMode.Value = false; 72 interruptTimestamps[number] = (ushort)timerSoftware30Bit.Value; //only lower bits are latched 73 74 this.Log(LogLevel.Noisy, "Saved timestamp for interrupt {0}.", number); 75 } 76 else 77 { 78 this.Log(LogLevel.Warning, "The interrupt {0} is masked: 0x{1:X}.", number, interruptMask.Value); 79 } 80 } 81 82 //This is currently not used - exposed for debug purposes. GetInterruptTimestamp(uint number)83 public ushort GetInterruptTimestamp(uint number) 84 { 85 if(number >= 8) 86 { 87 this.Log(LogLevel.Warning, "No such interrupt: {0}", number); 88 return 0; 89 } 90 return interruptTimestamps[number]; 91 } 92 93 public long Size => 0x200; 94 DefineRegisters()95 private void DefineRegisters() 96 { 97 /* 98 Ignoring source frequency and all compensation registers, 99 as we only simulate the 30-bit timer, which is 100 supposed to count every 1ms 101 */ 102 Registers.TimerConfiguration.Define32(this) 103 .WithFlag(0, writeCallback: EnableCounter, name: "SPT_EN") 104 .WithTag("CLK_SRC_SEL", 1, 1) 105 .WithValueField(2, 8, out interruptMask, name: "INT_MASK_N") 106 .WithValueField(10, 8, writeCallback: (_, val) => UpdateFFEKickOffPeriod((uint)val), name: "FFE_TO_PERIOD") 107 .WithTag("PMU_TO_PERIOD", 18, 4) 108 .WithReservedBits(22, 10); 109 110 Registers.SleepMode.Define32(this) 111 .WithFlag(0, flagField: out sleepMode, name: "SLEEP_MODE") 112 .WithReservedBits(1, 31); 113 114 Registers.UpdateTimerValue.Define32(this) 115 .WithValueField(0, 30, writeCallback: (_, val) => UpdateSoftwareTimer((uint)val), name: "UPDATE_TIMER_VALUE") 116 .WithReservedBits(30, 1); 117 118 Registers.SpareBits.Define32(this) 119 .WithValueField(0, 8, name: "SPARE_BITS") 120 .WithReservedBits(8, 24); 121 122 Registers.TimerValue.Define32(this) 123 .WithValueField(0, 30, FieldMode.Read, valueProviderCallback: _ => (uint)timerSoftware30Bit.Value, name: "TIMER_VALUE") 124 .WithReservedBits(30, 1); 125 126 Registers.EventCounterValue.Define32(this) 127 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (uint)ffeKickOffPeriod, name: "EVENT_CNT_VALUE") 128 .WithReservedBits(8, 24); 129 130 Registers.MsecCounterValue.Define32(this) 131 //this should count the clock events reported by Clock Event Generator, and drive the 1ms timer. We don't support this granularity, so we return a default value 132 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0x40, name: "MS_CNT_VALUE") 133 .WithReservedBits(8, 24); 134 } 135 EnableCounter(bool _, bool value)136 private void EnableCounter(bool _, bool value) 137 { 138 if(value) 139 { 140 timerSoftware30Bit.Enabled = true; 141 this.Log(LogLevel.Noisy, "30-bit counter enabled"); 142 if(ffeKickOffPeriod != 0x0) 143 { 144 timerFFEKickOffUpCounter.Enabled = true; 145 timerFFEKickOffUpCounter.Limit = ffeKickOffPeriod; 146 this.Log(LogLevel.Noisy, "FFE Kick off counter enabled"); 147 } 148 } 149 else 150 { 151 timerFFEKickOffUpCounter.Enabled = false; 152 timerFFEKickOffUpCounter.Reset(); 153 154 timerSoftware30Bit.Enabled = false; 155 this.Log(LogLevel.Noisy, "Counters disabled"); 156 } 157 enabled = value; 158 } 159 UpdateFFEKickOffPeriod(uint value)160 private void UpdateFFEKickOffPeriod(uint value) 161 { 162 if(value == 0x0) 163 { 164 timerFFEKickOffUpCounter.Enabled = false; 165 } 166 else if(value < 0x4 || value > 0x64) 167 { 168 this.Log(LogLevel.Warning, "Trying to update the FFE kick off period with a reserved value 0x{0:X}", value); 169 } 170 else 171 { 172 if(enabled) 173 { 174 timerFFEKickOffUpCounter.Enabled = true; 175 this.Log(LogLevel.Noisy, "FFE Kick off counter enabled"); 176 } 177 timerFFEKickOffUpCounter.Limit = value; 178 } 179 180 ffeKickOffPeriod = value; 181 } 182 UpdateSoftwareTimer(uint value)183 private void UpdateSoftwareTimer(uint value) 184 { 185 if(enabled) 186 { 187 this.Log(LogLevel.Warning, "Updating the 30-bit timer while it is enabled"); 188 } 189 timerSoftware30Bit.Value = value; 190 } 191 192 public GPIO FFEKickOff { get; private set; } 193 194 private bool enabled; 195 private IFlagRegisterField sleepMode; 196 private IValueRegisterField interruptMask; 197 private ulong ffeKickOffPeriod; 198 199 private ushort[] interruptTimestamps; 200 201 private readonly LimitTimer timerSoftware30Bit; 202 private readonly LimitTimer timerFFEKickOffUpCounter; 203 204 private const int NumberOfInterrupts = 8; 205 206 private enum Registers 207 { 208 TimerConfiguration = 0x0, 209 SleepMode = 0x004, 210 ErrorCompensation40ms = 0x008, 211 ErrorCompensation1s0 = 0x00C, 212 ErrorCompensation1s1 = 0x010, 213 ErrorCompensation1s2 = 0x014, 214 ErrorCompensation1s3 = 0x018, 215 ErrorCompensationRTC0 = 0x01C, 216 ErrorCompensationRTC1 = 0x020, 217 ErrorCompensationRTC2 = 0x024, 218 ErrorCompensationRTC3 = 0x028, 219 UpdateTimerValue = 0x02C, 220 SpareBits = 0x030, 221 TimerValue = 0x034, 222 EventCounterValue = 0x038, 223 MsecCounterValue = 0x03C 224 } 225 } 226 } 227