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