aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/flash_algo.toml11
-rw-r--r--.cargo/imxrt1010.toml12
-rw-r--r--.cargo/imxrt1040.toml12
-rw-r--r--.cargo/imxrt1170.toml12
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock586
-rw-r--r--Cargo.toml72
-rw-r--r--LICENSE201
-rw-r--r--README.md59
-rw-r--r--imxrt1010/Cargo.toml10
-rw-r--r--imxrt1010/src/lib.rs33
-rw-r--r--imxrt1010evk/Cargo.toml20
-rw-r--r--imxrt1010evk/build.rs8
-rw-r--r--imxrt1010evk/examples/smoke.rs36
-rw-r--r--imxrt1010evk/src/lib.rs7
-rw-r--r--imxrt1010evk/src/main.rs25
-rw-r--r--imxrt1040/Cargo.toml10
-rw-r--r--imxrt1040/src/lib.rs50
-rw-r--r--imxrt1040evk/Cargo.toml20
-rw-r--r--imxrt1040evk/build.rs8
-rw-r--r--imxrt1040evk/examples/smoke.rs36
-rw-r--r--imxrt1040evk/src/lib.rs7
-rw-r--r--imxrt1040evk/src/main.rs25
-rw-r--r--imxrt1160/Cargo.toml8
-rw-r--r--imxrt1160/src/lib.rs20
-rw-r--r--imxrt1160evk/Cargo.toml12
-rw-r--r--imxrt1160evk/src/lib.rs6
-rw-r--r--imxrt1160evk/src/main.rs25
-rw-r--r--imxrt1170/Cargo.toml9
-rw-r--r--imxrt1170/src/lib.rs20
-rw-r--r--imxrt1170evk/Cargo.toml20
-rw-r--r--imxrt1170evk/build.rs8
-rw-r--r--imxrt1170evk/examples/smoke.rs35
-rw-r--r--imxrt1170evk/src/lib.rs6
-rw-r--r--imxrt1170evk/src/main.rs25
-rw-r--r--link.x4
-rw-r--r--src/flash/adesto.rs7
-rw-r--r--src/flash/issi.rs101
-rw-r--r--src/flash/winbond.rs92
-rw-r--r--src/imxrt10xx.rs100
-rw-r--r--src/imxrt11xx.rs218
-rw-r--r--src/lib.rs479
-rw-r--r--src/sequences/common.rs82
43 files changed, 2538 insertions, 0 deletions
diff --git a/.cargo/flash_algo.toml b/.cargo/flash_algo.toml
new file mode 100644
index 0000000..51a41a8
--- /dev/null
+++ b/.cargo/flash_algo.toml
@@ -0,0 +1,11 @@
+[build]
+target = "thumbv7em-none-eabi"
+
+[target.'cfg(all(target_os = "none", target_arch = "arm"))']
+rustflags = [
+ "-Clink-arg=-nmagic",
+ "-Clink-arg=-Tlink.x",
+ "-Clink-arg=-Tmemory.x",
+ "-Clink-arg=-Tdefmt.x",
+]
+runner = "target-gen elf --fixed-load-address"
diff --git a/.cargo/imxrt1010.toml b/.cargo/imxrt1010.toml
new file mode 100644
index 0000000..645c968
--- /dev/null
+++ b/.cargo/imxrt1010.toml
@@ -0,0 +1,12 @@
+[build]
+target = "thumbv7em-none-eabihf"
+target-dir = "target/imxrt1010"
+
+[target.thumbv7em-none-eabihf]
+runner = "probe-rs run --chip=mimxrt1010"
+rustflags = [
+ "-Clink-arg=--nmagic",
+ "-Clink-arg=-Timxrt-link.x",
+ "-Clink-arg=-Tdefmt.x",
+ "-Ctarget-cpu=cortex-m7",
+]
diff --git a/.cargo/imxrt1040.toml b/.cargo/imxrt1040.toml
new file mode 100644
index 0000000..2223c2d
--- /dev/null
+++ b/.cargo/imxrt1040.toml
@@ -0,0 +1,12 @@
+[build]
+target = "thumbv7em-none-eabihf"
+target-dir = "target/imxrt1040"
+
+[target.thumbv7em-none-eabihf]
+runner = "probe-rs run --chip=mimxrt1040"
+rustflags = [
+ "-Clink-arg=--nmagic",
+ "-Clink-arg=-Timxrt-link.x",
+ "-Clink-arg=-Tdefmt.x",
+ "-Ctarget-cpu=cortex-m7",
+]
diff --git a/.cargo/imxrt1170.toml b/.cargo/imxrt1170.toml
new file mode 100644
index 0000000..1648dbb
--- /dev/null
+++ b/.cargo/imxrt1170.toml
@@ -0,0 +1,12 @@
+[build]
+target = "thumbv7em-none-eabihf"
+target-dir = "target/imxrt1170"
+
+[target.thumbv7em-none-eabihf]
+runner = "probe-rs run --chip=mimxrt1170"
+rustflags = [
+ "-Clink-arg=--nmagic",
+ "-Clink-arg=-Timxrt-link.x",
+ "-Clink-arg=-Tdefmt.x",
+ "-Ctarget-cpu=cortex-m7",
+]
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..fad3e71
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,586 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "bare-metal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "bitfield"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cortex-m"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
+dependencies = [
+ "bare-metal",
+ "bitfield",
+ "critical-section",
+ "embedded-hal",
+ "volatile-register",
+]
+
+[[package]]
+name = "cortex-m-rt"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
+dependencies = [
+ "cortex-m-rt-macros",
+]
+
+[[package]]
+name = "cortex-m-rt-macros"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
+name = "defmt"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
+dependencies = [
+ "bitflags",
+ "defmt-macros",
+]
+
+[[package]]
+name = "defmt-macros"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
+dependencies = [
+ "defmt-parser",
+ "proc-macro-error2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.111",
+]
+
+[[package]]
+name = "defmt-parser"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "defmt-rtt"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e"
+dependencies = [
+ "critical-section",
+ "defmt",
+]
+
+[[package]]
+name = "embedded-hal"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
+dependencies = [
+ "nb 0.1.3",
+ "void",
+]
+
+[[package]]
+name = "flash-algorithm"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf0040922d40f085f9b4902ed60479bcc93389a59c253f3f715cc42cfc1ff1ee"
+
+[[package]]
+name = "imxrt-drivers-ccm-10xx"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-ccm-11xx"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-dcdc"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-edma"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-enet"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-flexspi"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-gpc-11xx"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-gpio"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-iomuxc-10xx"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-iomuxc-11xx"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-lpspi"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-pit"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-pmu-11xx"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-drivers-rtwdog"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-flash-algos"
+version = "0.1.0"
+dependencies = [
+ "defmt",
+ "flash-algorithm",
+ "imxrt-drivers-ccm-10xx",
+ "imxrt-drivers-ccm-11xx",
+ "imxrt-drivers-dcdc",
+ "imxrt-drivers-flexspi",
+ "imxrt-drivers-gpc-11xx",
+ "imxrt-drivers-pmu-11xx",
+ "imxrt-drivers-rtwdog",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt-rt"
+version = "0.2.0"
+source = "git+https://github.com/imxrt-rs/imxrt-rt#eb47674359ea27cc5e7824a234067491ff01ab13"
+dependencies = [
+ "cfg-if",
+ "cortex-m-rt",
+]
+
+[[package]]
+name = "imxrt1010"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "cortex-m",
+ "imxrt-drivers-ccm-10xx",
+ "imxrt-drivers-dcdc",
+ "imxrt-drivers-flexspi",
+ "imxrt-drivers-gpio",
+ "imxrt-drivers-iomuxc-10xx",
+ "imxrt-drivers-lpspi",
+ "imxrt-drivers-pit",
+ "imxrt-drivers-rtwdog",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt1010-flash-algos"
+version = "0.1.0"
+dependencies = [
+ "imxrt-flash-algos",
+ "imxrt1010",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt1010evk"
+version = "0.1.0"
+dependencies = [
+ "cortex-m",
+ "defmt",
+ "defmt-rtt",
+ "flash-algorithm",
+ "imxrt-rt",
+ "imxrt1010-flash-algos",
+ "panic-probe",
+]
+
+[[package]]
+name = "imxrt1040"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "cortex-m",
+ "imxrt-drivers-ccm-10xx",
+ "imxrt-drivers-dcdc",
+ "imxrt-drivers-edma",
+ "imxrt-drivers-enet",
+ "imxrt-drivers-flexspi",
+ "imxrt-drivers-gpio",
+ "imxrt-drivers-iomuxc-10xx",
+ "imxrt-drivers-lpspi",
+ "imxrt-drivers-pit",
+ "imxrt-drivers-rtwdog",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt1040-flash-algos"
+version = "0.1.0"
+dependencies = [
+ "imxrt-flash-algos",
+ "imxrt1040",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt1040evk"
+version = "0.1.0"
+dependencies = [
+ "cortex-m",
+ "defmt",
+ "defmt-rtt",
+ "flash-algorithm",
+ "imxrt-rt",
+ "imxrt1040-flash-algos",
+ "panic-probe",
+]
+
+[[package]]
+name = "imxrt1160"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "cortex-m",
+ "imxrt-drivers-ccm-11xx",
+ "imxrt-drivers-edma",
+ "imxrt-drivers-enet",
+ "imxrt-drivers-flexspi",
+ "imxrt-drivers-gpc-11xx",
+ "imxrt-drivers-gpio",
+ "imxrt-drivers-iomuxc-11xx",
+ "imxrt-drivers-lpspi",
+ "imxrt-drivers-pit",
+ "imxrt-drivers-pmu-11xx",
+ "imxrt-drivers-rtwdog",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt1160-flash-algos"
+version = "0.1.0"
+dependencies = [
+ "imxrt-flash-algos",
+ "imxrt1160",
+]
+
+[[package]]
+name = "imxrt1160evk"
+version = "0.1.0"
+dependencies = [
+ "cortex-m",
+ "defmt-rtt",
+ "flash-algorithm",
+ "imxrt1160-flash-algos",
+ "panic-probe",
+]
+
+[[package]]
+name = "imxrt1170"
+version = "0.1.0"
+source = "git+https://git.mciantyre.dev/imxrt-drivers#76199f21616ad86cf68f3b063c1ce23c6fc5a52f"
+dependencies = [
+ "cortex-m",
+ "imxrt-drivers-ccm-11xx",
+ "imxrt-drivers-enet",
+ "imxrt-drivers-flexspi",
+ "imxrt-drivers-gpc-11xx",
+ "imxrt-drivers-gpio",
+ "imxrt-drivers-iomuxc-11xx",
+ "imxrt-drivers-lpspi",
+ "imxrt-drivers-pit",
+ "imxrt-drivers-pmu-11xx",
+ "imxrt-drivers-rtwdog",
+ "ral-registers",
+]
+
+[[package]]
+name = "imxrt1170-flash-algos"
+version = "0.1.0"
+dependencies = [
+ "imxrt-flash-algos",
+ "imxrt1170",
+]
+
+[[package]]
+name = "imxrt1170evk"
+version = "0.1.0"
+dependencies = [
+ "cortex-m",
+ "defmt",
+ "defmt-rtt",
+ "flash-algorithm",
+ "imxrt-rt",
+ "imxrt1170-flash-algos",
+ "panic-probe",
+]
+
+[[package]]
+name = "nb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
+dependencies = [
+ "nb 1.1.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+
+[[package]]
+name = "panic-probe"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a"
+dependencies = [
+ "cortex-m",
+ "defmt",
+]
+
+[[package]]
+name = "proc-macro-error-attr2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "proc-macro-error2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
+dependencies = [
+ "proc-macro-error-attr2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.111",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ral-registers"
+version = "0.2.0"
+source = "git+https://git.mciantyre.dev/ral-registers?branch=v0.2#495fbc58b6f4d2e751b16883a2c5cee3c598fd79"
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.111",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "vcell"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "volatile-register"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
+dependencies = [
+ "vcell",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..a961bff
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,72 @@
+[package]
+name = "imxrt-flash-algos"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+defmt = { workspace = true }
+flash-algorithm = { workspace = true }
+imxrt-drivers-flexspi = { workspace = true }
+ral-registers = { workspace = true }
+
+imxrt-drivers-rtwdog = { workspace = true }
+imxrt-drivers-ccm-10xx = { workspace = true }
+imxrt-drivers-ccm-11xx = { workspace = true }
+imxrt-drivers-dcdc = { workspace = true }
+imxrt-drivers-pmu-11xx = { workspace = true }
+imxrt-drivers-gpc-11xx = { workspace = true }
+
+[workspace]
+members = ["imxrt1010", "imxrt1010evk", "imxrt1040", "imxrt1040evk", "imxrt1160", "imxrt1160evk", "imxrt1170", "imxrt1170evk"]
+
+[workspace.dependencies]
+imxrt-drivers-flexspi = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt-drivers-rtwdog = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt-drivers-ccm-10xx = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt-drivers-ccm-11xx = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt-drivers-dcdc = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt-drivers-gpc-11xx = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt-drivers-pmu-11xx = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+
+imxrt1010 = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt1040 = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt1160 = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+imxrt1170 = { git = "https://git.mciantyre.dev/imxrt-drivers" }
+ral-registers = { version = "0.2" }
+
+imxrt-rt = "0.2"
+
+cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
+
+defmt = "1.0.0"
+defmt-rtt = "1.1.0"
+panic-probe = { version = "1.0.0", features = ["print-defmt"] }
+
+flash-algorithm = { version = "0.6.0", default-features = false, features = ["erase-chip", "read-flash"] }
+
+imxrt-flash-algos = { path = "." }
+imxrt1010-flash-algos = { path = "imxrt1010" }
+imxrt1040-flash-algos = { path = "imxrt1040" }
+imxrt1160-flash-algos = { path = "imxrt1160" }
+imxrt1170-flash-algos = { path = "imxrt1170" }
+imxrt1010evk = { path = "imxrt1010evk" }
+imxrt1040evk = { path = "imxrt1040evk" }
+imxrt1160evk = { path = "imxrt1160evk" }
+imxrt1170evk = { path = "imxrt1170evk" }
+
+[patch.crates-io.ral-registers]
+git = "https://git.mciantyre.dev/ral-registers"
+branch = "v0.2"
+
+[patch.crates-io.imxrt-rt]
+git = "https://github.com/imxrt-rs/imxrt-rt"
+
+[profile.dev]
+codegen-units = 1
+overflow-checks = true
+
+[profile.release]
+codegen-units = 1
+opt-level = "s"
+overflow-checks = true
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..20a3adf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,59 @@
+This repository contains [flash programming algorithms][fpa] for i.MX RT MCUs.
+The algorithms are designed for use in [probe-rs]. They're written in Rust and
+support a variety of MCUs and flash parts.
+
+These flash programming algorithms compete with NXP's flash programming
+algorithms, which are distributed in device family packs (DFPs). NXP's
+algorithms also work with probe-rs, so prefer those if you want something
+official.
+
+I've found these algorithms can program flash 2x to 4x faster than NXP's
+offerings. These algorithms definitely support page crossings; meaning, if the
+debug probe wants to program 4KiB into flash, the algorithm will automatically
+chunk it into pages of 256 bytes. This trick reduces I/O overhead between host
+and debug probe. (Admittedly, NXP's algorithms might support this, but I never
+tried. It's fun writing these algorithms!)
+
+Supported boards include
+
+- IMXRT1010EVK
+- IMXRT1040EVK
+- IMXRT1160EVK
+- IMXRT1170EVK
+
+You'll find the algorithms in their respective top-level directories. These
+four boards sport four different MCUs, which you'll also find in the top-level
+directories. Furthermore, those four boards have three different flash parts;
+the flash part implementation is in `src/` as part of the core package.
+
+Before attempting to generate the flash programming algorithms for [probe-rs],
+install the `target-gen` tool. It's available in the probe-rs repository. [My
+fork][probe-rs-fork] has some `target-gen` patches that might be useful.
+
+Here's how to generate the algorithm, represented as YAML, for the 1010EVK:
+
+```
+cargo --config .cargo/flash_algo.toml run --release --package=imxrt1010evk
+```
+
+To generate the algorithm for a different EVK, change the `--package` selection.
+
+Some of the EVKs have a smoke test that checks read, erase, and write with its
+flash part. Keep in mind that this will manipulate your board's flash, and it
+might change something you care about!
+
+To run the smoke test on the 1010EVK, power on your EVK and connect it to your
+host. Then, submit
+
+```
+cargo --config .cargo/imxrt1010.toml run --release --package=imxrt1010evk --example=smoke
+```
+
+to flash the example with probe-rs. If you change the EVK, change the Cargo
+configuration for your MCU as well as your package.
+
+[fpa]: https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/flashAlgorithm.html
+[probe-rs]: https://probe.rs
+[probe-rs-fork]: https://github.com/mciantyre/probe-rs
+
+License: APACHE-2.0
diff --git a/imxrt1010/Cargo.toml b/imxrt1010/Cargo.toml
new file mode 100644
index 0000000..2e5d975
--- /dev/null
+++ b/imxrt1010/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "imxrt1010-flash-algos"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+imxrt-flash-algos = { workspace = true }
+imxrt1010 = { workspace = true }
+ral-registers = { workspace = true }
diff --git a/imxrt1010/src/lib.rs b/imxrt1010/src/lib.rs
new file mode 100644
index 0000000..be48362
--- /dev/null
+++ b/imxrt1010/src/lib.rs
@@ -0,0 +1,33 @@
+//! Interface package for writing flash algorithms for 1010 MCUs.
+#![no_std]
+
+pub use imxrt_flash_algos::*;
+use imxrt1010::{ccm, dcdc, instances};
+
+pub struct Imxrt1010;
+impl imxrt10xx::Imxrt10xx for Imxrt1010 {
+ const FLEXSPI1_INSTANCE: imxrt1010::flexspi::Instance = unsafe { instances::flexspi() };
+
+ const CCM_INSTANCE: ccm::CCM = unsafe { instances::ccm() };
+
+ const CCM_ANALOG_INSTANCE: ccm::CCM_ANALOG = unsafe { instances::ccm_analog() };
+
+ const DCDC_INSTANCE: dcdc::Instance = unsafe { instances::dcdc() };
+
+ const FLEXSPI_FIFO_CAPACITY_BYTES: usize = 128;
+
+ fn configure_clocks(ccm: ccm::CCM, ccm_analog: ccm::CCM_ANALOG, dcdc: dcdc::Instance) {
+ dcdc::set_target_vdd_soc(dcdc, 1250);
+
+ ccm::clock_gate::set(ccm, ccm::gates::FLEXSPI, false.into());
+
+ ccm::pll3::restart(ccm_analog);
+
+ ccm::flexspi1_clk::set_divider(ccm, 4);
+ ccm::flexspi1_clk::set_selection(ccm, ccm::flexspi1_clk::Selection::Pll3);
+
+ ccm::clock_gate::set(ccm, ccm::gates::FLEXSPI, true.into());
+ }
+}
+
+pub type Algorithm<F> = imxrt10xx::Algorithm<Imxrt1010, F>;
diff --git a/imxrt1010evk/Cargo.toml b/imxrt1010evk/Cargo.toml
new file mode 100644
index 0000000..455d16d
--- /dev/null
+++ b/imxrt1010evk/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "imxrt1010evk"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+cortex-m = { workspace = true }
+defmt-rtt = { workspace = true }
+
+imxrt1010-flash-algos = { workspace = true }
+flash-algorithm = { workspace = true }
+panic-probe = { workspace = true }
+
+[build-dependencies]
+imxrt-rt = { workspace = true, features = ["device"] }
+
+[dev-dependencies]
+defmt = { workspace = true }
+imxrt-rt = { workspace = true, features = ["device"] }
diff --git a/imxrt1010evk/build.rs b/imxrt1010evk/build.rs
new file mode 100644
index 0000000..2c747cc
--- /dev/null
+++ b/imxrt1010evk/build.rs
@@ -0,0 +1,8 @@
+use imxrt_rt::Family;
+
+fn main() {
+ imxrt_rt::RuntimeBuilder::from_ram(Family::Imxrt1010)
+ .device_script_name("imxrt1010.x")
+ .build()
+ .unwrap();
+}
diff --git a/imxrt1010evk/examples/smoke.rs b/imxrt1010evk/examples/smoke.rs
new file mode 100644
index 0000000..820d610
--- /dev/null
+++ b/imxrt1010evk/examples/smoke.rs
@@ -0,0 +1,36 @@
+#![no_main]
+#![no_std]
+
+use defmt_rtt as _;
+use imxrt1010evk::{self, Algorithm};
+use panic_probe as _;
+
+const LAST_SECTOR: usize = Algorithm::flash_size_bytes() - Algorithm::sector_size_bytes();
+
+#[imxrt_rt::entry]
+fn main() -> ! {
+ for _ in 0..5 {
+ let mut at25sf128 = Algorithm::initialize();
+
+ let mut sector = [0_u8; Algorithm::sector_size_bytes()];
+ at25sf128.flash_read(0x400, &mut sector[..4]);
+ let tag = u32::from_le_bytes(sector[..4].try_into().unwrap());
+ defmt::assert!(0x42464346 == tag, "{=u32:#010X}", tag);
+
+ at25sf128.flash_erase_sector(LAST_SECTOR + 256);
+
+ sector.fill(0);
+ at25sf128.flash_write(LAST_SECTOR, &sector);
+
+ sector.fill(u8::MAX);
+ at25sf128.flash_read(LAST_SECTOR, &mut sector);
+ defmt::assert!(sector.iter().all(|byte| *byte == 0));
+
+ at25sf128.flash_erase_sector(LAST_SECTOR + 123);
+
+ at25sf128.flash_read(LAST_SECTOR, &mut sector);
+ defmt::assert!(sector.iter().all(|byte| *byte == u8::MAX));
+ }
+ defmt::println!("Pass");
+ loop {}
+}
diff --git a/imxrt1010evk/src/lib.rs b/imxrt1010evk/src/lib.rs
new file mode 100644
index 0000000..5bc0437
--- /dev/null
+++ b/imxrt1010evk/src/lib.rs
@@ -0,0 +1,7 @@
+#![no_std]
+
+use cortex_m as _;
+
+pub use imxrt1010_flash_algos::flash::adesto::At25sf128 as Flash;
+
+pub type Algorithm = imxrt1010_flash_algos::Algorithm<Flash>;
diff --git a/imxrt1010evk/src/main.rs b/imxrt1010evk/src/main.rs
new file mode 100644
index 0000000..c1c715d
--- /dev/null
+++ b/imxrt1010evk/src/main.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt_rtt as _;
+use panic_probe as _;
+
+use imxrt1010evk::Algorithm;
+
+flash_algorithm::algorithm!(Algorithm, {
+ device_name: "imxrt1010_at25sf128_120mhz",
+ device_type: DeviceType::Onchip,
+ flash_address: Algorithm::flash_address() as _,
+ flash_size: Algorithm::flash_size_bytes() as _,
+ // We support page crossings. Suggest a larger page
+ // size so that probe-rs places larger buffers into
+ // RAM per programming operation, reducing overhead.
+ page_size: 4096,
+ empty_value: 0xFF,
+ program_time_out: 1000,
+ erase_time_out: 2000,
+ sectors: [{
+ size: Algorithm::sector_size_bytes() as u32,
+ address: 0x0,
+ }]
+});
diff --git a/imxrt1040/Cargo.toml b/imxrt1040/Cargo.toml
new file mode 100644
index 0000000..43d33bc
--- /dev/null
+++ b/imxrt1040/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "imxrt1040-flash-algos"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+imxrt-flash-algos = { workspace = true }
+imxrt1040 = { workspace = true }
+ral-registers = { workspace = true }
diff --git a/imxrt1040/src/lib.rs b/imxrt1040/src/lib.rs
new file mode 100644
index 0000000..556f18e
--- /dev/null
+++ b/imxrt1040/src/lib.rs
@@ -0,0 +1,50 @@
+//! Interface package for writing flash algorithms for 1040 MCUs.
+#![no_std]
+
+use core::num::NonZero;
+
+pub use imxrt_flash_algos::*;
+use imxrt1040::{ccm, dcdc, instances};
+
+const AHB_CONFIG: ccm::AbhConfiguration = ccm::AbhConfiguration {
+ div_sel: 100,
+ arm_divider: 2,
+ ahb_divider: 1,
+};
+const _: () = assert!(AHB_CONFIG.ahb_frequency() == 600_000_000);
+
+fn configure_clocks(ccm: ccm::CCM, ccm_analog: ccm::CCM_ANALOG, dcdc: dcdc::Instance) {
+ dcdc::set_target_vdd_soc(dcdc, 1250);
+ ccm::configure_ahb(ccm, ccm_analog, &AHB_CONFIG);
+
+ ccm::clock_gate::set(ccm, ccm::gates::FLEXSPI1, false.into());
+
+ ccm::pll2::restart(ccm_analog);
+ ral_registers::modify_reg!(ccm::ral::ccm_analog, ccm_analog, PFD_528, PFD2_FRAC: PFD2_FRAC.get());
+
+ ccm::flexspi1_clk::set_divider(ccm, 3);
+ ccm::flexspi1_clk::set_selection(ccm, ccm::flexspi1_clk::Selection::Pll2Pfd2);
+
+ ccm::clock_gate::set(ccm, ccm::gates::FLEXSPI1, true.into());
+}
+
+const PFD2_FRAC: NonZero<u32> = ccm::pll2::pll_pfd_divider(396_000_000).unwrap();
+
+pub struct Imxrt1040;
+impl imxrt10xx::Imxrt10xx for Imxrt1040 {
+ const FLEXSPI1_INSTANCE: imxrt1040::flexspi::Instance = unsafe { instances::flexspi1() };
+
+ const CCM_INSTANCE: ccm::CCM = unsafe { instances::ccm() };
+
+ const CCM_ANALOG_INSTANCE: ccm::CCM_ANALOG = unsafe { instances::ccm_analog() };
+
+ const DCDC_INSTANCE: dcdc::Instance = unsafe { instances::dcdc() };
+
+ const FLEXSPI_FIFO_CAPACITY_BYTES: usize = 128;
+
+ fn configure_clocks(ccm: ccm::CCM, ccm_analog: ccm::CCM_ANALOG, dcdc: dcdc::Instance) {
+ self::configure_clocks(ccm, ccm_analog, dcdc);
+ }
+}
+
+pub type Algorithm<F> = imxrt10xx::Algorithm<Imxrt1040, F>;
diff --git a/imxrt1040evk/Cargo.toml b/imxrt1040evk/Cargo.toml
new file mode 100644
index 0000000..3df3f8a
--- /dev/null
+++ b/imxrt1040evk/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "imxrt1040evk"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+cortex-m = { workspace = true }
+defmt-rtt = { workspace = true }
+
+imxrt1040-flash-algos = { workspace = true }
+flash-algorithm = { workspace = true }
+panic-probe = { workspace = true }
+
+[build-dependencies]
+imxrt-rt = { workspace = true, features = ["device"] }
+
+[dev-dependencies]
+defmt = { workspace = true }
+imxrt-rt = { workspace = true, features = ["device"] }
diff --git a/imxrt1040evk/build.rs b/imxrt1040evk/build.rs
new file mode 100644
index 0000000..a02ccc0
--- /dev/null
+++ b/imxrt1040evk/build.rs
@@ -0,0 +1,8 @@
+use imxrt_rt::Family;
+
+fn main() {
+ imxrt_rt::RuntimeBuilder::from_ram(Family::Imxrt1040)
+ .device_script_name("imxrt1040.x")
+ .build()
+ .unwrap();
+}
diff --git a/imxrt1040evk/examples/smoke.rs b/imxrt1040evk/examples/smoke.rs
new file mode 100644
index 0000000..3730125
--- /dev/null
+++ b/imxrt1040evk/examples/smoke.rs
@@ -0,0 +1,36 @@
+#![no_main]
+#![no_std]
+
+use defmt_rtt as _;
+use imxrt1040evk::{self, Algorithm};
+use panic_probe as _;
+
+const LAST_SECTOR: usize = Algorithm::flash_size_bytes() - Algorithm::sector_size_bytes();
+
+#[imxrt_rt::entry]
+fn main() -> ! {
+ for _ in 0..5 {
+ let mut w25q64 = Algorithm::initialize();
+
+ let mut sector = [0_u8; Algorithm::sector_size_bytes()];
+ w25q64.flash_read(0, &mut sector[..4]);
+ let tag = u32::from_le_bytes(sector[..4].try_into().unwrap());
+ defmt::assert!(0x42464346 == tag, "{=u32:#010X}", tag);
+
+ w25q64.flash_erase_sector(LAST_SECTOR + 256);
+
+ sector.fill(0);
+ w25q64.flash_write(LAST_SECTOR, &sector);
+
+ sector.fill(u8::MAX);
+ w25q64.flash_read(LAST_SECTOR, &mut sector);
+ defmt::assert!(sector.iter().all(|byte| *byte == 0));
+
+ w25q64.flash_erase_sector(LAST_SECTOR + 123);
+
+ w25q64.flash_read(LAST_SECTOR, &mut sector);
+ defmt::assert!(sector.iter().all(|byte| *byte == u8::MAX));
+ }
+ defmt::println!("Pass");
+ loop {}
+}
diff --git a/imxrt1040evk/src/lib.rs b/imxrt1040evk/src/lib.rs
new file mode 100644
index 0000000..ad2bb57
--- /dev/null
+++ b/imxrt1040evk/src/lib.rs
@@ -0,0 +1,7 @@
+#![no_std]
+
+use cortex_m as _;
+
+pub use imxrt1040_flash_algos::flash::winbond::W25q64 as Flash;
+
+pub type Algorithm = imxrt1040_flash_algos::Algorithm<Flash>;
diff --git a/imxrt1040evk/src/main.rs b/imxrt1040evk/src/main.rs
new file mode 100644
index 0000000..87d76cf
--- /dev/null
+++ b/imxrt1040evk/src/main.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt_rtt as _;
+use panic_probe as _;
+
+use imxrt1040evk::Algorithm;
+
+flash_algorithm::algorithm!(Algorithm, {
+ device_name: "imxrt1040_w25q64_132mhz",
+ device_type: DeviceType::Onchip,
+ flash_address: Algorithm::flash_address() as _,
+ flash_size: Algorithm::flash_size_bytes() as _,
+ // We support page crossings. Suggest a larger page
+ // size so that probe-rs places larger buffers into
+ // RAM per programming operation, reducing overhead.
+ page_size: 4096,
+ empty_value: 0xFF,
+ program_time_out: 1000,
+ erase_time_out: 2000,
+ sectors: [{
+ size: Algorithm::sector_size_bytes() as u32,
+ address: 0x0,
+ }]
+});
diff --git a/imxrt1160/Cargo.toml b/imxrt1160/Cargo.toml
new file mode 100644
index 0000000..20eff57
--- /dev/null
+++ b/imxrt1160/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "imxrt1160-flash-algos"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+imxrt-flash-algos = { workspace = true }
+imxrt1160 = { workspace = true }
diff --git a/imxrt1160/src/lib.rs b/imxrt1160/src/lib.rs
new file mode 100644
index 0000000..33b0de9
--- /dev/null
+++ b/imxrt1160/src/lib.rs
@@ -0,0 +1,20 @@
+//! Interface package for writing flash algorithms for 1160 MCUs.
+#![no_std]
+
+pub use imxrt_flash_algos::*;
+pub struct Imxrt1160;
+
+impl imxrt11xx::Imxrt11xx for Imxrt1160 {
+ const CCM_INSTANCE: imxrt1160::ccm::Instance = unsafe { imxrt1160::instances::ccm() };
+ const CCM_PLL_INSTANCE: imxrt1160::ccm::pll::Instance = unsafe { imxrt1160::instances::pll() };
+ const FLEXSPI1_INSTANCE: imxrt1160::flexspi::Instance =
+ unsafe { imxrt1160::instances::flexspi1() };
+ const GPC_CPU_INSTANCE: imxrt1160::gpc_cpu_mode_ctrl::Instance =
+ unsafe { imxrt1160::instances::gpc_cpu_mode_ctrl0() };
+ const PMU_INSTANCE: imxrt1160::pmu::Instance = unsafe { imxrt1160::instances::pmu() };
+ const RTWDOG_INSTANCE: imxrt1160::rtwdog::Instance = unsafe { imxrt1160::instances::wdog3() };
+
+ const FLEXSPI_FIFO_CAPACITY_BYTES: usize = 128;
+}
+
+pub type Algorithm<F> = imxrt11xx::Algorithm<Imxrt1160, F>;
diff --git a/imxrt1160evk/Cargo.toml b/imxrt1160evk/Cargo.toml
new file mode 100644
index 0000000..450c974
--- /dev/null
+++ b/imxrt1160evk/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "imxrt1160evk"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+cortex-m = { workspace = true }
+defmt-rtt = { workspace = true }
+
+imxrt1160-flash-algos = { workspace = true }
+flash-algorithm = { workspace = true }
+panic-probe = { workspace = true }
diff --git a/imxrt1160evk/src/lib.rs b/imxrt1160evk/src/lib.rs
new file mode 100644
index 0000000..82292dc
--- /dev/null
+++ b/imxrt1160evk/src/lib.rs
@@ -0,0 +1,6 @@
+#![no_std]
+
+use cortex_m as _;
+
+pub use imxrt1160_flash_algos::flash::issi::Is25WP128 as Flash;
+pub type Algorithm = imxrt1160_flash_algos::Algorithm<Flash>;
diff --git a/imxrt1160evk/src/main.rs b/imxrt1160evk/src/main.rs
new file mode 100644
index 0000000..8c50649
--- /dev/null
+++ b/imxrt1160evk/src/main.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt_rtt as _;
+use panic_probe as _;
+
+use imxrt1160evk::Algorithm;
+
+flash_algorithm::algorithm!(Algorithm, {
+ device_name: "imxrt1160_is25wp128_133mhz",
+ device_type: DeviceType::Onchip,
+ flash_address: Algorithm::flash_address() as _,
+ flash_size: Algorithm::flash_size_bytes() as _,
+ // We support page crossings. Suggest a larger page
+ // size so that probe-rs places larger buffers into
+ // RAM per programming operation, reducing overhead.
+ page_size: 4096,
+ empty_value: 0xFF,
+ program_time_out: 1000,
+ erase_time_out: 2000,
+ sectors: [{
+ size: Algorithm::sector_size_bytes() as u32,
+ address: 0x0,
+ }]
+});
diff --git a/imxrt1170/Cargo.toml b/imxrt1170/Cargo.toml
new file mode 100644
index 0000000..9b43b2d
--- /dev/null
+++ b/imxrt1170/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "imxrt1170-flash-algos"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+imxrt-flash-algos = { workspace = true }
+imxrt1170 = { workspace = true }
diff --git a/imxrt1170/src/lib.rs b/imxrt1170/src/lib.rs
new file mode 100644
index 0000000..89469b1
--- /dev/null
+++ b/imxrt1170/src/lib.rs
@@ -0,0 +1,20 @@
+//! Interface package for writing flash algorithms on 1170 MCUs.
+#![no_std]
+
+pub use imxrt_flash_algos::*;
+pub struct Imxrt1170;
+
+impl imxrt11xx::Imxrt11xx for Imxrt1170 {
+ const CCM_INSTANCE: imxrt1170::ccm::Instance = unsafe { imxrt1170::instances::ccm() };
+ const CCM_PLL_INSTANCE: imxrt1170::ccm::pll::Instance = unsafe { imxrt1170::instances::pll() };
+ const FLEXSPI1_INSTANCE: imxrt1170::flexspi::Instance =
+ unsafe { imxrt1170::instances::flexspi1() };
+ const GPC_CPU_INSTANCE: imxrt1170::gpc_cpu_mode_ctrl::Instance =
+ unsafe { imxrt1170::instances::gpc_cpu_mode_ctrl0() };
+ const PMU_INSTANCE: imxrt1170::pmu::Instance = unsafe { imxrt1170::instances::pmu() };
+ const RTWDOG_INSTANCE: imxrt1170::rtwdog::Instance = unsafe { imxrt1170::instances::wdog3() };
+
+ const FLEXSPI_FIFO_CAPACITY_BYTES: usize = 256;
+}
+
+pub type Algorithm<F> = imxrt11xx::Algorithm<Imxrt1170, F>;
diff --git a/imxrt1170evk/Cargo.toml b/imxrt1170evk/Cargo.toml
new file mode 100644
index 0000000..2e65e13
--- /dev/null
+++ b/imxrt1170evk/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "imxrt1170evk"
+version = "0.1.0"
+edition = "2024"
+publish = false
+
+[dependencies]
+cortex-m = { workspace = true }
+defmt-rtt = { workspace = true }
+
+imxrt1170-flash-algos = { workspace = true }
+flash-algorithm = { workspace = true }
+panic-probe = { workspace = true }
+
+[build-dependencies]
+imxrt-rt = { workspace = true, features = ["device"] }
+
+[dev-dependencies]
+defmt = { workspace = true }
+imxrt-rt = { workspace = true, features = ["device"] }
diff --git a/imxrt1170evk/build.rs b/imxrt1170evk/build.rs
new file mode 100644
index 0000000..c4379fc
--- /dev/null
+++ b/imxrt1170evk/build.rs
@@ -0,0 +1,8 @@
+use imxrt_rt::Family;
+
+fn main() {
+ imxrt_rt::RuntimeBuilder::from_ram(Family::Imxrt1170)
+ .device_script_name("imxrt1170.x")
+ .build()
+ .unwrap();
+}
diff --git a/imxrt1170evk/examples/smoke.rs b/imxrt1170evk/examples/smoke.rs
new file mode 100644
index 0000000..df91789
--- /dev/null
+++ b/imxrt1170evk/examples/smoke.rs
@@ -0,0 +1,35 @@
+#![no_main]
+#![no_std]
+
+use defmt_rtt as _;
+use imxrt1170evk::{self, Algorithm};
+use panic_probe as _;
+
+const LAST_SECTOR: usize = Algorithm::flash_size_bytes() - Algorithm::sector_size_bytes();
+
+#[imxrt_rt::entry]
+fn main() -> ! {
+ for _ in 0..5 {
+ let mut is25wp = Algorithm::initialize();
+
+ let mut sector = [0_u8; Algorithm::sector_size_bytes()];
+ is25wp.flash_read(0x400, &mut sector[..4]);
+ defmt::assert!(0x42464346 == u32::from_le_bytes(sector[..4].try_into().unwrap()));
+
+ is25wp.flash_erase_sector(LAST_SECTOR + 256);
+
+ sector.fill(0);
+ is25wp.flash_write(LAST_SECTOR, &sector);
+
+ sector.fill(u8::MAX);
+ is25wp.flash_read(LAST_SECTOR, &mut sector);
+ defmt::assert!(sector.iter().all(|byte| *byte == 0));
+
+ is25wp.flash_erase_sector(LAST_SECTOR + 123);
+
+ is25wp.flash_read(LAST_SECTOR, &mut sector);
+ defmt::assert!(sector.iter().all(|byte| *byte == u8::MAX));
+ }
+ defmt::println!("Pass");
+ loop {}
+}
diff --git a/imxrt1170evk/src/lib.rs b/imxrt1170evk/src/lib.rs
new file mode 100644
index 0000000..5410acd
--- /dev/null
+++ b/imxrt1170evk/src/lib.rs
@@ -0,0 +1,6 @@
+#![no_std]
+
+use cortex_m as _;
+
+pub use imxrt1170_flash_algos::flash::issi::Is25WP128 as Flash;
+pub type Algorithm = imxrt1170_flash_algos::Algorithm<Flash>;
diff --git a/imxrt1170evk/src/main.rs b/imxrt1170evk/src/main.rs
new file mode 100644
index 0000000..6b8483b
--- /dev/null
+++ b/imxrt1170evk/src/main.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt_rtt as _;
+use panic_probe as _;
+
+use imxrt1170evk::Algorithm;
+
+flash_algorithm::algorithm!(Algorithm, {
+ device_name: "imxrt1170_is25wp128_133mhz",
+ device_type: DeviceType::Onchip,
+ flash_address: Algorithm::flash_address() as _,
+ flash_size: Algorithm::flash_size_bytes() as _,
+ // We support page crossings. Suggest a larger page
+ // size so that probe-rs places larger buffers into
+ // RAM per programming operation, reducing overhead.
+ page_size: 4096,
+ empty_value: 0xFF,
+ program_time_out: 1000,
+ erase_time_out: 2000,
+ sectors: [{
+ size: Algorithm::sector_size_bytes() as u32,
+ address: 0x0,
+ }]
+});
diff --git a/link.x b/link.x
new file mode 100644
index 0000000..cd26f7d
--- /dev/null
+++ b/link.x
@@ -0,0 +1,4 @@
+MEMORY
+{
+ DTCM(rwx): ORIGIN = 0x20000100, LENGTH = 32K - 256
+}
diff --git a/src/flash/adesto.rs b/src/flash/adesto.rs
new file mode 100644
index 0000000..0c21d8c
--- /dev/null
+++ b/src/flash/adesto.rs
@@ -0,0 +1,7 @@
+//! Adesto serial NOR flash.
+//!
+//! Looks just like a Winbond flash part.
+
+pub use super::winbond::Winbond as Adesto;
+
+pub type At25sf128 = Adesto<{ 128 / 8 * 1024 * 1024 }>;
diff --git a/src/flash/issi.rs b/src/flash/issi.rs
new file mode 100644
index 0000000..1d36bb0
--- /dev/null
+++ b/src/flash/issi.rs
@@ -0,0 +1,101 @@
+//! ISSI Serial NOR Flash.
+
+use super::*;
+use crate::{ImxrtFlashAlgorithm, sequences::common as sequences};
+
+pub type Is25WP128 = Issi<{ 128 / 8 * 1024 * 1024 }, 15>;
+
+/// An ISSI serial NOR flash driver.
+pub struct Issi<const FLASH_CAPACITY_BYTES: usize, const DUMMY_CYCLES: u8>;
+
+impl<const FLASH_CAPACITY_BYTES: usize, const DUMMY_CYCLES: u8> ImxrtFlashAlgorithm
+ for Issi<FLASH_CAPACITY_BYTES, DUMMY_CYCLES>
+{
+ const FLASH_CAPACITY_BYTES: usize = FLASH_CAPACITY_BYTES;
+ const FLASH_PAGE_SIZE_BYTES: usize = 256;
+ const FLASH_SECTOR_SIZE_BYTES: usize = 4096;
+
+ fn initialize(flexspi: imxrt_drivers_flexspi::Instance) {
+ defmt::assert_eq!(
+ READ,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ READ.seq_id,
+ &[sequences::seq_fast_read_quad_io(DUMMY_CYCLES)],
+ ))
+ );
+
+ defmt::assert_eq!(
+ SET_READ_PARAMS,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ SET_READ_PARAMS.seq_id,
+ &[sequences::SEQ_SET_READ_PARAMS_VOL]
+ ))
+ );
+
+ defmt::assert_eq!(
+ WRITE_ENABLE,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ WRITE_ENABLE.seq_id,
+ &[sequences::SEQ_WRITE_ENABLE]
+ ))
+ );
+
+ defmt::assert_eq!(
+ ERASE_SECTOR,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ ERASE_SECTOR.seq_id,
+ &[sequences::SEQ_WRITE_ENABLE, sequences::SEQ_ERASE_SECTOR]
+ ))
+ );
+
+ defmt::assert_eq!(
+ READ_STATUS,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ READ_STATUS.seq_id,
+ &[sequences::SEQ_READ_STATUS]
+ ))
+ );
+
+ defmt::assert_eq!(
+ PAGE_PROGRAM,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ PAGE_PROGRAM.seq_id,
+ &[
+ sequences::SEQ_WRITE_ENABLE,
+ sequences::SEQ_PAGE_PROGRAM_QUAD_INPUT
+ ]
+ ))
+ );
+
+ defmt::assert_eq!(
+ CHIP_ERASE,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ CHIP_ERASE.seq_id,
+ &[sequences::SEQ_WRITE_ENABLE, sequences::SEQ_ERASE_CHIP]
+ ))
+ );
+
+ defmt::assert_eq!(
+ RESET,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ RESET.seq_id,
+ &[sequences::SEQ_RSTEN, sequences::SEQ_RST]
+ ))
+ );
+
+ let set_read_params_data = [DUMMY_CYCLES << 3];
+ crate::start_ip_cmd(flexspi, SET_READ_PARAMS, 0, &set_read_params_data);
+ crate::transmit_bytes(flexspi, &set_read_params_data);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::clear_tx_fifo(flexspi);
+ crate::wait_for_idle(flexspi);
+ }
+}
diff --git a/src/flash/winbond.rs b/src/flash/winbond.rs
new file mode 100644
index 0000000..afac605
--- /dev/null
+++ b/src/flash/winbond.rs
@@ -0,0 +1,92 @@
+//! Winbond Serial NOR Flash.
+
+use super::*;
+use crate::{ImxrtFlashAlgorithm, sequences::common as sequences};
+
+pub type W25q64 = Winbond<{ 64 / 8 * 1024 * 1024 }>;
+
+/// A Winbond serial NOR flash driver.
+pub struct Winbond<const FLASH_CAPACITY_BYTES: usize>;
+
+impl<const FLASH_CAPACITY_BYTES: usize> ImxrtFlashAlgorithm for Winbond<FLASH_CAPACITY_BYTES> {
+ const FLASH_CAPACITY_BYTES: usize = FLASH_CAPACITY_BYTES;
+ const FLASH_PAGE_SIZE_BYTES: usize = 256;
+ const FLASH_SECTOR_SIZE_BYTES: usize = 4096;
+
+ fn initialize(flexspi: imxrt_drivers_flexspi::Instance) {
+ defmt::assert_eq!(
+ READ,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ READ.seq_id,
+ &[sequences::seq_fast_read_quad_io(6)],
+ ))
+ );
+
+ defmt::assert_eq!(
+ SET_READ_PARAMS,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ SET_READ_PARAMS.seq_id,
+ &[sequences::SEQ_SET_READ_PARAMS_VOL]
+ ))
+ );
+
+ defmt::assert_eq!(
+ WRITE_ENABLE,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ WRITE_ENABLE.seq_id,
+ &[sequences::SEQ_WRITE_ENABLE]
+ ))
+ );
+
+ defmt::assert_eq!(
+ ERASE_SECTOR,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ ERASE_SECTOR.seq_id,
+ &[sequences::SEQ_WRITE_ENABLE, sequences::SEQ_ERASE_SECTOR]
+ ))
+ );
+
+ defmt::assert_eq!(
+ READ_STATUS,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ READ_STATUS.seq_id,
+ &[sequences::SEQ_READ_STATUS]
+ ))
+ );
+
+ defmt::assert_eq!(
+ PAGE_PROGRAM,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ PAGE_PROGRAM.seq_id,
+ &[
+ sequences::SEQ_WRITE_ENABLE,
+ sequences::SEQ_PAGE_PROGRAM_QUAD_INPUT
+ ]
+ ))
+ );
+
+ defmt::assert_eq!(
+ CHIP_ERASE,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ CHIP_ERASE.seq_id,
+ &[sequences::SEQ_WRITE_ENABLE, sequences::SEQ_ERASE_CHIP]
+ ))
+ );
+
+ defmt::assert_eq!(
+ RESET,
+ defmt::unwrap!(crate::install_ip_cmd(
+ flexspi,
+ RESET.seq_id,
+ &[sequences::SEQ_RSTEN, sequences::SEQ_RST]
+ ))
+ );
+ }
+}
diff --git a/src/imxrt10xx.rs b/src/imxrt10xx.rs
new file mode 100644
index 0000000..fe414fe
--- /dev/null
+++ b/src/imxrt10xx.rs
@@ -0,0 +1,100 @@
+use core::marker::PhantomData;
+
+use imxrt_drivers_ccm_10xx::{ccm, ccm_analog};
+use imxrt_drivers_dcdc as dcdc;
+use imxrt_drivers_flexspi as flexspi;
+
+const FLEXSPI1_BASE: u32 = 0x6000_0000;
+
+pub trait Imxrt10xx: 'static {
+ const FLEXSPI1_INSTANCE: flexspi::Instance;
+ const CCM_INSTANCE: ccm::Instance;
+ const CCM_ANALOG_INSTANCE: ccm_analog::Instance;
+ const DCDC_INSTANCE: dcdc::Instance;
+
+ const FLEXSPI_FIFO_CAPACITY_BYTES: usize;
+
+ fn configure_clocks(ccm: ccm::Instance, ccm_analog: ccm_analog::Instance, dcdc: dcdc::Instance);
+}
+
+pub struct Algorithm<C: Imxrt10xx, F: crate::ImxrtFlashAlgorithm>(PhantomData<(C, F)>);
+
+impl<C: Imxrt10xx, F: crate::ImxrtFlashAlgorithm> Algorithm<C, F> {
+ pub const fn flash_size_bytes() -> usize {
+ F::FLASH_CAPACITY_BYTES
+ }
+ pub const fn flash_address() -> usize {
+ FLEXSPI1_BASE as _
+ }
+ pub const fn sector_size_bytes() -> usize {
+ F::FLASH_SECTOR_SIZE_BYTES
+ }
+ pub const fn page_size_bytes() -> usize {
+ F::FLASH_PAGE_SIZE_BYTES
+ }
+
+ pub fn initialize() -> Self {
+ C::configure_clocks(C::CCM_INSTANCE, C::CCM_ANALOG_INSTANCE, C::DCDC_INSTANCE);
+ crate::reset(C::FLEXSPI1_INSTANCE, F::FLASH_CAPACITY_BYTES / 1024, 128);
+ F::initialize(C::FLEXSPI1_INSTANCE);
+ Algorithm(PhantomData)
+ }
+
+ pub fn flash_read(&mut self, address: usize, data: &mut [u8]) {
+ crate::flash::read(C::FLEXSPI1_INSTANCE, address, data);
+ }
+
+ pub fn flash_erase_sector(&mut self, address: usize) {
+ crate::flash::erase_sector(C::FLEXSPI1_INSTANCE, address);
+ }
+
+ pub fn flash_write(&mut self, address: usize, data: &[u8]) {
+ crate::flash::write(C::FLEXSPI1_INSTANCE, address, data);
+ }
+}
+
+impl<C: Imxrt10xx, F: crate::ImxrtFlashAlgorithm> flash_algorithm::FlashAlgorithm
+ for Algorithm<C, F>
+{
+ fn new(
+ _: u32,
+ _: u32,
+ _: flash_algorithm::Function,
+ ) -> Result<Self, flash_algorithm::ErrorCode> {
+ Ok(Self::initialize())
+ }
+
+ fn erase_all(&mut self) -> Result<(), flash_algorithm::ErrorCode> {
+ crate::flash::erase_chip(C::FLEXSPI1_INSTANCE);
+ Ok(())
+ }
+
+ fn erase_sector(&mut self, address: u32) -> Result<(), flash_algorithm::ErrorCode> {
+ self.flash_erase_sector(address.saturating_sub(FLEXSPI1_BASE) as usize);
+ Ok(())
+ }
+
+ fn program_page(
+ &mut self,
+ address: u32,
+ data: &[u8],
+ ) -> Result<(), flash_algorithm::ErrorCode> {
+ self.flash_write(address.saturating_sub(FLEXSPI1_BASE) as usize, data);
+ Ok(())
+ }
+
+ fn read_flash(
+ &mut self,
+ address: u32,
+ data: &mut [u8],
+ ) -> Result<(), flash_algorithm::ErrorCode> {
+ self.flash_read(address.saturating_sub(FLEXSPI1_BASE) as usize, data);
+ Ok(())
+ }
+}
+
+impl<C: Imxrt10xx, F: crate::ImxrtFlashAlgorithm> Drop for Algorithm<C, F> {
+ fn drop(&mut self) {
+ F::deinitialize(C::FLEXSPI1_INSTANCE);
+ }
+}
diff --git a/src/imxrt11xx.rs b/src/imxrt11xx.rs
new file mode 100644
index 0000000..ed83668
--- /dev/null
+++ b/src/imxrt11xx.rs
@@ -0,0 +1,218 @@
+use core::{marker::PhantomData, num::NonZero};
+
+use imxrt_drivers_ccm_11xx::ral_11xx as ccm;
+use imxrt_drivers_flexspi as flexspi;
+use imxrt_drivers_gpc_11xx::cpu_mode_ctrl as gpc_cpu;
+use imxrt_drivers_pmu_11xx as pmu;
+use imxrt_drivers_rtwdog as rtwdog;
+
+const FLEXSPI1_BASE: u32 = 0x3000_0000;
+
+/// Establish the core, bus, and FlexSPI
+/// clock frequencies.
+fn configure_clocks(
+ ccm: ccm::Instance,
+ pll: ccm::pll::Instance,
+ pmu: pmu::Instance,
+ gpc_cpu: gpc_cpu::Instance,
+) {
+ // Switch the core to something stable before we
+ // start changing upstream sources.
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::M7,
+ const { ccm::mux(ccm::ClockRoot::M7, ccm::ClockSource::Xtal) },
+ NO_DIVIDER,
+ );
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::Bus,
+ const { ccm::mux(ccm::ClockRoot::Bus, ccm::ClockSource::Xtal) },
+ NO_DIVIDER,
+ );
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::BusLpsr,
+ const { ccm::mux(ccm::ClockRoot::BusLpsr, ccm::ClockSource::Xtal) },
+ NO_DIVIDER,
+ );
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::Flexspi1,
+ const { ccm::mux(ccm::ClockRoot::Flexspi1, ccm::ClockSource::Xtal) },
+ NO_DIVIDER,
+ );
+
+ // Prepare PLL power, GPC setpoints.
+ pmu::enable_pll_reference_voltage(pmu, true);
+ pmu::set_phy_ldo_setpoints(pmu, u16::MAX);
+ pmu::enable_phy_ldo_setpoints(pmu);
+ pmu::enable_pll_reference_setpoints(pmu);
+
+ for clock_source in {
+ use ccm::ClockSource::*;
+ [
+ Pll1, Pll1Clk, Pll1Div2, Pll1Div5, ArmPll, ArmPllClk, //
+ Pll2, Pll2Clk, Pll2Pfd0, Pll2Pfd1, Pll2Pfd2, Pll2Pfd3, //
+ Pll3, Pll3Clk, Pll3Div2, Pll3Pfd0, Pll3Pfd1, Pll3Pfd2, Pll3Pfd3, //
+ ]
+ } {
+ ccm::set_source_setpoints(ccm, clock_source, u16::MAX, 0);
+ ccm::enable_source_setpoints(ccm, clock_source).unwrap();
+ }
+
+ ccm::pll::enable_sys_pll1_setpoints(pll);
+ ccm::pll::enable_arm_pll_setpoints(pll, ARM_PLL_POST_DIV, ARM_PLL_DIV_SELECT);
+ ccm::pll::enable_sys_pll2_setpoints(pll);
+ ccm::pll::enable_sys_pll3_setpoints(pll);
+
+ gpc_cpu::request_setpoint_transition(gpc_cpu, 1).unwrap();
+
+ ccm::pll::set_pll3_pfd_fracs(
+ pll,
+ [
+ SYS_PLL3_PFD0_DIV,
+ SYS_PLL3_PFD1_DIV,
+ SYS_PLL3_PFD2_DIV,
+ SYS_PLL3_PFD3_DIV,
+ ],
+ );
+ ccm::pll::update_pll3_pfd_fracs(pll, [true, true, true, true]);
+
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::M7,
+ const { ccm::mux(ccm::ClockRoot::M7, ccm::ClockSource::ArmPll) },
+ M7_DIVIDER,
+ );
+
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::Bus,
+ const { ccm::mux(ccm::ClockRoot::Bus, ccm::ClockSource::Pll1Div5) },
+ BUS_DIVIDER,
+ );
+
+ ccm::set_clock_root(
+ ccm,
+ ccm::ClockRoot::Flexspi1,
+ const { ccm::mux(ccm::ClockRoot::Flexspi1, ccm::ClockSource::Pll3Pfd0) },
+ FLEXSPI1_DIVIDER,
+ );
+}
+
+const NO_DIVIDER: NonZero<u8> = NonZero::new(1).unwrap();
+
+const M7_DIVIDER: NonZero<u8> = NO_DIVIDER;
+const BUS_DIVIDER: NonZero<u8> = NO_DIVIDER;
+const FLEXSPI1_DIVIDER: NonZero<u8> = NonZero::new(2).unwrap();
+
+const ARM_PLL_DIV_SELECT: ccm::pll::ArmPllDivSelect = ccm::pll::ArmPllDivSelect::new(200).unwrap();
+const ARM_PLL_POST_DIV: ccm::pll::ArmPllPostDiv = ccm::pll::ArmPllPostDiv::Div4;
+const SYS_PLL3_PFD0_DIV: ccm::pll::PfdFrac = ccm::pll::PfdFrac::new(33).unwrap();
+const SYS_PLL3_PFD1_DIV: ccm::pll::PfdFrac = ccm::pll::PfdFrac::new(27).unwrap();
+const SYS_PLL3_PFD2_DIV: ccm::pll::PfdFrac = ccm::pll::PfdFrac::new(21).unwrap();
+const SYS_PLL3_PFD3_DIV: ccm::pll::PfdFrac = ccm::pll::PfdFrac::new(17).unwrap();
+
+pub trait Imxrt11xx: 'static {
+ const FLEXSPI1_INSTANCE: flexspi::Instance;
+ const CCM_INSTANCE: ccm::Instance;
+ const PMU_INSTANCE: pmu::Instance;
+ const CCM_PLL_INSTANCE: ccm::pll::Instance;
+ const GPC_CPU_INSTANCE: gpc_cpu::Instance;
+ const RTWDOG_INSTANCE: rtwdog::Instance;
+
+ const FLEXSPI_FIFO_CAPACITY_BYTES: usize;
+}
+
+pub struct Algorithm<C: Imxrt11xx, F: crate::ImxrtFlashAlgorithm>(PhantomData<(C, F)>);
+
+impl<C: Imxrt11xx, F: crate::ImxrtFlashAlgorithm> Algorithm<C, F> {
+ pub const fn flash_size_bytes() -> usize {
+ F::FLASH_CAPACITY_BYTES
+ }
+ pub const fn flash_address() -> usize {
+ FLEXSPI1_BASE as _
+ }
+ pub const fn sector_size_bytes() -> usize {
+ F::FLASH_SECTOR_SIZE_BYTES
+ }
+ pub const fn page_size_bytes() -> usize {
+ F::FLASH_PAGE_SIZE_BYTES
+ }
+
+ pub fn initialize() -> Self {
+ rtwdog::disable(C::RTWDOG_INSTANCE);
+ configure_clocks(
+ C::CCM_INSTANCE,
+ C::CCM_PLL_INSTANCE,
+ C::PMU_INSTANCE,
+ C::GPC_CPU_INSTANCE,
+ );
+ crate::reset(
+ C::FLEXSPI1_INSTANCE,
+ F::FLASH_CAPACITY_BYTES / 1024,
+ C::FLEXSPI_FIFO_CAPACITY_BYTES,
+ );
+ F::initialize(C::FLEXSPI1_INSTANCE);
+ Algorithm(PhantomData)
+ }
+
+ pub fn flash_read(&mut self, address: usize, data: &mut [u8]) {
+ crate::flash::read(C::FLEXSPI1_INSTANCE, address, data);
+ }
+
+ pub fn flash_erase_sector(&mut self, address: usize) {
+ crate::flash::erase_sector(C::FLEXSPI1_INSTANCE, address);
+ }
+
+ pub fn flash_write(&mut self, address: usize, data: &[u8]) {
+ crate::flash::write(C::FLEXSPI1_INSTANCE, address, data);
+ }
+}
+
+impl<C: Imxrt11xx, F: crate::ImxrtFlashAlgorithm> flash_algorithm::FlashAlgorithm
+ for Algorithm<C, F>
+{
+ fn new(
+ _: u32,
+ _: u32,
+ _: flash_algorithm::Function,
+ ) -> Result<Self, flash_algorithm::ErrorCode> {
+ Ok(Self::initialize())
+ }
+
+ fn erase_all(&mut self) -> Result<(), flash_algorithm::ErrorCode> {
+ crate::flash::erase_chip(C::FLEXSPI1_INSTANCE);
+ Ok(())
+ }
+
+ fn erase_sector(&mut self, address: u32) -> Result<(), flash_algorithm::ErrorCode> {
+ self.flash_erase_sector(address.saturating_sub(FLEXSPI1_BASE) as usize);
+ Ok(())
+ }
+
+ fn program_page(
+ &mut self,
+ address: u32,
+ data: &[u8],
+ ) -> Result<(), flash_algorithm::ErrorCode> {
+ self.flash_write(address.saturating_sub(FLEXSPI1_BASE) as usize, data);
+ Ok(())
+ }
+
+ fn read_flash(
+ &mut self,
+ address: u32,
+ data: &mut [u8],
+ ) -> Result<(), flash_algorithm::ErrorCode> {
+ self.flash_read(address.saturating_sub(FLEXSPI1_BASE) as usize, data);
+ Ok(())
+ }
+}
+
+impl<C: Imxrt11xx, F: crate::ImxrtFlashAlgorithm> Drop for Algorithm<C, F> {
+ fn drop(&mut self) {
+ F::deinitialize(C::FLEXSPI1_INSTANCE);
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..38ae43e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,479 @@
+#![no_std]
+
+use imxrt_drivers_flexspi as flexspi;
+pub use imxrt_drivers_flexspi::{SeqId, SeqIdRange, Sequence};
+use ral_registers as ral;
+
+pub mod sequences {
+ pub mod common;
+}
+
+/// Helpers for interacting with flash.
+pub mod flash {
+ use super::{IpCmd, SeqId, flexspi};
+
+ pub mod adesto;
+ pub mod issi;
+ pub mod winbond;
+
+ const READ: IpCmd = IpCmd::new(SeqId::Seq00, 1).unwrap();
+ const READ_STATUS: IpCmd = IpCmd::new(SeqId::Seq01, 1).unwrap();
+ const WRITE_ENABLE: IpCmd = IpCmd::new(SeqId::Seq03, 1).unwrap();
+ const ERASE_SECTOR: IpCmd = IpCmd::new(SeqId::Seq05, 2).unwrap();
+ const PAGE_PROGRAM: IpCmd = IpCmd::new(SeqId::Seq09, 2).unwrap();
+ const CHIP_ERASE: IpCmd = IpCmd::new(SeqId::Seq11, 2).unwrap();
+
+ const SET_READ_PARAMS: IpCmd = IpCmd::new(SeqId::Seq02, 1).unwrap();
+ const RESET: IpCmd = IpCmd::new(SeqId::Seq07, 2).unwrap();
+
+ const PAGE_SIZE_BYTES: usize = 256;
+
+ const STATUS_WIP: u8 = 1 << 0;
+
+ /// Write data to the flash array, starting at `flash_start`.
+ ///
+ /// The implementation handles page crossings and writes into the
+ /// middle of pages. The call blocks until the peripheral and flash
+ /// array is idle.
+ ///
+ /// This call does not eagerly erase any flash sectors. You'll need
+ /// to do that yourself.
+ pub fn write(flexspi: flexspi::Instance, mut flash_start: usize, data: &[u8]) {
+ for page in aligned_chunks(flash_start, data, PAGE_SIZE_BYTES) {
+ crate::start_ip_cmd(flexspi, PAGE_PROGRAM, flash_start, page);
+ crate::transmit_bytes(flexspi, page);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::clear_tx_fifo(flexspi);
+ crate::wait_for_idle(flexspi);
+
+ flash_start = flash_start.saturating_add(page.len());
+
+ wait_for_wip_clear(flexspi);
+ }
+ }
+
+ /// Poll the status register while WIP is set.
+ fn wait_for_wip_clear(flexspi: flexspi::Instance) {
+ while {
+ let status = read_status(flexspi);
+ status & STATUS_WIP != 0
+ } {}
+ }
+
+ /// Read the status register.
+ fn read_status(flexspi: flexspi::Instance) -> u8 {
+ let mut status = [0_u8; 1];
+ crate::start_ip_cmd(flexspi, READ_STATUS, 0, &status);
+ crate::receive_bytes(flexspi, &mut status);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::clear_rx_fifo(flexspi);
+ crate::wait_for_idle(flexspi);
+ status[0]
+ }
+
+ /// Read data from the flash array starting at `flash_start`.
+ ///
+ /// The implementation handles reads into the middle of pages, along
+ /// with page crossings. The call blocks until all bytes are in the
+ /// `data` buffer.
+ pub fn read(flexspi: flexspi::Instance, flash_start: usize, data: &mut [u8]) {
+ crate::start_ip_cmd(flexspi, READ, flash_start, data);
+ crate::receive_bytes(flexspi, data);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::clear_rx_fifo(flexspi);
+ crate::wait_for_idle(flexspi);
+ }
+
+ /// Erase a sector.
+ ///
+ /// `flash_start` is an address in the flash part. The call
+ /// erases the entire sector, even if `flash_start` isn't a
+ /// sector aligned address. Blocks until the flash array is
+ /// idle.
+ pub fn erase_sector(flexspi: flexspi::Instance, flash_start: usize) {
+ crate::start_ip_cmd(flexspi, ERASE_SECTOR, flash_start, &[]);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::wait_for_idle(flexspi);
+
+ wait_for_wip_clear(flexspi);
+ }
+
+ /// Erase the entire chip.
+ pub fn erase_chip(flexspi: flexspi::Instance) {
+ crate::start_ip_cmd(flexspi, CHIP_ERASE, 0, &[]);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::wait_for_idle(flexspi);
+
+ wait_for_wip_clear(flexspi);
+ }
+
+ /// Reset the flash chip.
+ pub fn reset(flexspi: flexspi::Instance) {
+ crate::start_ip_cmd(flexspi, RESET, 0, &[]);
+ crate::wait_for_ip_cmd_done(flexspi);
+ crate::wait_for_idle(flexspi);
+
+ wait_for_wip_clear(flexspi);
+ }
+
+ /// Produce chunks of bytes suitable for page aligned writing.
+ fn aligned_chunks(start: usize, bytes: &[u8], page_size: usize) -> impl Iterator<Item = &[u8]> {
+ let next_page_start = page_size - (start % page_size);
+ core::iter::once(&bytes[..next_page_start])
+ .chain(bytes[next_page_start..].chunks(page_size))
+ }
+
+ #[cfg(test)]
+ mod tests {
+ extern crate std;
+ use std::vec::Vec;
+
+ use super::aligned_chunks;
+
+ fn make_buffer() -> Vec<u8> {
+ let mut buffer = Vec::with_capacity(1024);
+ for i in 1..=4 {
+ buffer.extend(std::iter::repeat(i).take(256));
+ }
+ assert_eq!(buffer.len(), 1024);
+ buffer
+ }
+
+ #[test]
+ fn aligned() {
+ let buffer = make_buffer();
+ let mut chunks = aligned_chunks(256, &buffer, 256);
+ assert_eq!(chunks.next().unwrap(), &[1; 256][..]);
+ assert_eq!(chunks.next().unwrap(), &[2; 256][..]);
+ assert_eq!(chunks.next().unwrap(), &[3; 256][..]);
+ assert_eq!(chunks.next().unwrap(), &[4; 256][..]);
+ assert_eq!(chunks.next(), None);
+ }
+
+ #[test]
+ fn unaligned_by_one() {
+ let buffer = make_buffer();
+ let mut chunks = aligned_chunks(1, &buffer, 256);
+ assert_eq!(chunks.next().unwrap(), &buffer[0..255]);
+ assert_eq!(chunks.next().unwrap(), &buffer[255..511]);
+ assert_eq!(chunks.next().unwrap(), &buffer[511..767]);
+ assert_eq!(chunks.next().unwrap(), &buffer[767..1023]);
+ assert_eq!(chunks.next().unwrap(), &buffer[1023..]);
+ assert_eq!(chunks.next(), None);
+ }
+
+ #[test]
+ fn unalged_by_almost_a_page() {
+ let buffer = make_buffer();
+ let mut chunks = aligned_chunks(255, &buffer, 256);
+ assert_eq!(chunks.next().unwrap(), &buffer[0..1]);
+ assert_eq!(chunks.next().unwrap(), &buffer[1..257]);
+ assert_eq!(chunks.next().unwrap(), &buffer[257..513]);
+ assert_eq!(chunks.next().unwrap(), &buffer[513..769]);
+ assert_eq!(chunks.next().unwrap(), &buffer[769..1024]);
+ assert_eq!(chunks.next(), None);
+ }
+ }
+}
+
+pub mod imxrt10xx;
+pub mod imxrt11xx;
+
+/// A handle for an IP command sequence.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct IpCmd {
+ seq_id: SeqId,
+ seq_num: usize,
+}
+
+impl defmt::Format for IpCmd {
+ fn format(&self, fmt: defmt::Formatter) {
+ defmt::write!(
+ fmt,
+ "IpCmd({=usize},{=usize})",
+ self.seq_id as usize,
+ self.seq_num
+ )
+ }
+}
+
+impl IpCmd {
+ /// Create an IP command handle for a command sequence.
+ pub const fn for_sequences(seq_id: SeqId, seqs: &[Sequence]) -> Option<Self> {
+ Self::new(seq_id, seqs.len())
+ }
+
+ /// Create an IP command handle for executing the given
+ /// sequence.
+ pub const fn new(seq_id: SeqId, seq_num: usize) -> Option<Self> {
+ if seq_num == 0
+ || seq_num > flexspi::MAX_SEQ_PER_IP_CMD
+ || (seq_id as usize) + seq_num > flexspi::LUT_SIZE
+ {
+ None
+ } else {
+ Some(IpCmd { seq_id, seq_num })
+ }
+ }
+}
+
+/// Reset the peripheral.
+///
+/// Specify how large your flash part is, in KiB. Also
+/// specify how large your TX and RX FIFOs are for your
+/// FlexSPI peripheral.
+///
+/// After this call, you should install IP command sequences.
+pub fn reset(flexspi: flexspi::Instance, flash_size_kib: usize, fifo_capacity_bytes: usize) {
+ ral::write_reg!(flexspi, flexspi, MCR0, SWRESET: 1);
+ while ral::read_reg!(flexspi, flexspi, MCR0, SWRESET == 1) {}
+
+ ral::modify_reg!(flexspi, flexspi, MCR0, MDIS: 1);
+
+ ral::modify_reg!(flexspi, flexspi, MCR0,
+ AHBGRANTWAIT: 0xFF,
+ IPGRANTWAIT: 0xFF,
+ SCKFREERUNEN: 0,
+ COMBINATIONEN: 0,
+ DOZEEN: 0,
+ HSEN: 0,
+ SERCLKDIV: DIVIDE_1,
+ ATDFEN: IP_BUS,
+ ARDFEN: IP_BUS,
+ RXCLKSRC: LOOPBACK_DQS,
+ );
+ ral::write_reg!(flexspi, flexspi, MCR1, SEQWAIT: 0xFFFF, AHBBUSWAIT: 0xFFFF);
+ ral::write_reg!(flexspi, flexspi, INTEN, 0);
+ ral::write_reg!(flexspi, flexspi, INTR, u32::MAX);
+ ral::write_reg!(
+ flexspi,
+ flexspi,
+ FLSHCR0[flexspi::A1],
+ flash_size_kib as u32
+ );
+
+ ral::write_reg!(flexspi, flexspi, IPRXFCR, RXWMRK: 0, RXDMAEN: 0, CLRIPRXF: 1);
+
+ let tfdr_bytes = tfdr_len(flexspi) * size_of::<u32>();
+ let txmrk_bytes = tfdr_bytes.min(fifo_capacity_bytes);
+ let txwmrk = txmrk_bytes.div_ceil(size_of::<u64>()).saturating_sub(1);
+ ral::write_reg!(flexspi, flexspi, IPTXFCR, TXWMRK: txwmrk as u32, TXDMAEN: 0, CLRIPTXF: 1);
+
+ ral::modify_reg!(flexspi, flexspi, MCR0, MDIS: 0);
+}
+
+/// Install the a command sequence for an IP command.
+///
+/// Assumes that the peripheral is idle. You should install
+/// sequences after reset.
+///
+/// Returns the handle to the command sequence. Note that you
+/// can pre-compute this sequence and check it against the
+/// return. However, the handle isn't valid until this call.
+pub fn install_ip_cmd(
+ flexspi: flexspi::Instance,
+ seq_id: SeqId,
+ sequences: &[Sequence],
+) -> Option<IpCmd> {
+ let ip_cmd = IpCmd::for_sequences(seq_id, sequences)?;
+
+ flexspi::unlock_lut(flexspi);
+ for (seq_id, seq) in SeqIdRange::start(seq_id).zip(sequences) {
+ flexspi::set_sequence(flexspi, seq_id, seq);
+ }
+ flexspi::lock_lut(flexspi);
+
+ Some(ip_cmd)
+}
+
+/// Start a previously-installed IP command.
+///
+/// Generally, this call tries to minimize how long it blocks the CPU.
+/// If you're transmitting or receiving data, you should perform that
+/// operation as soon as this call returns.
+///
+/// If you're reading / writing from a register, set flash start to
+/// zero. If you have nothing to read or write, set flash start to
+/// zero and supply an empty buffer.
+///
+/// Call assumes that there is no in-progress IP command.
+pub fn start_ip_cmd(flexspi: flexspi::Instance, ip_cmd: IpCmd, flash_start: usize, data: &[u8]) {
+ ral::write_reg!(flexspi, flexspi, IPCR0, flash_start as u32);
+ ral::write_reg!(flexspi, flexspi, IPCR1,
+ IDATSZ: data.len() as u32,
+ ISEQNUM: ip_cmd.seq_num.saturating_sub(1) as u32,
+ ISEQID: ip_cmd.seq_id as u32,
+ );
+ ral::write_reg!(flexspi, flexspi, IPCMD, TRG: 1);
+}
+
+/// Clear the TX FIFO.
+#[inline]
+pub fn clear_tx_fifo(flexspi: flexspi::Instance) {
+ ral::modify_reg!(flexspi, flexspi, IPTXFCR, CLRIPTXF: 1);
+}
+
+/// Clear the RX FIFO.
+#[inline]
+pub fn clear_rx_fifo(flexspi: flexspi::Instance) {
+ ral::modify_reg!(flexspi, flexspi, IPRXFCR, CLRIPRXF: 1);
+}
+
+/// Transmit bytes to the flash part.
+///
+/// Returns as soon as all data has reached the transmit FIFO.
+/// You must then wait for the IP command to complete then clear
+/// the transmit FIFO.
+pub fn transmit_bytes(flexspi: flexspi::Instance, data: &[u8]) {
+ // Reset clamps the watermark to the maximum size of the
+ // TFDR array.
+ let txwmrk = ral::read_reg!(flexspi, flexspi, IPTXFCR, TXWMRK).saturating_add(1) as usize;
+ let len = txwmrk * const { size_of::<u64>() / size_of::<u32>() };
+
+ let words = data.chunks(size_of::<u32>()).map(|words| {
+ let mut scratch = [0_u8; size_of::<u32>()];
+ scratch[..words.len()].copy_from_slice(words);
+ u32::from_le_bytes(scratch)
+ });
+
+ let mut idx = 0;
+ for word in words {
+ if idx == len {
+ ral::write_reg!(flexspi, flexspi, INTR, IPTXWE: 1);
+ idx = 0;
+ }
+ while ral::read_reg!(flexspi, flexspi, INTR, IPTXWE != 1) {}
+ ral::write_reg!(flexspi, flexspi, TFDR[idx], word);
+ idx += 1;
+ }
+
+ ral::write_reg!(flexspi, flexspi, INTR, IPTXWE: 1);
+}
+
+/// Receive bytes from the flash part.
+///
+/// Returns as soon as data is received into your buffer.
+/// When this returns, you must wait for the IP command to complete
+/// then clear the RX FIFO.
+pub fn receive_bytes(flexspi: flexspi::Instance, data: &mut [u8]) {
+ let fifo = FifoReader::new(flexspi, data.len());
+ let words = data.chunks_mut(size_of::<u32>());
+
+ for (src, dst) in fifo.zip(words) {
+ let src = src.to_le_bytes();
+ dst.copy_from_slice(&src[..dst.len()]);
+ }
+
+ ral::write_reg!(flexspi, flexspi, INTR, IPRXWA: 1);
+}
+
+/// Wait for an IP command to complete.
+///
+/// This will clear the flag.
+pub fn wait_for_ip_cmd_done(flexspi: flexspi::Instance) {
+ while ral::read_reg!(flexspi, flexspi, INTR, IPCMDDONE != 1) {}
+ ral::write_reg!(flexspi, flexspi, INTR, IPCMDDONE: 1);
+}
+
+/// Wait for the FlexSPI interface to become idle.
+pub fn wait_for_idle(flexspi: flexspi::Instance) {
+ while ral::read_reg!(flexspi, flexspi, STS0, ARBIDLE != 1) {}
+}
+
+/// Returns the length of the pointed-at array.
+const fn array_ptr_len<T, const N: usize>(_: *const [T; N]) -> usize {
+ N
+}
+
+/// Returns the number of `u32`s that fit in `TFDR`.
+///
+/// This may be less than the actual FIFO capacity.
+const fn tfdr_len(flexspi: flexspi::Instance) -> usize {
+ // Safety: Users swear that the FlexSPI instance's
+ // pointer is valid for the whole register block.
+ array_ptr_len(unsafe { &raw const (*flexspi.as_ptr()).TFDR })
+}
+
+/// Iterator over the RX FIFO.
+///
+/// After you discard the reader, you should wait for the
+/// IP command to complete. Then, you should clear the RX
+/// FIFO.
+///
+/// This implementation performs no eager bounds checking.
+/// You must make sure that the watermark is valid in the
+/// control register.
+struct FifoReader {
+ flexspi: flexspi::Instance,
+ /// The number of bytes buffered in the FIFO per
+ /// the watermark level.
+ watermark: usize,
+ /// The total number of bytes to receive.
+ bytes: usize,
+ /// Our index into the FIFO. Each index represents
+ /// four bytes pulled from the FIFO.
+ idx: usize,
+}
+
+impl FifoReader {
+ /// Create an interator that reads `bytes` from the RX FIFO.
+ fn new(flexspi: flexspi::Instance, bytes: usize) -> Self {
+ let watermark =
+ (ral::read_reg!(flexspi, flexspi, IPRXFCR, RXWMRK).saturating_add(1) * 8) as usize;
+ Self {
+ flexspi,
+ watermark,
+ bytes,
+ idx: 0,
+ }
+ }
+
+ /// Spin until there is data available in the FIFO.
+ ///
+ /// We're supposed to pivot our polling when we have less than
+ /// a watermark of data remaining. Some early testing showed this
+ /// isn't strictly necessary, at least on some IP blocks.
+ /// Nevertheless, we follow the guidance here. (The DMA path can't
+ /// do this, but we have to...?)
+ fn wait_for_watermark(&self) {
+ if self.bytes >= self.watermark {
+ while ral::read_reg!(flexspi, self.flexspi, INTR, IPRXWA != 1) {}
+ } else {
+ while ral::read_reg!(flexspi, self.flexspi, IPRXFSTS, FILL == 0) {}
+ }
+ }
+}
+
+impl Iterator for FifoReader {
+ type Item = u32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.bytes == 0 {
+ return None;
+ }
+
+ self.wait_for_watermark();
+
+ let word = ral::read_reg!(flexspi, self.flexspi, RFDR[self.idx]);
+ self.idx += 1;
+ self.bytes = self.bytes.saturating_sub(size_of::<u32>());
+
+ if self.idx * 4 >= self.watermark {
+ self.idx = 0;
+ ral::write_reg!(flexspi, self.flexspi, INTR, IPRXWA: 1);
+ }
+
+ Some(word)
+ }
+}
+
+pub trait ImxrtFlashAlgorithm: 'static {
+ const FLASH_CAPACITY_BYTES: usize;
+ const FLASH_SECTOR_SIZE_BYTES: usize;
+ const FLASH_PAGE_SIZE_BYTES: usize;
+
+ fn initialize(flexspi: flexspi::Instance);
+ fn deinitialize(flexspi: flexspi::Instance) {
+ crate::flash::reset(flexspi);
+ }
+}
diff --git a/src/sequences/common.rs b/src/sequences/common.rs
new file mode 100644
index 0000000..cd3d573
--- /dev/null
+++ b/src/sequences/common.rs
@@ -0,0 +1,82 @@
+//! Common SPI sequences.
+
+use imxrt_drivers_flexspi::{Instr, Pads, SDR_CMD, SDR_DUMMY, SDR_RADDR, SDR_READ, SDR_WRITE};
+
+pub use crate::flexspi::Sequence;
+
+/// Enable write access.
+///
+/// This should be sequenced before configuration register
+/// updates, page programs, and erases.
+pub const SEQ_WRITE_ENABLE: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x06);
+ Sequence(instr)
+};
+
+/// Program a page using four output data signals.
+pub const SEQ_PAGE_PROGRAM_QUAD_INPUT: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x32);
+ instr[1] = Instr::new(SDR_RADDR, Pads::One, 24);
+ instr[2] = Instr::new(SDR_WRITE, Pads::Four, 0);
+ Sequence(instr)
+};
+
+/// Erase a 4KiB sector.
+pub const SEQ_ERASE_SECTOR: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x20);
+ instr[1] = Instr::new(SDR_RADDR, Pads::One, 24);
+ Sequence(instr)
+};
+
+/// Erase the entire chip.
+pub const SEQ_ERASE_CHIP: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x60);
+ Sequence(instr)
+};
+
+/// Read the status register.
+pub const SEQ_READ_STATUS: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x05);
+ instr[1] = Instr::new(SDR_READ, Pads::One, 0);
+ Sequence(instr)
+};
+
+/// Form a read sequence with a given number of
+/// dummy cycles.
+///
+/// The sequence sends the read address using four signals.
+pub const fn seq_fast_read_quad_io(dummy_cycles: u8) -> Sequence {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0xEB);
+ instr[1] = Instr::new(SDR_RADDR, Pads::Four, 24);
+ instr[2] = Instr::new(SDR_DUMMY, Pads::Four, dummy_cycles);
+ instr[3] = Instr::new(SDR_READ, Pads::Four, 0);
+ Sequence(instr)
+}
+
+/// Set parameters for reading data (volatile).
+pub const SEQ_SET_READ_PARAMS_VOL: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0xC0);
+ instr[1] = Instr::new(SDR_WRITE, Pads::One, 0);
+ Sequence(instr)
+};
+
+/// Software reset enable.
+pub const SEQ_RSTEN: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x66);
+ Sequence(instr)
+};
+
+/// Software reset.
+pub const SEQ_RST: Sequence = {
+ let mut instr = [Instr::STOP; _];
+ instr[0] = Instr::new(SDR_CMD, Pads::One, 0x99);
+ Sequence(instr)
+};