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; 8 using System.Collections.ObjectModel; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using System.Collections.Generic; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.IRQControllers 17 { 18 public class STM32H7_EXTI : BasicDoubleWordPeripheral, IKnownSize, IIRQController, INumberedGPIOOutput 19 { STM32H7_EXTI(IMachine machine)20 public STM32H7_EXTI(IMachine machine) : base(machine) 21 { 22 var innerConnections = new Dictionary<int, IGPIO>(); 23 for(var i = 0; i < CoreCount * LinesPerCore; i++) 24 { 25 innerConnections[i] = new GPIO(); 26 } 27 Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections); 28 29 cores = new STM32_EXTICore[CoreCount]; 30 for(var i = 0; i < CoreCount; i++) 31 { 32 cores[i] = new STM32_EXTICore(this, LineConfigurations[i]); 33 } 34 35 DefineRegisters(); 36 Reset(); 37 } 38 Reset()39 public override void Reset() 40 { 41 base.Reset(); 42 foreach(var connection in Connections) 43 { 44 connection.Value.Unset(); 45 } 46 } 47 OnGPIO(int number, bool value)48 public void OnGPIO(int number, bool value) 49 { 50 if(number >= NumberOfLines) 51 { 52 this.Log(LogLevel.Error, "GPIO number {0} is out of range [0; {1})", number, NumberOfLines); 53 return; 54 } 55 56 var numberInCore = (byte)(number % LinesPerCore); 57 var core = cores[number / LinesPerCore]; 58 59 if(core.CanSetInterruptValue(numberInCore, value, out var isLineConfigurable)) 60 { 61 if(isLineConfigurable) 62 { 63 // Configurable line can only be set from this place 64 value = true; 65 core.UpdatePendingValue(numberInCore, true); 66 } 67 Connections[number].Set(value); 68 } 69 } 70 71 public long Size => 0x400; 72 73 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 74 public long NumberOfLines => Connections.Count; 75 DefineRegisters()76 private void DefineRegisters() 77 { 78 // We can't use DefineMany here as each of the InterruptMaskX registers has a different reset value 79 for(var idx = 0; idx < CoreCount; idx++) 80 { 81 var core = cores[idx]; 82 83 RegistersCollection.DefineRegister((long)Registers.RisingTriggerSelection1 + idx * 0x20) 84 .WithValueField(0, 32, out core.RisingEdgeMask, name: "RTSR"); 85 86 RegistersCollection.DefineRegister((long)Registers.FallingTriggerSelection1 + idx * 0x20) 87 .WithValueField(0, 32, out core.FallingEdgeMask, name: "FTSR"); 88 89 RegistersCollection.DefineRegister((long)Registers.SoftwareInterruptEvent1 + idx * 0x20) 90 .WithValueField(0, 32, name: "SWIER", valueProviderCallback: _ => 0x0, 91 changeCallback: (_, value) => 92 { 93 BitHelper.ForeachActiveBit(value & core.InterruptMask.Value, bit => 94 { 95 var connectionNumber = LinesPerCore * idx + bit; 96 if(!Connections.TryGetValue(connectionNumber, out var irq)) 97 { 98 this.Log(LogLevel.Warning, "Cannot set software interrupt on line {0} as it does not exist", connectionNumber); 99 return; 100 } 101 core.UpdatePendingValue(bit, true); 102 irq.Set(); 103 }); 104 }); 105 106 // All configurable lines are by default masked. So the reset value is the inverse of if the line is configurable 107 RegistersCollection.DefineRegister((long)Registers.InterruptMask1 + idx * 0x10, ~LineConfigurations[idx]) 108 .WithValueField(0, 32, out core.InterruptMask, name: "CPUIMR"); 109 110 RegistersCollection.DefineRegister((long)Registers.Pending1 + idx * 0x10) 111 .WithValueField(0, 32, out core.PendingInterrupts, FieldMode.Read | FieldMode.WriteOneToClear, name: "CPUPR", 112 writeCallback: (_, value) => BitHelper.ForeachActiveBit(value, bit => Connections[bit].Unset())); 113 } 114 } 115 116 private readonly STM32_EXTICore[] cores; 117 118 // This is for the STM32H7 configuration 119 // Set bit means that this interrupt is configurable 120 private static readonly uint[] LineConfigurations = new uint[CoreCount] 121 { 122 0x3FFFFF, 123 0xA0000, 124 0x740000, 125 }; 126 127 private const int CoreCount = 3; 128 private const int LinesPerCore = 32; 129 130 private enum Registers 131 { 132 RisingTriggerSelection1 = 0x00, // EXTI_RTSR1 133 FallingTriggerSelection1 = 0x04, // EXTI_FTSR1 134 SoftwareInterruptEvent1 = 0x08, // EXTI_SWIER1 135 D3PendingMask1 = 0x0C, // EXTI_D3PMR1 136 D3PendingClearSelection1Low = 0x10, // EXTI_D3PCR1L 137 D3PendingClearSelection1High = 0x14, // EXTI_D3PCR1H 138 // Intentional gap 139 RisingTriggerSelection2 = 0x20, // EXTI_RTSR2 140 FallingTriggerSelection2 = 0x24, // EXTI_FTSR2 141 SoftwareInterruptEvent2 = 0x28, // EXTI_SWIER2 142 D3PendingMask2 = 0x2C, // EXTI_D3PMR2 143 D3PendingClearSelection2Low = 0x30, // EXTI_D3PCR2L 144 D3PendingClearSelection2High = 0x34, // EXTI_D3PCR2H 145 // Intentional gap 146 RisingTriggerSelection3 = 0x40, // EXTI_RTSR3 147 FallingTriggerSelection3 = 0x44, // EXTI_FTSR3 148 SoftwareInterruptEvent3 = 0x48, // EXTI_SWIER3 149 D3PendingMask3 = 0x4C, // EXTI_D3PMR3 150 D3PendingClearSelection3Low = 0x50, // EXTI_D3PCR3L 151 D3PendingClearSelection3High = 0x54, // EXTI_D3PCR3H 152 // Intentional gap 153 InterruptMask1 = 0x80, // EXTI_CPUIMR1 154 EventMask1 = 0x84, // EXTI_CPUEMR1 155 Pending1 = 0x88, // EXTI_CPUPR1 156 // Intentional gap 157 InterruptMask2 = 0x90, // EXTI_CPUIMR2 158 EventMask2 = 0x94, // EXTI_CPUEMR2 159 Pending2 = 0x98, // EXTI_CPUPR2 160 // Intentional gap 161 InterruptMask3 = 0xA0, // EXTI_CPUIMR3 162 EventMask3 = 0xA4, // EXTI_CPUEMR3 163 Pending3 = 0xA8, // EXTI_CPUPR3 164 } 165 } 166 } 167