Update PTME code #635

Merged
meggert merged 20 commits from update-ptme-code-2 into v4.0.0-dev 2023-05-31 15:23:15 +02:00
8 changed files with 40 additions and 77 deletions

View File

@ -19,10 +19,16 @@ will consitute of a breaking change warranting a new major release:
# [v4.0.0] to be released # [v4.0.0] to be released
- eive-tmtc: v4.0.0 (to be released) - eive-tmtc: v4.0.0 (to be released)
TODO: New firmware package version.
## Fixed ## Fixed
- CFDP low level protocol bugfix. Requires fsfw update and tmtc update. - CFDP low level protocol bugfix. Requires fsfw update and tmtc update.
- Important bugfixes for PTME. See `q7s-package` CHANGELOG.
## Changed
- Removed PTME busy/ready signals. Those were not used anyway because register reads are used now.
# [v3.0.0] to be released # [v3.0.0] to be released
@ -100,6 +106,7 @@ will consitute of a breaking change warranting a new major release:
# [v2.0.5] 2023-05-11 # [v2.0.5] 2023-05-11
- The dual lane assembly transition failed handler started new transitions towards the current mode - The dual lane assembly transition failed handler started new transitions towards the current mode
instead of the target mode. This means that if the dual lane assembly never reached the initial instead of the target mode. This means that if the dual lane assembly never reached the initial
submode (e.g. mode normal and submode dual side), it will transition back to the current mode, submode (e.g. mode normal and submode dual side), it will transition back to the current mode,

View File

@ -82,14 +82,12 @@ static constexpr char EN_RW_4[] = "enable_rw_4";
static constexpr char RAD_SENSOR_CHIP_SELECT[] = "rad_sensor_chip_select"; static constexpr char RAD_SENSOR_CHIP_SELECT[] = "rad_sensor_chip_select";
static constexpr char ENABLE_RADFET[] = "enable_radfet"; static constexpr char ENABLE_RADFET[] = "enable_radfet";
static constexpr char PAPB_BUSY_SIGNAL_VC0[] = "papb_busy_signal_vc0";
static constexpr char PAPB_EMPTY_SIGNAL_VC0[] = "papb_empty_signal_vc0"; static constexpr char PAPB_EMPTY_SIGNAL_VC0[] = "papb_empty_signal_vc0";
static constexpr char PAPB_BUSY_SIGNAL_VC1[] = "papb_busy_signal_vc1";
static constexpr char PAPB_EMPTY_SIGNAL_VC1[] = "papb_empty_signal_vc1"; static constexpr char PAPB_EMPTY_SIGNAL_VC1[] = "papb_empty_signal_vc1";
static constexpr char PAPB_BUSY_SIGNAL_VC2[] = "papb_busy_signal_vc2";
static constexpr char PAPB_EMPTY_SIGNAL_VC2[] = "papb_empty_signal_vc2"; static constexpr char PAPB_EMPTY_SIGNAL_VC2[] = "papb_empty_signal_vc2";
static constexpr char PAPB_BUSY_SIGNAL_VC3[] = "papb_busy_signal_vc3";
static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3"; static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3";
static constexpr char PTME_RESETN[] = "ptme_resetn"; static constexpr char PTME_RESETN[] = "ptme_resetn";
static constexpr char RS485_EN_TX_CLOCK[] = "tx_clock_enable_ltc2872"; static constexpr char RS485_EN_TX_CLOCK[] = "tx_clock_enable_ltc2872";

View File

@ -730,20 +730,12 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
// GPIO definitions of signals connected to the virtual channel interfaces of the PTME IP Core // GPIO definitions of signals connected to the virtual channel interfaces of the PTME IP Core
GpioCookie* gpioCookiePtmeIp = new GpioCookie; GpioCookie* gpioCookiePtmeIp = new GpioCookie;
GpiodRegularByLineName* gpio = nullptr; GpiodRegularByLineName* gpio = nullptr;
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC0, "PAPB VC0");
gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC0, "PAPB VC0"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC0, "PAPB VC0");
gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_EMPTY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC1, "PAPB VC1");
gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC1, "PAPB VC1"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC1, "PAPB VC1");
gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_EMPTY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC2, "PAPB VC2");
gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC2, "PAPB VC2"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC2, "PAPB VC2");
gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_EMPTY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN", gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN",
@ -752,18 +744,14 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs"); gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs");
// Creating virtual channel interfaces // Creating virtual channel interfaces
VirtualChannelIF* vc0 = VirtualChannelIF* vc0 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_EMPTY,
new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_BUSY, gpioIds::VC0_PAPB_EMPTY, q7s::UIO_PTME, q7s::uiomapids::PTME_VC0);
q7s::UIO_PTME, q7s::uiomapids::PTME_VC0); VirtualChannelIF* vc1 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_EMPTY,
VirtualChannelIF* vc1 = q7s::UIO_PTME, q7s::uiomapids::PTME_VC1);
new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_BUSY, gpioIds::VC1_PAPB_EMPTY, VirtualChannelIF* vc2 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_EMPTY,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC1); q7s::UIO_PTME, q7s::uiomapids::PTME_VC2);
VirtualChannelIF* vc2 = VirtualChannelIF* vc3 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_EMPTY,
new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_BUSY, gpioIds::VC2_PAPB_EMPTY, q7s::UIO_PTME, q7s::uiomapids::PTME_VC3);
q7s::UIO_PTME, q7s::uiomapids::PTME_VC2);
VirtualChannelIF* vc3 =
new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_BUSY, gpioIds::VC3_PAPB_EMPTY,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC3);
// Creating ptme object and adding virtual channel interfaces // Creating ptme object and adding virtual channel interfaces
Ptme* ptme = new Ptme(objects::PTME); Ptme* ptme = new Ptme(objects::PTME);
ptme->addVcInterface(ccsds::VC0, vc0); ptme->addVcInterface(ccsds::VC0, vc0);

View File

@ -93,15 +93,10 @@ enum gpioId_t {
EN_RW_CS, EN_RW_CS,
SPI_MUX, SPI_MUX,
VC0_PAPB_EMPTY, VC0_PAPB_EMPTY,
VC0_PAPB_BUSY,
VC1_PAPB_EMPTY, VC1_PAPB_EMPTY,
VC1_PAPB_BUSY,
VC2_PAPB_EMPTY, VC2_PAPB_EMPTY,
VC2_PAPB_BUSY,
VC3_PAPB_EMPTY, VC3_PAPB_EMPTY,
VC3_PAPB_BUSY,
PTME_RESETN, PTME_RESETN,
PDEC_RESET, PDEC_RESET,

View File

@ -7,20 +7,16 @@
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface/ServiceInterface.h"
PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId,
gpioId_t papbEmptyId, std::string uioFile, int mapNum) std::string uioFile, int mapNum)
: gpioComIF(gpioComIF), : gpioComIF(gpioComIF), papbEmptyId(papbEmptyId), uioFile(std::move(uioFile)), mapNum(mapNum) {}
papbBusyId(papbBusyId),
papbEmptyId(papbEmptyId),
uioFile(std::move(uioFile)),
mapNum(mapNum) {}
PapbVcInterface::~PapbVcInterface() {} PapbVcInterface::~PapbVcInterface() {}
ReturnValue_t PapbVcInterface::initialize() { ReturnValue_t PapbVcInterface::initialize() {
UioMapper uioMapper(uioFile, mapNum); UioMapper uioMapper(uioFile, mapNum);
ReturnValue_t result = uioMapper.getMappedAdress(const_cast<uint32_t**>(&vcBaseReg), ReturnValue_t result = uioMapper.getMappedAdress(const_cast<uint32_t**>(&vcBaseReg),
UioMapper::Permissions::WRITE_ONLY); UioMapper::Permissions::READ_WRITE);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
return result; return result;
} }
@ -69,12 +65,6 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) {
// idx += 4; // idx += 4;
// } // }
for (size_t idx = 0; idx < size; idx++) { for (size_t idx = 0; idx < size; idx++) {
// This delay is super-important, DO NOT REMOVE!
// Polling the GPIO or the config register too often messes up the scheduler.
// TODO: Maybe this should not be done like this. It would be better if there was a custom
// FPGA module which can accept packets and then takes care of dumping that packet into
// the PTME. DMA would be an ideal solution for this.
nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
if (pollInterfaceReadiness(2, false) == returnvalue::OK) { if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
*(vcBaseReg + DATA_REG_OFFSET) = static_cast<uint32_t>(data[idx]); *(vcBaseReg + DATA_REG_OFFSET) = static_cast<uint32_t>(data[idx]);
} else { } else {
@ -82,7 +72,6 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
} }
nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
if (pollInterfaceReadiness(2, false) == returnvalue::OK) { if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
completePacketTransfer(); completePacketTransfer();
} else { } else {
@ -99,7 +88,7 @@ void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) {
void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; } void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; }
ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries, ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries,
bool checkReadyState) const { bool checkReadyForPacketState) const {
uint32_t busyIdx = 0; uint32_t busyIdx = 0;
nextDelay.tv_nsec = FIRST_DELAY_PAPB_POLLING_NS; nextDelay.tv_nsec = FIRST_DELAY_PAPB_POLLING_NS;
@ -108,13 +97,16 @@ ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries,
// Bit 5, see PTME ptme_001_01-0-7-r2 Table 31. // Bit 5, see PTME ptme_001_01-0-7-r2 Table 31.
uint32_t reg = *vcBaseReg; uint32_t reg = *vcBaseReg;
bool busy = (reg >> 5) & 0b1; bool busy = (reg >> 5) & 0b1;
bool ready = (reg >> 6) & 0b1; bool readyForPacket = (reg >> 6) & 0b1;
if (not busy) { if (checkReadyForPacketState) {
if (not busy and readyForPacket) {
return returnvalue::OK;
} else if (not busy and not readyForPacket) {
return PAPB_BUSY;
}
} else if (not busy) {
return returnvalue::OK; return returnvalue::OK;
} }
if (checkReadyState and not ready) {
return PAPB_BUSY;
}
busyIdx++; busyIdx++;
if (busyIdx >= maxPollRetries) { if (busyIdx >= maxPollRetries) {
@ -131,24 +123,22 @@ ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries,
return returnvalue::OK; return returnvalue::OK;
} }
void PapbVcInterface::isVcInterfaceBufferEmpty() { bool PapbVcInterface::isVcInterfaceBufferEmpty() {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
gpio::Levels papbEmptyState = gpio::Levels::HIGH; gpio::Levels papbEmptyState = gpio::Levels::HIGH;
result = gpioComIF->readGpio(papbEmptyId, papbEmptyState); result = gpioComIF->readGpio(papbEmptyId, papbEmptyState);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
sif::warning << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal" sif::error << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal"
<< std::endl; << std::endl;
return; return true;
} }
if (papbEmptyState == gpio::Levels::HIGH) { if (papbEmptyState == gpio::Levels::HIGH) {
sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is empty" << std::endl; return true;
} else {
sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is not empty" << std::endl;
} }
return; return false;
} }
bool PapbVcInterface::isBusy() const { return pollInterfaceReadiness(0, true) == PAPB_BUSY; } bool PapbVcInterface::isBusy() const { return pollInterfaceReadiness(0, true) == PAPB_BUSY; }

View File

@ -30,8 +30,7 @@ class PapbVcInterface : public VirtualChannelIF {
* @param uioFile UIO file providing access to the PAPB bus * @param uioFile UIO file providing access to the PAPB bus
* @param mapNum Map number of UIO map associated with this virtual channel * @param mapNum Map number of UIO map associated with this virtual channel
*/ */
PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, gpioId_t papbEmptyId, PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, std::string uioFile, int mapNum);
std::string uioFile, int mapNum);
virtual ~PapbVcInterface(); virtual ~PapbVcInterface();
bool isBusy() const override; bool isBusy() const override;
@ -83,9 +82,6 @@ class PapbVcInterface : public VirtualChannelIF {
static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40; static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40;
LinuxLibgpioIF* gpioComIF = nullptr; LinuxLibgpioIF* gpioComIF = nullptr;
/** Pulled to low when virtual channel not ready to receive data */
gpioId_t papbBusyId = gpio::NO_GPIO;
/** High when external buffer memory of virtual channel is empty */ /** High when external buffer memory of virtual channel is empty */
gpioId_t papbEmptyId = gpio::NO_GPIO; gpioId_t papbEmptyId = gpio::NO_GPIO;
@ -120,13 +116,14 @@ class PapbVcInterface : public VirtualChannelIF {
* *
* @return returnvalue::OK when ready to receive data else PAPB_BUSY. * @return returnvalue::OK when ready to receive data else PAPB_BUSY.
*/ */
inline ReturnValue_t pollInterfaceReadiness(uint32_t maxPollRetries, bool checkReadyState) const; inline ReturnValue_t pollInterfaceReadiness(uint32_t maxPollRetries,
bool checkReadyForPacketState) const;
/** /**
* @brief This function can be used for debugging to check whether there are packets in * @brief This function can be used for debugging to check whether there are packets in
* the packet buffer of the virtual channel or not. * the packet buffer of the virtual channel or not.
*/ */
void isVcInterfaceBufferEmpty(); bool isVcInterfaceBufferEmpty();
/** /**
* @brief This function sends a complete telemetry transfer frame data field (1105 bytes) * @brief This function sends a complete telemetry transfer frame data field (1105 bytes)

View File

@ -42,13 +42,7 @@ ReturnValue_t PersistentLogTmStoreTask::performOperation(uint8_t opCode) {
if (not someonesBusy) { if (not someonesBusy) {
TaskFactory::delayTask(100); TaskFactory::delayTask(100);
} else if (vcBusyDuringDump) { } else if (vcBusyDuringDump) {
// TODO: Might not be necessary
sif::debug << "VC busy, delaying" << std::endl;
TaskFactory::delayTask(10); TaskFactory::delayTask(10);
} else {
// TODO: Would be best to remove this, but not delaying here can lead to evil issues.
// Polling the PAPB of the PTME core too often leads to scheuduling issues.
TaskFactory::delayTask(2);
} }
} }
} }

View File

@ -24,13 +24,7 @@ ReturnValue_t PersistentSingleTmStoreTask::performOperation(uint8_t opCode) {
if (not busy) { if (not busy) {
TaskFactory::delayTask(100); TaskFactory::delayTask(100);
} else if (dumpContext.vcBusyDuringDump) { } else if (dumpContext.vcBusyDuringDump) {
sif::debug << "VC busy, delaying" << std::endl;
// TODO: Might not be necessary
TaskFactory::delayTask(10); TaskFactory::delayTask(10);
} else {
// TODO: Would be best to remove this, but not delaying here can lead to evil issues.
// Polling the PAPB of the PTME core too often leads to scheuduling issues.
TaskFactory::delayTask(2);
} }
} }
} }