DK-57VTS-LPC1788 - Configuring the EMC for SDRAM

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

DK-57VTS-LPC1788 - Configuring the EMC for SDRAM

2,459 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Tue Jul 19 10:04:19 MST 2011
The SDRAM has to be programmed before you can use it as a display buffer for the LCD.  And before you can program the SDRAM, you have to setup the LPC1788 so it can talk to the SDRAM.

The SDRAM on this development board is a MT48LC2M32B2, which is a 2Mx32bit chip ( 8.38MB or 67.108Mb ).

Since all timing values for the LPC1788 are in units of CLK counts, and all timing values in the data sheet are in nanoseconds, the first thing I needed was a macro to convert these units.

Here is a macro for converting nanoseconds to CLK counts:
<code>
#include "LPC177x_8x.h"
#include "system_LPC177x_8x.h"

#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    
</code>

The EMCClock value is from the CMSIS, located in "system_LPC177x_8x.h", and is the current EMC clock value.  The macro NS_2_CLKS converts a given value in nanoseconds to the equivalent number of clock cycles for the current EMC clock...

Another feature of the SDRAM is the order in which the BANK, ROW, and COLUMN are set.  You can have BANK-ROW-COLUMN or ROW-BANK-COLUMN.  I did some experimenting with this, and found a slight increase in performance when I used ROW-BANK-COLUMN order, but you can experiment with this yourself.

I defined a compiler switch to make it easier to change:
<code>
#define ROW_BANK_COLUMN    // default is BANK_ROW_COLUMN (ROW_BANK_COLUMN is ~0.2% faster...   
</code>

So, here's how to setup the LPC1788 and the SDRAM...
<code>
/***********************************************************************************************
** Function name:    SDRAM_init                                                                
**                                                                                             
** Descriptions:     Initialize SDRAM (Synchronous Dynamic Random Access Memory)               
**                                                                                             
** parameters:       None                                                                      
** Returned value:   TRUE when complete                                                        
**                                                                                             
***********************************************************************************************/
uint32_t INIT_SDRAM( void )
   {
   uint32_t Temp = Temp;
   uint32_t CAS_Latency, RAS_Latency;  // these can be 1, 2, or 3 clks                         
   uint32_t DelayConstant;
</code>

I used variables for the CAS and RAS latency values, as well as the delay constant so I could adjust them easily during development.

So the first thing to do is setup the LPC1788.  This entails defining the SDRAM control signals, defining how we want the device to react to a reset signal, and enabling the power to the device (peripheral power), as follows:
<code>
   // configure SDRAM control signals                                                          
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P2_16 |= 1;              // CASN @ P2.16         (SDRAM Column Address Strobe)   
   LPC_IOCON->P2_17 |= 1;              // RASN @ P2.17         (SDRAM Row Address Strobe)      
   LPC_IOCON->P2_18 |= 1;              // CLK[0] @ P2.18       (SDRAM System Clock)            
   LPC_IOCON->P2_20 |= 1;              // DYCSN[0] @ P2.20     (SDRAM Chip Select)             
   LPC_IOCON->P2_24 |= 1;              // CKE[0] @ P2.24       (SDRAM Clock Enable)            
   LPC_IOCON->P2_28 |= 1;              // DQM[0] @ P2.28       (SDRAM Data Input/Output Mask)  
   LPC_IOCON->P2_29 |= 1;              // DQM[1] @ P2.29       (SDRAM Data Input/Output Mask)  
   LPC_IOCON->P2_30 |= 1;              // DQM[2] @ P2.30       (SDRAM Data Input/Output Mask)  
   LPC_IOCON->P2_31 |= 1;              // DQM[3] @ P2.31       (SDRAM Data Input/Output Mask)  

   // configure for 32-bit external data bus                                                   
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P3_0 |= 1;               // D0 @ P3.0                                            
   LPC_IOCON->P3_1 |= 1;               // D1 @ P3.1                                            
   LPC_IOCON->P3_2 |= 1;               // D2 @ P3.2                                            
   LPC_IOCON->P3_3 |= 1;               // D3 @ P3.3                                            
   LPC_IOCON->P3_4 |= 1;               // D4 @ P3.4                                            
   LPC_IOCON->P3_5 |= 1;               // D5 @ P3.5                                            
   LPC_IOCON->P3_6 |= 1;               // D6 @ P3.6                                            
   LPC_IOCON->P3_7 |= 1;               // D7 @ P3.7                                            
   LPC_IOCON->P3_8 |= 1;               // D8 @ P3.8                                            
   LPC_IOCON->P3_9 |= 1;               // D9 @ P3.9                                            
   LPC_IOCON->P3_10 |= 1;              // D10 @ P3.10                                          
   LPC_IOCON->P3_11 |= 1;              // D11 @ P3.11                                          
   LPC_IOCON->P3_12 |= 1;              // D12 @ P3.12                                          
   LPC_IOCON->P3_13 |= 1;              // D13 @ P3.13                                          
   LPC_IOCON->P3_14 |= 1;              // D14 @ P3.14                                          
   LPC_IOCON->P3_15 |= 1;              // D15 @ P3.15                                          
   LPC_IOCON->P3_16 |= 1;              // D16 @ P3.16                                          
   LPC_IOCON->P3_17 |= 1;              // D17 @ P3.17                                          
   LPC_IOCON->P3_18 |= 1;              // D18 @ P3.18                                          
   LPC_IOCON->P3_19 |= 1;              // D19 @ P3.19                                          
   LPC_IOCON->P3_20 |= 1;              // D20 @ P3.20                                          
   LPC_IOCON->P3_21 |= 1;              // D21 @ P3.21                                          
   LPC_IOCON->P3_22 |= 1;              // D22 @ P3.22                                          
   LPC_IOCON->P3_23 |= 1;              // D23 @ P3.23                                          
   LPC_IOCON->P3_24 |= 1;              // D24 @ P3.24                                          
   LPC_IOCON->P3_25 |= 1;              // D25 @ P3.25                                          
   LPC_IOCON->P3_26 |= 1;              // D26 @ P3.26                                          
   LPC_IOCON->P3_27 |= 1;              // D27 @ P3.27                                          
   LPC_IOCON->P3_28 |= 1;              // D28 @ P3.28                                          
   LPC_IOCON->P3_29 |= 1;              // D29 @ P3.29                                          
   LPC_IOCON->P3_30 |= 1;              // D30 @ P3.30                                          
   LPC_IOCON->P3_31 |= 1;              // D31 @ P3.31                                          

   // configure for 32-bit external address bus                                                
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P4_0 |= 1;               //A0 @ P4.0                                             
   LPC_IOCON->P4_1 |= 1;               //A1 @ P4.1                                             
   LPC_IOCON->P4_2 |= 1;               //A2 @ P4.2                                             
   LPC_IOCON->P4_3 |= 1;               //A3 @ P4.3                                             
   LPC_IOCON->P4_4 |= 1;               //A4 @ P4.4                                             
   LPC_IOCON->P4_5 |= 1;               //A5 @ P4.5                                             
   LPC_IOCON->P4_6 |= 1;               //A6 @ P4.6                                             
   LPC_IOCON->P4_7 |= 1;               //A7 @ P4.7                                             
   LPC_IOCON->P4_8 |= 1;               //A8 @ P4.8                                             
   LPC_IOCON->P4_9 |= 1;               //A9 @ P4.9                                             
   LPC_IOCON->P4_10 |= 1;              //A10 @ P4.10                                           
   LPC_IOCON->P4_11 |= 1;              //A11 @ P4.11                                           
   LPC_IOCON->P4_12 |= 1;              //A12 @ P4.12                                           
   LPC_IOCON->P4_13 |= 1;              //A13 @ P4.13                                           
   LPC_IOCON->P4_14 |= 1;              //A14 @ P4.14                                           
   LPC_IOCON->P4_15 |= 1;              //A15 @ P4.15                                           
   LPC_IOCON->P4_16 |= 1;              //A16 @ P4.16                                           
   LPC_IOCON->P4_17 |= 1;              //A17 @ P4.17                                           
   LPC_IOCON->P4_18 |= 1;              //A18 @ P4.18                                           
   LPC_IOCON->P4_19 |= 1;              //A19 @ P4.19                                           
   //LPC_IOCON->P4_20 |= 1;             //A20 @ P4.20                                            
   //LPC_IOCON->P4_21 |= 1;             //A21 @ P4.21                                            
   //LPC_IOCON->P4_22 |= 1;             //A22 @ P4.22                                            
   //LPC_IOCON->P4_23 |= 1;             //A23 @ P4.23                                            
  
   // Configure write enable for SDRAM...                                                      
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P4_25 |= 1;              // WEN @ P4.25                                          

   // Enable EMC Reset, (both EMC resets are asserted when any type of reset occurs)           
   // [Ref: LPC178x_7x_UM_1.4 page 32 ]                                                        
   // ------------------------------------------------------------------------------------------
   LPC_SC->SCS &= ~(1<<1);    // clears bit 1, which is the power on default state anyway...   
  
   // Enable EMC power/clock control bit...                                                    
   // ------------------------------------------------------------------------------------------
   LPC_SC->PCONP   |= (1<<11);        
</code>

Next thing to do is define the values for the variables at the beginning (CAS_Latency, RAS_Latency, and the DelayConstant).
I chose these values for functionality, but you can play around with them to see the effect they have on SDRAM performance.
The datasheet for the MT48LC2M32B2 has recommend values, based on the operating frequency.

I chose an arbitrary frequency of 100Mhz as a switch from lower latency to higher latency, as follows:
<code>
   if( SystemCoreClock < 100000000 )   // longer delay constant, and shorter CAS and RAS latency
      {                                //                                                      
      LPC_SC->EMCCLKSEL = 0x00000001;  // EMC uses a clock rate at 1/2 CPU clock rate...       
      LPC_SC->CLKOUTCFG = 0x00000110;  // enables clock, sets clock source as CPU clk / 2...   
      CAS_Latency = 1;
      RAS_Latency = 1;
      DelayConstant = 0x00000604;
      }
   else
      {
      LPC_SC->EMCCLKSEL = 0x00000000;  // EMC uses a clock rate equal to CPU clock rate...     
      LPC_SC->CLKOUTCFG = 0x00000100;  // enables clock, sets clock source as CPU clk / 1...   
      CAS_Latency = 2;
      RAS_Latency = 2;
      DelayConstant = 0x00000A05;
      }

   // [Ref: LPC178x_7x_UM_1.4 page 183 ]                                                                                   
   // ----------------------------------------------------------------------------------------------------------------------
   // Bit      Symbol         Description                                                                              Reset
   //                                                                                                                  Value
   // ----------------------------------------------------------------------------------------------------------------------
   // 4:0      CMDDLY         Programmable delay value for EMC outputs in command delayed mode. See                    0x10
   //                         Section 10.12.6. The delay amount is roughly (CMDDLY+1) * 250 picoseconds.                   
   // 7:5      -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 12:8     FBCLKDLY       Programmable delay value for the feedback clock that controls input data sampling. See   0x02
   //                         Section 10.5.3. The delay amount is roughly (FBCLKDLY+1) * 250 picoseconds.                  
   // 15:13    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 20:16    CLKOUT0DLY     Programmable delay value for the CLKOUT0 output. This would typically be used in clock   0x00
   //                         delayed mode. See Section 10.12.6 The delay amount is roughly (CLKOUT0DLY+1) *               
   //                         250 picoseconds.                                                                             
   // 23:21    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 28:24    CLKOUT1DLY     Programmable delay value for the CLKOUT1 output. This would typically be used in clock   0x00
   //                         delayed mode. See Section 10.12.6 The delay amount is roughly (CLKOUT1DLY+1) *               
   //                         250 picoseconds.                                                                             
   // 31:29    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   LPC_SC->EMCDLYCTL   = DelayConstant;

   // Reset the EMC and put configuration back to power up reset (little-endian mode, 1:1 clock)
   // [Ref: LPC178x_7x_UM_0.01 page 156]                                                       
   // ------------------------------------------------------------------------------------------
   LPC_EMC->Control = 0x00000001;   // Disable Address mirror, leave EMC enabled...            
   LPC_EMC->Config  = 0x00000000;
  
   // define connection configuration [32 bit external bus address mapping]                    
   // ------------------------------------------------------------------------------------------
   #ifdef ROW_BANK_COLUMN
      LPC_EMC->DynamicConfig0 = 0x00004300;  // row, bank, column, 32-bit
   #else
      LPC_EMC->DynamicConfig0 = 0x00005300;  // bank, row, column, 32-bit
   #endif 
  
   // define delays for RAS and CAS...                                                         
   // bits 1:0-RAS latency, bits 9:8-CAS latency (in CCLK cycle counts)                        
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicRasCas0 = RAS_Latency + (CAS_Latency<<8);    // reset value is 3 and 3       
     
   // configure the dynamic memory read strategy.  Note: This register is used for all four    
   // dynamic memory chip selects. Therefore the worst case value for all of the chip selects  
   // must be programmed.                                                                      
   // [Ref: LPC178x_7x_UM_0.01 page 169 Table 117]                                             
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicReadConfig = 0x00000001;  // Command delayed strategy, using EMCCLKDELAY    
                                             // (command delayed, clock out not delayed)       
</code>

Also, in the above code, the Control, Config, DynamicConfig, DynamicRasCas, and DynamicReadConfig registers are programmed.  The notes will give you an idea of what's going on, but I recommend you read the user's manual for a complete description of these registers.

The next thing to do is program the controller with the part specific timing values.  This is where the macros at the beginning will come in handy.

These values are programmed as follows:
<code>
   // *** Taken from the MT48LC2M32B2 data sheet, page 47 ***                                  
   // configure timing, from Table 18:                                                         
   // -6 (6ns part) timings (see marking on actual chip: MT48LC2M32B2-6)                       
   // NOTE: all timing values for LPC1788 are in units of CLK counts                           
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicRP   = NS_2_CLKS(18);     // tRP: precharge command period             (18ns)
   LPC_EMC->DynamicRAS  = NS_2_CLKS(42);     // tRAS: active to precharge command period  (42ns)
   LPC_EMC->DynamicSREX = NS_2_CLKS(70);     // tXSR: self-refresh exit time              (70ns)
   LPC_EMC->DynamicAPR  = NS_2_CLKS(18);     // tAPR: last-data-out to active command time     
                                             // note: no tAPR value, using tRCD value     (18ns)
   LPC_EMC->DynamicDAL  = CAS_Latency+2;     // tDAL: data-in to active command time           
                                             //       for CL=3, tDAL = 5 tCK                   
                                             //       for CL=2, tDAL = 4 tCK                   
                                             //       for CL=1, tDAL = 3 tCK                   
   LPC_EMC->DynamicWR   = (NS_2_CLKS(6)+1);  // tWR: write recovery time is (12ns) UNLESS we're
                                             //      using AUTO PRECHARGE, then it's   (tCK+6ns)
   LPC_EMC->DynamicRC   = NS_2_CLKS(60);     // tRC: ACTIVE-to-ACTIVE command period      (60ns)
   LPC_EMC->DynamicRFC  = NS_2_CLKS(60);     // tRFC: AUTO REFRESH period                 (60ns)
   LPC_EMC->DynamicXSR  = NS_2_CLKS(70);     // tXSR: Exit self refresh to ACTIVE command (70ns)
   LPC_EMC->DynamicRRD  = NS_2_CLKS(12);     // tRRD: active bank A to active bank B command   
                                             //       latency                             (12ns)
   LPC_EMC->DynamicMRD  = 2;                 // tMRD: LOAD MODE REGISTER command to ACTIVE or  
                                             // REFRESH command time                      (2tCK)
</code>

With all of these parameters programmed, we are now ready to send commands to the SDRAM chip.

An important note:
<code>
   // *** Taken from the MT48LC2M32B2 data sheet, page 10 ***                                  
   // ------------------------------------------------------------------------------------------
   // SDRAMs must be powered up and initialized in a predefined manner. Operational            
   // procedures other than those specified may result in undefined operation. After power is  
   // applied to VDD and VDDQ (simultaneously) and the clock is stable (stable clock is        
   // defined as a signal cycling within timing constraints specified for the clock pin), the  
   // SDRAM requires a 100us delay prior to issuing any command other than a COMMAND           
   // INHIBIT or NOP. Starting at some point during this 100us period, and continuing at least 
   // through the end of this period, COMMAND INHIBIT or NOP commands must be                  
   // applied.                                                                                 
   // When the 100us delay has been satisfied with at least one COMMAND INHIBIT or NOP         
   // command having been applied, a PRECHARGE command should be applied. All banks            
   // must then be precharged, thereby placing the device in the all banks idle state.         
   // ------------------------------------------------------------------------------------------
</code>

so the first command we send to the SDRAM is a NOP command, and we wait for the 100us...
<code>
   // *** Taken from the MT48LC2M32B2 data sheet, page 17 ***                                  
   // The NO OPERATION (NOP) command is used to perform an NOP to an SDRAM that is             
   // selected (CS# is LOW). This prevents unwanted commands from being registered during      
   // idle or wait states. Operations already in progress are not affected.                    
   // ------------------------------------------------------------------------------------------
   // Send command: NOP                                                                        
   // (CLKOUT runs continuously; All clock enables are driven HIGH continuously)               
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000183;
  
   // wait > 100us                                                                             
   // ------------------------------------------------------------------------------------------
   for( Temp=NS_2_CLKS(100000); Temp; Temp-- );
</code>

The next command is the PRECHARGE-ALL command, and we set the correct refresh period, as follows:
<code>
   // *** Taken from the MT48LC2M32B2 data sheet, page 18 ***                                  
   // The PRECHARGE command is used to deactivate the open row in a particular bank or         
   // the open row in all banks. The bank(s) will be available for a subsequent row access a   
   // specified time (tRP) after the PRECHARGE command is issued. Input A10 determines         
   // whether one or all banks are to be precharged, and in the case where only one bank is to 
   // be precharged, inputs BA0 and BA1 select the bank. Otherwise BA0 and BA1 are treated     
   // as “Don’t Care.” After a bank has been precharged, it is in the idle state and must be   
   // activated prior to any READ or WRITE commands being issued to that bank.                 
   // ------------------------------------------------------------------------------------------
   // Send command: PRECHARGE-ALL and set the shortest possible refresh period                 
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000103;
   LPC_EMC->DynamicRefresh = 0x00000001;  // 1 x 16 = 16 CCLKs between SDRAM refresh cycles    
  
   // wait >128 clks                                                                           
   // ------------------------------------------------------------------------------------------
   for( Temp=128; Temp; Temp-- );

   // *** Taken from the MT48LC2M32B2 data sheet, page 18 ***                                  
   // Regardless of device width, the                                                          
   // 64Mb SDRAM requires 4,096 AUTO REFRESH cycles every 64ms (commercial and industrial)     
   // or 16ms (automotive). Providing a distributed AUTO REFRESH command every                 
   // 15.625us (commercial and industrial) or 3.906us (automotive) will meet the refresh       
   // requirement and ensure that each row is refreshed. Alternatively, 4,096 AUTO REFRESH     
   // commands can be issued in a burst at the minimum cycle rate (tRFC), once every 64ms      
   // (commercial and industrial) or 16ms (automotive).                                        
   // so... Refresh period (4,096 rows) tREF – 64ms                                            
   // which gives a time of 15.625us per row, or 15625ns                                       
   // ------------------------------------------------------------------------------------------
   // Set correct refresh period                                                               
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicRefresh = NS_2_CLKS(15625)>>4;     // Refresh units are x16
  
   // wait >128 clks                                                                           
   // ------------------------------------------------------------------------------------------
   for( Temp=128; Temp; Temp-- );
</code>

And finally, we send the mode command, which tells the SDRAM what the burst length is, the type of burst, the latency, etc., as follows:

<code>
   // Set mode register in SDRAM                                                               
   // A0–A10 define the op-code written to the mode register                                   
   // Bank select is A13-A14, address is A0-A12, data I/O mask is DQM0-DQM3...                 
   // ------------------------------------------------------------------------------------------
   // Mode register table for Micron's MT48LCxx (MT48LC2M32B2P ON FDI BOARD)                   
   //    bit 9:   Write Burst Mode: Programmed burst length(0), Single Location Access(1)      
   //    bit 8~7: Operating Mode: Standard Operation(0) is the only thing defined              
   //    bit 6~4: CAS latency: 001(1), 010(2), 011(3)                                          
   //    bit 3:   Type of Burst: Sequential(0) or Interleaved(1)                               
   //    bit 2~0: Burst length: 000(1), 001(2), 010(4), 011(8), 111(Full Page)                 
   //                                                                                          
  
   #ifdef ROW_BANK_COLUMN  // shift includes bank                                              
      Temp = *((volatile uint32_t *)(SDRAM_BASE_ADDR|((0x02+(CAS_Latency<<4))<<12))); 
   #else                   // shift excludes bank                                              
      Temp = *((volatile uint32_t *)(SDRAM_BASE_ADDR|((0x02+(CAS_Latency<<4))<<10))); 
   #endif
  
   // wait >128 clks                                                                           
   // ------------------------------------------------------------------------------------------
   for( Temp=128; Temp; Temp-- );
</code>

Notice the shift distance changes depending on which configuration we're using?  (ROW_BANK_COLUMN versus BANK_ROW_COLUMN)

The final step is to put the SDRAM into NORMAL operation by sending the NORMAL command, as follows:
<code>
   // ------------------------------------------------------------------------------------------
   //Send command: NORMAL                                                                      
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000000;
  
   //Enable buffer
   LPC_EMC->DynamicConfig0 |= 0x00080000;
  
   return( TRUE );
   }
</code>

This will setup your SDRAM properly for the DK-57VTS-LPC1788 development kit.

Hope this helps,

-Dave
Labels (1)
0 Kudos
35 Replies

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by hariharan on Tue Oct 22 06:29:24 MST 2013
Hi,

i'm new to this forum, I dont know how to configure EMC for sdram. i'm using
SDRAM - MT48LC2M32B2P-6:G TR 8MB SDRAM connected A0-14 and D0-15
24bit parallel LCD
Now i'm facing flickering issue due to some timing mismatch..
Please provide me sdram init function or please review the code

<******************* code start***********************>
volatile uint32_t i;
volatile uint32_t dwtemp;
EMC_DYN_MEM_Config_Type config;
TIM_TIMERCFG_Type TIM_ConfigStruct;

TIM_ConfigStruct.PrescaleOption = TIM_PRESCALE_USVAL;
TIM_ConfigStruct.PrescaleValue = 1;

// Set configuration for Tim_config and Tim_MatchConfig
TIM_Init(LPC_TIM0, TIM_TIMER_MODE,&TIM_ConfigStruct);

config.ChipSize = 64;
config.AddrBusWidth = 32;
config.AddrMap = EMC_ADD_MAP_ROW_BANK_COL;
config.CSn = 0;
config.DataWidth = 16;
config.TotalSize = SDRAM_SIZE;

config.CASLatency= 3;
config.RASLatency= 3;
config.Active2ActivePeriod = 10;
config.ActiveBankLatency = 12;
config.AutoRefrehPeriod = 10;
config.DataWidth = 16;
config.TotalSize = SDRAM_SIZE;

config.CASLatency= 3;
config.RASLatency= 3;
config.Active2ActivePeriod = 10;
config.ActiveBankLatency = 12;
config.AutoRefrehPeriod = 10;
config.DataIn2ActiveTime = 5;
config.DataOut2ActiveTime = 18;
config.WriteRecoveryTime = 7;
config.ExitSelfRefreshTime = EMC_NS2CLK(70);
config.LoadModeReg2Active = 1;
config.PrechargeCmdPeriod = 18;
config.ReadConfig = 1; /* Command delayed strategy, using EMCCLKDELAY */
config.RefreshTime = EMC_SDRAM_REFRESH(60);
config.Active2PreChargeTime = 18;
config.SeftRefreshExitTime = EMC_NS2CLK(70);
DynMem_Init(&config);

TIM_Waitms(100);
EMC_DynCtrlSDRAMInit(EMC_DYNAMIC_CTRL_SDRAM_NOP); /* Issue NOP command */

TIM_Waitms(100); /* wait 200ms */
EMC_DynCtrlSDRAMInit(EMC_DYNAMIC_CTRL_SDRAM_PALL); /* Issue Pre-charge command */

for(i = 0; i < 0x80; i++); /* wait 128 AHB clock cycles */

TIM_Waitms(100);
EMC_DynCtrlSDRAMInit(EMC_DYNAMIC_CTRL_SDRAM_MODE); /* Issue MODE command */

dwtemp = *((volatile uint32_t *)(SDRAM_BASE_ADDR|((0x02+(config.CASLatency<<4))<<12))); /* Mode Register Setting: 4 burst, 2 CAS latency */
//Timing for 48/60/72MHZ Bus
EMC_DynCtrlSDRAMInit(EMC_DYNAMIC_CTRL_SDRAM_NORMAL); /* Issue NORMAL command */

//enable buffers
EMC_DynMemConfigB(0, EMC_DYNAMIC_CFG_BUFF_ENABLED);


TIM_DeInit(LPC_TIM0);
<******************* code end ***********************>
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by kiselev.mak on Sun Mar 10 23:32:48 MST 2013
Hello, Dave!
Thanks for your response!

Thanks for advice about using the memory utility, I'll try it soon.
About "DelayConstant" - Thanks! I'll try your method for value setup.

About the read strategy I found the information in LPC24xx User Manual on page 82:
<pre>Important: Especially it should be highlighted that the default clock delay methodology
requires the output clock to be delayed externally to the chip to avoid hold time issue for
the SDRAM. In most application boards, there will be no such external delay circuit and
the application should write correct value to the EMCDynamicReadConfig register to use
Command Delay Strategy. The Clock Delay Strategy is the default setting on reset!</pre>
LPC177x8x User Manual doesn't contain this important text.

0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Fri Mar 08 16:35:01 MST 2013
Hi Evgeniy,

I'm sorry - I didn't mean to state that you should NOT write to memory using the memory window in the debugger; only that I was unable to...  :-)

I was, however, able to use the utility (as I noted)...

Regarding calculating the DelayConstant:

I didn't calculate it - I came up with it empirically (trial and error). 

The amount of delay you need is going to be layout dependent and device dependent. 

The delays for both are described here:
<code>
   // Setup delays  (Note:  UM has an error in Table #140 - should look like this:                                         
   // [Ref: LPC178x_7x_UM_1.4 page 183 ]                                                                                   
   // ----------------------------------------------------------------------------------------------------------------------
   // Bit      Symbol         Description                                                                              Reset
   //                                                                                                                  Value
   // ----------------------------------------------------------------------------------------------------------------------
   // 4:0      CMDDLY         Programmable delay value for EMC outputs in command delayed mode. See                    0x10
   //                         Section 10.12.6. The delay amount is roughly (CMDDLY+1) * 250 picoseconds.                   
   // 7:5      -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 12:8     FBCLKDLY       Programmable delay value for the feedback clock that controls input data sampling. See   0x02
   //                         Section 10.5.3. The delay amount is roughly (FBCLKDLY+1) * 250 picoseconds.                  
   // 15:13    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 20:16    CLKOUT0DLY     Programmable delay value for the CLKOUT0 output. This would typically be used in clock   0x00
   //                         delayed mode. See Section 10.12.6 The delay amount is roughly (CLKOUT0DLY+1) *               
   //                         250 picoseconds.                                                                             
   // 23:21    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 28:24    CLKOUT1DLY     Programmable delay value for the CLKOUT1 output. This would typically be used in clock   0x00
   //                         delayed mode. See Section 10.12.6 The delay amount is roughly (CLKOUT1DLY+1) *               
   //                         250 picoseconds.                                                                             
   // 31:29    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   //                                                                                                                      
   //  ---X XXXX ---X XXXX ---X XXXX ---X XXXX => DelayConstant = 0x00000C04                                               
   //  0000 0000 0000 0000 0000 1100 0000 0100 => CMDDLY=0x04=1250ps & FBCLKDLY=0x0C=3.25ns                                
</code>

This says that the largest value for CMDDLY is 0x1F, and the largest value for FBCLKDLY is 0x1F.  I started with these values and began reducing them, while running a comprehensive memory test between each change...

When I got to a value that started making the memory test fail, I decided this was a limit, and backed off by 1

I did this to try and optimize the speed/performance of my memory chip.

Remember, these values will have no effect unless you enable the delay process in the DynamicReadConfig register, as shown here:

<code>
   // configure the dynamic memory read strategy.  Note: This register is used for all four    
   // dynamic memory chip selects. Therefore the worst case value for all of the chip selects  
   // must be programmed.                                                                      
   // [Ref: LPC178x_7x_UM_0.01 page 169 Table 117]                                             
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicReadConfig = 0x00000001;  // Command delayed strategy, using EMCCLKDELAY    
                                             // (command delayed, clock out not delayed)       
</code>

I'm sorry I can't be of more help here.

Perhaps one of the EMC Guru's out there would like to chime in and better explain?

Regarding a link:  No, I don't have an internet link to provide you.  Have you searched the net for this topic?

I hope this helps,

0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by kiselev.mak on Mon Feb 25 03:43:54 MST 2013
Hi, Dave!
Thanks for the response.

you wrote:<pre>Within the debugger, you cannot write to memory using the memory window - you'd have to use the utility in P(eripherals, C(locking and Power Control, C(lock Dividers</pre>


Why? Can you give me the reference to information on cautions in case of reading/writing of memory in a debugger?
I can read/write all of microcontroller memory in debugger including external memory (in this case the picture on my LCD changes in real time). I don`t know why you cannot write to memory using the memory window.

Also I have another questions about your configuration code.
1) How do you calculate "DelayConstant"?
2) Based on what you have configured the dynamic memory read strategy? Can you give me a internet link to information to learn about read strategy?


Thanks a lot, Dave!
Best regards, Evgeniy.
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Fri Feb 22 10:58:47 MST 2013
Hi,

I repeated your experiment, and I got the same results as you - I am puzzled by this, so I am going to investigate...

Edited a few minutes later... - After looking into this a little further, and reading the user's manual, here is what I found:

The EMC is enabled on reset, so you'd actually have to turn it off if you didn't want to use it.

Within the debugger, you cannot write to memory using the memory window - you'd have to use the utility in P(eripherals, C(locking and Power Control, C(lock Dividers
Here you can change the value of the EMCDIV from 0x00 to 0x01, and verify it with a scope on P2.18 - I did this and it works fine...

The CLKOUT function has nothing to do with the EMC clock - it has to do with the CLKOUT pin, which can be either P1.25 or P1.27.

Since I am not using those pins for a CLKOUT, but rather for my LCD color lines, I shouldn't have had that line of code in there...

I'm not sure what I was doing back then, but clearly this line of code is useless, since the pins have already been configured for the LCD...

I recommend you delete this line of code.

Hope this helps,
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by kiselev.mak on Fri Feb 22 06:04:42 MST 2013
Hello, Dave!
Thanks for your answer!
I have the PCB design same as well as yours. SDRAM have clocked from P2[18]/CLKOUT0.
I check the CLKOUTCFG register in runtime in debugger (Keil uVision4) and can change that value in runtime.
Nothing changed in having access to SDRAM when I change the value from 0x00000000 to 0x00000100 and vice versa (excepting what that register reads as 0x00000300 after changing value to 0x00000100, because bit:9 - CLKOUT activity indication.Reads as 1 when CLKOUT is enabled.). In case of any value of CLKOUTCFG register (address 0x400FC1C8) LCD controller have access to SDRAM and LCD have a good picture.
Can you repeat my experiment in runtime in debugger mode?
Sorry about my bad English. I'm Russian.
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Tue Feb 19 09:56:04 MST 2013
Sure,

The EMC_CLK[0] pin (P2.18 on my device) feeds the CLK signal on my SDRAM.

The speed of the clock is set by this register.

The value 0x00000110 means the following:

bits: 3:0 - use the CPU clock as the CLKOUT source
bits: 7:4 - clock is divided by 2 (so that means use CPU clock / 2)
bit : 8   - enable the clock...

You can read about this in the user's manual, in Table 35, on page 45.

Perhaps the reason you are able to remove this line of code without affecting the performance is because you are defining it somewhere else.

If you are using the CMSIS functions, you will notice in the system_LPC177x_8x.c file that this parameter is set in the configuration wizard.

I hope this helps,

-Dave
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by kiselev.mak on Mon Feb 18 04:10:10 MST 2013
Hello, Dave!
Thank you for your initialization code.
I have one question about that: why do you enable the clock output by this code:

 LPC_SC->CLKOUTCFG = 0x00000110; 


I have tried without this code. Nothing changed in work with External Memory and all looks like all right. Could you explain to me the function of this code?
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Tue Sep 25 09:03:12 MST 2012
I have several suggestions:

1. when you post code, surround the post with < code >  before  and < / code > after...

It makes the post look nicer and easier to read (like this:)
<code>
void myroutine( void )
   {
   int VeryEasy;

   ThisIsEasyToRead();
   return( VeryEasy );
   }
</code>

2.  Post a schematic of your board, and maybe even a layout.  This will help me see if the device is connected correctly.

3.  If this is not a custom board (eg: you purchased it from a reputable company or a development kit board), then we can assume the board layout is correct, and skip the previous step.

4.  Your delay constants could be the problem.  The value stored to EMCDLYCTL register were chosen for MY board, which is dependent on MY layout.  If your board is not the exact same board, then your delay constants will be different.  The maximum delay value is 0x00001F1F...

5.  You might try slowing everything down to something like 24Mhz (not 120Mhz or even 96Mhz)...  Speed is nice, but this could be compounding the problems for you during development...  Start slow, and once it works well, slowly increase the speed, adjusting the delay constants as necessary.

6.  Use a comprehensive memory test routine, and dump your results out to the serial port - you can capture serial output with TeraTerm or HyperTerminal...  using the debugger to capture timing results can sometimes introduce errors just from the overhead of the debugger...

Hope this helps,
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee

Content originally posted in LPCWare by shricco on Tue Sep 25 08:00:04 MST 2012
Hi Dave,

sorry for my late response. But the problem is still the same. As I am using the LPC1788 and the same RAM as you do - the code to init the ram is exactly the same as yours:

#include "sdram_init.h"


void INIT_SDRAM( void ){
uint32_t Temp = 0; // =Temp
uint32_t CAS_Latency, RAS_Latency;// these can be 1,2 or 3 clks
uint32_t DelayConstant;
        uint32_t clock = SystemCoreClock;
    // configure SDRAM control signals                                                          
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P2_16 |= 1;              // CASN @ P2.16         (SDRAM Column Address Strobe)   
   LPC_IOCON->P2_17 |= 1;              // RASN @ P2.17         (SDRAM Row Address Strobe)      
   LPC_IOCON->P2_18 |= 1;              // CLK[0] @ P2.18       (SDRAM System Clock)            
   LPC_IOCON->P2_20 |= 1;              // DYCSN[0] @ P2.20     (SDRAM Chip Select)             
   LPC_IOCON->P2_24 |= 1;              // CKE[0] @ P2.24       (SDRAM Clock Enable)            
   LPC_IOCON->P2_28 |= 1;              // DQM[0] @ P2.28       (SDRAM Data Input/Output Mask)  
   LPC_IOCON->P2_29 |= 1;              // DQM[1] @ P2.29       (SDRAM Data Input/Output Mask)  
   LPC_IOCON->P2_30 |= 1;              // DQM[2] @ P2.30       (SDRAM Data Input/Output Mask)  
   LPC_IOCON->P2_31 |= 1;              // DQM[3] @ P2.31       (SDRAM Data Input/Output Mask)  

   // configure for 32-bit external data bus                                                   
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P3_0 |= 1;               // D0 @ P3.0                                            
   LPC_IOCON->P3_1 |= 1;               // D1 @ P3.1                                            
   LPC_IOCON->P3_2 |= 1;               // D2 @ P3.2                                            
   LPC_IOCON->P3_3 |= 1;               // D3 @ P3.3                                            
   LPC_IOCON->P3_4 |= 1;               // D4 @ P3.4                                            
   LPC_IOCON->P3_5 |= 1;               // D5 @ P3.5                                            
   LPC_IOCON->P3_6 |= 1;               // D6 @ P3.6                                            
   LPC_IOCON->P3_7 |= 1;               // D7 @ P3.7                                            
   LPC_IOCON->P3_8 |= 1;               // D8 @ P3.8                                            
   LPC_IOCON->P3_9 |= 1;               // D9 @ P3.9                                            
   LPC_IOCON->P3_10 |= 1;              // D10 @ P3.10                                          
   LPC_IOCON->P3_11 |= 1;              // D11 @ P3.11                                          
   LPC_IOCON->P3_12 |= 1;              // D12 @ P3.12                                          
   LPC_IOCON->P3_13 |= 1;              // D13 @ P3.13                                          
   LPC_IOCON->P3_14 |= 1;              // D14 @ P3.14                                          
   LPC_IOCON->P3_15 |= 1;              // D15 @ P3.15                                          
   LPC_IOCON->P3_16 |= 1;              // D16 @ P3.16                                          
   LPC_IOCON->P3_17 |= 1;              // D17 @ P3.17                                          
   LPC_IOCON->P3_18 |= 1;              // D18 @ P3.18                                          
   LPC_IOCON->P3_19 |= 1;              // D19 @ P3.19                                          
   LPC_IOCON->P3_20 |= 1;              // D20 @ P3.20                                          
   LPC_IOCON->P3_21 |= 1;              // D21 @ P3.21                                          
   LPC_IOCON->P3_22 |= 1;              // D22 @ P3.22                                          
   LPC_IOCON->P3_23 |= 1;              // D23 @ P3.23                                          
   LPC_IOCON->P3_24 |= 1;              // D24 @ P3.24                                          
   LPC_IOCON->P3_25 |= 1;              // D25 @ P3.25                                          
   LPC_IOCON->P3_26 |= 1;              // D26 @ P3.26                                          
   LPC_IOCON->P3_27 |= 1;              // D27 @ P3.27                                          
   LPC_IOCON->P3_28 |= 1;              // D28 @ P3.28                                          
   LPC_IOCON->P3_29 |= 1;              // D29 @ P3.29                                          
   LPC_IOCON->P3_30 |= 1;              // D30 @ P3.30                                          
   LPC_IOCON->P3_31 |= 1;              // D31 @ P3.31                                          

   // configure for 32-bit external address bus                                                
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P4_0 |= 1;               //A0 @ P4.0                                             
   LPC_IOCON->P4_1 |= 1;               //A1 @ P4.1                                             
   LPC_IOCON->P4_2 |= 1;               //A2 @ P4.2                                             
   LPC_IOCON->P4_3 |= 1;               //A3 @ P4.3                                             
   LPC_IOCON->P4_4 |= 1;               //A4 @ P4.4                                             
   LPC_IOCON->P4_5 |= 1;               //A5 @ P4.5                                             
   LPC_IOCON->P4_6 |= 1;               //A6 @ P4.6                                             
   LPC_IOCON->P4_7 |= 1;               //A7 @ P4.7                                             
   LPC_IOCON->P4_8 |= 1;               //A8 @ P4.8                                             
   LPC_IOCON->P4_9 |= 1;               //A9 @ P4.9                                             
   LPC_IOCON->P4_10 |= 1;              //A10 @ P4.10                                           
   LPC_IOCON->P4_11 |= 1;              //A11 @ P4.11                                           
   LPC_IOCON->P4_12 |= 1;              //A12 @ P4.12                                           
   LPC_IOCON->P4_13 |= 1;              //A13 @ P4.13                                           
   LPC_IOCON->P4_14 |= 1;              //A14 @ P4.14                                           
   LPC_IOCON->P4_15 |= 1;              //A15 @ P4.15                                           
   LPC_IOCON->P4_16 |= 1;              //A16 @ P4.16                                           
   LPC_IOCON->P4_17 |= 1;              //A17 @ P4.17                                           
   LPC_IOCON->P4_18 |= 1;              //A18 @ P4.18                                           
   LPC_IOCON->P4_19 |= 1;              //A19 @ P4.19                                           
   //LPC_IOCON->P4_20 |= 1;             //A20 @ P4.20                                            
   //LPC_IOCON->P4_21 |= 1;             //A21 @ P4.21                                            
   //LPC_IOCON->P4_22 |= 1;             //A22 @ P4.22                                            
   //LPC_IOCON->P4_23 |= 1;             //A23 @ P4.23                                            
  
   // Configure write enable for SDRAM...                                                      
   // ------------------------------------------------------------------------------------------
   LPC_IOCON->P4_25 |= 1;              // WEN @ P4.25                                          

   // Enable EMC Reset, (both EMC resets are asserted when any type of reset occurs)           
   // [Ref: LPC178x_7x_UM_1.4 page 32 ]                                                        
   // ------------------------------------------------------------------------------------------
   LPC_SC->SCS &= ~(1<<1);    // clears bit 1, which is the power on default state anyway...   
  
   // Enable EMC power/clock control bit...                                                    
   // ------------------------------------------------------------------------------------------
   LPC_SC->PCONP   |= (1<<11);   


if( SystemCoreClock < 100000000 )   // longer delay constant, and shorter CAS and RAS latency
      {                                //                                                      
      LPC_SC->EMCCLKSEL = 0x00000001;  // EMC uses a clock rate at 1/2 CPU clock rate...       
      LPC_SC->CLKOUTCFG = 0x00000110;  // enables clock, sets clock source as CPU clk / 2...   
      CAS_Latency = 3;
      RAS_Latency = 3;
      DelayConstant = 0x00000604;
      }
   else
      {
      LPC_SC->EMCCLKSEL = 0x00000000;  // EMC uses a clock rate equal to CPU clock rate...     
      LPC_SC->CLKOUTCFG = 0x00000100;  // enables clock, sets clock source as CPU clk / 1...   
      CAS_Latency = 2;
      RAS_Latency = 2;
      DelayConstant = 0x00000A05;
      }

   // [Ref: LPC178x_7x_UM_1.4 page 183 ]                                                                                   
   // ----------------------------------------------------------------------------------------------------------------------
   // Bit      Symbol         Description                                                                              Reset
   //                                                                                                                  Value
   // ----------------------------------------------------------------------------------------------------------------------
   // 4:0      CMDDLY         Programmable delay value for EMC outputs in command delayed mode. See                    0x10
   //                         Section 10.12.6. The delay amount is roughly (CMDDLY+1) * 250 picoseconds.                   
   // 7:5      -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 12:8     FBCLKDLY       Programmable delay value for the feedback clock that controls input data sampling. See   0x02
   //                         Section 10.5.3. The delay amount is roughly (FBCLKDLY+1) * 250 picoseconds.                  
   // 15:13    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 20:16    CLKOUT0DLY     Programmable delay value for the CLKOUT0 output. This would typically be used in clock   0x00
   //                         delayed mode. See Section 10.12.6 The delay amount is roughly (CLKOUT0DLY+1) *               
   //                         250 picoseconds.                                                                             
   // 23:21    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   // 28:24    CLKOUT1DLY     Programmable delay value for the CLKOUT1 output. This would typically be used in clock   0x00
   //                         delayed mode. See Section 10.12.6 The delay amount is roughly (CLKOUT1DLY+1) *               
   //                         250 picoseconds.                                                                             
   // 31:29    -              Reserved. Read value is undefined, only zero should be written.                          NA  
   LPC_SC->EMCDLYCTL   = DelayConstant;

   // Reset the EMC and put configuration back to power up reset (little-endian mode, 1:1 clock)
   // [Ref: LPC178x_7x_UM_0.01 page 156]                                                       
   // ------------------------------------------------------------------------------------------
   LPC_EMC->Control = 0x00000001;   // Disable Address mirror, leave EMC enabled...            
   LPC_EMC->Config  = 0x00000000;
  
   // define connection configuration [32 bit external bus address mapping]                    
   // ------------------------------------------------------------------------------------------
   #ifdef ROW_BANK_COLUMN
      LPC_EMC->DynamicConfig0 = 0x00004300;  // row, bank, column, 32-bit
   #else
      LPC_EMC->DynamicConfig0 = 0x00005300;  // bank, row, column, 32-bit
   #endif 
  
   // define delays for RAS and CAS...                                                         
   // bits 1:0-RAS latency, bits 9:8-CAS latency (in CCLK cycle counts)                        
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicRasCas0 = RAS_Latency + (CAS_Latency<<8);    // reset value is 3 and 3       
     
   // configure the dynamic memory read strategy.  Note: This register is used for all four    
   // dynamic memory chip selects. Therefore the worst case value for all of the chip selects  
   // must be programmed.                                                                      
   // [Ref: LPC178x_7x_UM_0.01 page 169 Table 117]                                             
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicReadConfig = 0x00000001;  // Command delayed strategy, using EMCCLKDELAY    
                                             // (command delayed, clock out not delayed)



// *** Taken from the MT48LC2M32B2 data sheet, page 47 ***                                  
   // configure timing, from Table 18:                                                         
   // -6 (6ns part) timings (see marking on actual chip: MT48LC2M32B2-6)                       
   // NOTE: all timing values for LPC1788 are in units of CLK counts                           
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicRP   = NS_2_CLKS(18);     // tRP: precharge command period             (18ns)
   LPC_EMC->DynamicRAS  = NS_2_CLKS(42);     // tRAS: active to precharge command period  (42ns)
   LPC_EMC->DynamicSREX = NS_2_CLKS(70);     // tXSR: self-refresh exit time              (70ns)
   LPC_EMC->DynamicAPR  = NS_2_CLKS(18);     // tAPR: last-data-out to active command time     
                                             // note: no tAPR value, using tRCD value     (18ns)
   LPC_EMC->DynamicDAL  = CAS_Latency+2;     // tDAL: data-in to active command time           
                                             //       for CL=3, tDAL = 5 tCK                   
                                             //       for CL=2, tDAL = 4 tCK                   
                                             //       for CL=1, tDAL = 3 tCK                   
   LPC_EMC->DynamicWR   = (NS_2_CLKS(6)+1);  // tWR: write recovery time is (12ns) UNLESS we're
                                             //      using AUTO PRECHARGE, then it's   (tCK+6ns)
   LPC_EMC->DynamicRC   = NS_2_CLKS(60);     // tRC: ACTIVE-to-ACTIVE command period      (60ns)
   LPC_EMC->DynamicRFC  = NS_2_CLKS(60);     // tRFC: AUTO REFRESH period                 (60ns)
   LPC_EMC->DynamicXSR  = NS_2_CLKS(70);     // tXSR: Exit self refresh to ACTIVE command (70ns)
   LPC_EMC->DynamicRRD  = NS_2_CLKS(12);     // tRRD: active bank A to active bank B command   
                                             //       latency                             (12ns)
   LPC_EMC->DynamicMRD  = 2;                 // tMRD: LOAD MODE REGISTER command to ACTIVE or  
                                             // REFRESH command time                      (2tCK)

// *** Taken from the MT48LC2M32B2 data sheet, page 10 ***                                  
   // ------------------------------------------------------------------------------------------
   // SDRAMs must be powered up and initialized in a predefined manner. Operational            
   // procedures other than those specified may result in undefined operation. After power is  
   // applied to VDD and VDDQ (simultaneously) and the clock is stable (stable clock is        
   // defined as a signal cycling within timing constraints specified for the clock pin), the  
   // SDRAM requires a 100us delay prior to issuing any command other than a COMMAND           
   // INHIBIT or NOP. Starting at some point during this 100us period, and continuing at least 
   // through the end of this period, COMMAND INHIBIT or NOP commands must be                  
   // applied.                                                                                 
   // When the 100us delay has been satisfied with at least one COMMAND INHIBIT or NOP         
   // command having been applied, a PRECHARGE command should be applied. All banks            
   // must then be precharged, thereby placing the device in the all banks idle state.         
   // ------------------------------------------------------------------------------------------

// *** Taken from the MT48LC2M32B2 data sheet, page 17 ***                                  
   // The NO OPERATION (NOP) command is used to perform an NOP to an SDRAM that is             
   // selected (CS# is LOW). This prevents unwanted commands from being registered during      
   // idle or wait states. Operations already in progress are not affected.                    
   // ------------------------------------------------------------------------------------------
   // Send command: NOP                                                                        
   // (CLKOUT runs continuously; All clock enables are driven HIGH continuously)               
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000183;
  
   // wait > 100us                                                                             
   // ------------------------------------------------------------------------------------------
   for( Temp=NS_2_CLKS(100000+1); Temp; Temp-- );

// *** Taken from the MT48LC2M32B2 data sheet, page 18 ***                                  
   // The PRECHARGE command is used to deactivate the open row in a particular bank or         
   // the open row in all banks. The bank(s) will be available for a subsequent row access a   
   // specified time (tRP) after the PRECHARGE command is issued. Input A10 determines         
   // whether one or all banks are to be precharged, and in the case where only one bank is to 
   // be precharged, inputs BA0 and BA1 select the bank. Otherwise BA0 and BA1 are treated     
   // as “Don’t Care.” After a bank has been precharged, it is in the idle state and must be   
   // activated prior to any READ or WRITE commands being issued to that bank.                 
   // ------------------------------------------------------------------------------------------
   // Send command: PRECHARGE-ALL and set the shortest possible refresh period                 
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000103;
   LPC_EMC->DynamicRefresh = 0x00000001;  // 1 x 16 = 16 CCLKs between SDRAM refresh cycles    
  
   // wait >128 clks                                                                           
   // ------------------------------------------------------------------------------------------
   for( Temp=128; Temp; Temp-- );


   // *** Taken from the MT48LC2M32B2 data sheet, page 18 ***                                  
   // Regardless of device width, the                                                          
   // 64Mb SDRAM requires 4,096 AUTO REFRESH cycles every 64ms (commercial and industrial)     
   // or 16ms (automotive). Providing a distributed AUTO REFRESH command every                 
   // 15.625us (commercial and industrial) or 3.906us (automotive) will meet the refresh       
   // requirement and ensure that each row is refreshed. Alternatively, 4,096 AUTO REFRESH     
   // commands can be issued in a burst at the minimum cycle rate (tRFC), once every 64ms      
   // (commercial and industrial) or 16ms (automotive).                                        
   // so... Refresh period (4,096 rows) tREF – 64ms                                            
   // which gives a time of 15.625us per row, or 15625ns                                       
   // ------------------------------------------------------------------------------------------
   // Set correct refresh period                                                               
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicRefresh = NS_2_CLKS(3906>>4);     // Refresh units are x16
  
   // wait >128 clks                                                                           
   // ------------------------------------------------------------------------------------------
   for( Temp=128; Temp; Temp-- );
   // ------------------------------------------------------------------------------------------
   // Send command: MODE                                                                       
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000083;
    
   // Set mode register in SDRAM                                                               
   // A0–A10 define the op-code written to the mode register                                   
   // Bank select is A13-A14, address is A0-A12, data I/O mask is DQM0-DQM3...                 
   // ------------------------------------------------------------------------------------------
   // Mode register table for Micron's MT48LCxx (MT48LC2M32B2P ON FDI BOARD)                   
   //    bit 9:   Write Burst Mode: Programmed burst length(0), Single Location Access(1)      
   //    bit 8~7: Operating Mode: Standard Operation(0) is the only thing defined              
   //    bit 6~4: CAS latency: 001(1), 010(2), 011(3)                                          
   //    bit 3:   Type of Burst: Sequential(0) or Interleaved(1)                               
   //    bit 2~0: Burst length: 000(1), 001(2), 010(4), 011(8), 111(Full Page)                 
   //                                                                                          
  
   #ifdef ROW_BANK_COLUMN  // shift includes bank                                              
      Temp = *((volatile uint32_t *)(SDRAM_BASE_ADDR|((0x02+(CAS_Latency<<4))<<12))); 
   #else                   // shift excludes bank                                              
      Temp = *((volatile uint32_t *)(SDRAM_BASE_ADDR|((0x02+(CAS_Latency<<4))<<10))); 
   #endif
  
   // wait >128 clks                                                                           
   // ------------------------------------------------------------------------------------------
   for( Temp=128; Temp; Temp-- );

// ------------------------------------------------------------------------------------------
   //Send command: NORMAL                                                                      
   // ------------------------------------------------------------------------------------------
   LPC_EMC->DynamicControl = 0x00000000;
  
   //Enable buffer
   LPC_EMC->DynamicConfig0 |= 0x00080000;

  
}

The routine to test the rame is very simple:

volatile unsigned long *start= (volatile unsigned long *) 0xA0000000UL;
  int value=0;
  int i;
  for(i = 0; i < 64 * 1048576; i+= 4){
    *start = value; 
    start++;
    value++;
  }

When i watch the memory during execution of the loop it looks good, but after the loop the memory gets faulty. I try to attache a screenshot - but last time i didnt work.

In the screenshot you can see these weired E0 values and it seems they follow the same rule. Of each 4 byte range the third byte is e0. So not the whole ram seems to get corrupted.

Little update: I now figured out that only the lower 2 bytes remain in ram and the upper 2 bytes get lost of each 4 byte value. Very weired.

I tried it with 120Mhz and 96 Mhz system clock.

Thanks again.

0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Fri Aug 31 08:02:24 MST 2012
Well, let's see - I'm not sure there's enough information in your description to tell you what's wrong, but I'd be willing to assist.

Can you post your code?

Probably it would be a good idea to post both the initialization code, and the memory test routines.

I'll have a look and see if I can see something.
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by shricco on Fri Aug 31 04:04:43 MST 2012
Hi Dave,

I followed your instructions and was able to successfully set up the ram with my LPC1788. The only Problem i have now is, that after some time the value of the memory gets faulty. As for example i tried to write a value at each available address (0xa0000000 - 0xa3fffffff). So far everything works fine. But after some time (at the end of my memory test) the value of the memory just changes mostly into "e0" all over.

Did i miss something in your instruction?

Any help or hint would be amazing.

Thanks
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Wed Apr 04 09:29:13 MST 2012
Wolfgang is correct, it's not easy, but it is possible - when you follow the steps outlined in your SDRAM User's Manual... You must also have the processor setup correctly, and obviously you have to have the SDRAM connected correctly.

Is this on a PCB that you made yourself, or is it something you purchased?  If you did your own board, then did you follow the layout guidelines for SDRAM/DDR ??

What speed are you running the EMC?  What have you already tried?  Can you describe what you have, and and what you have done so far?

Hope this helps,


0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by wmues on Wed Apr 04 06:13:45 MST 2012
engin,

your question is too unspecific to be answered. Connecting a SDRAM is a complex process, and there are many possibilities to get it wrong.

Please post your code here.

Do you have the EMC clock divider set to "divide by 2"? The EMC clock is driven by the CPU clock, and is limited to 80 MHz.
0 Kudos

1,749 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by e135193 on Wed Mar 28 01:47:49 MST 2012
Hello Dave

     As far as I understand you have a knowledge of EMC configuration of SDRAM(s). I need your help cos I confused.

     First of all, I'm using LPC1788 with IS42S16400,SDRAM and running CPU with 120MHz. I'm trying to configure my EMC peripheral for 16bit bus length, However I could not. I read your thread many times and refer the datasheet of my SDRAM,IS42S16400.

     Could you please give me a hand ?

Best Regards

engin
0 Kudos

1,751 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Wed Mar 21 20:11:26 MST 2012
That sounds like a good idea, Phil.  Thanks for contributing to this thread.
0 Kudos

1,751 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by PhilYoung on Wed Mar 21 10:57:01 MST 2012
This code seems to have a potential problem in that it cannot be guaranteed to perform correctly if it's compiled with optimization enabled.

The problem is that the volatile only qualifies the r-value, not the l-value and the value assigned to temp is not required later, therefore the optimization can completely remove the access to memory that sets up the mode command on the address pins.

In order to ensure that it is not completely removed either optimization needs to be disabled for this section, or the value needs to be assigned to something that prevents the compiler from optimizing it away.

probably the safest solution, which I often use for this type of issue, is simply to declare a global volatile unsigned variable and then whenever I need to ensure a read occurs I simply assign the result to this variable. As the variable is a global volatile there is no way the compiler can optimize the assignment away which then means the code is safe at all optimization levels.
0 Kudos

1,751 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Dave on Tue Aug 23 21:21:09 MST 2011
Okay, that's pretty cool - two multiplies and some shifting...

I looked around on the net for some more tricks on "time efficient" coding, and ran across this:

http://www.hackersdelight.org/divcMore.pdf

just wanted to share.

Thanks again Wolfgang,

0 Kudos

1,751 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Marc Crandall on Fri Aug 19 08:24:02 MST 2011
Good question.
What compiler are you using?  I'm using Keil's armcc.  The for loop delay as written by Dave above is not being optimized away with my current settings, however you are right it may very well be if/when I change my optimization settings. (although I'm not sure of this?)

You can always flag the section with --no_remove (or similar depending on you compiler/linker)

why does making the variable volatile make "ugly code"?  What I think I will do in the end in insert some inline assembly for these delay loops.

If you make the variable volatile and see what assembly is generated you can quickly find out the number of clock cycles per loop and divide the loop count accordingly.

For example:

<pre>
     for(i = 0xFF; i ; i--);
000004  4608              MOV      r0,r1
000006  e002              B        |L1.14|
                  |L1.8|
000008  f1a00101          SUB      r1,r0,#1
00000c  4608              MOV      r0,r1
                  |L1.14|
00000e  2800              CMP      r0,#0
000010  d1fa              BNE      |L1.8|
;;;12        
;;;13        
;;;14         for(j = 0xFF; j ; j--);
000012  f04f01ff          MOV      r1,#0xff
000016  4a07              LDR      r2,|L1.52|
000018  6011              STR      r1,[r2,#0]  ; j
00001a  e005              B        |L1.40|
                  |L1.28|
00001c  4905              LDR      r1,|L1.52|
00001e  6809              LDR      r1,[r1,#0]  ; j
000020  f1a10101          SUB      r1,r1,#1
000024  4a03              LDR      r2,|L1.52|
000026  6011              STR      r1,[r2,#0]  ; j
                  |L1.40|
000028  4902              LDR      r1,|L1.52|
00002a  6809              LDR      r1,[r1,#0]  ; j
00002c  2900              CMP      r1,#0
00002e  d1f5              BNE      |L1.28|
</pre>

So in this case "j" is volatile and "i" is not.  Just count up the clock cycles in each loop and divide the initial count - should work.

But really this is a very minor delay that is only done in init so you could just live with the extended delays. (i.e. just make it volatile and know you are waiting several times the required minimums)
0 Kudos

1,751 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by wmues on Fri Aug 19 06:43:56 MST 2011
Hi all,

I have done a short delay loop which is compatible with compiler optimisation:

<code>
// Delay loop for short busy waits
static inline void wait_clocks( unsigned int clocks)
{
   do {
      asm("":::"memory"); // hint for gcc: do not remove this loop!
   } while (--clocks);
}
</code>
0 Kudos