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 using System.Collections.Generic; 8 using System.Linq; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Time; 14 using Antmicro.Renode.Peripherals.Timers; 15 using Antmicro.Renode.Peripherals.GPIOPort; 16 17 namespace Antmicro.Renode.Peripherals.X86 18 { 19 public class Quark_PWM : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput 20 { Quark_PWM(IMachine machine)21 public Quark_PWM(IMachine machine) : base(machine, NumberOfInternalTimers) 22 { 23 IRQ = new GPIO(); 24 internalLock = new object(); 25 interruptStatus = new bool[NumberOfInternalTimers]; 26 timers = new LimitTimer[NumberOfInternalTimers]; 27 interruptMask = new bool[NumberOfInternalTimers]; 28 alternativeLoadCount = new uint[NumberOfInternalTimers]; 29 operationMode = new OperationMode[NumberOfInternalTimers]; 30 runningMode = new RunningMode[NumberOfInternalTimers]; 31 for(int i = 0; i < timers.Length; i++) 32 { 33 timers[i] = new LimitTimer(machine.ClockSource, 32000000, this, i.ToString(), eventEnabled: true); 34 var j = i; 35 timers[i].LimitReached += () => HandleLimitReached(j); 36 } 37 PrepareRegisters(); 38 } 39 Reset()40 public override void Reset() 41 { 42 base.Reset(); 43 for(int i = 0; i < NumberOfInternalTimers; i++) 44 { 45 alternativeLoadCount[i] = 0; 46 operationMode[i] = OperationMode.Timer; 47 runningMode[i] = RunningMode.Free; 48 interruptMask[i] = false; 49 interruptStatus[i] = false; 50 timers[i].Reset(); 51 } 52 registers.Reset(); 53 IRQ.Unset(); 54 } 55 ReadDoubleWord(long offset)56 public uint ReadDoubleWord(long offset) 57 { 58 return registers.Read(offset); 59 } 60 WriteDoubleWord(long offset, uint value)61 public void WriteDoubleWord(long offset, uint value) 62 { 63 registers.Write(offset, value); 64 } 65 66 public GPIO IRQ { get; private set; } 67 68 public long Size { get { return 0xC0;} } 69 PrepareRegisters()70 private void PrepareRegisters() 71 { 72 var dict = new Dictionary<long, DoubleWordRegister> 73 { 74 {(long)Registers.TimersInterruptStatus, new DoubleWordRegister(this) 75 // we have to reverse it as: Bit position corresponds to PWM/Timer number 76 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(interruptStatus.Zip(interruptMask, (isSet, isMasked) => isSet && !isMasked).Reverse())) 77 }, 78 {(long)Registers.TimersRawInterruptStatus, new DoubleWordRegister(this) 79 // we have to reverse it as: Bit position corresponds to PWM/Timer number 80 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(interruptStatus.Reverse())) 81 }, 82 {(long)Registers.TimersEndOfInterrupt, new DoubleWordRegister(this) 83 .WithValueField(0, 4, FieldMode.Read, readCallback: (_, __) => HandleEndOfInterrupt()) 84 } 85 }; 86 87 for(int i = 0; i < timers.Length; i++) 88 { 89 var j = i; 90 var offset = 0x14 * i; 91 dict[(long)Registers.Timer1LoadCount + offset] = new DoubleWordRegister(this) 92 .WithValueField(0, 32, writeCallback: (_, val) => { 93 timers[j].Limit = val; 94 timers[j].ResetValue(); 95 }); 96 97 dict[(long)Registers.Timer1CurrentValue + offset] = new DoubleWordRegister(this) 98 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => (uint)timers[j].Value); 99 100 dict[(long)Registers.Timer1Control + offset] = new DoubleWordRegister(this) 101 .WithFlag(0, name: "Enable", writeCallback: (_, val) => timers[j].Enabled = val) 102 .WithFlag(1, name: "Timer Mode", writeCallback: (_, val) => runningMode[j] = val ? RunningMode.UserDefinedCount : RunningMode.Free) 103 .WithFlag(2, name: "Interrupt Mask", writeCallback: (_, val) => interruptMask[j] = val) 104 .WithFlag(3, name: "PWM/Timer Mode", writeCallback: (_, val) => operationMode[j] = val ? OperationMode.PWM : OperationMode.Timer); 105 106 dict[(long)Registers.Timer1EndOfInterrupt + offset] = new DoubleWordRegister(this) 107 .WithValueField(0, 1, FieldMode.Read, readCallback: (_, __) => HandleEndOfInterrupt(j)); 108 109 dict[(long)Registers.Timer1InterruptStatus + offset] = new DoubleWordRegister(this) 110 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => interruptStatus[j]); 111 112 // here we have a different registers offset! 113 dict[(long)Registers.Timer1LoadCount2 + 0x4 * i] = new DoubleWordRegister(this) 114 .WithValueField(0, 32, writeCallback: (_, val) => alternativeLoadCount[j] = (uint)val); 115 } 116 117 registers = new DoubleWordRegisterCollection(this, dict); 118 } 119 HandleLimitReached(int timerId)120 private void HandleLimitReached(int timerId) 121 { 122 lock(internalLock) 123 { 124 interruptStatus[timerId] = true; 125 if(!interruptMask[timerId]) 126 { 127 IRQ.Set(); 128 } 129 130 if(operationMode[timerId] == OperationMode.Timer) 131 { 132 if(runningMode[timerId] == RunningMode.Free) 133 { 134 // i'm not sure if it is correct as the documentation is quite confusing 135 timers[timerId].Limit = uint.MaxValue; 136 timers[timerId].ResetValue(); 137 } 138 } 139 else 140 { 141 var currentLimit = (uint)timers[timerId].Limit; 142 timers[timerId].Limit = alternativeLoadCount[timerId]; 143 timers[timerId].ResetValue(); 144 alternativeLoadCount[timerId] = currentLimit; 145 Connections[timerId].Toggle(); 146 } 147 } 148 } 149 HandleEndOfInterrupt(int? timerId = null)150 private void HandleEndOfInterrupt(int? timerId = null) 151 { 152 lock(internalLock) 153 { 154 if(timerId.HasValue) 155 { 156 interruptStatus[timerId.Value] = false; 157 if(interruptStatus.All(x => !x)) 158 { 159 IRQ.Unset(); 160 } 161 } 162 else 163 { 164 for(int i = 0; i < interruptStatus.Length; i++) 165 { 166 interruptStatus[i] = false; 167 } 168 IRQ.Unset(); 169 } 170 } 171 } 172 173 private DoubleWordRegisterCollection registers; 174 private LimitTimer[] timers; 175 private bool[] interruptStatus; 176 private bool[] interruptMask; 177 private uint[] alternativeLoadCount; 178 private OperationMode[] operationMode; 179 private RunningMode[] runningMode; 180 private readonly object internalLock; 181 182 private const int NumberOfInternalTimers = 4; 183 184 public enum OperationMode 185 { 186 PWM, 187 Timer 188 } 189 190 public enum RunningMode 191 { 192 Free, 193 UserDefinedCount 194 } 195 196 private enum Registers : long 197 { 198 Timer1LoadCount = 0x0, 199 Timer1CurrentValue = 0x4, 200 Timer1Control = 0x8, 201 Timer1EndOfInterrupt = 0xC, 202 Timer1InterruptStatus = 0x10, 203 Timer2LoadCount = 0x14, 204 Timer2CurrentValue = 0x18, 205 Timer2Control = 0x1C, 206 Timer2EndOfInterrupt = 0x20, 207 Timer2InterruptStatus = 0x24, 208 Timer3LoadCount = 0x28, 209 Timer3CurrentValue = 0x2C, 210 Timer3Control = 0x30, 211 Timer3EndOfInterrupt = 0x34, 212 Timer3InterruptStatus = 0x38, 213 Timer4LoadCount = 0x3C, 214 Timer4CurrentValue = 0x40, 215 Timer4Control = 0x44, 216 Timer4EndOfInterrupt = 0x48, 217 Timer4InterruptStatus = 0x4C, 218 TimersInterruptStatus = 0xA0, 219 TimersEndOfInterrupt = 0xA4, 220 TimersRawInterruptStatus = 0xA8, 221 Timer1LoadCount2 = 0xB0, 222 Timer2LoadCount2 = 0xB4, 223 Timer3LoadCount2 = 0xB8, 224 Timer4LoadCount2 = 0xBC, 225 } 226 } 227 } 228 229