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 System.Collections.ObjectModel; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class PULP_Timer : BasicDoubleWordPeripheral, IKnownSize, INumberedGPIOOutput 17 { PULP_Timer(IMachine machine, long frequency)18 public PULP_Timer(IMachine machine, long frequency) : base(machine) 19 { 20 interruptEnable = new IFlagRegisterField[NumberOfTimers]; 21 oneShot = new IFlagRegisterField[NumberOfTimers]; 22 cycleMode = new IFlagRegisterField[NumberOfTimers]; 23 24 var irqs = new Dictionary<int, IGPIO>(); 25 for(var i = 0; i < NumberOfTimers; i++) 26 { 27 irqs[i] = new GPIO(); 28 } 29 Connections = new ReadOnlyDictionary<int, IGPIO>(irqs); 30 31 timers = new ComparingTimer[NumberOfTimers]; 32 for(var i = 0; i < NumberOfTimers; i++) 33 { 34 var j = i; 35 timers[j] = new ComparingTimer(machine.ClockSource, frequency, this, $"Timer {j}", limit: uint.MaxValue, direction: Time.Direction.Ascending, workMode: Time.WorkMode.Periodic, 36 enabled: false, eventEnabled: true, compare: uint.MaxValue); 37 38 timers[j].CompareReached += delegate 39 { 40 this.Log(LogLevel.Noisy, "Timer {0} IRQ compare event", i); 41 if(interruptEnable[j].Value) 42 { 43 Connections[j].Blink(); //verified with RTL 44 } 45 if(oneShot[j].Value) 46 { 47 timers[j].Enabled = false; 48 } 49 if(cycleMode[j].Value) 50 { 51 timers[j].Value = 0; 52 } 53 }; 54 55 var shift = j * 4; 56 57 var configReg = ((Registers)(Registers.ConfigLow + shift)).Define(this) 58 .WithFlag(0, 59 writeCallback: (_, val) => 60 { 61 if(val) 62 { 63 timers[j].Enabled = val; 64 } 65 }, 66 valueProviderCallback: _ => timers[j].Enabled, 67 name: $"Timer {j} enable (EN)") 68 .WithFlag(1, 69 writeCallback: (_, val) => 70 { 71 if(val) 72 { 73 timers[j].Value = 0; 74 // this also should affect the prescaller 75 } 76 }, valueProviderCallback: _ => false, name: "RST") 77 .WithFlag(2, out interruptEnable[j], name: "IRQEN") 78 .WithTag("IEM", 3, 1) 79 .WithFlag(4, out cycleMode[j], name: "MODE") 80 .WithFlag(5, out oneShot[j], name: "ONE_S") 81 .WithTag("PEN", 6, 1) 82 .WithTag("CCFG", 7, 1) 83 .WithTag("PVAL", 8, 8) 84 .WithReservedBits(16, 15) 85 ; 86 if(j == 0) 87 { 88 // 64-bit timer mode not yet implemented 89 configReg.Tag("CASC", 31, 1); 90 } 91 else 92 { 93 configReg.Reserved(31, 1); 94 } 95 96 ((Registers)(Registers.CounterValueLow + shift)).Define(this) 97 .WithValueField(0, 32, 98 writeCallback: (_, val) => timers[j].Value = val, 99 valueProviderCallback: _ => 100 { 101 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 102 { 103 // being here means we are on the CPU thread 104 cpu.SyncTime(); 105 } 106 return (uint)timers[j].Value; 107 }, 108 name: $"Timer {j} counter value (CNT)") 109 ; 110 111 ((Registers)(Registers.ComparatorLow + shift)).Define(this) 112 .WithValueField(0, 32, 113 writeCallback: (_, val) => 114 { 115 this.Log(LogLevel.Debug, "Setting Timer {0} Compare to: {1:X}", j, val); 116 timers[j].Compare = val; 117 }, 118 valueProviderCallback: _ => 119 { 120 return (uint)timers[j].Compare; 121 }, 122 name: $"Timer {j} comparator value (CMP)") 123 ; 124 } 125 } 126 Reset()127 public override void Reset() 128 { 129 base.Reset(); 130 for(var i = 0; i < NumberOfTimers; i++) 131 { 132 timers[i].Reset(); 133 } 134 // no need to reset Connections, as they only blink 135 } 136 137 public long Size => 0x80; 138 139 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 140 141 private ComparingTimer[] timers; 142 private readonly IFlagRegisterField[] interruptEnable; 143 private readonly IFlagRegisterField[] oneShot; 144 private readonly IFlagRegisterField[] cycleMode; 145 146 private const int NumberOfTimers = 2; 147 148 private enum Registers : long 149 { 150 ConfigLow = 0x0, 151 ConfigHigh = 0x4, 152 CounterValueLow = 0x8, 153 CounterValueHigh = 0xC, 154 ComparatorLow = 0x10, 155 ComparatorHigh = 0x14, 156 StartCountingLow = 0x18, 157 StartCountingHigh = 0x1C, 158 ResetLow = 0x20, 159 ResetHigh = 0x24 160 } 161 } 162 } 163