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:
Swift Tian 2024-04-26 16:05:21 +08:00 committed by Anas Nashif
parent 2b103137ab
commit 5dfdf2bc05
7 changed files with 449 additions and 0 deletions

View 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()

View 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!

View 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

View 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>;
};
};
};

View File

@ -0,0 +1,3 @@
CONFIG_STDOUT_CONSOLE=y
CONFIG_FLASH=y
CONFIG_MSPI=y

View 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

View 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;
}