Compare commits
16 Commits
82a18430dd
...
fc1fedf890
Author | SHA1 | Date |
---|---|---|
occheung | fc1fedf890 | |
occheung | 3b4a81eceb | |
occheung | 04c0fa4074 | |
occheung | 8ce3200b5d | |
occheung | f1069d951d | |
occheung | adc5807ff1 | |
occheung | d5f9480645 | |
occheung | 2e86838a22 | |
occheung | 19ea898c31 | |
occheung | 68d58857cf | |
occheung | d6fdc36c78 | |
occheung | eafd26bbc5 | |
occheung | 3252dfcd42 | |
occheung | 52986e214f | |
occheung | 7e09318919 | |
occheung | 5fd7f36c65 |
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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`.
|
||||
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
"1kc2i3c7lmbx6fxlhw1wd3v679zh4mldyiyvaa24jarwlxnp5wq8"
|
||||
"1wzbn4r6gya72lgp8464izi0fhvp9pmmi1qck7qcw4ww8p8lzq7p"
|
||||
|
|
|
@ -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()
|
2
memory.x
2
memory.x
|
@ -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 */
|
||||
|
|
|
@ -13,7 +13,7 @@ rustPlatform.buildRustPackage rec {
|
|||
|
||||
cargoPatches = [ ./itm-cargo-lock.patch ];
|
||||
|
||||
cargoSha256 = "1lgv8nhzbzfw9cl4rhj46a86h9jygz0ih3j4zw5nd1346xgmz7b8";
|
||||
cargoSha256 = "1x2pagfxwhgxliygw7325qsg3ccn276f4slg9cql0sf1s304qj4g";
|
||||
|
||||
nativeBuildInputs = [ pkgconfig ];
|
||||
|
||||
|
|
15
shell.nix
15
shell.nix
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
33
src/dds.rs
33
src/dds.rs
|
@ -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
|
||||
|
|
479
src/flash.rs
479
src/flash.rs
|
@ -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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(|_| ())
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
|
||||
}
|
200
src/main.rs
200
src/main.rs
|
@ -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,
|
||||
|
|
160
src/mqtt_mux.rs
160
src/mqtt_mux.rs
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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],
|
||||
}
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue