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 System.Collections.Generic;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Time;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.DoubleWordToByte)]
18     public class NPCX_ITIM : BasicDoubleWordPeripheral, IKnownSize
19     {
NPCX_ITIM(IMachine machine, long lfclkFrequency = DefaultLFCLKFrequency, long apb2Frequency = DefaultAPB2Frequency, bool is64Bit = false)20         public NPCX_ITIM(IMachine machine, long lfclkFrequency = DefaultLFCLKFrequency, long apb2Frequency = DefaultAPB2Frequency, bool is64Bit = false) : base(machine)
21         {
22             this.lfclkFrequency = lfclkFrequency;
23             this.apb2Frequency = apb2Frequency;
24 
25             timer = new LimitTimer(machine.ClockSource, apb2Frequency, this, "timer", direction: Direction.Descending, workMode: WorkMode.Periodic, divider: 1, eventEnabled: true);
26             timer.LimitReached += () =>
27             {
28                 timeoutStatus.Value = true;
29                 UpdateInterrupts();
30             };
31             IRQ = new GPIO();
32 
33             DefineRegisters(is64Bit);
34         }
35 
Reset()36         public override void Reset()
37         {
38             base.Reset();
39             timer.Reset();
40             IRQ.Unset();
41             timerLimitLow = 0;
42             timerLimitHigh = 0;
43         }
44 
45         public long Size => 0x1000;
46 
47         public GPIO IRQ { get; }
48 
DefineRegisters(bool is64Bit)49         private void DefineRegisters(bool is64Bit)
50         {
51             Registers.Prescaler.Define(this)
52                 .WithReservedBits(0, 8)
53                 .WithValueField(8, 8, name: "PRE_8 (Prescaler Value)",
54                     changeCallback: (_, value) => timer.Divider = (int)value + 1)
55                 .WithReservedBits(16, 16);
56 
57             Registers.ControlAndStatus.Define(this)
58                 .WithFlag(0, out timeoutStatus, FieldMode.Read | FieldMode.WriteOneToClear, name: "TO_STS (Timeout Status)")
59                 .WithReservedBits(1, 1)
60                 .WithFlag(2, out interruptEnabled, name: "TO_IE (Timeout Interrupt Enable)",
61                     changeCallback: (_, __) => UpdateInterrupts())
62                 .WithFlag(3, out wakeupEnabled, name: "TO_WUE (Timeout Wake-Up Enable)",
63                     changeCallback: (_, __) => UpdateInterrupts())
64                 .WithFlag(4, name: "CKSEL (Input Clock Select)",
65                     changeCallback: (_, value) => timer.Frequency = value ? lfclkFrequency : apb2Frequency)
66                 .WithReservedBits(5, 2)
67                 .WithFlag(7, name: "ITEN (ITIM32 Module Enable)",
68                     changeCallback: (_, value) =>
69                     {
70                         if(!value)
71                         {
72                             timeoutStatus.Value = false;
73                             timer.ResetValue();
74                         }
75                         UpdateInterrupts();
76                         timer.Enabled = value;
77                     })
78                 .WithReservedBits(8, 24);
79 
80             Registers.Counter.Define(this)
81                 .WithValueField(0, 32, name: "CNT_32 (32-Bit Counter Value)",
82                     valueProviderCallback: _ => timer.Enabled ? GetTimerValue(true) : timerLimitLow,
83                     writeCallback: (_, value) =>
84                     {
85                         timerLimitLow = (uint)value;
86                         UpdateTimerLimit();
87                     });
88 
89             if(is64Bit)
90             {
91                 Registers.CounterHigh.Define(this)
92                     .WithValueField(0, 32, name: "CNT_64H (64-Bit Counter High DWord Value)",
93                         valueProviderCallback: _ => timer.Enabled ? GetTimerValue(false) >> 32 : timerLimitHigh,
94                         writeCallback: (_, value) =>
95                         {
96                             timerLimitHigh = (uint)value;
97                             UpdateTimerLimit();
98                         });
99             }
100         }
101 
UpdateInterrupts()102         private void UpdateInterrupts()
103         {
104             IRQ.Set(timeoutStatus.Value && (interruptEnabled.Value || wakeupEnabled.Value));
105         }
106 
GetTimerValue(bool syncTime)107         private ulong GetTimerValue(bool syncTime)
108         {
109             if(syncTime && machine.GetSystemBus(this).TryGetCurrentCPU(out var cpu))
110             {
111                 cpu.SyncTime();
112             }
113             return timer.Value;
114         }
115 
UpdateTimerLimit()116         private void UpdateTimerLimit()
117         {
118             ulong limit = (ulong)timerLimitHigh << 32 | timerLimitLow;
119             // To prevent overflow don't add 1 to the limit when a maximum value is requested
120             timer.Limit = checked(limit == ulong.MaxValue ? limit : limit + 1);
121             timer.ResetValue();
122         }
123 
124         private IFlagRegisterField timeoutStatus;
125         private IFlagRegisterField interruptEnabled;
126         private IFlagRegisterField wakeupEnabled;
127 
128         private uint timerLimitLow;
129         private uint timerLimitHigh;
130 
131         private readonly LimitTimer timer;
132         private readonly long lfclkFrequency;
133         private readonly long apb2Frequency;
134 
135         private const long DefaultLFCLKFrequency = 32768;
136         private const long DefaultAPB2Frequency = 10000000;
137 
138         private enum Registers
139         {
140             // Prescaler register is defined at offset 0x1
141             // Due to current lack of support for register collections
142             // with multiple widths, this register will be implemented
143             // as the second byte in a 4 byte register
144             Prescaler           = 0x0, // ITPRE32n or ITPRE64
145             ControlAndStatus    = 0x4, // ITCTS32n or ITCTS64
146             Counter             = 0x8, // ITCNT32n or ITCNT64L
147             CounterHigh         = 0xC, // ITCNT64H
148         }
149     }
150 }
151