1 // 2 // Copyright (c) 2010-2020 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.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Utilities; 14 using Antmicro.Renode.Logging; 15 16 namespace Antmicro.Renode.Peripherals.GPIOPort 17 { 18 // This model currently does not support interrupts 19 public class PULP_APB_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 20 { PULP_APB_GPIO(IMachine machine)21 public PULP_APB_GPIO(IMachine machine) : base(machine, NumberOfGPIOs) 22 { 23 var registersMap = new Dictionary<long, DoubleWordRegister> 24 { 25 {(long)Registers.PadInput_00_31, new DoubleWordRegister(this) 26 .WithFlags(0, 32, FieldMode.Read, name: "PADIN / Pad input value register", 27 valueProviderCallback: (idx, _) => ReadInputPin(idx) 28 ) 29 }, 30 {(long)Registers.PadInput_32_63, new DoubleWordRegister(this) 31 .WithFlags(0, 32, FieldMode.Read, name: "PADIN / Pad input value register", 32 valueProviderCallback: (idx, _) => ReadInputPin(idx + 32) 33 ) 34 }, 35 {(long)Registers.PadOutput_00_31, new DoubleWordRegister(this) 36 .WithFlags(0, 32, name: "PADOUT / Pad Output value register", 37 writeCallback: (idx, _, val) => WriteOutputPin(idx, val, false), 38 // Not logging on input pins, as it's not illegal to read this 39 valueProviderCallback: (idx, _) => gpioDirection[idx] == Direction.Out ? Connections[idx].IsSet : false 40 ) 41 }, 42 {(long)Registers.PadOutputSet_00_31, new DoubleWordRegister(this) 43 .WithFlags(0, 32, FieldMode.Write, name: "PADOUTSET / Pad Output set register", 44 writeCallback: (idx, _, val) => 45 { 46 if(val) 47 { 48 WriteOutputPin(idx, val, true); 49 } 50 } 51 ) 52 }, 53 {(long)Registers.PadOutputClear_00_31, new DoubleWordRegister(this) 54 .WithFlags(0, 32, FieldMode.Write, name: "PADOUTCLR / Pad Output clear register", 55 writeCallback: (idx, _, val) => 56 { 57 if(val) 58 { 59 WriteOutputPin(idx, !val, true); 60 } 61 } 62 ) 63 }, 64 {(long)Registers.PadOutput_32_63, new DoubleWordRegister(this) 65 .WithFlags(0, 32, name: "PADOUT / Pad Output value register", 66 writeCallback: (idx, _, val) => WriteOutputPin(idx + 32, val, false), 67 // Not logging on input pins, as it's not illegal to read this 68 valueProviderCallback: (idx, _) => gpioDirection[idx + 32] == Direction.Out ? Connections[idx + 32].IsSet : false 69 ) 70 }, 71 {(long)Registers.PadOutputSet_32_63, new DoubleWordRegister(this) 72 .WithFlags(0, 32, FieldMode.Write, name: "PADOUTSET / Pad Output set register", 73 writeCallback: (idx, _, val) => 74 { 75 if(val) 76 { 77 WriteOutputPin(idx + 32, val, true); 78 } 79 } 80 ) 81 }, 82 {(long)Registers.PadOutputClear_32_63, new DoubleWordRegister(this) 83 .WithFlags(0, 32, FieldMode.Write, name: "PADOUTCLR / Pad Output clear register", 84 writeCallback: (idx, _, val) => 85 { 86 if(val) 87 { 88 WriteOutputPin(idx + 32, !val, true); 89 } 90 } 91 ) 92 }, 93 {(long)Registers.GpioEnable_00_31, new DoubleWordRegister(this) 94 .WithFlags(0, 32, name: "GPIOEN / GPIO enable register", 95 writeCallback: (idx, _, val) => gpioClockEnabled[idx] = val, 96 valueProviderCallback: (idx, _) => gpioClockEnabled[idx] 97 ) 98 }, 99 {(long)Registers.GpioEnable_32_63, new DoubleWordRegister(this) 100 .WithFlags(0, 32, name: "GPIOEN / GPIO enable register", 101 writeCallback: (idx, _, val) => gpioClockEnabled[idx + 32] = val, 102 valueProviderCallback: (idx, _) => gpioClockEnabled[idx + 32] 103 ) 104 }, 105 {(long)Registers.PadDirection_00_31, new DoubleWordRegister(this) 106 .WithFlags(0, 32, name: "PADDIR / GPIO pad direction configuration register", 107 writeCallback: (idx, _, val) => gpioDirection[idx] = val ? Direction.Out : Direction.In, 108 valueProviderCallback: (idx, _) => gpioDirection[idx] == Direction.Out 109 ) 110 }, 111 {(long)Registers.PadDirection_32_63, new DoubleWordRegister(this) 112 .WithFlags(0, 32, name: "PADDIR / GPIO pad direction configuration register", 113 writeCallback: (idx, _, val) => gpioDirection[idx + 32] = val ? Direction.Out : Direction.In, 114 valueProviderCallback: (idx, _) => gpioDirection[idx + 32] == Direction.Out 115 ) 116 }, 117 }; 118 119 registers = new DoubleWordRegisterCollection(this, registersMap); 120 } 121 ReadDoubleWord(long offset)122 public uint ReadDoubleWord(long offset) 123 { 124 return registers.Read(offset); 125 } 126 WriteDoubleWord(long offset, uint value)127 public void WriteDoubleWord(long offset, uint value) 128 { 129 registers.Write(offset, value); 130 } 131 Reset()132 public override void Reset() 133 { 134 base.Reset(); 135 registers.Reset(); 136 for(var i = 0; i < NumberOfGPIOs; ++i) 137 { 138 gpioClockEnabled[i] = false; 139 gpioDirection[i] = Direction.In; 140 } 141 } 142 ReadInputPin(int pin)143 private bool ReadInputPin(int pin) 144 { 145 if(gpioDirection[pin] != Direction.In) 146 { 147 // Trying to read pin that is not configured as input 148 return false; 149 } 150 if(!gpioClockEnabled[pin]) 151 { 152 // According to docs, clock gating is effective when you gate 4 pins at once. We don't simulate this here 153 this.Log(LogLevel.Noisy, "Trying to read pin #{0} that has clock disabled", pin); 154 return false; 155 } 156 157 return State[pin]; 158 } 159 WriteOutputPin(int pin, bool val, bool isExplicit)160 private void WriteOutputPin(int pin, bool val, bool isExplicit) 161 { 162 if(gpioDirection[pin] == Direction.Out) 163 { 164 Connections[pin].Set(val); 165 } 166 else if(isExplicit) 167 { 168 // Log only if the driver specifically affets this pin and it's not properly configured 169 this.Log(LogLevel.Noisy, "Trying to write to pin #{pin} that is not configured as output"); 170 } 171 } 172 173 public long Size => 0x1000; 174 175 private const int NumberOfGPIOs = 64; 176 private bool[] gpioClockEnabled = new bool[NumberOfGPIOs]; 177 private Direction[] gpioDirection = new Direction[NumberOfGPIOs]; 178 179 private readonly DoubleWordRegisterCollection registers; 180 181 public enum Direction 182 { 183 In, 184 Out, 185 }; 186 187 private enum Registers 188 { 189 PadDirection_00_31 = 0x0, 190 GpioEnable_00_31 = 0x4, 191 PadInput_00_31 = 0x8, 192 PadOutput_00_31 = 0xC, 193 PadOutputSet_00_31 = 0x10, 194 PadOutputClear_00_31 = 0x14, 195 InterruptEnable_00_31 = 0x18, 196 InterruptType_00_15 = 0x1C, 197 InterruptType_16_31 = 0x20, 198 InterruptStatus_00_31 = 0x24, 199 PadConfiguration_00_07 = 0x28, 200 PadConfiguration_08_15 = 0x2C, 201 PadConfiguration_16_23 = 0x30, 202 PadConfiguration_24_31 = 0x34, 203 PadDirection_32_63 = 0x38, 204 GpioEnable_32_63 = 0x3c, 205 PadInput_32_63 = 0x40, 206 PadOutput_32_63 = 0x44, 207 PadOutputSet_32_63 = 0x48, 208 PadOutputClear_32_63 = 0x4c, 209 InterruptEnable_32_63 = 0x50, 210 InterruptType_32_47 = 0x54, 211 InterruptType_48_63 = 0x58, 212 InterruptStatus_32_63 = 0x5c, 213 PadConfiguration_32_39 = 0x60, 214 PadConfiguration_40_47 = 0x64, 215 PadConfiguration_48_55 = 0x68, 216 PadConfiguration_56_63 = 0x6c, 217 } 218 } 219 } 220 221