Compare commits

...

163 Commits

Author SHA1 Message Date
muellerr 18b6ee2c41 Embassy Multi-Priority Example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-18 13:20:05 +02:00
muellerr dfe68131c2 Merge pull request 'continue SPI slave' (#85) from spi-slave into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #85
2026-05-18 11:48:55 +02:00
muellerr af23c47fc7 continue SPI slave
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-18 11:38:41 +02:00
muellerr 32e0e27ca7 Merge pull request 'prepare next zynq7000 version' (#84) from bump-zynq7000 into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #84
2026-05-15 19:30:08 +02:00
muellerr bdc4780bcc small tweak
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-15 19:29:58 +02:00
muellerr 7df10e6ea1 prepare next zynq7000 version
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-15 19:28:25 +02:00
muellerr 306ef90094 Merge pull request 'UART improvements' (#83) from update-uart-interrupt-reception into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #83
2026-05-15 14:37:36 +02:00
muellerr fcd971a7d3 UART improvements
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-15 14:37:21 +02:00
muellerr c09d75b602 Merge pull request 'zynq7000-rt and zynq7000-mmu release' (#82) from zynq7000-rt-zynq7000-mmu-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #82
2026-05-08 17:18:21 +02:00
muellerr c72ba780d4 zynq7000-rt and zynq7000-mmu release
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-08 17:16:10 +02:00
muellerr 5472293907 Merge pull request 'prepare PAC release' (#81) from prep-pac-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #81
2026-05-08 17:09:09 +02:00
muellerr 80ad791061 prepare PAC release
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 17:07:08 +02:00
muellerr f78f159fde Merge pull request 'add clock enable for SPI and UART' (#80) from add-clk-enables into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #80
2026-05-08 17:00:50 +02:00
muellerr 1d66fcd077 add clock enable for SPI and UART
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 17:00:15 +02:00
muellerr 7bf6322fc2 Merge pull request 'improve register names' (#79) from improve-register-names into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #79
2026-05-08 16:48:13 +02:00
muellerr 90e4604187 improve register names
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 16:47:05 +02:00
muellerr 34727bf48e Merge pull request 'introduce interrupt registry' (#78) from interrupt-registry into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #78
2026-05-08 16:30:58 +02:00
muellerr 3bd62fc1eb introduce interrupt registry
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 16:29:59 +02:00
muellerr 11b37e9c2b Merge pull request 'Async OLED example' (#77) from oled-asynch into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #77
2026-05-08 14:43:49 +02:00
muellerr 9ed6ac32ce Async OLED example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 14:42:50 +02:00
muellerr 7eeedec527 Merge pull request 'bump aarch32 deps' (#76) from bump-aarch32-dependencies into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #76
2026-05-08 12:55:37 +02:00
muellerr d30273aa0c bump aarch32 deps
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 12:53:31 +02:00
muellerr 79c6aea160 Merge pull request 'fix ping reply for net example' (#75) from fix-ping-for-net-example into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #75
2026-05-08 12:50:43 +02:00
muellerr b85e2cf1a3 fix ping reply for net example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 12:50:21 +02:00
muellerr 78e2dd23f9 Merge pull request 'OLED example' (#74) from oled-driver into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #74
2026-05-07 20:04:59 +02:00
muellerr df887d5665 Add OLED example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-07 20:01:58 +02:00
muellerr 62eebc6770 Merge pull request 'update async SPI module' (#70) from update-asynch-spi-module into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #70
2026-05-07 18:02:27 +02:00
muellerr 5a44b3f658 update async SPI module
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-05 18:41:21 +02:00
muellerr c85f492c03 Merge pull request 'bugfix for SPI AMBA clock control' (#73) from spi-amba-clk-ctrl-bugfix into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/73
2026-05-05 18:41:00 +02:00
muellerr 20102a2b7a bugfix for SPI AMBA clock control
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-05 18:40:42 +02:00
muellerr 110adc8a63 Merge pull request 'configure pull up properly' (#72) from oled-sdin-pullup into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/72
2026-05-05 15:46:31 +02:00
muellerr 6fe5ac5edb configure pull up properly
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-05 15:05:38 +02:00
muellerr 923f988a32 Merge pull request 'fix asynch uart TX' (#71) from fix-asynch-uart into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/71
2026-05-04 19:32:26 +02:00
muellerr a6c40c5fa9 fix asynch uart TX
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 19:31:50 +02:00
muellerr 21a930dc09 Merge pull request 'update asynch logger, simplify it' (#68) from update-asynch-logger into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/68
2026-05-04 18:01:29 +02:00
muellerr 8215eec0cf clippy fix
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 17:54:44 +02:00
muellerr bf6f492c49 update asynch logger, simplify it
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 17:02:22 +02:00
muellerr f7074bcf7d Merge pull request 'update BD and XDC' (#69) from update-hw-design into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/69
2026-05-04 16:57:46 +02:00
muellerr 349509c90c some minor clean up
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 16:56:31 +02:00
muellerr c38c98dfb0 update BD and XDC
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 16:39:33 +02:00
muellerr 69537126ec formatting
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-04-15 18:14:20 +02:00
muellerr 004e862715 Merge pull request 'Function to set FIFO Trigger of PS UART' (#67) from meier/ps-uart-set-fifo-trigger into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #67
2026-04-15 10:02:46 +02:00
meierj f1312c1b17 added pub function to set the rx fifo trigger of the ps uart
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-04-14 17:01:39 +02:00
muellerr 602448b456 Merge pull request 'use local path in README links' (#66) from another-small-readme-tweak into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #66
2026-04-01 14:25:30 +02:00
muellerr 21cafe4668 use local path in README links
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-04-01 14:23:36 +02:00
muellerr 00f64ad2be Merge pull request 'try this little readme tweak' (#65) from small-readme-tweak into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #65
2026-04-01 14:17:37 +02:00
muellerr 41af66433a try this little readme tweak
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-04-01 14:16:17 +02:00
muellerr 7dc471d687 Merge pull request 'prepare PAC v0.2.0 release' (#64) from prepare-pac-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #64
2026-04-01 14:14:55 +02:00
muellerr 41078f9150 prepare PAC v0.2.0 release
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-04-01 14:13:58 +02:00
muellerr e9a56f73af Merge pull request 'add defmt support for PAC' (#63) from add-defmt-support-pac into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #63
2026-04-01 14:01:41 +02:00
muellerr 7f91e039cb add defmt support for PAC
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-04-01 13:58:48 +02:00
muellerr 9e185fe353 Merge pull request 'bugfix for DDR init' (#62) from bugfix-ddr-init into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #62
2026-04-01 11:01:18 +02:00
muellerr b17470a12b bugfix for DDR init
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-04-01 10:59:58 +02:00
muellerr 2a6012cbc3 Merge pull request 'improve FSBL' (#61) from improve-fsbl into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #61
2026-03-31 20:12:51 +02:00
muellerr b786a67d38 improve FSBL
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-31 20:10:52 +02:00
muellerr b9b0d7b1bc Merge pull request 'Add PL reset de-assert in FSBL' (#60) from add-pl-deassert-in-fsbl into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #60
2026-03-31 18:29:05 +02:00
muellerr 56692b7dae Add PL reset de-assert in FSBL
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-31 18:28:35 +02:00
muellerr 06f08b2378 Merge pull request 'smaller tweaks' (#59) from smaller-tweaks into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #59
2026-03-31 18:21:03 +02:00
muellerr 0722681674 smaller tweaks
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-31 18:20:39 +02:00
muellerr caf9c49422 Merge pull request 'Add SDIO support' (#21) from add-sdio-support into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #21
2026-03-27 12:58:27 +01:00
Robin Mueller 3890795aa9 initial SD card support
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-27 12:56:00 +01:00
muellerr b9a64b76a4 Merge pull request 'readme tweak' (#58) from readme-tweak into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #58
2026-03-25 10:39:03 +01:00
muellerr c51e0c782a readme tweak
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-25 10:38:48 +01:00
muellerr 70fa1db75f Merge pull request 'bump dependencies and improve documentation' (#57) from bump-deps-improve-docs into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #57
2026-03-25 10:37:36 +01:00
muellerr a196d6c139 bump dependencies and improve documentation
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-25 10:36:06 +01:00
muellerr 530fc7a8dc Merge pull request 'firmware/zynq7000-hal/Cargo.toml aktualisiert: Replace dependency' (#56) from nehlichp/replace-paste into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #56
2026-03-25 10:26:23 +01:00
muellerr d2372ad41c actually use pastey as well
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-25 10:24:19 +01:00
nehlichp 9cdec1ffba firmware/zynq7000-hal/Cargo.toml aktualisiert
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
https://rustsec.org/advisories/RUSTSEC-2024-0436
2026-03-23 14:57:25 +01:00
muellerr 5e4e687403 Merge pull request 'try-to-fix-embassy-docs' (#55) from try-to-fix-embassy-docs into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #55
2026-03-13 17:52:16 +01:00
muellerr d07ffb3ae1 try to fix embassy docs
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-13 17:50:35 +01:00
muellerr 332779326a Merge pull request 'some CI fixes' (#54) from ci-fixes into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #54
2026-03-13 17:47:32 +01:00
muellerr 558f38682d some CI fixes
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-13 17:45:57 +01:00
muellerr 3263b9964d Merge pull request 'some CI fixes' (#53) from ci-fixes into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #53
2026-03-13 17:35:11 +01:00
muellerr 0d11210173 some CI fixes
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-13 17:34:46 +01:00
muellerr 9c187bd657 Merge pull request 'another tweak for the QSPI spansion driver' (#52) from tweak-for-qspi-spansion-driver into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #52
2026-03-10 19:46:25 +01:00
muellerr a2d1efde46 another tweak for the QSPI spansion driver
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-10 15:00:20 +01:00
muellerr 16ecc6e71c Merge pull request 'QSPI Spansion Remove Page Alignement Constraint' (#51) from meier/qspi-remove-page-alignment-constraint into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #51
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-03-10 13:28:43 +01:00
meierj ecabeb1a4b adapted qspi example and qpsi-flasher to api changes in qspi_spansion
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-10 11:25:55 +01:00
meierj 7494036099 changed read_page to read
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-06 16:04:07 +01:00
meierj 21427bf019 removed page alignment constraint for qspi spansion programming
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-06 15:35:45 +01:00
muellerr fc1fdada3e changelog
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-03-06 13:43:50 +01:00
muellerr 3f01b61770 Merge pull request 'Fixed Bit Positions of QSPI Reset Register' (#50) from meier/qspi-reset-bugfix into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #50
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-03-06 13:34:47 +01:00
meierj dd97bc3177 corrected bit positions of qspi reset register
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-03-06 13:15:57 +01:00
muellerr efd55042b2 Merge pull request 'fmt' (#49) from minor-qspi-improvements-and-tweaks into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #49
2026-02-28 12:49:36 +01:00
muellerr 8bafdc20e7 fmt
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-28 12:49:18 +01:00
muellerr 401643f8ec Merge pull request 'minor-qspi-improvements-and-tweaks' (#48) from minor-qspi-improvements-and-tweaks into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #48
2026-02-28 12:47:32 +01:00
muellerr 2b11516bf5 more double reads
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-28 12:46:19 +01:00
muellerr 703af93bb9 more improvements
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-28 12:37:06 +01:00
muellerr 1fe16466ab Merge pull request 'use div-ceil for QSPI clock calc' (#45) from small-qspi-clock-tweak into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #45
2026-02-26 17:20:41 +01:00
muellerr 47a17e7550 use div-ceil for QSPI clock calc
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-26 17:20:32 +01:00
muellerr af025c5490 Merge pull request 'chunked read' (#47) from chunked-read into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #47
2026-02-26 17:19:40 +01:00
muellerr c15e3828b7 Spansion QSPI module improvements
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-26 17:19:08 +01:00
muellerr 9ef548e1bc Merge pull request 'Add slow read for Spansion QSPI' (#46) from add-slow-read-spansion-qspi into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #46
2026-02-25 17:31:59 +01:00
muellerr 3eb8467bf5 add register double read
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-25 15:57:21 +01:00
muellerr 4112e336ef add slow read
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-25 15:51:37 +01:00
muellerr a58d398d82 Merge pull request 'prepare zedboard BSP release' (#44) from prepare-zedboard-bsp-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #44
2026-02-14 19:37:31 +01:00
muellerr fd178e1d3b prepare zedboard BSP release
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-14 19:30:13 +01:00
muellerr e8a6c88e2b Merge pull request 'prepare zynq7000-embassy release' (#43) from prepare-zynq7000-embassy-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #43
2026-02-14 19:23:53 +01:00
muellerr 8606bcbd63 prepare zynq7000-embassy release
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-14 19:23:38 +01:00
muellerr 2b6e27875f re-generate lock file
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-14 19:15:43 +01:00
muellerr fa6f7d836e Merge pull request 'prepare zynq-mmu v0.1.2' (#42) from zynq-mmu-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #42
2026-02-14 19:10:59 +01:00
muellerr b7093706f5 prepare zynq-mmu v0.1.2
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-14 19:10:32 +01:00
Robin Mueller 56131100e7 Merge pull request #2 from us-irs/prep-rt-patch-release
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
zynq7000-rt v0.2.0
2026-02-14 18:56:23 +01:00
Robin Mueller 9357464db0 zynq7000-rt v0.2.0
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-02-14 18:54:23 +01:00
muellerr 79161ffe58 Merge pull request 'try allowing hw server ip' (#41) from try-allowing-setting-hw-server-ip into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #41
2026-02-14 17:49:09 +01:00
muellerr 7c3051db46 try allowing hw server ip
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-14 17:48:56 +01:00
muellerr 89ee6d7451 Merge pull request 'improve SPI implemenetation' (#40) from improve-spi-implementation into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #40
2026-02-14 17:45:36 +01:00
muellerr 3b23e9be05 Merge pull request 'division by zero check to avoid panic' (#39) from mohr/div_zero into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #39
Reviewed-by: Robin Müller <muellerr@irs.uni-stuttgart.de>
2026-02-14 17:45:20 +01:00
muellerr d34143ceef improve SPI implemenetation
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-12 16:56:13 +01:00
mohr 1abbe7b9c9 division by zero check to avoid panic
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-11 12:06:21 +01:00
muellerr 802ba665c7 Merge pull request 'bugfix for PL programming routine' (#38) from pl-programming-bugfix into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #38
2026-02-07 01:36:05 +01:00
muellerr 3ba6c63554 bugfix for PL programming routine
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-07 01:35:27 +01:00
Robin Mueller e3a05cc650 Merge pull request #1 from adamgreig/fixes
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Fix typos in zynq7000-hal
2026-02-04 14:04:41 +01:00
Adam Greig 0583c9231d Fix typos in zynq7000-hal 2026-02-03 22:39:23 +00:00
muellerr 4ec2f2bae3 Merge pull request 'bumped aarch32 dependencies' (#37) from bump-aarch32-crates into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #37
2026-02-02 15:40:03 +01:00
muellerr 0d6713f248 bumped aarch32 dependencies
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-02-02 15:39:18 +01:00
muellerr 7affec2cd5 Merge pull request 'defmt tests' (#35) from defmt-tests into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #35
2026-01-20 22:06:01 +01:00
muellerr 7d4f5769a2 defmt tests
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-01-20 22:05:46 +01:00
muellerr c160303729 Merge pull request 'improve repository structure' (#36) from improve-repo-structure into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #36
2026-01-20 14:07:43 +01:00
muellerr 1082fcc921 improve repository structure
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-01-20 14:06:41 +01:00
muellerr 8582c33eed Merge pull request 'update README' (#34) from update-readme into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #34
2026-01-16 15:27:05 +01:00
muellerr 1c51d1e9ab update README
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-01-16 15:26:08 +01:00
muellerr dcec28922e Merge pull request 'use stable in CI' (#33) from ci-use-stable into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #33
2026-01-16 15:23:46 +01:00
muellerr 084a2ff9a9 use stable in CI
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-01-16 15:21:51 +01:00
muellerr 1d5fbe6d0e Merge pull request 'start using stable toolchain' (#32) from use-stable-toolchain into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #32
2026-01-12 11:57:05 +01:00
muellerr 2aefb73a36 start using stable toolchain
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-01-09 11:50:35 +01:00
muellerr e43cab0312 Merge pull request 'improve GIC modules' (#31) from improve-gic-modules into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #31
2025-12-06 14:38:59 +01:00
muellerr b5f5ccb52c improve GIC modules
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-12-06 14:37:35 +01:00
muellerr 25c326c3f1 Merge pull request 'move to default aarch32-rt kmain method instead of boot_core' (#30) from zynq7000-rt-use-kmain into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #30
2025-12-02 23:33:03 +01:00
muellerr 116cb496d9 move to default aarch32-rt kmain method instead of boot_core
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-12-02 23:30:17 +01:00
muellerr 777634b914 Merge pull request 'update to vivado 2025.2' (#28) from update-vivado-2025.2 into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #28
2025-11-28 17:39:17 +01:00
Robin Mueller cab37a87a3 update to vivado 2025.2
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-11-28 17:38:55 +01:00
muellerr 62cc54fd9b Merge pull request 'docs and changelog' (#27) from docs-and-changelog into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #27
2025-11-28 16:00:58 +01:00
Robin Mueller d445454a54 docs and changelog
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-11-28 15:58:50 +01:00
muellerr 0235e1d769 Merge pull request 'zynq7000-rt: small bugs in startup code' (#26) from mohr/fixes into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #26
2025-11-28 14:52:03 +01:00
mohr df0fc1acc3 Changelog
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-11-28 14:49:58 +01:00
mohr 52e17c739d zynq7000-rt: small bugs in startup code
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-11-28 13:30:06 +01:00
muellerr 1553737c77 Merge pull request 'UART and docs update' (#25) from uart-and-docs-update into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #25
2025-11-28 13:09:17 +01:00
Robin Mueller 893d2e870e UART and docs update
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-11-28 13:06:39 +01:00
muellerr 13dd737666 Merge pull request 'docs and clippy' (#24) from docs-and-clippy into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #24
2025-11-27 20:29:55 +01:00
Robin Mueller 1add03abd5 docs and clippy
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-11-27 12:47:08 +01:00
muellerr 06702e2c92 Merge pull request 'README updated after switch to aarch32 dependency' (#23) from mohr/readme into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #23
2025-11-24 22:12:03 +01:00
mohr fcb171c093 README updated after switch to aarch32 dependency
ci / Check build (push) Failing after 8s
ci / Check formatting (push) Failing after 8s
ci / Check Documentation Build (push) Failing after 7s
ci / Clippy (push) Failing after 9s
ci / Check build (pull_request) Failing after 9s
ci / Check formatting (pull_request) Failing after 7s
ci / Check Documentation Build (pull_request) Failing after 7s
ci / Clippy (pull_request) Failing after 7s
2025-11-24 13:36:43 +01:00
muellerr 0aa86cd085 Merge pull request 'replaced cortex-ar and cortex-a-rt by aarch32-rt and aarch32-cpu' (#22) from updated-rt-cpu-crate-name into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #22
2025-11-02 19:37:58 +01:00
Robin Mueller a8339153c4 replaced cortex-ar and cortex-a-rt by aarch32-rt and aarch32-cpu
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-11-02 19:35:11 +01:00
muellerr b394da69f3 Merge pull request 'imrpoved ethernet mio typing check' (#20) from improve-eth-typing-checks into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #20
2025-10-27 23:15:43 +01:00
Robin Mueller 064f99ddea improved ethernet mio typing check
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-27 23:13:43 +01:00
muellerr 2e1efd154d Merge pull request 'rename register blocks' (#19) from rename-registers into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #19
2025-10-27 23:12:43 +01:00
Robin Mueller c53b6ae2bb rename register blocks
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-27 11:26:25 +01:00
muellerr 709f9555ef Merge pull request 'improve HAL docs' (#18) from improve-hal-docs into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #18
2025-10-09 11:38:01 +02:00
Robin Mueller 16af746e35 improve HAL docs
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-10-09 11:36:44 +02:00
Robin Mueller bdadadd928 build alloc for HAL docs
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 11:13:27 +02:00
Robin Mueller c7f8aa8657 missing changelog entries
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 11:09:24 +02:00
Robin Mueller 9596cf4dae prep patch releases
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 11:07:46 +02:00
muellerr 0fa69f9eb9 Merge pull request 'only compile arch specific code for cortex-a' (#17) from target-cfg-cortex-a-low-level-crates into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #17
2025-10-09 11:01:47 +02:00
Robin Mueller 687be0e515 only compile arch specific code for cortex-a
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-10-09 10:58:42 +02:00
muellerr 4e2da0a790 Merge pull request 'smaller tweaks and docs API unification' (#16) from smaller-updates into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #16
2025-10-09 10:58:17 +02:00
Robin Mueller 0206d15b2e smaller tweaks and docs API unification
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 10:58:01 +02:00
Robin Mueller 98aedf2249 another small typo
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 00:51:25 +02:00
Robin Mueller c6be77c098 another typo
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 00:46:40 +02:00
Robin Mueller a68d3d1784 small typo
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2025-10-09 00:46:15 +02:00
muellerr 20092410c0 Merge pull request 'add various changelogs' (#15) from changelogs into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #15
2025-10-09 00:43:21 +02:00
Robin Mueller a20ad9f621 add various changelogs
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-10-09 00:42:45 +02:00
216 changed files with 10357 additions and 3545 deletions
+2 -1
View File
@@ -2,7 +2,7 @@
# The following two env variables need to be set for the supplied runner.sh script to work.
# Absolute path to the Vitis install directory.
# AMD_TOOLS = "/tools/Xilinx/Vitis/2024.1"
# AMD_TOOLS = "/tools/2025.2/Vitis"
# Absolute path to the PS7 initialization TCL script.
# TCL_INIT_SCRIPT = "/home/$user/$project/$sdt_dir/ps7_init.tcl"
@@ -10,3 +10,4 @@
# running the application. You only need to do this once for unchanged bitstream as long as you
# do not reset the whole board.
# ZYNQ_BITSTREAM = "/home/$user/$project/$sdt_dir/bitstream.bit"
# HW_SERVER_IP = "localhost"
+16 -21
View File
@@ -8,14 +8,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v3
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: just check zynq
- uses: dtolnay/rust-toolchain@stable
- run: just check tools
- run: just check zynq7000-boot-image
with:
targets: "armv7a-none-eabihf"
- run: just check-dir firmware
- run: just check-dir host
build:
name: Check build
@@ -24,14 +21,11 @@ jobs:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v3
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: just build zynq
- uses: dtolnay/rust-toolchain@stable
- run: just build tools
- run: just build zynq7000-boot-image
with:
targets: "armv7a-none-eabihf"
- run: just build-zynq
- run: just build-dir host
fmt:
name: Check formatting
@@ -42,9 +36,9 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: just fmt zynq
- run: just fmt tools
- run: just fmt zynq7000-boot-image
targets: "armv7a-none-eabihf"
- run: just check-fmt-dir firmware
- run: just check-fmt-dir host
docs:
name: Check Documentation Build
@@ -55,6 +49,7 @@ jobs:
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
targets: "armv7a-none-eabihf"
- run: just docs-zynq
clippy:
@@ -63,13 +58,13 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v3
- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rust-src
- run: just clippy zynq
targets: "armv7a-none-eabihf"
- run: just clippy-dir firmware
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- run: just clippy tools
- run: just clippy zynq7000-boot-image
- run: just clippy-dir host
+2 -2
View File
@@ -1,7 +1,7 @@
target
xsct-output.log
app.map
/app.map
/xsct-output.log
/.vscode
/Cargo.lock
/.cargo/config.toml
+22 -30
View File
@@ -4,52 +4,55 @@ Zynq 7000 Bare-Metal Rust Support
This crate collection provides support to write bare-metal Rust applications for the AMD Zynq 7000
family of SoCs.
<p align="center">
<img src="./ferris-zedboard.jpeg" alt="Ferris on the Zedboard" width="400" />
</p>
# List of crates
This project contains the following crates:
## [Zynq Workspace](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq)
## [Firmware Workspace](./firmware)
This workspace contains libraries and application which can only be run on the target system.
- The [`zynq7000-rt`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-rt)
- The [`zynq7000-rt`](./firmware/zynq7000-rt)
run-time crate containing basic low-level startup code necessary to boot a Rust app on the
Zynq7000.
- The [`zynq7000`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000) PAC
crate containing basic low-level register definitions.
- The [`zynq7000-mmu`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-hal)
- The [`zynq7000`](./firmware/zynq7000) PAC crate containing basic low-level register access API.
- The [`zynq7000-mmu`](./firmware/zynq7000-mmu)
crate containing common MMU abstractions used by both the HAL and the run-time crate.
- The [`zynq7000-hal`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`zynq7000-embassy`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-embassy)
crate containing support for running the embassy-rs asynchronous run-time.
- The [`zynq7000-hal`](./firmware/zynq7000-hal) HAL crate containing higher-level abstractions on
top of the PAC register crate.
- The [`zynq7000-embassy`](./firmware/zynq7000-embassy) crate containing an embassy-rs time driver
using the global timer counter peripheral.
This project was developed using a Zedboard, so there are several crates available targeted towards
this board:
- The [`zedboard-bsp`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-bsp)
- The [`zedboard-bsp`](./firmware/zedboard-bsp)
crate containing board specific components for the Zedboard.
- The [`zedboard-fsbl`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-fsbl)
- The [`zedboard-fsbl`](./firmware/zedboard-fsbl)
contains a simple first-stage bootloader application for the Zedboard.
- The [`zedboard-qspi-flasher`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-qspi-flasher)
- The [`zedboard-qspi-flasher`](./firmware/zedboard-qspi-flasher)
contains an application which is able to flash a boot binary from DDR to the QSPI.
It also contains the following helper crates:
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples)
- The [`examples`](./firmware/examples)
folder contains various example applications crates using the HAL and the PAC.
This folder also contains dedicated example applications using the
[`embassy`](https://github.com/embassy-rs/embassy) native Rust RTOS.
## Other libraries and tools
- The [`zedboard-fpga-design`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zedboard-fpga-design)
- The [`zedboard-fpga-design`](./zedboard-fpga-design)
folder contains a sample FPGA design and block design which was used in some of the provided software examples. The project was created with Vivado version 2024.1.
The folder contains a README with all the steps required to load this project from a TCL script.
- The [`zynq7000-boot-image`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-boot-image)
- The [`zynq7000-boot-image`](./host/zynq7000-boot-image)
library contains generic helpers to interface with the AMD
[boot binary](https://docs.amd.com/r/en-US/ug1283-bootgen-user-guide).
- The [`tools/zynq7000-ps7init-extract`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract)
- The [`zynq7000-ps7init-extract`](./host/zynq7000-ps7init-extract)
tool allows extracting configuration from the AMD generated `ps7init.tcl` file which contains
static configuration parameters for DDR initialization.
@@ -77,14 +80,6 @@ You can then adapt the files in `.vscode` to your needs.
# Building the blinky example
Building an application requires `nightly` to build the `core` and `alloc` library
because the `thumbv7a-none-eabihf` Rust target only has Tier 3 support
If you have not installed it yet, you can do so with
```sh
rustup toolchain install nightly
```
Assuming you have the following segments inside your `.cargo/config.toml`
```toml
@@ -100,10 +95,6 @@ rustflags = [
# "-Clink-args=-Map=app.map"
]
# Tier 3 target, so no pre-compiled artifacts included.
[unstable]
build-std = ["core", "alloc"]
[build]
target = "armv7a-none-eabihf"
```
@@ -111,7 +102,8 @@ target = "armv7a-none-eabihf"
You can build the blinky example app using
```sh
cargo build --example simple
cd zynq
cargo build --bin blinky
```
@@ -168,7 +160,7 @@ Zedboard are configured for JTAG boot.
You can use the `-tui` argument to also have a terminal UI.
This repository provides a `scripts/runner.sh` which performs all the steps specified above.
The `.cargo/def-config.toml` script contains the runner and some template environmental
The `.cargo/config.toml.template` script contains the runner and some template environmental
variables that need to be set for this to work. The command above also loaded the app, but
this task can be performed by the `zynq7000-init.py` wrapper as well.
Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

@@ -6,15 +6,16 @@ rustflags = [
"-Ctarget-feature=+vfp3",
"-Ctarget-feature=+neon",
"-Clink-arg=-Tlink.x",
# Breaks builds not using/including defmt..
# "-Clink-arg=-Tdefmt.x",
# If this is not enabled, debugging / stepping can become problematic.
"-Cforce-frame-pointers=yes",
# Can be useful for debugging.
# "-Clink-args=-Map=app.map"
]
# Tier 3 target, so no pre-compiled artifacts included.
[unstable]
build-std = ["core", "alloc"]
[build]
target = "armv7a-none-eabihf"
[env]
DEFMT_LOG = "info"
+51
View File
@@ -0,0 +1,51 @@
[workspace]
resolver = "3"
members = [
"zynq7000-rt",
"zynq7000-mmu",
"zynq7000",
"zynq7000-hal",
"zynq7000-embassy",
"examples/simple",
"examples/embassy",
"examples/zedboard",
"examples/defmt",
"examples/multiprio",
"zedboard-bsp",
"zedboard-qspi-flasher",
]
exclude = [
# Exclude, can not be built with debug optimization level, too large.
"zedboard-fsbl",
]
# cargo build/run
[profile.dev]
# default is opt-level = '0', but that makes very
# verbose machine code
opt-level = 's'
# trade compile speed for slightly better optimisations
codegen-units = 1
# cargo build/run --release
[profile.release]
# Optimize for maximum speed.
opt-level = 3
# trade compile speed for slightly better optimisations
codegen-units = 1
# Use Link Time Optimisations to further inline things across
# crates
lto = 'fat'
# Leave the debug symbols in (default is no debug info)
debug = 2
[patch.crates-io]
embassy-executor = { path = "../../embassy/embassy-executor" }
embassy-executor-macros = { path = "../../embassy/embassy-executor-macros" }
embassy-time-driver = { path = "../../embassy/embassy-time-driver" }
embassy-time = { path = "../../embassy/embassy-time" }
embassy-executor-timer-queue = { path = "../../embassy/embassy-executor-timer-queue" }
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "defmt"
version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "DEFMT test app"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal", features = ["defmt"] }
defmt = "1"
defmt-rtt = "1"
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.4"
log = "0.4"
+32
View File
@@ -0,0 +1,32 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rustc-link-arg=-Tdefmt.x");
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
}
@@ -3,11 +3,18 @@ MEMORY
/* Zedboard: 512 MB DDR3. Only use 63 MB for now, should be plenty for a bare-metal app.
Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
/*CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M*/
/* CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
/* STACK: ORIGIN = 0x3F00000, LENGTH = 1M */
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
/* Use the upper OCM as the stack */
REGION_ALIAS("STACKS", OCM_UPPER);
SECTIONS
{
@@ -20,3 +27,10 @@ SECTIONS
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 1K);
PROVIDE(_sys_stack_size = 32K);
+83
View File
@@ -0,0 +1,83 @@
//! Simple blinky app, showing a PAC variant and a HAL variant.
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use defmt_rtt as _;
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
use zynq7000_hal::{
InteruptConfig,
clocks::Clocks,
gpio::{Output, PinState, mio},
priv_tim::CpuPrivateTimer,
time::Hertz,
};
pub const LIB: Lib = Lib::Hal;
// Define the clock frequency as a constant.
//
// Not required for the PAC mode, is required for clean delays in HAL mode.
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
#[derive(Debug)]
pub enum Lib {
Pac,
Hal,
}
#[zynq7000_rt::entry]
fn main() -> ! {
let dp = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(InteruptConfig::AllInterruptsToCpu0),
})
.expect("Failed to initialize Zynq7000");
defmt::println!("-- Zynq7000 defmt test application --");
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
defmt::info!("clocks {:?}", clocks);
// Unwrap okay, we only call this once on core 0 here.
let mut cpu_tim = CpuPrivateTimer::take(clocks.arm_clocks()).unwrap();
let mio_pins = mio::Pins::new(dp.gpio);
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::High);
loop {
defmt::info!("toggling LED!");
led.toggle().unwrap();
cpu_tim.delay_ms(1000);
}
}
#[zynq7000_rt::irq]
fn irq_handler() {}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {
nop();
}
}
@@ -11,44 +11,27 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = { version = "0.3", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
zynq7000-embassy = { path = "../../zynq7000-embassy" }
dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev = "10319bdeae9ace3bb0fc79a15da2869c5bf50f52", features = ["async"] }
embedded-hal-async = "1"
embassy-sync = "0.8"
static_cell = "2"
critical-section = "1"
heapless = "0.9"
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
fugit = "0.4"
log = "0.4"
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = [
"arch-cortex-ar",
embassy-executor = { version = "0.10", features = [
"platform-z7",
"executor-thread",
]}
# TODO: Remove generic-queue-16 feature as soon as upstream executor is used again.
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] }
# cargo build/run
[profile.dev]
# default is opt-level = '0', but that makes very
# verbose machine code
opt-level = 's'
# trade compile speed for slightly better optimisations
codegen-units = 1
# cargo build/run --release
[profile.release]
# default is opt-level = '3', but that makes quite
# verbose machine code
opt-level = 's'
# trade compile speed for slightly better optimisations
codegen-units = 1
# Use Link Time Optimisations to further inline things across
# crates
lto = 'fat'
# Leave the debug symbols in (default is no debug info)
debug = 2
# embassy-executor = { git = "https://github.com/robamu/embassy.git", branch = "add-z7-arch-support", features = ["platform-z7", "executor-thread"] }
# embassy-executor = { git = "https://github.com/robamu/embassy.git", features = ["platform-cortex-ar", "executor-thread"] }
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
+25
View File
@@ -0,0 +1,25 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
SECTIONS
{
/* Uncached memory */
.uncached (NOLOAD) : ALIGN(4) {
. = ALIGN(4);
_sbss_uncached = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}
@@ -2,8 +2,8 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
@@ -12,7 +12,8 @@ use log::{error, info, warn};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::Configurator,
gpio::{Flex, Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -21,28 +22,23 @@ use zynq7000_hal::{
};
use zynq7000::Peripherals;
use zynq7000_rt as _;
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
/// Try to talk to a DHT22 sensor connected at MIO0.
const DHT22_AT_MIO0: bool = true;
/// Open drain pin testing. MIO9 needs to be tied to MIO14.
const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false;
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -50,7 +46,7 @@ async fn main(_spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -67,7 +63,7 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = Uart::new_with_mio(
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
@@ -76,13 +72,7 @@ async fn main(_spawner: Spawner) -> ! {
uart.write_all(b"-- Zynq 7000 DHT22 --\n\r").unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let mut delay = Delay;
@@ -162,23 +152,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -1,31 +1,29 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{BootMode, InteruptConfig, clocks, gic, gpio, gtc, time::Hertz, uart};
use zynq7000_hal::{
BootMode, InteruptConfig, clocks, generic_interrupt_handler, gpio, gtc, time::Hertz, uart,
};
use zynq7000_rt as _;
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
@@ -45,7 +43,7 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio(
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
@@ -53,14 +51,7 @@ async fn main(_spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -75,23 +66,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -2,23 +2,24 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use log::info;
use zynq7000::Peripherals;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::Configurator,
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
time::Hertz,
uart::{ClockConfig, Config, TxAsync, Uart, on_interrupt_tx},
uart::{self, ClockConfig, Config, TxAsync, Uart},
};
use zynq7000_rt as _;
@@ -26,16 +27,12 @@ use zynq7000_rt as _;
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[unsafe(export_name = "main")]
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
@@ -44,7 +41,7 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -61,7 +58,7 @@ async fn main(spawner: Spawner) -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = Uart::new_with_mio(
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
@@ -70,57 +67,60 @@ async fn main(spawner: Spawner) -> ! {
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
.unwrap();
uart.flush().unwrap();
let (tx, _rx) = uart.split();
let mut logger = TxAsync::new(tx);
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let (tx, _rx) = uart.split();
let logger = TxAsync::new(tx, true);
let mut log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, logger).unwrap();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
spawner.spawn(led_task(led).unwrap());
let mut log_buf: [u8; 2048] = [0; 2048];
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
logger.write(&log_buf[0..next_frame_len]).await;
}
spawner.spawn(hello_task().unwrap());
log_runner.run().await
}
#[embassy_executor::task]
async fn led_task(mut mio_led: Output) {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
mio_led.toggle().unwrap();
info!("Toggling LED");
info!(
"Toggling LED ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[embassy_executor::task]
async fn hello_task() {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
info!(
"Hello from another task ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -147,6 +147,7 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
loop {}
}
@@ -0,0 +1,183 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_io::Write as _;
use log::error;
use zynq7000_hal::{
InteruptConfig, clocks,
executor::InterruptExecutor,
generic_interrupt_handler,
gic::{SgiInterrupt, TargetCpus},
gpio::{self, Output},
gtc,
time::Hertz,
uart,
};
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
static INTERUPT_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main(executor = "zynq7000_hal::executor::Executor")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
let mio_pins = gpio::mio::Pins::new(periphs.gpio);
let led = gpio::Output::new_for_mio(mio_pins.mio7, gpio::PinState::Low);
let sgi_interrupt = SgiInterrupt::new(0).unwrap();
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Interrupt Executor --\n\r")
.unwrap();
uart.flush().unwrap();
//let (tx, _rx) = uart.split();
// let mut logger = uart::TxAsync::new(tx, true);
//let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
log::info!(
"interrupt counter: {}",
CNTR.load(core::sync::atomic::Ordering::Relaxed)
);
let spawner = INTERUPT_EXECUTOR.start(sgi_interrupt, TargetCpus::CPU_0);
zynq7000_hal::register_interrupt(
zynq7000_hal::Interrupt::Sgi(sgi_interrupt),
on_interrupt_low_prio,
);
spawner.spawn(led_task(led).unwrap());
log::info!(
"interrupt counter: {}",
CNTR.load(core::sync::atomic::Ordering::Relaxed)
);
//Delay.delay_ms(1).await;
/*
log::info!(
"interrupt counter: {}",
CNTR.load(core::sync::atomic::Ordering::Relaxed)
);
*/
//let mut log_buf: [u8; 2048] = [0; 2048];
loop {
log::info!("THR alive");
/*
let read_bytes = log_reader.read(&mut log_buf).await;
if read_bytes > 0 {
// Unwrap okay, checked that size is larger than 0.
logger.write(&log_buf[0..read_bytes]).unwrap().await;
}
*/
Ticker::every(Duration::from_millis(1000)).next().await;
}
}
static CNTR: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
unsafe fn interrupt_handler(_ctx: *mut ()) {
CNTR.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
unsafe {
INTERUPT_EXECUTOR.on_interrupt();
}
CNTR.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
#[embassy_executor::task]
async fn led_task(mut mio_led: Output) {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
mio_led.toggle().unwrap();
log::info!(
"Toggling LED ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
log::info!(
"Interrupt handled, counter: {}",
CNTR.load(core::sync::atomic::Ordering::Relaxed)
);
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
@@ -7,8 +7,8 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::{digital::StatefulOutputPin, pwm::SetDutyCycle};
@@ -18,7 +18,8 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::Configurator,
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -32,17 +33,13 @@ use zynq7000_rt as _;
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -50,7 +47,7 @@ async fn main(_spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -74,22 +71,15 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = Uart::new_with_mio(
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
.unwrap();
uart.write_all(b"-- Zynq 7000 PWM example--\n\r").unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -112,23 +102,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -1,30 +1,24 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use log::error;
use zynq7000_hal::{InteruptConfig, clocks, gic, gpio, gtc, time::Hertz};
use zynq7000_rt as _;
use zynq7000_hal::{InteruptConfig, clocks, generic_interrupt_handler, gpio, gtc, time::Hertz};
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
@@ -48,22 +42,11 @@ async fn main(_spawner: Spawner) -> ! {
#[zynq7000_rt::irq]
pub fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "embassy-multiprio"
version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Embassy examples for the Zynq7000 SoC"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
zynq7000-embassy = { path = "../../zynq7000-embassy" }
dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev = "10319bdeae9ace3bb0fc79a15da2869c5bf50f52", features = ["async"] }
embedded-hal-async = "1"
embassy-sync = "0.8"
static_cell = "2"
critical-section = "1"
heapless = "0.9"
embedded-io = "0.7"
embedded-hal = "1"
arbitrary-int = "2"
fugit = "0.4"
log = "0.4"
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.10", features = [
"platform-z7",
"executor-thread",
"executor-interrupt",
]}
+25
View File
@@ -0,0 +1,25 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
SECTIONS
{
/* Uncached memory */
.uncached (NOLOAD) : ALIGN(4) {
. = ALIGN(4);
_sbss_uncached = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}
+170
View File
@@ -0,0 +1,170 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use arbitrary_int::u5;
use core::panic::PanicInfo;
use embassy_executor::{InterruptExecutor, Spawner};
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write as _;
use log::error;
use zynq7000_hal::{
InteruptConfig, clocks, generic_interrupt_handler,
gic::SgiInterrupt,
gpio::{self, Output},
gtc,
time::Hertz,
uart,
};
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
static INTERUPT_EXECUTOR_LOW_PRIO: InterruptExecutor = InterruptExecutor::new();
static INTERUPT_EXECUTOR_MED_PRIO: InterruptExecutor = InterruptExecutor::new();
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let mut gic = unsafe { zynq7000_hal::gic::Configurator::steal() };
gic.set_all_sgi_interrupt_targets_cpu0();
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
let mio_pins = gpio::mio::Pins::new(periphs.gpio);
let led = gpio::Output::new_for_mio(mio_pins.mio7, gpio::PinState::Low);
let sgi_interrupt_low_prio = SgiInterrupt::new(0).unwrap();
let sgi_interrupt_med_prio = SgiInterrupt::new(1).unwrap();
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Interrupt Executor --\n\r")
.unwrap();
uart.flush().unwrap();
let (tx, _rx) = uart.split();
let mut logger = uart::TxAsync::new(tx, true);
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
gic.set_sgi_interrupt_priority(sgi_interrupt_low_prio, u5::new(2));
gic.set_sgi_interrupt_priority(sgi_interrupt_med_prio, u5::new(1));
zynq7000_hal::register_interrupt(
zynq7000_hal::Interrupt::Sgi(sgi_interrupt_low_prio),
on_interrupt_low_prio,
);
zynq7000_hal::register_interrupt(
zynq7000_hal::Interrupt::Sgi(sgi_interrupt_med_prio),
on_interrupt_med_prio,
);
let spawner = INTERUPT_EXECUTOR_LOW_PRIO.start(sgi_interrupt_low_prio.as_u4());
spawner.spawn(led_task(led).unwrap());
let spawner = INTERUPT_EXECUTOR_MED_PRIO.start(sgi_interrupt_med_prio.as_u4());
spawner.spawn(hello_task().unwrap());
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let read_bytes = log_reader.read(&mut log_buf).await;
if read_bytes > 0 {
// Unwrap okay, checked that size is larger than 0.
logger.write(&log_buf[0..read_bytes]).unwrap().await;
}
Ticker::every(Duration::from_millis(1000)).next().await;
}
}
#[embassy_executor::task]
async fn led_task(mut mio_led: Output) {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
mio_led.toggle().unwrap();
log::info!(
"Toggling LED ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[embassy_executor::task]
async fn hello_task() {
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
log::info!("Hello from the low priority task");
ticker.next().await;
}
}
unsafe fn on_interrupt_low_prio() {
unsafe {
INTERUPT_EXECUTOR_LOW_PRIO.on_interrupt();
}
}
unsafe fn on_interrupt_med_prio() {
unsafe {
INTERUPT_EXECUTOR_MED_PRIO.on_interrupt();
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
@@ -9,16 +9,11 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-ar = "0.3"
aarch32-cpu = { version = "0.3" }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
fugit = "0.4"
log = "0.4"
[profile.release]
codegen-units = 1
debug = true
lto = true
+31
View File
@@ -0,0 +1,31 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
SECTIONS
{
/* Uncached memory */
.uncached (NOLOAD) : ALIGN(4) {
. = ALIGN(4);
_sbss_uncached = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 1K);
PROVIDE(_sys_stack_size = 32K);
@@ -2,8 +2,8 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
use zynq7000::Peripherals;
use zynq7000_hal::{
@@ -13,7 +13,6 @@ use zynq7000_hal::{
priv_tim::CpuPrivateTimer,
time::Hertz,
};
use zynq7000_rt as _;
pub const LIB: Lib = Lib::Hal;
@@ -31,21 +30,12 @@ pub enum Lib {
Hal,
}
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
l2_cache::init_with_defaults(&mut unsafe { zynq7000::l2_cache::L2Cache::new_mmio_fixed() });
#[zynq7000_rt::entry]
fn main() -> ! {
l2_cache::init_with_defaults(&mut unsafe { zynq7000::l2_cache::Registers::new_mmio_fixed() });
match LIB {
Lib::Pac => {
let mut gpio = unsafe { zynq7000::gpio::Gpio::new_mmio_fixed() };
let mut gpio = unsafe { zynq7000::gpio::Registers::new_mmio_fixed() };
gpio.bank_0().modify_dirm(|v| v | ZEDBOARD_LED_MASK);
gpio.bank_0().modify_out_en(|v| v | ZEDBOARD_LED_MASK);
loop {
@@ -2,14 +2,15 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
use cortex_ar::asm::nop;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{
Interrupt,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gic,
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -18,31 +19,20 @@ use zynq7000_hal::{
uart::{ClockConfig, Config, Uart},
};
use zynq7000_rt as _;
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
#[zynq7000_rt::entry]
fn main() -> ! {
let mut dp = zynq7000::Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = gic::Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -62,7 +52,7 @@ pub fn main() -> ! {
// This structure holds all MIO pins.
let mio_pins = mio::Pins::new(dp.gpio);
let mut uart = Uart::new_with_mio(
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
@@ -70,14 +60,7 @@ pub fn main() -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 GTC Ticks example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
loop {
@@ -94,8 +77,8 @@ pub fn main() -> ! {
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
let mut gic_helper = gic::InterruptGuard::new();
let irq_info = gic_helper.interrupt_info();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
@@ -2,15 +2,15 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
use cortex_ar::asm::nop;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gic::{self, Configurator, Interrupt},
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -19,31 +19,20 @@ use zynq7000_hal::{
uart::{ClockConfig, Config, Uart},
};
use zynq7000_rt as _;
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
#[zynq7000_rt::entry]
fn main() -> ! {
let mut dp = zynq7000::Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -62,7 +51,7 @@ pub fn main() -> ! {
gtc.enable();
let mio_pins = mio::Pins::new(dp.gpio);
let mut uart = Uart::new_with_mio(
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
@@ -71,13 +60,7 @@ pub fn main() -> ! {
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {boot_mode:?}");
@@ -96,13 +79,12 @@ pub fn main() -> ! {
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
let mut gic_helper = gic::InterruptGuard::new();
let irq_info = gic_helper.interrupt_info();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
// TODO: Call embassy on interrupt handler here soon.
MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
+123
View File
@@ -0,0 +1,123 @@
//! Example which uses the UART1 to send log messages.
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{self, Configurator, Interrupt, SgiInterrupt},
gpio::{Output, PinState, mio},
l2_cache,
time::Hertz,
uart::{ClockConfig, Config, Uart},
};
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
static SGI_COUNTER: AtomicU64 = AtomicU64::new(0);
#[zynq7000_rt::entry]
fn main() -> ! {
let mut dp = zynq7000::Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.set_all_sgi_interrupt_targets_cpu0();
gic.enable();
// Enable interrupt exception.
unsafe { gic.enable_interrupts() };
// Set up the UART, we are logging with it.
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mio_pins = mio::Pins::new(dp.gpio);
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Software Interrupt example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {boot_mode:?}");
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
loop {
let gtc = SGI_COUNTER.load(core::sync::atomic::Ordering::Relaxed);
info!("Hello, world!");
info!("SGI counter: {gtc}");
gic.trigger_software_interrupt(SgiInterrupt::new(0).unwrap());
led.toggle().unwrap();
for _ in 0..5_000_000 {
nop();
}
}
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::InterruptGuard::new();
let irq_info = gic_helper.interrupt_info();
match irq_info.interrupt() {
Interrupt::Sgi(_sgi) => {
// TODO: Send ID to main thread.
SGI_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
Interrupt::Ppi(_ppi_interrupt) => (),
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
@@ -2,23 +2,13 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use zynq7000_rt as _;
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
#[zynq7000_rt::entry]
fn main() -> ! {
loop {
cortex_ar::asm::nop();
nop();
}
}
@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = "0.3"
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
@@ -20,32 +20,29 @@ zedboard-bsp = { path = "../../zedboard-bsp" }
num_enum = { version = "0.7", default-features = false }
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
embedded-io = "0.7"
bitbybit = "1.4"
bitbybit = "2"
arbitrary-int = "2"
embedded-io-async = "0.6"
embedded-io-async = "0.7"
critical-section = "1"
static_cell = "2"
embedded-alloc = "0.6"
embedded-alloc = "0.7"
embedded-hal = "1"
embedded-hal-bus = { version = "0.3", features = ["async"] }
embedded-hal-async = "1"
fugit = "0.3"
dummy-pin = "1"
fugit = "0.4"
fugit-03 = { version = "0.3", package = "fugit" }
embedded-graphics = "0.8"
log = "0.4"
rand = { version = "0.9", default-features = false, features = ["small_rng"] }
ssd1306 = { version = "0.10", features = ["async"] }
tinybmp = "0.7"
rand = { version = "0.10", default-features = false }
embassy-executor = { git = "https://github.com/us-irs/embassy.git", branch = "cortex-ar-update", features = [
"arch-cortex-ar",
"executor-thread",
]}
# TODO: Remove generic-queue-16 feature as soon as upstream executor is used again.
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] }
embassy-net = { version = "0.7", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp"] }
embassy-sync = { version = "0.7" }
# TODO: Bump as soon as new compatible smoltcp/embassy-net version is released.
heapless = "0.8"
axi-uartlite = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uartlite.git" }
axi-uart16550 = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uart16550.git" }
[profile.release]
codegen-units = 1
debug = true
lto = true
embassy-executor = { version = "0.10", features = ["platform-cortex-ar", "executor-thread"] }
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
embassy-net = { version = "0.9", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp", "auto-icmp-echo-reply"] }
embedded-sdmmc = { git = "https://github.com/robamu/embedded-sdmmc-rs.git", branch = "all-features" }
embassy-sync = { version = "0.8" }
heapless = "0.9"
axi-uartlite = { version = "0.1" }
axi-uart16550 = { version = "0.1" }
Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
version="1.1"
width="0.16in" height="0.106667in"
viewBox="0 0 48 32">
<defs>
</defs>
<image id="raster0"
x="0"
y="0"
width="48"
height="32"
opacity="1.000000"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAQAAAD+3TOXAAAHDmVYSWZJSSoACAAAAAAADgAAAAkA/gAEAAEAAAABAAAAAAEEAAEAAAAAAQAAAQEEAAEAAACqAAAAAgEDAAMAAACAAAAAAwEDAAEAAAAGAAAABgEDAAEAAAAGAAAAFQEDAAEAAAADAAAAAQIEAAEAAACGAAAAAgIEAAEAAACIBgAAAAAAAAgACAAIAP/Y/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAqgEAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A+f6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr6/+CX/ACSHQv8At4/9KJK+QK+v/gl/ySHQv+3j/wBKJKAPnzxr8Jte8B6NDqmqXemzQS3C26rayOzBirNk7kUYwh7+lcHX0/8AtHf8k80//sKx/wDoqWvmCgAroPAn/JQ/DX/YVtf/AEatc/XQeBP+Sh+Gv+wra/8Ao1aAPqP4s+CtS8eeFbXS9LntIZ4r1LhmunZVKhHXA2qxzlx29a+VPFPhu88I+I7vQ7+SCS6tdm94GJQ7kVxgkA9GHavuevkD42/8le13/t3/APSeOgDz+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK+g/hL8WdB0rw74f8Hz2mpNqD3BtxIkaGLdLOxU5Lg4+cZ49etfPlWLC+uNM1G2v7OTy7q1lSaF9oO11IKnB4OCB1oA+968j+J3xZ0HSofEXg+e01JtQeye3EiRoYt0sOVOS4OPnGePXrXjn/AAu34h/9DD/5JW//AMbrj9b1vUfEesT6tq1x9ovp9vmS7FTdtUKOFAA4AHAoAz6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9mZmF3EAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAhhJREFUSMft1T1I1WEUBvDfVQn1hoHoDQlMqCSlaLAiCKMhbIpqKLCtTQKJoLWh76GgpQ+isKWMgoiiocgpKoKEoCHCoHJJ1GjRAvu4p8GPrt57Ta/W5HmW9/w553ne9z3nf14WbJ6tXMu/or7vPM76aoVavbbMH3W/NtXSwnU/hJd6hA5LPXZ0btQbJNQLYVBMQdqgcBc1agoVCD1uZ1FnYtAt313+O9WiPAIzw8UpeUnnbEZy1C1WpskzqzJCLvnozowF3nnkm4ax3OU+CJU4IQU3bFMuDGuaEDgyY/I/GN1vwhshUKbf3hJb7TOiy4CUbgmstER61vXqsxpvbZo4yXEpQZuQtt4LIdTjaQG7H0erPWOrDmFENc1C6NUlhEYcKJj+s3IpPyf8U6NHuTDxoQ8NDhUs8FqzSmfGvIdKxu+vzZAQ2rXnS45pvEkYViuE04oyS1TlldAs6WM+gcjrTcJJXcL27C6o0yvtis45CaQ9Fw7mbrRlHsyhf/7gZiZpccZ6SNKOeZi+a5R6kutPapEWwq95OMW+cdLMSo9IgGO+ueTLdNMvhw1M8u7JOz2HVQuh1G7XsnsqR3n7ddqpGKFp6g4SWRKw2LAKQ6DGOiusVaF1SuxVfd7q1pORW+mT0izWHLdQ53ABz1OxEMr/HhiSNs6SvkSjIu9zFyiXRCG2KzuvKGdgwn4VBQhU5eHLI7Jg/8d+A9OUD/TqXTt5AAAAAElFTkSuQmCCAA==" />
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

+32
View File
@@ -0,0 +1,32 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
SECTIONS
{
/* Uncached memory */
.uncached (NOLOAD) : ALIGN(4) {
. = ALIGN(4);
_sbss_uncached = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 1K);
PROVIDE(_irq_stack_size = 2K);
PROVIDE(_sys_stack_size = 32K);
@@ -24,15 +24,15 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::{net::Ipv4Addr, panic::PanicInfo};
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_net::{Ipv4Cidr, StaticConfigV4, tcp::TcpSocket, udp::UdpSocket};
use embassy_time::{Duration, Timer};
use embedded_io::Write;
use embedded_io_async::Write as _;
use log::{LevelFilter, debug, error, info, warn};
use rand::{RngCore, SeedableRng};
use rand::{Rng, SeedableRng};
use zedboard::PS_CLOCK_FREQUENCY;
use zedboard_bsp::phy_marvell;
use zynq7000_hal::{
@@ -42,7 +42,8 @@ use zynq7000_hal::{
eth::{
AlignedBuffer, ClockDivSet, EthernetConfig, EthernetLowLevel, embassy_net::InterruptResult,
},
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::{Configurator, Interrupt},
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
@@ -104,12 +105,9 @@ pub enum IpMode {
StackReady,
}
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
@@ -204,7 +202,6 @@ async fn tcp_task(mut tcp: TcpSocket<'static>) -> ! {
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -220,7 +217,7 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -237,7 +234,7 @@ async fn main(spawner: Spawner) -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = Uart::new_with_mio(
let mut uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
@@ -245,7 +242,7 @@ async fn main(spawner: Spawner) -> ! {
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe { zynq7000_hal::log::uart_blocking::init_unsafe_single_core(uart, LOG_LEVEL, false) };
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, LOG_LEVEL, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -282,6 +279,12 @@ async fn main(spawner: Spawner) -> ! {
"Calculated RGMII clock configuration: {:?}, errors (missmatch from ideal rate in hertz): {:?}",
clk_divs, clk_errors
);
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Eth0),
custom_eth_interupt_handler,
);
// Unwrap okay, we use a standard clock config, and the clock config should never fail.
let eth_cfg = EthernetConfig::new(
zynq7000_hal::eth::ClockConfig::new(clk_divs.cfg_1000_mbps),
@@ -290,7 +293,7 @@ async fn main(spawner: Spawner) -> ! {
);
// Configures all the physical pins for ethernet operation and sets up the
// ethernet peripheral.
let mut eth = zynq7000_hal::eth::Ethernet::new_with_mio(
let mut eth = zynq7000_hal::eth::Ethernet::new_with_mio_eth_0(
eth_ll,
eth_cfg,
gpio_pins.mio.mio16,
@@ -377,9 +380,9 @@ async fn main(spawner: Spawner) -> ! {
let tcp_socket = TcpSocket::new(stack, RX_TCP_BUFS.take(), TX_TCP_BUFS.take());
// Spawn all embassy tasks.
spawner.spawn(embassy_net_task(runner)).unwrap();
spawner.spawn(udp_task(udp_socket)).unwrap();
spawner.spawn(tcp_task(tcp_socket)).unwrap();
spawner.spawn(embassy_net_task(runner).unwrap());
spawner.spawn(udp_task(udp_socket).unwrap());
spawner.spawn(tcp_task(tcp_socket).unwrap());
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
@@ -465,36 +468,24 @@ async fn main(spawner: Spawner) -> ! {
}
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Eth0 {
// This generic library provided interrupt handler takes care of waking
// the driver on received or sent frames while also reporting anomalies
// and errors.
let result = zynq7000_hal::eth::embassy_net::on_interrupt(
zynq7000_hal::eth::EthernetId::Eth0,
);
if result.has_errors() {
ETH_ERR_QUEUE.try_send(result).ok();
}
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
// Safety: Only called by interrupt handler, registered in global interrupt handler map.
unsafe fn custom_eth_interupt_handler() {
// This generic library provided interrupt handler takes care of waking
// the driver on received or sent frames while also reporting anomalies
// and errors.
let result = zynq7000_hal::eth::embassy_net::on_interrupt(zynq7000_hal::eth::EthernetId::Eth0);
if result.has_errors() {
ETH_ERR_QUEUE.try_send(result).ok();
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -8,8 +8,8 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
@@ -20,8 +20,8 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler,
gic::Configurator,
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
i2c, l2_cache,
@@ -36,17 +36,13 @@ use zynq7000_rt as _;
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
const I2C_ADDR_SEL: I2cAddr = I2cAddr::Sa0Low;
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -58,7 +54,7 @@ async fn main(_spawner: Spawner) -> ! {
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -76,7 +72,7 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio(
let mut uart = uart::Uart::new_with_mio_for_uart_1(
dp.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
@@ -84,14 +80,8 @@ async fn main(_spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard I2C L3GD20H example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -157,24 +147,13 @@ async fn main(_spawner: Spawner) -> ! {
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[unsafe(no_mangle)]
@@ -9,8 +9,8 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
@@ -20,14 +20,15 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler,
gic::Configurator,
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
spi::{self, SpiAsync, SpiId, SpiWithHwCs, SpiWithHwCsAsync, on_interrupt},
log::asynch::UartLoggerRunner,
spi::{self, SpiAsync, SpiWithHwCs, SpiWithHwCsAsync},
time::Hertz,
uart::{self, TxAsync, on_interrupt_tx},
uart::{self, TxAsync},
};
use zynq7000::{Peripherals, slcr::LevelShifterConfig, spi::DelayControl};
@@ -39,17 +40,13 @@ const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
const DEBUG_SPI_CLK_CONFIG: bool = false;
const BLOCKING: bool = false;
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -60,15 +57,20 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// SPI reference clock must be larger than the CPU 1x clock.
let spi_ref_clk_div = spi::calculate_largest_allowed_spi_ref_clk_divisor(&clocks)
.unwrap()
.value()
- 1;
spi::configure_spi_ref_clk(&mut clocks, arbitrary_int::u6::new(spi_ref_clk_div as u8));
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
assert!(
clocks.io_clocks().spi_clk().to_raw()
> (clocks.arm_clocks().cpu_1x_clk().to_raw() as f32 * 1.2) as u32,
"SPI reference clock must be larger than CPU 1x clock"
);
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -86,7 +88,7 @@ async fn main(spawner: Spawner) -> ! {
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio(
let mut uart = uart::Uart::new_with_mio_for_uart_1(
dp.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
@@ -94,30 +96,32 @@ async fn main(spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
.unwrap();
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let (tx, _) = uart.split();
let tx_async = TxAsync::new(tx, true);
let log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async).unwrap();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
if DEBUG_SPI_CLK_CONFIG {
info!(
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}, DIV: {:?}",
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}",
clocks.arm_clocks().cpu_1x_clk(),
clocks.io_clocks().ref_clk(),
clocks.io_clocks().spi_clk(),
spi_ref_clk_div
);
}
let mut spi = spi::Spi::new_one_hw_cs(
dp.spi_1,
clocks.io_clocks(),
spi::Config::new(
// 10 MHz maximum rating of the sensor.
zynq7000::spi::BaudDivSel::By64,
//l3gd20::MODE,
// l3gd20::MODE,
embedded_hal::spi::MODE_3,
spi::SlaveSelectConfig::AutoWithAutoStart,
spi::SlaveSelectConfig::AutoCsAutoStart,
),
(
gpio_pins.mio.mio12,
@@ -127,10 +131,13 @@ async fn main(spawner: Spawner) -> ! {
gpio_pins.mio.mio13,
)
.unwrap();
let sclk = Hertz::from_raw(
clocks.io_clocks().spi_clk().to_raw() / zynq7000::spi::BaudDivSel::By64.div_value() as u32,
);
let mod_id = spi.regs().read_mod_id();
assert_eq!(mod_id, spi::MODULE_ID);
assert!(spi.sclk() <= Hertz::from_raw(10_000_000));
let min_delay = (spi.sclk().raw() * 5) / 1_000_000_000;
assert!(sclk <= Hertz::from_raw(10_000_000));
let min_delay = (sclk.to_raw() * 5) / 1_000_000_000;
spi.inner().configure_delays(
DelayControl::builder()
.with_inter_word_cs_deassert(0)
@@ -159,25 +166,17 @@ async fn main(spawner: Spawner) -> ! {
}
}
spawner.spawn(logger_task(uart)).unwrap();
spawner.spawn(logger_task(log_runner).unwrap());
if BLOCKING {
blocking_application(mio_led, emio_leds, spi).await;
blocking_application(mio_led, emio_leds, spi).await
} else {
non_blocking_application(mio_led, emio_leds, spi).await;
non_blocking_application(mio_led, emio_leds, spi).await
}
}
#[embassy_executor::task]
pub async fn logger_task(uart: uart::Uart) {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx);
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
tx_async.write(&log_buf[0..next_frame_len]).await;
}
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
log_runner.run().await
}
pub async fn blocking_application(
@@ -241,30 +240,13 @@ pub async fn non_blocking_application(
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Spi1 {
on_interrupt(SpiId::Spi1);
} else if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[unsafe(no_mangle)]
@@ -0,0 +1,276 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use dummy_pin::DummyPin;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Ticker};
use embedded_graphics::{Drawable as _, geometry::Point};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use embedded_io::Write;
use log::info;
use ssd1306::{Ssd1306Async, prelude::*};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
log::asynch::UartLoggerRunner,
spi::{self, SpiAsync},
time::Hertz,
uart::{self, TxAsync},
};
use embedded_graphics::image::Image;
use tinybmp::Bmp;
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard Async OLED example --\n\r";
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
assert!(
clocks.io_clocks().spi_clk().to_raw()
> (clocks.arm_clocks().cpu_1x_clk().to_raw() as f32 * 1.2) as u32,
"SPI reference clock must be larger than CPU 1x clock"
);
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
let (tx, _) = uart.split();
let tx_async = TxAsync::new(tx, true);
let log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async)
.expect("Failed to initialize async logger");
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
info!(
"SPI reference clock speed: {:?}",
clocks.io_clocks().spi_clk()
);
spawner.spawn(logger_task(log_runner).unwrap());
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let emio_leds: [gpio::Output; 8] = [
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
];
spawner.spawn(blinky_task(mio_led, emio_leds).unwrap());
let dc_pin = gpio::Output::new_for_emio(gpio_pins.emio.take(11).unwrap(), gpio::PinState::High);
let mut reset_pin =
gpio::Output::new_for_emio(gpio_pins.emio.take(12).unwrap(), gpio::PinState::High);
let mut oled_vdd_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(13).unwrap(), gpio::PinState::High);
let mut oled_vbat_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(14).unwrap(), gpio::PinState::High);
Delay.delay_ms(100).await;
let spi = spi::Spi::new_for_emio(
periphs.spi_0,
spi::Config::calculate_for_io_clock(
Hertz::MHz(8),
clocks.io_clocks(),
embedded_hal::spi::MODE_0,
spi::SlaveSelectConfig::AutoCsManualStart,
),
)
.expect("Failed to initialize SPI");
let spi_asynch = SpiAsync::new(spi);
let exclusive_device = ExclusiveDevice::new(spi_asynch, DummyPin::new_high(), NoDelay)
.expect("Failed to create exclusive SPI device");
let spi_if = SPIInterface::new(exclusive_device, dc_pin);
let mut ssd1306 = Ssd1306Async::new(spi_if, DisplaySize128x32, DisplayRotation::Rotate180);
oled_vdd_switch.set_low();
oled_vbat_switch.set_low();
Delay.delay_ms(100).await;
ssd1306
.reset(&mut reset_pin, &mut embassy_time::Delay {})
.await
.expect("display reset error");
let mut display = ssd1306.into_buffered_graphics_mode();
display.init().await.expect("display init error");
// Include the BMP file data.
let ferris_data = include_bytes!("../../assets/ferris-flat-happy-small.bmp");
let rust_logo_data = include_bytes!("../../assets/rust-logo-single-path.bmp");
// Parse the BMP file.
let bmp_rust = Bmp::from_slice(rust_logo_data).expect("BMP loading error");
let bmp_ferris = Bmp::from_slice(ferris_data).expect("BMP loading error");
// Draw the image with the top left corner at (10, 20) by wrapping it in
// an embedded-graphics `Image`.
Image::new(&bmp_rust, Point::new(0, 0))
.draw(&mut display)
.expect("image drawing error");
Image::new(&bmp_ferris, Point::new(32, 0))
.draw(&mut display)
.expect("image drawing error");
display.flush().await.unwrap();
let mut ticker = Ticker::every(Duration::from_millis(50));
let mut ferris = FerrisMovement::new(32, Direction::Right, 32, 76);
loop {
Image::new(&bmp_ferris, Point::new(ferris.pos as i32, 0))
.draw(&mut display)
.expect("image drawing error");
display.flush().await.expect("flush error");
ferris.step();
ticker.next().await;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Right,
Left,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FerrisMovement {
pub pos: u16,
pub dir: Direction,
pub left_threshold: u16,
pub right_threshold: u16,
}
impl FerrisMovement {
/// Creates a new movement controller.
/// Assumes: left_threshold <= right_threshold and pos is within that range.
pub fn new(pos: u16, dir: Direction, left_threshold: u16, right_threshold: u16) -> Self {
Self {
pos,
dir,
left_threshold,
right_threshold,
}
}
/// Move one tick and "bounce" between thresholds.
pub fn step(&mut self) {
match self.dir {
Direction::Right => {
if self.pos >= self.right_threshold {
self.dir = Direction::Left; // flip at right boundary
} else {
self.pos += 1;
}
}
Direction::Left => {
if self.pos <= self.left_threshold {
self.dir = Direction::Right; // flip at left boundary
} else {
self.pos -= 1;
}
}
}
}
}
#[embassy_executor::task]
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
log_runner.run().await
}
#[embassy_executor::task]
pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output; 8]) {
let mut ticker = Ticker::every(Duration::from_millis(200));
loop {
mio_led.toggle().unwrap();
// Create a wave pattern for emio_leds
for led in emio_leds.iter_mut() {
led.toggle().unwrap();
ticker.next().await; // Wait for the next ticker for each toggle
}
ticker.next().await;
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
loop {}
}
+271
View File
@@ -0,0 +1,271 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use dummy_pin::DummyPin;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Ticker};
use embedded_graphics::{Drawable as _, geometry::Point};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use embedded_io::Write;
use log::info;
use ssd1306::{Ssd1306, prelude::*};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
log::asynch::UartLoggerRunner,
spi,
time::Hertz,
uart::{self, TxAsync},
};
use embedded_graphics::image::Image;
use tinybmp::Bmp;
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard OLED example --\n\r";
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
assert!(
clocks.io_clocks().spi_clk().to_raw()
> (clocks.arm_clocks().cpu_1x_clk().to_raw() as f32 * 1.2) as u32,
"SPI reference clock must be larger than CPU 1x clock"
);
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
let (tx, _) = uart.split();
let tx_async = TxAsync::new(tx, true);
let log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async)
.expect("Failed to initialize async logger");
spawner.spawn(logger_task(log_runner).unwrap());
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
info!(
"SPI reference clock speed: {:?}",
clocks.io_clocks().spi_clk()
);
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let emio_leds: [gpio::Output; 8] = [
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
];
spawner.spawn(blinky_task(mio_led, emio_leds).unwrap());
let dc_pin = gpio::Output::new_for_emio(gpio_pins.emio.take(11).unwrap(), gpio::PinState::High);
let mut reset_pin =
gpio::Output::new_for_emio(gpio_pins.emio.take(12).unwrap(), gpio::PinState::High);
let mut oled_vdd_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(13).unwrap(), gpio::PinState::High);
let mut oled_vbat_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(14).unwrap(), gpio::PinState::High);
Delay.delay_ms(100).await;
let spi = spi::Spi::new_for_emio(
periphs.spi_0,
spi::Config::calculate_for_io_clock(
Hertz::MHz(8),
clocks.io_clocks(),
embedded_hal::spi::MODE_0,
spi::SlaveSelectConfig::AutoCsManualStart,
),
)
.expect("Failed to initialize SPI");
let exclusive_device = ExclusiveDevice::new(spi, DummyPin::new_high(), NoDelay)
.expect("Failed to create exclusive SPI device");
let spi_if = SPIInterface::new(exclusive_device, dc_pin);
let mut ssd1306 = Ssd1306::new(spi_if, DisplaySize128x32, DisplayRotation::Rotate180);
oled_vdd_switch.set_low();
oled_vbat_switch.set_low();
Delay.delay_ms(100).await;
ssd1306.reset(&mut reset_pin, &mut embassy_time::Delay {});
let mut display = ssd1306.into_buffered_graphics_mode();
display.init().expect("display init error");
// Include the BMP file data.
let ferris_data = include_bytes!("../../assets/ferris-flat-happy-small.bmp");
let rust_logo_data = include_bytes!("../../assets/rust-logo-single-path.bmp");
// Parse the BMP file.
let bmp_rust = Bmp::from_slice(rust_logo_data).unwrap();
let bmp_ferris = Bmp::from_slice(ferris_data).unwrap();
// Draw the image with the top left corner at (10, 20) by wrapping it in
// an embedded-graphics `Image`.
Image::new(&bmp_rust, Point::new(0, 0))
.draw(&mut display)
.expect("image draw error");
Image::new(&bmp_ferris, Point::new(32, 0))
.draw(&mut display)
.expect("image draw error");
display.flush().expect("display flush error");
let mut ticker = Ticker::every(Duration::from_millis(50));
let mut ferris = FerrisMovement::new(32, Direction::Right, 32, 76);
loop {
Image::new(&bmp_ferris, Point::new(ferris.pos as i32, 0))
.draw(&mut display)
.expect("image draw error");
display.flush().expect("display flush error");
ferris.step();
ticker.next().await;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Right,
Left,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FerrisMovement {
pub pos: u16,
pub dir: Direction,
pub left_threshold: u16,
pub right_threshold: u16,
}
impl FerrisMovement {
/// Creates a new movement controller.
/// Assumes: left_threshold <= right_threshold and pos is within that range.
pub fn new(pos: u16, dir: Direction, left_threshold: u16, right_threshold: u16) -> Self {
Self {
pos,
dir,
left_threshold,
right_threshold,
}
}
/// Move one tick and "bounce" between thresholds.
pub fn step(&mut self) {
match self.dir {
Direction::Right => {
if self.pos >= self.right_threshold {
self.dir = Direction::Left; // flip at right boundary
} else {
self.pos += 1;
}
}
Direction::Left => {
if self.pos <= self.left_threshold {
self.dir = Direction::Right; // flip at left boundary
} else {
self.pos -= 1;
}
}
}
}
}
#[embassy_executor::task]
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
log_runner.run().await
}
#[embassy_executor::task]
pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output; 8]) {
let mut ticker = Ticker::every(Duration::from_millis(200));
loop {
mio_led.toggle().unwrap();
// Create a wave pattern for emio_leds
for led in emio_leds.iter_mut() {
led.toggle().unwrap();
ticker.next().await; // Wait for the next ticker for each toggle
}
ticker.next().await;
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
loop {}
}
@@ -1,8 +1,9 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use arbitrary_int::{traits::Integer as _, u2};
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
@@ -10,10 +11,13 @@ use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zedboard_bsp::qspi_spansion;
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart};
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc, prelude::*, qspi, uart,
};
use zynq7000_rt as _;
const DISPLAY_CLOCK_CONFIG: bool = false;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI example --\n\r";
const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombination {
vendor: qspi::QspiVendor::WinbondAndSpansion,
@@ -21,19 +25,16 @@ const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombin
two_devices: false,
};
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
const ERASE_PROGRAM_READ_TEST: bool = false;
const TEST_QSPI_BASE: u32 = 0x20000;
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
@@ -54,24 +55,20 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio(
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
if DISPLAY_CLOCK_CONFIG {
log::debug!("clock config: {:?}", clocks);
}
let qspi_clock_config =
qspi::ClockConfig::calculate_with_loopback(qspi::SrcSelIo::IoPll, &clocks, 100.MHz())
@@ -94,7 +91,14 @@ async fn main(_spawner: Spawner) -> ! {
let qspi_io_mode = qspi.into_io_mode(false);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(
qspi_io_mode,
qspi_spansion::Config {
set_quad_bit_if_necessary: true,
latency_config: Some(u2::ZERO),
clear_write_protection: true,
},
);
let rdid = spansion_qspi.read_rdid_extended();
info!(
@@ -107,27 +111,48 @@ async fn main(_spawner: Spawner) -> ! {
);
let cr1 = spansion_qspi.read_configuration_register();
info!("QSPI Configuration Register 1: {:?}", cr1);
let sr = spansion_qspi.read_status_register_1();
info!("QSPI Status Register: {:?}", sr);
let mut write_buf: [u8; u8::MAX as usize + 1] = [0x0; u8::MAX as usize + 1];
let mut write_buf: [u8; 100 * qspi_spansion::PAGE_SIZE] = [0x0; 100 * qspi_spansion::PAGE_SIZE];
for (idx, byte) in write_buf.iter_mut().enumerate() {
*byte = idx as u8;
*byte = (idx % u8::MAX as usize) as u8;
}
let mut read_buf = [0u8; 256];
let mut read_buf = [0u8; 100 * qspi_spansion::PAGE_SIZE];
if ERASE_PROGRAM_READ_TEST {
info!("performing erase, program, read test");
spansion_qspi
.erase_sector(0x10000)
.erase_sector(TEST_QSPI_BASE)
.expect("erasing sector failed");
spansion_qspi.read_page_fast_read(0x10000, &mut read_buf, true);
spansion_qspi.read_fast_read(TEST_QSPI_BASE, &mut read_buf, true);
for read in read_buf.iter() {
assert_eq!(*read, 0xFF);
}
read_buf.fill(0);
spansion_qspi.program_page(0x10000, &write_buf).unwrap();
spansion_qspi.read_page_fast_read(0x10000, &mut read_buf, true);
for (read, written) in read_buf.iter().zip(write_buf.iter()) {
assert_eq!(read, written);
let mut current_offset = 0_usize;
let mut current_qspi_offset = TEST_QSPI_BASE;
for (idx, chunk) in write_buf
.chunks(qspi_spansion::RECOMMENDED_PROGRAM_PAGE_SIZE)
.enumerate()
{
spansion_qspi
.program(current_qspi_offset, chunk)
.expect("programming failed");
spansion_qspi.read_fast_read(
current_qspi_offset,
&mut read_buf[current_offset..current_offset + chunk.len()],
true,
);
assert_eq!(
chunk,
&read_buf[current_offset..current_offset + chunk.len()],
"read and write missmatch at chunk index {}, data offset {}",
idx,
current_offset
);
current_offset += chunk.len();
current_qspi_offset += chunk.len() as u32;
}
info!("test successful");
}
@@ -137,7 +162,7 @@ async fn main(_spawner: Spawner) -> ! {
let guard = spansion_lqspi.read_guard();
unsafe {
core::ptr::copy_nonoverlapping(
(qspi::QSPI_START_ADDRESS + 0x10000) as *const u8,
(qspi::QSPI_START_ADDRESS + TEST_QSPI_BASE as usize) as *const u8,
read_buf.as_mut_ptr(),
256,
);
@@ -163,23 +188,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+258
View File
@@ -0,0 +1,258 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::error;
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::gpio::Input;
use zynq7000_hal::sd::SdClockConfig;
use zynq7000_hal::{BootMode, clocks, gpio, gtc, sd::SdCardUninit, uart};
use zynq7000_hal::{generic_interrupt_handler, prelude::*};
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard SDIO example --\n\r";
// These are off by default because they write to the SD card as well.
const LOW_LEVEL_TESTS: bool = false;
const SDMMC_RS_TESTS: bool = false;
#[derive(Debug, Clone, Copy)]
pub struct DummyTimeSource;
impl embedded_sdmmc::TimeSource for DummyTimeSource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
embedded_sdmmc::Timestamp::from_calendar(1970, 1, 1, 0, 0, 0).unwrap()
}
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let sdio_clock_config =
SdClockConfig::calculate_for_io_clock(clocks.io_clocks(), 100.MHz(), 10.MHz()).unwrap();
log::info!("SDIO clock config: {:?}", sdio_clock_config);
let sd_card_uninit = SdCardUninit::new_for_sdio_0(
periphs.sdio_0,
sdio_clock_config,
// On the zedboard, the bank has a 1.8 V voltage which is shifted up to 3.3 V by a
// level shifter.
zynq7000_hal::sd::IoType::LvCmos18,
gpio_pins.mio.mio40,
gpio_pins.mio.mio41,
(
gpio_pins.mio.mio42,
gpio_pins.mio.mio43,
gpio_pins.mio.mio44,
gpio_pins.mio.mio45,
),
)
.unwrap();
let card_detect = Input::new_for_mio(gpio_pins.mio.mio47).unwrap();
let write_protect = Input::new_for_mio(gpio_pins.mio.mio46).unwrap();
// The card detect being active low makes sense according to the Zedboard docs. Not sure
// about write-protect though.. It seems that write protect on means that the
// the pin is pulled high.
log::info!("Card detect state: {:?}", card_detect.is_low());
log::info!("Write protect state: {:?}", write_protect.is_high());
let capabilities = sd_card_uninit.ll().capabilities();
log::debug!("SDIO Capabilities: {:?}", capabilities);
let present_state = sd_card_uninit.ll().read_present_state();
log::debug!("SD present state: {:?}", present_state);
let boot_mode = BootMode::new_from_regs();
log::info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(200));
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let sd_result = sd_card_uninit.initialize();
let sd_card = match sd_result {
Ok(card) => {
log::info!("SD card info: {:?}", card.card_info());
card
}
Err(e) => {
panic!("SDIO init error: {e:?}");
}
};
let mut buf: [u8; 4096] = [0; 4096];
if LOW_LEVEL_TESTS {
log::info!("doing SD card low-level tests");
let mut cache_buf: [u8; 4096] = [0; 4096];
// cache the data, will be written back later..
sd_card
.read_multiple_blocks(&mut cache_buf, 0x1000)
.unwrap();
let mut write_data: [u8; 4096] = [0; 4096];
for chunk in write_data.chunks_mut(u8::MAX as usize) {
for (idx, byte) in chunk.iter_mut().enumerate() {
*byte = idx as u8;
}
}
sd_card.write_multiple_blocks(&write_data, 0x1000).unwrap();
sd_card.read_multiple_blocks(&mut buf, 0x1000).unwrap();
for chunk in buf.chunks(u8::MAX as usize) {
for (idx, byte) in chunk.iter().enumerate() {
assert_eq!(idx as u8, *byte);
}
}
sd_card.write_multiple_blocks(&cache_buf, 0x1000).unwrap();
log::info!("SD card low-level tests success");
}
buf.fill(0);
if SDMMC_RS_TESTS {
log::info!("doing SD card embedded-sdmmc-rs tests");
// Now let's look for volumes (also known as partitions) on our block device.
// To do this we need a Volume Manager. It will take ownership of the block device.
let volume_mgr = embedded_sdmmc::VolumeManager::new(sd_card, DummyTimeSource);
// Try and access Volume 0 (i.e. the first partition).
// The volume object holds information about the filesystem on that volume.
let volume0 = volume_mgr
.open_volume(embedded_sdmmc::VolumeIdx(0))
.unwrap();
// Open the root directory (mutably borrows from the volume).
let mut current_dir = volume0.open_root_dir().unwrap();
log::info!("iterating root directory");
current_dir
.iterate_dir(|entry| {
log::info!("{:?}", entry);
core::ops::ControlFlow::Continue(())
})
.unwrap();
let new_file = current_dir
.open_file_in_dir("__T.TXT", embedded_sdmmc::Mode::ReadWriteCreateOrTruncate)
.unwrap();
let string = "test string\n";
new_file.write(string.as_bytes()).unwrap();
new_file.close().unwrap();
let read_new = current_dir
.open_file_in_dir("__T.TXT", embedded_sdmmc::Mode::ReadOnly)
.unwrap();
assert_eq!(read_new.length(), string.len() as u32);
read_new.read(&mut buf).unwrap();
let buf_as_str = core::str::from_utf8(&buf[0..string.len()]).unwrap();
assert_eq!(buf_as_str, string);
read_new.close().unwrap();
current_dir.delete_entry_in_dir("__T.TXT").unwrap();
assert_eq!(
current_dir.find_directory_entry("__T.TXT").unwrap_err(),
embedded_sdmmc::Error::NotFound
);
if current_dir.find_directory_entry("_TDIR").is_ok() {
current_dir.delete_entry_in_dir("_TDIR").unwrap();
}
current_dir.make_dir_in_dir("_TDIR").unwrap();
current_dir.change_dir("_TDIR").unwrap();
current_dir.change_dir("..").unwrap();
current_dir.delete_entry_in_dir("_TDIR").unwrap();
current_dir.close().unwrap();
log::info!("SD card embedded-sdmmc-rs success");
}
loop {
mio_led.toggle().unwrap();
ticker.next().await; // Wait for the next cycle of the ticker
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
@@ -0,0 +1,587 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::{cell::RefCell, panic::PanicInfo, sync::atomic::AtomicU8};
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_io::Write;
use embedded_io_async::Read as _;
use log::info;
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000::spi::FifoWrite;
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
log::asynch::UartLoggerRunner,
spi::{self, SpiAsync},
uart::{self, TxAsync},
};
use zynq7000_rt as _;
#[derive(Debug, PartialEq, Eq)]
pub enum TestType {
Blocking,
Async,
}
const TEST_TYPE: TestType = TestType::Async;
const INIT_STRING: &str = "-- Zynq 7000 SPI slave example --\n\r";
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
Delay.delay_ms(1).await;
let (tx, _rx) = uart.split();
let tx_asynch = TxAsync::new(tx, true);
let logger_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_asynch)
.expect("Failed to initialize async logger");
spawner.spawn(logger_task(logger_runner).unwrap());
// This pin is used to select between a routing from SPI0 to OLED, or SPI0 to SPI1. Low selects
// the SPI0 to OLED interface.
let _spi_mux_pin =
gpio::Output::new_for_emio(gpio_pins.emio.take(15).unwrap(), gpio::PinState::High);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let emio_leds: [gpio::Output; 8] = [
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
];
spawner.spawn(blinky_task(mio_led, emio_leds).unwrap());
// Enable loopback.
spi::enable_spi0_to_spi1_loopback();
let mut spi_slave = spi::SpiSlave::new(
spi::SpiId::Spi1,
periphs.spi_1,
spi::SlaveConfig {
mode: spi::MODE_0,
enable_modefail: true,
},
);
zynq7000_hal::register_interrupt(spi_slave.interrupt_id(), spi_interrupt_handler);
let spi_master = spi::Spi::new_for_emio(
periphs.spi_0,
spi::Config::new(
// 10 MHz maximum rating of the sensor.
zynq7000::spi::BaudDivSel::By64,
// l3gd20::MODE,
embedded_hal::spi::MODE_0,
spi::SlaveSelectConfig::AutoCsManualStart,
),
)
.unwrap();
let cs_pin = spi::ChipSelectPin::new(spi_master.id(), spi::ChipSelect::Slave0);
// We pre-load the FIFO with a fixed, known sequence.
for i in 0..spi::FIFO_DEPTH / 2 {
spi_slave.write_tx_data(i as u8);
}
log::info!("SPI slave initialized, enabling interrupts and peripheral");
spi_slave.enable_interrupts(true);
spi_slave.enable();
static RX_DATA_PIPE: static_cell::ConstStaticCell<
embassy_sync::pipe::Pipe<CriticalSectionRawMutex, 256>,
> = static_cell::ConstStaticCell::new(embassy_sync::pipe::Pipe::new());
let pipe = RX_DATA_PIPE.take();
let (mut slave_rx_reader, writer) = pipe.split();
critical_section::with(|cs| {
RX_DATA_WRITER.borrow(cs).replace(Some(writer));
});
if TEST_TYPE == TestType::Blocking {
let mut spi_master =
embedded_hal_bus::spi::ExclusiveDevice::new(spi_master, cs_pin, embassy_time::Delay)
.expect("creating exlusive SPI master failed");
let current_rx_val = blocking_tests_small(&mut spi_master, &mut slave_rx_reader).await;
log::info!("blocking tests with small data blocks done");
Delay.delay_ms(10).await;
blocking_tests_large(&mut spi_master, current_rx_val, &mut slave_rx_reader).await;
log::info!("blocking tests with large data blocks done");
} else {
let spi_master = SpiAsync::new(spi_master);
let mut spi_master =
embedded_hal_bus::spi::ExclusiveDevice::new(spi_master, cs_pin, embassy_time::Delay)
.expect("creating exlusive SPI master failed");
let current_rx_val =
blocking_tests_small_async(&mut spi_master, &mut slave_rx_reader).await;
log::info!("async tests with small data blocks done");
Delay.delay_ms(10).await;
blocking_tests_large_async(&mut spi_master, current_rx_val, &mut slave_rx_reader).await;
log::info!("blocking tests with large data blocks done");
}
log::info!("SPI slave tests done");
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
ticker.next().await; // Wait for the next cycle of the ticker
}
}
pub async fn blocking_tests_small(
spi_master: &mut impl embedded_hal::spi::SpiDevice,
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
) -> u8 {
let mut tx_buf = [0; 64];
let mut buf = [0; 64];
// Write test.
// We should read back 0,1,2,3 here but the sent values are ignored.
spi_master.write(&[1, 2, 3, 4]).unwrap();
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
assert_eq!(
&buf[0..4],
&[1, 2, 3, 4],
"slave did not receive the expected data"
);
// Read test.
spi_master.read(&mut buf[0..4]).unwrap();
// Now we expect 4,5,6,7 sent by the SPI slave
assert_eq!(
&buf[0..4],
&[4, 5, 6, 7],
"slave did not send the expected data"
);
// Slave should receive dummy data
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
assert_eq!(
&buf[0..4],
&[0, 0, 0, 0],
"slave did not receive the expected data"
);
// Transfer test.
for (i, item) in tx_buf.iter_mut().enumerate().take(16) {
*item = (i * 2) as u8;
}
spi_master
.transfer(&mut buf[0..16], &tx_buf[0..16])
.unwrap();
for i in 8..24 {
assert_eq!(
buf[i - 8],
i as u8,
"slave did not receive the expected data"
);
}
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
for (i, item) in buf.iter().enumerate().take(16) {
assert_eq!(
*item,
(i * 2) as u8,
"slave did not send the expected data"
);
}
// Transfer in place test.
for (i, item) in buf.iter_mut().enumerate().take(16) {
*item = (i * 2) as u8;
}
spi_master.transfer_in_place(&mut buf[0..16]).unwrap();
for i in 24..40 {
assert_eq!(
buf[i - 24],
i as u8,
"slave did not receive the expected data"
);
}
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
for (i, item) in buf.iter().enumerate().take(16) {
assert_eq!(
*item,
(i * 2) as u8,
"slave did not send the expected data"
);
}
40
}
pub async fn blocking_tests_large(
spi_master: &mut impl embedded_hal::spi::SpiDevice,
mut current_rx_val: u8,
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
) -> u8 {
const BUF_LEN: usize = 164;
let mut buf = [0; BUF_LEN];
// Write test.
for (i, item) in buf.iter_mut().enumerate() {
*item = i as u8;
}
spi_master.write(&buf).unwrap();
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for (i, item) in buf.iter().enumerate() {
assert_eq!(*item, i as u8, "slave did not receive the expected data");
}
current_rx_val = current_rx_val.wrapping_add(buf.len() as u8);
// Read test.
spi_master.read(&mut buf).unwrap();
for item in &buf {
assert_eq!(
*item, current_rx_val,
"slave did not send the expected data"
);
current_rx_val = current_rx_val.wrapping_add(1);
}
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for item in &buf {
assert_eq!(*item, 0, "slave did not receive the expected data");
}
// Transfer test.
let mut tx_buf = [0; BUF_LEN];
for (i, item) in tx_buf.iter_mut().enumerate() {
*item = i as u8;
}
spi_master.transfer(&mut buf, &tx_buf).unwrap();
for item in &buf {
assert_eq!(
*item, current_rx_val,
"slave did not send the expected data"
);
current_rx_val = current_rx_val.wrapping_add(1);
}
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for (i, item) in buf.iter().enumerate() {
assert_eq!(*item, i as u8, "slave did not receive the expected data");
}
// Transfer in place test.
for (i, item) in buf.iter_mut().enumerate() {
*item = i as u8;
}
spi_master.transfer_in_place(&mut buf).unwrap();
for item in &buf {
assert_eq!(
*item, current_rx_val,
"slave did not send the expected data"
);
current_rx_val = current_rx_val.wrapping_add(1);
}
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for (i, item) in buf.iter().enumerate() {
assert_eq!(*item, i as u8, "slave did not receive the expected data");
}
current_rx_val
}
pub async fn blocking_tests_small_async(
spi_master: &mut impl embedded_hal_async::spi::SpiDevice,
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
) -> u8 {
let mut tx_buf = [0; 64];
let mut buf = [0; 64];
// Write test.
// We should read back 0,1,2,3 here but the sent values are ignored.
spi_master.write(&[1, 2, 3, 4]).await.unwrap();
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
assert_eq!(
&buf[0..4],
&[1, 2, 3, 4],
"slave did not receive the expected data"
);
// Read test.
spi_master.read(&mut buf[0..4]).await.unwrap();
// Now we expect 4,5,6,7 sent by the SPI slave
assert_eq!(
&buf[0..4],
&[4, 5, 6, 7],
"slave did not send the expected data"
);
// Slave should receive dummy data
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
assert_eq!(
&buf[0..4],
&[0, 0, 0, 0],
"slave did not receive the expected data"
);
// Transfer test.
for (i, item) in tx_buf.iter_mut().enumerate().take(16) {
*item = (i * 2) as u8;
}
spi_master
.transfer(&mut buf[0..16], &tx_buf[0..16])
.await
.unwrap();
for i in 8..24 {
assert_eq!(
buf[i - 8],
i as u8,
"slave did not receive the expected data"
);
}
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
for (i, item) in buf.iter().enumerate().take(16) {
assert_eq!(
*item,
(i * 2) as u8,
"slave did not send the expected data"
);
}
// Transfer in place test.
for (i, item) in buf.iter_mut().enumerate().take(16) {
*item = (i * 2) as u8;
}
spi_master.transfer_in_place(&mut buf[0..16]).await.unwrap();
for i in 24..40 {
assert_eq!(
buf[i - 24],
i as u8,
"slave did not receive the expected data"
);
}
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
for (i, item) in buf.iter().enumerate().take(16) {
assert_eq!(
*item,
(i * 2) as u8,
"slave did not send the expected data"
);
}
40
}
pub async fn blocking_tests_large_async(
spi_master: &mut impl embedded_hal_async::spi::SpiDevice,
mut current_rx_val: u8,
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
) -> u8 {
const BUF_LEN: usize = 164;
let mut buf = [0; BUF_LEN];
// Write test.
for (i, item) in buf.iter_mut().enumerate() {
*item = i as u8;
}
spi_master.write(&buf).await.unwrap();
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for (i, &item) in buf.iter().enumerate() {
assert_eq!(item, i as u8, "slave did not receive the expected data");
}
current_rx_val = current_rx_val.wrapping_add(buf.len() as u8);
// Read test.
spi_master.read(&mut buf).await.unwrap();
for &item in &buf {
assert_eq!(item, current_rx_val, "slave did not send the expected data");
current_rx_val = current_rx_val.wrapping_add(1);
}
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for &item in &buf {
assert_eq!(item, 0, "slave did not receive the expected data");
}
// Transfer test.
let mut tx_buf = [0; BUF_LEN];
for (i, item) in tx_buf.iter_mut().enumerate() {
*item = i as u8;
}
spi_master.transfer(&mut buf, &tx_buf).await.unwrap();
for &item in &buf {
assert_eq!(item, current_rx_val, "slave did not send the expected data");
current_rx_val = current_rx_val.wrapping_add(1);
}
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for (i, &item) in buf.iter().enumerate() {
assert_eq!(item, i as u8, "slave did not receive the expected data");
}
// Transfer in place test.
for (i, item) in buf.iter_mut().enumerate() {
*item = i as u8;
}
spi_master.transfer_in_place(&mut buf).await.unwrap();
for &item in &buf {
assert_eq!(item, current_rx_val, "slave did not send the expected data");
current_rx_val = current_rx_val.wrapping_add(1);
}
slave_rx_reader
.read_exact(&mut buf[0..BUF_LEN])
.await
.unwrap();
for (i, &item) in buf.iter().enumerate() {
assert_eq!(item, i as u8, "slave did not receive the expected data");
}
current_rx_val
}
#[embassy_executor::task]
pub async fn logger_task(mut logger_task: UartLoggerRunner) {
logger_task.run().await;
}
static RX_DATA_WRITER: critical_section::Mutex<
RefCell<Option<embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 256>>>,
> = critical_section::Mutex::new(RefCell::new(None));
unsafe fn spi_interrupt_handler() {
static CURRENT_TX_VAL: AtomicU8 = AtomicU8::new((spi::FIFO_DEPTH / 2) as u8);
let mut buf = [0; 64];
let mut current_val = CURRENT_TX_VAL.load(core::sync::atomic::Ordering::Relaxed);
let mut spi = unsafe { spi::SpiLowLevel::steal(spi::SpiId::Spi1) };
let status = spi.read_interrupt_status();
spi.write_interrupt_status(status);
let mut index = 0;
while spi.read_interrupt_status().rx_not_empty() && index < buf.len() {
buf[index] = spi.read_rx_data().value();
index += 1;
}
while !spi.read_interrupt_status().tx_full() {
spi.write_tx_data(FifoWrite::new(current_val));
current_val = current_val.wrapping_add(1);
}
CURRENT_TX_VAL.store(current_val, core::sync::atomic::Ordering::Relaxed);
critical_section::with(|cs| {
let opt_writer = RX_DATA_WRITER.borrow(cs).borrow();
if let Some(writer) = opt_writer.as_ref() {
// In a real application, you would read the received data from the SPI peripheral here.
// For this example, we just write a dummy value to the pipe to demonstrate the concept.
let _ = writer.try_write(&buf[0..index]);
}
});
}
#[embassy_executor::task]
pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output; 8]) {
let mut ticker = Ticker::every(Duration::from_millis(200));
loop {
mio_led.toggle().unwrap();
// Create a wave pattern for emio_leds
for led in emio_leds.iter_mut() {
led.toggle().unwrap();
ticker.next().await; // Wait for the next ticker for each toggle
}
ticker.next().await;
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
loop {}
}
@@ -1,22 +1,20 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use axi_uart16550::AxiUart16550;
use axi_uartlite::AxiUartlite;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use fugit::RateExtU32;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler, gic,
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
@@ -31,12 +29,9 @@ const INIT_STRING: &str = "-- Zynq 7000 Zedboard blocking UART example --\n\r";
const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000;
const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
@@ -99,7 +94,6 @@ impl UartMultiplexer {
}
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -109,7 +103,7 @@ async fn main(_spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = gic::Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -126,7 +120,7 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut log_uart = Uart::new_with_mio(
let mut log_uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
@@ -135,13 +129,7 @@ async fn main(_spawner: Spawner) -> ! {
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: Co-operative multi-tasking is used.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
log_uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(log_uart, log::LevelFilter::Trace, false);
// UART0 routed through EMIO to PL pins.
let mut uart_0 =
@@ -151,7 +139,8 @@ async fn main(_spawner: Spawner) -> ! {
// TODO: Can we determine/read the clock frequency to the FPGAs as well?
let (clk_config, error) =
axi_uart16550::ClkConfig::new_autocalc_with_error(100.MHz(), 115200).unwrap();
axi_uart16550::ClockConfig::new_autocalc_with_error(fugit_03::HertzU32::MHz(100), 115200)
.unwrap();
assert!(error < 0.02);
let mut uart_16550 = unsafe {
AxiUart16550::new(
@@ -219,23 +208,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -25,11 +25,11 @@
#![no_main]
extern crate alloc;
use aarch32_cpu::asm::nop;
use alloc::format;
use axi_uart16550::AxiUart16550;
use axi_uartlite::AxiUartlite;
use core::{cell::RefCell, panic::PanicInfo};
use cortex_ar::asm::nop;
use critical_section::Mutex;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
@@ -37,19 +37,20 @@ use embedded_alloc::LlffHeap as Heap;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write as _;
use heapless::spsc::Queue;
use log::{error, info, warn};
use log::{info, warn};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler,
gic::{Configurator, Interrupt},
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
time::Hertz,
uart::{ClockConfig, Config, Uart},
uart::{self, ClockConfig, Config, Uart},
};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum UartMode {
Uart0ToUartlite,
Uart0ToUart16550,
@@ -74,6 +75,14 @@ const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
pub const UARTLITE_PL_INT_ID: usize = 0;
pub const UART16550_PL_INT_ID: usize = 1;
pub const UART_SPEED: u32 = 115_200;
// Other common baud rates to test with:
// pub const UART_SPEED: u32 = 9600;
// pub const UART_SPEED: u32 = 230_400;
// pub const UART_SPEED: u32 = 912_600;
const RB_SIZE: usize = 512;
// These queues are used to send all data received in the UART interrupt handlers to the main
@@ -88,19 +97,16 @@ static QUEUE_UART16550: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, R
// Those are all used by the interrupt handler, so we have to do the Mutex dance.
static RX_UART_0: Mutex<RefCell<Option<zynq7000_hal::uart::Rx>>> = Mutex::new(RefCell::new(None));
static UART_0_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
static UART_0_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8>>>> =
Mutex::new(RefCell::new(None));
static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8>>>> =
Mutex::new(RefCell::new(None));
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8>>>> =
Mutex::new(RefCell::new(None));
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
@@ -164,7 +170,6 @@ impl UartMultiplexer {
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c);
@@ -175,7 +180,7 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
// AXI UARTLite documentation mentions that a rising-edge sensitive interrupt is generated,
@@ -197,7 +202,7 @@ async fn main(spawner: Spawner) -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut log_uart = Uart::new_with_mio(
let mut log_uart = Uart::new_with_mio_for_uart_1(
dp.uart_1,
Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
@@ -214,14 +219,21 @@ async fn main(spawner: Spawner) -> ! {
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
}
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
log_uart,
log::LevelFilter::Trace,
false,
)
};
// Register the interrupts for the PL.
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Pl0),
on_interrupt_axi_uartlite,
);
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Pl1),
on_interrupt_axi_16550,
);
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Uart0),
on_interrupt_uart_0,
);
zynq7000_hal::log::uart_blocking::init_with_busy_flag(log_uart, log::LevelFilter::Trace, false);
// Set up UART multiplexing before creating and configuring the UARTs.
let mut uart_mux = UartMultiplexer::new([
@@ -229,24 +241,42 @@ async fn main(spawner: Spawner) -> ! {
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
]);
let mut uart_speed = UART_SPEED;
match UART_MODE {
UartMode::Uart0ToUartlite => uart_mux.select(UartSel::Uart0ToUartlite),
UartMode::Uart0ToUart16550 => uart_mux.select(UartSel::Uart0ToUart16550),
UartMode::UartliteToUart16550 => uart_mux.select(UartSel::UartliteToUart16550),
}
if (UART_MODE == UartMode::Uart0ToUartlite || UART_MODE == UartMode::UartliteToUart16550)
&& uart_speed != 115200
{
log::warn!("UARTLITE speed is not configurable. Hardcoding UART speed to 115200");
uart_speed = 115200;
}
let uart0_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), uart_speed)
.unwrap()
.0;
// UART0 routed through EMIO to PL pins.
let uart_0 =
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart_clk_config)).unwrap();
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart0_clk_config)).unwrap();
// Safety: Valid address of AXI UARTLITE.
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
// We need to call this before splitting the structure, because the interrupt signal is
// used for both TX and RX, so the API is only exposed for this structure.
uartlite.enable_interrupt();
let (clk_config, error) =
axi_uart16550::ClkConfig::new_autocalc_with_error(clocks.pl_clocks()[0], 115200).unwrap();
assert!(error < 0.02);
let (clk_config, error) = axi_uart16550::ClockConfig::new_autocalc_with_error(
fugit_03::HertzU32::from_raw(clocks.pl_clocks()[0].to_raw()),
uart_speed,
)
.unwrap();
if error > 0.02 {
log::warn!(
"Calculated clock config for AXI UART16550 has error of {} %, which is higher than 2%. This may lead to incorrect baud rate. Consider changing the input clock or the target baud rate.",
(error * 100.0)
);
}
let _uart_16550 = unsafe {
AxiUart16550::new(
AXI_UAR16550_BASE_ADDR,
@@ -276,7 +306,7 @@ async fn main(spawner: Spawner) -> ! {
let (uartlite_prod, mut uartlite_cons) = QUEUE_UARTLITE.take().split();
let (uart16550_prod, mut uart16550_cons) = QUEUE_UART16550.take().split();
// Use our helper function to start RX handling.
uart_0_rx.start_interrupt_driven_reception();
uart_0_rx.start_interrupt_driven_reception(0xFF);
// Use our helper function to start RX handling.
uart_16550_rx.start_interrupt_driven_reception();
critical_section::with(|cs| {
@@ -288,20 +318,20 @@ async fn main(spawner: Spawner) -> ! {
.replace(uart16550_prod);
RX_UART_0.borrow(cs).borrow_mut().replace(uart_0_rx);
});
spawner.spawn(led_task(mio_led, emio_leds)).unwrap();
spawner.spawn(led_task(mio_led, emio_leds).unwrap());
match UART_MODE {
UartMode::Uart0ToUartlite => {
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
spawner.spawn(uartlite_task(uartlite_tx).unwrap());
spawner.spawn(uart_0_task(uart_0_tx).unwrap());
}
UartMode::Uart0ToUart16550 => {
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
spawner.spawn(uart_0_task(uart_0_tx).unwrap());
spawner.spawn(uart_16550_task(uart_16550_tx).unwrap());
}
UartMode::UartliteToUart16550 => {
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
spawner.spawn(uartlite_task(uartlite_tx).unwrap());
spawner.spawn(uart_16550_task(uart_16550_tx).unwrap());
}
}
let mut read_buf: [u8; RB_SIZE] = [0; RB_SIZE];
@@ -391,7 +421,8 @@ async fn uartlite_task(uartlite: axi_uartlite::Tx) {
#[embassy_executor::task]
async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) {
let mut ticker = Ticker::every(Duration::from_millis(1000));
let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx);
let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx, false);
let str0 = build_print_string("UART0:", "Hello World");
let str1 = build_print_string(
"UART0:",
@@ -400,7 +431,7 @@ async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) {
let mut idx = 0;
let print_strs = [str0.as_bytes(), str1.as_bytes()];
loop {
tx_async.write(print_strs[idx]).await;
tx_async.write(print_strs[idx]).unwrap().await;
idx += 1;
if idx == 2 {
idx = 0;
@@ -433,36 +464,12 @@ async fn uart_16550_task(uart_tx: axi_uart16550::Tx) {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => match spi_interrupt {
zynq7000_hal::gic::SpiInterrupt::Pl0 => {
on_interrupt_axi_uartlite();
}
zynq7000_hal::gic::SpiInterrupt::Pl1 => {
on_interrupt_axi_16550();
}
zynq7000_hal::gic::SpiInterrupt::Uart0 => {
on_interrupt_uart_0();
}
_ => (),
},
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
fn on_interrupt_axi_uartlite() {
@@ -492,19 +499,19 @@ fn on_interrupt_axi_16550() {
let iir = rx.read_iir();
if let Ok(int_id) = iir.int_id() {
match int_id {
axi_uart16550::registers::IntId2::ReceiverLineStatus => {
axi_uart16550::registers::InterruptId2::ReceiverLineStatus => {
let errors = rx.on_interrupt_receiver_line_status(iir);
warn!("Receiver line status error: {errors:?}");
}
axi_uart16550::registers::IntId2::RxDataAvailable
| axi_uart16550::registers::IntId2::CharTimeout => {
axi_uart16550::registers::InterruptId2::RxDataAvailable
| axi_uart16550::registers::InterruptId2::CharTimeout => {
read_bytes = rx.on_interrupt_data_available_or_char_timeout(int_id, &mut buf);
}
axi_uart16550::registers::IntId2::ThrEmpty => {
axi_uart16550::registers::InterruptId2::ThrEmpty => {
let mut tx = unsafe { axi_uart16550::Tx::steal(AXI_UAR16550_BASE_ADDR as usize) };
axi_uart16550::tx_async::on_interrupt_tx(&mut tx, 0);
}
axi_uart16550::registers::IntId2::ModemStatus => (),
axi_uart16550::registers::InterruptId2::ModemStatus => (),
}
}
// Send received RX data to main task.
@@ -531,8 +538,9 @@ fn on_interrupt_uart_0() {
.on_interrupt(&mut buf, true)
.read_bytes();
});
// Safety: This function is only called once inside the interrupt handler.
// Handle TX next: Handle pending asynchronous TX operations.
zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0);
unsafe { zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0) };
// Send received RX data to main task.
if read_bytes > 0 {
critical_section::with(|cs| {
@@ -569,6 +577,7 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
loop {}
}
@@ -1,31 +1,27 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, uart};
use zynq7000_hal::{BootMode, clocks, generic_interrupt_handler, gpio, gtc, uart};
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r";
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
@@ -46,21 +42,15 @@ async fn main(_spawner: Spawner) -> ! {
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio(
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -92,23 +82,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+1 -1
View File
@@ -5,4 +5,4 @@ break main
load
continue
# continue
+29
View File
@@ -0,0 +1,29 @@
Change Log
=======
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
## Fixed
- QSPI robustness fixes. Read, fast-read and write operations are now chunked according to the 252
byte limit specified in the TRM.
## Added
- QSPI constructor can now optionally clear block protection and set latency configuration.
## Changed
- Alignment rules of Spansion QSPI page program now only require 4 byte aligned size.
# [v0.1.0]
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zedboard-bsp-v0.1.0...HEAD
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zedboard-bsp-v0.1.0
@@ -10,9 +10,14 @@ keywords = ["no-std", "zedboard", "bare-metal", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
zynq7000 = { path = "../zynq7000", version = "0.1" }
zynq7000 = { path = "../zynq7000", version = "0.4" }
zynq7000-hal = { path = "../zynq7000-hal", version = "0.1" }
bitbybit = "1.4"
bitbybit = "2"
log = "0.4"
arbitrary-int = "2"
num_enum = { version = "0.7", default-features = false }
thiserror = { version = "2", default-features = false }
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
rustdoc-args = ["--generate-link-to-definition"]
@@ -1,7 +1,7 @@
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program."]
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract) program."]
#![doc = r""]
#![doc = r"This configuration file contains static DDR configuration parameters extracted from the"]
#![doc = r"AMD ps7init.tcl file"]
#![doc = r"AMD ps7init.tcl file. It was generated for the MT41K128M16JT-125 DDR chip."]
use zynq7000::ddrc::regs;
use zynq7000_hal::ddr::DdrcConfigSet;
pub const DDRC_CONFIG_ZEDBOARD: DdrcConfigSet = DdrcConfigSet {
@@ -34,7 +34,7 @@ pub const DDRC_CONFIG_ZEDBOARD: DdrcConfigSet = DdrcConfigSet {
ctrl_reg5: regs::CtrlReg5::new_with_raw_value(0x00466111),
ctrl_reg6: regs::CtrlReg6::new_with_raw_value(0x00032222),
che_t_zq: regs::CheTZq::new_with_raw_value(0x10200802),
che_t_zq_short_interval_reg: regs::CheTZqShortInterval::new_with_raw_value(0x10200802),
che_t_zq_short_interval_reg: regs::CheTZqShortInterval::new_with_raw_value(0x0690cb73),
deep_powerdown: regs::DeepPowerdown::new_with_raw_value(0x000001fe),
reg_2c: regs::Reg2c::new_with_raw_value(0x1cffffff),
reg_2d: regs::Reg2d::new_with_raw_value(0x00000200),
@@ -1,10 +1,11 @@
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program."]
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract) program."]
#![doc = r""]
#![doc = r"This configuration file contains static DDRIOB configuration parameters extracted from the"]
#![doc = r"AMD ps7init.tcl file"]
#![doc = r"AMD ps7init.tcl file. It was generated for the MT41K128M16JT-125 DDR chip."]
use zynq7000::ddrc::regs;
use zynq7000_hal::ddr::DdriobConfigSet;
pub const DDRIOB_CONFIG_SET_ZEDBOARD: DdriobConfigSet = DdriobConfigSet {
ddr_control: zynq7000::slcr::ddriob::DdrControl::new_with_raw_value(0x00000260),
addr0: regs::DdriobConfig::new_with_raw_value(0x00000600),
addr1: regs::DdriobConfig::new_with_raw_value(0x00000600),
data0: regs::DdriobConfig::new_with_raw_value(0x00000672),
@@ -2,10 +2,17 @@ use core::cell::RefCell;
use arbitrary_int::{prelude::*, u24};
use zynq7000_hal::qspi::{
FIFO_DEPTH, LinearQspiConfig, QspiIoMode, QspiIoTransferGuard, QspiLinearAddressing,
FIFO_DEPTH, LinearQspiConfig, MAX_BYTES_PER_TRANSFER_IO_MODE, QspiIoMode, QspiLinearAddressing,
QspiLinearReadGuard,
};
/// 4 bytes are reserved for command byte and address. Rounded down at a 16 byte boundary,
/// recommended by flash memory datasheet.
pub const MAX_DATA_BYTES_PER_WRITE: usize = 240;
/// Probably the most performant chunk/program size to program the chip without crossing page
/// boundaries without exceeding the FIFO size.
pub const RECOMMENDED_PROGRAM_PAGE_SIZE: usize = 128;
pub const QSPI_DEV_COMBINATION_REV_F: zynq7000_hal::qspi::QspiDeviceCombination =
zynq7000_hal::qspi::QspiDeviceCombination {
vendor: zynq7000_hal::qspi::QspiVendor::WinbondAndSpansion,
@@ -13,7 +20,8 @@ pub const QSPI_DEV_COMBINATION_REV_F: zynq7000_hal::qspi::QspiDeviceCombination
two_devices: false,
};
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RegisterId {
/// WRR
WriteRegisters = 0x01,
@@ -64,7 +72,8 @@ pub enum SectorArchictecture {
Hybrid = 0x01,
}
pub const PAGE_SIZE: usize = 256;
pub const PAGE_SIZE: usize = 0x100;
pub const SECTOR_SIZE: usize = 0x10000;
#[derive(Debug, Clone, Copy)]
pub struct BaseDeviceId {
@@ -159,8 +168,7 @@ impl ExtendedDeviceId {
}
}
#[bitbybit::bitfield(u8)]
#[derive(Debug)]
#[bitbybit::bitfield(u8, debug, forbid_overlaps)]
pub struct StatusRegister1 {
#[bit(7, rw)]
status_register_write_disable: bool,
@@ -168,25 +176,18 @@ pub struct StatusRegister1 {
programming_error: bool,
#[bit(5, r)]
erase_error: bool,
#[bit(4, r)]
bp_2: bool,
#[bit(3, r)]
bp_1: bool,
#[bit(2, r)]
bp_0: bool,
#[bits(2..=4, rw)]
block_protection: u3,
#[bit(1, r)]
write_enable_latch: bool,
#[bit(0, r)]
write_in_progress: bool,
}
#[bitbybit::bitfield(u8)]
#[derive(Debug)]
#[bitbybit::bitfield(u8, debug, forbid_overlaps)]
pub struct ConfigRegister1 {
#[bit(7, rw)]
latency_code_1: bool,
#[bit(6, rw)]
latency_code_0: bool,
#[bits(6..=7, rw)]
latency_code: u2,
/// This is an OTP bit. It can not be set back to 0 once it has been set to 1!
#[bit(5, rw)]
tbprot: bool,
@@ -223,27 +224,60 @@ pub enum ProgramPageError {
ProgrammingErrorBitSet,
#[error("address error: {0}")]
Addr(#[from] AddrError),
#[error("data is larger than page size {PAGE_SIZE}")]
DataLargerThanPage,
#[error("program data is larger than page size {PAGE_SIZE}")]
DataTooLarge,
#[error("program data is not aligned to 4 bytes")]
NotAligned,
#[error("program data crosses page boundary")]
CrossesPageBoundary,
}
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
pub struct Config {
pub set_quad_bit_if_necessary: bool,
pub latency_config: Option<u2>,
pub clear_write_protection: bool,
}
impl Config {
pub fn sr_or_cr_update_possibly_required(&self) -> bool {
self.set_quad_bit_if_necessary
|| self.latency_config.is_some()
|| self.clear_write_protection
}
}
pub struct QspiSpansionS25Fl256SIoMode(RefCell<QspiIoMode>);
impl QspiSpansionS25Fl256SIoMode {
pub fn new(qspi: QspiIoMode, set_quad_bit_if_necessary: bool) -> Self {
pub fn new(qspi: QspiIoMode, config: Config) -> Self {
let mut spansion_qspi = QspiSpansionS25Fl256SIoMode(RefCell::new(qspi));
if set_quad_bit_if_necessary {
spansion_qspi.clear_status();
let mut write_required = false;
if config.sr_or_cr_update_possibly_required() {
let mut cr1 = spansion_qspi.read_configuration_register();
if cr1.quad() {
// Quad bit is already set.
return spansion_qspi;
if config.set_quad_bit_if_necessary && !cr1.quad() {
cr1.set_quad(true);
write_required = true;
}
if let Some(latency_config) = config.latency_config
&& cr1.latency_code() != latency_config
{
cr1.set_latency_code(latency_config);
write_required = true;
}
cr1.set_quad(true);
// Preserve the status register by reading it first.
let sr1 = spansion_qspi.read_status_register_1();
// Safety: Only the QUAD bit was set while all other bits are preserved.
unsafe {
spansion_qspi.write_status_and_config_register(sr1, cr1);
let mut sr1 = spansion_qspi.read_status_register_1();
if config.clear_write_protection && sr1.block_protection() != u3::ZERO {
sr1.set_status_register_write_disable(false);
sr1.set_block_protection(u3::ZERO);
write_required = true;
}
if write_required {
// Safety: Only the QUAD bit was set while all other bits are preserved.
unsafe {
spansion_qspi.write_status_and_config_register(sr1, cr1);
}
}
}
spansion_qspi
@@ -257,6 +291,16 @@ impl QspiSpansionS25Fl256SIoMode {
QspiSpansionS25Fl256SLinearMode(qspi)
}
pub fn set_write_protection(&mut self, write_protection: u3) {
unsafe {
self.modify_status_and_config_register(|mut sr, cr| {
sr.set_status_register_write_disable(false);
sr.set_block_protection(write_protection);
(sr, cr)
});
}
}
pub fn write_enable(&mut self) {
let qspi = self.0.get_mut();
let mut transfer = qspi.transfer_guard();
@@ -301,7 +345,38 @@ impl QspiSpansionS25Fl256SIoMode {
/// # Safety
///
/// Misuse of this API does not lead to undefined behavior. However, it writes the
/// configuration register, which as OTP bits. Changing these bits from 0 to 1 is an
/// configuration register, which is OTP bits. Changing these bits from 0 to 1 is an
/// irreversible operation.
pub unsafe fn modify_status_and_config_register(
&mut self,
f: impl FnOnce(StatusRegister1, ConfigRegister1) -> (StatusRegister1, ConfigRegister1),
) {
self.write_enable();
let mut qspi = self.0.borrow_mut();
let mut transfer = qspi.transfer_guard();
let sr1 = self.read_status_register_1();
let cr1 = self.read_configuration_register();
let (sr1, cr1) = f(sr1, cr1);
transfer.write_word_txd_11(u32::from_ne_bytes([
RegisterId::WriteRegisters as u8,
sr1.raw_value(),
cr1.raw_value(),
0x00,
]));
transfer.start();
while !transfer.read_status().rx_above_threshold() {}
transfer.read_rx_data();
}
/// Write a new value for the status register. It is strongly recommended to read both
/// the status and config register first and preserve all unchanged bits.
///
/// This API must be used if the QUAD bit (CR1\[1\]) is set.
///
/// # Safety
///
/// Misuse of this API does not lead to undefined behavior. However, it writes the
/// configuration register, which is OTP bits. Changing these bits from 0 to 1 is an
/// irreversible operation.
pub unsafe fn write_status_and_config_register(
&mut self,
@@ -388,10 +463,10 @@ impl QspiSpansionS25Fl256SIoMode {
/// This function will block until the operation has completed.
pub fn erase_sector(&mut self, addr: u32) -> Result<(), EraseError> {
if addr + 0x10000 > u24::MAX.as_u32() {
if addr + SECTOR_SIZE as u32 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
if !addr.is_multiple_of(0x10000) {
if !addr.is_multiple_of(SECTOR_SIZE as u32) {
return Err(AddrError::Alignment.into());
}
self.write_enable();
@@ -429,19 +504,35 @@ impl QspiSpansionS25Fl256SIoMode {
}
}
/// This function also takes care of enabling writes before programming the page.
/// This function will block until the operation has completed.
///
/// The data length max not exceed the page size [PAGE_SIZE].
pub fn program_page(&mut self, addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
pub fn write_pages(&mut self, mut addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
if addr + data.len() as u32 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
if !addr.is_multiple_of(0x100) {
return Err(AddrError::Alignment.into());
for chunk in data.chunks(RECOMMENDED_PROGRAM_PAGE_SIZE) {
self.program(addr, chunk)?;
addr += chunk.len() as u32;
}
if data.len() > PAGE_SIZE {
return Err(ProgramPageError::DataLargerThanPage);
Ok(())
}
/// This function also takes care of enabling writes before programming the page.
/// This function will block until the operation has completed.
///
/// The data length may not exceed [MAX_DATA_BYTES_PER_WRITE]. Furthermore, the data needs
/// to be aligned to 4 bytes and the programming operation is not allowed to cross a page.
/// boundary. It is recommended to program in 128 byte chunks.
pub fn program(&mut self, addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
if addr + data.len() as u32 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
if data.len() > MAX_DATA_BYTES_PER_WRITE {
return Err(ProgramPageError::DataTooLarge);
}
if !data.len().is_multiple_of(4) {
return Err(ProgramPageError::NotAligned);
}
if (addr as usize % PAGE_SIZE) + data.len() > PAGE_SIZE {
return Err(ProgramPageError::CrossesPageBoundary);
}
self.write_enable();
let qspi = self.0.get_mut();
@@ -455,7 +546,8 @@ impl QspiSpansionS25Fl256SIoMode {
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index: u32 = 0;
let mut current_byte_index = 0;
let fifo_writes = data.len().div_ceil(4);
// Full four byte writes.
let fifo_writes = data.len() / 4;
// Fill the FIFO until it is full.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(u32::from_ne_bytes(
@@ -465,52 +557,14 @@ impl QspiSpansionS25Fl256SIoMode {
));
current_byte_index += 4;
}
transfer.start();
let mut wait_for_tx_slot = |transfer: &mut QspiIoTransferGuard| loop {
let status = transfer.read_status();
if status.rx_above_threshold() {
transfer.read_rx_data();
read_index = read_index.wrapping_add(4);
}
if !status.tx_full() {
break;
}
};
while current_byte_index < data.len() {
// Immediately fill the FIFO again with the remaining 8 bytes.
wait_for_tx_slot(&mut transfer);
let word = match core::cmp::min(4, data.len() - current_byte_index) {
1 => {
let mut bytes = [0; 4];
bytes[0] = data[current_byte_index];
u32::from_ne_bytes(bytes)
}
2 => {
let mut bytes = [0; 4];
bytes[0..2].copy_from_slice(&data[current_byte_index..current_byte_index + 2]);
u32::from_ne_bytes(bytes)
}
3 => {
let mut bytes = [0; 4];
bytes[0..3].copy_from_slice(&data[current_byte_index..current_byte_index + 3]);
u32::from_ne_bytes(bytes)
}
4 => u32::from_ne_bytes(
data[current_byte_index..current_byte_index + 4]
.try_into()
.unwrap(),
),
_ => unreachable!(),
};
transfer.write_word_txd_00(word);
current_byte_index += 4;
}
// Wait until the transfer is done by waiting until all RX bytes have been received.
while read_index < data.len() as u32 {
if transfer.read_status().rx_above_threshold() {
// Double read to avoid RX underflows as specified in TRM.
let status_read = transfer.read_status();
if status_read.rx_above_threshold() && transfer.read_status().rx_above_threshold() {
transfer.read_rx_data();
read_index = read_index.wrapping_add(4);
}
@@ -533,84 +587,134 @@ impl QspiSpansionS25Fl256SIoMode {
}
}
pub fn read_page_fast_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool) {
fn generic_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool, fast_read: bool) {
let mut offset = 0;
let reg_id = if fast_read {
RegisterId::FastRead
} else {
RegisterId::Read
};
let mut qspi = self.0.borrow_mut();
let mut transfer = qspi.transfer_guard();
let raw_word: [u8; 4] = [
RegisterId::FastRead as u8,
((addr >> 16) & 0xff) as u8,
((addr >> 8) & 0xff) as u8,
(addr & 0xff) as u8,
];
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index = 0;
let mut written_words = 0;
let mut bytes_to_write = buf.len();
let mut max_chunk_size = MAX_BYTES_PER_TRANSFER_IO_MODE - 4;
if dummy_byte {
bytes_to_write += 1;
}
let fifo_writes = bytes_to_write.div_ceil(4);
// Fill the FIFO until it is full or all 0 bytes have been written.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(0);
written_words += 1;
max_chunk_size -= 1;
}
transfer.start();
let mut reply_word_index = 0;
while offset < buf.len() {
// Calculate the size of the current chunk (max 248 bytes)
let chunk_size = core::cmp::min(max_chunk_size, buf.len() - offset);
let current_addr = addr + offset as u32;
while read_index < buf.len() {
if transfer.read_status().rx_above_threshold() {
let reply = transfer.read_rx_data();
if reply_word_index == 0 {
reply_word_index += 1;
continue;
// Create a mutable slice for the current chunk
let chunk_slice = &mut buf[offset..offset + chunk_size];
// This ensures the hardware transaction (Chip Select, etc.) restarts for each chunk.
{
let mut transfer = qspi.transfer_guard();
let raw_word: [u8; 4] = [
reg_id as u8,
((current_addr >> 16) & 0xff) as u8,
((current_addr >> 8) & 0xff) as u8,
(current_addr & 0xff) as u8,
];
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index = 0;
let mut written_words = 0;
// Use chunk_size instead of the full buffer length
let mut bytes_to_write = chunk_size;
if dummy_byte {
bytes_to_write += 1;
}
let reply_as_bytes = reply.to_ne_bytes();
let reply_size = core::cmp::min(buf.len() - read_index, 4);
read_index += match (reply_size, reply_word_index == 1 && dummy_byte) {
(1, false) => {
buf[read_index] = reply_as_bytes[0];
1
let fifo_writes = bytes_to_write.div_ceil(4);
// Fill the FIFO until it is full or all 0 bytes have been written.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(0);
written_words += 1;
}
transfer.start();
let mut reply_word_index = 0;
// Loop based on the current chunk's size
while read_index < chunk_size {
let rx_is_above_threshold = transfer.read_status().rx_above_threshold();
// See p.374 of the TRM: Do a double read to ensure this is correct information.
if rx_is_above_threshold && transfer.read_status().rx_above_threshold() {
let reply = transfer.read_rx_data();
if reply_word_index == 0 {
reply_word_index += 1;
continue;
}
let reply_as_bytes = reply.to_ne_bytes();
// Calculate remaining bytes in this specific chunk
let reply_size = core::cmp::min(chunk_size - read_index, 4);
read_index += match (reply_size, reply_word_index == 1 && dummy_byte) {
(1, false) => {
chunk_slice[read_index] = reply_as_bytes[0];
1
}
(1, true) => {
chunk_slice[read_index] = reply_as_bytes[1];
1
}
(2, false) => {
chunk_slice[read_index..read_index + 2]
.copy_from_slice(&reply_as_bytes[0..2]);
2
}
(2, true) => {
chunk_slice[read_index..read_index + 2]
.copy_from_slice(&reply_as_bytes[1..3]);
2
}
(3, false) => {
chunk_slice[read_index..read_index + 3]
.copy_from_slice(&reply_as_bytes[0..3]);
3
}
(3, true) => {
chunk_slice[read_index..read_index + 3]
.copy_from_slice(&reply_as_bytes[1..4]);
3
}
(4, false) => {
chunk_slice[read_index..read_index + 4]
.copy_from_slice(&reply_as_bytes[0..4]);
4
}
(4, true) => {
chunk_slice[read_index..read_index + 3]
.copy_from_slice(&reply_as_bytes[1..4]);
3
}
_ => unreachable!(),
};
reply_word_index += 1;
}
(1, true) => {
buf[read_index] = reply_as_bytes[1];
1
if written_words < fifo_writes && !transfer.read_status().tx_full() {
transfer.write_word_txd_00(0);
written_words += 1;
}
(2, false) => {
buf[read_index..read_index + 2].copy_from_slice(&reply_as_bytes[0..2]);
2
}
(2, true) => {
buf[read_index..read_index + 2].copy_from_slice(&reply_as_bytes[1..3]);
2
}
(3, false) => {
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[0..3]);
3
}
(3, true) => {
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[1..4]);
3
}
(4, false) => {
buf[read_index..read_index + 4].copy_from_slice(&reply_as_bytes[0..4]);
4
}
(4, true) => {
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[1..4]);
3
}
_ => unreachable!(),
};
reply_word_index += 1;
}
if written_words < fifo_writes && !transfer.read_status().tx_full() {
transfer.write_word_txd_00(0);
written_words += 1;
}
}
offset += chunk_size;
}
}
pub fn read_fast_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool) {
self.generic_read(addr, buf, dummy_byte, true)
}
/// Only works if the clock speed is slower than 50 MHz according to datasheet.
pub fn read_page_read(&self, addr: u32, buf: &mut [u8]) {
self.generic_read(addr, buf, false, false)
}
}
/// If the Spansion QSPI is used in linear addressed mode, no IO operations are allowed.
@@ -618,10 +722,12 @@ pub struct QspiSpansionS25Fl256SLinearMode(QspiLinearAddressing);
impl QspiSpansionS25Fl256SLinearMode {
pub const BASE_ADDR: usize = QspiLinearAddressing::BASE_ADDRESS;
pub const PAGE_SIZE: usize = PAGE_SIZE;
pub const SECTOR_SIZE: usize = SECTOR_SIZE;
pub fn into_io_mode(self, dual_flash: bool) -> QspiSpansionS25Fl256SIoMode {
let qspi = self.0.into_io_mode(dual_flash);
QspiSpansionS25Fl256SIoMode::new(qspi, false)
QspiSpansionS25Fl256SIoMode::new(qspi, Config::default())
}
pub fn read_guard(&mut self) -> QspiLinearReadGuard<'_> {
+42
View File
@@ -0,0 +1,42 @@
[package]
name = "zedboard-fsbl"
version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Rust First Stage Bootloader for the Zedboard"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
zynq7000-boot-image = { path = "../../host/zynq7000-boot-image" }
zedboard-bsp = { path = "../zedboard-bsp" }
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.4"
log = "0.4"
arbitrary-int = "2"
# cargo build/run
[profile.dev]
# default is opt-level = '0', but that makes very
# verbose machine code
opt-level = 's'
# trade compile speed for slightly better optimisations
codegen-units = 1
# cargo build/run --release
[profile.release]
# Optimize for size.
opt-level = 's'
# trade compile speed for slightly better optimisations
codegen-units = 1
# Use Link Time Optimisations to further inline things across
# crates
lto = 'fat'
# Leave the debug symbols in (default is no debug info)
debug = 2
@@ -1,7 +1,7 @@
MEMORY
{
/* The Zynq7000 has 192 kB of OCM memory which can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
/* The Zynq7000 has 256 kB of OCM memory of which 196 kB can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 196K
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
/* Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This can
be used for something like DMA descriptors, but the DDR needs to be set up first in addition
@@ -9,7 +9,10 @@ MEMORY
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
/* Use the upper OCM as the stack */
REGION_ALIAS("STACKS", OCM_UPPER);
SECTIONS
{
@@ -22,3 +25,9 @@ SECTIONS
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 2K);
PROVIDE(_sys_stack_size = 32K);
@@ -8,14 +8,15 @@
#![no_std]
#![no_main]
use arbitrary_int::u6;
use aarch32_cpu::asm::nop;
use arbitrary_int::traits::Integer as _;
use arbitrary_int::{u2, u6};
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embedded_io::Write as _;
use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq7000_boot_image::DestinationDevice;
use zynq7000_hal::priv_tim;
use zynq7000_hal::clocks::ArmClocks;
use zynq7000_hal::{
BootMode,
clocks::{
@@ -23,13 +24,13 @@ use zynq7000_hal::{
pll::{PllConfig, configure_arm_pll, configure_io_pll},
},
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
devcfg, gic, gpio, l2_cache,
gic, gpio, l2_cache,
prelude::*,
qspi::{self, QSPI_START_ADDRESS},
time::Hertz,
uart::{ClockConfig, Config, Uart},
};
use zynq7000_rt as _;
use zynq7000_hal::{generic_interrupt_handler, priv_tim};
// PS clock input frequency.
const PS_CLK: Hertz = Hertz::from_raw(33_333_333);
@@ -43,7 +44,7 @@ const IO_CLK: Hertz = Hertz::from_raw(1_000_000_000);
const DDR_FREQUENCY: Hertz = Hertz::from_raw(533_333_333);
/// 1067 MHz.
const DDR_CLK: Hertz = Hertz::from_raw(2 * DDR_FREQUENCY.raw());
const DDR_CLK: Hertz = Hertz::from_raw(2 * DDR_FREQUENCY.to_raw());
const PERFORM_DDR_MEMTEST: bool = false;
@@ -60,17 +61,8 @@ pub const ELF_BASE_ADDR: usize = 0x100000;
/// 8 MB reserved for application ELF.
pub const BOOT_BIN_STAGING_OFFSET: usize = 8 * 1024 * 1024;
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
#[zynq7000_rt::entry]
fn main() -> ! {
let boot_mode = BootMode::new_from_regs();
// The unwraps are okay here, the provided clock frequencies are standard values also used
// by other Xilinx tools.
@@ -84,6 +76,20 @@ pub fn main() -> ! {
);
let mut periphs = zynq7000::Peripherals::take().unwrap();
l2_cache::disable();
// Initialize the ARM clock. Safety: We only run this once.
unsafe {
ArmClocks::new_with_cpu_clock_init(
ARM_CLK,
zynq7000_hal::clocks::CpuClockRatio::SixToTwoToOne,
u6::new(2),
);
// This is done by the AMD FSBL.
zynq7000_hal::Slcr::with(|val| {
val.gpiob().modify_ctrl(|val| val.with_vref_en(true));
});
}
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLK).unwrap();
@@ -94,7 +100,7 @@ pub fn main() -> ! {
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut logger_uart = Uart::new_with_mio(
let mut logger_uart = Uart::new_with_mio_for_uart_1(
periphs.uart_1,
Config::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
@@ -103,17 +109,10 @@ pub fn main() -> ! {
logger_uart
.write_all(b"-- Zedboard Rust FSBL --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
logger_uart,
log::LevelFilter::Trace,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(logger_uart, log::LevelFilter::Trace, true);
// Set up the global interrupt controller.
let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd);
let mut gic = gic::Configurator::new_with_init(periphs.gicc, periphs.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -172,13 +171,21 @@ pub fn main() -> ! {
);
let qspi_io_mode = qspi.into_io_mode(false);
let spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(
qspi_io_mode,
qspi_spansion::Config {
set_quad_bit_if_necessary: true,
latency_config: Some(u2::ZERO),
clear_write_protection: true,
},
);
let spansion_lqspi =
spansion_qspi.into_linear_addressed(qspi_spansion::QSPI_DEV_COMBINATION_REV_F.into());
qspi_boot(spansion_lqspi, priv_tim);
}
loop {
cortex_ar::asm::nop();
aarch32_cpu::asm::nop();
}
}
@@ -266,7 +273,7 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
};
// The DMA will read from the linear mapped QSPI directly, so it
// has to be configured for reads using the guard!
devcfg::configure_bitstream_non_secure(true, boot_bin_slice)
zynq7000_hal::pl::configure_bitstream_non_secure(true, boot_bin_slice)
.expect("unexpected unaligned address");
log::info!("loaded bitstream successfully");
}
@@ -314,6 +321,10 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
}
}
// The PL is in reset state after power-up. This method needs to be called in the first-stage
// bootloader to put it out of reset.
zynq7000_hal::pl::deassert_reset();
match opt_jump_addr {
Some(jump_addr) => {
log::info!("jumping to address {}", jump_addr);
@@ -321,10 +332,11 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
// Some clean up and preparation for jumping to the user application.
zynq7000_hal::cache::clean_and_invalidate_data_cache();
cortex_ar::register::TlbIAll::write();
cortex_ar::register::BpIAll::write();
cortex_ar::asm::dsb();
cortex_ar::asm::isb();
aarch32_cpu::register::TlbIAll::write();
aarch32_cpu::register::BpIAll::write();
l2_cache::disable();
aarch32_cpu::asm::dsb();
aarch32_cpu::asm::isb();
let jump_func: extern "C" fn() -> ! = unsafe { core::mem::transmute(jump_addr) };
jump_func();
@@ -333,6 +345,15 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
log::warn!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
@@ -0,0 +1 @@
/boot.bin
@@ -4,13 +4,14 @@ version = "0.1.0"
edition = "2024"
[dependencies]
cortex-ar = { version = "0.3" }
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
zynq7000-boot-image = { path = "../../zynq7000-boot-image" }
zynq7000-boot-image = { path = "../../host/zynq7000-boot-image" }
zedboard-bsp = { path = "../zedboard-bsp" }
embedded-io = "0.7"
embedded-hal = "1"
arbitrary-int = "2"
log = "0.4"
libm = "0.2"
+19
View File
@@ -0,0 +1,19 @@
Zedboard QSPI flasher
============
This application flashes a boot binary generated by the AMD `bootgen` utility from DDR
to the Zedboard QSPI. This project contains a `qspi-flasher.tcl` script which can be invoked
with `xsct` to flash a `boot.bin` and the QSPI flasher to DDR and then run the application.
The main `justfile` provides a convenience runner:
```sh
just flash-nor-zedboard <path to my boot.bin>
```
Please note that `xsct` must be callable for this to be usable which is part of a Xilinx Vitis installation.
If the hardware server is running on a remote target the IP address can be specified by setting the environment variable ip_address_hw_server.
````sh
$ export ip_address_hw_server=<ip-address>
````
+31
View File
@@ -0,0 +1,31 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
}
+25
View File
@@ -0,0 +1,25 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
}
REGION_ALIAS("VECTORS", CODE)
REGION_ALIAS("DATA", CODE);
SECTIONS
{
/* Uncached memory */
.uncached (NOLOAD) : ALIGN(4) {
. = ALIGN(4);
_sbss_uncached = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}
@@ -3,8 +3,9 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use arbitrary_int::{traits::Integer as _, u2};
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embedded_hal::{delay::DelayNs as _, digital::StatefulOutputPin as _};
use embedded_io::Write as _;
use log::{error, info};
@@ -36,19 +37,10 @@ const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombin
two_devices: false,
};
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI flasher --\n\r";
#[unsafe(export_name = "main")]
pub fn main() -> ! {
#[zynq7000_rt::entry]
fn main() -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(LevelShifterConfig::EnableAll),
@@ -66,21 +58,14 @@ pub fn main() -> ! {
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio(
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Info,
false,
)
};
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Info, true);
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -106,7 +91,14 @@ pub fn main() -> ! {
let qspi_io_mode = qspi.into_io_mode(false);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(
qspi_io_mode,
qspi_spansion::Config {
set_quad_bit_if_necessary: true,
latency_config: Some(u2::ZERO),
clear_write_protection: true,
},
);
let mut boot_bin_slice = unsafe {
core::slice::from_raw_parts(BOOT_BIN_BASE_ADDR as *const _, BootHeader::FIXED_SIZED_PART)
@@ -130,7 +122,7 @@ pub fn main() -> ! {
);
let mut current_addr = 0;
let mut read_buf = [0u8; 256];
let mut read_buf = [0u8; qspi_spansion::PAGE_SIZE];
let mut next_checkpoint = 0.05;
while current_addr < boot_bin_size {
if current_addr % 0x10000 == 0 {
@@ -146,10 +138,13 @@ pub fn main() -> ! {
}
}
}
let write_size = core::cmp::min(256, boot_bin_size - current_addr);
let write_size = core::cmp::min(
qspi_spansion::RECOMMENDED_PROGRAM_PAGE_SIZE,
boot_bin_size - current_addr,
);
let write_slice = &boot_bin_slice[current_addr..current_addr + write_size];
log::debug!("Programming address {:#x}", current_addr);
match spansion_qspi.program_page(current_addr as u32, write_slice) {
match spansion_qspi.program(current_addr as u32, write_slice) {
Ok(()) => {}
Err(e) => {
log::error!(
@@ -161,11 +156,7 @@ pub fn main() -> ! {
}
}
if VERIFY_PROGRAMMING {
spansion_qspi.read_page_fast_read(
current_addr as u32,
&mut read_buf[0..write_size],
true,
);
spansion_qspi.read_fast_read(current_addr as u32, &mut read_buf[0..write_size], true);
if &read_buf[0..write_size] != write_slice {
error!(
"data verification failed at address {:#x}: wrote {:x?}, read {:x?}",
+21
View File
@@ -0,0 +1,21 @@
Change Log
=======
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.1.1] 2026-03-13
- Try to fix docs build for docs.rs
# [v0.1.0] 2026-02-14
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-embassy-v0.1.0...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-embassy-v0.1.0...zynq7000-embassy-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-embassy-v0.1.0
@@ -1,9 +1,9 @@
[package]
name = "zynq7000-embassy"
version = "0.1.0"
version = "0.1.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Embassy-rs support for the Zynq7000 family of SoCs"
description = "Embassy time support for the Zynq7000 family of SoCs"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
@@ -17,3 +17,7 @@ zynq7000-hal = { path = "../zynq7000-hal", version = "0.1" }
embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.3"
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
rustdoc-args = ["--generate-link-to-definition"]
@@ -2,11 +2,10 @@
[![docs.rs](https://img.shields.io/docsrs/zynq7000-embassy)](https://docs.rs/zynq7000-embassy)
[![ci](https://github.com/us-irs/zynq7000-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/us-irs/zynq7000-rs/actions/workflows/ci.yml)
# Embassy-rs support for the AMD Zynq7000 SoC family
# Embassy time support for the AMD Zynq7000 SoC family
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
AMD Zynq7000 SoC family. Currently, it contains the time driver to allow using embassy-rs. It
currently provides one driver using the global timer peripheral provided by the Zynq7000 PS for
this purpose.
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) time support for
the AMD Zynq7000 SoC family. It currently provides one driver using the global timer peripheral
provided by the Zynq7000 PS for this purpose.
The documentation contains more information on how to use this crate.
@@ -1,3 +1,10 @@
//! # Embassy time support for the AMD Zynq7000 SoC family
//!
//! This project contains the [embassy-rs](https://github.com/embassy-rs/embassy) time support for
//! the AMD Zynq7000 SoC family. It currently provides one driver using the global timer peripheral
//! provided by the Zynq7000 PS for this purpose.
//!
//! The [crate::init] method must be called once for the time driver to work properly.
#![no_std]
use core::cell::{Cell, RefCell};
@@ -59,9 +66,19 @@ impl GtcTimerDriver {
///
/// This has to be called ONCE at system initialization.
pub unsafe fn init(&'static self, arm_clock: &ArmClocks, mut gtc: GlobalTimerCounter) {
fn safe_interrupt_handler() {
// Safety: See safety notes of [zynq7000_hal::generic_interrupt_handler].
unsafe {
on_interrupt();
}
}
zynq7000_hal::register_interrupt(
zynq7000_hal::gic::Interrupt::Ppi(zynq7000_hal::gic::PpiInterrupt::GlobalTimer),
safe_interrupt_handler,
);
CPU_3X2X_CLK.set(arm_clock.cpu_3x2x_clk()).unwrap();
SCALE
.set(arm_clock.cpu_3x2x_clk().raw() as u64 / TICK_HZ)
.set(arm_clock.cpu_3x2x_clk().to_raw() as u64 / TICK_HZ)
.unwrap();
gtc.set_cpu_3x2x_clock(arm_clock.cpu_3x2x_clk());
gtc.set_prescaler(0);
+56
View File
@@ -0,0 +1,56 @@
Change Log
=======
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
## Fixed
- Bugfix for DDR initialization: `calibrate_iob_impedance_for_ddr3` and `calibrate_iob_impedance`
now expect a `zynq7000::slcr::ddriob::DdrControl` input argument. This register write was
missing
- Several bugfixes and improvements for GIC module. Some of the registers previously were
completely overwritten instead of only modifying their own bit portions. Also allow targeting
interrupts without clearing other CPU target.
- Do not reset the UART on TX future creation anymore, which lead to glitches and invalid data.
- Robustness improvements for the asynchronous UART TX module.
- SPI1 AMBA clock control bits are now enabled and disabled properly
## Changed
- Increased reliabily of PS UART interrupt reception, which was proven to be buggy for higher baud
rates: Force user to configure RTO value, encouraging non-zero values, and use a RX FIFO trigger
value of FIFO depth divided by 2 by default.
- `devcfg` moved to `pl` module
- Added division by zero check in gtc frequency_to_ticks to avoid runtime panic
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
respectively.
- `log::rb` module replaced by `log::asynch` module which uses an asynchronous embassy pipe
for logging.
- GIC data structures: Removed the `Gic` prefix which already is part of the module name.
- Renamed `GicInterruptHelper` to `InterruptGuard`. It acknowledges the end of interrupts on drop.
## Added
- Method to de-assert PL reset.
- ARM clock initialization for the `ArmClocks` structure
- The `ArmClocks` structure now caches the CPU clock ratio
- New generic interrupt registry and generic interrupt handler which uses the registry.
Primary interface is the `crate::generic_interrupt_handler` function and the
`crate::register_interrupt` function.
# [v0.1.1] 2025-10-10
Documentation fixes.
# [v0.1.0] 2025-10-09
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-hal-v0.1.0...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-hal-v0.1.0...zynq7000-hal-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-hal-v0.1.0
@@ -1,6 +1,6 @@
[package]
name = "zynq7000-hal"
version = "0.1.0"
version = "0.1.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Hardware Abstraction Layer (HAL) for the Zynq7000 family of SoCs"
@@ -11,16 +11,15 @@ keywords = ["no-std", "hal", "amd", "zynq7000", "bare-metal"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = { version = "0.3" }
zynq7000 = { path = "../zynq7000", version = "0.1" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1" }
aarch32-cpu = { version = "0.3" }
zynq7000 = { path = "../zynq7000", version = "0.4" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.2" }
static_assertions = "1.1"
bitbybit = "1.4"
bitbybit = "2"
arbitrary-int = "2"
thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }
ringbuf = { version = "0.4.8", default-features = false }
bitflags = "2"
embedded-hal-nb = "1"
embedded-io = "0.7"
embedded-hal = "1"
@@ -28,22 +27,27 @@ embedded-hal-async = "1"
heapless = "0.9"
static_cell = "2"
delegate = "0.13"
paste = "1"
pastey = "0.2.1"
nb = "1"
fugit = "0.3"
fugit = "0.4"
critical-section = "1"
libm = "0.2"
log = "0.4"
embassy-sync = "0.7"
embassy-sync = "0.8"
embassy-net-driver = "0.2"
smoltcp = { version = "0.12", default-features = false, features = ["proto-ipv4", "medium-ethernet", "socket-raw"] }
smoltcp = { version = "0.13", default-features = false, features = ["proto-ipv4", "medium-ethernet", "socket-raw"] }
vcell = "0.1"
raw-slicee = "0.1"
embedded-io-async = "0.7"
serde = { version = "1", optional = true, features = ["derive"] }
defmt = { version = "1", optional = true }
embedded-sdmmc = { git = "https://github.com/robamu/embedded-sdmmc-rs.git", branch = "all-features" }
bytemuck = "1.25"
[features]
std = ["thiserror/std", "alloc"]
alloc = []
defmt = ["dep:defmt", "fugit/defmt", "zynq7000/defmt"]
# These devices have a lower pin count.
7z010-7z007s-clg225 = []
@@ -52,4 +56,5 @@ approx = "0.5"
[package.metadata.docs.rs]
features = ["alloc"]
targets = ["armv7a-none-eabihf"]
rustdoc-args = ["--generate-link-to-definition"]
@@ -5,12 +5,12 @@
# HAL for the AMD Zynq 7000 SoC family
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000).
hardware abstraction on top of the [peripheral access API](../zynq7000).
It is the result of reading the datasheet for the device and encoding a type-safe layer over the
raw PAC. This crate also implements traits specified by the
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
various drivers in the embedded rust ecosystem.
The [top-level README](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs) and the documentation
The [top-level README](../../README.md) and the documentation
contain more information on how to use this crate.
@@ -1,13 +1,17 @@
//! # Cache management module
//!
//! A lot of cache maintenance operations for this SoC have to be performed on both the L1 and the
//! L2 cache in the correct order. This module provides commonly required operations.
use core::sync::atomic::compiler_fence;
use cortex_ar::{
use aarch32_cpu::{
asm::dsb,
cache::{
clean_and_invalidate_data_cache_line_to_poc, clean_data_cache_line_to_poc,
invalidate_data_cache_line_to_poc,
},
};
use zynq7000::l2_cache::{L2Cache, MmioL2Cache};
use zynq7000::l2_cache::{MmioRegisters, Registers};
pub const CACHE_LINE_SIZE: usize = 32;
@@ -15,7 +19,7 @@ pub const CACHE_LINE_SIZE: usize = 32;
#[error("alignment error, addresses and lengths must be aligned to 32 byte cache line length")]
pub struct AlignmentError;
pub fn clean_and_invalidate_l2c_line(l2c: &mut MmioL2Cache<'static>, addr: u32) {
pub fn clean_and_invalidate_l2c_line(l2c: &mut MmioRegisters<'static>, addr: u32) {
l2c.write_clean_by_pa(addr);
l2c.write_invalidate_by_pa(addr);
}
@@ -24,16 +28,16 @@ pub fn clean_and_invalidate_l2c_line(l2c: &mut MmioL2Cache<'static>, addr: u32)
pub fn clean_and_invalidate_data_cache() {
dsb();
cortex_ar::cache::clean_l1_data_cache::<2, 5, 8>();
aarch32_cpu::cache::clean_l1_data_cache::<2, 5, 8>();
dsb();
// Clean all ways in L2 cache.
let mut l2c = unsafe { L2Cache::new_mmio_fixed() };
let mut l2c = unsafe { Registers::new_mmio_fixed() };
l2c.write_clean_invalidate_by_way(0xffff);
while l2c.read_cache_sync().busy() {}
compiler_fence(core::sync::atomic::Ordering::SeqCst);
cortex_ar::cache::clean_and_invalidate_l1_data_cache::<2, 5, 8>();
aarch32_cpu::cache::clean_and_invalidate_l1_data_cache::<2, 5, 8>();
dsb();
}
@@ -50,7 +54,7 @@ pub fn invalidate_data_cache_range(addr: u32, len: usize) -> Result<(), Alignmen
}
let mut current_addr = addr;
let end_addr = addr.saturating_add(len as u32);
let mut l2c = unsafe { L2Cache::new_mmio_fixed() };
let mut l2c = unsafe { Registers::new_mmio_fixed() };
dsb();
// Invalidate outer caches lines first, see chapter 3.3.10 of the L2C technical reference
@@ -99,7 +103,7 @@ pub fn clean_and_invalidate_data_cache_range(addr: u32, len: usize) -> Result<()
dsb();
// Clean and invalidates outer cache.
let mut l2c = unsafe { L2Cache::new_mmio_fixed() };
let mut l2c = unsafe { Registers::new_mmio_fixed() };
current_addr = addr;
while current_addr < end_addr {
// ARM errate 588369 specifies that clean and invalidate need to be separate, but the
@@ -151,7 +155,7 @@ pub fn clean_data_cache_range(addr: u32, len: usize) -> Result<(), AlignmentErro
dsb();
// Clean and invalidates outer cache.
let mut l2c = unsafe { L2Cache::new_mmio_fixed() };
let mut l2c = unsafe { Registers::new_mmio_fixed() };
current_addr = addr;
while current_addr < end_addr {
l2c.write_clean_by_pa(current_addr);
@@ -1,21 +1,24 @@
//! Clock module.
//! # Clock module
use arbitrary_int::{prelude::*, u6};
pub mod pll;
pub use zynq7000::slcr::clocks::CpuClockRatio;
use zynq7000::slcr::{
ClockControl,
ClockControlRegisters,
clocks::{
ClockkRatioSelect, DualCommonPeriphIoClockControl, FpgaClockControl, GigEthClockControl,
SingleCommonPeriphIoClockControl,
ArmClockControl, ClockRatioSelectReg, DualCommonPeriphIoClockControl, FpgaClockControl,
GigEthClockControl, SingleCommonPeriphIoClockControl,
},
};
use super::time::Hertz;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ArmClocks {
ref_clk: Hertz,
ratio: CpuClockRatio,
cpu_1x_clk: Hertz,
cpu_2x_clk: Hertz,
cpu_3x2x_clk: Hertz,
@@ -23,29 +26,89 @@ pub struct ArmClocks {
}
impl ArmClocks {
/// Configure the ARM clocks based on the ARM PLL input clock.
///
/// # Safety
///
/// This changes the CPU clock frequency. You must pass the ARM PLL clock frequency and
/// you must ensure that this is only run once during system initialization, for example
/// in the first-stage bootloader.
pub unsafe fn new_with_cpu_clock_init(
arm_pll_clk: Hertz,
clock_ratio: CpuClockRatio,
divisor: u6,
) -> Self {
unsafe {
crate::slcr::Slcr::with(|slcr| {
slcr.clk_ctrl().write_clk_ratio_select(
ClockRatioSelectReg::builder().with_sel(clock_ratio).build(),
);
slcr.clk_ctrl().write_arm_clk_ctrl(
ArmClockControl::builder()
.with_cpu_peri_clk_act(true)
.with_cpu_1x_clk_act(true)
.with_cpu_2x_clk_act(true)
.with_cpu_3or2x_clk_act(true)
.with_cpu_6or4x_clk_act(true)
.with_divisor(divisor)
.with_srcsel(zynq7000::slcr::clocks::SrcSelArm::ArmPll)
.build(),
);
});
}
let cpu_6x4x_clk = arm_pll_clk / divisor.as_u32();
let cpu_1x_clk = match clock_ratio {
CpuClockRatio::FourToTwoToOne => cpu_6x4x_clk / 4,
CpuClockRatio::SixToTwoToOne => cpu_6x4x_clk / 6,
};
Self {
ref_clk: arm_pll_clk,
ratio: clock_ratio,
cpu_1x_clk,
cpu_2x_clk: cpu_1x_clk * 2,
cpu_3x2x_clk: match clock_ratio {
CpuClockRatio::SixToTwoToOne => cpu_1x_clk * 3,
CpuClockRatio::FourToTwoToOne => cpu_1x_clk * 2,
},
cpu_6x4x_clk,
}
}
#[inline]
pub const fn ratio(&self) -> CpuClockRatio {
self.ratio
}
/// Reference clock provided by ARM PLL which is used to calculate all other clock frequencies.
#[inline]
pub const fn ref_clk(&self) -> Hertz {
self.ref_clk
}
#[inline]
pub const fn cpu_1x_clk(&self) -> Hertz {
self.cpu_1x_clk
}
#[inline]
pub const fn cpu_2x_clk(&self) -> Hertz {
self.cpu_2x_clk
}
#[inline]
pub const fn cpu_3x2x_clk(&self) -> Hertz {
self.cpu_3x2x_clk
}
#[inline]
pub const fn cpu_6x4x_clk(&self) -> Hertz {
self.cpu_6x4x_clk
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DdrClocks {
/// DDR reference clock generated by the DDR PLL.
ref_clk: Hertz,
@@ -114,6 +177,7 @@ impl DdrClocks {
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IoClocks {
/// Reference clock provided by IO PLL which is used to calculate all other clock frequencies.
ref_clk: Hertz,
@@ -194,7 +258,9 @@ impl IoClocks {
}
}
// TODO: Display impl for clock config.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clocks {
ps_clk: Hertz,
arm_pll_out: Hertz,
@@ -250,7 +316,7 @@ impl Clocks {
/// It assumes that the clock already has been configured, for example by a first-stage
/// bootloader, or the PS7 initialization script.
pub fn new_from_regs(ps_clk_freq: Hertz) -> Result<Self, ClockReadError> {
let mut clk_regs = unsafe { ClockControl::new_mmio_fixed() };
let mut clk_regs = unsafe { ClockControlRegisters::new_mmio_fixed() };
let arm_pll_cfg = clk_regs.read_arm_pll_ctrl();
let io_pll_cfg = clk_regs.read_io_pll_ctrl();
@@ -273,25 +339,27 @@ impl Clocks {
zynq7000::slcr::clocks::SrcSelArm::DdrPll => ddr_pll_out,
zynq7000::slcr::clocks::SrcSelArm::IoPll => io_pll_out,
};
let clk_sel = clk_regs.read_clk_621_true();
let clk_sel = clk_regs.read_clk_ratio_select();
if arm_clk_ctrl.divisor().as_u32() == 0 {
return Err(ClockReadError::DivisorZero(DivisorZero(ClockModuleId::Arm)));
}
let arm_clk_divided = arm_base_clk / arm_clk_ctrl.divisor().as_u32();
let arm_clks = match clk_sel.sel() {
ClockkRatioSelect::FourToTwoToOne => ArmClocks {
CpuClockRatio::FourToTwoToOne => ArmClocks {
ref_clk: arm_pll_out,
cpu_1x_clk: arm_clk_divided / 4,
cpu_2x_clk: arm_clk_divided / 2,
cpu_3x2x_clk: arm_clk_divided / 2,
cpu_6x4x_clk: arm_clk_divided,
ratio: clk_sel.sel(),
},
ClockkRatioSelect::SixToTwoToOne => ArmClocks {
CpuClockRatio::SixToTwoToOne => ArmClocks {
ref_clk: arm_pll_out,
cpu_1x_clk: arm_clk_divided / 6,
cpu_2x_clk: arm_clk_divided / 3,
cpu_3x2x_clk: arm_clk_divided / 2,
cpu_6x4x_clk: arm_clk_divided,
ratio: clk_sel.sel(),
},
};
@@ -501,7 +569,7 @@ impl Clocks {
/// The reference clock will only be the RX clock in loopback mode. For the TX block,
/// the reference clock is used if the EMIO enable bit `GEM{0,1}_CLK_CTRL[6]` is set to 0.
pub fn calculate_gem_0_ref_clock(&self) -> Result<Hertz, DivisorZero> {
let clk_regs = unsafe { ClockControl::new_mmio_fixed() };
let clk_regs = unsafe { ClockControlRegisters::new_mmio_fixed() };
self.calculate_gem_ref_clock(clk_regs.read_gem_0_clk_ctrl(), ClockModuleId::Gem0)
}
@@ -514,7 +582,7 @@ impl Clocks {
/// The reference clock will only be the RX clock in loopback mode. For the TX block,
/// the reference clock is used if the EMIO enable bit `GEM{0,1}_CLK_CTRL[6]` is set to 0.
pub fn calculate_gem_1_ref_clock(&self) -> Result<Hertz, DivisorZero> {
let clk_regs = unsafe { ClockControl::new_mmio_fixed() };
let clk_regs = unsafe { ClockControlRegisters::new_mmio_fixed() };
self.calculate_gem_ref_clock(clk_regs.read_gem_0_clk_ctrl(), ClockModuleId::Gem1)
}
}
@@ -1,3 +1,4 @@
//! # PLL support module
use core::sync::atomic::AtomicBool;
use arbitrary_int::{u4, u7, u10};
@@ -37,7 +38,7 @@ impl PllConfig {
ps_clk: Hertz,
target_clk: Hertz,
) -> Result<Self, PllConfigCtorError> {
if ps_clk.raw() == 0 {
if ps_clk.to_raw() == 0 {
return Err(PllConfigCtorError::InvalidInput);
}
let mul = target_clk / ps_clk;
@@ -195,7 +196,7 @@ impl PllConfig {
/// This function configures the ARM PLL based on the provided [PllConfig].
pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
@@ -204,20 +205,20 @@ pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
/// This function configures the IO PLL based on the provided [PllConfig].
pub fn configure_io_pll(boot_mode: BootMode, pll_config: PllConfig) {
if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
unsafe { configure_io_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the DDR PLL based on the provided [PllConfig].
pub fn configure_ddr_pll(boot_mode: BootMode, pll_config: PllConfig) {
if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
unsafe { configure_ddr_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the ARM PLL based on the provided [PllConfig].
@@ -309,7 +310,7 @@ unsafe fn configure_pll_unchecked(
boot_mode: BootMode,
cfg: PllConfig,
pll_type: PllType,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
slcr: &mut zynq7000::slcr::MmioRegisters<'static>,
pll_ctrl_reg: *mut zynq7000::slcr::clocks::PllControl,
pll_cfg_reg: *mut zynq7000::slcr::clocks::PllConfig,
) {
@@ -346,9 +347,7 @@ unsafe fn configure_pll_unchecked(
while ((slcr.clk_ctrl().read_pll_status().raw_value() >> pll_type.bit_offset_pll_locked())
& 0b1)
!= 1
{
cortex_ar::asm::nop();
}
{}
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
pll_ctrl.set_bypass_force(false);
@@ -1,6 +1,6 @@
//! Low-level DDR configuration module.
use arbitrary_int::{prelude::*, u2, u3, u6};
use zynq7000::ddrc::{MmioDdrController, regs::*};
use zynq7000::ddrc::{MmioRegisters, regs::*};
use zynq7000::slcr::{clocks::DciClockControl, ddriob::DdriobConfig};
use crate::{clocks::DdrClocks, time::Hertz};
@@ -28,7 +28,7 @@ pub fn calculate_dci_divisors(ddr_clks: &DdrClocks) -> DciClkConfig {
/// Calculate the required DCI divisors for the given DDR clock frequency.
pub fn calculate_dci_divisors_with_ddr_clk(ddr_clk: Hertz) -> DciClkConfig {
let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
let target_div = ddr_clk.to_raw().div_ceil(DCI_MAX_FREQ.to_raw());
let mut config = DciClkConfig {
div0: u6::new(u6::MAX.value()),
div1: u6::new(u6::MAX.value()),
@@ -86,20 +86,42 @@ pub unsafe fn configure_dci(ddr_clk: &DdrClocks) {
///
/// This function writes to the DDR IOB related registers. It should only be called once during
/// DDR initialization.
pub unsafe fn calibrate_iob_impedance_for_ddr3(dci_clk_cfg: DciClkConfig, poll_for_done: bool) {
pub unsafe fn calibrate_iob_impedance_for_ddr3(
ddr_control: zynq7000::slcr::ddriob::DdrControl,
dci_clk_cfg: DciClkConfig,
poll_for_done: bool,
) {
unsafe {
calibrate_iob_impedance(
ddr_control,
dci_clk_cfg,
u3::new(0),
u2::new(0),
u3::new(0b001),
u3::new(0),
u2::new(0),
CalibrationParams::new_ddr3(),
poll_for_done,
);
}
}
/// DDR IOB impedance calibration parameters.
pub struct CalibrationParams {
pub pref_opt2: u3,
pub pref_opt1: u2,
pub nref_opt4: u3,
pub nref_opt2: u3,
pub nref_opt1: u2,
}
impl CalibrationParams {
pub const fn new_ddr3() -> Self {
Self {
pref_opt2: u3::new(0),
pref_opt1: u2::new(0),
nref_opt4: u3::new(0b001),
nref_opt2: u3::new(0),
nref_opt1: u2::new(0),
}
}
}
/// Calibrates the IOB impedance according to to TRM p.325, DDR IOB Impedance calibration.
///
/// This function will also enable the DCI clock with the provided clock configuration.
@@ -115,12 +137,9 @@ pub unsafe fn calibrate_iob_impedance_for_ddr3(dci_clk_cfg: DciClkConfig, poll_f
/// This function writes to the DDR IOB related registers. It should only be called once during
/// DDR initialization.
pub unsafe fn calibrate_iob_impedance(
ddr_control: zynq7000::slcr::ddriob::DdrControl,
dci_clk_cfg: DciClkConfig,
pref_opt2: u3,
pref_opt1: u2,
nref_opt4: u3,
nref_opt2: u3,
nref_opt1: u2,
calibration_params: CalibrationParams,
poll_for_done: bool,
) {
// Safety: Only writes to DDR IOB related registers.
@@ -134,45 +153,35 @@ pub unsafe fn calibrate_iob_impedance(
.build(),
);
let mut ddriob = slcr.ddriob();
ddriob.modify_dci_ctrl(|mut val| {
val.set_reset(true);
ddriob.write_ddr_control(ddr_control);
ddriob.modify_dci_control(|val| val.with_reset(true));
ddriob.modify_dci_control(|val| val.with_reset(false));
ddriob.modify_dci_control(|val| val.with_reset(true));
ddriob.modify_dci_control(|mut val| {
val.set_pref_opt2(calibration_params.pref_opt2);
val.set_pref_opt1(calibration_params.pref_opt1);
val.set_nref_opt4(calibration_params.nref_opt4);
val.set_nref_opt2(calibration_params.nref_opt2);
val.set_nref_opt1(calibration_params.nref_opt1);
val
});
ddriob.modify_dci_ctrl(|mut val| {
val.set_reset(false);
val
});
ddriob.modify_dci_ctrl(|mut val| {
val.set_reset(true);
val
});
ddriob.modify_dci_ctrl(|mut val| {
val.set_pref_opt2(pref_opt2);
val.set_pref_opt1(pref_opt1);
val.set_nref_opt4(nref_opt4);
val.set_nref_opt2(nref_opt2);
val.set_nref_opt1(nref_opt1);
val
});
ddriob.modify_dci_ctrl(|mut val| {
ddriob.modify_dci_control(|mut val| {
val.set_update_control(false);
val
});
ddriob.modify_dci_ctrl(|mut val| {
ddriob.modify_dci_control(|mut val| {
val.set_enable(true);
val
});
if poll_for_done {
while !slcr.ddriob().read_dci_status().done() {
// Wait for the DDR IOB impedance calibration to complete.
cortex_ar::asm::nop();
}
while !slcr.ddriob().read_dci_status().done() {}
}
});
}
/// Static configuration for DDR IOBs.
pub struct DdriobConfigSet {
pub ddr_control: zynq7000::slcr::ddriob::DdrControl,
pub addr0: DdriobConfig,
pub addr1: DdriobConfig,
pub data0: DdriobConfig,
@@ -272,7 +281,7 @@ pub struct DdrcConfigSet {
///
/// It does NOT take care of taking the DDR controller out of reset and polling for DDR
/// configuration completion.
pub fn configure_ddr_config(ddrc: &mut MmioDdrController<'static>, cfg_set: &DdrcConfigSet) {
pub fn configure_ddr_config(ddrc: &mut MmioRegisters<'static>, cfg_set: &DdrcConfigSet) {
ddrc.write_ddrc_ctrl(cfg_set.ctrl);
// Write all configuration registers.
ddrc.write_two_rank_cfg(cfg_set.two_rank);
@@ -1,5 +1,6 @@
//! # DDR module
use arbitrary_int::u6;
use zynq7000::ddrc::MmioDdrController;
use zynq7000::ddrc::MmioRegisters;
use crate::{
BootMode,
@@ -43,7 +44,7 @@ impl DdrClockSetupConfig {
/// This function consumes the DDRC register block once and thus provides a safe interface for DDR
/// initialization.
pub fn configure_ddr_for_ddr3(
mut ddrc_regs: MmioDdrController<'static>,
mut ddrc_regs: MmioRegisters<'static>,
boot_mode: BootMode,
clk_setup_cfg: DdrClockSetupConfig,
ddriob_cfg: &DdriobConfigSet,
@@ -75,7 +76,7 @@ pub fn configure_ddr_for_ddr3(
ll::configure_iob(ddriob_cfg);
// Do not wait for completion, it takes a bit of time. We can set all the DDR config registers
// before polling for completion.
ll::calibrate_iob_impedance_for_ddr3(dci_clk_cfg, false);
ll::calibrate_iob_impedance_for_ddr3(ddriob_cfg.ddr_control, dci_clk_cfg, false);
}
ll::configure_ddr_config(&mut ddrc_regs, ddr_cfg);
// Safety: This is only called once during DDR initialization, and we only modify DDR related
@@ -84,7 +85,7 @@ pub fn configure_ddr_for_ddr3(
let ddriob_shared = slcr.regs().ddriob_shared();
// Wait for DDR IOB impedance calibration to complete first.
while !ddriob_shared.read_dci_status().done() {
cortex_ar::asm::nop();
aarch32_cpu::asm::nop();
}
log::debug!("DDR IOB impedance calib done");
@@ -98,7 +99,7 @@ pub fn configure_ddr_for_ddr3(
!= zynq7000::ddrc::regs::OperatingMode::NormalOperation
{
// Wait for the soft reset to complete.
cortex_ar::asm::nop();
aarch32_cpu::asm::nop();
}
}
@@ -128,7 +129,7 @@ pub mod memtest {
/// This tests writes and reads on a memory block starting at the base address
/// with the size `words` times 4.
pub unsafe fn walking_one_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
unsafe { walking_value_test(true, base_addr, words) }
unsafe { walking_value_test(false, base_addr, words) }
}
/// # Safety
@@ -8,11 +8,6 @@ use crate::{clocks::IoClocks, enable_amba_peripheral_clock, slcr::Slcr, time::He
use super::{EthernetId, PsEthernet as _};
pub struct EthernetLowLevel {
id: EthernetId,
pub regs: zynq7000::eth::MmioEthernet<'static>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Speed {
Mbps10,
@@ -52,7 +47,10 @@ impl ClockDivisors {
/// Calls [Self::calculate_for_rgmii], assuming that the IO clock is the reference clock,
/// which is the default clock for the Ethernet module.
pub fn calculate_for_rgmii_and_io_clock(io_clks: IoClocks, target_speed: Speed) -> (Self, u32) {
pub fn calculate_for_rgmii_and_io_clock(
io_clks: &IoClocks,
target_speed: Speed,
) -> (Self, u32) {
Self::calculate_for_rgmii(io_clks.ref_clk(), target_speed)
}
@@ -71,8 +69,8 @@ impl ClockDivisors {
let mut best_div_1 = u6::new(0);
for div_1 in 1..=u6::MAX.as_usize() {
for div_0 in 1..=u6::MAX.as_usize() {
let clk_rate = ref_clk.raw() / div_0 as u32 / div_1 as u32;
let diff = (target_speed.raw() as i64 - clk_rate as i64).unsigned_abs() as u32;
let clk_rate = ref_clk.to_raw() / div_0 as u32 / div_1 as u32;
let diff = (target_speed.to_raw() as i64 - clk_rate as i64).unsigned_abs() as u32;
if diff < smallest_diff {
smallest_diff = diff;
best_div_0 = u6::new(div_0 as u8);
@@ -174,10 +172,19 @@ impl ClockDivSet {
/// Ethernet low-level interface.
///
/// Basic building block for higher-level abstraction.
pub struct EthernetLowLevel {
id: EthernetId,
/// Register block. Direct public access is allowed to allow low-level operations.
pub regs: zynq7000::eth::MmioRegisters<'static>,
}
impl EthernetLowLevel {
/// Creates a new instance of the Ethernet low-level interface.
///
/// Returns [None] if the given registers block base address does not correspond to a valid
/// Ethernet peripheral.
#[inline]
pub fn new(regs: zynq7000::eth::MmioEthernet<'static>) -> Option<Self> {
pub fn new(regs: zynq7000::eth::MmioRegisters<'static>) -> Option<Self> {
regs.id()?;
Some(EthernetLowLevel {
id: regs.id().unwrap(),
@@ -196,41 +203,15 @@ impl EthernetLowLevel {
id,
regs: unsafe {
match id {
EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(),
EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(),
EthernetId::Eth0 => zynq7000::eth::Registers::new_mmio_fixed_0(),
EthernetId::Eth1 => zynq7000::eth::Registers::new_mmio_fixed_1(),
}
},
}
}
pub fn reset(&mut self, cycles: usize) {
let assert_reset = match self.id {
EthernetId::Eth0 => EthernetReset::builder()
.with_gem1_ref_rst(false)
.with_gem0_ref_rst(true)
.with_gem1_rx_rst(false)
.with_gem0_rx_rst(true)
.with_gem1_cpu1x_rst(false)
.with_gem0_cpu1x_rst(true)
.build(),
EthernetId::Eth1 => EthernetReset::builder()
.with_gem1_ref_rst(true)
.with_gem0_ref_rst(false)
.with_gem1_rx_rst(true)
.with_gem0_rx_rst(false)
.with_gem1_cpu1x_rst(true)
.with_gem0_cpu1x_rst(false)
.build(),
};
unsafe {
Slcr::with(|regs| {
regs.reset_ctrl().write_eth(assert_reset);
for _ in 0..cycles {
cortex_ar::asm::nop();
}
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
});
}
reset(self.id, cycles);
}
#[inline]
@@ -383,3 +364,34 @@ impl EthernetLowLevel {
self.id
}
}
/// Resets the Ethernet peripheral with the given ID.
pub fn reset(id: EthernetId, cycles: usize) {
let assert_reset = match id {
EthernetId::Eth0 => EthernetReset::builder()
.with_gem1_ref_rst(false)
.with_gem0_ref_rst(true)
.with_gem1_rx_rst(false)
.with_gem0_rx_rst(true)
.with_gem1_cpu1x_rst(false)
.with_gem0_cpu1x_rst(true)
.build(),
EthernetId::Eth1 => EthernetReset::builder()
.with_gem1_ref_rst(true)
.with_gem0_ref_rst(false)
.with_gem1_rx_rst(true)
.with_gem0_rx_rst(false)
.with_gem1_cpu1x_rst(true)
.with_gem0_cpu1x_rst(false)
.build(),
};
unsafe {
Slcr::with(|regs| {
regs.reset_ctrl().write_eth(assert_reset);
for _ in 0..cycles {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
});
}
}
@@ -4,7 +4,7 @@ use zynq7000::eth::{MdcClockDivisor, PhyMaintenance};
use super::{EthernetId, ll::EthernetLowLevel};
pub struct Mdio {
regs: zynq7000::eth::MmioEthernet<'static>,
regs: zynq7000::eth::MmioRegisters<'static>,
clause22: bool,
}
@@ -1,8 +1,9 @@
//! # Ethernet module
use arbitrary_int::{u2, u3};
pub use zynq7000::eth::MdcClockDivisor;
use zynq7000::eth::{
BurstLength, DmaRxBufSize, GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, InterruptControl, InterruptStatus,
MmioEthernet, RxStatus, TxStatus,
MmioRegisters, RxStatus, TxStatus,
};
pub use ll::{ClockConfig, ClockDivSet, Duplex, EthernetLowLevel, Speed};
@@ -53,18 +54,18 @@ impl EthernetId {
/// # Safety
///
/// Circumvents ownership and safety guarantees of the HAL.
pub const unsafe fn steal_regs(&self) -> zynq7000::eth::MmioEthernet<'static> {
pub const unsafe fn steal_regs(&self) -> zynq7000::eth::MmioRegisters<'static> {
unsafe {
match self {
EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(),
EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(),
EthernetId::Eth0 => zynq7000::eth::Registers::new_mmio_fixed_0(),
EthernetId::Eth1 => zynq7000::eth::Registers::new_mmio_fixed_1(),
}
}
}
pub fn clk_config_regs(
&self,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
slcr: &mut zynq7000::slcr::MmioRegisters<'static>,
) -> (
*mut zynq7000::slcr::clocks::GigEthClockControl,
*mut zynq7000::slcr::clocks::GigEthRclkControl,
@@ -83,13 +84,13 @@ impl EthernetId {
}
pub trait PsEthernet {
fn reg_block(&self) -> MmioEthernet<'static>;
fn reg_block(&self) -> MmioRegisters<'static>;
fn id(&self) -> Option<EthernetId>;
}
impl PsEthernet for MmioEthernet<'static> {
impl PsEthernet for MmioRegisters<'static> {
#[inline]
fn reg_block(&self) -> MmioEthernet<'static> {
fn reg_block(&self) -> MmioRegisters<'static> {
unsafe { self.clone() }
}
@@ -105,42 +106,31 @@ impl PsEthernet for MmioEthernet<'static> {
}
}
pub trait TxClockPin: MioPin {
const ETH_ID: EthernetId;
}
pub trait TxControlPin: MioPin {
const ETH_ID: EthernetId;
}
pub trait TxData0Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait TxData1Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait TxData2Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait TxData3Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait RxClockPin: MioPin {
const ETH_ID: EthernetId;
}
pub trait RxControlPin: MioPin {
const ETH_ID: EthernetId;
}
pub trait RxData0Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait RxData1Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait RxData2Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait RxData3Pin: MioPin {
const ETH_ID: EthernetId;
}
pub trait Eth0TxClockPin: MioPin {}
pub trait Eth0TxControlPin: MioPin {}
pub trait Eth0TxData0Pin: MioPin {}
pub trait Eth0TxData1Pin: MioPin {}
pub trait Eth0TxData2Pin: MioPin {}
pub trait Eth0TxData3Pin: MioPin {}
pub trait Eth0RxClockPin: MioPin {}
pub trait Eth0RxControlPin: MioPin {}
pub trait Eth0RxData0Pin: MioPin {}
pub trait Eth0RxData1Pin: MioPin {}
pub trait Eth0RxData2Pin: MioPin {}
pub trait Eth0RxData3Pin: MioPin {}
pub trait Eth1TxClockPin: MioPin {}
pub trait Eth1TxControlPin: MioPin {}
pub trait Eth1TxData0Pin: MioPin {}
pub trait Eth1TxData1Pin: MioPin {}
pub trait Eth1TxData2Pin: MioPin {}
pub trait Eth1TxData3Pin: MioPin {}
pub trait Eth1RxClockPin: MioPin {}
pub trait Eth1RxControlPin: MioPin {}
pub trait Eth1RxData0Pin: MioPin {}
pub trait Eth1RxData1Pin: MioPin {}
pub trait Eth1RxData2Pin: MioPin {}
pub trait Eth1RxData3Pin: MioPin {}
pub trait MdClockPin: MioPin {}
pub trait MdIoPin: MioPin {}
@@ -149,95 +139,50 @@ impl MdClockPin for Pin<Mio52> {}
impl MdIoPin for Pin<Mio53> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxClockPin for Pin<Mio16> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0TxClockPin for Pin<Mio16> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxControlPin for Pin<Mio21> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0TxControlPin for Pin<Mio21> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData0Pin for Pin<Mio17> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0TxData0Pin for Pin<Mio17> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData1Pin for Pin<Mio18> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0TxData1Pin for Pin<Mio18> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData2Pin for Pin<Mio19> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0TxData2Pin for Pin<Mio19> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData3Pin for Pin<Mio20> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0TxData3Pin for Pin<Mio20> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxClockPin for Pin<Mio22> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0RxClockPin for Pin<Mio22> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxControlPin for Pin<Mio27> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0RxControlPin for Pin<Mio27> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData0Pin for Pin<Mio23> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0RxData0Pin for Pin<Mio23> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData1Pin for Pin<Mio24> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0RxData1Pin for Pin<Mio24> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData2Pin for Pin<Mio25> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0RxData2Pin for Pin<Mio25> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData3Pin for Pin<Mio26> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl Eth0RxData3Pin for Pin<Mio26> {}
impl TxClockPin for Pin<Mio28> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxControlPin for Pin<Mio33> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData0Pin for Pin<Mio29> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData1Pin for Pin<Mio30> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData2Pin for Pin<Mio31> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData3Pin for Pin<Mio32> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxClockPin for Pin<Mio34> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxControlPin for Pin<Mio39> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData0Pin for Pin<Mio35> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData1Pin for Pin<Mio36> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData2Pin for Pin<Mio37> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData3Pin for Pin<Mio38> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl Eth1TxClockPin for Pin<Mio28> {}
impl Eth1TxControlPin for Pin<Mio33> {}
impl Eth1TxData0Pin for Pin<Mio29> {}
impl Eth1TxData1Pin for Pin<Mio30> {}
impl Eth1TxData2Pin for Pin<Mio31> {}
impl Eth1TxData3Pin for Pin<Mio32> {}
impl Eth1RxClockPin for Pin<Mio34> {}
impl Eth1RxControlPin for Pin<Mio39> {}
impl Eth1RxData0Pin for Pin<Mio35> {}
impl Eth1RxData1Pin for Pin<Mio36> {}
impl Eth1RxData2Pin for Pin<Mio37> {}
impl Eth1RxData3Pin for Pin<Mio38> {}
/// Calculate the CPU 1x clock divisor required to achieve a clock speed which is below
/// 2.5 MHz, as specified by the 802.3 standard.
pub fn calculate_mdc_clk_div(arm_clks: &ArmClocks) -> Option<MdcClockDivisor> {
let div = arm_clks.cpu_1x_clk().raw().div_ceil(MAX_MDC_SPEED.raw());
let div = arm_clks
.cpu_1x_clk()
.to_raw()
.div_ceil(MAX_MDC_SPEED.to_raw());
match div {
0..8 => Some(MdcClockDivisor::Div8),
8..16 => Some(MdcClockDivisor::Div16),
@@ -324,26 +269,26 @@ const IRQ_CLEAR_ALL: InterruptStatus = InterruptStatus::builder()
.build();
impl Ethernet {
/// Creates a new Ethernet instance with the given configuration while also
/// configuring all the necessary MIO pins.
/// Creates a new Ethernet instance for the Ethernet 0 block with the given configuration while
/// also configuring all the necessary MIO pins.
#[allow(clippy::too_many_arguments)]
pub fn new_with_mio<
TxClock: TxClockPin,
TxControl: TxControlPin,
TxData0: TxData0Pin,
TxData1: TxData1Pin,
TxData2: TxData2Pin,
TxData3: TxData3Pin,
RxClock: RxClockPin,
RxControl: RxControlPin,
RxData0: RxData0Pin,
RxData1: RxData1Pin,
RxData2: RxData2Pin,
RxData3: RxData3Pin,
pub fn new_with_mio_eth_0<
TxClock: Eth0TxClockPin,
TxControl: Eth0TxControlPin,
TxData0: Eth0TxData0Pin,
TxData1: Eth0TxData1Pin,
TxData2: Eth0TxData2Pin,
TxData3: Eth0TxData3Pin,
RxClock: Eth0RxClockPin,
RxControl: Eth0RxControlPin,
RxData0: Eth0RxData0Pin,
RxData1: Eth0RxData1Pin,
RxData2: Eth0RxData2Pin,
RxData3: Eth0RxData3Pin,
MdClock: MdClockPin,
MdIo: MdIoPin,
>(
mut ll: ll::EthernetLowLevel,
ll: ll::EthernetLowLevel,
config: EthernetConfig,
tx_clk: TxClock,
tx_ctrl: TxControl,
@@ -352,6 +297,59 @@ impl Ethernet {
rx_ctrl: RxControl,
rx_data: (RxData0, RxData1, RxData2, RxData3),
md_pins: Option<(MdClock, MdIo)>,
) -> Self {
Self::new_with_mio(
ll, config, tx_clk, tx_ctrl, tx_data, rx_clk, rx_ctrl, rx_data, md_pins,
)
}
/// Creates a new Ethernet instance for the Ethernet 1 block with the given configuration while
/// also configuring all the necessary MIO pins.
#[allow(clippy::too_many_arguments)]
pub fn new_with_mio_eth_1<
TxClock: Eth1TxClockPin,
TxControl: Eth1TxControlPin,
TxData0: Eth1TxData0Pin,
TxData1: Eth1TxData1Pin,
TxData2: Eth1TxData2Pin,
TxData3: Eth1TxData3Pin,
RxClock: Eth1RxClockPin,
RxControl: Eth1RxControlPin,
RxData0: Eth1RxData0Pin,
RxData1: Eth1RxData1Pin,
RxData2: Eth1RxData2Pin,
RxData3: Eth1RxData3Pin,
MdClock: MdClockPin,
MdIo: MdIoPin,
>(
ll: ll::EthernetLowLevel,
config: EthernetConfig,
tx_clk: TxClock,
tx_ctrl: TxControl,
tx_data: (TxData0, TxData1, TxData2, TxData3),
rx_clk: RxClock,
rx_ctrl: RxControl,
rx_data: (RxData0, RxData1, RxData2, RxData3),
md_pins: Option<(MdClock, MdIo)>,
) -> Self {
Self::new_with_mio(
ll, config, tx_clk, tx_ctrl, tx_data, rx_clk, rx_ctrl, rx_data, md_pins,
)
}
// Creates a new Ethernet instance with the given configuration while also
// configuring all the necessary MIO pins.
#[allow(clippy::too_many_arguments)]
fn new_with_mio<MdClock: MdClockPin, MdIo: MdIoPin>(
mut ll: ll::EthernetLowLevel,
config: EthernetConfig,
tx_clk: impl MioPin,
tx_ctrl: impl MioPin,
tx_data: (impl MioPin, impl MioPin, impl MioPin, impl MioPin),
rx_clk: impl MioPin,
rx_ctrl: impl MioPin,
rx_data: (impl MioPin, impl MioPin, impl MioPin, impl MioPin),
md_pins: Option<(MdClock, MdIo)>,
) -> Self {
Self::common_init(&mut ll, config.mac_address);
let tx_mio_config = zynq7000::slcr::mio::Config::builder()
@@ -594,7 +592,7 @@ impl Ethernet {
}
#[inline]
pub fn regs(&mut self) -> &MmioEthernet<'static> {
pub fn regs(&mut self) -> &MmioRegisters<'static> {
&self.ll.regs
}
@@ -608,7 +606,7 @@ impl Ethernet {
}
#[inline]
pub fn regs_mut(&mut self) -> &mut MmioEthernet<'static> {
pub fn regs_mut(&mut self) -> &mut MmioRegisters<'static> {
&mut self.ll.regs
}

Some files were not shown because too many files have changed in this diff Show More