1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using Antmicro.Renode.Logging;
9 using Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Time;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
17     public class RenesasRZG_GTM : BasicDoubleWordPeripheral, IKnownSize
18     {
RenesasRZG_GTM(IMachine machine, long frequency)19         public RenesasRZG_GTM(IMachine machine, long frequency) : base(machine)
20         {
21             timer = new LimitTimer(machine.ClockSource, frequency, this, "timer", FreeRunLimit, Direction.Descending, workMode: WorkMode.Periodic, eventEnabled: true);
22             timer.LimitReached += HandleLimitReached;
23             IRQ = new GPIO();
24 
25             DefineRegisters();
26             Reset();
27         }
28 
Reset()29         public override void Reset()
30         {
31             base.Reset();
32             IRQ.Unset();
33             timer.Reset();
34         }
35 
36         public GPIO IRQ { get; }
37 
38         public long Size => 0x24;
39 
DefineRegisters()40         private void DefineRegisters()
41         {
42             Registers.Compare.Define(this)
43                 .WithValueField(0, 32, out newTimerLimit, name: "OSTMnCMP",
44                     writeCallback: (_, value) =>
45                     {
46                         if(operatingMode.Value == OperatingMode.FreeRunning && value >= TimerValue)
47                         {
48                             // Limit is only updated if the new compare
49                             // value can be reached before the roll-over
50                             SetTimerLimit(value);
51                         }
52                     });
53 
54             Registers.Counter.Define(this)
55                 .WithValueField(0, 32, FieldMode.Read, name: "OSTMnCNT",
56                     valueProviderCallback: _ => TimerValue);
57 
58             Registers.CountEnableStatus.Define(this)
59                 .WithFlag(0, FieldMode.Read, name: "OSTMnTE",
60                     valueProviderCallback: _ => timer.Enabled)
61                 .WithReservedBits(1, 31);
62 
63             Registers.CountStartTrigger.Define(this)
64                 .WithFlag(0, FieldMode.Write, name: "OSTMnTS",
65                     writeCallback: (_, value) =>
66                     {
67                         if(!value)
68                         {
69                             return;
70                         }
71 
72                         SetTimerLimit(newTimerLimit.Value);
73                         if(timer.Enabled)
74                         {
75                             // Restart
76                             if(operatingMode.Value == OperatingMode.Interval)
77                             {
78                                 timer.ResetValue();
79                                 UpdateInterrupts(countingStart: true);
80                             }
81                         }
82                         else
83                         {
84                             // Regular start
85                             timer.ResetValue();
86                             timer.Enabled = true;
87                             UpdateInterrupts(countingStart: true);
88                         }
89                     })
90                 .WithReservedBits(1, 31);
91 
92             Registers.CountStopTrigger.Define(this)
93                 .WithFlag(0, FieldMode.Write, name: "OSTMnTT",
94                     writeCallback: (_, value) =>
95                     {
96                         if(value)
97                         {
98                             timer.Enabled = false;
99                         }
100                     })
101                 .WithReservedBits(1, 31);
102 
103             Registers.Control.Define(this)
104                 .WithFlag(0, out interruptWhenCountingStarts, name: "OSTMnMD0")
105                 .WithEnumField(1, 1, out operatingMode, name: "OSTMnMD1",
106                     changeCallback: (_, newMode) =>
107                     {
108                         switch(newMode)
109                         {
110                             case OperatingMode.Interval:
111                                 timer.Direction = Direction.Descending;
112                                 break;
113                             case OperatingMode.FreeRunning:
114                                 timer.Direction = Direction.Ascending;
115                                 break;
116                             default:
117                                 throw new Exception("Unreachable");
118                         }
119                     })
120                 .WithReservedBits(2, 30);
121         }
122 
UpdateInterrupts(bool countingStart = false)123         private void UpdateInterrupts(bool countingStart = false)
124         {
125             if(countingStart && !interruptWhenCountingStarts.Value)
126             {
127                 return;
128             }
129 
130             IRQ.Blink();
131             this.DebugLog("IRQ triggered");
132         }
133 
SetTimerLimit(ulong value)134         private void SetTimerLimit(ulong value)
135         {
136             value++;
137             if(timer.Limit != value)
138             {
139                 timer.Limit = value;
140             }
141         }
142 
HandleLimitReached()143         private void HandleLimitReached()
144         {
145             switch(operatingMode.Value)
146             {
147                 case OperatingMode.Interval:
148                     SetTimerLimit(newTimerLimit.Value);
149                     break;
150                 case OperatingMode.FreeRunning:
151                 {
152                     var currentValue = timer.Value;
153                     // Handle roll-over
154                     if(currentValue == FreeRunLimit)
155                     {
156                         SetTimerLimit(newTimerLimit.Value);
157                         return;
158                     }
159                     else
160                     {
161                         SetTimerLimit(FreeRunLimit);
162                         timer.Value = currentValue;
163                         // fallthrough to trigger IRQ
164                     }
165                     break;
166                 }
167                 default:
168                     throw new Exception("Unreachable");
169             }
170             UpdateInterrupts();
171         }
172 
173         private ulong TimerValue
174         {
175             get
176             {
177                 if(timer.Enabled && sysbus.TryGetCurrentCPU(out var cpu))
178                 {
179                     cpu.SyncTime();
180                 }
181                 return timer.Value;
182             }
183         }
184 
185         private IEnumRegisterField<OperatingMode> operatingMode;
186         private IFlagRegisterField interruptWhenCountingStarts;
187         private IValueRegisterField newTimerLimit;
188 
189         private readonly LimitTimer timer;
190 
191         private const ulong FreeRunLimit = (1UL << 32) - 1;
192 
193         private enum OperatingMode
194         {
195             Interval    = 0,
196             FreeRunning = 1,
197         }
198 
199         private enum Registers
200         {
201             Compare           = 0x00, // OSTMnCMP
202             Counter           = 0x04, // OSTMnCNT
203             CountEnableStatus = 0x10, // OSTMnTE
204             CountStartTrigger = 0x14, // OSTMnTS
205             CountStopTrigger  = 0x18, // OSTMnTT
206             Control           = 0x20, // OSTMnCTL
207         }
208     }
209 }
210