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 Antmicro.Renode.Peripherals.Bus; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Logging; 12 using System.Collections.Generic; 13 using System.Collections.ObjectModel; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.IRQControllers 17 { 18 public class STM32F4_EXTI : BasicDoubleWordPeripheral, IKnownSize, IIRQController, INumberedGPIOOutput 19 { STM32F4_EXTI(IMachine machine, int numberOfOutputLines = 14, int firstDirectLine = DefaultFirstDirectLine)20 public STM32F4_EXTI(IMachine machine, int numberOfOutputLines = 14, int firstDirectLine = DefaultFirstDirectLine) : base(machine) 21 { 22 var innerConnections = new Dictionary<int, IGPIO>(); 23 for(var i = 0; i < numberOfOutputLines; ++i) 24 { 25 innerConnections[i] = new GPIO(); 26 } 27 Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections); 28 29 // All lines lower than firstDirectLine are configurable so they should be masked 30 // treatOutOfRangeLinesAsDirect is set to true to preserve backwards compatibility 31 core = new STM32_EXTICore(this, BitHelper.CalculateQuadWordMask(firstDirectLine, 0), treatOutOfRangeLinesAsDirect: true, allowMaskingDirectLines: false); 32 33 numberOfLinesMask = BitHelper.CalculateQuadWordMask((int)NumberOfLines, 0); 34 35 DefineRegisters(); 36 Reset(); 37 } 38 OnGPIO(int number, bool value)39 public void OnGPIO(int number, bool value) 40 { 41 if(number >= NumberOfLines) 42 { 43 this.Log(LogLevel.Error, "GPIO number {0} is out of range [0; {1})", number, NumberOfLines); 44 return; 45 } 46 var lineNumber = (byte)number; 47 48 if(core.CanSetInterruptValue(lineNumber, value, out var isLineConfigurable)) 49 { 50 // Configurable lines can only be set in this place. 51 value = isLineConfigurable ? true : value; 52 core.UpdatePendingValue(lineNumber, value); 53 Connections[number].Set(value); 54 } 55 } 56 Reset()57 public override void Reset() 58 { 59 base.Reset(); 60 softwareInterrupt = 0; 61 foreach(var gpio in Connections) 62 { 63 gpio.Value.Unset(); 64 } 65 } 66 67 public long Size => 0x400; 68 69 public IReadOnlyDictionary<int, IGPIO> Connections { get; } 70 71 public long NumberOfLines => Connections.Count; 72 DefineRegisters()73 private void DefineRegisters() 74 { 75 Registers.InterruptMask.Define(this) 76 .WithValueField(0, 32, out core.InterruptMask, name: "IMR"); 77 78 // Blank implementation to preserve backwards compatibility with the previous version of this model 79 Registers.EventMask.Define(this) 80 .WithValueField(0, 32, name: "EMR"); 81 82 Registers.RisingTriggerSelection.Define(this) 83 .WithValueField(0, 32, out core.RisingEdgeMask, name: "RTSR"); 84 85 Registers.FallingTriggerSelection.Define(this) 86 .WithValueField(0, 32, out core.FallingEdgeMask, name: "FTSR"); 87 88 Registers.SoftwareInterruptEvent.Define(this) 89 .WithValueField(0, 32, name: "SWIER", valueProviderCallback: _ => softwareInterrupt, 90 writeCallback: (_, value) => 91 { 92 value &= numberOfLinesMask; 93 BitHelper.ForeachActiveBit(value & core.InterruptMask.Value, x => Connections[x].Set()); 94 }); 95 96 Registers.PendingRegister.Define(this) 97 .WithValueField(0, 32, out core.PendingInterrupts, FieldMode.Read | FieldMode.WriteOneToClear, name: "PR", 98 writeCallback: (_, value) => 99 { 100 softwareInterrupt &= ~value; 101 value &= numberOfLinesMask; 102 BitHelper.ForeachActiveBit(value, x => Connections[x].Unset()); 103 }); 104 } 105 106 // We treat lines above 23 as direct by default for backwards compatibility with 107 // the old behavior of the EXTI model. 108 protected const int DefaultFirstDirectLine = 23; 109 110 private ulong softwareInterrupt; 111 112 private readonly ulong numberOfLinesMask; 113 private readonly STM32_EXTICore core; 114 115 private enum Registers 116 { 117 InterruptMask = 0x0, 118 EventMask = 0x4, 119 RisingTriggerSelection = 0x8, 120 FallingTriggerSelection = 0xC, 121 SoftwareInterruptEvent = 0x10, 122 PendingRegister = 0x14 123 } 124 } 125 } 126