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.Time;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Peripherals.CPU;
14 using Antmicro.Renode.Peripherals.Miscellaneous;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.Timers
18 {
19     public class MAX32650_Timer : LimitTimer, IDoubleWordPeripheral, IKnownSize
20     {
MAX32650_Timer(IMachine machine, MAX32650_GCR gcr)21         public MAX32650_Timer(IMachine machine, MAX32650_GCR gcr) : base(machine.ClockSource, gcr.SysClk / 2, eventEnabled: true, direction: Direction.Ascending)
22         {
23             registers = new DoubleWordRegisterCollection(this, DefineRegisters());
24             this.machine = machine;
25 
26             gcr.SysClkChanged += UpdateTimerFrequency;
27             LimitReached += OnCompare;
28 
29             IRQ = new GPIO();
30         }
31 
WriteDoubleWord(long address, uint value)32         public void WriteDoubleWord(long address, uint value)
33         {
34             registers.Write(address, value);
35         }
36 
ReadDoubleWord(long address)37         public uint ReadDoubleWord(long address)
38         {
39             return registers.Read(address);
40         }
41 
Reset()42         public override void Reset()
43         {
44             base.Reset();
45             registers.Reset();
46             IRQ.Unset();
47             prescaler = 0;
48         }
49 
50         public long Size => 0x400;
51 
52         public GPIO IRQ { get; }
53 
UpdateTimerFrequency(long newSysClkFrequency)54         private void UpdateTimerFrequency(long newSysClkFrequency)
55         {
56             // Peripheral clock frequency is half of the System clock frequency
57             Frequency = newSysClkFrequency / 2;
58         }
59 
OnCompare()60         private void OnCompare()
61         {
62             Value = 1;
63             interruptPending.Value = true;
64             UpdateInterrupts();
65         }
66 
UpdateInterrupts()67         private void UpdateInterrupts()
68         {
69             IRQ.Set(interruptPending.Value);
70         }
71 
DefineRegisters()72         private Dictionary<long, DoubleWordRegister> DefineRegisters()
73         {
74             return new Dictionary<long, DoubleWordRegister>()
75             {
76                 {(long)Registers.Counter, new DoubleWordRegister(this)
77                     .WithValueField(0, 32, name: "CNT.count",
78                         valueProviderCallback: _ => (uint)Value,
79                         changeCallback: (_, value) => Value = value)
80                 },
81                 {(long)Registers.Compare, new DoubleWordRegister(this)
82                     .WithValueField(0, 32, name: "CMP.compare",
83                         valueProviderCallback: _ => (uint)Limit,
84                         changeCallback: (_, value) =>
85                         {
86                             Limit = value;
87                             Value = 1;
88                         })
89                 },
90                 {(long)Registers.Interrupt, new DoubleWordRegister(this)
91                     .WithFlag(0, out interruptPending, name: "INT.irq",
92                         writeCallback: (_, __) => interruptPending.Value = false)
93                     .WithReservedBits(1, 31)
94                     .WithChangeCallback((_, __) => UpdateInterrupts())
95                 },
96                 {(long)Registers.Control, new DoubleWordRegister(this, 0x1000)
97                     .WithEnumField<DoubleWordRegister, TimerMode>(0, 3, name: "CN.tmode",
98                         changeCallback: (_, newMode) =>
99                         {
100                             switch(newMode)
101                             {
102                                 case TimerMode.OneShot:
103                                     Mode = WorkMode.OneShot;
104                                     break;
105                                 case TimerMode.Continuous:
106                                     Mode = WorkMode.Periodic;
107                                     break;
108                                 default:
109                                     this.Log(LogLevel.Warning, "Timer mode set to an unsupported mode: {0}; ignoring", newMode);
110                                     break;
111                             }
112                         })
113                     .WithValueField(3, 3, name: "CN.pres",
114                         writeCallback: (_, val) =>
115                         {
116                             prescaler = (prescaler & 0x8) | (uint)val;
117                             Divider = 1 << (int)prescaler;
118                         })
119                     .WithTaggedFlag("CN.tpol", 6)
120                     .WithFlag(7, name: "CN.ten",
121                         valueProviderCallback: _ => Enabled,
122                         changeCallback: (_, value) =>
123                         {
124                             Enabled = value;
125                             Value = 1;
126                         })
127                     .WithFlag(8, name: "CN.pres3",
128                         writeCallback: (_, val) =>
129                         {
130                             BitHelper.SetBit(ref prescaler, 3, val);
131                             Divider = 1 << (int)prescaler;
132                         })
133                     .WithTaggedFlag("CN.pwmsync", 9)
134                     .WithTaggedFlag("CN.nolhpol", 10)
135                     .WithTaggedFlag("CN.nollpol", 11)
136                     // We are using flag instead of tagged flag to hush down unnecessary log messages
137                     .WithFlag(12, name: "CN.pwmckbd")
138                     .WithReservedBits(13, 19)
139                 }
140             };
141         }
142 
143         private uint prescaler;
144 
145         private IFlagRegisterField interruptPending;
146 
147         private readonly IMachine machine;
148         private readonly DoubleWordRegisterCollection registers;
149 
150         private enum TimerMode : byte
151         {
152             OneShot = 0x00,
153             Continuous,
154             Counter,
155             PWM,
156             Capture,
157             Compare,
158             Gated,
159             CaptureCompare,
160         }
161 
162         private enum Registers : long
163         {
164             Counter = 0x00,
165             Compare = 0x04,
166             PWM = 0x08,
167             Interrupt = 0x0C,
168             Control = 0x10,
169             NonOverlappingCompare = 0x14,
170         }
171     }
172 }
173