samples: mspi: Add a mspi flash sample
The sample code is copied from spi_flash. To flash API, there is no difference if the bus is spi or mspi. Signed-off-by: Swift Tian <swift.tian@ambiq.com>
This commit is contained in:
parent
2b103137ab
commit
5dfdf2bc05
13
samples/drivers/mspi/mspi_flash/CMakeLists.txt
Normal file
13
samples/drivers/mspi/mspi_flash/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(mspi_flash)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
||||
|
||||
if(CONFIG_FLASH_MSPI_ATXP032)
|
||||
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/include/zephyr/drivers)
|
||||
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/drivers/mspi)
|
||||
endif()
|
||||
44
samples/drivers/mspi/mspi_flash/README.rst
Normal file
44
samples/drivers/mspi/mspi_flash/README.rst
Normal file
@ -0,0 +1,44 @@
|
||||
.. zephyr:code-sample:: mspi-nor
|
||||
:name: JEDEC MSPI-NOR flash
|
||||
:relevant-api: flash_interface
|
||||
|
||||
Use the flash API to interact with a MSPI NOR serial flash memory device.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample demonstrates using the :ref:`flash API <flash_api>` on a MSPI NOR serial flash
|
||||
memory device. While trivial it is an example of direct access and
|
||||
allows confirmation that the flash is working and that automatic power
|
||||
savings is correctly implemented.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The application will build only for a target that has a :ref:`devicetree <dt-guide>`
|
||||
``flash0`` alias that refers to an entry with the following bindings as a compatible:
|
||||
|
||||
* :dtcompatible:`jedec,spi-nor`, `ambiq,mspi-device`
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/drivers/mspi/mspi_flash
|
||||
:board: apollo3p_evb
|
||||
:goals: build flash
|
||||
:compact:
|
||||
|
||||
Sample Output
|
||||
=============
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
*** Booting Zephyr OS build zephyr-v2.3.0-2142-gca01d2e1d748 ***
|
||||
|
||||
JEDEC MSPI-NOR flash testing
|
||||
==========================
|
||||
|
||||
Test 1: Flash erase
|
||||
Flash erase succeeded!
|
||||
|
||||
Test 2: Flash write
|
||||
Attempting to write 4 bytes
|
||||
Data read matches data written. Good!
|
||||
9
samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.conf
Normal file
9
samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.conf
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2024 Ambiq Micro Inc. <www.ambiq.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CONFIG_FLASH_MSPI_ATXP032=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=0
|
||||
CONFIG_MSPI_INIT_PRIORITY=40
|
||||
CONFIG_FLASH_INIT_PRIORITY=50
|
||||
CONFIG_PM_DEVICE=y
|
||||
171
samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.overlay
Normal file
171
samples/drivers/mspi/mspi_flash/boards/apollo3p_evb.overlay
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Ambiq Micro Inc. <www.ambiq.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
flash0 = &atxp032;
|
||||
};
|
||||
};
|
||||
|
||||
&gpio64_95 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio32_63 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&mspi1 {
|
||||
|
||||
pinctrl-0 = <&mspi1_default>;
|
||||
pinctrl-1 = <&mspi1_sleep>;
|
||||
pinctrl-2 = <&mspi1_psram>;
|
||||
pinctrl-3 = <&mspi1_flash>;
|
||||
pinctrl-names = "default","sleep","psram","flash";
|
||||
status = "okay";
|
||||
|
||||
ce-gpios = <&gpio64_95 5 GPIO_ACTIVE_LOW>,
|
||||
<&gpio32_63 18 GPIO_ACTIVE_LOW>;
|
||||
|
||||
cmdq-buffer-location = ".mspi_buff";
|
||||
cmdq-buffer-size = <256>;
|
||||
|
||||
aps6404l: aps6404l@0 {
|
||||
compatible = "mspi-aps6404l";
|
||||
size = <DT_SIZE_M(64)>;
|
||||
reg = <0>;
|
||||
status = "disabled";
|
||||
mspi-max-frequency = <48000000>;
|
||||
mspi-io-mode = "MSPI_IO_MODE_QUAD";
|
||||
mspi-data-rate = "MSPI_DATA_RATE_SINGLE";
|
||||
mspi-hardware-ce-num = <0>;
|
||||
read-command = <0xEB>;
|
||||
write-command = <0x38>;
|
||||
command-length = "INSTR_1_BYTE";
|
||||
address-length = "ADDR_3_BYTE";
|
||||
rx-dummy = <6>;
|
||||
tx-dummy = <0>;
|
||||
xip-config = <1 0 0 0>;
|
||||
ce-break-config = <1024 3>;
|
||||
ambiq,timing-config-mask = <3>;
|
||||
ambiq,timing-config = <0 6 0 0 0 0 0 0>;
|
||||
};
|
||||
|
||||
atxp032: atxp032@1 {
|
||||
compatible = "mspi-atxp032";
|
||||
size = <DT_SIZE_M(32)>;
|
||||
reg = <1>;
|
||||
status = "okay";
|
||||
mspi-max-frequency = <48000000>;
|
||||
mspi-io-mode = "MSPI_IO_MODE_OCTAL";
|
||||
mspi-data-rate = "MSPI_DATA_RATE_SINGLE";
|
||||
mspi-hardware-ce-num = <0>;
|
||||
read-command = <0x0B>;
|
||||
write-command = <0x02>;
|
||||
command-length = "INSTR_1_BYTE";
|
||||
address-length = "ADDR_4_BYTE";
|
||||
rx-dummy = <8>;
|
||||
tx-dummy = <0>;
|
||||
xip-config = <1 0 0 0>;
|
||||
ce-break-config = <0 0>;
|
||||
ambiq,timing-config-mask = <3>;
|
||||
ambiq,timing-config = <0 8 0 0 0 0 0 0>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
|
||||
mspi1_sleep: mspi1_sleep{
|
||||
|
||||
group1 {
|
||||
pinmux = <GPIO_P51>,
|
||||
<GPIO_P52>,
|
||||
<GPIO_P53>,
|
||||
<GPIO_P54>,
|
||||
<GPIO_P55>,
|
||||
<GPIO_P56>,
|
||||
<GPIO_P57>,
|
||||
<GPIO_P58>,
|
||||
<GPIO_P59>,
|
||||
<GPIO_P69>,
|
||||
<GPIO_P50>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
mspi1_psram: mspi1_psram{
|
||||
|
||||
group1 {
|
||||
pinmux = <MSPI1_0_P51>,
|
||||
<MSPI1_1_P52>,
|
||||
<MSPI1_2_P53>,
|
||||
<MSPI1_3_P54>,
|
||||
<MSPI1_4_P55>,
|
||||
<MSPI1_5_P56>,
|
||||
<MSPI1_6_P57>,
|
||||
<MSPI1_7_P58>;
|
||||
drive-strength = "0.75";
|
||||
ambiq,iom-mspi = <0>;
|
||||
ambiq,iom-num = <1>;
|
||||
};
|
||||
|
||||
group2 {
|
||||
pinmux = <MSPI1_8_P59>;
|
||||
drive-strength = "0.75";
|
||||
ambiq,iom-mspi = <0>;
|
||||
ambiq,iom-num = <2>;
|
||||
};
|
||||
|
||||
group3 {
|
||||
pinmux = <NCE69_P69>;
|
||||
drive-strength = "1.0";
|
||||
ambiq,iom-mspi = <0>;
|
||||
ambiq,iom-num = <1>;
|
||||
};
|
||||
|
||||
group4 {
|
||||
pinmux = <GPIO_P50>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
mspi1_flash: mspi1_flash{
|
||||
|
||||
group1 {
|
||||
pinmux = <MSPI1_0_P51>,
|
||||
<MSPI1_1_P52>,
|
||||
<MSPI1_2_P53>,
|
||||
<MSPI1_3_P54>,
|
||||
<MSPI1_4_P55>,
|
||||
<MSPI1_5_P56>,
|
||||
<MSPI1_6_P57>,
|
||||
<MSPI1_7_P58>;
|
||||
drive-strength = "0.75";
|
||||
ambiq,iom-mspi = <0>;
|
||||
ambiq,iom-num = <1>;
|
||||
};
|
||||
|
||||
group2 {
|
||||
pinmux = <MSPI1_8_P59>;
|
||||
drive-strength = "0.75";
|
||||
ambiq,iom-mspi = <0>;
|
||||
ambiq,iom-num = <2>;
|
||||
};
|
||||
|
||||
group3 {
|
||||
pinmux = <NCE50_P50>;
|
||||
drive-strength = "1.0";
|
||||
ambiq,iom-mspi = <0>;
|
||||
ambiq,iom-num = <1>;
|
||||
};
|
||||
|
||||
group4 {
|
||||
pinmux = <GPIO_P69>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
3
samples/drivers/mspi/mspi_flash/prj.conf
Normal file
3
samples/drivers/mspi/mspi_flash/prj.conf
Normal file
@ -0,0 +1,3 @@
|
||||
CONFIG_STDOUT_CONSOLE=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_MSPI=y
|
||||
20
samples/drivers/mspi/mspi_flash/sample.yaml
Normal file
20
samples/drivers/mspi/mspi_flash/sample.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
sample:
|
||||
name: MSPI Flash Sample
|
||||
tests:
|
||||
sample.drivers.mspi.flash:
|
||||
tags:
|
||||
- mspi
|
||||
- flash
|
||||
filter: dt_compat_enabled("jedec,spi-nor") or dt_compat_enabled("mspi-atxp032")
|
||||
platform_exclude: hifive_unmatched
|
||||
harness: console
|
||||
harness_config:
|
||||
type: multi_line
|
||||
ordered: true
|
||||
regex:
|
||||
- "Test 1: Flash erase"
|
||||
- "Flash erase succeeded!"
|
||||
- "Test 2: Flash write"
|
||||
- "Attempting to write 4 bytes"
|
||||
- "Data read matches data written. Good!!"
|
||||
depends_on: mspi
|
||||
189
samples/drivers/mspi/mspi_flash/src/main.c
Normal file
189
samples/drivers/mspi/mspi_flash/src/main.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Ambiq Micro Inc. <www.ambiq.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SPI_FLASH_TEST_REGION_OFFSET 0xff000
|
||||
|
||||
#define SPI_FLASH_SECTOR_SIZE 4096
|
||||
|
||||
#define SPI_FLASH_MULTI_SECTOR_TEST
|
||||
|
||||
int single_sector_test(const struct device *flash_dev)
|
||||
{
|
||||
const uint8_t expected[] = { 0x55, 0xaa, 0x66, 0x99 };
|
||||
const size_t len = sizeof(expected);
|
||||
uint8_t buf[sizeof(expected)];
|
||||
int rc;
|
||||
|
||||
printf("\nPerform test on single sector");
|
||||
/* Write protection needs to be disabled before each write or
|
||||
* erase, since the flash component turns on write protection
|
||||
* automatically after completion of write and erase
|
||||
* operations.
|
||||
*/
|
||||
printf("\nTest 1: Flash erase\n");
|
||||
|
||||
/* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and
|
||||
* SPI_FLASH_SECTOR_SIZE = flash size
|
||||
*/
|
||||
rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET,
|
||||
SPI_FLASH_SECTOR_SIZE);
|
||||
if (rc != 0) {
|
||||
printf("Flash erase failed! %d\n", rc);
|
||||
} else {
|
||||
printf("Flash erase succeeded!\n");
|
||||
}
|
||||
|
||||
printf("\nTest 2: Flash write\n");
|
||||
|
||||
printf("Attempting to write %zu bytes\n", len);
|
||||
rc = flash_write(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, expected, len);
|
||||
if (rc != 0) {
|
||||
printf("Flash write failed! %d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0, len);
|
||||
rc = flash_read(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, buf, len);
|
||||
if (rc != 0) {
|
||||
printf("Flash read failed! %d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(expected, buf, len) == 0) {
|
||||
printf("Data read matches data written. Good!!\n");
|
||||
} else {
|
||||
const uint8_t *wp = expected;
|
||||
const uint8_t *rp = buf;
|
||||
const uint8_t *rpe = rp + len;
|
||||
|
||||
printf("Data read does not match data written!!\n");
|
||||
while (rp < rpe) {
|
||||
printf("%08x wrote %02x read %02x %s\n",
|
||||
(uint32_t)(SPI_FLASH_TEST_REGION_OFFSET + (rp - buf)),
|
||||
*wp, *rp, (*rp == *wp) ? "match" : "MISMATCH");
|
||||
++rp;
|
||||
++wp;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined SPI_FLASH_MULTI_SECTOR_TEST
|
||||
int multi_sector_test(const struct device *flash_dev)
|
||||
{
|
||||
const uint8_t expected[] = { 0x55, 0xaa, 0x66, 0x99 };
|
||||
const size_t len = sizeof(expected);
|
||||
uint8_t buf[sizeof(expected)];
|
||||
int rc;
|
||||
|
||||
printf("\nPerform test on multiple consequtive sectors");
|
||||
|
||||
/* Write protection needs to be disabled before each write or
|
||||
* erase, since the flash component turns on write protection
|
||||
* automatically after completion of write and erase
|
||||
* operations.
|
||||
*/
|
||||
printf("\nTest 1: Flash erase\n");
|
||||
|
||||
/* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and
|
||||
* SPI_FLASH_SECTOR_SIZE = flash size
|
||||
* Erase 2 sectors for check for erase of consequtive sectors
|
||||
*/
|
||||
rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET, SPI_FLASH_SECTOR_SIZE * 2);
|
||||
if (rc != 0) {
|
||||
printf("Flash erase failed! %d\n", rc);
|
||||
} else {
|
||||
/* Read the content and check for erased */
|
||||
memset(buf, 0, len);
|
||||
size_t offs = SPI_FLASH_TEST_REGION_OFFSET;
|
||||
|
||||
while (offs < SPI_FLASH_TEST_REGION_OFFSET + 2 * SPI_FLASH_SECTOR_SIZE) {
|
||||
rc = flash_read(flash_dev, offs, buf, len);
|
||||
if (rc != 0) {
|
||||
printf("Flash read failed! %d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
if (buf[0] != 0xff) {
|
||||
printf("Flash erase failed at offset 0x%x got 0x%x\n",
|
||||
offs, buf[0]);
|
||||
return 1;
|
||||
}
|
||||
offs += SPI_FLASH_SECTOR_SIZE;
|
||||
}
|
||||
printf("Flash erase succeeded!\n");
|
||||
}
|
||||
|
||||
printf("\nTest 2: Flash write\n");
|
||||
|
||||
size_t offs = SPI_FLASH_TEST_REGION_OFFSET;
|
||||
|
||||
while (offs < SPI_FLASH_TEST_REGION_OFFSET + 2 * SPI_FLASH_SECTOR_SIZE) {
|
||||
printf("Attempting to write %zu bytes at offset 0x%x\n", len, offs);
|
||||
rc = flash_write(flash_dev, offs, expected, len);
|
||||
if (rc != 0) {
|
||||
printf("Flash write failed! %d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0, len);
|
||||
rc = flash_read(flash_dev, offs, buf, len);
|
||||
if (rc != 0) {
|
||||
printf("Flash read failed! %d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(expected, buf, len) == 0) {
|
||||
printf("Data read matches data written. Good!!\n");
|
||||
} else {
|
||||
const uint8_t *wp = expected;
|
||||
const uint8_t *rp = buf;
|
||||
const uint8_t *rpe = rp + len;
|
||||
|
||||
printf("Data read does not match data written!!\n");
|
||||
while (rp < rpe) {
|
||||
printf("%08x wrote %02x read %02x %s\n",
|
||||
(uint32_t)(offs + (rp - buf)),
|
||||
*wp, *rp, (*rp == *wp) ? "match" : "MISMATCH");
|
||||
++rp;
|
||||
++wp;
|
||||
}
|
||||
}
|
||||
offs += SPI_FLASH_SECTOR_SIZE;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct device *flash_dev = DEVICE_DT_GET(DT_ALIAS(flash0));
|
||||
|
||||
if (!device_is_ready(flash_dev)) {
|
||||
printk("%s: device not ready.\n", flash_dev->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\n%s SPI flash testing\n", flash_dev->name);
|
||||
printf("==========================\n");
|
||||
|
||||
if (single_sector_test(flash_dev)) {
|
||||
return 1;
|
||||
}
|
||||
#if defined SPI_FLASH_MULTI_SECTOR_TEST
|
||||
if (multi_sector_test(flash_dev)) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
printf("==========================\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user