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