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 
8 using System;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Peripherals.Timers;
15 using Antmicro.Renode.Time;
16 
17 namespace Antmicro.Renode.Peripherals
18 {
19     public class HiFive_PWM : IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput, IPeripheralRegister<IGPIOReceiver, NumberRegistrationPoint<int>>
20     {
HiFive_PWM(IMachine machine, uint frequency = 16000000)21         public HiFive_PWM(IMachine machine, uint frequency = 16000000)
22         {
23             connections = new Dictionary<int, IGPIO>
24             {
25                 {0, new GPIO()},
26                 {1, new GPIO()},
27                 {2, new GPIO()},
28                 {3, new GPIO()}
29             };
30 
31             this.machine = machine;
32 
33             IRQ = new GPIO();
34             interruptPending = new IFlagRegisterField[NumberOfComparers];
35             compare = new IValueRegisterField[NumberOfComparers];
36 
37             rawTimer = new LimitTimer(machine.ClockSource, frequency, this, nameof(rawTimer), TimerLimit + 1, workMode: WorkMode.Periodic, direction: Direction.Ascending, eventEnabled: true);
38             rawTimer.LimitReached += HandleLimitReached;
39 
40             timers = new ComparingTimer[NumberOfComparers];
41             for(var i = 0; i < timers.Length; i++)
42             {
43                 var j = i;
44                 timers[i] = new ComparingTimer(machine.ClockSource, frequency, this, (i + 1).ToString(), CompareMask + 1, workMode: WorkMode.Periodic, compare: CompareMask, direction: Direction.Ascending, eventEnabled: true);
45                 timers[i].CompareReached += () =>
46                 {
47                     // handle 'pwmzerocmp' flag (defined only for timer0)
48                     if(i == 0)
49                     {
50                         // documentation says that this should be done one cycle after the 'pwms' counter reaches the compare value!
51                         if(timers[i].Value == compare[i].Value && resetAfterMatch.Value)
52                         {
53                             SetValue(0);
54                             HandleLimitReached();
55                         }
56                     }
57 
58                     UpdateCompare(j);
59                 };
60             }
61 
62             var registersMap = new Dictionary<long, DoubleWordRegister>
63             {
64                 {(long)Registers.Configuration, new DoubleWordRegister(this)
65                     .WithValueField(0, 4, out scale, name: "pwmscale", writeCallback: (_, value) =>
66                     {
67                         Array.ForEach(timers, t => t.Divider = (uint)(1 << (int)value));
68                         Array.ForEach(timers, t => t.Value = (rawTimer.Value >> (int)value) & CompareMask);
69                     })
70                     .WithFlag(8, out sticky, name: "pwmsticky")
71                     .WithFlag(9, out resetAfterMatch, name: "zerocmp")
72                     .WithFlag(12, out enableAlways, name: "pwmenalways", writeCallback: (_, __) => RecalculateEnable())
73                     .WithFlag(13, out enableOneShot, name: "pwmenoneshot", writeCallback: (_, __) => RecalculateEnable())
74                     .WithFlag(28, out interruptPending[0], name: "pwmcmp0ip")
75                     .WithFlag(29, out interruptPending[1], name: "pwmcmp1ip")
76                     .WithFlag(30, out interruptPending[2], name: "pwmcmp2ip")
77                     .WithFlag(31, out interruptPending[3], name: "pwmcmp3ip")
78                     // this is a global update
79                     .WithWriteCallback((_, __) => UpdateInterrupt())
80                 },
81 
82                 {(long)Registers.Count, new DoubleWordRegister(this)
83                     .WithValueField(0, 31, name: "pwmcount", valueProviderCallback: _ => (uint)rawTimer.Value, writeCallback: (_, value) =>
84                     {
85                         SetValue((uint)value);
86                         UpdateInterrupt();
87                     })
88                     .WithReservedBits(31, 1)
89                 },
90 
91                 {(long)Registers.ScaledCount, new DoubleWordRegister(this)
92                     .WithValueField(0, 16, FieldMode.Read, name: "pwms", valueProviderCallback: _ => (uint)timers[0].Value)
93                     .WithReservedBits(16, 16)
94                 },
95 
96                 {(long)Registers.Compare0, new DoubleWordRegister(this, 0xFFFF)
97                     .WithValueField(0, 16, out compare[0], name: "pwmcmp0", writeCallback: (_, value) => UpdateCompare(0))
98                     .WithReservedBits(16, 16)
99                 },
100 
101                 {(long)Registers.Compare1, new DoubleWordRegister(this, 0xFFFF)
102                     .WithValueField(0, 16, out compare[1], name: "pwmcmp1", writeCallback: (_, value) => UpdateCompare(1))
103                     .WithReservedBits(16, 16)
104                 },
105 
106                 {(long)Registers.Compare2, new DoubleWordRegister(this, 0xFFFF)
107                     .WithValueField(0, 16, out compare[2], name: "pwmcmp2", writeCallback: (_, value) => UpdateCompare(2))
108                     .WithReservedBits(16, 16)
109                 },
110 
111                 {(long)Registers.Compare3, new DoubleWordRegister(this, 0xFFFF)
112                     .WithValueField(0, 16, out compare[3], name: "pwmcmp3", writeCallback: (_, value) => UpdateCompare(3))
113                     .WithReservedBits(16, 16)
114                 }
115             };
116 
117             registers = new DoubleWordRegisterCollection(this, registersMap);
118         }
119 
Reset()120         public void Reset()
121         {
122             registers.Reset();
123             UpdateInterrupt();
124         }
125 
ReadDoubleWord(long offset)126         public uint ReadDoubleWord(long offset)
127         {
128             return registers.Read(offset);
129         }
130 
WriteDoubleWord(long offset, uint value)131         public void WriteDoubleWord(long offset, uint value)
132         {
133             registers.Write(offset, value);
134         }
135 
136         public GPIO IRQ { get; private set; }
137 
138         public long Size => 0x1000;
139 
140         public IReadOnlyDictionary<int, IGPIO> Connections => connections;
141 
RecalculateEnable()142         private void RecalculateEnable()
143         {
144             var enabled = (enableAlways.Value || enableOneShot.Value);
145             rawTimer.Enabled = enabled;
146             Array.ForEach(timers, t => t.Enabled = enabled);
147         }
148 
HandleLimitReached()149         private void HandleLimitReached()
150         {
151             enableOneShot.Value = false;
152             RecalculateEnable();
153         }
154 
UpdateInterrupt()155         private void UpdateInterrupt()
156         {
157             var shouldBeSet = false;
158             for(var i = 0; i < timers.Length; i++)
159             {
160                 var isInterrupt = (timers[i].Value >= compare[i].Value);
161                 if(sticky.Value)
162                 {
163                     interruptPending[i].Value |= isInterrupt;
164                 }
165                 else
166                 {
167                     interruptPending[i].Value = isInterrupt;
168                 }
169 
170                 connections[i].Set(isInterrupt);
171                 shouldBeSet |= isInterrupt;
172             }
173 
174             IRQ.Set(shouldBeSet);
175         }
176 
UpdateCompare(int i)177         private void UpdateCompare(int i)
178         {
179             timers[i].Compare = (timers[i].Value == 0)
180                 ? compare[i].Value
181                 : 0;
182 
183             UpdateInterrupt();
184         }
185 
SetValue(uint value)186         private void SetValue(uint value)
187         {
188             rawTimer.Value = value;
189             Array.ForEach(timers, t => t.Value = (value >> (int)scale.Value) & CompareMask);
190         }
191 
Register(IGPIOReceiver peripheral, NumberRegistrationPoint<int> registrationPoint)192         public void Register(IGPIOReceiver peripheral, NumberRegistrationPoint<int> registrationPoint)
193         {
194             machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
195         }
196 
Unregister(IGPIOReceiver peripheral)197         public void Unregister(IGPIOReceiver peripheral)
198         {
199             machine.UnregisterAsAChildOf(this, peripheral);
200         }
201 
202         private IFlagRegisterField[] interruptPending;
203         private IFlagRegisterField sticky;
204         private IFlagRegisterField resetAfterMatch;
205         private IFlagRegisterField enableAlways;
206         private IFlagRegisterField enableOneShot;
207         private IValueRegisterField scale;
208         private IValueRegisterField[] compare;
209 
210         private readonly LimitTimer rawTimer;
211         private readonly ComparingTimer[] timers;
212         private readonly DoubleWordRegisterCollection registers;
213         private readonly Dictionary<int, IGPIO> connections;
214         private readonly IMachine machine;
215 
216         private enum Registers
217         {
218             Configuration = 0x0,
219             // 0x4 is reserved
220             Count = 0x8,
221             // 0xC is reserved
222             ScaledCount = 0x10,
223             // 0x14, 0x18, 0x1C are reserved
224             Compare0 = 0x20,
225             Compare1 = 0x24,
226             Compare2 = 0x28,
227             Compare3 = 0x2C
228         }
229 
230         private const uint TimerLimit = (1u << 31) - 1;
231         private const uint CompareMask = (1u << 16) - 1;
232         private const int NumberOfComparers = 4;
233     }
234 }