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.Core; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Time; 12 13 namespace Antmicro.Renode.Peripherals.Timers 14 { 15 public class MiV_CoreTimer : LimitTimer, IDoubleWordPeripheral, IKnownSize 16 { MiV_CoreTimer(IMachine machine, long clockFrequency)17 public MiV_CoreTimer(IMachine machine, long clockFrequency) : base(machine.ClockSource, clockFrequency, limit: uint.MaxValue, autoUpdate: true, eventEnabled: true) 18 { 19 this.machine = machine; 20 IRQ = new GPIO(); 21 LimitReached += delegate 22 { 23 IRQ.Set(true); 24 }; 25 26 var registersMap = new Dictionary<long, DoubleWordRegister> 27 { 28 {(long)Registers.Load, new DoubleWordRegister(this) 29 .WithValueField(0, 32, name: "LoadValue", 30 writeCallback: (_, val) => 31 { 32 Limit = val; 33 if(Mode == WorkMode.OneShot) 34 { 35 Enabled = true; 36 } 37 }, 38 valueProviderCallback: (_) => checked((uint)Limit) 39 ) 40 }, 41 42 {(long)Registers.Value, new DoubleWordRegister(this, 0xFFFFFFFF) 43 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => { 44 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 45 { 46 cpu.SyncTime(); 47 } 48 return checked((uint)Value); 49 }, name: "CurrentValue")}, 50 51 {(long)Registers.Control, new DoubleWordRegister(this) 52 .WithFlag(0, writeCallback: (_, val) => Enabled = val, valueProviderCallback: _ => Enabled, name: "TimerEnable") 53 .WithFlag(1, writeCallback: (_, val) => EventEnabled = val, valueProviderCallback: _ => EventEnabled, name: "InterruptEnable") 54 .WithValueField(2, 1, writeCallback: (_, val) => Mode = val == 0 ? WorkMode.Periodic : WorkMode.OneShot, valueProviderCallback: _ => Mode == WorkMode.OneShot ? 1 : 0u, name: "TimerMode")}, 55 // bits 31:3 not used according to the documentation 56 57 {(long)Registers.ClockPrescale, new DoubleWordRegister(this) 58 .WithValueField(0, 4, name: "Prescale", writeCallback: (_, val) => 59 { 60 Divider = (2 << (val < 9 ? (int)val : 9)); 61 }, valueProviderCallback: _ => 62 { 63 var currDivider = (Divider >> 1) - 1; 64 var result = 0u; 65 while(currDivider != 0) 66 { 67 currDivider >>= 1; 68 result++; 69 } 70 return result; 71 })}, 72 73 {(long)Registers.InterruptClear, new DoubleWordRegister(this) 74 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, __) => { IRQ.Set(false); this.ClearInterrupt(); })}, 75 76 {(long)Registers.RawInterruptStatus, new DoubleWordRegister(this) 77 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => this.RawInterrupt)}, 78 79 {(long)Registers.MaskedInterruptStatus, new DoubleWordRegister(this) 80 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => this.Interrupt)} 81 }; 82 83 registers = new DoubleWordRegisterCollection(this, registersMap); 84 } 85 ReadDoubleWord(long offset)86 public uint ReadDoubleWord(long offset) 87 { 88 return registers.Read(offset); 89 } 90 Reset()91 public override void Reset() 92 { 93 base.Reset(); 94 registers.Reset(); 95 IRQ.Set(false); 96 } 97 WriteDoubleWord(long offset, uint value)98 public void WriteDoubleWord(long offset, uint value) 99 { 100 registers.Write(offset, value); 101 } 102 103 public GPIO IRQ { get; private set; } 104 105 public long Size => 0x1C; 106 107 private readonly DoubleWordRegisterCollection registers; 108 private readonly IMachine machine; 109 110 private enum Registers : long 111 { 112 Load = 0x0, 113 Value = 0x04, 114 Control = 0x08, 115 ClockPrescale = 0x0C, 116 InterruptClear = 0x10, 117 RawInterruptStatus = 0x14, 118 MaskedInterruptStatus = 0x18 119 } 120 } 121 } 122