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.Collections.Generic;
8 using Antmicro.Renode.Peripherals.Bus;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Time;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     [AllowedTranslations(AllowedTranslation.DoubleWordToQuadWord)]
18     // Based on IA-PC HPET (High Precision Event Timers) Specification
19     public class HPET : LimitTimer, IQuadWordPeripheral, IKnownSize
20     {
HPET(IMachine machine, long frequency = 100000000)21         public HPET(IMachine machine, long frequency = 100000000)
22             : base(machine.ClockSource, frequency, direction: Direction.Ascending, limit: uint.MaxValue)
23         {
24             if(frequency < MinimumFrequency) {
25                 throw new ConstructionException($"Provided frequency = {frequency}, but it should be at least {MinimumFrequency}");
26             }
27 
28             var registersMap = new Dictionary<long, QuadWordRegister>
29             {
30                 {(long)Registers.GeneralCapabilitiesAndID, new QuadWordRegister(this)
31                     .WithValueField(32, 32, FieldMode.Read, name: "COUNTER_CLK_PERIOD",
32                         valueProviderCallback: _ => (ulong)(1e15 / Frequency))
33                     .WithTag("VENDOR_ID", 16, 16)
34                     .WithTaggedFlag("LEG_RT_CAP", 15)
35                     .WithReservedBits(14, 1)
36                     .WithTaggedFlag("COUNT_SIZE_CAP", 13)
37                     .WithTag("NUM_TIM_CAP", 8, 4)
38                     .WithTag("REV_ID", 0, 8)
39                 },
40                 {(long)Registers.GeneralConfiguration, new QuadWordRegister(this)
41                     .WithReservedBits(2, 62)
42                     .WithTaggedFlag("LEG_RT_CNF", 1)
43                     .WithFlag(0, valueProviderCallback: _ => Enabled,
44                         writeCallback: (_, value) => Enabled = value, name: "ENABLE_CNF")
45                 },
46                 {(long)Registers.GeneralInterruptStatus, new QuadWordRegister(this)
47                     .WithReservedBits(32, 32)
48                     .WithTag("Tn_INT_STS", 0, 32)
49                 },
50                 {(long)Registers.MainCounterValue, new QuadWordRegister(this)
51                     .WithValueField(0, 64, valueProviderCallback: _ =>
52                     {
53                         if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
54                         {
55                             cpu.SyncTime();
56                         }
57                         return (uint)Value;
58                     },
59                     writeCallback: (_, val) =>
60                     {
61                         if(Enabled)
62                         {
63                             this.WarningLog("Setting timer value when timer is enabled is not permited, ignoring");
64                             return;
65                         }
66                         Value = val;
67                     })
68                 },
69             };
70             for(var i = 0; i < NumberOfComparators; i++)
71             {
72                 registersMap.Add(
73                     (long)Registers.Timer0ConfigurationAndCapability + 0x20 * i,
74                     new QuadWordRegister(this)
75                         .WithTag($"T{i}_INT_ROUTE_CAP", 32, 32)
76                         .WithReservedBits(16, 16)
77                         .WithTaggedFlag($"T{i}_FSB_INT_DEL_CAP", 15)
78                         .WithTaggedFlag($"T{i}_FSB_EN_CNF", 14)
79                         .WithTag($"T{i}_INT_ROUTE_CNF", 9, 5)
80                         .WithTaggedFlag("Tn_32MODE_CNF", 8)
81                         .WithReservedBits(7, 1)
82                         .WithTaggedFlag($"T{i}_VAL_SET_CNF", 6)
83                         .WithTaggedFlag($"T{i}_SIZE_CAP", 5)
84                         .WithTaggedFlag($"T{i}_PER_INT_CAP", 4)
85                         .WithTaggedFlag($"T{i}_TYPE_CNF", 3)
86                         .WithTaggedFlag($"T{i}_INT_ENB_CNF", 2)
87                         .WithTaggedFlag($"T{i}_INT_TYPE_CNF", 1)
88                         .WithReservedBits(0, 1)
89                 );
90                 registersMap.Add(
91                     (long)Registers.Timer0ComparatorValue + 0x20 * i,
92                     new QuadWordRegister(this)
93                         .WithTag($"T{i}_COMPARATOR_VALUE", 0, 64)
94                 );
95                 registersMap.Add(
96                     (long)Registers.Timer0FSBInterruptRoute + 0x20 * i,
97                     new QuadWordRegister(this)
98                         .WithTag($"T{i}_FSB_INT_ADDR", 32, 32)
99                         .WithTag($"T{i}_FSB_INT_VAL", 0, 32)
100                 );
101             }
102             registers = new QuadWordRegisterCollection(this, registersMap);
103         }
104 
ReadQuadWord(long offset)105         public ulong ReadQuadWord(long offset)
106         {
107             return registers.Read(offset);
108         }
109 
WriteQuadWord(long offset, ulong value)110         public void WriteQuadWord(long offset, ulong value)
111         {
112             registers.Write(offset, value);
113         }
114 
Reset()115         public override void Reset()
116         {
117             base.Reset();
118             registers.Reset();
119         }
120 
121         public long Size => 0x158;
122 
123         private readonly QuadWordRegisterCollection registers;
124 
125         private const int NumberOfComparators = 3;
126 
127         // According to HPET Specification (2.4),
128         // COUNTER_CLK_PERIOD should be at most 10^8, so frequency must be at least 10^7.
129         private const int MinimumFrequency = (int)1e7;
130 
131         private enum Registers : long
132         {
133             GeneralCapabilitiesAndID = 0x0,
134             GeneralConfiguration = 0x10,
135             GeneralInterruptStatus = 0x20,
136             MainCounterValue = 0xf0,
137             Timer0ConfigurationAndCapability = 0x100,
138             Timer0ComparatorValue = 0x108,
139             Timer0FSBInterruptRoute = 0x110,
140             Timer1ConfigurationAndCapability = 0x120,
141             Timer1ComparatorValue = 0x128,
142             Timer1FSBInterruptRoute = 0x130,
143             Timer2ConfigurationAndCapability = 0x140,
144             Timer2ComparatorValue = 0x148,
145             Timer2FSBInterruptRoute = 0x150,
146         }
147     }
148 }
149