Skip to content

AXI QSPI寄存器调试w25q64的XIP模式

minichao9901 edited this page May 7, 2024 · 7 revisions

说明

  • 用了2个AXI Quad SPI核,控制同一个Qspi Flash。这2个QSPI IP,用一个引脚spi_sel来选择
  • spi_sel=0,选择Qspi1,用于编程数据,写入Qspi Flash
  • spi_sel=1,选择Qspi2,用于XIP模式运行,以存储器映射方式读取Qspi Flash
  • 结论:以循环方式执行XIP,波形乱,结果异常。主要是地址都发错了。
  • 结论:以CDMA方式执行XIP,波形正常,结果正常。但是结果没有写入ddr?
  • 结论:这个AXI QuadSPI的IP,必须以CDMA的方式运行。(结论有误)

bd设计

  • 例程:cdma_gpio_uart_qspi
  • z7020+v3扩展版 image image image

c程序

#include "ACZ702_Lib/COMMON.h"

//xsct% mrd 0x4100001C 1
//4100001C:   00000000
//
//xsct% mrd 0x41000020 1
//41000020:   00000400
//
//xsct% mrd 0x41000028 1
//41000028:   0000202B
//
//xsct% mrd 0x41000040 1
//Memory read error at 0x41000040. Blocked address 0x41000040. Cannot read write-only register
//xsct% mrd 0x41000060 1
//41000060:   00000106
//
//xsct% mrd 0x41000064 1
//41000064:   00000025
//
//xsct% mrd 0x41000068 1
//Memory read error at 0x41000068. Blocked address 0x41000068. Cannot read write-only register
//xsct% mrd 0x4100006C 1
//Memory read error at 0x4100006C. Memory read aborted. External abort
//xsct% mrd 0x41000070 1
//41000070:   00000001
//
//xsct% mrd 0x41000074 1
//41000074:   00000000

#define PS_KEY 15
#define PS_LED 11
int main_ps_io(void)
{
    uint8_t State;   //存放按键(MIO47)的电平状态,0为低电平,1为高电平

    PS_GPIO_Init(); //初始化PS端MIO和EMIO
    PS_GPIO_SetMode(PS_LED, OUTPUT, 0);
    //设置PL_LED(EMIO0)为输出并且初始为低电平
    PS_GPIO_SetMode(PS_KEY, INPUT, 0); //设置PS_KEY(MIO47)方向为输入

//    while(1)
//    {
//        State = PS_GPIO_GetPort(PS_KEY);   //读取PS_KEY的电平值并存储到State变量里
//        PS_GPIO_SetPort(PS_LED,!State);    //将State变量的值取非赋予PS_LED来输出
//    }
//    return 0;
}


typedef struct{
	u8 txbuf[256];
	u8 rxbuf[256];
	u8 length;
}t_buf;

t_buf WriteBuffer[]={
    //s1: read id
	{{0x90},{0x00},6},
	{{0x9f},{0x00},8},
	{{0x4b},{0x00},13},

	//s2: sector erase
	{{0x06},{0x00},1},
	{{0x20,0x00,0x00,0x10},{0x00},4},
	{{0xff},{0x00},0}, //length=0 => delay_ms

	//s3: page program
	{{0x06},{0x00},1},
	{{0x02,0x00,0x00,0x10,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88},{0x00},250},
	{{0xff},{0x00},0}, //length=0 => delay_ms

	//s4: spi/dspi/qspi read
	{{0x0b,0x00,0x00,0x10},{0x00},250},
	{{0x3b,0x00,0x00,0x10},{0x00},250},
	{{0x6b,0x00,0x00,0x10},{0x00},250},

//	//s5: sector erase
//	{{0x06},{0x00},1},
//	{{0x20,0x00,0x00,0x10},{0x00},4},
//	{{0xff},{0x00},0}, //length=0 => delay_ms
//
//	//s6: qspi program
//	{{0x06},{0x00},1},
//	{{0x32,0x00,0x00,0x10,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x11,0x22},{0x00},12},
//	{{0xff},{0x00},0}, //length=0 => delay_ms
//
//	//s7: qspi read
//	{{0x6b,0x00,0x00,0x10},{0x00},14},
//	{{0xbb,0x00,0x00,0x10},{0x00},14},
//	{{0xeb,0x00,0x00,0x10},{0x00},14},

	//s8: enter qpi mode (QE=1), 貌似不支持
//	{{0x38},{0x00},1},   //not support
//	{{0x01,0x00,0x02},{0x00},3},
//	{{0xA3,0x00,0x00,0x00},{0x00},4},
//
//	//s9: sector erase
//	{{0x06},{0x00},1},
//	{{0x20,0x00,0x00,0x10},{0x00},4},
//	{{0xff},{0x00},0}, //length=0 => delay_ms
//
//	//s10: qpi read
//	{{0x0b,0x00,0x00,0x10},{0x00},14},
//
//	//s11: qpi program
//	{{0x06},{0x00},1},
//	{{0x02,0x00,0x00,0x10,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11},{0x00},12},
//	{{0xff},{0x00},0}, //length=0 => delay_ms
//
//	//s12: qpi read
//	{{0xbb,0x00,0x00,0x10},{0x00},14},
//	{{0xeb,0x00,0x00,0x10},{0x00},14},
};



void print_buffer(u8 *pdata, int length)
{
	for(int i=0; i<length; i++){
		xil_printf("pdata[%d]=%x\r\n", i, pdata[i]);
	}
	xil_printf("********************\r\n");
}


void test_flash()
{
	for(int i=0; i<sizeof(WriteBuffer)/sizeof(*WriteBuffer); i++){
		if(WriteBuffer[i].length==0){
			usleep(WriteBuffer[i].txbuf[0]*1000);
			continue;
		}

		AXI_SPI_Transfer(&AXI_SPI0, 0, WriteBuffer[i].rxbuf, WriteBuffer[i].txbuf, WriteBuffer[i].length);
		xil_printf("Exec CMD=%x\r\n\r\n", WriteBuffer[i].txbuf[0]);
		print_buffer(WriteBuffer[i].rxbuf, WriteBuffer[i].length);
	}
}

u8 txBuffer[4096];
u8 rxBuffer[4096];

void pulling_sr()
{
	u32 ticks=0;
	while(ticks++<10){
		txBuffer[0]=0x05;
		AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 2);
		u8 sr0=rxBuffer[1];
		usleep(1);

		txBuffer[0]=0x35;
		AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 2);
		u8 sr1=rxBuffer[1];
		usleep(1);

		txBuffer[0]=0x15;
		AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 2);
		u8 sr2=rxBuffer[1];
		usleep(1);

		//u32 sr=(sr2<<15)+(sr1<<8)+sr0;
		xil_printf("%x,%x,%x\r\n",sr0,sr1,sr2);
	}
}

void test_flash2()
{
//	//s1:QE=1
//	txBuffer[0]=0x06;
//	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);
//
//	txBuffer[0]=0x31;
//	txBuffer[1]=1<<1;
//	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);


	//s2: sector erase
	txBuffer[0]=0x06;
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);

	txBuffer[0]=0x20;
	txBuffer[1]=0x00;
	txBuffer[2]=0x00;
	txBuffer[3]=0x10;
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4);
	//pulling_sr();
	usleep(100*1000);

	//s3: page program
	txBuffer[0]=0x06;
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);

	txBuffer[0]=0x02;
	txBuffer[1]=0x00;
	txBuffer[2]=0x00;
	txBuffer[3]=0x10;

	for(int i=4; i<4096; i++){
		txBuffer[i]=i;
	}
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
	//pulling_sr();
	usleep(100*1000);

	//s4: spi/dspi/qspi read
	txBuffer[0]=0x0b;
	txBuffer[1]=0x00;
	txBuffer[2]=0x00;
	txBuffer[3]=0x10;
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
	usleep(100*1000);
	for(int i=0; i<32; i++) xil_printf("%d\r\n", rxBuffer[i]);
	xil_printf("*******************\r\n");

	txBuffer[0]=0x3b;
	txBuffer[1]=0x00;
	txBuffer[2]=0x00;
	txBuffer[3]=0x10;
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
	usleep(100*1000);
	for(int i=0; i<32; i++) xil_printf("%d\r\n", rxBuffer[i]);
	xil_printf("*******************\r\n");

	txBuffer[0]=0x6b;
	txBuffer[1]=0x00;
	txBuffer[2]=0x00;
	txBuffer[3]=0x10;
	AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
	usleep(100*1000);
	for(int i=0; i<32; i++) xil_printf("%d\r\n", rxBuffer[i]);
	xil_printf("*******************\r\n");
}




#define QSPI_BASE 0x41000000
void config_spi()
{
	Xil_Out32(QSPI_BASE+0x40, 0xa);        //soft_rst
	Xil_Out32(QSPI_BASE+0x28, 0x3fff);     //global_ie
	Xil_Out32(QSPI_BASE+0x1c, 0x80000000); //ie
}

void polling_tx_fifo_is_empty()
{
	u32 status=0;
	do{
		status=Xil_In32(QSPI_BASE+0x64);
	}
	while(!(status&0b100));
}

void start_spi()
{
	Xil_Out32(QSPI_BASE+0x70, 0x00);  //选择0通道cs
	Xil_Out32(QSPI_BASE+0x60, 0x86);  //使能master,开始发数据
	polling_tx_fifo_is_empty();
	Xil_Out32(QSPI_BASE+0x70, 0x01);  //选择0通道cs的cs拉高
	Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master
}

void read_data(u32 num)
{

	for(int i=0; i<num; i++){
		u8 v=Xil_In32(QSPI_BASE+0x6c);
		printf("%x\r\n",v);
	}
}

void read_cmd(u8 cmd, u32 num)
{
	config_spi();

	Xil_Out32(QSPI_BASE+0x60, 0x1e6);  //复位tx,rx fifo
	Xil_Out32(QSPI_BASE+0x60, 0x186);  //释放tx,rx fifo
	Xil_Out32(QSPI_BASE+0x68, cmd);
	for(int i=0; i<num; i++){
		Xil_Out32(QSPI_BASE+0x68, 0x00);
	}

	start_spi();
	read_data(num);
}

void read_id()
{
	read_cmd(0x9f,8);
}

void read_status()
{
	read_cmd(0x05,2);
	read_cmd(0x35,2);    //QE bit is default=1
	//read_cmd(0x15,2);  //cmd15 do not support
}

void write_data(u8 cmd, u8 *pdata, u8 length)
{
	Xil_Out32(QSPI_BASE+0x60, 0x1e6);
	Xil_Out32(QSPI_BASE+0x60, 0x186);
	Xil_Out32(QSPI_BASE+0x68, cmd);
	for(int i=0; i<length; i++){
		Xil_Out32(QSPI_BASE+0x68, *pdata++);
	}

	start_spi();
}

volatile u8 status;
void polling_qspi_busy()
{
	do{
		config_spi();

		Xil_Out32(QSPI_BASE+0x60, 0x1e6);
		Xil_Out32(QSPI_BASE+0x60, 0x186);
		Xil_Out32(QSPI_BASE+0x68, 0x05);
		Xil_Out32(QSPI_BASE+0x68, 0x00);

		start_spi();
		Xil_In32(QSPI_BASE+0x6c);  //first data drop
		status=Xil_In32(QSPI_BASE+0x6c);
	}
	while(status==0x03);
}


void wren()
{
	write_data(0x06, NULL, 0);
}

void se(u8 sector_num)
{
	wren();

	u8 tbuffer[3]={sector_num, 0x00, 0x00};
	write_data(0x20, &tbuffer[0],3);
}

void be()
{
	wren();
	write_data(0xc7, NULL,0);
}

void pp(u8 cmd, u32 address, u8 *pdata, u8 length)
{
	wren();
	Xil_Out32(QSPI_BASE+0x68, cmd);
	Xil_Out32(QSPI_BASE+0x68, (address>>16)&0xff);
	Xil_Out32(QSPI_BASE+0x68, (address>>8)&0xff);
	Xil_Out32(QSPI_BASE+0x68, address&0xff);
	for(int i=0; i<length; i++){
		Xil_Out32(QSPI_BASE+0x68, *pdata++);
	}

	start_spi();
}

void read(u8 cmd, u32 address, u8 *pdata, u8 length)
{
	Xil_Out32(QSPI_BASE+0x68, cmd);
	Xil_Out32(QSPI_BASE+0x68, (address>>16)&0xff);
	Xil_Out32(QSPI_BASE+0x68, (address>>8)&0xff);
	Xil_Out32(QSPI_BASE+0x68, address&0xff);
	for(int i=0; i<length; i++){
		Xil_Out32(QSPI_BASE+0x68, 0x00);
	}

	start_spi();
	read_data(length);
}


void pp_try_multi(u8 cmd, u32 address, u8 *pdata, u8 length)
{
/* 这个程序表明,AXI QUAD SPI这个IP的CS是完全由软件管理的,可以非常灵活的拉高或者拉低
 * 这样可以实现,一个很长的Packet。这个是在API函数中没有实现的功能。必须用寄存器手搓。
 */
	wren();
	Xil_Out32(QSPI_BASE+0x68, cmd);
	Xil_Out32(QSPI_BASE+0x68, (address>>16)&0xff);
	Xil_Out32(QSPI_BASE+0x68, (address>>8)&0xff);
	Xil_Out32(QSPI_BASE+0x68, address&0xff);
	u8 *pdata_tmp=pdata;
	for(int i=0; i<length; i++){
		Xil_Out32(QSPI_BASE+0x68, *pdata_tmp++);
	}

	Xil_Out32(QSPI_BASE+0x70, 0x00);  //选择0通道cs
	Xil_Out32(QSPI_BASE+0x60, 0x86);  //使能master,开始发数据
	polling_tx_fifo_is_empty();
//	Xil_Out32(QSPI_BASE+0x70, 0x01);  //选择0通道cs的cs拉高
	Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master


	pdata_tmp=pdata;
	for(int i=0; i<length; i++){
		Xil_Out32(QSPI_BASE+0x68, *pdata_tmp++);
	}

//	Xil_Out32(QSPI_BASE+0x70, 0x00);  //选择0通道cs
	Xil_Out32(QSPI_BASE+0x60, 0x86);  //使能master,开始发数据
	polling_tx_fifo_is_empty();
//	Xil_Out32(QSPI_BASE+0x70, 0x01);  //选择0通道cs的cs拉高
	Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master

	pdata_tmp=pdata;
	for(int i=0; i<length; i++){
		Xil_Out32(QSPI_BASE+0x68, *pdata_tmp++);
	}

//	Xil_Out32(QSPI_BASE+0x70, 0x00);  //选择0通道cs
	Xil_Out32(QSPI_BASE+0x60, 0x86);  //使能master,开始发数据
	polling_tx_fifo_is_empty();
//	Xil_Out32(QSPI_BASE+0x70, 0x01);  //选择0通道cs的cs拉高
	Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master

}



#define CDMA_BASE 0x43000000
#define QSPI_XIP_BASE 0x40000000

typedef u8 (*buffer8_type)[128];
typedef u32 (*buffer32_type)[128];
buffer8_type buffer8=(buffer8_type)0x11000000;
buffer32_type buffer32=(buffer32_type)0x12000000;

void wait_for_idle()
{
	u32 tmp;
	while(1){
		tmp=Xil_In32(CDMA_BASE+04);
		if(((tmp>>1)&0x1) == 1){
			break;
		}
	}
}

void xip_for_read_test()
{
	for(int i=0; i<128; i++){
		*buffer8[i]=Xil_In8(QSPI_XIP_BASE+0x0c0000+i);
	}
	for(int i=0; i<32; i++){
		*buffer32[i]=Xil_In32(QSPI_XIP_BASE+0x0c0000+4*i);
	}
	for(int i=0; i<128; i++){
		printf("%x\r\n",*buffer8[i]);
	}
	for(int i=0; i<32; i++){
		printf("%x\r\n",*buffer32[i]);
	}
}

void xip_dma_read_test()
{
	//Xil_Out32(CDMA_BASE+0x00, 0x00000020);  //key_hole_wr=1
	Xil_Out32(CDMA_BASE+0x18, QSPI_XIP_BASE+0x0c0000);  //src_addr
	Xil_Out32(CDMA_BASE+0x20, (u32)buffer8);  //dest_addr
	Xil_Out32(CDMA_BASE+0x28, 128);         //burst_length
	wait_for_idle();

	Xil_DCacheInvalidateRange((u32)buffer8, 128);
	for(int i=0; i<128; i++){
		printf("%d\r\n",*buffer8[i]);
	}
}

int main(void)
{

	//初始化通用中断控制器
	ScuGic_Init();
	main_ps_io();

	//初始化AXI_SPI0,设为主机模式
	AXI_SPI_Init(&AXI_SPI0, XPAR_SPI_0_DEVICE_ID, XSP_MASTER_OPTION);

	//初始化私有定时器中断,定时间隔100ms
	ScuTimer_Int_Init(100000);

//	for(int i=0; i<sizeof(WriteBuffer)/sizeof(*WriteBuffer); i++){
//		if(WriteBuffer[i].length==0){
//			usleep(WriteBuffer[i].txbuf[0]*1000);
//			continue;
//		}
//
//		AXI_SPI_Transfer(&AXI_SPI0, 0, WriteBuffer[i].rxbuf, WriteBuffer[i].txbuf, WriteBuffer[i].length);
//		xil_printf("Exec CMD=%x\r\n\r\n", WriteBuffer[i].txbuf[0]);
//		print_buffer(WriteBuffer[i].rxbuf, WriteBuffer[i].length);
//	}


	while(0) {
		if(PS_GPIO_GetPort(PS_KEY)==0){
			usleep(100*1000);
			if(PS_GPIO_GetPort(PS_KEY)==0){
				//test_flash2();
				read_id(8);     //ok
				read_status();  //ok

				se(12);   //ok
				//read_status();
				//polling_qspi_busy();   //ok,33ms
				usleep(200*1000);

				u8 tbuffer[128];
				for(int i=0; i<128; i++){
					tbuffer[i]=i;
				}
				pp(0x02,0x0c0000,&tbuffer[0],128);
				//polling_qspi_busy();  //ok,172us
				//read_status();
				usleep(100*1000);

				read(0x03,0x0c0000, NULL, 128);  //ok,1wire
				read(0x0b,0x0c0000, NULL, 128);  //ok,1wire
				read(0x3b,0x0c0000, NULL, 128);  //ok,2wire
				read(0x6b,0x0c0000, NULL, 128);  //half-ok, 4wire
				read(0xeb,0x0c0000, NULL, 128);  //half-ok, 4wire

//				pp_try_multi(0x32,0x0b0000,&tbuffer[0],128);  //多个burst,也是可以的
//				polling_qspi_busy();
			}
		}
	}


	while(1) {
		if(PS_GPIO_GetPort(PS_KEY)==0){
			usleep(100*1000);
			if(PS_GPIO_GetPort(PS_KEY)==0){
//				xip_for_read_test();
//				usleep(1000);
				xip_dma_read_test();
			}
		}
	}

	return 0;
}

波形

image image

以循环方式执行XIP,波形乱,结果异常。主要是地址都发错了。

image image

以CDMA方式执行XIP,波形正常,结果正常。

image image 但是这个结果并没有写入ddr中去。

结论:

这个AXI QuadSPI的IP,必须以CDMA的方式运行。但是结果并没有写入ddr中去,原因不详。

补充说明

  • 用xsct执行mrd命令,并不是burst读,而是转换成一个一个的read,因此本质是for read
  • 以下是mrd 0x400c000 128读取qspi flash的波形,可以看的很清楚。因此从qspi读取实际是比较慢的,而mrd命令要求立即返回结果,所以实际读不到值。

image image image

Clone this wiki locally