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 }