小猫爪:i.MX RT1050学习笔记14-FlexSPI-FLASH使用2-IAR FLASH算法中的使用
1 前言
在前面介绍了RT1050启动时会读取镜像的FCB数据对FlexSPI进行初始化,在下载代码进FLASH时,我们需要使用FLASH算法,那我们怎样在IAR FLASH算法中对FlexSPI进行初始化呢,或者说怎样针对自己的FLASH设计自己的算法呢?
2 FLASH算法解析
首先去了解一下官方的源码做了什么,打开NXP RT1050的IAR FLASH算法源码(路径:C:\Program Files (x86)\IAR Systems\Embedded Workbench 8.4\arm\src\flashloader\NXP\FlashIMXRT1050_EVK_FlexSPI);
首先我们看看初始化函数,FLASH算法的FlashInit初始化函数源码如下:
uint32_t FlashInit(void *base_of_flash, uint32_t image_size,
uint32_t link_address, uint32_t flags,
int argc, char const *argv[])
#else
uint32_t FlashInit(void *base_of_flash, uint32_t image_size,
uint32_t link_address, uint32_t flags)
#endif /* USE_ARGC_ARGV */
{
uint32_t result;
mass_erase_done = 0;
/**/
#if defined(MCIMXRT1020) || defined(MCIMXRT1015)
if( 2 > ((SRC_SBMR1>>1) & 0x7))
#elif defined(MCIMXRT1010)
if( 1 > ((SRC_SBMR1>>1) & 0x3))
#else
if( 2 > ((SRC_SBMR1>>8) & 0x7))
#endif
{
device = &QSPIFlash;
}
else
{
device = &HyperFlash;
}
/*init*/
#if USE_ARGC_ARGV
result = device->init(base_of_flash, argc,argv);
#else
result = device->init(base_of_flash);
#endif /* USE_ARGC_ARGV */
if((RESULT_ERROR != result) && device->erase_chip)
{
//Mass erase or erase only
if (flags & (FLAG_ERASE_ONLY | FLAG_MASS_ERASE))
{
if(RESULT_OK != device->erase_chip())
{
result = RESULT_ERROR;
} else if(flags & (FLAG_ERASE_ONLY))
{
result = RESULT_ERASE_DONE;
}
else
{
mass_erase_done = 1;
}
}
}
return result;
}
首先会读取SRC_SBMR1寄存器的状态去读取当前是哪一种启动设备启动,然后再去调用该设备对应的FLASH算法,我们以QSPI为例,看看它的初始化里做了什么?
static uint32_t init(void *base_of_flash,int argc, char const *argv[])
#else
static uint32_t init(void *base_of_flash)
#endif /* USE_ARGC_ARGV */
{
uint32_t result = (RESULT_ERROR);
uint32_t mfid;
a1_size = 0;
b1_size = 0;
// QE_mask = (1<<1); /*QE bit in WB,AT and GD devices*/
/**/
FLEXSPI_MCR0 = 0xFFFF8802;
FLEXSPI_MCR1 = 0xFFFFFFFF;
FLEXSPI_MCR2 = 0x200041F7;
FLEXSPI_AHBCR = 0x00000078;
FLEXSPI_AHBRXBUF0CR0 = 0x00000000;
FLEXSPI_AHBRXBUF1CR0 = 0x00000000;
FLEXSPI_AHBRXBUF2CR0 = 0x00000000;
FLEXSPI_FLSHA1CR0 = 0x00010000;
FLEXSPI_FLSHA2CR0 = 0x00000000;
FLEXSPI_FLSHB1CR0 = 0x00010000;
FLEXSPI_FLSHB2CR0 = 0x00000000;
FLEXSPI_FLSHA1CR1 = 0x00000063;
FLEXSPI_FLSHB1CR1 = 0x00000063;
//FLEXSPI_FLSHA1CR2 = (READ_SEQUENCE);
FLEXSPI_FLSHCR4 = 0x00000000;
//FLEXSPI_DLLACR = 0x00001D00;
//FLEXSPI_DLLBCR = 0x00001D00;
/*Enable Flex SPI module*/
FLEXSPI_MCR0 &= ~(1<<1);
/*Initialize LUT table*/
InitLUT();
/*Software reset*/
FLEXSPI_MCR0 |= (1<<0);
while((1<<0) & FLEXSPI_MCR0);
/*Read A1 ID*/
ReadID(0x00000000,&mfid);
switch(mfid & 0x000000FF)
{
case 0xC2:
mfid -= 0X0100; /*Macronix density corection*/
case 0x9D:
// QE_mask = (1<<6);
// SetQE_RDWR();
case 0x01:
case 0xC8:
case 0xEF:
case 0x1F:
a1_size = 1<<(((mfid >> 8) & 0x001F)+1); /*0x1F mask needed for Macronix devices.
This will work for up to 2GB devices*/
// ReadQEReg(0x00000000, &a1_QEReg);
/* if(!(a1_QEReg & QE_mask))
{
WriteEnable(0x00000000);
WriteQEReg(0x00000000, a1_QEReg | QE_mask);
}*/
break;
default:
break;
}
/*Read B1 ID*/
ReadID(FLEXSPI_FLSHA1CR0*1024,&mfid);
switch(mfid & 0x00FF)
{
case 0xC2:
mfid -= 0X0100; /*Macronix density corection*/
case 0x9D:
// QE_mask = (1<<6);
// SetQE_RDWR();
case 0x01:
case 0xC8:
case 0xEF:
case 0x1F:
b1_size = 1<<(((mfid >> 8) & 0x001F)+1);/*0x1F mask needed for Macronix devices.
This will work for up to 2GB devices*/
// ReadQEReg(FLEXSPI_FLSHA1CR0*1024, &b1_QEReg);
/*if(!(b1_QEReg & QE_mask))
{
WriteEnable(FLEXSPI_FLSHA1CR0*1024);
WriteQEReg(FLEXSPI_FLSHA1CR0*1024, b1_QEReg | QE_mask);
}*/
break;
default:
break;
}
/**/
// if(FlFindOption("--setQE", 0, argc, argv)) {
// a1_QEReg |= QE_mask;
// b1_QEReg |= QE_mask;
// } else if(FlFindOption("--clrQE", 0, argc, argv)) {
// a1_QEReg &= ~QE_mask;
// b1_QEReg &= ~QE_mask;
// }
FLEXSPI_FLSHA1CR0 = a1_size/1024;
FLEXSPI_FLSHB1CR0 = b1_size/1024;
if(SECTOR_SIZE < (a1_size + b1_size))
{
sprintf(LAYOUT_OVERRIDE_BUFFER,"%d 0x%X\0",(a1_size + b1_size)/SECTOR_SIZE,SECTOR_SIZE);
result = (OVERRIDE_LAYOUT);
/**/
if(1<<24 < a1_size)
{
/*A1 device is bigger*/
/*Change LUT Read, Write and Erase
sequences to use 4Byte address*/
SetLUT32b();
if(0 != b1_size)
{
if(1<<24 >= b1_size)
{
/*2 devices with
none compatible size*/
result = (RESULT_ERROR);
}
}
}
else
{
if(1<<24 < b1_size)
{
/*B1 device is bigger*/
/*Change LUT Read, Write and Erase
sequences to use 4Byte address*/
SetLUT32b();
if( 0 != a1_size)
{
/*2 devices with
none compatible size*/
result = (RESULT_ERROR);
}
}
}
}
else
{
strcpy(ERROR_MESSAGE_BUFFER, init_err);
result = RESULT_ERROR_WITH_MSG;
}
return result;
}
初始化的步骤非常简单,具体内容如下:
①初始化FlexSPI相关寄存器。(FLEXSPI_MCRx,FLEXSPI_AHBCR, FLEXSPI_AHBRXBUFxCRx)
②初始化FLASH参数相关的寄存器。(FLEXSPI_FLSHxxCRx, FLEXSPI_FLSHCR4等。)
③初始化LUT表
④复位FlexSPI
⑤通过读取寄存器获取当前FLASH的大小,如果超出24位寻址,则修改为32位寻址。
可以看到初始化的过程和在上一章所介绍的启动时初始化的内容一摸一样,唯一的区别就是在标准工程中,我们可以通过结构体来对寄存器进行初始化,但是对于FLASH算法来说,则是通过直接对寄存器赋值的方式来进行初始化(提议两个捷径得到寄存器最佳值:首先在RAM中完成对FlexSPI的调试,读出相应寄存器的值,再写到FLASH算法中)。
我们再看一下FLASH算法是怎么实现写操作的,废话不多说直接上源码:
static uint32_t write(uint32_t addr,
uint32_t count,
char const *buffer)
{
uint32_t result = (RESULT_OK);
uint32_t loadaddr = addr-FlexSPI_AHB_BASE;
uint8_t status;
uint32_t size = PAGE_SIZE - ((uint32_t)addr&(PAGE_SIZE-1));
while(count)
{
size = (size<count)?size:count;
/*Clear QSPI Flash status flags*/
WriteEnable(loadaddr);
/*Page Program*/
PageProgram(loadaddr,(uint64_t *)buffer,size);
/*Wait the end of the operation on the
QSPI Flash side*/
do
{
/*Read QSPI Flash Status Flags*/
ReadStatusReg(loadaddr, &status);
}while((status & (1<<0)));
/*Program error*/
count -= size;
loadaddr += size;
buffer += size;
size = PAGE_SIZE;
}
return result;
}
不难看出一个写操作总共调用了三个函数,先发送写使能指令(WriteEnable),再开始写数据(PageProgram),最后发送读指令读取状态确认擦写完成(ReadStatusReg),在这里我们就单独拿出PageProgram来做个介绍。上源码:
static uint32_t PageProgram(uint32_t addr, const uint64_t * data,uint16_t size)
{
uint32_t result = RESULT_OK;
/*Clear interrupts*/
FLEXSPI_INTR = 0x00000F3F;
/*Set Transfer address*/
FLEXSPI_IPCR0 = addr;
/*Set Sequence ID 1,Sequence Num 0 and Data size*/
FLEXSPI_IPCR1 = (0<<20) | (PAGE_PROG_SEQUENCE<<16) | (size);
/*Clear fifo. Watermark is 8 bytes*/
FLEXSPI_IPTXFCR = 1;
/*Start trensfer*/
FLEXSPI_IPCMD = 1;
while(size)
{
while(!(FLEXSPI_INTR & (1<<6)));
*((uint64_t *) &FLEXSPI_TFDR0) = *data++;
/*write data to fifo*/
FLEXSPI_INTR |= (1<<6);
size -= 8;
/*check for errors*/
if(FLEXSPI_INTR & ((1<<1) | (1<<3)))
{
result = RESULT_ERROR;
break;
}
}
while(!(FLEXSPI_STS0 & (1<<1)));
/*check for errors*/
if(FLEXSPI_INTR & ((1<<1) | (1<<3)))
{
result = RESULT_ERROR;
}
return result;
}
不难看出,整个写的过程就是一个完整的通过IP命令操作FlexSPI的过程(具体流程可以参考我之前的文章《小猫爪:i.MX RT1050学习笔记12-FlexSPI简介》),在这里它调用了LUT表的序列PAGE_PROG_SEQUENCE,让我们看看序列1是什么?继续上源码?
// lut_table[PAGE_PROG_LUT_INDEX+0] = FLEXSPI_LUT_INST(LUT_CODE_CMD_SDR, LUT_PADS_ONE, QSPI_CMD_PPQ);
lut_table[PAGE_PROG_LUT_INDEX+0] = FLEXSPI_LUT_INST(LUT_CODE_CMD_SDR, LUT_PADS_ONE, QSPI_CMD_PP);
lut_table[PAGE_PROG_LUT_INDEX+1] = FLEXSPI_LUT_INST(LUT_CODE_RADDR_SDR, LUT_PADS_ONE, ADDR_3B);
lut_table[PAGE_PROG_LUT_INDEX+2] = FLEXSPI_LUT_INST(LUT_CODE_WRITE_SDR, LUT_PADS_ONE, 0);
lut_table[PAGE_PROG_LUT_INDEX+3] = FLEXSPI_LUT_INST(LUT_CODE_STOP, 0, 0);
lut_table[PAGE_PROG_LUT_INDEX+4] = FLEXSPI_LUT_INST(LUT_CODE_STOP, 0, 0);
lut_table[PAGE_PROG_LUT_INDEX+5] = FLEXSPI_LUT_INST(LUT_CODE_STOP, 0, 0);
lut_table[PAGE_PROG_LUT_INDEX+6] = FLEXSPI_LUT_INST(LUT_CODE_STOP, 0, 0);
lut_table[PAGE_PROG_LUT_INDEX+7] = FLEXSPI_LUT_INST(LUT_CODE_STOP, 0, 0);
整理一下就是:
指令序号 | 指令名称 | PAD数量 | OPCODE | 描述 |
---|---|---|---|---|
1 | CMD_SDR | PADS_ONE | QSPI_CMD_PP | SDR模式1线模式向FLASH发送写指令 |
2 | RADDR_SDR | PADS_ONE | ADDR_3B | SDR模式1线模式向FLASH发送24位地址 |
3 | WRITE_SDR | PADS_ONE | 0 | SDR模式写指令,开始写数据,写的数据大小由FLEXSPI_IPCR1[IDATSZ]决定 |
4 | STOP | PADS_ONE | 0 | STOP命令,释放CS |
说到这里,大家可能对LUT表的使用已经非常熟悉了吧(注意:对LUT表其他指令的解析在另外两篇文章中也有)。
大家可能会有疑问,FlexSPI相关时钟和引脚的初始化呢,在FLASH算法中找不到引脚初始化?因为FLASH算法中根本就没有引脚初始化,那引脚初始化在哪里呢,答案在我的文章中《小猫爪:嵌入式小知识09-KEIL/IAR FLASH算法》提到的.mac文件中,打开.mac文件,可以发现里面就有对FlexSPI相关引脚的初始化。
(注意:IAR中,如果我们想改变Flash算法在RAM中的具体位置(因为对于RT这种可以调节OCRAM大小的MCU来说,如果Flash算法所在地址和大小冲突了则会导致IAR校验失败导致下载失败),可以通过改变Flash算法工程的链接文件来改变Flash算法在RAM中的具体位置。)
根据以上的操作,我们就能针对FLASH编写自己的IAR FLASH算法啦。