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 using Antmicro.Renode.Peripherals.Bus;
12 
13 namespace Antmicro.Renode.Peripherals.Timers
14 {
15     // this is a model of LiteX timer in the default configuration:
16     // * width: 32 bits
17     // * csr data width: 8 bit
18     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
19     public class LiteX_Timer : BasicDoubleWordPeripheral, IKnownSize
20     {
LiteX_Timer(IMachine machine, long frequency)21         public LiteX_Timer(IMachine machine, long frequency) : base(machine)
22         {
23             uptimeTimer = new LimitTimer(machine.ClockSource, frequency, this, nameof(uptimeTimer), direction: Antmicro.Renode.Time.Direction.Ascending, enabled: true);
24             innerTimer = new LimitTimer(machine.ClockSource, frequency, this, nameof(innerTimer), eventEnabled: true, autoUpdate: true);
25             innerTimer.LimitReached += delegate
26             {
27                 this.Log(LogLevel.Noisy, "Limit reached");
28                 irqPending.Value = true;
29                 UpdateInterrupts();
30 
31                 if(reloadValue == 0)
32                 {
33                     this.Log(LogLevel.Noisy, "No realod value - disabling the timer");
34                     innerTimer.Enabled = false;
35                 }
36                 innerTimer.Limit = reloadValue;
37             };
38             DefineRegisters();
39         }
40 
Reset()41         public override void Reset()
42         {
43             base.Reset();
44             innerTimer.Reset();
45             uptimeTimer.Reset();
46             latchedValue = 0;
47             loadValue = 0;
48             reloadValue = 0;
49             uptimeLatchedValue = 0;
50 
51             UpdateInterrupts();
52         }
53 
54         public GPIO IRQ { get; } = new GPIO();
55 
56         public long Size => 0x68;
57 
DefineRegisters()58         private void DefineRegisters()
59         {
60             // LOAD0 contains most significant 8 bits
61             // LOAD3 contains least significant 8 bits
62             Registers.Load0.DefineMany(this, SubregistersCount, (reg, idx) =>
63             {
64                 reg.WithValueField(0, 8, name: $"LOAD{idx}", writeCallback: (_, val) =>
65                 {
66                     BitHelper.ReplaceBits(ref loadValue, width: 8, source: (uint)val, destinationPosition: 24 - idx * 8);
67                 });
68             });
69 
70             // RELOAD0 contains most significant 8 bits
71             // RELOAD3 contains least significant 8 bits
72             Registers.Reload0.DefineMany(this, SubregistersCount, (reg, idx) =>
73             {
74                 reg.WithValueField(0, 8, name: $"RELOAD{idx}", writeCallback: (_, val) =>
75                 {
76                     BitHelper.ReplaceBits(ref reloadValue, width: 8, source: (uint)val, destinationPosition: 24 - idx * 8);
77                 });
78             });
79 
80             Registers.TimerEnable.Define32(this)
81                 .WithFlag(0, name: "ENABLE", writeCallback: (_, val) =>
82                 {
83                     if(innerTimer.Enabled == val)
84                     {
85                         return;
86                     }
87 
88                     if(val)
89                     {
90                         innerTimer.Limit = loadValue;
91                         this.Log(LogLevel.Noisy, "Enabling timer. Load value: 0x{0:X}, reload value: 0x{1:X}", loadValue, reloadValue);
92                     }
93 
94                     innerTimer.Enabled = val;
95                 })
96             ;
97 
98             Registers.TimerUpdateValue.Define32(this)
99                 .WithFlag(0, FieldMode.WriteOneToClear, name: "UPDATE_VALUE", writeCallback: (_, val) =>
100                 {
101                     if(val)
102                     {
103                         if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
104                         {
105                             // being here means we are on the CPU thread
106                             cpu.SyncTime();
107                         }
108 
109                         latchedValue = (uint)innerTimer.Value;
110                     }
111                 });
112             ;
113 
114             // VALUE0 contains most significant 8 bits
115             // VALUE3 contains least significant 8 bits
116             Registers.Value0.DefineMany(this, SubregistersCount, (reg, idx) =>
117             {
118                 reg.WithValueField(0, 8, FieldMode.Read, name: $"VALUE{idx}", valueProviderCallback: _ =>
119                 {
120                     return BitHelper.GetValue(latchedValue, 24 - idx * 8, 8);
121                 });
122             });
123 
124             Registers.EventStatus.Define32(this)
125                 .WithFlag(0, FieldMode.Read, name: "EV_STATUS", valueProviderCallback: _ => innerTimer.Value == 0)
126             ;
127 
128             Registers.EventPending.Define32(this)
129                 .WithFlag(0, out irqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "EV_PENDING", changeCallback: (_, __) => UpdateInterrupts())
130             ;
131 
132             Registers.EventEnable.Define32(this)
133                 .WithFlag(0, out irqEnabled, name: "EV_ENABLE", changeCallback: (_, __) => UpdateInterrupts())
134             ;
135 
136             Registers.UptimeLatch.Define32(this)
137                 .WithFlag(0, FieldMode.WriteOneToClear, name: "UPTIME_LATCH", writeCallback: (_, val) =>
138                 {
139                     if(val)
140                     {
141                         if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
142                         {
143                             // being here means we are on the CPU thread
144                             cpu.SyncTime();
145                         }
146 
147                         uptimeLatchedValue = uptimeTimer.Value;
148                     }
149                 });
150 
151             // UPTIME_CYCLES0 contains most significant 8 bits
152             // UPTIME_CYCLES7 contains least significant 8 bits
153             Registers.UptimeCycles0.DefineMany(this, 8, (reg, idx) =>
154             {
155                 reg.WithValueField(0, 8, FieldMode.Read, name: $"UPTIME_CYCLES{idx}", valueProviderCallback: _ =>
156                 {
157                     return (byte)BitHelper.GetValue(uptimeLatchedValue, 56 - idx * 8, 8);
158                 });
159             });
160         }
161 
UpdateInterrupts()162         private void UpdateInterrupts()
163         {
164             this.Log(LogLevel.Noisy, "Setting IRQ: {0}", irqPending.Value && irqEnabled.Value);
165             IRQ.Set(irqPending.Value && irqEnabled.Value);
166         }
167 
168         private IFlagRegisterField irqEnabled;
169         private IFlagRegisterField irqPending;
170 
171         private uint latchedValue;
172         private uint loadValue;
173         private uint reloadValue;
174 
175         private ulong uptimeLatchedValue;
176 
177         private readonly LimitTimer innerTimer;
178         private readonly LimitTimer uptimeTimer;
179 
180         private const int SubregistersCount = 4;
181 
182         private enum Registers
183         {
184             Load0 = 0x0,
185             Load1 = 0x4,
186             Load2 = 0x8,
187             Load3 = 0xC,
188 
189             Reload0 = 0x10,
190             Reload1 = 0x14,
191             Reload2 = 0x18,
192             Reload3 = 0x1C,
193 
194             TimerEnable = 0x20,
195             TimerUpdateValue = 0x24,
196 
197             Value0 = 0x28,
198             Value1 = 0x2C,
199             Value2 = 0x30,
200             Value3 = 0x34,
201 
202             EventStatus = 0x38,
203             EventPending = 0x3c,
204             EventEnable = 0x40,
205 
206             UptimeLatch = 0x44,
207 
208             UptimeCycles0 = 0x48,
209             UptimeCycles1 = 0x4C,
210             UptimeCycles2 = 0x50,
211             UptimeCycles3 = 0x54,
212             UptimeCycles4 = 0x58,
213             UptimeCycles5 = 0x5C,
214             UptimeCycles6 = 0x60,
215             UptimeCycles7 = 0x64
216         }
217     }
218 }
219