summaryrefslogtreecommitdiff
path: root/python/openvino/runtime/coredla_device/stream_controller
diff options
context:
space:
mode:
Diffstat (limited to 'python/openvino/runtime/coredla_device/stream_controller')
-rw-r--r--python/openvino/runtime/coredla_device/stream_controller/app/dla_registers.h45
-rw-r--r--python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.c80
-rw-r--r--python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.h22
-rw-r--r--python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.c426
-rw-r--r--python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.h86
-rw-r--r--python/openvino/runtime/coredla_device/stream_controller/app/stream_controller_messages.h90
-rwxr-xr-xpython/openvino/runtime/coredla_device/stream_controller/build.sh54
7 files changed, 803 insertions, 0 deletions
diff --git a/python/openvino/runtime/coredla_device/stream_controller/app/dla_registers.h b/python/openvino/runtime/coredla_device/stream_controller/app/dla_registers.h
new file mode 100644
index 0000000..d77c5ab
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/app/dla_registers.h
@@ -0,0 +1,45 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+//the numbers below are byte addresses, must be a multiple of 4 since each access is 32 bits
+static const uint32_t DLA_DMA_CSR_OFFSET_INTERRUPT_CONTROL = 512; //0x200
+static const uint32_t DLA_DMA_CSR_OFFSET_INTERRUPT_MASK = 516;
+static const uint32_t DLA_DMA_CSR_OFFSET_CONFIG_BASE_ADDR = 528; //0x210
+static const uint32_t DLA_DMA_CSR_OFFSET_CONFIG_RANGE_MINUS_TWO = 532;
+static const uint32_t DLA_DMA_CSR_OFFSET_INPUT_OUTPUT_BASE_ADDR = 536;
+static const uint32_t DLA_DMA_CSR_OFFSET_DESC_DIAGNOSTICS = 540;
+static const uint32_t DLA_DMA_CSR_OFFSET_INTERMEDIATE_BASE_ADDR = 544; //0x220
+static const uint32_t DLA_DMA_CSR_OFFSET_COMPLETION_COUNT = 548;
+static const uint32_t DLA_DMA_CSR_OFFSET_CLOCKS_ACTIVE_LO = 576; //0x240
+static const uint32_t DLA_DMA_CSR_OFFSET_CLOCKS_ACTIVE_HI = 580;
+static const uint32_t DLA_DMA_CSR_OFFSET_CLOCKS_ALL_JOBS_LO = 584;
+static const uint32_t DLA_DMA_CSR_OFFSET_CLOCKS_ALL_JOBS_HI = 588;
+static const uint32_t DLA_DMA_CSR_OFFSET_DEBUG_NETWORK_ADDR = 592; //0x250
+static const uint32_t DLA_DMA_CSR_OFFSET_DEBUG_NETWORK_VALID = 596;
+static const uint32_t DLA_DMA_CSR_OFFSET_DEBUG_NETWORK_DATA = 600;
+
+//bit positions in interrupt control and mask
+static const uint32_t DLA_DMA_CSR_INTERRUPT_ERROR_BIT = 0;
+static const uint32_t DLA_DMA_CSR_INTERRUPT_DONE_BIT = 1;
+
+//bit positions in descriptor diagnostic
+static const uint32_t DLA_DMA_CSR_DESC_DIAGNOSTICS_OVERFLOW_BIT = 0;
+static const uint32_t DLA_DMA_CSR_DESC_DIAGNOSTICS_ALMOST_FULL_BIT = 1;
+static const uint32_t DLA_DMA_CSR_DESC_DIAGNOSTICS_OUT_OF_INFERENCES_BIT = 2;
+
+//descriptor queue
+//runtime knows how many jobs it has enqueued and how many jobs have finished
+//runtime is responsible for not overflowing the descriptor queue, it must limit the number of outstanding jobs queued in hardware
+static const uint32_t DLA_DMA_CSR_DESCRIPTOR_QUEUE_LOGICAL_SIZE = 64; //max number of jobs that runtime can enqueue
+static const uint32_t DLA_DMA_CSR_DESCRIPTOR_QUEUE_WORDS_PER_JOB = 8; //how many words in the queue are needed to enqueue 1 job
+static const uint32_t DLA_DMA_CSR_DESCRIPTOR_QUEUE_PHYSICAL_SIZE = DLA_DMA_CSR_DESCRIPTOR_QUEUE_LOGICAL_SIZE * DLA_DMA_CSR_DESCRIPTOR_QUEUE_WORDS_PER_JOB; //number of words in the hardware queue
diff --git a/python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.c b/python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.c
new file mode 100644
index 0000000..1a12def
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.c
@@ -0,0 +1,80 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "message_handlers.h"
+#include "stream_controller_messages.h"
+
+bool InitializeStreamControllerMessageHandler(StreamController* this, volatile uint32_t* pPayload)
+{
+ InitializeStreamControllerPayload* pInitializePayload = (InitializeStreamControllerPayload*)pPayload;
+ this->InitializeStreamController(this,
+ pInitializePayload->_sourceBufferSize,
+ pInitializePayload->_dropSourceBuffers,
+ pInitializePayload->_numInferenceRequests);
+ this->SendMessage(this, MessageType_NoOperation, NULL, 0);
+ return true;
+}
+
+bool ScheduleItemMessageHandler(StreamController* this, volatile uint32_t* pPayload)
+{
+ volatile CoreDlaJobPayload* pCoreDlaJobPayload = (volatile CoreDlaJobPayload*)pPayload;
+ this->NewInferenceRequestReceived(this, pCoreDlaJobPayload);
+ this->SendMessage(this, MessageType_NoOperation, NULL, 0);
+ return true;
+}
+
+bool PingMessageHandler(StreamController* this, volatile uint32_t* pPayload)
+{
+ this->SendMessage(this, MessageType_Pong, NULL, 0);
+ return true;
+}
+
+bool GetStatusMessageHandler(StreamController* this, volatile uint32_t* pPayload)
+{
+ StatusMessagePayload statusMessagePayload;
+ statusMessagePayload._status = this->_status;
+ statusMessagePayload._statusLineNumber = this->_statusLineNumber;
+ statusMessagePayload._numReceivedSourceBuffers = this->_numReceivedSourceBuffers;
+ statusMessagePayload._numScheduledInferences = this->_numScheduledInferences;
+ statusMessagePayload._numExecutedJobs = this->_numExecutedJobs;
+ this->SendMessage(this, MessageType_Status, &statusMessagePayload, sizeof(statusMessagePayload));
+ return true;
+}
+
+bool ManualArmDmaTransferMessageHandler(StreamController* this, volatile uint32_t* pPayload)
+{
+ ManualArmDmaTransferPayload* pManualArmDmaTransferPayload = (ManualArmDmaTransferPayload*)pPayload;
+ CoreDlaJobItem emptyJob = {};
+ this->_debugJob = emptyJob;
+ this->_debugJob._payload._inputAddressDDR = pManualArmDmaTransferPayload->_inputAddressDDR;
+ this->_sourceBufferSize = pManualArmDmaTransferPayload->_sourceBufferSize;
+ bool fromHPS = (pManualArmDmaTransferPayload->_fromHPS != 0);
+ this->ArmDmaTransfer(this, &this->_debugJob, fromHPS);
+ this->SendMessage(this, MessageType_NoOperation, NULL, 0);
+ return true;
+}
+
+bool ManualScheduleDlaInferenceMessageHandler(StreamController* this, volatile uint32_t* pPayload)
+{
+ ManualScheduleDlaInferencePayload* pManualScheduleDlaInferencePayload = (ManualScheduleDlaInferencePayload*)pPayload;
+ CoreDlaJobItem emptyJob = {};
+ this->_debugJob = emptyJob;
+ this->_debugJob._payload._configurationBaseAddressDDR = pManualScheduleDlaInferencePayload->_configurationBaseAddressDDR;
+ this->_debugJob._payload._configurationSize = pManualScheduleDlaInferencePayload->_configurationSize;
+ this->_debugJob._payload._inputAddressDDR = pManualScheduleDlaInferencePayload->_inputAddressDDR;
+ this->ScheduleDlaInference(this, &this->_debugJob);
+ this->SendMessage(this, MessageType_NoOperation, NULL, 0);
+ return true;
+}
+
+
diff --git a/python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.h b/python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.h
new file mode 100644
index 0000000..a7e5187
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/app/message_handlers.h
@@ -0,0 +1,22 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include "stream_controller.h"
+
+extern bool InitializeStreamControllerMessageHandler(StreamController* this, volatile uint32_t* pPayload);
+extern bool ScheduleItemMessageHandler(StreamController* this, volatile uint32_t* pPayload);
+extern bool PingMessageHandler(StreamController* this, volatile uint32_t* pPayload);
+extern bool GetStatusMessageHandler(StreamController* this, volatile uint32_t* pPayload);
+extern bool ManualArmDmaTransferMessageHandler(StreamController* this, volatile uint32_t* pPayload);
+extern bool ManualScheduleDlaInferenceMessageHandler(StreamController* this, volatile uint32_t* pPayload);
diff --git a/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.c b/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.c
new file mode 100644
index 0000000..ad8b372
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.c
@@ -0,0 +1,426 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "stream_controller.h"
+#include "message_handlers.h"
+#include "sys/alt_cache.h"
+#include "dla_registers.h"
+#include <string.h>
+
+static const uint32_t messageReadyMagicNumber = 0x55225522;
+static const uint32_t mailboxBaseAddress = 0x40000;
+static const uint32_t mailboxSize = 0x1000;
+static const uint32_t dlaBaseAddress = 0x30000;
+
+static void Start(StreamController* this);
+static void Reset(StreamController* this);
+static bool InitializeMsgDma(StreamController* this);
+static bool ArmDmaTransfer(StreamController* this, CoreDlaJobItem* pFillJob, bool fromHPS);
+static void RunEventLoop(StreamController* this);
+static void WriteToDlaCsr(StreamController* this, uint32_t addr, uint32_t data);
+static void InitializeStreamController(StreamController* this, uint32_t sourceBufferSize, uint32_t dropSourceBuffers, uint32_t numInferenceRequests);
+static void SetStatus(StreamController* this, NiosStatusType statusType, uint32_t lineNumber);
+static MessageType ReceiveMessage(StreamController* this, volatile MessageHeader* pReceiveMessage);
+static bool SendMessage(StreamController* this,
+ MessageType messageType,
+ void* pPayload,
+ size_t payloadSize);
+static void NewSourceBuffer(StreamController* this);
+static void ScheduleDlaInference(StreamController* this, CoreDlaJobItem* pJob);
+static void NewInferenceRequestReceived(StreamController* this, volatile CoreDlaJobPayload* pJobPayload);
+static void MsgDmaIsr(void* pContext);
+
+int main()
+{
+ StreamController streamController = {};
+ StreamController* this = &streamController;
+
+ this->Start = Start;
+ this->Reset = Reset;
+ this->InitializeMsgDma = InitializeMsgDma;
+ this->ArmDmaTransfer = ArmDmaTransfer;
+ this->RunEventLoop = RunEventLoop;
+ this->WriteToDlaCsr = WriteToDlaCsr;
+ this->InitializeStreamController = InitializeStreamController;
+ this->SetStatus = SetStatus;
+ this->ReceiveMessage = ReceiveMessage;
+ this->SendMessage = SendMessage;
+ this->NewSourceBuffer = NewSourceBuffer;
+ this->ScheduleDlaInference = ScheduleDlaInference;
+ this->NewInferenceRequestReceived = NewInferenceRequestReceived;
+
+ // Message handlers
+ this->GetStatusMessageHandler = GetStatusMessageHandler;
+ this->ScheduleItemMessageHandler = ScheduleItemMessageHandler;
+ this->PingMessageHandler = PingMessageHandler;
+ this->InitializeStreamControllerMessageHandler = InitializeStreamControllerMessageHandler;
+ this->ManualArmDmaTransferMessageHandler = ManualArmDmaTransferMessageHandler;
+ this->ManualScheduleDlaInferenceMessageHandler = ManualScheduleDlaInferenceMessageHandler;
+
+ this->Reset(this);
+ this->Start(this);
+
+ return 0;
+}
+
+static void Start(StreamController* this)
+{
+ // Clear the mailbox memory
+ uint8_t* pMailbox = (uint8_t*)(mailboxBaseAddress);
+ memset(pMailbox, 0, mailboxSize);
+
+ if (this->InitializeMsgDma(this))
+ {
+ // Run the main event loop
+ this->RunEventLoop(this);
+ }
+}
+
+static bool InitializeMsgDma(StreamController* this)
+{
+ this->_pMsgDevice = alt_msgdma_open(DLA_MSGDMA_0_CSR_NAME);
+ if (this->_pMsgDevice)
+ {
+ alt_msgdma_register_callback(this->_pMsgDevice, MsgDmaIsr, 0, this);
+ alt_dcache_flush_all();
+ return true;
+ }
+ else
+ {
+ this->SetStatus(this, NiosStatusType_MsgDmaFailed, __LINE__);
+ return false;
+ }
+}
+
+static bool ArmDmaTransfer(StreamController* this, CoreDlaJobItem* pFillJob, bool fromHPS)
+{
+ this->_pFillingImageJob = pFillJob;
+
+ alt_u32* pWriteBuffer = (alt_u32*)this->_pFillingImageJob->_payload._inputAddressDDR;
+ alt_u32 length = this->_sourceBufferSize;
+ alt_u32 control = ALTERA_MSGDMA_DESCRIPTOR_CONTROL_TRANSFER_COMPLETE_IRQ_MASK;
+
+ int r = 0;
+ if (fromHPS)
+ {
+ r = alt_msgdma_construct_extended_st_to_mm_descriptor(this->_pMsgDevice,
+ &this->_msgdmaDescriptor,
+ pWriteBuffer,
+ length,
+ control,
+ 0,
+ 0,
+ 1);
+ }
+ else
+ {
+ r = alt_msgdma_construct_extended_mm_to_st_descriptor(this->_pMsgDevice,
+ &this->_msgdmaDescriptor,
+ pWriteBuffer,
+ length,
+ control,
+ 0,
+ 0,
+ 1);
+ }
+
+ if (r == 0)
+ {
+ r = alt_msgdma_extended_descriptor_async_transfer(this->_pMsgDevice, &this->_msgdmaDescriptor);
+ if (r != 0)
+ {
+ this->SetStatus(this, NiosStatusType_AsyncTransferFailed, __LINE__);
+ }
+ }
+ else
+ {
+ this->SetStatus(this, NiosStatusType_BadDescriptor, __LINE__);
+ }
+
+ return (r == 0);
+}
+
+static void RunEventLoop(StreamController* this)
+{
+ volatile MessageHeader* pReceiveMessage = (MessageHeader*)(mailboxBaseAddress);
+
+ uint32_t previousIsrCount = this->_isrCount;
+
+ while (true)
+ {
+ uint32_t isrCount = this->_isrCount;
+
+ if (isrCount != previousIsrCount)
+ {
+ this->NewSourceBuffer(this);
+ }
+
+ if (pReceiveMessage->_messageReadyMagicNumber == messageReadyMagicNumber)
+ {
+ this->ReceiveMessage(this, pReceiveMessage);
+ }
+
+ previousIsrCount = isrCount;
+ }
+}
+
+static MessageType ReceiveMessage(StreamController* this, volatile MessageHeader* pReceiveMessage)
+{
+ MessageType messageType = pReceiveMessage->_messageType;
+ uint32_t sequenceId = pReceiveMessage->_sequenceID;
+ this->_commandCounter++;
+
+ bool ok = false;
+
+ volatile uint32_t* pPayload = &pReceiveMessage->_payload;
+
+ if (messageType == MessageType_GetStatus)
+ ok = this->GetStatusMessageHandler(this, pPayload);
+ else if (messageType == MessageType_ScheduleItem)
+ ok = this->ScheduleItemMessageHandler(this, pPayload);
+ else if (messageType == MessageType_Ping)
+ ok = this->PingMessageHandler(this, pPayload);
+ else if (messageType == MessageType_InitializeStreamController)
+ ok = this->InitializeStreamControllerMessageHandler(this, pPayload);
+ else if (messageType == MessageType_ManualArmDmaTransfer)
+ ok = this->ManualArmDmaTransferMessageHandler(this, pPayload);
+ else if (messageType == MessageType_ManualScheduleDlaInference)
+ ok = this->ManualScheduleDlaInferenceMessageHandler(this, pPayload);
+
+ if (!ok)
+ this->SetStatus(this, NiosStatusType_BadMessage, __LINE__);
+
+ pReceiveMessage->_messageReadyMagicNumber = sequenceId;
+
+ if ((this->_lastReceiveSequenceID != 0) && ((this->_lastReceiveSequenceID + 1) != sequenceId))
+ {
+ // If the DLA plugin has restarted, the first message will be InitializeStreamController
+ // with a sequence ID of 0
+ if ((sequenceId != 0) || (messageType != MessageType_InitializeStreamController))
+ this->SetStatus(this, NiosStatusType_BadMessageSequence, __LINE__);
+ }
+
+ this->_lastReceiveSequenceID = sequenceId;
+ return messageType;
+}
+
+static bool SendMessage(StreamController* this,
+ MessageType messageType,
+ void *pPayload,
+ size_t payloadSize)
+{
+ uint32_t mailboxSendAddress = mailboxBaseAddress + (mailboxSize / 2);
+ uint32_t* pMailbox = (uint32_t*)mailboxSendAddress;
+ MessageHeader* pSendMessage = (MessageHeader*)(pMailbox);
+ void* pPayloadDestination = &pSendMessage->_payload;
+
+ pSendMessage->_messageType = messageType;
+ pSendMessage->_sequenceID = this->_sendSequenceID;
+
+ if (payloadSize > 0)
+ memcpy(pPayloadDestination, pPayload, payloadSize);
+
+ // Signal the message as ready
+ pSendMessage->_messageReadyMagicNumber = messageReadyMagicNumber;
+
+ this->_sendSequenceID++;
+ return true;
+}
+
+// We have received a new source buffer via the msgdma
+static void NewSourceBuffer(StreamController* this)
+{
+ // Read the response to flush the buffer
+ CoreDlaJobItem* pJustFilledJob = this->_pFillingImageJob;
+ CoreDlaJobItem* pNextFillJob = NULL;
+
+ uint32_t bufferSequence = this->_numReceivedSourceBuffers;
+ this->_numReceivedSourceBuffers++;
+
+ // Have we just captured a manually armed DMA transfer?
+ if (pJustFilledJob == &this->_debugJob)
+ return;
+
+ if (this->_dropSourceBuffers > 0)
+ {
+ // If _dropSourceBuffers = 1, we process 1, drop 1 etc
+ // if _dropSourceBuffers = 2, we process 1, drop 2, process 1, drop 2 etc
+ if (bufferSequence % (this->_dropSourceBuffers + 1) != 0)
+ {
+ // Drop this buffer, capture the next one in its place
+ this->ArmDmaTransfer(this, pJustFilledJob, true);
+ return;
+ }
+ }
+
+ pJustFilledJob->_hasSourceBuffer = true;
+
+ if (pJustFilledJob->_pNextJob->_hasSourceBuffer)
+ {
+ // No space in the next job, so keep filling the same job
+ pNextFillJob = pJustFilledJob;
+
+ // It already has a buffer but we have to
+ // consider this as dropped as we will write another
+ // in its place
+ pNextFillJob->_hasSourceBuffer = false;
+ }
+ else
+ {
+ pNextFillJob = pJustFilledJob->_pNextJob;
+ }
+
+ // Re-arm the DMA transfer
+ this->ArmDmaTransfer(this, pNextFillJob, true);
+
+ // If there are less than two scheduled buffers, then we can schedule another one
+ // _pNextInferenceRequestJob is the executing job if it is marked as scheduled
+
+ uint32_t nScheduled = 0;
+ if (this->_pNextInferenceRequestJob->_scheduledWithDLA)
+ nScheduled++;
+ if (this->_pNextInferenceRequestJob->_pNextJob->_scheduledWithDLA)
+ nScheduled++;
+
+ if (nScheduled < 2)
+ this->ScheduleDlaInference(this, pJustFilledJob);
+}
+
+static void NewInferenceRequestReceived(StreamController* this, volatile CoreDlaJobPayload* pJobPayload)
+{
+ // Once we have received all '_totalNumInferenceRequests' inference requests,
+ // we set the state to running and can now capture the input dma's
+ bool wasRunning = this->_running;
+ this->_numInferenceRequests++;
+ this->_running = (this->_numInferenceRequests >= this->_totalNumInferenceRequests);
+
+ CoreDlaJobItem* pThisJob = this->_pNextInferenceRequestJob;
+
+ // Store the job details and move to the next
+ uint32_t previousAddress = pThisJob->_payload._inputAddressDDR;
+ pThisJob->_payload = *pJobPayload;
+
+ // This job has just completed so clear its state
+ pThisJob->_scheduledWithDLA = false;
+ pThisJob->_hasSourceBuffer = false;
+
+ // The jobs are recycled by the DLA plugin so the inputAddrDDR should
+ // stay the same for each _jobs[n]
+ if ((pThisJob->_payload._inputAddressDDR != previousAddress) && (previousAddress != 0))
+ this->SetStatus(this, NiosStatusType_Error, __LINE__);
+
+ this->_pNextInferenceRequestJob = this->_pNextInferenceRequestJob->_pNextJob;
+
+ if (wasRunning)
+ {
+ this->_numExecutedJobs++;
+
+ // Check if we have any jobs ready to be scheduled. Maximum of 2 can have _scheduledWithDLA set
+ if (!this->_pNextInferenceRequestJob->_scheduledWithDLA && this->_pNextInferenceRequestJob->_hasSourceBuffer)
+ {
+ this->ScheduleDlaInference(this, this->_pNextInferenceRequestJob);
+ }
+ else if (!this->_pNextInferenceRequestJob->_pNextJob->_scheduledWithDLA && this->_pNextInferenceRequestJob->_pNextJob->_hasSourceBuffer)
+ {
+ this->ScheduleDlaInference(this, this->_pNextInferenceRequestJob->_pNextJob);
+ }
+ }
+ else if (this->_running)
+ {
+ // We have just started running
+ // Arm the DMA transfer to start receiving source buffers
+ this->ArmDmaTransfer(this, &this->_jobs[0], true);
+ }
+}
+
+static void ScheduleDlaInference(StreamController* this, CoreDlaJobItem* pJob)
+{
+ // The DLA has an input FIFO. By setting the base address register,
+ // we add this request to the FIFO
+ pJob->_scheduledWithDLA = true;
+ this->_numScheduledInferences++;
+
+ CoreDlaJobPayload* pJobPayload = &pJob->_payload;
+ this->WriteToDlaCsr(this, DLA_DMA_CSR_OFFSET_CONFIG_BASE_ADDR, pJobPayload->_configurationBaseAddressDDR);
+ this->WriteToDlaCsr(this, DLA_DMA_CSR_OFFSET_CONFIG_RANGE_MINUS_TWO, pJobPayload->_configurationSize);
+ this->WriteToDlaCsr(this, DLA_DMA_CSR_OFFSET_INPUT_OUTPUT_BASE_ADDR, pJobPayload->_inputAddressDDR);
+}
+
+static void SetStatus(StreamController* this, NiosStatusType statusType, uint32_t lineNumber)
+{
+ this->_status = statusType;
+ this->_statusLineNumber = lineNumber;
+}
+
+static void InitializeStreamController(StreamController* this,
+ uint32_t sourceBufferSize,
+ uint32_t dropSourceBuffers,
+ uint32_t numInferenceRequests)
+{
+ // This is called once when the inference app is run,
+ // so acts like a reset
+ this->_sourceBufferSize = sourceBufferSize;
+ this->_dropSourceBuffers = dropSourceBuffers;
+ this->_totalNumInferenceRequests = numInferenceRequests;
+ this->_jobs = malloc(sizeof(CoreDlaJobItem) * this->_totalNumInferenceRequests);
+
+ // Reset any previous state
+ this->Reset(this);
+}
+
+static void Reset(StreamController* this)
+{
+ CoreDlaJobItem emptyJob = {};
+ uint32_t lastIndex = this->_totalNumInferenceRequests - 1;
+
+ // Set up the circular job buffers
+ for (uint32_t i = 0; i < this->_totalNumInferenceRequests; i++)
+ {
+ this->_jobs[i] = emptyJob;
+ this->_jobs[i]._index = i;
+ uint32_t previousIndex = (i == 0) ? lastIndex : i - 1;
+ uint32_t nextIndex = (i == lastIndex) ? 0 : i + 1;
+ this->_jobs[i]._pPreviousJob = &this->_jobs[previousIndex];
+ this->_jobs[i]._pNextJob = &this->_jobs[nextIndex];
+ }
+
+ this->_pNextInferenceRequestJob = &this->_jobs[0];
+ this->_pFillingImageJob = &this->_jobs[0];
+ this->_status = NiosStatusType_OK;
+ this->_statusLineNumber = 0;
+ this->_commandCounter = 0;
+ this->_numInferenceRequests = 0;
+ this->_numExecutedJobs = 0;
+ this->_numScheduledInferences = 0;
+ this->_lastReceiveSequenceID = 0;
+ this->_sendSequenceID = 0;
+ this->_running = false;
+ this->_isrCount = 0;
+ this->_numReceivedSourceBuffers = 0;
+}
+
+static void WriteToDlaCsr(StreamController* this, uint32_t addr, uint32_t data)
+{
+ uint32_t* pRegister = (uint32_t*)(dlaBaseAddress + addr);
+ pRegister[0] = data;
+}
+
+// Incrementing the ISR count here will result in NewSourceBuffer above being called
+// in the event loop
+static void MsgDmaIsr(void* pContext)
+{
+ StreamController* this = (StreamController*)pContext;
+ this->_isrCount++;
+}
+
+
diff --git a/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.h b/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.h
new file mode 100644
index 0000000..8b19066
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller.h
@@ -0,0 +1,86 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include "altera_msgdma.h"
+#include "system.h"
+#include "stream_controller_messages.h"
+
+typedef struct CoreDlaJobItem
+{
+ uint32_t _index;
+ bool _hasSourceBuffer;
+ bool _scheduledWithDLA;
+ CoreDlaJobPayload _payload;
+ struct CoreDlaJobItem* _pPreviousJob;
+ struct CoreDlaJobItem* _pNextJob;
+} CoreDlaJobItem;
+
+typedef struct StreamController
+{
+ void (*Start)(struct StreamController* this);
+ void (*Reset)(struct StreamController* this);
+ bool (*InitializeMsgDma)(struct StreamController* this);
+ bool (*ArmDmaTransfer)(struct StreamController* this, CoreDlaJobItem* pFillJob, bool fromHPS);
+ void (*RunEventLoop)(struct StreamController* this);
+ void (*WriteToDlaCsr)(struct StreamController* this, uint32_t addr, uint32_t data);
+ void (*InitializeStreamController)(struct StreamController* this,
+ uint32_t sourceBufferSize,
+ uint32_t dropSourceBuffers,
+ uint32_t numInferenceRequests);
+ void (*SetStatus)(struct StreamController* this,
+ NiosStatusType statusType, uint32_t lineNumber);
+ MessageType (*ReceiveMessage)(struct StreamController *this, volatile MessageHeader* pReceiveMessage);
+ bool (*SendMessage)(struct StreamController* this,
+ MessageType messageType,
+ void* pPayload,
+ size_t payloadSize);
+ void (*NewSourceBuffer)(struct StreamController* this);
+ void (*ScheduleDlaInference)(struct StreamController* this, CoreDlaJobItem* pJob);
+ void (*NewInferenceRequestReceived)(struct StreamController* this, volatile CoreDlaJobPayload* pJob);
+
+ // Message handlers
+ bool (*GetStatusMessageHandler)(struct StreamController* this, volatile uint32_t* pPayload);
+ bool (*ScheduleItemMessageHandler)(struct StreamController* this, volatile uint32_t* pPayload);
+ bool (*PingMessageHandler)(struct StreamController* this, volatile uint32_t* pPayload);
+ bool (*InitializeStreamControllerMessageHandler)(struct StreamController* this, volatile uint32_t* pPayload);
+ bool (*ManualArmDmaTransferMessageHandler)(struct StreamController* this, volatile uint32_t* pPayload);
+ bool (*ManualScheduleDlaInferenceMessageHandler)(struct StreamController* this, volatile uint32_t* pPayload);
+
+ CoreDlaJobItem* _jobs;
+ CoreDlaJobItem* _pNextInferenceRequestJob;
+ CoreDlaJobItem* _pFillingImageJob;
+ CoreDlaJobItem _debugJob;
+ NiosStatusType _status;
+ uint32_t _statusLineNumber;
+ uint32_t _commandCounter;
+ uint32_t _sourceBufferSize;
+ uint32_t _dropSourceBuffers;
+ uint32_t _totalNumInferenceRequests;
+ uint32_t _numInferenceRequests;
+ uint32_t _numExecutedJobs;
+ uint32_t _numScheduledInferences;
+ uint32_t _lastReceiveSequenceID;
+ uint32_t _sendSequenceID;
+ bool _running;
+ uint32_t _numReceivedSourceBuffers;
+ volatile uint32_t _isrCount;
+ alt_msgdma_dev* _pMsgDevice;
+ alt_msgdma_extended_descriptor _msgdmaDescriptor;
+} StreamController;
diff --git a/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller_messages.h b/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller_messages.h
new file mode 100644
index 0000000..3891326
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/app/stream_controller_messages.h
@@ -0,0 +1,90 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <stdint.h>
+
+typedef enum
+{
+ MessageType_Invalid,
+ MessageType_NoOperation,
+ MessageType_GetStatus,
+ MessageType_Status,
+ MessageType_ScheduleItem,
+ MessageType_Ping,
+ MessageType_Pong,
+ MessageType_InitializeStreamController,
+ MessageType_ManualArmDmaTransfer,
+ MessageType_ManualScheduleDlaInference
+} MessageType;
+
+typedef enum
+{
+ NiosStatusType_OK = 1000,
+ NiosStatusType_Error,
+ NiosStatusType_BadMessage,
+ NiosStatusType_BadMessageSequence,
+ NiosStatusType_BadDescriptor,
+ NiosStatusType_AsyncTransferFailed,
+ NiosStatusType_MsgDmaFailed,
+ NiosStatusType_InvalidParameter
+} NiosStatusType;
+
+typedef struct
+{
+ uint32_t _messageReadyMagicNumber;
+ uint32_t _messageType;
+ uint32_t _sequenceID;
+ uint32_t _payload;
+} MessageHeader;
+
+// Message payloads:
+
+typedef struct
+{
+ uint32_t _configurationBaseAddressDDR;
+ uint32_t _configurationSize;
+ uint32_t _inputAddressDDR;
+ uint32_t _outputAddressDDR;
+} CoreDlaJobPayload;
+
+typedef struct
+{
+ uint32_t _sourceBufferSize;
+ uint32_t _dropSourceBuffers;
+ uint32_t _numInferenceRequests;
+} InitializeStreamControllerPayload;
+
+typedef struct
+{
+ NiosStatusType _status;
+ uint32_t _statusLineNumber;
+ uint32_t _numReceivedSourceBuffers;
+ uint32_t _numScheduledInferences;
+ uint32_t _numExecutedJobs;
+} StatusMessagePayload;
+
+typedef struct
+{
+ uint32_t _sourceBufferSize;
+ uint32_t _inputAddressDDR;
+ uint32_t _fromHPS;
+} ManualArmDmaTransferPayload;
+
+typedef struct
+{
+ uint32_t _configurationBaseAddressDDR;
+ uint32_t _configurationSize;
+ uint32_t _inputAddressDDR;
+} ManualScheduleDlaInferencePayload;
+
diff --git a/python/openvino/runtime/coredla_device/stream_controller/build.sh b/python/openvino/runtime/coredla_device/stream_controller/build.sh
new file mode 100755
index 0000000..2d22c5e
--- /dev/null
+++ b/python/openvino/runtime/coredla_device/stream_controller/build.sh
@@ -0,0 +1,54 @@
+#! /bin/bash
+# Run in Nios V Command Shell, Quartus Prime 22.4 or later
+
+quartus_project=$1
+qsys_file=$2
+hex_file=$3
+
+usage()
+{
+ echo "Usage:"
+ echo " build.sh <quartus_project_file> <qsys_file> <destination_hex_file>"
+}
+
+if [ -z "$quartus_project" ]; then
+ usage
+ exit 1
+fi
+
+if [ -z "$qsys_file" ]; then
+ usage
+ exit 1
+fi
+
+if [ -z "$hex_file" ]; then
+ usage
+ exit 1
+fi
+
+if [ ! -f "$quartus_project" ]; then
+ echo Quartus project file not found "$quartus_project"
+ usage
+ exit 1
+fi
+
+if [ ! -f "$qsys_file" ]; then
+ echo qsys file not found "$qsys_file"
+ usage
+ exit 1
+fi
+
+# Export the bsp folder from the Quartus project, create the
+# CMakeFiles.txt for the application, build the app, then
+# build the stream_controller.hex binary, in the 'build' folder
+
+niosv-bsp -c --quartus-project=$quartus_project --qsys=$qsys_file --type=hal bsp/settings.bsp
+niosv-app --bsp-dir=bsp --app-dir=app --srcs=app --elf-name=stream_controller.elf
+
+# cmake dependency, version 3.14.10 or later. https://cmake.org/download/
+cmake -B build -DCMAKE_BUILD_TYPE=Release app
+cmake --build build
+elf2hex build/stream_controller.elf -b 0x0 -w 32 -e 0x1ffff -r 4 -o build/stream_controller.hex
+cp build/stream_controller.hex $hex_file
+
+exit 0