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

format and increase robustness of interuppt handling

Signed-off-by: Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
This commit is contained in:
Niklas Eiling 2024-01-26 13:08:51 +01:00 committed by Niklas Eiling
parent edb996ebfd
commit 7c6d350eb0
4 changed files with 167 additions and 146 deletions

View file

@ -18,32 +18,23 @@ namespace ip {
class InterruptController : public Core {
public:
using IrqMaskType = uint32_t;
static constexpr int maxIrqs = 32;
using IrqMaskType = uint32_t;
static constexpr int maxIrqs = 32;
virtual
~InterruptController();
virtual ~InterruptController();
virtual
bool init() override;
virtual bool init() override;
bool enableInterrupt(IrqMaskType mask, bool polling);
bool enableInterrupt(IrqPort irq, bool polling)
{
return enableInterrupt(1 << irq.num, polling);
}
bool enableInterrupt(IrqMaskType mask, bool polling);
bool enableInterrupt(IrqPort irq, bool polling) {
return enableInterrupt(1 << irq.num, polling);
}
bool disableInterrupt(IrqMaskType mask);
bool disableInterrupt(IrqPort irq)
{
return disableInterrupt(1 << irq.num);
}
bool disableInterrupt(IrqMaskType mask);
bool disableInterrupt(IrqPort irq) { return disableInterrupt(1 << irq.num); }
int waitForInterrupt(int irq);
int waitForInterrupt(IrqPort irq)
{
return waitForInterrupt(irq.num);
}
ssize_t waitForInterrupt(int irq);
ssize_t waitForInterrupt(IrqPort irq) { return waitForInterrupt(irq.num); }
private:

View file

@ -381,120 +381,135 @@ bool Dma::readScatterGather(void *buf, size_t len)
Dma::Completion Dma::writeCompleteScatterGather()
{
Completion c;
XAxiDma_Bd *bd = nullptr, *curBd;
auto txRing = XAxiDma_GetTxRing(&xDma);
int ret = XST_FAILURE;
static size_t errcnt = 32;
Completion c;
XAxiDma_Bd *bd = nullptr, *curBd;
auto txRing = XAxiDma_GetTxRing(&xDma);
int ret = XST_FAILURE;
static size_t errcnt = 32;
c.interrupts = irqs[mm2sInterrupt].irqController->waitForInterrupt(irqs[mm2sInterrupt].num);
c.interrupts = irqs[mm2sInterrupt].irqController->waitForInterrupt(
irqs[mm2sInterrupt].num);
hwLock.lock();
if ((c.bds = XAxiDma_BdRingFromHw(txRing, writeCoalesce, &bd)) < writeCoalesce)
{
logger->warn("Send partial batch of {}/{} BDs.", c.bds, writeCoalesce);
if(errcnt-- == 0) {
hwLock.unlock();
throw RuntimeError("too many partial batches");
}
}
hwLock.lock();
if ((c.bds = XAxiDma_BdRingFromHw(txRing, writeCoalesce, &bd)) <
writeCoalesce) {
logger->warn("Send partial batch of {}/{} BDs.", c.bds, writeCoalesce);
if (errcnt-- == 0) {
hwLock.unlock();
throw RuntimeError("too many partial batches");
}
}
// Acknowledge the interrupt
auto irqStatus = XAxiDma_BdRingGetIrq(txRing);
XAxiDma_BdRingAckIrq(txRing, irqStatus);
// Acknowledge the interrupt
auto irqStatus = XAxiDma_BdRingGetIrq(txRing);
XAxiDma_BdRingAckIrq(txRing, irqStatus);
if (c.bds == 0) {
c.bytes = 0;
hwLock.unlock();
return c;
}
if (c.bds == 0) {
c.bytes = 0;
hwLock.unlock();
return c;
}
if (bd == nullptr) {
hwLock.unlock();
throw RuntimeError("Bd was null.");
}
if (bd == nullptr) {
hwLock.unlock();
throw RuntimeError("Bd was null.");
}
curBd = bd;
for (size_t i = 0; i < c.bds; i++) {
ret = XAxiDma_BdGetSts(curBd);
if ((ret & XAXIDMA_BD_STS_ALL_ERR_MASK) || (!(ret & XAXIDMA_BD_STS_COMPLETE_MASK))) {
hwLock.unlock();
throw RuntimeError("Bd Status register shows error: {}", ret);
break;
}
curBd = bd;
for (size_t i = 0; i < c.bds; i++) {
ret = XAxiDma_BdGetSts(curBd);
if ((ret & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
(!(ret & XAXIDMA_BD_STS_COMPLETE_MASK))) {
hwLock.unlock();
throw RuntimeError("Bd Status register shows error: {}", ret);
}
c.bytes += XAxiDma_BdGetLength(bd, txRing->MaxTransferLen);
curBd = (XAxiDma_Bd *) XAxiDma_BdRingNext(txRing, curBd);
}
c.bytes += XAxiDma_BdGetLength(bd, txRing->MaxTransferLen);
curBd = (XAxiDma_Bd *)XAxiDma_BdRingNext(txRing, curBd);
}
ret = XAxiDma_BdRingFree(txRing, c.bds, bd);
if (ret != XST_SUCCESS) {
hwLock.unlock();
throw RuntimeError("Failed to free {} TX BDs {}", c.bds, ret);
}
ret = XAxiDma_BdRingFree(txRing, c.bds, bd);
if (ret != XST_SUCCESS) {
hwLock.unlock();
throw RuntimeError("Failed to free {} TX BDs {}", c.bds, ret);
}
hwLock.unlock();
return c;
hwLock.unlock();
return c;
}
Dma::Completion Dma::readCompleteScatterGather()
{
Completion c;
XAxiDma_Bd *bd = nullptr, *curBd;
auto rxRing = XAxiDma_GetRxRing(&xDma);
int ret = XST_FAILURE;
static size_t errcnt = 32;
Completion c;
XAxiDma_Bd *bd = nullptr, *curBd;
auto rxRing = XAxiDma_GetRxRing(&xDma);
int ret = XST_FAILURE;
static size_t errcnt = 32;
c.interrupts = irqs[s2mmInterrupt].irqController->waitForInterrupt(irqs[s2mmInterrupt].num);
ssize_t intrs = irqs[s2mmInterrupt].irqController->waitForInterrupt(
irqs[s2mmInterrupt].num);
hwLock.lock();
// Wait until the data has been received by the RX channel.
if ((c.bds = XAxiDma_BdRingFromHw(rxRing, readCoalesce, &bd)) < readCoalesce)
{
logger->warn("Got partial batch of {}/{} BDs.", c.bds, readCoalesce);
if(errcnt-- == 0) {
hwLock.unlock();
throw RuntimeError("too many partial batches");
}
}
if (intrs < 0) {
logger->warn("Interrupt error or timeout");
c.interrupts = 0;
return c;
} else {
c.interrupts = intrs;
}
hwLock.lock();
auto irqStatus = XAxiDma_BdRingGetIrq(rxRing);
XAxiDma_BdRingAckIrq(rxRing, irqStatus);
if (!(irqStatus & XAXIDMA_IRQ_IOC_MASK)) {
logger->error("Expected IOC interrupt but IRQ status is: {:#x}", irqStatus);
return c;
}
// logger->trace("Read IRQ status: {:#x}", irqStatus);
// Acknowledge the interrupt. Has no effect if no interrupt has occured.
auto irqStatus = XAxiDma_BdRingGetIrq(rxRing);
XAxiDma_BdRingAckIrq(rxRing, irqStatus);
// Wait until the data has been received by the RX channel.
if ((c.bds = XAxiDma_BdRingFromHw(rxRing, readCoalesce, &bd)) <
readCoalesce) {
logger->warn("Got partial batch of {}/{} BDs.", c.bds, readCoalesce);
if (errcnt-- == 0) {
hwLock.unlock();
throw RuntimeError("too many partial batches");
}
}
if (c.bds == 0) {
c.bytes = 0;
hwLock.unlock();
return c;
}
if (c.bds == 0) {
c.bytes = 0;
hwLock.unlock();
return c;
}
if (bd == nullptr) {
hwLock.unlock();
throw RuntimeError("Bd was null.");
}
if (bd == nullptr) {
hwLock.unlock();
throw RuntimeError("Bd was null.");
}
curBd = bd;
for (size_t i = 0; i < c.bds; i++) {
ret = XAxiDma_BdGetSts(curBd);
if ((ret & XAXIDMA_BD_STS_ALL_ERR_MASK) || (!(ret & XAXIDMA_BD_STS_COMPLETE_MASK))) {
hwLock.unlock();
throw RuntimeError("Bd Status register shows error: {}", ret);
break;
}
curBd = bd;
for (size_t i = 0; i < c.bds; i++) {
// logger->trace("Read BD {}/{}: {:#x}", i, c.bds,
// XAxiDma_BdGetBufAddr(curBd));
ret = XAxiDma_BdGetSts(curBd);
if ((ret & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
(!(ret & XAXIDMA_BD_STS_COMPLETE_MASK))) {
hwLock.unlock();
throw RuntimeError("Bd Status register shows error: {}", ret);
}
c.bytes += XAxiDma_BdGetActualLength(bd, rxRing->MaxTransferLen);
curBd = (XAxiDma_Bd *) XAxiDma_BdRingNext(rxRing, curBd);
}
c.bytes += XAxiDma_BdGetActualLength(bd, rxRing->MaxTransferLen);
curBd = (XAxiDma_Bd *)XAxiDma_BdRingNext(rxRing, curBd);
}
// Free all processed RX BDs for future transmission.
ret = XAxiDma_BdRingFree(rxRing, c.bds, bd);
if (ret != XST_SUCCESS) {
hwLock.unlock();
throw RuntimeError("Failed to free {} TX BDs {}.", c.bds, ret);
}
// Free all processed RX BDs for future transmission.
ret = XAxiDma_BdRingFree(rxRing, c.bds, bd);
if (ret != XST_SUCCESS) {
hwLock.unlock();
throw RuntimeError("Failed to free {} TX BDs {}.", c.bds, ret);
}
hwLock.unlock();
return c;
hwLock.unlock();
return c;
}
bool Dma::writeSimple(const void *buf, size_t len)

View file

@ -117,38 +117,52 @@ InterruptController::disableInterrupt(InterruptController::IrqMaskType mask)
return true;
}
int
InterruptController::waitForInterrupt(int irq)
{
assert(irq < maxIrqs);
ssize_t InterruptController::waitForInterrupt(int irq) {
assert(irq < maxIrqs);
const uintptr_t base = getBaseAddr(registerMemory);
const uintptr_t base = getBaseAddr(registerMemory);
if (this->polling[irq]) {
uint32_t isr, mask = 1 << irq;
if (this->polling[irq]) {
uint32_t isr, mask = 1 << irq;
do {
// Poll status register
isr = XIntc_In32(base + XIN_ISR_OFFSET);
pthread_testcancel();
} while ((isr & mask) != mask);
do {
// Poll status register
isr = XIntc_In32(base + XIN_ISR_OFFSET);
pthread_testcancel();
} while ((isr & mask) != mask);
// Acknowledge interrupt
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
// Acknowledge interrupt
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
// We can only tell that there has been (at least) one interrupt
return 1;
}
else {
uint64_t count;
// We can only tell that there has been (at least) one interrupt
return 1;
} else {
uint64_t count;
int sret;
fd_set rfds;
struct timeval tv = {.tv_sec = 1, .tv_usec = 0};
FD_ZERO(&rfds);
FD_SET(efds[irq], &rfds);
// Block until there has been an interrupt, read number of interrupts
ssize_t ret = read(efds[irq], &count, sizeof(count));
if (ret != sizeof(count))
return -1;
sret = select(efds[irq] + 1, &rfds, NULL, NULL, &tv);
if (sret == -1) {
logger->error("select() failed: {}", strerror(errno));
return -1;
} else if (sret == 0) {
logger->warn("timeout waiting for interrupt {}", irq);
return -1;
}
// Block until there has been an interrupt, read number of interrupts
ssize_t ret = read(efds[irq], &count, sizeof(count));
if (ret != sizeof(count)) {
logger->error("Failed to read from eventfd: {}", strerror(errno));
return -1;
}
return static_cast<int>(count);
}
logger->debug("Received {} interrupts on {}", count, irq);
return count;
}
}
static char n[] = "intc";

View file

@ -121,6 +121,7 @@ void writeToDmaFromStdIn(std::shared_ptr<villas::fpga::ip::Dma> dma) {
void readFromDmaToStdOut(
std::shared_ptr<villas::fpga::ip::Dma> dma,
std::unique_ptr<fpga::BufferedSampleFormatter> formatter) {
auto &alloc = villas::HostRam::getAllocator();
const std::shared_ptr<villas::MemoryBlock> block[] = {
@ -138,19 +139,19 @@ void readFromDmaToStdOut(
// Setup read transfer
dma->read(*block[0], block[0]->getSize());
int cnt = 0;
while (true) {
logger->trace("Read from stream and write to address {}:{:p}",
block[next]->getAddrSpaceId(), block[next]->getOffset());
SPDLOG_TRACE("Read from stream and write to address {}:{:#x}",
block[next]->getAddrSpaceId(), block[next]->getOffset());
// We could use the number of interrupts to determine if we missed a chunk of data
dma->read(*block[next], block[next]->getSize());
auto c = dma->readComplete();
if (c.interrupts > 1) {
logger->warn("Missed {} interrupts", c.interrupts - 1);
SPDLOG_WARN("Missed {} interrupts", c.interrupts - 1);
}
logger->debug("bytes: {}, intrs: {}, bds: {}", c.bytes, c.interrupts,
c.bds);
SPDLOG_TRACE("bytes: {}, intrs: {}, bds: {}", c.bytes, c.interrupts, c.bds);
try {
for (size_t i = 0; i * 4 < c.bytes; i++) {
int32_t ival = mem[cur][i];
@ -160,11 +161,11 @@ void readFromDmaToStdOut(
*((float *)(&ival)); // cppcheck-suppress invalidPointerCast
#pragma GCC diagnostic pop
formatter->format(fval);
printf("%#x\n", ival);
printf("%d: %#x\n", cnt++, ival);
}
formatter->output(std::cout);
} catch (const std::exception &e) {
logger->warn("Failed to output data: {}", e.what());
SPDLOG_WARN("Failed to output data: {}", e.what());
}
cur = next;
@ -202,7 +203,7 @@ int main(int argc, char *argv[]) {
// Logging setup
logging.setLevel(spdlog::level::debug);
logging.setLevel(spdlog::level::trace);
fpga::setupColorHandling();
if (configFile.empty()) {