diff --git a/Cargo.lock b/Cargo.lock index 3487d39..a4dbe79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,11 +8,42 @@ name = "bare-metal" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "built" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cast" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cc" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cortex-m" version = "0.5.7" @@ -62,6 +93,73 @@ name = "embedded_types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "git2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libgit2-sys" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libz-sys" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (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 = "nb" version = "0.1.1" @@ -131,7 +229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "oxcc" -version = "0.0.1" +version = "0.1.0" dependencies = [ "cortex-m 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rt 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -139,11 +237,26 @@ dependencies = [ "embedded-hal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "nucleo-f767zi 0.0.1 (git+https://github.com/jonlamb-gh/nucleo-f767zi.git)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "oxcc-bootloader 0.1.0", "panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "panic-semihosting 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "oxcc-bootloader" +version = "0.1.0" +dependencies = [ + "built 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-semihosting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=21396867114d267da06f19cc54cc4a1883b900a5)", + "stm32f7 0.2.2 (git+https://github.com/jonlamb-gh/stm32-rs.git?branch=stm32f767zit6-patches)", +] + [[package]] name = "panic-abort" version = "0.3.1" @@ -158,6 +271,16 @@ dependencies = [ "cortex-m-semihosting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.19" @@ -192,6 +315,39 @@ name = "rand_core" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "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)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smoltcp" +version = "0.4.0" +source = "git+https://github.com/m-labs/smoltcp?rev=21396867114d267da06f19cc54cc4a1883b900a5#21396867114d267da06f19cc54cc4a1883b900a5" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "stm32f7" version = "0.2.2" @@ -227,21 +383,67 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "url" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "vcell" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vcpkg" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -255,16 +457,48 @@ dependencies = [ "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi" +version = "0.3.6" +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.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" "checksum bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1bdcf9294ed648c7cd29b11db06ea244005aeef50ae8f605b1a3af2940bf8f92" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum built 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61f5aae2fa15b68fbcf0cbab64e659a55d10e9bacc55d3470ef77ae73030d755" +"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" +"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum cortex-m 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4573199c5b1e9b0eeae418b46f7c0af5fdf11b3057f83880810dfef68dd1dcb5" "checksum cortex-m-rt 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9dea4ad5f88b4ccfba2b738ebe42f9452b80481c44aae42c594cc66cf2c5f3c0" "checksum cortex-m-rt-macros 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f96e6af14f78ca987ba5487592a199878a7b17ee65b60e0b4aa563fc00965f4f" "checksum cortex-m-semihosting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "54d46ec4730314a01de4504328ef4ed6b2c51b63815caac4847ac9e70f88c9e5" "checksum embedded-hal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "26944677e4934eb5fb4025501dc0d6cdbcf6bfabd6200fcfee2e7e8eef8c0362" "checksum embedded_types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea8dc5db8dae723ecf68d863ff0011500e29fbbede193fa8fb3ca032595d3f6a" +"checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libgit2-sys 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4916b5addc78ec36cc309acfcdf0b9f9d97ab7b84083118b248709c5b7029356" +"checksum libz-sys 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c7bdca442aa002a930e6eb2a71916cabe46d91ffec8df66db0abfb1bc83469ab" +"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" +"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 nb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "69f380b5fe9fab8c0d7a6a99cda23e2cc0463bedb2cbc3aada0813b98496ecdc" "checksum nucleo-f767zi 0.0.1 (git+https://github.com/jonlamb-gh/nucleo-f767zi.git)" = "" "checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" @@ -275,16 +509,32 @@ dependencies = [ "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" "checksum panic-semihosting 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1017854db1621a236488ac359b89b19a56dcb1cb45127376d04f23128cea7210" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe022fb8c8bd254524b0b3305906c1921fa37a84a644e29079a9e62200c3901" "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"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.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" +"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=21396867114d267da06f19cc54cc4a1883b900a5)" = "" "checksum stm32f7 0.2.2 (git+https://github.com/jonlamb-gh/stm32-rs.git?branch=stm32f767zit6-patches)" = "" "checksum stm32f767-hal 0.0.1 (git+https://github.com/jonlamb-gh/stm32f767-hal.git)" = "" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum toml 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b7e7d59d55f36979a9dd86d71ae54657a5e9c7fdb4fa2212f4064e2d32f9dcda" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" "checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" +"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"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 aa5a4cd..5e52263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,10 @@ license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/jonlamb-gh/oxcc" +[dependencies.oxcc-bootloader] +path = "oxcc-bootloader/" +version = "0.1.0" + [dependencies.panic-abort] version = "0.3.1" optional = true diff --git a/memory.x b/memory.x index bca9958..34e9331 100644 --- a/memory.x +++ b/memory.x @@ -3,8 +3,12 @@ MEMORY /* NOTE K = KiBi = 1024 bytes */ /* STM32F767ZI 2 MByte FLASH, 512 KByte RAM */ - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 512K + /* FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K */ + + /* Using OxCC bootloader, user firmware starts at 0x0802_0000 */ + FLASH (rx) : ORIGIN = 0x08020000, LENGTH = (2048K - 128K) + /* First 8 bytes are reserved for the bootloader sticky flag word */ + RAM (xrw) : ORIGIN = 0x20000008, LENGTH = (512K - 8) } /* This is where the call stack will be allocated. */ diff --git a/oxcc-bootloader/.gdbinit b/oxcc-bootloader/.gdbinit new file mode 100644 index 0000000..f2bf090 --- /dev/null +++ b/oxcc-bootloader/.gdbinit @@ -0,0 +1,21 @@ +target remote :3333 + +# print demangled symbols by default +set print asm-demangle on + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.fifo uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load +step diff --git a/oxcc-bootloader/.gitignore b/oxcc-bootloader/.gitignore new file mode 100644 index 0000000..2bb8d25 --- /dev/null +++ b/oxcc-bootloader/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +.idea diff --git a/oxcc-bootloader/Cargo.lock b/oxcc-bootloader/Cargo.lock new file mode 100644 index 0000000..5541091 --- /dev/null +++ b/oxcc-bootloader/Cargo.lock @@ -0,0 +1,390 @@ +[[package]] +name = "aligned" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bare-metal" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "built" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cortex-m" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal 0.2.3 (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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m-rt-macros 0.1.2 (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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-semihosting" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "git2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libgit2-sys" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libz-sys" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (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 = "oxcc-bootloader" +version = "0.1.0" +dependencies = [ + "built 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-semihosting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=21396867114d267da06f19cc54cc4a1883b900a5)", + "stm32f7 0.2.2 (git+https://github.com/jonlamb-gh/stm32-rs.git?branch=stm32f767zit6-patches)", +] + +[[package]] +name = "panic-abort" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.19 (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 = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "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)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smoltcp" +version = "0.4.0" +source = "git+https://github.com/m-labs/smoltcp?rev=21396867114d267da06f19cc54cc4a1883b900a5#21396867114d267da06f19cc54cc4a1883b900a5" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stm32f7" +version = "0.2.2" +source = "git+https://github.com/jonlamb-gh/stm32-rs.git?branch=stm32f767zit6-patches#54dafd12e4741573ad10cc530ac7e242ef68e87b" +dependencies = [ + "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.79 (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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "url" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vcell" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vcpkg" +version = "0.2.6" +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.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.6" +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.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" +"checksum bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1bdcf9294ed648c7cd29b11db06ea244005aeef50ae8f605b1a3af2940bf8f92" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum built 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61f5aae2fa15b68fbcf0cbab64e659a55d10e9bacc55d3470ef77ae73030d755" +"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" +"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum cortex-m 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4573199c5b1e9b0eeae418b46f7c0af5fdf11b3057f83880810dfef68dd1dcb5" +"checksum cortex-m-rt 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d86cfa89fa220d3cb7a41133693e2d302d18e9a632298ffb3738f175c8c12325" +"checksum cortex-m-rt-macros 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3139fdadccaa0db6fa96637678ced9b0b97e4f10047c9ab603d125048e107d1a" +"checksum cortex-m-semihosting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "54d46ec4730314a01de4504328ef4ed6b2c51b63815caac4847ac9e70f88c9e5" +"checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libgit2-sys 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "93f2b22fce91fb820363cf88a849a8f8fdfd8be37774b6a9dd6cbda05cf940e6" +"checksum libz-sys 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c7bdca442aa002a930e6eb2a71916cabe46d91ffec8df66db0abfb1bc83469ab" +"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" +"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 panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe022fb8c8bd254524b0b3305906c1921fa37a84a644e29079a9e62200c3901" +"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"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.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" +"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=21396867114d267da06f19cc54cc4a1883b900a5)" = "" +"checksum stm32f7 0.2.2 (git+https://github.com/jonlamb-gh/stm32-rs.git?branch=stm32f767zit6-patches)" = "" +"checksum syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)" = "854b08a640fc8f54728fb95321e3ec485b365a97fe47609797c671addd1dde69" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" +"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" +"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" +"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"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/oxcc-bootloader/Cargo.toml b/oxcc-bootloader/Cargo.toml new file mode 100644 index 0000000..4906648 --- /dev/null +++ b/oxcc-bootloader/Cargo.toml @@ -0,0 +1,48 @@ +# The main binary application is the bootloader firmware +[package] +name = "oxcc-bootloader" +version = "0.1.0" +authors = ["Jon Lamb"] + +# Provides an interface for user firmware to reset into the bootloader +[lib] +name = "oxcc_bootloader_lib" +path = "src/lib.rs" + +[[bin]] +name = "oxcc-bootloader" +path = "src/main.rs" + +[dependencies] +cortex-m = "0.5.7" +cortex-m-rt = "0.6.3" +# switching over to semihosting can be useful for debugging +#panic-semihosting = "0.4.0" +panic-abort = "0.3.1" +cortex-m-semihosting = "0.3.1" +byteorder = { version = "1.2.2", default-features = false } + +[dependencies.smoltcp] +git = "https://github.com/m-labs/smoltcp" +rev = "21396867114d267da06f19cc54cc4a1883b900a5" +default-features = false +features = ["proto-ipv4", "socket-tcp"] + +[dependencies.stm32f7] +git = "https://github.com/jonlamb-gh/stm32-rs.git" +branch = "stm32f767zit6-patches" +version = "0.2.2" +features = ["stm32f7x7", "rt"] + +[build-dependencies] +built = "0.3.0" + +[profile.dev] +codegen-units = 1 +incremental = false + +[profile.release] +debug = true +lto = true +codegen-units = 1 +incremental = false diff --git a/oxcc-bootloader/README.md b/oxcc-bootloader/README.md new file mode 100644 index 0000000..b274d12 --- /dev/null +++ b/oxcc-bootloader/README.md @@ -0,0 +1,133 @@ +# OxCC Bootloader + +An FOTA capable bootloader used to enable `OxCC` firmware +updates via TCP or CAN. + +Inspired by [blethrs](https://github.com/AirborneEngineering/blethrs). + +## Building + +**NOTE**: debug builds take up too much space, the bootloader needs to fit in a specific flash sector. + +Can this be detected at compile-time? + +```bash +cargo build --release +``` + +## Deploying + +```bash +./scripts/deploy +``` + +## Default Config + +Without a valid config in flash, `OxCC` defaults to: + +- IP address: `10.1.1.0/24` +- gateway: `10.1.1.1` +- MAC: `02:00:01:02:03:04` + +## Debugging + +```bash +cargo run --release +``` + +```text +|-=-=-=-=-=-=-= 0xCC Bootloader =-=-=-=-=-=-=- +| Version 0.1.0 7c0ed4b +| Platform thumbv7em-none-eabihf +| Built on Tue, 25 Sep 2018 13:20:52 GMT +| rustc 1.30.0-nightly (cb6d2dfa8 2018-09-16) +|-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + Initialising cache... OK + Initialising clocks... OK + Initialising GPIOs... OK + Reading configuration... OK +UserConfig: + MAC Address: 02:00:03:07:03:05 + IP Address: 10.1.1.10/24 + Gateway: 10.1.1.1 + Checksum: B3819D1F + + Initialising Ethernet... OK + Waiting for link... OK + Initialising network... OK + Ready. +``` + +## Using `firmware-updater` + +An example host-side updater Python [script](firmware-updater) is used to talk to the bootloader. + +```bash +./firmware-updator -h +``` + +### Info + +```bash +./firmware-updater 10.1.1.10 info + +Connecting to bootloader... +Received bootloader information: +Version: 0.1.0 c661da9 +Built: Tue, 25 Sep 2018 15:52:39 GMT +Compiler: rustc 1.30.0-nightly (cb6d2dfa8 2018-09-16) +MCU ID: 303138353436511600450038 +``` + +### Boot + +```bash +./firmware-updater 10.1.1.10 boot + +Connecting to bootloader... +Received bootloader information: +Version: 0.1.0 c661da9 +Built: Tue, 25 Sep 2018 15:52:39 GMT +Compiler: rustc 1.30.0-nightly (cb6d2dfa8 2018-09-16) +MCU ID: 303138353436511600450038 + +Sending reboot command... +``` + +### Deploying New Firmware + +#### Reset to Bootloader + +Instruct OxCC firmware to reset into the bootloader with a CAN frame: + +- CAN ID: `0xF0` +- DLC: `8` +- DATA: not used yet + +Or hold down the user-button and reset the board. + + +#### Program Flash + +```bash +# ELF to binary +arm-none-eabi-objcopy -O binary ../target/thumbv7em-none-eabihf/release/oxcc oxcc.bin + +./firmware-updater 10.1.1.10 program oxcc.bin + +Connecting to bootloader... +Received bootloader information: +Version: 0.1.0 c661da9 +Built: Tue, 25 Sep 2018 15:52:39 GMT +Compiler: rustc 1.30.0-nightly (cb6d2dfa8 2018-09-16) +MCU ID: 303138353436511600450038 + +Erasing (may take a few seconds)... +Writing 53.14kB in 54 segments... +100%|██████████████████████████████████████████████████████████████████| 54/54 [00:00<00:00, 59.56kB/s] +Writing completed successfully. Reading back... +100%|██████████████████████████████████████████████████████████████████| 54/54 [00:00<00:00, 76.50kB/s] +Readback successful. +Sending reboot command... +``` diff --git a/oxcc-bootloader/build.rs b/oxcc-bootloader/build.rs new file mode 100644 index 0000000..3bfdd5d --- /dev/null +++ b/oxcc-bootloader/build.rs @@ -0,0 +1,6 @@ +extern crate built; + +fn main() { + // Gather build information + built::write_built_file().expect("Failed to acquire build-time information"); +} diff --git a/oxcc-bootloader/firmware-updater b/oxcc-bootloader/firmware-updater new file mode 100755 index 0000000..818b107 --- /dev/null +++ b/oxcc-bootloader/firmware-updater @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +# +# NOTE: this is more or less a copy of `blethrs.py` from: +# https://github.com/AirborneEngineering/blethrs/blob/master/blethrs.py +# +# TODO: proper Rust CLI app? +# + +import time +import struct +import socket +import argparse +import crcmod + +try: + from tqdm import tqdm +except ImportError: + print("Notice: tqdm not installed, install for progress bars.") + + def tqdm(x, *args, **kwargs): + return x + + +commands = { + "info": 0, + "read": 1, + "erase": 2, + "write": 3, + "boot": 4, +} + + +errors = { + 0: "Success", + 1: "Invalid Address", + 2: "Length Not Multiple of 4", + 3: "Length Too Long", + 4: "Data Length Incorrect", + 5: "Erase Error", + 6: "Write Error", + 7: "Flash Error", + 8: "Network Error", + 9: "Internal Error", +} + + +class BootloaderError(Exception): + def __init__(self, errno): + self.errno = errno + + def __str__(self): + if self.errno in errors: + return "{}".format(errors[self.errno]) + else: + return "Unknown error {}".format(self.errno) + + +class MismatchError(Exception): + def __init__(self, addr, tx, rx): + self.addr = addr + self.tx = tx + self.rx = rx + + def __str__(self): + return "Mismatch at address {:08X}: {:02X}!={:02X}".format( + self.addr, self.tx, self.rx) + + +def boot_request(hostname, boot_req_port, bootloader_port, n_attempts=10): + print("Sending UDP boot request to port {}...".format(boot_req_port)) + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + cmd = struct.pack("5I", config_bytes) + raw = struct.pack("<5I", *u32) + crc = crc32(raw) + crc_bytes = struct.pack(" = None; +use config::{BOOTLOAD_FLAG_ADDRESS, BOOTLOAD_FLAG_VALUE}; + +/// 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 stm32f7x7::RCC) -> bool { + let result = rcc.csr.read().sftrstf().bit_is_set(); + rcc.csr.modify(|_, w| w.rmvf().set_bit()); + result +} + +/// Returns true if the bootload flag is set: RAM 0x2000_0000 == 0xB00110AD +/// +/// Clears the flag before returning, so this answer is only valid once. +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 + }) +} + +fn clear_flag() { + cortex_m::interrupt::free(|_| unsafe { + core::ptr::write_volatile(BOOTLOAD_FLAG_ADDRESS as *mut u32, 0); + }); +} + +/// Trigger a reset that will cause us to boot into the bootloader next go +/// around +#[allow(unused)] +pub fn reset_to_bootloader() -> ! { + set_flag(); + sw_reset(); + unreachable!(); +} + +fn set_flag() { + cortex_m::interrupt::free(|_| unsafe { + core::ptr::write_volatile(BOOTLOAD_FLAG_ADDRESS as *mut u32, BOOTLOAD_FLAG_VALUE); + }); +} + +/// Trigger a reset that will cause us to bootload the user application next go +/// around +pub fn reset_to_user_firmware() { + clear_flag(); + sw_reset(); +} + +fn sw_reset() { + // It's troublesome to require SCB be passed in here, and + // we're literally about to reset the whole microcontroller, + // so safety is not such a huge concern. + let aircr = 0xE000ED0C as *mut u32; + unsafe { *aircr = (0x5FA << 16) | (1 << 2) }; +} + +/// Jump to user code at the given address. +/// +/// Doesn't disable interrupts so only call this right at boot, +/// when no interrupt sources will be enabled. +pub fn bootload(scb: &mut cortex_m::peripheral::SCB, address: u32) { + unsafe { + let sp = *(address as *const u32); + let rv = *((address + 4) as *const u32); + + USER_RESET = Some(core::mem::transmute(rv)); + scb.vtor.write(address); + cortex_m::register::msp::write(sp); + (USER_RESET.unwrap())(); + } +} diff --git a/oxcc-bootloader/src/cache.rs b/oxcc-bootloader/src/cache.rs new file mode 100644 index 0000000..05f2aaf --- /dev/null +++ b/oxcc-bootloader/src/cache.rs @@ -0,0 +1,9 @@ +use stm32f7::stm32f7x7; + +/// Enable I and D cache +pub fn cache_enable(core_peripherals: &mut stm32f7x7::CorePeripherals) { + core_peripherals.SCB.enable_icache(); + core_peripherals + .SCB + .enable_dcache(&mut core_peripherals.CPUID); +} diff --git a/oxcc-bootloader/src/config.rs b/oxcc-bootloader/src/config.rs new file mode 100644 index 0000000..935642e --- /dev/null +++ b/oxcc-bootloader/src/config.rs @@ -0,0 +1,85 @@ +//! Chip and board specific configuration settings go here. +use bootload; +use stm32f7x7; + +/// TCP port to listen on +pub const TCP_PORT: u16 = 7776; + +/// 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_8000, + 0x0801_0000, + 0x0801_8000, + 0x0802_0000, + 0x0804_0000, + 0x0808_0000, + 0x080C_0000, + 0x0810_0000, + 0x0814_0000, + 0x0818_0000, + 0x081C_0000, +]; + +/// Final valid address in flash +pub const FLASH_END: u32 = 0x081F_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. +/// SRAM1 starts at 0x2002_0000 +/// DTCM RAM starts at 0x2000_0000 +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 stm32f7x7::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 PC13 (user-button) is HIGH for at least a + // full byte period of the UART + let cond1 = bootload::was_software_reset(&mut peripherals.RCC) && bootload::flag_set(); + + // User button on PC13, pull-down/active-high + peripherals.RCC.ahb1enr.modify(|_, w| w.gpiocen().enabled()); + peripherals.GPIOC.moder.modify(|_, w| w.moder13().input()); + peripherals + .GPIOC + .pupdr + .modify(|_, w| w.pupdr13().pull_down()); + + 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.GPIOC.idr.read().idr13().bit_is_set(); + } + + peripherals + .RCC + .ahb1enr + .modify(|_, w| w.gpiocen().disabled()); + cond1 || cond2 +} diff --git a/oxcc-bootloader/src/ethernet.rs b/oxcc-bootloader/src/ethernet.rs new file mode 100644 index 0000000..a069ff0 --- /dev/null +++ b/oxcc-bootloader/src/ethernet.rs @@ -0,0 +1,519 @@ +use core; +use cortex_m; +use stm32f7x7; + +use smoltcp::{ + self, + phy::{self, DeviceCapabilities}, + time::Instant, + wire::EthernetAddress, +}; + +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 +/// * tdes1: transmit buffer lengths +/// * tdes2: transmit buffer address +/// * tdes3: not used +/// +/// Note that Copy and Clone are derived to support initialising an array of +/// TDes, but you may not move a TDes after its address has been given to the +/// ETH_DMA engine. +#[derive(Copy, Clone)] +#[repr(C, packed)] +struct TDes { + tdes0: u32, + tdes1: u32, + tdes2: u32, + tdes3: u32, +} + +impl TDes { + /// Initialises this TDes to point at the given buffer. + pub fn init(&mut self, tdbuf: &[u32]) { + // Set FS and LS on each descriptor: each will hold a single full segment. + self.tdes0 = (1 << 29) | (1 << 28); + // Store pointer to associated buffer. + self.tdes2 = tdbuf.as_ptr() as u32; + // No second buffer. + self.tdes3 = 0; + } + + /// Mark this TDes as end-of-ring. + pub fn set_end_of_ring(&mut self) { + self.tdes0 |= 1 << 21; + } + + /// Return true if the RDes is not currently owned by the DMA + pub fn available(&self) -> bool { + self.tdes0 & (1 << 31) == 0 + } + + /// Release this RDes back to DMA engine for transmission + pub unsafe fn release(&mut self) { + self.tdes0 |= 1 << 31; + } + + /// Set the length of data in the buffer pointed to by this TDes + pub unsafe fn set_length(&mut self, length: usize) { + self.tdes1 = (length as u32) & 0x1FFF; + } + + /// Access the buffer pointed to by this descriptor + pub unsafe fn buf_as_slice_mut(&self) -> &mut [u8] { + core::slice::from_raw_parts_mut(self.tdes2 as *mut _, self.tdes1 as usize & 0x1FFF) + } +} + +/// Store a ring of TDes and associated buffers +struct TDesRing { + td: [TDes; ETH_NUM_TD], + tbuf: [[u32; ETH_BUF_SIZE / 4]; ETH_NUM_TD], + tdidx: usize, +} + +static mut TDESRING: TDesRing = TDesRing { + td: [TDes { + tdes0: 0, + tdes1: 0, + tdes2: 0, + tdes3: 0, + }; ETH_NUM_TD], + tbuf: [[0; ETH_BUF_SIZE / 4]; ETH_NUM_TD], + tdidx: 0, +}; + +impl TDesRing { + /// Initialise this TDesRing + /// + /// The current memory address of the buffers inside this TDesRing will be + /// stored in the descriptors, so ensure the TDesRing is not moved + /// after initialisation. + pub fn init(&mut self) { + for (td, tdbuf) in self.td.iter_mut().zip(self.tbuf.iter()) { + td.init(&tdbuf[..]); + } + self.td.last_mut().unwrap().set_end_of_ring(); + } + + /// Return the address of the start of the TDes ring + pub fn ptr(&self) -> *const TDes { + self.td.as_ptr() + } + + /// Return true if a TDes is available for use + pub fn available(&self) -> bool { + self.td[self.tdidx].available() + } + + /// Return the next available TDes if any are available, otherwise None + pub fn next(&mut self) -> Option<&mut TDes> { + if self.available() { + let rv = Some(&mut self.td[self.tdidx]); + self.tdidx = (self.tdidx + 1) % ETH_NUM_TD; + rv + } else { + None + } + } +} + +/// Receive Descriptor representation +/// +/// * rdes0: ownership bit and received packet metadata +/// * rdes1: receive buffer lengths and settings +/// * rdes2: receive buffer address +/// * rdes3: not used +/// +/// Note that Copy and Clone are derived to support initialising an array of +/// TDes, but you may not move a TDes after its address has been given to the +/// ETH_DMA engine. +#[derive(Copy, Clone)] +#[repr(C, packed)] +struct RDes { + rdes0: u32, + rdes1: u32, + rdes2: u32, + rdes3: u32, +} + +impl RDes { + /// Initialises this RDes to point at the given buffer. + pub fn init(&mut self, rdbuf: &[u32]) { + // Mark each RDes as owned by the DMA engine. + self.rdes0 = 1 << 31; + // Store length of and pointer to associated buffer. + self.rdes1 = rdbuf.len() as u32 * 4; + self.rdes2 = rdbuf.as_ptr() as u32; + // No second buffer. + self.rdes3 = 0; + } + + /// Mark this RDes as end-of-ring. + pub fn set_end_of_ring(&mut self) { + self.rdes1 |= 1 << 15; + } + + /// Return true if the RDes is not currently owned by the DMA + pub fn available(&self) -> bool { + self.rdes0 & (1 << 31) == 0 + } + + /// Release this RDes back to the DMA engine + pub unsafe fn release(&mut self) { + self.rdes0 |= 1 << 31; + } + + /// Access the buffer pointed to by this descriptor + pub unsafe fn buf_as_slice(&self) -> &[u8] { + core::slice::from_raw_parts(self.rdes2 as *const _, (self.rdes0 >> 16) as usize & 0x3FFF) + } +} + +/// Store a ring of RDes and associated buffers +struct RDesRing { + rd: [RDes; ETH_NUM_RD], + rbuf: [[u32; ETH_BUF_SIZE / 4]; ETH_NUM_RD], + rdidx: usize, +} + +static mut RDESRING: RDesRing = RDesRing { + rd: [RDes { + rdes0: 0, + rdes1: 0, + rdes2: 0, + rdes3: 0, + }; ETH_NUM_RD], + rbuf: [[0; ETH_BUF_SIZE / 4]; ETH_NUM_RD], + rdidx: 0, +}; + +impl RDesRing { + /// Initialise this RDesRing + /// + /// The current memory address of the buffers inside this TDesRing will be + /// stored in the descriptors, so ensure the TDesRing is not moved + /// after initialisation. + pub fn init(&mut self) { + for (rd, rdbuf) in self.rd.iter_mut().zip(self.rbuf.iter()) { + rd.init(&rdbuf[..]); + } + self.rd.last_mut().unwrap().set_end_of_ring(); + } + + /// Return the address of the start of the RDes ring + pub fn ptr(&self) -> *const RDes { + self.rd.as_ptr() + } + + /// Return true if a RDes is available for use + pub fn available(&self) -> bool { + self.rd[self.rdidx].available() + } + + /// Return the next available RDes if any are available, otherwise None + pub fn next(&mut self) -> Option<&mut RDes> { + if self.available() { + let rv = Some(&mut self.rd[self.rdidx]); + self.rdidx = (self.rdidx + 1) % ETH_NUM_RD; + rv + } else { + None + } + } +} + +/// Ethernet device driver +pub struct EthernetDevice { + rdring: &'static mut RDesRing, + tdring: &'static mut TDesRing, + eth_mac: stm32f7x7::ETHERNET_MAC, + eth_dma: stm32f7x7::ETHERNET_DMA, +} + +static mut BUFFERS_USED: bool = false; + +impl EthernetDevice { + /// Create a new uninitialised EthernetDevice. + /// + /// You must move in ETH_MAC, ETH_DMA, and they are then kept by the device. + /// + /// You may only call this function once; subsequent calls will panic. + pub fn new( + eth_mac: stm32f7x7::ETHERNET_MAC, + eth_dma: stm32f7x7::ETHERNET_DMA, + ) -> EthernetDevice { + cortex_m::interrupt::free(|_| unsafe { + if BUFFERS_USED { + panic!("EthernetDevice already created"); + } + BUFFERS_USED = true; + EthernetDevice { + rdring: &mut RDESRING, + tdring: &mut TDESRING, + eth_mac, + eth_dma, + } + }) + } + + /// Initialise the ethernet driver. + /// + /// Sets up the descriptor structures, sets up the peripheral clocks and + /// GPIO configuration, and configures the ETH MAC and DMA peripherals. + /// + /// Brings up the PHY and then blocks waiting for a network link. + pub fn init(&mut self, rcc: &mut stm32f7x7::RCC, addr: EthernetAddress) { + self.tdring.init(); + self.rdring.init(); + + self.init_peripherals(rcc, addr); + + self.phy_reset(); + self.phy_init(); + } + + pub fn link_established(&mut self) -> bool { + return self.phy_poll_link(); + } + + pub fn block_until_link(&mut self) { + while !self.link_established() {} + } + + /// Resume suspended TX DMA operation + pub fn resume_tx_dma(&mut self) { + if self.eth_dma.dmasr.read().tps().is_suspended() { + self.eth_dma.dmatpdr.write(|w| w.tpd().poll()); + } + } + + /// Resume suspended RX DMA operation + pub fn resume_rx_dma(&mut self) { + if self.eth_dma.dmasr.read().rps().is_suspended() { + self.eth_dma.dmarpdr.write(|w| w.rpd().poll()); + } + } + + /// Sets up the device peripherals. + fn init_peripherals(&mut self, rcc: &mut stm32f7x7::RCC, mac: EthernetAddress) { + // Reset ETH_MAC and ETH_DMA + rcc.ahb1rstr.modify(|_, w| w.ethmacrst().reset()); + rcc.ahb1rstr.modify(|_, w| w.ethmacrst().clear_bit()); + self.eth_dma.dmabmr.modify(|_, w| w.sr().reset()); + while self.eth_dma.dmabmr.read().sr().is_reset() {} + + // Set MAC address + let mac = mac.as_bytes(); + self.eth_mac.maca0lr.write(|w| { + w.maca0l().bits( + (mac[0] as u32) << 0 + | (mac[1] as u32) << 8 + | (mac[2] as u32) << 16 + | (mac[3] as u32) << 24, + ) + }); + self.eth_mac + .maca0hr + .write(|w| w.maca0h().bits((mac[4] as u16) << 0 | (mac[5] as u16) << 8)); + + // Enable RX and TX. We'll set link speed and duplex at link-up. + self.eth_mac + .maccr + .write(|w| w.re().enabled().te().enabled().cstf().enabled()); + + // Tell the ETH DMA the start of each ring + self.eth_dma + .dmatdlar + .write(|w| w.stl().bits(self.tdring.ptr() as u32)); + self.eth_dma + .dmardlar + .write(|w| w.srl().bits(self.rdring.ptr() as u32)); + + // Set DMA bus mode + self.eth_dma + .dmabmr + .modify(|_, w| w.aab().aligned().pbl().pbl1()); + + // Flush TX FIFO + self.eth_dma.dmaomr.write(|w| w.ftf().flush()); + while self.eth_dma.dmaomr.read().ftf().is_flush() {} + + // Set DMA operation mode to store-and-forward and start DMA + self.eth_dma.dmaomr.write(|w| { + w.rsf() + .store_forward() + .tsf() + .store_forward() + .st() + .started() + .sr() + .started() + }); + } + + /// Read a register over SMI. + fn smi_read(&mut self, reg: u8) -> u16 { + // Use PHY address 00000, set register address, set clock to HCLK/102, start + // read. + self.eth_mac.macmiiar.write(|w| { + w.mb() + .busy() + .pa() + .bits(ETH_PHY_ADDR) + .cr() + .cr_150_168() + .mr() + .bits(reg) + }); + + // Wait for read + while self.eth_mac.macmiiar.read().mb().is_busy() {} + + // Return result + self.eth_mac.macmiidr.read().td().bits() + } + + /// Write a register over SMI. + fn smi_write(&mut self, reg: u8, val: u16) { + // Use PHY address 00000, set write data, set register address, set clock to + // HCLK/102, start write operation. + self.eth_mac.macmiidr.write(|w| w.td().bits(val)); + self.eth_mac.macmiiar.write(|w| { + w.mb() + .busy() + .pa() + .bits(ETH_PHY_ADDR) + .mw() + .write() + .cr() + .cr_150_168() + .mr() + .bits(reg) + }); + + while self.eth_mac.macmiiar.read().mb().is_busy() {} + } + + /// Reset the connected PHY and wait for it to come out of reset. + fn phy_reset(&mut self) { + self.smi_write(0x00, 1 << 15); + while self.smi_read(0x00) & (1 << 15) == (1 << 15) {} + } + + /// Command connected PHY to initialise. + fn phy_init(&mut self) { + self.smi_write(0x00, 1 << 12); + } + + /// Poll PHY to determine link status. + fn phy_poll_link(&mut self) -> bool { + let bsr = self.smi_read(0x01); + let bcr = self.smi_read(0x00); + let lpa = self.smi_read(0x05); + + // No link without autonegotiate + if bcr & (1 << 12) == 0 { + return false; + } + // No link if link is down + if bsr & (1 << 2) == 0 { + return false; + } + // No link if remote fault + if bsr & (1 << 4) != 0 { + return false; + } + // No link if autonegotiate incomplete + if bsr & (1 << 5) == 0 { + return false; + } + // No link if other side can't do 100Mbps full duplex + if lpa & (1 << 8) == 0 { + return false; + } + + // Got link. Configure MAC to 100Mbit/s and full duplex. + self.eth_mac + .maccr + .modify(|_, w| w.fes().fes100().dm().full_duplex()); + + true + } +} + +pub struct TxToken(*mut EthernetDevice); +pub struct RxToken(*mut EthernetDevice); + +impl phy::TxToken for TxToken { + fn consume(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result + where + F: FnOnce(&mut [u8]) -> smoltcp::Result, + { + // There can only be a single EthernetDevice and therefore all TxTokens are + // wrappers to a raw pointer to it. Unsafe required to dereference this + // pointer and call the various TDes methods. + assert!(len <= ETH_BUF_SIZE); + unsafe { + let tdes = (*self.0).tdring.next().unwrap(); + tdes.set_length(len); + let result = f(tdes.buf_as_slice_mut()); + tdes.release(); + (*self.0).resume_tx_dma(); + result + } + } +} + +impl phy::RxToken for RxToken { + fn consume(self, _timestamp: Instant, f: F) -> smoltcp::Result + where + F: FnOnce(&[u8]) -> smoltcp::Result, + { + // There can only be a single EthernetDevice and therefore all RxTokens are + // wrappers to a raw pointer to it. Unsafe required to dereference this + // pointer and call the various RDes methods. + unsafe { + let rdes = (*self.0).rdring.next().unwrap(); + let result = f(rdes.buf_as_slice()); + rdes.release(); + (*self.0).resume_rx_dma(); + result + } + } +} + +// Implement the smoltcp Device interface +impl<'a> phy::Device<'a> for EthernetDevice { + type RxToken = RxToken; + type TxToken = TxToken; + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = 1500; + caps.max_burst_size = Some(core::cmp::min(ETH_NUM_TD, ETH_NUM_RD)); + caps + } + + fn receive(&mut self) -> Option<(RxToken, TxToken)> { + if self.rdring.available() && self.tdring.available() { + Some((RxToken(self), TxToken(self))) + } else { + None + } + } + + fn transmit(&mut self) -> Option { + if self.tdring.available() { + Some(TxToken(self)) + } else { + None + } + } +} diff --git a/oxcc-bootloader/src/flash.rs b/oxcc-bootloader/src/flash.rs new file mode 100644 index 0000000..4b41f53 --- /dev/null +++ b/oxcc-bootloader/src/flash.rs @@ -0,0 +1,286 @@ +use core; +use stm32f7x7; + +use core::fmt; +use {Error, Result}; + +const CONFIG_MAGIC: u32 = 0x67797870; + +use config::{FLASH_CONFIG, FLASH_END, FLASH_SECTOR_ADDRESSES, FLASH_USER}; + +static mut FLASH: Option = None; + +/// Call to move the flash peripheral into this module +pub fn init(flash: stm32f7x7::FLASH) { + unsafe { FLASH = Some(flash) }; +} + +/// User configuration. Must live in flash at FLASH_CONFIG, 0x0800_C000. +/// `magic` must be set to 0x67797870. `checksum` must be the CRC32 of the +/// preceeding bytes. +#[derive(Copy, Clone)] +#[repr(C, packed)] +pub struct UserConfig { + magic: u32, + pub mac_address: [u8; 6], + pub ip_address: [u8; 4], + pub ip_gateway: [u8; 4], + pub ip_prefix: u8, + _padding: [u8; 1], + checksum: u32, +} + +impl fmt::Display for UserConfig { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "UserConfig:")?; + writeln!( + f, + " MAC Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", + self.mac_address[0], + self.mac_address[1], + self.mac_address[2], + self.mac_address[3], + self.mac_address[4], + self.mac_address[5] + )?; + writeln!( + f, + " IP Address: {}.{}.{}.{}/{}", + self.ip_address[0], + self.ip_address[1], + self.ip_address[2], + self.ip_address[3], + self.ip_prefix + )?; + writeln!( + f, + " Gateway: {}.{}.{}.{}", + self.ip_gateway[0], self.ip_gateway[1], self.ip_gateway[2], self.ip_gateway[3] + )?; + writeln!(f, " Checksum: {:08X}", self.checksum as u32) + } +} + +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. + /// If a valid config cannot be read, the default one is returned instead. + pub fn get(crc: &mut stm32f7x7::CRC) -> 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 + if cfg.magic != CONFIG_MAGIC { + return None; + } + + // Validate checksum + let len = core::mem::size_of::() / 4; + crc.cr.write(|w| w.reset().reset()); + for idx in 0..(len - 1) { + let val = unsafe { *(adr.offset(idx as isize)) }; + crc.dr.write(|w| w.dr().bits(val)); + } + let crc_computed = crc.dr.read().dr().bits(); + + if crc_computed == cfg.checksum { + Some(cfg.clone()) + } else { + None + } + } +} + +/// Try to determine if there is valid code in the user flash at 0x0801_0000. +/// Returns Some(u32) with the address to jump to if so, and None if not. +pub fn valid_user_code() -> Option { + let reset_vector: u32 = unsafe { *((FLASH_USER + 4) as *const u32) }; + if reset_vector >= FLASH_USER && reset_vector <= FLASH_END { + Some(FLASH_USER) + } else { + None + } +} + +/// Check if address+length is valid for read/write flash. +fn check_address_valid(address: u32, length: usize) -> Result<()> { + if address < FLASH_CONFIG { + Err(Error::InvalidAddress) + } else if address > (FLASH_END - length as u32 + 1) { + Err(Error::InvalidAddress) + } else { + Ok(()) + } +} + +/// Check length is a multiple of 4 and no greater than 1024 +fn check_length_valid(length: usize) -> Result<()> { + if length % 4 != 0 { + Err(Error::LengthNotMultiple4) + } else if length > 1024 { + Err(Error::LengthTooLong) + } else { + Ok(()) + } +} + +/// Check the specified length matches the amount of data available +fn check_length_correct(length: usize, data: &[u8]) -> Result<()> { + if length != data.len() { + Err(Error::DataLengthIncorrect) + } else { + Ok(()) + } +} + +/// Try to get the FLASH peripheral +fn get_flash_peripheral() -> Result<&'static mut stm32f7x7::FLASH> { + match unsafe { FLASH.as_mut() } { + Some(flash) => Ok(flash), + None => Err(Error::InternalError), + } +} + +/// Try to unlock flash +fn unlock(flash: &mut stm32f7x7::FLASH) -> Result<()> { + // Wait for any ongoing operations + while flash.sr.read().bsy().bit_is_set() {} + + // Attempt unlock + flash.keyr.write(|w| w.key().bits(0x45670123)); + flash.keyr.write(|w| w.key().bits(0xCDEF89AB)); + + // Verify success + match flash.cr.read().lock().is_unlocked() { + true => Ok(()), + false => Err(Error::FlashError), + } +} + +/// Lock flash +fn lock(flash: &mut stm32f7x7::FLASH) { + flash.cr.write(|w| w.lock().locked()); +} + +/// Erase flash sectors that cover the given address and length. +pub fn erase(address: u32, length: usize) -> Result<()> { + check_address_valid(address, length)?; + let address_start = address; + let address_end = address + length as u32; + for (idx, sector_start) in FLASH_SECTOR_ADDRESSES.iter().enumerate() { + let sector_start = *sector_start; + let sector_end = match FLASH_SECTOR_ADDRESSES.get(idx + 1) { + Some(adr) => *adr - 1, + None => FLASH_END, + }; + if (address_start >= sector_start && address_start <= sector_end) + || (address_end >= sector_start && address_end <= sector_end) + || (address_start <= sector_start && address_end >= sector_end) + { + erase_sector(idx as u8)?; + } + } + Ok(()) +} + +/// Erase specified sector +fn erase_sector(sector: u8) -> Result<()> { + if (sector as usize) >= FLASH_SECTOR_ADDRESSES.len() { + return Err(Error::InternalError); + } + let flash = get_flash_peripheral()?; + unlock(flash)?; + + // Erase. + // UNSAFE: We've verified that `sector` Result<&'static [u8]> { + check_address_valid(address, length)?; + check_length_valid(length)?; + let address = address as *const _; + unsafe { Ok(core::slice::from_raw_parts::<'static, u8>(address, length)) } +} + +/// Write to flash. +/// Returns () on success, None on failure. +/// length must be a multiple of 4. +pub fn write(address: u32, length: usize, data: &[u8]) -> Result<()> { + check_address_valid(address, length)?; + check_length_valid(length)?; + check_length_correct(length, data)?; + let flash = get_flash_peripheral()?; + unlock(flash)?; + + // Set parallelism to write in 32 bit chunks, and enable programming. + // Note reset value has 1 for lock so we need to explicitly clear it. + flash + .cr + .write(|w| w.lock().unlocked().psize().psize32().pg().program()); + + for idx in 0..(length / 4) { + let offset = idx * 4; + let word: u32 = (data[offset] as u32) + | (data[offset + 1] as u32) << 8 + | (data[offset + 2] as u32) << 16 + | (data[offset + 3] as u32) << 24; + let write_address = (address + offset as u32) as *mut u32; + unsafe { core::ptr::write_volatile(write_address, word) }; + + // Wait for write + while flash.sr.read().bsy().bit_is_set() {} + + // Check for errors + let sr = flash.sr.read(); + if sr.pgserr().bit_is_set() + || sr.pgperr().bit_is_set() + || sr.pgaerr().bit_is_set() + || sr.wrperr().bit_is_set() + { + lock(flash); + // TODO - getting flash write errors when writing configs? + //panic!("SR = 0x{:X} len {}", sr.bits(), length); + return Err(Error::WriteError); + } + } + + lock(flash); + + Ok(()) +} diff --git a/oxcc-bootloader/src/gpio.rs b/oxcc-bootloader/src/gpio.rs new file mode 100644 index 0000000..13b15dc --- /dev/null +++ b/oxcc-bootloader/src/gpio.rs @@ -0,0 +1,85 @@ +use stm32f7::stm32f7x7; + +/// Set up GPIOs for ethernet. +/// +/// All GPIO clocks are already enabled. +pub fn gpio_init(peripherals: &mut stm32f7x7::Peripherals) { + let gpioa = &peripherals.GPIOA; + let gpiob = &peripherals.GPIOB; + let gpioc = &peripherals.GPIOC; + let gpiog = &peripherals.GPIOG; + + // Status LED (red) on PB14 + gpiob.moder.modify(|_, w| w.moder14().output()); + gpiob.odr.modify(|_, w| w.odr14().set_bit()); + + // User button on PC13, pull-down/active-high + gpioc.moder.modify(|_, w| w.moder13().input()); + gpioc.pupdr.modify(|_, w| w.pupdr13().pull_down()); + + // Configure ethernet related GPIO: + // GPIOA 1, 2, 7 + // GPIOB 13 + // GPIOC 1, 4, 5 + // GPIOG 2, 11, 13 + // All set to AF11 and very high speed + gpioa.moder.modify(|_, w| { + w.moder1() + .alternate() + .moder2() + .alternate() + .moder7() + .alternate() + }); + gpiob.moder.modify(|_, w| w.moder13().alternate()); + gpioc.moder.modify(|_, w| { + w.moder1() + .alternate() + .moder4() + .alternate() + .moder5() + .alternate() + }); + gpiog.moder.modify(|_, w| { + w.moder2() + .alternate() + .moder11() + .alternate() + .moder13() + .alternate() + }); + gpioa.ospeedr.modify(|_, w| { + w.ospeedr1() + .very_high_speed() + .ospeedr2() + .very_high_speed() + .ospeedr7() + .very_high_speed() + }); + gpiob.ospeedr.modify(|_, w| w.ospeedr13().very_high_speed()); + gpioc.ospeedr.modify(|_, w| { + w.ospeedr1() + .very_high_speed() + .ospeedr4() + .very_high_speed() + .ospeedr5() + .very_high_speed() + }); + gpiog.ospeedr.modify(|_, w| { + w.ospeedr2() + .very_high_speed() + .ospeedr11() + .very_high_speed() + .ospeedr13() + .very_high_speed() + }); + gpioa + .afrl + .modify(|_, w| w.afrl1().af11().afrl2().af11().afrl7().af11()); + gpiob.afrh.modify(|_, w| w.afrh13().af11()); + gpioc + .afrl + .modify(|_, w| w.afrl1().af11().afrl4().af11().afrl5().af11()); + gpiog.afrl.modify(|_, w| w.afrl2().af11()); + gpiog.afrh.modify(|_, w| w.afrh11().af11().afrh13().af11()); +} diff --git a/oxcc-bootloader/src/lib.rs b/oxcc-bootloader/src/lib.rs new file mode 100644 index 0000000..4b70dd9 --- /dev/null +++ b/oxcc-bootloader/src/lib.rs @@ -0,0 +1,29 @@ +#![no_std] + +extern crate cortex_m; +extern crate stm32f7; + +#[allow(dead_code, unused_variables)] +mod bootload; +#[allow(dead_code, unused_variables)] +mod config; + +use stm32f7::stm32f7x7; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Error { + Success, + InvalidAddress, + LengthNotMultiple4, + LengthTooLong, + DataLengthIncorrect, + EraseError, + WriteError, + FlashError, + NetworkError, + InternalError, +} + +pub type Result = core::result::Result; + +pub use self::bootload::reset_to_bootloader; diff --git a/oxcc-bootloader/src/main.rs b/oxcc-bootloader/src/main.rs new file mode 100644 index 0000000..2616bdd --- /dev/null +++ b/oxcc-bootloader/src/main.rs @@ -0,0 +1,175 @@ +#![no_std] +#![no_main] + +extern crate byteorder; +extern crate cortex_m; +extern crate cortex_m_rt; +extern crate cortex_m_semihosting; +extern crate oxcc_bootloader_lib; +extern crate panic_abort; +// Can be useful for debugging +//extern crate panic_semihosting; +extern crate smoltcp; +extern crate stm32f7; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; +use stm32f7::stm32f7x7; + +/// Try to print over semihosting if a debugger is available +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + use core::fmt::Write; + use cortex_m; + use cortex_m_semihosting; + if unsafe { (*cortex_m::peripheral::DCB::ptr()).dhcsr.read() & 1 == 1 } { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + write!(stdout, $($arg)*).ok(); + } + } + }) +} + +/// Try to print a line over semihosting if a debugger is available +#[macro_export] +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +mod bootload; +mod cache; +mod config; +mod ethernet; +mod flash; +mod gpio; +mod network; +mod rcc; +mod systick; + +use cache::cache_enable; +use gpio::gpio_init; +use oxcc_bootloader_lib::{Error, Result}; +use rcc::rcc_init; +use systick::systick_init; + +// Pull in build information (from `built` crate) +mod build_info { + #![allow(dead_code)] + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +#[entry] +fn main() -> ! { + let mut peripherals = stm32f7x7::Peripherals::take().unwrap(); + let mut core_peripherals = stm32f7x7::CorePeripherals::take().unwrap(); + + // Jump to user code if it exists and hasn't asked us to run + if let Some(address) = flash::valid_user_code() { + if !config::should_enter_bootloader(&mut peripherals) { + bootload::bootload(&mut core_peripherals.SCB, address); + } + } + + println!(""); + println!("|-=-=-=-=-=-=-= 0xCC Bootloader =-=-=-=-=-=-=-"); + println!( + "| Version {} {}", + build_info::PKG_VERSION, + build_info::GIT_VERSION.unwrap() + ); + println!("| Platform {}", build_info::TARGET); + println!("| Built on {}", build_info::BUILT_TIME_UTC); + println!("| {}", build_info::RUSTC_VERSION); + println!("|-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); + + print!(" Initialising cache... "); + cache_enable(&mut core_peripherals); + println!("OK"); + + print!(" Initialising clocks... "); + rcc_init(&mut peripherals); + println!("OK"); + + print!(" Initialising GPIOs... "); + gpio_init(&mut peripherals); + println!("OK"); + + print!(" Reading configuration... "); + let cfg = match flash::UserConfig::get(&mut peripherals.CRC) { + Some(cfg) => { + println!("OK"); + cfg + } + None => { + println!("Err\nCouldn't read configuration, using default."); + flash::DEFAULT_CONFIG + } + }; + println!("{}", cfg); + let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&cfg.mac_address); + + print!(" Initialising Ethernet... "); + let mut ethdev = + ethernet::EthernetDevice::new(peripherals.ETHERNET_MAC, peripherals.ETHERNET_DMA); + ethdev.init(&mut peripherals.RCC, mac_addr); + println!("OK"); + + print!(" Waiting for link... "); + ethdev.block_until_link(); + println!("OK"); + + print!(" 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, cidr); + println!("OK"); + + // Move flash peripheral into flash module + flash::init(peripherals.FLASH); + + // TODO - Blink status LED? + println!(" Ready.\n"); + + // Begin periodic tasks via systick + systick_init(&mut core_peripherals.SYST); + + loop { + cortex_m::asm::wfi(); + } +} + +static mut SYSTICK_TICKS: u32 = 0; +static mut SYSTICK_RESET_AT: Option = None; + +#[exception] +fn SysTick() { + let ticks = unsafe { core::ptr::read_volatile(&SYSTICK_TICKS) + 1 }; + unsafe { core::ptr::write_volatile(&mut SYSTICK_TICKS, ticks) }; + network::poll(i64::from(ticks)); + if let Some(reset_time) = unsafe { core::ptr::read_volatile(&SYSTICK_RESET_AT) } { + if ticks >= reset_time { + println!("Performing scheduled reset"); + bootload::reset_to_user_firmware(); + } + } +} + +/// Reset after some ms delay. +pub fn schedule_reset(delay: u32) { + cortex_m::interrupt::free(|_| unsafe { + let ticks = core::ptr::read_volatile(&SYSTICK_TICKS) + delay; + core::ptr::write_volatile(&mut SYSTICK_RESET_AT, Some(ticks)); + }); +} + +#[exception] +fn HardFault(ef: &ExceptionFrame) -> ! { + panic!("HardFault at {:#?}", ef); +} + +#[exception] +fn DefaultHandler(irqn: i16) { + panic!("Unhandled exception (IRQn = {})", irqn); +} diff --git a/oxcc-bootloader/src/network.rs b/oxcc-bootloader/src/network.rs new file mode 100644 index 0000000..7925ec9 --- /dev/null +++ b/oxcc-bootloader/src/network.rs @@ -0,0 +1,213 @@ +use core::fmt::Write; + +use smoltcp; +use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, Neighbor, NeighborCache}; +use smoltcp::socket::{SocketHandle, SocketSet, SocketSetItem, TcpSocket, TcpSocketBuffer}; +use smoltcp::time::Instant; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; + +use cortex_m; + +use byteorder::{ByteOrder, LittleEndian}; + +use build_info; +use ethernet::EthernetDevice; +use flash; +use Error; + +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]; + let mut len = [0u8; 4]; + socket.recv_slice(&mut adr[..]).ok(); + socket.recv_slice(&mut len[..]).ok(); + let adr = LittleEndian::read_u32(&adr); + let len = LittleEndian::read_u32(&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 mut resp = [0u8; 4]; + LittleEndian::write_u32(&mut resp, status as u32); + socket.send_slice(&resp).unwrap(); +} + +/// Respond to the information request command with our build information. +fn cmd_info(socket: &mut TcpSocket) { + // Read the device unique ID, see 45.6 + let id1: u32 = unsafe { *(0x1FF0_F420 as *const u32) }; + let id2: u32 = unsafe { *(0x1FF0_F424 as *const u32) }; + let id3: u32 = unsafe { *(0x1FF0_F428 as *const u32) }; + + send_status(socket, Error::Success); + write!( + socket, + "Version: {} {}\r\nBuilt: {}\r\nCompiler: {}\r\nMCU ID: {:08X}{:08X}{:08X}\r\n", + build_info::PKG_VERSION, + build_info::GIT_VERSION.unwrap(), + build_info::BUILT_TIME_UTC, + build_info::RUSTC_VERSION, + id3, + id2, + id1 + ).ok(); +} + +fn cmd_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), + }; +} + +fn cmd_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), + } +} + +fn cmd_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), + } +} + +fn cmd_boot(socket: &mut TcpSocket) { + send_status(socket, Error::Success); + ::schedule_reset(50); +} + +// Stores the underlying data buffers. If these were included in Network, +// they couldn't live in BSS and therefore take up a load of flash space. +struct NetworkBuffers { + tcp_tx_buf: [u8; 1536], + tcp_rx_buf: [u8; 1536], +} + +static mut NETWORK_BUFFERS: NetworkBuffers = NetworkBuffers { + tcp_tx_buf: [0u8; 1536], + tcp_rx_buf: [0u8; 1536], +}; + +// Stores all the smoltcp required structs. +pub struct Network<'a> { + neighbor_cache_storage: [Option<(IpAddress, Neighbor)>; 16], + ip_addr: Option<[IpCidr; 1]>, + eth_iface: Option>, + sockets_storage: [Option>; 1], + sockets: Option>, + tcp_handle: Option, + initialised: bool, +} + +static mut NETWORK: Network = Network { + neighbor_cache_storage: [None; 16], + ip_addr: None, + eth_iface: None, + sockets_storage: [None], + sockets: None, + tcp_handle: None, + initialised: false, +}; + +/// Initialise the static NETWORK. +/// +/// Sets up the required EthernetInterface and sockets. +/// +/// Do not call more than once or this function will panic. +pub fn init<'a>(eth_dev: EthernetDevice, mac_addr: EthernetAddress, ip_addr: IpCidr) { + // Unsafe required for access to NETWORK. + // NETWORK.initialised guards against calling twice. + unsafe { + cortex_m::interrupt::free(|_| { + if NETWORK.initialised { + panic!("NETWORK already initialised"); + } + NETWORK.initialised = true; + }); + + let neighbor_cache = NeighborCache::new(&mut NETWORK.neighbor_cache_storage.as_mut()[..]); + + NETWORK.ip_addr = Some([ip_addr]); + NETWORK.eth_iface = Some( + EthernetInterfaceBuilder::new(eth_dev) + .ethernet_addr(mac_addr) + .neighbor_cache(neighbor_cache) + .ip_addrs(&mut NETWORK.ip_addr.as_mut().unwrap()[..]) + .finalize(), + ); + + NETWORK.sockets = Some(SocketSet::new(&mut NETWORK.sockets_storage.as_mut()[..])); + let tcp_rx_buf = TcpSocketBuffer::new(&mut NETWORK_BUFFERS.tcp_rx_buf.as_mut()[..]); + let tcp_tx_buf = TcpSocketBuffer::new(&mut NETWORK_BUFFERS.tcp_tx_buf.as_mut()[..]); + let tcp_socket = TcpSocket::new(tcp_rx_buf, tcp_tx_buf); + NETWORK.tcp_handle = Some(NETWORK.sockets.as_mut().unwrap().add(tcp_socket)); + } +} + +/// Poll network stack. +/// +/// Arrange for this function to be called frequently. +pub fn poll(time_ms: i64) { + // Unsafe required to access static mut NETWORK. + // Since the entire poll is run in an interrupt-free context no + // other access to NETWORK can occur. + cortex_m::interrupt::free(|_| unsafe { + // Bail out early if NETWORK is not initialised. + if !NETWORK.initialised { + return; + } + + let sockets = NETWORK.sockets.as_mut().unwrap(); + + // Handle TCP + { + let mut socket = sockets.get::(NETWORK.tcp_handle.unwrap()); + if !socket.is_open() { + socket.listen(TCP_PORT).unwrap(); + } + 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 = LittleEndian::read_u32(&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), + _ => (), + }; + socket.close(); + } + } + + // Poll smoltcp + let timestamp = Instant::from_millis(time_ms); + match NETWORK.eth_iface.as_mut().unwrap().poll(sockets, timestamp) { + Ok(_) | Err(smoltcp::Error::Exhausted) => (), + Err(_) => (), + } + }); +} diff --git a/oxcc-bootloader/src/rcc.rs b/oxcc-bootloader/src/rcc.rs new file mode 100644 index 0000000..2a59b07 --- /dev/null +++ b/oxcc-bootloader/src/rcc.rs @@ -0,0 +1,88 @@ +use stm32f7::stm32f7x7; + +/// Set up PLL to 168MHz from 16MHz HSI +pub fn rcc_init(peripherals: &mut stm32f7x7::Peripherals) { + let rcc = &peripherals.RCC; + let flash = &peripherals.FLASH; + let syscfg = &peripherals.SYSCFG; + + // Reset all peripherals + rcc.ahb1rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + rcc.ahb1rstr.write(|w| unsafe { w.bits(0) }); + rcc.ahb2rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + rcc.ahb2rstr.write(|w| unsafe { w.bits(0) }); + rcc.ahb3rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + rcc.ahb3rstr.write(|w| unsafe { w.bits(0) }); + rcc.apb1rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + rcc.apb1rstr.write(|w| unsafe { w.bits(0) }); + rcc.apb2rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + rcc.apb2rstr.write(|w| unsafe { w.bits(0) }); + + // Ensure HSI is on and stable + rcc.cr.modify(|_, w| w.hsion().set_bit()); + while rcc.cr.read().hsion().bit_is_clear() {} + + // Set system clock to HSI + rcc.cfgr.modify(|_, w| w.sw().hsi()); + while !rcc.cfgr.read().sws().is_hsi() {} + + // Clear registers to reset value + rcc.cr.write(|w| w.hsion().set_bit()); + rcc.cfgr.write(|w| unsafe { w.bits(0) }); + + // Configure PLL: 16MHz /8 *168 /2, source HSI + rcc.pllcfgr.write(|w| unsafe { + w.pllq() + .bits(4) + .pllsrc() + .hsi() + .pllp() + .div2() + .plln() + .bits(168) + .pllm() + .bits(8) + }); + // Activate PLL + rcc.cr.modify(|_, w| w.pllon().set_bit()); + + // Set other clock domains: PPRE2 to /2, PPRE1 to /4, HPRE to /1 + rcc.cfgr + .modify(|_, w| w.ppre2().div2().ppre1().div4().hpre().div1()); + + // Flash setup: prefetch enabled, 5 wait states (OK for 3.3V + // at 168MHz) + flash.acr.write(|w| w.prften().set_bit().latency().bits(5)); + + // Swap system clock to PLL + rcc.cfgr.modify(|_, w| w.sw().pll()); + while !rcc.cfgr.read().sws().is_pll() {} + + // Set SYSCFG early to RMII mode + rcc.apb2enr.modify(|_, w| w.syscfgen().enabled()); + syscfg.pmc.modify(|_, w| w.mii_rmii_sel().set_bit()); + + // Set up peripheral clocks + rcc.ahb1enr.modify(|_, w| { + w.gpioaen() + .enabled() + .gpioben() + .enabled() + .gpiocen() + .enabled() + .gpioden() + .enabled() + .gpioeen() + .enabled() + .gpiogen() + .enabled() + .crcen() + .enabled() + .ethmacrxen() + .enabled() + .ethmactxen() + .enabled() + .ethmacen() + .enabled() + }); +} diff --git a/oxcc-bootloader/src/systick.rs b/oxcc-bootloader/src/systick.rs new file mode 100644 index 0000000..51b3ffc --- /dev/null +++ b/oxcc-bootloader/src/systick.rs @@ -0,0 +1,11 @@ +use cortex_m; +use stm32f7::stm32f7x7; + +/// Set up the systick to provide a 1ms timebase +pub fn systick_init(syst: &mut stm32f7x7::SYST) { + syst.set_reload((168_000_000 / 8) / 1000); + syst.clear_current(); + syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::External); + syst.enable_interrupt(); + syst.enable_counter(); +} diff --git a/src/can_protocols/bootloader_can_protocol.rs b/src/can_protocols/bootloader_can_protocol.rs new file mode 100644 index 0000000..23f62a7 --- /dev/null +++ b/src/can_protocols/bootloader_can_protocol.rs @@ -0,0 +1,3 @@ +pub const OSCC_BOOTLOADER_RESET_CAN_ID: u16 = 0xF0; + +pub const OSCC_BOOTLOADER_RESET_CAN_DLC: u8 = 8; diff --git a/src/config.rs b/src/config.rs index 18e8657..d4805c8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,4 @@ +use bootloader_can_protocol::*; use brake_can_protocol::*; use fault_can_protocol::*; use nucleo_f767zi::hal::can::{ @@ -77,6 +78,7 @@ pub fn gather_control_can_filters() -> [CanFilterConfig; 3] { f1.filter_id_high = 0; // filter 2 stores the enable control IDs for brake, throttle, and steering + // and the bootloader reset command ID // FIFO_1 let mut f2 = CanFilterConfig::default(); f2.filter_number = 2; @@ -87,7 +89,7 @@ pub fn gather_control_can_filters() -> [CanFilterConfig; 3] { f2.filter_mask_id_low = u32::from(OSCC_BRAKE_ENABLE_CAN_ID << 5); f2.filter_id_low = u32::from(OSCC_THROTTLE_ENABLE_CAN_ID << 5); f2.filter_mask_id_high = u32::from(OSCC_STEERING_ENABLE_CAN_ID << 5); - f2.filter_id_high = 0; + f2.filter_id_high = u32::from(OSCC_BOOTLOADER_RESET_CAN_ID << 5); [f0, f1, f2] } diff --git a/src/fw_update.rs b/src/fw_update.rs new file mode 100644 index 0000000..68be4be --- /dev/null +++ b/src/fw_update.rs @@ -0,0 +1,20 @@ +use bootloader_can_protocol::*; +use nucleo_f767zi::hal::can::CanFrame; +use oxcc_bootloader_lib::reset_to_bootloader; +use oxcc_error::OxccError; + +// TODO - cleanup CAN protocols and namespaces, use/validate data bytes? +pub fn process_rx_frame(can_frame: &CanFrame) -> Result<(), OxccError> { + if let CanFrame::DataFrame(ref frame) = can_frame { + let id: u32 = frame.id().into(); + let dlc = frame.data().len(); + + if id == u32::from(OSCC_BOOTLOADER_RESET_CAN_ID) { + if dlc == usize::from(OSCC_BOOTLOADER_RESET_CAN_DLC) { + reset_to_bootloader(); + } + } + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 1aa0696..7dd8b58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ extern crate panic_abort; extern crate panic_semihosting; #[macro_use] extern crate typenum; +extern crate oxcc_bootloader_lib; mod board; mod can_gateway_module; @@ -23,12 +24,15 @@ mod dac_mcp4922; mod dtc; mod dual_signal; mod fault_condition; +mod fw_update; mod oxcc_error; mod ranges; mod steering_module; mod throttle_module; mod types; +#[path = "can_protocols/bootloader_can_protocol.rs"] +mod bootloader_can_protocol; #[path = "can_protocols/brake_can_protocol.rs"] mod brake_can_protocol; #[path = "can_protocols/fault_can_protocol.rs"] @@ -118,6 +122,7 @@ fn main() -> ! { writeln!(debug_console, "OxCC is running").unwrap(); // TODO - some of these are worthy of disabling controls? + // NOTE - these are currently cleared when using the fota-bootloader if board.reset_conditions.low_power { writeln!(debug_console, "WARNING: low-power reset detected") .expect(DEBUG_WRITE_FAILURE); @@ -262,6 +267,9 @@ fn process_control_can_frames( modules .steering .process_rx_frame(&rx_frame, debug_console)?; + + // process any firmware-update/bootloader frames + fw_update::process_rx_frame(&rx_frame)?; } Err(e) => { // report all but BufferExhausted (no data)