Debugging Issue with Custom Board using i.MX RT1021 and W25Q80DV Flash

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Debugging Issue with Custom Board using i.MX RT1021 and W25Q80DV Flash

Jump to solution
400 Views
salva214
Contributor III

Hello NXP Community,

I am currently working on a custom board that uses an i.MX RT1021 (iMXRT1021CAF4B) and a W25Q80DV Flash, connected on the same pin as the one on the EVK, with XIP code.

I am encountering an issue when trying to debug my project via JTAG using an NXP MCU-Link. When I start debugging MCUXpresso never reach the main() and if I suspend the debugger, I receive the following message: “Break at address ‘0x215a90’ with no debug information available, or outside of program code.” whit always the same address.

Here is my configuration object for the flexspi_nor_config.c file:

/* FLEXSPI memory config block related defintions */
#define FLEXSPI_CFG_BLK_TAG     (0x42464346UL) // ascii "FCFB" Big Endian
#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0
#define FLEXSPI_CFG_BLK_SIZE    (512)

const flexspi_nor_config_t qspiflash_config = {
    .memConfig =
        {
            .tag = FLEXSPI_CFG_BLK_TAG,
            .version = FLEXSPI_CFG_BLK_VERSION,
            .readSampleClksrc=kFlexSPIReadSampleClk_LoopbackInternally,
            .csHoldTime = 3u,
            .csSetupTime = 3u,
            .deviceModeCfgEnable = true,
            .deviceModeType = kDeviceConfigCmdType_QuadEnable,
            .deviceModeSeq =
                {
                    .seqNum = 1,
                    .seqId = 4,
                },
            .deviceModeArg = 0x40, // Set QE bit in status register
            .controllerMiscOption = 0u,
            .deviceType = kFlexSpiDeviceType_SerialNOR,
            .sflashPadType = kSerialFlash_4Pads,
            .serialClkFreq = kFlexSpiSerialClk_80MHz,
            .lutCustomSeqEnable = 0u,
            .lookupTable =
                {
                    [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), // Fast Read Quad I/O
                    [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04),
                    [4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x01),   // Read Status Register
                    [8] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0x00),       // Write Enable
                    [12] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x20, RADDR_SDR, FLEXSPI_1PAD, 0x18), // Erase Sector
                    [16] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x02, RADDR_SDR, FLEXSPI_1PAD, 0x18), // Page Program
                    [17] = FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0x04, STOP, FLEXSPI_1PAD, 0x00),
                    [20] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x31, WRITE_SDR, FLEXSPI_1PAD, 0x01), // Set QE bit in status register
                },
        },
        .pageSize = 256u,
        .sectorSize = 4 * 1024u,
        .blockSize = 64 * 1024u,
        .isUniformBlockSize = false,
};

I would appreciate any guidance or suggestions on how to resolve this issue.

Thank you in advance for your help.

Best, 
Salvatore

0 Kudos
Reply
1 Solution
6 Replies
175 Views
salva214
Contributor III

Hello, could someone please provide a more detailed explanation of these NOR flash parameters? I’m unable to devise a solution to this problem without specifically using the flash chip from the evaluation board.

0 Kudos
Reply
317 Views
salva214
Contributor III

Hi @diego_charles, thanks for your reply. 
here is the SRC Register:

image.png

 

I was also having trouble with the BOOT_MODE[1:0] pins, because i didn't know about the BT_FUSE_SEL and i was stuck in Serial Downloader... this post helped me understanding: Programming the i.MXRT1021 - how hard can it be? | Details | Hackaday.io

 

 

 

About the LUT in general and specific for my memory, I found this relevant posts, which (i think) pointed in the right direction: 

I read again the AN12183.pdf and now I understood why in this screen there was only 1 LUT sequences (the QUAD-READ)
image.png

 Then I tried to arrange a test using "Flexspi_nor_polling_transfer" demo code, loaded in internal RAM, for finding the correct LUT for my memory and I came up with a strange behaviour (project attached)...

using the following LUTs:

 

 

const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
    /* Normal read mode -SDR */
    /* Normal read mode -SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Fast read mode - SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    /* In XIP, the speed of external flash is set to 133MHz, and the external flash require to match 8 corresponding dummy cycles. 
     * However, other non XIP boot targets are not suitable for XIP boot flow, uses flash default configuration */
#if defined(XIP_BOOT_HEADER_ENABLE) && XIP_BOOT_HEADER_ENABLE
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
        kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x0A, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
#else
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
        kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
#endif

    /* Fast read quad mode - SDR */
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
#if defined(XIP_BOOT_HEADER_ENABLE) && XIP_BOOT_HEADER_ENABLE
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
        kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
#else
    [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
        kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),
#endif    

    /* Read extend parameters */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] =
        //FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),
    	FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Write Enable - OK */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Erase Sector - OK */
    [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
        //FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xD7, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    	FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),

    /* Page Program - single mode - OK */
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Page Program - quad mode - OK */
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
    [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Read ID */
    [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Enable Quad mode - OK*/
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),

    /* Enter QPI mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_ENTERQPI] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

    /* Exit QPI mode */
    [4 * NOR_CMD_LUT_SEQ_IDX_EXITQPI] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xF5, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

	/* Read status(1) register - OK */
	[4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
		FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

	/* Read status(2) register - OK */
	[4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG2] =
		FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

    /* Erase whole chip - OK*/
    [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
};

 

 

with this App.h: 

 

/*
 * Copyright 2021 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#ifndef _APP_H_
#define _APP_H_

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/*${macro:start}*/
#define EXAMPLE_FLEXSPI                 FLEXSPI
#define FLASH_SIZE                      0x100000 /* 1MByte */
#define EXAMPLE_FLEXSPI_AMBA_BASE       FlexSPI_AMBA_BASE
#define FLASH_PAGE_SIZE                 256
#define EXAMPLE_SECTOR                  6 //6
#define SECTOR_SIZE                     0x1000 /* 4K */
#define EXAMPLE_FLEXSPI_CLOCK           kCLOCK_FlexSpi
#define FLASH_PORT                      kFLEXSPI_PortA1
#define EXAMPLE_FLEXSPI_RX_SAMPLE_CLOCK kFLEXSPI_ReadSampleClkLoopbackInternally //kFLEXSPI_ReadSampleClkLoopbackFromDqsPad

#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL        7
#define NOR_CMD_LUT_SEQ_IDX_READ_FAST          14
#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD     0
#define NOR_CMD_LUT_SEQ_IDX_READSTATUS         1
#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE        2
#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR        3
#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6
#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD   4
#define NOR_CMD_LUT_SEQ_IDX_READID             8
#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG     9
#define NOR_CMD_LUT_SEQ_IDX_ENTERQPI           10
#define NOR_CMD_LUT_SEQ_IDX_EXITQPI            11
#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG      12
#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG2	   13
#define NOR_CMD_LUT_SEQ_IDX_ERASECHIP          5

#define CUSTOM_LUT_LENGTH        60
#define FLASH_QUAD_ENABLE        0x200 //0x40
#define FLASH_BUSY_STATUS_POL    1
#define FLASH_BUSY_STATUS_OFFSET 0
#define FLASH_ERROR_STATUS_MASK  0x0e

/*
 * If cache is enabled, this example should maintain the cache to make sure
 * CPU core accesses the memory, not cache only.
 */
#define CACHE_MAINTAIN 1

/*${macro:end}*/

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*${variable:start}*/
#if (defined CACHE_MAINTAIN) && (CACHE_MAINTAIN == 1)
typedef struct _flexspi_cache_status
{
    volatile bool DCacheEnableFlag;
    volatile bool ICacheEnableFlag;
} flexspi_cache_status_t;
#endif
/*${variable:end}*/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*${prototype:start}*/
void BOARD_InitHardware(void);

static inline void flexspi_clock_init()
{
#if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)
    /* Switch to PLL2 for XIP to avoid hardfault during re-initialize clock. */
    CLOCK_InitSysPfd(kCLOCK_Pfd2, 24);    /* Set PLL2 PFD2 clock 396MHZ. */
    CLOCK_SetMux(kCLOCK_FlexspiMux, 0x2); /* Choose PLL2 PFD2 clock as flexspi source clock. */
    CLOCK_SetDiv(kCLOCK_FlexspiDiv, 3);   /* flexspi clock 133M. */
#else
    const clock_usb_pll_config_t g_ccmConfigUsbPll = {.loopDivider = 0U};

    CLOCK_InitUsb1Pll(&g_ccmConfigUsbPll);
    CLOCK_InitUsb1Pfd(kCLOCK_Pfd0, 24);   /* Set PLL3 PFD0 clock 360MHZ. */
    CLOCK_SetMux(kCLOCK_FlexspiMux, 0x3); /* Choose PLL3 PFD0 clock as flexspi source clock. */
    CLOCK_SetDiv(kCLOCK_FlexspiDiv, 2);   /* flexspi clock 120M. */
#endif
}
/*${prototype:end}*/

#endif /* _APP_H_ */

 

Based on this information found in the flash datasheet: 

image.png

 

And modifying the main() as this: 

 

 

int main(void)
{
    uint32_t i = 0;
    status_t status;
    uint8_t vendorID = 0;
    uint8_t statusReg = 0;

    BOARD_ConfigMPU();
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitDebugConsole();

    flexspi_nor_flash_init(EXAMPLE_FLEXSPI);

    PRINTF("\r\nFLEXSPI example started!\r\n");

    /* Get vendor ID. */
    status = flexspi_nor_get_vendor_id(EXAMPLE_FLEXSPI, &vendorID);
    if (status != kStatus_Success)
    {
        return status;
    }
    PRINTF("Vendor ID: 0x%x\r\n", vendorID);

#if !(defined(XIP_EXTERNAL_FLASH))
    /* Erase whole chip . */
    PRINTF("Erasing whole chip over FlexSPI...\r\n");

    status = flexspi_nor_erase_chip(EXAMPLE_FLEXSPI);
    if (status != kStatus_Success)
    {
        return status;
    }
    PRINTF("Erase finished !\r\n");

#endif

    status = flexspi_nor_flash_read_status(EXAMPLE_FLEXSPI, &statusReg);
	if (status != kStatus_Success)
	{
		PRINTF("ERROR getting status reg...\r\n");
	}
	else
	{
		PRINTF("STATUS REG = 0x%02x\r\n", statusReg);
	}

	status = flexspi_nor_flash_read_status2(EXAMPLE_FLEXSPI, &statusReg);
	if (status != kStatus_Success)
	{
		PRINTF("ERROR getting status reg...\r\n");
	}
	else
	{
		PRINTF("STATUS REG 2 = 0x%02x\r\n", statusReg);
	}

    /* Enter quad mode. */
    status = flexspi_nor_enable_quad_mode(EXAMPLE_FLEXSPI);
    if (status != kStatus_Success)
    {
        return status;
    }
    else
    {

        PRINTF("Quad Mode enabled succesfully! \r\n\r\n");
    }

    /* Erase sectors. */
    PRINTF("Erasing Serial NOR over FlexSPI...\r\n");
    status = flexspi_nor_flash_erase_sector(EXAMPLE_FLEXSPI, EXAMPLE_SECTOR * SECTOR_SIZE);
    if (status != kStatus_Success)
    {
        PRINTF("Erase sector failure !\r\n");
        return -1;
    }

    memset(s_nor_program_buffer, 0xFFU, sizeof(s_nor_program_buffer));

    DCACHE_InvalidateByRange(EXAMPLE_FLEXSPI_AMBA_BASE + EXAMPLE_SECTOR * SECTOR_SIZE, FLASH_PAGE_SIZE);

    memcpy(s_nor_read_buffer, (void *)(EXAMPLE_FLEXSPI_AMBA_BASE + EXAMPLE_SECTOR * SECTOR_SIZE),
           sizeof(s_nor_read_buffer));

    if (memcmp(s_nor_program_buffer, s_nor_read_buffer, sizeof(s_nor_program_buffer)))
    {
        PRINTF("Erase data -  read out data value incorrect !\r\n ");
        return -1;
    }
    else
    {
        PRINTF("Erase data - successfully. \r\n");
    }

    for (i = 0; i < 0xFFU; i++)
    {
        s_nor_program_buffer[i] = i;
    }

    status =
        flexspi_nor_flash_page_program(EXAMPLE_FLEXSPI, (EXAMPLE_SECTOR * SECTOR_SIZE), (void *)s_nor_program_buffer);
    if (status != kStatus_Success)
    {
        PRINTF("Page program failure !\r\n");
        return -1;
    }

    DCACHE_InvalidateByRange(EXAMPLE_FLEXSPI_AMBA_BASE + EXAMPLE_SECTOR * SECTOR_SIZE, FLASH_PAGE_SIZE);

    memcpy(s_nor_read_buffer, (void *)(EXAMPLE_FLEXSPI_AMBA_BASE + EXAMPLE_SECTOR * SECTOR_SIZE),
           sizeof(s_nor_read_buffer));

    if (memcmp(s_nor_read_buffer, s_nor_program_buffer, sizeof(s_nor_program_buffer)) != 0)
    {
        PRINTF("Program data -  read out data value incorrect !\r\n");
        //return -1;
    }
    else
    {
        PRINTF("Program data - successfully. \r\n");
    }

    status = flexspi_nor_flash_read(EXAMPLE_FLEXSPI, (EXAMPLE_SECTOR * SECTOR_SIZE)-1, (uint32_t *)(void *)s_nor_read_buffer, FLASH_PAGE_SIZE);
    if (status != kStatus_Success)
        {
            PRINTF("Page READ failure !\r\n");
            return -1;
        }

    if (memcmp(s_nor_read_buffer, s_nor_program_buffer, sizeof(s_nor_program_buffer)) != 0)
        {
            PRINTF("Program READ 2 -  read out data value incorrect !\r\n");
            return -1;
        }
        else
        {
            PRINTF("Program data 2 - successfully. \r\n");
        }

    while (1)
    {
    }
}

 

 

 

I obtain the following output:

 

 

FLEXSPI example started!
Vendor ID: 0xef
STATUS REG = 0x 2
STATUS REG 2 = 0x 2
Quad Mode enabled succesfully! 

Erasing Serial NOR over FlexSPI...
Erase data - successfully. 
Program data -  read out data value incorrect !
Program data 2 - successfully.

 

 

 

I had to add the "Program data 2" check, using a READ LUT (instead of the memcpy) and adding a "-1" to the address, because the first one, with memcpy, failed the check, because it was missing the first byte and instead had one more byte at the end. I logged this adding a PRINTF in the fsl_flexspi.c READ and WRITE blocking functions: 

 

 

status_t FLEXSPI_WriteBlocking(FLEXSPI_Type *base, uint8_t *buffer, size_t size)
{
...
/* Write watermark level data into tx fifo . */
        if (size >= 8U * txWatermark)
        {
            for (i = 0U; i < 2U * txWatermark; i++)
            {
                base->TFDR[i] = *(uint32_t *)(void *)buffer;
                PRINTF("BUFFER TX[%d]: 0x%04x \r\n", i, *(uint32_t *)(void *)buffer);
                buffer += 4U;
            }

            size = size - 8U * txWatermark;
        }
        else
        {
            /* Write word aligned data into tx fifo. */
            for (i = 0U; i < (size / 4U); i++)
            {
                base->TFDR[i] = *(uint32_t *)(void *)buffer;
                PRINTF("else BUFFER TX[%d]: 0x%02x \r\n", i, buffer[i]);
                buffer += 4U;
            }
...
}

status_t FLEXSPI_ReadBlocking(FLEXSPI_Type *base, uint8_t *buffer, size_t size)
{
...
/* Read watermark level data from rx fifo. */
        if (size >= 8U * rxWatermark)
        {
            for (i = 0U; i < 2U * rxWatermark; i++)
            {
                *(uint32_t *)(void *)buffer = base->RFDR[i];
                PRINTF("<-- BUFFER RX[%d]: 0x%04x \r\n", i, *(uint32_t *)(void *)buffer);
                buffer += 4U;
            }

            size = size - 8U * rxWatermark;
        }
...
}

 

 

 

here are the logs:

 

 

BUFFER TX[0]: 0x3020100    <--- 0x00 is the first byte in WRITE
BUFFER TX[1]: 0x7060504 
BUFFER TX[0]: 0xb0a0908 
BUFFER TX[1]: 0xf0e0d0c 
BUFFER TX[0]: 0x13121110 
BUFFER TX[1]: 0x17161514 
BUFFER TX[0]: 0x1b1a1918 
BUFFER TX[1]: 0x1f1e1d1c 
BUFFER TX[0]: 0x23222120 
BUFFER TX[1]: 0x27262524 
BUFFER TX[0]: 0x2b2a2928 
BUFFER TX[1]: 0x2f2e2d2c 
BUFFER TX[0]: 0x33323130 
BUFFER TX[1]: 0x37363534 
BUFFER TX[0]: 0x3b3a3938 
BUFFER TX[1]: 0x3f3e3d3c 
BUFFER TX[0]: 0x43424140 
BUFFER TX[1]: 0x47464544 
BUFFER TX[0]: 0x4b4a4948 
BUFFER TX[1]: 0x4f4e4d4c 
BUFFER TX[0]: 0x53525150 
BUFFER TX[1]: 0x57565554 
BUFFER TX[0]: 0x5b5a5958 
BUFFER TX[1]: 0x5f5e5d5c 
BUFFER TX[0]: 0x63626160 
BUFFER TX[1]: 0x67666564 
BUFFER TX[0]: 0x6b6a6968 
BUFFER TX[1]: 0x6f6e6d6c 
BUFFER TX[0]: 0x73727170 
BUFFER TX[1]: 0x77767574 
BUFFER TX[0]: 0x7b7a7978 
BUFFER TX[1]: 0x7f7e7d7c 
BUFFER TX[0]: 0x83828180 
BUFFER TX[1]: 0x87868584 
BUFFER TX[0]: 0x8b8a8988 
BUFFER TX[1]: 0x8f8e8d8c 
BUFFER TX[0]: 0x93929190 
BUFFER TX[1]: 0x97969594 
BUFFER TX[0]: 0x9b9a9998 
BUFFER TX[1]: 0x9f9e9d9c 
BUFFER TX[0]: 0xa3a2a1a0 
BUFFER TX[1]: 0xa7a6a5a4 
BUFFER TX[0]: 0xabaaa9a8 
BUFFER TX[1]: 0xafaeadac 
BUFFER TX[0]: 0xb3b2b1b0 
BUFFER TX[1]: 0xb7b6b5b4 
BUFFER TX[0]: 0xbbbab9b8 
BUFFER TX[1]: 0xbfbebdbc 
BUFFER TX[0]: 0xc3c2c1c0 
BUFFER TX[1]: 0xc7c6c5c4 
BUFFER TX[0]: 0xcbcac9c8 
BUFFER TX[1]: 0xcfcecdcc 
BUFFER TX[0]: 0xd3d2d1d0 
BUFFER TX[1]: 0xd7d6d5d4 
BUFFER TX[0]: 0xdbdad9d8 
BUFFER TX[1]: 0xdfdedddc 
BUFFER TX[0]: 0xe3e2e1e0 
BUFFER TX[1]: 0xe7e6e5e4 
BUFFER TX[0]: 0xebeae9e8 
BUFFER TX[1]: 0xefeeedec 
BUFFER TX[0]: 0xf3f2f1f0 
BUFFER TX[1]: 0xf7f6f5f4 
BUFFER TX[0]: 0xfbfaf9f8 
BUFFER TX[1]: 0xfffefdfc 

<-- BUFFER RX[0]: 0x4030201    <--- 0x01 is the first byte in READ
<-- BUFFER RX[1]: 0x8070605 
<-- BUFFER RX[0]: 0xc0b0a09 
<-- BUFFER RX[1]: 0x100f0e0d 
<-- BUFFER RX[0]: 0x14131211 
<-- BUFFER RX[1]: 0x18171615 
<-- BUFFER RX[0]: 0x1c1b1a19 
<-- BUFFER RX[1]: 0x201f1e1d 
<-- BUFFER RX[0]: 0x24232221 
<-- BUFFER RX[1]: 0x28272625 
<-- BUFFER RX[0]: 0x2c2b2a29 
<-- BUFFER RX[1]: 0x302f2e2d 
<-- BUFFER RX[0]: 0x34333231 
<-- BUFFER RX[1]: 0x38373635 
<-- BUFFER RX[0]: 0x3c3b3a39 
<-- BUFFER RX[1]: 0x403f3e3d 
<-- BUFFER RX[0]: 0x44434241 
<-- BUFFER RX[1]: 0x48474645 
<-- BUFFER RX[0]: 0x4c4b4a49 
<-- BUFFER RX[1]: 0x504f4e4d 
<-- BUFFER RX[0]: 0x54535251 
<-- BUFFER RX[1]: 0x58575655 
<-- BUFFER RX[0]: 0x5c5b5a59 
<-- BUFFER RX[1]: 0x605f5e5d 
<-- BUFFER RX[0]: 0x64636261 
<-- BUFFER RX[1]: 0x68676665 
<-- BUFFER RX[0]: 0x6c6b6a69 
<-- BUFFER RX[1]: 0x706f6e6d 
<-- BUFFER RX[0]: 0x74737271 
<-- BUFFER RX[1]: 0x78777675 
<-- BUFFER RX[0]: 0x7c7b7a79 
<-- BUFFER RX[1]: 0x807f7e7d 
<-- BUFFER RX[0]: 0x84838281 
<-- BUFFER RX[1]: 0x88878685 
<-- BUFFER RX[0]: 0x8c8b8a89 
<-- BUFFER RX[1]: 0x908f8e8d 
<-- BUFFER RX[0]: 0x94939291 
<-- BUFFER RX[1]: 0x98979695 
<-- BUFFER RX[0]: 0x9c9b9a99 
<-- BUFFER RX[1]: 0xa09f9e9d 
<-- BUFFER RX[0]: 0xa4a3a2a1 
<-- BUFFER RX[1]: 0xa8a7a6a5 
<-- BUFFER RX[0]: 0xacabaaa9 
<-- BUFFER RX[1]: 0xb0afaead 
<-- BUFFER RX[0]: 0xb4b3b2b1 
<-- BUFFER RX[1]: 0xb8b7b6b5 
<-- BUFFER RX[0]: 0xbcbbbab9 
<-- BUFFER RX[1]: 0xc0bfbebd 
<-- BUFFER RX[0]: 0xc4c3c2c1 
<-- BUFFER RX[1]: 0xc8c7c6c5 
<-- BUFFER RX[0]: 0xcccbcac9 
<-- BUFFER RX[1]: 0xd0cfcecd 
<-- BUFFER RX[0]: 0xd4d3d2d1 
<-- BUFFER RX[1]: 0xd8d7d6d5 
<-- BUFFER RX[0]: 0xdcdbdad9 
<-- BUFFER RX[1]: 0xe0dfdedd 
<-- BUFFER RX[0]: 0xe4e3e2e1 
<-- BUFFER RX[1]: 0xe8e7e6e5 
<-- BUFFER RX[0]: 0xecebeae9 
<-- BUFFER RX[1]: 0xf0efeeed 
<-- BUFFER RX[0]: 0xf4f3f2f1 
<-- BUFFER RX[1]: 0xf8f7f6f5 
<-- BUFFER RX[0]: 0xfcfbfaf9 
<-- BUFFER RX[1]: 0xfffffefd    <--- 0xff is read twice (the block was erased) 

 

 

 

Why is this happening? I also tried to change the numbers of dummy cycles without any effect. 

Why I need to add a -1 in the address of the read instruction for having the correct data back? 

 

In the while i also tried to redo a new version of the evkmimxrt1020_flexspi_nor_config.c, for my project, with the new information, hoping there was enough (I tried also the commented values):

 

 

#define FLASH_DUMMY_CYCLES_QUAD 	0x04 //0x08 //0x06
#define ADDRESS_24BIT 				0x18
#define UNKNOWN_READ_PARAM			0x04

// #define FLASH_DUMMY_VALUE 0x02

const
    flexspi_nor_config_t
        qspiflash_config =
            {
                .memConfig =
                    {
                        .tag = FLEXSPI_CFG_BLK_TAG,
                        .version = FLEXSPI_CFG_BLK_VERSION,
                        .readSampleClksrc=kFlexSPIReadSampleClk_LoopbackFromDqsPad,
                        .csHoldTime = 3u,
                        .csSetupTime = 3u,
                        .controllerMiscOption = 0, //(1u << kFlexSpiMiscOffset_SafeConfigFreqEnable),
                        .deviceType = kFlexSpiDeviceType_SerialNOR,
                        .sflashPadType = kSerialFlash_4Pads,
                        .serialClkFreq = kFlexSpiSerialClk_80MHz, //kFlexSpiSerialClk_100MHz //kFlexSpiSerialClk_30MHz
                        .sflashA1Size = 1u * 1024u * 1024u,
                        /* Enable flash configuration feature */
                        //.configCmdEnable = 1u,
                        //.configModeType[0] = kDeviceConfigCmdType_QuadEnable, // kDeviceConfigCmdType_Generic,
                        /* Set configuration command sequences */
                        /*.configCmdSeqs[0] =
                            {
                                .seqNum = 1,
                                .seqId = 12,
                                .reserved = 0,
                            },*/
                        /* Prepare setting value for Read Register in flash */
                        //.configCmdArgs[0] = 0x40, //((FLASH_DUMMY_VALUE << 3) | 0xE0),
                        .lookupTable =
                            {
                                // Read (Fast Read Quad I/O)
                                FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, ADDRESS_24BIT),
                                FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, FLASH_DUMMY_CYCLES_QUAD, READ_SDR, FLEXSPI_4PAD, UNKNOWN_READ_PARAM),

								// READ Normal
								//FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x03, RADDR_SDR, FLEXSPI_1PAD, 0x18),
								//FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD, 0x04, STOP, FLEXSPI_1PAD, 0),
                            },
                    },
                .pageSize = 256u,
                .sectorSize = 4u * 1024u,
                .ipcmdSerialClkFreq = 1u,
                .blockSize = 64u * 1024u,
                .isUniformBlockSize = false,
};

 

 

 

but ended up with this situation, still not working but with different behavior, because now i see some different address in the registers:

image.png

image.png

but from the oscilloscope, i can see that the flash SCLK line, starts at 30MHz and then goes up to 80MHz as expected, then after a short amount, it stops clocking.  

The only things i still don't understand about the LUT is the last param in the READ LUT, 0x04, which i defined as "UNKNOWN_READ_PARAM" in my code abobe. What is the meaning of this parameter?

 

 

For completeness of information, I also report what I have modified to reorganize the flexRAM of the device (in case this was the cause of the problem):

 

  • fsl_flexspi_nor_boot.c

 

 

/*
 * Copyright 2017-2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_flexspi_nor_boot.h"

/* Relocating Flex-RAM */
extern void ResetISR(void);

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.xip_device"
#endif

#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__)
__attribute__((section(".boot_hdr.ivt"), used))
#elif defined(__ICCARM__)
#pragma location = ".boot_hdr.ivt"
#endif
/*************************************
 *  IVT Data
 *************************************/
const ivt image_vector_table = {
    IVT_HEADER,                    /* IVT Header */
	//IMAGE_ENTRY_ADDRESS,           /* Image Entry Function */
	(uint32_t)ResetISR,			   /* Image Entry Function */
    IVT_RSVD,                      /* Reserved = 0 */
    (uint32_t)DCD_ADDRESS,         /* Address where DCD information is stored */
    (uint32_t)BOOT_DATA_ADDRESS,   /* Address where BOOT Data Structure is stored */
    (uint32_t)&image_vector_table, /* Pointer to IVT Self (absolute address */
    (uint32_t)CSF_ADDRESS,         /* Address where CSF file is stored */
    IVT_RSVD                       /* Reserved = 0 */
};

#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__)
__attribute__((section(".boot_hdr.boot_data"), used))
#elif defined(__ICCARM__)
#pragma location = ".boot_hdr.boot_data"
#endif
/*************************************
 *  Boot Data
 *************************************/
const BOOT_DATA_T g_boot_data = {
    FLASH_BASE,  /* boot start location */
    FLASH_SIZE,  /* size */
    PLUGIN_FLAG, /* Plugin flag*/
    0xFFFFFFFFU  /* empty - extra data word */
};
#endif

 

 

  • startup_mimxrt1021.c (ResetISR)

 

 

...
//*****************************************************************************
// Reset entry point for your code.
// Sets up a simple runtime environment and initializes the C/C++
// library.
//*****************************************************************************
__attribute__ ((naked, section(".after_vectors.reset")))
void ResetISR(void) {
    // Disable interrupts
    __asm volatile ("cpsid i");
    //__asm volatile ("MSR MSP, %0" : : "r" (&_vStackTop) : );

	/* Reallocating the FlexRAM */
	/*  https://community.nxp.com/t5/i-MX-RT-Knowledge-Base/Reallocating-the-FlexRAM/ta-p/1117649
	 *  DTC=192KB, ITC=0KB, OC=64KB
	 *	{O,O,D,D,D,D,D,D}
	 *	LSB --------> MSB
	 *
	 *	{10,10,10,10,10,10,01,01}
	 *	MSB ---------------> LSB
	 */
	__asm (".syntax unified\n"

	"LDR R0, =0x2002fffc\n"//load initial value of stack pointer into R0 = ultima locazione usabile della stack mem
	"MSR MSP,R0\n" //re-initialize stack pointer by new value

	"LDR R0, =0x400ac044\n"//Address of register IOMUXC_GPR_GPR17
	"LDR R1, =0xaaa5\n"//FlexRAM configuration DTC = 192KB, ITC = 0KB, OC = 64KB
	"STR R1,[R0]\n"

	"LDR R0,=0x400ac040\n"//Address of register IOMUXC_GPR_GPR16
	"LDR R1,[R0]\n"
	"ORR R1,R1,#4\n"//The 4 corresponds to setting the FLEXRAM_BANK_CFG_SEL bit in register IOMUXC_GPR_GPR16
	"STR R1,[R0]\n"

	".syntax divided\n");

#if defined (__USE_CMSIS)
...​

 

 

  • board.c (MPU Settings)

 

 

...
/* MPU configuration. */
void BOARD_ConfigMPU(void)
{
    /* Disable I cache and D cache */
    if (SCB_CCR_IC_Msk == (SCB_CCR_IC_Msk & SCB->CCR))
    {
        SCB_DisableICache();
    }
    if (SCB_CCR_DC_Msk == (SCB_CCR_DC_Msk & SCB->CCR))
    {
        SCB_DisableDCache();
    }

    /* Disable MPU */
    ARM_MPU_Disable();

    /* MPU configure:
     * Use ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable,
     * SubRegionDisable, Size)
     * API in mpu_armv7.h.
     * param DisableExec       Instruction access (XN) disable bit,0=instruction fetches enabled, 1=instruction fetches
     * disabled.
     * param AccessPermission  Data access permissions, allows you to configure read/write access for User and
     * Privileged mode.
     *      Use MACROS defined in mpu_armv7.h:
     * ARM_MPU_AP_NONE/ARM_MPU_AP_PRIV/ARM_MPU_AP_URO/ARM_MPU_AP_FULL/ARM_MPU_AP_PRO/ARM_MPU_AP_RO
     * Combine TypeExtField/IsShareable/IsCacheable/IsBufferable to configure MPU memory access attributes.
     *  TypeExtField  IsShareable  IsCacheable  IsBufferable   Memory Attribute    Shareability        Cache
     *     0             x           0           0             Strongly Ordered    shareable
     *     0             x           0           1              Device             shareable
     *     0             0           1           0              Normal             not shareable   Outer and inner write
     * through no write allocate
     *     0             0           1           1              Normal             not shareable   Outer and inner write
     * back no write allocate
     *     0             1           1           0              Normal             shareable       Outer and inner write
     * through no write allocate
     *     0             1           1           1              Normal             shareable       Outer and inner write
     * back no write allocate
     *     1             0           0           0              Normal             not shareable   outer and inner
     * noncache
     *     1             1           0           0              Normal             shareable       outer and inner
     * noncache
     *     1             0           1           1              Normal             not shareable   outer and inner write
     * back write/read acllocate
     *     1             1           1           1              Normal             shareable       outer and inner write
     * back write/read acllocate
     *     2             x           0           0              Device              not shareable
     *  Above are normal use settings, if your want to see more details or want to config different inner/outter cache
     * policy.
     *  please refer to Table 4-55 /4-56 in arm cortex-M7 generic user guide <dui0646b_cortex_m7_dgug.pdf>
     * param SubRegionDisable  Sub-region disable field. 0=sub-region is enabled, 1=sub-region is disabled.
     * param Size              Region size of the region to be configured. use ARM_MPU_REGION_SIZE_xxx MACRO in
     * mpu_armv7.h.
     */

    /*
     * Add default region to deny access to whole address space to workaround speculative prefetch.
     * Refer to Arm errata 1013783-B for more details.
     *
     */
    /* Region 0 setting: Instruction access disabled, No data access permission. */
    MPU->RBAR = ARM_MPU_RBAR(0, 0x00000000U);
    MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_NONE, 0, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_4GB);

    /* Region 1 setting: Memory with Device type, not shareable, non-cacheable. */
    MPU->RBAR = ARM_MPU_RBAR(1, 0x80000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_512MB);

    /* Region 2 setting: Memory with Device type, not shareable,  non-cacheable. */
    MPU->RBAR = ARM_MPU_RBAR(2, 0x60000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_512MB);

#if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)
    /* Region 3 setting: Memory with Normal type, not shareable, outer/inner write back. */
    MPU->RBAR = ARM_MPU_RBAR(3, 0x60000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_RO, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_1MB);
#endif

    /* Region 4 setting: Memory with Device type, not shareable, non-cacheable. */
    MPU->RBAR = ARM_MPU_RBAR(4, 0x00000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_1GB);

    /* **********************
	 * Relocating Flex-RAM
	 * https://community.nxp.com/t5/i-MX-RT-Knowledge-Base/Reallocating-the-FlexRAM/ta-p/1117649
	 * https://community.nxp.com/t5/i-MX-Processors/RT1021-reallocate-FlexRAM/m-p/1667222#M207407
	 * **********************
	 */
	/* Region 5 setting: Memory with Normal type, not shareable, outer/inner write back */
	/* ITC Memory */
	MPU->RBAR = ARM_MPU_RBAR(5, 0x00000000U);
	MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, 0);

	/* Region 6 setting: Memory with Normal type, not shareable, outer/inner write back */
	/* DTC Memory */
	MPU->RBAR = ARM_MPU_RBAR(6, 0x20000000U);
	MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_256KB);

	/* Region 7 setting: Memory with Normal type, not shareable, outer/inner write back */
	/* OC Memory */
	MPU->RBAR = ARM_MPU_RBAR(7, 0x20200000U);
	MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_64KB);
	/************************/

    /* Region 8 setting: Memory with Normal type, not shareable, outer/inner write back */
    MPU->RBAR = ARM_MPU_RBAR(8, 0x80000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_32MB);

    /* Region 10 setting: Memory with Device type, not shareable, non-cacheable */
    MPU->RBAR = ARM_MPU_RBAR(10, 0x40000000);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_4MB);

    /* Enable MPU */
    ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk);

    /* Enable I cache and D cache */
    SCB_EnableDCache();
    SCB_EnableICache();
}
...​

 

 

I believe I am close to the solution, I hope this information will be useful to understand what is not working… thanks in advance to everyone!

0 Kudos
Reply
354 Views
salva214
Contributor III

Hello everyone, I have some additional information to share.

While searching online for similar cases, I came across several threads discussing this issue. However, there seems to be limited documentation from NXP on this topic. Many of these threads suggest using the MCUXpresso Secure Provisioning tool or the MCU Boot Utility (which I am already familiar with), but none of these methods have allowed me to run my code. Here are some of the links I found:

 

Solved: XIP from QSPI NOR Flash on MIMXRT1062 - NXP Community

RT1050 evkb Unable to download and debug QSPI Flash - NXP Community

How to Enable Debugging for FLEXSPI NOR Flash (nxp.com)

Here are the steps I followed:

  1. Establish a connection with the ROM bootloader.
  2. image.pngFind a valid NOR flash configuration and verify it.
  3. image.pngProduce a bootable image of the NOR flash configuration and write it.image.png
  4. Build and write everything to the MCU.image.pngimage.png
  5. Read back the memory and verify it.

image.png

image.png

 

At this point, if I reboot with BOOT[1:0] = 0b00, nothing happens. The device is neither in ROM bootloader mode (as I can no longer see it via the MCU Boot Utility) nor is it running. I also cannot debug the code via MCUXpresso, as I consistently end up in the same situation.

image.png

 

Here is my Memory configuration, which works on the EVK:

image.png

Note: I need more DTC (192K) than ITC RAM (0 needed, but 4 is because otherwise MCUXpresso gives me an error), allowing the OC to be the minimum allowed (64K)

And here are my Preprocessor directives (-D):

image.png

 

I hope this information provides more detail than before.

Thank you in advance!

0 Kudos
Reply
327 Views
diego_charles
NXP TechSupport
NXP TechSupport

Hi @salva214 

Thank you for effort adding this extra information!

What is the value of the boot mode and boot config in the  SRC register? Please check this register via debug in the MCUXpresso IDE.

Diego

0 Kudos
Reply
241 Views
salva214
Contributor III

Hi @diego_charles,

I just realized that I responded incorrectly to your last post.
Can I ask you if you can help me with the information that I didn’t understand (In the previous post in bold)?

Thanks

0 Kudos
Reply