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