External SDRAM EMC Configuration Help W9816G6JH-6

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

External SDRAM EMC Configuration Help W9816G6JH-6

Jump to solution
1,300 Views
guitardenver
Contributor IV

Hello,

I am developing firmware on the LPC54605J256ET180. I have custom hardware with an external SDRAM chip connected to the EMC. I was hoping I could get some help configuring all the different timing registers for my RAM chip.

I am using the NXP emc driver and following the demo app used for the OM13092 LPCXpresso54608 Dev board.

RAM chip:

Winbond W9816G6JH-6

MCU Setup:

1) Using 12MHz external crystal and running core clock at 180MHz

Initialization Code:

// The SDRAM timing.
#define HW_SDRAM_REFRESHPERIOD_NS (32 * 1000000 / 2048) // 4096 rows/ 64ms) // 4096 rows/ 64ms
#define HW_SDRAM_TRP_NS (18u) ///< Precharge command period
#define HW_SDRAM_TRAS_NS (42u) ///< Active to precharge command period
#define HW_SDRAM_TSREX_NS (72u) ///< Self-refresh exit time
#define HW_SDRAM_TAPR_NS (18u) ///< Last-data-out to active command time
#define HW_SDRAM_TWRDELT_NS (22u) ///< Write recovery time
#define HW_SDRAM_TRC_NS (60u) ///< Selects the active to active command period
#define HW_SDRAM_RFC_NS (60u) ///< Selects the auto-refresh period
#define HW_SDRAM_XSR_NS (72u) ///< Time for exit self-refresh to active command
#define HW_SDRAM_RRD_NS (12u) ///< Latency for active bank A to active bank B
#define HW_SDRAM_MRD_NCLK (2u)
#define HW_SDRAM_RAS_NCLK (2u)
#define HW_SDRAM_MODEREG_VALUE (0x23u)
#define HW_SDRAM_DEV_MEMORYMAP (0x21u) // 16Mbits (1M*16, 2banks, 11 rows, 8 columns)
static void HW_InitSDRAM(void) {
emc_basic_config_t basicConfig;
emc_dynamic_timing_config_t dynTiming;
emc_dynamic_chip_config_t dynChipConfig;

// Initialize the I/O pins
HW_InitSDRAMPins();
// BOARD_InitPins();

/* Basic configuration. */
basicConfig.endian = kEMC_LittleEndian;
basicConfig.fbClkSrc = kEMC_IntloopbackEmcclk;
/*
* EMC Clock = CPU FREQ/2 here can fit CPU freq from 12M ~ 180M.
* If you change the divide to 0 and EMC clock is larger than 100M
* please take refer to emc.dox to adjust EMC clock delay.
*/
basicConfig.emcClkDiv = 1;
/* Dynamic memory timing configuration. */
dynTiming.readConfig = kEMC_Cmddelay;
dynTiming.refreshPeriod_Nanosec = HW_SDRAM_REFRESHPERIOD_NS;
dynTiming.tRp_Ns = HW_SDRAM_TRP_NS;
dynTiming.tRas_Ns = HW_SDRAM_TRAS_NS;
dynTiming.tSrex_Ns = HW_SDRAM_TSREX_NS;
dynTiming.tApr_Ns = HW_SDRAM_TAPR_NS;
dynTiming.tWr_Ns =
(((1000000000 / CLOCK_GetFreq(kCLOCK_EMC)) * 1) + HW_SDRAM_TWRDELT_NS); /* one clk + HW_SDRAM_TWRDELT_NS */
dynTiming.tDal_Ns = dynTiming.tWr_Ns + dynTiming.tRp_Ns;
dynTiming.tRc_Ns = HW_SDRAM_TRC_NS;
dynTiming.tRfc_Ns = HW_SDRAM_RFC_NS;
dynTiming.tXsr_Ns = HW_SDRAM_XSR_NS;
dynTiming.tRrd_Ns = HW_SDRAM_RRD_NS;
dynTiming.tMrd_Nclk = HW_SDRAM_MRD_NCLK;
/* Dynamic memory chip specific configuration: Chip 0 - MTL48LC8M16A2B4-6A */
dynChipConfig.chipIndex = 0;
dynChipConfig.dynamicDevice = kEMC_Sdram;
dynChipConfig.rAS_Nclk = HW_SDRAM_RAS_NCLK;
dynChipConfig.sdramModeReg = HW_SDRAM_MODEREG_VALUE;
dynChipConfig.sdramExtModeReg = 0; /* it has no use for normal sdram */
dynChipConfig.devAddrMap = HW_SDRAM_DEV_MEMORYMAP;
/* EMC Basic configuration. */
EMC_Init(EMC, &basicConfig);
/* EMC Dynamc memory configuration. */
EMC_DynamicMemInit(EMC, &dynTiming, &dynChipConfig, 1);
}

With the code above, it passes the test routines, BUT only when I run the MCU core clock at 96MHz with BOARD_BootClockFROHF96M.

But when I run the core clock at 180MHz with BOARD_BootClockPLL180M, it fails the _______.

I increased the clock divider, but that does not seem to get it to work. I'm guessing the timing parameters are wrong. I kinda had to guess on some of them because i'm not sure how to get them from the datasheet. 

Specifically the HW_SDRAM_REFRESHPERIOD_NS parameter. I'm not sure how to choose this value with the datasheet.

The test routines are the same ones used in the lpcxpresso54608_driver_examples_emc_sdram. But here there are anyway:

#define SDRAM_BASE_ADDR 0xa0000000
#define SDRAM_SIZE_BYTES 2000000 //(8 * 1024 * 1024)
#define SDRAM_EXAMPLE_DATALEN (SDRAM_SIZE_BYTES / 4)
#define SDRAM_TEST_PATTERN (2)
pti_return_t SDRAM_DataBusCheck(volatile uint32_t* address) {
uint32_t data = 0;

/* Write the walking 1's data test. */
for(data = 1; data != 0; data <<= 1) {
*address = data;

/* Read the data out of the address and check. */
if(*address != data) {
return pti_error;
}
}
return pti_success;
}

pti_return_t SDRAM_AddressBusCheck(volatile uint32_t* address, uint32_t bytes) {
uint32_t pattern = 0x55555555;
uint32_t size = bytes / 4;
uint32_t offset;
uint32_t checkOffset;

/* write the pattern to the power-of-two address. */
for(offset = 1; offset < size; offset <<= 1) {
   address[offset] = pattern;
}
address[0] = ~pattern;

/* Read and check. */
uint32_t *ptr = NULL;
for(offset = 1; offset < size; offset <<= 1) {
   ptr = &address[offset];
   if(*ptr != pattern) {
      return pti_error;
   }
}

if(address[0] != ~pattern) {
   return pti_error;
}

/* Change the data to the revert one address each time
* and check there is no effect to other address. */
for(offset = 1; offset < size; offset <<= 1) {
   address[offset] = ~pattern;
   for(checkOffset = 1; checkOffset < size; checkOffset <<= 1) {
      if((checkOffset != offset) && (address[checkOffset] != pattern)) {
         return pti_error;
      }
   }
   address[offset] = pattern;
}
return pti_success;
}

static void test(void) {
volatile uint32_t index;
uint32_t* sdram = (uint32_t*)SDRAM_BASE_ADDR; /* SDRAM start address. */

/* Data/address bus check. */
if(SDRAM_DataBusCheck(sdram) != pti_success) {
LOG_DEBUG(MOD_ALL_BASE, "\r\n SDRAM data bus check is failure.\r\n");
}

if(SDRAM_AddressBusCheck(sdram, SDRAM_SIZE_BYTES) != pti_success) {
LOG_DEBUG(MOD_ALL_BASE, "\r\n SDRAM address bus check is failure.\r\n");
}

LOG_DEBUG(MOD_ALL_BASE, "\r\n Start EMC SDRAM access example.\r\n");
LOG_DEBUG_PF(MOD_ALL_BASE, "\r\n SDRAM Write Start, Start Address 0x%x, Data Length %d !\r\n", sdram,
SDRAM_EXAMPLE_DATALEN);
/* Prepare data and write to SDRAM. */
for(index = 0; index < SDRAM_EXAMPLE_DATALEN; index++) {
*(uint32_t*)(sdram + index) = index;
}
LOG_DEBUG(MOD_ALL_BASE, "\r\n SDRAM Write finished!\r\n");

LOG_DEBUG_PF(MOD_ALL_BASE, "\r\n SDRAM Read/Check Start, Start Address 0x%x, Data Length %d !\r\n", sdram,
SDRAM_EXAMPLE_DATALEN);
/* Read data from the SDRAM. */
uint32_t* ptr = NULL;
uint32_t value = 0;
for(index = 0; index < SDRAM_EXAMPLE_DATALEN; index++) {
ptr = sdram + index;
value = *ptr;
if(value != index) {
LOG_DEBUG(MOD_ALL_BASE, "\r\n SDRAM Write Data and Read Data Check Error!\r\n");
break;
}
}

LOG_DEBUG(MOD_ALL_BASE, "\r\n SDRAM Write Data and Read Data Succeed.\r\n");
}

If someone could help me tune the EMC to work with my RAM chip I would greatly appreciate it.

Link to datasheet for SDRAM chip:
http://www.winbond.com/resource-files/w9816g6jh_a02.pdf 

Labels (2)
0 Kudos
1 Solution
1,081 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Matt,

Generally speaking, the "high-performance bus width" mode can generate low latency timing to access the SDRAM, but we have not detailed description to clarify the feature and highlight the difference between two modes. I am sorry.

After the modification, can you access the W9816 correctly?

BR

Xiangjun Rong

View solution in original post

0 Kudos
6 Replies
1,081 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Matt,

for the following code:

void BOARD_InitSDRAM(void)
{
    emc_basic_config_t basicConfig;
    emc_dynamic_timing_config_t dynTiming;
    emc_dynamic_chip_config_t dynChipConfig;

    /* Basic configuration. */
    basicConfig.endian = kEMC_LittleEndian;
    basicConfig.fbClkSrc = kEMC_IntloopbackEmcclk;
    /* EMC Clock = CPU FREQ/2 here can fit CPU freq from 12M ~ 180M.
     * If you change the divide to 0 and EMC clock is larger than 100M
     * please take refer to emc.dox to adjust EMC clock delay.
     */
    basicConfig.emcClkDiv = 1;
    /* Dynamic memory timing configuration. */
    dynTiming.readConfig = kEMC_Cmddelay;
    dynTiming.refreshPeriod_Nanosec = SDRAM_REFRESHPERIOD_NS;
    dynTiming.tRp_Ns = SDRAM_TRP_NS;

After you set the core clock to 180mhz, If you change the clock divider basicConfig.emcClkDiv = 3; in the case, the EMC_CLK will be 180MHz/(3+1)=45MHz, what is the result?

BR

Xiangjun Rong

0 Kudos
1,081 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I think the W9816G6JH-6 has different row size with the MT48LC8M16A2B4. So the parameter #define SDRAM_REFRESHPERIOD_NS is different

assume the the clock frequency is 100MHz.

for the MT48LC8M16A2B4, the SDRAM_REFRESHPERIOD_NS  is  (64 * 1000000 / 4096) /* 4096 rows/ 64ms */

for theW9816G6JH-6, the SDRAM_REFRESHPERIOD_NS  is  (64 * 1000000 / 2048) /* 2048 rows/ 64ms */

Pls have a try

BR

XiangJun Rong

0 Kudos
1,081 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

The W9816G6JH is 512K*2Bank*16 bits SDRAM, it is 1M*16 bits, based on the Table 656. Address mapping, so the parameter

#define SDRAM_DEV_MEMORYMAP (0x01u) /* 16Mbits (1M*16, 2banks, 11rows, 8 columns)*/

Pls have a try

BR

XiangJun Rong

pastedImage_1.png

0 Kudos
1,081 Views
guitardenver
Contributor IV

XiangJun Rong,

Thanks for the reply.

What is the difference between the "high-performance bus width" and just "16 bit bus width"?

0 Kudos
1,082 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Matt,

Generally speaking, the "high-performance bus width" mode can generate low latency timing to access the SDRAM, but we have not detailed description to clarify the feature and highlight the difference between two modes. I am sorry.

After the modification, can you access the W9816 correctly?

BR

Xiangjun Rong

0 Kudos
1,081 Views
guitardenver
Contributor IV

Yes, that was the fix. Although I needed to use the "16 bit bus", not the high performance one. Does not work on high performance. Too bad there is not any details of what the difference is.

Another parts of code was overwriting it to use the high performance. I have it like this now and it seems to pass all tests. Thank you for your help.

#define HW_SDRAM_DEV_MEMORYMAP (0x21u) // 16Mbits (1M*16, 2banks, 11 rows, 8 columns)
0 Kudos