通过Zynq FPGA对雷龙SD NAND进行测试

目录

引言:

[一、SD NAND特征](#一、SD NAND特征)

[1.1 SD卡简介](#1.1 SD卡简介)

[1.2 SD卡Block图](#1.2 SD卡Block图)

二、SD卡样片

三、Zynq测试平台搭建

[3.1 测试流程](#3.1 测试流程)

[3.2 SOC搭建](#3.2 SOC搭建)

四、软件搭建

五、测试结果

六、总结


引言:

在嵌入式系统开发中,SD NAND因其高可靠性、低功耗和小封装等优势,被广泛应用于数据存储和传输场景。本文详细介绍了基于Zynq-7020 FPGA平台对CSNP4GCR01-AMW和CSNP32GCR01-AOW两种型号SD NAND芯片的读写性能测试过程及结果。测试包括SD卡的初始化、FATFS文件系统的搭建、数据读写速度测量及一致性验证,并分析了不同容量SD NAND的性能差异。本测试使用LGA-8封装芯片,并通过转接板兼容传统SD卡接口,为无卡槽嵌入式设计提供了灵活选择。测试结果表明,这些芯片在嵌入式存储领域具有较高的应用价值,同时为后续项目提供了可靠的数据支持和性能参考。

SD NAND-雷龙发展有限公司http://www.longsto.com/product/31.html

一、SD NAND特征

1.1 SD卡简介

雷龙的SD NAND有很多型号,在测试中使用的是CSNP4GCR01-AMW与CSNP32GCR01-AOW。芯片是基于NAND FLASH和 SD控制器实现的SD卡。具有强大的坏块管理和纠错功能,并且在意外掉电的情况下同样能保证数据的安全。

其特点如下:

接口支持SD2.0 2线或4线;

电压支持:2.7V-3.6V;

默认模式:可变时钟速率0 - 25MHz,高达12.5 MB/s的接口速度(使用4条并行数据线)

高速模式:可变时钟速率0 - 50MHz,高达25 MB/s的接口速度(使用4条并行数据线)

工作温度:-40°C ~ +85°C

存储温度:-55°C ~ +125°C

待机电流小于250uA

修正内存字段错误;

内容保护机制------符合SDMI最高安全标准

SDNAND密码保护(CMD42 - LOCK_UNLOCK)

采用机械开关的写保护功能

内置写保护功能(永久和临时)

应用程序特定命令

舒适擦除机制

该SD卡支持SDIO读写和SPI读写,最高读写速度可达25MB/s,实际读写速度要结合MCU和接口情况实测获得。通常在简单嵌入式系统并对读写速度要求不高的情况下,会使用SPI协议进行读写。但不管使用SDIO还是SPI都需要符合相关的协议规范,才能建立相应的文件系统;

1.2 SD卡Block图

该SD卡封装为LGA-8;引脚分配与定义如下;在这里插入图片描述:

二、SD卡样片

与样片同时寄来的还有转接板,转接板将LGA-8封装的芯片转接至SD卡封装,这样只需将转接板插入SD卡槽即可使用。

在这里插入图片描述:

三、Zynq测试平台搭建

  • 测试平台为 Xilinx 的Zynq 7020 FPGA芯片;

  • 板卡:Digilent Zybo Z7

  • Vivado版本:2018.3

  • 文件系统:FATFS

  • SD卡接口:SD2.0

3.1 测试流程

本次测试主要针对4G和32G两个不同容量的SD卡,在Zynq FPGA上搭建SD卡读写回路,从而对SD卡读写速度进行测试,并检验读写一致性;

测试流程:

进入测试程序前,首先会对SD卡初始化并初始化建立FATFS文件系统,随后进入测试SD卡测试程序,在测试程序中,会写入一定大小的文件,然后对写入文件的时间进行测量,得到写入时间;然后再将写入的文件读出,测量获得读出时间,并将读出数据与写入数据相比较,检测是否读写出错。

通过写入时间、读出时间可计算得到写入速度、读出速度;将以上过程重复100次并打印报告。

3.2 SOC搭建

硬件搭建框图如下,我们在本次系统中使用PS端的SDIO接口来驱动SD NAND芯片,并通过UART向PC打印报告;

PL端的硬件搭建也很简单,只需一个Timer定时器来做时间测量;

我们直接使用Zybo板卡文件创建一个工程,工程会将Zybo具有的硬件资源配置好;

首先点击setting->IP->Repository->+;添加Timer IP核的路径,Timer IP核会在工程中给出;

点击Create Block Design创建BD工程

在创建的过程中添加 Zynq 内核;

由于我们使用了板卡文件,所以内核IP是配置好的,我们只需稍作修改即可,如果是其他板卡,则需要自行配置DDR等配置;

双击内核IP,点击Clock Configuration->PL Fabric Clocks,将FCLK_CLK0的时钟频率修改为100Mhz

添加TimerA IP;

依次点击上方的自动设计,完成SOC搭建;

点击BD设计,并创建顶层文件

生成比特流文件;

在生成比特流文件后,将其导入SDK;

点击Export->Export Hardware,导出硬件;然后点击Launch SDK打开SDK进行软件设计;

四、软件搭建

在SDK中新建一个空白工程;

点击file -> new -> Application project;

在新建的过程中创建一个main.c文件,并在里面编写测试程序如下:

在每次读写开始前,通过TimerA0_start()函数开始计时,在读写结束后可以通过TimerA0_stop()结束计时,从而测得消耗时间。

相应的Timer驱动函数在user/TimerA_user.c中定义;

cpp 复制代码
#include "xparameters.h" /* SDK 生成的参数 */

#include "xsdps.h" /* SD 设备驱动程序 */

#include "xil_printf.h"

#include "ff.h"

#include "xil_cache.h"

#include "xplatform_info.h"

#include "time.h"

#include "../user/headfile.h 的



#define    PACK_LEN       32764



static FIL fil;        /* File object */

static FATFS fatfs;



static char FileName[32] = "Test.txt";

static char *SD_File;



char DestinationAddress[PACK_LEN] ;



char txt[1024];

char test_buffer[PACK_LEN];



void TimerA0_init()

{

    TimerA_reset(TimerA0);//reset timerA device

    TimerA_Set_Clock_Division(TimerA0,100);//divide clock as 100000000/100 = 1Mhz

    TimerA_Stop_Counter(TimerA0);//stop timerA

}



void TimerA0_start()

{

    TimerA_SetAs_CONTINUS_Mode(TimerA0);

}



void TimerA0_stop()

{

    TimerA_Stop_Counter(TimerA0);

}









uint32 SDCard_test()

{

    uint8 Res;

    uint32 NumBytesRead;

    uint32 NumBytesWritten;

    uint32 BuffCnt;

    uint8 work[FF_MAX_SS];

    uint32 take_time=0;

    uint32 speed = 0;

    uint32 test_time = 0;

    uint32 w_t=0;

    uint32 r_t=0;

    float wsum = 0;

    float rsum = 0;





    TCHAR *Path = "0:/";



    for(int i=0;i<PACK_LEN;i++)

    {

        test_buffer[i] = 'a';

    }



    Res = f_mount(&fatfs, Path, 0);



    if (Res != FR_OK) {

        return XST_FAILURE;

    }



    Res = f_mkfs(Path, FM_FAT32, 0, work, sizeof work);

    if (Res != FR_OK) {

        return XST_FAILURE;

    }



    SD_File = (char *)FileName;



    Res = f_open(&fil, SD_File, FA_CREATE_ALWAYS | FA_WRITE | FA_READ);

    if (Res) {

        return XST_FAILURE;

    }



    Res = f_lseek(&fil, 0);

    if (Res) {

        return XST_FAILURE;

    }



    while(1)

    {

        TimerA_reset(TimerA0);

        TimerA0_start();

        Res = f_write(&fil, (const void*)test_buffer, PACK_LEN,

                &NumBytesWritten);

        TimerA0_stop();

        take_time = TimerA_Read_Counter_Register(TimerA0);

        w_t+=take_time;

        xil_printf("--------------------------------\n");

        xil_printf("take time:%d us\n",take_time);

        speed = PACK_LEN*(1000000/((float)(take_time)));

        sprintf(txt,"write speed:%.2f MB/s\n",(float)(speed)/1024/1024);

        wsum = wsum+speed;

        xil_printf(txt);

        xil_printf("--------------------------------\n");

        if (Res) {

            return XST_FAILURE;

        }



        Res = f_lseek(&fil, 0);

        if (Res) {

            return XST_FAILURE;

        }



        TimerA_reset(TimerA0);

        TimerA0_start();

        Res = f_read(&fil, (void*)DestinationAddress, PACK_LEN,

                &NumBytesRead);

        TimerA0_stop();

        take_time = TimerA_Read_Counter_Register(TimerA0);

        r_t+=take_time;

        xil_printf("--------------------------------\n");

        xil_printf("take time:%d us\n",take_time);

        speed = PACK_LEN*(1000000/((float)(take_time)));

        sprintf(txt,"read speed:%.2f MB/s\n",(float)(speed)/1024/1024);

        rsum = rsum+speed;

xil_printf(txt);

xil_printf("--------------------------------\n");

if (Res) {

返回 XST_FAILURE;

        }





for(BuffCnt = 0;BuffCnt < PACK_LEN;BuffCnt++){

if(test_buffer[BuffCnt] != DestinationAddress[BuffCnt]){

xil_printf("%dno",BuffCnt);

返回 XST_FAILURE;

            }

        }

xil_printf("测试编号:%d 数据检查正确!\n",test_time+1);

test_time++;

如果(test_time==100)

        {

sprintf(txt,"总写入量:%.2f KB,花费时间:%.2f ms,写入速度:%.2f MB/s\n",PACK_LEN*100/1024.0,w_t/100.0/1000.0,wsum/100/1024/1024);

xil_printf(txt);

sprintf(txt,"总读取量:%.2f KB,花费时间:%.2f ms,读取速度:%.2f MB/s\n",PACK_LEN*100/1024.0,r_t/100.0/1000.0,rsum/100/1024/1024);

xil_printf(txt);

分辨率 = f_close(&fil);

if (Res) {

返回 XST_FAILURE;

            }

返回 0;

        }

    }



}



int main(void) (无效)

{

TimerA0_init();



SDCard_test();

xil_printf("完成");

返回 0;

}

五、测试结果

经测试,两种型号的芯片读写速度如下图表所示。

其SD NAND的读写速度随着读写数据量的增加而增加,并且读速率大于写速率,这符合SD卡的特性;

对比两种型号SD NAND芯片,发现CSNP32GCR01-AOW型号具有更高的读写速度;

六、总结

本来打算拿这些样片去试试信息安全领域是否有所应用,但发现其似乎内置了复位或初始化,导致无法提取上电时的不确定值,故无法提取该SD NAND的物理不可克隆特性,所以这方面的测试无法进行;

对于芯片正常读写的测试结果,还是很让人满意的,芯片的价格也很合理。并且LGA-8封装更适合无卡槽的嵌入式开发板设计,在一定的应用领域有着简化硬件设计、减小硬件面积的功能。

相关推荐
JaneZJW17 小时前
Linux C编程:文件IO(概念、打开、读、写、关闭)
linux·c语言·stm32·单片机·嵌入式
Wanliang Li1 天前
Linux电源管理——Device Power Management Interface
linux·嵌入式·virtio·电源管理·suspend
迎风打盹儿4 天前
VIVADO FIFO (同步和异步) IP 核详细使用配置步骤
ip·verilog·fpga·vivado·fifo
YunB西风英5 天前
(STM32笔记)十二、DMA的基础知识与用法 第二部分
笔记·stm32·嵌入式硬件·学习·dma·嵌入式
Ruoyo1765 天前
关于编写测试用例的细枝末节
测试用例·fpga
北城笑笑5 天前
FPGA 21 ,深入理解 Verilog 中的基数,以及二进制数与十进制数之间的关系( Verilog中的基数 )
fpga开发·fpga
飞凌嵌入式7 天前
飞凌嵌入式i.MX8M Mini核心板已支持Linux6.1
嵌入式硬件·嵌入式·飞凌嵌入式
JaneZJW7 天前
江科大STM32入门——读写备份寄存器(BKP)&实时时钟(RTC)笔记整理
笔记·stm32·单片机·嵌入式·rtc·bkp
国产化创客8 天前
国产OS移植工业物联网OPC-UA协议
物联网·嵌入式·通信协议
国产化创客8 天前
RK3399开发板Linux实时性改造
linux·物联网·嵌入式·实时操作系统