i.MX RT Crossover MCUs Knowledge Base

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

i.MX RT Crossover MCUs Knowledge Base

Discussions

Sort by:
This document describes how to program a bootable image into the recovery Flash device using i.MX RT600 MCUs. Click here to access the full application note.
View full article
NXP Updated the Hardware Development Guide for the MIMXRT1160/1170 Processor (MIMXRT1170HDUG) The main difference is: Updated capacitance value of VDDA_1P8_IN in Table 1 and Table 2 From 0.1uF to 1uF     This will help improve the robustness of the circuit at low temperatures.
View full article
How to create RT AVB switch&endpoint platform 1. Abstract In the previous article, it talked about how to use a single-point RT1170 as a talker and a single-point RT1170 as a listener, and connect the two boards directly to implement AVB endpoint testing. However, in actual use, many applications are multipoint to multipoint, but AVB switch is required. Therefore, based on the previous article, this article adds another listener endpoint and AVB switch to implement an AVB platform with one talker and two listeners. Fig 1 The AVB switch can be a third-party AVB switch product. Of course, you can also consider using NXP's upcoming new product RT1180. This chip has AVB/TSN switch function, and our RT1180 supporting stack has also been released. 2. Platform creation This article will use two AVB switches to do AVB testing: one uses the NXP official MIMXRT1180-EVK as an AVB switch, and the other uses the third-party product MOTU's AVB switch. The endpoints use three NXP MIMXRT1170-EVK boards, one for talker configuration and the other two for listener configuration. For the configuration of RT1170 as endpoint, that is, talker and listener, you can refer to the previous article: RT1170 AVB fresh tasting Here you can directly start quickly, take the avb_app.bin prepared in the stack and burn it directly to MIMXRT1170-EVK for talker and listener configuration. Of course, if there are some customized functions that modify the source code, you can also refer to the above article to recompile, generate the avb_app.bin file and then burn it. 2.1 Software and hardware Hardware:       MOTU AVB SWITCH(switch)       MIMXRT1180-EVK*1(switch)       MIMXRT1170-EVK*3(1: talker, 2: listener), hardware need to be modified, refer to the previous document Software: RT1170 AVB/TSN stack: genavb_tsn-mcuxpresso-SDK_2_13_0-5_6_1: https://mcuxpresso.nxp.com/download/52643189c4d74a7b26b8e096ab28df0e RT1180 AVB/TSN stack: genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0 : https://mcuxpresso.nxp.com/download/c584c33a8d4f55c29b5505b9be8f537a   2.2 Configure RT1170 AVB endpoints Directly burn the files in avbstack: genavb_tsn-mcuxpresso-SDK_2_13_0-5_6_1\binaries\genavb-avb_audio_app-evaluation-freertos_rt1176-5_6_1.tar\genavb-avb_audio_app-evaluation-freertos_rt1176-5_6_1\release\avb_app.bin to the three MIMXRT1170-EVK development boards and enter the serial download mode to program: Fig 2 The three boards are burned with the same code. After burning, let the board enter the internal boot mode and configure the talker and listener through the serial port. After the code is burned successfully, the onboard serial port will keep sending log information. You only need to enter INSERT on the keyboard to enter the shell command line state. 2.2.1 1MIMXRT1170-EVK do the talker configuration cd .. ls mkdir avb_app write avb_app/mclock_role 0 mkdir avdecc write avdecc/btb_mode 0 mkdir fgptp write fgptp/gmCapable 1 mkdir port0 write port0/hw_addr 00:22:33:44:55:66 2.2.2 2 MIMXRT1170-EVK do the listener configuration cd .. ls mkdir avb_app write avb_app/mclock_role 1 mkdir avdecc write avdecc/btb_mode 1 write avdecc/talker_id 0x00049f4455660000 2.3 AVB Switch configuration     The following are two SWITCH configuration connections: 2.3.1 MOTU AVB Switch Use MOTU AVB switch as the AVB switch connection block diagram: Fig 3   The physical board connections are as follows: Fig 4 For the dedicated AVB switch, no specific configuration is required, because you can think of it as a switch with AVB function, which can realize the forwarding function of AVB data. You only need to connect the 1G network port of a talker and the 1G network ports of two listeners to the network port of MOTU AVB SWITCH. Then as long as the functions of the talker and the listener are normal, the entire audio transmission can be normal. The talker is responsible for collecting the audio data information of the microphone and then forwarding it to the two listeners for playback. Of course, the two listeners need to be connected to the speakers respectively. 2.3.2 RT1180 AVB switch For the configuration of RT1180 AVB switch, there are two methods: quick start and self-compilation. If there is no change in the source code, you can directly use the bin file that comes with the stack. Here you need to pay attention to select the correct bin file. RT1180 has two cores: CM33 and CM7 cores. The CM33 image supports the TSN/AVB bridge function, that is, the switch, and the CM7 image supports the TSN endpoint function.    MIMXRT1180-EVK contains multi-network ports, the situation is: Fig 5 Fig 6 Therefore, when using the AVB switch network port, you need to pay attention to using ENET0, 1, 2, and 3 ports. The connection diagram of using MIMXRT1180-EVK as the AVB switch network port is as follows: Fig 7 The actual connection diagram is as follows: Fig 8 To implement the RT1180 code, you need to download the RT1180 M33 TSN bridge code to the MIMXRT1180-EVK board. If the source code of the AVB/TSN stack does not need to be modified, you can use the ready-made bin file for testing: genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0\binaries\genavb-tsn_app-evaluation-freertos_rt1189_cm33-6_0_0\release\tsn_app.bin There are many ways to burn, you can use tools or command line methods. The tool can be MCUBootutility or the official SEC tool. Here we choose to use the MCUBootutility tool, download link: https://github.com/JayHeng/NXP-MCUBootUtility/releases/tag/v6.2.0 If you use the SEC tool to download, you can refer to the stack documentation: genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0\doc\ NXP_GenAVB_TSN_MCUXpresso_User_s_Guide_6_0_rev0.pdf, chapter 11 Flash Image booting. When use the MCUBootutility tool, it needs to do the modification: \NXP-MCUBootUtility-6.2.0\src\targets\MIMXRT1189 \MIMXRT1189\bltargetconfig.py Modify: #flexspiNorMemBase0 = 0x38000000 # CM33 Secure #flexspiNorMemBase0Ns = 0x28000000 # CM33 Non-Secure To: flexspiNorMemBase0 = 0x28000000 # CM33 Non-Secure flexspiNorMemBase0Ns = 0x38000000 # CM33 Secure Fig 9 Burn the tsn_app.bin to the RT1180 address 0x2800b000。 Let the MIMXRT1180-EVK board enter serial download mode,SW5:1-OFF,2-OFF,3-OFF,4-ON. Then, find another usb cable to connect J33 to do the code flash downloading. After the code is programmed, need to enter the internal boot mode for QSPI: SW5:1-OFF,2-ON,3-OFF,4-OFF. This completes the burning of the app with AVB switch function. This code does not need to enter the shell to configure the filesystem like RT1170. For the RT1180 bridge code, after burning, the switch function will be built-in after restarting. Of course, if you need to recompile your own project, you can directly refer to the stack documentation: NXP_GenAVB_TSN_MCUXpresso_User_s_Guide_6_0_rev0.pdf. If you use Linux system to compile, the method is the same as RT1170, three steps:      (1) Patch the AVB stack for the RT1180 SDK     (2)add two soft links to the RT1180 AVB stack, one for the board SDK and the other for the AVB SDK source code. The structure is as follows:   Fig 10    (3) At last, build ./ build_release.sh \genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0\genavb-apps-freertos-6_0_0.tar\genavb-apps-freertos-6_0_0\boards\evkmimxrt1180\demo_apps\avb_tsn\tsn_app\cm33\armgcc\ build_release.sh Then, it will generate the according tsn_app.bin file. 3. AVB network data packet analysis I have always wanted to check the AVB network data packets, so I thought of the following method to do it. I also found a general network switch that can package some of the network ports to specific network ports. This method is used here just to check the basic packets. In principle, the general switch does not have the AVB physical layer function, so it should have some impact on the synchronization function. However, due to the limitation of the equipment, this article only has a basic understanding of the AVB data packet structure. Prepare a switch with port mirror function: NETGERA plus switch ProSAFE GS105E. Then configure the switch to mirror the data of ports 2 and 3 to port 1: Fig 11 Then the entire AVB system connection diagram is as follows: Fig 12 The physical connection diagram is as follows: Fig 13 Open the entire system platform and let the system function run, that is, the talker endpoint has sound input and the amplifiers of the two listener endpoints have output. Open the wireshark software on the PC and capture the packets. The captured situation is as follows: Fig 14 As you can see, there are many AVTP packets, and there are two destination addresses. To analyze AVTP packets, you must first know what the standard AVTP packets are like. The standard packets have the following structure: Fig 15 Next, open the wireshark software, configure the network port to be captured, and compare the captured data packets: Fig 16 As you can see, the whole packet is basically captured, but the details, such as VLAN tag and IEC 61883 header, are not present. This is probably caused by the physical layer of ordinary switches cannot support AVB. However, the audio data above can still be seen, and it is indeed dual-channel, but the data is only transmitted through one channel. Therefore, for the RT1170 listener, although a dual-channel speaker is connected, the two speakers correspond to the left and right channels, but when listening, only one speaker channel has sound, and the other has no sound. This is consistent with the captured data packet. The source of this is that the stack code uses one channel for microphone acquisition, and although the audio is configured with two channels, there is actually only one channel with data. So far, the architecture and test of the AVB switch&endpoint platform have been realized. The test effect can be viewed in the video.    
View full article
1.    Abstract When using the RT600 SDK USB composite example, the customer found that the default HS interval was 125us, and the code was: mimxrt685audevk_dev_composite_hid_audio_unified_bm. However, in actual applications, the 125us packet interval for data transmission will cause a large interrupt load on the CPU, so the customer hopes to change the interval to a larger during, such as 1ms. After changing interval to 1ms, it is found that the data packet can be sent to the RT chip, but there is indeed a problem with the playback on the RT side, speaker no voice. Changing it to 500us in synchronous mode is possible work, but at 500us, the customer's CPU load still reaches more than 80%, which is not convenient for subsequent application code expansion, so it is still hoped to achieve a 1ms method. This article will give the transmission of different intervals of UAC and provide a solution for 1ms intervals. 2.    Test situation    Board:MIMXRT685-AUD-EVK    SDK:SDK_2_15_000_MIMXRT685-AUD-EVK    USB Analyzer:Lecroy USB-TOS2-A01-X 2.1 Platform connection situation First, given the connection between MIMXRT685-AUD-EVK, USB analyzer and audio source. This article mainly tests the RT685 UAC speaker function, that is, USB transmits audio source data to RT685, and plays the audio source through a player (which can be headphones), or speaker. The J7 USB port of EVK is connected to the A port of Lecroy, and the B port of Lecroy is connected to the audio source, which can be a PC or a mobile phone. If using a PC, the speaker needs to be selected as USB AUDIO+HID DEMO, because the name of the board after UAC enumeration is USB AUDIO+HID DEMO. The other end of Lecroy is connected to the PC for transmitting USB bus data. The specific connection diagram is as follows:      Fig 1 EVK USB analyzer connection 2.2 Different audio interval data package situation This chapter gives the modifications under different intervals, as well as the results of USB analyzer packet capture. The clock system of the audio synchronous/isochronous audio endpoint can be synchronized through SOF. The USB 1.0 sampling rate must be locked to the 1ms SOF beat. The USB2.0 high-speed HS endpoint can be locked to the 125us SOF beat. If you want to change the interval, it is usually a multiple of 125us, that is, it can be 250us, 500us, 1ms, etc. The modification method is usually to directly modify the USB stack's usb_audio_config.h:   #define HS_ISO_OUT_ENDP_INTERVAL (0x01) The relationship between HS_ISO_OUT_ENDP_INTERVAL and the real during time is: 125us*2^( HS_ISO_OUT_ENDP_INTERVAL -1) So: HS_ISO_OUT_ENDP_INTERVAL=1 : 125us HS_ISO_OUT_ENDP_INTERVAL=2 : 250us HS_ISO_OUT_ENDP_INTERVAL=3 : 500us HS_ISO_OUT_ENDP_INTERVAL=4 : 1000us The following are the four situations mentioned above respectively through USB analyzer packet capture test. 2.2.1 125us interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x01) Fig 2 125us interval It can be seen that when the interval is configured to 125us, the SOF beat interval in front of each OUT packet is 125us, and the length of the OUT data packet is 24Byte. The data in this 24Byte packet is the actual audio data. In this case, the playback is normal   2.2.2 250us interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x02) The packet capture configured as 250us is shown in the figure below. It can be seen that the SOF beat interval in front of the two OUT packets is 250us, and the length of the OUT data packet is 48 Byte. In other words, as the interval increases, the length of the data packet also increases proportionally, that is, the transmission time is longer, but a packet contains more data packets. At this time, the RT side also needs to provide more USB receiving buffers to receive data. Fig 3 250us interval This situation, the speaker play normally, have the audio sound. 2.2.3 500us interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x03) Fig 4 500us interval SYNC mode At this time, you can see that the data packet has become 96 Bytes, and the data content is also normal audio data, but the playback has problems and no sound can be heard. However, if you configure: #define USB_DEVICE_AUDIO_USE_SYNC_MODE (0U) It is not the SYNC mode, it can hear the audio sound, the packet situation is: Fig 5 500us interval no SYNC mode However, the asynchronous mode also has problems. After a long period of operation, it may become out of sync and the playback may become stuck. Therefore, it is still necessary to use the 1ms method in the synchronous mode. 2.2.4 1ms interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x04) Fig 6 interval 1ms It can be seen that the data packet length has become 192 bytes, and the SOF beat interval in front of the OUT packet has indeed become 1ms. The audio data packet also looks like normal audio data, so the audio data here is successfully transmitted from USB to RT. However, the playback is abnormal and no sound can be heard. 3. 1ms interval solution Now let's debug the project to find the problem. Because the USB analyzer can show that the audio data of the USB bus is actually transmitted, we must first check whether the USB receives the data packet in the kUSB_DeviceAudioEventStreamRecvResponse event in USB_DeviceAudioCompositeCallback of audio_unified.c, whether the data packet length is correct, and whether the data content looks like audio data. The following is the debug result: Fig 7 data packet received situation So from this point of view, the USB interface data packet is received, and the data looks like real audio data. If it is abnormal data, it is either all 0 or irregular data that changes, but the test result cannot be played. So now to check the I2S playback function, composite.h file, TxCallback function: Fig 8 I2S play data buffer We can see, the real data transfer to the I2S data buffer are always 0, and the reality should be g_composite.audioUnified.startPlayFlag none zero, and also use: s_TxTransfer.dataSize = g_composite.audioUnified.audioPlayTransferSize; s_TxTransfer.data=audioPlayDataBuff + g_composite.audioUnified.tdReadNumberPlay; But, after testing, audioPlayDataBuff data are also 0. So the issue should be the USB received side, receive the data, but when save the data to the audioPlayDataBuff have issues, go back to audio_unified.c Fig 9 USB_DeviceAudioCompositeCallback Fig10 USB_AudioSpeakerPutBuffer After testing, in the Fig 9, audioUnified.tdWriteNumberPlay never larger than g_deviceAudioComposite->audioUnified.audioPlayTransferSize * AUDIO_CLASS_2_0_HS_LOW_LATENCY_TRANSFER_COUNT=192*6   #define AUDIO_CLASS_2_0_HS_LOW_LATENCY_TRANSFER_COUNT \ (0x06U) /* 6 means 6 mico frames (6*125us), make sure the latency is smaller than 1ms for sync mode */ The low latency here is a delay buffer. Therefore, the USB cache data must at least be able to hold the delayed data packet. Unit 1 represents an interval frame. Now it is changed to an interval of 1ms, 6 delay units, which means that the delay here is 6ms. Here, the receiving buffer size needs to be larger, which is related to the following definition: Fig 11 USB device callback g_composite.audioUnified.audioPlayBufferSize = AUDIO_PLAY_BUFFER_SIZE_ONE_FRAME * AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT; #define AUDIO_PLAY_BUFFER_SIZE_ONE_FRAME AUDIO_OUT_TRANSFER_LENGTH_ONE_FRAME #define AUDIO_OUT_FORMAT_CHANNELS (0x02U) #define AUDIO_OUT_FORMAT_SIZE (0x02) #define AUDIO_OUT_SAMPLING_RATE_KHZ (48) /* transfer length during 1 ms */ #define AUDIO_OUT_TRANSFER_LENGTH_ONE_FRAME \ (AUDIO_OUT_SAMPLING_RATE_KHZ * AUDIO_OUT_FORMAT_CHANNELS * AUDIO_OUT_FORMAT_SIZE) #define AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT \ (2U) /* 2 units size buffer (1 unit means the size to play during 1ms) */ Here, try to set larger AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT, let it should be at least can put 6ms data, as 1 unit is 1ms play data size, and it needs to have more than 6ms, so, set to 12 unit, the modified definition: #define AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT 12 Build the project and download it, now capture the USB bus data again: Fig 12 1ms interval data packet play successfully   This data waveform is the data that can be played normally. Below is the video of the MIMXRT685-AUD-EVK test. The attachment provides two demos of RT685 and RT1170, both modified to 1ms interval, and the modification method of RT1170 is exactly the same 4.    Summarize Whether it is RT600, RT1170, or more precisely the UAC code of the entire RT series, if you need to modify the interval, the main focus is on the following macros in usb_audio_config.h, the following is for 1ms interval: #define HS_ISO_OUT_ENDP_INTERVAL (0x04)//(0x01)//(0X04) #define AUDIO_CLASS_2_0_HS_LOW_LATENCY_TRANSFER_COUNT \ (0x06U) /* 6 means 16 mico frames (6*125us), make sure the latency is smaller than 1ms for ehci high speed */ #define AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT \ (12U)//(2U) /* 2 units size buffer (1 unit means the size to play during 1ms) */ Test video:
View full article
The RT1170 supports the ability to trigger dual ADC’s in SyncMode or AsyncMode via the ADC External Trigger Control (ADC_ETC): In SyncMode, ADC1 and ADC2 are controlled by the same trigger source. In AsyncMode, ADC1 and ADC2 are controlled by separate trigger source. In AsyncMode (TRIGa_CTRL[SYNC_MODE]==0), the ADC conversion clock frequency maximum is 88 MHz, but in SyncMode (TRIGa_CTRL[SYNC_MODE]==1), the ADC conversion clock frequency must be constraint to a lower frequency due to switching noise inherent to its design architecture.  Reducing the conversion clock frequency reduces the switching noise that is observed. NXP is currently conducting further characterization in order to specify the maximum frequency of conversion in SyncMode across process, voltage, and temperature.  However, on typical samples at room temperature 60 MHz is the maximum frequency.
View full article
RT1170 camera CSI Y8 format modification 1.Abstract RT1170's CSI can support YUV format. The so-called YUV is divided into three components: Y represents luminance, that is, grayscale value; UV represents chrominance, which describes chroma and saturation. Similar to RGB, YUV is also a color encoding method, which can separate luminance information Y from chroma information UV. If you want to display black and white, you can have no UV information, only Y information, that is, Y800=Y8, and you can also display the complete image. For RT1170 YUV, the official SDK provides demo based on the YUV444 format, but in actual use, some customers need the function of the Y8 format, so how should they configure it based on the existing YUV SDK? From the reference manual of RT1170, you can see the following information: Fig 1 This description can be understood as requiring the Y8 mode, as long as the configuration: CSI_CR20[BINARY_EN]=0 CSI_CR20[BIG_END]=1 However, in reality, with this configuration, the original YUV code cannot display the camera data. So how should the camera's Y8 configuration be done to display black and white images on the LCD? This article will give a detailed explanation. 2. RT1170 CSI Camera Y8 format configuration and testing 2.1 Hardware and software situation Board:MIMXRT1170-EVK REV C4 LCD:  RK055AHD091 Camera:OV5640 Code:SDK_2_15_000_MIMXRT1170-EVK\boards\evkmimxrt1170\driver_examples\csi\mipi_yuv\cm7 IDE: MCUXPresso IDE v11.9.0 2.2 Y8 formation configuration   In fact, for CSI_CR20 configuration, you also need to enable the Histogram function, which is the following register bits: Fig 2 Here, based on the current SDK demo evkmimxrt1170_csi_mipi_yuv_cm7 demo, modify it to the Y8 format, list the modification points, mainly modify the file:csi_mipi_yuv.c (1) static void DEMO_InitPxp(void) Modify: PXP_SetCsc1Mode(DEMO_PXP, kPXP_Csc1YCbCr2RGB); To: PXP_SetCsc1Mode(DEMO_PXP, kPXP_Csc1YUV2RGB); If this item is not modified, LCD will just display the Green color. (2)static void DEMO_InitCamera(void) Before BOARD_InitMipiCsi(); Add the this code: CSI->CR20 |= CSI_CR20_QRCODE_EN_MASK | CSI_CR20_HISTOGRAM_EN_MASK; Here, didn’t configure CSI_CR20[BINARY_EN]=0, as after reseting, this bit is default to 0. If in the practical usage, this bit is modified to 1, then here, need to modify BINARY_EN to 0, it means the format is Y8, not Y1. The reason that can’t display the correct Y8 previously, is caused by the bit HISTOGRAM_EN is not set. (3) static void DEMO_CSI_MIPI_YUV(void) Modify structure psBufferConfig as follows: pxp_ps_buffer_config_t psBufferConfig = { .pixelFormat = kPXP_PsPixelFormatY8, //kPXP_PsPixelFormatYUV1P444, /* Note: This is 32-bit per pixel */ .swapByte = false, .bufferAddrU = 0U, .bufferAddrV = 0U, .pitchBytes = DEMO_CAMERA_WIDTH,//DEMO_CAMERA_WIDTH * DEMO_CAMERA_BUFFER_BPP,// }; Mainly 2 points: .pixelFormat = kPXP_PsPixelFormatY8, .pitchBytes  = DEMO_CAMERA_WIDTH, If you only change the pixel format to Y8, but pitchBytes is not changed to the camera width, the resulting LCD display will be a small strip on the top, instead of the entire LCD screen showing the camera's Y8 format black and white image. So far, all Y8-related modification projects have been completed. Finally, it should be noted that the default SDK LCD display is not the one selected in this article: RK055AHD091. So you need to modify the DEMO_PANEL macro in display_support.h to the following: #define DEMO_PANEL DEMO_PANEL_RK055AHD091 Then, build the project, and download it to the board MIMXRT1170-EVK. 2.3 Test result after modification Below we use the same color picture to test the YUV and Y8 display effects in front of the camera. here are the pictures:  the camera format of the picture on the left is YUV444, and the picture on the right is in Y8 format. You can see that the left one is in color, and the right one is in black and white. The black and white Y8 camera data acquisition and LCD display have been successfully completed.
View full article
RT1170 CMSIS DAP+IDE debug based on ECC enabled 1.    Abstract This article aims to solve the problem that after enabling the ECC function, RT1170 cannot be debugged using CMSIS DAP in the three major IDEs (MCUXpresso, IAR, MDK). ECC is enabled by burning the relevant fuses and enabling ROM preloading, which means that the ROM will help initialize the RAM. However, in actual use, it is found that different debuggers have different appearances on the IDE. For example, Segger JLINK can directly implement debugging, but when CMSIS DAP is combined with the three major IDEs, there will be a problem that the code cannot be debugged after being downloaded to RAM. Here, taking MCUXpresso IDE as an example, after burning the ECC-related fuses on the MIMXRT1170-EVK board, if the project is burned to RAM, it can be debugged directly. However, if the project is burned to flash, there will be problems with flashloader: Fig 1    So is this problem caused by the mismatch of flashloader, or does it require additional operations? This article will give a specific solution! 2. RT1170 ECC basic enable and solution 2.1 RT1170 ECC basic    ECC stands for Error Correcting Code, which can detect and correct memory errors. So what ECC does RT1170 have? They are: MECC64, XECC, FlexRAM ECC. MECC64 MECC64 supports 1-bit error correction and 2-bit error detection,to 2bit, it cannot correct errors, just detect the error. MECC64 is mainly protects OCRAM1 and 2 in the chip. MECC1 protects OCRAM1, and MECC2 protects OCRAM2. OCRAM1 ECC and OCRAM2 ECC are used to store ECC check values. If ECC64 is not enabled, it can be used as a normal OCRAM. An 8-bit ECC check value (8 bits) is calculated for every 64 bits of data. The ECC algorithm is Hsiao Hamming. XECC XECC is External ECC controller, which is used to provide ECC function for external storage space. XECC supports 1-bit error correction and 2-bit error detection. External memory includes XECC_FLEXSPI1, XECC_FLEXSPI2, and XECC+SEMC. XECC can calculate a 4-bit ECC check value for every 4 bits. The XECC check value is immediately following the original value. For example, for a 32-bit data, a 4-bit ECC check value is generated for every 4 bits. 32-bit original data needs to generate 32-bit ECC check data, which requires a total of 64 bits of space. Algorithm: Hsiao Hamming algorithm FlexRAM ECC FlexRAM ECC is used to protect the ITCM, DTCM and OCRAM of FlexRAM. It supports 1-bit error correction and 2-bit error detection. A 7/8-bit ECC check value is calculated for every 4 bytes of DTCM or 8 bytes of ITCM/OCRAM, and the ECC check value is placed in the ECC RAM. 2.2 RT1170 ECC enable    The method used in this article to enable ECC is to directly enable the relevant fuse bit. - MECC_ENABLE (0x840[2]) = 1 - XECC_ENABLE (0x840[3]) = 1 - ROM preloading (0x950[0]) = 1 - FLEXRAMECC_ENABLE (0x840[15]) = 1 For more software configuration information, please refer to the official application note AN13204: https://www.nxp.com/docs/en/application-note/AN13204.pdf The following is the situation after burning the relevant fuses. Burning the fuses can enter the serial download mode and use MCUbootutility to burn: Fig 2 2.3 ECC debug issue solution methods After testing in many ways, such as initializing RAM in the script, because of the characteristics of ECC, RAM needs to be flashed once, but it is found that the general code speed of flashing RAM directly is too slow, resulting in download timeout problems, and then it is changed to use DMA to move data to RAM to ensure that RAM is flashed once, but the result is still not good, so flashing RAM is not the fundamental way to solve the debug problem. Finally, by chance, ECC is turned off in the connection script first, especially FlexRAM ECC, and it is found that the burning algorithm can be called to perform external flash operations at this time. In this way, the code can be successfully downloaded, and then reset to let the ROM turn on the ECC function by itself. The reason why the RAM project can work is that the process of downloading RAM is a process of flashing RAM, so the RAM code can work directly. For the debugger burning and simulation of Flash code, it is still necessary to turn off the ECC module first, mainly the FlexRAM ECC module. Of course, for the sake of insurance, we can directly turn off all MECC and FlexRAM ECC register enable bits, let the flashloader work first, and directly control the register address: 0x40014100=0;0x40018100=0;0x40028108=0; 0x40014100 PIPE_ECC_EN[ECC_EN], control MECC1 0x40018100 PIPE_ECC_EN[ECC_EN], control MECC2 Fig 3 0x40028108 FLEXRAM_CTRL ECC_EN, control FlexRAM ECC Fig 4 According to the actual test situation, disabling FlexRAM ECC is effective. The problem should be that the area where the flashloader used is stored is the DTCM of FlexRAM. Check the area of ​​the Flashloader of the burning algorithm as follows: Fig 5 3  Three major IDEs script and testing Here, share the three IDE(MCUXPresso, IAR, MDK)+CMSIS DAP+ECC related modified script file. 3.1 MCUXpresso IDE Script path in the IDE: C:\nxp\MCUXpressoIDE_11.9.0_2144\ide\LinkServer\binaries\Scripts Prepare one script file T1170_connect_M7_wake_M4_ecc.scp,copy it to the above path, and the content is: 1 REM ====================================== 2 REM Copyright 2020-2024 NXP 3 REM All rights reserved. 4 REM SPDX-License-Identifier: BSD-3-Clause 5 REM ====================================== 100 REM ======================================================================= 110 REM RT1170_connect_M7_wake_M4.scp 150 REM ======================================================================= 160 PRINT "RT1170 Connect M7 and Wake M4 Script" 170 REM ======================================================================= 180 REM Uncomment ProbeList for standalone script use (outside the stub) 190 REM ======================================================================= 200 REM ProbeList 210 p% = ProbeFirstFound 220 REM ProbeOpenByIndex p% 230 WireSwdConnect p% 240 SelectProbeCore p% 0 250 CMInitApDp this 252 REM ======================================================================= 254 REM Disable ECC 256 GOSUB 1500 260 REM ======================================================================= 270 REM The M4 AP is not visible while the core is held in reset 280 REM Prepare a spin code in RAM and wake up / reset the M4 to it 290 REM This serves two purposes: 300 REM - enables the M4 AP, required for debug visibility 310 REM - prevents M4 code from interfering with flash programming on M7 320 REM ======================================================================= 330 REM Prepare spin code 340 GOSUB 1000 350 REM ======================================================================= 360 PRINT "Setting M4 clock" 370 REM Set m4_clk_root to OSC_RC_400M / 2: CLOCK_ROOT1 = mux(2), div(1) 380 Poke32 this 0x40CC0080 0x201 390 PRINT "Resetting M4 core" 400 REM Save current reset SRMR and prevent M4 SW reset affecting the system 410 s% = Peek32 this 0x40C04004 420 Poke32 this 0x40C04004 0x00000C00 430 Poke32 this 0x40C04284 0x1 440 Poke32 this 0x40C04004 s% 450 REM ======================================================================= 460 REM Release M4 if needed 500 s% = Peek32 this 0x40c04000 510 IF s% & 1 == 1 THEN GOTO 560 520 PRINT "Releasing M4" 530 s% = s% | 1 540 Poke32 this 0x40c04000 s% 550 REM ======================================================================= 560 PRINT "View cores on the DAP AP" 570 WireSwdConnect p% 580 CoreList p% 590 SelectProbeCore p% 0 600 REM ======================================================================= 610 REM Potentially FlexRAM might need to be set to ensure TCMs are available 620 REM Uncomment next line if needed 630 REM GOSUB 800 640 REM ======================================================================= 650 REM Finished - 0 to select the M7, 1 to select M4 660 d% = 0 670 REM ======================================================================= 680 REM Setup VTOR in preparation for VECTRESET 690 GOSUB 1300 700 REM ======================================================================= 710 END 800 REM ====================== SUB: Configure FlexRAM ======================== 810 PRINT "Configuring FlexRAM for 256KB I-TCM, 256KB D-TCM, 0KB OCRAM" 820 REM FlexRAM TCM_CTRL - force RAM clocking ON and set fast mode = b100 830 Poke32 this 0x40028000 0x4 840 REM IOMUXC_GPR17/18 FlexRAM 32KB banks allocation - I(b11), D(b10), OC(b01) 850 Poke32 this 0x400E4044 0x0000AAFF 860 Poke32 this 0x400E4048 0x0000AAFF 870 REM IOMUXC_GPR16 Enable FLEXRAM_BANK_CFG in GPR16/17 880 s% = Peek32 this 0x400E4040 890 s% = s% | 4 900 Poke32 this 0x400E4040 s% 910 RETURN 1000 REM ==================== SUB: Set up M4 spin code ======================== 1010 REM Setup some spin code into an area of D-TCM (0x2021FF00) 1020 REM Condensed vector table format taking up 2 words of memory: 1030 REM - x00: SP (dummy), two back-to-back branch-to-self opcodes (b 0) 1040 REM - x04: PC - points to address x00 (+1 Thumb) 1050 PRINT "Setting M4 spin code" 1060 Poke32 this 0x2021FF00 0xE7FEE7FE 1070 Poke32 this 0x2021FF04 0x2021FF01 1080 REM Set top/bottom 16 bits of RAM address into CM4 VTOR iomuxc_lpsr_GPR0/1 1090 Poke32 this 0x40C0C000 0xFF00 1100 Poke32 this 0x40C0C004 0x2021 1110 RETURN 1300 REM ==================== SUB: Setup CM7 VTOR ============================= 1310 REM Upon VECTRESET, VTOR is loaded with the value from this register. 1320 REM If the address is invalid, a hard fault occurs after VECTRESET. 1330 REM These registers are set in preparation for a pre-flash driver VECTRESET 1340 REM requested by the stub. 1350 REM BootROM VTOR 1360 s% = 0x210000 1370 REM Set addr >> 7 into CM7 VTOR iomuxc_lpsr_GPR26 (RevB) or 0x400e404c (Rev A) 1380 v% = Peek32 this 0x40C84800 1390 IF v% & 0x00FFFFF0 == 0x1170A0 Then GOTO 1420 1400 Poke32 this 0x40C0C068 s% >> 7 1410 GOTO 1430 1420 Poke32 this 0x400E404C s% >> 7 1430 RETURN 1440 REM ======================================================================= 1500 REM ====================== SUB: Disable M7 TCM ECC and OCRAM ECC ========== 1510 REM FlexRAM_CTRL - disable TCM ECC and OCRAM ECC 1520 Poke32 this 0x40028108 0x00000000 1530 REM MECC1/2 PIPE_ECC_EN - disable ECC 1540 Poke32 this 0x40014100 0x00000000 1550 Poke32 this 0x40018100 0x00000000 1560 RETURN  MCUXpresso debug configuration,in the “connect script” item, select the above prepared .scp file: Fig 6 The result after Debug is: Fig 7 We can see, the code downloading and debugging all works now. 3.2 IAR IDE ECC script and testing IAR project script path: \MIMXRT1170-EVK-hello_world_demo_cm7\hello_world_demo_cm7\evkmimxrt1170 Prepare the file: evkmimxrt1170_connect_cm7_disableECC.mac The content is:   /* * Copyright 2019-2021 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ __var rev; initSysPll2() { __var t; // ANADIG_PLL_PLL_528_CTRL t = __readMemory32(0x40C84240, "Memory"); if (t & 0x800000) { // SysPll2 has been initialized t &= ~0x40000000; __writeMemory32(t, 0x40C84240, "Memory"); return; } t = __readMemory32(0x40C84270, "Memory"); t |= 0x80808080; __writeMemory32(t, 0x40C84270, "Memory"); t = __readMemory32(0x40C84240, "Memory"); t &= ~(0x802000); t |= 0x40000000; __writeMemory32(t, 0x40C84240, "Memory"); // ANADIG_PLL_PLL_528_MFN __writeMemory32(0, 0x40C84280, "Memory"); // ANADIG_PLL_PLL_528_MFI __writeMemory32(22, 0x40C84290, "Memory"); // ANADIG_PLL_PLL_528_MFD __writeMemory32(0x0FFFFFFF, 0x40C842A0, "Memory"); // ANADIG_PLL_PLL_528_CTRL __writeMemory32(0x40000008, 0x40C84240, "Memory"); __delay(30); // ANADIG_PLL_PLL_528_CTRL t = __readMemory32(0x40C84240, "Memory"); t |= 0x800000 | 0x800; __writeMemory32(t, 0x40C84240, "Memory"); __delay(250); t = __readMemory32(0x40C84240, "Memory"); t &= ~0x800; __writeMemory32(t, 0x40C84240, "Memory"); do { t = __readMemory32(0x40C84240, "Memory"); } while ((t & 0x20000000) == 0); t |= 0x2000; __writeMemory32(t, 0x40C84240, "Memory"); t &= ~0x40000000; __writeMemory32(t, 0x40C84240, "Memory"); } initSysPll2Pfd1() { __var t, stable; t = __readMemory32(0x40C84270, "Memory"); if (((t & 0x8000) != 0) || (((t & 0x3F00) >> 😎 != 16)) { stable = t & 0x4000; t |= 0x8000; __writeMemory32(t, 0x40C84270, "Memory"); t = __readMemory32(0x40C84270, "Memory"); t &= ~0x3F00; t |= 16 << 8; __writeMemory32(t, 0x40C84270, "Memory"); t = __readMemory32(0x40C84250, "Memory"); t ^= 0x4; __writeMemory32(t, 0x40C84250, "Memory"); t = __readMemory32(0x40C84270, "Memory"); t &= ~0x8000; __writeMemory32(t, 0x40C84270, "Memory"); do { t = __readMemory32(0x40C84270, "Memory") & 0x4000; } while (t == stable); } else { t &= ~0x8000; __writeMemory32(t, 0x40C84270, "Memory"); } } SDRAM_WaitIpCmdDone() { __var reg; do { reg = __readMemory32(0x400D403C, "Memory"); __delay(10); }while((reg & 0x3) == 0); __writeMemory32(0x00000003, 0x400D403C, "Memory"); // clear IPCMDERR and IPCMDDONE bits } setSemcClock() { initSysPll2(); initSysPll2Pfd1(); // Set SEMC root clock to use sys pll2 pfd1 divided by 3: 198Mhz __writeMemory32(0x602, 0x40cc0200, "Memory"); } initSDRAM() { // Config IOMUX __writeMemory32(0x00000000, 0x400E8010, "Memory"); __writeMemory32(0x00000000, 0x400E8014, "Memory"); __writeMemory32(0x00000000, 0x400E8018, "Memory"); __writeMemory32(0x00000000, 0x400E801C, "Memory"); __writeMemory32(0x00000000, 0x400E8020, "Memory"); __writeMemory32(0x00000000, 0x400E8024, "Memory"); __writeMemory32(0x00000000, 0x400E8028, "Memory"); __writeMemory32(0x00000000, 0x400E802C, "Memory"); __writeMemory32(0x00000000, 0x400E8030, "Memory"); __writeMemory32(0x00000000, 0x400E8034, "Memory"); __writeMemory32(0x00000000, 0x400E8038, "Memory"); __writeMemory32(0x00000000, 0x400E803C, "Memory"); __writeMemory32(0x00000000, 0x400E8040, "Memory"); __writeMemory32(0x00000000, 0x400E8044, "Memory"); __writeMemory32(0x00000000, 0x400E8048, "Memory"); __writeMemory32(0x00000000, 0x400E804C, "Memory"); __writeMemory32(0x00000000, 0x400E8050, "Memory"); __writeMemory32(0x00000000, 0x400E8054, "Memory"); __writeMemory32(0x00000000, 0x400E8058, "Memory"); __writeMemory32(0x00000000, 0x400E805C, "Memory"); __writeMemory32(0x00000000, 0x400E8060, "Memory"); __writeMemory32(0x00000000, 0x400E8064, "Memory"); __writeMemory32(0x00000000, 0x400E8068, "Memory"); __writeMemory32(0x00000000, 0x400E806C, "Memory"); __writeMemory32(0x00000000, 0x400E8070, "Memory"); __writeMemory32(0x00000000, 0x400E8074, "Memory"); __writeMemory32(0x00000000, 0x400E8078, "Memory"); __writeMemory32(0x00000000, 0x400E807C, "Memory"); __writeMemory32(0x00000000, 0x400E8080, "Memory"); __writeMemory32(0x00000000, 0x400E8084, "Memory"); __writeMemory32(0x00000000, 0x400E8088, "Memory"); __writeMemory32(0x00000000, 0x400E808C, "Memory"); __writeMemory32(0x00000000, 0x400E8090, "Memory"); __writeMemory32(0x00000000, 0x400E8094, "Memory"); __writeMemory32(0x00000000, 0x400E8098, "Memory"); __writeMemory32(0x00000000, 0x400E809C, "Memory"); __writeMemory32(0x00000000, 0x400E80A0, "Memory"); __writeMemory32(0x00000000, 0x400E80A4, "Memory"); __writeMemory32(0x00000000, 0x400E80A8, "Memory"); __writeMemory32(0x00000010, 0x400E80AC, "Memory"); // EMC_39, DQS PIN, enable SION __writeMemory32(0x00000000, 0x400E80B8, "Memory"); __writeMemory32(0x00000000, 0x400E80BC, "Memory"); __writeMemory32(0x00000000, 0x400E80C0, "Memory"); __writeMemory32(0x00000000, 0x400E80C4, "Memory"); __writeMemory32(0x00000000, 0x400E80C8, "Memory"); __writeMemory32(0x00000000, 0x400E80CC, "Memory"); __writeMemory32(0x00000000, 0x400E80D0, "Memory"); __writeMemory32(0x00000000, 0x400E80D4, "Memory"); __writeMemory32(0x00000000, 0x400E80D8, "Memory"); __writeMemory32(0x00000000, 0x400E80DC, "Memory"); __writeMemory32(0x00000000, 0x400E80E0, "Memory"); __writeMemory32(0x00000000, 0x400E80E4, "Memory"); __writeMemory32(0x00000000, 0x400E80E8, "Memory"); __writeMemory32(0x00000000, 0x400E80EC, "Memory"); __writeMemory32(0x00000000, 0x400E80F0, "Memory"); __writeMemory32(0x00000000, 0x400E80F4, "Memory"); __writeMemory32(0x00000000, 0x400E80F8, "Memory"); __writeMemory32(0x00000000, 0x400E80FC, "Memory"); // PAD ctrl // PDRV = 1b (normal); PULL = 10b (PD) __writeMemory32(0x00000008, 0x400E8254, "Memory"); __writeMemory32(0x00000008, 0x400E8258, "Memory"); __writeMemory32(0x00000008, 0x400E825C, "Memory"); __writeMemory32(0x00000008, 0x400E8260, "Memory"); __writeMemory32(0x00000008, 0x400E8264, "Memory"); __writeMemory32(0x00000008, 0x400E8268, "Memory"); __writeMemory32(0x00000008, 0x400E826C, "Memory"); __writeMemory32(0x00000008, 0x400E8270, "Memory"); __writeMemory32(0x00000008, 0x400E8274, "Memory"); __writeMemory32(0x00000008, 0x400E8278, "Memory"); __writeMemory32(0x00000008, 0x400E827C, "Memory"); __writeMemory32(0x00000008, 0x400E8280, "Memory"); __writeMemory32(0x00000008, 0x400E8284, "Memory"); __writeMemory32(0x00000008, 0x400E8288, "Memory"); __writeMemory32(0x00000008, 0x400E828C, "Memory"); __writeMemory32(0x00000008, 0x400E8290, "Memory"); __writeMemory32(0x00000008, 0x400E8294, "Memory"); __writeMemory32(0x00000008, 0x400E8298, "Memory"); __writeMemory32(0x00000008, 0x400E829C, "Memory"); __writeMemory32(0x00000008, 0x400E82A0, "Memory"); __writeMemory32(0x00000008, 0x400E82A4, "Memory"); __writeMemory32(0x00000008, 0x400E82A8, "Memory"); __writeMemory32(0x00000008, 0x400E82AC, "Memory"); __writeMemory32(0x00000008, 0x400E82B0, "Memory"); __writeMemory32(0x00000008, 0x400E82B4, "Memory"); __writeMemory32(0x00000008, 0x400E82B8, "Memory"); __writeMemory32(0x00000008, 0x400E82BC, "Memory"); __writeMemory32(0x00000008, 0x400E82C0, "Memory"); __writeMemory32(0x00000008, 0x400E82C4, "Memory"); __writeMemory32(0x00000008, 0x400E82C8, "Memory"); __writeMemory32(0x00000008, 0x400E82CC, "Memory"); __writeMemory32(0x00000008, 0x400E82D0, "Memory"); __writeMemory32(0x00000008, 0x400E82D4, "Memory"); __writeMemory32(0x00000008, 0x400E82D8, "Memory"); __writeMemory32(0x00000008, 0x400E82DC, "Memory"); __writeMemory32(0x00000008, 0x400E82E0, "Memory"); __writeMemory32(0x00000008, 0x400E82E4, "Memory"); __writeMemory32(0x00000008, 0x400E82E8, "Memory"); __writeMemory32(0x00000008, 0x400E82EC, "Memory"); __writeMemory32(0x00000008, 0x400E82F0, "Memory"); __writeMemory32(0x00000008, 0x400E82FC, "Memory"); __writeMemory32(0x00000008, 0x400E8300, "Memory"); __writeMemory32(0x00000008, 0x400E8304, "Memory"); __writeMemory32(0x00000008, 0x400E8308, "Memory"); __writeMemory32(0x00000008, 0x400E830C, "Memory"); __writeMemory32(0x00000008, 0x400E8310, "Memory"); __writeMemory32(0x00000008, 0x400E8314, "Memory"); __writeMemory32(0x00000008, 0x400E8318, "Memory"); __writeMemory32(0x00000008, 0x400E831C, "Memory"); __writeMemory32(0x00000008, 0x400E8320, "Memory"); __writeMemory32(0x00000008, 0x400E8324, "Memory"); __writeMemory32(0x00000008, 0x400E8328, "Memory"); __writeMemory32(0x00000008, 0x400E832C, "Memory"); __writeMemory32(0x00000008, 0x400E8330, "Memory"); __writeMemory32(0x00000008, 0x400E8334, "Memory"); __writeMemory32(0x00000008, 0x400E8338, "Memory"); __writeMemory32(0x00000008, 0x400E833C, "Memory"); __writeMemory32(0x00000008, 0x400E8340, "Memory"); // Config SDR Controller Registers/ __writeMemory32(0x10000004, 0x400d4000, "Memory"); // MCR __writeMemory32(0x00000081, 0x400d4008, "Memory"); // BMCR0 __writeMemory32(0x00000081, 0x400d400C, "Memory"); // BMCR1 __writeMemory32(0x8000001D, 0x400d4010, "Memory"); // BR0, 64MB __writeMemory32(0x00000F32, 0x400d4040, "Memory"); // SDRAMCR0, 32bit __writeMemory32(0x00772A22, 0x400d4044, "Memory"); // SDRAMCR1 __writeMemory32(0x00010A0D, 0x400d4048, "Memory"); // SDRAMCR2 __writeMemory32(0x21210408, 0x400d404C, "Memory"); // SDRAMCR3 __writeMemory32(0x80000000, 0x400d4090, "Memory"); // IPCR0 __writeMemory32(0x00000002, 0x400d4094, "Memory"); // IPCR1 __writeMemory32(0x00000000, 0x400d4098, "Memory"); // IPCR2 __writeMemory32(0xA55A000F, 0x400d409C, "Memory"); // IPCMD, SD_CC_IPREA SDRAM_WaitIpCmdDone(); __writeMemory32(0xA55A000C, 0x400d409C, "Memory"); // SD_CC_IAF SDRAM_WaitIpCmdDone(); __writeMemory32(0xA55A000C, 0x400d409C, "Memory"); // SD_CC_IAF SDRAM_WaitIpCmdDone(); __writeMemory32(0x00000033, 0x400d40A0, "Memory"); // IPTXDAT __writeMemory32(0xA55A000A, 0x400d409C, "Memory"); // SD_CC_IMS SDRAM_WaitIpCmdDone(); __writeMemory32(0x00000017, 0x400d4150, "Memory"); // DCCR __writeMemory32(0x21210409, 0x400d404C, "Memory"); // enable sdram self refresh after initialization done. __message "SDRAM init done"; } restoreFlexRAM() { __var base; __var value; base = 0x400E4000; value = __readMemory32(base + 0x44, "Memory"); value &= ~(0xFFFF); value |= 0xFFAA; __writeMemory32(value, base + 0x44, "Memory"); value = __readMemory32(base + 0x48, "Memory"); value &= ~(0xFFFF); value |= 0xFFAA; __writeMemory32(value, base + 0x48, "Memory"); value = __readMemory32(base + 0x40, "Memory"); value &= ~(0xFF << 8); value |= 0x7 | (0xaa << 8); __writeMemory32(value, base + 0x40, "Memory"); __message "FlexRAM configuration is restored"; } clearECC() { __writeMemory32(0x00000000, 0x40014100, "Memory"); __writeMemory32(0x00000000, 0x40018100, "Memory"); __writeMemory32(0x00000000, 0x40028108, "Memory"); } execUserPreload() { restoreFlexRAM(); setSemcClock(); initSDRAM(); clearECC(); }   Mainly add the above red code, which is used to clear the MECC and FlexRAM ECC enable bit. Add the modified mac script: Fig 8 Debug result is: Fig 9 We can see, in the IAR also can do the code downloading and debugging, the script also works for the ECC enabled board. 3.3 MDK IDE ECC script and test result   Open the project path: \MIMXRT1170-EVK-hello_world_demo_cm7\hello_world_demo_cm7\evkmimxrt1170 Prepare the file:evkmimxrt1170_flexspi_nor_sdram.ini, the content is:   /* * Copyright 2019-2021 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ FUNC void restoreFlexRAM(void) { unsigned int value; value = _RDWORD(0x400E4044); value &= ~(0xFFFF); value |= 0xFFAA; _WDWORD(0x400E4044, value); value = _RDWORD(0x400E4048); value &= ~(0xFFFF); value |= 0xFFAA; _WDWORD(0x400E4048, value); value = _RDWORD(0x400E4040); value &= ~(0xFF << 8); value |= 0x7 | (0xAA << 8); _WDWORD(0x400E4040, value); } FUNC void SDRAM_WaitIpCmdDone(void) { unsigned long reg; do { reg = _RDWORD(0x400D403C); }while((reg & 0x3) == 0); _WDWORD(0x400D403C,0x00000003); // clear IPCMDERR and IPCMDDONE bits } FUNC void EnableOSC400M(void) { unsigned int reg; // CTRL1: power down reg = _RDWORD(0x40C84050); reg &= ~0x1; _WDWORD(0x40C84050,reg); // CTRL2: enable clock reg = _RDWORD(0x40C84060); reg |= 0x1; _WDWORD(0x40C84060,reg); } FUNC void EnableOSC24M(void) { unsigned int reg; reg = _RDWORD(0x40C84020); if(0 == (reg & 0x10)) { reg = 0x14; // OSC_EN and LP_EN _WDWORD(0x40C84020,reg); reg = _RDWORD(0x40C84020); while (0 == (reg & 0x80000000)); } } FUNC void EnablePllLdo(void) { unsigned int reg; // CTRL_AI_CTRL _WDWORD(0x40C84820,0x00000000); // CTRL_AI_WDATA _WDWORD(0x40C84830,0x00000105); // PMU_LDO_PLL reg = _RDWORD(0x40C84500); reg |= 0x10000; _WDWORD(0x40C84500,reg); _Sleep_(100); // PMU_POWER_DETECT_CTRL _WDWORD(0x40C84580,0x00000100); _Sleep_(1); // PMU_REF_CTRL _WDWORD(0x40C84570,0x00000010); } FUNC void InitSysPll2Pfd1(void) { unsigned int reg; unsigned int stable; // ANADIG_PLL_PLL_528_PFD reg = _RDWORD(0x40C84270); if (((reg & 0x8000) != 0) || (((reg & 0x3F00) >> 😎 != 16)) { stable = reg & 0x4000; reg |= 0x8000; _WDWORD(0x40C84270,reg); reg = _RDWORD(0x40C84270); reg &= ~0x3F00; reg |= 16 << 8; _WDWORD(0x40C84270,reg); reg = _RDWORD(0x40C84250); reg ^= 0x4; _WDWORD(0x40C84250,reg); reg = _RDWORD(0x40C84270); reg &= ~0x8000; _WDWORD(0x40C84270,reg); do { reg = _RDWORD(0x40C84270) & 0x4000; } while (reg == stable); } else { //syspll2 pfd1 has been initialized already reg &= ~0x8000; _WDWORD(0x40C84270,reg); } } FUNC void InitSysPll2(void) { unsigned int reg; // ANADIG_PLL_PLL_528_CTRL reg = _RDWORD(0x40C84240); if (reg & 0x800000) { // SysPll2 has been initialized reg &= ~0x40000000; _WDWORD(0x40C84240, reg); return; } reg = _RDWORD(0x40C84270); reg |= 0x80808080; _WDWORD(0x40C84270, reg); reg = _RDWORD(0x40C84240); reg &= ~(0x802000); reg |= 0x40000000; _WDWORD(0x40C84240, reg); // ANADIG_PLL_PLL_528_MFN _WDWORD(0x40C84280, 0); // ANADIG_PLL_PLL_528_MFI _WDWORD(0x40C84290, 22); // ANADIG_PLL_PLL_528_MFD _WDWORD(0x40C842A0, 0x0FFFFFFF); // ANADIG_PLL_PLL_528_CTRL _WDWORD(0x40C84240, 0x8 | 0x40000000); _Sleep_(30); // ANADIG_PLL_PLL_528_CTRL reg = _RDWORD(0x40C84240); reg |= 0x800000 | 0x800; _WDWORD(0x40C84240, reg); _Sleep_(250); reg = _RDWORD(0x40C84240); reg &= ~0x800; _WDWORD(0x40C84240, reg); do { reg = _RDWORD(0x40C84240); } while ((reg & 0x20000000) == 0); reg |= 0x2000; _WDWORD(0x40C84240, reg); reg &= ~0x40000000; _WDWORD(0x40C84240, reg); } FUNC void SetSemcClock(void) { //EnableOSC400M(); EnablePllLdo(); InitSysPll2(); InitSysPll2Pfd1(); // Set SEMC root clock // Use sys pll2 pfd1 divided by 3: 198Mhz _WDWORD(0x40CC0200,0x00000602); } FUNC void _clock_init(void) { SetSemcClock(); } FUNC void _sdr_Init(void) { // Config IOMUX _WDWORD(0x400E8010, 0x00000000); _WDWORD(0x400E8014, 0x00000000); _WDWORD(0x400E8018, 0x00000000); _WDWORD(0x400E801C, 0x00000000); _WDWORD(0x400E8020, 0x00000000); _WDWORD(0x400E8024, 0x00000000); _WDWORD(0x400E8028, 0x00000000); _WDWORD(0x400E802C, 0x00000000); _WDWORD(0x400E8030, 0x00000000); _WDWORD(0x400E8034, 0x00000000); _WDWORD(0x400E8038, 0x00000000); _WDWORD(0x400E803C, 0x00000000); _WDWORD(0x400E8040, 0x00000000); _WDWORD(0x400E8044, 0x00000000); _WDWORD(0x400E8048, 0x00000000); _WDWORD(0x400E804C, 0x00000000); _WDWORD(0x400E8050, 0x00000000); _WDWORD(0x400E8054, 0x00000000); _WDWORD(0x400E8058, 0x00000000); _WDWORD(0x400E805C, 0x00000000); _WDWORD(0x400E8060, 0x00000000); _WDWORD(0x400E8064, 0x00000000); _WDWORD(0x400E8068, 0x00000000); _WDWORD(0x400E806C, 0x00000000); _WDWORD(0x400E8070, 0x00000000); _WDWORD(0x400E8074, 0x00000000); _WDWORD(0x400E8078, 0x00000000); _WDWORD(0x400E807C, 0x00000000); _WDWORD(0x400E8080, 0x00000000); _WDWORD(0x400E8084, 0x00000000); _WDWORD(0x400E8088, 0x00000000); _WDWORD(0x400E808C, 0x00000000); _WDWORD(0x400E8090, 0x00000000); _WDWORD(0x400E8094, 0x00000000); _WDWORD(0x400E8098, 0x00000000); _WDWORD(0x400E809C, 0x00000000); _WDWORD(0x400E80A0, 0x00000000); _WDWORD(0x400E80A4, 0x00000000); _WDWORD(0x400E80A8, 0x00000000); _WDWORD(0x400E80AC, 0x00000010); // EMC_39, DQS PIN, enable SION _WDWORD(0x400E80B8, 0x00000000); _WDWORD(0x400E80BC, 0x00000000); _WDWORD(0x400E80C0, 0x00000000); _WDWORD(0x400E80C4, 0x00000000); _WDWORD(0x400E80C8, 0x00000000); _WDWORD(0x400E80CC, 0x00000000); _WDWORD(0x400E80D0, 0x00000000); _WDWORD(0x400E80D4, 0x00000000); _WDWORD(0x400E80D8, 0x00000000); _WDWORD(0x400E80DC, 0x00000000); _WDWORD(0x400E80E0, 0x00000000); _WDWORD(0x400E80E4, 0x00000000); _WDWORD(0x400E80E8, 0x00000000); _WDWORD(0x400E80EC, 0x00000000); _WDWORD(0x400E80F0, 0x00000000); _WDWORD(0x400E80F4, 0x00000000); _WDWORD(0x400E80F8, 0x00000000); _WDWORD(0x400E80FC, 0x00000000); // PAD ctrl // PDRV = 1b (normal); PULL = 10b (PD) _WDWORD(0x400E8254, 0x00000008); _WDWORD(0x400E8258, 0x00000008); _WDWORD(0x400E825C, 0x00000008); _WDWORD(0x400E8260, 0x00000008); _WDWORD(0x400E8264, 0x00000008); _WDWORD(0x400E8268, 0x00000008); _WDWORD(0x400E826C, 0x00000008); _WDWORD(0x400E8270, 0x00000008); _WDWORD(0x400E8274, 0x00000008); _WDWORD(0x400E8278, 0x00000008); _WDWORD(0x400E827C, 0x00000008); _WDWORD(0x400E8280, 0x00000008); _WDWORD(0x400E8284, 0x00000008); _WDWORD(0x400E8288, 0x00000008); _WDWORD(0x400E828C, 0x00000008); _WDWORD(0x400E8290, 0x00000008); _WDWORD(0x400E8294, 0x00000008); _WDWORD(0x400E8298, 0x00000008); _WDWORD(0x400E829C, 0x00000008); _WDWORD(0x400E82A0, 0x00000008); _WDWORD(0x400E82A4, 0x00000008); _WDWORD(0x400E82A8, 0x00000008); _WDWORD(0x400E82AC, 0x00000008); _WDWORD(0x400E82B0, 0x00000008); _WDWORD(0x400E82B4, 0x00000008); _WDWORD(0x400E82B8, 0x00000008); _WDWORD(0x400E82BC, 0x00000008); _WDWORD(0x400E82C0, 0x00000008); _WDWORD(0x400E82C4, 0x00000008); _WDWORD(0x400E82C8, 0x00000008); _WDWORD(0x400E82CC, 0x00000008); _WDWORD(0x400E82D0, 0x00000008); _WDWORD(0x400E82D4, 0x00000008); _WDWORD(0x400E82D8, 0x00000008); _WDWORD(0x400E82DC, 0x00000008); _WDWORD(0x400E82E0, 0x00000008); _WDWORD(0x400E82E4, 0x00000008); _WDWORD(0x400E82E8, 0x00000008); _WDWORD(0x400E82EC, 0x00000008); _WDWORD(0x400E82F0, 0x00000008); _WDWORD(0x400E82FC, 0x00000008); _WDWORD(0x400E8300, 0x00000008); _WDWORD(0x400E8304, 0x00000008); _WDWORD(0x400E8308, 0x00000008); _WDWORD(0x400E830C, 0x00000008); _WDWORD(0x400E8310, 0x00000008); _WDWORD(0x400E8314, 0x00000008); _WDWORD(0x400E8318, 0x00000008); _WDWORD(0x400E831C, 0x00000008); _WDWORD(0x400E8320, 0x00000008); _WDWORD(0x400E8324, 0x00000008); _WDWORD(0x400E8328, 0x00000008); _WDWORD(0x400E832C, 0x00000008); _WDWORD(0x400E8330, 0x00000008); _WDWORD(0x400E8334, 0x00000008); _WDWORD(0x400E8338, 0x00000008); _WDWORD(0x400E833C, 0x00000008); _WDWORD(0x400E8340, 0x00000008); // Config SDR Controller Registers/ _WDWORD(0x400d4000,0x10000004); // MCR _WDWORD(0x400d4008,0x00000081); // BMCR0 _WDWORD(0x400d400C,0x00000081); // BMCR1 _WDWORD(0x400d4010,0x8000001D); // BR0, 64MB _WDWORD(0x400d4040,0x00000F32); // SDRAMCR0, 32bit _WDWORD(0x400d4044,0x00772A22); // SDRAMCR1 _WDWORD(0x400d4048,0x00010A0D); // SDRAMCR2 _WDWORD(0x400d404C,0x21210408); // SDRAMCR3 _WDWORD(0x400d4090,0x80000000); // IPCR0 _WDWORD(0x400d4094,0x00000002); // IPCR1 _WDWORD(0x400d4098,0x00000000); // IPCR2 _WDWORD(0x400d409C,0xA55A000F); // IPCMD, SD_CC_IPREA SDRAM_WaitIpCmdDone(); _WDWORD(0x400d409C,0xA55A000C); // SD_CC_IAF SDRAM_WaitIpCmdDone(); _WDWORD(0x400d409C,0xA55A000C); // SD_CC_IAF SDRAM_WaitIpCmdDone(); _WDWORD(0x400d40A0,0x00000033); // IPTXDAT _WDWORD(0x400d409C,0xA55A000A); // SD_CC_IMS SDRAM_WaitIpCmdDone(); _WDWORD(0x400d4150,0x00000017); // DCCR _WDWORD(0x400d404C,0x21210409 ); // enable sdram self refresh again after initialization done. } FUNC void Setup (void) { SP = _RDWORD(0x30002000); // Setup Stack Pointer PC = _RDWORD(0x30002004); // Setup Program Counter _WDWORD(0xE000ED08, 0x30002000); // Setup Vector Table Offset Register } FUNC void DisableECC(){ _WDWORD(0x40014100, 0x00000000); _WDWORD(0x40018100, 0x00000000); _WDWORD(0x40028108, 0x00000000); } FUNC void OnResetExec (void) { // executes upon software RESET _clock_init(); _sdr_Init(); DisableECC(); Setup(); // Setup for Running } restoreFlexRAM(); _clock_init(); _sdr_Init(); DisableECC(); LOAD %L INCREMENTAL // Download Setup(); // Setup for Running // g, main  In the project, add the prepared script file: Fig 10 Debug result is: Fig 11 We can see, in MDK, debugging can also be successful using CMSIS DAP. Information sharing: For MCUXPresso IDE, subsequent scripts will automatically add ECC support, the new version is MCUXpresso11.10.0. Scripts for other IDEs need to be added by themselves.  
View full article
There are two main methods for importing a project from GUI Guider to MCUXpresso: Linking the whole GUI Guider project into MCUXpresso. Copying and replacing the GUI on a pre-built LVGL project on MCUXpresso (like the "lvgl_guider" SDK example code). Although the first method is quite convenient, there are times when a user might have a GUI already on an established project. In this case, the second method might be very useful. However, when trying to add lottie widgets to a GUI of an already established project (like the "lvgl_guider" SDK example code), extra steps are required, as this widget uses a proprietary library from Samsung which requires extra steps to add and enable. This document describes the steps needed to add rlottie widgets to a project that is already established in MCUXpresso. GUI Guider 1.8.0, MCUXpresso v11.10.0 and SDK 2.16.000 were used for this document, although the process should be the same for future versions.   Once the Lottie widget has been added to the GUI on GUI Guider, you will want to follow the common steps to import this GUI into the MCUXPresso project. Replace the "custom" and "generated" folders on the MCUXpresso project with the GUI Guider folders: <GUI Guider Project Installation>\custom. <GUI Guider Project Installation>\generated.   TIP: You can open the default location of the MCUXpresso project on the file explorer by selecting the project, opening the "Show In" window by pressing Alt + Shift + W, and selecting "System Explorer":   TIP: You can open the default location of the GUI Guider project on the file explorer by clicking on the green folder icon on the top menu bar:   Copy the "lib" folder from: <GUI Guider Project Installation>\lib into the MCUXpresso project.   Copy the "rlottie" folder from: <GUI Guider Project Installation>\sdk\core\rlottie into the MCUXpresso project.   That’s it for file management. Now, in MCUXpresso: Include the "lib" and "rlottie" folders as source folders by adding their path under: Project properties > C/C++ General > Paths and Symbols > Source Location.   Include the rlottie folder as include path by adding its path under the following two compilers' include paths: Project properties > C/C++ Build > Settings > MCU C++ Compiler > Includes > Include Paths.   Project properties > C/C++ Build > Settings > MCU C Compiler > Includes > Include Paths.   As mentioned on the LVGL documentation for "Rlottie player", we need to add the "-rlottie" flag to the linker, but also link the rlottie library (librlottie.a) to the project. This is done by setting the following on Project Properties > C/C++ Build > Settings > MCU C++ Linker > Libraries:   Finally, enable the macro definition: #define LV_USE_RLOTTIE 1 under the "lv_conf.h" file on "source" to tell LVGL that we are using the rlottie library.   With these steps, the rlottie application was imported, along with its headers and libraries, and this rlottie feature was enabled by linking them to the build configuration. Because of this, the application compiles without any errors. Great! Note: There's a possibility that the following error shows up when compiling: If this is the case, simply change the following macro in "source" > "lv_conf.h" from '0' to '1' to enable user data in the lv_font_t variable type:   However, when executing the application, the screen goes black. Turns out, as soon as the application tries to execute the first rlottie instruction from the ".a" archived library, it is unable to execute anything, which causes the application to halt and get stuck on a black screen. This happens as soon as the application calls line 113 of the "lv_rlottie.c" file to construct the rlottie widget: (This file is under <project folder>\lvgl\lvgl\src\extra\libs\rlottie)   But there was no issue when building the application, so what gives? Well, the Rlottie library is quite memory heavy, so we also need to provide it with memory according to its requirements. We can do this by increasing the heap and stack size from their "default" state to something like 0x800000 for the stack and 0x1000 for the heap. These values are what GUI Guider provides to its projects when using Rlottie widgets.   With this, the MCUXpresso project will now have the rlottie libraries enabled, and also have enough memory to successfully debug/run the project on the i.MX RT board.       Happy "Lottie-ing"!   Edwin.
View full article
Frequently, we receive questions related to the DQS pins present on i.MXRT whether how to use it and what it is exactly the function of this as well as why it is important to use it. The goal of this document is answering these most common questions and expose the most common mistakes when connecting this pin. Lets start defining why DQS signal is helpful for memory interfaces; DQS stands as data strobe and it is the clock signal for the data lines used to solve an issue during the memory read. The controller must first transmit the clock to memory, where it arrives x ns later, then the memory sends data bits to the controller and this takes x nanoseconds. There is a clock skew, which limits how fast you can transmit. On iMXRT family it is present on FlexSPI and SEMC interfaces where you can connect multiple memories, it also allows to have multiple configurations as not all memories provide DQS signal on the memory. The next section will detail the particular configuration on each memory interface, RT1170 data will be used but information on RT10xx family is also applicable on this.  SEMC SEMC has two configurations for DQS pad, on DQSMD register.   For DQSMD = 0: We do not have an exact maximum/minimum for the achievable frequency, we only know that when DQSMD we will not reach the maximum SEMC frequency on SDRAM. There could be variations on the frequency on this mode. It is impossible to run at the max 200MHz 1 and meet this input timing spec on datasheet, so the clock frequency needs to be decreased to ensure you still meet timing., this depends on the data output delay spec for the memory that is being used. For DQSMD = 1: As the signal delay is calculated in DQS pad, 200MHz 1 frequency can be achieved on this mode, please consider that the pin needs to be floating or apply extra capacitance on special cases which will be discussed below. As SDRAM device don't output DQS signal, so it take DQS pad as loopback and measure signals delay, and take this delay to compensate and get the correct data strobe point, this can cover most application case and get the good performance, however, if external signal delay is big, it has the complicated topology and long trace, so it can't take DQS pad delay to compensate external SDRAM signal delay. There are two methods to adjust the delay, the first one is using Delay Chain Control Register(DCCR) while the other one is adding capacitance to the DQS pad; unfortunately there is no formula to calculate the register value and capacitance as this is related to SDRAM signal layout, different layout will get the different signal delay. There are some particular cases where more than 3 SDRAMs were added to RT1xx, since the combined memories capacitance exceeded the pad capacitance there were issues using the memory at the max speed; this was solved by adding extra capacitance to DQS pin.   FlexSPI FlexSPI DQS pins behaves similarly as the one we found on SEMC with some difference on the available configuration and maximum speeds. For FlexSPI device there are three different modes of configuration controlled by the RXCLKSRC field on MCR0 register. RXCLKsrc=0x0 (Internal dummy read strobe and internal loopback) In this mode DQS pin not used so an alternative option for this pin can be configured, however the achieved frequency is the lowest as the timings for highest speeds cannot be achieved.   RXCLKsrc=0x1 (Internal dummy read strobe and loopback from DQS pad) In this mode FlexSPI uses DQS pin and it must be configured for the FlexSPI function, it is not an option to use it for a different purpose in this mode. The internally generated read strobe is sent to the DQS pin and is sampled at the pin to match more closely the data pin timings. The timing for sampling with an internal dummy read strobe loopback is very similar to the timing for loopback from pad but it can achieve a higher frequency than loopbacking internally however not the highest one. Similarly to the described on the SEMC side, there are some special cases where signal delay is big, the design has a complicated topology or long traces were the solution is adding extra capacitance to DQS pad, As this is dependent of design there is no formula to calculate the needed capacitance.   RXCLKsrc=0x3 (Flash-memory-provided read strobe) In this mode DQS signal is provided by the connected memory, this mode allows maximum frequency for the memory however only certain memories provide this signal. The FlexSPI controller delays the read strobe for one half cycle of the serial root clock (with DLL), then samples read data with the delayed strobe. Conclusion On i.mxRT family commonly uses external memories to execute code or access important data where good performance on the device is needed. To optimize the access speed of the memory DQS signal is always needed as it may limit the speed rate. 1 Please consult the device specific datasheet for detailed rates.   
View full article