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