Hello,
I'm using the Embedded Artists LPC1788 development kit and I'm trying to get a very simple RTX application running using the external SDRAM.
Here is what I have so far:
1) A simple RTX application that runs (blinks an LED) with no problem when I use the internal RAM. This same application crashes with a bus fault in "os_sys_init" when I us the external SDRAM.
2) I configure and initialize the external SDRAM before the call to __main (this is working as I can run the same blinking LED using the SDRAM with no RTX.
3) Even if I let me STACK and HEAP remain in the internal RAM but have variables etc.. in the SDRAM I get the bus fault.
4) The bus fault is an IMPRECISERR: Imprecise data bus error
The call stack looks like this when the fault occurs:
HardFault_Handler -> rt_put_rdy_first() -> rt_dispatch() -> rt_task_create() -> SVC_Hanlder...
Does anyone have any suggestions? I see no reason why this should cause a fault.
Thanks.
MAC
Dave,
Thanks for the update. Yes, the data sheet conflicts itself as other NXP individuals and documents...
I agree. It's unfortunate. The last time I contacted NXP with a concern (there was an error in their latest CMSIS file system_lpc17xx.c), the response I got was the following:
Hello David Thank you for contacting NXP Semiconductors. I reviewed the system_LPC177x_8x.c file and it seems to be correct. Please download the latest version and let me know if you still see the problem. You can download the latest example code from: " tab at the bottom, and press the "Expand All" button at the top. Notice the check boxes for the Power Control for Peripherals Register (PCONP). <Only the first one is checked> Now, press the "Text Editor" button at the bottom, and go to line #200. Notice the PCONP value does not match the check box settings? Now take a look at line #197, which has the following code: #define USBCLKSEL_Val (0x00000001|(0x02<<8)) Notice the first numeric value matches the check box settings? Return to the "Configuration Wizard", and check the first 4 boxes in the PCONP setup. Now return to the "Text Editor" and take a look at line #197. Notice the first numeric value has changed from 0x00000001 to 0x0000000F? This is because of a mis-alignment in your macro settings for the Wizard. The fix is simple, but I think Paul should take a closer look... Best Regards, David Brooks
after that, I got this response:
David I have reviewed the system_LPC177x_8x.c file and agree with your findings. I changed the following starting on 188: #define CLOCK_SETUP 1 #define SCS_Val 0x00000021 #define CLKSRCSEL_Val 0x00000001 #define PLL0_SETUP 1 #define PLL0CFG_Val 0x00000009 #define PLL1_SETUP 1 #define PLL1CFG_Val 0x00000023 #define CCLKSEL_Val 0x00000001 #define USBCLKSEL_Val 0x00000001 #define EMCCLKSEL_Val 0x00000001 #define PCLKSEL_Val 0x00000002 #define PCONP_Val 0x042887DE #define CLKOUTCFG_Val 0x00000100 I have notified the microcontroller applications engineer and it will be fixed in the next version. Regards Paul
It seems uncanny to me... but what is, is...
-Dave
NXP sample code tends to look like it was written by students. Their code packages can potentially be used as a complement to the datasheet when figuring something out, but in most situations, the code needs to be rewritten - or not used at all.
Hi Dave,
We found that same error in system_lpc17xx.c as a matter of fact in the last version of system_LPC177x_8x.c there was also an error.
If using the configuration wizard in uVision there was an extra #define making all the settings offset.
Talk about trick to track down.
I'm not using the FDI board I'm using the EA board so I don't have the same RAM chip.
I took a look at your setup and I'm doing pretty much the same thing with slightly modified values (from SDRAM datasheet) I also did this both in C and assembler but that is for a different reason.
Long story short I still had intermittent errors at 120MHz but it ran well at 96MHz.
What does your test consist of? As Tamir pointed out above it's important to have a thorough test. I used the test from the link Tamir pointed and I never get Data bus or Address bus errors I always get the errors in the full mem read/write test.
Let's see what NXP says to your reply.
M
I noticed that in your macro:
#define NS_2_CLKS(ns) ( (uint32_t)( (double)(ns) * tCLK_ns ) + 1 ) // convert ns to CCLKs
you have "+ 1" is that correct? I thought that the + 1 happened in hardware.
For example see table 118 from the user manual for DaynamicRP:
Table 118. Dynamic Memory Precharge Command Period register (EMCDynamictRP - address 0x2009 C030) bit 3:0 Precharge command period (tRP) 0x0 - 0xE n + 1 clock cycles. The delay is in EMCCLK cycles. 0x0F 0xF 16 clock cycles (POR reset value).
So if the datasheet says 18ns aren't you adding one extra clock cycle?
I could just be missing something but this may actually explain why my setting aren't working.
[PART 1 OF 2 MESSAGES] I have two memory tests:
Here's the first one...
/*********************************************************************************************** ** Function name: MemTest ** ** Descriptions: Simple test for SDRAM memory ** ** parameters: Count - a counter for repetitive testing that shows up on TeraTerm ** Returned value: None ** ***********************************************************************************************/ void MemTest( uint32_t Count ) { uint32_t register *uint32_t_ptr; uint16_t register *uint16_t_ptr; register uint32_t Data32; register uint16_t Data16; uint32_t i, k, ErrorCount; uint32_t RelativeTimer; ANSIgotoxy(1,1); printf("Evaluating SDRAM performance: %u\r\n", Count); ANSIsetcolor( BRIGHT, ANSI_YELLOW, ANSI_BLACK ); // set Hyperterm colors // Clear content before 16 bit access test // --------------------------------------------------------- uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); printf("\t...clearing SDRAM (%u bytes)...............", (uint32_t)(SDRAM_SIZE)); RelativeTimer = SystemTimer; k = SDRAM_SIZE>>3; // size in bytes divided by 4 (32bit access) for ( i=0; i<k; i++ ) *uint32_t_ptr++ = 0; RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%u ms.)\r\n", RelativeTimer ); uint32_t_ptr = (uint32_t *)SDRAM_BASE_ADDR; // Test 16-bit write performance... // --------------------------------------------------------- k = SDRAM_SIZE>>2; // size in bytes divided by 2 (16bit access) uint16_t_ptr = (uint16_t *)(SDRAM_BASE_ADDR); printf("\t...testing SDRAM 16-bit write (%u words)...", k); RelativeTimer = SystemTimer; for ( i=0; i<k; i++ ) *uint16_t_ptr++ = ( i%65536 ); RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%u ms.)\r\n", RelativeTimer ); // Test 16-bit read performance... // --------------------------------------------------------- uint16_t_ptr = (uint16_t *)(SDRAM_BASE_ADDR); printf("\t...testing SDRAM 16-bit read (%u words)....", k); ErrorCount = 0; RelativeTimer = SystemTimer; for ( i=0; i<k; i++ ) { Data16 = *uint16_t_ptr++; if( Data16 != ( i%65536 ) ) ErrorCount++; } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%u ms.), Errors: %u\r\n", RelativeTimer, ErrorCount ); // Test 32-bit write performance... // --------------------------------------------------------- k = SDRAM_SIZE>>3; // size in bytes divided by 4 (32bit access) uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); printf("\t...testing SDRAM 32-bit write (%u dwords)..", k); RelativeTimer = SystemTimer; for ( i=0; i<k; i++ ) *uint32_t_ptr++ = ( i ); RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%u ms.)\r\n", RelativeTimer ); // Test 32-bit read performance... // --------------------------------------------------------- uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); printf("\t...testing SDRAM 32-bit read (%u dwords)...", k); ErrorCount = 0; RelativeTimer = SystemTimer; for ( i=0; i<k; i++ ) { Data32 = *uint32_t_ptr++; if( Data32 != ( i ) ) ErrorCount++; } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%u ms.), Errors: %u\r\n", RelativeTimer, ErrorCount ); ANSIsetcolor( BRIGHT, ANSI_CYAN, ANSI_BLACK ); // set Hyperterm colors }
[PART 2 OF 2 MESSAGES]
Here's the second one...
/*********************************************************************************************** ** Function name: ComprehensiveMemTest ** ** Descriptions: Comprehensive test for SDRAM memory ** ** parameters: Count - a counter for repetitive testing that shows up on TeraTerm ** Returned value: None ** ***********************************************************************************************/ void ComprehensiveMemTest( uint32_t Count ) { uint32_t register *uint32_t_ptr; uint16_t register *uint16_t_ptr; register uint32_t Mask32; register uint16_t Mask16; uint32_t i, k, ErrorCount; uint32_t RelativeTimer; ANSIsetcolor( BRIGHT, ANSI_CYAN, ANSI_BLACK ); // set Hyperterm colors printf("\r\nComprehensive SDRAM performance: %u\r\n\n", Count); ANSIsetcolor( BRIGHT, ANSI_YELLOW, ANSI_BLACK ); // set Hyperterm colors // Clear content before 16 bit access test // --------------------------------------------------------- uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); printf("...clearing SDRAM (%u bytes).....................", (uint32_t)(SDRAM_SIZE)); RelativeTimer = SystemTimer; k = SDRAM_SIZE>>3; // size in bytes divided by 4 (32bit access) for( i=0; i<k; i++ ) *uint32_t_ptr++ = 0; RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%u ms.)\r\n", RelativeTimer ); uint32_t_ptr = (uint32_t *)SDRAM_BASE_ADDR; // Test 16-bit performance... // --------------------------------------------------------- k = SDRAM_SIZE>>2; // size in bytes divided by 2 (16bit access) /* walking bit test */ /* ---------------- */ ErrorCount = 0; printf("...Walking-bit test: SDRAM 16-bit (%u words).....", k); RelativeTimer = SystemTimer; for( Mask16=0x0001; Mask16; Mask16 <<= 1 ) { uint16_t_ptr = (uint16_t *)(SDRAM_BASE_ADDR); for( i=0; i<k; i++ ) { *uint16_t_ptr = Mask16; if( *uint16_t_ptr++ != Mask16 ) ErrorCount++; } } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%06.3f sec) (Errors: %u)\r\n", (float)(RelativeTimer)/1000.0, ErrorCount ); /* alternate bit test */ /* ------------------ */ ErrorCount = 0; printf("...Alternate-bit test: SDRAM 16-bit (%u words)...", k); RelativeTimer = SystemTimer; for( Mask16=0x5555; Mask16!=0x5554; Mask16 <<= 1 ) { uint16_t_ptr = (uint16_t *)(SDRAM_BASE_ADDR); for( i=0; i<k; i++ ) { *uint16_t_ptr = Mask16; if( *uint16_t_ptr++ != Mask16 ) ErrorCount++; } } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%06.3f sec) (Errors: %u)\r\n", (float)(RelativeTimer)/1000.0, ErrorCount ); /* unique value test */ /* ----------------- */ ErrorCount = 0; printf("...Unique value test: SDRAM 16-bit (%u words)....", k); uint16_t_ptr = (uint16_t *)(SDRAM_BASE_ADDR); RelativeTimer = SystemTimer; for( i=0; i<k; i++ ) *uint16_t_ptr++ = ( i%65536 ); uint16_t_ptr = (uint16_t *)(SDRAM_BASE_ADDR); for( i=0; i<k; i++ ) { if( *uint16_t_ptr++ != ( i%65536 ) ) ErrorCount++; } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%06.3f sec) (Errors: %u)\r\n\n", (float)(RelativeTimer)/1000.0, ErrorCount ); // Test 32-bit performance... // --------------------------------------------------------- k = SDRAM_SIZE>>3; // size in bytes divided by 4 (32bit access) /* walking bit test */ /* ---------------- */ ErrorCount = 0; printf("...Walking-bit test: SDRAM 32-bit (%u dwords)....", k); RelativeTimer = SystemTimer; for( Mask32=0x00000001; Mask32; Mask32 <<= 1 ) { uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); for( i=0; i<k; i++ ) { *uint32_t_ptr = Mask32; if( *uint32_t_ptr++ != Mask32 ) ErrorCount++; } } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%06.3f sec) (Errors: %u)\r\n", (float)(RelativeTimer)/1000.0, ErrorCount ); /* alternate bit test */ /* ------------------ */ ErrorCount = 0; printf("...Alternate-bit test: SDRAM 32-bit (%u words)...", k); RelativeTimer = SystemTimer; for( Mask32=0x55555555; Mask32!=0x55555554; Mask32 <<= 1 ) { uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); for( i=0; i<k; i++ ) { *uint32_t_ptr = Mask32; if( *uint32_t_ptr++ != Mask32 ) ErrorCount++; } } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%06.3f sec) (Errors: %u)\r\n", (float)(RelativeTimer)/1000.0, ErrorCount ); /* unique value test */ /* ----------------- */ ErrorCount = 0; printf("...Unique value test: SDRAM 32-bit (%u words)....", k); uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); RelativeTimer = SystemTimer; for( i=0; i<k; i++ ) *uint32_t_ptr++ = i; uint32_t_ptr = (uint32_t *)(SDRAM_BASE_ADDR); for( i=0; i<k; i++ ) { if( *uint32_t_ptr++ != i ) ErrorCount++; } RelativeTimer = SystemTimer - RelativeTimer; printf( "complete! (%06.3f sec) (Errors: %u)\r\n\n", (float)(RelativeTimer)/1000.0, ErrorCount ); ANSIsetcolor( BRIGHT, ANSI_CYAN, ANSI_BLACK ); // set Hyperterm colors }
...and the output on TeraTerm looks like this:
Evaluating SDRAM performance: 0 ...clearing SDRAM (8388608 bytes)...............complete! (92 ms.) ...testing SDRAM 16-bit write (2097152 words)...complete! (154 ms.) ...testing SDRAM 16-bit read (2097152 words)....complete! (310 ms.), Errors: 0 ...testing SDRAM 32-bit write (1048576 dwords)..complete! (87 ms.) ...testing SDRAM 32-bit read (1048576 dwords)...complete! (149 ms.), Errors: 0 Comprehensive SDRAM performance: 0 ...clearing SDRAM (8388608 bytes).....................complete! (92 ms.) ...Walking-bit test: SDRAM 16-bit (2097152 words).....complete! (05.234 sec) (Errors: 0) ...Alternate-bit test: SDRAM 16-bit (2097152 words)...complete! (00.695 sec) (Errors: 0) ...Unique value test: SDRAM 16-bit (2097152 words)....complete! (00.450 sec) (Errors: 0) ...Walking-bit test: SDRAM 32-bit (1048576 dwords)....complete! (04.837 sec) (Errors: 0) ...Alternate-bit test: SDRAM 32-bit (1048576 words)...complete! (00.302 sec) (Errors: 0) ...Unique value test: SDRAM 32-bit (1048576 words)....complete! (00.226 sec) (Errors: 0) System ready for USB testing...
Marc,
The macro is correct. The "+1" handles rounding. Since I'm casting to a double, doing the math, and then casting to an unsigned int, (which truncates the result) adding one will always insure I round up.
I'm not sure that rounding explains the macro?
Here is my interpretation:
#define tCLK_ns ((double)EMCClock / 1000000000.0 ) // CCLK period in ns #define NS_2_CLKS(ns) ( (uint32_t)( (double)(ns) * tCLK_ns ) + 1 ) // convert ns to CCLKs LPC_EMC->DynamicRP = NS_2_CLKS(18); // tRP: precharge command period (18ns)
Doing the math...
tCLK_ns = 120000000 / 1000000000.0 = 0.12 clk/ns
NS_2_CLKS(18) = (18 * 0.12) + 1 = 3.16 (trunckated = 3)
This means you are setting DynamicRP to 4 clock cycles (since writting a 3 means n+1 clk) and 4 clock cycles is more like 33ns and not the 25ns which 3 clock cycles would be
Do you see my confusion?
Marc
Oh man, I screwed up! I just noticed I can't seem to divide correctly by shifting (lol)
k = SDRAM_SIZE>>2; // size in bytes divided by 2 (16bit access)
the comments show intent, but the code shows gross error...
when you go back through the code, and change the shift values to the correct ones, the timing output from TeraTerm now looks like this:
Evaluating SDRAM performance: 0 ...clearing SDRAM (8388608 bytes)...............complete! (185 ms.) ...testing SDRAM 16-bit write (4194304 words)...complete! (307 ms.) ...testing SDRAM 16-bit read (4194304 words)....complete! (621 ms.), Errors: 0 ...testing SDRAM 32-bit write (2097152 dwords)..complete! (173 ms.) ...testing SDRAM 32-bit read (2097152 dwords)...complete! (298 ms.), Errors: 0 Comprehensive SDRAM performance: 0 ...clearing SDRAM (8388608 bytes).....................complete! (185 ms.) ...Walking-bit test: SDRAM 16-bit (4194304 words).....complete! (10.468 sec) (Errors: 0) ...Alternate-bit test: SDRAM 16-bit (4194304 words)...complete! (01.390 sec) (Errors: 0) ...Unique value test: SDRAM 16-bit (4194304 words)....complete! (00.900 sec) (Errors: 0) ...Walking-bit test: SDRAM 32-bit (2097152 dwords)....complete! (09.675 sec) (Errors: 0) ...Alternate-bit test: SDRAM 32-bit (2097152 words)...complete! (00.605 sec) (Errors: 0) ...Unique value test: SDRAM 32-bit (2097152 words)....complete! (00.452 sec) (Errors: 0) System ready for USB testing...
I guess the whole point here was to show that the SDRAM worked fine at 120Mhz, not how ignorant I am...
Yes I do - good observation!
So, I modified the macro to remove the addition of 1...
And here are my new performance results (essentially the same):
Evaluating SDRAM performance: 0 ...clearing SDRAM (8388608 bytes)...............complete! (185 ms.) ...testing SDRAM 16-bit write (4194304 words)...complete! (305 ms.) ...testing SDRAM 16-bit read (4194304 words)....complete! (621 ms.), Errors: 0 ...testing SDRAM 32-bit write (2097152 dwords)..complete! (171 ms.) ...testing SDRAM 32-bit read (2097152 dwords)...complete! (298 ms.), Errors: 0 Comprehensive SDRAM performance: 0 ...clearing SDRAM (8388608 bytes).....................complete! (185 ms.) ...Walking-bit test: SDRAM 16-bit (4194304 words).....complete! (10.442 sec) (Errors: 0) ...Alternate-bit test: SDRAM 16-bit (4194304 words)...complete! (01.382 sec) (Errors: 0) ...Unique value test: SDRAM 16-bit (4194304 words)....complete! (00.895 sec) (Errors: 0) ...Walking-bit test: SDRAM 32-bit (2097152 dwords)....complete! (09.675 sec) (Errors: 0) ...Alternate-bit test: SDRAM 32-bit (2097152 words)...complete! (00.605 sec) (Errors: 0) ...Unique value test: SDRAM 32-bit (2097152 words)....complete! (00.450 sec) (Errors: 0)
So no errors even when the "+ 1" is removed. Hmm, I was about to try adding the "+ 1".
Thanks for all the comments, I think I'll just wait t0 see what NXP responds before spending more time on this item.
Cheers.
Well, I got NXP's final answer...
The EMC is rated at 80 MHz. It may work above that, but it is not characterized above 80 MHz. Regards Paul
Go figure...
Hello Folks,
I'm not sure if any of you saw this but I found another bug that would affect this topic.
Dave - Your macro #define tCLK_ns ((double)EMCClock / 1000000000.0 ) may be calculating incorrectly again but only because of an NXP bug.
The EMCClock variable is calculated incorrectly in the "system_LPC177x_8x.c" file.
All of the lines like this (and similar):
EMCClock = (OSC_CLK / ((LPC_SC->EMCCLKSEL & 0x01)+1));
Should be more like this:
EMCClock = (SystemCoreClock / ((LPC_SC->EMCCLKSEL & 0x01)+1));
Anyway, this would skew the macro calculation by a factor of 2.
Here is a better explanation of the issue I found:
www.lpcware.com/.../emc-clock
Hi Marc,
I took a look at the definition for EMCClock, and this is what I found:
uint32_t EMCClock = __EMC_CLK; /*!< EMC Clock Frequency */
tracing the definition for __EMC_CLK further reveals this:
#if ((CCLKSEL_Val & 0x100) == 0) /* cclk = sysclk */ #if ((CLKSRCSEL_Val & 0x01) == 0) /* sysclk = irc_clk */ #define __CORE_CLK (IRC_OSC / __CCLK_DIV) #define __PER_CLK (IRC_OSC/ __PCLK_DIV) #define __EMC_CLK (IRC_OSC/ __ECLK_DIV) #else /* sysclk = osc_clk */ #define __CORE_CLK (OSC_CLK / __CCLK_DIV) #define __PER_CLK (OSC_CLK/ __PCLK_DIV) #define __EMC_CLK (OSC_CLK/ __ECLK_DIV) #endif #else /* cclk = pll_clk */ #if ((CLKSRCSEL_Val & 0x01) == 0) /* sysclk = irc_clk */ #define __CORE_CLK (__PLL0_CLK(IRC_OSC) / __CCLK_DIV) #define __PER_CLK (__PLL0_CLK(IRC_OSC) / __PCLK_DIV) #define __EMC_CLK (__PLL0_CLK(IRC_OSC) / __ECLK_DIV) #else /* sysclk = osc_clk */ #define __CORE_CLK (__PLL0_CLK(OSC_CLK) / __CCLK_DIV) #define __PER_CLK (__PLL0_CLK(OSC_CLK) / __PCLK_DIV) #define __EMC_CLK (__PLL0_CLK(OSC_CLK) / __ECLK_DIV) #endif #endif
When this is evaluated at compile time, since I'm using the PLL0 clock, and my sysclk is the osc_clk, I get simply:
#define __CORE_CLK (__PLL0_CLK(OSC_CLK) / __CCLK_DIV) #define __PER_CLK (__PLL0_CLK(OSC_CLK) / __PCLK_DIV) #define __EMC_CLK (__PLL0_CLK(OSC_CLK) / __ECLK_DIV)
evaluating the macro __PLL0_CLK(), I get:
#define __M ((PLL0CFG_Val & 0x1F) + 1) #define __PLL0_CLK(__F_IN) (__F_IN * __M)
Since OSC_CLK is defined as XTAL, which is 12M, and __PLL0_CLK(12M) = 120M (since __M = 10 for me), the __EMC_CLK value is calculated correctly (I believe).
Can you please explain this better for me?
When the CPU clock divider is 1 than this will calculate correctly. The problem is when it isn't.
This is what I believe is required as a change:
#define __CORE_CLK (__PLL0_CLK(OSC_CLK) / __CCLK_DIV) #define __PER_CLK (__PLL0_CLK(OSC_CLK) / __PCLK_DIV) #define __EMC_CLK (__CORE_CLK / __ECLK_DIV)
The reason is that the fig. 7 in the user manual is incorrect. The EMC Clock divider is not feed from the mux (sys_clk or pll0_clk) it is feed from the CPU clock.
So in your setup if you divided the CPU clock to 60MHz (by 2) the EMC Clock would still calculate to 120MHz which is incorrect.
Hope this helps.
Regards,