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 Antmicro.Renode.Logging; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Time; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 17 public class RenesasRZG_GTM : BasicDoubleWordPeripheral, IKnownSize 18 { RenesasRZG_GTM(IMachine machine, long frequency)19 public RenesasRZG_GTM(IMachine machine, long frequency) : base(machine) 20 { 21 timer = new LimitTimer(machine.ClockSource, frequency, this, "timer", FreeRunLimit, Direction.Descending, workMode: WorkMode.Periodic, eventEnabled: true); 22 timer.LimitReached += HandleLimitReached; 23 IRQ = new GPIO(); 24 25 DefineRegisters(); 26 Reset(); 27 } 28 Reset()29 public override void Reset() 30 { 31 base.Reset(); 32 IRQ.Unset(); 33 timer.Reset(); 34 } 35 36 public GPIO IRQ { get; } 37 38 public long Size => 0x24; 39 DefineRegisters()40 private void DefineRegisters() 41 { 42 Registers.Compare.Define(this) 43 .WithValueField(0, 32, out newTimerLimit, name: "OSTMnCMP", 44 writeCallback: (_, value) => 45 { 46 if(operatingMode.Value == OperatingMode.FreeRunning && value >= TimerValue) 47 { 48 // Limit is only updated if the new compare 49 // value can be reached before the roll-over 50 SetTimerLimit(value); 51 } 52 }); 53 54 Registers.Counter.Define(this) 55 .WithValueField(0, 32, FieldMode.Read, name: "OSTMnCNT", 56 valueProviderCallback: _ => TimerValue); 57 58 Registers.CountEnableStatus.Define(this) 59 .WithFlag(0, FieldMode.Read, name: "OSTMnTE", 60 valueProviderCallback: _ => timer.Enabled) 61 .WithReservedBits(1, 31); 62 63 Registers.CountStartTrigger.Define(this) 64 .WithFlag(0, FieldMode.Write, name: "OSTMnTS", 65 writeCallback: (_, value) => 66 { 67 if(!value) 68 { 69 return; 70 } 71 72 SetTimerLimit(newTimerLimit.Value); 73 if(timer.Enabled) 74 { 75 // Restart 76 if(operatingMode.Value == OperatingMode.Interval) 77 { 78 timer.ResetValue(); 79 UpdateInterrupts(countingStart: true); 80 } 81 } 82 else 83 { 84 // Regular start 85 timer.ResetValue(); 86 timer.Enabled = true; 87 UpdateInterrupts(countingStart: true); 88 } 89 }) 90 .WithReservedBits(1, 31); 91 92 Registers.CountStopTrigger.Define(this) 93 .WithFlag(0, FieldMode.Write, name: "OSTMnTT", 94 writeCallback: (_, value) => 95 { 96 if(value) 97 { 98 timer.Enabled = false; 99 } 100 }) 101 .WithReservedBits(1, 31); 102 103 Registers.Control.Define(this) 104 .WithFlag(0, out interruptWhenCountingStarts, name: "OSTMnMD0") 105 .WithEnumField(1, 1, out operatingMode, name: "OSTMnMD1", 106 changeCallback: (_, newMode) => 107 { 108 switch(newMode) 109 { 110 case OperatingMode.Interval: 111 timer.Direction = Direction.Descending; 112 break; 113 case OperatingMode.FreeRunning: 114 timer.Direction = Direction.Ascending; 115 break; 116 default: 117 throw new Exception("Unreachable"); 118 } 119 }) 120 .WithReservedBits(2, 30); 121 } 122 UpdateInterrupts(bool countingStart = false)123 private void UpdateInterrupts(bool countingStart = false) 124 { 125 if(countingStart && !interruptWhenCountingStarts.Value) 126 { 127 return; 128 } 129 130 IRQ.Blink(); 131 this.DebugLog("IRQ triggered"); 132 } 133 SetTimerLimit(ulong value)134 private void SetTimerLimit(ulong value) 135 { 136 value++; 137 if(timer.Limit != value) 138 { 139 timer.Limit = value; 140 } 141 } 142 HandleLimitReached()143 private void HandleLimitReached() 144 { 145 switch(operatingMode.Value) 146 { 147 case OperatingMode.Interval: 148 SetTimerLimit(newTimerLimit.Value); 149 break; 150 case OperatingMode.FreeRunning: 151 { 152 var currentValue = timer.Value; 153 // Handle roll-over 154 if(currentValue == FreeRunLimit) 155 { 156 SetTimerLimit(newTimerLimit.Value); 157 return; 158 } 159 else 160 { 161 SetTimerLimit(FreeRunLimit); 162 timer.Value = currentValue; 163 // fallthrough to trigger IRQ 164 } 165 break; 166 } 167 default: 168 throw new Exception("Unreachable"); 169 } 170 UpdateInterrupts(); 171 } 172 173 private ulong TimerValue 174 { 175 get 176 { 177 if(timer.Enabled && sysbus.TryGetCurrentCPU(out var cpu)) 178 { 179 cpu.SyncTime(); 180 } 181 return timer.Value; 182 } 183 } 184 185 private IEnumRegisterField<OperatingMode> operatingMode; 186 private IFlagRegisterField interruptWhenCountingStarts; 187 private IValueRegisterField newTimerLimit; 188 189 private readonly LimitTimer timer; 190 191 private const ulong FreeRunLimit = (1UL << 32) - 1; 192 193 private enum OperatingMode 194 { 195 Interval = 0, 196 FreeRunning = 1, 197 } 198 199 private enum Registers 200 { 201 Compare = 0x00, // OSTMnCMP 202 Counter = 0x04, // OSTMnCNT 203 CountEnableStatus = 0x10, // OSTMnTE 204 CountStartTrigger = 0x14, // OSTMnTS 205 CountStopTrigger = 0x18, // OSTMnTT 206 Control = 0x20, // OSTMnCTL 207 } 208 } 209 } 210