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.Logging;
9 using Antmicro.Renode.Time;
10 using Antmicro.Renode.Core.Structure.Registers;
11 
12 namespace Antmicro.Renode.Peripherals.Timers
13 {
14     // This timer model is limited to only one match channel (channel 0), and only in reset mode (MR0R=1).
15     public class LPC_CTimer : BasicDoubleWordPeripheral, IKnownSize
16     {
LPC_CTimer(IMachine machine, long frequency = DefaultFrequency)17         public LPC_CTimer(IMachine machine, long frequency = DefaultFrequency) : base(machine)
18         {
19             this.frequency = frequency;
20             timer = new LimitTimer(machine.ClockSource, frequency, this, nameof(timer),
21                 direction: Direction.Ascending, eventEnabled: true);
22             timer.LimitReached += () =>
23             {
24                 match0InterruptFlag.Value = true;
25                 UpdateInterrupt();
26             };
27             IRQ = new GPIO();
28 
29             DefineRegisters();
30             Reset();
31         }
32 
Reset()33         public override void Reset()
34         {
35             IRQ.Unset();
36             base.Reset();
37         }
38 
39         public GPIO IRQ { get; }
40         public long Size => 0x100;
41 
DefineRegisters()42         private void DefineRegisters()
43         {
44             Registers.Interrupt.Define(this)
45                 .WithFlag(0, name: "MR0INT",
46                     mode: FieldMode.Read | FieldMode.WriteOneToClear, flagField: out match0InterruptFlag)
47                 .WithTaggedFlag("MR1INT", 1)
48                 .WithTaggedFlag("MR2INT", 2)
49                 .WithTaggedFlag("MR3INT", 3)
50                 .WithTaggedFlag("CR0INT", 4)
51                 .WithTaggedFlag("CR1INT", 5)
52                 .WithTaggedFlag("CR2INT", 6)
53                 .WithReservedBits(7, 25)
54                 .WithChangeCallback((_, __) => UpdateInterrupt());
55 
56             Registers.TimerControl.Define(this)
57                 .WithFlag(0, name: "CEN",
58                     valueProviderCallback: _ => timer.Enabled,
59                     changeCallback: (_, value) => timer.Enabled = value)
60                 .WithFlag(1, name: "CRST",
61                     changeCallback: (_, value) =>
62                     {
63                         if(value)
64                         {
65                             timer.Value = 0;
66                         }
67                         // The counters remain reset until CRST is returned to zero.
68                         timer.Enabled = !value;
69                     })
70                 .WithReservedBits(2, 30);
71 
72             Registers.TimerCounter.Define(this)
73                 .WithValueField(0, 32, name: "TCVAL",
74                     mode: FieldMode.Read, valueProviderCallback: _ => timer.Value);
75 
76             Registers.Prescale.Define(this)
77                 .WithValueField(0, 32, name: "PRVAL",
78                     valueProviderCallback: _ => (ulong)timer.Divider - 1,
79                     changeCallback: (_, value) => timer.Divider = (int)value + 1);
80 
81             Registers.PrescaleCounter.Define(this)
82                 .WithTag("PCVAL", 0, 32)
83                 .WithReadCallback((_, __) => this.WarningLog("Prescale counter is not implemented"));
84 
85             Registers.MatchControl.Define(this)
86                 .WithFlag(0, name: "MR0I",
87                     flagField: out match0InterruptEnable)
88                 .WithFlag(1, name: "MR0R",
89                     writeCallback: (_, value) =>
90                     {
91                         if(!value)
92                         {
93                             this.WarningLog("Non-resetting compare is not implemented");
94                         }
95                     })
96                 .WithTaggedFlag("MR0S", 2) // not planned controllable TC stop per match channel, unused by Zephyr and Linux
97                 .WithTaggedFlag("MR1I", 3)
98                 .WithTaggedFlag("MR1R", 4)
99                 .WithTaggedFlag("MR1S", 5)
100                 .WithTaggedFlag("MR2I", 6)
101                 .WithTaggedFlag("MR2R", 7)
102                 .WithTaggedFlag("MR2S", 8)
103                 .WithTaggedFlag("MR3I", 9)
104                 .WithTaggedFlag("MR3R", 10)
105                 .WithTaggedFlag("MR3S", 11)
106                 .WithReservedBits(12, 12)
107                 .WithTaggedFlag("MR0RL", 24)
108                 .WithTaggedFlag("MR1RL", 25)
109                 .WithTaggedFlag("MR2RL", 26)
110                 .WithTaggedFlag("MR3RL", 27)
111                 .WithReservedBits(28, 4)
112                 .WithWriteCallback((_, __) => UpdateInterrupt());
113 
114             Registers.Match0.Define(this)
115                 .WithValueField(0, 32, name: "MATCH",
116                     valueProviderCallback: _ => timer.Limit,
117                     changeCallback: (_, value) => timer.Limit = value);
118 
119             Registers.Match1.Define(this)
120                 .WithTag("MATCH", 0, 32);
121 
122             Registers.Match2.Define(this)
123                 .WithTag("MATCH", 0, 32);
124 
125             Registers.Match3.Define(this)
126                 .WithTag("MATCH", 0, 32);
127 
128             Registers.CaptureControl.Define(this)
129                 .WithTaggedFlag("CAP0RE", 0)
130                 .WithTaggedFlag("CAP0FE", 1)
131                 .WithTaggedFlag("CAP0I", 2)
132                 .WithTaggedFlag("CAP1RE", 3)
133                 .WithTaggedFlag("CAP1FE", 4)
134                 .WithTaggedFlag("CAP1I", 5)
135                 .WithTaggedFlag("CAP2RE", 6)
136                 .WithTaggedFlag("CAP2FE", 7)
137                 .WithTaggedFlag("CAP2I", 8)
138                 .WithReservedBits(9, 23);
139 
140             Registers.Capture0.DefineMany(this, NumberOfChannels, (register, i) => register
141                 .WithTag("CAP", 0, 32)
142             );
143 
144             Registers.ExternalMatch.Define(this)
145                 .WithTaggedFlag("EM0", 0)
146                 .WithTaggedFlag("EM1", 1)
147                 .WithTaggedFlag("EM2", 2)
148                 .WithTaggedFlag("EM3", 3)
149                 .WithTag("EMC0", 4, 2)
150                 .WithTag("EMC1", 6, 2)
151                 .WithTag("EMC2", 8, 2)
152                 .WithTag("EMC3", 10, 2)
153                 .WithReservedBits(12, 20);
154 
155             Registers.CountControl.Define(this)
156                 .WithTag("CTMODE", 0, 2)
157                 .WithTag("CINSEL", 2, 2)
158                 .WithTaggedFlag("ENCC", 4)
159                 .WithTag("SELCC", 5, 2)
160                 .WithReservedBits(8, 24);
161 
162             Registers.PwmControl.Define(this)
163                 .WithTaggedFlag("PWMEN0", 0)
164                 .WithTaggedFlag("PWMEN1", 1)
165                 .WithTaggedFlag("PWMEN2", 2)
166                 .WithTaggedFlag("PWMEN3", 3)
167                 .WithReservedBits(5, 27);
168 
169             Registers.Match0Shadow.DefineMany(this, NumberOfChannels, (register, i) => register
170                 .WithTag("SHADOW", 0, 32)
171             );
172         }
173 
UpdateInterrupt()174         private void UpdateInterrupt()
175         {
176             var irqValue = match0InterruptEnable.Value && match0InterruptFlag.Value;
177             IRQ.Set(irqValue);
178             this.DebugLog("IRQ set to {0}", irqValue);
179         }
180 
181         private IFlagRegisterField match0InterruptFlag;
182         private IFlagRegisterField match0InterruptEnable;
183 
184         private readonly LimitTimer timer;
185         private readonly long frequency;
186 
187         // Currently only one channel (index 0) is implemented
188         private const int NumberOfChannels = 4;
189         private const long DefaultFrequency = 10000000;
190 
191         private enum Registers : uint
192         {
193             Interrupt = 0x00,
194             TimerControl = 0x04,
195             TimerCounter = 0x08,
196             Prescale = 0x0c,
197             PrescaleCounter = 0x10,
198             MatchControl = 0x14,
199             Match0 = 0x18,
200             Match1 = 0x1c,
201             Match2 = 0x20,
202             Match3 = 0x24,
203             CaptureControl = 0x28,
204             Capture0 = 0x2c,
205             Capture1 = 0x30,
206             Capture2 = 0x34,
207             Capture3 = 0x38,
208             ExternalMatch = 0x3c,
209             // mind the gap
210             CountControl = 0x70,
211             PwmControl = 0x74,
212             Match0Shadow = 0x78,
213             Match1Shadow = 0x7c,
214             Match2Shadow = 0x80,
215             Match3Shadow = 0x84,
216         }
217     }
218 }
219