本文简单描述如何使用Memory Test模板来测试RAM和Flash,此处以SDRAM和EPCS为例。
使用环境:Altera Quartus 9.1 SP1 + Nios II 9.1 Software Build Tools for Eclipse SP1
步骤1 在SOPC Builder中配置RAM和FLash
图1 例化epcs控制器和sdram控制器
注意观察epcs控制器和sdram控制器的起始和终止地址。此处epcs controller,例化为epcs,起始地址是0x01002090,终止地址是0x01002097;sdram controller,与epcs类似。当然也可查看之后生成的system.h。
步骤2 使用Nios II中的Memory Test模板来测试
1. 使用Memory Test模板创建软件工程
图2 创建软件工程
2. 编译程序及检查相关设置
编程成功后,打开system.h,查看与存储器相关的映射内容。
#define ALT_MODULE_CLASS_epcs altera_avalon_epcs_flash_controller
#define EPCS_BASE 0x1001800
#define EPCS_IRQ 1
#define EPCS_IRQ_INTERRUPT_CONTROLLER_ID 0
#define EPCS_NAME "/dev/epcs"
#define EPCS_REGISTER_OFFSET 512
#define EPCS_SPAN 2048
#define EPCS_TYPE "altera_avalon_epcs_flash_controller"
代码1 EPCS相关的映射
#define ALT_MODULE_CLASS_sdram altera_avalon_new_sdram_controller
#define SDRAM_BASE 0x800000
#define SDRAM_CAS_LATENCY 3
#define SDRAM_CONTENTS_INFO ""
#define SDRAM_INIT_NOP_DELAY 0.0
#define SDRAM_INIT_REFRESH_COMMANDS 2
#define SDRAM_IRQ -1
#define SDRAM_IRQ_INTERRUPT_CONTROLLER_ID -1
#define SDRAM_IS_INITIALIZED 1
#define SDRAM_NAME "/dev/sdram"
#define SDRAM_POWERUP_DELAY 100.0
#define SDRAM_REFRESH_PERIOD 15.625
#define SDRAM_REGISTER_DATA_IN 1
#define SDRAM_SDRAM_ADDR_WIDTH 22
#define SDRAM_SDRAM_BANK_WIDTH 2
#define SDRAM_SDRAM_COL_WIDTH 8
#define SDRAM_SDRAM_DATA_WIDTH 16
#define SDRAM_SDRAM_NUM_BANKS 4
#define SDRAM_SDRAM_NUM_CHIPSELECTS 1
#define SDRAM_SDRAM_ROW_WIDTH 12
#define SDRAM_SHARED_DATA 0
#define SDRAM_SIM_MODEL_BASE 1
#define SDRAM_SPAN 8388608
#define SDRAM_STARVATION_INDICATOR 0
#define SDRAM_TRISTATE_BRIDGE_SLAVE ""
#define SDRAM_TYPE "altera_avalon_new_sdram_controller"
#define SDRAM_T_AC 5.5
#define SDRAM_T_MRD 3
#define SDRAM_T_RCD 20.0
#define SDRAM_T_RFC 70.0
#define SDRAM_T_RP 20.0
#define SDRAM_T_WR 14.0
代码2 SDRAM相关的映射
3. 运行程序
运行程序后,在Nios II Console出现如下字样。
图3 Nios II Console 显示结果
此命令行界面,提示输入a,测试RAM;输入b,测试Flash;输入q,退出。
由于程序是在SDRAM中运行的,那么我们先测试EPCS。选择b,回车;接下来输入EPCS设备名,出现如下提示。
图4 提示测试EPCS Flash
命令行提示,EPCS设备已经打开;区域0有8个块等信息;并询问是否测试块3。此处选择y,开始测试。
图5 测试EPCS
在EPCS的块3,写Flash和读Flash操作被测试完毕。然后自动关闭EPCS设备。
回车后,选择a,接着测试SDRAM。
图6 提示输入测试SDRAM的起始地址
提示输入其实地址。从system.h可以看到,SDRAM从0x800000开始寻址;在SOPC Builder中,更可以清楚地了解到SDRAM的起始和终止地址。然而,程序是在SDRAM中运行的;如果在此处,直接输入SDRAM寻址的起始地址,有可能引起冲突,以致于程序无法继续运行下去。那我们先测试其他地址开始的内容,此处以0x900000为例。
图6 提示输入测试SDRAM的终止地址
输入大于测试SDRAM的起始地址的地址即可,此处以0xA00000为例。
图7 输入测试SDRAM的终止地址,显示测试结果
测试结果显示:从地址0x900000到地址0xA00000,数据总线测试通过;地址总线测试通过,字节和半字数据接入测试通过;单独位测试通过。
附录1 mem_test.c
此处,水平有限,不做解析,请读者自行分析。着重分析和学习其中的存储器操作方法。
/**************************************************************************
* Copyright (c) 2004 Altera Corporation, San Jose, California, USA. *
* All rights reserved. All use of this software and documentation is *
* subject to the License Agreement located at the end of this file below.*
*************************************************************************/
/**************************************************************************
*
*
* Description
***************
* This is a test program which tests RAM and flash memory.
*
*
* Requirements
****************
* This is a "Hosted" application. According to the ANSI C standard, hosted
* applications can rely on numerous system-services (including properly-
* initialized device drivers and, in this case, STDOUT).
*
* When this program is compiled, code is added before main(), so that all
* devices are properly-initialized and all system-services (e.g. the
* library) are ready-to-use. In this hosted environment, all standard C
* programs will run.
*
* A hosted application (like this example) does not need to concern itself
* with initializing devices. As long as it only calls C Standard Library
* functions, a hosted application can run "as if on a workstation."
*
* An application runs in a hosted environment if it declares the function
* main(), which this application does.
*
* This software example requires a STDOUT component such as a UART or
* JTAG UART, a CFI flash component, and 2 RAM components (one for running
* the program, and one for testing) Therefore it can run on the following
* hardware examples:
*
* Nios Development Board, Stratix II Edition:
* - Standard (DMA RAM test will not run)
* - Full Featured
*
* DSP Development Board, Stratix II Edition:
* - Standard (DMA RAM test will not run)
* - Full Featured
*
* Nios Development Board, Stratix Edition:
* - Standard (DMA RAM test will not run)
* - Full Featured
*
* Nios Development Board, Stratix Professional Edition:
* - Standard (DMA RAM test will not run)
* - Full Featured
*
* Nios Development Board, Cyclone Edition:
* - Standard (DMA RAM test will not run)
* - Full Featured
*
* Nios Development Board, Cyclone II Edition:
* - Standard (DMA RAM test will not run)
* - Full Featured
*
* Note: This example will not run on the Nios II Instruction Set Simulator
*
* Peripherals Exercised by SW
*******************************
* The example's purpose is to test RAM and flash, as well as demonstrate the
* use of the DMA controller and flash API in NiosII.
*
* The RAM test routine performs the following operations:
* 1.) Tests the address and data lines for shorts and opens.
* 2.) Tests byte and half-word access.
* 3.) Tests every bit in the memory to store both '1' and '0'.
* 4.) Tests DMA access to the memory.
*
* IMPORTANT: The RAM test is destructive to the contents of the RAM. For this
* reason, you MUST assure that none of the software sections are located in
* the RAM being tested. This requires that code, data, and exception
* locations must all be in a memory seperate from the one being tested.
* These locations can be adjusted in Nios II IDE and SOPC Builder.
*
*
* The flash tests demonstrate the use of the flash programming API. After the
* flash device specified is opened, the test routine searches for a block in
* the device that is already erased. This prevents any overwriting of
* important data that may be programmed in flash. When an erased block is
* found, the routine performs a test of the flash API calls on that block.
*
* The following API functions are then run to test the flash interface:
*
* - alt_get_flash_info
* This function queries the flash device and collects various information
* about it. In the example, the results of this query are compared to what
* is expected, and an error is reported in the event of a mismatch.
* - alt_write_flash
* This function writes a specified number of bytes to the flash device.
* In the example, this function is called repeatedly in a loop to write a
* lengthy amount of data.
* - alt_read_flash
* This function reads a specified number of bytes of data from the flash
* device. In the example, alt_read_flash is used to read back and test
* all of the writing routines.
* - alt_erase_flash_block
* This function performs a block erase on the flash device.
* - alt_write_flash_block
* This function writes an erase block of data to the flash device.
*
* During the test, status and error information is passed to the user via
* printf's.
*
* Software Files
******************
* memtest.c - Main C file that contains all memory testing code in this
* example.
*
**************************************************************************/
#include
#include
#include
#include
#include
#include "sys/alt_dma.h"
#include "system.h"
#include "sys/alt_flash.h"
#include "sys/alt_flash_dev.h"
/* Mode parameters for Flash Test */
#define TEST 1
#define SHOWMAP 2
#define CFI 3
#define EPCS 4
#define QUIT_WITHOUT_TESTING -1
/* One nice define for going to menu entry functions. */
#define MenuCase(letter,proc) case letter:proc; break;
/* Global DMA "transaction finished" flag */
#ifdef DMA_NAME
static volatile int rx_done = 0;
#endif /* DMA_NAME */
/******************************************************************
* Function: MenuHeader
*
* Purpose: Prints the menu header.
*
******************************************************************/
static void MenuHeader(void)
{
printf("\n\n");
printf(" <----> Nios II Memory Test. <---->\n");
printf("This software example tests the memory in your system to assure it\n");
printf("is working properly. This test is destructive to the contents of\n");
printf("the memory it tests. Assure the memory being tested does not contain\n");
printf("the executable or data sections of this code or the exception address\n");
printf("of the system.\n");
}
/******************************************************************
* Function: MenuBegin
*
* Purpose: Prints the top portion of the menu.
*
******************************************************************/
static void MenuBegin( char *title )
{
printf("\n\n");
printf("----------------------------------\n");
printf("%s\n",title);
printf("----------------------------------\n");
}
/******************************************************************
* Function: MenuItem
*
* Purpose: Prints selection items in the menu, enumerated by the
* specified letter.
*
******************************************************************/
static void MenuItem( char letter, char *name )
{
printf(" %c: %s\n" ,letter, name);
}
/******************************************************************
* Function: GetInputString
*
* Purpose: Parses an input string for the character '\n'. Then
* returns the string, minus any '\r' characters it
* encounters.
*
******************************************************************/
void GetInputString( char* entry, int size, FILE * stream )
{
int i;
int ch = 0;
for(i = 0; (ch != '\n') && (i < size); )
{
if( (ch = getc(stream)) != '\r')
{
entry[i] = ch;
i++;
}
}
}
/******************************************************************
* Function: MenuEnd
*
* Purpose: Prints the end of the menu, then captures and returns
* the user's selection.
*
******************************************************************/
static int MenuEnd( char lowLetter, char highLetter )
{
static char entry[4];
static char ch;
printf(" q: Exit\n");
printf("----------------------------------\n");
printf("\nSelect Choice (%c-%c): [Followed by ]",lowLetter,highLetter);
GetInputString( entry, sizeof(entry), stdin );
if(sscanf(entry, "%c\n", &ch))
{
if( ch >= 'A' && ch <= 'Z' )
ch += 'a' - 'A';
if( ch == 27 )
ch = 'q';
}
return ch;
}
/******************************************************************
* Function: MemGetAddressRange
*
* Purpose: Gathers a range of memory from the user.
*
******************************************************************/
static int MemGetAddressRange(int* base_address, int* end_address)
{
char line[12];
while(1)
{
/* Get the base address */
printf("Base address to start memory test: (i.e. 0x800000)\n");
printf(">");
GetInputString( line, sizeof(line), stdin );
/* Check the format to make sure it was entered as hex */
if(sscanf(line, "0x%X", base_address) != 1)
{
printf("%s\n", line);
printf(" -ERROR: Invalid base address entered. Address must be in the form '0x800000'\n\n");
continue;
}
/* Get the end address */
printf("End Address:\n");
printf(">");
GetInputString( line, sizeof(line), stdin );
/* Check the format to make sure it was entered as hex */
if(sscanf(line, "0x%X", end_address) != 1)
{
printf(" -ERROR: Invalid end address entered. Address must be in the form '0x8FFFFF'\n\n");
continue;
}
/* Make sure end address is greater than base address. */
if (end_address <= base_address)
{
printf(" -ERROR: End address must be greater than the start address\n\n");
continue;
}
break;
}
return(0);
}
/******************************************************************
* Function: MemTestDataBus
*
* Purpose: Tests that the data bus is connected with no
* stuck-at's, shorts, or open circuits.
*
******************************************************************/
static int MemTestDataBus(unsigned int address)
{
unsigned int pattern;
unsigned int ret_code = 0x0;
/* Perform a walking 1's test at the given address. */
for (pattern = 1; pattern != 0; pattern <<= 1)
{
/* Write the test pattern. */
IOWR_32DIRECT(address, 0, pattern);
/* Read it back (immediately is okay for this test). */
if (IORD_32DIRECT(address, 0) != pattern)
{
ret_code = pattern;
break;
}
}
return ret_code;
}
/******************************************************************
* Function: MemTestAddressBus
*
* Purpose: Tests that the address bus is connected with no
* stuck-at's, shorts, or open circuits.
*
******************************************************************/
static int MemTestAddressBus(unsigned int memory_base, unsigned int nBytes)
{
unsigned int address_mask = (nBytes - 1);
unsigned int offset;
unsigned int test_offset;
unsigned int pattern = 0xAAAAAAAA;
unsigned int antipattern = 0x55555555;
unsigned int ret_code = 0x0;
/* Write the default pattern at each of the power-of-two offsets. */
for (offset = sizeof(unsigned int); (offset & address_mask) != 0; offset <<= 1)
{
IOWR_32DIRECT(memory_base, offset, pattern);
}
/* Check for address bits stuck high. */
test_offset = 0;
IOWR_32DIRECT(memory_base, test_offset, antipattern);
for (offset = sizeof(unsigned int); (offset & address_mask) != 0; offset <<= 1)
{
if (IORD_32DIRECT(memory_base, offset) != pattern)
{
ret_code = (memory_base+offset);
break;
}
}
/* Check for address bits stuck low or shorted. */
IOWR_32DIRECT(memory_base, test_offset, pattern);
for (test_offset = sizeof(unsigned int); (test_offset & address_mask) != 0; test_offset <<= 1)
{
if (!ret_code)
{
IOWR_32DIRECT(memory_base, test_offset, antipattern);
for (offset = sizeof(unsigned int); (offset & address_mask) != 0; offset <<= 1)
{
if ((IORD_32DIRECT(memory_base, offset) != pattern) && (offset != test_offset))
{
ret_code = (memory_base + test_offset);
break;
}
}
IOWR_32DIRECT(memory_base, test_offset, pattern);
}
}
return ret_code;
}
/******************************************************************
* Function: MemTest8_16BitAccess
*
* Purpose: Tests that the memory at the specified base address
* can be read and written in both byte and half-word
* modes.
*
******************************************************************/
static int MemTest8_16BitAccess(unsigned int memory_base)
{
int ret_code = 0x0;
/* Write 4 bytes */
IOWR_8DIRECT(memory_base, 0, 0x0A);
IOWR_8DIRECT(memory_base, 1, 0x05);
IOWR_8DIRECT(memory_base, 2, 0xA0);
IOWR_8DIRECT(memory_base, 3, 0x50);
/* Read it back as one word */
if(IORD_32DIRECT(memory_base, 0) != 0x50A0050A)
{
ret_code = memory_base;
}
/* Read it back as two half-words */
if (!ret_code)
{
if ((IORD_16DIRECT(memory_base, 2) != 0x50A0) ||
(IORD_16DIRECT(memory_base, 0) != 0x050A))
{
ret_code = memory_base;
}
}
/* Read it back as 4 bytes */
if (!ret_code)
{
if ((IORD_8DIRECT(memory_base, 3) != 0x50) ||
(IORD_8DIRECT(memory_base, 2) != 0xA0) ||
(IORD_8DIRECT(memory_base, 1) != 0x05) ||
(IORD_8DIRECT(memory_base, 0) != 0x0A))
{
ret_code = memory_base;
}
}
/* Write 2 half-words */
if (!ret_code)
{
IOWR_16DIRECT(memory_base, 0, 0x50A0);
IOWR_16DIRECT(memory_base, 2, 0x050A);
/* Read it back as one word */
if(IORD_32DIRECT(memory_base, 0) != 0x050A50A0)
{
ret_code = memory_base;
}
}
/* Read it back as two half-words */
if (!ret_code)
{
if ((IORD_16DIRECT(memory_base, 2) != 0x050A) ||
(IORD_16DIRECT(memory_base, 0) != 0x50A0))
{
ret_code = memory_base;
}
}
/* Read it back as 4 bytes */
if (!ret_code)
{
if ((IORD_8DIRECT(memory_base, 3) != 0x05) ||
(IORD_8DIRECT(memory_base, 2) != 0x0A) ||
(IORD_8DIRECT(memory_base, 1) != 0x50) ||
(IORD_8DIRECT(memory_base, 0) != 0xA0))
{
ret_code = memory_base;
}
}
return(ret_code);
}
/******************************************************************
* Function: MemTestDevice
*
* Purpose: Tests that every bit in the memory device within the
* specified address range can store both a '1' and a '0'.
*
******************************************************************/
static int MemTestDevice(unsigned int memory_base, unsigned int nBytes)
{
unsigned int offset;
unsigned int pattern;
unsigned int antipattern;
unsigned int ret_code = 0x0;
/* Fill memory with a known pattern. */
for (pattern = 1, offset = 0; offset < nBytes; pattern++, offset+=4)
{
IOWR_32DIRECT(memory_base, offset, pattern);
}
printf(" .");
/* Check each location and invert it for the second pass. */
for (pattern = 1, offset = 0; offset < nBytes; pattern++, offset+=4)
{
if (IORD_32DIRECT(memory_base, offset) != pattern)
{
ret_code = (memory_base + offset);
break;
}
antipattern = ~pattern;
IOWR_32DIRECT(memory_base, offset, antipattern);
}
printf(" .");
/* Check each location for the inverted pattern and zero it. */
for (pattern = 1, offset = 0; offset < nBytes; pattern++, offset+=4)
{
antipattern = ~pattern;
if (IORD_32DIRECT(memory_base, offset) != antipattern)
{
ret_code = (memory_base + offset);
break;
}
IOWR_32DIRECT(memory_base, offset, 0x0);
}
return ret_code;
}
/******************************************************************
* Function: dma_done
*
* Purpose: Called when a DMA recieve transaction is complete.
* Increments rx_done to signal to the main program that
* the transaction is done.
*
******************************************************************/
#ifdef DMA_NAME
static void dma_done (void* handle, void* data)
{
rx_done++;
}
#endif /* DMA_NAME */
/******************************************************************
* Function: MemDMATest
*
* Purpose: Tests every bit in the memory device within the
* specified address range using DMA. The DMA controller provides
* a more rigourous test of the memory since it performs back-to-
* back memory accesses at full system speed.
*
******************************************************************/
#ifdef DMA_NAME
static int MemDMATest(unsigned int memory_base, unsigned int nBytes)
{
int rc;
int ret_code = 0;
int pattern, offset;
alt_dma_txchan txchan;
alt_dma_rxchan rxchan;
void* data_written;
void* data_read;
/* Get a couple buffers for the test */
data_written = (void*)alt_uncached_malloc(0x1000);
data_read = (void*)alt_uncached_malloc(0x1000);
/* Fill write buffer with known values */
for (pattern = 1, offset = 0; offset < 0x1000; pattern++, offset+=4)
{
IOWR_32DIRECT((int)data_written, offset, pattern);
}
/* Create the transmit channel */
if ((txchan = alt_dma_txchan_open("/dev/dma")) == NULL)
{
printf ("Failed to open transmit channel\n");
exit (1);
}
/* Create the receive channel */
if ((rxchan = alt_dma_rxchan_open("/dev/dma")) == NULL)
{
printf ("Failed to open receive channel\n");
exit (1);
}
for(offset = memory_base; offset < (memory_base + nBytes); offset += 0x1000)
{
/* Use DMA to transfer from write buffer to memory under test */
/* Post the transmit request */
if ((rc = alt_dma_txchan_send (txchan, data_written, 0x1000, NULL, NULL)) < 0)
{
printf ("Failed to post transmit request, reason = %i\n", rc);
exit (1);
}
/* Post the receive request */
if ((rc = alt_dma_rxchan_prepare (rxchan, (void*)offset, 0x1000, dma_done, NULL)) < 0)
{
printf ("Failed to post read request, reason = %i\n", rc);
exit (1);
}
/* Wait for transfer to complete */
while (!rx_done);
rx_done = 0;
/* Clear the read buffer before we fill it */
memset(data_read, 0, 0x1000);
/* Use DMA to read data back into read buffer from memory under test */
/* Post the transmit request */
if ((rc = alt_dma_txchan_send (txchan, (void*)offset, 0x1000, NULL, NULL)) < 0)
{
printf ("Failed to post transmit request, reason = %i\n", rc);
exit (1);
}
/* Post the receive request */
if ((rc = alt_dma_rxchan_prepare (rxchan, data_read, 0x1000, dma_done, NULL)) < 0)
{
printf ("Failed to post read request, reason = %i\n", rc);
exit (1);
}
/* Wait for transfer to complete */
while (!rx_done);
rx_done = 0;
if (memcmp(data_written, data_read, 0x1000))
{
ret_code = offset;
break;
}
}
alt_uncached_free(data_written);
alt_uncached_free(data_read);
return ret_code;
}
#endif /* DMA_NAME */
/******************************************************************
* Function: TestRam
*
* Purpose: Performs a full-test on the RAM specified. The tests
* run are:
* - MemTestDataBus
* - MemTestAddressBus
* - MemTest8_16BitAccess
* - MemTestDevice
* - MemDMATest
*
******************************************************************/
static void TestRam(void)
{
int memory_base, memory_end, memory_size;
int ret_code = 0x0;
/* Find out what range of memory we are testing */
MemGetAddressRange(&memory_base, &memory_end);
memory_size = (memory_end - memory_base);
printf("\n");
printf("Testing RAM from 0x%X to 0x%X\n", memory_base, (memory_base + memory_size));
/* Test Data Bus. */
ret_code = MemTestDataBus(memory_base);
if (ret_code)
printf(" -Data bus test failed at bit 0x%X", (int)ret_code);
else
printf(" -Data bus test passed\n");
/* Test Address Bus. */
if (!ret_code)
{
ret_code = MemTestAddressBus(memory_base, memory_size);
if (ret_code)
printf(" -Address bus test failed at address 0x%X", (int)ret_code);
else
printf(" -Address bus test passed\n");
}
/* Test byte and half-word access. */
if (!ret_code)
{
ret_code = MemTest8_16BitAccess(memory_base);
if (ret_code)
printf(" -Byte and half-word access test failed at address 0x%X", (int)ret_code);
else
printf(" -Byte and half-word access test passed\n");
}
/* Test that each bit in the device can store both 1 and 0. */
if (!ret_code)
{
printf(" -Testing each bit in memory device.");
ret_code = MemTestDevice(memory_base, memory_size);
if (ret_code)
printf(" failed at address 0x%X", (int)ret_code);
else
printf(" passed\n");
}
/* Test DMA access to the RAM if DMA exists */
#ifdef DMA_NAME
if (!ret_code)
{
printf(" -Testing memory using DMA.");
ret_code = MemDMATest(memory_base, memory_size);
if (ret_code)
printf(" failed at address 0x%X", (int)ret_code);
else
printf(" passed\n");
}
#endif /* DMA_NAME */
if (!ret_code)
printf("Memory at 0x%X Okay\n", memory_base);
}
/******************************************************************
* Function: FlashCheckIfBlockErased
*
* Purpose: Checks the specified flash block to see if it is
* completely erased (all 0xFFFFFFFF).
*
******************************************************************/
static int FlashCheckIfBlockErased(alt_flash_fd* fd, int block, flash_region* regions)
{
int i, j;
int ret_code = 0x0;
char block_is_erased = 0x1;
alt_u8 *data_read;
/* Get a buffer */
data_read = malloc(64);
/* Initialize the flag */
block_is_erased = 0x1;
for(i = 0; i < regions->block_size; i += 64)
{
ret_code = alt_read_flash(fd, ((block * regions->block_size) + i), data_read, 64);
for(j=0; j < 64; j+=1)
{
if(*(data_read+j) != 0xFF)
{
/* If this byte isn't erased, then neither is the block */
block_is_erased = 0x0;
break;
}
}
if (block_is_erased == 0x0)
break;
}
/* Block is erased if we indexed through all block locations */
if(i == regions->block_size)
ret_code = 1;
else
ret_code = 0;
free(data_read);
return ret_code;
}
/******************************************************************
* Function: FlashTestBlockWrite
*
* Purpose: Tests that the function alt_write_flash_block is
* is working properly.
*
******************************************************************/
static int FlashTestBlockWrite(int block, int *error, alt_flash_fd* fd, flash_region* regions)
{
int i;
int ret_code = 0x0;
int test_offset;
alt_u8 *data_written;
alt_u8 *data_read;
/* Get a couple buffers for the test */
data_written = malloc(100);
data_read = malloc(100);
test_offset = (regions->offset + (block * regions->block_size));
/* Fill write buffer with 100 values (incremented by 3) */
for(i=0; i < 100; i++)
*(data_written + i) = (i * 3);
/* Write the buffer to flash starting 0x40 bytes from the beginning of the block. */
printf(" -Testing \"alt_write_flash_block\".");
ret_code = alt_write_flash_block(fd, test_offset, (test_offset + 0x40), data_written, 100);
if (!ret_code)
{
/* Now read it back into the read_buffer */
ret_code = alt_read_flash(fd, (test_offset + 0x40), data_read, 100);
if(!ret_code)
{
/* See if they match */
if (memcmp(data_written, data_read, 100))
{
printf(" FAILED.\n");
*error++;
}
else
printf(" passed.\n");
}
}
/* Test unaligned writes */
if(!ret_code)
{
/* Erase the block */
ret_code = alt_erase_flash_block(fd, test_offset, regions->block_size);
/* Write the buffer to flash on an unaligned address. */
printf(" -Testing unaligned writes.");
ret_code = alt_write_flash_block(fd, test_offset, (test_offset + 0x43), data_written, 100);
if (!ret_code)
{
/* Now read it back into the read_buffer */
ret_code = alt_read_flash(fd, (test_offset + 0x43), data_read, 100);
if(!ret_code)
{
/* See if they match */
if (memcmp(data_written, data_read, 100))
{
printf(" FAILED.\n");
*error++;
}
else
printf(" passed.\n");
}
}
}
/* Free up the buffers we allocated. */
free(data_written);
free(data_read);
return ret_code;
}
/******************************************************************
* Function: FlashTestReadWrite
*
* Purpose: Tests that the functions alt_write_flash and
* alt_read_flash are working properly, as well as tests
* that every bit in the specified block can store both
* a '1' and '0'.
*
******************************************************************/
static int FlashTestReadWrite(int block, int *error, alt_flash_fd* fd, flash_region* regions)
{
int i;
int ret_code = 0x0;
int test_offset;
alt_u8 *data_written;
alt_u8 *data_read;
/* Get a couple buffers for the tests */
data_written = malloc(regions->block_size);
data_read = malloc(regions->block_size);
/* Calculate the offset at which the block lives */
test_offset = (regions->offset + (block * regions->block_size));
printf("\n -Starting Flash Test.\n");
printf(" -Testing \"alt_write_flash\" and \"alt_read_flash\".\n");
/* Fill buffer with incrementing values */
for(i=0; i < regions->block_size; i++)
*(data_written + i) = i;
/* Write the buffer to flash block */
ret_code = alt_write_flash(fd, test_offset, data_written, regions->block_size);
if (!ret_code)
{
/* Read flash block into read buffer */
ret_code = alt_read_flash(fd, test_offset, data_read, regions->block_size);
if(!ret_code)
{
/* See if they match */
if (memcmp(data_written, data_read, regions->block_size))
{
printf(" pass 1 - FAILED.\n");
*error++;
}
else
printf(" pass 1 - passed.\n");
}
/* Now fill the buffer with decrementing values (invert the incrementing ones) */
for(i=0; i < regions->block_size; i++)
*(data_written + i) = ~((alt_u8)(i));
/* Write the buffer to flash block */
ret_code = alt_write_flash(fd, test_offset, data_written, regions->block_size);
if (!ret_code)
{
/* Read flash block into read buffer */
ret_code = alt_read_flash(fd, test_offset, data_read, regions->block_size);
if(!ret_code)
{
/* See if they match */
if (memcmp(data_written, data_read, regions->block_size))
{
printf(" pass 2 - FAILED.\n");
*error++;
}
else
printf(" pass 2 - passed.\n");
}
}
if (*error)
ret_code = 1;
}
/* Free up the buffers we allocated */
free(data_written);
free(data_read);
return ret_code;
}
/******************************************************************
* Function: FlashTestBlockErase
*
* Purpose: Tests that the function alt_erase_flash_block is
* is working properly. Assumes that the specified
* flash block contains some non-0xFFFFFFFF data before
* this function is called.
*
******************************************************************/
static int FlashTestBlockErase(int block, int *error, alt_flash_fd* fd, flash_region* regions)
{
int ret_code = 0x0;
int test_offset;
/* Calculate the offset of the block */
test_offset = (regions->offset + (block * regions->block_size));
printf(" -Testing \"alt_erase_flash_block\".");
ret_code = alt_erase_flash_block(fd, test_offset, regions->block_size);
/* Check that the erase was successful. */
if (!ret_code)
{
if(FlashCheckIfBlockErased(fd, block, regions))
printf(" passed.\n");
else
{
printf(" FAILED\n");
*error++;
}
}
return ret_code;
}
/******************************************************************
* Function: FlashRunTests
*
* Purpose: Performs a full-test on the Flash specified. The tests
* run are:
* - alt_write_flash
* - alt_read_flash
* - alt_erase_flash_block
* - alt_write_flash_block
*
******************************************************************/
static void FlashRunTests(alt_flash_fd* fd, int block, flash_region* regions)
{
int ret_code = 0x0;
int error = 0x0;
int test_offset;
/* Calculate the offset of the block */
test_offset = (regions->offset + (block * regions->block_size));
/* Test reading and writing functions */
ret_code = FlashTestReadWrite(block, &error, fd, regions);
/* Test the erase function */
if (!ret_code)
{
ret_code = FlashTestBlockErase(block, &error, fd, regions);
}
/* Test the block write function */
if (!ret_code)
{
ret_code = FlashTestBlockWrite(block, &error, fd, regions);
}
/* Erase the block so we dont fill one up each time we run the test */
printf(" -Returning block %d to its erased state.\n", block);
alt_erase_flash_block(fd, test_offset, regions->block_size);
printf(" -Flash tests complete.\n");
if(ret_code || error)
{
printf(" -At least one test failed.\n\n");
}
}
/******************************************************************
* Function: GetFlashName
*
* Purpose: Gets the name of the flash to test from the user
* Defaults to "/dev/ext_flash", the name of the flash
* component in the Nios II example designs.
*
******************************************************************/
static int GetFlashName(char line[30], int flash_type)
{
char ch = 0x0;
int i;
if (flash_type == CFI)
{
printf("\nEnter the name of the CFI flash device to be opened,\n");
printf("or just press to open \"/dev/ext_flash\"\n");
printf(">");
}
else if (flash_type == EPCS)
{
printf("\nEnter the name of the EPCS flash device to be opened,\n");
printf("or just press to open \"/dev/epcs_controller\"\n");
printf(">");
}
for(i = 0; ch != '\n'; i++)
{
ch = getc(stdin);
if(ch == '\r' || ch == '\n')
{
/* Hitting defaults to the standard component name */
if( i <= 1 )
{
if (flash_type == CFI)
strcpy(line, "/dev/ext_flash\0");
else if (flash_type == EPCS)
strcpy(line, "/dev/epcs_controller\0");
}
else
/* Properly terminate the string. */
line[i] = '\0';
}
else
line[i] = ch;
}
return 0;
}
/******************************************************************
* Function: FlashErase
*
* Purpose: Erases 1 or all blocks in the specified flash device.
*
******************************************************************/
static void FlashErase(int flash_type)
{
alt_flash_fd* fd;
int test_offset;
int ret_code;
flash_region* regions;
int number_of_regions;
alt_u8 entry[4];
alt_u8 flashname[30];
unsigned int block;
/* Get the name of the flash we are erasing */
ret_code = GetFlashName(flashname, flash_type);
fd = alt_flash_open_dev(flashname);
if (fd)
{
/* Find out some useful stuff about the flash */
ret_code = alt_get_flash_info(fd, ®ions, &number_of_regions);
if (!ret_code)
{
printf(" -Region has %d blocks.\n", regions->number_of_blocks);
printf(" -Which block would you like to erase?\n");
printf(" -> ");
GetInputString( entry, sizeof(entry), stdin );
if(entry[0] == 'a')
{
printf(" -Erase ALL blocks? (y/n) ");
GetInputString( entry, sizeof(entry), stdin );
if(entry[0] == 'y')
{
/* Erase all blocks */
printf(" -Erasing %d blocks. Please Wait.\n", (regions->number_of_blocks));
for(block = 0; block < regions->number_of_blocks; block++)
{
/* Dont erase it if it's already erased silly. */
if ((FlashCheckIfBlockErased(fd, block, regions)) == 0)
{
test_offset = (regions->offset + (block * regions->block_size));
alt_erase_flash_block(fd, test_offset, regions->block_size);
}
/* Just a simple progress meter so we dont get bored waiting for the flash to erase. */
printf(".");
if(((block + 1) % 80) == 0)
{
printf("\n");
}
}
printf("\n -All Blocks Erased.\n");
}
else
{
printf("Erased zero blocks.\n");
}
}
/* Just erase one block */
if(sscanf(entry, "%d\n", &block))
{
if ((block >= 0) && (block <= (regions->number_of_blocks - 1)))
{
test_offset = (regions->offset + (block * regions->block_size));
alt_erase_flash_block(fd, test_offset, regions->block_size);
printf(" -Block %d erased.\n", block);
}
else
{
printf(" -Block number entered is %d\n", block);
printf(" -Block number must be between 0 and %d.\n", (regions->number_of_blocks - 1));
}
}
}
printf(" -Closing flash \"%s\".\n", flashname);
alt_flash_close_dev(fd);
}
}
/******************************************************************
* Function: FlashFindErasedBlocks
*
* Purpose: Looks through the specified flash for blocks which
* are completely erased. If the mode parameter is
* TEST, this function simply returns the index of the
* first block which is completely erased. If the mode
* parameter is SHOWMAP, the function prints a list of
* all blocks, indicating which ones are erased.
*
******************************************************************/
static int FlashFindErasedBlocks(alt_flash_fd* fd, flash_region* regions, int number_of_regions, int mode)
{
int region_index, block_index;
int block_erased = 0x0;
alt_u8 entry[5];
unsigned int block;
/* Currently only supports flashes with 1 region, but region loop is left here for possible */
/* future implementation */
for(region_index = 0; region_index < number_of_regions; region_index++)
{
printf(" -Checking Region %d for erased blocks.\n", region_index);
/* SHOWMAP mode has a legend reminding us what little plus and minus signs mean */
if(mode == SHOWMAP)
{
printf(" erased block = '-'\n");
printf(" unerased block = '+'\n\n");
}
/* Check those blocks. */
for(block_index = 0; block_index < (regions->number_of_blocks); block_index++)
{
block_erased = FlashCheckIfBlockErased(fd, block_index, regions);
/* If it's erased and were running in TEST mode, we're done */
if(block_erased && (mode == TEST))
break;
/* If in SHOWMAP mode, mark block as either erased or not-erased. */
else if(block_erased && (mode == SHOWMAP))
printf(" Block %3d @ 0x%8.8X:\t-\n", block_index, (regions->offset + (block_index * regions->block_size)));
else if(!block_erased && (mode == SHOWMAP))
printf(" Block %3d @ 0x%8.8X:\t+\n", block_index, (regions->offset + (block_index * regions->block_size)));
}
/* Special case if no blocks are erased (TEST mode only)*/
if(( block_index == ( regions->number_of_blocks )) && ( mode == TEST ))
{
printf(" -Found no erased blocks. Please enter the number of the block\n");
printf(" you would like to test. Enter 'q' to quit without testing flash.\n");
printf(" -> ");
GetInputString( entry, sizeof(entry), stdin );
if(entry[0] == 'q')
{
block_index = QUIT_WITHOUT_TESTING;
break;
}
else if(sscanf(entry, "%d\n", &block))
{
if ((block >= 0) && (block <= (regions->number_of_blocks - 1)))
{
block_index = block;
break;
}
else
{
printf(" -Block number entered is %d\n", block);
printf(" -Block number must be between 0 and %d.\n", (regions->number_of_blocks - 1));
}
}
}
/* Break out of the region loop if we've found an erased block to test. */
if(block_erased && (mode == TEST))
break;
}
return block_index;
}
/******************************************************************
* Function: TestFlash
*
* Purpose: Opens the specified flash device. If the mode
* parameter is TEST, the function finds an erased
* block, then tests it. If the mode parameter is
* SHOWMAP, the function lists all blocks in the flash and
* indicates which ones are erased. The flash is closed
* at the end of the function.
*
******************************************************************/
static void TestFlash(int mode, int flash_type)
{
alt_flash_fd* fd;
int number_of_regions;
int block;
flash_region* regions;
int ret_code = 0x0;
alt_u8 entry[4];
alt_u8 flashname[30];
ret_code = GetFlashName(flashname, flash_type);
fd = alt_flash_open_dev(flashname);
if (fd)
{
printf(" -Successfully opened %s\n", flashname);
/* Get some useful info about the flash */
ret_code = alt_get_flash_info(fd, ®ions, &number_of_regions);
if (!ret_code)
{
printf(" -Region 0 contains %d blocks.\n", regions->number_of_blocks);
block = FlashFindErasedBlocks(fd, regions, number_of_regions, mode);
/* If we're in TEST mode, ask if this block is okay to test. */
if(( mode == TEST ) && ( block != QUIT_WITHOUT_TESTING ))
{
printf(" -Block %d, at address 0x%X identified.\n", block, (regions->offset + (block * regions->block_size)));
printf(" -Would you like to test this block? (y/n)");
GetInputString(entry, sizeof(entry), stdin);
if ( entry[0] == 'y' && entry[1] == '\n' )
{
/* Test that Flash! */
FlashRunTests(fd, block, regions);
printf(" -Closing flash device \"%s\".\n", flashname);
alt_flash_close_dev(fd);
}
}
}
}
else
{
printf(" -ERROR: Could not open %s\n", flashname);
}
}
/******************************************************************
* Function: TopMenu
*
* Purpose: Generates the top level menu.
*
******************************************************************/
static int TopMenu( void )
{
char ch;
/* Print the top-level menu to stdout */
while (1)
{
MenuBegin(" Memory Test Main Menu");
MenuItem( 'a', "Test RAM" );
MenuItem( 'b', "Test Flash");
#ifdef EPCS_CONTROLLER_NAME
MenuItem( 'c', "Test EPCS Serial Flash");
ch = MenuEnd( 'a', 'c' );
#else
ch = MenuEnd( 'a', 'b' );
#endif /* EPCS_CONTROLLER_NAME */
switch(ch)
{
MenuCase('a',TestRam());
MenuCase('b',TestFlash(TEST, CFI));
MenuCase('e',FlashErase(CFI)); /* hidden option */
MenuCase('m',TestFlash(SHOWMAP, CFI)); /* hidden option */
#ifdef EPCS_CONTROLLER_NAME
MenuCase('c',TestFlash(TEST, EPCS));
MenuCase('f',FlashErase(EPCS)); /* hidden option */
MenuCase('s',TestFlash(SHOWMAP, EPCS)); /* hidden option */
#endif /* EPCS_CONTROLLER_NAME */
case 'q': break;
default: printf("\n -ERROR: %c is an invalid entry. Please try again\n", ch); break;
}
if (ch == 'q')
break;
printf("\nPress enter to continue...\n");
while( (( ch = getc(stdin)) != '\n' ) && ( ch != EOF ));
}
return (ch);
}
/******************************************************************
* Function: main
*
* Purpose: Continually prints the menu and performs the actions
* requested by the user.
*
******************************************************************/
int main(void)
{
int ch;
/* Print the Header */
MenuHeader();
/* Print the menu and do what the user requests, until they hit 'q' */
while (1)
{
ch = TopMenu();
if (ch == 'q')
{
printf( "\nExiting from Memory Test.\n");
break;
}
}
return (0);
}
/******************************************************************************
* *
* License Agreement *
* *
* Copyright (c) 2004 Altera Corporation, San Jose, California, USA. *
* All rights reserved. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
* *
* This agreement shall be governed in all respects by the laws of the State *
* of California and by the laws of the United States of America. *
* Altera does not recommend, suggest or require that this reference design *
* file be used in conjunction or combination with any other product. *
******************************************************************************/
参考
1. Altera.Nios II Software Developer's Handbook