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 Antmicro.Renode.Core; 8 using Antmicro.Renode.Core.Extensions; 9 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Utilities; 12 13 namespace Antmicro.Renode.Peripherals.GPIOPort 14 { 15 public class SAMD21_GPIO : BaseGPIOPort, IBytePeripheral, IDoubleWordPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IKnownSize 16 { SAMD21_GPIO(IMachine machine)17 public SAMD21_GPIO(IMachine machine) : base(machine, NumberOfPins) 18 { 19 RegistersCollection = new ByteRegisterCollection(this); 20 configurationRegister = new DoubleWordRegister(this); 21 22 pinIsOutput = new bool[NumberOfPins]; 23 pinInputEnabled = new bool[NumberOfPins]; 24 25 DefineRegisters(); 26 } 27 ReadDoubleWord(long offset)28 public uint ReadDoubleWord(long offset) 29 { 30 if(offset != (long)Registers.WriteConfiguration) 31 { 32 return this.ReadDoubleWordUsingByte(offset); 33 } 34 // NOTE: We are using additional DoubleWordRegister for WriteConfiguration (0x28) 35 // to simplify implementation of transactions 36 return configurationRegister.Read(); 37 } 38 WriteDoubleWord(long offset, uint value)39 public void WriteDoubleWord(long offset, uint value) 40 { 41 if(offset != (long)Registers.WriteConfiguration) 42 { 43 this.WriteDoubleWordUsingByte(offset, value); 44 return; 45 } 46 // NOTE: We are using additional DoubleWordRegister for WriteConfiguration (0x28) 47 // to simplify implementation of transactions 48 configurationRegister.Write(0, value); 49 } 50 ReadByte(long offset)51 public byte ReadByte(long offset) 52 { 53 return RegistersCollection.Read(offset); 54 } 55 WriteByte(long offset, byte value)56 public void WriteByte(long offset, byte value) 57 { 58 RegistersCollection.Write(offset, value); 59 } 60 61 public ByteRegisterCollection RegistersCollection { get; } 62 63 public long Size => 0x80; 64 DefineRegisters()65 private void DefineRegisters() 66 { 67 Registers.Direction.DefineMany(this, 4, (register, index) => 68 { 69 register.WithFlags(0, 8, name: $"DIR[{(index+1)*8-1}:{index*8}]", 70 valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j], 71 writeCallback: (j, _, value) => pinIsOutput[index * 8 + j] = value); 72 }); 73 74 Registers.DirectionClear.DefineMany(this, 4, (register, index) => 75 { 76 register.WithFlags(0, 8, name: $"DIRCLR[{(index+1)*8-1}:{index*8}]", 77 valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j], 78 writeCallback: (j, _, value) => { if(value) pinIsOutput[index * 8 + j] = false; }); 79 }); 80 81 Registers.DirectionSet.DefineMany(this, 4, (register, index) => 82 { 83 register.WithFlags(0, 8, name: $"DIRSET[{(index+1)*8-1}:{index*8}]", 84 valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j], 85 writeCallback: (j, _, value) => { if(value) pinIsOutput[index * 8 + j] = true; }); 86 }); 87 88 Registers.DirectionToggle.DefineMany(this, 4, (register, index) => 89 { 90 register.WithFlags(0, 8, name: $"DIRTGL[{(index+1)*8-1}:{index*8}]", 91 valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j], 92 writeCallback: (j, _, value) => { if(value) pinIsOutput[index * 8 + j] ^= true; }); 93 }); 94 95 Registers.Output.DefineMany(this, 4, (register, index) => 96 { 97 register.WithFlags(0, 8, name: $"OUT[{(index+1)*8-1}:{index*8}]", 98 valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet, 99 writeCallback: (j, _, value) => Connections[index * 8 + j].Set(value)); 100 }); 101 102 Registers.OutputClear.DefineMany(this, 4, (register, index) => 103 { 104 register.WithFlags(0, 8, name: $"OUTCLR[{(index+1)*8-1}:{index*8}]", 105 valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet, 106 writeCallback: (j, _, value) => { if(value) Connections[index * 8 + j].Unset(); }); 107 }); 108 109 Registers.OutputSet.DefineMany(this, 4, (register, index) => 110 { 111 register.WithFlags(0, 8, name: $"OUTSET[{(index+1)*8-1}:{index*8}]", 112 valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet, 113 writeCallback: (j, _, value) => { if(value) Connections[index * 8 + j].Set(); }); 114 }); 115 116 Registers.OutputToggle.DefineMany(this, 4, (register, index) => 117 { 118 register.WithFlags(0, 8, name: $"OUTTGL[{(index+1)*8-1}:{index*8}]", 119 valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet, 120 writeCallback: (j, _, value) => { if(value) Connections[index * 8 + j].Toggle(); }); 121 }); 122 123 Registers.Input.DefineMany(this, 4, (register, index) => 124 { 125 register.WithFlags(0, 8, FieldMode.Read, name: $"IN[{(index+1)*8-1}:{index*8}]", 126 valueProviderCallback: (j, _) => pinInputEnabled[index * 8 + j] && State[index * 8 + j]); 127 }); 128 129 configurationRegister 130 .WithValueField(0, 16, out var pinMask, name: "PINMASK") 131 .WithTaggedFlag("PMUXEN", 16) 132 .WithFlag(17, out var pinInputBuffer, name: "INEN") 133 .WithTaggedFlag("PULLEN", 18) 134 .WithReservedBits(19, 3) 135 .WithTaggedFlag("DRVSTR", 22) 136 .WithReservedBits(23, 1) 137 .WithTag("PMUX", 24, 4) 138 .WithTaggedFlag("WRPMUX", 28) 139 .WithReservedBits(29, 1) 140 .WithFlag(30, out var writePinConfig, name: "WRPINCFG") 141 .WithFlag(31, out var halfWordSelect, name: "HWSEL") 142 .WithWriteCallback((_, __) => 143 { 144 if(!writePinConfig.Value) 145 { 146 return; 147 } 148 149 var mask = pinMask.Value << (halfWordSelect.Value ? 16 : 0); 150 BitHelper.ForeachActiveBit(mask, index => 151 { 152 pinInputEnabled[index] = pinInputBuffer.Value; 153 }); 154 }); 155 156 Registers.PinConfiguration.DefineMany(this, NumberOfPins, (register, index) => 157 { 158 register 159 .WithTaggedFlag("PMUXEN", 0) 160 .WithFlag(1, name: "INEN", 161 valueProviderCallback: _ => pinInputEnabled[index], 162 writeCallback: (_, value) => pinInputEnabled[index] = value) 163 .WithTaggedFlag("PULLEN", 2) 164 .WithReservedBits(3, 3) 165 .WithTaggedFlag("DRVSTR", 6) 166 .WithReservedBits(7, 1) 167 ; 168 }); 169 } 170 171 public const int NumberOfPins = 32; 172 173 private bool[] pinIsOutput; 174 private bool[] pinInputEnabled; 175 private readonly DoubleWordRegister configurationRegister; 176 177 private enum Registers 178 { 179 Direction = 0x00, 180 DirectionClear = 0x04, 181 DirectionSet = 0x08, 182 DirectionToggle = 0xC, 183 Output = 0x10, 184 OutputClear = 0x14, 185 OutputSet = 0x18, 186 OutputToggle = 0x1C, 187 Input = 0x20, 188 Control = 0x24, 189 WriteConfiguration = 0x28, 190 PeripheralMultiplexing = 0x2C, 191 PinConfiguration = 0x40, 192 } 193 } 194 } 195