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 System.Collections.ObjectModel;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class PULP_Timer : BasicDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput
17     {
PULP_Timer(IMachine machine, long frequency)18         public PULP_Timer(IMachine machine, long frequency) : base(machine)
19         {
20             interruptEnable = new IFlagRegisterField[NumberOfTimers];
21             oneShot = new IFlagRegisterField[NumberOfTimers];
22             cycleMode = new IFlagRegisterField[NumberOfTimers];
23 
24             var irqs = new Dictionary<int, IGPIO>();
25             for(var i = 0; i < NumberOfTimers; i++)
26             {
27                 irqs[i] = new GPIO();
28             }
29             Connections = new ReadOnlyDictionary<int, IGPIO>(irqs);
30 
31             timers = new ComparingTimer[NumberOfTimers];
32             for(var i = 0; i < NumberOfTimers; i++)
33             {
34                 var j = i;
35                 timers[j] = new ComparingTimer(machine.ClockSource, frequency, this, $"Timer {j}", limit: uint.MaxValue, direction: Time.Direction.Ascending, workMode: Time.WorkMode.Periodic,
36                     enabled: false, eventEnabled: true, compare: uint.MaxValue);
37 
38                 timers[j].CompareReached += delegate
39                 {
40                     this.Log(LogLevel.Noisy, "Timer {0} IRQ compare event", i);
41                     if(interruptEnable[j].Value)
42                     {
43                         Connections[j].Blink(); //verified with RTL
44                     }
45                     if(oneShot[j].Value)
46                     {
47                         timers[j].Enabled = false;
48                     }
49                     if(cycleMode[j].Value)
50                     {
51                         timers[j].Value = 0;
52                     }
53                 };
54 
55                 var shift = j * 4;
56 
57                 var configReg = ((Registers)(Registers.ConfigLow + shift)).Define(this)
58                     .WithFlag(0,
59                         writeCallback: (_, val) =>
60                         {
61                             if(val)
62                             {
63                                 timers[j].Enabled = val;
64                             }
65                         },
66                         valueProviderCallback: _ => timers[j].Enabled,
67                         name: $"Timer {j} enable (EN)")
68                     .WithFlag(1,
69                         writeCallback: (_, val) =>
70                         {
71                             if(val)
72                             {
73                                 timers[j].Value = 0;
74                                 // this also should affect the prescaller
75                             }
76                         }, valueProviderCallback: _ => false, name: "RST")
77                     .WithFlag(2, out interruptEnable[j], name: "IRQEN")
78                     .WithTag("IEM", 3, 1)
79                     .WithFlag(4, out cycleMode[j], name: "MODE")
80                     .WithFlag(5, out oneShot[j], name: "ONE_S")
81                     .WithTag("PEN", 6, 1)
82                     .WithTag("CCFG", 7, 1)
83                     .WithTag("PVAL", 8, 8)
84                     .WithReservedBits(16, 15)
85                 ;
86                 if(j == 0)
87                 {
88                     // 64-bit timer mode not yet implemented
89                     configReg.Tag("CASC", 31, 1);
90                 }
91                 else
92                 {
93                     configReg.Reserved(31, 1);
94                 }
95 
96                 ((Registers)(Registers.CounterValueLow + shift)).Define(this)
97                     .WithValueField(0, 32,
98                         writeCallback: (_, val) => timers[j].Value = val,
99                         valueProviderCallback: _ =>
100                         {
101                             if(machine.SystemBus.TryGetCurrentCPU(out var cpu))
102                             {
103                                 // being here means we are on the CPU thread
104                                 cpu.SyncTime();
105                             }
106                             return (uint)timers[j].Value;
107                         },
108                         name: $"Timer {j} counter value (CNT)")
109                 ;
110 
111                 ((Registers)(Registers.ComparatorLow + shift)).Define(this)
112                     .WithValueField(0, 32,
113                         writeCallback: (_, val) =>
114                         {
115                             this.Log(LogLevel.Debug, "Setting Timer {0} Compare to: {1:X}", j, val);
116                             timers[j].Compare = val;
117                         },
118                         valueProviderCallback: _ =>
119                         {
120                             return (uint)timers[j].Compare;
121                         },
122                         name: $"Timer {j} comparator value (CMP)")
123                 ;
124             }
125         }
126 
Reset()127         public override void Reset()
128         {
129             base.Reset();
130             for(var i = 0; i < NumberOfTimers; i++)
131             {
132                 timers[i].Reset();
133             }
134             // no need to reset Connections, as they only blink
135         }
136 
137         public long Size => 0x80;
138 
139         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
140 
141         private ComparingTimer[] timers;
142         private readonly IFlagRegisterField[] interruptEnable;
143         private readonly IFlagRegisterField[] oneShot;
144         private readonly IFlagRegisterField[] cycleMode;
145 
146         private const int NumberOfTimers = 2;
147 
148         private enum Registers : long
149         {
150             ConfigLow = 0x0,
151             ConfigHigh = 0x4,
152             CounterValueLow = 0x8,
153             CounterValueHigh = 0xC,
154             ComparatorLow = 0x10,
155             ComparatorHigh = 0x14,
156             StartCountingLow = 0x18,
157             StartCountingHigh = 0x1C,
158             ResetLow = 0x20,
159             ResetHigh = 0x24
160         }
161     }
162 }
163