can we put the weights in xip spi flash



  • the ram is scarce and precious,i note some where k210 has xip feature on some spi port.can we put the weights in flash(no need to move by code?) and make more room to put middle result?



  • it should use the blow function in lib , not one in the attachment
    enum w25qxx_status_t w25qxx_init(uint8_t spi_index)
    so it run!!
    but it seem to be big endian???
    u16 test:u16_test_ptr = 0x54400400

    0x22,0x11,0x44,0x33,0x66,0x55,0x88,0x77,0xaa,0x99,0xcc,0xbb,0xee,0xdd,0x00,0xff,
    0x2211,0x4433,0x6655,0x8877,0xaa99,0xccbb,0xeedd,0x00ff,
    same data,the first line is interpret as byte,
    the send interpret as short ,got wrong????



  • when run to main.c line 86 got core dump,the run code is load on boot stage ?
    get /home/meter/k210/kendryte-standalone-sdk-0.5.2/src/flash_xip/main.c 181
    core dump: misaligned load
    Cause 0x0000000000000004, EPC 0x0000000080001088
    reg00 = 0x0000000000000000, reg01 = 0x0000000080001088

    i rewrite the test addr to start from 1MB,wtf?

    print_here();

    w25qxx_read_data(TEST_OFF + 0, u8_test_buf, 128, W25QXX_STANDARD);
    print_here();
    w25qxx_read_data(TEST_OFF + 1024, u16_test_buf, 128, W25QXX_STANDARD);


  • do it slow down the apu speed as the extern ram is slower than inner ram?


  • Staff

    Yes.

    Use function w25qxx_enable_xip_mode();

    File: w25qxx.c

    #include "w25qxx.h"
    #include "fpioa.h"
    #include "spi.h"
    #include "sysctl.h"
    #include "dmac.h"
    
    /* clang-format off */
    #define SPI_SLAVE_SELECT			(0x01)
    
    #define w25qxx_FLASH_PAGE_SIZE			256
    #define w25qxx_FLASH_SECTOR_SIZE		4096
    #define w25qxx_FLASH_PAGE_NUM_PER_SECTOR	16
    #define w25qxx_FLASH_CHIP_SIZE			(16777216 UL)
    
    #define WRITE_ENABLE				0x06
    #define WRITE_DISABLE				0x04
    #define READ_REG1				0x05
    #define READ_REG2				0x35
    #define READ_REG3				0x15
    #define WRITE_REG1				0x01
    #define WRITE_REG2				0x31
    #define WRITE_REG3				0x11
    #define READ_DATA				0x03
    #define FAST_READ				0x0B
    #define FAST_READ_DUAL_OUTPUT			0x3B
    #define FAST_READ_QUAL_OUTPUT			0x6B
    #define FAST_READ_DUAL_IO			0xBB
    #define FAST_READ_QUAL_IO			0xEB
    #define DUAL_READ_RESET				0xFFFF
    #define QUAL_READ_RESET				0xFF
    #define PAGE_PROGRAM				0x02
    #define QUAD_PAGE_PROGRAM			0x32
    #define SECTOR_ERASE				0x20
    #define BLOCK_32K_ERASE				0x52
    #define BLOCK_64K_ERASE				0xD8
    #define CHIP_ERASE				0x60
    #define READ_ID					0x90
    #define ENABLE_QPI				0x38
    #define EXIT_QPI				0xFF
    #define ENABLE_RESET				0x66
    #define RESET_DEVICE				0x99
    
    #define REG1_BUSY_MASK				0x01
    #define REG2_QUAL_MASK				0x02
    /* clang-format on */
    
    enum w25qxx_status_t (*w25qxx_page_program_fun)(uint32_t addr, uint8_t *data_buf, uint32_t length);
    enum w25qxx_status_t (*w25qxx_read_fun)(uint32_t addr, uint8_t *data_buf, uint32_t length);
    
    static enum w25qxx_status_t w25qxx_stand_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length);
    static enum w25qxx_status_t w25qxx_quad_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length);
    static enum w25qxx_status_t w25qxx_page_program(uint32_t addr, uint8_t *data_buf, uint32_t length);
    static enum w25qxx_status_t w25qxx_quad_page_program(uint32_t addr, uint8_t *data_buf, uint32_t length);
    
    static volatile spi_t *spi_handle;
    static uint8_t dfs_offset, tmod_offset, frf_offset, dma_tx_line, dma_rx_line;
    
    static enum w25qxx_status_t w25qxx_receive_data(uint8_t *cmd_buff, uint8_t cmd_len, uint8_t *rx_buff, uint32_t rx_len)
    {
    	uint32_t index, fifo_len;
    
    	spi_handle->ctrlr0 = (0x07 << dfs_offset) | (0x03 << tmod_offset);
    	spi_handle->ctrlr1 = rx_len - 1;
    	spi_handle->ssienr = 0x01;
    	while (cmd_len--)
    		spi_handle->dr[0] = *cmd_buff++;
    	spi_handle->ser = SPI_SLAVE_SELECT;
    	while (rx_len) {
    		fifo_len = spi_handle->rxflr;
    		fifo_len = fifo_len < rx_len ? fifo_len : rx_len;
    		for (index = 0; index < fifo_len; index++)
    			*rx_buff++ = spi_handle->dr[0];
    		rx_len -= fifo_len;
    	}
    	spi_handle->ser = 0x00;
    	spi_handle->ssienr = 0x00;
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_send_data(uint8_t *cmd_buff, uint8_t cmd_len, uint8_t *tx_buff, uint32_t tx_len)
    {
    	uint32_t index, fifo_len;
    
    	spi_handle->ctrlr0 = (0x07 << dfs_offset) | (0x01 << tmod_offset);
    	spi_handle->ssienr = 0x01;
    	while (cmd_len--)
    		spi_handle->dr[0] = *cmd_buff++;
    	fifo_len = 32 - spi_handle->txflr;
    	fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
    	for (index = 0; index < fifo_len; index++)
    		spi_handle->dr[0] = *tx_buff++;
    	tx_len -= fifo_len;
    	spi_handle->ser = SPI_SLAVE_SELECT;
    	while (tx_len) {
    		fifo_len = 32 - spi_handle->txflr;
    		fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
    		for (index = 0; index < fifo_len; index++)
    			spi_handle->dr[0] = *tx_buff++;
    		tx_len -= fifo_len;
    	}
    	while ((spi_handle->sr & 0x05) != 0x04)
    		;
    	spi_handle->ser = 0x00;
    	spi_handle->ssienr = 0x00;
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_receive_data_enhanced(uint32_t *cmd_buff, uint8_t cmd_len, uint8_t *rx_buff, uint32_t rx_len)
    {
    	uint32_t index, fifo_len;
    
    	spi_handle->ctrlr1 = rx_len - 1;
    	spi_handle->ssienr = 0x01;
    	while (cmd_len--)
    		spi_handle->dr[0] = *cmd_buff++;
    	spi_handle->ser = SPI_SLAVE_SELECT;
    	while (rx_len) {
    		fifo_len = spi_handle->rxflr;
    		fifo_len = fifo_len < rx_len ? fifo_len : rx_len;
    		for (index = 0; index < fifo_len; index++)
    			*rx_buff++ = spi_handle->dr[0];
    		rx_len -= fifo_len;
    	}
    	spi_handle->ser = 0x00;
    	spi_handle->ssienr = 0x00;
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_send_data_enhanced(uint32_t *cmd_buff, uint8_t cmd_len, uint8_t *tx_buff, uint32_t tx_len)
    {
    	uint32_t index, fifo_len;
    
    	spi_handle->ssienr = 0x01;
    	while (cmd_len--)
    		spi_handle->dr[0] = *cmd_buff++;
    	fifo_len = 32 - spi_handle->txflr;
    	fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
    	for (index = 0; index < fifo_len; index++)
    		spi_handle->dr[0] = *tx_buff++;
    	tx_len -= fifo_len;
    	spi_handle->ser = SPI_SLAVE_SELECT;
    	while (tx_len) {
    		fifo_len = 32 - spi_handle->txflr;
    		fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
    		for (index = 0; index < fifo_len; index++)
    			spi_handle->dr[0] = *tx_buff++;
    		tx_len -= fifo_len;
    	}
    	while ((spi_handle->sr & 0x05) != 0x04)
    		;
    	spi_handle->ser = 0x00;
    	spi_handle->ssienr = 0x00;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_init(uint8_t spi_index)
    {
    	sysctl_clock_enable(SYSCTL_CLOCK_FPIOA);
    	if (spi_index == 0) {
    		sysctl_reset(SYSCTL_RESET_SPI0);
    		sysctl_clock_enable(SYSCTL_CLOCK_SPI0);
    		sysctl_clock_set_threshold(SYSCTL_THRESHOLD_SPI0, 0);
    		fpioa_set_function(27, FUNC_SPI0_SS0);
    		fpioa_set_function(24, FUNC_SPI0_SCLK);
    		fpioa_set_function(26, FUNC_SPI0_D0);
    		fpioa_set_function(25, FUNC_SPI0_D1);
    		fpioa_set_function(23, FUNC_SPI0_D2);
    		fpioa_set_function(22, FUNC_SPI0_D3);
    		// fpioa_set_function(8, FUNC_SPI0_SS0);
    		// fpioa_set_function(9, FUNC_SPI0_SCLK);
    		// fpioa_set_function(10, FUNC_SPI0_D0);
    		// fpioa_set_function(11, FUNC_SPI0_D1);
    		// fpioa_set_function(12, FUNC_SPI0_D2);
    		// fpioa_set_function(13, FUNC_SPI0_D3);
    		spi_handle = spi[0];
    		dfs_offset = 16;
    		tmod_offset = 8;
    		frf_offset = 21;
    		dma_rx_line = 0;
    		dma_tx_line = 1;
    	} else if (spi_index == 1) {
    		sysctl_reset(SYSCTL_RESET_SPI1);
    		sysctl_clock_enable(SYSCTL_CLOCK_SPI1);
    		sysctl_clock_set_threshold(SYSCTL_THRESHOLD_SPI1, 0);
    		fpioa_set_function(27, FUNC_SPI1_SS0);
    		fpioa_set_function(24, FUNC_SPI1_SCLK);
    		fpioa_set_function(26, FUNC_SPI1_D0);
    		fpioa_set_function(25, FUNC_SPI1_D1);
    		fpioa_set_function(23, FUNC_SPI1_D2);
    		fpioa_set_function(22, FUNC_SPI1_D3);
    		// fpioa_set_function(8, FUNC_SPI1_SS0);
    		// fpioa_set_function(9, FUNC_SPI1_SCLK);
    		// fpioa_set_function(10, FUNC_SPI1_D0);
    		// fpioa_set_function(11, FUNC_SPI1_D1);
    		// fpioa_set_function(12, FUNC_SPI1_D2);
    		// fpioa_set_function(13, FUNC_SPI1_D3);
    		spi_handle = spi[1];
    		dfs_offset = 16;
    		tmod_offset = 8;
    		frf_offset = 21;
    		dma_rx_line = 2;
    		dma_tx_line = 3;
    	} else {
    		// sysctl_reset(SYSCTL_RESET_SPI3);
    		sysctl_clock_enable(SYSCTL_CLOCK_SPI3);
    		sysctl_clock_set_threshold(SYSCTL_THRESHOLD_SPI3, 0);
    		sysctl->peri.spi3_xip_en = 0;
    		spi_handle = spi[3];
    		dfs_offset = 0;
    		tmod_offset = 10;
    		frf_offset = 22;
    		dma_rx_line = 6;
    		dma_tx_line = 7;
    	}
    
    	spi_handle->baudr = 0x0A;
    	spi_handle->imr = 0x00;
    	spi_handle->dmatdlr = 0x10;
    	spi_handle->dmardlr = 0x0F;
    	spi_handle->ser = 0x00;
    	spi_handle->ssienr = 0x00;
    
    	w25qxx_page_program_fun = w25qxx_page_program;
    	w25qxx_read_fun = w25qxx_stand_read_data;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_read_id(uint8_t *manuf_id, uint8_t *device_id)
    {
    	uint8_t cmd[4] = {READ_ID, 0x00, 0x00, 0x00};
    	uint8_t data[2];
    
    	w25qxx_receive_data(cmd, 4, data, 2);
    	*manuf_id = data[0];
    	*device_id = data[1];
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_write_enable(void)
    {
    	uint8_t cmd[1] = {WRITE_ENABLE};
    
    	w25qxx_send_data(cmd, 1, 0, 0);
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_write_status_reg(uint8_t reg1_data, uint8_t reg2_data)
    {
    	uint8_t cmd[3] = {WRITE_REG1, reg1_data, reg2_data};
    
    	w25qxx_write_enable();
    	w25qxx_send_data(cmd, 3, 0, 0);
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_read_status_reg1(uint8_t *reg_data)
    {
    	uint8_t cmd[1] = {READ_REG1};
    	uint8_t data[1];
    
    	w25qxx_receive_data(cmd, 1, data, 1);
    	*reg_data = data[0];
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_read_status_reg2(uint8_t *reg_data)
    {
    	uint8_t cmd[1] = {READ_REG2};
    	uint8_t data[1];
    
    	w25qxx_receive_data(cmd, 1, data, 1);
    	*reg_data = data[0];
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_is_busy(void)
    {
    	uint8_t status;
    
    	w25qxx_read_status_reg1(&status);
    	if (status & REG1_BUSY_MASK)
    		return W25QXX_BUSY;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_sector_erase(uint32_t addr)
    {
    	uint8_t cmd[4] = {SECTOR_ERASE};
    
    	cmd[1] = (uint8_t)(addr >> 16);
    	cmd[2] = (uint8_t)(addr >> 8);
    	cmd[3] = (uint8_t)(addr);
    	w25qxx_write_enable();
    	w25qxx_send_data(cmd, 4, 0, 0);
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_32k_block_erase(uint32_t addr)
    {
    	uint8_t cmd[4] = {BLOCK_32K_ERASE};
    
    	cmd[1] = (uint8_t)(addr >> 16);
    	cmd[2] = (uint8_t)(addr >> 8);
    	cmd[3] = (uint8_t)(addr);
    	w25qxx_write_enable();
    	w25qxx_send_data(cmd, 4, 0, 0);
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_64k_block_erase(uint32_t addr)
    {
    	uint8_t cmd[4] = {BLOCK_64K_ERASE};
    
    	cmd[1] = (uint8_t)(addr >> 16);
    	cmd[2] = (uint8_t)(addr >> 8);
    	cmd[3] = (uint8_t)(addr);
    	w25qxx_write_enable();
    	w25qxx_send_data(cmd, 4, 0, 0);
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_chip_erase(void)
    {
    	uint8_t cmd[1] = {CHIP_ERASE};
    
    	w25qxx_write_enable();
    	w25qxx_send_data(cmd, 1, 0, 0);
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_enable_quad_mode(void)
    {
    	uint8_t reg_data;
    
    	w25qxx_read_status_reg2(&reg_data);
    	if (!(reg_data & REG2_QUAL_MASK)) {
    		reg_data |= REG2_QUAL_MASK;
    		w25qxx_write_status_reg(0x00, reg_data);
    	}
    	w25qxx_page_program_fun = w25qxx_quad_page_program;
    	w25qxx_read_fun = w25qxx_quad_read_data;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_disable_quad_mode(void)
    {
    	uint8_t reg_data;
    
    	w25qxx_read_status_reg2(&reg_data);
    	if (reg_data & REG2_QUAL_MASK) {
    		reg_data &= (~REG2_QUAL_MASK);
    		w25qxx_write_status_reg(0x00, reg_data);
    	}
    	w25qxx_page_program_fun = w25qxx_page_program;
    	w25qxx_read_fun = w25qxx_stand_read_data;
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_page_program(uint32_t addr, uint8_t *data_buf, uint32_t length)
    {
    	uint8_t cmd[4] = {PAGE_PROGRAM};
    
    	cmd[1] = (uint8_t)(addr >> 16);
    	cmd[2] = (uint8_t)(addr >> 8);
    	cmd[3] = (uint8_t)(addr);
    	w25qxx_write_enable();
    	w25qxx_send_data(cmd, 4, data_buf, length);
    	while (w25qxx_is_busy() == W25QXX_BUSY)
    		;
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_quad_page_program(uint32_t addr, uint8_t *data_buf, uint32_t length)
    {
    	uint32_t cmd[2];
    
    	cmd[0] = QUAD_PAGE_PROGRAM;
    	cmd[1] = addr;
    	w25qxx_write_enable();
    	spi_handle->ctrlr0 = (0x01 << tmod_offset) | (0x07 << dfs_offset) | (0x02 << frf_offset);
    	spi_handle->spi_ctrlr0 = (0x06 << 2) | (0x02 << 8);
    	w25qxx_send_data_enhanced(cmd, 2, data_buf, length);
    	while (w25qxx_is_busy() == W25QXX_BUSY)
    		;
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_sector_program(uint32_t addr, uint8_t *data_buf)
    {
    	uint8_t index;
    
    	for (index = 0; index < w25qxx_FLASH_PAGE_NUM_PER_SECTOR; index++) {
    		w25qxx_page_program_fun(addr, data_buf, w25qxx_FLASH_PAGE_SIZE);
    		addr += w25qxx_FLASH_PAGE_SIZE;
    		data_buf += w25qxx_FLASH_PAGE_SIZE;
    	}
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_write_data(uint32_t addr, uint8_t *data_buf, uint32_t length)
    {
    	uint32_t sector_addr, sector_offset, sector_remain, write_len, index;
    	uint8_t swap_buf[w25qxx_FLASH_SECTOR_SIZE];
    	uint8_t *pread, *pwrite;
    
    	while (length) {
    		sector_addr = addr & (~(w25qxx_FLASH_SECTOR_SIZE - 1));
    		sector_offset = addr & (w25qxx_FLASH_SECTOR_SIZE - 1);
    		sector_remain = w25qxx_FLASH_SECTOR_SIZE - sector_offset;
    		write_len = length < sector_remain ? length : sector_remain;
    		w25qxx_read_fun(sector_addr, swap_buf, w25qxx_FLASH_SECTOR_SIZE);
    		pread = swap_buf + sector_offset;
    		pwrite = data_buf;
    		for (index = 0; index < write_len; index++) {
    			if ((*pwrite) != ((*pwrite) & (*pread))) {
    				w25qxx_sector_erase(sector_addr);
    				while (w25qxx_is_busy() == W25QXX_BUSY)
    					;
    				break;
    			}
    			pwrite++;
    			pread++;
    		}
    		if (write_len == w25qxx_FLASH_SECTOR_SIZE)
    			w25qxx_sector_program(sector_addr, data_buf);
    		else {
    			pread = swap_buf + sector_offset;
    			pwrite = data_buf;
    			for (index = 0; index < write_len; index++)
    				*pread++ = *pwrite++;
    			w25qxx_sector_program(sector_addr, swap_buf);
    		}
    		length -= write_len;
    		addr += write_len;
    		data_buf += write_len;
    	}
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_write_data_direct(uint32_t addr, uint8_t *data_buf, uint32_t length)
    {
    	uint32_t page_remain, write_len;
    
    	while (length) {
    		page_remain = w25qxx_FLASH_PAGE_SIZE - (addr & (w25qxx_FLASH_PAGE_SIZE - 1));
    		write_len = length < page_remain ? length : page_remain;
    		w25qxx_page_program_fun(addr, data_buf, write_len);
    		length -= write_len;
    		addr += write_len;
    		data_buf += write_len;
    	}
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t _w25qxx_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length, enum w25qxx_read_t mode)
    {
    	uint32_t cmd[2];
    
    	switch (mode) {
    	case W25QXX_STANDARD:
    		*(((uint8_t *)cmd) + 0) = READ_DATA;
    		*(((uint8_t *)cmd) + 1) = (uint8_t)(addr >> 16);
    		*(((uint8_t *)cmd) + 2) = (uint8_t)(addr >> 8);
    		*(((uint8_t *)cmd) + 3) = (uint8_t)(addr >> 0);
    		w25qxx_receive_data((uint8_t *)cmd, 4, data_buf, length);
    		break;
    	case W25QXX_STANDARD_FAST:
    		*(((uint8_t *)cmd) + 0) = FAST_READ;
    		*(((uint8_t *)cmd) + 1) = (uint8_t)(addr >> 16);
    		*(((uint8_t *)cmd) + 2) = (uint8_t)(addr >> 8);
    		*(((uint8_t *)cmd) + 3) = (uint8_t)(addr >> 0);
    		*(((uint8_t *)cmd) + 4) = 0xFF;
    		w25qxx_receive_data((uint8_t *)cmd, 5, data_buf, length);
    		break;
    	case W25QXX_DUAL:
    		cmd[0] = FAST_READ_DUAL_OUTPUT;
    		cmd[1] = addr;
    		spi_handle->ctrlr0 = (0x02 << tmod_offset) | (0x07 << dfs_offset) | (0x01 << frf_offset);
    		spi_handle->spi_ctrlr0 = (0x06 << 2) | (0x02 << 8) | (0x08 << 11);
    		w25qxx_receive_data_enhanced(cmd, 2, data_buf, length);
    		break;
    	case W25QXX_DUAL_FAST:
    		cmd[0] = FAST_READ_DUAL_IO;
    		cmd[1] = addr << 8;
    		spi_handle->ctrlr0 = (0x02 << tmod_offset) | (0x07 << dfs_offset) | (0x01 << frf_offset);
    		spi_handle->spi_ctrlr0 = (0x08 << 2) | (0x02 << 8) | 0x01;
    		w25qxx_receive_data_enhanced(cmd, 2, data_buf, length);
    		break;
    	case W25QXX_QUAD:
    		cmd[0] = FAST_READ_QUAL_OUTPUT;
    		cmd[1] = addr;
    		spi_handle->ctrlr0 = (0x02 << tmod_offset) | (0x07 << dfs_offset) | (0x02 << frf_offset);
    		spi_handle->spi_ctrlr0 = (0x06 << 2) | (0x02 << 8) | (0x08 << 11);
    		w25qxx_receive_data_enhanced(cmd, 2, data_buf, length);
    		break;
    	case W25QXX_QUAD_FAST:
    		cmd[0] = FAST_READ_QUAL_IO;
    		cmd[1] = addr << 8;
    		spi_handle->ctrlr0 = (0x02 << tmod_offset) | (0x07 << dfs_offset) | (0x02 << frf_offset);
    		spi_handle->spi_ctrlr0 = (0x08 << 2) | (0x02 << 8) | (0x04 << 11) | 0x01;
    		w25qxx_receive_data_enhanced(cmd, 2, data_buf, length);
    		break;
    	}
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length, enum w25qxx_read_t mode)
    {
    	uint32_t len;
    
    	while (length) {
    		len = length >= 0x010000 ? 0x010000 : length;
    		_w25qxx_read_data(addr, data_buf, len, mode);
    		addr += len;
    		data_buf += len;
    		length -= len;
    	}
    	return W25QXX_OK;
    }
    
    static enum w25qxx_status_t w25qxx_stand_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length)
    {
    	return w25qxx_read_data(addr, data_buf, length, W25QXX_STANDARD_FAST);
    }
    
    static enum w25qxx_status_t w25qxx_quad_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length)
    {
    	return w25qxx_read_data(addr, data_buf, length, W25QXX_QUAD_FAST);
    }
    
    enum w25qxx_status_t w25qxx_enable_xip_mode(void)
    {
    	if (spi_handle != spi[3])
    		return W25QXX_ERROR;
    
    	spi_handle->xip_ctrl = (0x01 << 29) | (0x02 << 26) | (0x01 << 23) | (0x01 << 22) | (0x04 << 13) |
    			   (0x01 << 12) | (0x02 << 9) | (0x06 << 4) | (0x01 << 2) | 0x02;
    	spi_handle->xip_incr_inst = 0xEB;
    	spi_handle->xip_mode_bits = 0x00;
    	spi_handle->xip_ser = 0x01;
    	spi_handle->ssienr = 0x01;
    	sysctl->peri.spi3_xip_en = 1;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_disable_xip_mode(void)
    {
    	sysctl->peri.spi3_xip_en = 0;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_read_data_dma(uint32_t addr, uint32_t *data_buf, uint32_t length)
    {
    	uint32_t len;
    
    	spi_handle->baudr = 0x02;
    	spi_handle->ctrlr0 = (0x02 << tmod_offset) | (0x1F << dfs_offset) | (0x02 << frf_offset);
    	spi_handle->spi_ctrlr0 = (0x08 << 2) | (0x02 << 8) | (0x04 << 11) | 0x01;
    	dmac->channel[0].sar = (uint64_t)(&spi_handle->dr[0]);
    	dmac->channel[0].ctl = (((uint64_t)1 << 47) | ((uint64_t)15 << 48) | ((uint64_t)1 << 38) | ((uint64_t)15 << 39) | ((uint64_t)3 << 18) | ((uint64_t)3 << 14) | ((uint64_t)2 << 11) | ((uint64_t)2 << 8) | ((uint64_t)1 << 4));
    	dmac->channel[0].cfg = (((uint64_t)1 << 49) | ((uint64_t)2 << 32));
    	// sysctl->misc.dma_sel0 = dma_rx_line;
    	sysctl_dma_select(SYSCTL_DMA_CHANNEL_0, dma_rx_line);
    	dmac->channel[0].intstatus_en = 0xFFFFFFFF;
    	while (length) {
    		len = length >= 0x010000 ? 0x010000 : length;
    		length -= len;
    		spi_handle->dmacr = 0x01;
    		spi_handle->ctrlr1 = len - 1;
    		spi_handle->ssienr = 0x01;
    		spi_handle->dr[0] = FAST_READ_QUAL_IO;
    		spi_handle->dr[0] = addr << 8;
    		addr += (len * 4);
    		if (len > 0x0F) {
    			dmac->channel[0].dar = (uint64_t)data_buf;
    			dmac->channel[0].block_ts = (len & 0xFFFFFFF0) - 1;
    			dmac->channel[0].intclear = 0xFFFFFFFF;
    			dmac->chen = 0x0101;
    			spi_handle->ser = SPI_SLAVE_SELECT;
    			while ((dmac->channel[0].intstatus & 0x02) == 0)
    				;
    			data_buf += (len & 0xFFFFFFF0);
    			len &= 0x0F;
    		} else
    			spi_handle->ser = SPI_SLAVE_SELECT;
    		while ((spi_handle->sr & 0x05) != 0x04)
    			;
    		while (len--)
    			*data_buf++ = spi_handle->dr[0];
    		spi_handle->ser = 0x00;
    		spi_handle->ssienr = 0x00;
    		spi_handle->dmacr = 0x00;
    	}
    	spi_handle->baudr = 0x0A;
    	return W25QXX_OK;
    }
    
    enum w25qxx_status_t w25qxx_write_data_dma(uint32_t addr, uint32_t *data_buf, uint32_t length)
    {
    	uint32_t len;
    
    	spi_handle->baudr = 0x02;
    	dmac->channel[0].dar = (uint64_t)(&spi_handle->dr[0]);
    	dmac->channel[0].ctl = (((uint64_t)1 << 47) | ((uint64_t)15 << 48) | ((uint64_t)1 << 38) | ((uint64_t)15 << 39) | ((uint64_t)3 << 18) | ((uint64_t)3 << 14) | ((uint64_t)2 << 11) | ((uint64_t)2 << 8) | ((uint64_t)1 << 6));
    	dmac->channel[0].cfg = (((uint64_t)1 << 49) | ((uint64_t)1 << 32));
    	// sysctl->misc.dma_sel0 = dma_tx_line;
    	sysctl_dma_select(SYSCTL_DMA_CHANNEL_0, dma_tx_line);
    	dmac->channel[0].intstatus_en = 0xFFFFFFFF;
    	while (length) {
    		len = length >= w25qxx_FLASH_PAGE_SIZE / 4 ? w25qxx_FLASH_PAGE_SIZE / 4 : length;
    		length -= len;
    		w25qxx_write_enable();
    		spi_handle->ctrlr0 = (0x01 << tmod_offset) | (0x1F << dfs_offset) | (0x02 << frf_offset);
    		spi_handle->spi_ctrlr0 = (0x06 << 2) | (0x02 << 8);
    		spi_handle->dmacr = 0x02;
    		spi_handle->ssienr = 0x01;
    		spi_handle->dr[0] = QUAD_PAGE_PROGRAM;
    		spi_handle->dr[0] = addr;
    		dmac->channel[0].sar = (uint64_t)data_buf;
    		dmac->channel[0].block_ts = len - 1;
    		dmac->channel[0].intclear = 0xFFFFFFFF;
    		dmac->chen = 0x0101;
    		addr += (len * 4);
    		data_buf += len;
    		len = len >= 16 ? 16 : len;
    		while (spi_handle->txflr < len)
    			;
    		spi_handle->ser = SPI_SLAVE_SELECT;
    		while ((dmac->channel[0].intstatus & 0x02) == 0)
    			;
    		while ((spi_handle->sr & 0x05) != 0x04)
    			;
    		spi_handle->ser = 0x00;
    		spi_handle->ssienr = 0x00;
    		spi_handle->dmacr = 0x00;
    		while (w25qxx_is_busy() == W25QXX_BUSY)
    			;
    	}
    	spi_handle->baudr = 0x0A;
    	return W25QXX_OK;
    }
    
    

    File: w25qxx.h

    /**
     * @file
     * @brief      winbond w25qxx series driver
     */
    #ifndef _W25QXX_H
    #define _W25QXX_H
    
    #include <stdint.h>
    
    /**
     * @brief      w25qxx operating status enumerate
     */
    enum w25qxx_status_t {
    	W25QXX_OK = 0,
    	W25QXX_BUSY,
    	W25QXX_ERROR,
    };
    
    /**
     * @brief      w25qxx read operating enumerate
     */
    enum w25qxx_read_t {
    	W25QXX_STANDARD = 0,
    	W25QXX_STANDARD_FAST,
    	W25QXX_DUAL,
    	W25QXX_DUAL_FAST,
    	W25QXX_QUAD,
    	W25QXX_QUAD_FAST,
    };
    
    enum w25qxx_status_t w25qxx_init(uint8_t spi_index);
    enum w25qxx_status_t w25qxx_is_busy(void);
    enum w25qxx_status_t w25qxx_chip_erase(void);
    enum w25qxx_status_t w25qxx_enable_quad_mode(void);
    enum w25qxx_status_t w25qxx_disable_quad_mode(void);
    enum w25qxx_status_t w25qxx_sector_erase(uint32_t addr);
    enum w25qxx_status_t w25qxx_32k_block_erase(uint32_t addr);
    enum w25qxx_status_t w25qxx_64k_block_erase(uint32_t addr);
    enum w25qxx_status_t w25qxx_read_status_reg1(uint8_t *reg_data);
    enum w25qxx_status_t w25qxx_read_status_reg2(uint8_t *reg_data);
    enum w25qxx_status_t w25qxx_write_status_reg(uint8_t reg1_data, uint8_t reg2_data);
    enum w25qxx_status_t w25qxx_read_id(uint8_t *manuf_id, uint8_t *device_id);
    enum w25qxx_status_t w25qxx_write_data(uint32_t addr, uint8_t *data_buf, uint32_t length);
    enum w25qxx_status_t w25qxx_write_data_direct(uint32_t addr, uint8_t *data_buf, uint32_t length);
    enum w25qxx_status_t w25qxx_read_data(uint32_t addr, uint8_t *data_buf, uint32_t length, enum w25qxx_read_t mode);
    enum w25qxx_status_t w25qxx_enable_xip_mode(void);
    enum w25qxx_status_t w25qxx_disable_xip_mode(void);
    enum w25qxx_status_t w25qxx_write_data_dma(uint32_t addr, uint32_t *data_buf, uint32_t length);
    enum w25qxx_status_t w25qxx_read_data_dma(uint32_t addr, uint32_t *data_buf, uint32_t length);
    
    #endif
    
    

    File: main.c

    #include <stdio.h>
    #include "bsp.h"
    #include "sysctl.h"
    #include "plic.h"
    #include "utils.h"
    #include "fpioa.h"
    #include "uarths.h"
    #include "w25qxx.h"
    #include "board_config.h"
    
    
    #define PLL0_OUTPUT_FREQ 800000000UL
    
    
    static uint8_t *u8_test_ptr = SPI3_BASE_ADDR;
    static uint8_t u8_test_buf[16] =
    {
        0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00
    };
    
    static uint16_t *u16_test_ptr = SPI3_BASE_ADDR + 1024;
    static uint16_t u16_test_buf[8] =
    {
        0x1122,0x3344,0x5566,0x7788,0x99AA,0xBBCC,0xDDEE,0xFF00
    };
    
    static uint32_t *u32_test_ptr = SPI3_BASE_ADDR + 2048;
    static uint32_t u32_test_buf[4] =
    {
        0x11223344,0x55667788,0x99AABBCC,0xDDEEFF00
    };
    
    static uint64_t *u64_test_ptr = SPI3_BASE_ADDR + 3072;
    static uint64_t u64_test_buf[2] =
    {
        0x1122334455667788,0x99AABBCCDDEEFF00
    };
    
    static void io_mux_init(void)
    {
    }
    
    static void io_set_power(void)
    {
        sysctl_set_power_mode(SYSCTL_POWER_BANK0, POWER_BANK0_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK1, POWER_BANK1_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK2, POWER_BANK2_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK3, POWER_BANK3_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK4, POWER_BANK4_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK5, POWER_BANK5_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK6, POWER_BANK6_SELECT);
        sysctl_set_power_mode(SYSCTL_POWER_BANK7, POWER_BANK7_SELECT);
    }
    
    int main(void)
    {
        /* Set CPU clk */
        sysctl_pll_set_freq(SYSCTL_PLL0, PLL0_OUTPUT_FREQ);
        uarths_init();
        io_set_power();
        io_mux_init();
        plic_init();
        printf("spi master test\n");
    	w25qxx_init(3);
    	w25qxx_enable_quad_mode();
        uint8_t manuf_id, device_id;
        w25qxx_read_id(&manuf_id, &device_id);
    	if (manuf_id != 0xC8 || device_id != 0x17) {
    		printf("manuf_id:0x%02x, device_id:0x%02x\n", manuf_id, device_id);
    		printf("_TEST_FAIL_\n");
    		return 0;
    	}
        /* enable global interrupt */
        sysctl_enable_irq();
        /* system start */
        printf("System start\n");
    
        w25qxx_sector_erase(0);
        while (w25qxx_is_busy() != W25QXX_OK);
        
        w25qxx_write_data_direct(0, u8_test_buf, 128);
        w25qxx_write_data_direct(1024, u16_test_buf, 128);
        w25qxx_write_data_direct(2048, u32_test_buf, 128);
        w25qxx_write_data_direct(3072, u64_test_buf, 128);
        
        w25qxx_read_data(0, u8_test_buf, 128, W25QXX_STANDARD);
        w25qxx_read_data(1024, u16_test_buf, 128, W25QXX_STANDARD);
        w25qxx_read_data(2048, u32_test_buf, 128, W25QXX_STANDARD);
        w25qxx_read_data(3072, u64_test_buf, 128, W25QXX_STANDARD);
        
        printf("\nu8 test:");
        for (uint32_t i = 0; i < 16; i++)
            printf("0x%02x,", u8_test_buf[i]);
        printf("\nu16 test:");
        for (uint32_t i = 0; i < 8; i++)
            printf("0x%04x,", u16_test_buf[i]);
        printf("\nu32 test:");
        for (uint32_t i = 0; i < 4; i++)
            printf("0x%08x,", u32_test_buf[i]);
        printf("\nu64 test:");
        for (uint32_t i = 0; i < 2; i++)
            printf("0x%016lx,", u64_test_buf[i]);
        
        printf("\n\nxip test");
        w25qxx_enable_xip_mode();
        printf("\nu8 test:");
        for (uint32_t i = 0; i < 16; i++)
            printf("0x%02x,", u8_test_ptr[i]);
        printf("\nu16 test:");
        for (uint32_t i = 0; i < 8; i++)
            printf("0x%04x,", u16_test_ptr[i]);
        printf("\nu32 test:");
        for (uint32_t i = 0; i < 4; i++)
            printf("0x%08x,", u32_test_ptr[i]);
        printf("\nu64 test:");
        for (uint32_t i = 0; i < 2; i++)
            printf("0x%016lx,", u64_test_ptr[i]);
        printf("\ntest finish\n");
        w25qxx_disable_xip_mode();
    }