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