Compare commits

..

16 Commits

Author SHA1 Message Date
occheung fc1fedf890 main: incoporate alloc, tls, and flash 2020-12-18 17:46:56 +08:00
occheung 3b4a81eceb fix itm 2020-12-18 17:45:58 +08:00
occheung 04c0fa4074 flash_mem: alloc 2020-12-18 17:45:42 +08:00
occheung 8ce3200b5d flash_gen: fix endianness 2020-12-18 17:44:38 +08:00
occheung f1069d951d urukul: failsave for PLL timeout 2020-12-18 17:43:59 +08:00
occheung adc5807ff1 mqtt: support sfkv 2020-12-18 17:42:26 +08:00
occheung d5f9480645 sfkv: init 2020-12-18 17:39:45 +08:00
occheung 2e86838a22 nal: emigrate to tls 2020-12-18 17:39:24 +08:00
occheung 19ea898c31 nal: migrate to tls 2020-12-18 17:38:58 +08:00
occheung 68d58857cf flash: init abstraction 2020-12-18 17:37:56 +08:00
occheung d6fdc36c78 add sfkv, tls dependencies 2020-12-18 17:37:11 +08:00
occheung eafd26bbc5 Merge branch 'master' of https://git.m-labs.hk/occheung/firmware-development 2020-10-08 15:03:05 +08:00
occheung 3252dfcd42 readme: fix slash 2020-10-06 15:12:12 +08:00
occheung 52986e214f flash: fix flash read 2020-10-06 15:03:53 +08:00
occheung 7e09318919 nix: simplify 2020-10-06 15:02:49 +08:00
occheung 5fd7f36c65 flash: generate bin thru py 2020-10-06 15:01:04 +08:00
18 changed files with 1908 additions and 398 deletions

777
Cargo.lock generated
View File

@ -1,5 +1,60 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aead"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
dependencies = [
"generic-array 0.14.4",
"heapless",
]
[[package]]
name = "aes"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
"aes-soft",
"aesni",
"cipher",
]
[[package]]
name = "aes-gcm"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "aes-soft"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
"cipher",
"opaque-debug",
]
[[package]]
name = "aesni"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
"cipher",
"opaque-debug",
]
[[package]]
name = "aligned"
version = "0.3.4"
@ -9,6 +64,16 @@ dependencies = [
"as-slice",
]
[[package]]
name = "alloc-cortex-m"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c46567ff10c713079e2b9ee67638073471a6b265e22c0bcd8ca96f63545cc90b"
dependencies = [
"cortex-m",
"linked_list_allocator",
]
[[package]]
name = "as-slice"
version = "0.1.3"
@ -20,6 +85,12 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
[[package]]
name = "autocfg"
version = "1.0.0"
@ -53,6 +124,35 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bitvec"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2838fdd79e8776dbe07a106c784b0f8dda571a21b2750a092cc4cbaa653c8e"
dependencies = [
"funty",
"radium",
"wyz",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "block-cipher"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f337a3e6da609650eb74e02bc9fac7b735049f7623ab12f2e4c719316fcc7e80"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "byteorder"
version = "1.3.4"
@ -68,12 +168,71 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "ccm"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7"
dependencies = [
"aead",
"cipher",
"subtle",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chacha20"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "244fbce0d47e97e8ef2f63b81d5e05882cb518c68531eb33194990d7b7e85845"
dependencies = [
"stream-cipher",
"zeroize",
]
[[package]]
name = "chacha20poly1305"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bf18d374d66df0c05cdddd528a7db98f78c28e2519b120855c4f84c5027b1f5"
dependencies = [
"aead",
"chacha20",
"poly1305",
"stream-cipher",
"zeroize",
]
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "cortex-m"
version = "0.6.3"
@ -143,6 +302,132 @@ dependencies = [
"syn",
]
[[package]]
name = "cpuid-bool"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crypto-mac"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]]
name = "crypto-mac"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]]
name = "crypto-mac"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]]
name = "ctr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
dependencies = [
"cipher",
]
[[package]]
name = "curve25519-dalek"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8492de420e9e60bc9a1d66e2dbb91825390b738a388606600663fc529b4b307"
dependencies = [
"byteorder",
"digest",
"rand_core",
"subtle",
"zeroize",
]
[[package]]
name = "derivative"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "ecdsa"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87bf8bfb05ea8a6f74ddf48c7d1774851ba77bbe51ac984fdfa6c30310e1ff5f"
dependencies = [
"elliptic-curve",
"hmac 0.9.0",
"signature",
]
[[package]]
name = "ed25519"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c66a534cbb46ab4ea03477eae19d5c22c01da8258030280b7bd9d8433fb6ef"
dependencies = [
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"sha2",
"zeroize",
]
[[package]]
name = "elliptic-curve"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396db09c483e7fca5d4fdb9112685632b3e76c9a607a2649c1bf904404a01366"
dependencies = [
"bitvec",
"digest",
"ff",
"generic-array 0.14.4",
"group",
"rand_core",
"subtle",
"zeroize",
]
[[package]]
name = "embedded-hal"
version = "0.2.4"
@ -184,6 +469,23 @@ dependencies = [
"syn",
]
[[package]]
name = "ff"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01646e077d4ebda82b73f1bca002ea1e91561a77df2431a9e79729bcc31950ef"
dependencies = [
"bitvec",
"rand_core",
"subtle",
]
[[package]]
name = "funty"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8"
[[package]]
name = "generic-array"
version = "0.12.3"
@ -212,6 +514,26 @@ dependencies = [
"version_check",
]
[[package]]
name = "ghash"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6e27f0689a6e15944bdce7e45425efb87eaa8ab0c6e87f11d0987a9133e2531"
dependencies = [
"polyval",
]
[[package]]
name = "group"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc11f9f5fbf1943b48ae7c2bf6846e7d827a512d1be4f23af708f5ca5d01dde1"
dependencies = [
"ff",
"rand_core",
"subtle",
]
[[package]]
name = "hash32"
version = "0.1.1"
@ -236,13 +558,55 @@ dependencies = [
"as-slice",
"generic-array 0.13.2",
"hash32",
"serde",
"stable_deref_trait",
]
[[package]]
name = "hkdf"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe1149865383e4526a43aee8495f9a325f0b806c63ce6427d06336a590abbbc9"
dependencies = [
"digest",
"hmac 0.8.1",
]
[[package]]
name = "hmac"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840"
dependencies = [
"crypto-mac 0.8.0",
"digest",
]
[[package]]
name = "hmac"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff"
dependencies = [
"crypto-mac 0.9.1",
"digest",
]
[[package]]
name = "hmac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
dependencies = [
"crypto-mac 0.10.0",
"digest",
]
[[package]]
name = "humpback-dds"
version = "0.1.0"
dependencies = [
"alloc-cortex-m",
"cortex-m",
"cortex-m-log",
"cortex-m-rt",
@ -257,8 +621,12 @@ dependencies = [
"nom",
"panic-halt",
"panic-itm",
"rand_core",
"ryu",
"serde",
"sfkv",
"smoltcp",
"smoltcp-tls",
"stm32h7xx-hal",
]
@ -268,10 +636,19 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg",
"autocfg 1.0.0",
"hashbrown",
]
[[package]]
name = "intrusive-collections"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca8c0bb831cd60d4dda79a58e3705ca6eb47efb65d665651a8d672213ec3db"
dependencies = [
"memoffset",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -281,13 +658,25 @@ dependencies = [
"spin",
]
[[package]]
name = "libm"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
[[package]]
name = "linked_list_allocator"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84565678e403453d1a27a0886882b3b271701e65146d972d9d7d9a4c4a0ff498"
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
@ -296,12 +685,27 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "memoffset"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"autocfg 1.0.0",
]
[[package]]
name = "minimq"
version = "0.1.0"
@ -345,6 +749,93 @@ dependencies = [
"version_check",
]
[[package]]
name = "num-bigint-dig"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a"
dependencies = [
"autocfg 0.1.7",
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand",
"smallvec",
"zeroize",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg 1.0.0",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
"autocfg 1.0.0",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg 1.0.0",
"libm",
]
[[package]]
name = "num_enum"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066"
dependencies = [
"derivative",
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "p256"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280ed58e7e5f3052b6e2f596fa40c7eff4c27c4b6b6deecb5d685ba5c2080980"
dependencies = [
"ecdsa",
"elliptic-curve",
"sha2",
]
[[package]]
name = "panic-halt"
version = "0.2.0"
@ -379,6 +870,48 @@ dependencies = [
"proc-macro-hack",
]
[[package]]
name = "poly1305"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ce46de8e53ee414ca4d02bfefac75d8c12fba948b76622a40b4be34dfce980"
dependencies = [
"universal-hash",
]
[[package]]
name = "polyval"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3fd900a291ceb8b99799cc8cd3d1d3403a51721e015bc533528b2ceafcc443c"
dependencies = [
"cfg-if 1.0.0",
"universal-hash",
]
[[package]]
name = "postcard"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e3f5c2e9a91383c6594ec68aa2dfdfe19a3c86f34b088ba7203f2483d2682f"
dependencies = [
"heapless",
"postcard-cobs",
"serde",
]
[[package]]
name = "postcard-cobs"
version = "0.1.5-pre"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-hack"
version = "0.5.18"
@ -409,6 +942,65 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
[[package]]
name = "radium"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64de9a0c5361e034f1aefc9f71a86871ec870e766fe31a009734a989b329286a"
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rsa"
version = "0.3.0"
source = "git+https://github.com/RustCrypto/RSA.git?rev=c879eb2#c879eb27175d08f092f9e014abf16a467c3cf393"
dependencies = [
"byteorder",
"digest",
"lazy_static",
"num-bigint-dig",
"num-integer",
"num-iter",
"num-traits",
"rand",
"subtle",
"zeroize",
]
[[package]]
name = "rtic-core"
version = "0.3.0"
@ -456,6 +1048,79 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sfkv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f5bfac3f66a7c10a6f37ee81aeaa471f4d35dc21665b59ad7c555adcb9e8aa"
dependencies = [
"byteorder",
"postcard",
"serde",
]
[[package]]
name = "sha-1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
"cpuid-bool",
"digest",
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
"cpuid-bool",
"digest",
"opaque-debug",
]
[[package]]
name = "signature"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210"
dependencies = [
"digest",
"rand_core",
]
[[package]]
name = "smallvec"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
[[package]]
name = "smoltcp"
version = "0.6.0"
@ -463,7 +1128,37 @@ source = "git+https://github.com/smoltcp-rs/smoltcp.git#bdfa44270e9c59b3095b555c
dependencies = [
"bitflags",
"byteorder",
"managed",
"managed 0.7.2",
]
[[package]]
name = "smoltcp-tls"
version = "0.1.0"
source = "git+https://git.m-labs.hk/M-Labs/smoltcp-tls.git#0c6807f59315e040244b3400c3eae691540eef6b"
dependencies = [
"aes-gcm",
"byteorder",
"ccm",
"chacha20poly1305",
"chrono",
"ed25519-dalek",
"embedded-nal",
"generic-array 0.14.4",
"heapless",
"hkdf",
"hmac 0.10.1",
"intrusive-collections",
"log",
"managed 0.8.0",
"nom",
"num_enum",
"p256",
"rand_core",
"rsa",
"sha-1",
"sha2",
"smoltcp",
"x25519-dalek",
]
[[package]]
@ -508,6 +1203,22 @@ dependencies = [
"void",
]
[[package]]
name = "stream-cipher"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c80e15f898d8d8f25db24c253ea615cc14acf418ff307822995814e7d42cfa89"
dependencies = [
"block-cipher",
"generic-array 0.14.4",
]
[[package]]
name = "subtle"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
[[package]]
name = "syn"
version = "1.0.38"
@ -519,6 +1230,18 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "synstructure"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "typenum"
version = "1.12.0"
@ -531,6 +1254,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "universal-hash"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]]
name = "vcell"
version = "0.1.2"
@ -557,3 +1290,41 @@ checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
dependencies = [
"vcell",
]
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]]
name = "x25519-dalek"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc614d95359fd7afc321b66d2107ede58b246b844cf5d8a0adcca413e439f088"
dependencies = [
"curve25519-dalek",
"rand_core",
"zeroize",
]
[[package]]
name = "zeroize"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]

View File

@ -18,6 +18,13 @@ minimq = { git = "https://github.com/quartiq/minimq.git", branch = "master" }
heapless = "0.5.6"
nom = { version = "5.1.2", default-features = false, features = [] }
ryu = "1.0"
smoltcp-tls = { git = "https://git.m-labs.hk/M-Labs/smoltcp-tls.git", features = [ "nal_tcp_stack" ] }
rand_core = { version = "0.5.1", default-features = false, features = [] }
sfkv = "0.1.0"
serde = { version = "1.0", default-features = false, features = ["derive"] }
# Allocator
alloc-cortex-m = "0.4.0"
# Logging and Panicking
panic-itm = "0.4.1"

View File

@ -25,7 +25,7 @@ openocd-flash
## Networking Setup for First-time User
Provide them to setup Humpback-DDS:
- IP Address of Humpback-DDS
- Address block of the local area network
- Bit length of the LAN prefix
- MAC Address of Humpback-DDS
- IP Address of MQTT broker
- Device name of Humpback-DDS
@ -50,7 +50,7 @@ Parameters:
openocd-flash-customised 192.168.1.200/24 AC:6F:7A:DE:D6:C8 192.168.1.125 "Urukul"
```
The device will be named `Urukul`.
It has `192.168.1.200` as IPv4 Address, inside a `\24` network, with `AC:6F:7A:DE:D6:C8` as MAC address.
It has `192.168.1.200` as IPv4 Address, inside a `/24` network, with `AC:6F:7A:DE:D6:C8` as MAC address.
It will connect to a MQTT broker at `192.168.1.125:1883`.

View File

@ -1 +1 @@
"1kc2i3c7lmbx6fxlhw1wd3v679zh4mldyiyvaa24jarwlxnp5wq8"
"1wzbn4r6gya72lgp8464izi0fhvp9pmmi1qck7qcw4ww8p8lzq7p"

49
flash.py Normal file
View File

@ -0,0 +1,49 @@
import argparse
import struct
def get_argparser():
parser = argparse.ArgumentParser(description="MQTT-controlled 4-channel DDS signal generator")
parser.add_argument("cidr", nargs=1,
metavar=("Client_CIDR"),
help="IPv4 Address of Humpback-DDS in CIDR notation")
parser.add_argument("mac_addr", nargs=1,
metavar=("Client_MAC"),
help="MAC Address of Humpback-DDS")
parser.add_argument("broker_ip", nargs=1,
metavar=("Broker_IP"),
help="IP address of MQTT broker")
parser.add_argument("name", nargs=1,
metavar=("Name"),
help="Device name of Humpback-DDS")
return parser
def write_record(f, key, value):
key_size = len(key) + 1
value_size = len(value)
record_size = key_size + value_size + 4
f.write(struct.pack(">l", record_size))
f.write(key.encode())
f.write(b"\x00")
f.write(value)
def write_end_marker(f):
f.write(b"\xff\xff\xff\xff")
def main():
args = get_argparser().parse_args()
with open("flash_config.bin", "wb") as fo:
write_record(fo, "CIDR", args.cidr[0].encode())
write_record(fo, "MAC", args.mac_addr[0].encode())
write_record(fo, "BrokerIP", args.broker_ip[0].encode())
write_record(fo, "Name", args.name[0].encode())
write_end_marker(fo)
if __name__ == "__main__":
main()

View File

@ -1,7 +1,7 @@
MEMORY
{
/* FLASH and RAM are mandatory memory regions */
FLASH : ORIGIN = 0x08000000, LENGTH = 1024K
FLASH : ORIGIN = 0x08000000, LENGTH = 1920K
RAM : ORIGIN = 0x20000000, LENGTH = 128K
/* AXISRAM */

View File

@ -13,7 +13,7 @@ rustPlatform.buildRustPackage rec {
cargoPatches = [ ./itm-cargo-lock.patch ];
cargoSha256 = "1lgv8nhzbzfw9cl4rhj46a86h9jygz0ih3j4zw5nd1346xgmz7b8";
cargoSha256 = "1x2pagfxwhgxliygw7325qsg3ccn276f4slg9cql0sf1s304qj4g";
nativeBuildInputs = [ pkgconfig ];

View File

@ -21,27 +21,16 @@ let
'';
openOCDFlashCustomised = writeShellScriptBin "openocd-flash-customised" ''
IFS='.|/' read -r a b c d e <<< $1
((ip = (a << 32) + (b << 24) + (c << 16) + (d << 8) + e))
IFS=':' read -r a b c d e f <<< $2
((mac = (16#$a << 40) + (16#$b << 32) + (16#$c << 24) + (16#$d << 16) + (16#$e << 8) + 16#$f))
IFS='.' read -r a b c d <<< $3
((broker_ip = (a << 24) + (b << 16) + (c << 8) + d))
touch temp_name
printf "%s\x04" "$4" > temp_name
python3 flash.py $1 $2 $3 $4
openocd -f openocd/openocd.cfg \
-c "init
reset init
halt
stm32h7x mass_erase 1
flash write_image erase target/thumbv7em-none-eabihf/release/humpback-dds
flash filld 0x08100000 $ip 1
flash filld 0x08100020 $mac 1
flash fillw 0x08100040 $broker_ip 1
flash write_image temp_name 0x08100060 bin
flash write_image flash_config.bin 0x081e0000 bin
reset run
shutdown"
rm temp_name
'';
in

72
src/config.rs Normal file
View File

@ -0,0 +1,72 @@
use smoltcp as net;
use net::wire::IpCidr;
use net::wire::EthernetAddress;
use embedded_nal as nal;
use nal::IpAddr;
use heapless::{ String, consts::* };
use serde::{ Serialize, Deserialize };
use crate::urukul::ClockSource;
use crate::flash_store::FlashStore;
use core::str::FromStr;
#[derive(Debug)]
pub struct NetConfig {
pub ip_cidr: IpCidr,
pub eth_addr: EthernetAddress,
pub broker_ip: IpAddr,
pub name: String<U64>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UrukulConfig {
pub clk_src: ClockSource,
pub clk_freq: f64,
pub clk_div: u8,
pub profile: u8,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChannelConfig {
pub sw: bool,
pub att: f32,
pub sys_clk: f64,
pub freq: f64,
pub phase: f64,
pub asf: f64,
}
pub fn get_net_config(store: &mut FlashStore) -> NetConfig {
let net_config = NetConfig {
ip_cidr: {
match store.read_str("CIDR").unwrap() {
Some(cidr) => IpCidr::from_str(cidr).unwrap(),
None => IpCidr::from_str("192.168.1.200/24").unwrap(),
}
},
eth_addr: {
match store.read_str("MAC").unwrap() {
Some(mac) => EthernetAddress::from_str(mac).unwrap(),
None => EthernetAddress::from_str("AC:6F:7A:DE:D6:C8").unwrap()
}
},
broker_ip: {
match store.read_str("BrokerIP").unwrap() {
Some(ip) => IpAddr::from_str(ip).unwrap(),
None => IpAddr::from_str("192.168.1.125").unwrap(),
}
},
name: {
match store.read_str("Name").unwrap() {
Some(name) => String::from(name),
None => String::from("HumpbackDDS")
}
}
};
log::info!("Net config: {:?}", net_config);
net_config
}

View File

@ -253,7 +253,12 @@ where
}
// Finally, try enabling PLL
else {
self.enable_pll(f_sys_clk)
self.enable_pll(f_sys_clk)?;
if self.f_sys_clk == self.get_sys_clk_frequency()? {
Ok(())
} else {
Err(Error::WaitRetry)
}
}
}
@ -844,6 +849,32 @@ where
pub fn get_f_sys_clk(&mut self) -> f64 {
self.f_sys_clk
}
// Acquire real f_sys_clk
pub fn get_sys_clk_frequency(&mut self) -> Result<f64, Error<E>> {
// Calculate the new system clock frequency, examine the clock tree
let mut configuration_queries = [
// Acquire PLL status
(DDSCFRMask::PLL_ENABLE, 0),
// Acquire N-divider, to adjust VCO if necessary
(DDSCFRMask::N, 0),
// Acquire REF_CLK divider bypass
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0)
];
self.get_configurations(&mut configuration_queries)?;
if configuration_queries[0].1 == 1 {
// Recalculate sys_clk
let divider :f64 = configuration_queries[1].1.into();
Ok(self.f_ref_clk * divider)
}
else if configuration_queries[2].1 == 0 {
Ok(self.f_ref_clk / 2.0)
}
else {
Ok(self.f_ref_clk)
}
}
}
// Strong check for bytes passed to a register

View File

@ -1,91 +1,408 @@
use embedded_hal::{
digital::v2::{OutputPin, InputPin},
blocking::spi::Transfer,
blocking::delay::DelayUs,
};
//! Flash memory, with major reference from STM32F7xx_HAL crate,
//! as well as @Astro 's work in STM32F4xx_HAL
#[derive(Debug)]
pub enum FPGAFlashError {
SPICommunicationError,
NegotiationError,
ResetStatusError,
use stm32h7xx_hal::pac::FLASH;
/// Base address of flash memory on AXIM interface.
const FLASH_BASE: *mut u8 = 0x800_0000 as *mut u8;
/// The last valid flash address in STM32H743
const MAX_FLASH_ADDRESS: *mut u8 = 0x81F_FFFF as *mut u8;
/// Flash programming error.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Busy,
Locked,
WriteProtection,
ProgrammingSequence,
Strobe,
Inconsistency,
Operation,
ErrorCorrectionCode,
ReadProtection,
ReadSecure,
WriteError
}
const DATA: &'static [u8] = include_bytes!("../build/top.bin");
/// Embedded flash memory.
pub struct Flash {
registers: FLASH,
}
// A public method to flash iCE40 FPGA on Humpback
pub fn flash_ice40_fpga<SPI: Transfer<u8>,
SS: OutputPin,
RST: OutputPin,
DELAY: DelayUs<u32>,
DONE: InputPin>
(mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError>
{
// Data buffer setup
let mut dummy_byte :[u8; 1] = [0x00];
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
// Drive CRESET_B low
creset.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least 200ns
delay.delay_us(1_u32);
// Drive CRESET_B high
creset.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least another 1200us to clear internal config memory
delay.delay_us(1200_u32);
// Before data transmission starts, check if C_DONE is truly low
// If C_DONE is high, the FPGA reset procedure is unsuccessful
match cdone.is_low() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Set SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send 8 dummy clock, effectively 1 byte of 0x00
spi.transfer(&mut dummy_byte)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send the whole image without interruption
for byte in DATA.into_iter() {
let mut single_byte_slice = [*byte];
spi.transfer(&mut single_byte_slice)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
impl Flash {
/// Creates a new Flash instance.
pub fn new(flash: FLASH) -> Self {
let mut flash = Self { registers: flash };
// Lock both banks initially
flash.lock();
flash
}
// Drive SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
/// Unlocks the FLASH_CR1/2 register.
pub fn unlock(&mut self) {
// Note: Unlocking an unlocked bank will cause HardFault
// Send at another 100 dummy clocks (choosing 13 bytes)
spi.transfer(&mut dummy_13_bytes)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Unlock bank 1 if needed.
if self.bank1_is_locked() {
self.registers.bank1_mut().keyr.write(|w| unsafe {
w.keyr().bits(0x45670123)
});
self.registers.bank1_mut().keyr.write(|w| unsafe {
w.keyr().bits(0xCDEF89AB)
});
}
// Check the CDONE output from FPGA
// CDONE needs to be high
match cdone.is_high() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Unlock bank 2 if needed.
if self.bank2_is_locked() {
self.registers.bank2_mut().keyr.write(|w| unsafe {
w.keyr().bits(0x45670123)
});
self.registers.bank2_mut().keyr.write(|w| unsafe {
w.keyr().bits(0xCDEF89AB)
});
}
}
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
spi.transfer(&mut dummy_13_bytes).map_err(|_| FPGAFlashError::SPICommunicationError)?;
Ok(())
/// Unlocks the FLASH_OPTCR register
pub fn unlock_optcr(&mut self) {
if self.optcr_is_locked() {
self.registers.optkeyr_mut().write(|w| unsafe {
w.optkeyr().bits(0x08192A3B)
});
self.registers.optkeyr_mut().write(|w| unsafe {
w.optkeyr().bits(0x4C5D6E7F)
});
}
}
/// Locks the FLASH_CR1/2 register.
pub fn lock(&mut self) {
self.registers.bank1_mut().cr.modify(|_, w| w.lock().set_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.lock().set_bit());
}
/// Lock the FLASH_OPTCR register
pub fn lock_optcr(&mut self) {
self.registers.optcr_mut().modify(|_, w| w.optlock().set_bit());
}
// More literal methods to get bank status
fn bank1_is_locked(&self) -> bool {
self.registers.bank1_mut().cr.read().lock().bit_is_set()
}
fn bank2_is_locked(&self) -> bool {
self.registers.bank2_mut().cr.read().lock().bit_is_set()
}
fn optcr_is_locked(&self) -> bool {
self.registers.optcr().read().optlock().bit_is_set()
}
pub fn is_locked(&self) -> bool {
self.bank1_is_locked() || self.bank2_is_locked()
}
/// Returns `true` if a write/erase operation is in progress.
fn is_busy(&self) -> bool {
let (sr1, sr2) = (
self.registers.bank1_mut().sr.read(),
self.registers.bank2_mut().sr.read()
);
sr1.bsy().bit_is_set() || sr2.bsy().bit_is_set()
}
/// Returns `true` if a write/erase operation is in a command queue buffer.
fn is_queuing(&self) -> bool {
let (sr1, sr2) = (
self.registers.bank1_mut().sr.read(),
self.registers.bank2_mut().sr.read()
);
sr1.qw().bit_is_set() || sr2.qw().bit_is_set()
}
/// Returns `true` if write buffer is not empty in bank 1
fn bank1_is_buffering(&self) -> bool {
let sr1 = self.registers.bank1_mut().sr.read();
sr1.wbne().bit_is_set()
}
/// Returns `true` if write buffer is not empty in bank 2
fn bank2_is_buffering(&self) -> bool {
let sr2 = self.registers.bank2_mut().sr.read();
sr2.wbne().bit_is_set()
}
/// Erase a sector.
pub fn erase_sector(&mut self, bank_number: u8, sector_number: u8) -> Result<(), Error> {
// Assert parameters validity
assert!(bank_number == 1 || bank_number == 2);
assert!(sector_number < 8);
// 1. Check & clear error flags
self.clear_errors();
// 2. Unlock FLASH_CR1/2 register if necessary
self.unlock();
// 3. Set SER1/2 & SNB1/2 bits in FLASH_CR1/2 register
// 4. Set START1/2 bit in FLASH_CR1/2 register
match bank_number {
1 => {
self.registers.bank1_mut().cr.modify(|_, w| unsafe {
w.ser()
.set_bit()
.snb()
.bits(sector_number)
});
self.registers.bank1_mut().cr.modify(|_, w| {
w.start().set_bit()
});
},
2 => {
self.registers.bank2_mut().cr.modify(|_, w| unsafe {
w.ser()
.set_bit()
.snb()
.bits(sector_number)
});
self.registers.bank2_mut().cr.modify(|_, w| {
w.start().set_bit()
});
},
_ => unreachable!()
}
// Lock the flash CR again
self.lock();
Ok(())
}
/// Erase a bank.
pub fn erase_bank(&mut self, bank_number: u8) -> Result<(), Error> {
// Assert parameters validity
assert!(bank_number == 1 || bank_number == 2);
// 1. Check & clear error flags
self.clear_errors();
// 2. Unlock FLASH_CR1/2 register if necessary
self.unlock();
// 3 & 4. Set BER1/2 bit and START1/2 bit in FLASH_CR1/2 register
// Wait until the corresponding QW1/2 bit is cleared.
// Note: By setting BER1/2 bit alongside START1/2,
// mass erase is invoked since it supersedes sector erase.
match bank_number {
1 => {
self.registers.bank1_mut().cr.modify(|_, w| {
w.ber()
.set_bit()
.start()
.set_bit()
});
while self.registers.bank1_mut().sr.read().qw().bit_is_set() {}
},
2 => {
self.registers.bank2_mut().cr.modify(|_, w| {
w.ber()
.set_bit()
.start()
.set_bit()
});
while self.registers.bank2_mut().sr.read().qw().bit_is_set() {}
},
_ => unreachable!()
}
// Lock the flash CR again
self.lock();
Ok(())
}
/// Mass erases of the flash memory.
pub fn mass_erase(&mut self) -> Result<(), Error> {
// 1. Check & clear error flags
self.clear_errors();
// 2. Unlock Flash_CR1&2, FLASH_OPTCR registers
self.unlock();
self.unlock_optcr();
// 3. Set MER in FLASH_OPTCR to 1, wait until both QW to clear
self.registers.optcr_mut().modify(|_, w| {
w.mer().set_bit()
});
while self.is_queuing() {}
// Lock the flash CR and OPTCR again
self.lock();
self.lock_optcr();
Ok(())
}
/// Program flash words (32-bytes).
/// Flashing incomplete flash word is "tolerated", but you have been warned..
pub fn program<'a, 'b>(
&'a mut self,
start_offset: usize,
data: &'b [u8],
) -> Result<(), Error> {
if (start_offset % 32 != 0) || (data.len() % 32 != 0) {
log::warn!("Warning: This flash operation might not be supported...");
log::warn!("Consider force writing the data in buffer...");
}
self.clear_errors();
// Invoke single writes per 32-bytes row
let mut current_address = start_offset;
let mut remaining_data = data;
while remaining_data.len() != 0 {
let single_write_size = 32 - (current_address % 32);
// Determine the index that split the coming row and the remaining bytes.
let splitting_index = core::cmp::min(
single_write_size,
remaining_data.len()
);
let single_row_data = &remaining_data[..splitting_index];
// 1. Unlock FLASH_CR1/2 register if necessary
self.unlock();
// 2. Set PG bit in FLASH_CR1/2
self.registers.bank1_mut().cr.modify(|_, w| {
w.pg().set_bit()
});
self.registers.bank2_mut().cr.modify(|_, w| {
w.pg().set_bit()
});
// 3. Check Protection
// There should not be any data protection anyway...
// 4. Write data byte by byte
for (index, byte) in single_row_data.iter().enumerate() {
while self.is_busy() {}
match self.check_errors() {
Ok(_) => {
let address: *mut u8 = unsafe {
FLASH_BASE.add(current_address + index)
};
if address > MAX_FLASH_ADDRESS {
self.registers.bank1_mut().cr.modify(|_, w| w.pg().clear_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.pg().clear_bit());
return Err(Error::WriteError);
} else {
unsafe {
core::ptr::write_volatile(address, *byte);
}
}
},
Err(error) => {
self.registers.bank1_mut().cr.modify(|_, w| w.pg().clear_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.pg().clear_bit());
return Err(error);
}
}
}
// 5. Wait till the command queue of the device to be empty
while self.is_queuing() {}
// Modify remaining data and the address for next 32-bytes row.
remaining_data = &remaining_data[splitting_index..];
current_address += single_row_data.len();
}
// Reset PG1/2
self.registers.bank1_mut().cr.modify(|_, w| w.pg().clear_bit());
self.registers.bank2_mut().cr.modify(|_, w| w.pg().clear_bit());
// Lock FLASH_CR1/2 register
self.lock();
Ok(())
}
/// Force empty the bytes buffer for flash programming
/// Warning: It can invalidate the whole flash due to invalid CRC.
pub fn force_write<'a>(
&'a mut self
) -> Result<(), Error> {
if self.bank1_is_buffering() {
self.registers.bank1_mut().cr.modify(
|_, w| w.fw().set_bit()
);
}
if self.bank2_is_buffering() {
self.registers.bank2_mut().cr.modify(
|_, w| w.fw().set_bit()
);
}
Ok(())
}
/// Read a slice from flash memory
pub fn read(&self, start_offset: usize, len: usize) -> &'static [u8] {
let address = unsafe {
FLASH_BASE.add(start_offset)
};
unsafe { core::slice::from_raw_parts(address, len) }
}
/// Releases the flash peripheral.
pub fn free(self) -> FLASH {
self.registers
}
/// Checks the error flags.
fn check_errors(&self) -> Result<(), Error> {
let (sr1, sr2) = (
self.registers.bank1_mut().sr.read(),
self.registers.bank2_mut().sr.read()
);
if sr1.wrperr().bit_is_set() || sr2.wrperr().bit_is_set() {
Err(Error::WriteProtection)
} else if sr1.pgserr().bit_is_set() || sr2.pgserr().bit_is_set() {
Err(Error::ProgrammingSequence)
} else if sr1.strberr().bit_is_set() || sr2.strberr().bit_is_set() {
Err(Error::Strobe)
} else if sr1.incerr().bit_is_set() || sr2.incerr().bit_is_set() {
Err(Error::Inconsistency)
} else if sr1.operr().bit_is_set() || sr2.operr().bit_is_set() {
Err(Error::Operation)
} else if sr1.sneccerr1().bit_is_set() || sr1.dbeccerr().bit_is_set()
|| sr2.sneccerr1().bit_is_set() || sr2.dbeccerr().bit_is_set() {
Err(Error::ErrorCorrectionCode)
} else if sr1.rdperr().bit_is_set() || sr2.rdperr().bit_is_set() {
Err(Error::ReadProtection)
} else if sr1.rdserr().bit_is_set() || sr2.rdserr().bit_is_set() {
Err(Error::ReadSecure)
} else {
Ok(())
}
}
/// Clears all error flags.
fn clear_errors(&mut self) {
self.registers.bank1_mut().ccr.write(|w| {
w.clr_wrperr()
.set_bit()
.clr_pgserr()
.set_bit()
.clr_strberr()
.set_bit()
.clr_incerr()
.set_bit()
.clr_operr()
.set_bit()
.clr_rdperr()
.set_bit()
.clr_rdserr()
.set_bit()
.clr_sneccerr()
.set_bit()
.clr_dbeccerr()
.set_bit()
});
self.registers.bank2_mut().ccr.write(|w| {
w.clr_wrperr()
.set_bit()
.clr_pgserr()
.set_bit()
.clr_strberr()
.set_bit()
.clr_incerr()
.set_bit()
.clr_operr()
.set_bit()
.clr_rdperr()
.set_bit()
.clr_rdserr()
.set_bit()
.clr_sneccerr()
.set_bit()
.clr_dbeccerr()
.set_bit()
});
}
}

171
src/flash_store.rs Normal file
View File

@ -0,0 +1,171 @@
use crate::flash::Flash;
use crate::flash::Error as FlashError;
use stm32h7xx_hal::pac::FLASH;
use sfkv::{ StoreBackend, Store };
use log::error;
// Use the first 8KiB of last sector of bank 2 for config
// i.e. 0x081E0000 to 0x081E2000
// The sector size might be too large for `compact()` to work with intended benefit.
pub const FLASH_SECTOR_SIZE: usize = 0x2000;
pub const FLASH_BANK: u8 = 2;
pub const FLASH_SECTOR: u8 = 7;
pub const FLASH_SECTOR_OFFSET: usize = 0x1E0000;
pub static mut BACKUP_SPACE: [u8; FLASH_SECTOR_SIZE] = [0x00; FLASH_SECTOR_SIZE];
// Middle space will pretend to be the flash (i.e. the fake flash, or a very large cache)
// STM32H7 flash is an abomination.
// static mut MIDDLE_SPACE: [u8; FLASH_SECTOR_SIZE] = [0xFF; FLASH_SECTOR_SIZE];
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SFKVProcessType {
Length,
Type,
Space,
Data,
}
// Manage access to flash memory
// Motivation: STM32H7 flash only accepts 32-bytes transaction, SFKV mandates byte-by-byte access
// A middle layer is needed to inplement SFKV, that is acceptable to STM32H7 flash
// Idea: Forces BACKUP_SPACE to act as a cache.
pub struct FakeFlashManager {
// FSM: Track the type of data being programmed
process_type: SFKVProcessType
}
impl StoreBackend for FakeFlashManager {
type Data = [u8];
// Return
fn data(&self) -> &Self::Data {
unsafe { &BACKUP_SPACE }
}
type Error = FlashError;
fn erase(&mut self) -> Result<(), Self::Error> {
self.reset_state();
Ok(())
}
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error> {
// Skip programming if payload is 0 length
if payload.len() == 0 {
self.advance_state();
return Ok(());
}
// Examine payload address
// If it is not identical to the programming address of the bankup space,
// Program trailing bytes of 0xFF to prevent misinterpretation of data.
unsafe {
let cache_ptr: *const u8 = &BACKUP_SPACE[offset] as *const u8;
let payload_ptr: *const u8 = &(*payload)[0] as *const u8;
BACKUP_SPACE[offset..(offset+payload.len())].copy_from_slice(payload);
// This part program extra trailing termination bytes (4 0xFF s)
// However, if the remaining space is too small, there is also no need to program these bytes
// Bytes that are too short will not be picked up by the iterator in sfkv.
// If the data is programmed back to the exact same spot,
// then we can assert that no k-v pair were ditched
// Then there is no concern of accidentally interpreting unused flash memory as malformatted.
if (cache_ptr != payload_ptr)
&& (offset+payload.len()+4 <= FLASH_SECTOR_SIZE)
&& self.process_type == SFKVProcessType::Data
{
BACKUP_SPACE[(offset+payload.len())..(offset+payload.len()+4)].copy_from_slice(&[0xFF; 4]);
}
}
self.advance_state();
Ok(())
}
fn backup_space(&self) -> &'static mut [u8] {
unsafe { &mut BACKUP_SPACE }
}
}
impl FakeFlashManager {
pub fn new() -> Self {
Self {
process_type: SFKVProcessType::Length,
}
}
pub fn advance_state(&mut self) {
self.process_type = match self.process_type {
SFKVProcessType::Length => {
SFKVProcessType::Type
},
SFKVProcessType::Type => {
SFKVProcessType::Space
},
SFKVProcessType::Space => {
SFKVProcessType::Data
},
SFKVProcessType::Data => {
SFKVProcessType::Length
}
};
}
pub fn reset_state(&mut self) {
self.process_type = SFKVProcessType::Length;
}
}
pub type FlashStore = Store<FakeFlashManager>;
fn init_flash_cache(flash: FLASH) -> Flash {
let flash = Flash::new(flash);
// Initialize backup space
// Now backup space acts like a cache,
// sfkv will perform in-place operation in cache
// flash will only be updated after invoking `save()`
unsafe {
BACKUP_SPACE.copy_from_slice(
flash.read(FLASH_SECTOR_OFFSET, FLASH_SECTOR_SIZE)
);
}
flash
}
fn init_flash_store() -> FlashStore {
let ffm = FakeFlashManager::new();
let mut store = FlashStore::new(ffm);
// just try to read the store
match store.get_bytes_used() {
Ok(_) => {}
Err(e) => {
error!("corrupt store, erasing. error: {:?}", e);
let _ = store.erase()
.map_err(|e| error!("flash erase failed: {:?}", e));
}
}
store
}
pub fn init_flash(flash: FLASH) -> (Flash, FlashStore) {
let flash = init_flash_cache(flash);
let store = init_flash_store();
(flash, store)
}
pub fn update_flash(flash: &mut Flash, store: &FlashStore) -> Result<(), FlashError> {
flash.erase_sector(FLASH_BANK, FLASH_SECTOR)?;
let flash_size = store.get_bytes_used().unwrap();
let save_size = if (flash_size % 0x20) == 0 {
flash_size
} else {
flash_size + 0x20 - (flash_size % 0x20)
};
unsafe {
flash.program(FLASH_SECTOR_OFFSET, &BACKUP_SPACE[..save_size]).map(|_| ())
}
}

91
src/fpga.rs Normal file
View File

@ -0,0 +1,91 @@
use embedded_hal::{
digital::v2::{OutputPin, InputPin},
blocking::spi::Transfer,
blocking::delay::DelayUs,
};
#[derive(Debug)]
pub enum FPGAFlashError {
SPICommunicationError,
NegotiationError,
ResetStatusError,
}
const DATA: &'static [u8] = include_bytes!("../build/top.bin");
// A public method to flash iCE40 FPGA on Humpback
pub fn flash_ice40_fpga<SPI: Transfer<u8>,
SS: OutputPin,
RST: OutputPin,
DELAY: DelayUs<u32>,
DONE: InputPin>
(mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError>
{
// Data buffer setup
let mut dummy_byte :[u8; 1] = [0x00];
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
// Drive CRESET_B low
creset.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least 200ns
delay.delay_us(1_u32);
// Drive CRESET_B high
creset.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least another 1200us to clear internal config memory
delay.delay_us(1200_u32);
// Before data transmission starts, check if C_DONE is truly low
// If C_DONE is high, the FPGA reset procedure is unsuccessful
match cdone.is_low() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Set SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send 8 dummy clock, effectively 1 byte of 0x00
spi.transfer(&mut dummy_byte)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send the whole image without interruption
for byte in DATA.into_iter() {
let mut single_byte_slice = [*byte];
spi.transfer(&mut single_byte_slice)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
}
// Drive SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send at another 100 dummy clocks (choosing 13 bytes)
spi.transfer(&mut dummy_13_bytes)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Check the CDONE output from FPGA
// CDONE needs to be high
match cdone.is_high() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
spi.transfer(&mut dummy_13_bytes).map_err(|_| FPGAFlashError::SPICommunicationError)?;
Ok(())
}

View File

@ -2,25 +2,30 @@
#![no_std]
#![feature(core_intrinsics)]
#![feature(assoc_char_funcs)]
#![feature(alloc_error_handler)]
use log::{ trace, warn };
use stm32h7xx_hal::gpio::Speed;
use stm32h7xx_hal::rng::Rng;
use stm32h7xx_hal::{pac, prelude::*, spi};
use stm32h7xx_hal::ethernet;
use smoltcp as net;
use minimq::{
embedded_nal::{ IpAddr, Ipv4Addr },
MqttClient, QoS
};
use smoltcp_tls as tls;
use minimq::{ MqttClient, QoS };
use cortex_m;
use cortex_m_rt::entry;
use alloc_cortex_m::CortexMHeap;
use rtic::cyccnt::{Instant, U32Ext};
use rand_core::{RngCore, CryptoRng};
use tls::TlsRng;
use tls::tls::TlsSocket;
use tls::tcp_stack::NetworkStack;
use heapless::{ String, consts, consts::* };
use core::convert::TryInto;
use core::alloc::Layout;
#[macro_use]
pub mod bitmask_macro;
@ -30,17 +35,31 @@ use crate::cpld::CPLD;
pub mod config_register;
pub mod attenuator;
pub mod dds;
pub mod nal_tcp_client;
use crate::nal_tcp_client::{ NetStorage, NetworkStack };
pub mod flash;
use crate::flash::flash_ice40_fpga;
pub mod net_store;
use crate::net_store::NetStorage;
pub mod fpga;
use crate::fpga::flash_ice40_fpga;
pub mod mqtt_mux;
use crate::mqtt_mux::MqttMux;
pub mod urukul;
use crate::urukul::Urukul;
pub mod flash;
pub mod config;
use crate::config::get_net_config;
pub mod flash_store;
use crate::flash_store::init_flash;
mod logger;
#[global_allocator]
static ALLOCATOR: CortexMHeap = CortexMHeap::empty();
#[alloc_error_handler]
fn oom(_: Layout) -> ! {
warn!("Out of memory!");
loop {}
}
static mut NET_STORE: NetStorage = NetStorage {
// Placeholder for the real IP address, which is initialized at runtime.
ip_addrs: [net::wire::IpCidr::Ipv6(
@ -53,25 +72,40 @@ static mut NET_STORE: NetStorage = NetStorage {
#[link_section = ".sram3.eth"]
static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new();
macro_rules! add_socket {
($sockets:ident, $tx_storage:ident, $rx_storage:ident) => {
let mut $rx_storage = [0; 4096];
let mut $tx_storage = [0; 4096];
let tcp_socket = {
let tx_buffer = net::socket::TcpSocketBuffer::new(&mut $tx_storage[..]);
let rx_buffer = net::socket::TcpSocketBuffer::new(&mut $rx_storage[..]);
net::socket::TcpSocket::new(tx_buffer, rx_buffer)
};
let _handle = $sockets.add(tcp_socket);
};
struct RngStruct {
rng: Rng
}
impl RngCore for RngStruct {
fn next_u32(&mut self) -> u32 {
self.rng.gen().unwrap()
}
fn next_u64(&mut self) -> u64 {
(u64::from(self.next_u32()) << 32) | u64::from(self.next_u32())
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.rng.fill(dest).unwrap();
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
Ok(self.fill_bytes(dest))
}
}
impl CryptoRng for RngStruct {}
impl TlsRng for RngStruct {}
#[entry]
fn main() -> ! {
// Initialize the allocator BEFORE you use it
let start = cortex_m_rt::heap_start() as usize;
let size = 32768; // in bytes
unsafe { ALLOCATOR.init(start, size) }
let mut cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();
@ -104,6 +138,17 @@ fn main() -> ! {
cp.DWT.enable_cycle_counter();
// Instantiate random number generator
let mut rng = RngStruct {
rng: dp.RNG.constrain(ccdr.peripheral.RNG, &ccdr.clocks)
};
// Create sfkv store and flash storage manager
let (flash, mut flash_store) = init_flash(dp.FLASH);
// Acquire client/broker IP Address, client MAC address from flash memory
let net_config = get_net_config(&mut flash_store);
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
@ -112,58 +157,6 @@ fn main() -> ! {
let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF);
let gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG);
// Acquire client/broker IP Address, client MAC address from flash memory
let ipv4_addr_cidr = unsafe {
let ipv4_bits = core::ptr::read(0x08100000 as *const u64);
net::wire::IpCidr::new(
net::wire::IpAddress::v4(
((ipv4_bits >> 32) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 24) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 16) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 8) & 0xFF).try_into().unwrap()
),
((ipv4_bits >> 0) & 0xFF).try_into().unwrap()
)
};
let mac_addr = unsafe {
let mac_bits = core::ptr::read(0x08100020 as *const u64);
net::wire::EthernetAddress([
((mac_bits >> 40) & 0xFF).try_into().unwrap(),
((mac_bits >> 32) & 0xFF).try_into().unwrap(),
((mac_bits >> 24) & 0xFF).try_into().unwrap(),
((mac_bits >> 16) & 0xFF).try_into().unwrap(),
((mac_bits >> 8) & 0xFF).try_into().unwrap(),
(mac_bits & 0xFF).try_into().unwrap(),
])
};
let broker_ipv4_addr = unsafe {
let ipv4_bits = core::ptr::read(0x08100040 as *const u64);
Ipv4Addr::new(
((ipv4_bits >> 24) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 16) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 8) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 0) & 0xFF).try_into().unwrap()
)
};
let device_name: String<U32> = unsafe {
let mut name = String::new();
let mut addr = 0x08100060;
loop {
let c = core::ptr::read(addr as *const u8);
if c == 4 {
break;
} else {
name.push(c as char).unwrap();
addr += 1;
}
}
name
};
// Note: ITM doesn't work beyond this, due to a pin conflict between:
// - FPGA_SPI: SCK (af5)
// - ST_LINK SWO (af0)
@ -214,7 +207,7 @@ fn main() -> ! {
dp.ETHERNET_MTL,
dp.ETHERNET_DMA,
&mut DES_RING,
mac_addr.clone(),
net_config.eth_addr.clone(),
)
};
@ -222,7 +215,7 @@ fn main() -> ! {
let store = unsafe { &mut NET_STORE };
store.ip_addrs[0] = ipv4_addr_cidr;
store.ip_addrs[0] = net_config.ip_cidr;
let neighbor_cache = net::iface::NeighborCache::new(&mut store.neighbor_cache[..]);
@ -231,7 +224,7 @@ fn main() -> ! {
routes.add_default_ipv4_route(default_v4_gw).unwrap();
let mut net_interface = net::iface::EthernetInterfaceBuilder::new(eth_dma)
.ethernet_addr(mac_addr)
.ethernet_addr(net_config.eth_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(&mut store.ip_addrs[..])
.routes(routes)
@ -269,12 +262,11 @@ fn main() -> ! {
let switch = CPLD::new(spi, (cs0, cs1, cs2), io_update);
let parts = switch.split();
let mut urukul = Urukul::new(
let urukul = Urukul::new(
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7
);
urukul.reset().unwrap();
let mut mqtt_mux = MqttMux::new(urukul, device_name.as_str());
let mut mqtt_mux = MqttMux::new(urukul, flash, flash_store, net_config.name.as_str());
// Time unit in ms
let mut time: u32 = 0;
@ -284,16 +276,35 @@ fn main() -> ! {
let mut next_ms = Instant::now();
next_ms += 400_000.cycles();
let mut socket_set_entries: [_; 8] = Default::default();
let mut sockets = net::socket::SocketSet::new(&mut socket_set_entries[..]);
add_socket!(sockets, rx_storage, tx_storage);
let mut tls_socket_entries: [_; 1] = Default::default();
let mut tls_socket_set = smoltcp_tls::set::TlsSocketSet::new(
&mut tls_socket_entries[..]
);
let tcp_stack = NetworkStack::new(&mut net_interface, sockets);
let mut tx_storage = [0; 4096];
let mut rx_storage = [0; 4096];
let tx_buffer = net::socket::TcpSocketBuffer::new(&mut tx_storage[..]);
let rx_buffer = net::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
let mut tcp_socket = net::socket::TcpSocket::new(rx_buffer, tx_buffer);
tcp_socket.set_keep_alive(
Some(net::time::Duration::from_secs(2))
);
let tls_socket = TlsSocket::new(
tcp_socket,
&mut rng,
None
);
let _ = tls_socket_set.add(tls_socket);
let tls_stack = NetworkStack::new(
tls_socket_set
);
let mut client = MqttClient::<consts::U256, _>::new(
IpAddr::V4(broker_ipv4_addr),
device_name.as_str(),
tcp_stack,
net_config.broker_ip,
net_config.name.as_str(),
tls_stack,
)
.unwrap();
@ -311,9 +322,8 @@ fn main() -> ! {
// eth Poll if necessary
// Do not poll if eth link is down
if tick && client.network_stack.update_delay(time) == 0 && eth_mac.phy_poll_link() {
client.network_stack.update(time);
}
while !eth_mac.phy_poll_link() {}
client.network_stack.poll(&mut net_interface, net::time::Instant::from_millis(time));
// Process MQTT messages about Urukul/Control
let connection = match client
@ -323,7 +333,7 @@ fn main() -> ! {
}) {
Ok(_) => true,
Err(e) => {
warn!("{:?}", e);
log::info!("Warn: {:?}", e);
false
},
};
@ -339,7 +349,7 @@ fn main() -> ! {
}
if connection && !has_subscribed && tick {
let mut str_builder: String<U128> = String::from(device_name.as_str());
let mut str_builder: String<U128> = String::from(net_config.name.as_str());
str_builder.push_str("/Control/#").unwrap();
match client.subscribe(str_builder.as_str(), &[]) {
Ok(()) => has_subscribed = true,

View File

@ -13,6 +13,9 @@ use core::convert::TryInto;
use crate::urukul::ClockSource as UrukulClockSource;
use crate::urukul::Urukul;
use crate::urukul::Error;
use crate::flash_store::{ FlashStore, update_flash };
use crate::flash::Flash;
use crate::config::{ UrukulConfig, ChannelConfig };
#[derive(Debug, Clone)]
pub enum MqttTopic {
@ -29,6 +32,8 @@ pub enum MqttTopic {
SingletoneAmplitude(u8, u8),
SingletonePhase(u8, u8),
Profile,
Save,
Load,
}
// Prossible change: Make this enum public to all comm protocol (if any)
@ -48,26 +53,131 @@ pub enum MqttCommand {
SingletoneFrequency(u8, u8, f64),
SingletoneAmplitude(u8, u8, f64),
SingletonePhase(u8, u8, f64),
Profile(u8)
Profile(u8),
Save,
Load,
}
pub struct MqttMux<'s, SPI> {
urukul: Urukul<SPI>,
yet_to_respond: Option<MqttCommand>,
flash_controller: Flash,
flash_store: FlashStore,
name: &'s str,
float_buffer: ryu::Buffer,
}
const CHANNELS: [&str; 4] = [ "ch0", "ch1", "ch2", "ch3" ];
static mut SERDE_BUFFER: [u8; 64] = [0; 64];
impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
pub fn new(urukul: Urukul<SPI>, name: &'s str) -> Self {
MqttMux {
pub fn new(urukul: Urukul<SPI>, flash_controller: Flash, flash_store: FlashStore, name: &'s str) -> Self {
Self {
urukul: urukul,
yet_to_respond: None,
flash_controller,
flash_store,
name: name,
float_buffer: ryu::Buffer::new(),
}
}
fn load_device(&mut self) -> Result<(), Error<E>> {
self.urukul.reset()?;
let profile = match self.flash_store
.read_value::<UrukulConfig>("urukul")
.unwrap()
{
Some(urukul_config) => {
self.urukul.set_clock(
urukul_config.clk_src,
urukul_config.clk_freq,
urukul_config.clk_div
)?;
self.urukul.set_profile(urukul_config.profile)?;
urukul_config.profile
},
None => 0,
};
for (channel, channel_tag) in CHANNELS.iter().enumerate() {
match self.flash_store
.read_value::<ChannelConfig>(channel_tag)
.unwrap()
{
Some(channel_config) => {
self.urukul.set_channel_switch(
channel as u32,
channel_config.sw
)?;
self.urukul.set_channel_attenuation(
channel as u8,
channel_config.att
)?;
self.urukul.set_channel_sys_clk(
channel as u8,
channel_config.sys_clk
)?;
self.urukul.set_channel_single_tone_profile(
channel as u8,
profile,
channel_config.freq,
channel_config.phase,
channel_config.asf,
)?;
},
None => ()
};
}
Ok(())
}
fn save_device(&mut self) -> Result<(), Error<E>> {
let urukul_config = UrukulConfig {
clk_src: {
self.urukul.get_clock_source()?
},
clk_freq: {
self.urukul.get_clock_frequency()
},
clk_div: {
self.urukul.get_clock_division()?
},
profile: {
self.urukul.get_profile()?
}
};
unsafe { self.flash_store.write_value("urukul", &urukul_config, &mut SERDE_BUFFER).unwrap(); }
for channel in 0..4 {
let (freq, phase, asf) = self.urukul.get_channel_single_tone_profile(
channel as u8,
urukul_config.profile
)?;
let channel_config = ChannelConfig {
sw: {
self.urukul.get_channel_switch_status(channel as u32)?
},
att: {
self.urukul.get_channel_attenuation(channel as u8)?
},
sys_clk: {
self.urukul.get_channel_sys_clk(channel as u8)?
},
freq,
phase,
asf
};
unsafe {
self.flash_store.write_value(CHANNELS[channel], &channel_config, &mut SERDE_BUFFER).unwrap();
}
}
update_flash(&mut self.flash_controller, &mut self.flash_store).unwrap();
Ok(())
}
// Instead of using a return type, the result of processing the command is stored internally
// Invoke process_mqtt_egress to get a response after invoking ingress handler
pub fn process_mqtt_ingress(&mut self, topic: &str, message: &[u8]) {
@ -273,6 +383,32 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
)).map_err(|_| Error::VectorOutOfSpace)?;
Ok(vec)
}
MqttCommand::Save => {
vec.push((
{
let mut topic_string = String::from(self.name);
topic_string.push_str("/Feedback/Save")
.map_err(|_| Error::StringOutOfSpace)?;
topic_string
},
String::from("Saved device.")
)).map_err(|_| Error::VectorOutOfSpace)?;
Ok(vec)
}
MqttCommand::Load => {
vec.push((
{
let mut topic_string = String::from(self.name);
topic_string.push_str("/Feedback/Load")
.map_err(|_| Error::StringOutOfSpace)?;
topic_string
},
String::from("Loaded from flash.")
)).map_err(|_| Error::VectorOutOfSpace)?;
Ok(vec)
}
},
None => Ok(vec),
}
@ -455,6 +591,20 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
return Ok(MqttTopic::Profile);
}
"Save" => {
if assigned_channel || assigned_profile {
return Err(Error::MqttCommandError)
}
return Ok(MqttTopic::Save);
}
"Load" => {
if assigned_channel || assigned_profile {
return Err(Error::MqttCommandError)
}
return Ok(MqttTopic::Load);
}
_ => return Err(Error::MqttCommandError),
};
}
@ -475,6 +625,8 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
MqttTopic::SingletoneAmplitude(ch, prof) => singletone_amplitude_message(ch, prof, message),
MqttTopic::SingletonePhase(ch, prof) => singletone_phase_message(ch, prof, message),
MqttTopic::Profile => profile_message(message),
MqttTopic::Save => Ok((message, MqttCommand::Save)),
MqttTopic::Load => Ok((message, MqttCommand::Load)),
}
}
@ -494,6 +646,8 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
MqttCommand::SingletoneAmplitude(ch, prof, ampl) => self.urukul.set_channel_single_tone_profile_amplitude(ch, prof, ampl),
MqttCommand::SingletonePhase(ch, prof, deg) => self.urukul.set_channel_single_tone_profile_phase(ch, prof, deg),
MqttCommand::Profile(prof) => self.urukul.set_profile(prof),
MqttCommand::Save => self.save_device(),
MqttCommand::Load => self.load_device(),
}
}

View File

@ -1,194 +0,0 @@
use core::cell::RefCell;
use nb;
use heapless::{consts, Vec};
use stm32h7xx_hal::ethernet;
use smoltcp as net;
use minimq::embedded_nal;
#[derive(Debug)]
pub enum NetworkError {
NoSocket,
ConnectionFailure,
ReadFailure,
WriteFailure,
}
pub struct NetStorage {
pub ip_addrs: [net::wire::IpCidr; 1],
pub neighbor_cache: [Option<(net::wire::IpAddress, net::iface::Neighbor)>; 8],
pub routes_cache: [Option<(net::wire::IpCidr, net::iface::Route)>; 8],
}
pub type NetworkInterface =
net::iface::EthernetInterface<'static, 'static, 'static, ethernet::EthernetDMA<'static>>;
// TODO: The network stack likely needs a time-tracking mechanic here to support
// blocking/nonblocking/timeout based operations.
pub struct NetworkStack<'a, 'b, 'c, 'n> {
network_interface: &'n mut NetworkInterface,
sockets: RefCell<net::socket::SocketSet<'a, 'b, 'c>>,
next_port: RefCell<u16>,
unused_handles: RefCell<Vec<net::socket::SocketHandle, consts::U16>>,
}
impl<'a, 'b, 'c, 'n> NetworkStack<'a, 'b, 'c, 'n> {
pub fn new(
interface: &'n mut NetworkInterface,
sockets: net::socket::SocketSet<'a, 'b, 'c>,
) -> Self {
let mut unused_handles: Vec<net::socket::SocketHandle, consts::U16> = Vec::new();
for socket in sockets.iter() {
unused_handles.push(socket.handle()).unwrap();
}
NetworkStack {
network_interface: interface,
sockets: RefCell::new(sockets),
next_port: RefCell::new(49152),
unused_handles: RefCell::new(unused_handles),
}
}
pub fn update_delay(&mut self, time: u32) -> u32 {
self.network_interface.poll_delay(
&mut self.sockets.borrow_mut(),
net::time::Instant::from_millis(time as i64),
).map_or(0, |next_poll_time| next_poll_time.total_millis() as u32)
}
pub fn update(&mut self, time: u32) -> bool {
self.network_interface.poll(
&mut self.sockets.borrow_mut(),
net::time::Instant::from_millis(time as i64),
).map_or(true, |changed| changed == false)
}
fn get_ephemeral_port(&self) -> u16 {
// Get the next ephemeral port
let current_port = self.next_port.borrow().clone();
let (next, wrap) = self.next_port.borrow().overflowing_add(1);
*self.next_port.borrow_mut() = if wrap { 49152 } else { next };
return current_port;
}
}
impl<'a, 'b, 'c, 'n> embedded_nal::TcpStack for NetworkStack<'a, 'b, 'c, 'n> {
type TcpSocket = net::socket::SocketHandle;
type Error = NetworkError;
fn open(&self, _mode: embedded_nal::Mode) -> Result<Self::TcpSocket, Self::Error> {
// TODO: Handle mode?
match self.unused_handles.borrow_mut().pop() {
Some(handle) => {
// Abort any active connections on the handle.
let mut sockets = self.sockets.borrow_mut();
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
internal_socket.abort();
Ok(handle)
}
None => Err(NetworkError::NoSocket),
}
}
fn connect(
&self,
socket: Self::TcpSocket,
remote: embedded_nal::SocketAddr,
) -> Result<Self::TcpSocket, Self::Error> {
// TODO: Handle socket mode?
let mut sockets = self.sockets.borrow_mut();
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket);
// If we're already in the process of connecting, ignore the request silently.
if internal_socket.is_open() {
return Ok(socket);
}
match remote.ip() {
embedded_nal::IpAddr::V4(addr) => {
let address = {
let octets = addr.octets();
net::wire::Ipv4Address::new(octets[0], octets[1], octets[2], octets[3])
};
internal_socket
.connect((address, remote.port()), self.get_ephemeral_port())
.map_err(|_| NetworkError::ConnectionFailure)?;
internal_socket
.set_keep_alive(Some(net::time::Duration::from_millis(1000)));
}
embedded_nal::IpAddr::V6(addr) => {
let address = {
let octets = addr.segments();
net::wire::Ipv6Address::new(
octets[0], octets[1], octets[2], octets[3], octets[4], octets[5],
octets[6], octets[7],
)
};
internal_socket
.connect((address, remote.port()), self.get_ephemeral_port())
.map_err(|_| NetworkError::ConnectionFailure)?;
internal_socket
.set_keep_alive(Some(net::time::Duration::from_millis(1000)));
}
};
Ok(socket)
}
fn is_connected(&self, socket: &Self::TcpSocket) -> Result<bool, Self::Error> {
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket);
Ok(socket.may_send() && socket.may_recv())
}
fn write(&self, socket: &mut Self::TcpSocket, buffer: &[u8]) -> nb::Result<usize, Self::Error> {
// TODO: Handle the socket mode.
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket);
let result = socket.send_slice(buffer);
match result {
Ok(num_bytes) => Ok(num_bytes),
Err(_) => Err(nb::Error::Other(NetworkError::WriteFailure)),
}
}
fn read(
&self,
socket: &mut Self::TcpSocket,
buffer: &mut [u8],
) -> nb::Result<usize, Self::Error> {
// TODO: Handle the socket mode.
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket);
let result = socket.recv_slice(buffer);
match result {
Ok(num_bytes) => Ok(num_bytes),
Err(_) => Err(nb::Error::Other(NetworkError::ReadFailure)),
}
}
fn close(&self, socket: Self::TcpSocket) -> Result<(), Self::Error> {
// TODO: Free the ephemeral port in use by the socket.
let mut sockets = self.sockets.borrow_mut();
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket);
internal_socket.close();
self.unused_handles.borrow_mut().push(socket).unwrap();
Ok(())
}
}

7
src/net_store.rs Normal file
View File

@ -0,0 +1,7 @@
use smoltcp as net;
pub struct NetStorage {
pub ip_addrs: [net::wire::IpCidr; 1],
pub neighbor_cache: [Option<(net::wire::IpAddress, net::iface::Neighbor)>; 8],
pub routes_cache: [Option<(net::wire::IpCidr, net::iface::Route)>; 8],
}

View File

@ -3,6 +3,8 @@ use embedded_hal::{
blocking::spi::Transfer,
};
use serde::{ Serialize, Deserialize };
use crate::config_register::ConfigRegister;
use crate::config_register::CFGMask;
use crate::config_register::StatusMask;
@ -28,9 +30,19 @@ pub enum Error<E> {
MqttCommandError,
VectorOutOfSpace,
StringOutOfSpace,
WaitRetry, // Prompt driver to just wait and retry
}
#[derive(Debug, Clone)]
impl<E> Error<E> {
pub fn is_wait_retry(&self) -> bool {
match self {
Error::WaitRetry => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClockSource {
OSC,
SMA,
@ -328,16 +340,39 @@ where
}
pub fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Error<E>> {
self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ())
loop {
if let Err(e) = self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ()) {
if e.is_wait_retry() {
cortex_m::asm::delay(400_000);
} else {
return Err(e);
}
} else {
break;
}
}
Ok(())
}
pub fn get_channel_sys_clk(&mut self, channel: u8) -> Result<f64, Error<E>> {
Ok(self.dds[usize::from(channel)].get_f_sys_clk())
}
pub fn get_channel_calculated_sys_clk(&mut self, channel: u8) -> Result<f64, Error<E>> {
self.dds[usize::from(channel)].get_sys_clk_frequency()
}
pub fn borrow_dds(&mut self, channel: u8) -> &mut DDS<SPI> {
&mut self.dds[usize::from(channel)]
}
pub fn borrow_config_register(&mut self) -> &mut ConfigRegister<SPI> {
&mut self.config_register
}
// Multi-dds channel functions
// Do not allow reading of DDS registers
// Make sure only 1 SPI transaction is compelted per function call
// Make sure only 1 SPI transaction is completed per function call
// Setup NU_MASK in configuration register
// This selects the DDS channels that will be covered by multi_channel DDS (spi3)