1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

clean up and comment ips/dma.cpp

This commit is contained in:
Niklas Eiling 2022-11-17 16:31:18 +01:00
parent 34c8458500
commit f105b08144
3 changed files with 97 additions and 50 deletions

View file

@ -96,8 +96,6 @@ private:
void setupScatterGatherRingRx();
void setupScatterGatherRingTx();
static constexpr size_t ringSize = sizeof(uint16_t) * XAXIDMA_BD_NUM_WORDS * 1024;
public:
static constexpr const char* s2mmPort = "S2MM";
static constexpr const char* mm2sPort = "MM2S";
@ -127,11 +125,26 @@ private:
XAxiDma xDma;
XAxiDma_Config xConfig;
bool configDone = false;
// use polling to wait for DMA completion or interrupts via efds
bool polling = false;
// Timeout after which the DMA controller issues in interrupt if no data has been received
// Delay is 125 x <delay> x (clock period of SG clock). SG clock is 100 MHz by default.
int delay = 0;
// Coalesce is the number of messages/BDs to wait for before issuing an interrupt
uint32_t writeCoalesce = 1;
uint32_t readCoalesce = 4;
int delay;
int coalesce;
// (maximum) size of a single message on the read channel in bytes.
// The message buffer/BD should have enough room for this many bytes.
size_t readMsgSize = 4;
// When using SG: ringBdSize is the maximum number of BDs usable in the ring
// Depending on alignment, the actual number of BDs usable can be smaller
static constexpr size_t requestedRingBdSize = 1024;
uint32_t actualRingBdSize = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, requestedRingBdSize);
MemoryBlock::UniquePtr sgRingTx;
MemoryBlock::UniquePtr sgRingRx;
};

View file

@ -42,11 +42,10 @@ static DmaFactory factory;
bool Dma::init()
{
coalesce = 1;
delay = 0;
// Check if configJson has been called
if (!configDone)
throw RuntimeError("DMA device not configured");
// If there is a scatter-gather interface, then this instance has it
// hasSG = busMasterInterfaces.count(sgInterface) == 1;
if (hasScatterGather())
logger->info("Scatter-Gather support: {}", hasScatterGather());
@ -65,12 +64,14 @@ bool Dma::init()
logger->debug("DMA selftest passed");
// Map buffer descriptors
if (hasScatterGather())
if (hasScatterGather()) {
if (actualRingBdSize < 2*readCoalesce || actualRingBdSize < 2*writeCoalesce) {
throw RuntimeError("Ring buffer size is too small for coalesce value");
}
setupScatterGather();
}
// Enable completion interrupts for both channels
XAxiDma_IntrEnable(&xDma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
XAxiDma_IntrEnable(&xDma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);
irqs[mm2sInterrupt].irqController->enableInterrupt(irqs[mm2sInterrupt], polling);
irqs[s2mmInterrupt].irqController->enableInterrupt(irqs[s2mmInterrupt], polling);
@ -94,11 +95,11 @@ void Dma::setupScatterGatherRingRx()
XAxiDma_BdRingIntDisable(rxRingPtr, XAXIDMA_IRQ_ALL_MASK);
// Set delay and coalescing
XAxiDma_BdRingSetCoalesce(rxRingPtr, coalesce, delay);
XAxiDma_BdRingSetCoalesce(rxRingPtr, readCoalesce, delay);
// Allocate and map space for BD ring in host RAM
auto &alloc = villas::HostRam::getAllocator();
sgRingRx = alloc.allocateBlock(ringSize);
sgRingRx = alloc.allocateBlock(requestedRingBdSize * sizeof(uint16_t) * XAXIDMA_BD_NUM_WORDS);
if (not card->mapMemoryBlock(*sgRingRx))
throw RuntimeError("Memory not accessible by DMA");
@ -109,9 +110,7 @@ void Dma::setupScatterGatherRingRx()
auto virtAddr = reinterpret_cast<uintptr_t>(mm.getTranslationFromProcess(sgRingRx->getAddrSpaceId()).getLocalAddr(0));
// Setup Rx BD space
auto bdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, sgRingRx->getSize());
ret = XAxiDma_BdRingCreate(rxRingPtr, physAddr, virtAddr, XAXIDMA_BD_MINIMUM_ALIGNMENT, bdCount);
ret = XAxiDma_BdRingCreate(rxRingPtr, physAddr, virtAddr, XAXIDMA_BD_MINIMUM_ALIGNMENT, actualRingBdSize);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to create RX ring: {}", ret);
@ -123,6 +122,8 @@ void Dma::setupScatterGatherRingRx()
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to clone BD template: {}", ret);
// Enable completion interrupt
XAxiDma_IntrEnable(&xDma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);
// Start the RX channel
ret = XAxiDma_BdRingStart(rxRingPtr);
if (ret != XST_SUCCESS)
@ -139,11 +140,11 @@ void Dma::setupScatterGatherRingTx()
XAxiDma_BdRingIntDisable(txRingPtr, XAXIDMA_IRQ_ALL_MASK);
// Set TX delay and coalesce
XAxiDma_BdRingSetCoalesce(txRingPtr, coalesce, delay);
XAxiDma_BdRingSetCoalesce(txRingPtr, writeCoalesce, delay);
// Allocate and map space for BD ring in host RAM
auto &alloc = villas::HostRam::getAllocator();
sgRingTx = alloc.allocateBlock(ringSize);
sgRingTx = alloc.allocateBlock(requestedRingBdSize * sizeof(uint16_t) * XAXIDMA_BD_NUM_WORDS);
if (not card->mapMemoryBlock(*sgRingTx))
throw RuntimeError("Memory not accessible by DMA");
@ -154,9 +155,7 @@ void Dma::setupScatterGatherRingTx()
auto virtAddr = reinterpret_cast<uintptr_t>(mm.getTranslationFromProcess(sgRingTx->getAddrSpaceId()).getLocalAddr(0));
// Setup TxBD space
auto bdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, sgRingTx->getSize());
ret = XAxiDma_BdRingCreate(txRingPtr, physAddr, virtAddr, XAXIDMA_BD_MINIMUM_ALIGNMENT, bdCount);
ret = XAxiDma_BdRingCreate(txRingPtr, physAddr, virtAddr, XAXIDMA_BD_MINIMUM_ALIGNMENT, actualRingBdSize);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to create TX BD ring: {}", ret);
@ -168,6 +167,8 @@ void Dma::setupScatterGatherRingTx()
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to clone TX ring BD: {}", ret);
// Enable completion interrupt
XAxiDma_IntrEnable(&xDma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
// Start the TX channel
ret = XAxiDma_BdRingStart(txRingPtr);
if (ret != XST_SUCCESS)
@ -258,6 +259,7 @@ bool Dma::read(const MemoryBlock &mem, size_t len)
return hasScatterGather() ? readScatterGather(buf, len) : readSimple(buf, len);
}
//Write a single message
bool Dma::writeScatterGather(const void *buf, size_t len)
{
// buf is address from view of DMA controller
@ -287,10 +289,6 @@ bool Dma::writeScatterGather(const void *buf, size_t len)
// TODO: Check if we really need this
XAxiDma_BdSetId(bd, (uintptr_t) buf);
ret = XAxiDma_BdRingSetCoalesce(txRing, 1, 0);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to set coalesce settings: {}.", ret);
// Give control of BD to HW. We should not access it until transfer is finished.
// Failure could also indicate that EOF is not set on last Bd
ret = XAxiDma_BdRingToHw(txRing, 1, bd);
@ -304,32 +302,42 @@ bool Dma::readScatterGather(void *buf, size_t len)
{
int ret = XST_FAILURE;
if (len < readCoalesce*readMsgSize)
throw RuntimeError("Read size is smaller than readCoalesce*msgSize. Cannot setup BDs.");
auto *rxRing = XAxiDma_GetRxRing(&xDma);
if (rxRing == nullptr)
throw RuntimeError("RxRing was null.");
XAxiDma_Bd *bd;
ret = XAxiDma_BdRingAlloc(rxRing, 1, &bd);
ret = XAxiDma_BdRingAlloc(rxRing, readCoalesce, &bd);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to alloc BD in RX ring: {}", ret);
ret = XAxiDma_BdSetBufAddr(bd, (uintptr_t) buf);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to set buffer address {:x} on BD {:x}: {}", (uintptr_t) buf, (uintptr_t) bd, ret);
auto curBd = bd;
char* curBuf = (char*)buf;
for (size_t i = 0; i < readCoalesce; i++) {
ret = XAxiDma_BdSetBufAddr(curBd, (uintptr_t) curBuf);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to set buffer address {:x} on BD {:x}: {}", (uintptr_t) buf, (uintptr_t) bd, ret);
ret = XAxiDma_BdSetLength(bd, len, rxRing->MaxTransferLen);
if (ret != XST_SUCCESS)
throw RuntimeError("Rx set length {} on BD {:x} failed {}", len, (uintptr_t) bd, ret);
ret = XAxiDma_BdSetLength(curBd, readMsgSize, rxRing->MaxTransferLen);
if (ret != XST_SUCCESS)
throw RuntimeError("Rx set length {} on BD {:x} failed {}", len, (uintptr_t) bd, ret);
// Receive BDs do not need to set anything for the control
// The hardware will set the SOF/EOF bits per stream status
XAxiDma_BdSetCtrl(bd, 0);
ret = XAxiDma_BdRingSetCoalesce(rxRing, 1, 0);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to set coalesce settings: {}.", ret);
// Receive BDs do not need to set anything for the control
// The hardware will set the SOF/EOF bits per stream status
XAxiDma_BdSetCtrl(curBd, 0);
ret = XAxiDma_BdRingToHw(rxRing, 1, bd);
// TODO: Check if we really need this
XAxiDma_BdSetId(curBd, (uintptr_t) buf);
curBuf += readMsgSize;
curBd = (XAxiDma_Bd *)XAxiDma_BdRingNext(rxRing, curBd);
}
ret = XAxiDma_BdRingToHw(rxRing, readCoalesce, bd);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to submit BD to RX ring: {}", ret);
@ -388,15 +396,38 @@ Dma::readCompleteScatterGather()
size_t bytesRead = 0;
// Wait until the data has been received by the RX channel.
if ((processedBds = XAxiDma_BdRingFromHw(rxRing, 1, &bd)) == 0)
if ((processedBds = XAxiDma_BdRingFromHw(rxRing, readCoalesce, &bd)) < readCoalesce)
{
/*auto intrNum = */irqs[s2mmInterrupt].irqController->waitForInterrupt(irqs[s2mmInterrupt].num);
//logger->info("Got {} interrupts (id: {}) from write channel", intrNum, irqs[mm2sInterrupt].num);
processedBds = XAxiDma_BdRingFromHw(rxRing, 1, &bd);
if (processedBds != 0) {
//Ignore partial batches
logger->warn("Ignoring partial batch of {} BDs.", processedBds);
ret = XAxiDma_BdRingFree(rxRing, processedBds, bd);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to free {} RX BDs {}", processedBds, ret);
}
//auto intrNum =
irqs[s2mmInterrupt].irqController->waitForInterrupt(irqs[s2mmInterrupt].num);
//If we got a partial batch on the first call, we have to receive up to readCoalesce*2
//to make sure we get a full batch of readCoalesce messages
processedBds = XAxiDma_BdRingFromHw(rxRing, readCoalesce*2, &bd);
}
if(processedBds < readCoalesce) {
// We got less than we expected. We already tried two times so let's give up.
throw RuntimeError("Read only {} BDs, expected {}.", processedBds, readCoalesce);
} else if(processedBds > readCoalesce) {
// If the first try was a partial batch, we receive two batches on the second try
// We ignore the first batch and only process the second one
while (processedBds > readCoalesce) {
bd = (XAxiDma_Bd *) XAxiDma_BdRingNext(rxRing, bd);
processedBds--;
}
ret = XAxiDma_BdRingFree(rxRing, processedBds-readCoalesce, bd);
if (ret != XST_SUCCESS)
throw RuntimeError("Failed to free {} RX BDs {}", processedBds, ret);
}
// At this point we have exactly readCoalesce BDs.
// Acknowledge the interrupt
// Acknowledge the interrupt. Has no effect if no interrupt has occured.
auto irqStatus = XAxiDma_BdRingGetIrq(rxRing);
XAxiDma_BdRingAckIrq(rxRing, irqStatus);
@ -623,6 +654,6 @@ DmaFactory::configureJson(Core& ip, json_t* json)
logger->error("Failed to parse DMA configuration");
return false;
}
dma.configDone = true;
return true;
}

View file

@ -124,14 +124,17 @@ int main(int argc, char* argv[])
// std::cerr << std::hex << mem[i] << ";";
//std::cerr << std::endl;
while (bytesRead >= 8) {
int64_t ival = (int64_t)(mem[1] & 0xFFFF) << 48 |
for (size_t i = 0; i*4 < bytesRead; i++) {
int32_t ival = mem[i++];
float dval = *((float*)(&ival));
std::cerr << std::hex << ival << "," << dval << std::endl;
/*int64_t ival = (int64_t)(mem[1] & 0xFFFF) << 48 |
(int64_t)(mem[1] & 0xFFFF0000) << 16 |
(int64_t)(mem[0] & 0xFFFF) << 16 |
(int64_t)(mem[0] & 0xFFFF0000) >> 16;
double dval = *((double*)(&ival));
std::cerr << std::hex << ival << "," << dval << std::endl;
bytesRead -= 8;
bytesRead -= 8;*/
//logger->info("Read value: {}", dval);
}
}