/// <summary>
        /// Detects serial-connected compatible FPGA boards.
        /// </summary>
        /// <returns>The serial port name where the FPGA board is connected to.</returns>
        private async Task <IEnumerable <string> > GetFpgaPortNames(IHardwareExecutionContext executionContext)
        {
            // Get all available serial ports in the system.
            var ports = SerialPort.GetPortNames();

            // If no serial ports were detected, then we can't do anything else.
            if (ports.Length == 0)
            {
                throw new SerialPortCommunicationException(
                          "No serial port detected (no serial ports are open). Is an FPGA board connected and is it powered up?");
            }

            var fpgaPortNames          = new ConcurrentBag <string>();
            var serialPortPingingTasks = new Task[ports.Length];

            for (int i = 0; i < ports.Length; i++)
            {
                serialPortPingingTasks[i] = Task.Factory.StartNew(portNameObject =>
                {
                    using (var serialPort = CreateSerialPort(executionContext))
                    {
                        var taskCompletionSource = new TaskCompletionSource <bool>();

                        serialPort.DataReceived += (sender, e) =>
                        {
                            if (serialPort.ReadByte() == CommunicationConstants.Serial.Signals.Ready)
                            {
                                fpgaPortNames.Add(serialPort.PortName);
                                taskCompletionSource.SetResult(true);
                            }
                        };

                        serialPort.PortName = (string)portNameObject;

                        try
                        {
                            serialPort.Open();
                            serialPort.Write(CommandTypes.WhoIsAvailable);
                        }
                        catch (IOException) { }
                        catch (UnauthorizedAccessException) { }     // This happens if the port is used by another app.

                        // Waiting a maximum of 3s for a response from the port.
                        taskCompletionSource.Task.Wait(3000);
                    }
                }, ports[i]);
            }

            await Task.WhenAll(serialPortPingingTasks);

            if (!fpgaPortNames.Any())
            {
                throw new SerialPortCommunicationException(
                          "No compatible FPGA board connected to any serial port or a connected FPGA is not answering. Is and FPGA board connected and is it powered up? If yes, is the SDK software running on it?");
            }

            return(fpgaPortNames);
        }
Example #2
0
        public void ConfigureSerialPort(SerialPort serialPort, IHardwareExecutionContext hardwareExecutionContext)
        {
            if (hardwareExecutionContext.HardwareRepresentation.DeviceManifest.Name != Nexys4DdrManifestProvider.DeviceName)
            {
                return;
            }

            serialPort.BaudRate = 230400;
        }
        protected void SetHardwareExecutionTime(
            CommunicationStateContext context,
            IHardwareExecutionContext executionContext,
            ulong executionTimeClockCycles)
        {
            context.HardwareExecutionInformation.HardwareExecutionTimeMilliseconds =
                1M / executionContext.HardwareRepresentation.DeviceManifest.ClockFrequencyHz * 1000 * executionTimeClockCycles;

            Logger.Information("Hardware execution took " + context.HardwareExecutionInformation.HardwareExecutionTimeMilliseconds + "ms.");
        }
        private SerialPort CreateSerialPort(IHardwareExecutionContext executionContext)
        {
            var serialPort = new SerialPort();

            serialPort.BaudRate     = CommunicationConstants.Serial.DefaultBaudRate;
            serialPort.Parity       = CommunicationConstants.Serial.DefaultParity;
            serialPort.StopBits     = CommunicationConstants.Serial.DefaultStopBits;
            serialPort.WriteTimeout = CommunicationConstants.Serial.DefaultWriteTimeoutMilliseconds;

            _serialPortConfigurators.InvokePipelineSteps(step => step.ConfigureSerialPort(serialPort, executionContext));

            return(serialPort);
        }
Example #5
0
        private SerialPort CreateSerialPort(IHardwareExecutionContext executionContext)
        {
            var serialPort = new SerialPort
            {
                BaudRate     = Serial.DefaultBaudRate,
                Parity       = Serial.DefaultParity,
                StopBits     = Serial.DefaultStopBits,
                WriteTimeout = Serial.DefaultWriteTimeoutMilliseconds
            };

            _serialPortConfigurators.InvokePipelineSteps(step => step.ConfigureSerialPort(serialPort, executionContext));

            return(serialPort);
        }
        public override async Task <IHardwareExecutionInformation> Execute(SimpleMemory simpleMemory,
                                                                           int memberId,
                                                                           IHardwareExecutionContext executionContext)
        {
            _devicePoolPopulator.PopulateDevicePoolIfNew(async() =>
            {
                var portNames = await GetFpgaPortNames(executionContext);
                return(portNames.Select(portName => new Device {
                    Identifier = portName
                }));
            });

            using (var device = await _devicePoolManager.ReserveDevice())
            {
                var context = BeginExecution();

                // Initializing some serial port connection settings (may be different with some FPGA boards).
                // For detailed info on how the SerialPort class works see: https://social.msdn.microsoft.com/Forums/vstudio/en-US/e36193cd-a708-42b3-86b7-adff82b19e5e/how-does-serialport-handle-datareceived?forum=netfxbcl
                // Also we might consider this: http://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport

                using (var serialPort = CreateSerialPort(executionContext))
                {
                    serialPort.PortName = device.Identifier;

                    try
                    {
                        // We try to open the serial port.
                        serialPort.Open();
                    }
                    catch (IOException ex)
                    {
                        throw new SerialPortCommunicationException(
                                  "Communication with the FPGA board through the serial port failed. Probably the FPGA board is not connected.",
                                  ex);
                    }

                    if (serialPort.IsOpen)
                    {
                        Logger.Information("The port {0} is ours.", serialPort.PortName);
                    }
                    else
                    {
                        throw new SerialPortCommunicationException(
                                  "Communication with the FPGA board through the serial port failed. The " +
                                  serialPort.PortName + " exists but it's used by another process.");
                    }

                    // Here we put together the data stream.

                    // Execute Order 66.
                    var outputBuffer = new byte[] { (byte)CommandTypes.Execution }
                    // Copying the input length, represented as bytes, to the output buffer.
                    .Append(BitConverter.GetBytes(simpleMemory.Memory.Length))
                    // Copying the member ID, represented as bytes, to the output buffer.
                    .Append(BitConverter.GetBytes(memberId))
                    // Copying the simple memory.
                    .Append(simpleMemory.Memory);

                    // Sending the data.
                    // Just using serialPort.Write() once with all the data would stop sending data after 16372 bytes so
                    // we need to create batches. Since the FPGA receives data in the multiples of 4 bytes we use a batch
                    // of 4 bytes. This seems to have no negative impact on performance compared to using
                    // serialPort.Write() once.
                    var maxBytesToSendAtOnce = 4;
                    for (int i = 0; i < (int)Math.Ceiling(outputBuffer.Length / (decimal)maxBytesToSendAtOnce); i++)
                    {
                        var remainingBytes = outputBuffer.Length - i * maxBytesToSendAtOnce;
                        var bytesToSend    = remainingBytes > maxBytesToSendAtOnce ? maxBytesToSendAtOnce : remainingBytes;
                        serialPort.Write(outputBuffer, i * maxBytesToSendAtOnce, bytesToSend);
                    }


                    // Processing the response.
                    var taskCompletionSource       = new TaskCompletionSource <bool>();
                    var communicationState         = CommunicationConstants.Serial.CommunicationState.WaitForFirstResponse;
                    var outputByteCountBytes       = new byte[4];
                    var outputByteCountByteCounter = 0;
                    var outputByteCount            = 0;         // The incoming byte buffer size.
                    var outputBytesReceivedCount   = 0;         // Just used to know when the data is ready.
                    var outputBytes              = new byte[0]; // The incoming buffer.
                    var executionTimeBytes       = new byte[8];
                    var executionTimeByteCounter = 0;

                    Action <byte, bool> processReceivedByte = (receivedByte, isLastOfBatch) =>
                    {
                        switch (communicationState)
                        {
                        case CommunicationConstants.Serial.CommunicationState.WaitForFirstResponse:
                            if (receivedByte == CommunicationConstants.Serial.Signals.Ping)
                            {
                                communicationState = CommunicationConstants.Serial.CommunicationState.ReceivingExecutionInformation;
                                serialPort.Write(CommunicationConstants.Serial.Signals.Ready);
                            }
                            else
                            {
                                throw new SerialPortCommunicationException(
                                          "Awaited a ping signal from the FPGA after it finished but received the following byte instead: " +
                                          receivedByte);
                            }
                            break;

                        case CommunicationConstants.Serial.CommunicationState.ReceivingExecutionInformation:
                            executionTimeBytes[executionTimeByteCounter] = receivedByte;
                            executionTimeByteCounter++;
                            if (executionTimeByteCounter == 8)
                            {
                                var executionTimeClockCycles = BitConverter.ToUInt64(executionTimeBytes, 0);

                                SetHardwareExecutionTime(context, executionContext, executionTimeClockCycles);

                                communicationState = CommunicationConstants.Serial.CommunicationState.ReceivingOutputByteCount;
                                serialPort.Write(CommunicationConstants.Serial.Signals.Ready);
                            }
                            break;

                        case CommunicationConstants.Serial.CommunicationState.ReceivingOutputByteCount:
                            outputByteCountBytes[outputByteCountByteCounter] = receivedByte;
                            outputByteCountByteCounter++;

                            if (outputByteCountByteCounter == 4)
                            {
                                outputByteCount = BitConverter.ToInt32(outputByteCountBytes, 0);

                                // Since the output's size can differ from the input size for optimization reasons,
                                // we take the explicit size into account.
                                outputBytes = new byte[outputByteCount];

                                Logger.Information("Incoming data size in bytes: {0}", outputByteCount);

                                communicationState = CommunicationConstants.Serial.CommunicationState.ReceivingOuput;
                                serialPort.Write(CommunicationConstants.Serial.Signals.Ready);
                            }
                            break;

                        case CommunicationConstants.Serial.CommunicationState.ReceivingOuput:
                            outputBytes[outputBytesReceivedCount] = receivedByte;
                            outputBytesReceivedCount++;

                            if (outputByteCount == outputBytesReceivedCount)
                            {
                                simpleMemory.Memory = outputBytes;

                                // Serial communication can give more data than we actually await, so need to
                                // set this.
                                communicationState = CommunicationConstants.Serial.CommunicationState.Finished;
                                serialPort.Write(CommunicationConstants.Serial.Signals.Ready);

                                taskCompletionSource.SetResult(true);
                            }
                            break;

                        default:
                            break;
                        }
                    };

                    // In this event we are receiving the useful data coming from the FPGA board.
                    serialPort.DataReceived += (s, e) =>
                    {
                        if (e.EventType == SerialData.Chars)
                        {
                            var inputBuffer = new byte[serialPort.BytesToRead];
                            serialPort.Read(inputBuffer, 0, inputBuffer.Length);

                            for (int i = 0; i < inputBuffer.Length; i++)
                            {
                                processReceivedByte(inputBuffer[i], i == inputBuffer.Length - 1);

                                if (communicationState == CommunicationConstants.Serial.CommunicationState.Finished)
                                {
                                    return;
                                }
                            }
                        }
                    };

                    await taskCompletionSource.Task;

                    EndExecution(context);

                    return(context.HardwareExecutionInformation);
                }
            }
        }
 abstract public Task <IHardwareExecutionInformation> Execute(
     SimpleMemory simpleMemory,
     int memberId,
     IHardwareExecutionContext executionContext);
        public override async Task <IHardwareExecutionInformation> Execute(SimpleMemory simpleMemory,
                                                                           int memberId,
                                                                           IHardwareExecutionContext executionContext)
        {
            _devicePoolPopulator.PopulateDevicePoolIfNew(async() =>
            {
                // Get the IP addresses of the FPGA boards.
                var fpgaEndpoints = await _fpgaIpEndpointFinder.FindFpgaEndpoints();

                if (!fpgaEndpoints.Any())
                {
                    throw new EthernetCommunicationException("Couldn't find any FPGAs on the network.");
                }

                return(fpgaEndpoints.Select(endpoint =>
                                            new Device {
                    Identifier = endpoint.Endpoint.Address.ToString(), Metadata = endpoint
                }));
            });


            using (var device = await _devicePoolManager.ReserveDevice())
            {
                var context = BeginExecution();

                IFpgaEndpoint fpgaEndpoint   = device.Metadata;
                var           fpgaIpEndpoint = fpgaEndpoint.Endpoint;

                Logger.Information("IP endpoint to communicate with via Ethernet: {0}:{1}", fpgaIpEndpoint.Address, fpgaIpEndpoint.Port);

                try
                {
                    using (var client = new TcpClient())
                    {
                        // Initialize the connection.
                        if (!await client.ConnectAsync(fpgaIpEndpoint, TcpConnectionTimeout))
                        {
                            throw new EthernetCommunicationException("Couldn't connect to FPGA before the timeout exceeded.");
                        }

                        using (var stream = client.GetStream())
                        {
                            // We send an execution signal to make the FPGA ready to receive the data stream.
                            var executionCommandTypeByte = new byte[] { (byte)CommandTypes.Execution };
                            stream.Write(executionCommandTypeByte, 0, executionCommandTypeByte.Length);

                            var executionCommandTypeResponseByte = await GetBytesFromStream(stream, 1);

                            if (executionCommandTypeResponseByte[0] != CommunicationConstants.Ethernet.Signals.Ready)
                            {
                                throw new EthernetCommunicationException("Awaited a ready signal from the FPGA after the execution byte was sent but received the following byte instead: " + executionCommandTypeResponseByte[0]);
                            }

                            // Here we put together the data stream.
                            var lengthBytes   = BitConverter.GetBytes(simpleMemory.Memory.Length);
                            var memberIdBytes = BitConverter.GetBytes(memberId);

                            // Copying the input length, represented as bytes, to the output buffer.
                            var outputBuffer = BitConverter.GetBytes(simpleMemory.Memory.Length)
                                               // Copying the member ID, represented as bytes, to the output buffer.
                                               .Append(BitConverter.GetBytes(memberId))
                                               // Copying the simple memory.
                                               .Append(simpleMemory.Memory);

                            // Sending data to the FPGA board.
                            stream.Write(outputBuffer, 0, outputBuffer.Length);


                            // Read the first batch of the TcpServer response bytes that will represent the execution time.
                            var executionTimeBytes = await GetBytesFromStream(stream, 8);

                            var executionTimeClockCycles = BitConverter.ToUInt64(executionTimeBytes, 0);

                            SetHardwareExecutionTime(context, executionContext, executionTimeClockCycles);

                            // Read the bytes representing the length of the simple memory.
                            var outputByteCountBytes = await GetBytesFromStream(stream, 4);

                            var outputByteCount = BitConverter.ToUInt32(outputByteCountBytes, 0);

                            Logger.Information("Incoming data size in bytes: {0}", outputByteCount);

                            // Finally read the memory itself.
                            var outputBytes = await GetBytesFromStream(stream, (int)outputByteCount);

                            simpleMemory.Memory = outputBytes;
                        }
                    }
                }
                catch (SocketException ex)
                {
                    throw new EthernetCommunicationException("An unexpected error occurred during the Ethernet communication.", ex);
                }

                EndExecution(context);

                return(context.HardwareExecutionInformation);
            }
        }
Example #9
0
        public override async Task <IHardwareExecutionInformation> Execute(
            SimpleMemory simpleMemory,
            int memberId,
            IHardwareExecutionContext executionContext)
        {
            _devicePoolPopulator.PopulateDevicePoolIfNew(async() =>
            {
                // Because the FPGA_GetNumberEndpoints function is not implemented in the current driver (and it's not
                // included in the CatapultNativeLibrary interface because of that) it's not possible to know the
                // number of endpoints. Instead this algorithm probes the first 8 indices. The single device is
                // expected to be in endpoint 0 according to spec so this will get at least one result always.
                var libraries = await Task.WhenAll(Enumerable.Range(0, 7).Select(i => Task.Run(() =>
                {
                    try
                    {
                        var config = executionContext.ProxyGenerationConfiguration.CustomConfiguration;
                        return(CatapultLibrary.Create(config, Logger, i));
                    }
                    catch (CatapultFunctionResultException ex)
                    {
                        // The illegal endpoint number messages are normal for higher endpoints if they aren't
                        // populated, so it's OK to suppress them.
                        if (!(i > 0 && ex.Status == Status.IllegalEndpointNumber))
                        {
                            Logger.Error(ex, $"Received {ex.Status} while trying to instantiate CatapultLibrary on EndPoint {i}. This device won't be used.");
                        }
                        return(null);
                    }
                })));

                return(libraries
                       .Where(x => x != null)
                       .Select(x => new Device(x.InstanceName, x, Device_Disposing)));
            });

            using (var device = await _devicePoolManager.ReserveDevice())
            {
                var             context = BeginExecution();
                CatapultLibrary lib     = device.Metadata;
                lib.WaitClean();
                lib.TesterOutput = TesterOutput;
                var dma = new SimpleMemoryAccessor(simpleMemory);

                // Sending the data.
                //var task = lib.AssignJob(memberId, dma.Get());
                var task         = lib.AssignJob(memberId, HotfixInput(dma.Get()));
                var outputBuffer = await task;

                // Processing the response.
                var executionTimeClockCycles = MemoryMarshal.Read <ulong>(outputBuffer.Span);
                SetHardwareExecutionTime(context, executionContext, executionTimeClockCycles);

                var outputPayloadByteCount = SimpleMemory.MemoryCellSizeBytes * (int)MemoryMarshal.Read <uint>(
                    outputBuffer.Slice(OutputHeaderSizes.HardwareExecutionTime).Span);
                if (outputBuffer.Length > OutputHeaderSizes.Total + outputPayloadByteCount)
                {
                    outputBuffer = outputBuffer.Slice(0, OutputHeaderSizes.Total + outputPayloadByteCount);
                }

                if (outputPayloadByteCount > SimpleMemory.MemoryCellSizeBytes)
                {
                    outputBuffer = HotfixOutput(outputBuffer);
                }
                dma.Set(outputBuffer, Constants.OutputHeaderSizes.Total / SimpleMemory.MemoryCellSizeBytes);
                Logger.Information("Incoming data size in bytes: {0}", outputPayloadByteCount);

                EndExecution(context);

                return(context.HardwareExecutionInformation);
            }
        }