blob: 6926087a2bb3909a9377ef1ebb316b060137abd5 [file] [log] [blame]
//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System.Linq;
using Antmicro.Renode.Time;
using Antmicro.Renode.Core;
using Antmicro.Renode.Exceptions;
using Antmicro.Renode.Logging;
using System.Collections.Generic;
using Antmicro.Renode.Utilities;
using Antmicro.Renode.Peripherals;
namespace Antmicro.Renode.Connectors
{
public static class GPIOConnectorExtensions
{
public static void CreateGPIOConnector(this Emulation emulation, string name)
{
emulation.ExternalsManager.AddExternal(new GPIOConnector(), name);
}
}
public class GPIOConnector : IExternal, IConnectable<IPeripheral>, IGPIOReceiver
{
public GPIOConnector()
{
connectorPin = new GPIO();
}
public void AttachTo(IPeripheral peripheral)
{
if(peripherals.Count == 2)
{
throw new RecoverableException("Only two peripherals can be connected to this GPIO Connector (source and destination).");
}
peripherals.Add(peripheral);
}
public void OnGPIO(int number, bool value)
{
if(!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts))
{
vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld);
}
var endpoints = connectorPin.Endpoints;
for(var i = 0; i < endpoints.Count; i++)
{
endpoints[i].Receiver.GetMachine().HandleTimeDomainEvent(connectorPin.Set, value, vts);
}
}
//This method should not be executed on a runnning emulation, as IGPIO.Connect call
//may lead to nondeterminism.
public void SelectSourcePin(INumberedGPIOOutput source, int pinNumber)
{
VerifyPeripheralOrThrow(source);
if(!source.Connections.TryGetValue(pinNumber, out IGPIO tempPin))
{
throw new RecoverableException("Peripheral {0} has no GPIO with number: {1}".FormatWith(source, pinNumber));
}
if(sourcePin != null)
{
sourcePin.Disconnect();
}
sourcePin = tempPin ?? throw new RecoverableException("Source PIN cannot be selected.");
sourcePin.Connect(this, 0);
}
//This method should not be executed on a runnning emulation, as IGPIO.Connect call
//may lead to nondeterminism.
public void SelectDestinationPin(IGPIOReceiver receiver, int pinNumber)
{
VerifyPeripheralOrThrow(receiver);
if(connectorPin.IsConnected)
{
this.Log(LogLevel.Warning, "Overwriting destination PIN connection.");
destinationMachine.MachineReset -= ResetDestinationPinState;
}
GetDestinationMachineAndAttachToEvent(receiver);
connectorPin.Connect(receiver, pinNumber);
}
public void DetachFrom(IPeripheral peripheral)
{
if(connectorPin.IsConnected && connectorPin.Endpoints.Any(x => x.Receiver == peripheral))
{
connectorPin.Disconnect(connectorPin.Endpoints.First(x => x.Receiver == peripheral));
destinationMachine.MachineReset -= ResetDestinationPinState;
}
else
{
if(sourcePin != null)
{
sourcePin.Disconnect();
}
}
peripherals.Remove(peripheral);
}
public void Reset()
{
if(sourcePin == null)
{
connectorPin.Set(false);
}
else
{
connectorPin.Set(sourcePin.IsSet);
}
}
private void VerifyPeripheralOrThrow(IPeripheral peripheral)
{
var attachedPeripheral = peripherals.FirstOrDefault(p => p == peripheral);
if(attachedPeripheral == null)
{
throw new RecoverableException("Peripheral {0} is not connected to the GPIO Connector.".FormatWith(peripheral));
}
}
private void GetDestinationMachineAndAttachToEvent(IPeripheral receiver)
{
if(!EmulationManager.Instance.CurrentEmulation.TryGetMachineForPeripheral(receiver, out destinationMachine))
{
throw new RecoverableException("Could not resolve machine for designated peripheral.");
}
destinationMachine.MachineReset += ResetDestinationPinState;
}
private void ResetDestinationPinState(IMachine machine)
{
Reset();
}
private readonly IGPIO connectorPin;
private IMachine destinationMachine;
private IGPIO sourcePin;
private ISet<IPeripheral> peripherals = new HashSet<IPeripheral>();
}
}