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:
parent
edb996ebfd
commit
7c6d350eb0
4 changed files with 167 additions and 146 deletions
|
@ -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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Add table
Reference in a new issue