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