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.Core.Structure.Registers; 9 using Antmicro.Renode.Utilities; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 13 namespace Antmicro.Renode.Peripherals.Timers 14 { 15 // this is a model of LiteX timer in the default configuration: 16 // * width: 32 bits 17 // * csr data width: 8 bit 18 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 19 public class LiteX_Timer : BasicDoubleWordPeripheral, IKnownSize 20 { LiteX_Timer(IMachine machine, long frequency)21 public LiteX_Timer(IMachine machine, long frequency) : base(machine) 22 { 23 uptimeTimer = new LimitTimer(machine.ClockSource, frequency, this, nameof(uptimeTimer), direction: Antmicro.Renode.Time.Direction.Ascending, enabled: true); 24 innerTimer = new LimitTimer(machine.ClockSource, frequency, this, nameof(innerTimer), eventEnabled: true, autoUpdate: true); 25 innerTimer.LimitReached += delegate 26 { 27 this.Log(LogLevel.Noisy, "Limit reached"); 28 irqPending.Value = true; 29 UpdateInterrupts(); 30 31 if(reloadValue == 0) 32 { 33 this.Log(LogLevel.Noisy, "No realod value - disabling the timer"); 34 innerTimer.Enabled = false; 35 } 36 innerTimer.Limit = reloadValue; 37 }; 38 DefineRegisters(); 39 } 40 Reset()41 public override void Reset() 42 { 43 base.Reset(); 44 innerTimer.Reset(); 45 uptimeTimer.Reset(); 46 latchedValue = 0; 47 loadValue = 0; 48 reloadValue = 0; 49 uptimeLatchedValue = 0; 50 51 UpdateInterrupts(); 52 } 53 54 public GPIO IRQ { get; } = new GPIO(); 55 56 public long Size => 0x68; 57 DefineRegisters()58 private void DefineRegisters() 59 { 60 // LOAD0 contains most significant 8 bits 61 // LOAD3 contains least significant 8 bits 62 Registers.Load0.DefineMany(this, SubregistersCount, (reg, idx) => 63 { 64 reg.WithValueField(0, 8, name: $"LOAD{idx}", writeCallback: (_, val) => 65 { 66 BitHelper.ReplaceBits(ref loadValue, width: 8, source: (uint)val, destinationPosition: 24 - idx * 8); 67 }); 68 }); 69 70 // RELOAD0 contains most significant 8 bits 71 // RELOAD3 contains least significant 8 bits 72 Registers.Reload0.DefineMany(this, SubregistersCount, (reg, idx) => 73 { 74 reg.WithValueField(0, 8, name: $"RELOAD{idx}", writeCallback: (_, val) => 75 { 76 BitHelper.ReplaceBits(ref reloadValue, width: 8, source: (uint)val, destinationPosition: 24 - idx * 8); 77 }); 78 }); 79 80 Registers.TimerEnable.Define32(this) 81 .WithFlag(0, name: "ENABLE", writeCallback: (_, val) => 82 { 83 if(innerTimer.Enabled == val) 84 { 85 return; 86 } 87 88 if(val) 89 { 90 innerTimer.Limit = loadValue; 91 this.Log(LogLevel.Noisy, "Enabling timer. Load value: 0x{0:X}, reload value: 0x{1:X}", loadValue, reloadValue); 92 } 93 94 innerTimer.Enabled = val; 95 }) 96 ; 97 98 Registers.TimerUpdateValue.Define32(this) 99 .WithFlag(0, FieldMode.WriteOneToClear, name: "UPDATE_VALUE", writeCallback: (_, val) => 100 { 101 if(val) 102 { 103 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 104 { 105 // being here means we are on the CPU thread 106 cpu.SyncTime(); 107 } 108 109 latchedValue = (uint)innerTimer.Value; 110 } 111 }); 112 ; 113 114 // VALUE0 contains most significant 8 bits 115 // VALUE3 contains least significant 8 bits 116 Registers.Value0.DefineMany(this, SubregistersCount, (reg, idx) => 117 { 118 reg.WithValueField(0, 8, FieldMode.Read, name: $"VALUE{idx}", valueProviderCallback: _ => 119 { 120 return BitHelper.GetValue(latchedValue, 24 - idx * 8, 8); 121 }); 122 }); 123 124 Registers.EventStatus.Define32(this) 125 .WithFlag(0, FieldMode.Read, name: "EV_STATUS", valueProviderCallback: _ => innerTimer.Value == 0) 126 ; 127 128 Registers.EventPending.Define32(this) 129 .WithFlag(0, out irqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "EV_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 130 ; 131 132 Registers.EventEnable.Define32(this) 133 .WithFlag(0, out irqEnabled, name: "EV_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 134 ; 135 136 Registers.UptimeLatch.Define32(this) 137 .WithFlag(0, FieldMode.WriteOneToClear, name: "UPTIME_LATCH", writeCallback: (_, val) => 138 { 139 if(val) 140 { 141 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 142 { 143 // being here means we are on the CPU thread 144 cpu.SyncTime(); 145 } 146 147 uptimeLatchedValue = uptimeTimer.Value; 148 } 149 }); 150 151 // UPTIME_CYCLES0 contains most significant 8 bits 152 // UPTIME_CYCLES7 contains least significant 8 bits 153 Registers.UptimeCycles0.DefineMany(this, 8, (reg, idx) => 154 { 155 reg.WithValueField(0, 8, FieldMode.Read, name: $"UPTIME_CYCLES{idx}", valueProviderCallback: _ => 156 { 157 return (byte)BitHelper.GetValue(uptimeLatchedValue, 56 - idx * 8, 8); 158 }); 159 }); 160 } 161 UpdateInterrupts()162 private void UpdateInterrupts() 163 { 164 this.Log(LogLevel.Noisy, "Setting IRQ: {0}", irqPending.Value && irqEnabled.Value); 165 IRQ.Set(irqPending.Value && irqEnabled.Value); 166 } 167 168 private IFlagRegisterField irqEnabled; 169 private IFlagRegisterField irqPending; 170 171 private uint latchedValue; 172 private uint loadValue; 173 private uint reloadValue; 174 175 private ulong uptimeLatchedValue; 176 177 private readonly LimitTimer innerTimer; 178 private readonly LimitTimer uptimeTimer; 179 180 private const int SubregistersCount = 4; 181 182 private enum Registers 183 { 184 Load0 = 0x0, 185 Load1 = 0x4, 186 Load2 = 0x8, 187 Load3 = 0xC, 188 189 Reload0 = 0x10, 190 Reload1 = 0x14, 191 Reload2 = 0x18, 192 Reload3 = 0x1C, 193 194 TimerEnable = 0x20, 195 TimerUpdateValue = 0x24, 196 197 Value0 = 0x28, 198 Value1 = 0x2C, 199 Value2 = 0x30, 200 Value3 = 0x34, 201 202 EventStatus = 0x38, 203 EventPending = 0x3c, 204 EventEnable = 0x40, 205 206 UptimeLatch = 0x44, 207 208 UptimeCycles0 = 0x48, 209 UptimeCycles1 = 0x4C, 210 UptimeCycles2 = 0x50, 211 UptimeCycles3 = 0x54, 212 UptimeCycles4 = 0x58, 213 UptimeCycles5 = 0x5C, 214 UptimeCycles6 = 0x60, 215 UptimeCycles7 = 0x64 216 } 217 } 218 } 219