SPIFFS: Optimized SPI data read/write.
Unaligned read/write from/to SPI data registers is rewritten in assembler to improve performance.
This commit is contained in:
parent
281faa2cac
commit
38cccbd456
2 changed files with 150 additions and 34 deletions
|
@ -45,40 +45,53 @@
|
||||||
* called where it needed and not.
|
* called where it needed and not.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SPI_WRITE_MAX_SIZE 32
|
#define SPI_WRITE_MAX_SIZE 64
|
||||||
#define SPI_READ_MAX_SIZE 32
|
|
||||||
|
// 64 bytes read causes hang
|
||||||
|
// http://bbs.espressif.com/viewtopic.php?f=6&t=2439
|
||||||
|
#define SPI_READ_MAX_SIZE 60
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy unaligned data to 4-byte aligned destination buffer.
|
||||||
|
*
|
||||||
|
* @param words Number of 4-byte words to write.
|
||||||
|
*
|
||||||
|
* @see unaligned_memcpy.S
|
||||||
|
*/
|
||||||
|
void memcpy_unaligned_src(volatile uint32_t *dst, uint8_t *src, uint8_t words);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy 4-byte aligned source data to unaligned destination buffer.
|
||||||
|
*
|
||||||
|
* @param bytes Number of byte to copy to dst.
|
||||||
|
*
|
||||||
|
* @see unaligned_memcpy.S
|
||||||
|
*/
|
||||||
|
void memcpy_unaligned_dst(uint8_t *dst, volatile uint32_t *src, uint8_t bytes);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low level SPI flash write. Write block of data up to 64 bytes.
|
* Low level SPI flash write. Write block of data up to 64 bytes.
|
||||||
*/
|
*/
|
||||||
static inline uint32_t IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr,
|
static inline void IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr,
|
||||||
uint8_t *buf, uint32_t size)
|
uint8_t *buf, uint32_t size)
|
||||||
{
|
{
|
||||||
Wait_SPI_Idle(chip); // wait for previous write to finish
|
|
||||||
|
|
||||||
SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24);
|
|
||||||
|
|
||||||
uint32_t words = size >> 2;
|
uint32_t words = size >> 2;
|
||||||
if (size & 0b11) {
|
if (size & 0b11) {
|
||||||
words++;
|
words++;
|
||||||
}
|
}
|
||||||
uint32_t data = 0;
|
|
||||||
for (uint32_t i = 0; i < (words << 2); i++) {
|
|
||||||
data >>= 8;
|
|
||||||
data |= (uint32_t)buf[i] << 24;
|
|
||||||
if (i & 0b11) {
|
|
||||||
SPI(0).W[i >> 2] = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SPI_write_enable(chip)) {
|
Wait_SPI_Idle(chip); // wait for previous write to finish
|
||||||
return ESP_SPIFFS_FLASH_ERROR;
|
|
||||||
}
|
SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24);
|
||||||
|
|
||||||
|
memcpy_unaligned_src(SPI(0).W, buf, words);
|
||||||
|
|
||||||
|
SPI_write_enable(chip);
|
||||||
|
|
||||||
SPI(0).CMD = SPI_CMD_PP;
|
SPI(0).CMD = SPI_CMD_PP;
|
||||||
while (SPI(0).CMD) {}
|
while (SPI(0).CMD) {}
|
||||||
|
|
||||||
return ESP_SPIFFS_FLASH_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,9 +110,7 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad
|
||||||
}
|
}
|
||||||
|
|
||||||
while (size >= SPI_WRITE_MAX_SIZE) {
|
while (size >= SPI_WRITE_MAX_SIZE) {
|
||||||
if (spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE)) {
|
spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE);
|
||||||
return ESP_SPIFFS_FLASH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
size -= SPI_WRITE_MAX_SIZE;
|
size -= SPI_WRITE_MAX_SIZE;
|
||||||
dest_addr += SPI_WRITE_MAX_SIZE;
|
dest_addr += SPI_WRITE_MAX_SIZE;
|
||||||
|
@ -110,9 +121,7 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi_write_data(flashchip, dest_addr, buf, size)) {
|
spi_write_data(flashchip, dest_addr, buf, size);
|
||||||
return ESP_SPIFFS_FLASH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ESP_SPIFFS_FLASH_OK;
|
return ESP_SPIFFS_FLASH_OK;
|
||||||
}
|
}
|
||||||
|
@ -185,15 +194,10 @@ static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr,
|
||||||
{
|
{
|
||||||
SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24);
|
SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24);
|
||||||
SPI(0).CMD = SPI_CMD_READ;
|
SPI(0).CMD = SPI_CMD_READ;
|
||||||
|
|
||||||
while (SPI(0).CMD) {};
|
while (SPI(0).CMD) {};
|
||||||
uint32_t data = 0;
|
|
||||||
for (uint32_t i = 0; i < size; i++) {
|
memcpy_unaligned_dst(buf, SPI(0).W, size);
|
||||||
if (!(i & 0b11)) {
|
|
||||||
data = SPI(0).W[i>>2];
|
|
||||||
}
|
|
||||||
buf[i] = 0xFF & data;
|
|
||||||
data >>= 8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
112
extras/spiffs/unaligned_memcpy.S
Normal file
112
extras/spiffs/unaligned_memcpy.S
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 sheinz (https://github.com/sheinz)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.section .iram1.text, "x"
|
||||||
|
.literal_position
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy unaligned data to 4-byte aligned buffer.
|
||||||
|
*/
|
||||||
|
.align 4
|
||||||
|
.global memcpy_unaligned_src
|
||||||
|
.type memcpy_unaligned_src, @function
|
||||||
|
memcpy_unaligned_src:
|
||||||
|
/* a2: dst, a3: src, a4: size */
|
||||||
|
ssa8l a3
|
||||||
|
srli a3, a3, 2
|
||||||
|
slli a3, a3, 2
|
||||||
|
beqz a4, u_src_end
|
||||||
|
l32i a6, a3, 0
|
||||||
|
u_src_loop:
|
||||||
|
l32i a7, a3, 4
|
||||||
|
src a8, a7, a6
|
||||||
|
memw
|
||||||
|
s32i a8, a2, 0
|
||||||
|
mov a6, a7
|
||||||
|
addi a3, a3, 4
|
||||||
|
addi a2, a2, 4
|
||||||
|
addi a4, a4, -1
|
||||||
|
bnez a4, u_src_loop
|
||||||
|
u_src_end:
|
||||||
|
movi a2, 0
|
||||||
|
ret.n
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy data from 4-byte aligned source to unaligned destination buffer.
|
||||||
|
*/
|
||||||
|
.align 4
|
||||||
|
.global memcpy_unaligned_dst
|
||||||
|
.type memcpy_unaligned_dst, @function
|
||||||
|
memcpy_unaligned_dst:
|
||||||
|
/* a2: dst, a3: src, a4: size */
|
||||||
|
beqz.n a4, u_dst_end
|
||||||
|
extui a5, a4, 0, 2
|
||||||
|
beqz.n a5, aligned_dst_loop
|
||||||
|
u_dst_loop:
|
||||||
|
/* Load data word */
|
||||||
|
memw
|
||||||
|
l32i.n a5, a3, 0
|
||||||
|
|
||||||
|
/* Save byte number 0 */
|
||||||
|
s8i a5, a2, 0
|
||||||
|
addi.n a4, a4, -1
|
||||||
|
beqz a4, u_dst_end
|
||||||
|
addi.n a2, a2, 1
|
||||||
|
|
||||||
|
/* Shift and save byte number 1 */
|
||||||
|
srli a5, a5, 8
|
||||||
|
s8i a5, a2, 0
|
||||||
|
addi.n a4, a4, -1
|
||||||
|
beqz a4, u_dst_end
|
||||||
|
addi.n a2, a2, 1
|
||||||
|
|
||||||
|
/* Shift and save byte number 2 */
|
||||||
|
srli a5, a5, 8
|
||||||
|
s8i a5, a2, 0
|
||||||
|
addi.n a4, a4, -1
|
||||||
|
beqz a4, u_dst_end
|
||||||
|
addi.n a2, a2, 1
|
||||||
|
|
||||||
|
/* Shift and save byte number 3 */
|
||||||
|
srli a5, a5, 8
|
||||||
|
s8i a5, a2, 0
|
||||||
|
addi.n a4, a4, -1
|
||||||
|
addi.n a2, a2, 1
|
||||||
|
|
||||||
|
/* Next word */
|
||||||
|
addi.n a3, a3, 4
|
||||||
|
bnez.n a4, u_dst_loop
|
||||||
|
ret.n
|
||||||
|
aligned_dst_loop:
|
||||||
|
memw
|
||||||
|
l32i a5, a3, 0
|
||||||
|
s32i a5, a2, 0
|
||||||
|
addi.n a3, a3, 4
|
||||||
|
addi.n a2, a2, 4
|
||||||
|
addi.n a4, a4, -4
|
||||||
|
bnez.n a4, aligned_dst_loop
|
||||||
|
u_dst_end: ret.n
|
||||||
|
|
Loading…
Reference in a new issue