From 95d2bc08874a28771ef0453674ac031500f058cd Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 27 Oct 2022 10:49:52 +0200 Subject: [PATCH] first IRQ impl --- bsp_q7s/boardconfig/busConf.h | 2 +- bsp_q7s/core/ObjectFactory.cpp | 7 +- linux/obc/PdecHandler.cpp | 110 ++++++++++++++++++++--- linux/obc/PdecHandler.h | 158 +++++++++++++-------------------- linux/obc/pdec.h | 59 ++++++++++++ 5 files changed, 223 insertions(+), 113 deletions(-) create mode 100644 linux/obc/pdec.h diff --git a/bsp_q7s/boardconfig/busConf.h b/bsp_q7s/boardconfig/busConf.h index 2fc1963c..1db53d3e 100644 --- a/bsp_q7s/boardconfig/busConf.h +++ b/bsp_q7s/boardconfig/busConf.h @@ -21,7 +21,7 @@ static constexpr char UIO_PDEC_REGISTERS[] = "/dev/uio_pdec_regs"; static constexpr char UIO_PTME[] = "/dev/uio_ptme"; static constexpr char UIO_PDEC_CONFIG_MEMORY[] = "/dev/uio_pdec_cfg_mem"; static constexpr char UIO_PDEC_RAM[] = "/dev/uio_pdec_ram"; -static constexpr char UIO_PDEC_IRQ = "/dev/uio_pdec_irq"; +static constexpr char UIO_PDEC_IRQ[] = "/dev/uio_pdec_irq"; static constexpr int MAP_ID_PTME_CONFIG = 3; namespace uiomapids { diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp index 82d066c8..2294631c 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/core/ObjectFactory.cpp @@ -773,8 +773,13 @@ void ObjectFactory::createCcsdsComponents(LinuxLibgpioIF* gpioComIF) { Levels::LOW); gpioCookiePdec->addGpio(gpioIds::PDEC_RESET, gpio); gpioChecker(gpioComIF->addGpios(gpioCookiePdec), "PDEC"); + struct UioNames uioNames {}; + uioNames.configMemory = q7s::UIO_PDEC_CONFIG_MEMORY; + uioNames.ramMemory = q7s::UIO_PDEC_RAM; + uioNames.registers = q7s::UIO_PDEC_REGISTERS; + uioNames.irq = q7s::UIO_PDEC_IRQ; new PdecHandler(objects::PDEC_HANDLER, objects::CCSDS_HANDLER, gpioComIF, gpioIds::PDEC_RESET, - q7s::UIO_PDEC_CONFIG_MEMORY, q7s::UIO_PDEC_RAM, q7s::UIO_PDEC_REGISTERS); + uioNames); GpioCookie* gpioRS485Chip = new GpioCookie; gpio = new GpiodRegularByLineName(q7s::gpioNames::RS485_EN_TX_CLOCK, "RS485 Transceiver", Direction::OUT, Levels::LOW); diff --git a/linux/obc/PdecHandler.cpp b/linux/obc/PdecHandler.cpp index 63aedbce..1530a59a 100644 --- a/linux/obc/PdecHandler.cpp +++ b/linux/obc/PdecHandler.cpp @@ -1,7 +1,9 @@ #include "PdecHandler.h" #include +#include #include +#include #include #include @@ -12,18 +14,21 @@ #include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/tmtcservices/TmTcMessage.h" #include "fsfw_hal/linux/uio/UioMapper.h" +#include "pdec.h" + +using namespace pdec; + +// If this is ever shared, protect it with a mutex! +uint32_t PdecHandler::CURRENT_FAR = 0; PdecHandler::PdecHandler(object_id_t objectId, object_id_t tcDestinationId, - LinuxLibgpioIF* gpioComIF, gpioId_t pdecReset, std::string uioConfigMemory, - std::string uioRamMemory, std::string uioRegisters) + LinuxLibgpioIF* gpioComIF, gpioId_t pdecReset, UioNames names) : SystemObject(objectId), tcDestinationId(tcDestinationId), gpioComIF(gpioComIF), pdecReset(pdecReset), - uioConfigMemory(uioConfigMemory), - uioRamMemory(uioRamMemory), - uioRegisters(uioRegisters), - actionHelper(this, nullptr) { + actionHelper(this, nullptr), + uioNames(names) { auto mqArgs = MqArgs(objectId, static_cast(this)); commandQueue = QueueFactory::instance()->createMessageQueue( QUEUE_SIZE, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs); @@ -47,22 +52,27 @@ ReturnValue_t PdecHandler::initialize() { ReturnValue_t result = returnvalue::OK; - UioMapper regMapper(uioRegisters); + UioMapper regMapper(uioNames.registers); result = regMapper.getMappedAdress(®isterBaseAddress, UioMapper::Permissions::READ_WRITE); if (result != returnvalue::OK) { return ObjectManagerIF::CHILD_INIT_FAILED; } - UioMapper configMemMapper(uioConfigMemory); + UioMapper configMemMapper(uioNames.configMemory); result = configMemMapper.getMappedAdress(&memoryBaseAddress, UioMapper::Permissions::READ_WRITE); if (result != returnvalue::OK) { return ObjectManagerIF::CHILD_INIT_FAILED; } - UioMapper ramMapper(uioRamMemory); + UioMapper ramMapper(uioNames.ramMemory); result = ramMapper.getMappedAdress(&ramBaseAddress, UioMapper::Permissions::READ_WRITE); if (result != returnvalue::OK) { return ObjectManagerIF::CHILD_INIT_FAILED; } + if (OP_MODE == Modes::IRQ and uioNames.irq == nullptr) { + sif::error << "Can not use IRQ mode if IRQ UIO name is invalid" << std::endl; + return returnvalue::FAILED; + } + writePdecConfig(); result = releasePdec(); @@ -110,7 +120,7 @@ ReturnValue_t PdecHandler::polledOperation() { case State::WAIT_FOR_RECOVERY: break; default: - sif::debug << "PdecHandler::performOperation: Invalid state" << std::endl; + sif::error << "PdecHandler::performOperation: Invalid state" << std::endl; break; } @@ -118,8 +128,76 @@ ReturnValue_t PdecHandler::polledOperation() { } ReturnValue_t PdecHandler::irqOperation() { + ReturnValue_t result = returnvalue::OK; + int fd = open(uioNames.irq, O_RDWR); + if (fd < 0) { + sif::error << "PdecHandler::irqOperation: Opening UIO IRQ file" << uioNames.irq << " failed" + << std::endl; + return returnvalue::FAILED; + } + + struct pollfd fds = {.fd = fd, .events = POLLIN, .revents = 0}; + // Used to unmask IRQ + uint32_t info = 1; + ssize_t nb = 0; + int ret = 0; + while (true) { readCommandQueue(); + switch (state) { + case State::INIT: + resetFarStatFlag(); + if (result != returnvalue::OK) { + // Requires reconfiguration and reinitialization of PDEC + triggerEvent(INVALID_FAR); + state = State::WAIT_FOR_RECOVERY; + return result; + } + state = State::RUNNING; + break; + case State::RUNNING: { + // TODO: Add poll() based IRQ handling + nb = write(fd, &info, sizeof(info)); + if (nb != static_cast(sizeof(info))) { + sif::error << "PdecHandler::irqOperation: Unmasking IRQ failed" << std::endl; + close(fd); + } + + ret = poll(&fds, 1, IRQ_TIMEOUT_MS); + if (ret == 0) { + // No TCs for timeout period + checkLocks(); + } else if (ret >= 1) { + nb = read(fd, &info, sizeof(info)); + if (nb == static_cast(sizeof(info))) { + uint32_t pisr = *(registerBaseAddress + PDEC_PISR_OFFSET); + if ((pisr & TC_NEW_MASK) == TC_NEW_MASK) { + // handle TC + handleNewTc(); + } + if ((pisr & TC_ABORT_MASK) == TC_ABORT_MASK) { + tcAbortCounter += 1; + } + if ((pisr & NEW_FAR_MASK) == NEW_FAR_MASK) { + // Read FAR here + CURRENT_FAR = readFar(); + } + // Clear interrupts with dummy read + ret = *(registerBaseAddress + PDEC_PIR_OFFSET); + } + } else { + sif::error << "PdecHandler::irqOperation: Poll error with errno " << errno << ": " + << strerror(errno) << std::endl; + triggerEvent(POLL_ERROR_PDEC, errno); + } + break; + } + case State::WAIT_FOR_RECOVERY: + break; + default: + sif::error << "PdecHandler::performOperation: Invalid state" << std::endl; + break; + } } return returnvalue::OK; } @@ -151,7 +229,7 @@ void PdecHandler::writePdecConfig() { if (OP_MODE == Modes::IRQ) { // Configure interrupt mask register to enable interrupts - *(memoryBaseAddress + PDEC_IMR_OFFSET) = pdecConfig.getImrReg(); + *(registerBaseAddress + PDEC_IMR_OFFSET) = pdecConfig.getImrReg(); } // Configure all MAP IDs as invalid @@ -173,17 +251,19 @@ void PdecHandler::writePdecConfig() { } ReturnValue_t PdecHandler::resetFarStatFlag() { - uint32_t pdecFar = *(registerBaseAddress + PDEC_FAR_OFFSET); + uint32_t pdecFar = readFar(); if (pdecFar != FAR_RESET) { sif::warning << "PdecHandler::resetFarStatFlag: FAR register did not match expected value." << " Read value: 0x" << std::hex << static_cast(pdecFar) << std::endl; + CURRENT_FAR = pdecFar; return returnvalue::FAILED; } #if OBSW_DEBUG_PDEC_HANDLER == 1 sif::debug << "PdecHandler::resetFarStatFlag: read FAR with value: 0x" << std::hex << pdecFar << std::endl; #endif /* OBSW_DEBUG_PDEC_HANDLER == 1 */ + CURRENT_FAR = pdecFar; return returnvalue::OK; } @@ -197,12 +277,14 @@ ReturnValue_t PdecHandler::releasePdec() { } bool PdecHandler::newTcReceived() { - uint32_t pdecFar = *(registerBaseAddress + PDEC_FAR_OFFSET); + uint32_t pdecFar = readFar(); if (pdecFar >> STAT_POSITION != NEW_FAR_RECEIVED) { + CURRENT_FAR = pdecFar; return false; } if (!checkFrameAna(pdecFar)) { + CURRENT_FAR = pdecFar; return false; } return true; @@ -519,6 +601,8 @@ void PdecHandler::printPdecMon() { sif::info << std::setw(30) << std::left << "Start sequence lock: " << lock << std::endl; } +uint32_t PdecHandler::readFar() { return *(registerBaseAddress + PDEC_FAR_OFFSET); } + std::string PdecHandler::getMonStatusString(uint32_t status) { switch (status) { case TC_CHANNEL_INACTIVE: diff --git a/linux/obc/PdecHandler.h b/linux/obc/PdecHandler.h index d003bae2..aad3cbdf 100644 --- a/linux/obc/PdecHandler.h +++ b/linux/obc/PdecHandler.h @@ -13,6 +13,13 @@ #include "fsfw_hal/common/gpio/gpioDefinitions.h" #include "fsfw_hal/linux/gpio/LinuxLibgpioIF.h" +struct UioNames { + const char* configMemory; + const char* ramMemory; + const char* registers; + const char* irq; +}; + /** * @brief This class controls the PDEC IP Core implemented in the programmable logic of the * Zynq-7020. All registers and memories of the PDEC IP Core are accessed via UIO @@ -33,6 +40,8 @@ */ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasActionsIF { public: + static constexpr dur_millis_t IRQ_TIMEOUT_MS = 500; + enum class Modes { POLLED, IRQ }; /** @@ -45,8 +54,7 @@ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasAc * @param uioregsiters String of uio device file same mapped to the PDEC register space */ PdecHandler(object_id_t objectId, object_id_t tcDestinationId, LinuxLibgpioIF* gpioComIF, - gpioId_t pdecReset, std::string uioConfigMemory, std::string uioRamMemory, - std::string uioRegisters); + gpioId_t pdecReset, UioNames names); virtual ~PdecHandler(); @@ -76,6 +84,7 @@ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasAc static const Event LOST_CARRIER_LOCK_PDEC = MAKE_EVENT(5, severity::INFO); //! [EXPORT] : [COMMENT] Lost bit lock static const Event LOST_BIT_LOCK_PDEC = MAKE_EVENT(6, severity::INFO); + static constexpr Event POLL_ERROR_PDEC = event::makeEvent(SUBSYSTEM_ID, 7, severity::MEDIUM); private: static const uint8_t INTERFACE_ID = CLASS_ID::PDEC_HANDLER; @@ -116,50 +125,6 @@ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasAc // Print PDEC monitor register static const ActionId_t PRINT_PDEC_MON = 1; - static const uint8_t STAT_POSITION = 31; - static const uint8_t FRAME_ANA_POSITION = 28; - static const uint8_t IREASON_POSITION = 25; - - static const uint8_t NEW_FAR_RECEIVED = 0; - - static const uint32_t FRAME_ANA_MASK = 0x70000000; - static const uint32_t IREASON_MASK = 0x0E000000; - - static const uint32_t TC_CHANNEL_INACTIVE = 0x0; - static const uint32_t TC_CHANNEL_ACTIVE = 0x1; - static const uint32_t TC_CHANNEL_TIMEDOUT = 0x2; - - static const uint32_t TC0_STATUS_MASK = 0x3; - static const uint32_t TC1_STATUS_MASK = 0xC; - static const uint32_t TC2_STATUS_MASK = 0x300; - static const uint32_t TC3_STATUS_MASK = 0xC00; - static const uint32_t TC4_STATUS_MASK = 0x30000; - static const uint32_t TC5_STATUS_MASK = 0xc00000; - // Lock register set to 1 when start sequence has been found (CLTU is beeing processed) - static const uint32_t LOCK_MASK = 0xc00000; - - static const uint32_t TC0_STATUS_POS = 0; - static const uint32_t TC1_STATUS_POS = 2; - static const uint32_t TC2_STATUS_POS = 4; - static const uint32_t TC3_STATUS_POS = 6; - static const uint32_t TC4_STATUS_POS = 8; - static const uint32_t TC5_STATUS_POS = 10; - // Lock register set to 1 when start sequence has been found (CLTU is beeing processed) - static const uint32_t LOCK_POS = 12; - - /** - * UIO is 4 byte aligned. Thus offset is calculated with "true offset" / 4 - * Example: PDEC_FAR = 0x2840 => Offset in virtual address space is 0xA10 - */ - static constexpr uint32_t PDEC_PIR_OFFSET = 0xA03; - static constexpr uint32_t PDEC_IMR_OFFSET = 0xA04; - static const uint32_t PDEC_FAR_OFFSET = 0xA10; - static const uint32_t PDEC_CLCW_OFFSET = 0xA12; - static const uint32_t PDEC_BFREE_OFFSET = 0xA24; - static const uint32_t PDEC_BPTR_OFFSET = 0xA25; - static const uint32_t PDEC_SLEN_OFFSET = 0xA26; - static const uint32_t PDEC_MON_OFFSET = 0xA27; - #ifdef TE0720_1CFA static const int CONFIG_MEMORY_MAP_SIZE = 0x400; static const int RAM_MAP_SIZE = 0x4000; @@ -233,6 +198,53 @@ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasAc enum class State : uint8_t { INIT, RUNNING, WAIT_FOR_RECOVERY }; + static uint32_t CURRENT_FAR; + + object_id_t tcDestinationId; + + AcceptsTelecommandsIF* tcDestination = nullptr; + + LinuxLibgpioIF* gpioComIF = nullptr; + + /** + * Reset signal is required to hold PDEC in reset state until the configuration has been + * written to the appropriate memory space. + * Can also be used to reboot PDEC in case of erros. + */ + gpioId_t pdecReset = gpio::NO_GPIO; + + uint32_t tcAbortCounter = 0; + + ActionHelper actionHelper; + + StorageManagerIF* tcStore = nullptr; + + MessageQueueIF* commandQueue = nullptr; + + State state = State::INIT; + + /** + * Pointer pointing to base address of the PDEC memory space. + * This address is equivalent with the base address of the section named configuration area in + * the PDEC datasheet. + */ + uint32_t* memoryBaseAddress = nullptr; + + uint32_t* ramBaseAddress = nullptr; + + // Pointer pointing to base address of register space + uint32_t* registerBaseAddress = nullptr; + + uint8_t tcSegment[TC_SEGMENT_LEN]; + + // Used to check carrier and bit lock changes (default set to no rf and no bitlock) + uint32_t lastClcw = 0xC000; + + bool carrierLock = false; + bool bitLock = false; + + UioNames uioNames; + /** * @brief Reads and handles messages stored in the commandQueue */ @@ -241,6 +253,8 @@ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasAc ReturnValue_t polledOperation(); ReturnValue_t irqOperation(); + uint32_t readFar(); + /** * @brief This functions writes the configuration parameters to the configuration * section of the PDEC. @@ -352,58 +366,6 @@ class PdecHandler : public SystemObject, public ExecutableObjectIF, public HasAc void printPdecMon(); std::string getMonStatusString(uint32_t status); - - object_id_t tcDestinationId; - - AcceptsTelecommandsIF* tcDestination = nullptr; - - LinuxLibgpioIF* gpioComIF = nullptr; - - /** - * Reset signal is required to hold PDEC in reset state until the configuration has been - * written to the appropriate memory space. - * Can also be used to reboot PDEC in case of erros. - */ - gpioId_t pdecReset = gpio::NO_GPIO; - - // UIO device file giving access to the PDEC configuration memory section - std::string uioConfigMemory; - - // UIO device file giving access to the PDEC RAM section - std::string uioRamMemory; - - // UIO device file giving access to the PDEC register space - std::string uioRegisters; - - ActionHelper actionHelper; - - StorageManagerIF* tcStore = nullptr; - - MessageQueueIF* commandQueue = nullptr; - - State state = State::INIT; - - /** - * Pointer pointing to base address of the PDEC memory space. - * This address is equivalent with the base address of the section named configuration area in - * the PDEC datasheet. - */ - uint32_t* memoryBaseAddress = nullptr; - - uint32_t* ramBaseAddress = nullptr; - - // Pointer pointing to base address of register space - uint32_t* registerBaseAddress = nullptr; - - uint32_t pdecFar = 0; - - uint8_t tcSegment[TC_SEGMENT_LEN]; - - // Used to check carrier and bit lock changes (default set to no rf and no bitlock) - uint32_t lastClcw = 0xC000; - - bool carrierLock = false; - bool bitLock = false; }; #endif /* LINUX_OBC_PDECHANDLER_H_ */ diff --git a/linux/obc/pdec.h b/linux/obc/pdec.h new file mode 100644 index 00000000..bc82d61d --- /dev/null +++ b/linux/obc/pdec.h @@ -0,0 +1,59 @@ +#ifndef LINUX_OBC_PDEC_H_ +#define LINUX_OBC_PDEC_H_ + +#include + +namespace pdec { + +static const uint8_t STAT_POSITION = 31; +static const uint8_t FRAME_ANA_POSITION = 28; +static const uint8_t IREASON_POSITION = 25; + +static const uint8_t NEW_FAR_RECEIVED = 0; + +static constexpr uint32_t NEW_FAR_MASK = 1 << 2; +static constexpr uint32_t TC_ABORT_MASK = 1 << 1; +static constexpr uint32_t TC_NEW_MASK = 1 << 0; + +static const uint32_t FRAME_ANA_MASK = 0x70000000; +static const uint32_t IREASON_MASK = 0x0E000000; + +static const uint32_t TC_CHANNEL_INACTIVE = 0x0; +static const uint32_t TC_CHANNEL_ACTIVE = 0x1; +static const uint32_t TC_CHANNEL_TIMEDOUT = 0x2; + +static const uint32_t TC0_STATUS_MASK = 0x3; +static const uint32_t TC1_STATUS_MASK = 0xC; +static const uint32_t TC2_STATUS_MASK = 0x300; +static const uint32_t TC3_STATUS_MASK = 0xC00; +static const uint32_t TC4_STATUS_MASK = 0x30000; +static const uint32_t TC5_STATUS_MASK = 0xc00000; +// Lock register set to 1 when start sequence has been found (CLTU is beeing processed) +static const uint32_t LOCK_MASK = 0xc00000; + +static const uint32_t TC0_STATUS_POS = 0; +static const uint32_t TC1_STATUS_POS = 2; +static const uint32_t TC2_STATUS_POS = 4; +static const uint32_t TC3_STATUS_POS = 6; +static const uint32_t TC4_STATUS_POS = 8; +static const uint32_t TC5_STATUS_POS = 10; +// Lock register set to 1 when start sequence has been found (CLTU is beeing processed) +static const uint32_t LOCK_POS = 12; + +/** + * UIO is 4 byte aligned. Thus offset is calculated with "true offset" / 4 + * Example: PDEC_FAR = 0x2840 => Offset in virtual address space is 0xA10 + */ +static constexpr uint32_t PDEC_PISR_OFFSET = 0xA02; +static constexpr uint32_t PDEC_PIR_OFFSET = 0xA03; +static constexpr uint32_t PDEC_IMR_OFFSET = 0xA04; +static const uint32_t PDEC_FAR_OFFSET = 0xA10; +static const uint32_t PDEC_CLCW_OFFSET = 0xA12; +static const uint32_t PDEC_BFREE_OFFSET = 0xA24; +static const uint32_t PDEC_BPTR_OFFSET = 0xA25; +static const uint32_t PDEC_SLEN_OFFSET = 0xA26; +static const uint32_t PDEC_MON_OFFSET = 0xA27; + +} // namespace pdec + +#endif /* LINUX_OBC_PDEC_H_ */