From 635bee2d21704fd76d066be0f66ce2c70ebaacb7 Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 30 Nov 2025 19:56:39 -0500 Subject: First commit --- .cargo/flash_algo.toml | 11 + .cargo/imxrt1010.toml | 12 + .cargo/imxrt1040.toml | 12 + .cargo/imxrt1170.toml | 12 + .gitignore | 1 + Cargo.lock | 586 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 72 +++++ LICENSE | 201 ++++++++++++++ README.md | 59 +++++ imxrt1010/Cargo.toml | 10 + imxrt1010/src/lib.rs | 33 +++ imxrt1010evk/Cargo.toml | 20 ++ imxrt1010evk/build.rs | 8 + imxrt1010evk/examples/smoke.rs | 36 +++ imxrt1010evk/src/lib.rs | 7 + imxrt1010evk/src/main.rs | 25 ++ imxrt1040/Cargo.toml | 10 + imxrt1040/src/lib.rs | 50 ++++ imxrt1040evk/Cargo.toml | 20 ++ imxrt1040evk/build.rs | 8 + imxrt1040evk/examples/smoke.rs | 36 +++ imxrt1040evk/src/lib.rs | 7 + imxrt1040evk/src/main.rs | 25 ++ imxrt1160/Cargo.toml | 8 + imxrt1160/src/lib.rs | 20 ++ imxrt1160evk/Cargo.toml | 12 + imxrt1160evk/src/lib.rs | 6 + imxrt1160evk/src/main.rs | 25 ++ imxrt1170/Cargo.toml | 9 + imxrt1170/src/lib.rs | 20 ++ imxrt1170evk/Cargo.toml | 20 ++ imxrt1170evk/build.rs | 8 + imxrt1170evk/examples/smoke.rs | 35 +++ imxrt1170evk/src/lib.rs | 6 + imxrt1170evk/src/main.rs | 25 ++ link.x | 4 + src/flash/adesto.rs | 7 + src/flash/issi.rs | 101 +++++++ src/flash/winbond.rs | 92 +++++++ src/imxrt10xx.rs | 100 +++++++ src/imxrt11xx.rs | 218 +++++++++++++++ src/lib.rs | 479 +++++++++++++++++++++++++++++++++ src/sequences/common.rs | 82 ++++++ 43 files changed, 2538 insertions(+) create mode 100644 .cargo/flash_algo.toml create mode 100644 .cargo/imxrt1010.toml create mode 100644 .cargo/imxrt1040.toml create mode 100644 .cargo/imxrt1170.toml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 imxrt1010/Cargo.toml create mode 100644 imxrt1010/src/lib.rs create mode 100644 imxrt1010evk/Cargo.toml create mode 100644 imxrt1010evk/build.rs create mode 100644 imxrt1010evk/examples/smoke.rs create mode 100644 imxrt1010evk/src/lib.rs create mode 100644 imxrt1010evk/src/main.rs create mode 100644 imxrt1040/Cargo.toml create mode 100644 imxrt1040/src/lib.rs create mode 100644 imxrt1040evk/Cargo.toml create mode 100644 imxrt1040evk/build.rs create mode 100644 imxrt1040evk/examples/smoke.rs create mode 100644 imxrt1040evk/src/lib.rs create mode 100644 imxrt1040evk/src/main.rs create mode 100644 imxrt1160/Cargo.toml create mode 100644 imxrt1160/src/lib.rs create mode 100644 imxrt1160evk/Cargo.toml create mode 100644 imxrt1160evk/src/lib.rs create mode 100644 imxrt1160evk/src/main.rs create mode 100644 imxrt1170/Cargo.toml create mode 100644 imxrt1170/src/lib.rs create mode 100644 imxrt1170evk/Cargo.toml create mode 100644 imxrt1170evk/build.rs create mode 100644 imxrt1170evk/examples/smoke.rs create mode 100644 imxrt1170evk/src/lib.rs create mode 100644 imxrt1170evk/src/main.rs create mode 100644 link.x create mode 100644 src/flash/adesto.rs create mode 100644 src/flash/issi.rs create mode 100644 src/flash/winbond.rs create mode 100644 src/imxrt10xx.rs create mode 100644 src/imxrt11xx.rs create mode 100644 src/lib.rs create mode 100644 src/sequences/common.rs 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 = imxrt10xx::Algorithm; 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, §or); + + 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; 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 = 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 = imxrt10xx::Algorithm; 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, §or); + + 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; 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 = imxrt11xx::Algorithm; 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; 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 = imxrt11xx::Algorithm; 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, §or); + + 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; 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; + +impl ImxrtFlashAlgorithm + for Issi +{ + 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; + +impl ImxrtFlashAlgorithm for Winbond { + 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(PhantomData<(C, F)>); + +impl Algorithm { + 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 flash_algorithm::FlashAlgorithm + for Algorithm +{ + fn new( + _: u32, + _: u32, + _: flash_algorithm::Function, + ) -> Result { + 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 Drop for Algorithm { + 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 = NonZero::new(1).unwrap(); + +const M7_DIVIDER: NonZero = NO_DIVIDER; +const BUS_DIVIDER: NonZero = NO_DIVIDER; +const FLEXSPI1_DIVIDER: NonZero = 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(PhantomData<(C, F)>); + +impl Algorithm { + 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 flash_algorithm::FlashAlgorithm + for Algorithm +{ + fn new( + _: u32, + _: u32, + _: flash_algorithm::Function, + ) -> Result { + 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 Drop for Algorithm { + 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 { + 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 { + 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::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 { + 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::(); + let txmrk_bytes = tfdr_bytes.min(fifo_capacity_bytes); + let txwmrk = txmrk_bytes.div_ceil(size_of::()).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 { + 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::() / size_of::() }; + + let words = data.chunks(size_of::()).map(|words| { + let mut scratch = [0_u8; size_of::()]; + 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::()); + + 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(_: *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 { + 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::()); + + 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) +}; -- cgit v1.2.3