QSPI Erratum workaround Topic is solved

Report here problems in any of ChibiOS components. This forum is NOT for support.
steved
Posts: 766
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 10 times
Been thanked: 120 times

QSPI Erratum workaround  Topic is solved

Postby steved » Wed Jul 22, 2020 2:45 pm

Some of the ST devices supporting QUAD SPI have an erratum listed "Extra data written in the FIFO at the end of a read transfer". (Section 2.4.1 for STM32F74xxx STM32F75xxx; Section 2.6.3 for STM32L476xx/STM32L486xx). The best-looking suggested workaround is to request an ABORT at the end of a transfer.

I have implemented it as the last thing in the wspi_lld_serve_interrupt() routine:

Code: Select all

 
  :
  :
  dmaStreamDisable(wspip->dma);
  if ((wspip->qspi->SR) & QUADSPI_SR_BUSY)
  {
    wspip->qspi->CR |= QUADSPI_CR_ABORT;      /* This avoids a situation described in Errata for some devices */
  }
}

I found that the test for BUSY was necessary.

I suspect that the scope of this problem may be much wider than the erratum suggests; I have been trying to track down a very similar problem on the 32F767, although not all the conditions were met. Not the whole answer, though.

User avatar
Giovanni
Site Admin
Posts: 13427
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 836 times
Been thanked: 730 times
Contact:

Re: QSPI Erratum workaround

Postby Giovanni » Wed Jul 22, 2020 4:15 pm

Hi,

Can it simply stay there for all devices or we need a check?

Giovanni

steved
Posts: 766
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 10 times
Been thanked: 120 times

Re: QSPI Erratum workaround

Postby steved » Wed Jul 22, 2020 4:26 pm

I think it can stay there for all devices; should be harmless for any devices not affected by the problem, since QSPI should never be busy at that point.

I haven't been able to find any information on the time an ABORT takes to complete; my initial presumption is that its short enough that there's no need to wait for the flag to clear.

Can't give a definitive answer at present; my own problems are still present and confusing me. But according to the erratum, something like the proposed fix is definitely required.

User avatar
Giovanni
Site Admin
Posts: 13427
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 836 times
Been thanked: 730 times
Contact:

Re: QSPI Erratum workaround

Postby Giovanni » Thu Jul 23, 2020 8:27 am

I am worried about aborting a "send" operation:

1) DMA finished sending.
2) QSPI still sending last frame.
3) DMA ISR aborts QSPI before the last frame is sent.

Giovanni

User avatar
Giovanni
Site Admin
Posts: 13427
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 836 times
Been thanked: 730 times
Contact:

Re: QSPI Erratum workaround

Postby Giovanni » Thu Jul 23, 2020 8:44 am

Hi

I want to try the other workaround, it looks safer: discarding extra words. In order to do this we need to know if we are sending or receiving so I had to add extra driver states.

I have no HW right now, could you try the following changes?

in hal_lld_wspi.c:

Code: Select all

static void wspi_lld_serve_interrupt(WSPIDriver *wspip) {

  /* Portable WSPI ISR code defined in the high level driver, note, it is
     a macro.*/
  _wspi_isr_code(wspip);

  /* Stop everything, we need to give DMA enough time to complete the ongoing
     operation. Race condition hidden here.*/
  while (dmaStreamGetTransactionSize(wspip->dma) > 0U)
    ;
//  dmaStreamDisable(wspip->dma);

  /* Handling of errata: Extra data written in the FIFO at the end of a
     read transfer.*/
  if (wspip->state == WSPI_RECEIVE) {
    while ((wspip->qspi->SR & QUADSPI_SR_BUSY) != 0U) {
      (void) wspip->qspi->DR;
    }
  }
}


Replace also the whole hal_wspi.h:

Code: Select all

/*
    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

/**
 * @file    hal_wspi.h
 * @brief   WSPI Driver macros and structures.
 *
 * @addtogroup WSPI
 * @{
 */

#ifndef HAL_WSPI_H
#define HAL_WSPI_H

#if (HAL_USE_WSPI == TRUE) || defined(__DOXYGEN__)

/*===========================================================================*/
/* Driver constants.                                                         */
/*===========================================================================*/

/*===========================================================================*/
/* Driver pre-compile time settings.                                         */
/*===========================================================================*/

/**
 * @name    WSPI configuration options
 * @{
 */
/**
 * @brief   Enables synchronous APIs.
 * @note    Disabling this option saves both code and data space.
 */
#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
#define WSPI_USE_WAIT                       TRUE
#endif

/**
 * @brief   Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
 * @note    Disabling this option saves both code and data space.
 */
#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define WSPI_USE_MUTUAL_EXCLUSION           TRUE
#endif
/** @} */

/*===========================================================================*/
/* Derived constants and error checks.                                       */
/*===========================================================================*/

/*===========================================================================*/
/* Driver data structures and types.                                         */
/*===========================================================================*/

/**
 * @brief   Driver state machine possible states.
 */
typedef enum {
  WSPI_UNINIT = 0,                  /**< Not initialized.                   */
  WSPI_STOP = 1,                    /**< Stopped.                           */
  WSPI_READY = 2,                   /**< Ready.                             */
  WSPI_SEND = 3,                    /**< Sending data.                      */
  WSPI_RECEIVE = 4,                 /**< Receiving data.                    */
  WSPI_COMPLETE = 5,                /**< Asynchronous operation complete.   */
  WSPI_MEMMAP = 6                   /**< In memory mapped mode.             */
} wspistate_t;

/**
 * @brief   Type of a structure representing an WSPI driver.
 */
typedef struct hal_wspi_driver WSPIDriver;

/**
 * @brief   Type of a structure representing an WSPI driver configuration.
 */
typedef struct hal_wspi_config WSPIConfig;

/**
 * @brief   Type of a WSPI notification callback.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object triggering the
 *                      callback
 */
typedef void (*wspicallback_t)(WSPIDriver *wspip);

/**
 * @brief   Type of a WSPI command descriptor.
 */
typedef struct {
  /**
   * @brief   Transfer configuration field.
   */
  uint32_t              cfg;
  /**
   * @brief   Command phase data.
   */
  uint32_t              cmd;
  /**
   * @brief   Address phase data.
   */
  uint32_t              addr;
  /**
   * @brief   Alternate phase data.
   */
  uint32_t              alt;
  /**
   * @brief   Number of dummy cycles to be inserted.
   */
  uint32_t              dummy;
} wspi_command_t;

/* Including the low level driver header, it exports information required
   for completing types.*/
#include "hal_wspi_lld.h"

#if !defined(WSPI_SUPPORTS_MEMMAP)
#error "low level does not define WSPI_SUPPORTS_MEMMAP"
#endif

#if !defined(WSPI_DEFAULT_CFG_MASKS)
#error "low level does not define WSPI_DEFAULT_CFG_MASKS"
#endif

/**
 * @brief   Driver configuration structure.
 */
struct hal_wspi_config {
  /**
   * @brief   Operation complete callback or @p NULL.
   */
  wspicallback_t            end_cb;
  /**
   * @brief   Operation error callback or @p NULL.
   */
  wspicallback_t            error_cb;
  /* End of the mandatory fields.*/
  wspi_lld_config_fields;
};

/**
 * @brief   Structure representing an WSPI driver.
 */
struct hal_wspi_driver {
  /**
   * @brief   Driver state.
   */
  wspistate_t               state;
  /**
   * @brief   Current configuration data.
   */
  const WSPIConfig          *config;
#if (WSPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
  /**
   * @brief   Waiting thread.
   */
  thread_reference_t        thread;
#endif /* WSPI_USE_WAIT */
#if (WSPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
  /**
   * @brief   Mutex protecting the peripheral.
   */
  mutex_t                   mutex;
#endif /* WSPI_USE_MUTUAL_EXCLUSION */
#if defined(WSPI_DRIVER_EXT_FIELDS)
  WSPI_DRIVER_EXT_FIELDS
#endif
  /* End of the mandatory fields.*/
  wspi_lld_driver_fields;
};

/*===========================================================================*/
/* Driver macros.                                                            */
/*===========================================================================*/

#if (WSPI_DEFAULT_CFG_MASKS == TRUE) || defined(__DOXYGEN__)
/**
 * @name    Transfer options
 * @note    The low level driver has the option to override the following
 *          definitions and use its own ones. In must take care to use
 *          the same name for the same function or compatibility is not
 *          ensured.
 * @{
 */
#define WSPI_CFG_CMD_MODE_MASK                  (7LU << 0LU)
#define WSPI_CFG_CMD_MODE_NONE                  (0LU << 0LU)
#define WSPI_CFG_CMD_MODE_ONE_LINE              (1LU << 0LU)
#define WSPI_CFG_CMD_MODE_TWO_LINES             (2LU << 0LU)
#define WSPI_CFG_CMD_MODE_FOUR_LINES            (3LU << 0LU)
#define WSPI_CFG_CMD_MODE_EIGHT_LINES           (4LU << 0LU)

#define WSPI_CFG_CMD_DTR                        (1LU << 3LU)

#define WSPI_CFG_CMD_SIZE_MASK                  (3LU << 4LU)
#define WSPI_CFG_CMD_SIZE_8                     (0LU << 4LU)
#define WSPI_CFG_CMD_SIZE_16                    (1LU << 4LU)
#define WSPI_CFG_CMD_SIZE_24                    (2LU << 4LU)
#define WSPI_CFG_CMD_SIZE_32                    (3LU << 4LU)

#define WSPI_CFG_ADDR_MODE_MASK                 (7LU << 8LU)
#define WSPI_CFG_ADDR_MODE_NONE                 (0LU << 8LU)
#define WSPI_CFG_ADDR_MODE_ONE_LINE             (1LU << 8LU)
#define WSPI_CFG_ADDR_MODE_TWO_LINES            (2LU << 8LU)
#define WSPI_CFG_ADDR_MODE_FOUR_LINES           (3LU << 8LU)
#define WSPI_CFG_ADDR_MODE_EIGHT_LINES          (4LU << 8LU)

#define WSPI_CFG_ADDR_DTR                       (1LU << 11LU)

#define WSPI_CFG_ADDR_SIZE_MASK                 (3LU << 12LU)
#define WSPI_CFG_ADDR_SIZE_8                    (0LU << 12LU)
#define WSPI_CFG_ADDR_SIZE_16                   (1LU << 12LU)
#define WSPI_CFG_ADDR_SIZE_24                   (2LU << 12LU)
#define WSPI_CFG_ADDR_SIZE_32                   (3LU << 12LU)

#define WSPI_CFG_ALT_MODE_MASK                  (7LU << 16LU)
#define WSPI_CFG_ALT_MODE_NONE                  (0LU << 16LU)
#define WSPI_CFG_ALT_MODE_ONE_LINE              (1LU << 16LU)
#define WSPI_CFG_ALT_MODE_TWO_LINES             (2LU << 16LU)
#define WSPI_CFG_ALT_MODE_FOUR_LINES            (3LU << 16LU)
#define WSPI_CFG_ALT_MODE_EIGHT_LINES           (4LU << 16LU)

#define WSPI_CFG_ALT_DTR                        (1LU << 19LU)

#define WSPI_CFG_ALT_SIZE_MASK                  (3LU << 20LU)
#define WSPI_CFG_ALT_SIZE_8                     (0LU << 20LU)
#define WSPI_CFG_ALT_SIZE_16                    (1LU << 20LU)
#define WSPI_CFG_ALT_SIZE_24                    (2LU << 20LU)
#define WSPI_CFG_ALT_SIZE_32                    (3LU << 20LU)

#define WSPI_CFG_DATA_MODE_MASK                 (7LU << 24LU)
#define WSPI_CFG_DATA_MODE_NONE                 (0LU << 24LU)
#define WSPI_CFG_DATA_MODE_ONE_LINE             (1LU << 24LU)
#define WSPI_CFG_DATA_MODE_TWO_LINES            (2LU << 24LU)
#define WSPI_CFG_DATA_MODE_FOUR_LINES           (3LU << 24LU)
#define WSPI_CFG_DATA_MODE_EIGHT_LINES          (4LU << 24LU)

#define WSPI_CFG_DATA_DTR                       (1LU << 27LU)

#define WSPI_CFG_DQS_ENABLE                     (1LU << 29LU)

#define WSPI_CFG_SIOO                           (1LU << 31LU)

#define WSPI_CFG_ALL_DTR                        (WSPI_CFG_CMD_DTR   |       \
                                                 WSPI_CFG_ADDR_DTR  |       \
                                                 WSPI_CFG_ALT_DTR   |       \
                                                 WSPI_CFG_DATA_DTR)
/** @} */
#endif /* WSPI_USE_DEFAULT_CFG_MASKS == TRUE */

/**
 * @name    Macro Functions
 * @{
 */
/**
 * @brief   Sends a command without data phase.
 * @post    At the end of the operation the configured callback is invoked.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 * @param[in] cmdp      pointer to the command descriptor
 *
 * @iclass
 */
#define wspiStartCommandI(wspip, cmdp) {                                    \
  osalDbgAssert(((cmdp)->cfg & WSPI_CFG_DATA_MODE_MASK) ==                  \
                WSPI_CFG_DATA_MODE_NONE,                                    \
                "data mode specified");                                     \
  (wspip)->state = WSPI_SEND;                                               \
  wspi_lld_command(wspip, cmdp);                                            \
}

/**
 * @brief   Sends data over the WSPI bus.
 * @details This asynchronous function starts a transmit operation.
 * @post    At the end of the operation the configured callback is invoked.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 * @param[in] cmdp      pointer to the command descriptor
 * @param[in] n         number of bytes to send or zero if no data phase
 * @param[in] txbuf     the pointer to the transmit buffer
 *
 * @iclass
 */
#define wspiStartSendI(wspip, cmdp, n, txbuf) {                             \
  osalDbgAssert(((cmdp)->cfg & WSPI_CFG_DATA_MODE_MASK) !=                  \
                WSPI_CFG_DATA_MODE_NONE,                                    \
                "data mode required");                                      \
  (wspip)->state = WSPI_SEND;                                               \
  wspi_lld_send(wspip, cmdp, n, txbuf);                                     \
}

/**
 * @brief   Receives data from the WSPI bus.
 * @details This asynchronous function starts a receive operation.
 * @post    At the end of the operation the configured callback is invoked.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 * @param[in] cmdp      pointer to the command descriptor
 * @param[in] n         number of bytes to receive or zero if no data phase
 * @param[out] rxbuf    the pointer to the receive buffer
 *
 * @iclass
 */
#define wspiStartReceiveI(wspip, cmdp, n, rxbuf) {                          \
  osalDbgAssert(((cmdp)->cfg & WSPI_CFG_DATA_MODE_MASK) !=                  \
                WSPI_CFG_DATA_MODE_NONE,                                    \
                "data mode required");                                      \
  (wspip)->state = WSPI_RECEIVE;                                            \
  wspi_lld_receive(wspip, cmdp, n, rxbuf);                                  \
}

#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__)
/**
 * @brief   Maps in memory space a WSPI flash device.
 * @pre     The memory flash device must be initialized appropriately
 *          before mapping it in memory space.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 * @param[in] cmdp      pointer to the command descriptor
 * @param[out] addrp    pointer to the memory start address of the mapped
 *                      flash or @p NULL
 *
 * @iclass
 */
#define wspiMapFlashI(wspip, cmdp, addrp)                                   \
  wspi_lld_map_flash(wspip, cmdp, addrp)

/**
 * @brief   Maps in memory space a WSPI flash device.
 * @post    The memory flash device must be re-initialized for normal
 *          commands exchange.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 *
 * @iclass
 */
#define wspiUnmapFlashI(wspip)                                              \
  wspi_lld_unmap_flash(wspip)
#endif /* WSPI_SUPPORTS_MEMMAP == TRUE */
/** @} */

/**
 * @name    Low level driver helper macros
 * @{
 */
#if (WSPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
/**
 * @brief   Wakes up the waiting thread.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 * @param[in] msg       the wakeup message
 *
 * @notapi
 */
#define _wspi_wakeup_isr(wspip, msg) {                                      \
  osalSysLockFromISR();                                                     \
  osalThreadResumeI(&(wspip)->thread, msg);                                 \
  osalSysUnlockFromISR();                                                   \
}
#else /* !WSPI_USE_WAIT */
#define _wspi_wakeup_isr(wspip, msg)
#endif /* !WSPI_USE_WAIT */

/**
 * @brief   Common ISR code.
 * @details This code handles the portable part of the ISR code:
 *          - Callback invocation.
 *          - Waiting thread wakeup, if any.
 *          - Driver state transitions.
 *          .
 * @note    This macro is meant to be used in the low level drivers
 *          implementation only.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 *
 * @notapi
 */
#define _wspi_isr_code(wspip) {                                             \
  if ((wspip)->config->end_cb) {                                            \
    (wspip)->state = WSPI_COMPLETE;                                         \
    (wspip)->config->end_cb(wspip);                                         \
    if ((wspip)->state == WSPI_COMPLETE)                                    \
      (wspip)->state = WSPI_READY;                                          \
  }                                                                         \
  else                                                                      \
    (wspip)->state = WSPI_READY;                                            \
  _wspi_wakeup_isr(wspip, MSG_OK);                                          \
}

/**
 * @brief   Common error ISR code.
 * @details This code handles the portable part of the ISR code:
 *          - Callback invocation.
 *          - Waiting thread wakeup, if any.
 *          - Driver state transitions.
 *          .
 * @note    This macro is meant to be used in the low level drivers
 *          implementation only.
 *
 * @param[in] wspip     pointer to the @p WSPIDriver object
 *
 * @notapi
 */
#define _wspi_error_code(wspip) {                                           \
  if ((wspip)->config->error_cb) {                                          \
    (wspip)->state = WSPI_COMPLETE;                                         \
    (wspip)->config->error_cb(wspip);                                       \
    if ((wspip)->state == WSPI_COMPLETE)                                    \
      (wspip)->state = WSPI_READY;                                          \
  }                                                                         \
  else                                                                      \
    (wspip)->state = WSPI_READY;                                            \
  _wspi_wakeup_isr(wspip, MSG_RESET);                                       \
}
/** @} */

/*===========================================================================*/
/* External declarations.                                                    */
/*===========================================================================*/

#ifdef __cplusplus
extern "C" {
#endif
  void wspiInit(void);
  void wspiObjectInit(WSPIDriver *wspip);
  void wspiStart(WSPIDriver *wspip, const WSPIConfig *config);
  void wspiStop(WSPIDriver *wspip);
  void wspiStartCommand(WSPIDriver *wspip, const wspi_command_t *cmdp);
  void wspiStartSend(WSPIDriver *wspip, const wspi_command_t *cmdp,
                     size_t n, const uint8_t *txbuf);
  void wspiStartReceive(WSPIDriver *wspip, const wspi_command_t *cmdp,
                        size_t n, uint8_t *rxbuf);
#if WSPI_USE_WAIT == TRUE
  bool wspiCommand(WSPIDriver *wspip, const wspi_command_t *cmdp);
  bool wspiSend(WSPIDriver *wspip, const wspi_command_t *cmdp,
                size_t n, const uint8_t *txbuf);
  bool wspiReceive(WSPIDriver *wspip, const wspi_command_t *cmdp,
                   size_t n, uint8_t *rxbuf);
#endif
#if WSPI_SUPPORTS_MEMMAP == TRUE
void wspiMapFlash(WSPIDriver *wspip,
                  const wspi_command_t *cmdp,
                  uint8_t **addrp);
void wspiUnmapFlash(WSPIDriver *wspip);
#endif
#if WSPI_USE_MUTUAL_EXCLUSION == TRUE
  void wspiAcquireBus(WSPIDriver *wspip);
  void wspiReleaseBus(WSPIDriver *wspip);
#endif
#ifdef __cplusplus
}
#endif

#endif /* HAL_USE_WSPI == TRUE */

#endif /* HAL_WSPI_H */

/** @} */


Giovanni

steved
Posts: 766
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 10 times
Been thanked: 120 times

Re: QSPI Erratum workaround

Postby steved » Thu Jul 23, 2020 10:07 am

I see your point about sending; whichever solution is finally adopted needs to test for WSPI_RECEIVE state.

I chose the ABORT option initially because I perceived it as having less overhead; the erratum is silent as to the number of bytes which might need to be read and discarded. If the most recent transaction is restarted, that could be a lot of bytes to read in an ISR. There's also an expectation (in my mind, at least) that an abort might tidy up the internal state of the QSPI peripheral better than just emptying the FIFO.

Against that, the abort option didn't solve my problem, so it will be interesting to see if reading the FIFO makes any difference.

I'll give your changes a try.

User avatar
Giovanni
Site Admin
Posts: 13427
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 836 times
Been thanked: 730 times
Contact:

Re: QSPI Erratum workaround

Postby Giovanni » Thu Jul 23, 2020 10:19 am

I think there is one extra word at most, it looks like a glitch triggered by very specific clock conditions and settings.

Giovanni

steved
Posts: 766
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 10 times
Been thanked: 120 times

Re: QSPI Erratum workaround

Postby steved » Thu Jul 23, 2020 11:36 am

Giovanni wrote:I think there is one extra word at most, it looks like a glitch triggered by very specific clock conditions and settings.

You're probably right, but difficult to tell; unfortunately that erratum looks like it was written by someone whose first language isn't English.
"An extra data is incorrectly written in the FIFO" could mean either "Some extra data is incorrectly written in the FIFO" or "An extra byte is incorrectly written in the FIFO", while "Read out the extra data until the BUSY flag goes low" suggests multiple bytes to me.

Anyway, the fix goes in with nothing else needing changing, although it's not been triggered on my hardware.

User avatar
Giovanni
Site Admin
Posts: 13427
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 836 times
Been thanked: 730 times
Contact:

Re: QSPI Erratum workaround

Postby Giovanni » Thu Jul 23, 2020 1:11 pm

Did you place a breakpoint in the loop?

There are a lot of conditions required for triggering the problem:

Code: Select all

- QUADSPI is used in indirect mode
- QUADSPI clock is AHB/2 (PRESCALER = 0x01 in the QUADSPI_CR)
- QUADSPI is in quad mode (DMODE = 0b11 in the QUADSPI_CCR)
- QUADSPI is in DDR mode (DDRM = 0b1 in the QUADSPI_CCR)


Giovanni

User avatar
Giovanni
Site Admin
Posts: 13427
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 836 times
Been thanked: 730 times
Contact:

Re: QSPI Erratum workaround

Postby Giovanni » Thu Jul 23, 2020 1:55 pm

For the time being, fixed as bug #1116.

Giovanni


Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 3 guests