Squash commits for public release

This commit is contained in:
2025-10-12 14:37:14 -04:00
commit 2effba13fa
74 changed files with 24002 additions and 0 deletions

View File

@@ -0,0 +1,698 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ap_int.h"
#include "ap_utils.h"
#include "ap_cint.h"
#include "ap_utils.h"
#include "ap_int.h"
#include "dma_sg_pcie_scheduler.h"
/*
* ------------------------------
* Registers and Masks of the DMA
* ------------------------------
*/
/*
* Tx Channel Registers Base Offset.
*/
#define XAXIDMA_TX_OFFSET 0x00000000
/*
* Rx Channel Registers Base Offset.
*/
#define XAXIDMA_RX_OFFSET 0x00000030
/*
* This Set of Registers are Applicable for both Channels of the DMA.
* Add XAXIDMA_TX_OFFSET to Get to TX channel, and XAXIDMA_RX_OFFSET to Get to RX Channel.
*/
#define XAXIDMA_CR_OFFSET 0x00000000 // Control Register.
#define XAXIDMA_SR_OFFSET 0x00000004 // Status Register.
#define XAXIDMA_SRCADDR_OFFSET 0x00000018 // Source Address Register.
#define XAXIDMA_DESTADDR_OFFSET 0x00000018 // Destination Address Register.
#define XAXIDMA_BUFFLEN_OFFSET 0x00000028 // Transfer Data Size Register.
#define XAXIDMA_CR_RUNSTOP_MASK 0x00000001 // Start/Stop DMA Channel Mask.
#define XAXIDMA_CR_RESET_MASK 0x00000004 // Reset DMA Mask.
#define XAXIDMA_IRQ_IOC_MASK 0x00001000 // Completion Interrupt Mask.
#define XAXIDMA_IRQ_DELAY_MASK 0x00002000 // Delay Interrupt Mask.
#define XAXIDMA_IRQ_ERROR_MASK 0x00004000 // Error Interrupt Mask.
#define XAXIDMA_IRQ_ALL_MASK 0x00007000 // All Interrupts Mask.
/*
* serve_mm2s_transfer()
*
* Invoked from the dma_sg_pcie_scheduler() Top Function.
*
* The Usability of this Function is as Follows:
*
* a --> Get from the Scatter/Gather List of the Source Memory the Physical Address of the Current Page to Transfer.
* b --> Set the Address Translation Register of the PCIe Bridge's Source AXI BAR with the Physical Address of the Current Page to Transfer.
* c --> Setup and Start the DMA.
*
* The Function Parameters are:
*
* 01 --> The AXI Master Interface of the Core (cfg).
* 02 --> The Base Address of the DMA.
* 03 --> The Data Size of the MM2S Transfer.
* 04 --> The Address of the Scatter/Gather List of the Source Memory.
* 05 --> The Address of the BCIe Bridge's Source AXI BAR.
* 06 --> The Offset in the PCIe Bridge of the Address Translation Register that Refers to the Source AXI BAR.
* 07 --> The Current Value of the Page Counter in order to Know which Physical Address to Extract from the Source Scatter/Gather List.
* 08 --> The Transfer Size for the Current Page which Might be Less than the Page Size.
*/
int serve_mm2s_transfer(/*01*/volatile ap_uint<32> *cfg,
/*02*/unsigned int dma_device_address,
/*03*/unsigned int src_data_size,
/*04*/unsigned int sgl_address,
/*05*/unsigned int axi_bar_src_address,
/*06*/unsigned int axi_bar_src_cfg_address,
/*07*/int page_counter,
/*08*/ap_uint<32>current_transfer_size
)
{
ap_uint<32> data_register_array[2]; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
ap_uint<32> data_register; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
/*
* ---------------------------------------------------------------------------------------------------------------------
* Get the Physical Address of the Current Page of the Scatter/Gather List and Set the Source AXI BAR of the PCIe Bridge
* ---------------------------------------------------------------------------------------------------------------------
*/
//Get the 64 Bit Physical Address of the Current Page from the Source Scatter/Gather List.
//The data_register_array[0] Holds the 32 LSBs of the Physical Address.
//The data_register_array[1] Holds the 32 MSBs of the Physical Address.
memcpy(data_register_array, (const ap_uint<32> *)(cfg + ((sgl_address + (page_counter * sizeof(ap_uint<64>))) / 4)), sizeof(ap_uint<64>));
data_register = data_register_array[0];
//Write the 32 LSBs of the Physical Address of the Current Page to the Lower Register of the Source AXI BAR.
memcpy((ap_uint<32> *)(cfg + (axi_bar_src_cfg_address) / 4), &data_register, sizeof(ap_uint<32>));
data_register = data_register_array[1];
//Write the 32 MSBs of the Physical Address of the Current Page to the Upper Register of the Source AXI BAR.
memcpy((ap_uint<32> *)(cfg + (axi_bar_src_cfg_address - 4) / 4), &data_register, sizeof(ap_uint<32>));
/*
* ---------------------------------------------
* Setup and Start DMA to Device Transfer (MM2S)
* ---------------------------------------------
*/
//Get from the Internal Register (axi_bar_src_address) of the Core the Source Address that the DMA will Use to Read the Initial Image Data.
//The Source Address of the DMA MM2S Channel will be the Source AXI BAR which Corresponds to the Physical Address of the Current Page.
data_register = axi_bar_src_address;
//Write the Source Address to the Source Register of the DMA.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_SRCADDR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
//Read the MM2S Control Register of the DMA.
memcpy(&data_register, (const ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_CR_OFFSET) / 4), sizeof(ap_uint<32>));
//Set the Recently Read Value with the Mask Required to Enable the MM2S DMA Channel.
data_register = data_register | XAXIDMA_CR_RUNSTOP_MASK;
//Write the new Value Back to the Control Register of the DMA in Order to Enable the MM2S Channel.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_CR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
//Write the Transfer Size to the MM2S Length Register of the DMA which Starts the MM2S Transfer.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_BUFFLEN_OFFSET) / 4), &current_transfer_size, sizeof(ap_uint<32>));
return 1;
}
/*
* serve_s2mm_transfer()
*
* Invoked from the dma_sg_pcie_scheduler() Top Function.
*
* The Usability of this Function is as Follows:
*
* a --> Get from the Scatter/Gather List of the Destination Memory the Physical Address of the Current Page to Transfer.
* b --> Set the Address Translation Register of the PCIe Bridge's Destination AXI BAR with the Physical Address of the Current Page to Transfer.
* c --> Setup and Start the DMA.
*
* The Function Parameters are:
*
* 01 --> The AXI Master Interface of the Core (cfg).
* 02 --> The Base Address of the DMA.
* 03 --> The Data Size of the S2MM Transfer.
* 04 --> The Address of the Scatter/Gather List of the Destination Memory.
* 05 --> The Address of the BCIe Bridge's Destination AXI BAR.
* 06 --> The Offset in the PCIe Bridge of the Address Translation Register that Refers to the Source AXI BAR.
* 07 --> The Current Value of the Page Counter in order to Know which Physical Address to Extract from the Source Scatter/Gather List.
* 08 --> The Transfer Size for the Current Page which Might be Less than the Page Size.
*/
int serve_s2mm_transfer(/*01*/volatile ap_uint<32> *cfg,
/*02*/unsigned int dma_device_address,
/*03*/unsigned int src_data_size,
/*04*/unsigned int sgl_address,
/*05*/unsigned int axi_bar_dst_address,
/*06*/unsigned int axi_bar_dst_cfg_address,
/*07*/int page_counter,
/*08*/ap_uint<32>current_transfer_size)
{
ap_uint<32> data_register_array[2]; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
ap_uint<32> data_register; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
/*
* ---------------------------------------------------------------------------------------------------------------------
* Get the Physical Address of the Current Page of the Scatter/Gather List and Set the Destination AXI BAR of the PCIe Bridge
* ---------------------------------------------------------------------------------------------------------------------
*/
//Get the 64 Bit Physical Address of the Current Page from the Destination Scatter/Gather List.
//The data_register_array[0] Holds the 32 LSBs of the Physical Address.
//The data_register_array[1] Holds the 32 MSBs of the Physical Address.
memcpy(data_register_array, (const ap_uint<32> *)(cfg + ((sgl_address + (page_counter * sizeof(ap_uint<64>))) / 4)), sizeof(ap_uint<64>));
data_register = data_register_array[0];
//Write the 32 LSBs of the Physical Address of the Current Page to the Lower Register of the Destination AXI BAR.
memcpy((ap_uint<32> *)(cfg + (axi_bar_dst_cfg_address) / 4), &data_register, sizeof(ap_uint<32>));
data_register = data_register_array[1];
//Write the 32 MSBs of the Physical Address of the Current Page to the Upper Register of the Destination AXI BAR.
memcpy((ap_uint<32> *)(cfg + (axi_bar_dst_cfg_address - 4) / 4), &data_register, sizeof(ap_uint<32>));
/*
* ---------------------------------------------
* Setup and Start Device to DMA Transfer (S2MM)
* ---------------------------------------------
*/
//Get from the Internal Register (axi_bar_dst_address) of the Core the Destination Address that the DMA will Use to Read the Initial Image Data.
//The Destination Address of the DMA S2MM Channel will be the Destination AXI BAR which Corresponds to the Physical Address of the Current Page.
data_register = axi_bar_dst_address;
//Write the Destination Address to the Source Register of the DMA.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_DESTADDR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
//Read the S2MM Control Register of the DMA.
memcpy(&data_register, (const ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_CR_OFFSET) / 4), sizeof(ap_uint<32>));
//Set the Recently Read Value with the Mask Required to Enable the S2MM DMA Channel.
data_register = data_register | XAXIDMA_CR_RUNSTOP_MASK;
//Write the new Value Back to the Control Register of the DMA in Order to Enable the S2MM Channel.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_CR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
//Write the Transfer Size to the S2MM Length Register of the DMA which Starts the S2MM Transfer.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_BUFFLEN_OFFSET) / 4), &current_transfer_size, sizeof(ap_uint<32>));
return 1;
}
/*
* serve_mm2s_interrupt()
*
* Invoked from the dma_sg_pcie_scheduler() Top Function.
*
* The Usability of this Function is to Acknowledge Triggered Interrupts on the MM2S Channel of the DMA.
*
* The Function Parameters are:
*
* 01 --> The AXI Master Interface of the Core (cfg).
* 02 --> The Base Address of the DMA.
*/
int serve_mm2s_interrupt(volatile ap_uint<32> *cfg, unsigned int dma_device_address)
{
ap_uint<32> data_register; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
//Read the DMA MM2S Status Register of the DMA to Get the IRQs.
memcpy(&data_register, (const ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_SR_OFFSET) / 4), sizeof(ap_uint<32>));
//Filter the Recently Read Value with the XAXIDMA_IRQ_ALL_MASK so as to Keep ONLY the IRQs that were Triggered.
data_register = data_register & XAXIDMA_IRQ_ALL_MASK;
//Write the new Value Back to the MM2S Status Register of the DMA which Acknowledges the Triggered Interrupts on the MM2S Channel.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_SR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
return 1;
}
/*
* serve_s2mm_interrupt()
*
* Invoked from the dma_sg_pcie_scheduler() Top Function.
*
* The Usability of this Function is to Acknowledge Triggered Interrupts on the S2MM Channel of the DMA.
*
* The Function Parameters are:
*
* 01 --> The AXI Master Interface of the Core (cfg).
* 02 --> The Base Address of the DMA.
*/
int serve_s2mm_interrupt(volatile ap_uint<32> *cfg, unsigned int dma_device_address)
{
ap_uint<32> data_register; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
//Read the DMA S2MM Status Register of the DMA to Get the IRQs.
memcpy(&data_register, (const ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET) / 4), sizeof(ap_uint<32>));
//Filter the Recently Read Value with the XAXIDMA_IRQ_ALL_MASK so as to Keep ONLY the IRQs that were Triggered.
data_register = data_register & XAXIDMA_IRQ_ALL_MASK;
//Write the new Value Back to the S2MM Status Register of the DMA which Acknowledges the Triggered Interrupts on the S2MM Channel.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
return 1;
}
/*
* dma_sg_pcie_scheduler() Top Function
*
* The Hardware Funtionality of the DMA SG PCIe Scheduler Core.
*
* The DMA SG PCIe Scheduler Core is Part of the Acceleration Group Scatter/Gather.
* It is Used to Manage the the MM2S and S2MM Channels of the DMA when a Scatter/Gather List is Required to Transfer the Image Data.
* The DMA SG PCIe Scheduler Interacts with the DMA of the Acceleration Group Scatter/Gather and the Configuration AXI Interface of the PCIe Bridge.
*
* The Sequential Steps of the Core's Functionality are as Follows:
*
* a --> Calculate the Number of Pages to Transfer for the MM2S and S2MM Channels of the DMA.
* b --> Enable the DMA MM2S Interrupts.
* c --> Enable the DMA S2MM Interrupts.
* d --> Start a Page Transfer over the MM2S Channel (See the serve_mm2s_transfer() Function for Details).
* e --> Start a Page Transfer over the S2MM Channel (See the serve_s2mm_transfer() Function for Details).
* f --> Loop for as long as Both Channels Require to Complete the Transfer of all the Pages (both_done).
* g --> In Every Loop Check if Either the MM2S or the S2MM Channels Have Triggered an Interrupt on Completion of the Page Transfer.
* h --> If any of the Channels Triggers an Interrupt then Clear the Channel's Interrupt
* (See the serve_mm2s_interrupt() and serve_s2MM_interrupt Functions for Details)
* and Start the Channel's next Page Transfer.
*
* The Function Parameters are the Input/Output Ports/Interfaces of the Core:
*
* 01 --------> The AXI Master Interface of the Core Used to Access External Devices and Memories.
* 02 --------> Single Bit Input Used to Receive External Interrupts from the DMA MM2S Channel.
* 03 --------> Single Bit Input Used to Receive External Interrupts from the DMA S2MM Channel.
* 04 to 12 --> Registers of the Core that are Accessed through the AXI Slave Lite Interface of the Core.
*/
int dma_sg_pcie_scheduler(/*01*/volatile ap_uint<32> *cfg,
/*02*/volatile ap_uint<1> *mm2s_intr_in,
/*03*/volatile ap_uint<1> *s2mm_intr_in,
/*04*/unsigned int dma_device_address,
/*05*/unsigned int requested_data_size,
/*06*/unsigned int page_size,
/*07*/unsigned int mm2s_sgl_address,
/*08*/unsigned int axi_bar_src_address,
/*09*/unsigned int axi_bar_src_cfg_address,
/*10*/unsigned int s2mm_sgl_address,
/*11*/unsigned int axi_bar_dst_address,
/*12*/unsigned int axi_bar_dst_cfg_address
)
{
/*
* The cfg is the AXI Master Interface of the Core.
*/
#pragma HLS INTERFACE m_axi port=cfg
/*
* The mm2s_intr_in is a Single Bit Input which is Used to Receive External Interrupts from the DMA MM2S Channel.
*/
#pragma HLS INTERFACE ap_none port=mm2s_intr_in
/*
* The s2mm_intr_in is a Single Bit Input which is Used to Receive External Interrupts from the DMA S2MM Channel.
*/
#pragma HLS INTERFACE ap_none port=s2mm_intr_in
/*
* The dma_device_address is a Register to Store the Base Address of the DMA that this Core
* will Need to Access through the cfg AXI Master Interface.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=dma_device_address bundle=cfg
/*
* The requested_data_size is a Register to Store the Size of the Data that will be Transferred.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=requested_data_size bundle=cfg
/*
* The page_size is a Register to Store the Size of each Page(Usually 4K in Linux) that will be Transferred.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=page_size bundle=cfg
/*
* The mm2s_sgl_address is a Register to Store the Address of the Scatter/Gather List of the Source Data.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=mm2s_sgl_address bundle=cfg
/*
* The axi_bar_src_address is a Register to Store the Address of the AXI BAR that the DMA will Use to Read the Source Data.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=axi_bar_src_address bundle=cfg
/*
* The axi_bar_src_cfg_address is a Register to Store the Address/Offset of the PCIe Bridge's Address Translation Register that Refers to the Source AXI BAR.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=axi_bar_src_cfg_address bundle=cfg
/*
* The s2mm_sgl_address is a Register to Store the Address of the Scatter/Gather List of the Destination Data.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=s2mm_sgl_address bundle=cfg
/*
* The axi_bar_dst_address is a Register to Store the Address of the AXI BAR that the DMA will Use to Write the Destination Data.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=axi_bar_dst_address bundle=cfg
/*
* The axi_bar_src_cfg_address is a Register to Store the Address/Offset of the PCIe Bridge's Address Translation Register that Refers to the Source AXI BAR.
* This Register is Accessed through the AXI Slave Lite Interface (s_axilite_cfg) of the Core.
*/
#pragma HLS INTERFACE s_axilite port=axi_bar_dst_cfg_address bundle=cfg
#pragma HLS INTERFACE s_axilite port=return bundle=cfg
ap_uint<32> data_register_array[2]; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
ap_uint<32> data_register; // Used to Temporalily Store Values when Reading or Writing from/to Registers of External Devices.
ap_uint<32> mm2s_data_size; // The Data Size to Transfer for a Page of the MM2S Channel (The Last Page may not be Full).
ap_uint<32> s2mm_data_size; // The Data Size to Transfer for a Page of the S2MM Channel (The Last Page may not be Full).
ap_uint<1> dma_mm2s_intr_in_value; // Used to Read the Last Value of the dma_mm2s_intr_in_value Input Port.
ap_uint<1> dma_s2mm_intr_in_value; // Used to Read the Last Value of the dma_s2mm_intr_in_value Input Port.
int mm2s_pages_counter = 0; // Used to Count the Number of Tranferred Pages for the MM2S Channel.
int s2mm_pages_counter = 0; // Used to Count the Number of Tranferred Pages for the S2MM Channel.
int mm2s_pages_number; // The Number of Pages to Transfer for the MM2S Channel.
int s2mm_pages_number; // The Number of Pages to Transfer for the S2MM Channel.
int mm2s_remaining_bytes; // Used to Count the Remaining Bytes of the MM2S Transfer.
int s2mm_remaining_bytes; // Used to Count the Remaining Bytes of the S2MM Transfer.
ap_uint<32> current_transfer_size;
int both_done = 0; // Flag to Know When Both Channels (MM2S/S2MM) are Done.
//Divide the Size of the Data to Transfer by the Page Size to Get the Number of Pages to Transfer over the MM2S and S2mm Channels.
mm2s_pages_number = requested_data_size / page_size;
s2mm_pages_number = requested_data_size / page_size;
/*
* If the Value of the MM2S Pages Number Multiplied by the Page Size is Less than the Initial Data Size
* then there is One More Page with Less Data than the Page Size.
*
* So, Increment mm2s_pages_number Variable by 1.
*/
if((mm2s_pages_number * page_size) < requested_data_size)
{
mm2s_pages_number = mm2s_pages_number + 1;
}
//Initialize the Remaining Bytes for the MM2S Channel to be Equal to the Data Transfer Size.
mm2s_remaining_bytes = requested_data_size;
/*
* If the Value of the S2MM Pages Number Multiplied by the Page Size is Less than the Initial Data Size
* then there is One More Page with Less Data than the Page Size.
*
* So, Increment s2mm_pages_number Variable by 1.
*/
if((s2mm_pages_number * page_size) < requested_data_size)
{
s2mm_pages_number = s2mm_pages_number + 1;
}
//Initialize the Remaining Bytes for the S2MM Channel to be Equal to the Data Transfer Size.
s2mm_remaining_bytes = requested_data_size;
/*
* ----------------------------------------------
* Enable the DMA MM2S Interrupts (DMA to Device)
* ----------------------------------------------
*/
//Read the Control Register of the MM2S Channel of the DMA.
memcpy(&data_register, (const ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_CR_OFFSET) / 4), sizeof(ap_uint<32>));
//Set the Recently Read Value with the Masks Required to Enable the IOC, Delay and Error IRQs.
//NOTE that IOC Stands for Interrupt On Complete.
data_register = data_register | (XAXIDMA_IRQ_ERROR_MASK | XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
//Write the new Value Back to the Control Register of the DMA to Enable the MM2S Interrupts.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_TX_OFFSET + XAXIDMA_CR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
/*
* ----------------------------------------------
* Enable the DMA S2MM Interrupts (Device to DMA)
* ----------------------------------------------
*/
//Read the Control Register of the S2MM Channel of the DMA.
memcpy(&data_register, (const ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_CR_OFFSET) / 4), sizeof(ap_uint<32>));
//Set the Recently Read Value with the Masks Required to Enable the IOC, Delay and Error IRQs.
//NOTE that IOC Stands for Interrupt On Complete.
data_register = data_register | (XAXIDMA_IRQ_ERROR_MASK | XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
//Write the new Value Back to the Control Register of the DMA to Enable the S2MM Interrupts.
memcpy((ap_uint<32> *)(cfg + (dma_device_address + XAXIDMA_RX_OFFSET + XAXIDMA_CR_OFFSET) / 4), &data_register, sizeof(ap_uint<32>));
/*
* If the Value of the Remaining Bytes is Larger that a Page Size then we Can Set the DMA to Transfer a whole Page over the MM2S Channel.
*/
if(mm2s_remaining_bytes >= page_size)
{
mm2s_data_size = page_size;
}
/*
* If the Value of the Remaining Bytes is Less that a Page Size then we Can Set the DMA to Transfer the Remaining Bytes over the MM2S Channel.
*/
if((mm2s_remaining_bytes > 0) && (mm2s_remaining_bytes < page_size))
{
mm2s_data_size = mm2s_remaining_bytes;
}
current_transfer_size = mm2s_data_size;
//Start a DMA Page Transfer over the MM2S Channel
//The Transfer Can be of Size Equal to a whole Page Size or Just the Remaining Bytes According to the current_transfer_size Variable.
serve_mm2s_transfer(cfg,
dma_device_address,
requested_data_size,
mm2s_sgl_address,
axi_bar_src_address,
axi_bar_src_cfg_address,
0,
current_transfer_size);
//Decrement the MM2S Channel's Remaining Bytes According to the mm2s_data_size Variable in order to Know Hom Many Bytes are left to be Transferred.
mm2s_remaining_bytes = mm2s_remaining_bytes - mm2s_data_size;
/*
* If the Value of the Remaining Bytes is Larger that a Page Size then we Can Set the DMA to Transfer a whole Page over the S2MM Channel.
*/
if(s2mm_remaining_bytes >= page_size)
{
s2mm_data_size = page_size;
}
/*
* If the Value of the Remaining Bytes is Less that a Page Size then we Can Set the DMA to Transfer the Remaining Bytes over the S2MM Channel.
*/
if((s2mm_remaining_bytes > 0) && (s2mm_remaining_bytes < page_size))
{
s2mm_data_size = s2mm_remaining_bytes;
}
current_transfer_size = s2mm_data_size;
//Start a DMA Page Transfer over the S2MM Channel
//The Transfer Can be of Size Equal to a whole Page Size or Just the Remaining Bytes According to the current_transfer_size Variable.
serve_s2mm_transfer(cfg,
dma_device_address,
requested_data_size,
s2mm_sgl_address,
axi_bar_dst_address,
axi_bar_dst_cfg_address,
0,
current_transfer_size);
//Decrement the S2MM Channel's Remaining Bytes According to the s2mm_data_size Variable in order to Know Hom Many Bytes are left to be Transferred.
s2mm_remaining_bytes = s2mm_remaining_bytes - s2mm_data_size;
//Start Looping for as Long as the Rest of the Pages for the MM2S and S2MM Channels are Being Transferred.
while(both_done < 2)
{
//Read the Current State of the mm2s_intr_in Input.
dma_mm2s_intr_in_value = *mm2s_intr_in;
//Read the Current State of the s2mm_intr_in Input.
dma_s2mm_intr_in_value = *s2mm_intr_in;
/*
* If we Have an Interrupt from the MM2S Channel than we Should Clear the Interrupt and Start the Next Page Transfer.
*/
if(dma_mm2s_intr_in_value == 1)
{
//Acknowledge the Triggered Interrupt of the DMA MM2S Channel.
serve_mm2s_interrupt(cfg, dma_device_address);
//If the MM2S Pages Counter of the Current Page Has Not Reached the Total Number of Pages then Proceed to Start the Next Page Transfer.
if(mm2s_pages_counter < (mm2s_pages_number - 1))
{
/*
* If the Value of the Remaining Bytes is Larger that a Page Size then we Can Set the DMA to Transfer a whole Page over the MM2S Channel.
*/
if(mm2s_remaining_bytes >= page_size)
{
mm2s_data_size = page_size;
}
/*
* If the Value of the Remaining Bytes is Less that a Page Size then we Can Set the DMA to Transfer the Remaining Bytes over the MM2S Channel.
*/
if((mm2s_remaining_bytes > 0) && (mm2s_remaining_bytes < page_size))
{
mm2s_data_size = mm2s_remaining_bytes;
}
current_transfer_size = mm2s_data_size;
//Start a DMA Page Transfer over the MM2S Channel According to the current_transfer_size Variable.
serve_mm2s_transfer(cfg,
dma_device_address,
requested_data_size,
mm2s_sgl_address,
axi_bar_src_address,
axi_bar_src_cfg_address,
mm2s_pages_counter + 1,
current_transfer_size);
//Decrement the MM2S Channel's Remaining Bytes According to the mm2s_data_size Variable in order to Know Hom Many Bytes are left to be Transferred.
mm2s_remaining_bytes = mm2s_remaining_bytes - mm2s_data_size;
}
//Increment the MM2S Pages Counter to Keep Track of the Remaining MM2S Pages to Transfer.
mm2s_pages_counter++;
}
/*
* If we Have an Interrupt from the S2MM Channel than we Should Clear the Interrupt and Start the Next Page Transfer.
*/
if(dma_s2mm_intr_in_value == 1)
{
//Acknowledge the Triggered Interrupt of the DMA S2MM Channel.
serve_s2mm_interrupt(cfg, dma_device_address);
//If the S2MM Pages Counter of the Current Page Has Not Reached the Total Number of Pages then Proceed to Start the Next Page Transfer.
if(s2mm_pages_counter < (s2mm_pages_number - 1))
{
/*
* If the Value of the Remaining Bytes is Larger that a Page Size then we Can Set the DMA to Transfer a whole Page over the S2MM Channel.
*/
if(s2mm_remaining_bytes >= page_size)
{
s2mm_data_size = page_size;
}
/*
* If the Value of the Remaining Bytes is Less that a Page Size then we Can Set the DMA to Transfer the Remaining Bytes over the S2MM Channel.
*/
if((s2mm_remaining_bytes > 0) && (s2mm_remaining_bytes < page_size))
{
s2mm_data_size = s2mm_remaining_bytes;
}
current_transfer_size = s2mm_data_size;
//Start a DMA Page Transfer over the S2MM Channel According to the current_transfer_size Variable.
serve_s2mm_transfer(cfg,
dma_device_address,
requested_data_size,
s2mm_sgl_address,
axi_bar_dst_address,
axi_bar_dst_cfg_address,
s2mm_pages_counter + 1,
current_transfer_size);
//Decrement the S2MM Channel's Remaining Bytes According to the s2mm_data_size Variable in order to Know Hom Many Bytes are left to be Transferred.
s2mm_remaining_bytes = s2mm_remaining_bytes - s2mm_data_size;
}
//Increment the S2MM Pages Counter to Keep Track of the Remaining S2MM Pages to Transfer.
s2mm_pages_counter++;
}
//If the MM2S Pages Counter Has Reached the Total Number of Pages then the MM2S Channel Has Finished the Data Transfer.
if(mm2s_pages_counter == (mm2s_pages_number))
{
//Increment the mm2s_pages_counter Variable so that will not Enter the Current if Condition Again.
mm2s_pages_counter++;
//Increment the both_done Variable on Behalf of the MM2S Channel.
//The both_done Variable will ONLY be Incremented Once on Behalf of the MM2S Channel because we will not Enter this if Condition Again.
//When the S2MM Channel, also, Increments the both_done Variable the Data Transfer is Completed (both_done =2).
both_done++;
}
//If the S2MM Pages Counter Has Reached the Total Number of Pages then the S2MM Channel Has Finished the Data Transfer.
if(s2mm_pages_counter == (s2mm_pages_number))
{
//Increment the s2mm_pages_counter Variable so that will not Enter the Current if Condition Again.
s2mm_pages_counter++;
//Increment the both_done Variable on Behalf of the S2MM Channel.
//The both_done Variable will ONLY be Incremented Once on Behalf of the S2MM Channel because we will not Enter this if Condition Again.
//When the MM2S Channel, also, Increments the both_done Variable the Data Transfer is Completed (both_done =2).
both_done++;
}
}
//Reset the Variables.
dma_mm2s_intr_in_value = 0;
dma_s2mm_intr_in_value = 0;
both_done = 0;
return 1;
}

View File

@@ -0,0 +1,126 @@
#define APM_READ_TRANSACTIONS_OFFSET 0
#define APM_READ_BYTES_OFFSET 4
#define APM_WRITE_TRANSACTIONS_OFFSET 8
#define APM_WRITE_BYTES_OFFSET 12
#define APM_PACKETS_OFFSET 16
#define APM_BYTES_OFFSET 20
#define APM_GCC_L_OFFSET 24
#define APM_GCC_U_OFFSET 28
#define CDMA_FETCH_TIME_START_L_OFFSET 32
#define CDMA_FETCH_TIME_START_U_OFFSET 36
#define CDMA_FETCH_TIME_END_L_OFFSET 40
#define CDMA_FETCH_TIME_END_U_OFFSET 44
#define CDMA_SEND_TIME_START_L_OFFSET 48
#define CDMA_SEND_TIME_START_U_OFFSET 52
#define CDMA_SEND_TIME_END_L_OFFSET 56
#define CDMA_SEND_TIME_END_U_OFFSET 60
#define DMA_ACCEL_TIME_START_L_OFFSET 64
#define DMA_ACCEL_TIME_START_U_OFFSET 68
#define DMA_ACCEL_TIME_END_L_OFFSET 72
#define DMA_ACCEL_TIME_END_U_OFFSET 76
struct image_info
{
ap_uint<32> rows;
ap_uint<32> columns;
ap_uint<64> size;
};
struct metrics
{
/*
* AXI Performance Monitor Metrics
*/
ap_uint<32> apm_read_transactions; //Offset 0 Bytes
ap_uint<32> apm_read_bytes; //Offset 4 Bytes
ap_uint<32> apm_write_transactions; //Offset 8 Bytes
ap_uint<32> apm_write_bytes; //Offset 12 Bytes
ap_uint<32> apm_packets; //Offset 16 Bytes
ap_uint<32> apm_bytes; //Offset 20 Bytes
ap_uint<32> apm_gcc_l; //Offset 24 Bytes
ap_uint<32> apm_gcc_u; //Offset 28 Bytes
ap_uint<32> cdma_fetch_time_start_l; //Offset 32 Bytes
ap_uint<32> cdma_fetch_time_start_u; //Offset 36 Bytes
ap_uint<32> cdma_fetch_time_end_l; //Offset 40 Bytes
ap_uint<32> cdma_fetch_time_end_u; //Offset 44 Bytes
ap_uint<32> cdma_send_time_start_l; //Offset 48 Bytes
ap_uint<32> cdma_send_time_start_u; //Offset 52 Bytes
ap_uint<32> cdma_send_time_end_l; //Offset 56 Bytes
ap_uint<32> cdma_send_time_end_u; //Offset 60 Bytes
ap_uint<32> dma_accel_time_start_l; //Offset 64 Bytes
ap_uint<32> dma_accel_time_start_u; //Offset 68 Bytes
ap_uint<32> dma_accel_time_end_l; //Offset 72 Bytes
ap_uint<32> dma_accel_time_end_u; //Offset 76 Bytes
struct image_info shared_image_info; // Offset 80 Bytes
/*
* Kernel and Userspace Metrics
*/
ap_uint<64> total_time_start;
ap_uint<64> total_time_end;
ap_uint<64> sleep_time_start;
ap_uint<64> sleep_time_end;
ap_uint<64> preparation_time_start;
ap_uint<64> preparation_time_end;
ap_uint<64> load_time_start;
ap_uint<64> load_time_end;
ap_uint<64> save_time_start;
ap_uint<64> save_time_end;
};
struct status_flags
{
ap_uint<32> accel_direct_0_occupied_pid;
ap_uint<32> accel_direct_1_occupied_pid;
ap_uint<32> accel_indirect_0_occupied_pid;
ap_uint<32> accel_indirect_1_occupied_pid;
ap_uint<32> accel_indirect_2_occupied_pid;
ap_uint<32> accel_indirect_3_occupied_pid;
ap_uint<32> accel_sg_0_occupied_pid;
ap_uint<32> accelerator_busy;
ap_uint<32> open_modules;
};
struct shared_repository
{
struct metrics accel_direct_0_shared_metrics;
struct metrics accel_direct_1_shared_metrics;
struct metrics accel_indirect_0_shared_metrics;
struct metrics accel_indirect_1_shared_metrics;
struct metrics accel_indirect_2_shared_metrics;
struct metrics accel_indirect_3_shared_metrics;
struct metrics accel_sg_0_shared_metrics;
struct status_flags shared_status_flags;
};

View File

@@ -0,0 +1,17 @@
open_project DMA_SG_PCIe_Scheduler
set_top dma_sg_pcie_scheduler
add_files dma_sg_pcie_scheduler.cpp
open_solution "solution1"
#The Part Refers to the Xilinx Virtex 7 VC707 FPGA Development Board
set_part {xc7vx485tffg1761-2}
create_clock -period 10 -name default
csynth_design
export_design -format ip_catalog -display_name "DMA SG PCIe Scheduler" -version "1.0"
exit