From 86ff0202bf5c5f5c6a5b52be8e71f53e6e9ba0bd Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 23 Jul 2020 14:42:58 +0200 Subject: [PATCH 1/4] Refactor `blethrs` into a library. Add `stm32-eth` implementation. This separates the flash, bootload and command-related items of `blethrs` into a separate `blethrs` library. The original `blethrs` bin target has been renamed to `blethrs-bin` and has been refactored to depend on the new `blethrs` library. A new `blethrs-stm32-eth` bin target has been added that demonstrates using the `stm32-eth` crate for handling the ethernet driver and providing the smoltcp integration. The `UserConfig::write_to_semihosting` method has been removed in favour of a `ufmt::uDisplay` implementation for `UserConfig` that has is now gated behind a `ufmt` feature (disabled by default). --- .gitignore | 2 +- Cargo.lock | 553 --------------------------- Cargo.toml | 29 +- blethrs-bin/Cargo.toml | 21 + blethrs-bin/Embed.toml | 13 + build.rs => blethrs-bin/build.rs | 0 memory.x => blethrs-bin/memory.x | 0 {src => blethrs-bin/src}/ethernet.rs | 5 +- {src => blethrs-bin/src}/main.rs | 201 ++++++---- {src => blethrs-bin/src}/network.rs | 21 +- blethrs-stm32-eth/Cargo.toml | 17 + blethrs-stm32-eth/Embed.toml | 13 + blethrs-stm32-eth/build.rs | 19 + blethrs-stm32-eth/memory.x | 5 + blethrs-stm32-eth/src/main.rs | 264 +++++++++++++ blethrs/Cargo.toml | 12 + {src => blethrs/src}/bootload.rs | 28 +- blethrs/src/cmd.rs | 133 +++++++ {src => blethrs/src}/flash.rs | 96 ++--- blethrs/src/lib.rs | 23 ++ blethrs/src/ufmt.rs | 37 ++ src/config.rs | 114 ------ 22 files changed, 758 insertions(+), 848 deletions(-) delete mode 100644 Cargo.lock create mode 100644 blethrs-bin/Cargo.toml create mode 100644 blethrs-bin/Embed.toml rename build.rs => blethrs-bin/build.rs (100%) rename memory.x => blethrs-bin/memory.x (100%) rename {src => blethrs-bin/src}/ethernet.rs (99%) rename {src => blethrs-bin/src}/main.rs (52%) rename {src => blethrs-bin/src}/network.rs (94%) create mode 100644 blethrs-stm32-eth/Cargo.toml create mode 100644 blethrs-stm32-eth/Embed.toml create mode 100644 blethrs-stm32-eth/build.rs create mode 100644 blethrs-stm32-eth/memory.x create mode 100644 blethrs-stm32-eth/src/main.rs create mode 100644 blethrs/Cargo.toml rename {src => blethrs/src}/bootload.rs (60%) create mode 100644 blethrs/src/cmd.rs rename {src => blethrs/src}/flash.rs (73%) create mode 100644 blethrs/src/lib.rs create mode 100644 blethrs/src/ufmt.rs delete mode 100644 src/config.rs diff --git a/.gitignore b/.gitignore index 70e3cae..a18277b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ - /target +Cargo.lock **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 557a0c5..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,553 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aligned" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "as-slice" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bare-metal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blethrs" -version = "0.1.3" -dependencies = [ - "built 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32f4 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ufmt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "built" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cargo-lock 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cargo-lock" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cc" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "chrono" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cortex-m" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cortex-m-rt" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cortex-m-rt-macros" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cortex-m-semihosting" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "git2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "jobserver" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libc" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libgit2-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", - "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libz-sys" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "managed" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num-integer" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "panic-halt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pkg-config" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro-hack" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "r0" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smoltcp" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "stm32f4" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "toml" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ufmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", - "ufmt-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ufmt-write 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ufmt-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ufmt-write" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "vcell" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vcpkg" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "volatile-register" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" -"checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum built 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20c01a8375121157fc0a9643af2d045272d86916d9a3e2cb491096971bdaf1c1" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cargo-lock 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8504b63dd1249fd1745b7b4ef9b6f7b107ddeb3c95370043c7dbcc38653a2679" -"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" -"checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" -"checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" -"checksum cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647" -"checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" -"checksum git2 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77519ef7c5beee314d0804d4534f01e0f9e8d9acdee2b7a48627e590b27e0ec4" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" -"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" -"checksum libgit2-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ec6bca50549d34a392611dde775123086acbd994e3fff64954777ce2dc2e51" -"checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" -"checksum proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" -"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" -"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" -"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" -"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" -"checksum smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" -"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum stm32f4 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44a3d6c58b14e63926273694e7dd644894513c5e35ce6928c4657ddb62cae976" -"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -"checksum ufmt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e7ecea7ef79d3f8f878eee614afdf5256475c63ad76139d4da6125617c784a0" -"checksum ufmt-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed813e34a2bfa9dc58ee2ed8c8314d25e6d70c911486d64b8085cb695cfac069" -"checksum ufmt-write 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" -"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" -"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" -"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 275511b..4096d02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,26 +1,9 @@ -[package] -name = "blethrs" -version = "0.1.3" -authors = ["Adam Greig "] - -[dependencies] -cortex-m = "0.6.2" -cortex-m-rt = "0.6.12" -cortex-m-semihosting = "0.3.5" -panic-halt = "0.2.0" -ufmt = "0.1.0" - -[dependencies.smoltcp] -version = "0.6.0" -default-features = false -features = ["proto-ipv4", "socket-tcp", "ethernet"] - -[dependencies.stm32f4] -version = "0.10.0" -features = ["stm32f407", "rt"] - -[build-dependencies] -built = { version = "0.4.0", features = ["git2", "chrono"] } +[workspace] +members = [ + "blethrs-bin", + "blethrs-stm32-eth", + "blethrs", +] [profile.release] debug = true diff --git a/blethrs-bin/Cargo.toml b/blethrs-bin/Cargo.toml new file mode 100644 index 0000000..79f70be --- /dev/null +++ b/blethrs-bin/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "blethrs-bin" +version = "0.1.3" +authors = ["Adam Greig "] + +[dependencies] +blethrs = { path = "../blethrs" } +cortex-m = "0.6.2" +cortex-m-rt = "0.6.12" +cortex-m-semihosting = "0.3.5" +panic-rtt-target = { version = "0.1", features = ["cortex-m"] } +rtt-target = { version = "0.2.0", features = ["cortex-m"] } +stm32f4xx-hal = { version = "0.8.3", features = ["stm32f407", "rt"] } + +[dependencies.smoltcp] +version = "0.6.0" +default-features = false +features = ["proto-ipv4", "socket-tcp", "ethernet"] + +[build-dependencies] +built = { version = "0.4.0", features = ["git2", "chrono"] } diff --git a/blethrs-bin/Embed.toml b/blethrs-bin/Embed.toml new file mode 100644 index 0000000..f14e673 --- /dev/null +++ b/blethrs-bin/Embed.toml @@ -0,0 +1,13 @@ +[default.probe] +protocol = "Swd" + +[default.flashing] +enabled = true + +[default.general] +chip = "STM32F407VGTx" + +[default.rtt] +enabled = true +show_timestamps = true +timeout = 3000 diff --git a/build.rs b/blethrs-bin/build.rs similarity index 100% rename from build.rs rename to blethrs-bin/build.rs diff --git a/memory.x b/blethrs-bin/memory.x similarity index 100% rename from memory.x rename to blethrs-bin/memory.x diff --git a/src/ethernet.rs b/blethrs-bin/src/ethernet.rs similarity index 99% rename from src/ethernet.rs rename to blethrs-bin/src/ethernet.rs index a06f469..dc86e42 100644 --- a/src/ethernet.rs +++ b/blethrs-bin/src/ethernet.rs @@ -1,6 +1,7 @@ use core; use cortex_m; -use stm32f407; +use crate::ETH_PHY_ADDR; +use stm32f4xx_hal::stm32 as stm32f407; use smoltcp::{self, phy::{self, DeviceCapabilities}, time::Instant, wire::EthernetAddress}; @@ -8,8 +9,6 @@ const ETH_BUF_SIZE: usize = 1536; const ETH_NUM_TD: usize = 4; const ETH_NUM_RD: usize = 4; -use ::config::ETH_PHY_ADDR; - /// Transmit Descriptor representation /// /// * tdes0: ownership bit and transmit settings diff --git a/src/main.rs b/blethrs-bin/src/main.rs similarity index 52% rename from src/main.rs rename to blethrs-bin/src/main.rs index dd23e80..1aa4eab 100644 --- a/src/main.rs +++ b/blethrs-bin/src/main.rs @@ -1,56 +1,22 @@ #![no_std] #![no_main] +extern crate blethrs; extern crate cortex_m; extern crate cortex_m_rt; extern crate cortex_m_semihosting; -extern crate panic_halt; -extern crate stm32f4; +extern crate panic_rtt_target; +extern crate rtt_target; +extern crate stm32f4xx_hal; extern crate smoltcp; -extern crate ufmt; +use blethrs::{flash, Error}; use cortex_m_rt::{entry, exception}; -use stm32f4::stm32f407; - - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Error { - Success, - InvalidAddress, - LengthNotMultiple4, - LengthTooLong, - DataLengthIncorrect, - EraseError, - WriteError, - FlashError, - NetworkError, - InternalError, -} - -pub type Result = core::result::Result; - -/// Try to print over semihosting if a debugger is available -#[macro_export] -macro_rules! print { - ($($arg:expr),*) => ({ - if unsafe { (*cortex_m::peripheral::DCB::ptr()).dhcsr.read() & 1 == 1 } { - match cortex_m_semihosting::hio::hstdout() { - Ok(mut stdout) => { - $( - stdout.write_all($arg.as_bytes()).ok(); - )* - }, - Err(_) => () - } - } - }) -} +use rtt_target::{rprintln, rtt_init_print}; +use stm32f4xx_hal::stm32 as stm32f407; -mod config; mod ethernet; mod network; -mod flash; -mod bootload; // Pull in build information (from `built` crate) mod build_info { @@ -58,6 +24,99 @@ mod build_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } +// Default configuration. +mod default { + const MAC_ADDR: [u8; 6] = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + const IP_ADDR: [u8; 4] = [169, 254, 141, 210]; + const IP_GATE: [u8; 4] = [IP_ADDR[0], IP_ADDR[1], IP_ADDR[2], 1]; + const IP_PREFIX: u8 = 24; + + pub fn config() -> blethrs::flash::UserConfig { + blethrs::flash::UserConfig::new(MAC_ADDR, IP_ADDR, IP_GATE, IP_PREFIX) + } +} + +/// TCP port to listen on +const TCP_PORT: u16 = 7777; +/// PHY address +const ETH_PHY_ADDR: u8 = 0; + +/// Only enter the user App if PD2 is high. +/// +/// Check if PD2 is LOW for at least a full byte period of the UART, indicating someone has +/// connected 3V to the external connector. +fn app_entry_cond(peripherals: &mut stm32f407::Peripherals) -> bool { + peripherals.RCC.ahb1enr.modify(|_, w| w.gpioden().enabled()); + peripherals.GPIOD.moder.modify(|_, w| w.moder2().input()); + let hsi_clk = 16_000_000; + let sync_baud = 1_000_000; + let bit_periods = 10; + let delay = (hsi_clk / sync_baud) * bit_periods; + let mut cond = true; + for _ in 0..delay { + cond &= peripherals.GPIOD.idr.read().idr2().bit_is_clear(); + } + peripherals.RCC.ahb1enr.modify(|_, w| w.gpioden().disabled()); + !cond +} + +/// Set up GPIOs for ethernet. +/// +/// You should enable 9 GPIOs used by the ethernet controller. All GPIO clocks are already enabled. +/// This is also a sensible place to turn on an LED or similar to indicate bootloader mode. +fn configure_gpio(peripherals: &mut stm32f407::Peripherals) { + let gpioa = &peripherals.GPIOA; + let gpiob = &peripherals.GPIOB; + let gpioc = &peripherals.GPIOC; + let gpioe = &peripherals.GPIOE; + + // Status LED + gpioe.moder.modify(|_, w| w.moder7().output()); + gpioe.odr.modify(|_, w| w.odr7().clear_bit()); + + // Configure ethernet related GPIO: + // GPIOA 1, 2, 7 + // GPIOC 1, 4, 5 + // GPIOG 11, 13, 14 + // All set to AF11 and very high speed. + gpioa.moder.modify(|_, w| + w.moder1().alternate() + .moder2().alternate() + .moder7().alternate()); + gpiob.moder.modify(|_, w| + w.moder11().alternate() + .moder12().alternate() + .moder13().alternate()); + gpioc.moder.modify(|_, w| + w.moder1().alternate() + .moder4().alternate() + .moder5().alternate()); + gpioa.ospeedr.modify(|_, w| + w.ospeedr1().very_high_speed() + .ospeedr2().very_high_speed() + .ospeedr7().very_high_speed()); + gpiob.ospeedr.modify(|_, w| + w.ospeedr11().very_high_speed() + .ospeedr12().very_high_speed() + .ospeedr13().very_high_speed()); + gpioc.ospeedr.modify(|_, w| + w.ospeedr1().very_high_speed() + .ospeedr4().very_high_speed() + .ospeedr5().very_high_speed()); + gpioa.afrl.modify(|_, w| + w.afrl1().af11() + .afrl2().af11() + .afrl7().af11()); + gpiob.afrh.modify(|_, w| + w.afrh11().af11() + .afrh12().af11() + .afrh13().af11()); + gpioc.afrl.modify(|_, w| + w.afrl1().af11() + .afrl4().af11() + .afrl5().af11()); +} + /// Set up PLL to 168MHz from 16MHz HSI fn rcc_init(peripherals: &mut stm32f407::Peripherals) { let rcc = &peripherals.RCC; @@ -150,69 +209,73 @@ fn main() -> ! { let mut peripherals = stm32f407::Peripherals::take().unwrap(); let mut core_peripherals = stm32f407::CorePeripherals::take().unwrap(); + rtt_init_print!(); + // Jump to user code if it exists and hasn't asked us to run match flash::valid_user_code() { - Some(address) => if !config::should_enter_bootloader(&mut peripherals) { - bootload::bootload(&mut core_peripherals.SCB, address); + Some(address) => if !blethrs::bootload::should_enter(&mut peripherals.RCC) { + if app_entry_cond(&mut peripherals) { + blethrs::bootload::bootload(&mut core_peripherals.SCB, address); + } }, None => (), } - print!("\n|-=-=-=-=-=-=-=-=-= blethrs =-=-=-=-=-=-=-=-=-\n"); - print!("| Version ", build_info::PKG_VERSION, " ", build_info::GIT_VERSION.unwrap(), "\n"); - print!("| Platform ", build_info::TARGET, "\n"); - print!("| Built on ", build_info::BUILT_TIME_UTC, "\n"); - print!("| ", build_info::RUSTC_VERSION, "\n"); - print!("|-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n"); + rprintln!("\n|-=-=-=-=-=-=-=-=-= blethrs =-=-=-=-=-=-=-=-=-"); + rprintln!("| Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); + rprintln!("| Platform {}", build_info::TARGET); + rprintln!("| Built on {}", build_info::BUILT_TIME_UTC); + rprintln!("| {}", build_info::RUSTC_VERSION); + rprintln!("|-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); - print!( " Initialising clocks... "); + rprintln!( " Initialising clocks... "); rcc_init(&mut peripherals); - print!("OK\n"); + rprintln!("OK"); - print!( " Initialising GPIOs... "); - config::configure_gpio(&mut peripherals); - print!("OK\n"); + rprintln!( " Initialising GPIOs... "); + configure_gpio(&mut peripherals); + rprintln!("OK"); - print!( " Reading configuration... "); + rprintln!( " Reading configuration... "); let cfg = match flash::UserConfig::get(&mut peripherals.CRC) { - Some(cfg) => { print!("OK\n"); cfg }, + Some(cfg) => { rprintln!("OK"); cfg }, None => { - print!("Err\nCouldn't read configuration, using default.\n"); - flash::DEFAULT_CONFIG + rprintln!("No existing configuration. Using default."); + default::config() }, }; - cfg.write_to_semihosting(); + //cfg.write_to_semihosting(); let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&cfg.mac_address); - print!( " Initialising Ethernet... "); + rprintln!( " Initialising Ethernet... "); let mut ethdev = ethernet::EthernetDevice::new( peripherals.ETHERNET_MAC, peripherals.ETHERNET_DMA); ethdev.init(&mut peripherals.RCC, mac_addr.clone()); - print!("OK\n"); + rprintln!("OK"); - print!( " Waiting for link... "); + rprintln!( " Waiting for link... "); ethdev.block_until_link(); - print!("OK\n"); + rprintln!("OK"); - print!( " Initialising network... "); + rprintln!( " Initialising network... "); let ip_addr = smoltcp::wire::Ipv4Address::from_bytes(&cfg.ip_address); let ip_cidr = smoltcp::wire::Ipv4Cidr::new(ip_addr, cfg.ip_prefix); let cidr = smoltcp::wire::IpCidr::Ipv4(ip_cidr); network::init(ethdev, mac_addr.clone(), cidr); - print!("OK\n"); + rprintln!("OK"); // Move flash peripheral into flash module flash::init(peripherals.FLASH); // Turn on STATUS LED - print!(" Ready.\n\n"); + rprintln!(" Ready.\n"); // Begin periodic tasks via systick systick_init(&mut core_peripherals.SYST); loop { - cortex_m::asm::wfi(); + core::sync::atomic::spin_loop_hint(); } } @@ -226,8 +289,8 @@ fn SysTick() { network::poll(ticks as i64); match unsafe { core::ptr::read_volatile(&SYSTICK_RESET_AT) } { Some(reset_time) => if ticks >= reset_time { - print!("Performing scheduled reset\n"); - bootload::reset_bootload(); + rprintln!("Performing scheduled reset"); + blethrs::bootload::reset(); }, None => (), } diff --git a/src/network.rs b/blethrs-bin/src/network.rs similarity index 94% rename from src/network.rs rename to blethrs-bin/src/network.rs index e70f169..18992e4 100644 --- a/src/network.rs +++ b/blethrs-bin/src/network.rs @@ -6,19 +6,12 @@ use smoltcp::socket::{SocketSet, SocketSetItem, SocketHandle, TcpSocket, TcpSock use cortex_m; -use ::flash; +use blethrs::flash; +use crate::TCP_PORT; use ::build_info; use ::Error; use ethernet::EthernetDevice; -const CMD_INFO: u32 = 0; -const CMD_READ: u32 = 1; -const CMD_ERASE: u32 = 2; -const CMD_WRITE: u32 = 3; -const CMD_BOOT: u32 = 4; - -use ::config::TCP_PORT; - /// Read an address and length from the socket fn read_adr_len(socket: &mut TcpSocket) -> (u32, usize) { let mut adr = [0u8; 4]; @@ -206,11 +199,11 @@ pub fn poll(time_ms: i64) { socket.recv_slice(&mut cmd[..]).ok(); let cmd = u32::from_le_bytes(cmd); match cmd { - CMD_INFO => cmd_info(&mut socket), - CMD_READ => cmd_read(&mut socket), - CMD_ERASE => cmd_erase(&mut socket), - CMD_WRITE => cmd_write(&mut socket), - CMD_BOOT => cmd_boot(&mut socket), + blethrs::cmd::INFO => cmd_info(&mut socket), + blethrs::cmd::READ => cmd_read(&mut socket), + blethrs::cmd::ERASE => cmd_erase(&mut socket), + blethrs::cmd::WRITE => cmd_write(&mut socket), + blethrs::cmd::BOOT => cmd_boot(&mut socket), _ => (), }; socket.close(); diff --git a/blethrs-stm32-eth/Cargo.toml b/blethrs-stm32-eth/Cargo.toml new file mode 100644 index 0000000..a3b3d70 --- /dev/null +++ b/blethrs-stm32-eth/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "blethrs-stm32-eth" +version = "0.1.0" +authors = ["mitchmindtree "] +edition = "2018" + +[dependencies] +blethrs = { path = "../blethrs" } +cortex-m-rtic = "0.5.3" +panic-rtt-target = { version = "0.1", features = ["cortex-m"] } +rtt-target = { version = "0.2.0", features = ["cortex-m"] } +smoltcp = { version = "0.6", default-features = false } +stm32-eth = { git = "https://github.com/mitchmindtree/stm32-eth", branch = "stm32f4_setup_less_feats", features = ["smoltcp-phy", "stm32f407"] } +stm32f4xx-hal = { version = "0.8.3", default-features = false, features = ["rt", "stm32f407"] } + +[build-dependencies] +built = { version = "0.4.0", features = ["git2", "chrono"] } diff --git a/blethrs-stm32-eth/Embed.toml b/blethrs-stm32-eth/Embed.toml new file mode 100644 index 0000000..f14e673 --- /dev/null +++ b/blethrs-stm32-eth/Embed.toml @@ -0,0 +1,13 @@ +[default.probe] +protocol = "Swd" + +[default.flashing] +enabled = true + +[default.general] +chip = "STM32F407VGTx" + +[default.rtt] +enabled = true +show_timestamps = true +timeout = 3000 diff --git a/blethrs-stm32-eth/build.rs b/blethrs-stm32-eth/build.rs new file mode 100644 index 0000000..7a6f252 --- /dev/null +++ b/blethrs-stm32-eth/build.rs @@ -0,0 +1,19 @@ +extern crate built; + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Gather build information + built::write_built_file().expect("Failed to acquire build-time information"); + + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); +} diff --git a/blethrs-stm32-eth/memory.x b/blethrs-stm32-eth/memory.x new file mode 100644 index 0000000..0cee2bf --- /dev/null +++ b/blethrs-stm32-eth/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 48K + RAM : ORIGIN = 0x20000008, LENGTH = 131064 +} diff --git a/blethrs-stm32-eth/src/main.rs b/blethrs-stm32-eth/src/main.rs new file mode 100644 index 0000000..4fe308f --- /dev/null +++ b/blethrs-stm32-eth/src/main.rs @@ -0,0 +1,264 @@ +#![no_main] +#![no_std] + +use blethrs::flash::UserConfig; +use panic_rtt_target as _; +use rtic::app; +use rtic::cyccnt::U32Ext as CyccntU32Ext; +use rtt_target::{rtt_init_print, rprintln}; +use smoltcp::{ + iface::{EthernetInterfaceBuilder, Neighbor, NeighborCache}, + socket::{SocketHandle, SocketSetItem, TcpSocket, TcpSocketBuffer}, + time::Instant, + wire::{EthernetAddress, IpAddress, IpCidr}, +}; +use stm32_eth::{ + {EthPins, PhyAddress, RingEntry, RxDescriptor, TxDescriptor}, + hal::gpio::GpioExt, + hal::rcc::RccExt, + hal::time::U32Ext as TimeU32Ext, +}; + +// Pull in build information (from `built` crate). +mod build_info { + #![allow(dead_code)] + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +// Default values that may otherwise be configured via flash. +mod default { + const MAC_ADDR: [u8; 6] = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + const IP_ADDR: [u8; 4] = [169, 254, 141, 210]; + const IP_GATE: [u8; 4] = [IP_ADDR[0], IP_ADDR[1], IP_ADDR[2], 1]; + const IP_PREFIX: u8 = 24; + + pub fn config() -> blethrs::flash::UserConfig { + blethrs::flash::UserConfig::new(MAC_ADDR, IP_ADDR, IP_GATE, IP_PREFIX) + } +} + +type Eth = stm32_eth::Eth<'static, 'static>; +type EthernetInterface = smoltcp::iface::EthernetInterface<'static, 'static, 'static, &'static mut Eth>; +type SocketSet = smoltcp::socket::SocketSet<'static, 'static, 'static>; + +const CYCLE_HZ: u32 = 168_000_000; +const ONE_SEC: u32 = CYCLE_HZ; +const ONE_MS: u32 = ONE_SEC / 1_000; +const PORT: u16 = 7777; +const MTU: usize = 1536; + +#[app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + #[init(0)] + now_ms: u32, + #[init(None)] + reset_ms: Option, + eth_iface: EthernetInterface, + sockets: SocketSet, + server_handle: SocketHandle, + } + + #[init(schedule = [every_ms])] + fn init(mut cx: init::Context) -> init::LateResources { + rtt_init_print!(); + + let cause = match blethrs::flash::valid_user_code() { + Some(address) if !blethrs::bootload::should_enter(&mut cx.device.RCC) => { + rprintln!("Loading user program!"); + blethrs::bootload::bootload(&mut cx.core.SCB, address); + loop { + core::sync::atomic::spin_loop_hint(); + } + }, + Some(_addr) => "User indicated", + None => "Invalid user program", + }; + + rprintln!("Running in bootload mode. Cause: {}.", cause); + + rprintln!("Setup clocks"); + cx.core.DWT.enable_cycle_counter(); + let rcc = cx.device.RCC.constrain(); + let clocks = rcc.cfgr.sysclk(CYCLE_HZ.hz()).freeze(); + + rprintln!("Reading user config"); + let cfg = match UserConfig::get(&mut cx.device.CRC) { + Some(cfg) => cfg, + None => { + rprintln!(" No existing configuration. Using default."); + default::config() + }, + }; + + rprintln!("Setup ethernet"); + let gpioa = cx.device.GPIOA.split(); + let gpiob = cx.device.GPIOB.split(); + let gpioc = cx.device.GPIOC.split(); + let eth_pins = EthPins { + ref_clk: gpioa.pa1, + md_io: gpioa.pa2, + md_clk: gpioc.pc1, + crs: gpioa.pa7, + tx_en: gpiob.pb11, + tx_d0: gpiob.pb12, + tx_d1: gpiob.pb13, + rx_d0: gpioc.pc4, + rx_d1: gpioc.pc5, + }; + let eth = { + static mut RX_RING: Option<[RingEntry; 8]> = None; + static mut TX_RING: Option<[RingEntry; 2]> = None; + static mut ETH: Option = None; + unsafe { + RX_RING = Some(Default::default()); + TX_RING = Some(Default::default()); + let eth = Eth::new( + cx.device.ETHERNET_MAC, + cx.device.ETHERNET_DMA, + &mut RX_RING.as_mut().unwrap()[..], + &mut TX_RING.as_mut().unwrap()[..], + PhyAddress::_0, + clocks, + eth_pins, + ).unwrap(); + ETH = Some(eth); + ETH.as_mut().unwrap() + } + }; + eth.enable_interrupt(); + + rprintln!("Setup TCP/IP"); + let [a0, a1, a2, a3] = cfg.ip_address; + let ip_addr = IpCidr::new(IpAddress::v4(a0, a1, a2, a3), cfg.ip_prefix); + let (ip_addrs, neighbor_storage) = { + static mut IP_ADDRS: Option<[IpCidr; 1]> = None; + static mut NEIGHBOR_STORAGE: [Option<(IpAddress, Neighbor)>; 16] = [None; 16]; + unsafe { + IP_ADDRS = Some([ip_addr]); + (IP_ADDRS.as_mut().unwrap(), &mut NEIGHBOR_STORAGE) + } + }; + let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]); + let ethernet_addr = EthernetAddress(cfg.mac_address); + let eth_iface = EthernetInterfaceBuilder::new(eth) + .ethernet_addr(ethernet_addr) + .ip_addrs(&mut ip_addrs[..]) + .neighbor_cache(neighbor_cache) + .finalize(); + let (server_socket, mut sockets) = { + static mut RX_BUFFER: [u8; MTU] = [0; MTU]; + static mut TX_BUFFER: [u8; MTU] = [0; MTU]; + static mut SOCKETS_STORAGE: [Option; 2] = [None, None]; + unsafe { + let server_socket = TcpSocket::new( + TcpSocketBuffer::new(&mut RX_BUFFER[..]), + TcpSocketBuffer::new(&mut TX_BUFFER[..]), + ); + let sockets = SocketSet::new(&mut SOCKETS_STORAGE[..]); + (server_socket, sockets) + } + }; + let server_handle = sockets.add(server_socket); + + // Move flash peripheral into flash module + blethrs::flash::init(cx.device.FLASH); + + // Schedule the `blink` and `every_ms` tasks. + cx.schedule.every_ms(cx.start + ONE_MS.cycles()).unwrap(); + + rprintln!("Run!"); + init::LateResources { eth_iface, sockets, server_handle } + } + + #[task(resources = [now_ms, reset_ms], schedule = [every_ms])] + fn every_ms(mut cx: every_ms::Context) { + let r = &mut cx.resources; + *r.now_ms = r.now_ms.wrapping_add(1); + + // Check for a reset countdown. + if let Some(ref mut ms) = *r.reset_ms { + *ms = ms.saturating_sub(1); + if *ms == 0 { + blethrs::bootload::reset(); + } + } + + cx.schedule.every_ms(cx.scheduled + ONE_MS.cycles()).unwrap(); + } + + #[task(binds = ETH, resources = [eth_iface, now_ms, sockets, server_handle, reset_ms])] + fn eth(mut cx: eth::Context) { + let r = &mut cx.resources; + // Clear interrupt flags. + r.eth_iface.device_mut().interrupt_handler(); + poll_eth_iface(r.eth_iface, r.sockets, *r.server_handle, *r.now_ms, r.reset_ms); + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + core::sync::atomic::spin_loop_hint(); + } + } + + extern "C" { + fn EXTI0(); + } +}; + +fn build_info() -> blethrs::cmd::BuildInfo<'static> { + blethrs::cmd::BuildInfo { + pkg_version: build_info::PKG_VERSION, + git_version: build_info::GIT_VERSION.expect("no git version found"), + built_time_utc: build_info::BUILT_TIME_UTC, + rustc_version: build_info::RUSTC_VERSION, + } +} + +fn poll_eth_iface( + iface: &mut EthernetInterface, + sockets: &mut SocketSet, + server_handle: SocketHandle, + now_ms: u32, + reset_ms: &mut Option, +) { + { + let mut socket = sockets.get::(server_handle); + handle_tcp(&mut socket, reset_ms); + } + + let now = Instant::from_millis(now_ms as i64); + if let Err(e) = iface.poll(sockets, now) { + rprintln!("An error occurred when polling: {}", e); + } +} + +fn handle_tcp(socket: &mut TcpSocket, reset_ms: &mut Option) { + if !socket.is_open() { + if let Err(e) = socket.listen(PORT) { + panic!("failed to listen on port {} of TCP socket: {}", PORT, e); + } + } + + if !socket.may_recv() && socket.may_send() { + socket.close(); + } + + if socket.can_recv() { + let mut cmd = [0u8; 4]; + socket.recv_slice(&mut cmd[..]).ok(); + let cmd = u32::from_le_bytes(cmd); + let build_info = build_info(); + match blethrs::cmd::handle_and_respond(cmd, &build_info, socket) { + Ok(reboot) if reboot => { + rprintln!("Resetting..."); + *reset_ms = Some(50); + }, + Err(_e) => rprintln!("Received unknown command: {}", cmd), + _ => (), + } + + socket.close(); + } +} diff --git a/blethrs/Cargo.toml b/blethrs/Cargo.toml new file mode 100644 index 0000000..8ae3750 --- /dev/null +++ b/blethrs/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "blethrs" +version = "0.1.0" +authors = ["mitchmindtree "] +edition = "2018" + +[dependencies] +cortex-m = "0.6.2" +cortex-m-rt = "0.6.12" +smoltcp = { version = "0.6", default-features = false, features = ["proto-ipv4", "socket-tcp"] } +stm32f4xx-hal = { version = "0.8.3", default-features = false, features = ["stm32f407", "rt"] } +ufmt = { version = "0.1.0", optional = true } diff --git a/src/bootload.rs b/blethrs/src/bootload.rs similarity index 60% rename from src/bootload.rs rename to blethrs/src/bootload.rs index 9bce33b..9b136e0 100644 --- a/src/bootload.rs +++ b/blethrs/src/bootload.rs @@ -1,9 +1,31 @@ use core; use cortex_m; -use stm32f407; +use stm32f4xx_hal::stm32 as stm32f407; + +/// Magic value used in this module to check if bootloader should start. +pub const BOOTLOAD_FLAG_VALUE: u32 = 0xB00110AD; +/// Address of magic value used in this module to check if bootloader should start. +pub const BOOTLOAD_FLAG_ADDRESS: u32 = 0x2000_0000; static mut USER_RESET: Option = None; -use ::config::{BOOTLOAD_FLAG_VALUE, BOOTLOAD_FLAG_ADDRESS}; + +/// This function should return true if the bootloader should enter bootload mode, +/// or false to immediately chainload the user firmware. +/// +/// By default we check if there was a software reset and a magic value is set in RAM, +/// but you could also check GPIOs etc here. +/// +/// Ensure any state change to the peripherals is reset before returning from this function. +pub fn should_enter(rcc: &mut stm32f407::RCC) -> bool { + // Our plan is: + // * If the reset was a software reset, and the magic flag is in the magic location, + // then the user firmware requested bootload, so enter bootload. + // * Otherwise we check if PD2 is LOW for at least a full byte period of the UART, + // indicating someone has connected 3V to the external connector. + let was_sw_reset = was_software_reset(rcc); + let cond1 = was_sw_reset && flag_set(); + cond1 +} /// Returns true if the most recent reset was due to a software request /// @@ -32,7 +54,7 @@ fn clear_flag() { } /// Trigger a reset that will cause us to bootload the user application next go around -pub fn reset_bootload() { +pub fn reset() { clear_flag(); // It's troublesome to require SCB be passed in here, and // we're literally about to reset the whole microcontroller, diff --git a/blethrs/src/cmd.rs b/blethrs/src/cmd.rs new file mode 100644 index 0000000..22f2943 --- /dev/null +++ b/blethrs/src/cmd.rs @@ -0,0 +1,133 @@ +use crate::{flash, Error}; +use smoltcp::socket::TcpSocket; + +/// Information about the build running on the device. +/// +/// This can be trivially generated via the `built` crate. +pub struct BuildInfo<'a> { + pub pkg_version: &'a str, + pub git_version: &'a str, + pub built_time_utc: &'a str, + pub rustc_version: &'a str, +} + +pub struct UnknownCommand; + +pub const INFO: u32 = 0; +pub const READ: u32 = 1; +pub const ERASE: u32 = 2; +pub const WRITE: u32 = 3; +pub const BOOT: u32 = 4; + +/// Read an address and length from the socket +fn read_adr_len(socket: &mut TcpSocket) -> (u32, usize) { + let mut adr = [0u8; 4]; + let mut len = [0u8; 4]; + socket.recv_slice(&mut adr[..]).ok(); + socket.recv_slice(&mut len[..]).ok(); + let adr = u32::from_le_bytes(adr); + let len = u32::from_le_bytes(len); + (adr, len as usize) +} + +/// Send a status word back at the start of a response +fn send_status(socket: &mut TcpSocket, status: Error) { + let resp = (status as u32).to_le_bytes(); + socket.send_slice(&resp).unwrap(); +} + +/// Read device unique ID, return as array of 24 ASCII hex digits +pub fn get_hex_id() -> [u8; 24] { + static HEX_DIGITS: [u8; 16] = [ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 65, 66, 67, 68, 69, 70, + ]; + let id1: [u8; 4] = unsafe { *(0x1FFF_7A10 as *const u32) }.to_le_bytes(); + let id2: [u8; 4] = unsafe { *(0x1FFF_7A14 as *const u32) }.to_le_bytes(); + let id3: [u8; 4] = unsafe { *(0x1FFF_7A18 as *const u32) }.to_le_bytes(); + let id = [ + id3[3], id3[2], id3[1], id3[0], + id2[3], id2[2], id2[1], id2[0], + id1[3], id1[2], id1[1], id1[0], + ]; + let mut out = [0u8; 24]; + for (idx, v) in id.iter().enumerate() { + let v1 = v & 0x0F; + let v2 = (v & 0xF0) >> 4; + out[idx*2 ] = HEX_DIGITS[v2 as usize]; + out[idx*2+1] = HEX_DIGITS[v1 as usize]; + } + out +} + +/// Respond to the information request command with our build information. +pub fn info(build_info: &BuildInfo, socket: &mut TcpSocket) { + + send_status(socket, Error::Success); + + socket.send_slice("blethrs ".as_bytes()).ok(); + socket.send_slice(build_info.pkg_version.as_bytes()).ok(); + socket.send_slice(" ".as_bytes()).ok(); + socket.send_slice(build_info.git_version.as_bytes()).ok(); + socket.send_slice("\r\nBuilt: ".as_bytes()).ok(); + socket.send_slice(build_info.built_time_utc.as_bytes()).ok(); + socket.send_slice("\r\nCompiler: ".as_bytes()).ok(); + socket.send_slice(build_info.rustc_version.as_bytes()).ok(); + socket.send_slice("\r\nMCU ID: ".as_bytes()).ok(); + socket.send_slice(&get_hex_id()).ok(); + socket.send_slice("\r\n".as_bytes()).ok(); +} + +pub fn read(socket: &mut TcpSocket) { + let (adr, len) = read_adr_len(socket); + match flash::read(adr, len) { + Ok(data) => { + send_status(socket, Error::Success); + socket.send_slice(data).unwrap(); + }, + Err(err) => send_status(socket, err), + }; +} + +pub fn erase(socket: &mut TcpSocket) { + let (adr, len) = read_adr_len(socket); + match flash::erase(adr, len) { + Ok(()) => send_status(socket, Error::Success), + Err(err) => send_status(socket, err), + } +} + +pub fn write(socket: &mut TcpSocket) { + let (adr, len) = read_adr_len(socket); + match socket.recv(|buf| (buf.len(), flash::write(adr, len, buf))) { + Ok(Ok(())) => send_status(socket, Error::Success), + Ok(Err(err)) => send_status(socket, err), + Err(_) => send_status(socket, Error::NetworkError), + } +} + +pub fn boot(socket: &mut TcpSocket) { + send_status(socket, Error::Success); +} + +/// Respond to the given command. +/// +/// Returns whether or not rebooting (via `bootload::reset`) is required. +pub fn handle_and_respond( + cmd: u32, + build_info: &BuildInfo, + socket: &mut TcpSocket, +) -> Result { + match cmd { + INFO => info(build_info, socket), + READ => read(socket), + ERASE => erase(socket), + WRITE => write(socket), + BOOT => { + boot(socket); + return Ok(true); + }, + _ => return Err(UnknownCommand), + }; + Ok(false) +} diff --git a/src/flash.rs b/blethrs/src/flash.rs similarity index 73% rename from src/flash.rs rename to blethrs/src/flash.rs index 271d32f..62ff6fa 100644 --- a/src/flash.rs +++ b/blethrs/src/flash.rs @@ -1,14 +1,20 @@ use core; -use stm32f407; - -use ufmt::uwrite; -use ::{Error, Result}; - +use crate::{Error, Result}; +use stm32f4xx_hal::stm32 as stm32f407; const CONFIG_MAGIC: u32 = 0x67797870; -use ::config::{FLASH_SECTOR_ADDRESSES, FLASH_END, FLASH_CONFIG, FLASH_USER}; - +/// Start address of each sector in flash +pub const FLASH_SECTOR_ADDRESSES: [u32; 12] = + [0x0800_0000, 0x0800_4000, 0x0800_8000, 0x0800_C000, + 0x0801_0000, 0x0802_0000, 0x0804_0000, 0x0806_0000, + 0x0808_0000, 0x080A_0000, 0x080C_0000, 0x080E_0000]; +/// Final valid address in flash +pub const FLASH_END: u32 = 0x080F_FFFF; +/// Address of configuration sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. +pub const FLASH_CONFIG: u32 = FLASH_SECTOR_ADDRESSES[3]; +/// Address of user firmware sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. +pub const FLASH_USER: u32 = FLASH_SECTOR_ADDRESSES[4]; static mut FLASH: Option = None; @@ -32,69 +38,23 @@ pub struct UserConfig { } impl UserConfig { - pub fn write_to_semihosting(&self) { - if unsafe { (*cortex_m::peripheral::DCB::ptr()).dhcsr.read() & 1 == 0 } { return; } - let mut stdout = match cortex_m_semihosting::hio::hstdout() { - Ok(stdout) => stdout, - Err(_) => { return; }, - }; - let mut stdout = WriteAdapter(&mut stdout); - let mut hexbuf = [0u8; 2]; - uwrite!(stdout, " MAC Address: ",).ok(); - uwrite!(stdout, "{}:", u8_to_hex(self.mac_address[0], &mut hexbuf)).ok(); - uwrite!(stdout, "{}:", u8_to_hex(self.mac_address[1], &mut hexbuf)).ok(); - uwrite!(stdout, "{}:", u8_to_hex(self.mac_address[2], &mut hexbuf)).ok(); - uwrite!(stdout, "{}:", u8_to_hex(self.mac_address[3], &mut hexbuf)).ok(); - uwrite!(stdout, "{}:", u8_to_hex(self.mac_address[4], &mut hexbuf)).ok(); - uwrite!(stdout, "{}\n", u8_to_hex(self.mac_address[5], &mut hexbuf)).ok(); - uwrite!(stdout, " IP Address: {}.{}.{}.{}/{}\n", - self.ip_address[0], self.ip_address[1], self.ip_address[2], self.ip_address[3], - self.ip_prefix).ok(); - uwrite!(stdout, " Gateway: {}.{}.{}.{}\n", - self.ip_gateway[0], self.ip_gateway[1], self.ip_gateway[2], - self.ip_gateway[3]).ok(); - uwrite!(stdout, " Checksum: {}\n", self.checksum as u32).ok(); - } -} - -struct WriteAdapter(pub W) where W: core::fmt::Write; - -impl ufmt::uWrite for WriteAdapter where W: core::fmt::Write { - type Error = core::fmt::Error; - - fn write_char(&mut self, c: char) -> core::result::Result<(), Self::Error> { - self.0.write_char(c) - } - - fn write_str(&mut self, s: &str) -> core::result::Result<(), Self::Error> { - self.0.write_str(s) + pub fn new( + mac_address: [u8; 6], + ip_address: [u8; 4], + ip_gateway: [u8; 4], + ip_prefix: u8, + ) -> Self { + UserConfig { + magic: 0, + mac_address, + ip_address, + ip_gateway, + ip_prefix, + _padding: [0u8; 1], + checksum: 0, + } } -} -fn u8_to_hex(x: u8, buf: &mut [u8]) -> &str { - static HEX_DIGITS: [u8; 16] = [ - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 65, 66, 67, 68, 69, 70, - ]; - let v1 = x & 0x0F; - let v2 = (x & 0xF0) >> 4; - buf[0] = HEX_DIGITS[v2 as usize]; - buf[1] = HEX_DIGITS[v1 as usize]; - unsafe { core::str::from_utf8_unchecked(buf) } -} - -pub static DEFAULT_CONFIG: UserConfig = UserConfig { - // Locally administered MAC - magic: 0, - mac_address: [0x02, 0x00, 0x01, 0x02, 0x03, 0x04], - ip_address: [10, 1, 1, 10], - ip_gateway: [10, 1, 1, 1], - ip_prefix: 24, - _padding: [0u8; 1], - checksum: 0, -}; - -impl UserConfig { /// Attempt to read the UserConfig from flash sector 3 at 0x0800_C000. /// If a valid config cannot be read, the default one is returned instead. pub fn get(crc: &mut stm32f407::CRC) -> Option { diff --git a/blethrs/src/lib.rs b/blethrs/src/lib.rs new file mode 100644 index 0000000..b15092a --- /dev/null +++ b/blethrs/src/lib.rs @@ -0,0 +1,23 @@ +#![no_std] + +pub mod bootload; +pub mod cmd; +pub mod flash; +#[cfg(feature = "ufmt")] +mod ufmt; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Error { + Success, + InvalidAddress, + LengthNotMultiple4, + LengthTooLong, + DataLengthIncorrect, + EraseError, + WriteError, + FlashError, + NetworkError, + InternalError, +} + +pub type Result = core::result::Result; diff --git a/blethrs/src/ufmt.rs b/blethrs/src/ufmt.rs new file mode 100644 index 0000000..beed05b --- /dev/null +++ b/blethrs/src/ufmt.rs @@ -0,0 +1,37 @@ +use crate::UserConfig; +use ufmt::uwrite; + +fn u8_to_hex(x: u8, buf: &mut [u8]) -> &str { + static HEX_DIGITS: [u8; 16] = [ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 65, 66, 67, 68, 69, 70, + ]; + let v1 = x & 0x0F; + let v2 = (x & 0xF0) >> 4; + buf[0] = HEX_DIGITS[v2 as usize]; + buf[1] = HEX_DIGITS[v1 as usize]; + unsafe { core::str::from_utf8_unchecked(buf) } +} + +impl ufmt::uDisplay for UserConfig { + fn fmt(&self, f: &mut ufmt::Formatter) -> core::result::Result<(), W::Error> + where + W: ufmt::uWrite, + { + let mut hexbuf = [0u8; 2]; + uwrite!(f, " MAC Address: ",)?; + uwrite!(f, "{}:", u8_to_hex(self.mac_address[0], &mut hexbuf))?; + uwrite!(f, "{}:", u8_to_hex(self.mac_address[1], &mut hexbuf))?; + uwrite!(f, "{}:", u8_to_hex(self.mac_address[2], &mut hexbuf))?; + uwrite!(f, "{}:", u8_to_hex(self.mac_address[3], &mut hexbuf))?; + uwrite!(f, "{}:", u8_to_hex(self.mac_address[4], &mut hexbuf))?; + uwrite!(f, "{}\n", u8_to_hex(self.mac_address[5], &mut hexbuf))?; + uwrite!(f, " IP Address: {}.{}.{}.{}/{}\n", + self.ip_address[0], self.ip_address[1], self.ip_address[2], self.ip_address[3], + self.ip_prefix)?; + uwrite!(f, " Gateway: {}.{}.{}.{}\n", + self.ip_gateway[0], self.ip_gateway[1], self.ip_gateway[2], + self.ip_gateway[3])?; + uwrite!(f, " Checksum: {}\n", self.checksum as u32) + } +} diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 58c80a8..0000000 --- a/src/config.rs +++ /dev/null @@ -1,114 +0,0 @@ -//! Chip and board specific configuration settings go here. -use stm32f407; -use ::bootload; - -/// TCP port to listen on -pub const TCP_PORT: u16 = 7777; - -/// PHY address -pub const ETH_PHY_ADDR: u8 = 0; - -/// Start address of each sector in flash -pub const FLASH_SECTOR_ADDRESSES: [u32; 12] = - [0x0800_0000, 0x0800_4000, 0x0800_8000, 0x0800_C000, - 0x0801_0000, 0x0802_0000, 0x0804_0000, 0x0806_0000, - 0x0808_0000, 0x080A_0000, 0x080C_0000, 0x080E_0000]; -/// Final valid address in flash -pub const FLASH_END: u32 = 0x080F_FFFF; -/// Address of configuration sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. -pub const FLASH_CONFIG: u32 = FLASH_SECTOR_ADDRESSES[3]; -/// Address of user firmware sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. -pub const FLASH_USER: u32 = FLASH_SECTOR_ADDRESSES[4]; - -/// Magic value used in this module to check if bootloader should start. -pub const BOOTLOAD_FLAG_VALUE: u32 = 0xB00110AD; -/// Address of magic value used in this module to check if bootloader should start. -pub const BOOTLOAD_FLAG_ADDRESS: u32 = 0x2000_0000; - -/// This function should return true if the bootloader should enter bootload mode, -/// or false to immediately chainload the user firmware. -/// -/// By default we check if there was a software reset and a magic value is set in RAM, -/// but you could also check GPIOs etc here. -/// -/// Ensure any state change to the peripherals is reset before returning from this function. -pub fn should_enter_bootloader(peripherals: &mut stm32f407::Peripherals) -> bool { - // Our plan is: - // * If the reset was a software reset, and the magic flag is in the magic location, - // then the user firmware requested bootload, so enter bootload. - // * Otherwise we check if PD2 is LOW for at least a full byte period of the UART, - // indicating someone has connected 3V to the external connector. - let cond1 = bootload::was_software_reset(&mut peripherals.RCC) && bootload::flag_set(); - - peripherals.RCC.ahb1enr.modify(|_, w| w.gpioden().enabled()); - peripherals.GPIOD.moder.modify(|_, w| w.moder2().input()); - - let hsi_clk = 16_000_000; - let sync_baud = 1_000_000; - let bit_periods = 10; - let delay = (hsi_clk / sync_baud) * bit_periods; - let mut cond2 = true; - for _ in 0..delay { - cond2 &= peripherals.GPIOD.idr.read().idr2().bit_is_clear(); - } - - peripherals.RCC.ahb1enr.modify(|_, w| w.gpioden().disabled()); - cond1 || cond2 -} - -/// Set up GPIOs for ethernet. -/// -/// You should enable 9 GPIOs used by the ethernet controller. All GPIO clocks are already enabled. -/// This is also a sensible place to turn on an LED or similar to indicate bootloader mode. -pub fn configure_gpio(peripherals: &mut stm32f407::Peripherals) { - let gpioa = &peripherals.GPIOA; - let gpiob = &peripherals.GPIOB; - let gpioc = &peripherals.GPIOC; - let gpioe = &peripherals.GPIOE; - - // Status LED - gpioe.moder.modify(|_, w| w.moder7().output()); - gpioe.odr.modify(|_, w| w.odr7().clear_bit()); - - // Configure ethernet related GPIO: - // GPIOA 1, 2, 7 - // GPIOC 1, 4, 5 - // GPIOG 11, 13, 14 - // All set to AF11 and very high speed. - gpioa.moder.modify(|_, w| - w.moder1().alternate() - .moder2().alternate() - .moder7().alternate()); - gpiob.moder.modify(|_, w| - w.moder11().alternate() - .moder12().alternate() - .moder13().alternate()); - gpioc.moder.modify(|_, w| - w.moder1().alternate() - .moder4().alternate() - .moder5().alternate()); - gpioa.ospeedr.modify(|_, w| - w.ospeedr1().very_high_speed() - .ospeedr2().very_high_speed() - .ospeedr7().very_high_speed()); - gpiob.ospeedr.modify(|_, w| - w.ospeedr11().very_high_speed() - .ospeedr12().very_high_speed() - .ospeedr13().very_high_speed()); - gpioc.ospeedr.modify(|_, w| - w.ospeedr1().very_high_speed() - .ospeedr4().very_high_speed() - .ospeedr5().very_high_speed()); - gpioa.afrl.modify(|_, w| - w.afrl1().af11() - .afrl2().af11() - .afrl7().af11()); - gpiob.afrh.modify(|_, w| - w.afrh11().af11() - .afrh12().af11() - .afrh13().af11()); - gpioc.afrl.modify(|_, w| - w.afrl1().af11() - .afrl4().af11() - .afrl5().af11()); -} From 1973e7e359816106aae25def24c7e7098813044b Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 30 Jul 2020 15:40:20 +0200 Subject: [PATCH 2/4] Prepare for supporting other stm32f devices This begins the refactoring required in order to support devices other than the stm32f407. A module for the stm32f107 has been added however the feature is entirely untested and currently does not compile. TODO - Refactor device-specific flash code into stm-specific modules. - Address issue of sharing device-specific constants between blethrs and python script. --- blethrs-bin/Cargo.toml | 2 +- blethrs-bin/src/ethernet.rs | 2 +- blethrs-bin/src/main.rs | 4 ++-- blethrs-stm32-eth/Cargo.toml | 8 ++++++-- blethrs-stm32-eth/Embed.toml | 1 + blethrs-stm32-eth/src/main.rs | 4 ++-- blethrs/Cargo.toml | 7 ++++++- blethrs/src/bootload.rs | 13 +++++-------- blethrs/src/flash.rs | 14 +++++++------- blethrs/src/lib.rs | 9 +++++++++ blethrs/src/stm32f107.rs | 3 +++ blethrs/src/stm32f407.rs | 3 +++ 12 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 blethrs/src/stm32f107.rs create mode 100644 blethrs/src/stm32f407.rs diff --git a/blethrs-bin/Cargo.toml b/blethrs-bin/Cargo.toml index 79f70be..4a0a14a 100644 --- a/blethrs-bin/Cargo.toml +++ b/blethrs-bin/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.3" authors = ["Adam Greig "] [dependencies] -blethrs = { path = "../blethrs" } +blethrs = { path = "../blethrs", features = ["stm32f407"] } cortex-m = "0.6.2" cortex-m-rt = "0.6.12" cortex-m-semihosting = "0.3.5" diff --git a/blethrs-bin/src/ethernet.rs b/blethrs-bin/src/ethernet.rs index dc86e42..f7bf278 100644 --- a/blethrs-bin/src/ethernet.rs +++ b/blethrs-bin/src/ethernet.rs @@ -1,7 +1,7 @@ +use blethrs::stm32 as stm32f407; use core; use cortex_m; use crate::ETH_PHY_ADDR; -use stm32f4xx_hal::stm32 as stm32f407; use smoltcp::{self, phy::{self, DeviceCapabilities}, time::Instant, wire::EthernetAddress}; diff --git a/blethrs-bin/src/main.rs b/blethrs-bin/src/main.rs index 1aa4eab..30453d9 100644 --- a/blethrs-bin/src/main.rs +++ b/blethrs-bin/src/main.rs @@ -11,9 +11,9 @@ extern crate stm32f4xx_hal; extern crate smoltcp; use blethrs::{flash, Error}; +use blethrs::stm32 as stm32f407; use cortex_m_rt::{entry, exception}; use rtt_target::{rprintln, rtt_init_print}; -use stm32f4xx_hal::stm32 as stm32f407; mod ethernet; mod network; @@ -213,7 +213,7 @@ fn main() -> ! { // Jump to user code if it exists and hasn't asked us to run match flash::valid_user_code() { - Some(address) => if !blethrs::bootload::should_enter(&mut peripherals.RCC) { + Some(address) => if !blethrs::bootload::should_enter_bootloader(&mut peripherals.RCC) { if app_entry_cond(&mut peripherals) { blethrs::bootload::bootload(&mut core_peripherals.SCB, address); } diff --git a/blethrs-stm32-eth/Cargo.toml b/blethrs-stm32-eth/Cargo.toml index a3b3d70..2706fa8 100644 --- a/blethrs-stm32-eth/Cargo.toml +++ b/blethrs-stm32-eth/Cargo.toml @@ -10,8 +10,12 @@ cortex-m-rtic = "0.5.3" panic-rtt-target = { version = "0.1", features = ["cortex-m"] } rtt-target = { version = "0.2.0", features = ["cortex-m"] } smoltcp = { version = "0.6", default-features = false } -stm32-eth = { git = "https://github.com/mitchmindtree/stm32-eth", branch = "stm32f4_setup_less_feats", features = ["smoltcp-phy", "stm32f407"] } -stm32f4xx-hal = { version = "0.8.3", default-features = false, features = ["rt", "stm32f407"] } +stm32-eth = { git = "https://github.com/mitchmindtree/stm32-eth", branch = "stm32f4_setup_less_feats", default-features = false, features = ["smoltcp-phy"] } [build-dependencies] built = { version = "0.4.0", features = ["git2", "chrono"] } + +[features] +stm32f407 = ["blethrs/stm32f407", "stm32-eth/stm32f407"] +# TODO add F107 stm32-eth feature. +stm32f107 = ["blethrs/stm32f107"] diff --git a/blethrs-stm32-eth/Embed.toml b/blethrs-stm32-eth/Embed.toml index f14e673..acf2b03 100644 --- a/blethrs-stm32-eth/Embed.toml +++ b/blethrs-stm32-eth/Embed.toml @@ -6,6 +6,7 @@ enabled = true [default.general] chip = "STM32F407VGTx" +#chip = "STM32F107RC" [default.rtt] enabled = true diff --git a/blethrs-stm32-eth/src/main.rs b/blethrs-stm32-eth/src/main.rs index 4fe308f..022abc1 100644 --- a/blethrs-stm32-eth/src/main.rs +++ b/blethrs-stm32-eth/src/main.rs @@ -47,7 +47,7 @@ const ONE_MS: u32 = ONE_SEC / 1_000; const PORT: u16 = 7777; const MTU: usize = 1536; -#[app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +#[app(device = blethrs::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] const APP: () = { struct Resources { #[init(0)] @@ -64,7 +64,7 @@ const APP: () = { rtt_init_print!(); let cause = match blethrs::flash::valid_user_code() { - Some(address) if !blethrs::bootload::should_enter(&mut cx.device.RCC) => { + Some(address) if !blethrs::bootload::should_enter_bootloader(&mut cx.device.RCC) => { rprintln!("Loading user program!"); blethrs::bootload::bootload(&mut cx.core.SCB, address); loop { diff --git a/blethrs/Cargo.toml b/blethrs/Cargo.toml index 8ae3750..fe32485 100644 --- a/blethrs/Cargo.toml +++ b/blethrs/Cargo.toml @@ -8,5 +8,10 @@ edition = "2018" cortex-m = "0.6.2" cortex-m-rt = "0.6.12" smoltcp = { version = "0.6", default-features = false, features = ["proto-ipv4", "socket-tcp"] } -stm32f4xx-hal = { version = "0.8.3", default-features = false, features = ["stm32f407", "rt"] } +stm32f4xx-hal = { version = "0.8.3", default-features = false, features = ["stm32f407", "rt"], optional = true } +stm32f1xx-hal = { version = "0.6.1", default-features = false, features = ["stm32f107", "rt"], optional = true } ufmt = { version = "0.1.0", optional = true } + +[features] +stm32f407 = ["stm32f4xx-hal"] +stm32f107 = ["stm32f1xx-hal"] diff --git a/blethrs/src/bootload.rs b/blethrs/src/bootload.rs index 9b136e0..c466e08 100644 --- a/blethrs/src/bootload.rs +++ b/blethrs/src/bootload.rs @@ -1,6 +1,6 @@ +use crate::stm32; use core; use cortex_m; -use stm32f4xx_hal::stm32 as stm32f407; /// Magic value used in this module to check if bootloader should start. pub const BOOTLOAD_FLAG_VALUE: u32 = 0xB00110AD; @@ -16,21 +16,17 @@ static mut USER_RESET: Option = None; /// but you could also check GPIOs etc here. /// /// Ensure any state change to the peripherals is reset before returning from this function. -pub fn should_enter(rcc: &mut stm32f407::RCC) -> bool { +pub fn should_enter_bootloader(rcc: &mut stm32::RCC) -> bool { // Our plan is: // * If the reset was a software reset, and the magic flag is in the magic location, // then the user firmware requested bootload, so enter bootload. - // * Otherwise we check if PD2 is LOW for at least a full byte period of the UART, - // indicating someone has connected 3V to the external connector. - let was_sw_reset = was_software_reset(rcc); - let cond1 = was_sw_reset && flag_set(); - cond1 + was_software_reset(rcc) && flag_set() } /// Returns true if the most recent reset was due to a software request /// /// Clears the reset cause before returning, so this answer is only valid once. -pub fn was_software_reset(rcc: &mut stm32f407::RCC) -> bool { +pub fn was_software_reset(rcc: &mut stm32::RCC) -> bool { let result = rcc.csr.read().sftrstf().bit_is_set(); rcc.csr.modify(|_, w| w.rmvf().set_bit()); result @@ -54,6 +50,7 @@ fn clear_flag() { } /// Trigger a reset that will cause us to bootload the user application next go around +// TODO: Rewrite to accept SCB and use `sys_reset`. pub fn reset() { clear_flag(); // It's troublesome to require SCB be passed in here, and diff --git a/blethrs/src/flash.rs b/blethrs/src/flash.rs index 62ff6fa..988e57e 100644 --- a/blethrs/src/flash.rs +++ b/blethrs/src/flash.rs @@ -1,6 +1,6 @@ use core; use crate::{Error, Result}; -use stm32f4xx_hal::stm32 as stm32f407; +use crate::stm32; const CONFIG_MAGIC: u32 = 0x67797870; @@ -16,10 +16,10 @@ pub const FLASH_CONFIG: u32 = FLASH_SECTOR_ADDRESSES[3]; /// Address of user firmware sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. pub const FLASH_USER: u32 = FLASH_SECTOR_ADDRESSES[4]; -static mut FLASH: Option = None; +static mut FLASH: Option = None; /// Call to move the flash peripheral into this module -pub fn init(flash: stm32f407::FLASH) { +pub fn init(flash: stm32::FLASH) { unsafe { FLASH = Some(flash) }; } @@ -57,7 +57,7 @@ impl UserConfig { /// Attempt to read the UserConfig from flash sector 3 at 0x0800_C000. /// If a valid config cannot be read, the default one is returned instead. - pub fn get(crc: &mut stm32f407::CRC) -> Option { + pub fn get(crc: &mut stm32::CRC) -> Option { // Read config from flash let adr = FLASH_CONFIG as *const u32; let cfg = unsafe { *(FLASH_CONFIG as *const UserConfig) }; @@ -127,7 +127,7 @@ fn check_length_correct(length: usize, data: &[u8]) -> Result<()> { } /// Try to get the FLASH peripheral -fn get_flash_peripheral() -> Result<&'static mut stm32f407::FLASH> { +fn get_flash_peripheral() -> Result<&'static mut stm32::FLASH> { match unsafe { FLASH.as_mut() } { Some(flash) => Ok(flash), None => Err(Error::InternalError), @@ -135,7 +135,7 @@ fn get_flash_peripheral() -> Result<&'static mut stm32f407::FLASH> { } /// Try to unlock flash -fn unlock(flash: &mut stm32f407::FLASH) -> Result<()> { +fn unlock(flash: &mut stm32::FLASH) -> Result<()> { // Wait for any ongoing operations while flash.sr.read().bsy().bit_is_set() {} @@ -151,7 +151,7 @@ fn unlock(flash: &mut stm32f407::FLASH) -> Result<()> { } /// Lock flash -fn lock(flash: &mut stm32f407::FLASH) { +fn lock(flash: &mut stm32::FLASH) { flash.cr.write(|w| w.lock().locked()); } diff --git a/blethrs/src/lib.rs b/blethrs/src/lib.rs index b15092a..1f5c983 100644 --- a/blethrs/src/lib.rs +++ b/blethrs/src/lib.rs @@ -3,9 +3,18 @@ pub mod bootload; pub mod cmd; pub mod flash; +#[cfg(feature = "stm32f107")] +pub mod stm32f107; +#[cfg(feature = "stm32f407")] +pub mod stm32f407; #[cfg(feature = "ufmt")] mod ufmt; +#[cfg(feature = "stm32f107")] +pub use stm32f107 as stm32; +#[cfg(feature = "stm32f407")] +pub use stm32f407 as stm32; + #[derive(Clone, Copy, PartialEq, Eq)] pub enum Error { Success, diff --git a/blethrs/src/stm32f107.rs b/blethrs/src/stm32f107.rs new file mode 100644 index 0000000..5bcf682 --- /dev/null +++ b/blethrs/src/stm32f107.rs @@ -0,0 +1,3 @@ +pub use stm32f1xx_hal::stm32::*; + +// TODO: Add 107-specific flash code here. diff --git a/blethrs/src/stm32f407.rs b/blethrs/src/stm32f407.rs new file mode 100644 index 0000000..b40aeb7 --- /dev/null +++ b/blethrs/src/stm32f407.rs @@ -0,0 +1,3 @@ +pub use stm32f4xx_hal::stm32::*; + +// TODO: Add 407-specific flash code here. From 6b95d1e5d5a7fe2b5a9ebd97e629a445c3de8929 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 30 Jul 2020 17:28:21 +0200 Subject: [PATCH 3/4] Move firmware into an fw submodule --- {.cargo => fw/.cargo}/config | 0 fw/.gitignore | 3 ++ Cargo.toml => fw/Cargo.toml | 2 +- fw/README.md | 3 ++ {blethrs-bin => fw/blethrs-bin}/Cargo.toml | 0 {blethrs-bin => fw/blethrs-bin}/Embed.toml | 0 {blethrs-bin => fw/blethrs-bin}/build.rs | 0 {blethrs-bin => fw/blethrs-bin}/memory.x | 0 .../blethrs-bin}/src/ethernet.rs | 0 {blethrs-bin => fw/blethrs-bin}/src/main.rs | 0 .../blethrs-bin}/src/network.rs | 0 .../blethrs-stm32-eth}/Cargo.toml | 0 .../blethrs-stm32-eth}/Embed.toml | 0 .../blethrs-stm32-eth}/build.rs | 0 .../blethrs-stm32-eth}/memory.x | 0 .../blethrs-stm32-eth}/src/main.rs | 24 +++++++------- {blethrs => fw/blethrs}/Cargo.toml | 1 + {blethrs => fw/blethrs}/src/bootload.rs | 0 {blethrs => fw/blethrs}/src/cmd.rs | 31 ++++++------------- {blethrs => fw/blethrs}/src/flash.rs | 0 {blethrs => fw/blethrs}/src/lib.rs | 14 +-------- {blethrs => fw/blethrs}/src/stm32f107.rs | 0 {blethrs => fw/blethrs}/src/stm32f407.rs | 0 {blethrs => fw/blethrs}/src/ufmt.rs | 0 24 files changed, 31 insertions(+), 47 deletions(-) rename {.cargo => fw/.cargo}/config (100%) create mode 100644 fw/.gitignore rename Cargo.toml => fw/Cargo.toml (100%) create mode 100644 fw/README.md rename {blethrs-bin => fw/blethrs-bin}/Cargo.toml (100%) rename {blethrs-bin => fw/blethrs-bin}/Embed.toml (100%) rename {blethrs-bin => fw/blethrs-bin}/build.rs (100%) rename {blethrs-bin => fw/blethrs-bin}/memory.x (100%) rename {blethrs-bin => fw/blethrs-bin}/src/ethernet.rs (100%) rename {blethrs-bin => fw/blethrs-bin}/src/main.rs (100%) rename {blethrs-bin => fw/blethrs-bin}/src/network.rs (100%) rename {blethrs-stm32-eth => fw/blethrs-stm32-eth}/Cargo.toml (100%) rename {blethrs-stm32-eth => fw/blethrs-stm32-eth}/Embed.toml (100%) rename {blethrs-stm32-eth => fw/blethrs-stm32-eth}/build.rs (100%) rename {blethrs-stm32-eth => fw/blethrs-stm32-eth}/memory.x (100%) rename {blethrs-stm32-eth => fw/blethrs-stm32-eth}/src/main.rs (94%) rename {blethrs => fw/blethrs}/Cargo.toml (92%) rename {blethrs => fw/blethrs}/src/bootload.rs (100%) rename {blethrs => fw/blethrs}/src/cmd.rs (87%) rename {blethrs => fw/blethrs}/src/flash.rs (100%) rename {blethrs => fw/blethrs}/src/lib.rs (59%) rename {blethrs => fw/blethrs}/src/stm32f107.rs (100%) rename {blethrs => fw/blethrs}/src/stm32f407.rs (100%) rename {blethrs => fw/blethrs}/src/ufmt.rs (100%) diff --git a/.cargo/config b/fw/.cargo/config similarity index 100% rename from .cargo/config rename to fw/.cargo/config diff --git a/fw/.gitignore b/fw/.gitignore new file mode 100644 index 0000000..a18277b --- /dev/null +++ b/fw/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +**/*.rs.bk diff --git a/Cargo.toml b/fw/Cargo.toml similarity index 100% rename from Cargo.toml rename to fw/Cargo.toml index 4096d02..5018c5e 100644 --- a/Cargo.toml +++ b/fw/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ + "blethrs", "blethrs-bin", "blethrs-stm32-eth", - "blethrs", ] [profile.release] diff --git a/fw/README.md b/fw/README.md new file mode 100644 index 0000000..19b03ca --- /dev/null +++ b/fw/README.md @@ -0,0 +1,3 @@ +This directory contains firmware-specific crates. As a result, it contains its +own workspace with its own optimisation profile along with its own cargo profile +for linking. diff --git a/blethrs-bin/Cargo.toml b/fw/blethrs-bin/Cargo.toml similarity index 100% rename from blethrs-bin/Cargo.toml rename to fw/blethrs-bin/Cargo.toml diff --git a/blethrs-bin/Embed.toml b/fw/blethrs-bin/Embed.toml similarity index 100% rename from blethrs-bin/Embed.toml rename to fw/blethrs-bin/Embed.toml diff --git a/blethrs-bin/build.rs b/fw/blethrs-bin/build.rs similarity index 100% rename from blethrs-bin/build.rs rename to fw/blethrs-bin/build.rs diff --git a/blethrs-bin/memory.x b/fw/blethrs-bin/memory.x similarity index 100% rename from blethrs-bin/memory.x rename to fw/blethrs-bin/memory.x diff --git a/blethrs-bin/src/ethernet.rs b/fw/blethrs-bin/src/ethernet.rs similarity index 100% rename from blethrs-bin/src/ethernet.rs rename to fw/blethrs-bin/src/ethernet.rs diff --git a/blethrs-bin/src/main.rs b/fw/blethrs-bin/src/main.rs similarity index 100% rename from blethrs-bin/src/main.rs rename to fw/blethrs-bin/src/main.rs diff --git a/blethrs-bin/src/network.rs b/fw/blethrs-bin/src/network.rs similarity index 100% rename from blethrs-bin/src/network.rs rename to fw/blethrs-bin/src/network.rs diff --git a/blethrs-stm32-eth/Cargo.toml b/fw/blethrs-stm32-eth/Cargo.toml similarity index 100% rename from blethrs-stm32-eth/Cargo.toml rename to fw/blethrs-stm32-eth/Cargo.toml diff --git a/blethrs-stm32-eth/Embed.toml b/fw/blethrs-stm32-eth/Embed.toml similarity index 100% rename from blethrs-stm32-eth/Embed.toml rename to fw/blethrs-stm32-eth/Embed.toml diff --git a/blethrs-stm32-eth/build.rs b/fw/blethrs-stm32-eth/build.rs similarity index 100% rename from blethrs-stm32-eth/build.rs rename to fw/blethrs-stm32-eth/build.rs diff --git a/blethrs-stm32-eth/memory.x b/fw/blethrs-stm32-eth/memory.x similarity index 100% rename from blethrs-stm32-eth/memory.x rename to fw/blethrs-stm32-eth/memory.x diff --git a/blethrs-stm32-eth/src/main.rs b/fw/blethrs-stm32-eth/src/main.rs similarity index 94% rename from blethrs-stm32-eth/src/main.rs rename to fw/blethrs-stm32-eth/src/main.rs index 022abc1..02fd7eb 100644 --- a/blethrs-stm32-eth/src/main.rs +++ b/fw/blethrs-stm32-eth/src/main.rs @@ -2,6 +2,7 @@ #![no_std] use blethrs::flash::UserConfig; +use core::convert::TryFrom; use panic_rtt_target as _; use rtic::app; use rtic::cyccnt::U32Ext as CyccntU32Ext; @@ -67,9 +68,7 @@ const APP: () = { Some(address) if !blethrs::bootload::should_enter_bootloader(&mut cx.device.RCC) => { rprintln!("Loading user program!"); blethrs::bootload::bootload(&mut cx.core.SCB, address); - loop { - core::sync::atomic::spin_loop_hint(); - } + "" }, Some(_addr) => "User indicated", None => "Invalid user program", @@ -248,17 +247,18 @@ fn handle_tcp(socket: &mut TcpSocket, reset_ms: &mut Option) { if socket.can_recv() { let mut cmd = [0u8; 4]; socket.recv_slice(&mut cmd[..]).ok(); - let cmd = u32::from_le_bytes(cmd); - let build_info = build_info(); - match blethrs::cmd::handle_and_respond(cmd, &build_info, socket) { - Ok(reboot) if reboot => { - rprintln!("Resetting..."); - *reset_ms = Some(50); + let cmd_u32 = u32::from_le_bytes(cmd); + match blethrs::cmd::Command::try_from(cmd_u32) { + Err(_) => rprintln!("Received unknown command: {}", cmd_u32), + Ok(cmd) => { + let build_info = build_info(); + let reboot = blethrs::cmd::handle_and_respond(cmd, &build_info, socket); + if reboot { + rprintln!("Resetting..."); + *reset_ms = Some(50); + } }, - Err(_e) => rprintln!("Received unknown command: {}", cmd), - _ => (), } - socket.close(); } } diff --git a/blethrs/Cargo.toml b/fw/blethrs/Cargo.toml similarity index 92% rename from blethrs/Cargo.toml rename to fw/blethrs/Cargo.toml index fe32485..a52808a 100644 --- a/blethrs/Cargo.toml +++ b/fw/blethrs/Cargo.toml @@ -5,6 +5,7 @@ authors = ["mitchmindtree "] edition = "2018" [dependencies] +blethrs-shared = { path = "../../blethrs-shared" } cortex-m = "0.6.2" cortex-m-rt = "0.6.12" smoltcp = { version = "0.6", default-features = false, features = ["proto-ipv4", "socket-tcp"] } diff --git a/blethrs/src/bootload.rs b/fw/blethrs/src/bootload.rs similarity index 100% rename from blethrs/src/bootload.rs rename to fw/blethrs/src/bootload.rs diff --git a/blethrs/src/cmd.rs b/fw/blethrs/src/cmd.rs similarity index 87% rename from blethrs/src/cmd.rs rename to fw/blethrs/src/cmd.rs index 22f2943..0a0f9a0 100644 --- a/blethrs/src/cmd.rs +++ b/fw/blethrs/src/cmd.rs @@ -1,6 +1,8 @@ use crate::{flash, Error}; use smoltcp::socket::TcpSocket; +pub use blethrs_shared::Command; + /// Information about the build running on the device. /// /// This can be trivially generated via the `built` crate. @@ -11,14 +13,6 @@ pub struct BuildInfo<'a> { pub rustc_version: &'a str, } -pub struct UnknownCommand; - -pub const INFO: u32 = 0; -pub const READ: u32 = 1; -pub const ERASE: u32 = 2; -pub const WRITE: u32 = 3; -pub const BOOT: u32 = 4; - /// Read an address and length from the socket fn read_adr_len(socket: &mut TcpSocket) -> (u32, usize) { let mut adr = [0u8; 4]; @@ -113,21 +107,16 @@ pub fn boot(socket: &mut TcpSocket) { /// Respond to the given command. /// /// Returns whether or not rebooting (via `bootload::reset`) is required. -pub fn handle_and_respond( - cmd: u32, - build_info: &BuildInfo, - socket: &mut TcpSocket, -) -> Result { +pub fn handle_and_respond(cmd: Command, build_info: &BuildInfo, socket: &mut TcpSocket) -> bool { match cmd { - INFO => info(build_info, socket), - READ => read(socket), - ERASE => erase(socket), - WRITE => write(socket), - BOOT => { + Command::Info => info(build_info, socket), + Command::Read => read(socket), + Command::Erase => erase(socket), + Command::Write => write(socket), + Command::Boot => { boot(socket); - return Ok(true); + return true; }, - _ => return Err(UnknownCommand), }; - Ok(false) + false } diff --git a/blethrs/src/flash.rs b/fw/blethrs/src/flash.rs similarity index 100% rename from blethrs/src/flash.rs rename to fw/blethrs/src/flash.rs diff --git a/blethrs/src/lib.rs b/fw/blethrs/src/lib.rs similarity index 59% rename from blethrs/src/lib.rs rename to fw/blethrs/src/lib.rs index 1f5c983..ba25d18 100644 --- a/blethrs/src/lib.rs +++ b/fw/blethrs/src/lib.rs @@ -15,18 +15,6 @@ pub use stm32f107 as stm32; #[cfg(feature = "stm32f407")] pub use stm32f407 as stm32; -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Error { - Success, - InvalidAddress, - LengthNotMultiple4, - LengthTooLong, - DataLengthIncorrect, - EraseError, - WriteError, - FlashError, - NetworkError, - InternalError, -} +pub use blethrs_shared::Error; pub type Result = core::result::Result; diff --git a/blethrs/src/stm32f107.rs b/fw/blethrs/src/stm32f107.rs similarity index 100% rename from blethrs/src/stm32f107.rs rename to fw/blethrs/src/stm32f107.rs diff --git a/blethrs/src/stm32f407.rs b/fw/blethrs/src/stm32f407.rs similarity index 100% rename from blethrs/src/stm32f407.rs rename to fw/blethrs/src/stm32f407.rs diff --git a/blethrs/src/ufmt.rs b/fw/blethrs/src/ufmt.rs similarity index 100% rename from blethrs/src/ufmt.rs rename to fw/blethrs/src/ufmt.rs From 29e5ece07f3410b844c203683a2429343a59377b Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 30 Jul 2020 17:28:48 +0200 Subject: [PATCH 4/4] Add `blethrs-link` alternativey to `blethrs.py` This adds a new `blethrs-link` crate. The crate acts as a toolkit that can be used for communicating with a `blethrs` bootloader instance. The crate benefits by sharing declared constants (such as flash addresses, magic values and error types) with the bootloader itself via a new `blethrs-shared` crate. An example `demo` CLI program has been added that behaves like the python script. It should be noted that the example's `configure` command currently only supports hard-coded values and does not allow for specifying configuration parameters via CLI args. The `blethrs-link` CRC checksum generation does not yet match the CRC generation performed by the STM32F407. This could potentially be solved by copying the CRC implementation performed by `crcmod` in `blethrs.py`. --- blethrs-link/.gitignore | 3 + blethrs-link/Cargo.toml | 13 ++ blethrs-link/examples/demo.rs | 57 ++++++++ blethrs-link/src/lib.rs | 223 +++++++++++++++++++++++++++++++ fw/README.md | 2 +- fw/blethrs-shared/.gitignore | 3 + fw/blethrs-shared/Cargo.toml | 5 + fw/blethrs-shared/src/lib.rs | 79 +++++++++++ fw/blethrs-stm32-eth/src/main.rs | 2 + fw/blethrs/Cargo.toml | 2 +- fw/blethrs/src/bootload.rs | 12 +- fw/blethrs/src/flash.rs | 35 +++-- 12 files changed, 413 insertions(+), 23 deletions(-) create mode 100644 blethrs-link/.gitignore create mode 100644 blethrs-link/Cargo.toml create mode 100644 blethrs-link/examples/demo.rs create mode 100644 blethrs-link/src/lib.rs create mode 100644 fw/blethrs-shared/.gitignore create mode 100644 fw/blethrs-shared/Cargo.toml create mode 100644 fw/blethrs-shared/src/lib.rs diff --git a/blethrs-link/.gitignore b/blethrs-link/.gitignore new file mode 100644 index 0000000..a18277b --- /dev/null +++ b/blethrs-link/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +**/*.rs.bk diff --git a/blethrs-link/Cargo.toml b/blethrs-link/Cargo.toml new file mode 100644 index 0000000..41a8e80 --- /dev/null +++ b/blethrs-link/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "blethrs-link" +version = "0.1.0" +authors = ["mitchmindtree "] +edition = "2018" + +[dependencies] +blethrs-shared = { path = "../fw/blethrs-shared" } +crc = "1.8" +log = "0.4.11" + +[dev-dependencies] +env_logger = "0.7.1" diff --git a/blethrs-link/examples/demo.rs b/blethrs-link/examples/demo.rs new file mode 100644 index 0000000..97e8f93 --- /dev/null +++ b/blethrs-link/examples/demo.rs @@ -0,0 +1,57 @@ +use blethrs_link as link; +use blethrs_shared::{FLASH_CONFIG, FLASH_USER}; +use std::path::Path; +use std::net::{SocketAddr, SocketAddrV4}; + +// The max number of bytes of a binary file to send via TCP at once. +// Should consider your network's MTU. +const CHUNK_SIZE: usize = 512; + +fn main() { + env_logger::init(); + + let mut args = std::env::args(); + + let ip_addr_s = args.nth(1).expect("expected IP address string"); + let port_s = args.next().expect("expected port string"); + let cmd_s = args.next().expect("expected command string"); + + let addr: SocketAddr = format!("{}:{}", ip_addr_s, port_s) + .parse::() + .expect("failed to parse valid socket address") + .into(); + + println!("Connecting to bootloader..."); + let data = link::info_cmd(&addr).unwrap(); + let s = std::str::from_utf8(&data).unwrap(); + println!("{}", s); + + match &cmd_s[..] { + "program" => { + let bin_path_s = args.next().expect("expected path to binary"); + let bin_path = Path::new(&bin_path_s); + let chunk_size = CHUNK_SIZE; + let flash_addr = FLASH_USER; + let bin_data = std::fs::read(&bin_path).unwrap(); + link::write_file(&addr, chunk_size, flash_addr, bin_data).unwrap(); + } + "configure" => { + let cfg_flash_addr = FLASH_CONFIG; + // TODO: These are just for testing - take these via arguments. + let ip = [10, 101, 0, 1]; + let mac = [0x00, 0x00, 0xAB, 0xCD, ip[2], ip[3]]; + let gw = [ip[0], ip[1], ip[2], 0]; + let prefix = 16; + link::write_config(&addr, cfg_flash_addr, &mac, &ip, &gw, prefix).unwrap(); + } + _ => (), + } + + match &cmd_s[..] { + "boot" | "program" | "configure" => { + println!("Sending reboot command..."); + link::boot_cmd(&addr).unwrap(); + } + _ => (), + } +} diff --git a/blethrs-link/src/lib.rs b/blethrs-link/src/lib.rs new file mode 100644 index 0000000..fdee4a7 --- /dev/null +++ b/blethrs-link/src/lib.rs @@ -0,0 +1,223 @@ +use blethrs_shared::{Command, CONFIG_MAGIC}; +use crc::crc32::{self, Hasher32}; +use std::convert::TryFrom; +use std::hash::Hasher; +use std::net::{SocketAddr, TcpStream}; +use std::time::Duration; +use std::io::{self, Read, Write}; + +#[derive(Debug)] +pub enum Error { + /// Failed to connect to the TCP socket. + TcpConnect(std::io::Error), + /// Error reported by the bootloader. + Bootloader(blethrs_shared::Error), + /// The received response was ill-formatted or contained an unexpected value. + InvalidResponse, + /// A mismatch between the written and read-back data occurred. + ReadMismatch { + flash_addr: u32, + wrote: u8, + read: u8, + } +} + +// Send the given command to the specified address via TCP and check the response. +fn interact(addr: &SocketAddr, cmd_bytes: &[u8]) -> Result, Error> { + let timeout = Duration::from_secs(2); + let mut attempts = 3; + let mut s = loop { + match TcpStream::connect_timeout(addr, timeout) { + Ok(s) => break s, + Err(e) => match e.kind() { + // Sometimes we get connection refused if the MCU is still busy. + io::ErrorKind::ConnectionRefused if attempts > 0 => { + attempts -= 1; + std::thread::sleep(std::time::Duration::from_millis(10)); + } + _ => return Err(Error::TcpConnect(e)), + } + } + }; + s.write(cmd_bytes).expect("failed to write command"); + let mut data = [0u8; 2048]; + s.read_exact(&mut data[..]).ok(); + check_response(&data[..]).map(|data| data.to_vec()) +} + +// Check the response by reading the `blethrs_shared::Error` from the first 4 bytes. +fn check_response(data: &[u8]) -> Result<&[u8], Error> { + match data { + &[a, b, c, d, ..] => { + let bytes = [a, b, c, d]; + let u = u32::from_le_bytes(bytes); + match blethrs_shared::Error::try_from(u) { + Ok(blethrs_shared::Error::Success) => Ok(&data[bytes.len()..]), + Ok(err) => Err(Error::Bootloader(err)), + Err(_) => Err(Error::InvalidResponse), + } + } + _ => Err(Error::InvalidResponse), + } +} + +/// Submit a request for info about the device and the blethrs build installed. +/// +/// Resturns the bytes of what should be a UTF8 string. +pub fn info_cmd(addr: &SocketAddr) -> Result, Error> { + let b = (Command::Info as u32).to_le_bytes(); + interact(addr, &b[..]) +} + +/// Submit a request to erase a region of the device flash. +pub fn erase_cmd(socket_addr: &SocketAddr, flash_addr: u32, len: u32) -> Result<(), Error> { + let mut b = vec![]; + b.extend_from_slice(&(Command::Erase as u32).to_le_bytes()); + b.extend_from_slice(&flash_addr.to_le_bytes()); + b.extend_from_slice(&len.to_le_bytes()); + interact(socket_addr, &b[..])?; + Ok(()) +} + +/// Submit a request to read from a region of the device flash. +pub fn read_cmd(socket_addr: &SocketAddr, flash_addr: u32, len: u32) -> Result, Error> { + let mut b = vec![]; + b.extend_from_slice(&(Command::Read as u32).to_le_bytes()); + b.extend_from_slice(&flash_addr.to_le_bytes()); + b.extend_from_slice(&len.to_le_bytes()); + interact(socket_addr, &b[..]) +} + +/// Submit a request to write to a region of the device flash. +/// +/// This will attempt to send all data within a single TCP packet, as a result large chunks of data +/// should be first split into chunks (see `write_file`). +pub fn write_cmd(socket_addr: &SocketAddr, flash_addr: u32, data: &[u8]) -> Result<(), Error> { + let mut b = vec![]; + b.extend_from_slice(&(Command::Write as u32).to_le_bytes()); + b.extend_from_slice(&flash_addr.to_le_bytes()); + b.extend_from_slice(&(data.len() as u32).to_le_bytes()); + b.extend_from_slice(data); + interact(socket_addr, &b[..])?; + Ok(()) +} + +/// Submit a request to attempt to boot into the loaded program. +pub fn boot_cmd(addr: &SocketAddr) -> Result<(), Error> { + let b = (Command::Boot as u32).to_le_bytes(); + interact(addr, &b[..])?; + Ok(()) +} + +/// Write the given binary file to the specified region in flash. +pub fn write_file( + socket_addr: &SocketAddr, + chunk_size: usize, + flash_addr: u32, + mut data: Vec, +) -> Result<(), Error> { + // We need to write in multiples of 4 bytes (since writes are word-by-word), so add padding to + // the end of the data. + if data.len() % 4 != 0 { + let padding = 4 - data.len() % 4; + data.extend(vec![0xFF; padding]); + } + let mut segments = data.len() / chunk_size; + if data.len() % chunk_size != 0 { + segments += 1; + } + + log::info!("Erasing (may take a few seconds)..."); + erase_cmd(socket_addr, flash_addr, data.len() as u32)?; + + log::info!("Writing {:.2}kB in {} segments...", data.len() as f32 / 1024.0, segments); + for (seg_progress, seg_i) in (0..segments).rev().enumerate() { + let seg_addr = flash_addr + (seg_i * chunk_size) as u32; + let start = seg_i * chunk_size; + let end = std::cmp::min(start + chunk_size, data.len()); + let seg_data = &data[start..end]; + write_cmd(socket_addr, seg_addr, seg_data)?; + log::info!(" {:.2}%", ((seg_progress + 1) * 100) as f32 / segments as f32); + } + + log::info!("Writing completed successfully. Reading back..."); + for seg_i in 0..segments { + let seg_addr = flash_addr + (seg_i * chunk_size) as u32; + let start = seg_i * chunk_size; + let end = std::cmp::min(start + chunk_size, data.len()); + let seg_data = &data[start..end]; + let r_data = read_cmd(socket_addr, seg_addr, chunk_size as u32)?; + if seg_data != &r_data[..seg_data.len()] { + for (i, (&wrote, &read)) in seg_data.iter().zip(&r_data).enumerate() { + if wrote != read { + let flash_addr = seg_addr + i as u32; + return Err(Error::ReadMismatch { flash_addr, wrote, read }); + } + } + } + log::info!(" {:.2}%", ((seg_i + 1) * 100) as f32 / segments as f32); + } + + log::info!("Readback successful."); + Ok(()) +} + +/// Write the given device configuration to the specified flash address. +pub fn write_config( + socket_addr: &SocketAddr, + cfg_flash_addr: u32, + mac: &[u8; 6], + ip: &[u8; 4], + gw: &[u8; 4], + prefix: u8, +) -> Result<(), Error> { + let mut b = vec![]; + b.extend_from_slice(&CONFIG_MAGIC.to_le_bytes()); + b.extend_from_slice(mac); + b.extend_from_slice(ip); + b.extend_from_slice(gw); + b.push(prefix); + let padding = 0u8; + b.push(padding); + let crc = { + let polynomial = 0x04C11DB7; + let init = 0xFFFFFFFF; + let mut digest = crc32::Digest::new_with_initial(polynomial, init); + + // Cast to u32 words. + let us: &[u32] = unsafe { + let len = b.len() / std::mem::size_of::(); + let u_ptr = b.as_ptr() as *const u32; + std::slice::from_raw_parts(u_ptr, len) + }; + + // Write them with endianness swapped (copying the python script). + for &u in us { + let u = u.reverse_bits(); + digest.write_u32(u); + } + + digest.sum32() + }; + b.extend_from_slice(&crc.to_le_bytes()); + + log::info!("Erasing old configuration..."); + erase_cmd(socket_addr, cfg_flash_addr, b.len() as u32)?; + + log::info!("Writing new configuration..."); + write_cmd(socket_addr, cfg_flash_addr, &b)?; + + log::info!("Reading back new configuration..."); + let r = read_cmd(socket_addr, cfg_flash_addr, b.len() as u32)?; + if b != r { + for (i, (&wrote, &read)) in b.iter().zip(&r).enumerate() { + if wrote != read { + let flash_addr = cfg_flash_addr + i as u32; + return Err(Error::ReadMismatch { flash_addr, wrote, read }); + } + } + } + + log::info!("Readback successful."); + Ok(()) +} diff --git a/fw/README.md b/fw/README.md index 19b03ca..564d24e 100644 --- a/fw/README.md +++ b/fw/README.md @@ -1,3 +1,3 @@ -This directory contains firmware-specific crates. As a result, it contains its +This directory contains crates pertaining firmware. As a result, it contains its own workspace with its own optimisation profile along with its own cargo profile for linking. diff --git a/fw/blethrs-shared/.gitignore b/fw/blethrs-shared/.gitignore new file mode 100644 index 0000000..a18277b --- /dev/null +++ b/fw/blethrs-shared/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +**/*.rs.bk diff --git a/fw/blethrs-shared/Cargo.toml b/fw/blethrs-shared/Cargo.toml new file mode 100644 index 0000000..6df0c80 --- /dev/null +++ b/fw/blethrs-shared/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "blethrs-shared" +version = "0.1.0" +authors = ["mitchmindtree "] +edition = "2018" diff --git a/fw/blethrs-shared/src/lib.rs b/fw/blethrs-shared/src/lib.rs new file mode 100644 index 0000000..6d72834 --- /dev/null +++ b/fw/blethrs-shared/src/lib.rs @@ -0,0 +1,79 @@ +//! Items shared between `blethrs` (firmware) and `blethrs-link` (software). + +#![no_std] + +#[repr(u32)] +pub enum Command { + Info = 0, + Read = 1, + Erase = 2, + Write = 3, + Boot = 4, +} + +pub struct UnknownValue; + +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + Success = 0, + InvalidAddress = 1, + LengthNotMultiple4 = 2, + LengthTooLong = 3, + DataLengthIncorrect = 4, + EraseError = 5, + WriteError = 6, + FlashError = 7, + NetworkError = 8, + InternalError = 9, +} + +pub const CONFIG_MAGIC: u32 = 0x67797870; + +/// Start address of each sector in flash +pub const FLASH_SECTOR_ADDRESSES: [u32; 12] = [ + 0x0800_0000, 0x0800_4000, 0x0800_8000, 0x0800_C000, + 0x0801_0000, 0x0802_0000, 0x0804_0000, 0x0806_0000, + 0x0808_0000, 0x080A_0000, 0x080C_0000, 0x080E_0000, +]; +/// Final valid address in flash +pub const FLASH_END: u32 = 0x080F_FFFF; +/// Address of configuration sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. +pub const FLASH_CONFIG: u32 = FLASH_SECTOR_ADDRESSES[3]; +/// Address of user firmware sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. +pub const FLASH_USER: u32 = FLASH_SECTOR_ADDRESSES[4]; + +impl core::convert::TryFrom for Command { + type Error = UnknownValue; + fn try_from(u: u32) -> Result { + let cmd = match u { + 0 => Command::Info, + 1 => Command::Read, + 2 => Command::Erase, + 3 => Command::Write, + 4 => Command::Boot, + _ => return Err(UnknownValue), + }; + Ok(cmd) + } +} + +impl core::convert::TryFrom for Error { + type Error = UnknownValue; + fn try_from(u: u32) -> Result { + let cmd = match u { + 0 => Error::Success, + 1 => Error::InvalidAddress, + 2 => Error::LengthNotMultiple4, + 3 => Error::LengthTooLong, + 4 => Error::DataLengthIncorrect, + 5 => Error::EraseError, + 6 => Error::WriteError, + 7 => Error::FlashError, + 8 => Error::NetworkError, + 9 => Error::InternalError, + _ => return Err(UnknownValue), + }; + Ok(cmd) + } +} diff --git a/fw/blethrs-stm32-eth/src/main.rs b/fw/blethrs-stm32-eth/src/main.rs index 02fd7eb..a37e8dc 100644 --- a/fw/blethrs-stm32-eth/src/main.rs +++ b/fw/blethrs-stm32-eth/src/main.rs @@ -78,6 +78,8 @@ const APP: () = { rprintln!("Setup clocks"); cx.core.DWT.enable_cycle_counter(); + // Enable clock for CRC. + cx.device.RCC.ahb1enr.modify(|_, w| w.crcen().enabled()); let rcc = cx.device.RCC.constrain(); let clocks = rcc.cfgr.sysclk(CYCLE_HZ.hz()).freeze(); diff --git a/fw/blethrs/Cargo.toml b/fw/blethrs/Cargo.toml index a52808a..a47dd77 100644 --- a/fw/blethrs/Cargo.toml +++ b/fw/blethrs/Cargo.toml @@ -5,7 +5,7 @@ authors = ["mitchmindtree "] edition = "2018" [dependencies] -blethrs-shared = { path = "../../blethrs-shared" } +blethrs-shared = { path = "../blethrs-shared" } cortex-m = "0.6.2" cortex-m-rt = "0.6.12" smoltcp = { version = "0.6", default-features = false, features = ["proto-ipv4", "socket-tcp"] } diff --git a/fw/blethrs/src/bootload.rs b/fw/blethrs/src/bootload.rs index c466e08..b071224 100644 --- a/fw/blethrs/src/bootload.rs +++ b/fw/blethrs/src/bootload.rs @@ -20,7 +20,9 @@ pub fn should_enter_bootloader(rcc: &mut stm32::RCC) -> bool { // Our plan is: // * If the reset was a software reset, and the magic flag is in the magic location, // then the user firmware requested bootload, so enter bootload. - was_software_reset(rcc) && flag_set() + let cond = was_software_reset(rcc) && flag_set(); + clear_flag(); + cond } /// Returns true if the most recent reset was due to a software request @@ -38,11 +40,17 @@ pub fn was_software_reset(rcc: &mut stm32::RCC) -> bool { pub fn flag_set() -> bool { cortex_m::interrupt::free(|_| unsafe { let flag = core::ptr::read_volatile(BOOTLOAD_FLAG_ADDRESS as *const u32); - clear_flag(); flag == BOOTLOAD_FLAG_VALUE }) } +/// Sets the flag indicating that we should enter the bootloader next time the device boots. +pub fn set_flag() { + cortex_m::interrupt::free(|_| unsafe { + core::ptr::write_volatile(BOOTLOAD_FLAG_ADDRESS as *mut u32, BOOTLOAD_FLAG_VALUE); + }); +} + fn clear_flag() { cortex_m::interrupt::free(|_| unsafe { core::ptr::write_volatile(BOOTLOAD_FLAG_ADDRESS as *mut u32, 0); diff --git a/fw/blethrs/src/flash.rs b/fw/blethrs/src/flash.rs index 988e57e..5d7135c 100644 --- a/fw/blethrs/src/flash.rs +++ b/fw/blethrs/src/flash.rs @@ -2,19 +2,7 @@ use core; use crate::{Error, Result}; use crate::stm32; -const CONFIG_MAGIC: u32 = 0x67797870; - -/// Start address of each sector in flash -pub const FLASH_SECTOR_ADDRESSES: [u32; 12] = - [0x0800_0000, 0x0800_4000, 0x0800_8000, 0x0800_C000, - 0x0801_0000, 0x0802_0000, 0x0804_0000, 0x0806_0000, - 0x0808_0000, 0x080A_0000, 0x080C_0000, 0x080E_0000]; -/// Final valid address in flash -pub const FLASH_END: u32 = 0x080F_FFFF; -/// Address of configuration sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. -pub const FLASH_CONFIG: u32 = FLASH_SECTOR_ADDRESSES[3]; -/// Address of user firmware sector. Must be one of the start addresses in FLASH_SECTOR_ADDRESSES. -pub const FLASH_USER: u32 = FLASH_SECTOR_ADDRESSES[4]; +pub use blethrs_shared::{CONFIG_MAGIC, FLASH_SECTOR_ADDRESSES, FLASH_END, FLASH_CONFIG, FLASH_USER}; static mut FLASH: Option = None; @@ -55,19 +43,28 @@ impl UserConfig { } } - /// Attempt to read the UserConfig from flash sector 3 at 0x0800_C000. - /// If a valid config cannot be read, the default one is returned instead. - pub fn get(crc: &mut stm32::CRC) -> Option { + /// Attempt to read the UserConfig from flash. + /// + /// This method checks that the `CONFIG_MAGIC` is set, but does *not* check the crc checksum. + pub fn get_unchecked() -> Option { // Read config from flash - let adr = FLASH_CONFIG as *const u32; let cfg = unsafe { *(FLASH_CONFIG as *const UserConfig) }; - // First check magic is correct + // Check magic is correct if cfg.magic != CONFIG_MAGIC { - return None; + None + } else { + Some(cfg.clone()) } + } + + /// Attempt to read the UserConfig from flash sector 3 at 0x0800_C000. + /// If a valid config cannot be read, the default one is returned instead. + pub fn get(crc: &mut stm32::CRC) -> Option { + let cfg = Self::get_unchecked()?; // Validate checksum + let adr = FLASH_CONFIG as *const u32; let len = core::mem::size_of::() / 4; crc.cr.write(|w| w.reset().reset()); for idx in 0..(len - 1) {