1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using Antmicro.Renode.Core; 8 using Antmicro.Renode.Logging; 9 using Antmicro.Renode.Time; 10 using Antmicro.Renode.Core.Structure.Registers; 11 12 namespace Antmicro.Renode.Peripherals.Timers 13 { 14 // This timer model is limited to only one match channel (channel 0), and only in reset mode (MR0R=1). 15 public class LPC_CTimer : BasicDoubleWordPeripheral, IKnownSize 16 { LPC_CTimer(IMachine machine, long frequency = DefaultFrequency)17 public LPC_CTimer(IMachine machine, long frequency = DefaultFrequency) : base(machine) 18 { 19 this.frequency = frequency; 20 timer = new LimitTimer(machine.ClockSource, frequency, this, nameof(timer), 21 direction: Direction.Ascending, eventEnabled: true); 22 timer.LimitReached += () => 23 { 24 match0InterruptFlag.Value = true; 25 UpdateInterrupt(); 26 }; 27 IRQ = new GPIO(); 28 29 DefineRegisters(); 30 Reset(); 31 } 32 Reset()33 public override void Reset() 34 { 35 IRQ.Unset(); 36 base.Reset(); 37 } 38 39 public GPIO IRQ { get; } 40 public long Size => 0x100; 41 DefineRegisters()42 private void DefineRegisters() 43 { 44 Registers.Interrupt.Define(this) 45 .WithFlag(0, name: "MR0INT", 46 mode: FieldMode.Read | FieldMode.WriteOneToClear, flagField: out match0InterruptFlag) 47 .WithTaggedFlag("MR1INT", 1) 48 .WithTaggedFlag("MR2INT", 2) 49 .WithTaggedFlag("MR3INT", 3) 50 .WithTaggedFlag("CR0INT", 4) 51 .WithTaggedFlag("CR1INT", 5) 52 .WithTaggedFlag("CR2INT", 6) 53 .WithReservedBits(7, 25) 54 .WithChangeCallback((_, __) => UpdateInterrupt()); 55 56 Registers.TimerControl.Define(this) 57 .WithFlag(0, name: "CEN", 58 valueProviderCallback: _ => timer.Enabled, 59 changeCallback: (_, value) => timer.Enabled = value) 60 .WithFlag(1, name: "CRST", 61 changeCallback: (_, value) => 62 { 63 if(value) 64 { 65 timer.Value = 0; 66 } 67 // The counters remain reset until CRST is returned to zero. 68 timer.Enabled = !value; 69 }) 70 .WithReservedBits(2, 30); 71 72 Registers.TimerCounter.Define(this) 73 .WithValueField(0, 32, name: "TCVAL", 74 mode: FieldMode.Read, valueProviderCallback: _ => timer.Value); 75 76 Registers.Prescale.Define(this) 77 .WithValueField(0, 32, name: "PRVAL", 78 valueProviderCallback: _ => (ulong)timer.Divider - 1, 79 changeCallback: (_, value) => timer.Divider = (int)value + 1); 80 81 Registers.PrescaleCounter.Define(this) 82 .WithTag("PCVAL", 0, 32) 83 .WithReadCallback((_, __) => this.WarningLog("Prescale counter is not implemented")); 84 85 Registers.MatchControl.Define(this) 86 .WithFlag(0, name: "MR0I", 87 flagField: out match0InterruptEnable) 88 .WithFlag(1, name: "MR0R", 89 writeCallback: (_, value) => 90 { 91 if(!value) 92 { 93 this.WarningLog("Non-resetting compare is not implemented"); 94 } 95 }) 96 .WithTaggedFlag("MR0S", 2) // not planned controllable TC stop per match channel, unused by Zephyr and Linux 97 .WithTaggedFlag("MR1I", 3) 98 .WithTaggedFlag("MR1R", 4) 99 .WithTaggedFlag("MR1S", 5) 100 .WithTaggedFlag("MR2I", 6) 101 .WithTaggedFlag("MR2R", 7) 102 .WithTaggedFlag("MR2S", 8) 103 .WithTaggedFlag("MR3I", 9) 104 .WithTaggedFlag("MR3R", 10) 105 .WithTaggedFlag("MR3S", 11) 106 .WithReservedBits(12, 12) 107 .WithTaggedFlag("MR0RL", 24) 108 .WithTaggedFlag("MR1RL", 25) 109 .WithTaggedFlag("MR2RL", 26) 110 .WithTaggedFlag("MR3RL", 27) 111 .WithReservedBits(28, 4) 112 .WithWriteCallback((_, __) => UpdateInterrupt()); 113 114 Registers.Match0.Define(this) 115 .WithValueField(0, 32, name: "MATCH", 116 valueProviderCallback: _ => timer.Limit, 117 changeCallback: (_, value) => timer.Limit = value); 118 119 Registers.Match1.Define(this) 120 .WithTag("MATCH", 0, 32); 121 122 Registers.Match2.Define(this) 123 .WithTag("MATCH", 0, 32); 124 125 Registers.Match3.Define(this) 126 .WithTag("MATCH", 0, 32); 127 128 Registers.CaptureControl.Define(this) 129 .WithTaggedFlag("CAP0RE", 0) 130 .WithTaggedFlag("CAP0FE", 1) 131 .WithTaggedFlag("CAP0I", 2) 132 .WithTaggedFlag("CAP1RE", 3) 133 .WithTaggedFlag("CAP1FE", 4) 134 .WithTaggedFlag("CAP1I", 5) 135 .WithTaggedFlag("CAP2RE", 6) 136 .WithTaggedFlag("CAP2FE", 7) 137 .WithTaggedFlag("CAP2I", 8) 138 .WithReservedBits(9, 23); 139 140 Registers.Capture0.DefineMany(this, NumberOfChannels, (register, i) => register 141 .WithTag("CAP", 0, 32) 142 ); 143 144 Registers.ExternalMatch.Define(this) 145 .WithTaggedFlag("EM0", 0) 146 .WithTaggedFlag("EM1", 1) 147 .WithTaggedFlag("EM2", 2) 148 .WithTaggedFlag("EM3", 3) 149 .WithTag("EMC0", 4, 2) 150 .WithTag("EMC1", 6, 2) 151 .WithTag("EMC2", 8, 2) 152 .WithTag("EMC3", 10, 2) 153 .WithReservedBits(12, 20); 154 155 Registers.CountControl.Define(this) 156 .WithTag("CTMODE", 0, 2) 157 .WithTag("CINSEL", 2, 2) 158 .WithTaggedFlag("ENCC", 4) 159 .WithTag("SELCC", 5, 2) 160 .WithReservedBits(8, 24); 161 162 Registers.PwmControl.Define(this) 163 .WithTaggedFlag("PWMEN0", 0) 164 .WithTaggedFlag("PWMEN1", 1) 165 .WithTaggedFlag("PWMEN2", 2) 166 .WithTaggedFlag("PWMEN3", 3) 167 .WithReservedBits(5, 27); 168 169 Registers.Match0Shadow.DefineMany(this, NumberOfChannels, (register, i) => register 170 .WithTag("SHADOW", 0, 32) 171 ); 172 } 173 UpdateInterrupt()174 private void UpdateInterrupt() 175 { 176 var irqValue = match0InterruptEnable.Value && match0InterruptFlag.Value; 177 IRQ.Set(irqValue); 178 this.DebugLog("IRQ set to {0}", irqValue); 179 } 180 181 private IFlagRegisterField match0InterruptFlag; 182 private IFlagRegisterField match0InterruptEnable; 183 184 private readonly LimitTimer timer; 185 private readonly long frequency; 186 187 // Currently only one channel (index 0) is implemented 188 private const int NumberOfChannels = 4; 189 private const long DefaultFrequency = 10000000; 190 191 private enum Registers : uint 192 { 193 Interrupt = 0x00, 194 TimerControl = 0x04, 195 TimerCounter = 0x08, 196 Prescale = 0x0c, 197 PrescaleCounter = 0x10, 198 MatchControl = 0x14, 199 Match0 = 0x18, 200 Match1 = 0x1c, 201 Match2 = 0x20, 202 Match3 = 0x24, 203 CaptureControl = 0x28, 204 Capture0 = 0x2c, 205 Capture1 = 0x30, 206 Capture2 = 0x34, 207 Capture3 = 0x38, 208 ExternalMatch = 0x3c, 209 // mind the gap 210 CountControl = 0x70, 211 PwmControl = 0x74, 212 Match0Shadow = 0x78, 213 Match1Shadow = 0x7c, 214 Match2Shadow = 0x80, 215 Match3Shadow = 0x84, 216 } 217 } 218 } 219