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 8 using System.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.SPI 16 { 17 public class SPIMultiplexer : SimpleContainer<ISPIPeripheral>, IGPIOReceiver, ISPIPeripheral 18 { SPIMultiplexer(IMachine machine, bool suppressExplicitFinishTransmission = true)19 public SPIMultiplexer(IMachine machine, bool suppressExplicitFinishTransmission = true) : base(machine) 20 { 21 this.suppressExplicitFinishTransmission = suppressExplicitFinishTransmission; 22 chipSelects = new HashSet<int>(); 23 activeLowSignals = new HashSet<int>(); 24 inputState = new Dictionary<int, bool>(); 25 } 26 OnGPIO(int number, bool value)27 public void OnGPIO(int number, bool value) 28 { 29 this.Log(LogLevel.Noisy, "GPIO #{0} set to {1}", number, value); 30 inputState[number] = value; 31 UpdateChipSelectState(); 32 } 33 SetActiveLow(int number)34 public void SetActiveLow(int number) 35 { 36 activeLowSignals.Add(number); 37 UpdateChipSelectState(); 38 } 39 SetActiveHigh(int number)40 public void SetActiveHigh(int number) 41 { 42 activeLowSignals.Remove(number); 43 UpdateChipSelectState(); 44 } 45 Reset()46 public override void Reset() 47 { 48 inputState.Clear(); 49 } 50 Transmit(byte data)51 public byte Transmit(byte data) 52 { 53 if(chipSelects.Count == 0) 54 { 55 this.Log(LogLevel.Warning, "Tried to transmit byte 0x{0:X}, but no device is currently selected - ignoring transfer and returning a dummy byte", data); 56 return 0xFF; 57 } 58 59 if(chipSelects.Count > 1) 60 { 61 this.Log(LogLevel.Warning, "Tried to transmit byte 0x{0:X}, but multiple devices are currently selected ({1}) - ignoring transfer and returning a dummy byte", data, Misc.PrettyPrintCollectionHex(chipSelects)); 62 return 0xFF; 63 } 64 65 var deviceAddress = chipSelects.First(); 66 if(!TryGetByAddress(deviceAddress, out var device)) 67 { 68 this.Log(LogLevel.Warning, "Tried to transmit byte 0x{0:X} to device 0x{1:X}, but it's not connected - ignoring transfer and returning a dummy byte", data, deviceAddress); 69 return 0xFF; 70 } 71 72 return device.Transmit(data); 73 } 74 FinishTransmission()75 public void FinishTransmission() 76 { 77 if(suppressExplicitFinishTransmission) 78 { 79 return; 80 } 81 82 if(chipSelects.Count == 0) 83 { 84 this.Log(LogLevel.Warning, "Tried to finish transmission, but no device is currently selected"); 85 return; 86 } 87 88 if(chipSelects.Count > 1) 89 { 90 this.Log(LogLevel.Warning, "Tried to finish transmission, but multiple devices are currently selected ({0})", Misc.PrettyPrintCollectionHex(chipSelects)); 91 return; 92 } 93 94 var deviceAddress = chipSelects.First(); 95 FinishTransmissionByAddress(deviceAddress); 96 } 97 FinishTransmissionByAddress(int deviceAddress)98 private void FinishTransmissionByAddress(int deviceAddress) 99 { 100 if(!TryGetByAddress(deviceAddress, out var device)) 101 { 102 this.Log(LogLevel.Warning, "Tried to finish transmission to device 0x{0:X}, but it's not connected", deviceAddress); 103 return; 104 } 105 106 this.Log(LogLevel.Noisy, "Finisihing transmission on device 0x{0:X}", deviceAddress); 107 device.FinishTransmission(); 108 } 109 UpdateChipSelectState()110 private void UpdateChipSelectState() 111 { 112 foreach(var state in inputState) 113 { 114 var number = state.Key; 115 var value = state.Value; 116 117 var active = activeLowSignals.Contains(number) ? !value : value; 118 if(active) 119 { 120 chipSelects.Add(number); 121 } 122 else 123 { 124 chipSelects.Remove(number); 125 FinishTransmissionByAddress(number); 126 } 127 } 128 } 129 130 private readonly HashSet<int> chipSelects; 131 private readonly HashSet<int> activeLowSignals; 132 private readonly Dictionary<int, bool> inputState; 133 private readonly bool suppressExplicitFinishTransmission; 134 } 135 } 136 137