Files
kunlun/dtest/dtest3/kl3_sfc_test/readme.md

249 lines
18 KiB
Markdown
Raw Permalink Normal View History

2024-09-28 14:24:04 +08:00
# sfc测试例程说明文档
本测试程序用于测试sfc基础功能主要包括通过sfc对flash执行寄存器读写数据擦除和读写等例程。
## sfc简介
sfc全程为spi flash controller从属于EMCexternal memory controller模块是芯片内部封装的用于访问外部flash的功能模块简化了软件访问外部flash的逻辑和时序其主要特点如下
* 支持软件访问和cache访问模式并发访问时硬件自动仲裁且flash在program和erase过程中可以自动挂起让cache优先访问提高系统访问的实时性。
* 支持两个cs访问cs0大小可配置访问地址超过cs0时自动切换到cs1两个cs均最大支持16MB。
* flash program支持512bytes dual page模式提高program效率。
* DPdata path逻辑实现spi和控制器之间的时钟跨域处理以及数据支持自动加密、解密AES
* 支持io map逻辑实现spi接口信号的remapping操作方便适配不同flash芯片接口。
* 支持io share实现sfc和smc io分时复用。
* 支持spi地址空间remapping。
* 支持spi接口任意波形发生器可配置posedge或者negedge收发可增加dummy cycle来改善io timing
sfc主要有两种工作模式
* 软件通过sfc寄存器发起的操作包括program modeerase modedata trans moderegister read/write mode。
* 硬件通过cache下发的read操作。目前flash不能通过cache write
## 测试例程说明
sfc测试例程使用到的外设包含sfc、uart、gptimer。
* sfc测试对象用于测试sfc对外部flash的访问和控制。
* uart使用uart0实现调试信息打印接收case配置信息等与PC的交互功能。
* gptimer使用gptimer0用于测试“普冉flash工厂模式”时延时功能。
sfc测试例程遵循自动化测试框架在例程执行之后初始化串口0通过串口发送“start”字段到PC并等待PC发送“config”字段来获取本次需要执行的测试case。
在获取到case组合之后初始化flash以及gptimer并根据case组合开始执行测试case。
### case0获取flash芯片厂商ID和设备ID
1. 获取sfdpthe serial flash discoverable parameter
使用data trans mode发送0x5a指令读取flash的sfdp。对应的api如下
sfc_sts_type_t sfc_read_sfdp(uint8_t *data, uint32_t raddr, uint32_t size)
读取到的sfdp字段含义见《P25Q32L_datasheet_V1.1.pdf》文件在本例程中读取了0x00~0x04,0x10~0x14字段获得的结果如下其中0x0对应的4个字节数据为固定数据0x10中的85hex则表示厂商ID。程序中未判断sfdp的值
[INFO] - UNIT_SFC : SFDP data(0x0): 50 44 46 53
[INFO] - UNIT_SFC : SFDP data(0x10): 3 1 0 85
2. 获取厂商ID和设备ID
使用register read/write mode发送RESread electronic singnature指令获取厂商ID对应的指令以及API情况如下
| 指令 | 说明 | api |
| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 0x9f | read identificationRDID<br />返回3字节分别是manufacturer id0x85<br />memory type id0x60capacity id0x16 | int sfc_get_dev_id(uint8_t *data) |
| 0x90 | read electronic manufacturer id and device idREMS<br />返回2字节分别是manufacturer id0x85device id0x15 | sfc_sts_type_t sfc_qspi_get_id_mult(uint8_t *data, uint8_t mode) |
| 0x92 | dual i/o read electronic manufacturer id and device idDREMS<br />返回6字节前2字节分别是manufacturer id0x85device id0x15<br />后4字节是前两个字节的重复 | sfc_sts_type_t sfc_qspi_get_id_mult(uint8_t *data, uint8_t mode) |
| 0x94 | quad i/o read electronic manufacturer id and device idQREMS<br />返回6字节前2字节分别是manufacturer id0x85device id0x15<br />后4字节是前两个字节的重复 | sfc_sts_type_t sfc_qspi_get_id_mult(uint8_t *data, uint8_t mode) |
已知fpga上memory子板使用的是4m puya flash芯片在通过api获取到数据之后会对比数据是否表示puya 4m flash如果不是则判断为获取失败例程返回失败。例程执行的打印如下
[INFO] - UNIT_SFC : start get flash id test, the correct manufacturer id is 0x85, device id is 0x15
[INFO] - UNIT_SFC : Manufacturer ID(api):0x85, memory type:0x60
[INFO] - UNIT_SFC : Manufacturer ID(serial):0x85, device id:0x15
[INFO] - UNIT_SFC : Manufacturer ID(qual):0x85, device id:0x15
[INFO] - UNIT_SFC : Manufacturer ID(quad):0x85, device id:0x15
### case1读取flash状态寄存器
该case用于读取flash状态寄存器使用register read/write mode发送0x05读取状态寄存器bit0~bit7或者0x35读取状态寄存器bit8~bit15来获取flash状态寄存器值。
由于在例程执行之初调用了flash_init()函数将sfc以及flash配置为quad模式因此读取到的状态寄存器中表示quad enable的bitbit9应当置位因此case中会判断该bit如果未置位则表示case执行失败。
### case2flash擦除测试
该case用于测试flash erase功能使用erase mode发送erase指令其流程如下
1. 初始化测试数组数组全部初始化为0x55.
2. 将测试数组写入flash。
3. 使用cache读取写入数据确保写入成功。
4. erase flash。
5. 用cache读取erase之后的数据
6. 判断写入数据是否为0x55以及erase之后的数据是否为0xff如果任意条件不满足则判定case执行失败。
在上述流程中使用不同的erase指令重复执行步骤2~步骤6用于验证不同的erase模式功能。erase指令如下
| 指令 | 说明 | api |
| ---- | --------------------------------- | ------------------------------------------------------------ |
| 0x81 | 按page256 bytes大小擦除flash | int IRAM_ATTR sfc_erase_page(uint32_t page_addr, uint8_t sw_mode) |
| 0x20 | 按sector4K bytes大小擦除flash | int IRAM_ATTR sfc_erase_sector(uint32_t sector_addr, uint8_t sw_mode) |
| 0xD8 | 按block64K bytes大小擦除flash | int IRAM_ATTR sfc_erase_block(uint32_t block_addr, uint8_t sw_mode) |
| 0xC7 | 擦除整个flash | int sfc_erase_chip(void) |
### case3flash写测试
该case用于测试flash写功能使用program mode发送写指令和数据完成写操作case流程如下
1. 以x55初始化测试数组。
2. 擦除flash数据。
3. 写入flash数据。
4. 使用cache读取写入的数据。
5. 判断读取的数据是否与写入的数据相同不同则判定case执行失败。
在上述流程中使用不同的写指令重复步骤2~步骤5验证不同的写模式写指令如下
| 指令 | 说明 | api |
| ---- | ------------------------------------------------- | ------------------------------------------------------------ |
| 0x02 | page program以单线模式写入1page flash数据 | int IRAM_ATTR sfc_write(uint8_t *data, uint32_t waddr,uint32_t size,uint8_t prog, uint8_t sw_mode) |
| 0x32 | quad page program以4线模式写入1 page flash数据 | int IRAM_ATTR sfc_write(uint8_t *data, uint32_t waddr,uint32_t size,uint8_t prog, uint8_t sw_mode) |
### case4flash读测试
该case用于测试flash读功能使用data trans mode发送读指令完成读操作case流程如下
1. 擦除flash数据
2. 初始化测试数组数组第一个字节置为0xbb
3. 写入测试数组。
4. 使用sfc api读取写入的数据仅读取一个字节
5. 判断读取的数据是否为写入的0xbb如果不是则判定case执行失败。
在上述流程中使用不同的读指令重复步骤4~步骤5验证不同的读模式读指令如下
| 指令 | 说明 | api |
| ---- | ------------------------------- | ------------------------------------------------------------ |
| 0x03 | read data bytes | int sfc_read(uint8_t *data, uint32_t raddr, uint32_t size, uint8_t mode) |
| 0x0B | read data bytes at higher speed | int sfc_read(uint8_t *data, uint32_t raddr, uint32_t size, uint8_t mode) |
| 0x3B | dual read mode | int sfc_read(uint8_t *data, uint32_t raddr, uint32_t size, uint8_t mode) |
| 0x6B | quad read mode | int sfc_read(uint8_t *data, uint32_t raddr, uint32_t size, uint8_t mode) |
| 0xBB | 2*IO read mode | int sfc_read(uint8_t *data, uint32_t raddr, uint32_t size, uint8_t mode) |
| 0xEB | 4*IO read mode | int sfc_read(uint8_t *data, uint32_t raddr, uint32_t size, uint8_t mode) |
### case5flash全片读写测试
该case与case3写测试类似不过是整片flash的读写测试其流程如下
1. 以0x55初始化测试数组。
2. 擦除flash全片
3. 以quad模式全片写入测试数组数据
4. 使用cache读取全片并判断数据是否与写入数据一致不一致则判定case执行失败。
5. 将数据初始化为0xaa重复步骤2~步骤4
### case6普冉flash测试模式full scan测试
按照puya提供的文档《P25Q32H FT测试方案.pdf》文档编写测试case代码。puya提供的测试流程如下
◼ Step1设置QE=1(WREN + WRSR(31H) + 02H) (RDSR(35H) check 02H)
◼ Step2 WREN + Chip erase(check busy状态判断是否擦完)
◼ Step3 Enter test mode
◼ Step4 D4H program 256byte AA,AA,AA,AA,55,55,55,55…AA,AA,AA,AA,55,55,55,55 to 地址0x000000H
◼ Step5 D4H program 256byte AA,AA,AA,AA,55,55,55,55…AA,AA,AA,AA,55,55,55,55 to 地址0x000100H
◼ Step6 D4H program 256byte AA,AA,AA,AA,55,55,55,55…AA,AA,AA,AA,55,55,55,55 to 地址0x000200H
◼ Step7 D4H program 256byte AA,AA,AA,AA,55,55,55,55…AA,AA,AA,AA,55,55,55,55 to 地址0x000300H
◼ Step8 D4H program 256byte 55,55,55,55, AA,AA,AA,AA…55,55,55,55, AA,AA,AA,AA to 地址0x000400H
◼ Step9 D4H program 256byte 55,55,55,55, AA,AA,AA,AA…55,55,55,55, AA,AA,AA,AA to 地址0x000500H
◼ Step10 D4H program 256byte 55,55,55,55, AA,AA,AA,AA…55,55,55,55, AA,AA,AA,AA to 地址0x000600H
◼ Step11 D4H program 256byte 55,55,55,55, AA,AA,AA,AA…55,55,55,55, AA,AA,AA,AA to 地址0x000700H
◼ Step8 Exit test mode
◼ Step9 EBH command verify CKBD
◼ Step10 WREN + Chip erase
编写的测试case中仅step9EBH command verify CKBD未采用puya提供的方法而是全片使用cache读取数据并判断的方式实现其他均按照上述流程执行。
需要注意的是在进入测试模式后flash芯片自动交换了cs和wp信号需要配置sfc io map交换cs和wp信号与flash保持同步。
【注】目前在退出测试模式后将cs和wp信号换回读取到的flash数据全部为0x00因此case中在退出测试模式后并未换回cs和wp信号直接读取flash数据目前验证数据OK。
### case7多核竞争访问测试
该测试为了验证多核间竞争访问flash时sfc仲裁功能是否有效读写是否存在异常情况。
测试的方法如下:
1. 规定每个core独立读写一个pagecore0读写page0core1读写page1core2读写page2另外存在一个公用的page3各core每次写入自己page的同时将数据一并写入到page3。
2. core0负责初始化flash的4个page为0并启动另外2个core。
3. core1和core2读出各自page的数据并将得到的值加一重新写入各自的page和共用的page重复100次。
4. core0不写flash而是使用cache每隔500ms读取一次4个page的数据重复读取30次此时core1和core2已经完成100次写入因此core0最后一次得到的值一定是100且公用page的值一定等于core1或者core2对应page的值凭此判断测试是否成功。
在开始测试时确实发现多核访问存在一些问题比如抓取到的spi波形异常指令后没有地址、地址信息错误等或者读取到的数据不符合预期通过沟通得知**sfc硬件仲裁机制是凌驾于sfc之上服务于cache**。即通过直接控制sfc即sfc api访问flash时需要软件自行仲裁sfc本身不存在仲裁机制多核竞争访问时一旦sfc start标志置位sfc均以当前的寄存器的值作为配置开始操作flash即指令之间能相互打断此时可能出现指令未执行或者指令执行出错等情况。
针对以上情况有以下2种解决办法
1. 所有flash的读写擦除都集中到一个core上操作当其他core需要操作flash时通过核间通信将操作告知指定的core。这样的好处是软件可以统一控制操作flash行为比如等待空闲之后再操作flash等但是需要与上层拟定方案并在驱动层做适配。
2. 使用自旋锁保证同一时刻仅一个flash操作被执行。好处是对现有代码改动较少但是core在等待其他core释放自旋锁期间无法做其他事情相当于浪费了cpu的能力甚至会减缓某些逻辑的执行。
目前采用的是方法2在sfc驱动层中使用spinlock0对flash read、write、erase均进行上锁并在执行完成之后解锁测试结果OK。
***
**在后续的讨论中考虑到spinlock死锁和效率问题取消了sfc驱动中增加spinlock的办法因此调整测试方法如下**
1. core0单独读写flash一个page读取page第一个word数据擦除后将数据加1重新写入数据从0开始计数重复x次。
2. core1、core2通过cache读取该数据先使用cache_invalidata函数抛弃无效数据之后重新读取。判断值是否顺序累加了x次在达到x次将scratch0寄存器core id * 2对应的bit置位bit0表示core已经读取到xbit1表示是否顺序读取到所有值。
3. core0每次更新数据之后读取scratch0寄存器并在core1、core2都读取到x之后不再读写flash并将core1、core2停止根据scratch0寄存器的值判定测试是否成功。
该测试还兼顾了测试cache优先访问的功能sfc中存在一个寄存器用于记录flash在erase和program过程中被挂起的次数在该测试执行前将次数清除并在执行之后读取该值能观察到次数显著增加说明cache确实打断了erase、program过程。
### case8flash地址重映射
该测试为了验证sfc所具有的地址重映射功能。
**【注】sfc地址重映射功能仅多cache读生效即使用sfc api访问flash时始终使用真实偏移地址。**
测试流程如下:
1. 将remap单位配置为64KB默认即为64KB擦除地址0x00和0x10000。
2. 将0x00地址写入256字节0x55数据并用api读取0x00和0x10000地址数据预期0x00对应的数据为0x55,0x10000对应的数据为0xff。如果数据错误则判定测试失败。
3. 通过直接更改寄存器实现remap 0x00和0x10000地址。
4. 使用cache重新读取0x00和0x10000数据预期0x00数据为0xff0x10000数据为0x55如果数据错误则判定失败。
5. 将重映射还原并通过cache读取数据确保还原成功。
6. 将remap单位配置为128KB操作地址更改为0x00和0x20000重复步骤2~步骤4。
测试结果OK。
### case9sfc挂载两颗flash芯片
该测试为了验证sfc支持双flash的功能因此需要在测试环境中焊接两颗flash芯片。
sfc支持两个cs信号即可以挂载两个flash芯片sfc存在SPI_CS0_CFG寄存器用于配置sfc访问两颗flash的行为具体行为如下
* 当寄存器中spi_cs1_force_sel置1时sfc强制访问cs1对应的flash芯片。
* 当寄存器中spi_cs1_force_sel置0时通过spi_cs0_size位配置flash 0芯片大小单位为MB最大配置为16如果访问的地址超过flash0配置的空间大小则自动访问cs1对应的flash。
测试流程较为简单,其步骤如下:
1. 在flash 0指定位置X写入数据A并读出数据对比以确保写入成功写入失败则判定测试失败。
2. 将spi_cs1_force_sel置1在flash 1指定位置X写入数据B并读取数据确保写入成功写入失败则判定测试失败。
3. 读取flash 0指定地址X判断读出数据是否为数据A不是则判定失败。
4. 将spi_cs1_force_sel置0spi_cs0_size位配置为4。
5. 读取地址X判断数据是否为数据A不是则判定失败。
6. 读取地址X + 0x400000判断数据是否为数据B不是则判定失败。
需要注意以下两点:
* 如果通过cache读取数据需要将cache space配置合理否则超出space的地址将会被抛弃。
* spi_cs0_size位配置的flash 0大小需要小于等于flash 0的真实大小。
以上测试程序运行时同步使用逻辑分析仪观察sfc信号发现cs信号符合预期测试结果通过。
### case10sfc monitor信号测试
该测试为了验证sfc monitor信号是否正常工作。
测试流程较为简单找到可用的gpio将sfc monitor信号通过io matrix绑定到gpio上使用逻辑分析已接入sfc真实信号和sfc monitor信号触发flash操作观察monitor信号是否与sfc真实信号保持一致。
测试结果通过monitor信号比真实信号晚35ns左右波形有微小失真但基本和真实sfc信号保持一致。
**【注】fpga上sfc的cs线存在在引出时存在问题将cs0信号引到了flash1cs1信号引到了flash0sfc正常情况下使用的cs0即真正使用的是flash1由于memory子板焊接的是flash1因此fpga保持现状不变。**