Compare commits

...

276 Commits
eth ... master

Author SHA1 Message Date
Sebastien Bourdeauducq 12975de2e1 flake: add missing attributes on rustc (for nixpkgs-unstable compat) 2024-11-16 17:31:34 +08:00
Sebastien Bourdeauducq 8c404829ef flake: update nixpkgs 2024-11-16 17:15:03 +08:00
Sebastien Bourdeauducq 8f041b017c switch to oxalica rust overlay 2024-11-16 17:14:37 +08:00
newell 5815baf88b Reorder Status.get_link to check for higher speeds before slower. 2024-11-15 13:09:17 -08:00
newell cc20478d91 Add i2c support 2024-10-04 23:38:28 -07:00
Sebastien Bourdeauducq 5ef3016554 flake: update dependencies 2024-09-30 14:15:10 +08:00
newell 6a45a0dfd0 ebaz4205 support
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-09-30 14:08:58 +08:00
Sebastien Bourdeauducq b2b3e5c933 flake: update dependencies 2024-07-09 17:03:22 +02:00
Simon Renblad 0efbbe39fe llvm: patch for artiq-zynq release-7 2024-06-06 14:06:56 +08:00
Sebastien Bourdeauducq 51b8111e79 flake: move to nixos 24.05 2024-05-28 17:37:50 +08:00
Simon Renblad 46dc25b89e add LLVM copy from nixpkgs 2024-05-24 10:57:12 +08:00
Sebastien Bourdeauducq 731684abb4 flake: switch to nixpkgs master, update dependencies 2024-05-09 10:02:43 +08:00
Florian Agbuya 195a21fe78 use nix format for arm gnu toolchain 2024-03-25 17:20:19 +08:00
Florian Agbuya 96cefe6f06 update fsbl source 2024-03-25 17:16:56 +08:00
morgan 7c58c0cf43 abort: rename to exception_vectors 2024-03-07 12:26:28 +08:00
morgan 9005b73316 experiment: add set_vector_table example 2024-03-07 12:03:49 +08:00
morgan b1994dbe16 abort: support custom exception vector table addr 2024-03-07 12:03:29 +08:00
morgan 5bd336c961 add support for using custom FIQ handler
cfg: add dummy FIQ handler cfg
abort: gate dummy FIQ handler using cfg
2024-02-02 17:01:07 +08:00
morgan 298f64a2f9 boot: enable FIQ
asm: add FIQ enable instruction
2024-02-02 16:34:28 +08:00
morgan 4168eb63a7 GIC: fix wrong core target config when enabling interrupt (#109)
# Summary

- Before the patch, an extra 1 is added to `target_cpu` and the interrupt will be configured to the wrong CPU target.

| target_cpu | bits set before patch | bits set after patch   |
| -----------| -----------                      | -----------                       |
| core0      | 0b10 (enable interrupt on core1) | 0b01 (enable interrupt on core0)  |
| core1      | 0b11 (enable interrupt on core0 & core1)| 0b10 (enable interrupt on core1) |

- [Correct ICDIPTR Register configuration from AMD](https://docs.xilinx.com/r/en-US/ug585-zynq-7000-SoC-TRM/Software-Generated-Interrupts-SGI?tocId=0TsxAmy8MHRPDsayG96K1Q)

Reviewed-on: M-Labs/zynq-rs#109
Co-authored-by: morgan <mc@m-labs.hk>
Co-committed-by: morgan <mc@m-labs.hk>
2023-12-19 18:41:03 +08:00
Sebastien Bourdeauducq a43b8bf64e mkbootimage: work around buffer overflow 2023-12-03 16:16:22 +08:00
Sebastien Bourdeauducq 91bae572f9 fix "unknown argument '-Wl,--undefined=AUDITABLE_VERSION_INFO'" 2023-12-03 11:09:31 +08:00
Sebastien Bourdeauducq 301f9236e5 switch to nixpkgs cargo-xbuild (with workaround for rust nonsense) 2023-12-03 10:55:05 +08:00
Sebastien Bourdeauducq 55b36ee37e switch to new nixpkgs release 2023-12-03 10:45:47 +08:00
morgan 24c804e6f0 libcortex_a9: add interrupt exit support for interrupt_handler macro (#107)
Co-authored-by: morgan <mc@m-labs.hk>
Co-committed-by: morgan <mc@m-labs.hk>
2023-11-20 12:30:27 +08:00
Sebastien Bourdeauducq be672ab662 flake: update dependencies 2023-10-20 17:46:01 +08:00
mwojcik 0106430805 remove gpio reset 2023-10-18 17:33:19 +08:00
jmatyas c15b54f92b kasli-soc: add support for PHY_RST GPIO 2023-08-31 12:58:59 +02:00
Sebastien Bourdeauducq de42a5d1b2 flake: update to LLVM 14 2023-08-07 23:26:58 +08:00
Sebastien Bourdeauducq ff03bf92a3 flake: update dependencies 2023-08-07 23:23:02 +08:00
Sebastien Bourdeauducq f20c008264 flake: nixpkgs 23.05 2023-05-27 18:20:53 +08:00
Sebastien Bourdeauducq 67dbb5932f flake: update mkbootimage 2022-11-30 22:36:56 +08:00
Sebastien Bourdeauducq dab5c6f070 flake: NixOS 22.11, update dependencies 2022-11-30 22:29:58 +08:00
Egor Savkin 0a3a777652 Fix soft_rst bit, add reboot function 2022-10-07 12:57:56 +08:00
mwojcik 92b3f3e1dd panic: turn on error_led for kasli_soc 2022-08-26 17:22:42 +08:00
mwojcik f586ba5a13 experiments: add error led test for kasli_soc 2022-08-26 17:22:42 +08:00
mwojcik 42cc256812 add error led 2022-08-26 17:22:42 +08:00
occheung 043a152b91 szl: change CPU frequency of Kasli-SoC to 1 GHz 2022-07-20 15:16:15 +08:00
Sebastien Bourdeauducq 6cd32f6ee0 flake: update dependencies 2022-05-31 21:02:28 +08:00
mwojcik 605c8f73a6 mutex: add async version of lock 2022-05-25 10:22:16 +08:00
mwojcik 56c27e98e4 config: add "fat_lfn" feature 2022-04-07 15:44:07 +08:00
mwojcik f496da4f3e config: create config dir if not present 2022-04-06 16:17:35 +08:00
mwojcik 1c8e2c318c eth phy: reset page to 0 on kasli-soc for ident 2022-03-10 17:11:41 +08:00
mwojcik 67880b4e0b openocd: moved common code from target files 2022-03-10 17:05:25 +08:00
Sebastien Bourdeauducq e96222ff6f update dependencies 2022-03-10 17:01:37 +08:00
mwojcik dc1ca3d3d0 szl: fallback to netboot if boot mode not supported 2022-03-10 16:48:00 +08:00
mwojcik c4c52c33b4 eth phy: use unreachable!() for impossible values 2022-03-10 16:45:29 +08:00
mwojcik 072fd5f015 eth phy: set LEDs only on Kasli-SoC 2022-03-10 16:42:25 +08:00
mwojcik 2b3c7e4b2f eth leds:
* break led0 from led1 - similar, but not the same settings (led2 not implemented)
* fix values, make it compilable
* set default behavior (one led for link, other for activity)
2022-03-10 16:42:25 +08:00
mwojcik 84d6d391ce libboard_zynq: eth phy for Marvell 88E1518
* add support for LED control registers
* support for registers on different pages
2022-03-10 16:42:25 +08:00
mwojcik 43e0440911 Kasli-SoC: Get MAC address from EEPROM (#90)
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-03-07 18:01:44 +08:00
mwojcik 3e95df1f64 pca954x_select: api supports no channel enabled 2022-02-11 13:46:51 +08:00
mwojcik 26ab2927b9 pca954x: log detected type 2022-02-11 12:00:39 +08:00
mwojcik dacc816eb4 pca954x: improve code, fix I2C_SW_RESET behavior 2022-02-11 11:22:53 +08:00
mwojcik fbc783d7ad flake: add pyftdi to env 2022-02-11 11:19:15 +08:00
mwojcik 14b0247716 pca954x: fix to work on cold boot 2022-02-09 17:23:33 +08:00
mwojcik bc41b91192 adjust pca address 2022-02-08 16:39:12 +08:00
mwojcik 3efc682bd6 add pca954x autodetection, pca9547 support 2022-02-08 15:40:17 +08:00
mwojcik e5e646f40e Additional outputs to flakes (#86)
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-02-04 18:42:35 +08:00
Sebastien Bourdeauducq 2e7cfe04c8 update copyright year 2022-01-27 18:00:24 +08:00
mwojcik 24e0d724f2 nix flakes support (#85)
Reviewed-on: M-Labs/zynq-rs#85
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-01-27 16:26:33 +08:00
Sebastien Bourdeauducq 823e909281 openocd: upgrade deprecated syntax 2021-12-03 18:42:45 +08:00
Sebastien Bourdeauducq 57d8d8fbc7 update dependencies 2021-08-09 15:05:05 +08:00
Sebastien Bourdeauducq a4902966d1 update Rust Mozilla overlay 2021-08-09 14:53:08 +08:00
Sebastien Bourdeauducq 2c161720fa revert a11cb852a8 2021-07-05 13:45:22 +08:00
topquark12 a42e5a95ff phy: fix issue 78, scan PHY MDIO addr starting at 0 2021-07-05 13:25:22 +08:00
Sebastien Bourdeauducq 03122f810a openocd: adapt to newer version (zc706) 2021-06-25 17:43:05 +08:00
Sebastien Bourdeauducq c7e9f85de2 openocd: adapt to newer version 2021-06-25 17:01:02 +08:00
Sebastien Bourdeauducq 104d1ef232 openocd: name kasli_soc consistently 2021-06-25 16:45:38 +08:00
Sebastien Bourdeauducq b0ec74d764 i2c: half_period -> unit_delay 2021-06-25 16:26:53 +08:00
Sebastien Bourdeauducq 4159aab6c8 i2c: conservative timing, avoid SCL/SDA races. Closes #83 2021-06-25 16:26:04 +08:00
Sebastien Bourdeauducq d18c77c0eb i2c: fix error messages 2021-06-25 16:23:50 +08:00
Sebastien Bourdeauducq 8dbe2cf9f3 add SZL multi-board package 2021-06-25 16:21:49 +08:00
Sebastien Bourdeauducq 040d41fd76 update cargosha256 2021-06-19 22:47:21 +08:00
Sebastien Bourdeauducq 9180918619 update dependencies 2021-06-19 22:40:09 +08:00
Sebastien Bourdeauducq 8ec7216e9a update cargo-xbuild 2021-06-19 22:25:07 +08:00
Sebastien Bourdeauducq 0e9d3f146a remove use of deprecated stdenv.lib 2021-06-19 22:04:14 +08:00
Sebastien Bourdeauducq 411eebd96c i2c: configure I2C_SW_RESET MIO on Kasli-SoC 2021-06-19 18:49:51 +08:00
sb10q e31adb722d README: add Kasli-SoC 2021-05-30 07:37:27 +08:00
Sebastien Bourdeauducq 759bf95a76 add Kasli-SoC POR control program 2021-05-29 17:43:21 +08:00
Sebastien Bourdeauducq f83ef218de update cargosha256 2021-05-29 14:13:51 +08:00
Sebastien Bourdeauducq 062b894a4b use updated upstream core_io 2021-05-29 14:09:11 +08:00
Sebastien Bourdeauducq 2f9019ca0f szl: update copyright year 2021-05-29 14:01:29 +08:00
Sebastien Bourdeauducq 7b29ab2628 update dependencies
Prevent cargo from messing up core_io this time.
2021-05-29 13:10:54 +08:00
Sebastien Bourdeauducq a9697ec8d8 Revert "update dependencies"
This reverts commit 0220cf19eb.
2021-05-29 13:05:35 +08:00
Sebastien Bourdeauducq 0220cf19eb update dependencies 2021-05-29 13:03:35 +08:00
Sebastien Bourdeauducq a11cb852a8 libboard_zynq: work around Kasli-SoC MDIO breakage (#78) 2021-05-29 12:50:28 +08:00
Sebastien Bourdeauducq 42cdedae91 openocd: fix Kasli-SoC nSRST 2021-05-29 12:17:59 +08:00
Sebastien Bourdeauducq 823ec374ba kasli-soc: internal JTAG 2021-05-28 18:44:07 +08:00
Sebastien Bourdeauducq 666c077cef openocd: add Kasli-SoC (with digilent external JTAG) 2021-05-28 18:22:17 +08:00
Sebastien Bourdeauducq def17829d9 LGPLv3 2021-04-06 16:46:38 +08:00
Astro ba252e72da libasync: provide get/set ack_delay (new in smoltcp 0.7.0) 2021-02-08 03:06:51 +01:00
Astro a2416f26a0 default.nix: update cargoSha256* 2021-02-04 18:29:00 +01:00
Astro bf9f1e33c8 update smoltcp 0.6.0 -> 0.7.0 2021-02-04 18:29:00 +01:00
Astro 481a0eb25f Revert "default.nix: update cargoSha256*"
This reverts commit 89e4d61dc0.
2021-02-04 18:27:44 +01:00
Astro 05c30dd52e Revert "update smoltcp 0.6.0 -> 0.7.0"
This reverts commit ccf758cc85.
2021-02-04 18:27:42 +01:00
Astro f816a04cef Revert "Revert "update smoltcp 0.6.0 -> 0.7.0""
This reverts commit faa64c2ce8.
2021-02-04 16:22:39 +01:00
Astro faa64c2ce8 Revert "update smoltcp 0.6.0 -> 0.7.0"
This reverts commit ccf758cc85.

Blocked by: https://github.com/smoltcp-rs/smoltcp/issues/415
2021-02-04 16:22:16 +01:00
Astro 89e4d61dc0 default.nix: update cargoSha256* 2021-02-04 00:32:57 +01:00
Astro 0b07e9fefc update Rust
the old nightly crashes with smoltcp 0.7.0
2021-02-03 22:42:00 +01:00
Astro ccf758cc85 update smoltcp 0.6.0 -> 0.7.0 2021-02-03 22:41:32 +01:00
pca006132 78d58d17ec libsupport_zynq: fix stack pointer problems 2021-01-28 12:33:06 +08:00
pca006132 06c646e61f libcortex_a9: added interrupt_handler macro
closes #73
2021-01-28 11:41:34 +08:00
pca006132 7681745282 experiments: fix broken build 2021-01-26 12:53:26 +08:00
pca006132 6e6612bc3e szl: move enable l2c to artiq-zynq 2021-01-26 11:19:33 +08:00
pca006132 b50540915d libcortex_a9: memcpy throughput optimization 2021-01-26 11:16:37 +08:00
pca006132 4aa252546f experiments: updated experiment 2021-01-15 17:08:20 +08:00
pca006132 b42869e655 libsupport_zynq: removed naked annotation 2021-01-15 17:08:20 +08:00
pca006132 d62c77e6e0 libcortex_a9: fixed usage of deprecated APIs 2021-01-15 17:08:20 +08:00
pca006132 4555afa624 libsupport_zynq: custom memcpy 2021-01-15 17:08:14 +08:00
pca006132 5923a66512 libsupport_zynq/boot: enable FPU on boot 2021-01-15 16:45:27 +08:00
pca006132 2c6cc58c07 updated rust and compiler_builtins 2021-01-15 16:45:11 +08:00
Astro b4d91e7904 nix/rust-platform: remove rustcSrc which had to be kept in sync 2021-01-15 00:52:22 +01:00
Astro 4c79c797d5 default.nix: filterSource 2021-01-15 00:51:45 +01:00
Astro 966e43e14e update dependency: linked_list_allocator 2020-12-24 00:52:14 +01:00
Astro 8432ff3e30 default.nix: update cargoSha256* 2020-11-20 17:32:52 +01:00
Astro 1cd4056370 libboard_zynq: remove unused eth phy name information 2020-11-20 17:21:38 +01:00
Astro ddff295ae1 libboard_zynq: implement eth hot-plugging 2020-11-20 17:12:22 +01:00
Astro 379b6b973a libboard_zynq: add support for target_kasli_soc's Marvell88E1512 eth phy 2020-11-19 20:38:10 +01:00
Astro 178ab38e35 experiments: delint 2020-11-19 20:27:05 +01:00
Astro 3172aba1a8 libboard_zynq: improve i2c doc 2020-11-19 20:26:48 +01:00
Astro a62ca507d0 libregister: allow rustdoc for register! macro 2020-11-19 20:26:18 +01:00
Astro 975202a653 libboard_zynq: enable i2c+eeprom for target_kasli_soc 2020-11-19 20:17:36 +01:00
Astro d76a77b443 libregister: s/libregister::/$crate::/ 2020-11-19 20:16:36 +01:00
Astro 500472b2a8 add feature target_kasli_soc to libsupport_zynq, libconfig, experiments, szl, default.nix 2020-11-19 19:41:38 +01:00
Astro 02217f27d1 libcortex_a9: remove useless target_* features 2020-11-19 19:38:57 +01:00
Astro a3eabf1947 libboard_zynq: prepare target_kasli_soc 2020-11-19 19:28:17 +01:00
Astro a32d7abb9a libboard_zynq: rename ddr DCI_FREQ to DCI_MAX_FREQ 2020-11-19 19:21:38 +01:00
Sebastien Bourdeauducq 0e8354faa1 remote_run.sh: clean up getopts 2020-11-18 17:45:43 +08:00
Sebastien Bourdeauducq 07f161d9d8 libconfig: fix ipv6 address configuration key 2020-11-18 10:47:48 +08:00
Sebastien Bourdeauducq ef160aa841 README: update board support info 2020-11-18 10:36:55 +08:00
Astro cb50c8d61b update deps, pin compiler_builtins to 0.1.35 2020-11-13 19:40:38 +01:00
Astro 7bbd16f143 default.nix: build crates for all targets 2020-11-13 18:07:22 +01:00
Astro 0714162113 rename target_cora_z7_10 to target_coraz7 globally 2020-11-13 17:56:47 +01:00
Astro 5b2c779cba libboard_zynq: delint ps7_init 2020-11-13 00:23:56 +01:00
Astro 0a40d4f36d libboard_zynq: fix zc706 build 2020-11-13 00:23:38 +01:00
Astro 55f8d02da8 libboard_zynq: remove ddr-only ps7_init for redpitaya 2020-11-13 00:12:43 +01:00
Astro 990fa56d6a libboard_zynq: complete ddr without ps7_init for redpitaya 2020-11-13 00:10:34 +01:00
Astro 8fd317d580 libboard_zynq: remove ps7_init for cora_z7_10 2020-11-11 14:21:48 +01:00
Astro 07fedddad9 libboard_zynq: doc ddr size limitation, correct target_redpitaya to 512MB 2020-11-11 13:25:55 +01:00
Astro 0fde82a982 szl: add target_cora_z7_10 2020-11-10 20:57:31 +01:00
Astro dffe3cb251 libboard_zynq: rm superfluous ddr settings for cora_z7_10 2020-11-10 20:53:46 +01:00
Astro b9323653bb libboard_zynq: complete ddr without ps7_init for cora_z7_10 2020-11-10 14:33:31 +01:00
Astro 515d3bb381 libboard_zynq: configure ddr while keeping rstb low 2020-11-08 22:47:59 +01:00
Astro 7e22010d7d libboard_zynq: fix pll_cp/pll_res swap in ClockSource::setup() 2020-11-08 22:46:43 +01:00
Astro 9ee77d8f44 libboard_zynq: indent ps7_init/cora_z7_10 2020-11-08 19:32:31 +01:00
Astro e508b78b3e libboard_zynq: add ps7_init for cora_z7_10 2020-11-08 19:28:59 +01:00
Astro aef010cb14 openocd: remove xilinx-tcl on Cora Z7 2020-11-08 18:44:41 +01:00
pca006132 d623913535 libconfig: fixed szl build.
to_lowercase would handle unicode which causes a huge increase in rodata
size, we have to use to_ascii_lowercase.
2020-11-04 20:47:40 +08:00
pca006132 0efc7a616f libconfig: implemented config write
Config write and config remove are now implemented.
Config keys are now treated case insensitively, which is consistence to
the filesystem behavior.
BOOT.BIN can be replaced by setting the config key "boot".
2020-10-30 16:11:18 +08:00
pca006132 22833ef0c6 libconfig/sd_reader: added FAT16_LBA ID 2020-10-17 13:37:18 +08:00
Sebastien Bourdeauducq 80d12d5780 libconfig: make default IP/MAC depend on board 2020-10-14 12:53:32 +08:00
Sebastien Bourdeauducq 4dd8c93729 add SZL for Red Pitaya 2020-10-14 12:35:23 +08:00
Sebastien Bourdeauducq 6266d28095 expose patched cargo-xbuild 2020-10-13 18:43:37 +08:00
Sebastien Bourdeauducq 1796d4e236 update some dependencies
Older versions do not compile with newer Rust.
2020-10-13 18:28:42 +08:00
Sebastien Bourdeauducq 56c94b0855 patch cargo-xbuild to ensure copied Cargo.lock is writable
https://github.com/rust-osdev/cargo-xbuild/issues/96
2020-10-13 18:27:52 +08:00
Sebastien Bourdeauducq 34a63d7732 use new location for libraries in Rust source 2020-10-13 18:27:01 +08:00
Sebastien Bourdeauducq 27d310a937 update Rust
Needed for compatibility with recent cargo-xbuild.
2020-10-13 18:25:39 +08:00
pca006132 f60d0589cc fix ps7_init compilation error and warnings 2020-10-01 00:17:47 +08:00
Sebastien Bourdeauducq 7c9edfdbd5 README: add introduction 2020-09-29 16:27:44 +08:00
pca006132 c336e450b1 libboard_zynq/eth/phy: add PEF7071 2020-09-29 16:01:54 +08:00
pca006132 6af453494b libboard_zynq/ddr: use ps7_init for redpitaya ddr 2020-09-26 17:01:37 +08:00
Sebastien Bourdeauducq ac3e6983b0 experiments: fix zc706 build 2020-09-09 21:30:56 +08:00
Sebastien Bourdeauducq 338f918531 experiments: update banner 2020-09-09 21:29:25 +08:00
Sebastien Bourdeauducq 4751fd6011 default.nix: build redpitaya-experiments 2020-09-09 21:28:32 +08:00
Sebastien Bourdeauducq e601ac9c45 remove flash support
PITA to get to work and most boards have SD.
2020-09-09 20:13:13 +08:00
Sebastien Bourdeauducq a6955edf14 add Red Pitaya support (WIP) 2020-09-09 20:10:05 +08:00
Sebastien Bourdeauducq c634313d5e update authors in cargo.toml 2020-09-09 19:36:25 +08:00
Sebastien Bourdeauducq 7360984efb add libconfig, libcoreio, szl from artiq-zynq a277e89b3ad; update dependencies 2020-09-09 17:56:50 +08:00
Sebastien Bourdeauducq 82794d3abd default.nix: make naming consistent with artiq-zynq 2020-09-09 17:12:08 +08:00
Sebastien Bourdeauducq 450ccef18e sync nix files with artiq-zynq c3f9a76f2a; add fsbl 2020-09-09 16:51:50 +08:00
Sebastien Bourdeauducq 4e18368aaf remove obsolete build.sh 2020-09-09 15:03:17 +08:00
Sebastien Bourdeauducq 75494421c5 cargo: remove unmaintained runner 2020-09-09 15:01:39 +08:00
Sebastien Bourdeauducq c4fb7b4c41 cargo: remove unmaintained dev profile 2020-09-09 15:01:03 +08:00
Sebastien Bourdeauducq a51f8f2eea openocd: remove ps7_init on Cora Z7 2020-09-09 15:00:09 +08:00
Sebastien Bourdeauducq 7680de26f0 openocd: sync with artiq-zynq 8bb1727e64 2020-09-09 14:58:18 +08:00
Sebastien Bourdeauducq 7edd192c3b remove outdated/unmaintained files 2020-09-09 14:57:03 +08:00
pca006132 4fef8a7192 libasync/executor: reduced reallocation for vector 2020-09-07 16:13:51 +08:00
pca006132 ae244082ed more cpu options 2020-09-07 16:13:51 +08:00
Sebastien Bourdeauducq 66c66447dd fix some compilation warnings 2020-09-06 00:17:59 +08:00
pca006132 02c67051e8 CPU options for better performance
L2 cache options and prefetch options
2020-09-04 16:38:48 +08:00
pca006132 08fd1391c5 libcortex_a9/mmu: enabled program flow prediction 2020-09-04 13:18:39 +08:00
pca006132 a116142f63 libsupport_zynq/ram: check ptr range for deallocation 2020-09-03 12:56:10 +08:00
pca006132 157439bc88 libcortex_a9/semaphore: mark new as const fn 2020-09-02 09:51:52 +08:00
pca006132 a73df780d0 libboard_zynq/slcr: fixed boot mode pins value
Notice that the bits in the table in UG585 are out of order.
2020-08-31 12:35:11 +08:00
pca006132 e73ec731aa libboard_zynq/smoltcp: default without ipv6 support
SZL netboot binary size too large with ipv6.
We can enable the ipv6 support in the runtime crate instead.
2020-08-31 12:07:20 +08:00
pca006132 73e4e4fd03 libcortex_a9/sync_channel: fixed memory leak
ptr::drop_in_place would not drop the box content properly,
the best way is to convert it back to a box and implicitly drop it.
2020-08-27 17:03:26 +08:00
Sebastien Bourdeauducq 273f9ea72b libboard_zynq/eth: fix comment 2020-08-24 21:47:10 +08:00
pca006132 671968bac3 libboard_zynq/eth: fixed tx lost packet 2020-08-24 15:51:01 +08:00
pca006132 39f672dde8 libasync/smoltcp/mod: prevent duplicated wakers 2020-08-24 15:25:03 +08:00
pca006132 c13ca614ef libcortex_a9/mutex: use AcqRel for CAS operations 2020-08-24 15:24:20 +08:00
pca006132 bb09d25378 libboard_zynq/ethernet: ethernet fix and config 2020-08-21 13:34:02 +08:00
pca006132 a1f859637a experiments: enabled L2 cache
...and removed some trailing spaces
2020-08-20 13:02:28 +08:00
pca006132 7cb2669c3b Updated cargo dependencies 2020-08-20 13:01:49 +08:00
pca006132 511c906d4d libcortex_a9/uncached: fixed mmu setting 2020-08-20 13:01:49 +08:00
pca006132 1ba0aa450f libsupport_zynq/boot: fix cache mainteinance opertaions 2020-08-20 13:01:49 +08:00
pca006132 283bc9b810 libcortex_a9: added L2 cache 2020-08-20 13:01:17 +08:00
Astro b268fe015a stdio::drop_uart(): add delay 2020-08-17 19:38:41 +02:00
Astro 64db9b0142 Merge pull request 'libboard_zynq: dead code, peripheral & regblock ctor names consistency' (#63) from harry/zynq-rs:cleanup into master 2020-08-17 23:38:22 +08:00
Astro 4b258c19f5 libasync: improve scheduling fairness between block_on and spawned tasks
in the libasync::smoltcp::Sockets::run() case the block_on iface.poll
loop would progress just one task before. now all tasks get to run in
each iteration.
2020-08-17 00:58:12 +02:00
Harry Ho 1a96a7550a libboard_zynq: make RegisterBlock constructors more consistent 2020-08-13 14:49:26 +08:00
Harry Ho 36947104e3 libboard_zynq: make constructor names more consistent 2020-08-13 13:31:53 +08:00
Harry Ho 11089d8a64 i2c: delete dead code 2020-08-12 16:51:25 +08:00
harry c69cd9951e Update README and build.sh (#59)
update build.sh to use rpi-4 directly; fix README

README: update build instruction

build.sh: fix missing package argument

Co-authored-by: Harry Ho <hh@m-labs.hk>
2020-08-11 11:24:21 +08:00
Harry Ho 76a4cac873 i2c: disable its usage on Cora Z7-10 2020-08-10 14:24:13 +08:00
Harry Ho 4614ed1371 i2c: simplify ctor_common() 2020-08-08 10:06:11 +08:00
pca006132 fa07bdb681 libcortex_a9/mmu: share ocm3.
This fixes issue #54.
2020-08-07 15:10:38 +08:00
Harry Ho 4565a75766 experiments: add I2C bitbang EEPROM writing/reading example 2020-08-07 11:10:20 +08:00
Harry Ho 16b2df91ca i2c: fix GPIO register mapping, I2C control & EEPROM write operations 2020-08-07 11:10:18 +08:00
Harry Ho f7d3135ec7 i2c: implement EEPROM operations; add CountDown waiting indication 2020-08-05 20:10:30 +08:00
Harry Ho c60230af25 i2c: implement basic i2c bitbanging 2020-08-05 17:35:33 +08:00
pca006132 e8ba73a8c7 Updated build instruction. 2020-08-05 17:08:24 +08:00
pca006132 3958953ceb libcortex_a9/sync_channel: added drop_elements function. 2020-08-05 15:29:28 +08:00
Sebastien Bourdeauducq a36a82d86d reduce ethernet verbosity 2020-08-04 22:15:01 +08:00
pca006132 25c6d5eeaa Changes usage of sev/wfe to spinlock functions. 2020-08-04 13:54:19 +08:00
pca006132 9e97102e12 libcortex_a9: implemented semaphore. 2020-08-04 13:34:08 +08:00
pca006132 b65606f2d0 libcortex_a9/sync_channel: added reset. 2020-08-03 15:50:31 +08:00
pca006132 ee4089c52e updated cargoSha256 2020-08-03 14:59:49 +08:00
pca006132 36c3fbdacd experiments: fixed linker script. 2020-08-03 14:48:44 +08:00
pca006132 8328ffc66b libsupport_zynq/ram: allow single allocator. 2020-08-03 14:48:44 +08:00
pca006132 84041a3154 libsupport_zynq/ram: use core0 allocator by default. 2020-08-03 14:48:44 +08:00
pca006132 5850401d72 libsupport_zynq/ram: split allocators for two cores. 2020-08-03 14:48:44 +08:00
pca006132 ccce37dffd linked_list_allocator: upgraded to 0.8.4
So we get the `used` function to check heap usage.
2020-08-03 14:48:17 +08:00
pca006132 3bbd1513fb build.sh: specify build experiments
Otherwise we cannot turn off the default feature for libsupport_zynq.
https://github.com/rust-lang/cargo/issues/8366#issuecomment-644995218
2020-08-03 14:09:36 +08:00
pca006132 7d38c53c18 libsupport_zynq/abort: moved core1 restart code to user code. 2020-08-03 14:09:36 +08:00
pca006132 02a2c4d1e3 experiments: updated example. 2020-08-03 12:35:17 +08:00
pca006132 12669124a4 libcortex_a9/mutex: added interrupt critical section mask. 2020-08-03 12:35:17 +08:00
pca006132 8f0a6bd5ea libsupport_zynq/abort: restart core1 main on core1 IRQ#0. 2020-08-03 12:35:17 +08:00
pca006132 c1f61b5673 libcortex_a9/boot: enable IRQ on reset. 2020-08-03 12:35:17 +08:00
pca006132 2927c43309 libboard_zynq/gic: refactored and added SGI functions. 2020-08-03 12:35:17 +08:00
Astro 187801c4a7 gic: start implementation 2020-08-03 12:35:17 +08:00
pca006132 91ece367f2 libboard_zynq/mpcore: added generated register definitions 2020-08-03 12:35:17 +08:00
Astro 1f05e6977e eth::phy: replace ExtendedStatus with PSSR 2020-07-29 21:49:18 +02:00
Astro e408a8b22d eth::phy::extended_status: fix cap_1000base_x_full() bit position 2020-07-29 21:29:28 +02:00
Astro 27effb6257 eth::phy: s/Marvel/Marvell/ 2020-07-29 20:08:38 +02:00
Astro de5f605d60 eth: refactor peripheral instance into type parameter, improve clock setup 2020-07-29 19:45:01 +02:00
pca006132 ad47521e4b libsupport_zynq/boot: fixed core1 disable. 2020-07-28 12:36:23 +08:00
pca006132 c50e72f91e experiments: use OCM instead of OCM3 (#54) 2020-07-28 12:36:23 +08:00
pca006132 b099c56569 libcortex_a9/sync_channel: new version compiled. 2020-07-28 12:36:16 +08:00
Astro ef4fb598fb ddr: improve dci divisors calculation 2020-07-28 00:43:33 +02:00
Astro 0aa75d3544 experiments: fix timer.get_us() usage 2020-07-22 23:47:57 +02:00
Astro f36b1a610e timer::global: wrap us in Microseconds, impl embedded_hal blocking delay traits 2020-07-22 23:41:15 +02:00
Astro 7f45d10af3 timer::global::CountDown: fix delaying from "up to" to "at least" the timespan 2020-07-22 22:43:10 +02:00
Astro 855d94c48e dmac: remove unused module 2020-07-20 19:42:32 +02:00
Sebastien Bourdeauducq 84f1380f48 libasync: assert that callback consumes data in smoltcp recv 2020-07-19 16:14:29 +08:00
Sebastien Bourdeauducq f8785c3f07 fix some compilation warnings 2020-07-19 15:39:08 +08:00
Sebastien Bourdeauducq 7b78bc0494 libasync: new stream.recv API
M-Labs/artiq-zynq#40 (comment)
2020-07-19 15:34:32 +08:00
Sebastien Bourdeauducq ef88a1313a shell.nix: remove gcc 2020-07-19 15:34:18 +08:00
Astro 484e385160 eth: implement DeviceCapabilities.max_burst_size
this is a hint that /could/ boost TCP performance.
2020-07-16 00:17:13 +02:00
pca006132 074438c3c7 libcortex_a9: added `try_lock` for mutex. 2020-07-15 16:44:01 +08:00
Astro 191abf6b8f mpidr: wrap with proper bitfield getters
Prevents callers from dealing with CORE_MASK.
2020-07-08 00:04:54 +02:00
Sebastien Bourdeauducq 371e59cef5 libboard_zynq: add fpgax_clk_ctrl registers 2020-07-07 19:37:51 +08:00
pca006132 e67efe439b libsupport: fixed core1 restart.
The TRM mentioned that user should stop the clock, de-assert the reset,
and then restart the clock for core reset.

This fixes the kernel restart problem in one of the zc706 board.
2020-07-07 10:17:15 +08:00
Astro e4e7141bf3 ddr: delint 2020-07-06 19:46:18 +02:00
Sebastien Bourdeauducq f68b5896ce remove unused imports 2020-07-06 21:03:36 +08:00
Sebastien Bourdeauducq e430600683 fix exception vectors 2020-07-06 21:02:46 +08:00
Sebastien Bourdeauducq 0c60d684e4 slcr: remove soft reset
Does not work and probably difficult to get to work.
2020-07-06 13:06:10 +08:00
Sebastien Bourdeauducq 6fa3a6bbd9 fix previous commit 2020-07-06 12:11:20 +08:00
Sebastien Bourdeauducq 7082e07a18 experiments: move BSS and stack to OCM3 2020-07-06 11:57:02 +08:00
Sebastien Bourdeauducq 21c0c5cbc8 Revert "simplify ps7_init"
What the simplified ps7_init does can now be reproduced by the DDRC driver.
On the other hand, we are still experiencing crazy Zynq instability issues, so keep the original ps7_init around for debugging.

This reverts commit 9fcf9243f2.
2020-07-06 11:55:04 +08:00
pca006132 90904634cd DDR: fixed register write.
Previously it writes `0x20066`, while the ps7_init set it to be
`0x200066`, notice the 1 more 0.
This should perform the same writes to the registers, so we do not have
to apply the ps7_init in artiq_zynq.
2020-07-06 11:46:37 +08:00
Sebastien Bourdeauducq ae4d3e2455 smoltcp: enable IPv6 2020-07-06 11:30:48 +08:00
Sebastien Bourdeauducq 9fcf9243f2 simplify ps7_init 2020-07-06 00:52:40 +08:00
pca006132 90e33f688a FPU: moved enable function to zc706 2020-07-03 16:02:34 +08:00
Astro f0697c3ec3 ddr: implement additional configuration 2020-07-03 02:20:10 +02:00
Astro b2c707d543 ddr: remove superfluous `_reg` from register names 2020-07-03 02:20:10 +02:00
pca006132 6195ad40c3 libsupport_zynq: make panic handler an optional feature 2020-06-29 10:05:46 +08:00
Sebastien Bourdeauducq dd288912af fix experiments build for Cora 2020-06-28 17:47:33 +08:00
Astro ec252b099c experiments: don't write raw blocks to the sdcard by default 2020-06-26 23:27:28 +02:00
Astro a16c639eaf experiments: add bandwidth tester 2020-06-26 22:36:52 +02:00
Astro c6fa18344e uncached: disable cachable/bufferable 2020-06-26 22:32:49 +02:00
Astro 5c69bbdad6 mmu: fix L1Table.update() flush 2020-06-26 22:31:56 +02:00
Astro c0e66a632c ps7_init: move from experiments to libboard_zynq 2020-06-25 01:40:42 +02:00
Astro b129d3e0df panic: fix CORE_MASK 2020-06-25 01:27:23 +02:00
Astro 1e4be13869 experiments: implement ps7_init::apply() 2020-06-25 01:27:02 +02:00
Astro eea042e2ee experiments: update ps7_init for zc706 2020-06-24 22:23:05 +02:00
134 changed files with 13126 additions and 18971 deletions

View File

@ -1,5 +1,4 @@
[target.armv7-none-eabihf] [target.armv7-none-eabihf]
runner = "./runner.sh"
rustflags = [ rustflags = [
"-C", "link-arg=-Tlink.x", "-C", "link-arg=-Tlink.x",
"-C", "target-feature=a9,armv7-a,neon", "-C", "target-feature=a9,armv7-a,neon",

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target /target
result*

216
Cargo.lock generated
View File

@ -2,181 +2,257 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "bit_field" name = "bit_field"
version = "0.10.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.3.4" version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.10" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "compiler_builtins" name = "compiler_builtins"
version = "0.1.27" version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
[[package]]
name = "core_io"
version = "0.1.20210325"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97f8932064288cc79feb4d343a399d353a6f6f001e586ece47fe518a9e8507df"
dependencies = [
"rustc_version",
]
[[package]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "0.2.3" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [ dependencies = [
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.3",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void",
] ]
[[package]] [[package]]
name = "experiments" name = "experiments"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal",
"libasync 0.0.0", "libasync",
"libboard_zynq 0.0.0", "libboard_zynq",
"libcortex_a9 0.0.0", "libcortex_a9",
"libregister 0.0.0", "libregister",
"libsupport_zynq 0.0.0", "libsupport_zynq",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log",
]
[[package]]
name = "fatfs"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e18f80a87439240dac45d927fd8f8081b6f1e34c03e97271189fa8a8c2e96c8f"
dependencies = [
"bitflags",
"byteorder",
"core_io",
"log",
] ]
[[package]] [[package]]
name = "libasync" name = "libasync"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal",
"libcortex_a9 0.0.0", "libcortex_a9",
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "nb 1.0.0",
"pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils",
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp",
] ]
[[package]] [[package]]
name = "libboard_zynq" name = "libboard_zynq"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field",
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal",
"libcortex_a9 0.0.0", "libasync",
"libregister 0.0.0", "libcortex_a9",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "libregister",
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log",
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.3",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "void",
"volatile-register",
]
[[package]]
name = "libconfig"
version = "0.1.0"
dependencies = [
"core_io",
"fatfs",
"libboard_zynq",
"log",
] ]
[[package]] [[package]]
name = "libcortex_a9" name = "libcortex_a9"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field",
"libregister 0.0.0", "libregister",
"volatile-register",
] ]
[[package]] [[package]]
name = "libregister" name = "libregister"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field",
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "vcell",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "volatile-register",
] ]
[[package]] [[package]]
name = "libsupport_zynq" name = "libsupport_zynq"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", "cc",
"libboard_zynq 0.0.0", "compiler_builtins",
"libcortex_a9 0.0.0", "libboard_zynq",
"libregister 0.0.0", "libcortex_a9",
"linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "libregister",
"r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked_list_allocator",
"r0",
] ]
[[package]] [[package]]
name = "linked_list_allocator" name = "linked_list_allocator"
version = "0.8.3" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.8" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [ dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if",
] ]
[[package]] [[package]]
name = "managed" name = "managed"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]] [[package]]
name = "nb" name = "nb"
version = "0.1.2" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.0.0",
]
[[package]]
name = "nb"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
[[package]] [[package]]
name = "pin-utils" name = "pin-utils"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "r0" name = "r0"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
[[package]]
name = "rustc_version"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
[[package]] [[package]]
name = "smoltcp" name = "smoltcp"
version = "0.6.0" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e4a069bef843d170df47e7c0a8bf8d037f217d9f5b325865acc3e466ffe40d3"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder",
"managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "managed",
]
[[package]]
name = "szl"
version = "0.1.0"
dependencies = [
"byteorder",
"core_io",
"libboard_zynq",
"libconfig",
"libcortex_a9",
"libregister",
"libsupport_zynq",
"log",
] ]
[[package]] [[package]]
name = "vcell" name = "vcell"
version = "0.1.2" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]] [[package]]
name = "void" name = "void"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]] [[package]]
name = "volatile-register" name = "volatile-register"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
dependencies = [ dependencies = [
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "vcell",
] ]
[metadata]
"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "38f18416546abfbf8d801c555a0e99524453e7214f9cc9107ad49de3d5948ccc"
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
"checksum linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6b60501dd4c850950bb43f970d544f6ce04e0ca021da2db2538fbe9d923f19e"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
"checksum r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

View File

@ -1,19 +1,20 @@
[workspace] [workspace]
members = [ members = [
"libregister", "libcortex_a9", "libregister",
"libboard_zynq", "libsupport_zynq", "libcortex_a9",
"libboard_zynq",
"libsupport_zynq",
"libasync", "libasync",
"libconfig",
"experiments", "experiments",
"szl",
] ]
[profile.dev]
panic = "abort"
lto = false
[profile.release] [profile.release]
panic = "abort" panic = "abort"
debug = true debug = true
# Link-Time Optimization: codegen-units = 1
# turn off if you get unusable debug symbols. opt-level = 's'
lto = true lto = true
opt-level = 'z' # Optimize for size. debug-assertions = false
overflow-checks = false

165
LICENSE Normal file
View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

108
README.md
View File

@ -1,47 +1,49 @@
# Build # Bare-metal Rust on Zynq-7000
Supported features:
* Clocking setup
* UART
* SDRAM setup
* Ethernet with smoltcp and async-await on TCP sockets
* SD card
* PL programming and startup
* Pure Rust SZL first-stage bootloader, with SD boot and netboot
* Control of second CPU core and message passing, with async-await support
Supported boards:
* Kasli-SoC
* ZC706
* Red Pitaya
* Cora Z7-10 (seems to also run on Cora Z7-07S, including dual-core support)
## Build
Zynq-rs is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
You can build SZL or experiments crate for the platform of your choice by using ``nix build`` command, e.g.
```shell ```shell
nix-shell --command "cargo xbuild --release" nix build .#coraz7-experiments
``` ```
Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments` Alternatively, you can still use ``cargo xbuild`` within ``nix develop`` shell.
# Debug
## Using the Xilinx toolchain
Tested with the ZC706 board.
Run the Xilinx Microprocessor Debugger:
```shell ```shell
/opt/Xilinx/14.7/ISE_DS/EDK/bin/lin64/xmd nix develop
cargo xbuild --release -p experiments
``` ```
Connect to target (given it is connected and you have permissions): Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments`, or `result/experiments.elf` for Nix Flakes build.
```tcl
connect arm hw
```
Leave xmd running. ## Debug
Start the Xilinx version of the GNU debugger with your latest build:
```shell
/opt/Xilinx/14.7/ISE_DS/EDK/gnu/arm/lin/bin/arm-xilinx-linux-gnueabi-gdb zc706
```
Connect the debugger to xmd over TCP on localhost:
```gdb
target remote :1234
```
Proceed using gdb with `load`, `c`
## Using OpenOCD
### Running on the ZC706 ### Running on the ZC706
```shell ```shell
nix-shell --command "cargo xbuild --release" nix develop
cargo xbuild --release -p experiments
cd openocd cd openocd
openocd -f zc706.cfg openocd -f zc706.cfg
``` ```
@ -49,7 +51,8 @@ openocd -f zc706.cfg
### Running on the Cora Z7-10 ### Running on the Cora Z7-10
```shell ```shell
nix-shell --command "cd experiments && cargo xbuild --release --no-default-features --features=target_cora_z7_10" nix develop
cargo xbuild --release -p experiments --no-default-features --features=target_coraz7
cd openocd cd openocd
openocd -f cora-z7-10.cfg openocd -f cora-z7-10.cfg
``` ```
@ -57,43 +60,10 @@ openocd -f cora-z7-10.cfg
### Loading a bitstream into volatile memory ### Loading a bitstream into volatile memory
```shell ```shell
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit" openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
``` ```
### Development Process ## License
Clone this repo onto your development/build machine and the raspberry pi that controls the Xilinx 7000 board
On the dev machine, the below script builds zc706 and secure copies it to the target pi (in your pi $HOME directory)
```shell
cd ~/zc706
./build.sh $your_user/ssh_id
```
On the pi, we need an information rich environment that includes a relatively reliable `gdb` experience (that includes `ctrl-p` and `ctrl-n` command history that persists across `cgdb` executions), run:
```shell
ssh pi4
cd zc706
./tmux.sh
```
Time to run your code with:
```shell
zynq-connect
zynq-restart
c
```
or, for a more succinct experience, (identical to above)
```shell
dc
dr
c
```
After every build on your dev machine, simply run:
```shell
dr
c
```
Sometimes you might need to type `load` after `dr`.
Copyright (C) 2019-2022 M-Labs Limited.
Released under the GNU LGPL v3. See the LICENSE file for details.

View File

@ -1 +0,0 @@
nix-shell --command "cargo xbuild --release" && scp -P 2204 -C target/armv7-none-eabihf/release/zc706-experiments $1@nixbld.m-labs.hk:/home/$1/zc706/zc706.elf

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +0,0 @@
{ # Use master branch of the overlay by default
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
rustManifest ? ./channel-rust-nightly.toml,
}:
let
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
rustcSrc = pkgs.fetchgit {
url = https://github.com/rust-lang/rust.git;
# master of 2020-04-25
rev = "14b15521c52549ebbb113173b4abecd124b5a823";
sha256 = "0a6bi8g636cajpdrpcfkpza95b7ss7041m9cs6hxcd7h8bf6xhwi";
fetchSubmodules = true;
};
targets = [];
rustChannelOfTargets = _channel: _date: targets:
(pkgs.lib.rustLib.fromManifestFile rustManifest {
inherit (pkgs) stdenv fetchurl patchelf;
}).rust.override { inherit targets; };
rust =
rustChannelOfTargets "nightly" null targets;
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustc = rust // { src = rustcSrc; };
cargo = rust;
});
gcc = pkgs.pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc;
xbuildRustPackage = { cargoFeatures, crateSubdir, ... } @ attrs:
let
buildPkg = rustPlatform.buildRustPackage attrs;
in
buildPkg.overrideAttrs ({ name, nativeBuildInputs, ... }: {
nativeBuildInputs =
nativeBuildInputs ++ [ pkgs.cargo-xbuild ];
buildPhase = ''
pushd ${crateSubdir}
cargo xbuild --release --frozen \
--no-default-features \
--features=${cargoFeatures}
popd
'';
XARGO_RUST_SRC = "${rustcSrc}/src";
installPhase = ''
mkdir $out
ls -la target/armv7-none-eabihf/release/
cp target/armv7-none-eabihf/release/${name} $out/${name}.elf
'';
});
xbuildCrate = name: crate: features: xbuildRustPackage rec {
name = "${crate}";
src = ./.;
crateSubdir = crate;
cargoSha256 = "0xlynsr94dyv0g41qwk5490w3wnzd5g70msaih6mcbgr3v4s2q34";
cargoFeatures = features;
doCheck = false;
dontFixup = true;
};
in {
inherit pkgs rustPlatform rustcSrc gcc;
zc706 = {
experiments-zc706 = xbuildCrate "experiments-zc706" "experiments" "target_zc706";
experiments-cora = xbuildCrate "experiments-cora" "experiments" "target_cora_z7_10";
};
}

View File

@ -2,12 +2,15 @@
name = "experiments" name = "experiments"
description = "Developing bare-metal Rust on Zynq" description = "Developing bare-metal Rust on Zynq"
version = "0.0.0" version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"] authors = ["M-Labs"]
edition = "2018" edition = "2018"
[features] [features]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706"] target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10", "libsupport_zynq/target_cora_z7_10"] target_coraz7 = ["libboard_zynq/target_coraz7", "libsupport_zynq/target_coraz7"]
target_ebaz4205 = ["libboard_zynq/target_ebaz4205", "libsupport_zynq/target_ebaz4205"]
target_redpitaya = ["libboard_zynq/target_redpitaya", "libsupport_zynq/target_redpitaya"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc"]
default = ["target_zc706"] default = ["target_zc706"]
[dependencies] [dependencies]
@ -16,5 +19,5 @@ embedded-hal = "0.2"
libregister = { path = "../libregister" } libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" } libcortex_a9 = { path = "../libcortex_a9" }
libboard_zynq = { path = "../libboard_zynq" } libboard_zynq = { path = "../libboard_zynq" }
libsupport_zynq = { path = "../libsupport_zynq" } libsupport_zynq = { path = "../libsupport_zynq", default-features = false, features = ["panic_handler", "dummy_fiq_handler"]}
libasync = { path = "../libasync" } libasync = { path = "../libasync" }

View File

@ -1,14 +1,4 @@
ENTRY(_boot_cores); ENTRY(Reset);
/* Provide some defaults */
PROVIDE(Reset = _boot_cores);
PROVIDE(UndefinedInstruction = Reset);
PROVIDE(SoftwareInterrupt = Reset);
PROVIDE(PrefetchAbort = Reset);
PROVIDE(DataAbort = Reset);
PROVIDE(ReservedException = Reset);
PROVIDE(IRQ = Reset);
PROVIDE(FIQ = Reset);
MEMORY MEMORY
{ {
@ -42,19 +32,37 @@ SECTIONS
*(.bss .bss.*); *(.bss .bss.*);
. = ALIGN(4); . = ALIGN(4);
__bss_end = .; __bss_end = .;
} > OCM } > OCM3
.irq_stack1 (NOLOAD) : ALIGN(8)
{
__irq_stack1_end = .;
. += 0x100;
__irq_stack1_start = .;
} > OCM3
.irq_stack0 (NOLOAD) : ALIGN(8)
{
__irq_stack0_end = .;
. += 0x100;
__irq_stack0_start = .;
} > OCM3
.stack1 (NOLOAD) : ALIGN(8) { .stack1 (NOLOAD) : ALIGN(8) {
__stack1_end = .; __stack1_end = .;
. += 0x200; . += 0x200;
__stack1_start = .; __stack1_start = .;
} > OCM } > OCM3
.stack0 (NOLOAD) : ALIGN(8) { .stack0 (NOLOAD) : ALIGN(8) {
__stack0_end = .; __stack0_end = .;
. = ORIGIN(OCM) + LENGTH(OCM) - 8; . = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
__stack0_start = .; __stack0_start = .;
} > OCM
/* unused heap0 to prevent the linker from complaining*/
__heap0_start = .;
__heap0_end = .;
} > OCM3
/DISCARD/ : /DISCARD/ :
{ {

View File

@ -1,10 +1,12 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(const_in_array_repeat_expressions)]
#![feature(naked_functions)]
#![feature(asm)]
extern crate alloc; extern crate alloc;
use alloc::{borrow::ToOwned, collections::BTreeMap, format}; use alloc::collections::BTreeMap;
use core::task::Poll;
use libasync::{ use libasync::{
delay, delay,
smoltcp::{Sockets, TcpStream}, smoltcp::{Sockets, TcpStream},
@ -14,64 +16,119 @@ use libboard_zynq::{
self as zynq, self as zynq,
clocks::source::{ArmPll, ClockSource, IoPll}, clocks::source::{ArmPll, ClockSource, IoPll},
clocks::Clocks, clocks::Clocks,
print, println, println, stdio,
sdio::sd_card::SdCard, mpcore,
gic,
smoltcp::{ smoltcp::{
self,
iface::{EthernetInterfaceBuilder, NeighborCache, Routes}, iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
time::Instant, time::Instant,
wire::{EthernetAddress, IpAddress, IpCidr}, wire::{EthernetAddress, IpAddress, IpCidr},
}, },
time::Milliseconds, time::Milliseconds,
}; };
#[cfg(feature = "target_zc706")]
use libboard_zynq::print;
use libcortex_a9::{ use libcortex_a9::{
mutex::Mutex, mutex::Mutex,
sync_channel::{self, sync_channel}, l2c::enable_l2_cache,
sync_channel::{Sender, Receiver},
sync_channel,
regs::{MPIDR, SP},
spin_lock_yield, notify_spin_lock,
asm, interrupt_handler
}; };
use libregister::RegisterR; use libregister::{RegisterR, RegisterW};
use libsupport_zynq::{ use libsupport_zynq::{
boot, ram, boot, exception_vectors, ram,
}; };
use log::info; use log::{info, warn};
use core::sync::atomic::{AtomicBool, Ordering};
mod ps7_init;
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef]; const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
static mut CORE1_REQ: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
static mut CORE1_RES: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
extern "C" {
static mut __stack1_start: u32;
}
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
let mpcore = mpcore::RegisterBlock::mpcore();
let mut gic = gic::InterruptController::gic(mpcore);
let id = gic.get_interrupt_id();
match MPIDR.read().cpu_id(){
0 => {
if id.0 == 0 {
println!("Interrupting core0...");
gic.end_interrupt(id);
return;
}
},
1 => {
if id.0 == 0 {
gic.end_interrupt(id);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
},
_ => {}
}
stdio::drop_uart();
println!("IRQ");
loop {}
});
pub fn restart_core1() {
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
CORE1_RESTART.store(true, Ordering::Relaxed);
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
while CORE1_RESTART.load(Ordering::Relaxed) {
spin_lock_yield();
}
}
#[no_mangle] #[no_mangle]
pub fn main_core0() { pub fn main_core0() {
exception_vectors::set_vector_table(0x0);
// zynq::clocks::CpuClocks::enable_io(1_250_000_000); // zynq::clocks::CpuClocks::enable_io(1_250_000_000);
println!("\nzc706 main"); enable_l2_cache(0x8);
println!("\nZynq experiments");
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
libboard_zynq::logger::init().unwrap(); libboard_zynq::logger::init().unwrap();
log::set_max_level(log::LevelFilter::Trace); log::set_max_level(log::LevelFilter::Trace);
info!( info!(
"Boot mode: {:?}", "Boot mode: {:?}",
zynq::slcr::RegisterBlock::new() zynq::slcr::RegisterBlock::slcr()
.boot_mode .boot_mode
.read() .read()
.boot_mode_pins() .boot_mode_pins()
); );
#[cfg(feature = "target_zc706")] #[cfg(any(
feature = "target_zc706",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
const CPU_FREQ: u32 = 800_000_000; const CPU_FREQ: u32 = 800_000_000;
#[cfg(feature = "target_cora_z7_10")] #[cfg(feature = "target_coraz7")]
const CPU_FREQ: u32 = 650_000_000; const CPU_FREQ: u32 = 650_000_000;
info!("Setup clock sources..."); info!("Setup clock sources...");
ArmPll::setup(2 * CPU_FREQ); ArmPll::setup(2 * CPU_FREQ);
Clocks::set_cpu_freq(CPU_FREQ); Clocks::set_cpu_freq(CPU_FREQ);
#[cfg(feature = "target_zc706")]
{
IoPll::setup(1_000_000_000); IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart(); libboard_zynq::stdio::drop_uart();
}
#[cfg(feature = "target_cora_z7_10")]
{
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
}
info!("PLLs set up"); info!("PLLs set up");
let clocks = zynq::clocks::Clocks::get(); let clocks = zynq::clocks::Clocks::get();
info!( info!(
@ -82,107 +139,87 @@ pub fn main_core0() {
clocks.cpu_1x() clocks.cpu_1x()
); );
let sd = libboard_zynq::sdio::SDIO::sdio0(true);
// only test SD card if it is inserted
if sd.is_card_inserted() {
let result = SdCard::from_sdio(sd);
match &result {
Ok(_) => info!("OK!"),
Err(a) => info!("{}", a),
};
const SIZE: usize = 512 * 2 + 1;
let mut sd_card = result.unwrap();
{
let buffer: [u8; SIZE] = [5; SIZE];
sd_card.write_block(0x0, 2, &buffer).unwrap();
}
let mut buffer: [u8; SIZE] = [0; SIZE];
sd_card.read_block(0x1, 2, &mut buffer[1..]).unwrap();
for i in 0..buffer.len() {
info!("buffer[{}] = {}", i, buffer[i]);
}
info!("End");
}
let mut flash = zynq::flash::Flash::new(200_000_000).linear_addressing_mode();
let flash_ram: &[u8] = unsafe { core::slice::from_raw_parts(flash.ptr(), flash.size()) };
for i in 0..=1 {
print!("Flash {}:", i);
for b in &flash_ram[(i * 16 * 1024 * 1024)..][..128] {
print!(" {:02X}", *b);
}
println!("");
}
let mut flash = flash.stop();
let timer = libboard_zynq::timer::GlobalTimer::start(); let timer = libboard_zynq::timer::GlobalTimer::start();
let mut ddr = zynq::ddr::DdrRam::new(); let mut ddr = zynq::ddr::DdrRam::ddrram();
#[cfg(not(feature = "target_zc706"))] #[cfg(not(feature = "target_zc706"))]
ddr.memtest(); ddr.memtest();
ram::init_alloc_ddr(&mut ddr); ram::init_alloc_ddr(&mut ddr);
#[cfg(dev)] info!("Send software interrupt to core0");
for i in 0..=1 { interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core0.into());
let mut flash_io = flash.manual_mode(i); info!("Core0 returned from interrupt");
// println!("rdcr={:02X}", flash_io.rdcr());
print!("Flash {} ID:", i);
for b in flash_io.rdid() {
print!(" {:02X}", b);
}
println!("");
print!("Flash {} I/O:", i);
for o in 0..8 {
const CHUNK: u32 = 8;
for b in flash_io.read(CHUNK * o, CHUNK as usize) {
print!(" {:02X}", b);
}
}
println!("");
flash_io.dump("Read cr1", 0x35); boot::Core1::start(false);
flash_io.dump("Read Autoboot", 0x14);
flash_io.dump("Read Bank", 0x16);
flash_io.dump("DLP Bank", 0x16);
flash_io.dump("Read ESig", 0xAB);
flash_io.dump("OTP Read", 0x4B);
flash_io.dump("DYB Read", 0xE0);
flash_io.dump("PPB Read", 0xE2);
flash_io.dump("ASP Read", 0x2B);
flash_io.dump("Password Read", 0xE7);
flash_io.write_enabled(|flash_io| { let core1_req = unsafe { &mut CORE1_REQ.0 };
flash_io.erase(0); let core1_res = unsafe { &mut CORE1_RES.1 };
});
flash_io.write_enabled(|flash_io| {
flash_io.program(0, [0x23054223; 0x100 >> 2].iter().cloned());
});
flash = flash_io.stop();
}
let core1 = boot::Core1::start(false);
let (mut core1_req, rx) = sync_channel(10);
*CORE1_REQ.lock() = Some(rx);
let (tx, mut core1_res) = sync_channel(10);
*CORE1_RES.lock() = Some(tx);
task::block_on(async { task::block_on(async {
for i in 0..10 { for i in 0..10 {
restart_core1();
core1_req.async_send(i).await; core1_req.async_send(i).await;
let j = core1_res.async_recv().await; let j = core1_res.async_recv().await;
println!("{} -> {}", i, j); println!("{} -> {}", i, j);
} }
}); });
core1.disable(); unsafe {
core1_req.drop_elements();
}
let eth = zynq::eth::Eth::default(HWADDR.clone()); // Test I2C
#[cfg(feature = "target_zc706")]
{
let mut i2c = zynq::i2c::I2c::i2c0();
i2c.init().unwrap();
println!("I2C bit-banging enabled");
let mut eeprom = zynq::i2c::eeprom::EEPROM::new(&mut i2c, 16);
// Write to 0x00 and 0x08
let eeprom_buffer: [u8; 22] = [
0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
];
eeprom.write(0x00, &eeprom_buffer[0..6]).unwrap();
eeprom.write(0x08, &eeprom_buffer[6..22]).unwrap();
println!("Data written to EEPROM");
let mut eeprom_buffer = [0u8; 24];
// Read from 0x00
eeprom.read(0x00, &mut eeprom_buffer).unwrap();
print!("Data read from EEPROM @ 0x00: (hex) ");
for i in 0..6 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
// Read from 0x08
eeprom.read(0x08, &mut eeprom_buffer).unwrap();
print!("Data read from EEPROM @ 0x08: (hex) ");
for i in 0..16 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
}
#[cfg(feature = "target_kasli_soc")]
{
let mut err_cdwn = timer.countdown();
let mut err_state = true;
let mut led = zynq::error_led::ErrorLED::error_led();
task::spawn( async move {
loop {
led.toggle(err_state);
err_state = !err_state;
delay(&mut err_cdwn, Milliseconds(1000)).await;
}
});
}
let eth = zynq::eth::Eth::eth0(HWADDR.clone());
println!("Eth on"); println!("Eth on");
const RX_LEN: usize = 8; const RX_LEN: usize = 4096;
// Number of transmission buffers (minimum is two because with // Number of transmission buffers (minimum is two because with
// one, duplicate packet transmission occurs) // one, duplicate packet transmission occurs)
const TX_LEN: usize = 8; const TX_LEN: usize = 4096;
let eth = eth.start_rx(RX_LEN); let eth = eth.start_rx(RX_LEN);
let mut eth = eth.start_tx(TX_LEN); let mut eth = eth.start_tx(TX_LEN);
@ -199,56 +236,45 @@ pub fn main_core0() {
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache)
.finalize(); .finalize();
#[cfg(feature = "target_zc706")]
ps7_init::report_differences();
Sockets::init(32); Sockets::init(32);
/// `chargen`
const TCP_PORT: u16 = 19;
async fn handle_connection(stream: TcpStream) -> smoltcp::Result<()> {
stream.send("Enter your name: ".bytes()).await?;
let name = stream
.recv(|buf| {
for (i, b) in buf.iter().enumerate() {
if *b == '\n' as u8 {
return match core::str::from_utf8(&buf[0..i]) {
Ok(name) => Poll::Ready((i + 1, Some(name.to_owned()))),
Err(_) => Poll::Ready((i + 1, None)),
};
}
}
if buf.len() > 100 {
// Too much input, consume all
Poll::Ready((buf.len(), None))
} else {
Poll::Pending
}
})
.await?;
match name {
Some(name) => stream.send(format!("Hello {}!\n", name).bytes()).await?,
None => {
stream
.send("I had trouble reading your name.\n".bytes())
.await?
}
}
let _ = stream.close().await;
Ok(())
}
let counter = alloc::rc::Rc::new(core::cell::RefCell::new(0)); const TCP_PORT: u16 = 19;
// (rx, tx)
let stats = alloc::rc::Rc::new(core::cell::RefCell::new((0, 0)));
let stats_tx = stats.clone();
task::spawn(async move { task::spawn(async move {
while let Ok(stream) = TcpStream::accept(TCP_PORT, 2048, 2408).await { while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
let counter = counter.clone(); let stats_tx = stats_tx.clone();
task::spawn(async move { task::spawn(async move {
*counter.borrow_mut() += 1; let tx_data = (0..=255).cycle().take(4096).collect::<alloc::vec::Vec<u8>>();
println!("Serving {} connections", *counter.borrow()); loop {
handle_connection(stream) // const CHUNK_SIZE: usize = 65536;
.await // match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
.unwrap_or_else(|e| println!("Connection: {:?}", e)); match stream.send_slice(&tx_data[..]).await {
*counter.borrow_mut() -= 1; Ok(_len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
println!("Now serving {} connections", *counter.borrow()); Err(e) => {
warn!("tx: {:?}", e);
break
}
}
}
});
}
});
let stats_rx = stats.clone();
task::spawn(async move {
while let Ok(stream) = TcpStream::accept(TCP_PORT+1, 0x10_0000, 0x10_0000).await {
let stats_rx = stats_rx.clone();
task::spawn(async move {
loop {
match stream.recv(|buf| (buf.len(), buf.len())).await {
Ok(len) => stats_rx.borrow_mut().0 += len,
Err(e) => {
warn!("rx: {:?}", e);
break
}
}
}
}); });
} }
}); });
@ -258,10 +284,16 @@ pub fn main_core0() {
loop { loop {
delay(&mut countdown, Milliseconds(1000)).await; delay(&mut countdown, Milliseconds(1000)).await;
let timestamp = timer.get_us(); let timestamp = timer.get_us().0;
let seconds = timestamp / 1_000_000; let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000; let micros = timestamp % 1_000_000;
info!("time: {:6}.{:06}s", seconds, micros); let (rx, tx) = {
let mut stats = stats.borrow_mut();
let result = *stats;
*stats = (0, 0);
result
};
info!("time: {:6}.{:06}s, rx: {}k/s, tx: {}k/s", seconds, micros, rx / 1024, tx / 1024);
} }
}); });
@ -270,27 +302,18 @@ pub fn main_core0() {
}) })
} }
static CORE1_REQ: Mutex<Option<sync_channel::Receiver<usize>>> = Mutex::new(None);
static CORE1_RES: Mutex<Option<sync_channel::Sender<usize>>> = Mutex::new(None);
static DONE: Mutex<bool> = Mutex::new(false); static DONE: Mutex<bool> = Mutex::new(false);
#[no_mangle] #[no_mangle]
pub fn main_core1() { pub fn main_core1() {
println!("Hello from core1!"); println!("Hello from core1!");
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
let mut req = None; interrupt_controller.enable_interrupts();
while req.is_none() { let req = unsafe { &mut CORE1_REQ.1 };
req = CORE1_REQ.lock().take(); let res = unsafe { &mut CORE1_RES.0 };
}
let req = req.unwrap();
let mut res = None;
while res.is_none() {
res = CORE1_RES.lock().take();
}
let mut res = res.unwrap();
for i in req { for i in req {
res.send(*i * *i); res.send(i * i);
} }
println!("core1 done!"); println!("core1 done!");

View File

@ -1,68 +0,0 @@
#![cfg(feature = "target_zc706")]
use libboard_zynq::println;
mod zc706;
// mod cora_z7_10;
#[cfg(feature = "target_zc706")]
use zc706 as target;
// #[cfg(feature = "target_cora_z7_10")]
// use cora_z7_10 as target;
pub fn report_differences() {
for (i, op) in target::INIT_DATA.iter().enumerate() {
let address = op.address();
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
.any(|later_op| later_op.address() == address);
if !overwritten_later {
op.report_difference();
}
}
}
pub enum InitOp {
MaskWrite(usize, usize, usize),
MaskPoll(usize, usize),
}
impl InitOp {
fn address(&self) -> usize {
match self {
InitOp::MaskWrite(address, _, _) => *address,
InitOp::MaskPoll(address, _) => *address,
}
}
fn read(&self) -> usize {
unsafe { *(self.address() as *const usize) }
}
fn difference(&self) -> Option<(usize, usize)> {
let (mask, expected) = match self {
InitOp::MaskWrite(_, mask, expected) =>
(*mask, *expected),
InitOp::MaskPoll(_, mask) =>
(*mask, *mask),
};
let actual = self.read();
if actual & mask == expected {
None
} else {
Some((actual & mask, expected))
}
}
pub fn report_difference(&self) {
if let Some((actual, expected)) = self.difference() {
println!(
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
self.address(),
self.read(),
actual,
expected
);
}
}
}

View File

@ -1,203 +0,0 @@
use super::InitOp::{self, *};
pub const INIT_DATA: &'static [InitOp] = &[
// ps7_mio_init_data_1_0
MaskWrite(0xF8000B40, 0x00000FFF, 0x00000600),
MaskWrite(0xF8000B44, 0x00000FFF, 0x00000600),
MaskWrite(0xF8000B48, 0x00000FFF, 0x00000672),
MaskWrite(0xF8000B4C, 0x00000FFF, 0x00000672),
MaskWrite(0xF8000B50, 0x00000FFF, 0x00000674),
MaskWrite(0xF8000B54, 0x00000FFF, 0x00000674),
MaskWrite(0xF8000B58, 0x00000FFF, 0x00000600),
MaskWrite(0xF8000B5C, 0xFFFFFFFF, 0x0018C61C),
MaskWrite(0xF8000B60, 0xFFFFFFFF, 0x00F9861C),
MaskWrite(0xF8000B64, 0xFFFFFFFF, 0x00F9861C),
MaskWrite(0xF8000B68, 0xFFFFFFFF, 0x00F9861C),
MaskWrite(0xF8000B6C, 0x000073FF, 0x00000209),
MaskWrite(0xF8000B70, 0x00000021, 0x00000021),
MaskWrite(0xF8000B70, 0x00000021, 0x00000020),
MaskWrite(0xF8000B70, 0x07FFFFFF, 0x00000823),
MaskWrite(0xF8000700, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000704, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000708, 0x00003FFF, 0x00000702),
MaskWrite(0xF800070C, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000710, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000714, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000718, 0x00003FFF, 0x00000702),
MaskWrite(0xF800071C, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000720, 0x00003FFF, 0x00000700),
MaskWrite(0xF8000724, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000728, 0x00003FFF, 0x00000600),
MaskWrite(0xF800072C, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000730, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000734, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000738, 0x00003FFF, 0x00000600),
MaskWrite(0xF800073C, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000740, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000744, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000748, 0x00003FFF, 0x00000302),
MaskWrite(0xF800074C, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000750, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000754, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000758, 0x00003FFF, 0x00000303),
MaskWrite(0xF800075C, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000760, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000764, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000768, 0x00003FFF, 0x00000303),
MaskWrite(0xF800076C, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000770, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000774, 0x00003FFF, 0x00000305),
MaskWrite(0xF8000778, 0x00003FFF, 0x00000304),
MaskWrite(0xF800077C, 0x00003FFF, 0x00000305),
MaskWrite(0xF8000780, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000784, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000788, 0x00003FFF, 0x00000304),
MaskWrite(0xF800078C, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000790, 0x00003FFF, 0x00000305),
MaskWrite(0xF8000794, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000798, 0x00003FFF, 0x00000304),
MaskWrite(0xF800079C, 0x00003FFF, 0x00000304),
MaskWrite(0xF80007A0, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007A4, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007A8, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007AC, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007B0, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007B4, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007B8, 0x00003F01, 0x00000201),
MaskWrite(0xF80007BC, 0x00003F01, 0x00000201),
MaskWrite(0xF80007C0, 0x00003FFF, 0x000002E0),
MaskWrite(0xF80007C4, 0x00003FFF, 0x000002E1),
MaskWrite(0xF80007C8, 0x00003FFF, 0x00000200),
MaskWrite(0xF80007CC, 0x00003FFF, 0x00000200),
MaskWrite(0xF80007D0, 0x00003FFF, 0x00000280),
MaskWrite(0xF80007D4, 0x00003FFF, 0x00000280),
MaskWrite(0xF8000830, 0x003F003F, 0x002F002E),
// ps7_pll_init_data_1_0
MaskWrite(0xF8000110, 0x003FFFF0, 0x000FA220),
MaskWrite(0xF8000100, 0x0007F000, 0x00028000),
MaskWrite(0xF8000100, 0x00000010, 0x00000010),
MaskWrite(0xF8000100, 0x00000001, 0x00000001),
MaskWrite(0xF8000100, 0x00000001, 0x00000000),
MaskPoll(0xF800010C, 0x00000001),
MaskWrite(0xF8000100, 0x00000010, 0x00000000),
MaskWrite(0xF8000120, 0x1F003F30, 0x1F000200),
MaskWrite(0xF8000114, 0x003FFFF0, 0x0012C220),
MaskWrite(0xF8000104, 0x0007F000, 0x00020000),
MaskWrite(0xF8000104, 0x00000010, 0x00000010),
MaskWrite(0xF8000104, 0x00000001, 0x00000001),
MaskWrite(0xF8000104, 0x00000001, 0x00000000),
MaskPoll(0xF800010C, 0x00000002),
MaskWrite(0xF8000104, 0x00000010, 0x00000000),
MaskWrite(0xF8000124, 0xFFF00003, 0x0C200003),
MaskWrite(0xF8000118, 0x003FFFF0, 0x001452C0),
MaskWrite(0xF8000108, 0x0007F000, 0x0001E000),
MaskWrite(0xF8000108, 0x00000010, 0x00000010),
MaskWrite(0xF8000108, 0x00000001, 0x00000001),
MaskWrite(0xF8000108, 0x00000001, 0x00000000),
MaskPoll(0xF800010C, 0x00000004),
MaskWrite(0xF8000108, 0x00000010, 0x00000000),
// ps7_clock_init_data_1_0
MaskWrite(0xF8000128, 0x03F03F01, 0x00700F01),
MaskWrite(0xF8000138, 0x00000011, 0x00000001),
MaskWrite(0xF8000140, 0x03F03F71, 0x00100801),
MaskWrite(0xF800014C, 0x00003F31, 0x00000501),
MaskWrite(0xF8000150, 0x00003F33, 0x00001401),
MaskWrite(0xF8000154, 0x00003F33, 0x00001402),
MaskWrite(0xF8000168, 0x00003F31, 0x00000501),
MaskWrite(0xF8000170, 0x03F03F30, 0x00200400),
MaskWrite(0xF80001C4, 0x00000001, 0x00000001),
MaskWrite(0xF800012C, 0x01FFCCCD, 0x01EC044D),
// ps7_ddr_init_data_1_0
MaskWrite(0xF8006000, 0x0001FFFF, 0x00000080),
MaskWrite(0xF8006004, 0x1FFFFFFF, 0x00081081),
MaskWrite(0xF8006008, 0x03FFFFFF, 0x03C0780F),
MaskWrite(0xF800600C, 0x03FFFFFF, 0x02001001),
MaskWrite(0xF8006010, 0x03FFFFFF, 0x00014001),
MaskWrite(0xF8006014, 0x001FFFFF, 0x0004159B),
MaskWrite(0xF8006018, 0xF7FFFFFF, 0x452460D2),
MaskWrite(0xF800601C, 0xFFFFFFFF, 0x720238E5),
MaskWrite(0xF8006020, 0xFFFFFFFC, 0x272872D0),
MaskWrite(0xF8006024, 0x0FFFFFFF, 0x0000003C),
MaskWrite(0xF8006028, 0x00003FFF, 0x00002007),
MaskWrite(0xF800602C, 0xFFFFFFFF, 0x00000008),
MaskWrite(0xF8006030, 0xFFFFFFFF, 0x00040930),
MaskWrite(0xF8006034, 0x13FF3FFF, 0x000116D4),
MaskWrite(0xF8006038, 0x00001FC3, 0x00000000),
MaskWrite(0xF800603C, 0x000FFFFF, 0x00000777),
MaskWrite(0xF8006040, 0xFFFFFFFF, 0xFFF00000),
MaskWrite(0xF8006044, 0x0FFFFFFF, 0x0FF66666),
MaskWrite(0xF8006048, 0x3FFFFFFF, 0x0003C248),
MaskWrite(0xF8006050, 0xFF0F8FFF, 0x77010800),
MaskWrite(0xF8006058, 0x0001FFFF, 0x00000101),
MaskWrite(0xF800605C, 0x0000FFFF, 0x00005003),
MaskWrite(0xF8006060, 0x000017FF, 0x0000003E),
MaskWrite(0xF8006064, 0x00021FE0, 0x00020000),
MaskWrite(0xF8006068, 0x03FFFFFF, 0x00284141),
MaskWrite(0xF800606C, 0x0000FFFF, 0x00001610),
MaskWrite(0xF80060A0, 0x00FFFFFF, 0x00008000),
MaskWrite(0xF80060A4, 0xFFFFFFFF, 0x10200802),
MaskWrite(0xF80060A8, 0x0FFFFFFF, 0x0690CB73),
MaskWrite(0xF80060AC, 0x000001FF, 0x000001FE),
MaskWrite(0xF80060B0, 0x1FFFFFFF, 0x1CFFFFFF),
MaskWrite(0xF80060B4, 0x000007FF, 0x00000200),
MaskWrite(0xF80060B8, 0x01FFFFFF, 0x00200066),
MaskWrite(0xF80060C4, 0x00000003, 0x00000000),
MaskWrite(0xF80060C8, 0x000000FF, 0x00000000),
MaskWrite(0xF80060DC, 0x00000001, 0x00000000),
MaskWrite(0xF80060F0, 0x0000FFFF, 0x00000000),
MaskWrite(0xF80060F4, 0x0000000F, 0x00000008),
MaskWrite(0xF8006114, 0x000000FF, 0x00000000),
MaskWrite(0xF8006118, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF800611C, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF8006120, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF8006124, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF800612C, 0x000FFFFF, 0x00033C03),
MaskWrite(0xF8006130, 0x000FFFFF, 0x00034003),
MaskWrite(0xF8006134, 0x000FFFFF, 0x0002F400),
MaskWrite(0xF8006138, 0x000FFFFF, 0x00030400),
MaskWrite(0xF8006140, 0x000FFFFF, 0x00000035),
MaskWrite(0xF8006144, 0x000FFFFF, 0x00000035),
MaskWrite(0xF8006148, 0x000FFFFF, 0x00000035),
MaskWrite(0xF800614C, 0x000FFFFF, 0x00000035),
MaskWrite(0xF8006154, 0x000FFFFF, 0x00000083),
MaskWrite(0xF8006158, 0x000FFFFF, 0x00000083),
MaskWrite(0xF800615C, 0x000FFFFF, 0x00000080),
MaskWrite(0xF8006160, 0x000FFFFF, 0x00000080),
MaskWrite(0xF8006168, 0x001FFFFF, 0x00000124),
MaskWrite(0xF800616C, 0x001FFFFF, 0x00000125),
MaskWrite(0xF8006170, 0x001FFFFF, 0x00000112),
MaskWrite(0xF8006174, 0x001FFFFF, 0x00000116),
MaskWrite(0xF800617C, 0x000FFFFF, 0x000000C3),
MaskWrite(0xF8006180, 0x000FFFFF, 0x000000C3),
MaskWrite(0xF8006184, 0x000FFFFF, 0x000000C0),
MaskWrite(0xF8006188, 0x000FFFFF, 0x000000C0),
MaskWrite(0xF8006190, 0xFFFFFFFF, 0x10040080),
MaskWrite(0xF8006194, 0x000FFFFF, 0x0001FC82),
MaskWrite(0xF8006204, 0xFFFFFFFF, 0x00000000),
MaskWrite(0xF8006208, 0x000F03FF, 0x000803FF),
MaskWrite(0xF800620C, 0x000F03FF, 0x000803FF),
MaskWrite(0xF8006210, 0x000F03FF, 0x000803FF),
MaskWrite(0xF8006214, 0x000F03FF, 0x000803FF),
MaskWrite(0xF8006218, 0x000F03FF, 0x000003FF),
MaskWrite(0xF800621C, 0x000F03FF, 0x000003FF),
MaskWrite(0xF8006220, 0x000F03FF, 0x000003FF),
MaskWrite(0xF8006224, 0x000F03FF, 0x000003FF),
MaskWrite(0xF80062A8, 0x00000FF7, 0x00000000),
MaskWrite(0xF80062AC, 0xFFFFFFFF, 0x00000000),
MaskWrite(0xF80062B0, 0x003FFFFF, 0x00005125),
MaskWrite(0xF80062B4, 0x0003FFFF, 0x000012A8),
MaskPoll(0xF8000B74, 0x00002000),
MaskWrite(0xF8006000, 0x0001FFFF, 0x00000081),
MaskPoll(0xF8006054, 0x00000007),
// ps7_peripherals_init_data_1_0
MaskWrite(0xF8000B48, 0x00000180, 0x00000180),
MaskWrite(0xF8000B4C, 0x00000180, 0x00000180),
MaskWrite(0xF8000B50, 0x00000180, 0x00000180),
MaskWrite(0xF8000B54, 0x00000180, 0x00000180),
MaskWrite(0xE0001034, 0x000000FF, 0x00000006),
MaskWrite(0xE0001018, 0x0000FFFF, 0x0000003E),
MaskWrite(0xE0001000, 0x000001FF, 0x00000017),
MaskWrite(0xE0001004, 0x00000FFF, 0x00000020),
MaskWrite(0xE000D000, 0x00080000, 0x00080000),
MaskWrite(0xF8007000, 0x20000000, 0x00000000),
];

49
flake.lock Normal file
View File

@ -0,0 +1,49 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1731652201,
"narHash": "sha256-XUO0JKP1hlww0d7mm3kpmIr4hhtR4zicg5Wwes9cPMg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c21b77913ea840f8bcf9adf4c41cecc2abffd38d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1719454714,
"narHash": "sha256-MojqG0lyUINkEk0b3kM2drsU5vyaF8DFZe/FAlZVOGs=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "d1c527659cf076ecc4b96a91c702d080b213801e",
"type": "github"
},
"original": {
"owner": "oxalica",
"ref": "snapshot/2024-08-01",
"repo": "rust-overlay",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

179
flake.nix Normal file
View File

@ -0,0 +1,179 @@
{
description = "Bare-metal Rust on Zynq-7000";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-24.05;
inputs.rust-overlay = {
url = "github:oxalica/rust-overlay?ref=snapshot/2024-08-01";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, rust-overlay }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import rust-overlay) crosspkgs-overlay ]; };
rust = pkgs.rust-bin.nightly."2021-01-28".default.override {
extensions = [ "rust-src" ];
targets = [ ];
};
rustPlatform = pkgs.makeRustPlatform {
rustc = rust // {
# https://github.com/oxalica/rust-overlay/commit/c48c2d76b68dd9ede0815fec53479375c61af857
targetPlatforms = pkgs.lib.platforms.all;
tier1TargetPlatforms = pkgs.lib.platforms.all;
badTargetPlatforms = [ ];
};
cargo = rust;
};
# https://doc.rust-lang.org/rustc/linker-plugin-lto.html#toolchain-compatibility
llvmPackages_11 = pkgs.recurseIntoAttrs (pkgs.callPackage (import ./llvm/11) ({
inherit (pkgs.stdenvAdapters) overrideCC;
buildLlvmTools = null;
targetLlvmLibraries = null;
targetLlvm = null;
}));
crosspkgs-overlay = (self: super: {
pkgsCross = super.pkgsCross // {
zynq-baremetal = import super.path {
system = "x86_64-linux";
crossSystem = {
config = "arm-none-eabihf";
libc = "newlib";
gcc.cpu = "cortex-a9";
gcc.fpu = "vfpv3";
};
};
};
});
mkbootimage = pkgs.stdenv.mkDerivation {
pname = "mkbootimage";
version = "2.3dev";
src = pkgs.fetchFromGitHub {
owner = "antmicro";
repo = "zynq-mkbootimage";
rev = "872363ce32c249f8278cf107bc6d3bdeb38d849f";
sha256 = "sha256-5FPyAhUWZDwHbqmp9J2ZXTmjaXPz+dzrJMolaNwADHs=";
};
propagatedBuildInputs = [ pkgs.libelf pkgs.pcre ];
patchPhase =
''
substituteInPlace Makefile --replace "git rev-parse --short HEAD" "echo nix"
'';
installPhase =
''
mkdir -p $out/bin
cp mkbootimage $out/bin
'';
hardeningDisable = [ "fortify" ];
};
fsbl = { board ? "zc706" }: pkgs.stdenv.mkDerivation {
name = "${board}-fsbl";
src = pkgs.fetchFromGitHub {
owner = "Xilinx";
repo = "embeddedsw";
rev = "xilinx_v2022.2";
sha256 = "sha256-UDz9KK/Hw3qM1BAeKif30rE8Bi6C2uvuZlvyvtJCMfw=";
};
nativeBuildInputs = [
pkgs.pkgsCross.zynq-baremetal.buildPackages.binutils
pkgs.pkgsCross.zynq-baremetal.buildPackages.gcc
];
patchPhase = ''
patchShebangs lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh
for x in lib/sw_apps/zynq_fsbl/src/Makefile lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh lib/bsp/standalone/src/arm/cortexa9/gcc/Makefile; do
substituteInPlace $x \
--replace "arm-none-eabi-" "arm-none-eabihf-"
done
'';
buildPhase = ''
cd lib/sw_apps/zynq_fsbl/src
make BOARD=${board} "CFLAGS=-DFSBL_DEBUG_INFO -g"
'';
installPhase = ''
mkdir $out
cp fsbl.elf $out
'';
doCheck = false;
dontFixup = true;
};
cargo-xbuild = pkgs.cargo-xbuild.overrideAttrs(oa: {
postPatch = "substituteInPlace src/sysroot.rs --replace 2021 2018";
});
build-crate = name: crate: features: rustPlatform.buildRustPackage rec {
name = "${crate}";
src = builtins.filterSource (path: type:
baseNameOf path != "target"
) ./.;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ cargo-xbuild llvmPackages_11.clang-unwrapped ];
buildPhase = ''
export XARGO_RUST_SRC="${rust}/lib/rustlib/src/rust/library"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
pushd ${crate}
cargo xbuild --release --frozen \
--no-default-features \
--features=${features}
popd
'';
installPhase = ''
mkdir -p $out $out/nix-support
cp target/armv7-none-eabihf/release/${name} $out/${name}.elf
echo file binary-dist $out/${name}.elf >> $out/nix-support/hydra-build-products
'';
doCheck = false;
dontFixup = true;
auditable = false;
};
targetCrates = target: {
"${target}-experiments" = build-crate "${target}-experiments" "experiments" "target_${target}";
"${target}-szl" = build-crate "${target}-szl" "szl" "target_${target}";
};
targets = ["zc706" "coraz7" "redpitaya" "kasli_soc" "ebaz4205"];
allTargetCrates = (builtins.foldl' (results: target:
results // targetCrates target
) {} targets);
szl = pkgs.runCommand "szl" {} (builtins.foldl' (commands: target:
let
szlResult = builtins.getAttr "${target}-szl" allTargetCrates;
in
commands + "ln -s ${szlResult}/szl.elf $out/szl-${target}.elf\n"
) "mkdir $out\n" targets);
in rec {
packages.x86_64-linux = {
inherit cargo-xbuild szl mkbootimage;
zc706-fsbl = fsbl { board = "zc706"; };
} // allTargetCrates ;
hydraJobs = packages.x86_64-linux;
inherit rust rustPlatform llvmPackages_11;
devShell.x86_64-linux = pkgs.mkShell {
name = "zynq-rs-dev-shell";
buildInputs = [
rust
cargo-xbuild
mkbootimage
pkgs.openocd pkgs.gdb
pkgs.openssh pkgs.rsync
llvmPackages_11.clang-unwrapped
(pkgs.python3.withPackages(ps: [ ps.pyftdi ]))
];
};
};
}

17
kasli_soc_por.py Normal file
View File

@ -0,0 +1,17 @@
from time import sleep
from pyftdi.ftdi import Ftdi
POR = 1 << 7
def main():
dev = Ftdi()
dev.open_bitbang_from_url("ftdi://ftdi:4232h/0")
dev.set_bitmode(POR, Ftdi.BitMode.BITBANG)
dev.write_data(bytes([0]))
sleep(0.1)
dev.write_data(bytes([POR]))
sleep(0.1)
dev.close()
if __name__ == "__main__":
main()

View File

@ -2,17 +2,17 @@
name = "libasync" name = "libasync"
description = "low-level async support" description = "low-level async support"
version = "0.0.0" version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"] authors = ["M-Labs"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
#futures = { version = "0.3", default-features = false } #futures = { version = "0.3", default-features = false }
pin-utils = "0.1.0-alpha.4" pin-utils = "0.1.0-alpha.4"
embedded-hal = "0.2" embedded-hal = "0.2"
nb = "0.1" nb = "1.0"
libcortex_a9 = { path = "../libcortex_a9" } libcortex_a9 = { path = "../libcortex_a9" }
[dependencies.smoltcp] [dependencies.smoltcp]
version = "0.6" version = "0.7"
default-features = false default-features = false
features = ["alloc"] features = ["alloc"]

View File

@ -6,7 +6,7 @@ use core::{
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
}; };
use alloc::{boxed::Box, collections::VecDeque as Deque}; use alloc::{boxed::Box, vec::Vec};
//use futures::future::FutureExt; //use futures::future::FutureExt;
use pin_utils::pin_mut; use pin_utils::pin_mut;
@ -31,7 +31,7 @@ static VTABLE: RawWakerVTable = {
/// ready should not move as long as this waker references it. That is /// ready should not move as long as this waker references it. That is
/// the reason for keeping Tasks in a pinned box. /// the reason for keeping Tasks in a pinned box.
fn wrap_waker(ready: &AtomicBool) -> Waker { fn wrap_waker(ready: &AtomicBool) -> Waker {
unsafe { Waker::from_raw(RawWaker::new(ready as *const _ as *const _, &VTABLE)) } unsafe { Waker::from_raw(RawWaker::new(ready as *const _ as *const (), &VTABLE)) }
} }
/// A single-threaded executor /// A single-threaded executor
@ -44,7 +44,7 @@ pub struct Executor {
/// Tasks reside on the heap, so that we just queue pointers. They /// Tasks reside on the heap, so that we just queue pointers. They
/// must also be pinned in memory because our RawWaker is a pointer /// must also be pinned in memory because our RawWaker is a pointer
/// to their `ready` field. /// to their `ready` field.
tasks: RefCell<Deque<Pin<Box<Task>>>>, tasks: RefCell<Vec<Pin<Box<Task>>>>,
} }
impl Executor { impl Executor {
@ -52,7 +52,7 @@ impl Executor {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
in_block_on: RefCell::new(false), in_block_on: RefCell::new(false),
tasks: RefCell::new(Deque::new()), tasks: RefCell::new(Vec::new()),
} }
} }
@ -72,6 +72,7 @@ impl Executor {
pin_mut!(f); pin_mut!(f);
let ready = AtomicBool::new(true); let ready = AtomicBool::new(true);
let waker = wrap_waker(&ready); let waker = wrap_waker(&ready);
let mut backup = Vec::new();
let val = loop { let val = loop {
// advance the main task // advance the main task
if ready.load(Ordering::Relaxed) { if ready.load(Ordering::Relaxed) {
@ -85,10 +86,9 @@ impl Executor {
// println!("ran block_on"); // println!("ran block_on");
} }
// println!("tasks: {}", self.tasks.borrow().len()); // advance all tasks
// advance other tasks core::mem::swap(&mut *self.tasks.borrow_mut(), &mut backup);
let next_task = self.tasks.borrow_mut().pop_front(); for mut task in backup.drain(..) {
if let Some(mut task) = next_task {
// NOTE we don't need a CAS operation here because `wake` invocations that come from // NOTE we don't need a CAS operation here because `wake` invocations that come from
// interrupt handlers (the only source of 'race conditions' (!= data races)) are // interrupt handlers (the only source of 'race conditions' (!= data races)) are
// "oneshot": they'll issue a `wake` and then disable themselves to not run again // "oneshot": they'll issue a `wake` and then disable themselves to not run again
@ -106,7 +106,7 @@ impl Executor {
} }
} }
// Requeue // Requeue
self.tasks.borrow_mut().push_back(task); self.tasks.borrow_mut().push(task);
} }
// // try to sleep; this will be a no-op if any of the previous tasks generated a SEV or an // // try to sleep; this will be a no-op if any of the previous tasks generated a SEV or an
@ -119,7 +119,7 @@ impl Executor {
pub fn spawn(&self, f: impl Future + 'static) { pub fn spawn(&self, f: impl Future + 'static) {
let task = Box::pin(Task::new(f)); let task = Box::pin(Task::new(f));
self.tasks.borrow_mut().push_back(task); self.tasks.borrow_mut().push(task);
} }
} }

View File

@ -7,17 +7,23 @@ use smoltcp::{
iface::EthernetInterface, iface::EthernetInterface,
phy::Device, phy::Device,
socket::SocketSet, socket::SocketSet,
time::Instant, time::{Duration, Instant},
}; };
use crate::task; use crate::task;
mod tcp_stream; mod tcp_stream;
pub use tcp_stream::TcpStream; pub use tcp_stream::TcpStream;
pub trait LinkCheck {
type Link;
fn is_idle(&self) -> bool;
fn check_link_change(&mut self) -> Option<Self::Link>;
}
static mut SOCKETS: Option<Sockets> = None; static mut SOCKETS: Option<Sockets> = None;
pub struct Sockets { pub struct Sockets {
sockets: RefCell<SocketSet<'static, 'static, 'static>>, sockets: RefCell<SocketSet<'static>>,
wakers: RefCell<Vec<Waker>>, wakers: RefCell<Vec<Waker>>,
} }
@ -41,14 +47,24 @@ impl Sockets {
/// Block and run executor indefinitely while polling the smoltcp /// Block and run executor indefinitely while polling the smoltcp
/// iface /// iface
pub fn run<'b, 'c, 'e, D: for<'d> Device<'d>>( pub fn run<'b, D: for<'d> Device<'d> + LinkCheck>(
iface: &mut EthernetInterface<'b, 'c, 'e, D>, iface: &mut EthernetInterface<'b, D>,
mut get_time: impl FnMut() -> Instant, mut get_time: impl FnMut() -> Instant,
) -> ! { ) -> ! {
task::block_on(async { task::block_on(async {
let mut last_link_check = Instant::from_millis(0);
const LINK_CHECK_INTERVAL: u64 = 500;
loop { loop {
let instant = get_time(); let instant = get_time();
Self::instance().poll(iface, instant); Self::instance().poll(iface, instant);
let dev = iface.device_mut();
if dev.is_idle() && instant >= last_link_check + Duration::from_millis(LINK_CHECK_INTERVAL) {
dev.check_link_change();
last_link_check = instant;
}
task::r#yield().await; task::r#yield().await;
} }
}) })
@ -58,9 +74,9 @@ impl Sockets {
unsafe { SOCKETS.as_ref().expect("Sockets") } unsafe { SOCKETS.as_ref().expect("Sockets") }
} }
fn poll<'b, 'c, 'e, D: for<'d> Device<'d>>( fn poll<'b, D: for<'d> Device<'d>>(
&self, &self,
iface: &mut EthernetInterface<'b, 'c, 'e, D>, iface: &mut EthernetInterface<'b, D>,
instant: Instant instant: Instant
) { ) {
let processed = { let processed = {
@ -81,7 +97,14 @@ impl Sockets {
/// TODO: this was called through eg. TcpStream, another poll() /// TODO: this was called through eg. TcpStream, another poll()
/// might want to send packets before sleeping for an interrupt. /// might want to send packets before sleeping for an interrupt.
pub(crate) fn register_waker(waker: Waker) { pub(crate) fn register_waker(waker: Waker) {
Self::instance().wakers.borrow_mut() let mut wakers = Self::instance().wakers.borrow_mut();
.push(waker); for (i, w) in wakers.iter().enumerate() {
if w.will_wake(&waker) {
let last = wakers.len() - 1;
wakers.swap(i, last);
return;
}
}
wakers.push(waker);
} }
} }

View File

@ -103,21 +103,19 @@ impl TcpStream {
/// Probe the receive buffer /// Probe the receive buffer
/// ///
/// Instead of handing you the data on the heap all at once, /// Your callback will only be called when there is some data available,
/// smoltcp's read interface is wrapped so that your callback can /// and it must consume at least one byte. It returns a tuple with the
/// just return `Poll::Pending` if there is not enough data /// number of bytes it consumed, and a user-defined return value of type R.
/// yet. Likewise, return the amount of bytes consumed from the
/// buffer in the `Poll::Ready` result.
pub async fn recv<F, R>(&self, f: F) -> Result<R> pub async fn recv<F, R>(&self, f: F) -> Result<R>
where where
F: Fn(&[u8]) -> Poll<(usize, R)>, F: Fn(&[u8]) -> (usize, R),
{ {
struct Recv<'a, F: FnOnce(&[u8]) -> Poll<(usize, R)>, R> { struct Recv<'a, F: FnOnce(&[u8]) -> (usize, R), R> {
stream: &'a TcpStream, stream: &'a TcpStream,
f: F, f: F,
} }
impl<'a, F: Fn(&[u8]) -> Poll<(usize, R)>, R> Future for Recv<'a, F, R> { impl<'a, F: Fn(&[u8]) -> (usize, R), R> Future for Recv<'a, F, R> {
type Output = Result<R>; type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
@ -128,13 +126,9 @@ impl TcpStream {
socket.recv(|buf| { socket.recv(|buf| {
if buf.len() > 0 { if buf.len() > 0 {
match (self.f)(buf) { let (amount, result) = (self.f)(buf);
Poll::Ready((amount, result)) => assert!(amount > 0);
(amount, Poll::Ready(Ok(result))), (amount, Poll::Ready(Ok(result)))
Poll::Pending =>
// 0 bytes consumed
(0, Poll::Pending),
}
} else { } else {
(0, Poll::Pending) (0, Poll::Pending)
} }
@ -268,6 +262,14 @@ impl TcpStream {
pub fn set_timeout(&mut self, duration: Option<Duration>) { pub fn set_timeout(&mut self, duration: Option<Duration>) {
self.with_socket(|mut socket| socket.set_timeout(duration)); self.with_socket(|mut socket| socket.set_timeout(duration));
} }
pub fn ack_delay(&self) -> Option<Duration> {
self.with_socket(|socket| socket.ack_delay())
}
pub fn set_ack_delay(&mut self, duration: Option<Duration>) {
self.with_socket(|mut socket| socket.set_ack_delay(duration));
}
} }
impl Drop for TcpStream { impl Drop for TcpStream {

View File

@ -2,12 +2,16 @@
name = "libboard_zynq" name = "libboard_zynq"
description = "Drivers for peripherals in the Zynq PS" description = "Drivers for peripherals in the Zynq PS"
version = "0.0.0" version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"] authors = ["M-Labs"]
edition = "2018" edition = "2018"
[features] [features]
target_zc706 = [] target_zc706 = []
target_cora_z7_10 = [] target_coraz7 = []
target_ebaz4205 = []
target_redpitaya = []
target_kasli_soc = []
ipv6 = [ "smoltcp/proto-ipv6" ]
[dependencies] [dependencies]
volatile-register = "0.2" volatile-register = "0.2"
@ -18,8 +22,9 @@ void = { version = "1", default-features = false }
log = "0.4" log = "0.4"
libregister = { path = "../libregister" } libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" } libcortex_a9 = { path = "../libcortex_a9" }
libasync = { path = "../libasync" }
[dependencies.smoltcp] [dependencies.smoltcp]
version = "0.6" version = "0.7"
features = ["ethernet", "proto-ipv4", "socket-tcp"] features = ["ethernet", "proto-ipv4", "socket-tcp"]
default-features = false default-features = false

View File

@ -1,3 +1,5 @@
use core::unimplemented;
use libregister::{RegisterR, RegisterRW}; use libregister::{RegisterR, RegisterRW};
use super::slcr; use super::slcr;
pub use slcr::ArmPllSource; pub use slcr::ArmPllSource;
@ -14,7 +16,7 @@ enum CpuClockMode {
impl CpuClockMode { impl CpuClockMode {
pub fn get() -> Self { pub fn get() -> Self {
let regs = slcr::RegisterBlock::new(); let regs = slcr::RegisterBlock::slcr();
if regs.clk_621_true.read().clk_621_true() { if regs.clk_621_true.read().clk_621_true() {
CpuClockMode::C621 CpuClockMode::C621
} else { } else {
@ -59,7 +61,7 @@ impl Clocks {
} }
pub fn cpu_6x4x(&self) -> u32 { pub fn cpu_6x4x(&self) -> u32 {
let slcr = slcr::RegisterBlock::new(); let slcr = slcr::RegisterBlock::slcr();
let arm_clk_ctrl = slcr.arm_clk_ctrl.read(); let arm_clk_ctrl = slcr.arm_clk_ctrl.read();
let pll = match arm_clk_ctrl.srcsel() { let pll = match arm_clk_ctrl.srcsel() {
ArmPllSource::ArmPll => self.arm, ArmPllSource::ArmPll => self.arm,
@ -92,7 +94,7 @@ impl Clocks {
} }
pub fn uart_ref_clk(&self) -> u32 { pub fn uart_ref_clk(&self) -> u32 {
let regs = slcr::RegisterBlock::new(); let regs = slcr::RegisterBlock::slcr();
let uart_clk_ctrl = regs.uart_clk_ctrl.read(); let uart_clk_ctrl = regs.uart_clk_ctrl.read();
let pll = match uart_clk_ctrl.srcsel() { let pll = match uart_clk_ctrl.srcsel() {
slcr::PllSource::ArmPll => slcr::PllSource::ArmPll =>
@ -101,12 +103,14 @@ impl Clocks {
self.ddr, self.ddr,
slcr::PllSource::IoPll => slcr::PllSource::IoPll =>
self.io, self.io,
slcr::PllSource::Emio =>
unimplemented!(),
}; };
pll / u32::from(uart_clk_ctrl.divisor()) pll / u32::from(uart_clk_ctrl.divisor())
} }
pub fn sdio_ref_clk(&self) -> u32 { pub fn sdio_ref_clk(&self) -> u32 {
let regs = slcr::RegisterBlock::new(); let regs = slcr::RegisterBlock::slcr();
let sdio_clk_ctrl = regs.sdio_clk_ctrl.read(); let sdio_clk_ctrl = regs.sdio_clk_ctrl.read();
let pll = match sdio_clk_ctrl.srcsel() { let pll = match sdio_clk_ctrl.srcsel() {
slcr::PllSource::ArmPll => slcr::PllSource::ArmPll =>
@ -115,6 +119,8 @@ impl Clocks {
self.ddr, self.ddr,
slcr::PllSource::IoPll => slcr::PllSource::IoPll =>
self.io, self.io,
slcr::PllSource::Emio =>
unimplemented!(),
}; };
pll / u32::from(sdio_clk_ctrl.divisor()) pll / u32::from(sdio_clk_ctrl.divisor())
} }

View File

@ -4,8 +4,14 @@ use super::slcr;
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
pub const PS_CLK: u32 = 33_333_333; pub const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_cora_z7_10")] #[cfg(feature = "target_coraz7")]
pub const PS_CLK: u32 = 50_000_000; pub const PS_CLK: u32 = 50_000_000;
#[cfg(feature = "target_ebaz4205")]
pub const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_redpitaya")]
pub const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_kasli_soc")]
pub const PS_CLK: u32 = 33_333_333;
/// (pll_fdiv_max, (pll_cp, pll_res, lock_cnt)) /// (pll_fdiv_max, (pll_cp, pll_res, lock_cnt))
const PLL_FDIV_LOCK_PARAM: &[(u16, (u8, u8, u16))] = &[ const PLL_FDIV_LOCK_PARAM: &[(u16, (u8, u8, u16))] = &[
@ -44,7 +50,7 @@ pub trait ClockSource {
/// get configured frequency /// get configured frequency
fn freq() -> u32 { fn freq() -> u32 {
let mut slcr = slcr::RegisterBlock::new(); let mut slcr = slcr::RegisterBlock::slcr();
let (pll_ctrl, _, _) = Self::pll_regs(&mut slcr); let (pll_ctrl, _, _) = Self::pll_regs(&mut slcr);
u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK
} }
@ -55,7 +61,7 @@ pub trait ClockSource {
/// 25.10.4 PLLs /// 25.10.4 PLLs
fn setup(target_freq: u32) { fn setup(target_freq: u32) {
let fdiv = (target_freq / PS_CLK).min(66) as u16; let fdiv = (target_freq / PS_CLK).min(66) as u16;
let (pll_res, pll_cp, lock_cnt) = PLL_FDIV_LOCK_PARAM.iter() let (pll_cp, pll_res, lock_cnt) = PLL_FDIV_LOCK_PARAM.iter()
.filter(|(fdiv_max, _)| fdiv <= *fdiv_max) .filter(|(fdiv_max, _)| fdiv <= *fdiv_max)
.nth(0) .nth(0)
.expect("PLL_FDIV_LOCK_PARAM") .expect("PLL_FDIV_LOCK_PARAM")

View File

@ -1,7 +1,9 @@
use libregister::{RegisterR, RegisterW, RegisterRW}; use libregister::{RegisterR, RegisterW, RegisterRW};
use log::{debug, info, error}; use log::{debug, info, error};
use crate::{print, println}; use crate::{print, println};
use super::slcr::{self, DdriobVrefSel}; use super::slcr;
#[cfg(feature = "target_zc706")]
use super::slcr::DdriobVrefSel;
use super::clocks::{Clocks, source::{DdrPll, ClockSource}}; use super::clocks::{Clocks, source::{DdrPll, ClockSource}};
mod regs; mod regs;
@ -10,26 +12,36 @@ mod regs;
/// Micron MT41J256M8HX-15E: 667 MHz DDR3 /// Micron MT41J256M8HX-15E: 667 MHz DDR3
const DDR_FREQ: u32 = 666_666_666; const DDR_FREQ: u32 = 666_666_666;
#[cfg(feature = "target_cora_z7_10")] #[cfg(feature = "target_coraz7")]
/// Micron MT41K256M16HA-125: 800 MHz DDR3L, max supported 533 MHz /// Micron MT41K256M16HA-125: 800 MHz DDR3L, max supported 533 MHz
const DDR_FREQ: u32 = 525_000_000; const DDR_FREQ: u32 = 525_000_000;
/// MT41K256M16HA-125 #[cfg(feature = "target_ebaz4205")]
const DCI_FREQ: u32 = 10_000_000; /// EtronTech Memory EM6GD16EWKG-12H: 800 MHz DDR3 at 533 MHz
const DDR_FREQ: u32 = 533_333_333;
#[cfg(feature = "target_redpitaya")]
/// Alliance Memory AS4C256M16D3B: 800 MHz DDR3 at 533 MHz
const DDR_FREQ: u32 = 533_333_333;
#[cfg(feature = "target_kasli_soc")]
/// MT41K256M16HA-125:E: 800 MHz DDR3L at 533 MHz
const DDR_FREQ: u32 = 533_333_333;
const DCI_MAX_FREQ: u32 = 10_000_000;
pub struct DdrRam { pub struct DdrRam {
regs: &'static mut regs::RegisterBlock, regs: &'static mut regs::RegisterBlock,
} }
impl DdrRam { impl DdrRam {
pub fn new() -> Self { pub fn ddrram() -> Self {
let clocks = Self::clock_setup(); let clocks = Self::clock_setup();
Self::calibrate_iob_impedance(&clocks);
Self::configure_iob(); Self::configure_iob();
Self::calibrate_iob_impedance(&clocks);
let regs = unsafe { regs::RegisterBlock::new() }; let regs = regs::RegisterBlock::ddrc();
let mut ddr = DdrRam { regs }; let mut ddr = DdrRam { regs };
ddr.reset_ddrc(); ddr.reset_ddrc(|ddr| ddr.configure());
ddr ddr
} }
@ -55,14 +67,35 @@ impl DdrRam {
clocks clocks
} }
fn calculate_dci_divisors(clocks: &Clocks) -> (u8, u8) {
let target = (DCI_MAX_FREQ - 1 + clocks.ddr) / DCI_MAX_FREQ;
let mut best = None;
let mut best_error = 0;
for divisor0 in 1..63 {
for divisor1 in 1..63 {
let current = (divisor0 as u32) * (divisor1 as u32);
let error = if current > target {
current - target
} else {
target - current
};
if best.is_none() || best_error > error {
best = Some((divisor0, divisor1));
best_error = error;
}
}
}
best.unwrap()
}
/// Zynq-7000 AP SoC Technical Reference Manual: /// Zynq-7000 AP SoC Technical Reference Manual:
/// 10.6.2 DDR IOB Impedance Calibration /// 10.6.2 DDR IOB Impedance Calibration
fn calibrate_iob_impedance(clocks: &Clocks) { fn calibrate_iob_impedance(clocks: &Clocks) {
let divisor0 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ) let (divisor0, divisor1) = Self::calculate_dci_divisors(clocks);
.max(1).min(63) as u8; debug!("DDR DCI clock: {} Hz (divisors={}*{})",
let divisor1 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ / u32::from(divisor0)) clocks.ddr / u32::from(divisor0) / u32::from(divisor1),
.max(1).min(63) as u8; divisor0, divisor1);
debug!("DDR DCI clock: {} Hz", clocks.ddr / u32::from(divisor0) / u32::from(divisor1));
slcr::RegisterBlock::unlocked(|slcr| { slcr::RegisterBlock::unlocked(|slcr| {
// Step 1. // Step 1.
@ -118,13 +151,23 @@ impl DdrRam {
.output_en(slcr::DdriobOutputEn::Obuf); .output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
let data1_config = data0_config.clone(); let data1_config = data0_config.clone();
#[cfg(feature = "target_cora_z7_10")] #[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let data0_config = slcr::DdriobConfig::zeroed() let data0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::VrefDifferential) .inp_type(slcr::DdriobInputType::VrefDifferential)
.term_en(true) .term_en(true)
.dci_type(slcr::DdriobDciType::Termination) .dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf); .output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_cora_z7_10")] #[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let data1_config = slcr::DdriobConfig::zeroed() let data1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true); .pullup_en(true);
slcr.ddriob_data0.write(data0_config); slcr.ddriob_data0.write(data0_config);
@ -138,16 +181,25 @@ impl DdrRam {
.output_en(slcr::DdriobOutputEn::Obuf); .output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
let diff1_config = diff0_config.clone(); let diff1_config = diff0_config.clone();
#[cfg(feature = "target_cora_z7_10")] #[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let diff0_config = slcr::DdriobConfig::zeroed() let diff0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::Differential) .inp_type(slcr::DdriobInputType::Differential)
.term_en(true) .term_en(true)
.dci_type(slcr::DdriobDciType::Termination) .dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf); .output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_cora_z7_10")] #[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let diff1_config = slcr::DdriobConfig::zeroed() let diff1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true); .pullup_en(true);
slcr.ddriob_diff0.write(diff0_config); slcr.ddriob_diff0.write(diff0_config);
slcr.ddriob_diff1.write(diff1_config); slcr.ddriob_diff1.write(diff1_config);
@ -164,12 +216,17 @@ impl DdrRam {
slcr.ddriob_drive_slew_clock.write(0x00F9861C); slcr.ddriob_drive_slew_clock.write(0x00F9861C);
} }
// Enable external V[REF] #[cfg(any(
#[cfg(feature = "target_cora_z7_10")] feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
slcr.ddriob_ddr_ctrl.modify(|_, w| w slcr.ddriob_ddr_ctrl.modify(|_, w| w
.vref_int_en(false) .vref_int_en(false)
.vref_ext_en_lower(true) .vref_ext_en_lower(true)
.vref_ext_en_upper(false) .vref_ext_en_upper(false)
.refio_en(true)
); );
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
slcr.ddriob_ddr_ctrl.modify(|_, w| w slcr.ddriob_ddr_ctrl.modify(|_, w| w
@ -181,8 +238,200 @@ impl DdrRam {
}); });
} }
fn configure(&mut self) {
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
self.regs.dram_param0.write(
regs::DramParam0::zeroed()
.t_rc(0x1a)
.t_rfc_min(0x9e)
.post_selfref_gap_x32(0x10)
);
#[cfg(feature = "target_ebaz4205")]
self.regs.dram_param0.write(
regs::DramParam0::zeroed()
.t_rc(0x1a)
.t_rfc_min(0x56)
.post_selfref_gap_x32(0x10)
);
#[cfg(feature = "target_redpitaya")]
self.regs.dram_param0.write(
regs::DramParam0::zeroed()
.t_rc(0x1b)
.t_rfc_min(0xa0)
.post_selfref_gap_x32(0x10)
);
#[cfg(feature = "target_zc706")]
self.regs.dram_param0.write(
regs::DramParam0::zeroed()
.t_rc(0x1b)
.t_rfc_min(0x56)
.post_selfref_gap_x32(0x10)
);
#[cfg(feature = "target_ebaz4205")]
self.regs.dram_param1.modify(
|_, w| w
.t_faw(0x16)
.t_ras_min(0x13)
);
#[cfg(feature = "target_redpitaya")]
self.regs.dram_param1.modify(
|_, w| w
.wr2pre(0x12)
.powerdown_to_x32(6)
.t_faw(0x16)
.t_ras_max(0x24)
.t_ras_min(0x13)
.t_cke(4)
);
self.regs.dram_param2.write(
regs::DramParam2::zeroed()
.write_latency(0x5)
.rd2wr(0x7)
.wr2rd(0xe)
.t_xp(0x4)
.pad_pd(0x0)
.rd2pre(0x4)
.t_rcd(0x7)
);
#[cfg(feature = "target_ebaz4205")]
self.regs.dram_param3.modify(
|_, w| w
.t_rp(7)
);
#[cfg(feature = "target_redpitaya")]
self.regs.dram_param3.modify(
|_, w| w
.t_ccd(4)
.t_rrd(6)
.refresh_margin(2)
.t_rp(7)
.refresh_to_x32(8)
.mobile(false)
.dfi_dram_clk_disable(false)
.read_latency(7)
.mode_ddr1_ddr2(true)
.dis_pad_pd(false)
);
self.regs.dram_emr_mr.write(
regs::DramEmrMr::zeroed()
.mr(0x930)
.emr(0x4)
);
#[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
self.regs.phy_configs[2].modify(
|_, w| w.data_slice_in_use(false)
);
#[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
self.regs.phy_configs[3].modify(
|_, w| w.data_slice_in_use(false)
);
self.regs.phy_cmd_timeout_rddata_cpt.modify(
|_, w| w
.rd_cmd_to_data(0x0)
.wr_cmd_to_data(0x0)
.we_to_re_delay(0x8)
.rdc_fifo_rst_disable(false)
.use_fixed_re(true)
.rdc_fifo_rst_err_cnt_clr(false)
.dis_phy_ctrl_rstn(false)
.clk_stall_level(false)
.gatelvl_num_of_dq0(0x7)
.wrlvl_num_of_dq0(0x7)
);
self.regs.reg_2c.write(
regs::Reg2C::zeroed()
.wrlvl_max_x1024(0xfff)
.rdlvl_max_x1024(0xfff)
.twrlvl_max_error(false)
.trdlvl_max_error(false)
.dfi_wr_level_en(true)
.dfi_rd_dqs_gate_level(true)
.dfi_rd_data_eye_train(true)
);
self.regs.dfi_timing.write(
regs::DfiTiming::zeroed()
.rddata_en(0x6)
.ctrlup_min(0x3)
.ctrlup_max(0x40)
);
#[cfg(feature = "target_zc706")]
self.regs.phy_init_ratios[3].write(
regs::PhyInitRatio::zeroed()
.wrlvl_init_ratio(0x21)
.gatelvl_init_ratio(0xee)
);
#[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_kasli_soc"),
)]
self.regs.reg_64.modify(
|_, w| w
.phy_ctrl_slave_ratio(0x100)
.phy_invert_clkout(true)
);
#[cfg(feature = "target_redpitaya")]
self.regs.reg_64.modify(
|_, w| w
.phy_bl2(false)
.phy_invert_clkout(true)
.phy_sel_logic(false)
.phy_ctrl_slave_ratio(0x100)
.phy_ctrl_slave_force(false)
.phy_ctrl_slave_delay(0)
.phy_lpddr(false)
.phy_cmd_latency(false)
);
self.regs.reg_65.write(
regs::Reg65::zeroed()
.wr_rl_delay(0x2)
.rd_rl_delay(0x4)
.dll_lock_diff(0xf)
.use_wr_level(true)
.use_rd_dqs_gate_level(true)
.use_rd_data_eye_level(true)
.dis_calib_rst(false)
.ctrl_slave_delay(0x0)
);
}
/// Reset DDR controller /// Reset DDR controller
fn reset_ddrc(&mut self) { fn reset_ddrc<F: FnMut(&mut Self)>(&mut self, mut f: F) {
#[cfg(feature = "target_zc706")]
let width = regs::DataBusWidth::Width32bit;
#[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let width = regs::DataBusWidth::Width16bit;
self.regs.ddrc_ctrl.modify(|_, w| w
.soft_rstb(false)
.powerdown_en(false)
.data_bus_width(width)
);
f(self);
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
unsafe { unsafe {
// row/column address bits // row/column address bits
@ -190,16 +439,19 @@ impl DdrRam {
self.regs.dram_addr_map_col.write(0xFFF00000); self.regs.dram_addr_map_col.write(0xFFF00000);
self.regs.dram_addr_map_row.write(0x0F666666); self.regs.dram_addr_map_row.write(0x0F666666);
} }
#[cfg(any(
feature = "target_coraz7",
feature = "target_ebaz4205",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
unsafe {
// row/column address bits
self.regs.dram_addr_map_bank.write(0x00000666);
self.regs.dram_addr_map_col.write(0xFFFF0000);
self.regs.dram_addr_map_row.write(0x0F555555);
}
#[cfg(feature = "target_zc706")]
let width = regs::DataBusWidth::Width32bit;
#[cfg(feature = "target_cora_z7_10")]
let width = regs::DataBusWidth::Width16bit;
self.regs.ddrc_ctrl.modify(|_, w| w
.soft_rstb(false)
.powerdown_en(false)
.data_bus_width(width)
);
self.regs.ddrc_ctrl.modify(|_, w| w self.regs.ddrc_ctrl.modify(|_, w| w
.soft_rstb(true) .soft_rstb(true)
.powerdown_en(false) .powerdown_en(false)
@ -210,7 +462,7 @@ impl DdrRam {
} }
pub fn status(&self) -> regs::ControllerStatus { pub fn status(&self) -> regs::ControllerStatus {
self.regs.mode_sts_reg.read().operating_mode() self.regs.mode_sts.read().operating_mode()
} }
pub fn ptr<T>(&mut self) -> *mut T { pub fn ptr<T>(&mut self) -> *mut T {
@ -220,10 +472,18 @@ impl DdrRam {
/// actually there's 1 MB more but starting at 0x0000_0000 /// actually there's 1 MB more but starting at 0x0000_0000
/// overlaps with OCM. /// overlaps with OCM.
pub fn size(&self) -> usize { pub fn size(&self) -> usize {
// DDR range ends at 0x3FFF_FFFF in the default SCU address
// filtering address map
#[cfg(feature = "target_zc706")] #[cfg(feature = "target_zc706")]
let megabytes = 1023; let megabytes = 1023;
#[cfg(feature = "target_cora_z7_10")] #[cfg(any(
let megabytes = 511; feature = "target_coraz7",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let megabytes = 512;
#[cfg(feature = "target_ebaz4205")]
let megabytes = 256;
megabytes * 1024 * 1024 megabytes * 1024 * 1024
} }
@ -239,7 +499,7 @@ impl DdrRam {
for megabyte in 0..slice.len() / (1024 * 1024) { for megabyte in 0..slice.len() / (1024 * 1024) {
let start = megabyte * 1024 * 1024 / 4; let start = megabyte * 1024 * 1024 / 4;
let end = ((megabyte + 1) * 1024 * 1024 / 4); let end = (megabyte + 1) * 1024 * 1024 / 4;
for b in slice[start..end].iter_mut() { for b in slice[start..end].iter_mut() {
expected.map(|expected| { expected.map(|expected| {
let read: u32 = *b; let read: u32 = *b;

View File

@ -1,6 +1,6 @@
use volatile_register::{RO, RW}; use volatile_register::{RO, RW};
use libregister::{register, register_bit, register_bits_typed}; use libregister::{register, register_at, register_bit, register_bits, register_bits_typed};
#[allow(unused)] #[allow(unused)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -29,70 +29,64 @@ pub enum ControllerStatus {
pub struct RegisterBlock { pub struct RegisterBlock {
pub ddrc_ctrl: DdrcCtrl, pub ddrc_ctrl: DdrcCtrl,
pub two_rank_cfg: RW<u32>, pub two_rank_cfg: RW<u32>,
pub hpr_reg: RW<u32>, pub hpr: RW<u32>,
pub lpr_reg: RW<u32>, pub lpr: RW<u32>,
pub wr_reg: RW<u32>, pub wr: RW<u32>,
pub dram_param_reg0: RW<u32>, pub dram_param0: DramParam0,
pub dram_param_reg1: RW<u32>, pub dram_param1: DramParam1,
pub dram_param_reg2: RW<u32>, pub dram_param2: DramParam2,
pub dram_param_reg3: RW<u32>, pub dram_param3: DramParam3,
pub dram_param_reg4: RW<u32>, pub dram_param4: RW<u32>,
pub dram_init_param: RW<u32>, pub dram_init_param: RW<u32>,
pub dram_emr_reg: RW<u32>, pub dram_emr: RW<u32>,
pub dram_emr_mr_reg: RW<u32>, pub dram_emr_mr: DramEmrMr,
pub dram_burst8_rdwr: RW<u32>, pub dram_burst8_rdwr: Burst8Rdwr,
pub dram_disable_dq: RW<u32>, pub dram_disable_dq: RW<u32>,
pub dram_addr_map_bank: RW<u32>, pub dram_addr_map_bank: RW<u32>,
pub dram_addr_map_col: RW<u32>, pub dram_addr_map_col: RW<u32>,
pub dram_addr_map_row: RW<u32>, pub dram_addr_map_row: RW<u32>,
pub dram_odt_reg: RW<u32>, pub dram_odt: RW<u32>,
pub phy_dbg_reg: RW<u32>, pub phy_dbg: RW<u32>,
pub phy_cmd_timeout_rddata_cpt: RW<u32>, pub phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRddataCpt,
pub mode_sts_reg: ModeStsReg, pub mode_sts: ModeStsReg,
pub dll_calib: RW<u32>, pub dll_calib: RW<u32>,
pub odt_delay_hold: RW<u32>, pub odt_delay_hold: RW<u32>,
pub ctrl_reg1: RW<u32>, pub ctrl1: RW<u32>,
pub ctrl_reg2: RW<u32>, pub ctrl2: RW<u32>,
pub ctrl_reg3: RW<u32>, pub ctrl3: RW<u32>,
pub ctrl_reg4: RW<u32>, pub ctrl4: RW<u32>,
_unused0: [RO<u32>; 2], _unused0: [RO<u32>; 2],
pub ctrl_reg5: RW<u32>, pub ctrl5: RW<u32>,
pub ctrl_reg6: RW<u32>, pub ctrl6: RW<u32>,
_unused1: [RO<u32>; 8], _unused1: [RO<u32>; 8],
pub che_refresh_timer01: RW<u32>, pub che_refresh_timer01: RW<u32>,
pub che_t_zq: RW<u32>, pub che_t_zq: CheTZq,
pub che_t_zq_short_interval_reg: RW<u32>, pub che_t_zq_short_interval: RW<u32>,
pub deep_pwrdwn_reg: RW<u32>, pub deep_pwrdwn: RW<u32>,
pub reg_2c: RW<u32>, pub reg_2c: Reg2C,
pub reg_2d: RW<u32>, pub reg_2d: RW<u32>,
pub dfi_timing: RW<u32>, pub dfi_timing: DfiTiming,
_unused2: [RO<u32>; 2], _unused2: [RO<u32>; 2],
pub che_ecc_control_reg_offset: RW<u32>, pub che_ecc_control_offset: RW<u32>,
pub che_corr_ecc_log_reg_offset: RW<u32>, pub che_corr_ecc_log_offset: RW<u32>,
pub che_corr_ecc_addr_reg_offset: RW<u32>, pub che_corr_ecc_addr_offset: RW<u32>,
pub che_corr_ecc_data_31_0_reg_offset: RW<u32>, pub che_corr_ecc_data_31_0_offset: RW<u32>,
pub che_corr_ecc_data_63_32_reg_offset: RW<u32>, pub che_corr_ecc_data_63_32_offset: RW<u32>,
pub che_corr_ecc_data_71_64_reg_offset: RW<u32>, pub che_corr_ecc_data_71_64_offset: RW<u32>,
pub che_uncorr_ecc_log_reg_offset: RW<u32>, pub che_uncorr_ecc_log_offset: RW<u32>,
pub che_uncorr_ecc_addr_reg_offset: RW<u32>, pub che_uncorr_ecc_addr_offset: RW<u32>,
pub che_uncorr_ecc_data_31_0_reg_offset: RW<u32>, pub che_uncorr_ecc_data_31_0_offset: RW<u32>,
pub che_uncorr_ecc_data_63_32_reg_offset: RW<u32>, pub che_uncorr_ecc_data_63_32_offset: RW<u32>,
pub che_uncorr_ecc_data_71_64_reg_offset: RW<u32>, pub che_uncorr_ecc_data_71_64_offset: RW<u32>,
pub che_ecc_stats_reg_offset: RW<u32>, pub che_ecc_stats_offset: RW<u32>,
pub ecc_scrub: RW<u32>, pub ecc_scrub: RW<u32>,
pub che_ecc_corr_bit_mask_31_0_reg_offset: RW<u32>, pub che_ecc_corr_bit_mask_31_0_offset: RW<u32>,
pub che_ecc_corr_bit_mask_63_32_reg_offset: RW<u32>, pub che_ecc_corr_bit_mask_63_32_offset: RW<u32>,
_unused3: [RO<u32>; 5], _unused3: [RO<u32>; 5],
pub phy_rcvr_enable: RW<u32>, pub phy_rcvr_enable: RW<u32>,
pub phy_config0: RW<u32>, pub phy_configs: [PhyConfig; 4],
pub phy_config1: RW<u32>,
pub phy_config2: RW<u32>,
pub phy_config3: RW<u32>,
_unused4: RO<u32>, _unused4: RO<u32>,
pub phy_init_ratio0: RW<u32>, pub phy_init_ratios: [PhyInitRatio; 4],
pub phy_init_ratio1: RW<u32>,
pub phy_init_ratio2: RW<u32>,
pub phy_init_ratio3: RW<u32>,
_unused5: RO<u32>, _unused5: RO<u32>,
pub phy_rd_dqs_cfg0: RW<u32>, pub phy_rd_dqs_cfg0: RW<u32>,
pub phy_rd_dqs_cfg1: RW<u32>, pub phy_rd_dqs_cfg1: RW<u32>,
@ -114,8 +108,8 @@ pub struct RegisterBlock {
pub wr_data_slv2: RW<u32>, pub wr_data_slv2: RW<u32>,
pub wr_data_slv3: RW<u32>, pub wr_data_slv3: RW<u32>,
_unused9: RO<u32>, _unused9: RO<u32>,
pub reg_64: RW<u32>, pub reg_64: Reg64,
pub reg_65: RW<u32>, pub reg_65: Reg65,
_unused10: [RO<u32>; 3], _unused10: [RO<u32>; 3],
pub reg69_6a0: RW<u32>, pub reg69_6a0: RW<u32>,
pub reg69_6a1: RW<u32>, pub reg69_6a1: RW<u32>,
@ -134,18 +128,12 @@ pub struct RegisterBlock {
_unused13: RO<u32>, _unused13: RO<u32>,
pub dll_lock_sts: RW<u32>, pub dll_lock_sts: RW<u32>,
pub phy_ctrl_sts: RW<u32>, pub phy_ctrl_sts: RW<u32>,
pub phy_ctrl_sts_reg2: RW<u32>, pub phy_ctrl_sts2: RW<u32>,
_unused14: [RO<u32>; 5], _unused14: [RO<u32>; 5],
pub axi_id: RW<u32>, pub axi_id: RW<u32>,
pub page_mask: RW<u32>, pub page_mask: RW<u32>,
pub axi_priority_wr_port0: RW<u32>, pub axi_priority_wr_ports: [RW<u32>; 4],
pub axi_priority_wr_port1: RW<u32>, pub axi_priority_rd_ports: [AxiPriorityRd; 4],
pub axi_priority_wr_port2: RW<u32>,
pub axi_priority_wr_port3: RW<u32>,
pub axi_priority_rd_port0: RW<u32>,
pub axi_priority_rd_port1: RW<u32>,
pub axi_priority_rd_port2: RW<u32>,
pub axi_priority_rd_port3: RW<u32>,
_unused15: [RO<u32>; 27], _unused15: [RO<u32>; 27],
pub excl_access_cfg0: RW<u32>, pub excl_access_cfg0: RW<u32>,
pub excl_access_cfg1: RW<u32>, pub excl_access_cfg1: RW<u32>,
@ -158,11 +146,7 @@ pub struct RegisterBlock {
pub lpddr_ctrl3: RW<u32>, pub lpddr_ctrl3: RW<u32>,
} }
impl RegisterBlock { register_at!(RegisterBlock, 0xF8006000, ddrc);
pub unsafe fn new() -> &'static mut Self {
&mut *(0xF8006000 as *mut _)
}
}
register!(ddrc_ctrl, DdrcCtrl, RW, u32); register!(ddrc_ctrl, DdrcCtrl, RW, u32);
register_bit!(ddrc_ctrl, register_bit!(ddrc_ctrl,
@ -172,8 +156,124 @@ register_bit!(ddrc_ctrl, powerdown_en, 1);
register_bits_typed!(ddrc_ctrl, data_bus_width, u8, DataBusWidth, 2, 3); register_bits_typed!(ddrc_ctrl, data_bus_width, u8, DataBusWidth, 2, 3);
// (ddrc_ctrl) ... // (ddrc_ctrl) ...
register!(dram_param0, DramParam0, RW, u32);
register_bits!(dram_param0, t_rc, u8, 0, 5);
register_bits!(dram_param0, t_rfc_min, u8, 6, 13);
register_bits!(dram_param0, post_selfref_gap_x32, u8, 14, 20);
register!(dram_param1, DramParam1, RW, u32);
register_bits!(dram_param1, wr2pre, u8, 0, 4);
register_bits!(dram_param1, powerdown_to_x32, u8, 5, 9);
register_bits!(dram_param1, t_faw, u8, 10, 15);
register_bits!(dram_param1, t_ras_max, u8, 16, 21);
register_bits!(dram_param1, t_ras_min, u8, 22, 26);
register_bits!(dram_param1, t_cke, u8, 28, 31);
register!(dram_param2, DramParam2, RW, u32);
register_bits!(dram_param2, write_latency, u8, 0, 4);
register_bits!(dram_param2, rd2wr, u8, 5, 9);
register_bits!(dram_param2, wr2rd, u8, 10, 14);
register_bits!(dram_param2, t_xp, u8, 15, 19);
register_bits!(dram_param2, pad_pd, u8, 20, 22);
register_bits!(dram_param2, rd2pre, u8, 23, 27);
register_bits!(dram_param2, t_rcd, u8, 28, 31);
register!(dram_param3, DramParam3, RW, u32);
register_bits!(dram_param3, t_ccd, u8, 2, 4);
register_bits!(dram_param3, t_rrd, u8, 5, 7);
register_bits!(dram_param3, refresh_margin, u8, 8, 11);
register_bits!(dram_param3, t_rp, u8, 12, 15);
register_bits!(dram_param3, refresh_to_x32, u8, 16, 20);
register_bit!(dram_param3, sdram, 21);
register_bit!(dram_param3, mobile, 22);
register_bit!(dram_param3, dfi_dram_clk_disable, 23);
register_bits!(dram_param3, read_latency, u8, 24, 28);
register_bit!(dram_param3, mode_ddr1_ddr2, 29);
register_bit!(dram_param3, dis_pad_pd, 30);
register!(dram_emr_mr, DramEmrMr, RW, u32);
register_bits!(dram_emr_mr, mr, u16, 0, 15);
register_bits!(dram_emr_mr, emr, u16, 16, 31);
register!(burst8_rdwr, Burst8Rdwr, RW, u32);
register_bits!(burst8_rdwr, burst_rdwr, u8, 0, 3);
register_bits!(burst8_rdwr, pre_cke_x1024, u16, 4, 13);
register_bits!(burst8_rdwr, post_cke_x1024, u16, 16, 25);
register_bit!(burst8_rdwr, burstchop, 28);
register!(phy_cmd_timeout_rddata_cpt, PhyCmdTimeoutRddataCpt, RW, u32);
register_bits!(phy_cmd_timeout_rddata_cpt, rd_cmd_to_data, u8, 0, 3);
register_bits!(phy_cmd_timeout_rddata_cpt, wr_cmd_to_data, u8, 4, 7);
register_bits!(phy_cmd_timeout_rddata_cpt, we_to_re_delay, u8, 8, 11);
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_disable, 15);
register_bit!(phy_cmd_timeout_rddata_cpt, use_fixed_re, 16);
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_err_cnt_clr, 17);
register_bit!(phy_cmd_timeout_rddata_cpt, dis_phy_ctrl_rstn, 18);
register_bit!(phy_cmd_timeout_rddata_cpt, clk_stall_level, 19);
register_bits!(phy_cmd_timeout_rddata_cpt, gatelvl_num_of_dq0, u8, 24, 27);
register_bits!(phy_cmd_timeout_rddata_cpt, wrlvl_num_of_dq0, u8, 28, 31);
register!(che_t_zq, CheTZq, RW, u32);
register_bit!(che_t_zq, dis_auto_zq, 0);
register_bit!(che_t_zq, ddr3, 1);
register_bits!(che_t_zq, t_mod, u8, 2, 11);
register_bits!(che_t_zq, t_zq_long_nop, u16, 12, 21);
register_bits!(che_t_zq, t_zq_short_nop, u16, 22, 31);
register!(reg_2c, Reg2C, RW, u32);
register_bits!(reg_2c, wrlvl_max_x1024, u16, 0, 11);
register_bits!(reg_2c, rdlvl_max_x1024, u16, 12, 23);
register_bit!(reg_2c, twrlvl_max_error, 24);
register_bit!(reg_2c, trdlvl_max_error, 25);
register_bit!(reg_2c, dfi_wr_level_en, 26);
register_bit!(reg_2c, dfi_rd_dqs_gate_level, 27);
register_bit!(reg_2c, dfi_rd_data_eye_train, 28);
register!(dfi_timing, DfiTiming, RW, u32);
register_bits!(dfi_timing, rddata_en, u8, 0, 4);
register_bits!(dfi_timing, ctrlup_min, u16, 5, 14);
register_bits!(dfi_timing, ctrlup_max, u16, 15, 24);
register!(phy_config, PhyConfig, RW, u32);
register_bit!(phy_config, data_slice_in_use, 0);
register_bit!(phy_config, rdlvl_inc_mode, 1);
register_bit!(phy_config, gatelvl_inc_mode, 2);
register_bit!(phy_config, wrlvl_inc_mode, 3);
register_bits!(phy_config, dq_offset, u8, 24, 30);
register!(phy_init_ratio, PhyInitRatio, RW, u32);
register_bits!(phy_init_ratio, wrlvl_init_ratio, u16, 0, 9);
register_bits!(phy_init_ratio, gatelvl_init_ratio, u16, 10, 19);
register!(reg_64, Reg64, RW, u32);
register_bit!(reg_64, phy_bl2, 1);
register_bit!(reg_64, phy_invert_clkout, 7);
register_bit!(reg_64, phy_sel_logic, 9);
register_bits!(reg_64, phy_ctrl_slave_ratio, u16, 10, 19);
register_bit!(reg_64, phy_ctrl_slave_force, 20);
register_bits!(reg_64, phy_ctrl_slave_delay, u8, 21, 27);
register_bit!(reg_64, phy_lpddr, 29);
register_bit!(reg_64, phy_cmd_latency, 30);
register!(reg_65, Reg65, RW, u32);
register_bits!(reg_65, wr_rl_delay, u8, 0, 4);
register_bits!(reg_65, rd_rl_delay, u8, 5, 9);
register_bits!(reg_65, dll_lock_diff, u8, 10, 13);
register_bit!(reg_65, use_wr_level, 14);
register_bit!(reg_65, use_rd_dqs_gate_level, 15);
register_bit!(reg_65, use_rd_data_eye_level, 16);
register_bit!(reg_65, dis_calib_rst, 17);
register_bits!(reg_65, ctrl_slave_delay, u8, 18, 19);
// Controller operation mode status // Controller operation mode status
register!(mode_sts_reg, register!(mode_sts_reg,
ModeStsReg, RO, u32); ModeStsReg, RO, u32);
register_bits_typed!(mode_sts_reg, operating_mode, u8, ControllerStatus, 0, 2); register_bits_typed!(mode_sts_reg, operating_mode, u8, ControllerStatus, 0, 2);
// (mode_sts_reg) ... // (mode_sts_reg) ...
register!(axi_priority_rd, AxiPriorityRd, RW, u32);
register_bits!(axi_priority_rd, arb_pri_rd_portn, u16, 0, 9);
register_bit!(axi_priority_rd, arb_disable_aging_rd_portn, 16);
register_bit!(axi_priority_rd, arb_disable_urgent_rd_portn, 17);
register_bit!(axi_priority_rd, arb_disable_page_match_rd_portn, 18);
register_bit!(axi_priority_rd, arb_set_hpr_rd_portn, 19);

View File

@ -1,4 +1,3 @@
use super::clocks::Clocks;
use super::time::Milliseconds; use super::time::Milliseconds;
use crate::slcr; use crate::slcr;
use embedded_hal::timer::CountDown; use embedded_hal::timer::CountDown;
@ -11,7 +10,7 @@ mod regs;
pub struct DevC { pub struct DevC {
regs: &'static mut regs::RegisterBlock, regs: &'static mut regs::RegisterBlock,
enabled: bool, enabled: bool,
count_down: super::timer::global::CountDown, count_down: super::timer::global::CountDown<Milliseconds>,
timeout_ms: Milliseconds, timeout_ms: Milliseconds,
} }

View File

@ -1,3 +0,0 @@
//! PrimeCell DMA Controller (PL330)
mod regs;

View File

@ -1,386 +0,0 @@
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
};
#[allow(unused)]
#[repr(C)]
pub struct RegisterBlock {
pub ds: Ds,
pub dpc: DPc,
pub inten: Inten,
pub es: Es,
pub intstatus: IntStatus,
pub intclr: IntClr,
pub fsm: Fsm,
pub fsc: Fsc,
pub ftm: Ftm,
pub ftc: [Ftc; 8],
pub cs0: Cs,
pub cpc0: Cpc,
pub cs1: Cs,
pub cpc1: Cpc,
pub cs2: Cs,
pub cpc2: Cpc,
pub cs3: Cs,
pub cpc3: Cpc,
pub cs4: Cs,
pub cpc4: Cpc,
pub cs5: Cs,
pub cpc5: Cpc,
pub cs6: Cs,
pub cpc6: Cpc,
pub cs7: Cs,
pub cpc7: Cpc,
pub sa0: Sa,
pub da0: Da,
pub cc0: Cc,
pub lc0_0: Lc,
pub lc0_1: Lc,
pub sa1: Sa,
pub da1: Da,
pub cc1: Cc,
pub lc1_0: Lc,
pub lc1_1: Lc,
pub sa2: Sa,
pub da2: Da,
pub cc2: Cc,
pub lc2_0: Lc,
pub lc2_1: Lc,
pub sa3: Sa,
pub da3: Da,
pub cc3: Cc,
pub lc3_0: Lc,
pub lc3_1: Lc,
pub sa4: Sa,
pub da4: Da,
pub cc4: Cc,
pub lc4_0: Lc,
pub lc4_1: Lc,
pub sa5: Sa,
pub da5: Da,
pub cc5: Cc,
pub lc5_0: Lc,
pub lc5_1: Lc,
pub sa6: Sa,
pub da6: Da,
pub cc6: Cc,
pub lc6_0: Lc,
pub lc6_1: Lc,
pub sa7: Sa,
pub da7: Da,
pub cc7: Cc,
pub lc7_0: Lc,
pub lc7_1: Lc,
pub dbgstatus: DbgStatus,
pub dbgcmd: DbgCmd,
pub dbginst0: DbgInst0,
pub dbginst1: DbgInst1,
pub cr0: Cr0,
pub cr1: Cr1,
pub cr2: Cr2,
pub cr3: Cr3,
pub cr4: Cr4,
pub crdn: Crdn,
pub wd: Wd,
pub periph_id_0: PeriphId0,
pub periph_id_1: PeriphId1,
pub periph_id_2: PeriphId2,
pub periph_id_3: PeriphId3,
pub pcell_id_0: PCellId0,
pub pcell_id_1: PCellId1,
pub pcell_id_2: PCellId2,
pub pcell_id_3: PCellId3,
}
register_at!(RegisterBlock, 0xF8004000, dmac0_ns);
register_at!(RegisterBlock, 0xF8003000, dmac0_s);
impl RegisterBlock {
pub fn channel_regs(&mut self, channel: usize) -> Option<ChannelRegisters>
{
match channel {
0 => Some(ChannelRegisters {
ftc: &mut self.ftc[0],
cs: &mut self.cs0,
cpc: &mut self.cpc0,
sa: &mut self.sa0,
da: &mut self.da0,
cc: &mut self.cc0,
lc: [&mut self.lc0_0, &mut self.lc0_1],
}),
1 => Some(ChannelRegisters {
ftc: &mut self.ftc[1],
cs: &mut self.cs1,
cpc: &mut self.cpc1,
sa: &mut self.sa1,
da: &mut self.da1,
cc: &mut self.cc1,
lc: [&mut self.lc1_0, &mut self.lc1_1],
}),
2 => Some(ChannelRegisters {
ftc: &mut self.ftc[2],
cs: &mut self.cs2,
cpc: &mut self.cpc2,
sa: &mut self.sa2,
da: &mut self.da2,
cc: &mut self.cc2,
lc: [&mut self.lc2_0, &mut self.lc2_1],
}),
3 => Some(ChannelRegisters {
ftc: &mut self.ftc[3],
cs: &mut self.cs3,
cpc: &mut self.cpc3,
sa: &mut self.sa3,
da: &mut self.da3,
cc: &mut self.cc3,
lc: [&mut self.lc3_0, &mut self.lc3_1],
}),
4 => Some(ChannelRegisters {
ftc: &mut self.ftc[4],
cs: &mut self.cs4,
cpc: &mut self.cpc4,
sa: &mut self.sa4,
da: &mut self.da4,
cc: &mut self.cc4,
lc: [&mut self.lc4_0, &mut self.lc4_1],
}),
5 => Some(ChannelRegisters {
ftc: &mut self.ftc[5],
cs: &mut self.cs5,
cpc: &mut self.cpc5,
sa: &mut self.sa5,
da: &mut self.da5,
cc: &mut self.cc5,
lc: [&mut self.lc5_0, &mut self.lc5_1],
}),
6 => Some(ChannelRegisters {
ftc: &mut self.ftc[6],
cs: &mut self.cs6,
cpc: &mut self.cpc6,
sa: &mut self.sa6,
da: &mut self.da6,
cc: &mut self.cc6,
lc: [&mut self.lc6_0, &mut self.lc6_1],
}),
7 => Some(ChannelRegisters {
ftc: &mut self.ftc[7],
cs: &mut self.cs7,
cpc: &mut self.cpc7,
sa: &mut self.sa7,
da: &mut self.da7,
cc: &mut self.cc7,
lc: [&mut self.lc7_0, &mut self.lc7_1],
}),
_ => None,
}
}
}
pub struct ChannelRegisters<'a> {
ftc: &'a mut Ftc,
cs: &'a mut Cs,
cpc: &'a mut Cpc,
sa: &'a mut Sa,
da: &'a mut Da,
cc: &'a mut Cc,
lc: [&'a mut Lc; 2],
}
#[allow(unused)]
#[repr(u8)]
pub enum WakeUpEvent{
// @missing: there's a binary prefix ahead of this as per TRM 1173 Wakeup_event
Event0 = 0b0000,
Event1 = 0b0001,
Event2 = 0b0010,
Event3 = 0b0011,
Event4 = 0b0100,
Event5 = 0b0101,
Event6 = 0b0110,
Event7 = 0b0111,
Event8 = 0b1000,
Event9 = 0b1001,
Event10 = 0b1010,
Event11 = 0b1011,
Event12 = 0b1100,
Event13 = 0b1101,
Event14 = 0b1110,
Event15 = 0b1111,
}
#[allow(unused)]
#[repr(u8)]
pub enum DMAStatus{
Stopped = 0b0000,
Executing = 0b0001,
CacheMiss = 0b0010,
UpdatingPc = 0b0011,
WaitingForEvent = 0b0100,
Reserved0 = 0b0101,
Reserved1 = 0b0110,
Reserved2 = 0b0111,
Reserved3 = 0b1000,
Reserved4 = 0b1001,
Reserved5 = 0b1010,
Reserved6 = 0b1011,
Reserved7 = 0b1100,
Reserved8 = 0b1101,
Reserved9 = 0b1110,
Faulting = 0b1111,
}
register!(ds, Ds, RW, u32);
register_bit!(ds, dns, 9);
register_bits_typed!(ds, wakeup_event, u8, WakeUpEvent, 4, 8);
register_bits_typed!(ds, dma_status, u8, DMAStatus, 0, 3);
register!(dpc, DPc, RW, u32);
register_bits!(dpc, pc_mgr, u8, 0, 31);
register!(inten, Inten, RW, u32);
register_bits!(inten, event_irq_select, u8, 0, 31);
register!(es, Es, RW, u32);
register_bits!(es, dmasev_active, u8, 0, 31);
register!(intstatus, IntStatus, RW, u32);
register_bits!(intstatus, irq_status, u8, 0, 31);
register!(intclr, IntClr, RW, u32);
register_bits!(intstatus, irq_clr, u8, 0, 31);
register!(fsm, Fsm, RW, u32);
register_bit!(fsm, fs_mgr, 0);
register!(fsc, Fsc, RW, u32);
register_bits!(fsc, fault_status, u8, 0, 7);
register!(ftm, Ftm, RW, u32);
register_bit!(ftm, dbg_instr, 30);
register_bit!(ftm, instr_fetch_err, 16);
register_bit!(ftm, mgr_evnt_err, 5);
register_bit!(ftm, dmago_err, 4);
register_bit!(ftm, operand_invalid, 1);
register_bit!(ftm, undef_instr, 0);
register!(ftc, Ftc, RW, u32);
register_bit!(ftc, lockup_err, 31);
register_bit!(ftc, dbg_instr, 30);
register_bit!(ftc, data_read_err, 18);
register_bit!(ftc, data_write_err, 17);
register_bit!(ftc, instr_fetch_err, 16);
register_bit!(ftc, st_data_unavailable, 13);
register_bit!(ftc, mfifo_err, 12);
register_bit!(ftc, ch_rdwr_err, 7);
register_bit!(ftc, ch_periph_err, 6);
register_bit!(ftc, ch_evnt_err, 5);
register_bit!(ftc, operand_invalid, 1);
register_bit!(ftc, undef_instr, 0);
register!(cs, Cs, RW, u32);
register_bit!(cs, cns, 21);
register_bit!(cs, dmawfp_periph, 15);
register_bit!(cs, dmawfp_b_ns, 14);
register_bits!(cs, wakeup_num, u8, 4, 8);
register_bits!(cs, channel_status, u8, 0, 3);
register!(cpc, Cpc, RW, u32);
register_bits!(cpc, pc_chnl, u8, 0, 31);
register!(sa, Sa, RW, u32);
register_bits!(sa, src_addr, u8, 0, 31);
register!(da, Da, RW, u32);
register_bits!(da, dest_addr, u8, 0, 31);
register!(cc, Cc, RW, u32);
register_bits!(cc, endian_swap_size, u8, 28, 30);
register_bits!(cc, dst_cache_ctrl, u8, 25, 27);
register_bits!(cc, dst_prot_ctrl, u8, 22, 24);
register_bits!(cc, dst_burst_len, u8, 18, 21);
register_bits!(cc, dst_burst_size, u8, 15, 17);
register_bit!(cc, dst_inc, 14);
register_bits!(cc, src_cache_ctrl, u8, 11, 13);
register_bits!(cc, src_prot_ctrl, u8, 8, 10);
register_bits!(cc, src_burst_len, u8, 4, 7);
register_bits!(cc, src_burst_size, u8, 1, 3);
register_bit!(cc, src_inc, 0);
register!(lc0, Lc, RW, u32);
register_bits!(lc0, loop_counter_iteration, u8, 0, 7);
register!(dbgstatus, DbgStatus, RW, u32);
register_bit!(dbgstatus, dbgstatus, 0);
register!(dbgcmd, DbgCmd, RW, u32);
register_bits!(dbgcmd, dbgcmd, u8, 0, 1);
register!(dbginst0, DbgInst0, RW, u32);
register_bits!(dbginst0, instruction_byte1, u8, 24, 31);
register_bits!(dbginst0, instruction_byte0, u8, 16, 23);
register_bits!(dbginst0, channel_num, u8, 8, 10);
register_bit!(dbginst0, debug_thread, 0);
register!(dbginst1, DbgInst1, RW, u32);
register_bits!(dbginst1, instruction_byte5, u8, 24, 31);
register_bits!(dbginst1, instruction_byte4, u8, 16, 23);
register_bits!(dbginst1, instruction_byte3, u8, 8, 10);
register_bits!(dbginst1, instruction_byte2, u8, 0, 7);
register!(cr0, Cr0, RW, u32);
register_bits!(cr0, num_events, u8, 17, 21);
register_bits!(cr0, num_periph_req, u8, 12, 16);
register_bits!(cr0, num_chnls, u8, 4, 6);
register_bit!(cr0, mgr_ns_at_rst, 2);
register_bit!(cr0, boot_en, 1);
register_bit!(cr0, periph_req, 0);
register!(cr1, Cr1, RW, u32);
register_bits!(cr1, num_icache_lines, u8, 4, 7);
register_bits!(cr1, icache_len, u8, 0, 2);
register!(cr2, Cr2, RW, u32);
register_bits!(cr2, boot_addr, u8, 0, 31);
register!(cr3, Cr3, RW, u32);
register_bits!(cr3, ins, u8, 0, 31);
register!(cr4, Cr4, RW, u32);
register_bits!(cr4, ins, u8, 0, 31);
register!(crdn, Crdn, RW, u32);
register_bits!(crdn, data_buffer_dep, u8, 20, 29);
register_bits!(crdn, rd_q_dep, u8, 16, 19);
register_bits!(crdn, rd_cap, u8, 12, 14);
register_bits!(crdn, wr_q_dep, u8, 8, 11);
register_bits!(crdn, wr_cap, u8, 4, 6);
register_bits!(crdn, data_width, u8, 0, 2);
register!(wd, Wd, RW, u32);
register_bit!(wd, wd_irq_only, 0);
register!(periph_id_0, PeriphId0, RW, u32);
register_bits!(periph_id_0, part_number_0, u8, 0, 7);
register!(periph_id_1, PeriphId1, RW, u32);
register_bits!(periph_id_1, designer_0, u8, 4, 7);
register_bits!(periph_id_1, part_number_1, u8, 0, 3);
register!(periph_id_2, PeriphId2, RW, u32);
register_bits!(periph_id_2, revision, u8, 4, 7);
register_bits!(periph_id_2, designer_1, u8, 0, 3);
register!(periph_id_3, PeriphId3, RW, u32);
register_bit!(periph_id_3, integration_cfg, 0);
register!(pcell_id_0, PCellId0, RW, u32);
register_bits!(pcell_id_0, pcell_id_0, u8, 0, 7);
register!(pcell_id_1, PCellId1, RW, u32);
register_bits!(pcell_id_1, pcell_id_1, u8, 0, 7);
register!(pcell_id_2, PCellId2, RW, u32);
register_bits!(pcell_id_2, pcell_id_2, u8, 0, 7);
register!(pcell_id_3, PCellId3, RW, u32);
register_bits!(pcell_id_3, pcell_id_3, u8, 0, 7);

View File

@ -0,0 +1,114 @@
use libregister::{RegisterRW, RegisterW};
use libregister::{register, register_at, register_bit, register_bits};
use super::slcr;
pub struct ErrorLED {
regs: RegisterBlock,
}
impl ErrorLED {
#[cfg(feature = "target_kasli_soc")]
pub fn error_led() -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Error LED at MIO pin 37
slcr.mio_pin_37.write(
slcr::MioPin37::zeroed()
.l3_sel(0b000)
.io_type(slcr::IoBufferType::Lvcmos25)
.pullup(true)
.disable_rcvr(true)
);
});
Self::error_led_common(0xFFFF - 0x0080)
}
fn error_led_common(gpio_output_mask: u16) -> Self {
// Setup register block
let self_ = Self {
regs: RegisterBlock::error_led(),
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(gpio_output_mask)
});
self_.regs.gpio_direction.modify(|_, w| {
w.lederr(true)
});
self_
}
fn led_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.lederr(oe)
})
}
fn led_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.lederr_o(o)
})
}
pub fn toggle(&mut self, state: bool) {
self.led_o(state);
self.led_oe(state);
}
}
pub struct RegisterBlock {
pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable,
}
impl RegisterBlock {
pub fn error_led() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_direction: GPIODirection::new(),
gpio_output_enable: GPIOOutputEnable::new()
}
}
}
register!(gpio_output_mask,
/// MASK_DATA_1_LSW:
/// Maskable output data for MIO[47:32]
GPIOOutputMask, RW, u32);
#[cfg(feature = "target_kasli_soc")]
register_at!(GPIOOutputMask, 0xE000A008, new);
#[cfg(feature = "target_kasli_soc")]
register_bit!(gpio_output_mask,
/// Output for LED_ERR (MIO[37])
lederr_o, 5);
#[cfg(feature = "target_kasli_soc")]
register_bits!(gpio_output_mask,
mask, u16, 16, 31);
register!(gpio_direction,
/// DIRM_1:
/// Direction mode for MIO[53:32]; 0/1 = in/out
GPIODirection, RW, u32);
#[cfg(feature = "target_kasli_soc")]
register_at!(GPIODirection, 0xE000A244, new);
#[cfg(feature = "target_kasli_soc")]
register_bit!(gpio_direction,
/// Direction for LED_ERR
lederr, 5);
register!(gpio_output_enable,
/// OEN_1:
/// Output enable for MIO[53:32]
GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_kasli_soc")]
register_at!(GPIOOutputEnable, 0xE000A248, new);
#[cfg(feature = "target_kasli_soc")]
register_bit!(gpio_output_enable,
/// Output enable for LED_ERR
lederr, 5);

View File

@ -1,5 +1,8 @@
use core::ops::{Deref, DerefMut}; use core::{
use log::{error, info, warn}; marker::PhantomData,
ops::{Deref, DerefMut},
};
use log::{debug, info, warn, error};
use libregister::*; use libregister::*;
use super::slcr; use super::slcr;
use super::clocks::Clocks; use super::clocks::Clocks;
@ -10,6 +13,9 @@ mod regs;
pub mod rx; pub mod rx;
pub mod tx; pub mod tx;
use super::time::Milliseconds;
use embedded_hal::timer::CountDown;
/// Size of all the buffers /// Size of all the buffers
pub const MTU: usize = 1536; pub const MTU: usize = 1536;
/// Maximum MDC clock /// Maximum MDC clock
@ -24,7 +30,7 @@ const TX_1000: u32 = 125_000_000;
pub struct Buffer(pub [u8; MTU]); pub struct Buffer(pub [u8; MTU]);
impl Buffer { impl Buffer {
pub fn new() -> Self { pub const fn new() -> Self {
Buffer([0; MTU]) Buffer([0; MTU])
} }
} }
@ -42,15 +48,127 @@ impl DerefMut for Buffer {
} }
} }
pub struct Eth<'r, RX, TX> { /// Gigabit Ethernet Peripheral
rx: RX, pub trait Gem {
tx: TX, fn setup_clock(tx_clock: u32);
inner: EthInner<'r>, fn regs() -> &'static mut regs::RegisterBlock;
phy: Phy,
} }
impl<'r> Eth<'r, (), ()> { /// first Gigabit Ethernet peripheral
pub fn default(macaddr: [u8; 6]) -> Self { pub struct Gem0;
impl Gem for Gem0 {
fn setup_clock(tx_clock: u32) {
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem0_clk_ctrl.write(
// 0x0050_0801: 8, 5: 100 Mb/s
// ...: 8, 1: 1000 Mb/s
#[cfg(not(feature = "target_ebaz4205"))]
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8),
// ebaz4205 -- EMIO
#[cfg(feature = "target_ebaz4205")]
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::Emio)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8)
);
// Enable gem0 recv clock
slcr.gem0_rclk_ctrl.write(
// 0x0000_0801
#[cfg(not(feature = "target_ebaz4205"))]
slcr::RclkCtrl::zeroed()
.clkact(true),
// ebaz4205 -- EMIO
#[cfg(feature = "target_ebaz4205")]
slcr::RclkCtrl::zeroed()
.clkact(true)
.srcsel(true)
);
});
}
fn regs() -> &'static mut regs::RegisterBlock {
regs::RegisterBlock::gem0()
}
}
/// second Gigabit Ethernet peripheal
pub struct Gem1;
impl Gem for Gem1 {
fn setup_clock(tx_clock: u32) {
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem1_clk_ctrl.write(
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8)
);
// Enable gem1 recv clock
slcr.gem1_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
fn regs() -> &'static mut regs::RegisterBlock {
regs::RegisterBlock::gem1()
}
}
fn calculate_tx_divisors(tx_clock: u32) -> (u8, u8) {
let io_pll = Clocks::get().io;
let target = (tx_clock - 1 + io_pll) / tx_clock;
let mut best = None;
let mut best_error = 0;
for divisor0 in 1..63 {
for divisor1 in 1..63 {
let current = (divisor0 as u32) * (divisor1 as u32);
let error = if current > target {
current - target
} else {
target - current
};
if best.is_none() || best_error > error {
best = Some((divisor0, divisor1));
best_error = error;
}
}
}
let result = best.unwrap();
debug!("Eth TX clock for {}: {} / {} / {} = {}",
tx_clock, io_pll,
result.0, result.1,
io_pll / result.0 as u32 / result.1 as u32
);
result
}
pub struct Eth<GEM: Gem, RX, TX> {
rx: RX,
tx: TX,
inner: EthInner<GEM>,
phy: Phy,
/// keep track of RX path occupation to avoid needless `check_link_change()`
idle: bool,
}
impl Eth<Gem0, (), ()> {
pub fn eth0(macaddr: [u8; 6]) -> Self {
#[cfg(not(feature = "target_ebaz4205"))]
slcr::RegisterBlock::unlocked(|slcr| { slcr::RegisterBlock::unlocked(|slcr| {
// Manual example: 0x0000_1280 // Manual example: 0x0000_1280
// MDIO // MDIO
@ -126,48 +244,48 @@ impl<'r> Eth<'r, (), ()> {
// RX_CLK // RX_CLK
slcr.mio_pin_22.write( slcr.mio_pin_22.write(
slcr::MioPin22::zeroed() slcr::MioPin22::zeroed()
.tri_enable(true)
.l0_sel(true) .l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl) .io_type(slcr::IoBufferType::Hstl)
.pullup(true) .pullup(true)
); );
// RX_CTRL // RX_CTRL
slcr.mio_pin_27.write( slcr.mio_pin_27.write(
slcr::MioPin27::zeroed() slcr::MioPin27::zeroed()
.tri_enable(true)
.l0_sel(true) .l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl) .io_type(slcr::IoBufferType::Hstl)
.pullup(true) .pullup(true)
); );
// RXD3 // RXD3
slcr.mio_pin_26.write( slcr.mio_pin_26.write(
slcr::MioPin26::zeroed() slcr::MioPin26::zeroed()
.tri_enable(true)
.l0_sel(true) .l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl) .io_type(slcr::IoBufferType::Hstl)
.pullup(true) .pullup(true)
); );
// RXD2 // RXD2
slcr.mio_pin_25.write( slcr.mio_pin_25.write(
slcr::MioPin25::zeroed() slcr::MioPin25::zeroed()
.tri_enable(true)
.l0_sel(true) .l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl) .io_type(slcr::IoBufferType::Hstl)
.pullup(true) .pullup(true)
); );
// RXD1 // RXD1
slcr.mio_pin_24.write( slcr.mio_pin_24.write(
slcr::MioPin24::zeroed() slcr::MioPin24::zeroed()
.tri_enable(true)
.l0_sel(true) .l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl) .io_type(slcr::IoBufferType::Hstl)
.pullup(true) .pullup(true)
); );
// RXD0 // RXD0
slcr.mio_pin_23.write( slcr.mio_pin_23.write(
slcr::MioPin23::zeroed() slcr::MioPin23::zeroed()
.tri_enable(true)
.l0_sel(true) .l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl) .io_type(slcr::IoBufferType::Hstl)
.pullup(true) .pullup(true)
); );
@ -182,132 +300,102 @@ impl<'r> Eth<'r, (), ()> {
} }
pub fn gem0(macaddr: [u8; 6]) -> Self { pub fn gem0(macaddr: [u8; 6]) -> Self {
Self::setup_gem0_clock(TX_1000); Self::gem_common(macaddr)
let regs = regs::RegisterBlock::gem0();
Self::from_regs(regs, macaddr)
} }
}
impl Eth<Gem1, (), ()> {
// TODO: Add a `eth1()`
pub fn gem1(macaddr: [u8; 6]) -> Self { pub fn gem1(macaddr: [u8; 6]) -> Self {
Self::setup_gem1_clock(TX_1000); Self::gem_common(macaddr)
}
}
let regs = regs::RegisterBlock::gem1();
Self::from_regs(regs, macaddr) impl<GEM: Gem> Eth<GEM, (), ()> {
fn gem_common(macaddr: [u8; 6]) -> Self {
GEM::setup_clock(TX_1000);
#[cfg(feature="target_kasli_soc")]
{
let mut eth_reset_pin = PhyRst::rst_pin();
eth_reset_pin.reset();
} }
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
let mut inner = EthInner { let mut inner = EthInner {
regs, gem: PhantomData,
link: None, link: None,
}; };
inner.init(); inner.init();
inner.configure(macaddr); inner.configure(macaddr);
let phy = Phy::find(&mut inner).expect("phy"); let phy = Phy::find(&mut inner).expect("phy");
phy.reset(&mut inner); phy.reset(&mut inner);
phy.restart_autoneg(&mut inner); phy.restart_autoneg(&mut inner);
#[cfg(feature="target_kasli_soc")]
phy.set_leds(&mut inner);
Eth { Eth {
rx: (), rx: (),
tx: (), tx: (),
inner, inner,
phy, phy,
idle: true,
} }
} }
} }
impl<'r, RX, TX> Eth<'r, RX, TX> { impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
pub fn setup_gem0_clock(tx_clock: u32) { pub fn start_rx(self, rx_size: usize) -> Eth<GEM, rx::DescList, TX> {
let io_pll = Clocks::get().io;
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem0_clk_ctrl.write(
// 0x0050_0801: 8, 5: 100 Mb/s
// ...: 8, 1: 1000 Mb/s
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(d0 as u8)
.divisor1(d1 as u8)
);
// Enable gem0 recv clock
slcr.gem0_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
pub fn setup_gem1_clock(tx_clock: u32) {
let io_pll = Clocks::get().io;
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem1_clk_ctrl.write(
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(d0 as u8)
.divisor1(d1 as u8)
);
// Enable gem1 recv clock
slcr.gem1_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
pub fn start_rx(self, rx_size: usize) -> Eth<'r, rx::DescList, TX> {
let new_self = Eth { let new_self = Eth {
rx: rx::DescList::new(rx_size), rx: rx::DescList::new(rx_size),
tx: self.tx, tx: self.tx,
inner: self.inner, inner: self.inner,
phy: self.phy, phy: self.phy,
idle: self.idle,
}; };
let list_addr = new_self.rx.list_addr(); let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0); assert!(list_addr & 0b11 == 0);
new_self.inner.regs.rx_qbar.write( GEM::regs().rx_qbar.write(
regs::RxQbar::zeroed() regs::RxQbar::zeroed()
.rx_q_baseaddr(list_addr >> 2) .rx_q_baseaddr(list_addr >> 2)
); );
new_self.inner.regs.net_ctrl.modify(|_, w| GEM::regs().net_ctrl.modify(|_, w|
w.rx_en(true) w.rx_en(true)
); );
new_self new_self
} }
pub fn start_tx(self, tx_size: usize) -> Eth<'r, RX, tx::DescList> { pub fn start_tx(self, tx_size: usize) -> Eth<GEM, RX, tx::DescList> {
let new_self = Eth { let new_self = Eth {
rx: self.rx, rx: self.rx,
tx: tx::DescList::new(tx_size), tx: tx::DescList::new(tx_size),
inner: self.inner, inner: self.inner,
phy: self.phy, phy: self.phy,
idle: self.idle,
}; };
let list_addr = &new_self.tx.list_addr(); let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0); assert!(list_addr & 0b11 == 0);
new_self.inner.regs.tx_qbar.write( GEM::regs().tx_qbar.write(
regs::TxQbar::zeroed() regs::TxQbar::zeroed()
.tx_q_baseaddr(list_addr >> 2) .tx_q_baseaddr(list_addr >> 2)
); );
new_self.inner.regs.net_ctrl.modify(|_, w| GEM::regs().net_ctrl.modify(|_, w|
w.tx_en(true) w.tx_en(true)
); );
new_self new_self
} }
} }
impl<'r, TX> Eth<'r, rx::DescList, TX> { impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> { pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> {
let status = self.inner.regs.rx_status.read(); let status = GEM::regs().rx_status.read();
if status.hresp_not_ok() { if status.hresp_not_ok() {
// Clear // Clear
self.inner.regs.rx_status.write( GEM::regs().rx_status.write(
regs::RxStatus::zeroed() regs::RxStatus::zeroed()
.hresp_not_ok(true) .hresp_not_ok(true)
); );
@ -315,7 +403,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
} }
if status.rx_overrun() { if status.rx_overrun() {
// Clear // Clear
self.inner.regs.rx_status.write( GEM::regs().rx_status.write(
regs::RxStatus::zeroed() regs::RxStatus::zeroed()
.rx_overrun(true) .rx_overrun(true)
); );
@ -323,7 +411,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
} }
if status.buffer_not_avail() { if status.buffer_not_avail() {
// Clear // Clear
self.inner.regs.rx_status.write( GEM::regs().rx_status.write(
regs::RxStatus::zeroed() regs::RxStatus::zeroed()
.buffer_not_avail(true) .buffer_not_avail(true)
); );
@ -335,28 +423,42 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
match result { match result {
Ok(None) => { Ok(None) => {
// No packet, clear status bit // No packet, clear status bit
self.inner.regs.rx_status.write( GEM::regs().rx_status.write(
regs::RxStatus::zeroed() regs::RxStatus::zeroed()
.frame_recd(true) .frame_recd(true)
); );
self.idle = true;
} }
_ => {} _ =>
self.idle = false,
} }
result result
} else { } else {
self.inner.check_link_change(&self.phy); self.idle = true;
Ok(None) Ok(None)
} }
} }
} }
impl<'r, RX> Eth<'r, RX, tx::DescList> { impl<GEM: Gem, TX> libasync::smoltcp::LinkCheck for &mut Eth<GEM, rx::DescList, TX> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> { type Link = Option<phy::Link>;
self.tx.send(self.inner.regs, length)
fn check_link_change(&mut self) -> Option<Self::Link> {
self.inner.check_link_change(&self.phy)
}
fn is_idle(&self) -> bool {
self.idle
} }
} }
impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescList> { impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
self.tx.send(GEM::regs(), length)
}
}
impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::DescList> {
type RxToken = rx::PktRef<'a>; type RxToken = rx::PktRef<'a>;
type TxToken = tx::Token<'a>; type TxToken = tx::Token<'a>;
@ -370,6 +472,7 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
let mut caps = DeviceCapabilities::default(); let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = MTU; caps.max_transmission_unit = MTU;
caps.max_burst_size = Some(self.rx.len().min(self.tx.len()));
caps.checksum = checksum_caps; caps.checksum = checksum_caps;
caps caps
@ -379,13 +482,14 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
match self.rx.recv_next() { match self.rx.recv_next() {
Ok(Some(pktref)) => { Ok(Some(pktref)) => {
let tx_token = tx::Token { let tx_token = tx::Token {
regs: self.inner.regs, regs: GEM::regs(),
desc_list: &mut self.tx, desc_list: &mut self.tx,
}; };
self.idle = false;
Some((pktref, tx_token)) Some((pktref, tx_token))
} }
Ok(None) => { Ok(None) => {
self.inner.check_link_change(&self.phy); self.idle = true;
None None
} }
Err(e) => { Err(e) => {
@ -397,33 +501,95 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
fn transmit(&'a mut self) -> Option<Self::TxToken> { fn transmit(&'a mut self) -> Option<Self::TxToken> {
Some(tx::Token { Some(tx::Token {
regs: self.inner.regs, regs: GEM::regs(),
desc_list: &mut self.tx, desc_list: &mut self.tx,
}) })
} }
}
pub struct PhyRst {
regs: regs::GpioRegisterBlock,
count_down: super::timer::global::CountDown<Milliseconds>,
}
impl PhyRst {
pub fn rst_pin() -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Hardware Reset for PHY
slcr.mio_pin_47.write(
slcr::MioPin47::zeroed()
.l3_sel(0b000)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
});
Self::eth_reset_common(0xFFFF - 0x8000)
}
fn delay_ms(&mut self, ms: u64) {
self.count_down.start(Milliseconds(ms));
nb::block!(self.count_down.wait()).unwrap();
}
fn eth_reset_common(gpio_output_mask: u16) -> Self {
let self_ = Self {
regs: regs::GpioRegisterBlock::regs(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(gpio_output_mask)
});
self_.regs.gpio_direction.modify(|_, w| {
w.phy_rst(true)
});
self_
}
fn oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.phy_rst(oe)
})
}
fn toggle(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.phy_rst(o)
})
}
pub fn reset(&mut self) {
self.toggle(false); // drive phy_rst (active LOW) pin low
self.oe(true); // enable pin's output
self.delay_ms(10);
self.toggle(true);
}
} }
struct EthInner<'r> { struct EthInner<GEM: Gem> {
regs: &'r mut regs::RegisterBlock, gem: PhantomData<GEM>,
link: Option<phy::Link>, link: Option<phy::Link>,
} }
impl<'r> EthInner<'r> { impl<GEM: Gem> EthInner<GEM> {
fn init(&mut self) { fn init(&mut self) {
// Clear the Network Control register. // Clear the Network Control register.
self.regs.net_ctrl.write(regs::NetCtrl::zeroed()); GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed());
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true)); GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
// Clear the Status registers. // Clear the Status registers.
self.regs.rx_status.write( GEM::regs().rx_status.write(
regs::RxStatus::zeroed() regs::RxStatus::zeroed()
.buffer_not_avail(true) .buffer_not_avail(true)
.frame_recd(true) .frame_recd(true)
.rx_overrun(true) .rx_overrun(true)
.hresp_not_ok(true) .hresp_not_ok(true)
); );
self.regs.tx_status.write( GEM::regs().tx_status.write(
regs::TxStatus::zeroed() regs::TxStatus::zeroed()
.used_bit_read(true) .used_bit_read(true)
.collision(true) .collision(true)
@ -437,7 +603,7 @@ impl<'r> EthInner<'r> {
.hresp_not_ok(true) .hresp_not_ok(true)
); );
// Disable all interrupts. // Disable all interrupts.
self.regs.intr_dis.write( GEM::regs().intr_dis.write(
regs::IntrDis::zeroed() regs::IntrDis::zeroed()
.mgmt_done(true) .mgmt_done(true)
.rx_complete(true) .rx_complete(true)
@ -467,29 +633,32 @@ impl<'r> EthInner<'r> {
.tsu_sec_incr(true) .tsu_sec_incr(true)
); );
// Clear the buffer queues. // Clear the buffer queues.
self.regs.rx_qbar.write( GEM::regs().rx_qbar.write(
regs::RxQbar::zeroed() regs::RxQbar::zeroed()
); );
self.regs.tx_qbar.write( GEM::regs().tx_qbar.write(
regs::TxQbar::zeroed() regs::TxQbar::zeroed()
); );
} }
fn configure(&mut self, macaddr: [u8; 6]) { fn configure(&mut self, macaddr: [u8; 6]) {
let clocks = Clocks::get(); let clocks = Clocks::get();
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1; let mut mdc_clk_div = clocks.cpu_1x() / MAX_MDC;
if clocks.cpu_1x() % MAX_MDC > 0 {
mdc_clk_div += 1;
}
self.regs.net_cfg.write( GEM::regs().net_cfg.write(
regs::NetCfg::zeroed() regs::NetCfg::zeroed()
.full_duplex(true) .full_duplex(true)
.gige_en(true) .gige_en(true)
.speed(true) .speed(true)
.no_broadcast(false) .no_broadcast(false)
.multi_hash_en(true) .multi_hash_en(true)
// Promiscuous mode (TODO?) .rx_1536_byte_frames(true)
.copy_all(true)
// Remove 4-byte Frame CheckSum // Remove 4-byte Frame CheckSum
.fcs_remove(true) .fcs_remove(true)
.dis_cp_pause_frame(true)
// RX checksum offload // RX checksum offload
.rx_chksum_offld_en(true) .rx_chksum_offld_en(true)
// One of the slower speeds // One of the slower speeds
@ -497,24 +666,25 @@ impl<'r> EthInner<'r> {
); );
let macaddr_msbs = let macaddr_msbs =
(u16::from(macaddr[0]) << 8) | (u16::from(macaddr[5]) << 8) |
u16::from(macaddr[1]); u16::from(macaddr[4]);
let macaddr_lsbs = let macaddr_lsbs =
(u32::from(macaddr[2]) << 24) | (u32::from(macaddr[3]) << 24) |
(u32::from(macaddr[3]) << 16) | (u32::from(macaddr[2]) << 16) |
(u32::from(macaddr[4]) << 8) | (u32::from(macaddr[1]) << 8) |
u32::from(macaddr[5]); u32::from(macaddr[0]);
self.regs.spec_addr1_top.write( // writing to bot would disable the specific address
regs::SpecAddrTop::zeroed() GEM::regs().spec_addr1_bot.write(
.addr_msbs(macaddr_msbs)
);
self.regs.spec_addr1_bot.write(
regs::SpecAddrBot::zeroed() regs::SpecAddrBot::zeroed()
.addr_lsbs(macaddr_lsbs) .addr_lsbs(macaddr_lsbs)
); );
// writing to top would enable it again
GEM::regs().spec_addr1_top.write(
regs::SpecAddrTop::zeroed()
.addr_msbs(macaddr_msbs)
);
GEM::regs().dma_cfg.write(
self.regs.dma_cfg.write(
regs::DmaCfg::zeroed() regs::DmaCfg::zeroed()
// 1536 bytes // 1536 bytes
.ahb_mem_rx_buf_size((MTU >> 6) as u8) .ahb_mem_rx_buf_size((MTU >> 6) as u8)
@ -530,7 +700,7 @@ impl<'r> EthInner<'r> {
.ahb_fixed_burst_len(0x10) .ahb_fixed_burst_len(0x10)
); );
self.regs.net_ctrl.write( GEM::regs().net_ctrl.write(
regs::NetCtrl::zeroed() regs::NetCtrl::zeroed()
.mgmt_port_en(true) .mgmt_port_en(true)
); );
@ -538,17 +708,11 @@ impl<'r> EthInner<'r> {
fn wait_phy_idle(&self) { fn wait_phy_idle(&self) {
while !self.regs.net_status.read().phy_mgmt_idle() {} while !GEM::regs().net_status.read().phy_mgmt_idle() {}
} }
fn check_link_change(&mut self, phy: &Phy) { fn check_link_change(&mut self, phy: &Phy) -> Option<Option<phy::Link>> {
// As the PHY access takes some time, exit early if there was
// already a link. TODO: check once per second.
if self.link.is_some() {
return
}
let link = phy.get_link(self); let link = phy.get_link(self);
// Check link state transition // Check link state transition
@ -557,17 +721,15 @@ impl<'r> EthInner<'r> {
Some(link) => { Some(link) => {
info!("eth: got {:?}", link); info!("eth: got {:?}", link);
use phy::LinkSpeed::*; use phy::{LinkDuplex::Full, LinkSpeed::*};
let txclock = match link.speed { let txclock = match link.speed {
S10 => TX_10, S10 => TX_10,
S100 => TX_100, S100 => TX_100,
S1000 => TX_1000, S1000 => TX_1000,
}; };
Eth::<(), ()>::setup_gem0_clock(txclock); GEM::setup_clock(txclock);
/* .full_duplex(false) doesn't work even if GEM::regs().net_cfg.modify(|_, w| w
half duplex has been negotiated. */ .full_duplex(link.duplex == Full)
self.regs.net_cfg.modify(|_, w| w
.full_duplex(true)
.gige_en(link.speed == S1000) .gige_en(link.speed == S1000)
.speed(link.speed != S10) .speed(link.speed != S10)
); );
@ -582,14 +744,17 @@ impl<'r> EthInner<'r> {
} }
self.link = link; self.link = link;
Some(link)
} else {
None
} }
} }
} }
impl<'r> PhyAccess for EthInner<'r> { impl<GEM: Gem> PhyAccess for EthInner<GEM> {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 { fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
self.wait_phy_idle(); self.wait_phy_idle();
self.regs.phy_maint.write( GEM::regs().phy_maint.write(
regs::PhyMaint::zeroed() regs::PhyMaint::zeroed()
.clause_22(true) .clause_22(true)
.operation(regs::PhyOperation::Read) .operation(regs::PhyOperation::Read)
@ -598,12 +763,12 @@ impl<'r> PhyAccess for EthInner<'r> {
.must_10(0b10) .must_10(0b10)
); );
self.wait_phy_idle(); self.wait_phy_idle();
self.regs.phy_maint.read().data() GEM::regs().phy_maint.read().data()
} }
fn write_phy(&mut self, addr: u8, reg: u8, data: u16) { fn write_phy(&mut self, addr: u8, reg: u8, data: u16) {
self.wait_phy_idle(); self.wait_phy_idle();
self.regs.phy_maint.write( GEM::regs().phy_maint.write(
regs::PhyMaint::zeroed() regs::PhyMaint::zeroed()
.clause_22(true) .clause_22(true)
.operation(regs::PhyOperation::Write) .operation(regs::PhyOperation::Write)

View File

@ -82,6 +82,10 @@ impl PhyRegister for Control {
fn addr() -> u8 { fn addr() -> u8 {
0 0
} }
fn page() -> u8 {
0
}
} }
impl From<u16> for Control { impl From<u16> for Control {

View File

@ -1,59 +0,0 @@
use bit_field::BitField;
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
#[derive(Clone, Copy, Debug)]
/// 1000Base-T Extended Status Register
pub struct ExtendedStatus(pub u16);
impl ExtendedStatus {
pub fn cap_1000base_t_half(&self) -> bool {
self.0.get_bit(12)
}
pub fn cap_1000base_t_full(&self) -> bool {
self.0.get_bit(13)
}
pub fn cap_1000base_x_half(&self) -> bool {
self.0.get_bit(14)
}
pub fn cap_1000base_x_full(&self) -> bool {
self.0.get_bit(12)
}
pub fn get_link(&self) -> Option<Link> {
if self.cap_1000base_t_half() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Half,
})
} else if self.cap_1000base_t_full() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Full,
})
} else if self.cap_1000base_x_half() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Half,
})
} else if self.cap_1000base_x_full() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Full,
})
} else {
None
}
}
}
impl PhyRegister for ExtendedStatus {
fn addr() -> u8 {
0xF
}
}
impl From<u16> for ExtendedStatus {
fn from(value: u16) -> Self {
ExtendedStatus(value)
}
}

View File

@ -11,6 +11,9 @@ pub struct PhyIdentifier {
} }
pub fn identify_phy<PA: PhyAccess>(pa: &mut PA, addr: u8) -> Option<PhyIdentifier> { pub fn identify_phy<PA: PhyAccess>(pa: &mut PA, addr: u8) -> Option<PhyIdentifier> {
#[cfg(feature = "target_kasli_soc")]
pa.write_phy(addr, 0x16, 0); //reset page
let id1 = pa.read_phy(addr, 2); let id1 = pa.read_phy(addr, 2);
let id2 = pa.read_phy(addr, 3); let id2 = pa.read_phy(addr, 3);
if id1 != 0xFFFF || id2 != 0xFFFF { if id1 != 0xFFFF || id2 != 0xFFFF {

View File

@ -0,0 +1,79 @@
use bit_field::BitField;
use super::{PhyRegister, Led0Control, Led1Control};
#[derive(Clone, Copy, Debug)]
/// LED Control Register
pub struct Leds(pub u16);
impl Leds {
pub fn led0(&self) -> Led0Control {
match self.0.get_bits(0..=3) {
0b0000 => Led0Control::OnLinkOffNoLink,
0b0001 => Led0Control::OnLinkBlinkActivityOffNoLink,
0b0010 => Led0Control::BlinkDependingOnLink,
0b0011 => Led0Control::OnActivityOffNoActivity,
0b0100 => Led0Control::BlinkActivityOffNoActivity,
0b0101 => Led0Control::OnTransmitOffNoTransmit,
0b0110 => Led0Control::OnCopperLinkOffElse,
0b0111 => Led0Control::On1000LinkOffElse,
0b1000 => Led0Control::ForceOff,
0b1001 => Led0Control::ForceOn,
0b1010 => Led0Control::ForceHiZ,
0b1011 => Led0Control::ForceBlink,
0b1100 => Led0Control::Mode1,
0b1101 => Led0Control::Mode2,
0b1110 => Led0Control::Mode3,
0b1111 => Led0Control::Mode4,
_ => unreachable!()
}
}
pub fn led1(&self) -> Led1Control {
match self.0.get_bits(4..=7) {
0b0000 => Led1Control::OnReceiveOffNoReceive,
0b0001 => Led1Control::OnLinkBlinkActivityOffNoLink,
0b0010 => Led1Control::OnLinkBlinkReceiveOffNoLink,
0b0011 => Led1Control::OnActivityOffNoActivity,
0b0100 => Led1Control::BlinkActivityOffNoActivity,
0b0101 => Led1Control::On100OrFiberOffElse,
0b0110 => Led1Control::On1001000LinkOffElse,
0b0111 => Led1Control::On100LinkOffElse,
0b1000 => Led1Control::ForceOff,
0b1001 => Led1Control::ForceOn,
0b1010 => Led1Control::ForceHiZ,
0b1011 => Led1Control::ForceBlink,
_ => unreachable!()
}
}
pub fn set_led0(mut self, setting: Led0Control) -> Self {
self.0.set_bits(0..=3, setting as u16);
self
}
pub fn set_led1(mut self, setting: Led1Control) -> Self {
self.0.set_bits(4..=7, setting as u16);
self
}
}
impl PhyRegister for Leds {
fn addr() -> u8 {
0x10
}
fn page() -> u8 {
3
}
}
impl From<u16> for Leds {
fn from(value: u16) -> Self {
Leds(value)
}
}
impl Into<u16> for Leds {
fn into(self) -> u16 {
self.0
}
}

View File

@ -2,12 +2,14 @@ pub mod id;
use id::{identify_phy, PhyIdentifier}; use id::{identify_phy, PhyIdentifier};
mod status; mod status;
pub use status::Status; pub use status::Status;
mod extended_status;
pub use extended_status::ExtendedStatus;
mod control; mod control;
pub use control::Control; pub use control::Control;
mod pssr;
pub use pssr::PSSR;
mod leds;
pub use leds::Leds;
#[derive(Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Link { pub struct Link {
pub speed: LinkSpeed, pub speed: LinkSpeed,
pub duplex: LinkDuplex, pub duplex: LinkDuplex,
@ -26,58 +28,105 @@ pub enum LinkDuplex {
Full, Full,
} }
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Led0Control {
OnLinkOffNoLink = 0b0000,
OnLinkBlinkActivityOffNoLink = 0b0001,
BlinkDependingOnLink = 0b0010,
OnActivityOffNoActivity = 0b0011,
BlinkActivityOffNoActivity = 0b0100,
OnTransmitOffNoTransmit = 0b0101,
OnCopperLinkOffElse = 0b0110,
On1000LinkOffElse = 0b0111,
ForceOff = 0b1000,
ForceOn = 0b1001,
ForceHiZ = 0b1010,
ForceBlink = 0b1011,
Mode1 = 0b1100,
Mode2 = 0b1101,
Mode3 = 0b1110,
Mode4 = 0b1111
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Led1Control {
OnReceiveOffNoReceive = 0b0000,
OnLinkBlinkActivityOffNoLink = 0b0001,
OnLinkBlinkReceiveOffNoLink = 0b0010,
OnActivityOffNoActivity = 0b0011,
BlinkActivityOffNoActivity = 0b0100,
On100OrFiberOffElse = 0b0101,
On1001000LinkOffElse = 0b0110,
On100LinkOffElse = 0b0111,
ForceOff = 0b1000,
ForceOn = 0b1001,
ForceHiZ = 0b1010,
ForceBlink = 0b1011,
}
pub trait PhyAccess { pub trait PhyAccess {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16; fn read_phy(&mut self, addr: u8, reg: u8) -> u16;
fn write_phy(&mut self, addr: u8, reg: u8, data: u16); fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
} }
pub trait PhyRegister {
fn addr() -> u8;
fn page() -> u8;
}
#[derive(Clone)] #[derive(Clone)]
pub struct Phy { pub struct Phy {
pub addr: u8, pub addr: u8,
device: PhyDevice,
} }
#[derive(Clone, Copy)] const OUI_MARVELL: u32 = 0x005043;
pub enum PhyDevice {
Marvel88E1116R,
Rtl8211E,
}
const OUI_MARVEL: u32 = 0x005043;
const OUI_REALTEK: u32 = 0x000732; const OUI_REALTEK: u32 = 0x000732;
const OUI_LANTIQ : u32 = 0x355969;
const OUI_ICPLUS : u32 = 0x0090c3;
//only change pages on Kasli-SoC's Marvel 88E11xx
#[cfg(feature="target_kasli_soc")]
const PAGE_REGISTER: u8 = 0x16;
impl Phy { impl Phy {
/// Probe all addresses on MDIO for a known PHY /// Probe all addresses on MDIO for a known PHY
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> { pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
for addr in 1..32 { (0..32).find(|addr| {
let device = match identify_phy(pa, addr) { match identify_phy(pa, *addr) {
Some(PhyIdentifier { Some(PhyIdentifier {
oui: OUI_MARVEL, oui: OUI_MARVELL,
// Marvell 88E1116R
model: 36, model: 36,
.. ..
}) => Some(PhyDevice::Marvel88E1116R), }) => true,
Some(PhyIdentifier {
oui: OUI_MARVELL,
// Marvell 88E1512
model: 29,
..
}) => true,
Some(PhyIdentifier { Some(PhyIdentifier {
oui: OUI_REALTEK, oui: OUI_REALTEK,
// RTL 8211E
model: 0b010001, model: 0b010001,
rev: 0b0101, rev: 0b0101,
}) => Some(PhyDevice::Rtl8211E), }) => true,
_ => None, Some(PhyIdentifier {
}; oui: OUI_LANTIQ,
match device { // Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6
Some(device) => model: 0,
return Some(Phy { addr, device }), ..
None => {} }) => true,
} Some(PhyIdentifier {
} oui: OUI_ICPLUS,
// IP101G-DS-R01
None model: 5,
} rev: 4,
}) => true,
pub fn name(&self) -> &'static str { _ => false,
match self.device {
PhyDevice::Marvel88E1116R => &"Marvel 88E1116R",
PhyDevice::Rtl8211E => &"RTL8211E",
} }
}).map(|addr| Phy { addr })
} }
pub fn read_reg<PA, PR>(&self, pa: &mut PA) -> PR pub fn read_reg<PA, PR>(&self, pa: &mut PA) -> PR
@ -85,6 +134,9 @@ impl Phy {
PA: PhyAccess, PA: PhyAccess,
PR: PhyRegister + From<u16>, PR: PhyRegister + From<u16>,
{ {
#[cfg(feature="target_kasli_soc")]
pa.write_phy(self.addr, PAGE_REGISTER, PR::page().into());
pa.read_phy(self.addr, PR::addr()).into() pa.read_phy(self.addr, PR::addr()).into()
} }
@ -94,6 +146,9 @@ impl Phy {
PR: PhyRegister + From<u16> + Into<u16>, PR: PhyRegister + From<u16> + Into<u16>,
F: FnMut(PR) -> PR, F: FnMut(PR) -> PR,
{ {
#[cfg(feature="target_kasli_soc")]
pa.write_phy(self.addr, PAGE_REGISTER, PR::page().into());
let reg = pa.read_phy(self.addr, PR::addr()).into(); let reg = pa.read_phy(self.addr, PR::addr()).into();
let reg = f(reg); let reg = f(reg);
pa.write_phy(self.addr, PR::addr(), reg.into()) pa.write_phy(self.addr, PR::addr(), reg.into())
@ -107,6 +162,14 @@ impl Phy {
self.modify_reg(pa, f) self.modify_reg(pa, f)
} }
pub fn modify_leds<PA, F>(&self, pa: &mut PA, f: F)
where
PA: PhyAccess,
F: FnMut(Leds) -> Leds,
{
self.modify_reg(pa, f)
}
pub fn get_control<PA: PhyAccess>(&self, pa: &mut PA) -> Control { pub fn get_control<PA: PhyAccess>(&self, pa: &mut PA) -> Control {
self.read_reg(pa) self.read_reg(pa)
} }
@ -120,12 +183,8 @@ impl Phy {
if !status.link_status() { if !status.link_status() {
None None
} else if status.cap_1000base_t_extended_status() { } else if status.cap_1000base_t_extended_status() {
let ext_status: ExtendedStatus = self.read_reg(pa); let phy_status: PSSR = self.read_reg(pa);
if let Some(link) = ext_status.get_link() { phy_status.get_link()
Some(link)
} else {
status.get_link()
}
} else { } else {
status.get_link() status.get_link()
} }
@ -144,8 +203,12 @@ impl Phy {
.set_restart_autoneg(true) .set_restart_autoneg(true)
); );
} }
}
pub trait PhyRegister { #[cfg(feature="target_kasli_soc")]
fn addr() -> u8; pub fn set_leds<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_leds(pa, |leds|
leds.set_led0(Led0Control::OnCopperLinkOffElse)
.set_led1(Led1Control::BlinkActivityOffNoActivity)
);
}
} }

View File

@ -0,0 +1,56 @@
use bit_field::BitField;
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
#[derive(Clone, Copy, Debug)]
/// PHY-Specific Status Register
pub struct PSSR(pub u16);
impl PSSR {
pub fn link(&self) -> bool {
self.0.get_bit(10)
}
pub fn duplex(&self) -> LinkDuplex {
if self.0.get_bit(13) {
LinkDuplex::Full
} else {
LinkDuplex::Half
}
}
pub fn speed(&self) -> Option<LinkSpeed> {
match self.0.get_bits(14..=15) {
0b00 => Some(LinkSpeed::S10),
0b01 => Some(LinkSpeed::S100),
0b10 => Some(LinkSpeed::S1000),
_ => None,
}
}
pub fn get_link(&self) -> Option<Link> {
if self.link() {
Some(Link {
speed: self.speed()?,
duplex: self.duplex(),
})
} else {
None
}
}
}
impl PhyRegister for PSSR {
fn addr() -> u8 {
0x11
}
fn page() -> u8 {
0
}
}
impl From<u16> for PSSR {
fn from(value: u16) -> Self {
PSSR(value)
}
}

View File

@ -55,12 +55,22 @@ impl Status {
pub fn get_link(&self) -> Option<Link> { pub fn get_link(&self) -> Option<Link> {
if ! self.link_status() { if ! self.link_status() {
None None
} else if self.cap_10base_t_half() { } else if self.cap_100base_tx_full() {
Some(Link { Some(Link {
speed: LinkSpeed::S10, speed: LinkSpeed::S100,
duplex: LinkDuplex::Full,
})
} else if self.cap_100base_tx_half() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Half, duplex: LinkDuplex::Half,
}) })
} else if self.cap_10base_t_full() { } else if self.cap_100base_t4() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Half,
})
} else if self.cap_10base_t2_full() {
Some(Link { Some(Link {
speed: LinkSpeed::S10, speed: LinkSpeed::S10,
duplex: LinkDuplex::Full, duplex: LinkDuplex::Full,
@ -70,26 +80,16 @@ impl Status {
speed: LinkSpeed::S10, speed: LinkSpeed::S10,
duplex: LinkDuplex::Half, duplex: LinkDuplex::Half,
}) })
} else if self.cap_10base_t2_full() { } else if self.cap_10base_t_full() {
Some(Link { Some(Link {
speed: LinkSpeed::S10, speed: LinkSpeed::S10,
duplex: LinkDuplex::Full, duplex: LinkDuplex::Full,
}) })
} else if self.cap_100base_t4() { } else if self.cap_10base_t_half() {
Some(Link { Some(Link {
speed: LinkSpeed::S100, speed: LinkSpeed::S10,
duplex: LinkDuplex::Half, duplex: LinkDuplex::Half,
}) })
} else if self.cap_100base_tx_half() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Half,
})
} else if self.cap_100base_tx_full() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Full,
})
} else { } else {
None None
} }
@ -100,6 +100,10 @@ impl PhyRegister for Status {
fn addr() -> u8 { fn addr() -> u8 {
1 1
} }
fn page() -> u8 {
0
}
} }
impl From<u16> for Status { impl From<u16> for Status {

View File

@ -1,6 +1,6 @@
use volatile_register::{RO, WO, RW}; use volatile_register::{RO, WO, RW};
use libregister::{register, register_bit, register_bits, register_bits_typed}; use libregister::{register, register_at, register_bit, register_bits, register_bits_typed};
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
@ -110,19 +110,52 @@ pub struct RegisterBlock {
pub design_cfg5: RO<u32>, pub design_cfg5: RO<u32>,
} }
impl RegisterBlock { pub struct GpioRegisterBlock {
const GEM0: *mut Self = 0xE000B000 as *mut _; pub gpio_output_mask: &'static mut OutputMask,
const GEM1: *mut Self = 0xE000C000 as *mut _; pub gpio_direction: &'static mut Direction,
pub gpio_output_enable: &'static mut OutputEnable,
}
pub fn gem0() -> &'static mut Self { impl GpioRegisterBlock {
unsafe { &mut *Self::GEM0 } pub fn regs() -> Self {
Self {
gpio_output_mask: OutputMask::new(),
gpio_direction: Direction::new(),
gpio_output_enable: OutputEnable::new(),
} }
pub fn gem1() -> &'static mut Self {
unsafe { &mut *Self::GEM1 }
} }
} }
register!(gpio_output_mask,
/// MASK_DATA_1_SW:
/// Maskable output data for MIO[47:32]
OutputMask, RW, u32);
register_at!(OutputMask, 0xE000A008, new);
register_bit!(gpio_output_mask,
/// Output for PHY_RST (MIO[47])
phy_rst, 15);
register_bits!(gpio_output_mask,
mask, u16, 16, 31);
register!(gpio_direction,
/// DIRM_1:
/// Direction mode for MIO[53:32]; 0/1 = in/out
Direction, RW, u32);
register_at!(Direction, 0xE000A244, new);
register_bit!(gpio_direction,
/// Direction for PHY_RST
phy_rst, 15);
register!(gpio_output_enable,
/// OEN_1:
/// Output enable for MIO[53:32]
OutputEnable, RW, u32);
register_at!(OutputEnable, 0xE000A248, new);
register_bit!(gpio_output_enable,
/// Output enable for PHY_RST
phy_rst, 15);
register_at!(RegisterBlock, 0xE000B000, gem0);
register_at!(RegisterBlock, 0xE000C000, gem1);
register!(net_ctrl, NetCtrl, RW, u32); register!(net_ctrl, NetCtrl, RW, u32);
register_bit!(net_ctrl, loopback_local, 1); register_bit!(net_ctrl, loopback_local, 1);
register_bit!(net_ctrl, rx_en, 2); register_bit!(net_ctrl, rx_en, 2);

View File

@ -93,6 +93,10 @@ impl DescList {
} }
} }
pub fn len(&self) -> usize {
self.list.len().min(self.buffers.len())
}
pub fn list_addr(&self) -> u32 { pub fn list_addr(&self) -> u32 {
&self.list[0] as *const _ as u32 &self.list[0] as *const _ as u32
} }
@ -104,6 +108,20 @@ impl DescList {
if entry.word0.read().used() { if entry.word0.read().used() {
let word1 = entry.word1.read(); let word1 = entry.word1.read();
let len = word1.frame_length_lsbs().into(); let len = word1.frame_length_lsbs().into();
let padding = {
let diff = len % 0x20;
if diff == 0 {
0
} else {
0x20 - diff
}
};
unsafe {
// invalidate the buffer
// we cannot do it in the drop function, as L2 cache data prefetch would prefetch
// the data, and there is no way for us to prevent that unless changing MMU table.
dci_slice(&mut self.buffers[self.next][0..len + padding]);
}
let buffer = &mut self.buffers[self.next][0..len]; let buffer = &mut self.buffers[self.next][0..len];
self.next += 1; self.next += 1;
@ -131,10 +149,6 @@ pub struct PktRef<'a> {
impl<'a> Drop for PktRef<'a> { impl<'a> Drop for PktRef<'a> {
fn drop(&mut self) { fn drop(&mut self) {
// Flush buffer from cache, to be filled by the peripheral
// before next read
dcci_slice(self.buffer);
self.entry.word0.modify(|_, w| w.used(false)); self.entry.word0.modify(|_, w| w.used(false));
dmb(); dmb();
} }

View File

@ -85,6 +85,10 @@ impl DescList {
} }
} }
pub fn len(&self) -> usize {
self.list.len().min(self.buffers.len())
}
pub fn list_addr(&self) -> u32 { pub fn list_addr(&self) -> u32 {
&self.list[0] as *const _ as u32 &self.list[0] as *const _ as u32
} }
@ -128,11 +132,9 @@ impl<'a> Drop for PktRef<'a> {
dcc_slice(self.buffer); dcc_slice(self.buffer);
self.entry.word1.modify(|_, w| w.used(false)); self.entry.word1.modify(|_, w| w.used(false));
if ! self.regs.tx_status.read().tx_go() { // Start the TX engine
// Start TX if not already running
self.regs.net_ctrl.modify(|_, w| w.start_tx(true)); self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
} }
}
} }
impl<'a> Deref for PktRef<'a> { impl<'a> Deref for PktRef<'a> {
@ -162,10 +164,7 @@ impl<'a> smoltcp::phy::TxToken for Token<'a> {
None => None =>
Err(smoltcp::Error::Exhausted), Err(smoltcp::Error::Exhausted),
Some(mut pktref) => { Some(mut pktref) => {
let result = f(pktref.deref_mut()); f(pktref.deref_mut())
// TODO: on result.is_err() don;t send
drop(pktref);
result
} }
} }
} }

View File

@ -1,41 +0,0 @@
pub trait BytesTransferExt: Sized {
// Turn u32 into u8
fn bytes_transfer(self) -> BytesTransfer<Self>
where
Self: Iterator<Item = u32>;
}
impl<I: Iterator<Item = u32>> BytesTransferExt for I {
// Turn u32 into u8
fn bytes_transfer(self) -> BytesTransfer<Self> {
BytesTransfer {
iter: self,
shift: 0,
word: 0,
}
}
}
pub struct BytesTransfer<I: Iterator<Item = u32> + Sized> {
iter: I,
shift: u8,
word: u32,
}
impl<I: Iterator<Item = u32> + Sized> Iterator for BytesTransfer<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
if self.shift > 0 {
self.shift -= 8;
Some((self.word >> self.shift) as u8)
} else {
self.iter.next()
.and_then(|word| {
self.shift = 32;
self.word = word;
self.next()
})
}
}
}

View File

@ -1,503 +0,0 @@
//! Quad-SPI Flash Controller
use core::marker::PhantomData;
use log::{error, info, warn};
use libregister::{RegisterR, RegisterW, RegisterRW};
use crate::{print, println};
use super::slcr;
use super::clocks::source::{IoPll, ClockSource};
mod regs;
mod bytes;
pub use bytes::{BytesTransferExt, BytesTransfer};
mod spi_flash_register;
use spi_flash_register::*;
mod transfer;
use transfer::Transfer;
const FLASH_BAUD_RATE: u32 = 50_000_000;
/// 16 MB
pub const SINGLE_CAPACITY: u32 = 0x1000000;
pub const SECTOR_SIZE: u32 = 0x10000;
pub const PAGE_SIZE: u32 = 0x100;
/// Instruction: Read Identification
const INST_RDID: u8 = 0x9F;
/// Instruction: Read
const INST_READ: u8 = 0x03;
/// Instruction: Quad I/O Fast Read
const INST_4IO_FAST_READ: u8 = 0xEB;
/// Instruction: Write Disable
const INST_WRDI: u8 = 0x04;
/// Instruction: Write Enable
const INST_WREN: u8 = 0x06;
/// Instruction: Program page
const INST_PP: u8 = 0x02;
/// Instruction: Erase 4K Block
const INST_BE_4K: u8 = 0x20;
#[derive(Clone)]
pub enum SpiWord {
W8(u8),
W16(u16),
W24(u32),
W32(u32),
}
impl From<u8> for SpiWord {
fn from(x: u8) -> Self {
SpiWord::W8(x)
}
}
impl From<u16> for SpiWord {
fn from(x: u16) -> Self {
SpiWord::W16(x)
}
}
impl From<u32> for SpiWord {
fn from(x: u32) -> Self {
SpiWord::W32(x)
}
}
/// Memory-mapped mode
pub struct LinearAddressing;
/// Manual I/O mode
pub struct Manual;
/// Flash Interface Driver
///
/// For 2x Spansion S25FL128SAGMFIR01
pub struct Flash<MODE> {
regs: &'static mut regs::RegisterBlock,
_mode: PhantomData<MODE>,
}
impl<MODE> Flash<MODE> {
fn transition<TO>(self) -> Flash<TO> {
Flash {
regs: self.regs,
_mode: PhantomData,
}
}
fn disable_interrupts(&mut self) {
self.regs.intr_dis.write(
regs::IntrDis::zeroed()
.rx_overflow(true)
.tx_fifo_not_full(true)
.tx_fifo_full(true)
.rx_fifo_not_empty(true)
.rx_fifo_full(true)
.tx_fifo_underflow(true)
);
}
fn clear_rx_fifo(&self) {
while self.regs.intr_status.read().rx_fifo_not_empty() {
let _ = self.regs.rx_data.read();
}
}
fn clear_interrupt_status(&mut self) {
self.regs.intr_status.write(
regs::IntrStatus::zeroed()
.rx_overflow(true)
.tx_fifo_underflow(true)
);
}
fn wait_tx_fifo_flush(&mut self) {
self.regs.config.modify(|_, w| w.man_start_com(true));
while !self.regs.intr_status.read().tx_fifo_not_full() {}
}
}
impl Flash<()> {
pub fn new(clock: u32) -> Self {
Self::enable_clocks(clock);
Self::setup_signals();
Self::reset();
let regs = regs::RegisterBlock::qspi();
let mut flash = Flash { regs, _mode: PhantomData };
flash.configure((FLASH_BAUD_RATE - 1 + clock) / FLASH_BAUD_RATE);
flash
}
/// typical: `200_000_000` Hz
fn enable_clocks(clock: u32) {
let io_pll = IoPll::freq();
let divisor = ((clock - 1 + io_pll) / clock)
.max(1).min(63) as u8;
slcr::RegisterBlock::unlocked(|slcr| {
slcr.lqspi_clk_ctrl.write(
slcr::LqspiClkCtrl::zeroed()
.src_sel(slcr::PllSource::IoPll)
.divisor(divisor)
.clkact(true)
);
});
}
fn setup_signals() {
slcr::RegisterBlock::unlocked(|slcr| {
// 1. Configure MIO pin 1 for chip select 0 output.
slcr.mio_pin_01.write(
slcr::MioPin01::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// Configure MIO pins 2 through 5 for I/O.
slcr.mio_pin_02.write(
slcr::MioPin02::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
slcr.mio_pin_03.write(
slcr::MioPin03::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
slcr.mio_pin_04.write(
slcr::MioPin04::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
slcr.mio_pin_05.write(
slcr::MioPin05::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
// 3. Configure MIO pin 6 for serial clock 0 output.
slcr.mio_pin_06.write(
slcr::MioPin06::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
// Option: Add Second Device Chip Select
// 4. Configure MIO pin 0 for chip select 1 output.
slcr.mio_pin_00.write(
slcr::MioPin00::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
// Option: Add Second Serial Clock
// 5. Configure MIO pin 9 for serial clock 1 output.
slcr.mio_pin_09.write(
slcr::MioPin09::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// Option: Add 4-bit Data
// 6. Configure MIO pins 10 through 13 for I/O.
slcr.mio_pin_10.write(
slcr::MioPin10::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
slcr.mio_pin_11.write(
slcr::MioPin11::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
slcr.mio_pin_12.write(
slcr::MioPin12::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
slcr.mio_pin_13.write(
slcr::MioPin13::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// Option: Add Feedback Output Clock
// 7. Configure MIO pin 8 for feedback clock.
slcr.mio_pin_08.write(
slcr::MioPin08::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
});
}
fn reset() {
slcr::RegisterBlock::unlocked(|slcr| {
slcr.lqspi_rst_ctrl.write(
slcr::LqspiRstCtrl::zeroed()
.ref_rst(true)
.cpu1x_rst(true)
);
slcr.lqspi_rst_ctrl.write(
slcr::LqspiRstCtrl::zeroed()
);
});
}
fn configure(&mut self, divider: u32) {
// Disable
self.regs.enable.write(
regs::Enable::zeroed()
);
self.disable_interrupts();
self.regs.lqspi_cfg.write(
regs::LqspiCfg::zeroed()
);
self.clear_rx_fifo();
self.clear_interrupt_status();
// for a baud_rate_div=1 LPBK_DLY_ADJ would be required
let mut baud_rate_div = 2u32;
while baud_rate_div < 7 && 2u32.pow(1 + baud_rate_div) < divider {
baud_rate_div += 1;
}
self.regs.config.write(regs::Config::zeroed()
.baud_rate_div(baud_rate_div as u8)
.mode_sel(true)
.leg_flsh(true)
.holdb_dr(true)
// 32 bits TX FIFO width
.fifo_width(0b11)
);
// Initialize RX/TX pipes thresholds
unsafe {
self.regs.rx_thres.write(1);
self.regs.tx_thres.write(1);
}
}
pub fn linear_addressing_mode(self) -> Flash<LinearAddressing> {
// Set manual start enable to auto mode.
// Assert the chip select.
self.regs.config.modify(|_, w| w
.man_start_en(false)
.pcs(false)
.manual_cs(false)
);
self.regs.lqspi_cfg.write(regs::LqspiCfg::zeroed()
// Quad I/O Fast Read
.inst_code(INST_4IO_FAST_READ)
.dummy_mask(0x2)
.mode_en(false)
.mode_bits(0xFF)
// 2 devices
.two_mem(true)
.u_page(false)
// Quad SPI mode
.lq_mode(true)
);
self.regs.enable.write(
regs::Enable::zeroed()
.spi_en(true)
);
self.transition()
}
pub fn manual_mode(self, chip_index: usize) -> Flash<Manual> {
self.regs.config.modify(|_, w| w
.man_start_en(true)
.manual_cs(true)
.endian(true)
);
self.regs.lqspi_cfg.write(regs::LqspiCfg::zeroed()
// Quad I/O Fast Read
.inst_code(INST_READ)
.dummy_mask(0x2)
.mode_en(false)
.mode_bits(0xFF)
// 2 devices
.two_mem(true)
.u_page(chip_index != 0)
// Quad SPI mode
.lq_mode(false)
);
self.transition()
}
}
impl Flash<LinearAddressing> {
/// Stop linear addressing mode
pub fn stop(self) -> Flash<()> {
self.regs.enable.modify(|_, w| w.spi_en(false));
// De-assert chip select.
self.regs.config.modify(|_, w| w.pcs(true));
self.transition()
}
pub fn ptr<T>(&mut self) -> *mut T {
0xFC00_0000 as *mut _
}
pub fn size(&self) -> usize {
2 * (SINGLE_CAPACITY as usize)
}
}
impl Flash<Manual> {
pub fn stop(self) -> Flash<()> {
self.transition()
}
pub fn read_reg<R: SpiFlashRegister>(&mut self) -> R {
let args = Some(R::inst_code());
let transfer = self.transfer(args.into_iter(), 2)
.bytes_transfer();
R::new(transfer.skip(1).next().unwrap())
}
pub fn read_reg_until<R, F, A>(&mut self, f: F) -> A
where
R: SpiFlashRegister,
F: Fn(R) -> Option<A>,
{
let mut result = None;
while result.is_none() {
let args = Some(R::inst_code());
for b in self.transfer(args.into_iter(), 32)
.bytes_transfer().skip(1) {
result = f(R::new(b));
if result.is_none() {
break;
}
}
}
result.unwrap()
}
/// Status Register-1 remains `0x00` immediately after invoking a command.
fn wait_while_sr1_zeroed(&mut self) -> SR1 {
self.read_reg_until::<SR1, _, SR1>(|sr1|
if sr1.is_zeroed() {
None
} else {
Some(sr1)
}
)
}
/// Read Identification
pub fn rdid(&mut self) -> core::iter::Skip<BytesTransfer<Transfer<core::option::IntoIter<u32>, u32>>> {
let args = Some((INST_RDID as u32) << 24);
self.transfer(args.into_iter(), 0x44)
.bytes_transfer().skip(1)
}
/// Read flash data
pub fn read(&mut self, offset: u32, len: usize
) -> core::iter::Take<core::iter::Skip<BytesTransfer<Transfer<core::option::IntoIter<u32>, u32>>>>
{
let args = Some(((INST_READ as u32) << 24) | (offset as u32));
self.transfer(args.into_iter(), len + 6)
.bytes_transfer().skip(6).take(len)
}
pub fn erase(&mut self, offset: u32) {
let args = Some(((INST_BE_4K as u32) << 24) | (offset as u32));
self.transfer(args.into_iter(), 4);
let sr1 = self.wait_while_sr1_zeroed();
if sr1.e_err() {
error!("E_ERR");
} else if sr1.p_err() {
error!("P_ERR");
} else if sr1.wip() {
info!("Erase in progress");
while self.read_reg::<SR1>().wip() {
print!(".");
}
println!("");
} else {
warn!("erased? sr1={:02X}", sr1.inner);
}
}
pub fn program<I: Iterator<Item=u32>>(&mut self, offset: u32, data: I) {
{
let len = 4 + 4 * data.size_hint().0;
let args = Some(SpiWord::W32(((INST_PP as u32) << 24) | (offset as u32))).into_iter()
.chain(data.map(SpiWord::W32));
self.transfer(args, len);
}
// let sr1 = self.wait_while_sr1_zeroed();
let sr1 = self.read_reg::<SR1>();
if sr1.e_err() {
error!("E_ERR");
} else if sr1.p_err() {
error!("P_ERR");
} else if sr1.wip() {
info!("Program in progress");
while self.read_reg::<SR1>().wip() {
print!(".");
}
println!("");
} else {
warn!("programmed? sr1={:02X}", sr1.inner);
}
}
pub fn write_enabled<F: Fn(&mut Self) -> R, R>(&mut self, f: F) -> R {
// Write Enable
let args = Some(INST_WREN);
self.transfer(args.into_iter(), 1);
self.regs.gpio.modify(|_, w| w.wp_n(true));
let sr1 = self.wait_while_sr1_zeroed();
if !sr1.wel() {
panic!("Cannot write-enable flash");
}
let result = f(self);
// Write Disable
let args = Some(INST_WRDI);
self.transfer(args.into_iter(), 1);
self.regs.gpio.modify(|_, w| w.wp_n(false));
result
}
pub fn transfer<'s: 't, 't, Args, W>(&'s mut self, args: Args, len: usize) -> Transfer<'t, Args, W>
where
Args: Iterator<Item = W>,
W: Into<SpiWord>,
{
Transfer::new(self, args, len)
}
pub fn dump(&mut self, label: &'_ str, inst_code: u8) {
print!("{}:", label);
let args = Some(u32::from(inst_code) << 24);
for b in self.transfer(args.into_iter(), 32).bytes_transfer() {
print!(" {:02X}", b);
}
println!("");
}
}

View File

@ -1,126 +0,0 @@
use volatile_register::{RO, WO, RW};
use libregister::{register, register_bit, register_bits};
#[repr(C)]
pub struct RegisterBlock {
pub config: Config,
pub intr_status: IntrStatus,
pub intr_en: IntrEn,
pub intr_dis: IntrDis,
pub intr_mask: RO<u32>,
pub enable: Enable,
pub delay: RW<u32>,
pub txd0: WO<u32>,
pub rx_data: RO<u32>,
pub slave_idle_count: RW<u32>,
pub tx_thres: RW<u32>,
pub rx_thres: RW<u32>,
pub gpio: QspiGpio,
pub _unused1: RO<u32>,
pub lpbk_dly_adj: RW<u32>,
pub _unused2: [RO<u32>; 17],
pub txd1: WO<u32>,
pub txd2: WO<u32>,
pub txd3: WO<u32>,
pub _unused3: [RO<u32>; 5],
pub lqspi_cfg: LqspiCfg,
pub lqspi_sts: RW<u32>,
pub _unused4: [RO<u32>; 21],
pub mod_id: RW<u32>,
}
impl RegisterBlock {
const BASE_ADDRESS: *mut Self = 0xE000D000 as *mut _;
pub fn qspi() -> &'static mut Self {
unsafe { &mut *Self::BASE_ADDRESS }
}
}
register!(config, Config, RW, u32);
register_bit!(config,
/// Enables master mode
mode_sel, 0);
register_bit!(config,
/// Clock polarity low/high
clk_pol, 1);
register_bit!(config,
/// Clock phase
clk_ph, 2);
register_bits!(config,
/// divider = 2 ** (1 + baud_rate_div)
baud_rate_div, u8, 3, 5);
register_bits!(config,
/// Must be set to 0b11
fifo_width, u8, 6, 7);
register_bit!(config,
/// Must be 0
ref_clk, 8);
register_bit!(config,
/// Peripheral Chip Select Line
pcs, 10);
register_bit!(config,
/// false: auto mode, true: manual CS mode
manual_cs, 14);
register_bit!(config,
/// false: auto mode, true: enables manual start enable
man_start_en, 15);
register_bit!(config,
/// false: auto mode, true: enables manual start command
man_start_com, 16);
register_bit!(config, holdb_dr, 19);
register_bit!(config,
/// false: little, true: endian
endian, 26);
register_bit!(config,
/// false: legacy SPI mode, true: Flash memory interface mode
leg_flsh, 31);
register!(intr_status, IntrStatus, RW, u32);
register_bit!(intr_status, rx_overflow, 0);
register_bit!(intr_status,
/// < tx_thres
tx_fifo_not_full, 2);
register_bit!(intr_status, tx_fifo_full, 3);
register_bit!(intr_status,
/// >= rx_thres
rx_fifo_not_empty, 4);
register_bit!(intr_status, rx_fifo_full, 5);
register_bit!(intr_status, tx_fifo_underflow, 6);
register!(intr_en, IntrEn, WO, u32);
register_bit!(intr_en, rx_overflow, 0);
register_bit!(intr_en, tx_fifo_not_full, 2);
register_bit!(intr_en, tx_fifo_full, 3);
register_bit!(intr_en, rx_fifo_not_empty, 4);
register_bit!(intr_en, rx_fifo_full, 5);
register_bit!(intr_en, tx_fifo_underflow, 6);
register!(intr_dis, IntrDis, WO, u32);
register_bit!(intr_dis, rx_overflow, 0);
register_bit!(intr_dis, tx_fifo_not_full, 2);
register_bit!(intr_dis, tx_fifo_full, 3);
register_bit!(intr_dis, rx_fifo_not_empty, 4);
register_bit!(intr_dis, rx_fifo_full, 5);
register_bit!(intr_dis, tx_fifo_underflow, 6);
register!(enable, Enable, RW, u32);
register_bit!(enable, spi_en, 0);
// named to avoid confusion with normal gpio
register!(qspi_gpio, QspiGpio, RW, u32);
register_bit!(qspi_gpio,
/// Write protect pin (inverted)
wp_n, 0);
register!(lqspi_cfg, LqspiCfg, RW, u32);
register_bits!(lqspi_cfg, inst_code, u8, 0, 7);
register_bits!(lqspi_cfg, dummy_mask, u8, 8, 10);
register_bits!(lqspi_cfg, mode_bits, u8, 16, 23);
register_bit!(lqspi_cfg, mode_on, 24);
register_bit!(lqspi_cfg, mode_en, 25);
register_bit!(lqspi_cfg, u_page, 28);
register_bit!(lqspi_cfg, sep_bus, 29);
register_bit!(lqspi_cfg, two_mem, 30);
register_bit!(lqspi_cfg, lq_mode, 31);

View File

@ -1,62 +0,0 @@
use bit_field::BitField;
pub trait SpiFlashRegister {
fn inst_code() -> u8;
fn new(src: u8) -> Self;
}
macro_rules! u8_register {
($name: ident, $doc: tt, $inst_code: expr) => {
#[derive(Clone)]
#[doc=$doc]
pub struct $name {
pub inner: u8,
}
impl SpiFlashRegister for $name {
fn inst_code() -> u8 {
$inst_code
}
fn new(src: u8) -> Self {
$name {
inner: src,
}
}
}
impl $name {
#[allow(unused)]
pub fn is_zeroed(&self) -> bool {
self.inner == 0
}
}
};
}
u8_register!(CR, "Configuration Register", 0x35);
u8_register!(SR1, "Status Register-1", 0x05);
impl SR1 {
/// Write In Progress
pub fn wip(&self) -> bool {
self.inner.get_bit(0)
}
/// Write Enable Latch
pub fn wel(&self) -> bool {
self.inner.get_bit(1)
}
/// Erase Error Occurred
pub fn e_err(&self) -> bool {
self.inner.get_bit(5)
}
/// Programming Error Occurred
pub fn p_err(&self) -> bool {
self.inner.get_bit(6)
}
}
u8_register!(SR2, "Status Register-2", 0x07);
u8_register!(BA, "Bank Address Register", 0xB9);

View File

@ -1,125 +0,0 @@
use libregister::{RegisterR, RegisterW, RegisterRW};
use super::regs;
use super::{SpiWord, Flash, Manual};
pub struct Transfer<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> {
flash: &'a mut Flash<Manual>,
args: Args,
sent: usize,
received: usize,
len: usize,
}
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Transfer<'a, Args, W> {
pub fn new(flash: &'a mut Flash<Manual>, args: Args, len: usize) -> Self {
flash.regs.config.modify(|_, w| w.pcs(false));
flash.regs.enable.write(
regs::Enable::zeroed()
.spi_en(true)
);
let mut xfer = Transfer {
flash,
args,
sent: 0,
received: 0,
len,
};
xfer.fill_tx_fifo();
xfer.flash.regs.config.modify(|_, w| w.man_start_com(true));
xfer
}
fn fill_tx_fifo(&mut self) {
while self.sent < self.len && !self.flash.regs.intr_status.read().tx_fifo_full() {
let arg = self.args.next()
.map(|n| n.into())
.unwrap_or(SpiWord::W32(0));
match arg {
SpiWord::W32(w) => {
// println!("txd0 {:08X}", w);
unsafe {
self.flash.regs.txd0.write(w);
}
self.sent += 4;
}
// Only txd0 can be used without flushing
_ => {
if !self.flash.regs.intr_status.read().tx_fifo_not_full() {
// Flush if necessary
self.flash.wait_tx_fifo_flush();
}
match arg {
SpiWord::W8(w) => {
// println!("txd1 {:02X}", w);
unsafe {
self.flash.regs.txd1.write(u32::from(w) << 24);
}
self.sent += 1;
}
SpiWord::W16(w) => {
unsafe {
self.flash.regs.txd2.write(u32::from(w) << 16);
}
self.sent += 2;
}
SpiWord::W24(w) => {
unsafe {
self.flash.regs.txd3.write(w << 8);
}
self.sent += 3;
}
SpiWord::W32(_) => unreachable!(),
}
self.flash.wait_tx_fifo_flush();
}
}
}
}
fn can_read(&mut self) -> bool {
self.flash.regs.intr_status.read().rx_fifo_not_empty()
}
fn read(&mut self) -> u32 {
let rx = self.flash.regs.rx_data.read();
self.received += 4;
rx
}
}
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Drop for Transfer<'a, Args, W> {
fn drop(&mut self) {
// Discard remaining rx_data
while self.can_read() {
self.read();
}
// Stop
self.flash.regs.enable.write(
regs::Enable::zeroed()
.spi_en(false)
);
self.flash.regs.config.modify(|_, w| w
.pcs(true)
.man_start_com(false)
);
}
}
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Iterator for Transfer<'a, Args, W> {
type Item = u32;
fn next<'s>(&'s mut self) -> Option<u32> {
if self.received >= self.len {
return None;
}
self.fill_tx_fifo();
while !self.can_read() {}
Some(self.read())
}
}

150
libboard_zynq/src/gic.rs Normal file
View File

@ -0,0 +1,150 @@
//! ARM Generic Interrupt Controller
use bit_field::BitField;
use libregister::{RegisterW, RegisterRW, RegisterR};
use super::mpcore;
#[derive(Debug, Clone, Copy)]
pub struct InterruptId(pub u8);
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum CPUCore {
Core0 = 0b01,
Core1 = 0b10
}
#[derive(Debug, Clone, Copy)]
pub struct TargetCPU(u8);
impl TargetCPU {
pub const fn none() -> TargetCPU {
TargetCPU(0)
}
pub const fn and(self, other: TargetCPU) -> TargetCPU {
TargetCPU(self.0 | other.0)
}
}
impl From<CPUCore> for TargetCPU {
fn from(core: CPUCore) -> Self {
TargetCPU(core as u8)
}
}
pub enum TargetList {
CPUList(TargetCPU),
Others,
This
}
impl From<CPUCore> for TargetList {
fn from(core: CPUCore) -> Self {
TargetList::CPUList(TargetCPU(core as u8))
}
}
impl From<TargetCPU> for TargetList {
fn from(cpu: TargetCPU) -> Self {
TargetList::CPUList(cpu)
}
}
#[derive(Debug, Clone, Copy)]
pub enum InterruptSensitivity {
Level,
Edge,
}
pub struct InterruptController {
mpcore: &'static mut mpcore::RegisterBlock,
}
impl InterruptController {
pub fn gic(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
InterruptController { mpcore }
}
pub fn disable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(false)
.enable_s(false));
// FIXME: Should we disable the distributor globally when we disable interrupt (for a single
// core)?
// self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
// .enable_non_secure(false));
}
/// enable interrupt signaling
pub fn enable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(true)
.enable_s(true));
self.mpcore.icddcr.modify(|_, w| w.enable_secure(true));
// Enable all interrupts except those of the lowest priority.
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
}
/// send software generated interrupt
pub fn send_sgi(&mut self, id: InterruptId, targets: TargetList) {
assert!(id.0 < 16);
self.mpcore.icdsgir.modify(|_, w| match targets {
TargetList::CPUList(list) => w.target_list_filter(0).cpu_target_list(list.0),
TargetList::Others => w.target_list_filter(0b01),
TargetList::This => w.target_list_filter(0b10)
}.sgiintid(id.0).satt(false));
}
/// enable the interrupt *for this core*.
/// Not needed for SGI.
pub fn enable(&mut self, id: InterruptId, target_cpu: CPUCore, sensitivity: InterruptSensitivity, priority: u8) {
// only 5 bits of the priority is useful
assert!(priority < 32);
self.disable_interrupts();
// enable
let m = (id.0 >> 5) as usize;
let n = (id.0 & 0x1F) as usize;
assert!(m < 3);
unsafe {
self.mpcore.icdiser[m].modify(|mut icdiser| *icdiser.set_bit(n, true));
}
// target cpu
let m = (id.0 >> 2) as usize;
let n = (8 * (id.0 & 3)) as usize;
unsafe {
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32));
}
// sensitivity
let m = (id.0 >> 4) as usize;
let n = (2 * (id.0 & 0xF)) as usize;
unsafe {
self.mpcore.icdicfr[m].modify(|mut icdicfr| *icdicfr.set_bits(n..=n+1, match sensitivity {
InterruptSensitivity::Level => 0b00,
InterruptSensitivity::Edge => 0b10,
}));
}
// priority
let offset = (id.0 % 4) * 8;
let priority: u32 = (priority as u32) << (offset + 3);
let mask: u32 = 0xFFFFFFFF ^ (0xFF << offset);
unsafe {
self.mpcore.icdipr[id.0 as usize / 4].modify(|v| (v & mask) | priority);
}
self.enable_interrupts();
}
pub fn end_interrupt(&mut self, id: InterruptId) {
self.mpcore.icceoir.modify(|_, w| w.eoiintid(id.0 as u32));
}
pub fn get_interrupt_id(&self) -> InterruptId {
InterruptId(self.mpcore.icciar.read().ackintid() as u8)
}
}

View File

@ -0,0 +1,130 @@
use super::I2c;
use crate::time::Milliseconds;
use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> {
i2c: &'a mut I2c,
#[cfg(not(feature = "target_ebaz4205"))]
port: u8,
address: u8,
page_size: u8,
count_down: crate::timer::global::CountDown<Milliseconds>
}
impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2c, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 2,
address: 0b1010100,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
}
}
#[cfg(feature = "target_kasli_soc")]
pub fn new(i2c: &'a mut I2c, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 3,
address: 0x57,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
}
}
#[cfg(feature = "target_zc706")]
fn select(&mut self) -> Result<(), &'static str> {
self.i2c.pca954x_select(0b1110100, Some(self.port))?;
Ok(())
}
#[cfg(feature = "target_kasli_soc")]
fn select(&mut self) -> Result<(), &'static str> {
// tca9548 is compatible with pca9548
self.i2c.pca954x_select(0b1110001, Some(self.port))?;
Ok(())
}
#[cfg(feature = "target_ebaz4205")]
fn select(&mut self) -> Result<(), &'static str> {
Ok(())
}
/// Random read
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
self.select()?;
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr)?;
self.i2c.restart()?;
self.i2c.write((self.address << 1) | 1)?;
let buf_len = buf.len();
for (i, byte) in buf.iter_mut().enumerate() {
*byte = self.i2c.read(i < buf_len - 1)?;
}
self.i2c.stop()?;
Ok(())
}
/// Smart multi-page writing
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
/// any roll-over when writing ambiguous memory regions.
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
self.select()?;
let buf_len = buf.len();
let mut pb: u8 = addr % self.page_size;
for (i, byte) in buf.iter().enumerate() {
if (i == 0) || (pb == 0) {
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr + (i as u8))?;
}
self.i2c.write(*byte)?;
pb += 1;
if (i == buf_len-1) || (pb == self.page_size) {
self.i2c.stop()?;
self.poll(1_000)?;
pb = 0;
}
}
Ok(())
}
/// Poll
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
self.select()?;
self.count_down.start(Milliseconds(timeout_ms));
loop {
self.i2c.start()?;
let ack = self.i2c.write(self.address << 1)?;
self.i2c.stop()?;
if ack {
break
};
if !self.count_down.waiting() {
return Err("I2C polling timeout")
}
}
Ok(())
}
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
let mut buffer = [0u8; 6];
self.read(0xFA, &mut buffer)?;
Ok(buffer)
}
}

View File

@ -0,0 +1,336 @@
//! I2C Bit-banging Controller
mod regs;
pub mod eeprom;
#[cfg(not(feature = "target_ebaz4205"))]
use super::slcr;
use super::time::Microseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW};
#[cfg(not(feature = "target_ebaz4205"))]
use libregister::RegisterW;
#[cfg(feature = "target_kasli_soc")]
use log::info;
pub enum I2cMultiplexer {
PCA9548 = 0,
#[cfg(feature = "target_kasli_soc")]
PCA9547 = 1,
}
pub struct I2c {
regs: regs::RegisterBlock,
count_down: super::timer::global::CountDown<Microseconds>,
pca_type: I2cMultiplexer
}
impl I2c {
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
pub fn i2c0() -> Self {
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
#[cfg(not(feature = "target_ebaz4205"))]
slcr::RegisterBlock::unlocked(|slcr| {
// SCL
slcr.mio_pin_50.write(
slcr::MioPin50::zeroed()
.l3_sel(0b000) // as GPIO 50
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// SDA
slcr.mio_pin_51.write(
slcr::MioPin51::zeroed()
.l3_sel(0b000) // as GPIO 51
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// On Kasli-SoC prototype, leakage through the unconfigured I2C_SW_RESET
// MIO pin develops enough voltage on the T21 gate to assert the reset.
// Configure the pin to avoid this problem.
#[cfg(feature = "target_kasli_soc")]
slcr.mio_pin_33.write(
slcr::MioPin33::zeroed()
.l3_sel(0b000)
.io_type(slcr::IoBufferType::Lvcmos33)
.pullup(false)
.disable_rcvr(true)
);
});
Self::i2c_common(0xFFFF - 0x000C, 0xFFFF - 0x0002)
}
fn i2c_common(gpio_output_mask: u16, _gpio_output_mask_lower: u16) -> Self {
// Setup register block
let self_ = Self {
regs: regs::RegisterBlock::i2c(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
pca_type: I2cMultiplexer::PCA9548 //default for zc706
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(gpio_output_mask)
});
// Setup GPIO driver direction
self_.regs.gpio_direction.modify(|_, w| {
w.scl(true).sda(true)
});
//Kasli-SoC only: I2C_SW_RESET configuration
#[cfg(feature = "target_kasli_soc")]
{
self_.regs.gpio_output_mask_lower.modify(|_, w| {
w.mask(_gpio_output_mask_lower)
});
self_.regs.gpio_direction.modify(|_, w| {
w.i2cswr(true)
});
}
self_
}
/// Delay for I2C operations, simple wrapper for nb.
fn delay_us(&mut self, us: u64) {
self.count_down.start(Microseconds(us));
nb::block!(self.count_down.wait()).unwrap();
}
fn unit_delay(&mut self) { self.delay_us(100) }
fn sda_i(&mut self) -> bool {
self.regs.gpio_input.read().sda()
}
fn scl_i(&mut self) -> bool {
self.regs.gpio_input.read().scl()
}
fn sda_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.sda(oe)
})
}
fn sda_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.sda_o(o)
})
}
fn scl_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.scl(oe)
})
}
fn scl_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.scl_o(o)
})
}
#[cfg(feature = "target_kasli_soc")]
fn i2cswr_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.i2cswr(oe)
})
}
#[cfg(feature = "target_kasli_soc")]
fn i2cswr_o(&mut self, o: bool) {
self.regs.gpio_output_mask_lower.modify(|_, w| {
w.i2cswr_o(o)
})
}
#[cfg(feature = "target_kasli_soc")]
fn pca_autodetect(&mut self) -> Result<I2cMultiplexer, &'static str> {
// start with resetting the PCA954X
// SDA must be clear (before start)
// reset time is 500ns, unit_delay (100us) to account for propagation
self.i2cswr_o(true);
self.unit_delay();
self.i2cswr_o(false);
self.unit_delay();
let pca954x_read_addr = (0x71 << 1) | 0x01;
self.start()?;
// read the config register
if !self.write(pca954x_read_addr)? {
return Err("PCA954X failed to ack read address");
}
let config = self.read(false)?;
let pca = match config {
0x00 => { info!("PCA9548 detected"); I2cMultiplexer::PCA9548 },
0x08 => { info!("PCA9547 detected"); I2cMultiplexer::PCA9547 },
_ => { return Err("Unknown response for PCA954X autodetect")},
};
self.stop()?;
Ok(pca)
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.scl_oe(false);
self.sda_oe(false);
self.scl_o(false);
self.sda_o(false);
// Check the I2C bus is ready
self.unit_delay();
self.unit_delay();
if !self.sda_i() {
// Try toggling SCL a few times
for _bit in 0..8 {
self.scl_oe(true);
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
}
}
if !self.sda_i() {
return Err("SDA is stuck low and doesn't get unstuck");
}
if !self.scl_i() {
return Err("SCL is stuck low");
}
// postcondition: SCL and SDA high
#[cfg(feature = "target_kasli_soc")]
{
self.i2cswr_oe(true);
self.pca_type = self.pca_autodetect()?;
}
Ok(())
}
pub fn start(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA high
if !self.scl_i() {
return Err("SCL is stuck low");
}
if !self.sda_i() {
return Err("SDA arbitration lost");
}
self.sda_oe(true);
self.unit_delay();
self.scl_oe(true);
self.unit_delay();
// postcondition: SCL and SDA low
Ok(())
}
pub fn restart(&mut self) -> Result<(), &'static str> {
// precondition SCL and SDA low
self.sda_oe(false);
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
self.start()?;
// postcondition: SCL and SDA low
Ok(())
}
pub fn stop(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA low
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
self.sda_oe(false);
self.unit_delay();
if !self.sda_i() {
return Err("SDA arbitration lost");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
// precondition: SCL and SDA low
// MSB first
for bit in (0..8).rev() {
self.sda_oe(data & (1 << bit) == 0);
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
self.scl_oe(true);
self.unit_delay();
}
self.sda_oe(false);
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
// Read ack/nack
let ack = !self.sda_i();
self.scl_oe(true);
self.unit_delay();
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(ack)
}
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
// precondition: SCL and SDA low
self.sda_oe(false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
if self.sda_i() { data |= 1 << bit }
self.scl_oe(true);
}
// Send ack/nack (true = nack, false = ack)
self.sda_oe(ack);
self.unit_delay();
self.scl_oe(false);
self.unit_delay();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(data)
}
pub fn pca954x_select(&mut self, address: u8, channel: Option<u8>) -> Result<(), &'static str> {
self.start()?;
// PCA9547 supports only one channel at a time
// for compatibility, PCA9548 is treated as such too
// channel - Some(x) - # of the channel [0,7], or None for all disabled
let setting = match self.pca_type {
I2cMultiplexer::PCA9548 => {
match channel {
Some(ch) => 1 << ch,
None => 0,
}
},
#[cfg(feature = "target_kasli_soc")]
I2cMultiplexer::PCA9547 => {
match channel {
Some(ch) => ch | 0x08,
None => 0,
}
}
};
if !self.write(address << 1)? {
return Err("PCA954X failed to ack write address")
}
if !self.write(setting)? {
return Err("PCA954X failed to ack control word")
}
self.stop()?;
Ok(())
}
}

View File

@ -0,0 +1,135 @@
use libregister::{
register, register_at,
register_bit, register_bits
};
// With reference to:
//
// artiq:artiq/gateware/targets/kasli.py:
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
//
// misoc:misoc/cores/gpio.py:
// class GPIOTristate(Module, AutoCSR):
// def __init__(self, signals, reset_out=0, reset_oe=0):
// l = len(signals)
// self._in = CSRStatus(l)
// self._out = CSRStorage(l, reset=reset_out)
// self._oe = CSRStorage(l, reset=reset_oe)
//
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
//
// Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA
// kasli_soc: GPIO 50, 51 == SCL, SDA; GPIO 33 == I2C_SW_RESET
// ebaz4205: GPIO (EMIO)
pub struct RegisterBlock {
pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_input: &'static mut GPIOInput,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable,
#[cfg(feature = "target_kasli_soc")]
pub gpio_output_mask_lower: &'static mut GPIOOutputMaskLower,
}
impl RegisterBlock {
pub fn i2c() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(),
gpio_direction: GPIODirection::new(),
gpio_output_enable: GPIOOutputEnable::new(),
#[cfg(feature = "target_kasli_soc")]
gpio_output_mask_lower: GPIOOutputMaskLower::new(),
}
}
}
register!(gpio_output_mask,
/// MASK_DATA_1_MSW:
/// Maskable output data for MIO[53:48]
GPIOOutputMask, RW, u32);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_at!(GPIOOutputMask, 0xE000A00C, new);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_output_mask,
/// Output for SCL
scl_o, 2);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_output_mask,
/// Output for SDA
sda_o, 3);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bits!(gpio_output_mask,
/// Mask for keeping bits except SCL and SDA unchanged
mask, u16, 16, 31);
register!(gpio_output_mask_lower,
/// MASK_DATA_1_LSW:
/// Maskable output data for MIO[47:32]
GPIOOutputMaskLower, RW, u32);
#[cfg(feature = "target_kasli_soc")]
register_at!(GPIOOutputMaskLower, 0xE000A008, new);
#[cfg(feature = "target_kasli_soc")]
register_bit!(gpio_output_mask_lower,
/// Output for I2C_SW_RESET (MIO[33])
i2cswr_o, 1);
#[cfg(feature = "target_kasli_soc")]
register_bits!(gpio_output_mask_lower,
mask, u16, 16, 31);
register!(gpio_input,
/// DATA_1_RO:
/// Input data for MIO[53:32]
GPIOInput, RO, u32);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_at!(GPIOInput, 0xE000A064, new);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_input,
/// Input for SCL
scl, 18);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_input,
/// Input for SDA
sda, 19);
register!(gpio_direction,
/// DIRM_1:
/// Direction mode for MIO[53:32]; 0/1 = in/out
GPIODirection, RW, u32);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_at!(GPIODirection, 0xE000A244, new);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_direction,
/// Direction for SCL
scl, 18);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_direction,
/// Direction for SDA
sda, 19);
#[cfg(feature = "target_kasli_soc")]
register_bit!(gpio_direction,
/// Direction for I2C_SW_RESET
i2cswr, 1);
register!(gpio_output_enable,
/// OEN_1:
/// Output enable for MIO[53:32]
GPIOOutputEnable, RW, u32);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_at!(GPIOOutputEnable, 0xE000A248, new);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_output_enable,
/// Output enable for SCL
scl, 18);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
register_bit!(gpio_output_enable,
/// Output enable for SDA
sda, 19);
#[cfg(feature = "target_kasli_soc")]
register_bit!(gpio_output_enable,
/// Output enable for I2C_SW_RESET
i2cswr, 1);

View File

@ -15,9 +15,13 @@ pub mod axi_hp;
pub mod axi_gp; pub mod axi_gp;
pub mod ddr; pub mod ddr;
pub mod mpcore; pub mod mpcore;
pub mod flash; pub mod gic;
pub mod dmac;
pub mod time; pub mod time;
pub mod timer; pub mod timer;
pub mod sdio; pub mod sdio;
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc", feature = "target_ebaz4205"))]
pub mod i2c;
pub mod logger; pub mod logger;
pub mod ps7_init;
#[cfg(feature="target_kasli_soc")]
pub mod error_led;

View File

@ -19,7 +19,7 @@ impl log::Log for Logger {
if self.enabled(record.metadata()) { if self.enabled(record.metadata()) {
let timestamp = unsafe { let timestamp = unsafe {
GlobalTimer::get() GlobalTimer::get()
}.get_us(); }.get_us().0;
let seconds = timestamp / 1_000_000; let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000; let micros = timestamp % 1_000_000;

View File

@ -8,47 +8,140 @@ use libregister::{
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
/// SCU Control Register
pub scu_control: ScuControl, pub scu_control: ScuControl,
pub scu_config: RO<u32>, /// SCU Configuration Register
pub scu_cpu_power: RW<u32>, pub scu_config: ScuConfig,
/// SCU CPU Power Status Register
pub scu_cpu_power_status: SCUCPUPowerStatusRegister,
/// SCU Invalidate All Registers in Secure State
pub scu_invalidate: ScuInvalidate, pub scu_invalidate: ScuInvalidate,
reserved0: [u32; 12], unused0: [u32; 12],
pub filter_start: RW<u32>, /// Filtering Start Address Register
pub filter_end: RW<u32>, pub filtering_start_address: FilteringStartAddressRegister,
reserved1: [u32; 2], /// Defined by FILTEREND input
pub scu_access_control: RW<u32>, pub filtering_end_address: FilteringEndAddressRegister,
pub scu_non_secure_access_control: RW<u32>, unused1: [u32; 2],
reserved2: [u32; 42], /// SCU Access Control (SAC) Register
pub iccicr: RW<u32>, pub scu_access_control_sac: SCUAccessControlRegisterSAC,
pub iccpmw: RW<u32>, /// SCU Non-secure Access Control Register SNSAC
pub iccbpr: RW<u32>, pub scu_non_secure_access_control: SCUNonSecureAccessControlRegister,
pub icciar: RW<u32>, unused2: [u32; 42],
pub icceoir: RW<u32>, /// CPU Interface Control Register
pub iccrpr: RW<u32>, pub iccicr: ICCICR,
pub icchpir: RW<u32>, /// Interrupt Priority Mask Register
pub iccabpr: RW<u32>, pub iccpmr: ICCPMR,
reserved3: [u32; 55], /// Binary Point Register
pub iccidr: RW<u32>, pub iccbpr: ICCBPR,
/// Interrupt Acknowledge Register
pub icciar: ICCIAR,
/// End Of Interrupt Register
pub icceoir: ICCEOIR,
/// Running Priority Register
pub iccrpr: ICCRPR,
/// Highest Pending Interrupt Register
pub icchpir: ICCHPIR,
/// Aliased Non-secure Binary Point Register
pub iccabpr: ICCABPR,
unused3: [u32; 55],
/// CPU Interface Implementer Identification Register
pub iccidr: ICCIDR,
/// Global Timer Counter Register 0
pub global_timer_counter0: ValueRegister, pub global_timer_counter0: ValueRegister,
pub global_timer_counter1: ValueRegister, pub global_timer_counter1: ValueRegister,
/// Global Timer Control Register
pub global_timer_control: GlobalTimerControl, pub global_timer_control: GlobalTimerControl,
pub global_timer_interrupt_status: RW<u32>, /// Global Timer Interrupt Status Register
pub global_timer_interrupt_status: GlobalTimerInterruptStatusRegister,
/// Comparator Value Register_0
pub comparator_value0: ValueRegister, pub comparator_value0: ValueRegister,
pub comparator_value1: ValueRegister, pub comparator_value1: ValueRegister,
pub auto_increment: ValueRegister, /// Auto-increment Register
reserved4: [u32; 249], pub auto_increment: RW<u32>,
pub private_timer_load: ValueRegister, unused4: [u32; 249],
pub private_timer_counter: ValueRegister, /// Private Timer Load Register
pub private_timer_control: RW<u32>, pub private_timer_load: RW<u32>,
pub private_timer_interrupt_status: RW<u32>, /// Private Timer Counter Register
reserved5: [u32; 4], pub private_timer_counter: RW<u32>,
pub watchdog_load: ValueRegister, /// Private Timer Control Register
pub watchdog_counter: ValueRegister, pub private_timer_control: PrivateTimerControlRegister,
pub watchdog_control: RW<u32>, /// Private Timer Interrupt Status Register
pub watchdog_interrupt_status: RW<u32>, pub private_timer_interrupt_status: PrivateTimerInterruptStatusRegister,
// there is plenty more (unimplemented) unused5: [u32; 4],
/// Watchdog Load Register
pub watchdog_load: RW<u32>,
/// Watchdog Counter Register
pub watchdog_counter: RW<u32>,
/// Watchdog Control Register
pub watchdog_control: WatchdogControlRegister,
/// Watchdog Interrupt Status Register
pub watchdog_interrupt_status: WatchdogInterruptStatusRegister,
/// Watchdog Reset Status Register
pub watchdog_reset_status: WatchdogResetStatusRegister,
/// Watchdog Disable Register
pub watchdog_disable: RW<u32>,
unused6: [u32; 626],
/// Distributor Control Register
pub icddcr: ICDDCR,
/// Interrupt Controller Type Register
pub icdictr: ICDICTR,
/// Distributor Implementer Identification Register
pub icdiidr: ICDIIDR,
unused7: [u32; 29],
/// Interrupt Security Register
pub icdisr0: RW<u32>,
pub icdisr1: RW<u32>,
pub icdisr2: RW<u32>,
unused8: [u32; 29],
/// Interrupt Set-enable Registers
pub icdiser: [RW<u32>; 3],
unused9: [u32; 29],
/// Interrupt Clear-Enable Register 0
pub icdicer0: RW<u32>,
/// Interrupt Clear-Enable Register 1
pub icdicer1: RW<u32>,
/// Interrupt Clear-Enable Register 2
pub icdicer2: RW<u32>,
unused10: [u32; 29],
/// Interrupt Set-pending Register
pub icdispr0: RW<u32>,
pub icdispr1: RW<u32>,
pub icdispr2: RW<u32>,
unused11: [u32; 29],
/// Interrupt Clear-Pending Register
pub icdicpr0: RW<u32>,
pub icdicpr1: RW<u32>,
pub icdicpr2: RW<u32>,
unused12: [u32; 29],
/// Active Bit register
pub icdabr0: RW<u32>,
pub icdabr1: RW<u32>,
pub icdabr2: RW<u32>,
unused13: [u32; 61],
/// Interrupt Priority Register
pub icdipr: [RW<u32>; 24],
unused14: [u32; 232],
/// Interrupt Processor Targets Registers
pub icdiptr: [RW<u32>; 24],
unused15: [u32; 232],
/// Interrupt Configuration Registers
pub icdicfr: [RW<u32>; 6],
unused16: [u32; 58],
/// PPI Status Register
pub ppi_status: PpiStatus,
/// SPI Status Register 0
pub spi_status_0: RO<u32>,
/// SPI Status Register 1
pub spi_status_1: RO<u32>,
unused17: [u32; 125],
/// Software Generated Interrupt Register
pub icdsgir: ICDSGIR,
} }
register_at!(RegisterBlock, 0xF8F00000, new);
register_at!(RegisterBlock, 0xF8F00000, mpcore);
register!(value_register, ValueRegister, RW, u32);
register_bits!(value_register, value, u32, 0, 31);
register!(scu_control, ScuControl, RW, u32); register!(scu_control, ScuControl, RW, u32);
register_bit!(scu_control, ic_standby_enable, 6); register_bit!(scu_control, ic_standby_enable, 6);
@ -61,10 +154,21 @@ register_bit!(scu_control, enable, 0);
impl ScuControl { impl ScuControl {
pub fn start(&mut self) { pub fn start(&mut self) {
self.modify(|_, w| w.enable(true)); self.modify(|_, w| w.enable(true).scu_speculative_linefill_enable(true));
} }
} }
register!(scu_config, ScuConfig, RO, u32);
register_bits!(scu_config, tag_ram_sizes, u8, 8, 15);
register_bits!(scu_config, cpus_smp, u8, 4, 7);
register_bits!(scu_config, cpu_number, u8, 0, 1);
register!(scu_cpu_power_status, SCUCPUPowerStatusRegister, RW, u32);
register_bits!(scu_cpu_power_status, cpu3_status, u8, 24, 25);
register_bits!(scu_cpu_power_status, cpu2_status, u8, 16, 17);
register_bits!(scu_cpu_power_status, cpu1_status, u8, 8, 9);
register_bits!(scu_cpu_power_status, cpu0_status, u8, 0, 1);
register!(scu_invalidate, ScuInvalidate, WO, u32); register!(scu_invalidate, ScuInvalidate, WO, u32);
register_bits!(scu_invalidate, cpu0_ways, u8, 0, 3); register_bits!(scu_invalidate, cpu0_ways, u8, 0, 3);
register_bits!(scu_invalidate, cpu1_ways, u8, 4, 7); register_bits!(scu_invalidate, cpu1_ways, u8, 4, 7);
@ -88,8 +192,71 @@ impl ScuInvalidate {
} }
} }
register!(value_register, ValueRegister, RW, u32); register!(filtering_start_address, FilteringStartAddressRegister, RW, u32);
register_bits!(value_register, value, u32, 0, 31); register_bits!(filtering_start_address, filtering_start_address, u32, 20, 31);
register_bits!(filtering_start_address, sbz, u32, 0, 19);
register!(filtering_end_address, FilteringEndAddressRegister, RW, u32);
register_bits!(filtering_end_address, filtering_end_address, u32, 20, 31);
register_bits!(filtering_end_address, sbz, u32, 0, 19);
register!(scu_access_control_sac, SCUAccessControlRegisterSAC, RW, u32);
register_bit!(scu_access_control_sac, cp_u3, 3);
register_bit!(scu_access_control_sac, cp_u2, 2);
register_bit!(scu_access_control_sac, cp_u1, 1);
register_bit!(scu_access_control_sac, cp_u0, 0);
register!(scu_non_secure_access_control, SCUNonSecureAccessControlRegister, RO, u32);
register_bits!(scu_non_secure_access_control, sbz, u32, 12, 31);
register_bit!(scu_non_secure_access_control, cpu3_global_timer, 11);
register_bit!(scu_non_secure_access_control, cpu2_global_timer, 10);
register_bit!(scu_non_secure_access_control, cpu1_global_timer, 9);
register_bit!(scu_non_secure_access_control, cpu0_global_timer, 8);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu3, 7);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu2, 6);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu1, 5);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu0, 4);
register_bit!(scu_non_secure_access_control, component_access_for_cpu3, 3);
register_bit!(scu_non_secure_access_control, component_access_for_cpu2, 2);
register_bit!(scu_non_secure_access_control, component_access_for_cpu1, 1);
register_bit!(scu_non_secure_access_control, component_access_for_cpu0, 0);
register!(iccicr, ICCICR, RW, u32);
register_bit!(iccicr, sbpr, 4);
register_bit!(iccicr, fiq_en, 3);
register_bit!(iccicr, ack_ctl, 2);
register_bit!(iccicr, enable_ns, 1);
register_bit!(iccicr, enable_s, 0);
register!(iccpmr, ICCPMR, RW, u32);
register_bits!(iccpmr, priority, u8, 0, 7);
register!(iccbpr, ICCBPR, RW, u32);
register_bits!(iccbpr, binary_point, u8, 0, 2);
register!(icciar, ICCIAR, RW, u32);
register_bits!(icciar, cpuid, u8, 10, 12);
register_bits!(icciar, ackintid, u32, 0, 9);
register!(icceoir, ICCEOIR, RW, u32);
register_bits!(icceoir, cpuid, u8, 10, 12);
register_bits!(icceoir, eoiintid, u32, 0, 9);
register!(iccrpr, ICCRPR, RW, u32);
register_bits!(iccrpr, priority, u8, 0, 7);
register!(icchpir, ICCHPIR, RW, u32);
register_bits!(icchpir, cpuid, u8, 10, 12);
register_bits!(icchpir, pendintid, u32, 0, 9);
register!(iccabpr, ICCABPR, RW, u32);
register_bits!(iccabpr, binary_point, u8, 0, 2);
register!(iccidr, ICCIDR, RO, u32);
register_bits!(iccidr, part_number, u32, 20, 31);
register_bits!(iccidr, architecture_version, u8, 16, 19);
register_bits!(iccidr, revision_number, u8, 12, 15);
register_bits!(iccidr, implementer, u32, 0, 11);
register!(global_timer_control, GlobalTimerControl, RW, u32); register!(global_timer_control, GlobalTimerControl, RW, u32);
register_bits!(global_timer_control, prescaler, u8, 8, 15); register_bits!(global_timer_control, prescaler, u8, 8, 15);
@ -97,3 +264,58 @@ register_bit!(global_timer_control, auto_increment_mode, 3);
register_bit!(global_timer_control, irq_enable, 2); register_bit!(global_timer_control, irq_enable, 2);
register_bit!(global_timer_control, comp_enablea, 1); register_bit!(global_timer_control, comp_enablea, 1);
register_bit!(global_timer_control, timer_enable, 0); register_bit!(global_timer_control, timer_enable, 0);
register!(global_timer_interrupt_status, GlobalTimerInterruptStatusRegister, RW, u32);
register_bit!(global_timer_interrupt_status, event_flag, 0);
register!(private_timer_control, PrivateTimerControlRegister, RW, u32);
register_bits!(private_timer_control, sbzp, u32, 16, 31);
register_bits!(private_timer_control, prescaler, u8, 8, 15);
register_bits!(private_timer_control, unk_sbzp, u8, 3, 7);
register_bit!(private_timer_control, irq_enable, 2);
register_bit!(private_timer_control, auto_reload, 1);
register_bit!(private_timer_control, timer_enable, 0);
register!(private_timer_interrupt_status, PrivateTimerInterruptStatusRegister, RW, u32);
register_bits!(private_timer_interrupt_status, unk_sbzp, u32, 1, 31);
register!(watchdog_control, WatchdogControlRegister, RW, u32);
register_bits!(watchdog_control, prescaler, u8, 8, 15);
register_bit!(watchdog_control, watchdog_mode, 3);
register_bit!(watchdog_control, it_enable, 2);
register_bit!(watchdog_control, auto_reload, 1);
register_bit!(watchdog_control, watchdog_enable, 0);
register!(watchdog_interrupt_status, WatchdogInterruptStatusRegister, RW, u32);
register_bit!(watchdog_interrupt_status, event_flag, 0);
register!(watchdog_reset_status, WatchdogResetStatusRegister, RW, u32);
register_bit!(watchdog_reset_status, reset_flag, 0);
register!(icddcr, ICDDCR, RW, u32);
register_bit!(icddcr, enable_non_secure, 1);
register_bit!(icddcr, enable_secure, 0);
register!(icdictr, ICDICTR, RO, u32);
register_bits!(icdictr, lspi, u8, 11, 15);
register_bit!(icdictr, security_extn, 10);
register_bits!(icdictr, sbz, u8, 8, 9);
register_bits!(icdictr, cpu_number, u8, 5, 7);
register_bits!(icdictr, it_lines_number, u8, 0, 4);
register!(icdiidr, ICDIIDR, RO, u32);
register_bits!(icdiidr, implementation_version, u8, 24, 31);
register_bits!(icdiidr, revision_number, u32, 12, 23);
register_bits!(icdiidr, implementer, u32, 0, 11);
register!(ppi_status, PpiStatus, RO, u32);
register_bits!(ppi_status, ppi_status, u8, 11, 15);
register_bits!(ppi_status, sbz, u32, 0, 10);
register!(icdsgir, ICDSGIR, RW, u32);
register_bits!(icdsgir, target_list_filter, u8, 24, 25);
register_bits!(icdsgir, cpu_target_list, u8, 16, 23);
register_bit!(icdsgir, satt, 15);
register_bits!(icdsgir, sbz, u32, 4, 14);
register_bits!(icdsgir, sgiintid, u8, 0, 3);

View File

@ -0,0 +1,108 @@
use crate::println;
#[cfg(feature = "target_zc706")]
mod zc706;
#[cfg(not(feature = "target_zc706"))]
mod none;
#[cfg(feature = "target_zc706")]
use zc706 as target;
#[cfg(not(feature = "target_zc706"))]
use none as target;
pub fn report_differences() {
for (i, op) in target::INIT_DATA.iter().enumerate() {
let address = op.address();
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
.any(|later_op| later_op.address() == address);
if !overwritten_later {
op.report_difference();
}
}
}
pub fn apply() {
for op in target::INIT_DATA {
op.apply();
}
}
#[derive(Clone, Debug)]
pub enum InitOp {
MaskWrite(usize, usize, usize),
MaskPoll(usize, usize),
MaskDelay(usize, usize),
}
impl InitOp {
fn address(&self) -> usize {
match self {
InitOp::MaskWrite(address, _, _) => *address,
InitOp::MaskPoll(address, _) => *address,
InitOp::MaskDelay(address, _) => *address,
}
}
fn read(&self) -> usize {
unsafe { *(self.address() as *const usize) }
}
fn difference(&self) -> Option<(usize, usize)> {
let expected = match self {
InitOp::MaskWrite(_, mask, expected) =>
Some((*mask, *expected)),
InitOp::MaskPoll(_, mask) =>
Some((*mask, *mask)),
_ => None,
};
match expected {
Some((mask, expected)) => {
let actual = self.read();
if actual & mask == expected {
None
} else {
Some((actual & mask, expected))
}
}
None =>
None
}
}
pub fn report_difference(&self) {
if let Some((actual, expected)) = self.difference() {
println!(
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
self.address(),
self.read(),
actual,
expected
);
}
}
pub fn apply(&self) {
let reg = self.address() as *mut usize;
println!("apply {:?}", self);
match self {
InitOp::MaskWrite(_, mask, val) =>
unsafe {
*reg = (val & mask) | (*reg & !mask);
},
InitOp::MaskPoll(_, mask) =>
while unsafe { *reg } & mask == 0 {},
InitOp::MaskDelay(_, mask) => {
let delay = get_number_of_cycles_for_delay(*mask);
while unsafe { *reg } < delay {
println!("W");
}
}
}
}
}
fn get_number_of_cycles_for_delay(delay: usize) -> usize {
const APU_FREQ: usize = 666666687;
APU_FREQ * delay/ (2 * 1000)
}

View File

@ -0,0 +1,4 @@
use super::InitOp;
pub const INIT_DATA: &'static [InitOp] = &[
];

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/// ADMA library /// ADMA library
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use super::SDIO; use super::Sdio;
use libcortex_a9::cache; use libcortex_a9::cache;
use libregister::{ use libregister::{
register, register_bit, register, register_bit,
@ -32,7 +32,7 @@ impl Adma2DescTable {
} }
/// Initialize the table and setup `adma_system_address` /// Initialize the table and setup `adma_system_address`
pub fn setup(&mut self, sdio: &mut SDIO, blk_cnt: u32, buffer: &[u8]) { pub fn setup(&mut self, sdio: &mut Sdio, blk_cnt: u32, buffer: &[u8]) {
let descr_table = &mut self.0; let descr_table = &mut self.0;
let blk_size = sdio let blk_size = sdio
.regs .regs

View File

@ -12,9 +12,9 @@ use log::{trace, debug};
use nb; use nb;
/// Basic SDIO Struct with common low-level functions. /// Basic SDIO Struct with common low-level functions.
pub struct SDIO { pub struct Sdio {
regs: &'static mut regs::RegisterBlock, regs: &'static mut regs::RegisterBlock,
count_down: super::timer::global::CountDown, count_down: super::timer::global::CountDown<Milliseconds>,
input_clk_hz: u32, input_clk_hz: u32,
card_type: CardType, card_type: CardType,
card_detect: bool, card_detect: bool,
@ -48,7 +48,7 @@ pub enum CardType {
CardMmc, CardMmc,
} }
impl SDIO { impl Sdio {
/// Initialize SDIO0 /// Initialize SDIO0
/// card_detect means if we would use the card detect pin, /// card_detect means if we would use the card detect pin,
/// false to disable card detection (assume there is card inserted) /// false to disable card detection (assume there is card inserted)
@ -105,7 +105,7 @@ impl SDIO {
); );
} }
// cora card detect pin // cora card detect pin
#[cfg(feature = "target_cora_z7_10")] #[cfg(feature = "target_coraz7")]
{ {
unsafe { unsafe {
slcr.sd0_wp_cd_sel.write(47 << 16); slcr.sd0_wp_cd_sel.write(47 << 16);
@ -116,12 +116,38 @@ impl SDIO {
.speed(true), .speed(true),
); );
} }
// kasli_soc and redpitaya card detect pin
#[cfg(any(feature = "target_kasli_soc", feature = "target_redpitaya"))]
{
unsafe {
slcr.sd0_wp_cd_sel.write(46 << 16);
}
slcr.mio_pin_46.write(
slcr::MioPin46::zeroed()
.io_type(slcr::IoBufferType::Lvcmos25)
.speed(true),
);
}
// ebaz4205 card detect pin
#[cfg(feature = "target_ebaz4205")]
{
unsafe {
slcr.sd0_wp_cd_sel.write(34 << 16);
}
slcr.mio_pin_34.write(
slcr::MioPin34::zeroed()
.io_type(slcr::IoBufferType::Lvcmos33)
.pullup(true)
.speed(true),
);
}
slcr.sdio_rst_ctrl.reset_sdio0(); slcr.sdio_rst_ctrl.reset_sdio0();
slcr.aper_clk_ctrl.enable_sdio0(); slcr.aper_clk_ctrl.enable_sdio0();
slcr.sdio_clk_ctrl.enable_sdio0(); slcr.sdio_clk_ctrl.enable_sdio0();
}); });
let clocks = Clocks::get(); let clocks = Clocks::get();
let mut self_ = SDIO { let mut self_ = Sdio {
regs: regs::RegisterBlock::sdio0(), regs: regs::RegisterBlock::sdio0(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(), count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
input_clk_hz: clocks.sdio_ref_clk(), input_clk_hz: clocks.sdio_ref_clk(),

View File

@ -1,4 +1,4 @@
use super::{adma::Adma2DescTable, cmd, CardType, CmdTransferError, SDIO}; use super::{adma::Adma2DescTable, cmd, CardType, CmdTransferError, Sdio};
use libcortex_a9::cache; use libcortex_a9::cache;
use libregister::{RegisterR, RegisterRW, RegisterW}; use libregister::{RegisterR, RegisterRW, RegisterW};
use log::{trace, debug}; use log::{trace, debug};
@ -37,7 +37,7 @@ enum CardVersion {
} }
pub struct SdCard { pub struct SdCard {
sdio: SDIO, sdio: Sdio,
adma2_desc_table: Adma2DescTable, adma2_desc_table: Adma2DescTable,
card_version: CardVersion, card_version: CardVersion,
hcs: bool, hcs: bool,
@ -171,8 +171,8 @@ impl SdCard {
Ok(()) Ok(())
} }
/// Convert SDIO into SdCard struct, error if no card inserted or it is not an SD card. /// Convert Sdio into SdCard struct, error if no card inserted or it is not an SD card.
pub fn from_sdio(mut sdio: SDIO) -> Result<Self, CardInitializationError> { pub fn from_sdio(mut sdio: Sdio) -> Result<Self, CardInitializationError> {
match sdio.identify_card()? { match sdio.identify_card()? {
CardType::CardSd => (), CardType::CardSd => (),
_ => return Err(CardInitializationError::NoCardInserted), _ => return Err(CardInitializationError::NoCardInserted),
@ -192,8 +192,8 @@ impl SdCard {
Ok(_self) Ok(_self)
} }
/// Convert SdCard struct back to SDIO struct. /// Convert SdCard struct back to Sdio struct.
pub fn to_sdio(self) -> SDIO { pub fn to_sdio(self) -> Sdio {
self.sdio self.sdio
} }

View File

@ -9,9 +9,11 @@ use libregister::{
#[repr(u8)] #[repr(u8)]
pub enum PllSource { pub enum PllSource {
IoPll = 0b00, IoPll = 0b000,
ArmPll = 0b10, ArmPll = 0b010,
DdrPll = 0b11, DdrPll = 0b011,
// Ethernet controller 0 EMIO clock
Emio = 0b100,
} }
#[repr(u8)] #[repr(u8)]
@ -102,19 +104,19 @@ pub struct RegisterBlock {
pub dbg_clk_ctrl: RW<u32>, pub dbg_clk_ctrl: RW<u32>,
pub pcap_clk_ctrl: RW<u32>, pub pcap_clk_ctrl: RW<u32>,
pub topsw_clk_ctrl: RW<u32>, pub topsw_clk_ctrl: RW<u32>,
pub fpga0_clk_ctrl: RW<u32>, pub fpga0_clk_ctrl: Fpga0ClkCtrl,
pub fpga0_thr_ctrl: RW<u32>, pub fpga0_thr_ctrl: RW<u32>,
pub fpga0_thr_cnt: RW<u32>, pub fpga0_thr_cnt: RW<u32>,
pub fpga0_thr_sta: RO<u32>, pub fpga0_thr_sta: RO<u32>,
pub fpga1_clk_ctrl: RW<u32>, pub fpga1_clk_ctrl: Fpga1ClkCtrl,
pub fpga1_thr_ctrl: RW<u32>, pub fpga1_thr_ctrl: RW<u32>,
pub fpga1_thr_cnt: RW<u32>, pub fpga1_thr_cnt: RW<u32>,
pub fpga1_thr_sta: RO<u32>, pub fpga1_thr_sta: RO<u32>,
pub fpga2_clk_ctrl: RW<u32>, pub fpga2_clk_ctrl: Fpga2ClkCtrl,
pub fpga2_thr_ctrl: RW<u32>, pub fpga2_thr_ctrl: RW<u32>,
pub fpga2_thr_cnt: RW<u32>, pub fpga2_thr_cnt: RW<u32>,
pub fpga2_thr_sta: RO<u32>, pub fpga2_thr_sta: RO<u32>,
pub fpga3_clk_ctrl: RW<u32>, pub fpga3_clk_ctrl: Fpga3ClkCtrl,
pub fpga3_thr_ctrl: RW<u32>, pub fpga3_thr_ctrl: RW<u32>,
pub fpga3_thr_cnt: RW<u32>, pub fpga3_thr_cnt: RW<u32>,
pub fpga3_thr_sta: RO<u32>, pub fpga3_thr_sta: RO<u32>,
@ -132,7 +134,7 @@ pub struct RegisterBlock {
pub can_rst_ctrl: RW<u32>, pub can_rst_ctrl: RW<u32>,
pub i2c_rst_ctrl: RW<u32>, pub i2c_rst_ctrl: RW<u32>,
pub uart_rst_ctrl: UartRstCtrl, pub uart_rst_ctrl: UartRstCtrl,
pub gpio_rst_ctrl: RW<u32>, pub gpio_rst_ctrl: GpioRstCtrl,
pub lqspi_rst_ctrl: LqspiRstCtrl, pub lqspi_rst_ctrl: LqspiRstCtrl,
pub smc_rst_ctrl: RW<u32>, pub smc_rst_ctrl: RW<u32>,
pub ocm_rst_ctrl: RW<u32>, pub ocm_rst_ctrl: RW<u32>,
@ -253,26 +255,18 @@ pub struct RegisterBlock {
pub ddriob_dci_ctrl: DdriobDciCtrl, pub ddriob_dci_ctrl: DdriobDciCtrl,
pub ddriob_dci_status: DdriobDciStatus, pub ddriob_dci_status: DdriobDciStatus,
} }
register_at!(RegisterBlock, 0xF8000000, new); register_at!(RegisterBlock, 0xF8000000, slcr);
impl RegisterBlock { impl RegisterBlock {
/// Required to modify any sclr register /// Required to modify any sclr register
pub fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R { pub fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
let mut self_ = Self::new(); let mut self_ = Self::slcr();
self_.slcr_unlock.unlock(); self_.slcr_unlock.unlock();
let r = f(&mut self_); let r = f(&mut self_);
self_.slcr_lock.lock(); self_.slcr_lock.lock();
r r
} }
/// Perform a soft reset
pub fn soft_reset(&mut self) {
self.pss_rst_ctrl.write(
PssRstCtrl::zeroed()
.soft_rst(true)
);
}
pub fn init_preload_fpga(&mut self) { pub fn init_preload_fpga(&mut self) {
// Assert FPGA top level output resets // Assert FPGA top level output resets
self.fpga_rst_ctrl.write( self.fpga_rst_ctrl.write(
@ -539,6 +533,20 @@ impl UartRstCtrl {
} }
} }
register!(gpio_rst_ctrl, GpioRstCtrl, RW, u32);
register_bit!(gpio_rst_ctrl, gpio_cpu1x_rst, 0);
register_at!(GpioRstCtrl, 0xF800022C, new);
impl GpioRstCtrl {
pub fn reset_gpio(&mut self) {
self.modify(|_, w|
w.gpio_cpu1x_rst(true)
);
self.modify(|_, w|
w.gpio_cpu1x_rst(false)
);
}
}
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32); register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
register_bit!(lqspi_clk_ctrl, clkact, 0); register_bit!(lqspi_clk_ctrl, clkact, 0);
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5); register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
@ -548,6 +556,26 @@ register!(lqspi_rst_ctrl, LqspiRstCtrl, RW, u32);
register_bit!(lqspi_rst_ctrl, ref_rst, 1); register_bit!(lqspi_rst_ctrl, ref_rst, 1);
register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0); register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0);
register!(fpga0_clk_ctrl, Fpga0ClkCtrl, RW, u32);
register_bits!(fpga0_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga0_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga0_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga1_clk_ctrl, Fpga1ClkCtrl, RW, u32);
register_bits!(fpga1_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga1_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga1_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga2_clk_ctrl, Fpga2ClkCtrl, RW, u32);
register_bits!(fpga2_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga2_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga2_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga3_clk_ctrl, Fpga3ClkCtrl, RW, u32);
register_bits!(fpga3_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga3_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga3_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga_rst_ctrl, FpgaRstCtrl, RW, u32); register!(fpga_rst_ctrl, FpgaRstCtrl, RW, u32);
register_bit!(fpga_rst_ctrl, fpga0_out_rst, 0); register_bit!(fpga_rst_ctrl, fpga0_out_rst, 0);
register_bit!(fpga_rst_ctrl, fpga1_out_rst, 1); register_bit!(fpga_rst_ctrl, fpga1_out_rst, 1);
@ -561,22 +589,36 @@ register_bit!(a9_cpu_rst_ctrl, a9_clkstop0, 4);
register_bit!(a9_cpu_rst_ctrl, a9_rst1, 1); register_bit!(a9_cpu_rst_ctrl, a9_rst1, 1);
register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0); register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0);
pub fn reboot() {
RegisterBlock::unlocked(|slcr| {
unsafe {
let reboot = slcr.reboot_status.read();
slcr.reboot_status.write(reboot & 0xF0FFFFFF);
slcr.pss_rst_ctrl.modify(|_, w| w.soft_rst(true));
}
});
}
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum BootModePins { pub enum BootModePins {
// CAUTION!
// The BOOT_MODE bits table 6-4 in UG585 are *out of order*.
Jtag = 0b000, Jtag = 0b000,
Nor = 0b001, Nor = 0b010,
Nand = 0b010, Nand = 0b100,
QuadSpi = 0b100, QuadSpi = 0b001,
SdCard = 0b110, SdCard = 0b101,
} }
register!(boot_mode, BootMode, RO, u32); register!(boot_mode, BootMode, RO, u32);
register_bit!(boot_mode, pll_bypass, 4); register_bit!(boot_mode, pll_bypass, 4);
register_bits_typed!(boot_mode, boot_mode_pins, u8, BootModePins, 0, 3); register_bit!(boot_mode, jtag_routing, 3);
register_bits_typed!(boot_mode, boot_mode_pins, u8, BootModePins, 0, 2);
register!(pss_rst_ctrl, PssRstCtrl, RW, u32); register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
register_bit!(pss_rst_ctrl, soft_rst, 1); register_bit!(pss_rst_ctrl, soft_rst, 0);
/// Used for MioPin*.io_type /// Used for MioPin*.io_type
#[repr(u8)] #[repr(u8)]

View File

@ -1,5 +1,5 @@
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use libcortex_a9::mutex::{Mutex, MutexGuard}; use libcortex_a9::{asm, mutex::{Mutex, MutexGuard}};
use crate::uart::Uart; use crate::uart::Uart;
const UART_RATE: u32 = 115_200; const UART_RATE: u32 = 115_200;
@ -10,7 +10,15 @@ pub fn get_uart<'a>() -> MutexGuard<'a, LazyUart> {
unsafe { UART.lock() } unsafe { UART.lock() }
} }
/// Deinitialize so that the Uart will be reinitialized on next
/// output.
///
/// Delays so that an outstanding transmission can finish.
pub fn drop_uart() { pub fn drop_uart() {
for _ in 0..1_000_000 {
asm::nop();
}
unsafe { UART = Mutex::new(LazyUart::Uninitialized); } unsafe { UART = Mutex::new(LazyUart::Uninitialized); }
} }
@ -37,7 +45,14 @@ impl DerefMut for LazyUart {
fn deref_mut(&mut self) -> &mut Uart { fn deref_mut(&mut self) -> &mut Uart {
match self { match self {
LazyUart::Uninitialized => { LazyUart::Uninitialized => {
let uart = Uart::serial(UART_RATE); #[cfg(any(feature = "target_coraz7", feature = "target_redpitaya"))]
let uart = Uart::uart0(UART_RATE);
#[cfg(any(
feature = "target_zc706",
feature = "target_ebaz4205",
feature = "target_kasli_soc",
))]
let uart = Uart::uart1(UART_RATE);
*self = LazyUart::Initialized(uart); *self = LazyUart::Initialized(uart);
self self
} }

View File

@ -8,3 +8,18 @@ impl core::ops::Add for Milliseconds {
Milliseconds(self.0 + rhs.0) Milliseconds(self.0 + rhs.0)
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Microseconds(pub u64);
impl core::ops::Add for Microseconds {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Microseconds(self.0 + rhs.0)
}
}
pub trait TimeSource<U> {
fn now(&self) -> U;
}

View File

@ -1,9 +1,10 @@
use core::ops::Add;
use void::Void; use void::Void;
use libregister::{RegisterR, RegisterW}; use libregister::{RegisterR, RegisterW};
use crate::{ use crate::{
clocks::Clocks, clocks::Clocks,
mpcore, mpcore,
time::Milliseconds, time::{Milliseconds, Microseconds, TimeSource},
}; };
/// "uptime" /// "uptime"
@ -15,13 +16,13 @@ pub struct GlobalTimer {
impl GlobalTimer { impl GlobalTimer {
/// Get the potentially uninitialized timer /// Get the potentially uninitialized timer
pub unsafe fn get() -> GlobalTimer { pub unsafe fn get() -> GlobalTimer {
let regs = mpcore::RegisterBlock::new(); let regs = mpcore::RegisterBlock::mpcore();
GlobalTimer { regs } GlobalTimer { regs }
} }
/// Get the timer with a reset /// Get the timer with a reset
pub fn start() -> GlobalTimer { pub fn start() -> GlobalTimer {
let mut regs = mpcore::RegisterBlock::new(); let mut regs = mpcore::RegisterBlock::mpcore();
Self::reset(&mut regs); Self::reset(&mut regs);
GlobalTimer { regs } GlobalTimer { regs }
} }
@ -79,41 +80,91 @@ impl GlobalTimer {
} }
/// read with high precision /// read with high precision
pub fn get_us(&self) -> u64 { pub fn get_us(&self) -> Microseconds {
let prescaler = self.regs.global_timer_control.read().prescaler() as u64; let prescaler = self.regs.global_timer_control.read().prescaler() as u64;
let clocks = Clocks::get(); let clocks = Clocks::get();
1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64 Microseconds(1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64)
} }
/// return a handle that has implements /// return a handle that has implements
/// `embedded_hal::timer::CountDown` /// `embedded_hal::timer::CountDown`
pub fn countdown(&self) -> CountDown { pub fn countdown<U>(&self) -> CountDown<U>
where
Self: TimeSource<U>,
{
CountDown { CountDown {
timer: self.clone(), timer: self.clone(),
timeout: Milliseconds(0), timeout: self.now(),
} }
} }
} }
impl TimeSource<Milliseconds> for GlobalTimer {
fn now(&self) -> Milliseconds {
self.get_time()
}
}
impl TimeSource<Microseconds> for GlobalTimer {
fn now(&self) -> Microseconds {
self.get_us()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct CountDown { pub struct CountDown<U> {
timer: GlobalTimer, timer: GlobalTimer,
timeout: Milliseconds, timeout: U,
} }
impl embedded_hal::timer::CountDown for CountDown { /// embedded-hal async API
type Time = Milliseconds; impl<U: Add<Output=U> + PartialOrd> embedded_hal::timer::CountDown for CountDown<U>
where
GlobalTimer: TimeSource<U>,
{
type Time = U;
fn start<T: Into<Self::Time>>(&mut self, count: T) { fn start<T: Into<Self::Time>>(&mut self, count: T) {
self.timeout = self.timer.get_time() + count.into(); self.timeout = self.timer.now() + count.into();
} }
fn wait(&mut self) -> nb::Result<(), Void> { fn wait(&mut self) -> nb::Result<(), Void> {
if self.timer.get_time() < self.timeout { if self.timer.now() <= self.timeout {
Err(nb::Error::WouldBlock) Err(nb::Error::WouldBlock)
} else { } else {
Ok(()) Ok(())
} }
} }
} }
impl<U: PartialOrd> CountDown<U>
where
GlobalTimer: TimeSource<U>,
{
pub fn waiting(&self) -> bool {
self.timer.now() <= self.timeout
}
}
/// embedded-hal sync API
impl embedded_hal::blocking::delay::DelayMs<u64> for GlobalTimer {
fn delay_ms(&mut self, ms: u64) {
use embedded_hal::timer::CountDown;
let mut countdown = self.countdown::<Milliseconds>();
countdown.start(Milliseconds(ms));
nb::block!(countdown.wait()).unwrap();
}
}
/// embedded-hal sync API
impl embedded_hal::blocking::delay::DelayUs<u64> for GlobalTimer {
fn delay_us(&mut self, us: u64) {
use embedded_hal::timer::CountDown;
let mut countdown = self.countdown::<Microseconds>();
countdown.start(Microseconds(us));
nb::block!(countdown.wait()).unwrap();
}
}

View File

@ -13,31 +13,8 @@ pub struct Uart {
} }
impl Uart { impl Uart {
#[cfg(feature = "target_zc706")] #[cfg(any(feature = "target_coraz7", feature = "target_redpitaya"))]
pub fn serial(baudrate: u32) -> Self { pub fn uart0(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Route UART 1 RxD/TxD Signals to MIO Pins
// TX pin
slcr.mio_pin_48.write(
slcr::MioPin48::zeroed()
.l3_sel(0b111)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// RX pin
slcr.mio_pin_49.write(
slcr::MioPin49::zeroed()
.tri_enable(true)
.l3_sel(0b111)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
});
Self::uart1(baudrate)
}
#[cfg(feature = "target_cora_z7_10")]
pub fn serial(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| { slcr::RegisterBlock::unlocked(|slcr| {
// Route UART 0 RxD/TxD Signals to MIO Pins // Route UART 0 RxD/TxD Signals to MIO Pins
// TX pin // TX pin
@ -56,10 +33,7 @@ impl Uart {
.pullup(true) .pullup(true)
); );
}); });
Self::uart0(baudrate)
}
pub fn uart0(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| { slcr::RegisterBlock::unlocked(|slcr| {
slcr.uart_rst_ctrl.reset_uart0(); slcr.uart_rst_ctrl.reset_uart0();
slcr.aper_clk_ctrl.enable_uart0(); slcr.aper_clk_ctrl.enable_uart0();
@ -72,7 +46,60 @@ impl Uart {
self_ self_
} }
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
pub fn uart1(baudrate: u32) -> Self { pub fn uart1(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Route UART 1 RxD/TxD Signals to MIO Pins
// TX pin
slcr.mio_pin_48.write(
slcr::MioPin48::zeroed()
.l3_sel(0b111)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// RX pin
slcr.mio_pin_49.write(
slcr::MioPin49::zeroed()
.tri_enable(true)
.l3_sel(0b111)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
});
slcr::RegisterBlock::unlocked(|slcr| {
slcr.uart_rst_ctrl.reset_uart1();
slcr.aper_clk_ctrl.enable_uart1();
slcr.uart_clk_ctrl.enable_uart1();
});
let mut self_ = Uart {
regs: regs::RegisterBlock::uart1(),
};
self_.configure(baudrate);
self_
}
#[cfg(feature = "target_ebaz4205")]
pub fn uart1(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Route UART 1 RxD/TxD Signals to MIO Pins
// TX pin
slcr.mio_pin_24.write(
slcr::MioPin24::zeroed()
.l3_sel(0b111)
.io_type(slcr::IoBufferType::Lvcmos33)
.pullup(true)
);
// RX pin
slcr.mio_pin_25.write(
slcr::MioPin25::zeroed()
.tri_enable(true)
.l3_sel(0b111)
.io_type(slcr::IoBufferType::Lvcmos33)
.pullup(true)
);
});
slcr::RegisterBlock::unlocked(|slcr| { slcr::RegisterBlock::unlocked(|slcr| {
slcr.uart_rst_ctrl.reset_uart1(); slcr.uart_rst_ctrl.reset_uart1();
slcr.aper_clk_ctrl.enable_uart1(); slcr.aper_clk_ctrl.enable_uart1();

20
libconfig/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "libconfig"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
[dependencies]
libboard_zynq = { path = "../libboard_zynq" }
core_io = { version = "0.1", features = ["collections"] }
fatfs = { version = "0.3", features = ["core_io"], default-features = false }
log = "0.4"
[features]
target_zc706 = []
target_coraz7 = []
target_ebaz4205 = []
target_redpitaya = []
target_kasli_soc = []
ipv6 = []
fat_lfn = [ "fatfs/alloc" ]

181
libconfig/src/bootgen.rs Normal file
View File

@ -0,0 +1,181 @@
use alloc::vec::Vec;
use core_io::{Error, Read, Seek, SeekFrom};
use libboard_zynq::devc;
use log::debug;
#[derive(Debug)]
pub enum BootgenLoadingError {
InvalidBootImageHeader,
MissingPartition,
EncryptedBitstream,
IoError(Error),
DevcError(devc::DevcError),
}
impl From<Error> for BootgenLoadingError {
fn from(error: Error) -> Self {
BootgenLoadingError::IoError(error)
}
}
impl From<devc::DevcError> for BootgenLoadingError {
fn from(error: devc::DevcError) -> Self {
BootgenLoadingError::DevcError(error)
}
}
impl core::fmt::Display for BootgenLoadingError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use BootgenLoadingError::*;
match self {
InvalidBootImageHeader => write!(
f,
"Invalid boot image header. Check if the file is correct."
),
MissingPartition => write!(f, "Partition not found. Check your compile configuration."),
EncryptedBitstream => write!(f, "Encrypted bitstream is not supported."),
IoError(e) => write!(f, "Error while reading: {}", e),
DevcError(e) => write!(f, "PCAP interface error: {}", e),
}
}
}
#[repr(C)]
struct PartitionHeader {
pub encrypted_length: u32,
pub unencrypted_length: u32,
pub word_length: u32,
pub dest_load_addr: u32,
pub dest_exec_addr: u32,
pub data_offset: u32,
pub attribute_bits: u32,
pub section_count: u32,
pub checksum_offset: u32,
pub header_offset: u32,
pub cert_offset: u32,
pub reserved: [u32; 4],
pub checksum: u32,
}
/// Read a u32 word from the reader.
fn read_u32<Reader: Read>(reader: &mut Reader) -> Result<u32, BootgenLoadingError> {
let mut buffer: [u8; 4] = [0; 4];
reader.read_exact(&mut buffer)?;
let mut result: u32 = 0;
for i in 0..4 {
result |= (buffer[i] as u32) << (i * 8);
}
Ok(result)
}
/// Load PL partition header.
fn load_pl_header<File: Read + Seek>(
file: &mut File,
) -> Result<Option<PartitionHeader>, BootgenLoadingError> {
let mut buffer: [u8; 0x40] = [0; 0x40];
file.read_exact(&mut buffer)?;
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
if header.attribute_bits & (2 << 4) != 0 {
Ok(Some(header))
} else {
Ok(None)
}
}
fn load_ps_header<File: Read + Seek>(
file: &mut File,
) -> Result<Option<PartitionHeader>, BootgenLoadingError> {
let mut buffer: [u8; 0x40] = [0; 0x40];
file.read_exact(&mut buffer)?;
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
if header.attribute_bits & (1 << 4) != 0 {
Ok(Some(header))
} else {
Ok(None)
}
}
/// Locate the partition from the image, and return the size (in bytes) of the partition if successful.
/// This function would seek the file to the location of the partition.
fn locate<
File: Read + Seek,
F: Fn(&mut File) -> Result<Option<PartitionHeader>, BootgenLoadingError>,
>(
file: &mut File,
f: F,
) -> Result<usize, BootgenLoadingError> {
file.seek(SeekFrom::Start(0))?;
const BOOT_HEADER_SIGN: u32 = 0x584C4E58;
// read boot header signature
file.seek(SeekFrom::Start(0x24))?;
if read_u32(file)? != BOOT_HEADER_SIGN {
return Err(BootgenLoadingError::InvalidBootImageHeader);
}
// find fsbl offset
file.seek(SeekFrom::Start(0x30))?;
// the length is in bytes, we have to convert it to words to compare with the partition offset
// later
let fsbl = read_u32(file)? / 4;
// read partition header offset
file.seek(SeekFrom::Start(0x9C))?;
let ptr = read_u32(file)?;
debug!("Partition header pointer = {:0X}", ptr);
file.seek(SeekFrom::Start(ptr as u64))?;
// at most 3 partition headers
for _ in 0..3 {
if let Some(header) = f(file)? {
let encrypted_length = header.encrypted_length;
let unencrypted_length = header.unencrypted_length;
debug!("Unencrypted length = {:0X}", unencrypted_length);
if encrypted_length != unencrypted_length {
return Err(BootgenLoadingError::EncryptedBitstream);
}
let start_addr = header.data_offset;
// skip fsbl
if start_addr == fsbl {
continue;
}
debug!("Partition start address: {:0X}", start_addr);
file.seek(SeekFrom::Start(start_addr as u64 * 4))?;
return Ok(unencrypted_length as usize * 4);
}
}
Err(BootgenLoadingError::MissingPartition)
}
/// Load bitstream from bootgen file.
/// This function parses the file, locate the bitstream and load it through the PCAP driver.
/// It requires a large buffer, please enable the DDR RAM before using it.
pub fn load_bitstream<File: Read + Seek>(file: &mut File) -> Result<(), BootgenLoadingError> {
let size = locate(file, load_pl_header)?;
unsafe {
// align to 64 bytes
let ptr = alloc::alloc::alloc(alloc::alloc::Layout::from_size_align(size, 64).unwrap());
let buffer = core::slice::from_raw_parts_mut(ptr, size);
file.read_exact(buffer).map_err(|e| {
core::ptr::drop_in_place(ptr);
e
})?;
let mut devcfg = devc::DevC::new();
devcfg.enable();
devcfg.program(&buffer).map_err(|e| {
core::ptr::drop_in_place(ptr);
e
})?;
core::ptr::drop_in_place(ptr);
Ok(())
}
}
pub fn get_runtime<File: Read + Seek>(file: &mut File) -> Result<Vec<u8>, BootgenLoadingError> {
let size = locate(file, load_ps_header)?;
let mut buffer = Vec::with_capacity(size);
unsafe {
buffer.set_len(size);
}
file.read_exact(&mut buffer)?;
Ok(buffer)
}

174
libconfig/src/lib.rs Normal file
View File

@ -0,0 +1,174 @@
#![no_std]
extern crate alloc;
use core::fmt;
use alloc::{string::FromUtf8Error, string::String, vec::Vec, rc::Rc};
use core_io::{self as io, BufRead, BufReader, Read, Write, Seek, SeekFrom};
use libboard_zynq::sdio;
pub mod sd_reader;
pub mod net_settings;
pub mod bootgen;
#[derive(Debug)]
pub enum Error<'a> {
SdError(sdio::sd_card::CardInitializationError),
IoError(io::Error),
Utf8Error(FromUtf8Error),
KeyNotFoundError(&'a str),
NoConfig,
}
pub type Result<'a, T> = core::result::Result<T, Error<'a>>;
impl<'a> fmt::Display for Error<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::SdError(error) => write!(f, "SD error: {}", error),
Error::IoError(error) => write!(f, "I/O error: {}", error),
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
Error::KeyNotFoundError(name) => write!(f, "Configuration key `{}` not found", name),
Error::NoConfig => write!(f, "Configuration not present"),
}
}
}
impl<'a> From<sdio::sd_card::CardInitializationError> for Error<'a> {
fn from(error: sdio::sd_card::CardInitializationError) -> Self {
Error::SdError(error)
}
}
impl<'a> From<io::Error> for Error<'a> {
fn from(error: io::Error) -> Self {
Error::IoError(error)
}
}
impl<'a> From<FromUtf8Error> for Error<'a> {
fn from(error: FromUtf8Error) -> Self {
Error::Utf8Error(error)
}
}
fn parse_config<'a>(
key: &'a str,
buffer: &mut Vec<u8>,
file: fatfs::File<sd_reader::SdReader>,
) -> Result<'a, ()> {
let prefix = [key, "="].concat().to_ascii_lowercase();
for line in BufReader::new(file).lines() {
let line = line?.to_ascii_lowercase();
if line.starts_with(&prefix) {
buffer.extend(line[prefix.len()..].as_bytes());
return Ok(());
}
}
Err(Error::KeyNotFoundError(key))
}
pub struct Config {
fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>,
}
const NEWLINE: &[u8] = b"\n";
impl Config {
pub fn new() -> Result<'static, Self> {
let sdio = sdio::Sdio::sdio0(true);
if !sdio.is_card_inserted() {
Err(sdio::sd_card::CardInitializationError::NoCardInserted)?;
}
let sd = sdio::sd_card::SdCard::from_sdio(sdio)?;
let reader = sd_reader::SdReader::new(sd);
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
Ok(Config { fs: Some(Rc::new(fs)) })
}
pub fn from_fs(fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>) -> Self {
Config { fs }
}
pub fn new_dummy() -> Self {
Config { fs: None }
}
pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec<u8>> {
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
let mut buffer: Vec<u8> = Vec::new();
match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) {
Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?,
Err(_) => match root_dir.open_file("/CONFIG.TXT") {
Ok(f) => parse_config(key, &mut buffer, f)?,
Err(_) => return Err(Error::KeyNotFoundError(key)),
},
};
Ok(buffer)
} else {
Err(Error::NoConfig)
}
}
pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> {
Ok(String::from_utf8(self.read(key)?)?)
}
pub fn remove<'b>(&self, key: &'b str) -> Result<'b, ()> {
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
match root_dir.remove(&["/CONFIG/", key, ".BIN"].concat()) {
Ok(()) => Ok(()),
Err(_) => {
let prefix = [key, "="].concat().to_ascii_lowercase();
match root_dir.create_file("/CONFIG.TXT") {
Ok(mut f) => {
let mut buffer = String::new();
f.read_to_string(&mut buffer)?;
f.seek(SeekFrom::Start(0))?;
f.truncate()?;
for line in buffer.lines() {
if line.len() > 0 && !line.to_ascii_lowercase().starts_with(&prefix) {
f.write(line.as_bytes())?;
f.write(NEWLINE)?;
}
}
Ok(())
},
Err(_) => Err(Error::KeyNotFoundError(key))
}
}
}
} else {
Err(Error::NoConfig)
}
}
pub fn write<'b>(&self, key: &'b str, value: Vec<u8>) -> Result<'b, ()> {
if self.fs.is_none() {
return Err(Error::NoConfig);
}
let fs = self.fs.as_ref().unwrap();
let root_dir = fs.root_dir();
let is_str = value.len() <= 100 && value.is_ascii() && !value.contains(&b'\n');
if key == "boot" {
let mut f = root_dir.create_file("/BOOT.BIN")?;
f.truncate()?;
f.write_all(&value)?;
drop(f);
} else {
let _ = self.remove(key);
if is_str {
let mut f = root_dir.create_file("/CONFIG.TXT")?;
f.seek(SeekFrom::End(0))?;
write!(f, "{}={}\n", key, String::from_utf8(value).unwrap())?;
} else {
let dir = root_dir.create_dir("/CONFIG")?;
let mut f = dir.create_file(&[key, ".BIN"].concat())?;
f.write_all(&value)?;
}
}
Ok(())
}
}

View File

@ -0,0 +1,92 @@
use core::fmt;
use libboard_zynq::smoltcp::wire::{EthernetAddress, IpAddress};
use super::Config;
pub struct NetAddresses {
pub hardware_addr: EthernetAddress,
pub ipv4_addr: IpAddress,
#[cfg(feature = "ipv6")]
pub ipv6_ll_addr: IpAddress,
#[cfg(feature = "ipv6")]
pub ipv6_addr: Option<IpAddress>
}
impl fmt::Display for NetAddresses {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAC={} IPv4={} ",
self.hardware_addr, self.ipv4_addr)?;
#[cfg(feature = "ipv6")]
{
write!(f, "IPv6-LL={}", self.ipv6_ll_addr)?;
match self.ipv6_addr {
Some(addr) => write!(f, " {}", addr)?,
None => write!(f, " IPv6: no configured address")?
}
}
Ok(())
}
}
#[cfg(feature = "target_kasli_soc")]
fn get_address_from_eeprom() -> EthernetAddress {
use libboard_zynq::i2c::{I2c, eeprom};
let mut i2c = I2c::i2c0();
i2c.init().unwrap();
let mut eeprom = eeprom::EEPROM::new(&mut i2c, 16);
let address = eeprom.read_eui48().unwrap_or([0x02, 0x00, 0x00, 0x00, 0x00, 0x56]);
EthernetAddress(address)
}
pub fn get_addresses(cfg: &Config) -> NetAddresses {
#[cfg(feature = "target_zc706")]
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x52]);
#[cfg(feature = "target_zc706")]
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 52);
#[cfg(feature = "target_coraz7")]
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x54]);
#[cfg(feature = "target_coraz7")]
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 54);
#[cfg(feature = "target_redpitaya")]
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x55]);
#[cfg(feature = "target_redpitaya")]
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 55);
#[cfg(feature = "target_kasli_soc")]
let mut hardware_addr = get_address_from_eeprom();
#[cfg(feature = "target_kasli_soc")]
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 56);
#[cfg(feature = "target_ebaz4205")]
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x57]);
#[cfg(feature = "target_ebaz4205")]
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 57);
if let Ok(Ok(addr)) = cfg.read_str("mac").map(|s| s.parse()) {
hardware_addr = addr;
}
if let Ok(Ok(addr)) = cfg.read_str("ip").map(|s| s.parse()) {
ipv4_addr = addr;
}
#[cfg(feature = "ipv6")]
let ipv6_addr = cfg.read_str("ip6").ok().and_then(|s| s.parse().ok());
#[cfg(feature = "ipv6")]
let ipv6_ll_addr = IpAddress::v6(
0xfe80, 0x0000, 0x0000, 0x0000,
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16),
((hardware_addr.0[2] as u16) << 8) | 0x00ff,
0xfe00 | (hardware_addr.0[3] as u16),
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16));
NetAddresses {
hardware_addr,
ipv4_addr,
#[cfg(feature = "ipv6")]
ipv6_ll_addr,
#[cfg(feature = "ipv6")]
ipv6_addr
}
}

304
libconfig/src/sd_reader.rs Normal file
View File

@ -0,0 +1,304 @@
use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write};
use fatfs;
use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError};
use log::debug;
use alloc::vec::Vec;
const MBR_SIGNATURE: [u8; 2] = [0x55, 0xAA];
const PARTID_FAT12: u8 = 0x01;
const PARTID_FAT16_LESS32M: u8 = 0x04;
const PARTID_FAT16: u8 = 0x06;
const PARTID_FAT32: u8 = 0x0B;
const PARTID_FAT32_LBA: u8 = 0x0C;
const PARTID_FAT16_LBA: u8 = 0x0E;
fn cmd_error_to_io_error(_: CmdTransferError) -> Error {
Error::new(ErrorKind::Other, "Command transfer error")
}
const BLOCK_SIZE: usize = 512;
/// SdReader struct implementing `Read + BufRead + Write + Seek` traits for `core_io`.
/// Used as an adaptor for fatfs crate, but could be used directly for raw data access.
///
/// Implementation: all read/writes would be split into unaligned and block-aligned parts,
/// unaligned read/writes would do a buffered read/write using a block-sized internal buffer,
/// while aligned transactions would be sent to the SD card directly for performance reason.
pub struct SdReader {
/// Internal SdCard handle.
sd: SdCard,
/// Read buffer with the size of 1 block.
buffer: Vec<u8>,
/// Address for the next byte.
byte_addr: u32,
/// Internal index for the next byte.
/// Normally in range `[0, BLOCK_SIZE - 1]`.
///
/// `index = BLOCK_SIZE` means that the `buffer` is invalid for the current `byte_addr`,
/// the next `fill_buf` call would fill the buffer.
index: usize,
/// Dirty flag indicating the content has to be flushed.
dirty: bool,
/// Base offset for translation from logical address to physical address.
offset: u32,
}
#[derive(Copy, Clone)]
#[allow(unused)]
// Partition entry enum, normally we would use entry1.
pub enum PartitionEntry {
Entry1 = 0x1BE,
Entry2 = 0x1CE,
Entry3 = 0x1DE,
Entry4 = 0x1EE,
}
impl SdReader {
/// Create SdReader from SdCard
pub fn new(sd: SdCard) -> SdReader {
let mut vec: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
unsafe {
vec.set_len(vec.capacity());
}
SdReader {
sd,
buffer: vec,
byte_addr: 0,
index: BLOCK_SIZE,
dirty: false,
offset: 0,
}
}
/// Internal read function for unaligned read.
/// The read must not cross block boundary.
fn read_unaligned(&mut self, buf: &mut [u8]) -> IoResult<usize> {
if buf.len() == 0 {
return Ok(0);
}
let filled_buffer = self.fill_buf()?;
for (dest, src) in buf.iter_mut().zip(filled_buffer.iter()) {
*dest = *src;
}
self.consume(buf.len());
Ok(buf.len())
}
/// Internal write function for unaligned write.
/// The write must not cross block boundary.
fn write_unaligned(&mut self, buf: &[u8]) -> IoResult<usize> {
if buf.len() == 0 {
return Ok(0);
}
// update buffer if needed, as we will flush the entire block later.
self.fill_buf()?;
self.dirty = true;
let dest_buffer = &mut self.buffer[self.index..];
for (src, dest) in buf.iter().zip(dest_buffer.iter_mut()) {
*dest = *src;
}
self.consume(buf.len());
Ok(buf.len())
}
/// Split the slice into three segments, with the middle block-aligned.
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
fn block_align<'b>(&self, buf: &'b [u8]) -> (&'b [u8], &'b [u8], &'b [u8]) {
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
if head_len > buf.len() {
(buf, &[], &[])
} else {
let remaining_length = buf.len() - head_len;
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
let (head, remaining) = buf.split_at(head_len);
let (mid, tail) = remaining.split_at(mid_length);
(head, mid, tail)
}
}
/// Split the mutable slice into three segments, with the middle block-aligned.
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
fn block_align_mut<'b>(&self, buf: &'b mut [u8]) -> (&'b mut [u8], &'b mut [u8], &'b mut [u8]) {
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
if head_len > buf.len() {
(buf, &mut [], &mut [])
} else {
let remaining_length = buf.len() - head_len;
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
let (head, remaining) = buf.split_at_mut(head_len);
let (mid, tail) = remaining.split_at_mut(mid_length);
(head, mid, tail)
}
}
/// Invalidate the buffer, so later unaligned read/write would reload the buffer from SD card.
fn invalidate_buffer(&mut self) {
self.index = BLOCK_SIZE;
}
/// Set the base offset of the SD card, to transform from physical address to logical address.
fn set_base_offset(&mut self, offset: u32) -> IoResult<u64> {
self.offset = offset;
self.seek(SeekFrom::Start(0))
}
/// Mount fatfs from partition entry, and return the fatfs object if success.
/// This takes the ownership of self, so currently there is no way to recover from an error,
/// except creating a new SD card instance.
pub fn mount_fatfs(mut self, entry: PartitionEntry) -> IoResult<fatfs::FileSystem<Self>> {
let mut buffer: [u8; 4] = [0; 4];
self.seek(SeekFrom::Start(0x1FE))?;
self.read_exact(&mut buffer[..2])?;
// check MBR signature
if buffer[..2] != MBR_SIGNATURE {
return Err(Error::new(
ErrorKind::InvalidData,
"Incorrect signature for MBR sector.",
));
}
// Read partition ID.
self.seek(SeekFrom::Start(entry as u64 + 0x4))?;
self.read_exact(&mut buffer[..1])?;
debug!("Partition ID: {:0X}", buffer[0]);
match buffer[0] {
PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 |
PARTID_FAT16_LBA | PARTID_FAT32 | PARTID_FAT32_LBA => {}
_ => {
return Err(Error::new(
ErrorKind::InvalidData,
"No FAT partition found for the specified entry.",
));
}
}
// Read LBA
self.seek(SeekFrom::Current(0x3))?;
self.read_exact(&mut buffer)?;
let mut lba: u32 = 0;
// Little endian
for i in 0..4 {
lba |= (buffer[i] as u32) << (i * 8);
}
// Set to logical address
self.set_base_offset(lba * BLOCK_SIZE as u32)?;
// setup fatfs
fatfs::FileSystem::new(self, fatfs::FsOptions::new())
}
}
impl Read for SdReader {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
let total_length = buf.len();
let (a, b, c) = self.block_align_mut(buf);
self.read_unaligned(a)?;
if b.len() > 0 {
// invalidate internal buffer
self.invalidate_buffer();
if let Err(_) = self.sd.read_block(
self.byte_addr / BLOCK_SIZE as u32,
(b.len() / BLOCK_SIZE) as u16,
b,
) {
// we have to allow partial read, as per the trait required
return Ok(a.len());
}
self.byte_addr += b.len() as u32;
}
if let Err(_) = self.read_unaligned(c) {
// we have to allow partial read, as per the trait required
return Ok(a.len() + b.len());
}
Ok(total_length)
}
}
impl BufRead for SdReader {
fn fill_buf(&mut self) -> IoResult<&[u8]> {
if self.index == BLOCK_SIZE {
// flush the buffer if it is dirty before overwriting it with new data
if self.dirty {
self.flush()?;
}
// reload buffer
self.sd
.read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer)
.map_err(cmd_error_to_io_error)?;
self.index = (self.byte_addr as usize) % BLOCK_SIZE;
}
Ok(&self.buffer[self.index..])
}
fn consume(&mut self, amt: usize) {
self.index += amt;
self.byte_addr += amt as u32;
}
}
impl Write for SdReader {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
let (a, b, c) = self.block_align(buf);
self.write_unaligned(a)?;
if b.len() > 0 {
self.flush()?;
self.invalidate_buffer();
if let Err(_) = self.sd.write_block(
self.byte_addr / BLOCK_SIZE as u32,
(b.len() / BLOCK_SIZE) as u16,
b,
) {
return Ok(a.len());
}
self.byte_addr += b.len() as u32;
}
if let Err(_) = self.write_unaligned(c) {
return Ok(a.len() + b.len());
}
Ok(buf.len())
}
fn flush(&mut self) -> IoResult<()> {
if self.dirty {
let block_addr = (self.byte_addr - self.index as u32) / (BLOCK_SIZE as u32);
self.sd
.write_block(block_addr, 1, &self.buffer)
.map_err(cmd_error_to_io_error)?;
self.dirty = false;
}
Ok(())
}
}
impl Seek for SdReader {
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
let raw_target = match pos {
SeekFrom::Start(x) => self.offset as i64 + x as i64,
SeekFrom::Current(x) => self.byte_addr as i64 + x,
SeekFrom::End(_) => panic!("SD card does not support seek from end"),
};
if raw_target < self.offset as i64 || raw_target > core::u32::MAX as i64 {
return Err(Error::new(ErrorKind::InvalidInput, "Invalid address"));
}
let target_byte_addr = raw_target as u32;
let address_same_block =
self.byte_addr / (BLOCK_SIZE as u32) == target_byte_addr / (BLOCK_SIZE as u32);
// if the buffer was invalidated, we consider seek as different block
let same_block = address_same_block && self.index != BLOCK_SIZE;
if !same_block {
self.flush()?;
}
self.byte_addr = target_byte_addr;
self.index = if same_block {
target_byte_addr as usize % BLOCK_SIZE
} else {
// invalidate the buffer as we moved to a different block
BLOCK_SIZE
};
Ok((self.byte_addr - self.offset) as u64)
}
}
impl Drop for SdReader {
fn drop(&mut self) {
// just try to flush it, ignore error if any
self.flush().unwrap_or(());
}
}

View File

@ -1,14 +1,14 @@
[package] [package]
name = "libcortex_a9" name = "libcortex_a9"
version = "0.0.0" version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"] authors = ["M-Labs"]
edition = "2018" edition = "2018"
[features] [features]
target_zc706 = [] power_saving = []
target_cora_z7_10 = [] default = []
default = ["target_zc706"]
[dependencies] [dependencies]
bit_field = "0.10" bit_field = "0.10"
volatile-register = "0.2"
libregister = { path = "../libregister" } libregister = { path = "../libregister" }

View File

@ -33,3 +33,50 @@ pub fn dsb() {
pub fn isb() { pub fn isb() {
unsafe { llvm_asm!("isb" :::: "volatile") } unsafe { llvm_asm!("isb" :::: "volatile") }
} }
/// Enable FIQ
#[inline]
pub unsafe fn enable_fiq() {
llvm_asm!("cpsie f":::: "volatile");
}
/// Enable IRQ
#[inline]
pub unsafe fn enable_irq() {
llvm_asm!("cpsie i":::: "volatile");
}
/// Disable IRQ, return if IRQ was originally enabled.
#[inline]
pub unsafe fn enter_critical() -> bool {
let mut cpsr: u32;
llvm_asm!(
"mrs $0, cpsr
cpsid i"
: "=r"(cpsr) ::: "volatile");
(cpsr & (1 << 7)) == 0
}
#[inline]
pub unsafe fn exit_critical(enable: bool) {
// https://stackoverflow.com/questions/40019929/temporarily-disable-interrupts-on-arm
let mask: u32 = if enable {
1 << 7
} else {
0
};
llvm_asm!(
"mrs r1, cpsr
bic r1, r1, $0
msr cpsr_c, r1"
:: "r"(mask) : "r1");
}
/// Exiting IRQ
#[inline]
pub unsafe fn exit_irq() {
llvm_asm!("
mrs r0, SPSR
msr CPSR, r0
" ::: "r0");
}

View File

@ -1,4 +1,5 @@
use super::asm::{dmb, dsb}; use super::asm::{dmb, dsb};
use super::l2c::*;
/// Invalidate TLBs /// Invalidate TLBs
#[inline(always)] #[inline(always)]
@ -44,9 +45,17 @@ pub fn dcisw(setway: u32) {
} }
} }
/// Data cache clean by set/way
#[inline(always)]
pub fn dccisw(setway: u32) {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c14, 2" :: "r" (setway) :: "volatile");
}
}
/// A made-up "instruction": invalidate all of the L1 D-Cache /// A made-up "instruction": invalidate all of the L1 D-Cache
#[inline(always)] #[inline(always)]
pub fn dciall() { pub fn dciall_l1() {
// the cache associativity could be read from a register, but will // the cache associativity could be read from a register, but will
// always be 4 in L1 data cache of a cortex a9 // always be 4 in L1 data cache of a cortex a9
let ways = 4; let ways = 4;
@ -71,6 +80,50 @@ pub fn dciall() {
} }
} }
/// A made-up "instruction": invalidate all of the L1 L2 D-Cache
#[inline(always)]
pub fn dciall() {
dmb();
l2_cache_invalidate_all();
dciall_l1();
}
/// A made-up "instruction": flush and invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dcciall_l1() {
// the cache associativity could be read from a register, but will
// always be 4 in L1 data cache of a cortex a9
let ways = 4;
let bit_pos_of_way = 30; // 32 - log2(ways)
// the cache sets could be read from a register, but are always
// 256 for the cores in the zync-7000; in general, 128 or 512 are
// also possible.
let sets = 256;
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
// select L1 data cache
unsafe {
llvm_asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
}
// Invalidate entire D-Cache by iterating every set and every way
for set in 0..sets {
for way in 0..ways {
dccisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
}
}
}
#[inline(always)]
pub fn dcciall() {
dmb();
dcciall_l1();
dsb();
l2_cache_clean_invalidate_all();
dcciall_l1();
dsb();
}
const CACHE_LINE: usize = 0x20; const CACHE_LINE: usize = 0x20;
const CACHE_LINE_MASK: usize = CACHE_LINE - 1; const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
@ -109,7 +162,16 @@ pub fn dccimvac(addr: usize) {
/// Data cache clean and invalidate for an object. /// Data cache clean and invalidate for an object.
pub fn dcci<T>(object: &T) { pub fn dcci<T>(object: &T) {
// ref: L2C310 TRM 3.3.10
dmb(); dmb();
for addr in object_cache_line_addrs(object) {
dccmvac(addr);
}
dsb();
for addr in object_cache_line_addrs(object) {
l2_cache_clean_invalidate(addr);
}
l2_cache_sync();
for addr in object_cache_line_addrs(object) { for addr in object_cache_line_addrs(object) {
dccimvac(addr); dccimvac(addr);
} }
@ -118,6 +180,14 @@ pub fn dcci<T>(object: &T) {
pub fn dcci_slice<T>(slice: &[T]) { pub fn dcci_slice<T>(slice: &[T]) {
dmb(); dmb();
for addr in slice_cache_line_addrs(slice) {
dccmvac(addr);
}
dsb();
for addr in slice_cache_line_addrs(slice) {
l2_cache_clean_invalidate(addr);
}
l2_cache_sync();
for addr in slice_cache_line_addrs(slice) { for addr in slice_cache_line_addrs(slice) {
dccimvac(addr); dccimvac(addr);
} }
@ -139,17 +209,28 @@ pub fn dcc<T>(object: &T) {
dccmvac(addr); dccmvac(addr);
} }
dsb(); dsb();
for addr in object_cache_line_addrs(object) {
l2_cache_clean(addr);
}
l2_cache_sync();
} }
/// Data cache clean for an object. Panics if not properly /// Data cache clean for an object. Panics if not properly
/// aligned and properly sized to be contained in an exact number of /// aligned and properly sized to be contained in an exact number of
/// cache lines. /// cache lines.
pub fn dcc_slice<T>(slice: &[T]) { pub fn dcc_slice<T>(slice: &[T]) {
if slice.len() == 0 {
return;
}
dmb(); dmb();
for addr in slice_cache_line_addrs(slice) { for addr in slice_cache_line_addrs(slice) {
dccmvac(addr); dccmvac(addr);
} }
dsb(); dsb();
for addr in slice_cache_line_addrs(slice) {
l2_cache_clean(addr);
}
l2_cache_sync();
} }
/// Data cache invalidate by memory virtual address. This and /// Data cache invalidate by memory virtual address. This and
@ -169,6 +250,10 @@ pub unsafe fn dci<T>(object: &mut T) {
assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci object beyond_addr must be aligned"); assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci object beyond_addr must be aligned");
dmb(); dmb();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
l2_cache_invalidate(addr);
}
l2_cache_sync();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) { for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
dcimvac(addr); dcimvac(addr);
} }
@ -183,6 +268,10 @@ pub unsafe fn dci_slice<T>(slice: &mut [T]) {
assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci slice beyond_addr must be aligned"); assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci slice beyond_addr must be aligned");
dmb(); dmb();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
l2_cache_invalidate(addr);
}
l2_cache_sync();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) { for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
dcimvac(addr); dcimvac(addr);
} }

14
libcortex_a9/src/fpu.rs Normal file
View File

@ -0,0 +1,14 @@
/// Enable FPU in the current core.
pub fn enable_fpu() {
unsafe {
llvm_asm!("
mrc p15, 0, r1, c1, c0, 2
orr r1, r1, (0b1111<<20)
mcr p15, 0, r1, c1, c0, 2
vmrs r1, fpexc
orr r1, r1, (1<<30)
vmsr fpexc, r1
":::"r1");
}
}

333
libcortex_a9/src/l2c.rs Normal file
View File

@ -0,0 +1,333 @@
use libregister::{register, register_at, register_bit, register_bits, RegisterRW, RegisterR, RegisterW};
use super::asm::dmb;
use volatile_register::RW;
/// enable L2 cache with specific prefetch offset
/// prefetch offset requires manual tuning, it seems that 8 is good for ZC706 current settings
pub fn enable_l2_cache(offset: u8) {
dmb();
let regs = RegisterBlock::new();
// disable L2 cache
regs.reg1_control.modify(|_, w| w.l2_enable(false));
regs.reg15_prefetch_ctrl.modify(|_, w|
w.instr_prefetch_en(true)
.data_prefetch_en(true)
.double_linefill_en(true)
.incr_double_linefill_en(true)
.pref_drop_en(true)
.prefetch_offset(offset)
);
regs.reg1_aux_control.modify(|_, w| {
w.early_bresp_en(true)
.instr_prefetch_en(true)
.data_prefetch_en(true)
.cache_replace_policy(true)
.way_size(3)
});
regs.reg1_tag_ram_control.modify(|_, w| w.ram_wr_access_lat(1).ram_rd_access_lat(1).ram_setup_lat(1));
regs.reg1_data_ram_control.modify(|_, w| w.ram_wr_access_lat(1).ram_rd_access_lat(2).ram_setup_lat(1));
// invalidate L2 ways
unsafe {
regs.reg7_inv_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
// write to a magic memory location with a magic sequence
// required in UG585 Section 3.4.10 Initialization Sequence
unsafe {
core::ptr::write_volatile(0xF8000008usize as *mut u32, 0xDF0D);
core::ptr::write_volatile(0xF8000A1Cusize as *mut u32, 0x020202);
core::ptr::write_volatile(0xF8000004usize as *mut u32, 0x767B);
}
regs.reg1_control.modify(|_, w| w.l2_enable(true));
dmb();
}
#[inline(always)]
pub fn l2_cache_invalidate_all() {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_inv_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
}
#[inline(always)]
pub fn l2_cache_clean_all() {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
}
#[inline(always)]
pub fn l2_cache_clean_invalidate_all() {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_inv_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
}
/// L2 cache sync, similar to dsb for L1 cache
#[inline(always)]
pub fn l2_cache_sync() {
let regs = RegisterBlock::new();
regs.reg7_cache_sync.write(Reg7CacheSync::zeroed().c(false));
}
#[inline(always)]
pub fn l2_cache_clean(addr: usize) {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_pa.write(addr as u32);
}
}
#[inline(always)]
pub fn l2_cache_invalidate(addr: usize) {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_inv_pa.write(addr as u32);
}
}
#[inline(always)]
pub fn l2_cache_clean_invalidate(addr: usize) {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_inv_pa.write(addr as u32);
}
}
#[repr(C)]
struct RegisterBlock {
/// cache ID register, Returns the 32-bit device ID code it reads off the CACHEID input bus.
/// The value is specified by the system integrator. Reset value: 0x410000c8
pub reg0_cache_id: Reg0CacheId,
/// cache type register, Returns the 32-bit cache type. Reset value: 0x1c100100
pub reg0_cache_type: Reg0CacheType,
unused0: [u32; 62],
/// control register, reset value: 0x0
pub reg1_control: Reg1Control,
/// auxilary control register, reset value: 0x02020000
pub reg1_aux_control: Reg1AuxControl,
/// Configures Tag RAM latencies
pub reg1_tag_ram_control: Reg1TagRamControl,
/// configures data RAM latencies
pub reg1_data_ram_control: Reg1DataRamControl,
unused1: [u32; 60],
/// Permits the event counters to be enabled and reset.
pub reg2_ev_counter_ctrl: Reg2EvCounterCtrl,
/// Enables event counter 1 to be driven by a specific event. Counter 1 increments when the
/// event occurs.
pub reg2_ev_counter1_cfg: Reg2EvCounter1Cfg,
/// Enables event counter 0 to be driven by a specific event. Counter 0 increments when the
/// event occurs.
pub reg2_ev_counter0_cfg: Reg2EvCounter0Cfg,
/// Enable the programmer to read off the counter value. The counter counts an event as
/// specified by the Counter Configuration Registers. The counter can be preloaded if counting
/// is disabled and reset by the Event Counter Control Register.
pub reg2_ev_counter1: RW<u32>,
/// Enable the programmer to read off the counter value. The counter counts an event as
/// specified by the Counter Configuration Registers. The counter can be preloaded if counting
/// is disabled and reset by the Event Counter Control Register.
pub reg2_ev_counter0: RW<u32>,
/// This register enables or masks interrupts from being triggered on the external pins of the
/// cache controller. Figure 3-8 on page 3-17 shows the register bit assignments. The bit
/// assignments enables the masking of the interrupts on both their individual outputs and the
/// combined L2CCINTR line. Clearing a bit by writing a 0, disables the interrupt triggering on
/// that pin. All bits are cleared by a reset. You must write to the register bits with a 1 to
/// enable the generation of interrupts. 1 = Enabled. 0 = Masked. This is the default.
pub reg2_int_mask: Reg2IntMask,
/// This register is a read-only.It returns the masked interrupt status. This register can be
/// accessed by secure and non-secure operations. The register gives an AND function of the raw
/// interrupt status with the values of the interrupt mask register. All the bits are cleared
/// by a reset. A write to this register is ignored. Bits read can be HIGH or LOW: HIGH If the
/// bits read HIGH, they reflect the status of the input lines triggering an interrupt. LOW If
/// the bits read LOW, either no interrupt has been generated, or the interrupt is masked.
pub reg2_int_mask_status: Reg2IntMaskStatus,
/// The Raw Interrupt Status Register enables the interrupt status that excludes the masking
/// logic. Bits read can be HIGH or LOW: HIGH If the bits read HIGH, they reflect the status of
/// the input lines triggering an interrupt. LOW If the bits read LOW, no interrupt has been
/// generated.
pub reg2_int_raw_status: Reg2IntRawStatus,
/// Clears the Raw Interrupt Status Register bits. When a bit is written as 1, it clears the
/// corresponding bit in the Raw Interrupt Status Register. When a bit is written as 0, it has
/// no effect
pub reg2_int_clear: Reg2IntClear,
unused2: [u32; 323],
/// Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and EB, are empty
pub reg7_cache_sync: Reg7CacheSync,
unused3: [u32; 15],
/// Invalidate Line by PA: Specific L2 cache line is marked as not valid
pub reg7_inv_pa: RW<u32>,
unused4: [u32; 2],
/// Invalidate by Way Invalidate all data in specified ways, including dirty data. An
/// Invalidate by way while selecting all cache ways is equivalent to invalidating all cache
/// entries. Completes as a background task with the way, or ways, locked, preventing
/// allocation.
pub reg7_inv_way: RW<u32>,
unused5: [u32; 12],
/// Clean Line by PA Write the specific L2 cache line to L3 main memory if the line is marked
/// as valid and dirty. The line is marked as not dirty. The valid bit is unchanged
pub reg7_clean_pa: RW<u32>,
unused6: [u32; 1],
/// Clean Line by Set/Way Write the specific L2 cache line within the specified way to L3 main
/// memory if the line is marked as valid and dirty. The line is marked as not dirty. The valid
/// bit is unchanged
pub reg7_clean_index: Reg7CleanIndex,
/// Clean by Way Writes each line of the specified L2 cache ways to L3 main memory if the line
/// is marked as valid and dirty. The lines are marked as not dirty. The valid bits are
/// unchanged. Completes as a background task with the way, or ways, locked, preventing
/// allocation.
pub reg7_clean_way: RW<u32>,
unused7: [u32; 12],
/// Clean and Invalidate Line by PA Write the specific L2 cache line to L3 main memory if the
/// line is marked as valid and dirty. The line is marked as not valid
pub reg7_clean_inv_pa: RW<u32>,
unused8: [u32; 1],
/// Clean and Invalidate Line by Set/Way Write the specific L2 cache line within the specified
/// way to L3 main memory if the line is marked as valid and dirty. The line is marked as not
/// valid
pub reg7_clean_inv_index: Reg7CleanInvIndex,
/// Clean and Invalidate by Way Writes each line of the specified L2 cache ways to L3 main
/// memory if the line is marked as valid and dirty. The lines are marked as not valid.
/// Completes as a background task with the way, or ways, locked, preventing allocation.
pub reg7_clean_inv_way: RW<u32>,
unused9: [u32; 0x1D8],
pub reg15_prefetch_ctrl: Reg15PrefetechCtrl,
}
register_at!(RegisterBlock, 0xF8F02000, new);
register!(reg0_cache_id, Reg0CacheId, RW, u32);
register_bits!(reg0_cache_id, implementer, u8, 24, 31);
register_bits!(reg0_cache_id, cache_id, u8, 10, 15);
register_bits!(reg0_cache_id, part_num, u8, 6, 9);
register_bits!(reg0_cache_id, rtl_release, u8, 0, 5);
register!(reg0_cache_type, Reg0CacheType, RW, u32);
register_bit!(reg0_cache_type, data_banking, 31);
register_bits!(reg0_cache_type, ctype, u8, 25, 28);
register_bit!(reg0_cache_type, h, 24);
register_bits!(reg0_cache_type, dsize_middsize_19, u8, 20, 22);
register_bit!(reg0_cache_type, l2_assoc_d, 18);
register_bits!(reg0_cache_type, l2cache_line_len_disize_11, u8, 12, 13);
register_bits!(reg0_cache_type, isize_midisize_7, u8, 8, 10);
register_bit!(reg0_cache_type, l2_assoc_i, 6);
register_bits!(reg0_cache_type, l2cache_line_len_i, u8, 0, 1);
register!(reg1_control, Reg1Control, RW, u32);
register_bit!(reg1_control, l2_enable, 0);
register!(reg1_aux_control, Reg1AuxControl, RW, u32);
register_bit!(reg1_aux_control, early_bresp_en, 30);
register_bit!(reg1_aux_control, instr_prefetch_en, 29);
register_bit!(reg1_aux_control, data_prefetch_en, 28);
register_bit!(reg1_aux_control, nonsec_inte_access_ctrl, 27);
register_bit!(reg1_aux_control, nonsec_lockdown_en, 26);
register_bit!(reg1_aux_control, cache_replace_policy, 25);
register_bits!(reg1_aux_control, force_write_alloc, u8, 23, 24);
register_bit!(reg1_aux_control, shared_attr_override_en, 22);
register_bit!(reg1_aux_control, parity_en, 21);
register_bit!(reg1_aux_control, event_mon_bus_en, 20);
register_bits!(reg1_aux_control, way_size, u8, 17, 19);
register_bit!(reg1_aux_control, associativity, 16);
register_bit!(reg1_aux_control, shared_attr_inva_en, 13);
register_bit!(reg1_aux_control, ex_cache_config, 12);
register_bit!(reg1_aux_control, store_buff_dev_lim_en, 11);
register_bit!(reg1_aux_control, high_pr_so_dev_rd_en, 10);
register_bit!(reg1_aux_control, full_line_zero_enable, 0);
register!(reg1_tag_ram_control, Reg1TagRamControl, RW, u32);
register_bits!(reg1_tag_ram_control, ram_wr_access_lat, u8, 8, 10);
register_bits!(reg1_tag_ram_control, ram_rd_access_lat, u8, 4, 6);
register_bits!(reg1_tag_ram_control, ram_setup_lat, u8, 0, 2);
register!(reg1_data_ram_control, Reg1DataRamControl, RW, u32);
register_bits!(reg1_data_ram_control, ram_wr_access_lat, u8, 8, 10);
register_bits!(reg1_data_ram_control, ram_rd_access_lat, u8, 4, 6);
register_bits!(reg1_data_ram_control, ram_setup_lat, u8, 0, 2);
register!(reg2_ev_counter_ctrl, Reg2EvCounterCtrl, RW, u32);
register_bit!(reg2_ev_counter_ctrl, ev_ctr_en, 0);
register!(reg2_ev_counter1_cfg, Reg2EvCounter1Cfg, RW, u32);
register_bits!(reg2_ev_counter1_cfg, ctr_ev_src, u8, 2, 5);
register_bits!(reg2_ev_counter1_cfg, ev_ctr_intr_gen, u8, 0, 1);
register!(reg2_ev_counter0_cfg, Reg2EvCounter0Cfg, RW, u32);
register_bits!(reg2_ev_counter0_cfg, ctr_ev_src, u8, 2, 5);
register_bits!(reg2_ev_counter0_cfg, ev_ctr_intr_gen, u8, 0, 1);
register!(reg2_int_mask, Reg2IntMask, RW, u32);
register_bit!(reg2_int_mask, decerr, 8);
register_bit!(reg2_int_mask, slverr, 7);
register_bit!(reg2_int_mask, errrd, 6);
register_bit!(reg2_int_mask, errrt, 5);
register_bit!(reg2_int_mask, errwd, 4);
register_bit!(reg2_int_mask, errwt, 3);
register_bit!(reg2_int_mask, parrd, 2);
register_bit!(reg2_int_mask, parrt, 1);
register_bit!(reg2_int_mask, ecntr, 0);
register!(reg2_int_mask_status, Reg2IntMaskStatus, RW, u32);
register_bit!(reg2_int_mask_status, decerr, 8);
register_bit!(reg2_int_mask_status, slverr, 7);
register_bit!(reg2_int_mask_status, errrd, 6);
register_bit!(reg2_int_mask_status, errrt, 5);
register_bit!(reg2_int_mask_status, errwd, 4);
register_bit!(reg2_int_mask_status, errwt, 3);
register_bit!(reg2_int_mask_status, parrd, 2);
register_bit!(reg2_int_mask_status, parrt, 1);
register_bit!(reg2_int_mask_status, ecntr, 0);
register!(reg2_int_raw_status, Reg2IntRawStatus, RW, u32);
register_bit!(reg2_int_raw_status, decerr, 8);
register_bit!(reg2_int_raw_status, slverr, 7);
register_bit!(reg2_int_raw_status, errrd, 6);
register_bit!(reg2_int_raw_status, errrt, 5);
register_bit!(reg2_int_raw_status, errwd, 4);
register_bit!(reg2_int_raw_status, errwt, 3);
register_bit!(reg2_int_raw_status, parrd, 2);
register_bit!(reg2_int_raw_status, parrt, 1);
register_bit!(reg2_int_raw_status, ecntr, 0);
register!(reg2_int_clear, Reg2IntClear, RW, u32, 0);
register_bit!(reg2_int_clear, decerr, 8, WTC);
register_bit!(reg2_int_clear, slverr, 7, WTC);
register_bit!(reg2_int_clear, errrd, 6, WTC);
register_bit!(reg2_int_clear, errrt, 5, WTC);
register_bit!(reg2_int_clear, errwd, 4, WTC);
register_bit!(reg2_int_clear, errwt, 3, WTC);
register_bit!(reg2_int_clear, parrd, 2, WTC);
register_bit!(reg2_int_clear, parrt, 1, WTC);
register_bit!(reg2_int_clear, ecntr, 0, WTC);
register!(reg7_cache_sync, Reg7CacheSync, RW, u32);
register_bit!(reg7_cache_sync, c, 0);
register!(reg7_clean_index, Reg7CleanIndex, RW, u32);
register_bits!(reg7_clean_index, way, u8, 28, 30);
register_bits!(reg7_clean_index, index, u8, 5, 11);
register_bit!(reg7_clean_index, c, 0);
register!(reg7_clean_inv_index, Reg7CleanInvIndex, RW, u32);
register_bits!(reg7_clean_inv_index, way, u8, 28, 30);
register_bits!(reg7_clean_inv_index, index, u8, 5, 11);
register_bit!(reg7_clean_inv_index, c, 0);
register!(reg15_prefetch_ctrl, Reg15PrefetechCtrl, RW, u32);
register_bit!(reg15_prefetch_ctrl, double_linefill_en, 30);
register_bit!(reg15_prefetch_ctrl, instr_prefetch_en, 29);
register_bit!(reg15_prefetch_ctrl, data_prefetch_en, 28);
register_bit!(reg15_prefetch_ctrl, pref_drop_en, 24);
register_bit!(reg15_prefetch_ctrl, incr_double_linefill_en, 23);
register_bits!(reg15_prefetch_ctrl, prefetch_offset, u8, 0, 4);

View File

@ -1,16 +1,81 @@
#![no_std] #![no_std]
#![feature(llvm_asm, global_asm)] #![feature(llvm_asm, global_asm)]
#![feature(never_type)] #![feature(never_type)]
#![feature(const_fn)]
extern crate alloc; extern crate alloc;
pub mod asm; pub mod asm;
pub mod regs;
pub mod cache; pub mod cache;
mod fpu;
pub mod l2c;
pub mod mmu; pub mod mmu;
pub mod mutex; pub mod mutex;
pub mod regs;
pub mod semaphore;
pub mod sync_channel; pub mod sync_channel;
mod uncached; mod uncached;
pub use fpu::enable_fpu;
pub use uncached::UncachedSlice; pub use uncached::UncachedSlice;
global_asm!(include_str!("exceptions.s")); global_asm!(include_str!("exceptions.s"));
#[inline]
pub fn spin_lock_yield() {
#[cfg(feature = "power_saving")]
asm::wfe();
}
#[inline]
pub fn notify_spin_lock() {
#[cfg(feature = "power_saving")]
{
asm::dsb();
asm::sev();
}
}
#[macro_export]
/// Interrupt handler, which setup the stack and preserve registers before jumping to actual interrupt handler.
/// Registers r0-r12, PC, SP and CPSR are restored after the actual handler.
///
/// - `name` is the name of the interrupt, should be the same as the one defined in vector table.
/// - `name2` is the name for the actual handler, should be different from name.
/// - `stack0` is the stack for the interrupt handler when called from core0.
/// - `stack1` is the stack for the interrupt handler when called from core1.
/// - `body` is the body of the actual interrupt handler, should be a normal unsafe rust function
/// body.
///
/// Note that the interrupt handler would use the same stack as normal programs by default.
macro_rules! interrupt_handler {
($name:ident, $name2:ident, $stack0:ident, $stack1:ident, $body:block) => {
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn $name() -> ! {
asm!(
// setup SP, depending on CPU 0 or 1
// and preserve registers
"sub lr, lr, #4",
"stmfd sp!, {{r0-r12, lr}}",
"mrc p15, #0, r0, c0, c0, #5",
concat!("movw r1, :lower16:", stringify!($stack0)),
concat!("movt r1, :upper16:", stringify!($stack0)),
"tst r0, #3",
concat!("movwne r1, :lower16:", stringify!($stack1)),
concat!("movtne r1, :upper16:", stringify!($stack1)),
"mov r0, sp",
"mov sp, r1",
"push {{r0, r1}}", // 2 registers are pushed to maintain 8 byte stack alignment
concat!("bl ", stringify!($name2)),
"pop {{r0, r1}}",
"mov sp, r0",
"ldmfd sp!, {{r0-r12, pc}}^", // caret ^ : copy SPSR to the CPSR
options(noreturn)
);
}
#[no_mangle]
pub unsafe extern "C" fn $name2() $body
};
}

View File

@ -1,5 +1,5 @@
use bit_field::BitField; use bit_field::BitField;
use super::{regs::*, asm, cache}; use super::{regs::*, asm::*, cache::*};
use libregister::RegisterW; use libregister::RegisterW;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -158,7 +158,7 @@ impl L1Table {
global: true, global: true,
shareable: true, shareable: true,
access: AccessPermissions::FullAccess, access: AccessPermissions::FullAccess,
tex: 0b101, tex: 0b0,
domain: 0b1111, domain: 0b1111,
exec: true, exec: true,
cacheable: true, cacheable: true,
@ -213,7 +213,7 @@ impl L1Table {
access: AccessPermissions::FullAccess, access: AccessPermissions::FullAccess,
tex: 0, tex: 0,
domain: 0, domain: 0,
exec: true, exec: false,
cacheable: false, cacheable: false,
bufferable: true, bufferable: true,
}); });
@ -338,7 +338,7 @@ impl L1Table {
/* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */ /* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */
self.direct_mapped_section(0xfff, L1Section { self.direct_mapped_section(0xfff, L1Section {
global: true, global: true,
shareable: false, shareable: true,
access: AccessPermissions::FullAccess, access: AccessPermissions::FullAccess,
tex: 0b100, tex: 0b100,
domain: 0, domain: 0,
@ -368,10 +368,19 @@ impl L1Table {
let result = f(&mut section); let result = f(&mut section);
entry.set_section(section); entry.set_section(section);
asm::dmb(); // Flush L1Dcache
cache::tlbiall(); dcciall();
asm::dsb(); // // TODO: L2?
asm::isb();
// Invalidate TLB
tlbiall();
// Invalidate all branch predictors
bpiall();
// ensure completion of the BP and TLB invalidation
dsb();
// synchronize context on this processor
isb();
result result
} }
@ -401,14 +410,15 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
.a(false) .a(false)
.c(true) .c(true)
.i(true) .i(true)
.z(true)
.unaligned(true) .unaligned(true)
); );
// Synchronization barriers // Synchronization barriers
// Allows MMU to start // Allows MMU to start
asm::dsb(); dsb();
// Flushes pre-fetch buffer // Flushes pre-fetch buffer
asm::isb(); isb();
f(); f();
} }

View File

@ -1,20 +1,13 @@
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicU32, Ordering}; use core::sync::atomic::{AtomicU32, Ordering};
use core::cell::UnsafeCell; use core::cell::UnsafeCell;
use super::asm::*; use core::task::{Context, Poll};
use core::pin::Pin;
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html) use core::future::Future;
#[inline] use super::{
fn wait_for_update() { spin_lock_yield, notify_spin_lock,
wfe(); asm::{enter_critical, exit_critical}
} };
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
#[inline]
fn signal_update() {
dsb();
sev();
}
const LOCKED: u32 = 1; const LOCKED: u32 = 1;
const UNLOCKED: u32 = 0; const UNLOCKED: u32 = 0;
@ -30,6 +23,23 @@ pub struct Mutex<T> {
unsafe impl<T: Send> Sync for Mutex<T> {} unsafe impl<T: Send> Sync for Mutex<T> {}
unsafe impl<T: Send> Send for Mutex<T> {} unsafe impl<T: Send> Send for Mutex<T> {}
struct Fut<'a, T>(&'a Mutex<T>);
impl<'a, T> Future for Fut<'a, T> {
type Output = MutexGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let irq = unsafe { enter_critical() };
if self.0.locked.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::AcqRel, Ordering::Relaxed).is_err() {
unsafe { exit_critical(irq) };
cx.waker().wake_by_ref();
Poll::Pending
}
else {
Poll::Ready(MutexGuard { mutex: self.0, irq })
}
}
}
impl<T> Mutex<T> { impl<T> Mutex<T> {
/// Constructor, const-fn /// Constructor, const-fn
pub const fn new(inner: T) -> Self { pub const fn new(inner: T) -> Self {
@ -41,18 +51,35 @@ impl<T> Mutex<T> {
/// Lock the Mutex, blocks when already locked /// Lock the Mutex, blocks when already locked
pub fn lock(&self) -> MutexGuard<T> { pub fn lock(&self) -> MutexGuard<T> {
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED { let mut irq = unsafe { enter_critical() };
wait_for_update(); while self.locked.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::AcqRel, Ordering::Relaxed).is_err() {
unsafe {
exit_critical(irq);
spin_lock_yield();
irq = enter_critical();
}
}
MutexGuard { mutex: self, irq }
}
pub async fn async_lock(&self) -> MutexGuard<'_, T> {
Fut(&self).await
}
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
let irq = unsafe { enter_critical() };
if self.locked.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::AcqRel, Ordering::Relaxed).is_err() {
unsafe { exit_critical(irq) };
None
} else {
Some(MutexGuard { mutex: self, irq })
} }
dmb();
MutexGuard { mutex: self }
} }
fn unlock(&self) { fn unlock(&self) {
dmb();
self.locked.store(UNLOCKED, Ordering::Release); self.locked.store(UNLOCKED, Ordering::Release);
signal_update(); notify_spin_lock();
} }
} }
@ -60,6 +87,7 @@ impl<T> Mutex<T> {
/// `Deref`/`DerefMutx` /// `Deref`/`DerefMutx`
pub struct MutexGuard<'a, T> { pub struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>, mutex: &'a Mutex<T>,
irq: bool,
} }
impl<'a, T> Deref for MutexGuard<'a, T> { impl<'a, T> Deref for MutexGuard<'a, T> {
@ -79,5 +107,6 @@ impl<'a, T> DerefMut for MutexGuard<'a, T> {
impl<'a, T> Drop for MutexGuard<'a, T> { impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
self.mutex.unlock(); self.mutex.unlock();
unsafe { exit_critical(self.irq) };
} }
} }

View File

@ -91,8 +91,19 @@ pub struct HVBAR;
def_reg_r!(HVBAR, u32, "mrc p15, 4, $0, c12, c0, 0"); def_reg_r!(HVBAR, u32, "mrc p15, 4, $0, c12, c0, 0");
def_reg_w!(HVBAR, u32, "mcr p15, 4, $0, c12, c0, 0"); def_reg_w!(HVBAR, u32, "mcr p15, 4, $0, c12, c0, 0");
/// Multiprocess Affinity Register
pub struct MPIDR; pub struct MPIDR;
def_reg_r!(MPIDR, u32, "mrc p15, 0, $0, c0, c0, 5"); def_reg_r!(MPIDR, mpidr::Read, "mrc p15, 0, $0, c0, c0, 5");
wrap_reg!(mpidr);
register_bits!(mpidr,
/// CPU core index
cpu_id, u8, 0, 1);
register_bits!(mpidr,
/// Processor index in "multi-socket" systems
cluster_id, u8, 8, 11);
register_bit!(mpidr,
/// true if part of uniprocessor system
u, 30);
pub struct DFAR; pub struct DFAR;
def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0"); def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0");
@ -145,6 +156,8 @@ register_bit!(actlr, excl, 7);
register_bit!(actlr, smp, 6); register_bit!(actlr, smp, 6);
register_bit!(actlr, write_full_line_of_zeros, 3); register_bit!(actlr, write_full_line_of_zeros, 3);
register_bit!(actlr, l1_prefetch_enable, 2); register_bit!(actlr, l1_prefetch_enable, 2);
// L2 cache prefetch hint, in UG585 section 3.4.8
register_bit!(actlr, l2_prefetch_enable, 1);
// Cache/TLB maintenance broadcast // Cache/TLB maintenance broadcast
register_bit!(actlr, fw, 0); register_bit!(actlr, fw, 0);
@ -160,7 +173,11 @@ impl RegisterRW for ACTLR {
impl ACTLR { impl ACTLR {
pub fn enable_smp(&mut self) { pub fn enable_smp(&mut self) {
self.modify(|_, w| w.smp(true).fw(true)); self.modify(|_, w| w.smp(true).fw(true).alloc_one_way(true));
}
pub fn enable_prefetch(&mut self) {
self.modify(|_, w| w.l1_prefetch_enable(true).l2_prefetch_enable(true))
} }
} }

View File

@ -0,0 +1,71 @@
use super::{spin_lock_yield, notify_spin_lock};
use core::{
task::{Context, Poll},
pin::Pin,
future::Future,
sync::atomic::{AtomicI32, Ordering}
};
pub struct Semaphore {
value: AtomicI32,
max: i32
}
impl Semaphore {
pub const fn new(value: i32, max: i32) -> Self {
Semaphore { value: AtomicI32::new(value), max}
}
pub fn try_wait(&self) -> Option<()> {
loop {
let value = self.value.load(Ordering::Relaxed);
if value > 0 {
if self.value.compare_exchange_weak(value, value - 1, Ordering::SeqCst, Ordering::Relaxed).is_ok() {
return Some(());
}
} else {
return None;
}
}
}
pub fn wait(&self) {
while self.try_wait().is_none() {
spin_lock_yield();
}
}
pub async fn async_wait(&self) {
struct Fut<'a>(&'a Semaphore);
impl Future for Fut<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.0.try_wait() {
Some(_) => Poll::Ready(()),
None => {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
Fut(&self).await
}
pub fn signal(&self) {
loop {
let value = self.value.load(Ordering::Relaxed);
if value < self.max {
if self.value.compare_exchange_weak(value, value + 1, Ordering::SeqCst, Ordering::Relaxed).is_ok() {
notify_spin_lock();
return;
}
} else {
return;
}
}
}
}

View File

@ -1,115 +1,72 @@
use core::{ use core::{
future::Future,
pin::Pin, pin::Pin,
ptr::null_mut, future::Future,
sync::atomic::{AtomicPtr, Ordering}, sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
task::{Context, Poll}, task::{Context, Poll},
}; };
use alloc::{ use alloc::boxed::Box;
boxed::Box, use super::{spin_lock_yield, notify_spin_lock};
sync::Arc,
vec::Vec,
};
use super::asm::*;
pub struct Sender<'a, T> where T: Clone {
type Channel<T> = Vec<AtomicPtr<T>>; list: &'a [AtomicPtr<T>],
write: &'a AtomicUsize,
/// Create a bounded channel read: &'a AtomicUsize,
///
/// Returns `(tx, rx)` where one should be used one the local core,
/// and the other is to be shared with another core.
pub fn sync_channel<T>(bound: usize) -> (Sender<T>, Receiver<T>) {
// allow for bound=0
let len = bound + 1;
let mut channel = Vec::with_capacity(len);
for _ in 0..len {
channel.push(AtomicPtr::default());
}
let channel = Arc::new(channel);
let sender = Sender {
channel: channel.clone(),
pos: 0,
};
let receiver = Receiver {
channel: channel,
pos: 0,
};
(sender, receiver)
} }
/// Sending half of a channel pub struct Receiver<'a, T> where T: Clone {
pub struct Sender<T> { list: &'a [AtomicPtr<T>],
channel: Arc<Channel<T>>, write: &'a AtomicUsize,
pos: usize, read: &'a AtomicUsize,
} }
impl<T> Sender<T> { impl<'a, T> Sender<'a, T> where T: Clone {
/// Blocking send pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
pub fn send<B: Into<Box<T>>>(&mut self, content: B) { Sender {list, write, read}
let ptr = Box::into_raw(content.into());
let entry = &self.channel[self.pos];
// try to write the new pointer if the current pointer is
// NULL, retrying while it is not NULL
while entry.compare_and_swap(null_mut(), ptr, Ordering::Acquire) != null_mut() {
// power-saving
wfe();
}
dsb();
// wake power-saving receivers
sev();
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
} }
/// Non-blocking send, handing you back ownership of the content on **failure** pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Result<(), B> {
pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Option<Box<T>> { let write = self.write.load(Ordering::Relaxed);
let ptr = Box::into_raw(content.into()); if (write + 1) % self.list.len() == self.read.load(Ordering::Acquire) {
let entry = &self.channel[self.pos]; Err(content)
// try to write the new pointer if the current pointer is
// NULL
if entry.compare_and_swap(null_mut(), ptr, Ordering::Acquire) == null_mut() {
dsb();
// wake power-saving receivers
sev();
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
// success
None
} else { } else {
let content = unsafe { Box::from_raw(ptr) }; let ptr = Box::into_raw(content.into());
// failure let entry = &self.list[write];
Some(content) let prev = entry.swap(ptr, Ordering::Relaxed);
// we allow other end get it first
self.write.store((write + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
if !prev.is_null() {
unsafe {
Box::from_raw(prev);
}
}
Ok(())
}
}
pub fn send<B: Into<Box<T>>>(&mut self, content: B) {
let mut content = content;
while let Err(back) = self.try_send(content) {
content = back;
spin_lock_yield();
} }
} }
pub async fn async_send<B: Into<Box<T>>>(&mut self, content: B) { pub async fn async_send<B: Into<Box<T>>>(&mut self, content: B) {
struct Send<'a, T> { struct Send<'a, 'b, T> where T: Clone, 'b: 'a {
sender: &'a mut Sender<T>, sender: &'a mut Sender<'b, T>,
content: Option<Box<T>>, content: Result<(), Box<T>>,
} }
impl<T> Future for Send<'_, T> { impl<T> Future for Send<'_, '_, T> where T: Clone {
type Output = (); type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.content.take() { match core::mem::replace(&mut self.content, Ok(())) {
Some(content) => { Err(content) => {
if let Some(content) = self.sender.try_send(content) { if let Err(content) = self.sender.try_send(content) {
// failure // failure
self.content = Some(content); self.content = Err(content);
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();
Poll::Pending Poll::Pending
} else { } else {
@ -117,93 +74,80 @@ impl<T> Sender<T> {
Poll::Ready(()) Poll::Ready(())
} }
} }
None => panic!("Send future polled after success"), Ok(_) => panic!("Send future polled after success"),
} }
} }
} }
Send { Send {
sender: self, sender: self,
content: Some(content.into()), content: Err(content.into()),
}.await }.await
} }
/// free all items in the queue. It is the user's responsibility to
/// ensure no reader is trying to copy the data.
pub unsafe fn drop_elements(&mut self) {
for v in self.list.iter() {
let original = v.swap(core::ptr::null_mut(), Ordering::Relaxed);
if !original.is_null() {
Box::from_raw(original);
}
}
}
/// Reset the `sync_channel`, *forget* all items in the queue. Affects both the sender and
/// receiver.
pub unsafe fn reset(&mut self) {
self.write.store(0, Ordering::Relaxed);
self.read.store(0, Ordering::Relaxed);
for v in self.list.iter() {
v.store(core::ptr::null_mut(), Ordering::Relaxed);
}
}
} }
impl<'a, T> Receiver<'a, T> where T: Clone {
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
Receiver {list, write, read}
/// Receiving half of a channel
pub struct Receiver<T> {
channel: Arc<Channel<T>>,
pos: usize,
}
impl<T> Receiver<T> {
/// Blocking receive
pub fn recv(&mut self) -> Box<T> {
let entry = &self.channel[self.pos];
loop {
dmb();
let ptr = entry.swap(null_mut(), Ordering::Release);
if ptr != null_mut() {
dsb();
// wake power-saving senders
sev();
let content = unsafe { Box::from_raw(ptr) };
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
} }
return content; pub fn try_recv(&mut self) -> Result<T, ()> {
} let read = self.read.load(Ordering::Relaxed);
if read == self.write.load(Ordering::Acquire) {
// power-saving Err(())
wfe();
}
}
/// Non-blocking receive
pub fn try_recv(&mut self) -> Option<Box<T>> {
let entry = &self.channel[self.pos];
dmb();
let ptr = entry.swap(null_mut(), Ordering::Release);
if ptr != null_mut() {
dsb();
// wake power-saving senders
sev();
let content = unsafe { Box::from_raw(ptr) };
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
Some(content)
} else { } else {
None let entry = &self.list[read];
let data = unsafe {
// we cannot deallocate the box
Box::leak(Box::from_raw(entry.load(Ordering::Relaxed)))
};
let result = data.clone();
self.read.store((read + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
Ok(result)
} }
} }
pub async fn async_recv(&mut self) -> Box<T> { pub fn recv(&mut self) -> T {
struct Recv<'a, T> { loop {
receiver: &'a mut Receiver<T>, if let Ok(data) = self.try_recv() {
return data;
}
spin_lock_yield();
}
} }
impl<T> Future for Recv<'_, T> { pub async fn async_recv(&mut self) -> T {
type Output = Box<T>; struct Recv<'a, 'b, T> where T: Clone, 'b: 'a {
receiver: &'a mut Receiver<'b, T>,
}
impl<T> Future for Recv<'_, '_, T> where T: Clone {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(content) = self.receiver.try_recv() { if let Ok(content) = self.receiver.try_recv() {
Poll::Ready(content) Poll::Ready(content)
} else { } else {
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();
@ -218,10 +162,26 @@ impl<T> Receiver<T> {
} }
} }
impl<T> Iterator for Receiver<T> { impl<'a, T> Iterator for Receiver<'a, T> where T: Clone {
type Item = Box<T>; type Item = T;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
Some(self.recv()) Some(self.recv())
} }
} }
#[macro_export]
/// Macro for initializing the sync_channel with static buffer and indexes.
/// Note that this requires `#![feature(const_in_array_repeat_expressions)]`
macro_rules! sync_channel {
($t: ty, $cap: expr) => {
{
use core::sync::atomic::{AtomicUsize, AtomicPtr};
use $crate::sync_channel::{Sender, Receiver};
static LIST: [AtomicPtr<$t>; $cap + 1] = [AtomicPtr::new(core::ptr::null_mut()); $cap + 1];
static WRITE: AtomicUsize = AtomicUsize::new(0);
static READ: AtomicUsize = AtomicUsize::new(0);
(Sender::new(&LIST, &WRITE, &READ), Receiver::new(&LIST, &WRITE, &READ))
}
};
}

View File

@ -2,7 +2,7 @@ use core::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
mem::{align_of, size_of}, mem::{align_of, size_of},
}; };
use alloc::alloc::{dealloc, Layout, LayoutErr}; use alloc::alloc::{dealloc, Layout, LayoutError};
use crate::mmu::{L1_PAGE_SIZE, L1Table}; use crate::mmu::{L1_PAGE_SIZE, L1Table};
pub struct UncachedSlice<T: 'static> { pub struct UncachedSlice<T: 'static> {
@ -12,7 +12,7 @@ pub struct UncachedSlice<T: 'static> {
impl<T> UncachedSlice<T> { impl<T> UncachedSlice<T> {
/// allocates in chunks of 1 MB /// allocates in chunks of 1 MB
pub fn new<F: Fn() -> T>(len: usize, default: F) -> Result<Self, LayoutErr> { pub fn new<F: Fn() -> T>(len: usize, default: F) -> Result<Self, LayoutError> {
// round to full pages // round to full pages
let size = ((len * size_of::<T>() - 1) | (L1_PAGE_SIZE - 1)) + 1; let size = ((len * size_of::<T>() - 1) | (L1_PAGE_SIZE - 1)) + 1;
let align = align_of::<T>() let align = align_of::<T>()
@ -23,9 +23,11 @@ impl<T> UncachedSlice<T> {
assert_eq!(start & (L1_PAGE_SIZE - 1), 0); assert_eq!(start & (L1_PAGE_SIZE - 1), 0);
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) { for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
// non-shareable device
L1Table::get() L1Table::get()
.update(page_start as *const (), |l1_section| { .update(page_start as *const (), |l1_section| {
l1_section.cacheable = false; l1_section.tex = 0b10;
l1_section.cacheable = true;
l1_section.bufferable = false; l1_section.bufferable = false;
}); });
} }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "libregister" name = "libregister"
version = "0.0.0" version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"] authors = ["M-Labs"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]

View File

@ -30,8 +30,9 @@ pub trait RegisterRW: RegisterR + RegisterW {
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! register_common { macro_rules! register_common {
($mod_name: ident, $struct_name: ident, $access: ty, $inner: ty) => ( ($mod_name: ident, $(#[$outer:meta])* $struct_name: ident, $access: ty, $inner: ty) => (
#[repr(C)] #[repr(C)]
$(#[$outer])*
pub struct $struct_name { pub struct $struct_name {
inner: $access, inner: $access,
} }
@ -52,7 +53,7 @@ macro_rules! register_common {
#[macro_export] #[macro_export]
macro_rules! register_r { macro_rules! register_r {
($mod_name: ident, $struct_name: ident) => ( ($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterR for $struct_name { impl $crate::RegisterR for $struct_name {
type R = $mod_name::Read; type R = $mod_name::Read;
#[inline] #[inline]
@ -67,7 +68,7 @@ macro_rules! register_r {
#[macro_export] #[macro_export]
macro_rules! register_w { macro_rules! register_w {
($mod_name: ident, $struct_name: ident) => ( ($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterW for $struct_name { impl $crate::RegisterW for $struct_name {
type W = $mod_name::Write; type W = $mod_name::Write;
#[inline] #[inline]
@ -88,7 +89,7 @@ macro_rules! register_w {
#[macro_export] #[macro_export]
macro_rules! register_rw { macro_rules! register_rw {
($mod_name: ident, $struct_name: ident) => ( ($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterRW for $struct_name { impl $crate::RegisterRW for $struct_name {
#[inline] #[inline]
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) { fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
unsafe { unsafe {
@ -101,7 +102,7 @@ macro_rules! register_rw {
} }
); );
($mod_name: ident, $struct_name: ident, $mask: expr) => ( ($mod_name: ident, $struct_name: ident, $mask: expr) => (
impl libregister::RegisterRW for $struct_name { impl $crate::RegisterRW for $struct_name {
#[inline] #[inline]
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) { fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
unsafe { unsafe {
@ -119,7 +120,7 @@ macro_rules! register_rw {
#[macro_export] #[macro_export]
macro_rules! register_vcell { macro_rules! register_vcell {
($mod_name: ident, $struct_name: ident) => ( ($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterR for $struct_name { impl $crate::RegisterR for $struct_name {
type R = $mod_name::Read; type R = $mod_name::Read;
#[inline] #[inline]
@ -128,7 +129,7 @@ macro_rules! register_vcell {
$mod_name::Read { inner } $mod_name::Read { inner }
} }
} }
impl libregister::RegisterW for $struct_name { impl $crate::RegisterW for $struct_name {
type W = $mod_name::Write; type W = $mod_name::Write;
#[inline] #[inline]
@ -141,7 +142,7 @@ macro_rules! register_vcell {
self.inner.set(w.inner); self.inner.set(w.inner);
} }
} }
impl libregister::RegisterRW for $struct_name { impl $crate::RegisterRW for $struct_name {
#[inline] #[inline]
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) { fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
let r = self.read(); let r = self.read();
@ -157,37 +158,37 @@ macro_rules! register_vcell {
#[macro_export] #[macro_export]
macro_rules! register { macro_rules! register {
// Define read-only register // Define read-only register
($mod_name: ident, $struct_name: ident, RO, $inner: ty) => ( ($mod_name: ident, $(#[$outer:meta])* $struct_name: ident, RO, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, libregister::RO<$inner>, $inner); $crate::register_common!($mod_name, $(#[$outer])* $struct_name, $crate::RO<$inner>, $inner);
libregister::register_r!($mod_name, $struct_name); $crate::register_r!($mod_name, $struct_name);
); );
// Define write-only register // Define write-only register
($mod_name: ident, $struct_name: ident, WO, $inner: ty) => ( ($mod_name: ident, $(#[$outer:meta])* $struct_name: ident, WO, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner); $crate::register_common!($mod_name, $(#[$outer])* $struct_name, volatile_register::WO<$inner>, $inner);
libregister::register_w!($mod_name, $struct_name); $crate::register_w!($mod_name, $struct_name);
); );
// Define read-write register // Define read-write register
($mod_name: ident, $struct_name: ident, RW, $inner: ty) => ( ($mod_name: ident, $(#[$outer:meta])* $struct_name: ident, RW, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner); $crate::register_common!($mod_name, $(#[$outer])* $struct_name, volatile_register::RW<$inner>, $inner);
libregister::register_r!($mod_name, $struct_name); $crate::register_r!($mod_name, $struct_name);
libregister::register_w!($mod_name, $struct_name); $crate::register_w!($mod_name, $struct_name);
libregister::register_rw!($mod_name, $struct_name); $crate::register_rw!($mod_name, $struct_name);
); );
// Define read-write register // Define read-write register
($mod_name: ident, $struct_name: ident, VolatileCell, $inner: ty) => ( ($mod_name: ident, $(#[$outer:meta])* $struct_name: ident, VolatileCell, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, VolatileCell<$inner>, $inner); $crate::register_common!($mod_name, $(#[$outer])* $struct_name, VolatileCell<$inner>, $inner);
libregister::register_vcell!($mod_name, $struct_name); $crate::register_vcell!($mod_name, $struct_name);
); );
// Define read-write register with mask on write (for WTC mixed access.) // Define read-write register with mask on write (for WTC mixed access.)
($mod_name: ident, $struct_name: ident, RW, $inner: ty, $mask: expr) => ( ($mod_name: ident, $(#[$outer:meta])* $struct_name: ident, RW, $inner: ty, $mask: expr) => (
libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner); $crate::register_common!($mod_name, $(#[$outer])* $struct_name, volatile_register::RW<$inner>, $inner);
libregister::register_r!($mod_name, $struct_name); $crate::register_r!($mod_name, $struct_name);
libregister::register_w!($mod_name, $struct_name); $crate::register_w!($mod_name, $struct_name);
libregister::register_rw!($mod_name, $struct_name, $mask); $crate::register_rw!($mod_name, $struct_name, $mask);
); );
} }

View File

@ -2,17 +2,29 @@
name = "libsupport_zynq" name = "libsupport_zynq"
description = "Software support for running in the Zynq PS" description = "Software support for running in the Zynq PS"
version = "0.0.0" version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"] authors = ["M-Labs"]
edition = "2018" edition = "2018"
[features] [features]
target_zc706 = ["libboard_zynq/target_zc706"] target_zc706 = ["libboard_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"] target_coraz7 = ["libboard_zynq/target_coraz7"]
target_ebaz4205 = ["libboard_zynq/target_ebaz4205"]
target_redpitaya = ["libboard_zynq/target_redpitaya"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc"]
panic_handler = []
dummy_irq_handler = []
dummy_fiq_handler = []
alloc_core = []
default = ["panic_handler", "dummy_irq_handler", "dummy_fiq_handler"]
[dependencies] [dependencies]
r0 = "1" r0 = "1"
compiler_builtins = "0.1" compiler_builtins = "=0.1.39"
linked_list_allocator = { version = "0.8", default-features = false } linked_list_allocator = { version = "0.8", default-features = false, features = ["const_mut_refs"] }
libregister = { path = "../libregister" } libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" } libcortex_a9 = { path = "../libcortex_a9" }
libboard_zynq = { path = "../libboard_zynq" } libboard_zynq = { path = "../libboard_zynq" }
[build-dependencies]
cc = { version = "1.0" }

24
libsupport_zynq/build.rs Normal file
View File

@ -0,0 +1,24 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
compile_memcpy();
}
fn compile_memcpy() {
use std::path::Path;
extern crate cc;
let cfg = &mut cc::Build::new();
cfg.compiler("clang");
cfg.no_default_flags(true);
cfg.warnings(false);
cfg.flag("--target=armv7-none-eabihf");
let sources = vec![
"memcpy.S",
];
let root = Path::new("src/asm");
for src in sources {
println!("cargo:rerun-if-changed={}", src);
cfg.file(root.join(src));
}
cfg.compile("memcpy");
}

View File

@ -1,21 +0,0 @@
use libboard_zynq::{println, slcr, stdio};
#[no_mangle]
pub unsafe extern "C" fn PrefetchAbort() {
stdio::drop_uart();
println!("PrefetchAbort");
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
loop {}
}
#[no_mangle]
pub unsafe extern "C" fn DataAbort() {
stdio::drop_uart();
println!("DataAbort");
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
loop {}
}

View File

@ -0,0 +1,626 @@
/* Copyright (c) 2013, Linaro Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Linaro Limited nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This memcpy routine is optimised for Cortex-A15 cores and takes advantage
of VFP or NEON when built with the appropriate flags.
Assumptions:
ARMv6 (ARMv7-a if using Neon)
ARM state
Unaligned accesses
LDRD/STRD support unaligned word accesses
If compiled with GCC, this file should be enclosed within following
pre-processing check:
if defined (__ARM_ARCH_7A__) && defined (__ARM_FEATURE_UNALIGNED)
*/
.syntax unified
/* This implementation requires ARM state. */
.arm
#ifdef __ARM_NEON__
.fpu neon
.arch armv7-a
# define FRAME_SIZE 4
# define USE_VFP
# define USE_NEON
#elif !defined (__SOFTFP__)
.arch armv6
.fpu vfpv2
# define FRAME_SIZE 32
# define USE_VFP
#else
.arch armv6
# define FRAME_SIZE 32
#endif
/* Old versions of GAS incorrectly implement the NEON align semantics. */
#ifdef BROKEN_ASM_NEON_ALIGN
#define ALIGN(addr, align) addr,:align
#else
#define ALIGN(addr, align) addr:align
#endif
#define PC_OFFSET 8 /* PC pipeline compensation. */
#define INSN_SIZE 4
/* Call parameters. */
#define dstin r0
#define src r1
#define count r2
/* Locals. */
#define tmp1 r3
#define dst ip
#define tmp2 r10
#ifndef USE_NEON
/* For bulk copies using GP registers. */
#define A_l r2 /* Call-clobbered. */
#define A_h r3 /* Call-clobbered. */
#define B_l r4
#define B_h r5
#define C_l r6
#define C_h r7
#define D_l r8
#define D_h r9
#endif
/* Number of lines ahead to pre-fetch data. If you change this the code
below will need adjustment to compensate. */
#define prefetch_lines 5
#ifdef USE_VFP
.macro cpy_line_vfp vreg, base
vstr \vreg, [dst, #\base]
vldr \vreg, [src, #\base]
vstr d0, [dst, #\base + 8]
vldr d0, [src, #\base + 8]
vstr d1, [dst, #\base + 16]
vldr d1, [src, #\base + 16]
vstr d2, [dst, #\base + 24]
vldr d2, [src, #\base + 24]
vstr \vreg, [dst, #\base + 32]
vldr \vreg, [src, #\base + prefetch_lines * 64 - 32]
vstr d0, [dst, #\base + 40]
vldr d0, [src, #\base + 40]
vstr d1, [dst, #\base + 48]
vldr d1, [src, #\base + 48]
vstr d2, [dst, #\base + 56]
vldr d2, [src, #\base + 56]
.endm
.macro cpy_tail_vfp vreg, base
vstr \vreg, [dst, #\base]
vldr \vreg, [src, #\base]
vstr d0, [dst, #\base + 8]
vldr d0, [src, #\base + 8]
vstr d1, [dst, #\base + 16]
vldr d1, [src, #\base + 16]
vstr d2, [dst, #\base + 24]
vldr d2, [src, #\base + 24]
vstr \vreg, [dst, #\base + 32]
vstr d0, [dst, #\base + 40]
vldr d0, [src, #\base + 40]
vstr d1, [dst, #\base + 48]
vldr d1, [src, #\base + 48]
vstr d2, [dst, #\base + 56]
vldr d2, [src, #\base + 56]
.endm
#endif
.macro def_fn f p2align=0
.text
.p2align \p2align
.global \f
.type \f, %function
\f:
.endm
.global __aeabi_memcpy
.global __aeabi_memcpy4
.global __aeabi_memcpy8
.set __aeabi_memcpy, fast_memcpy
.set __aeabi_memcpy4, fast_memcpy
.set __aeabi_memcpy8, fast_memcpy
def_fn fast_memcpy p2align=6
mov dst, dstin /* Preserve dstin, we need to return it. */
cmp count, #64
bge .Lcpy_not_short
/* Deal with small copies quickly by dropping straight into the
exit block. */
.Ltail63unaligned:
#ifdef USE_NEON
and tmp1, count, #0x38
rsb tmp1, tmp1, #(56 - PC_OFFSET + INSN_SIZE)
add pc, pc, tmp1
vld1.8 {d0}, [src]! /* 14 words to go. */
vst1.8 {d0}, [dst]!
vld1.8 {d0}, [src]! /* 12 words to go. */
vst1.8 {d0}, [dst]!
vld1.8 {d0}, [src]! /* 10 words to go. */
vst1.8 {d0}, [dst]!
vld1.8 {d0}, [src]! /* 8 words to go. */
vst1.8 {d0}, [dst]!
vld1.8 {d0}, [src]! /* 6 words to go. */
vst1.8 {d0}, [dst]!
vld1.8 {d0}, [src]! /* 4 words to go. */
vst1.8 {d0}, [dst]!
vld1.8 {d0}, [src]! /* 2 words to go. */
vst1.8 {d0}, [dst]!
tst count, #4
ldrne tmp1, [src], #4
strne tmp1, [dst], #4
#else
/* Copy up to 15 full words of data. May not be aligned. */
/* Cannot use VFP for unaligned data. */
and tmp1, count, #0x3c
add dst, dst, tmp1
add src, src, tmp1
rsb tmp1, tmp1, #(60 - PC_OFFSET/2 + INSN_SIZE/2)
/* Jump directly into the sequence below at the correct offset. */
add pc, pc, tmp1, lsl #1
ldr tmp1, [src, #-60] /* 15 words to go. */
str tmp1, [dst, #-60]
ldr tmp1, [src, #-56] /* 14 words to go. */
str tmp1, [dst, #-56]
ldr tmp1, [src, #-52]
str tmp1, [dst, #-52]
ldr tmp1, [src, #-48] /* 12 words to go. */
str tmp1, [dst, #-48]
ldr tmp1, [src, #-44]
str tmp1, [dst, #-44]
ldr tmp1, [src, #-40] /* 10 words to go. */
str tmp1, [dst, #-40]
ldr tmp1, [src, #-36]
str tmp1, [dst, #-36]
ldr tmp1, [src, #-32] /* 8 words to go. */
str tmp1, [dst, #-32]
ldr tmp1, [src, #-28]
str tmp1, [dst, #-28]
ldr tmp1, [src, #-24] /* 6 words to go. */
str tmp1, [dst, #-24]
ldr tmp1, [src, #-20]
str tmp1, [dst, #-20]
ldr tmp1, [src, #-16] /* 4 words to go. */
str tmp1, [dst, #-16]
ldr tmp1, [src, #-12]
str tmp1, [dst, #-12]
ldr tmp1, [src, #-8] /* 2 words to go. */
str tmp1, [dst, #-8]
ldr tmp1, [src, #-4]
str tmp1, [dst, #-4]
#endif
lsls count, count, #31
ldrhcs tmp1, [src], #2
ldrbne src, [src] /* Src is dead, use as a scratch. */
strhcs tmp1, [dst], #2
strbne src, [dst]
bx lr
.Lcpy_not_short:
/* At least 64 bytes to copy, but don't know the alignment yet. */
str tmp2, [sp, #-FRAME_SIZE]!
and tmp2, src, #7
and tmp1, dst, #7
cmp tmp1, tmp2
bne .Lcpy_notaligned
#ifdef USE_VFP
/* Magic dust alert! Force VFP on Cortex-A9. Experiments show
that the FP pipeline is much better at streaming loads and
stores. This is outside the critical loop. */
vmov.f32 s0, s0
#endif
/* SRC and DST have the same mutual 32-bit alignment, but we may
still need to pre-copy some bytes to get to natural alignment.
We bring DST into full 64-bit alignment. */
lsls tmp2, dst, #29
beq 1f
rsbs tmp2, tmp2, #0
sub count, count, tmp2, lsr #29
ldrmi tmp1, [src], #4
strmi tmp1, [dst], #4
lsls tmp2, tmp2, #2
ldrhcs tmp1, [src], #2
ldrbne tmp2, [src], #1
strhcs tmp1, [dst], #2
strbne tmp2, [dst], #1
1:
subs tmp2, count, #64 /* Use tmp2 for count. */
blt .Ltail63aligned
cmp tmp2, #512
bge .Lcpy_body_long
.Lcpy_body_medium: /* Count in tmp2. */
#ifdef USE_VFP
1:
vldr d0, [src, #0]
subs tmp2, tmp2, #64
vldr d1, [src, #8]
vstr d0, [dst, #0]
vldr d0, [src, #16]
vstr d1, [dst, #8]
vldr d1, [src, #24]
vstr d0, [dst, #16]
vldr d0, [src, #32]
vstr d1, [dst, #24]
vldr d1, [src, #40]
vstr d0, [dst, #32]
vldr d0, [src, #48]
vstr d1, [dst, #40]
vldr d1, [src, #56]
vstr d0, [dst, #48]
add src, src, #64
vstr d1, [dst, #56]
add dst, dst, #64
bge 1b
tst tmp2, #0x3f
beq .Ldone
.Ltail63aligned: /* Count in tmp2. */
and tmp1, tmp2, #0x38
add dst, dst, tmp1
add src, src, tmp1
rsb tmp1, tmp1, #(56 - PC_OFFSET + INSN_SIZE)
add pc, pc, tmp1
vldr d0, [src, #-56] /* 14 words to go. */
vstr d0, [dst, #-56]
vldr d0, [src, #-48] /* 12 words to go. */
vstr d0, [dst, #-48]
vldr d0, [src, #-40] /* 10 words to go. */
vstr d0, [dst, #-40]
vldr d0, [src, #-32] /* 8 words to go. */
vstr d0, [dst, #-32]
vldr d0, [src, #-24] /* 6 words to go. */
vstr d0, [dst, #-24]
vldr d0, [src, #-16] /* 4 words to go. */
vstr d0, [dst, #-16]
vldr d0, [src, #-8] /* 2 words to go. */
vstr d0, [dst, #-8]
#else
sub src, src, #8
sub dst, dst, #8
1:
ldrd A_l, A_h, [src, #8]
strd A_l, A_h, [dst, #8]
ldrd A_l, A_h, [src, #16]
strd A_l, A_h, [dst, #16]
ldrd A_l, A_h, [src, #24]
strd A_l, A_h, [dst, #24]
ldrd A_l, A_h, [src, #32]
strd A_l, A_h, [dst, #32]
ldrd A_l, A_h, [src, #40]
strd A_l, A_h, [dst, #40]
ldrd A_l, A_h, [src, #48]
strd A_l, A_h, [dst, #48]
ldrd A_l, A_h, [src, #56]
strd A_l, A_h, [dst, #56]
ldrd A_l, A_h, [src, #64]!
strd A_l, A_h, [dst, #64]!
subs tmp2, tmp2, #64
bge 1b
tst tmp2, #0x3f
bne 1f
ldr tmp2,[sp], #FRAME_SIZE
bx lr
1:
add src, src, #8
add dst, dst, #8
.Ltail63aligned: /* Count in tmp2. */
/* Copy up to 7 d-words of data. Similar to Ltail63unaligned, but
we know that the src and dest are 32-bit aligned so we can use
LDRD/STRD to improve efficiency. */
/* TMP2 is now negative, but we don't care about that. The bottom
six bits still tell us how many bytes are left to copy. */
and tmp1, tmp2, #0x38
add dst, dst, tmp1
add src, src, tmp1
rsb tmp1, tmp1, #(56 - PC_OFFSET + INSN_SIZE)
add pc, pc, tmp1
ldrd A_l, A_h, [src, #-56] /* 14 words to go. */
strd A_l, A_h, [dst, #-56]
ldrd A_l, A_h, [src, #-48] /* 12 words to go. */
strd A_l, A_h, [dst, #-48]
ldrd A_l, A_h, [src, #-40] /* 10 words to go. */
strd A_l, A_h, [dst, #-40]
ldrd A_l, A_h, [src, #-32] /* 8 words to go. */
strd A_l, A_h, [dst, #-32]
ldrd A_l, A_h, [src, #-24] /* 6 words to go. */
strd A_l, A_h, [dst, #-24]
ldrd A_l, A_h, [src, #-16] /* 4 words to go. */
strd A_l, A_h, [dst, #-16]
ldrd A_l, A_h, [src, #-8] /* 2 words to go. */
strd A_l, A_h, [dst, #-8]
#endif
tst tmp2, #4
ldrne tmp1, [src], #4
strne tmp1, [dst], #4
lsls tmp2, tmp2, #31 /* Count (tmp2) now dead. */
ldrhcs tmp1, [src], #2
ldrbne tmp2, [src]
strhcs tmp1, [dst], #2
strbne tmp2, [dst]
.Ldone:
ldr tmp2, [sp], #FRAME_SIZE
bx lr
.Lcpy_body_long: /* Count in tmp2. */
/* Long copy. We know that there's at least (prefetch_lines * 64)
bytes to go. */
#ifdef USE_VFP
/* Don't use PLD. Instead, read some data in advance of the current
copy position into a register. This should act like a PLD
operation but we won't have to repeat the transfer. */
vldr d3, [src, #0]
vldr d4, [src, #64]
vldr d5, [src, #128]
vldr d6, [src, #192]
vldr d7, [src, #256]
vldr d0, [src, #8]
vldr d1, [src, #16]
vldr d2, [src, #24]
add src, src, #32
subs tmp2, tmp2, #prefetch_lines * 64 * 2
blt 2f
1:
cpy_line_vfp d3, 0
cpy_line_vfp d4, 64
cpy_line_vfp d5, 128
add dst, dst, #3 * 64
add src, src, #3 * 64
cpy_line_vfp d6, 0
cpy_line_vfp d7, 64
add dst, dst, #2 * 64
add src, src, #2 * 64
subs tmp2, tmp2, #prefetch_lines * 64
bge 1b
2:
cpy_tail_vfp d3, 0
cpy_tail_vfp d4, 64
cpy_tail_vfp d5, 128
add src, src, #3 * 64
add dst, dst, #3 * 64
cpy_tail_vfp d6, 0
vstr d7, [dst, #64]
vldr d7, [src, #64]
vstr d0, [dst, #64 + 8]
vldr d0, [src, #64 + 8]
vstr d1, [dst, #64 + 16]
vldr d1, [src, #64 + 16]
vstr d2, [dst, #64 + 24]
vldr d2, [src, #64 + 24]
vstr d7, [dst, #64 + 32]
add src, src, #96
vstr d0, [dst, #64 + 40]
vstr d1, [dst, #64 + 48]
vstr d2, [dst, #64 + 56]
add dst, dst, #128
add tmp2, tmp2, #prefetch_lines * 64
b .Lcpy_body_medium
#else
/* Long copy. Use an SMS style loop to maximize the I/O
bandwidth of the core. We don't have enough spare registers
to synthesise prefetching, so use PLD operations. */
/* Pre-bias src and dst. */
sub src, src, #8
sub dst, dst, #8
pld [src, #8]
pld [src, #72]
subs tmp2, tmp2, #64
pld [src, #136]
ldrd A_l, A_h, [src, #8]
strd B_l, B_h, [sp, #8]
ldrd B_l, B_h, [src, #16]
strd C_l, C_h, [sp, #16]
ldrd C_l, C_h, [src, #24]
strd D_l, D_h, [sp, #24]
pld [src, #200]
ldrd D_l, D_h, [src, #32]!
b 1f
.p2align 6
2:
pld [src, #232]
strd A_l, A_h, [dst, #40]
ldrd A_l, A_h, [src, #40]
strd B_l, B_h, [dst, #48]
ldrd B_l, B_h, [src, #48]
strd C_l, C_h, [dst, #56]
ldrd C_l, C_h, [src, #56]
strd D_l, D_h, [dst, #64]!
ldrd D_l, D_h, [src, #64]!
subs tmp2, tmp2, #64
1:
strd A_l, A_h, [dst, #8]
ldrd A_l, A_h, [src, #8]
strd B_l, B_h, [dst, #16]
ldrd B_l, B_h, [src, #16]
strd C_l, C_h, [dst, #24]
ldrd C_l, C_h, [src, #24]
strd D_l, D_h, [dst, #32]
ldrd D_l, D_h, [src, #32]
bcs 2b
/* Save the remaining bytes and restore the callee-saved regs. */
strd A_l, A_h, [dst, #40]
add src, src, #40
strd B_l, B_h, [dst, #48]
ldrd B_l, B_h, [sp, #8]
strd C_l, C_h, [dst, #56]
ldrd C_l, C_h, [sp, #16]
strd D_l, D_h, [dst, #64]
ldrd D_l, D_h, [sp, #24]
add dst, dst, #72
tst tmp2, #0x3f
bne .Ltail63aligned
ldr tmp2, [sp], #FRAME_SIZE
bx lr
#endif
.Lcpy_notaligned:
pld [src]
pld [src, #64]
/* There's at least 64 bytes to copy, but there is no mutual
alignment. */
/* Bring DST to 64-bit alignment. */
lsls tmp2, dst, #29
pld [src, #(2 * 64)]
beq 1f
rsbs tmp2, tmp2, #0
sub count, count, tmp2, lsr #29
ldrmi tmp1, [src], #4
strmi tmp1, [dst], #4
lsls tmp2, tmp2, #2
ldrbne tmp1, [src], #1
ldrhcs tmp2, [src], #2
strbne tmp1, [dst], #1
strhcs tmp2, [dst], #2
1:
pld [src, #(3 * 64)]
subs count, count, #64
ldrmi tmp2, [sp], #FRAME_SIZE
bmi .Ltail63unaligned
pld [src, #(4 * 64)]
#ifdef USE_NEON
vld1.8 {d0-d3}, [src]!
vld1.8 {d4-d7}, [src]!
subs count, count, #64
bmi 2f
1:
pld [src, #(4 * 64)]
vst1.8 {d0-d3}, [ALIGN (dst, 64)]!
vld1.8 {d0-d3}, [src]!
vst1.8 {d4-d7}, [ALIGN (dst, 64)]!
vld1.8 {d4-d7}, [src]!
subs count, count, #64
bpl 1b
2:
vst1.8 {d0-d3}, [ALIGN (dst, 64)]!
vst1.8 {d4-d7}, [ALIGN (dst, 64)]!
ands count, count, #0x3f
#else
/* Use an SMS style loop to maximize the I/O bandwidth. */
sub src, src, #4
sub dst, dst, #8
subs tmp2, count, #64 /* Use tmp2 for count. */
ldr A_l, [src, #4]
ldr A_h, [src, #8]
strd B_l, B_h, [sp, #8]
ldr B_l, [src, #12]
ldr B_h, [src, #16]
strd C_l, C_h, [sp, #16]
ldr C_l, [src, #20]
ldr C_h, [src, #24]
strd D_l, D_h, [sp, #24]
ldr D_l, [src, #28]
ldr D_h, [src, #32]!
b 1f
.p2align 6
2:
pld [src, #(5 * 64) - (32 - 4)]
strd A_l, A_h, [dst, #40]
ldr A_l, [src, #36]
ldr A_h, [src, #40]
strd B_l, B_h, [dst, #48]
ldr B_l, [src, #44]
ldr B_h, [src, #48]
strd C_l, C_h, [dst, #56]
ldr C_l, [src, #52]
ldr C_h, [src, #56]
strd D_l, D_h, [dst, #64]!
ldr D_l, [src, #60]
ldr D_h, [src, #64]!
subs tmp2, tmp2, #64
1:
strd A_l, A_h, [dst, #8]
ldr A_l, [src, #4]
ldr A_h, [src, #8]
strd B_l, B_h, [dst, #16]
ldr B_l, [src, #12]
ldr B_h, [src, #16]
strd C_l, C_h, [dst, #24]
ldr C_l, [src, #20]
ldr C_h, [src, #24]
strd D_l, D_h, [dst, #32]
ldr D_l, [src, #28]
ldr D_h, [src, #32]
bcs 2b
/* Save the remaining bytes and restore the callee-saved regs. */
strd A_l, A_h, [dst, #40]
add src, src, #36
strd B_l, B_h, [dst, #48]
ldrd B_l, B_h, [sp, #8]
strd C_l, C_h, [dst, #56]
ldrd C_l, C_h, [sp, #16]
strd D_l, D_h, [dst, #64]
ldrd D_l, D_h, [sp, #24]
add dst, dst, #72
ands count, tmp2, #0x3f
#endif
ldr tmp2, [sp], #FRAME_SIZE
bne .Ltail63unaligned
bx lr
.size memcpy, . - memcpy

View File

@ -2,9 +2,9 @@ use r0::zero_bss;
use core::ptr::write_volatile; use core::ptr::write_volatile;
use libregister::{ use libregister::{
VolatileCell, VolatileCell,
RegisterR, RegisterW, RegisterRW, RegisterR, RegisterRW,
}; };
use libcortex_a9::{asm, regs::*, cache, mmu}; use libcortex_a9::{asm, l2c, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock, enable_fpu, interrupt_handler};
use libboard_zynq::{slcr, mpcore}; use libboard_zynq::{slcr, mpcore};
extern "C" { extern "C" {
@ -18,34 +18,28 @@ extern "C" {
static mut CORE1_ENABLED: VolatileCell<bool> = VolatileCell::new(false); static mut CORE1_ENABLED: VolatileCell<bool> = VolatileCell::new(false);
#[link_section = ".text.boot"] interrupt_handler!(Reset, reset_irq, __stack0_start, __stack1_start, {
#[no_mangle] // no need to setup stack here, as we already did when entering the handler
#[naked] match MPIDR.read().cpu_id() {
pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u32 = 0x3;
match MPIDR.read() & CORE_MASK {
0 => { 0 => {
SP.write(&mut __stack0_start as *mut _ as u32);
boot_core0(); boot_core0();
} }
1 => { 1 => {
while !CORE1_ENABLED.get() { while !CORE1_ENABLED.get() {
asm::wfe(); spin_lock_yield();
} }
SP.write(&mut __stack1_start as *mut _ as u32);
boot_core1(); boot_core1();
} }
_ => unreachable!(), _ => unreachable!(),
} }
} });
#[naked]
#[inline(never)] #[inline(never)]
unsafe fn boot_core0() -> ! { unsafe extern "C" fn boot_core0() -> ! {
l1_cache_init(); l1_cache_init();
let mpcore = mpcore::RegisterBlock::new(); enable_fpu();
let mpcore = mpcore::RegisterBlock::mpcore();
mpcore.scu_invalidate.invalidate_all_cores(); mpcore.scu_invalidate.invalidate_all_cores();
zero_bss(&mut __bss_start, &mut __bss_end); zero_bss(&mut __bss_start, &mut __bss_end);
@ -55,30 +49,35 @@ unsafe fn boot_core0() -> ! {
mmu::with_mmu(mmu_table, || { mmu::with_mmu(mmu_table, || {
mpcore.scu_control.start(); mpcore.scu_control.start();
ACTLR.enable_smp(); ACTLR.enable_smp();
ACTLR.enable_prefetch();
// TODO: Barriers reqd when core1 is not yet starting? // TODO: Barriers reqd when core1 is not yet starting?
asm::dmb(); asm::dmb();
asm::dsb(); asm::dsb();
asm::enable_fiq();
asm::enable_irq();
main_core0(); main_core0();
panic!("return from main"); panic!("return from main");
}); });
} }
#[naked]
#[inline(never)] #[inline(never)]
unsafe fn boot_core1() -> ! { unsafe extern "C" fn boot_core1() -> ! {
l1_cache_init(); l1_cache_init();
let mpcore = mpcore::RegisterBlock::new(); let mpcore = mpcore::RegisterBlock::mpcore();
mpcore.scu_invalidate.invalidate_core1(); mpcore.scu_invalidate.invalidate_core1();
let mmu_table = mmu::L1Table::get(); let mmu_table = mmu::L1Table::get();
mmu::with_mmu(mmu_table, || { mmu::with_mmu(mmu_table, || {
ACTLR.enable_smp(); ACTLR.enable_smp();
ACTLR.enable_prefetch();
// TODO: Barriers reqd when core1 is not yet starting? // TODO: Barriers reqd when core1 is not yet starting?
asm::dmb(); asm::dmb();
asm::dsb(); asm::dsb();
asm::enable_fiq();
asm::enable_irq();
main_core1(); main_core1();
panic!("return from main_core1"); panic!("return from main_core1");
}); });
@ -101,7 +100,7 @@ fn l1_cache_init() {
// for all of the L1 data cache rather than a (previously // for all of the L1 data cache rather than a (previously
// unspecified) combination of one cache set and one cache // unspecified) combination of one cache set and one cache
// way. // way.
dciall(); dciall_l1();
} }
pub struct Core1 { pub struct Core1 {
@ -131,12 +130,13 @@ impl Core1 {
unsafe { unsafe {
CORE1_ENABLED.set(true); CORE1_ENABLED.set(true);
} }
// Ensure values have been written to cache
asm::dmb();
// Flush cache-line // Flush cache-line
cache::dccmvac(unsafe { &CORE1_ENABLED } as *const _ as usize); cache::dcc(unsafe { &CORE1_ENABLED });
if sdram { if sdram {
cache::dccmvac(0); cache::dccmvac(0);
asm::dsb();
l2c::l2_cache_clean(0);
l2c::l2_cache_sync();
} }
// wake up core1 // wake up core1
@ -144,6 +144,7 @@ impl Core1 {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false)); slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false)); slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
}); });
notify_spin_lock();
Core1 {} Core1 {}
} }
@ -151,6 +152,8 @@ impl Core1 {
pub fn disable(&self) { pub fn disable(&self) {
unsafe { unsafe {
CORE1_ENABLED.set(false); CORE1_ENABLED.set(false);
cache::dccmvac(&CORE1_ENABLED as *const _ as usize);
asm::dsb();
} }
self.restart(); self.restart();
} }
@ -158,7 +161,9 @@ impl Core1 {
pub fn restart(&self) { pub fn restart(&self) {
slcr::RegisterBlock::unlocked(|slcr| { slcr::RegisterBlock::unlocked(|slcr| {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true)); slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false)); slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
}); });
} }
} }

View File

@ -0,0 +1,54 @@
use libregister::{RegisterR, RegisterW};
use libcortex_a9::{regs::{DFSR, MPIDR, VBAR}, interrupt_handler};
use libboard_zynq::{println, stdio};
pub fn set_vector_table(base_addr: u32){
VBAR.write(base_addr);
}
interrupt_handler!(UndefinedInstruction, undefined_instruction, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("UndefinedInstruction");
loop {}
});
interrupt_handler!(SoftwareInterrupt, software_interrupt, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("SoftwareInterrupt");
loop {}
});
interrupt_handler!(PrefetchAbort, prefetch_abort, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("PrefetchAbort");
loop {}
});
interrupt_handler!(DataAbort, data_abort, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("DataAbort on core {}", MPIDR.read().cpu_id());
println!("DFSR: {:03X}", DFSR.read());
loop {}
});
interrupt_handler!(ReservedException, reserved_exception, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("ReservedException");
loop {}
});
#[cfg(feature = "dummy_irq_handler")]
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("IRQ");
loop {}
});
#[cfg(feature = "dummy_fiq_handler")]
interrupt_handler!(FIQ, fiq, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("FIQ");
loop {}
});

View File

@ -1,13 +1,16 @@
#![no_std] #![no_std]
#![feature(naked_functions)]
#![feature(alloc_error_handler)] #![feature(alloc_error_handler)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![feature(naked_functions)]
#![feature(asm)]
pub extern crate alloc; pub extern crate alloc;
pub extern crate compiler_builtins; pub extern crate compiler_builtins;
pub mod boot; pub mod boot;
mod abort; pub mod exception_vectors;
#[cfg(feature = "panic_handler")]
mod panic; mod panic;
pub mod ram; pub mod ram;

View File

@ -1,4 +1,6 @@
use libboard_zynq::{slcr, print, println}; use libboard_zynq::{print, println};
#[cfg(feature = "target_kasli_soc")]
use libboard_zynq::error_led::ErrorLED;
#[panic_handler] #[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { fn panic(info: &core::panic::PanicInfo) -> ! {
@ -13,7 +15,10 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
} else { } else {
println!(""); println!("");
} }
#[cfg(feature = "target_kasli_soc")]
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset()); {
let mut err_led = ErrorLED::error_led();
err_led.toggle(true);
}
loop {} loop {}
} }

View File

@ -1,55 +1,100 @@
use alloc::alloc::Layout;
use core::alloc::GlobalAlloc; use core::alloc::GlobalAlloc;
use core::ptr::NonNull; use core::ptr::NonNull;
use alloc::alloc::Layout; use libcortex_a9::{
mutex::Mutex,
regs::MPIDR
};
use libregister::RegisterR;
use linked_list_allocator::Heap; use linked_list_allocator::Heap;
use libcortex_a9::mutex::Mutex; #[cfg(not(feature = "alloc_core"))]
use libboard_zynq::ddr::DdrRam; use libboard_zynq::ddr::DdrRam;
#[global_allocator] #[global_allocator]
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(Mutex::new(Heap::empty())); static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(
Mutex::new(Heap::empty()),
Mutex::new(Heap::empty()),
);
/// LockedHeap doesn't lock properly struct CortexA9Alloc(Mutex<Heap>, Mutex<Heap>);
struct CortexA9Alloc(Mutex<Heap>);
unsafe impl Sync for CortexA9Alloc {} unsafe impl Sync for CortexA9Alloc {}
unsafe impl GlobalAlloc for CortexA9Alloc { unsafe impl GlobalAlloc for CortexA9Alloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.0.lock() if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
&self.0
} else {
&self.1
}
.lock()
.allocate_first_fit(layout) .allocate_first_fit(layout)
.ok() .ok()
.map_or(0 as *mut u8, |allocation| allocation.as_ptr()) .map_or(0 as *mut u8, |allocation| allocation.as_ptr())
} }
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.0.lock() if cfg!(not(feature = "alloc_core"))
|| ((&__heap0_start as *const usize as usize <= ptr as usize)
&& ((ptr as usize) < &__heap0_end as *const usize as usize))
{
&self.0
} else {
&self.1
}
.lock()
.deallocate(NonNull::new_unchecked(ptr), layout) .deallocate(NonNull::new_unchecked(ptr), layout)
} }
} }
#[cfg(not(feature = "alloc_core"))]
pub fn init_alloc_ddr(ddr: &mut DdrRam) { pub fn init_alloc_ddr(ddr: &mut DdrRam) {
unsafe { unsafe {
ALLOCATOR.0.lock() ALLOCATOR
.0
.lock()
.init(ddr.ptr::<u8>() as usize, ddr.size()); .init(ddr.ptr::<u8>() as usize, ddr.size());
} }
} }
extern "C" { extern "C" {
static __heap_start: usize; static __heap0_start: usize;
static __heap_end: usize; static __heap0_end: usize;
#[cfg(feature = "alloc_core")]
static __heap1_start: usize;
#[cfg(feature = "alloc_core")]
static __heap1_end: usize;
} }
pub fn init_alloc_linker() { pub fn init_alloc_core0() {
unsafe { unsafe {
let start = &__heap_start as *const usize as usize; let start = &__heap0_start as *const usize as usize;
let end = &__heap_end as *const usize as usize; let end = &__heap0_end as *const usize as usize;
ALLOCATOR.0.lock() ALLOCATOR.0.lock().init(start, end - start);
.init(start, end - start);
} }
} }
#[cfg(feature = "alloc_core")]
pub fn init_alloc_core1() {
unsafe {
let start = &__heap1_start as *const usize as usize;
let end = &__heap1_end as *const usize as usize;
ALLOCATOR.1.lock().init(start, end - start);
}
}
#[alloc_error_handler] #[alloc_error_handler]
fn alloc_error(_: core::alloc::Layout) -> ! { fn alloc_error(layout: core::alloc::Layout) -> ! {
panic!("alloc_error") let id = MPIDR.read().cpu_id();
let used = if cfg!(not(feature = "alloc_core")) || id == 0 {
ALLOCATOR.0.lock().used()
} else {
ALLOCATOR.1.lock().used()
};
panic!(
"Core {} alloc_error, layout: {:?}, used memory: {}",
id,
layout,
used
);
} }

138
llvm/11/clang/default.nix Normal file
View File

@ -0,0 +1,138 @@
{ lib, stdenv, llvm_meta, fetch, fetchpatch, substituteAll, cmake, libxml2, libllvm, version, clang-tools-extra_src, python3
, buildLlvmTools
, fixDarwinDylibNames
, enableManpages ? false
, enablePolly ? false
}:
let
self = stdenv.mkDerivation ({
pname = "clang";
inherit version;
src = fetch "clang" "12sm91qx2m79cvj75a9aazf2x8xybjbd593dv6v7rxficpq8i0ha";
inherit clang-tools-extra_src;
unpackPhase = ''
unpackFile $src
mv clang-* clang
sourceRoot=$PWD/clang
unpackFile ${clang-tools-extra_src}
mv clang-tools-extra-* $sourceRoot/tools/extra
'';
nativeBuildInputs = [ cmake python3 ]
++ lib.optional enableManpages python3.pkgs.sphinx
++ lib.optional stdenv.hostPlatform.isDarwin fixDarwinDylibNames;
buildInputs = [ libxml2 libllvm ];
cmakeFlags = [
"-DCLANGD_BUILD_XPC=OFF"
"-DLLVM_ENABLE_RTTI=ON"
] ++ lib.optionals enableManpages [
"-DCLANG_INCLUDE_DOCS=ON"
"-DLLVM_ENABLE_SPHINX=ON"
"-DSPHINX_OUTPUT_MAN=ON"
"-DSPHINX_OUTPUT_HTML=OFF"
"-DSPHINX_WARNINGS_AS_ERRORS=OFF"
] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"-DLLVM_TABLEGEN_EXE=${buildLlvmTools.llvm}/bin/llvm-tblgen"
"-DCLANG_TABLEGEN=${buildLlvmTools.libclang.dev}/bin/clang-tblgen"
] ++ lib.optionals enablePolly [
"-DWITH_POLLY=ON"
"-DLINK_POLLY_INTO_TOOLS=ON"
];
patches = [
./purity.patch
# https://reviews.llvm.org/D51899
./gnu-install-dirs.patch
(substituteAll {
src = ../../clang-11-12-LLVMgold-path.patch;
libllvmLibdir = "${libllvm.lib}/lib";
})
];
postPatch = ''
sed -i -e 's/DriverArgs.hasArg(options::OPT_nostdlibinc)/true/' \
-e 's/Args.hasArg(options::OPT_nostdlibinc)/true/' \
lib/Driver/ToolChains/*.cpp
'' + lib.optionalString stdenv.hostPlatform.isMusl ''
sed -i -e 's/lgcc_s/lgcc_eh/' lib/Driver/ToolChains/*.cpp
'' + lib.optionalString stdenv.hostPlatform.isDarwin ''
substituteInPlace tools/extra/clangd/CMakeLists.txt \
--replace "NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB" FALSE
'';
outputs = [ "out" "lib" "dev" "python" ];
postInstall = ''
ln -sv $out/bin/clang $out/bin/cpp
# Move libclang to 'lib' output
moveToOutput "lib/libclang.*" "$lib"
moveToOutput "lib/libclang-cpp.*" "$lib"
substituteInPlace $out/lib/cmake/clang/ClangTargets-release.cmake \
--replace "\''${_IMPORT_PREFIX}/lib/libclang." "$lib/lib/libclang." \
--replace "\''${_IMPORT_PREFIX}/lib/libclang-cpp." "$lib/lib/libclang-cpp."
mkdir -p $python/bin $python/share/{clang,scan-view}
mv $out/bin/{git-clang-format,scan-view} $python/bin
if [ -e $out/bin/set-xcode-analyzer ]; then
mv $out/bin/set-xcode-analyzer $python/bin
fi
mv $out/share/clang/*.py $python/share/clang
mv $out/share/scan-view/*.py $python/share/scan-view
rm $out/bin/c-index-test
patchShebangs $python/bin
mkdir -p $dev/bin
cp bin/clang-tblgen $dev/bin
'';
passthru = {
inherit libllvm;
isClang = true;
hardeningUnsupportedFlags = [ "fortify3" ];
};
meta = llvm_meta // {
homepage = "https://clang.llvm.org/";
description = "A C language family frontend for LLVM";
longDescription = ''
The Clang project provides a language front-end and tooling
infrastructure for languages in the C language family (C, C++, Objective
C/C++, OpenCL, CUDA, and RenderScript) for the LLVM project.
It aims to deliver amazingly fast compiles, extremely useful error and
warning messages and to provide a platform for building great source
level tools. The Clang Static Analyzer and clang-tidy are tools that
automatically find bugs in your code, and are great examples of the sort
of tools that can be built using the Clang frontend as a library to
parse C/C++ code.
'';
mainProgram = "clang";
};
} // lib.optionalAttrs enableManpages {
pname = "clang-manpages";
buildPhase = ''
make docs-clang-man
'';
installPhase = ''
mkdir -p $out/share/man/man1
# Manually install clang manpage
cp docs/man/*.1 $out/share/man/man1/
'';
outputs = [ "out" ];
doCheck = false;
meta = llvm_meta // {
description = "man page for Clang ${version}";
};
});
in self

View File

@ -0,0 +1,235 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb4b801f01c8..77a8b43b22c8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,8 @@ endif()
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
project(Clang)
+ include(GNUInstallDirs)
+
set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard to conform to")
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
@@ -447,7 +449,7 @@ include_directories(BEFORE
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY include/clang include/clang-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT clang-headers
FILES_MATCHING
PATTERN "*.def"
@@ -457,7 +459,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/clang
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT clang-headers
FILES_MATCHING
PATTERN "CMakeFiles" EXCLUDE
@@ -477,7 +479,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
add_custom_target(bash-autocomplete DEPENDS utils/bash-autocomplete.sh)
install(PROGRAMS utils/bash-autocomplete.sh
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT bash-autocomplete)
if(NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-bash-autocomplete
diff --git a/cmake/modules/AddClang.cmake b/cmake/modules/AddClang.cmake
index 704278a0e93b..d25c8d325c71 100644
--- a/cmake/modules/AddClang.cmake
+++ b/cmake/modules/AddClang.cmake
@@ -123,9 +123,9 @@ macro(add_clang_library name)
install(TARGETS ${lib}
COMPONENT ${lib}
${export_to_clangtargets}
- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
- RUNTIME DESTINATION bin)
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-${lib}
@@ -170,7 +170,7 @@ macro(add_clang_tool name)
install(TARGETS ${name}
${export_to_clangtargets}
- RUNTIME DESTINATION bin
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT ${name})
if(NOT LLVM_ENABLE_IDE)
@@ -185,7 +185,7 @@ endmacro()
macro(add_clang_symlink name dest)
add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE)
# Always generate install targets
- llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE)
+ llvm_install_symlink(${name} ${dest} ${CMAKE_INSTALL_FULL_BINDIR} ALWAYS_GENERATE)
endmacro()
function(clang_target_link_libraries target type)
diff --git a/lib/Headers/CMakeLists.txt b/lib/Headers/CMakeLists.txt
index 0692fe75a441..6f201e7207d0 100644
--- a/lib/Headers/CMakeLists.txt
+++ b/lib/Headers/CMakeLists.txt
@@ -208,7 +208,7 @@ set_target_properties(clang-resource-headers PROPERTIES
FOLDER "Misc"
RUNTIME_OUTPUT_DIRECTORY "${output_dir}")
-set(header_install_dir lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}/include)
+set(header_install_dir ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}/include)
install(
FILES ${files} ${generated_files}
diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt
index ceef4b08637c..8efad5520ca4 100644
--- a/tools/c-index-test/CMakeLists.txt
+++ b/tools/c-index-test/CMakeLists.txt
@@ -54,7 +54,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
set_property(TARGET c-index-test APPEND PROPERTY INSTALL_RPATH
"@executable_path/../../lib")
else()
- set(INSTALL_DESTINATION bin)
+ set(INSTALL_DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
install(TARGETS c-index-test
diff --git a/tools/clang-format/CMakeLists.txt b/tools/clang-format/CMakeLists.txt
index 35ecdb11253c..d77d75de0094 100644
--- a/tools/clang-format/CMakeLists.txt
+++ b/tools/clang-format/CMakeLists.txt
@@ -21,20 +21,20 @@ if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE )
endif()
install(PROGRAMS clang-format-bbedit.applescript
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-format)
install(PROGRAMS clang-format-diff.py
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-format)
install(PROGRAMS clang-format-sublime.py
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-format)
install(PROGRAMS clang-format.el
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-format)
install(PROGRAMS clang-format.py
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-format)
install(PROGRAMS git-clang-format
- DESTINATION bin
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT clang-format)
diff --git a/tools/clang-rename/CMakeLists.txt b/tools/clang-rename/CMakeLists.txt
index cda8e29ec5b1..0134d8ccd70b 100644
--- a/tools/clang-rename/CMakeLists.txt
+++ b/tools/clang-rename/CMakeLists.txt
@@ -19,8 +19,8 @@ clang_target_link_libraries(clang-rename
)
install(PROGRAMS clang-rename.py
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-rename)
install(PROGRAMS clang-rename.el
- DESTINATION share/clang
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/clang
COMPONENT clang-rename)
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index 5cd9ac5cddc1..a197676fedbd 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -165,7 +165,7 @@ endif()
if(INTERNAL_INSTALL_PREFIX)
set(LIBCLANG_HEADERS_INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/include")
else()
- set(LIBCLANG_HEADERS_INSTALL_DESTINATION include)
+ set(LIBCLANG_HEADERS_INSTALL_DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif()
install(DIRECTORY ../../include/clang-c
@@ -196,7 +196,7 @@ foreach(PythonVersion ${CLANG_PYTHON_BINDINGS_VERSIONS})
COMPONENT
libclang-python-bindings
DESTINATION
- "lib${LLVM_LIBDIR_SUFFIX}/python${PythonVersion}/site-packages")
+ "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}/python${PythonVersion}/site-packages")
endforeach()
if(NOT LLVM_ENABLE_IDE)
add_custom_target(libclang-python-bindings)
diff --git a/tools/scan-build/CMakeLists.txt b/tools/scan-build/CMakeLists.txt
index ec0702d76f18..d25d982f51da 100644
--- a/tools/scan-build/CMakeLists.txt
+++ b/tools/scan-build/CMakeLists.txt
@@ -47,7 +47,7 @@ if(CLANG_INSTALL_SCANBUILD)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile})
list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile})
install(PROGRAMS bin/${BinFile}
- DESTINATION bin
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT scan-build)
endforeach()
@@ -61,7 +61,7 @@ if(CLANG_INSTALL_SCANBUILD)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${LibexecFile})
list(APPEND Depends ${CMAKE_BINARY_DIR}/libexec/${LibexecFile})
install(PROGRAMS libexec/${LibexecFile}
- DESTINATION libexec
+ DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}
COMPONENT scan-build)
endforeach()
@@ -89,7 +89,7 @@ if(CLANG_INSTALL_SCANBUILD)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/share/scan-build/${ShareFile})
list(APPEND Depends ${CMAKE_BINARY_DIR}/share/scan-build/${ShareFile})
install(FILES share/scan-build/${ShareFile}
- DESTINATION share/scan-build
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/scan-build
COMPONENT scan-build)
endforeach()
diff --git a/tools/scan-view/CMakeLists.txt b/tools/scan-view/CMakeLists.txt
index 22edb974bac7..9f140a9a4538 100644
--- a/tools/scan-view/CMakeLists.txt
+++ b/tools/scan-view/CMakeLists.txt
@@ -22,7 +22,7 @@ if(CLANG_INSTALL_SCANVIEW)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile})
list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile})
install(PROGRAMS bin/${BinFile}
- DESTINATION bin
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT scan-view)
endforeach()
@@ -36,7 +36,7 @@ if(CLANG_INSTALL_SCANVIEW)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/share/${ShareFile})
list(APPEND Depends ${CMAKE_BINARY_DIR}/share/scan-view/${ShareFile})
install(FILES share/${ShareFile}
- DESTINATION share/scan-view
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/scan-view
COMPONENT scan-view)
endforeach()
diff --git a/utils/hmaptool/CMakeLists.txt b/utils/hmaptool/CMakeLists.txt
index 62f2de0cb15c..6aa66825b6ec 100644
--- a/utils/hmaptool/CMakeLists.txt
+++ b/utils/hmaptool/CMakeLists.txt
@@ -10,7 +10,7 @@ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin/${CLANG_HM
list(APPEND Depends ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin/${CLANG_HMAPTOOL})
install(PROGRAMS ${CLANG_HMAPTOOL}
- DESTINATION bin
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT hmaptool)
add_custom_target(hmaptool ALL DEPENDS ${Depends})

View File

@ -0,0 +1,28 @@
From 4add81bba40dcec62c4ea4481be8e35ac53e89d8 Mon Sep 17 00:00:00 2001
From: Will Dietz <w@wdtz.org>
Date: Thu, 18 May 2017 11:56:12 -0500
Subject: [PATCH] "purity" patch for 5.0
---
lib/Driver/ToolChains/Gnu.cpp | 7 -------
1 file changed, 7 deletions(-)
diff --git a/lib/Driver/ToolChains/Gnu.cpp b/lib/Driver/ToolChains/Gnu.cpp
index fe3c0191bb..c6a482bece 100644
--- a/lib/Driver/ToolChains/Gnu.cpp
+++ b/lib/Driver/ToolChains/Gnu.cpp
@@ -487,12 +487,6 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
if (!IsStatic) {
if (Args.hasArg(options::OPT_rdynamic))
CmdArgs.push_back("-export-dynamic");
-
- if (!Args.hasArg(options::OPT_shared) && !IsStaticPIE) {
- CmdArgs.push_back("-dynamic-linker");
- CmdArgs.push_back(Args.MakeArgString(Twine(D.DyldPrefix) +
- ToolChain.getDynamicLinker(Args)));
- }
}
CmdArgs.push_back("-o");
--
2.11.0

296
llvm/11/default.nix Normal file
View File

@ -0,0 +1,296 @@
{ lowPrio, newScope, pkgs, lib, stdenv, cmake
, gccForLibs, preLibcCrossHeaders
, libxml2, python3, isl, fetchurl, overrideCC, wrapCCWith, wrapBintoolsWith
, buildLlvmTools # tools, but from the previous stage, for cross
, targetLlvmLibraries # libraries, but from the next stage, for cross
, targetLlvm
# This is the default binutils, but with *this* version of LLD rather
# than the default LLVM version's, if LLD is the choice. We use these for
# the `useLLVM` bootstrapping below.
, bootBintoolsNoLibc ?
if stdenv.targetPlatform.linker == "lld"
then null
else pkgs.bintoolsNoLibc
, bootBintools ?
if stdenv.targetPlatform.linker == "lld"
then null
else pkgs.bintools
}:
let
release_version = "11.1.0";
candidate = ""; # empty or "rcN"
dash-candidate = lib.optionalString (candidate != "") "-${candidate}";
version = "${release_version}${dash-candidate}"; # differentiating these (variables) is important for RCs
targetConfig = stdenv.targetPlatform.config;
fetch = name: sha256: fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/${name}-${release_version}${candidate}.src.tar.xz";
inherit sha256;
};
clang-tools-extra_src = fetch "clang-tools-extra" "18n1w1hkv931xzq02b34wglbv6zd6sd0r5kb8piwvag7klj7qw3n";
llvm_meta = {
license = lib.licenses.ncsa;
# See llvm/cmake/config-ix.cmake.
platforms =
lib.platforms.aarch64 ++
lib.platforms.arm ++
lib.platforms.mips ++
lib.platforms.riscv ++
lib.platforms.wasi ++
lib.platforms.x86;
};
tools = lib.makeExtensible (tools: let
callPackage = newScope (tools // { inherit stdenv cmake libxml2 python3 isl release_version version fetch buildLlvmTools; });
mkExtraBuildCommands0 = cc: ''
rsrc="$out/resource-root"
mkdir "$rsrc"
ln -s "${cc.lib}/lib/clang/${release_version}/include" "$rsrc"
echo "-resource-dir=$rsrc" >> $out/nix-support/cc-cflags
'';
mkExtraBuildCommands = cc: mkExtraBuildCommands0 cc + ''
ln -s "${targetLlvmLibraries.compiler-rt.out}/lib" "$rsrc/lib"
ln -s "${targetLlvmLibraries.compiler-rt.out}/share" "$rsrc/share"
'';
bintoolsNoLibc' =
if bootBintoolsNoLibc == null
then tools.bintoolsNoLibc
else bootBintoolsNoLibc;
bintools' =
if bootBintools == null
then tools.bintools
else bootBintools;
in {
libllvm = callPackage ./llvm {
inherit llvm_meta;
};
# `llvm` historically had the binaries. When choosing an output explicitly,
# we need to reintroduce `outputSpecified` to get the expected behavior e.g. of lib.get*
llvm = tools.libllvm;
libllvm-polly = callPackage ./llvm {
inherit llvm_meta;
enablePolly = true;
};
llvm-polly = tools.libllvm-polly.lib // { outputSpecified = false; };
libclang = callPackage ./clang {
inherit clang-tools-extra_src llvm_meta;
};
clang-unwrapped = tools.libclang;
clang-polly-unwrapped = callPackage ./clang {
inherit llvm_meta;
inherit clang-tools-extra_src;
libllvm = tools.libllvm-polly;
enablePolly = true;
};
llvm-manpages = lowPrio (tools.libllvm.override {
enableManpages = true;
python3 = pkgs.python3; # don't use python-boot
});
clang-manpages = lowPrio (tools.libclang.override {
enableManpages = true;
python3 = pkgs.python3; # don't use python-boot
});
# disabled until recommonmark supports sphinx 3
# lldb-manpages = lowPrio (tools.lldb.override {
# enableManpages = true;
# python3 = pkgs.python3; # don't use python-boot
# });
# pick clang appropriate for package set we are targeting
clang =
/**/ if stdenv.targetPlatform.libc == null then tools.clangNoLibc
else if stdenv.targetPlatform.useLLVM or false then tools.clangUseLLVM
else if (pkgs.targetPackages.stdenv or stdenv).cc.isGNU then tools.libstdcxxClang
else tools.libcxxClang;
libstdcxxClang = wrapCCWith rec {
cc = tools.clang-unwrapped;
# libstdcxx is taken from gcc in an ad-hoc way in cc-wrapper.
libcxx = null;
extraPackages = [
targetLlvmLibraries.compiler-rt
];
extraBuildCommands = mkExtraBuildCommands cc;
};
libcxxClang = wrapCCWith rec {
cc = tools.clang-unwrapped;
libcxx = targetLlvmLibraries.libcxx;
extraPackages = [
libcxx.cxxabi
targetLlvmLibraries.compiler-rt
];
extraBuildCommands = mkExtraBuildCommands cc;
};
lld = callPackage ./lld {
inherit llvm_meta;
};
lldb = callPackage ../common/lldb.nix {
src = fetch "lldb" "1vlyg015dyng43xqb8cg2l6r9ix8klibxsajazbfnckdnh54hwxj";
patches = [
./lldb/procfs.patch
./lldb/gnu-install-dirs.patch
];
inherit llvm_meta;
};
# Below, is the LLVM bootstrapping logic. It handles building a
# fully LLVM toolchain from scratch. No GCC toolchain should be
# pulled in. As a consequence, it is very quick to build different
# targets provided by LLVM and we can also build for what GCC
# doesnt support like LLVM. Probably we should move to some other
# file.
bintools-unwrapped = callPackage ../common/bintools.nix { };
bintoolsNoLibc = wrapBintoolsWith {
bintools = tools.bintools-unwrapped;
libc = preLibcCrossHeaders;
};
bintools = wrapBintoolsWith {
bintools = tools.bintools-unwrapped;
};
clangUseLLVM = wrapCCWith rec {
cc = tools.clang-unwrapped;
libcxx = targetLlvmLibraries.libcxx;
bintools = bintools';
extraPackages = [
libcxx.cxxabi
targetLlvmLibraries.compiler-rt
] ++ lib.optionals (!stdenv.targetPlatform.isWasm) [
targetLlvmLibraries.libunwind
];
extraBuildCommands = ''
echo "-rtlib=compiler-rt -Wno-unused-command-line-argument" >> $out/nix-support/cc-cflags
echo "-B${targetLlvmLibraries.compiler-rt}/lib" >> $out/nix-support/cc-cflags
'' + lib.optionalString (!stdenv.targetPlatform.isWasm) ''
echo "--unwindlib=libunwind" >> $out/nix-support/cc-cflags
'' + lib.optionalString (!stdenv.targetPlatform.isWasm && stdenv.targetPlatform.useLLVM or false) ''
echo "-lunwind" >> $out/nix-support/cc-ldflags
'' + lib.optionalString stdenv.targetPlatform.isWasm ''
echo "-fno-exceptions" >> $out/nix-support/cc-cflags
'' + mkExtraBuildCommands cc;
};
clangNoLibcxx = wrapCCWith rec {
cc = tools.clang-unwrapped;
libcxx = null;
bintools = bintools';
extraPackages = [
targetLlvmLibraries.compiler-rt
];
extraBuildCommands = ''
echo "-rtlib=compiler-rt" >> $out/nix-support/cc-cflags
echo "-B${targetLlvmLibraries.compiler-rt}/lib" >> $out/nix-support/cc-cflags
echo "-nostdlib++" >> $out/nix-support/cc-cflags
'' + mkExtraBuildCommands cc;
};
clangNoLibc = wrapCCWith rec {
cc = tools.clang-unwrapped;
libcxx = null;
bintools = bintoolsNoLibc';
extraPackages = [
targetLlvmLibraries.compiler-rt
];
extraBuildCommands = ''
echo "-rtlib=compiler-rt" >> $out/nix-support/cc-cflags
echo "-B${targetLlvmLibraries.compiler-rt}/lib" >> $out/nix-support/cc-cflags
'' + mkExtraBuildCommands cc;
};
clangNoCompilerRt = wrapCCWith rec {
cc = tools.clang-unwrapped;
libcxx = null;
bintools = bintoolsNoLibc';
extraPackages = [ ];
extraBuildCommands = ''
echo "-nostartfiles" >> $out/nix-support/cc-cflags
'' + mkExtraBuildCommands0 cc;
};
clangNoCompilerRtWithLibc = wrapCCWith rec {
cc = tools.clang-unwrapped;
libcxx = null;
bintools = bintools';
extraPackages = [ ];
extraBuildCommands = mkExtraBuildCommands0 cc;
};
});
libraries = lib.makeExtensible (libraries: let
callPackage = newScope (libraries // buildLlvmTools // { inherit stdenv cmake libxml2 python3 isl release_version version fetch; });
in {
compiler-rt-libc = callPackage ./compiler-rt {
inherit llvm_meta;
stdenv = if (stdenv.hostPlatform.useLLVM or false) || (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) || (stdenv.hostPlatform.isRiscV && stdenv.hostPlatform.is32bit)
then overrideCC stdenv buildLlvmTools.clangNoCompilerRtWithLibc
else stdenv;
};
compiler-rt-no-libc = callPackage ./compiler-rt {
inherit llvm_meta;
stdenv = if (stdenv.hostPlatform.useLLVM or false) || (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64)
then overrideCC stdenv buildLlvmTools.clangNoCompilerRt
else stdenv;
};
# N.B. condition is safe because without useLLVM both are the same.
compiler-rt = if stdenv.hostPlatform.isAndroid || (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) || (stdenv.hostPlatform.libc == "newlib")
then libraries.compiler-rt-libc
else libraries.compiler-rt-no-libc;
stdenv = overrideCC stdenv buildLlvmTools.clang;
libcxxStdenv = overrideCC stdenv buildLlvmTools.libcxxClang;
libcxx = callPackage ./libcxx {
inherit llvm_meta;
stdenv = if (stdenv.hostPlatform.useLLVM or false) || (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64)
then overrideCC stdenv buildLlvmTools.clangNoLibcxx
else stdenv;
};
libcxxabi = callPackage ./libcxxabi {
inherit llvm_meta;
stdenv = if (stdenv.hostPlatform.useLLVM or false) || (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64)
then overrideCC stdenv buildLlvmTools.clangNoLibcxx
else stdenv;
};
libunwind = callPackage ./libunwind {
inherit llvm_meta;
stdenv = if (stdenv.hostPlatform.useLLVM or false) || (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64)
then overrideCC stdenv buildLlvmTools.clangNoLibcxx
else stdenv;
};
openmp = callPackage ./openmp {
inherit llvm_meta targetLlvm;
};
});
noExtend = extensible: builtins.removeAttrs extensible [ "extend" ];
in { inherit tools libraries release_version; } // (noExtend libraries) // (noExtend tools)

363
llvm/11/llvm/default.nix Normal file
View File

@ -0,0 +1,363 @@
{ lib, stdenv, llvm_meta
, pkgsBuildBuild
, fetch
, fetchpatch
, cmake
, python3
, libffi
, enableGoldPlugin ? false
, libbfd
, libpfm
, libxml2
, ncurses
, version
, release_version
, zlib
, buildLlvmTools
, debugVersion ? false
, doCheck ? stdenv.isLinux && (!stdenv.isx86_32) && (!stdenv.hostPlatform.isMusl) && (!stdenv.hostPlatform.isRiscV)
&& (stdenv.hostPlatform == stdenv.buildPlatform)
, enableManpages ? false
, enableSharedLibraries ? !stdenv.hostPlatform.isStatic
# broken for Ampere eMAG 8180 (c2.large.arm on Packet) #56245
# broken for the armv7l builder
, enablePFM ? stdenv.isLinux && !stdenv.hostPlatform.isAarch
, enablePolly ? false # TODO should be on by default
}:
let
inherit (lib) optional optionals optionalString;
# Used when creating a version-suffixed symlink of libLLVM.dylib
shortVersion = with lib;
concatStringsSep "." (take 1 (splitString "." release_version));
# Ordinarily we would just the `doCheck` and `checkDeps` functionality
# `mkDerivation` gives us to manage our test dependencies (instead of breaking
# out `doCheck` as a package level attribute).
#
# Unfortunately `lit` does not forward `$PYTHONPATH` to children processes, in
# particular the children it uses to do feature detection.
#
# This means that python deps we add to `checkDeps` (which the python
# interpreter is made aware of via `$PYTHONPATH` populated by the python
# setup hook) are not picked up by `lit` which causes it to skip tests.
#
# Adding `python3.withPackages (ps: [ ... ])` to `checkDeps` also doesn't work
# because this package is shadowed in `$PATH` by the regular `python3`
# package.
#
# So, we "manually" assemble one python derivation for the package to depend
# on, taking into account whether checks are enabled or not:
python = if doCheck then
let
checkDeps = ps: with ps; [ psutil ];
in python3.withPackages checkDeps
else python3;
in stdenv.mkDerivation (rec {
pname = "llvm";
inherit version;
src = fetch pname "199yq3a214avcbi4kk2q0ajriifkvsr0l2dkx3a666m033ihi1ff";
polly_src = fetch "polly" "031r23ijhx7v93a5n33m2nc0x9xyqmx0d8xg80z7q971p6qd63sq";
unpackPhase = ''
unpackFile $src
mv llvm-${release_version}* llvm
sourceRoot=$PWD/llvm
'' + optionalString enablePolly ''
unpackFile $polly_src
mv polly-* $sourceRoot/tools/polly
'';
outputs = [ "out" "lib" "dev" "python" ];
nativeBuildInputs = [ cmake python ]
++ optionals enableManpages [ python3.pkgs.sphinx python3.pkgs.recommonmark ];
buildInputs = [ libxml2 libffi ]
++ optional enablePFM libpfm; # exegesis
propagatedBuildInputs = [ ncurses zlib ];
patches = [
# When cross-compiling we configure llvm-config-native with an approximation
# of the flags used for the normal LLVM build. To avoid the need for building
# a native libLLVM.so (which would fail) we force llvm-config to be linked
# statically against the necessary LLVM components always.
../../llvm-config-link-static.patch
./gnu-install-dirs.patch
# On older CPUs (e.g. Hydra/wendy) we'd be getting an error in this test.
(fetchpatch {
name = "uops-CMOV16rm-noreg.diff";
url = "https://github.com/llvm/llvm-project/commit/9e9f991ac033.diff";
sha256 = "sha256:12s8vr6ibri8b48h2z38f3afhwam10arfiqfy4yg37bmc054p5hi";
stripLen = 1;
})
# gcc-11 compat upstream patch
(fetchpatch {
url = "https://github.com/llvm/llvm-project/commit/b498303066a63a203d24f739b2d2e0e56dca70d1.patch";
sha256 = "sha256:0nh123kld0dgz2h941lng331dkj3wbm5lfxm375k1f569gv83hlk";
stripLen = 1;
})
# Fix invalid std::string(nullptr) for GCC 12
(fetchpatch {
name = "nvptx-gcc-12.patch";
url = "https://github.com/llvm/llvm-project/commit/99e64623ec9b31def9375753491cc6093c831809.patch";
sha256 = "0zjfjgavqzi2ypqwqnlvy6flyvdz8hi1anwv0ybwnm2zqixg7za3";
stripLen = 1;
})
(fetchpatch {
name = "dfaemitter-gcc-12.patch";
url = "https://github.com/llvm/llvm-project/commit/0841916e87a39e3c223c986e8da31e4a9a1432e3.patch";
sha256 = "1kckghvsngs51mqm82asy0s9vr19h8aqbw43a0w44mccqw6bzrwf";
stripLen = 1;
})
# Fix musl build.
(fetchpatch {
url = "https://github.com/llvm/llvm-project/commit/5cd554303ead0f8891eee3cd6d25cb07f5a7bf67.patch";
relative = "llvm";
hash = "sha256-XPbvNJ45SzjMGlNUgt/IgEvM2dHQpDOe6woUJY+nUYA=";
})
# Backport gcc-13 fixes with missing includes.
(fetchpatch {
name = "signals-gcc-13.patch";
url = "https://github.com/llvm/llvm-project/commit/ff1681ddb303223973653f7f5f3f3435b48a1983.patch";
hash = "sha256-CXwYxQezTq5vdmc8Yn88BUAEly6YZ5VEIA6X3y5NNOs=";
stripLen = 1;
})
(fetchpatch {
name = "base64-gcc-13.patch";
url = "https://github.com/llvm/llvm-project/commit/5e9be93566f39ee6cecd579401e453eccfbe81e5.patch";
hash = "sha256-PAwrVrvffPd7tphpwCkYiz+67szPRzRB2TXBvKfzQ7U=";
stripLen = 1;
})
] ++ lib.optional enablePolly ./gnu-install-dirs-polly.patch;
postPatch = optionalString stdenv.isDarwin ''
substituteInPlace cmake/modules/AddLLVM.cmake \
--replace 'set(_install_name_dir INSTALL_NAME_DIR "@rpath")' "set(_install_name_dir)" \
--replace 'set(_install_rpath "@loader_path/../''${CMAKE_INSTALL_LIBDIR}''${LLVM_LIBDIR_SUFFIX}" ''${extra_libdir})' ""
'' + ''
# FileSystem permissions tests fail with various special bits
substituteInPlace unittests/Support/CMakeLists.txt \
--replace "Path.cpp" ""
rm unittests/Support/Path.cpp
'' + optionalString stdenv.hostPlatform.isMusl ''
patch -p1 -i ${../../TLI-musl.patch}
substituteInPlace unittests/Support/CMakeLists.txt \
--replace "add_subdirectory(DynamicLibrary)" ""
rm unittests/Support/DynamicLibrary/DynamicLibraryTest.cpp
# valgrind unhappy with musl or glibc, but fails w/musl only
rm test/CodeGen/AArch64/wineh4.mir
'' + optionalString stdenv.hostPlatform.isAarch32 ''
# skip failing X86 test cases on 32-bit ARM
rm test/DebugInfo/X86/convert-debugloc.ll
rm test/DebugInfo/X86/convert-inlined.ll
rm test/DebugInfo/X86/convert-linked.ll
rm test/tools/dsymutil/X86/op-convert.test
rm test/tools/gold/X86/split-dwarf.ll
rm test/tools/llvm-readobj/ELF/dependent-libraries.test
'' + optionalString (stdenv.hostPlatform.system == "armv6l-linux") ''
# Seems to require certain floating point hardware (NEON?)
rm test/ExecutionEngine/frem.ll
'' + ''
patchShebangs test/BugPoint/compile-custom.ll.py
'' + ''
# Tweak tests to ignore namespace part of type to support
# gcc-12: https://gcc.gnu.org/PR103598.
# The change below mangles strings like:
# CHECK-NEXT: Starting llvm::Function pass manager run.
# to:
# CHECK-NEXT: Starting {{.*}}Function pass manager run.
for f in \
test/Other/new-pass-manager.ll \
test/Other/new-pm-defaults.ll \
test/Other/new-pm-lto-defaults.ll \
test/Other/new-pm-thinlto-defaults.ll \
test/Other/pass-pipeline-parsing.ll \
test/Transforms/Inline/cgscc-incremental-invalidate.ll \
test/Transforms/Inline/clear-analyses.ll \
test/Transforms/LoopUnroll/unroll-loop-invalidation.ll \
test/Transforms/SCCP/ipsccp-preserve-analysis.ll \
test/Transforms/SCCP/preserve-analysis.ll \
test/Transforms/SROA/dead-inst.ll \
test/tools/gold/X86/new-pm.ll \
; do
echo "PATCH: $f"
substituteInPlace $f \
--replace 'Starting llvm::' 'Starting {{.*}}' \
--replace 'Finished llvm::' 'Finished {{.*}}'
done
'';
preConfigure = ''
# Workaround for configure flags that need to have spaces
cmakeFlagsArray+=(
-DLLVM_LIT_ARGS='-svj''${NIX_BUILD_CORES} --no-progress-bar'
)
'';
# hacky fix: created binaries need to be run before installation
preBuild = ''
mkdir -p $out/
ln -sv $PWD/lib $out
'';
# E.g. mesa.drivers use the build-id as a cache key (see #93946):
LDFLAGS = optionalString (enableSharedLibraries && !stdenv.isDarwin) "-Wl,--build-id=sha1";
cmakeBuildType = if debugVersion then "Debug" else "Release";
cmakeFlags = with stdenv; let
# These flags influence llvm-config's BuildVariables.inc in addition to the
# general build. We need to make sure these are also passed via
# CROSS_TOOLCHAIN_FLAGS_NATIVE when cross-compiling or llvm-config-native
# will return different results from the cross llvm-config.
#
# Some flags don't need to be repassed because LLVM already does so (like
# CMAKE_BUILD_TYPE), others are irrelevant to the result.
flagsForLlvmConfig = [
"-DLLVM_INSTALL_CMAKE_DIR=${placeholder "dev"}/lib/cmake/llvm/"
"-DLLVM_ENABLE_RTTI=ON"
] ++ optionals enableSharedLibraries [
"-DLLVM_LINK_LLVM_DYLIB=ON"
];
in flagsForLlvmConfig ++ [
"-DLLVM_INSTALL_UTILS=ON" # Needed by rustc
"-DLLVM_BUILD_TESTS=${if doCheck then "ON" else "OFF"}"
"-DLLVM_ENABLE_FFI=ON"
"-DLLVM_HOST_TRIPLE=${stdenv.hostPlatform.config}"
"-DLLVM_DEFAULT_TARGET_TRIPLE=${stdenv.hostPlatform.config}"
"-DLLVM_ENABLE_DUMP=ON"
] ++ optionals stdenv.hostPlatform.isStatic [
# Disables building of shared libs, -fPIC is still injected by cc-wrapper
"-DLLVM_ENABLE_PIC=OFF"
"-DLLVM_BUILD_STATIC=ON"
# libxml2 needs to be disabled because the LLVM build system ignores its .la
# file and doesn't link zlib as well.
# https://github.com/ClangBuiltLinux/tc-build/issues/150#issuecomment-845418812
"-DLLVM_ENABLE_LIBXML2=OFF"
# This is a Shared Library not tied to LLVM_ENABLE_PIC
"-DLLVM_TOOL_REMARKS_SHLIB_BUILD=OFF"
] ++ optionals enableManpages [
"-DLLVM_BUILD_DOCS=ON"
"-DLLVM_ENABLE_SPHINX=ON"
"-DSPHINX_OUTPUT_MAN=ON"
"-DSPHINX_OUTPUT_HTML=OFF"
"-DSPHINX_WARNINGS_AS_ERRORS=OFF"
] ++ optionals (enableGoldPlugin) [
"-DLLVM_BINUTILS_INCDIR=${libbfd.dev}/include"
] ++ optionals isDarwin [
"-DLLVM_ENABLE_LIBCXX=ON"
"-DCAN_TARGET_i386=false"
] ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"-DCMAKE_CROSSCOMPILING=True"
"-DLLVM_TABLEGEN=${buildLlvmTools.llvm}/bin/llvm-tblgen"
(
let
nativeCC = pkgsBuildBuild.targetPackages.stdenv.cc;
nativeBintools = nativeCC.bintools.bintools;
nativeToolchainFlags = [
"-DCMAKE_C_COMPILER=${nativeCC}/bin/${nativeCC.targetPrefix}cc"
"-DCMAKE_CXX_COMPILER=${nativeCC}/bin/${nativeCC.targetPrefix}c++"
"-DCMAKE_AR=${nativeBintools}/bin/${nativeBintools.targetPrefix}ar"
"-DCMAKE_STRIP=${nativeBintools}/bin/${nativeBintools.targetPrefix}strip"
"-DCMAKE_RANLIB=${nativeBintools}/bin/${nativeBintools.targetPrefix}ranlib"
];
# We need to repass the custom GNUInstallDirs values, otherwise CMake
# will choose them for us, leading to wrong results in llvm-config-native
nativeInstallFlags = [
"-DCMAKE_INSTALL_PREFIX=${placeholder "out"}"
"-DCMAKE_INSTALL_BINDIR=${placeholder "out"}/bin"
"-DCMAKE_INSTALL_INCLUDEDIR=${placeholder "dev"}/include"
"-DCMAKE_INSTALL_LIBDIR=${placeholder "lib"}/lib"
"-DCMAKE_INSTALL_LIBEXECDIR=${placeholder "lib"}/libexec"
];
in "-DCROSS_TOOLCHAIN_FLAGS_NATIVE:list="
+ lib.concatStringsSep ";" (lib.concatLists [
flagsForLlvmConfig
nativeToolchainFlags
nativeInstallFlags
])
)
];
postBuild = ''
rm -fR $out
'';
preCheck = ''
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}$PWD/lib
'';
postInstall = ''
mkdir -p $python/share
mv $out/share/opt-viewer $python/share/opt-viewer
moveToOutput "bin/llvm-config*" "$dev"
substituteInPlace "$dev/lib/cmake/llvm/LLVMExports-${if debugVersion then "debug" else "release"}.cmake" \
--replace "\''${_IMPORT_PREFIX}/lib/lib" "$lib/lib/lib" \
--replace "$out/bin/llvm-config" "$dev/bin/llvm-config"
substituteInPlace "$dev/lib/cmake/llvm/LLVMConfig.cmake" \
--replace 'set(LLVM_BINARY_DIR "''${LLVM_INSTALL_PREFIX}")' 'set(LLVM_BINARY_DIR "''${LLVM_INSTALL_PREFIX}'"$lib"'")'
''
+ optionalString (stdenv.isDarwin && enableSharedLibraries) ''
ln -s $lib/lib/libLLVM.dylib $lib/lib/libLLVM-${shortVersion}.dylib
ln -s $lib/lib/libLLVM.dylib $lib/lib/libLLVM-${release_version}.dylib
''
+ optionalString (stdenv.buildPlatform != stdenv.hostPlatform) ''
cp NATIVE/bin/llvm-config $dev/bin/llvm-config-native
'';
inherit doCheck;
checkTarget = "check-all";
requiredSystemFeatures = [ "big-parallel" ];
meta = llvm_meta // {
homepage = "https://llvm.org/";
description = "A collection of modular and reusable compiler and toolchain technologies";
longDescription = ''
The LLVM Project is a collection of modular and reusable compiler and
toolchain technologies. Despite its name, LLVM has little to do with
traditional virtual machines. The name "LLVM" itself is not an acronym; it
is the full name of the project.
LLVM began as a research project at the University of Illinois, with the
goal of providing a modern, SSA-based compilation strategy capable of
supporting both static and dynamic compilation of arbitrary programming
languages. Since then, LLVM has grown to be an umbrella project consisting
of a number of subprojects, many of which are being used in production by
a wide variety of commercial and open source projects as well as being
widely used in academic research. Code in the LLVM project is licensed
under the "Apache 2.0 License with LLVM exceptions".
'';
};
} // lib.optionalAttrs enableManpages {
pname = "llvm-manpages";
buildPhase = ''
make docs-llvm-man
'';
propagatedBuildInputs = [];
installPhase = ''
make -C docs install
'';
postPatch = null;
postInstall = null;
outputs = [ "out" ];
doCheck = false;
meta = llvm_meta // {
description = "man pages for LLVM ${version}";
};
})

View File

@ -0,0 +1,106 @@
diff --git a/tools/polly/CMakeLists.txt b/tools/polly/CMakeLists.txt
index 9939097f743e..8cc538da912a 100644
--- a/tools/polly/CMakeLists.txt
+++ b/tools/polly/CMakeLists.txt
@@ -2,7 +2,11 @@
if (NOT DEFINED LLVM_MAIN_SRC_DIR)
project(Polly)
cmake_minimum_required(VERSION 3.4.3)
+endif()
+
+include(GNUInstallDirs)
+if (NOT DEFINED LLVM_MAIN_SRC_DIR)
# Where is LLVM installed?
find_package(LLVM CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${LLVM_CMAKE_DIR})
@@ -145,14 +149,14 @@ include_directories(
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY include/
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.h"
PATTERN ".svn" EXCLUDE
)
install(DIRECTORY ${POLLY_BINARY_DIR}/include/
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.h"
PATTERN "CMakeFiles" EXCLUDE
diff --git a/tools/polly/cmake/CMakeLists.txt b/tools/polly/cmake/CMakeLists.txt
index 211f95512717..f9e04a4844b6 100644
--- a/tools/polly/cmake/CMakeLists.txt
+++ b/tools/polly/cmake/CMakeLists.txt
@@ -79,18 +79,18 @@ file(GENERATE
# Generate PollyConfig.cmake for the install tree.
unset(POLLY_EXPORTS)
-set(POLLY_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
+set(POLLY_INSTALL_PREFIX "")
set(POLLY_CONFIG_LLVM_CMAKE_DIR "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
-set(POLLY_CONFIG_CMAKE_DIR "${POLLY_INSTALL_PREFIX}/${POLLY_INSTALL_PACKAGE_DIR}")
-set(POLLY_CONFIG_LIBRARY_DIRS "${POLLY_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}")
+set(POLLY_CONFIG_CMAKE_DIR "${POLLY_INSTALL_PREFIX}${CMAKE_INSTALL_PREFIX}/${POLLY_INSTALL_PACKAGE_DIR}")
+set(POLLY_CONFIG_LIBRARY_DIRS "${POLLY_INSTALL_PREFIX}${CMAKE_INSTALL_FULL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
if (POLLY_BUNDLED_ISL)
set(POLLY_CONFIG_INCLUDE_DIRS
- "${POLLY_INSTALL_PREFIX}/include"
- "${POLLY_INSTALL_PREFIX}/include/polly"
+ "${POLLY_INSTALL_PREFIX}${CMAKE_INSTALL_FULL_LIBDIR}"
+ "${POLLY_INSTALL_PREFIX}${CMAKE_INSTALL_FULL_LIBDIR}/polly"
)
else()
set(POLLY_CONFIG_INCLUDE_DIRS
- "${POLLY_INSTALL_PREFIX}/include"
+ "${POLLY_INSTALL_PREFIX}${CMAKE_INSTALL_FULL_INCLUDEDIR}"
${ISL_INCLUDE_DIRS}
)
endif()
@@ -100,12 +100,12 @@ endif()
foreach(tgt IN LISTS POLLY_CONFIG_EXPORTED_TARGETS)
get_target_property(tgt_type ${tgt} TYPE)
if (tgt_type STREQUAL "EXECUTABLE")
- set(tgt_prefix "bin/")
+ set(tgt_prefix "${CMAKE_INSTALL_BINDIR}/")
else()
- set(tgt_prefix "lib/")
+ set(tgt_prefix "${CMAKE_INSTALL_LIBDIR}/")
endif()
- set(tgt_path "${CMAKE_INSTALL_PREFIX}/${tgt_prefix}$<TARGET_FILE_NAME:${tgt}>")
+ set(tgt_path "${tgt_prefix}$<TARGET_FILE_NAME:${tgt}>")
file(RELATIVE_PATH tgt_path ${POLLY_CONFIG_CMAKE_DIR} ${tgt_path})
if (NOT tgt_type STREQUAL "INTERFACE_LIBRARY")
diff --git a/tools/polly/cmake/polly_macros.cmake b/tools/polly/cmake/polly_macros.cmake
index 86de6f10686e..91f30891ccbe 100644
--- a/tools/polly/cmake/polly_macros.cmake
+++ b/tools/polly/cmake/polly_macros.cmake
@@ -44,8 +44,8 @@ macro(add_polly_library name)
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "LLVMPolly")
install(TARGETS ${name}
EXPORT LLVMExports
- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX})
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX})
endif()
set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS ${name})
endmacro(add_polly_library)
diff --git a/tools/polly/lib/External/CMakeLists.txt b/tools/polly/lib/External/CMakeLists.txt
index 1039079cb49c..28b499ae1e9e 100644
--- a/tools/polly/lib/External/CMakeLists.txt
+++ b/tools/polly/lib/External/CMakeLists.txt
@@ -275,7 +275,7 @@ if (POLLY_BUNDLED_ISL)
install(DIRECTORY
${ISL_SOURCE_DIR}/include/
${ISL_BINARY_DIR}/include/
- DESTINATION include/polly
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/polly
FILES_MATCHING
PATTERN "*.h"
PATTERN "CMakeFiles" EXCLUDE

View File

@ -0,0 +1,417 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 247ad36d3845..815e2c4ba955 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -269,15 +269,21 @@ if (CMAKE_BUILD_TYPE AND
message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
endif()
+include(GNUInstallDirs)
+
set(LLVM_LIBDIR_SUFFIX "" CACHE STRING "Define suffix of library directory name (32/64)" )
-set(LLVM_TOOLS_INSTALL_DIR "bin" CACHE STRING "Path for binary subdirectory (defaults to 'bin')")
+set(LLVM_TOOLS_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
+ "Path for binary subdirectory (defaults to 'bin')")
mark_as_advanced(LLVM_TOOLS_INSTALL_DIR)
set(LLVM_UTILS_INSTALL_DIR "${LLVM_TOOLS_INSTALL_DIR}" CACHE STRING
"Path to install LLVM utilities (enabled by LLVM_INSTALL_UTILS=ON) (defaults to LLVM_TOOLS_INSTALL_DIR)")
mark_as_advanced(LLVM_UTILS_INSTALL_DIR)
+set(LLVM_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}/cmake/llvm" CACHE STRING
+ "Path for CMake subdirectory (defaults to lib/cmake/llvm)" )
+
# They are used as destination of target generators.
set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
@@ -559,9 +565,9 @@ option (LLVM_ENABLE_SPHINX "Use Sphinx to generate llvm documentation." OFF)
option (LLVM_ENABLE_OCAMLDOC "Build OCaml bindings documentation." ON)
option (LLVM_ENABLE_BINDINGS "Build bindings." ON)
-set(LLVM_INSTALL_DOXYGEN_HTML_DIR "share/doc/llvm/doxygen-html"
+set(LLVM_INSTALL_DOXYGEN_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/doxygen-html"
CACHE STRING "Doxygen-generated HTML documentation install directory")
-set(LLVM_INSTALL_OCAMLDOC_HTML_DIR "share/doc/llvm/ocaml-html"
+set(LLVM_INSTALL_OCAMLDOC_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/ocaml-html"
CACHE STRING "OCamldoc-generated HTML documentation install directory")
option (LLVM_BUILD_EXTERNAL_COMPILER_RT
@@ -1107,7 +1113,7 @@ endif()
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY include/llvm include/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "*.def"
@@ -1119,7 +1125,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
)
install(DIRECTORY ${LLVM_INCLUDE_DIR}/llvm ${LLVM_INCLUDE_DIR}/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "*.def"
@@ -1134,13 +1140,13 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if (LLVM_INSTALL_MODULEMAPS)
install(DIRECTORY include/llvm include/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "module.modulemap"
)
install(FILES include/llvm/module.install.modulemap
- DESTINATION include/llvm
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm
COMPONENT llvm-headers
RENAME "module.extern.modulemap"
)
diff --git a/cmake/modules/AddLLVM.cmake b/cmake/modules/AddLLVM.cmake
index b74adc11ade9..a5aa258cde30 100644
--- a/cmake/modules/AddLLVM.cmake
+++ b/cmake/modules/AddLLVM.cmake
@@ -766,9 +766,9 @@ macro(add_llvm_library name)
install(TARGETS ${name}
${export_to_llvmexports}
- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- RUNTIME DESTINATION bin COMPONENT ${name})
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${name})
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-${name}
@@ -981,7 +981,7 @@ function(process_llvm_pass_plugins)
"set(LLVM_STATIC_EXTENSIONS ${LLVM_STATIC_EXTENSIONS})")
install(FILES
${llvm_cmake_builddir}/LLVMConfigExtensions.cmake
- DESTINATION ${LLVM_INSTALL_PACKAGE_DIR}
+ DESTINATION ${LLVM_INSTALL_CMAKE_DIR}
COMPONENT cmake-exports)
set(ExtensionDef "${LLVM_BINARY_DIR}/include/llvm/Support/Extension.def")
@@ -1201,7 +1201,7 @@ macro(add_llvm_example name)
endif()
add_llvm_executable(${name} ${ARGN})
if( LLVM_BUILD_EXAMPLES )
- install(TARGETS ${name} RUNTIME DESTINATION examples)
+ install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples)
endif()
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endmacro(add_llvm_example name)
@@ -1819,7 +1819,7 @@ function(llvm_install_library_symlink name dest type)
set(full_name ${CMAKE_${type}_LIBRARY_PREFIX}${name}${CMAKE_${type}_LIBRARY_SUFFIX})
set(full_dest ${CMAKE_${type}_LIBRARY_PREFIX}${dest}${CMAKE_${type}_LIBRARY_SUFFIX})
- set(output_dir lib${LLVM_LIBDIR_SUFFIX})
+ set(output_dir ${CMAKE_INSTALL_FULL_LIBDIR}${LLVM_LIBDIR_SUFFIX})
if(WIN32 AND "${type}" STREQUAL "SHARED")
set(output_dir bin)
endif()
@@ -1836,7 +1836,7 @@ function(llvm_install_library_symlink name dest type)
endif()
endfunction()
-function(llvm_install_symlink name dest)
+function(llvm_install_symlink name dest output_dir)
cmake_parse_arguments(ARG "ALWAYS_GENERATE" "COMPONENT" "" ${ARGN})
foreach(path ${CMAKE_MODULE_PATH})
if(EXISTS ${path}/LLVMInstallSymlink.cmake)
@@ -1859,7 +1859,7 @@ function(llvm_install_symlink name dest)
set(full_dest ${dest}${CMAKE_EXECUTABLE_SUFFIX})
install(SCRIPT ${INSTALL_SYMLINK}
- CODE "install_symlink(${full_name} ${full_dest} ${LLVM_TOOLS_INSTALL_DIR})"
+ CODE "install_symlink(${full_name} ${full_dest} ${output_dir})"
COMPONENT ${component})
if (NOT LLVM_ENABLE_IDE AND NOT ARG_ALWAYS_GENERATE)
@@ -1942,7 +1942,8 @@ function(add_llvm_tool_symlink link_name target)
endif()
if ((TOOL_IS_TOOLCHAIN OR NOT LLVM_INSTALL_TOOLCHAIN_ONLY) AND LLVM_BUILD_TOOLS)
- llvm_install_symlink(${link_name} ${target})
+ GNUInstallDirs_get_absolute_install_dir(output_dir LLVM_TOOLS_INSTALL_DIR)
+ llvm_install_symlink(${link_name} ${target} ${output_dir})
endif()
endif()
endfunction()
@@ -2064,9 +2065,9 @@ function(llvm_setup_rpath name)
if (APPLE)
set(_install_name_dir INSTALL_NAME_DIR "@rpath")
- set(_install_rpath "@loader_path/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
+ set(_install_rpath "@loader_path/../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
elseif(UNIX)
- set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
+ set(_install_rpath "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir})
if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)")
set_property(TARGET ${name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-z,origin ")
diff --git a/cmake/modules/AddOCaml.cmake b/cmake/modules/AddOCaml.cmake
index 554046b20edf..4d1ad980641e 100644
--- a/cmake/modules/AddOCaml.cmake
+++ b/cmake/modules/AddOCaml.cmake
@@ -144,9 +144,9 @@ function(add_ocaml_library name)
endforeach()
if( APPLE )
- set(ocaml_rpath "@executable_path/../../../lib${LLVM_LIBDIR_SUFFIX}")
+ set(ocaml_rpath "@executable_path/../../../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
elseif( UNIX )
- set(ocaml_rpath "\\$ORIGIN/../../../lib${LLVM_LIBDIR_SUFFIX}")
+ set(ocaml_rpath "\\$ORIGIN/../../../${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
endif()
list(APPEND ocaml_flags "-ldopt" "-Wl,-rpath,${ocaml_rpath}")
diff --git a/cmake/modules/AddSphinxTarget.cmake b/cmake/modules/AddSphinxTarget.cmake
index b5babb30abcf..190b1222a9f9 100644
--- a/cmake/modules/AddSphinxTarget.cmake
+++ b/cmake/modules/AddSphinxTarget.cmake
@@ -84,7 +84,7 @@ function (add_sphinx_target builder project)
endif()
elseif (builder STREQUAL html)
string(TOUPPER "${project}" project_upper)
- set(${project_upper}_INSTALL_SPHINX_HTML_DIR "share/doc/${project}/html"
+ set(${project_upper}_INSTALL_SPHINX_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/html"
CACHE STRING "HTML documentation install directory for ${project}")
# '/.' indicates: copy the contents of the directory directly into
diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt
index 4b8879f65fe4..f01920bcc60f 100644
--- a/cmake/modules/CMakeLists.txt
+++ b/cmake/modules/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
+set(LLVM_INSTALL_PACKAGE_DIR ${LLVM_INSTALL_CMAKE_DIR} CACHE STRING "Path for CMake subdirectory (defaults to 'cmake/llvm')")
set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
# First for users who use an installed LLVM, create the LLVMExports.cmake file.
@@ -108,13 +108,13 @@ foreach(p ${_count})
set(LLVM_CONFIG_CODE "${LLVM_CONFIG_CODE}
get_filename_component(LLVM_INSTALL_PREFIX \"\${LLVM_INSTALL_PREFIX}\" PATH)")
endforeach(p)
-set(LLVM_CONFIG_INCLUDE_DIRS "\${LLVM_INSTALL_PREFIX}/include")
+set(LLVM_CONFIG_INCLUDE_DIRS "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
set(LLVM_CONFIG_INCLUDE_DIR "${LLVM_CONFIG_INCLUDE_DIRS}")
set(LLVM_CONFIG_MAIN_INCLUDE_DIR "${LLVM_CONFIG_INCLUDE_DIRS}")
-set(LLVM_CONFIG_LIBRARY_DIRS "\${LLVM_INSTALL_PREFIX}/lib\${LLVM_LIBDIR_SUFFIX}")
+set(LLVM_CONFIG_LIBRARY_DIRS "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}\${LLVM_LIBDIR_SUFFIX}")
set(LLVM_CONFIG_CMAKE_DIR "\${LLVM_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}")
set(LLVM_CONFIG_BINARY_DIR "\${LLVM_INSTALL_PREFIX}")
-set(LLVM_CONFIG_TOOLS_BINARY_DIR "\${LLVM_INSTALL_PREFIX}/bin")
+set(LLVM_CONFIG_TOOLS_BINARY_DIR "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
# Generate a default location for lit
if (LLVM_INSTALL_UTILS AND LLVM_BUILD_UTILS)
diff --git a/cmake/modules/LLVMInstallSymlink.cmake b/cmake/modules/LLVMInstallSymlink.cmake
index 09fed8085c23..aa79f192abf0 100644
--- a/cmake/modules/LLVMInstallSymlink.cmake
+++ b/cmake/modules/LLVMInstallSymlink.cmake
@@ -10,7 +10,7 @@ function(install_symlink name target outdir)
set(LINK_OR_COPY copy)
endif()
- set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/${outdir}/")
+ set(bindir "${DESTDIR}${outdir}/")
message(STATUS "Creating ${name}")
diff --git a/docs/CMake.rst b/docs/CMake.rst
index 1f908d3e95b1..1315e0aa40e1 100644
--- a/docs/CMake.rst
+++ b/docs/CMake.rst
@@ -196,7 +196,7 @@ CMake manual, or execute ``cmake --help-variable VARIABLE_NAME``.
**LLVM_LIBDIR_SUFFIX**:STRING
Extra suffix to append to the directory where libraries are to be
installed. On a 64-bit architecture, one could use ``-DLLVM_LIBDIR_SUFFIX=64``
- to install libraries to ``/usr/lib64``.
+ to install libraries to ``/usr/lib64``. See also ``CMAKE_INSTALL_LIBDIR``.
**CMAKE_C_FLAGS**:STRING
Extra flags to use when compiling C source files.
@@ -516,8 +516,8 @@ LLVM-specific variables
**LLVM_INSTALL_DOXYGEN_HTML_DIR**:STRING
The path to install Doxygen-generated HTML documentation to. This path can
- either be absolute or relative to the CMAKE_INSTALL_PREFIX. Defaults to
- `share/doc/llvm/doxygen-html`.
+ either be absolute or relative to the ``CMAKE_INSTALL_PREFIX``. Defaults to
+ `${CMAKE_INSTALL_DOCDIR}/${project}/doxygen-html`.
**LLVM_ENABLE_SPHINX**:BOOL
If specified, CMake will search for the ``sphinx-build`` executable and will make
@@ -548,13 +548,33 @@ LLVM-specific variables
**LLVM_INSTALL_SPHINX_HTML_DIR**:STRING
The path to install Sphinx-generated HTML documentation to. This path can
- either be absolute or relative to the CMAKE_INSTALL_PREFIX. Defaults to
- `share/doc/llvm/html`.
+ either be absolute or relative to the ``CMAKE_INSTALL_PREFIX``. Defaults to
+ `${CMAKE_INSTALL_DOCDIR}/${project}/html`.
**LLVM_INSTALL_OCAMLDOC_HTML_DIR**:STRING
The path to install OCamldoc-generated HTML documentation to. This path can
- either be absolute or relative to the CMAKE_INSTALL_PREFIX. Defaults to
- `share/doc/llvm/ocaml-html`.
+ either be absolute or relative to the ``CMAKE_INSTALL_PREFIX``. Defaults to
+ `${CMAKE_INSTALL_DOCDIR}/${project}/ocaml-html`.
+
+**CMAKE_INSTALL_BINDIR**:STRING
+ The path to install binary tools, relative to the ``CMAKE_INSTALL_PREFIX``.
+ Defaults to `bin`.
+
+**CMAKE_INSTALL_LIBDIR**:STRING
+ The path to install libraries, relative to the ``CMAKE_INSTALL_PREFIX``.
+ Defaults to `lib`.
+
+**CMAKE_INSTALL_INCLUDEDIR**:STRING
+ The path to install header files, relative to the ``CMAKE_INSTALL_PREFIX``.
+ Defaults to `include`.
+
+**CMAKE_INSTALL_DOCDIR**:STRING
+ The path to install documentation, relative to the ``CMAKE_INSTALL_PREFIX``.
+ Defaults to `share/doc`.
+
+**CMAKE_INSTALL_MANDIR**:STRING
+ The path to install manpage files, relative to the ``CMAKE_INSTALL_PREFIX``.
+ Defaults to `share/man`.
**LLVM_CREATE_XCODE_TOOLCHAIN**:BOOL
macOS Only: If enabled CMake will generate a target named
@@ -752,9 +772,11 @@ the ``cmake`` command or by setting it directly in ``ccmake`` or ``cmake-gui``).
This file is available in two different locations.
-* ``<INSTALL_PREFIX>/lib/cmake/llvm/LLVMConfig.cmake`` where
- ``<INSTALL_PREFIX>`` is the install prefix of an installed version of LLVM.
- On Linux typically this is ``/usr/lib/cmake/llvm/LLVMConfig.cmake``.
+* ``<LLVM_INSTALL_PACKAGE_DIR>LLVMConfig.cmake`` where
+ ``<LLVM_INSTALL_PACKAGE_DIR>`` is the location where LLVM CMake modules are
+ installed as part of an installed version of LLVM. This is typically
+ ``cmake/llvm/`` within the lib directory. On Linux, this is typically
+ ``/usr/lib/cmake/llvm/LLVMConfig.cmake``.
* ``<LLVM_BUILD_ROOT>/lib/cmake/llvm/LLVMConfig.cmake`` where
``<LLVM_BUILD_ROOT>`` is the root of the LLVM build tree. **Note: this is only
diff --git a/examples/Bye/CMakeLists.txt b/examples/Bye/CMakeLists.txt
index bb96edb4b4bf..678c22fb43c8 100644
--- a/examples/Bye/CMakeLists.txt
+++ b/examples/Bye/CMakeLists.txt
@@ -14,6 +14,6 @@ if (NOT WIN32)
BUILDTREE_ONLY
)
- install(TARGETS ${name} RUNTIME DESTINATION examples)
+ install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples)
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endif()
diff --git a/include/llvm/CMakeLists.txt b/include/llvm/CMakeLists.txt
index b46319f24fc8..2feabd1954e4 100644
--- a/include/llvm/CMakeLists.txt
+++ b/include/llvm/CMakeLists.txt
@@ -5,5 +5,5 @@ add_subdirectory(Frontend)
# If we're doing an out-of-tree build, copy a module map for generated
# header files into the build area.
if (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
- configure_file(module.modulemap.build module.modulemap COPYONLY)
+ configure_file(module.modulemap.build ${LLVM_INCLUDE_DIR}/module.modulemap COPYONLY)
endif (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in
index 63cef75368b7..6295478b1f3d 100644
--- a/tools/llvm-config/BuildVariables.inc.in
+++ b/tools/llvm-config/BuildVariables.inc.in
@@ -23,6 +23,10 @@
#define LLVM_CXXFLAGS "@LLVM_CXXFLAGS@"
#define LLVM_BUILDMODE "@LLVM_BUILDMODE@"
#define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@"
+#define LLVM_INSTALL_BINDIR "@CMAKE_INSTALL_BINDIR@"
+#define LLVM_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@"
+#define LLVM_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@"
+#define LLVM_INSTALL_CMAKEDIR "@LLVM_INSTALL_CMAKE_DIR@"
#define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@"
#define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@"
#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@"
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 7e74b7c90816..f185e9283f83 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -358,12 +358,26 @@ int main(int argc, char **argv) {
("-I" + ActiveIncludeDir + " " + "-I" + ActiveObjRoot + "/include");
} else {
ActivePrefix = CurrentExecPrefix;
- ActiveIncludeDir = ActivePrefix + "/include";
- SmallString<256> path(StringRef(LLVM_TOOLS_INSTALL_DIR));
- sys::fs::make_absolute(ActivePrefix, path);
- ActiveBinDir = std::string(path.str());
- ActiveLibDir = ActivePrefix + "/lib" + LLVM_LIBDIR_SUFFIX;
- ActiveCMakeDir = ActiveLibDir + "/cmake/llvm";
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_INCLUDEDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveIncludeDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_BINDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveBinDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveLibDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_CMAKEDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveCMakeDir = std::string(path.str());
+ }
ActiveIncludeOption = "-I" + ActiveIncludeDir;
}
diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt
index 2963f97cad88..69d66c9c9ca1 100644
--- a/tools/lto/CMakeLists.txt
+++ b/tools/lto/CMakeLists.txt
@@ -25,7 +25,7 @@ add_llvm_library(LTO SHARED INSTALL_WITH_TOOLCHAIN ${SOURCES} DEPENDS
intrinsics_gen)
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h
- DESTINATION include/llvm-c
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm-c
COMPONENT LTO)
if (APPLE)
diff --git a/tools/opt-viewer/CMakeLists.txt b/tools/opt-viewer/CMakeLists.txt
index ead73ec13a8f..250362021f17 100644
--- a/tools/opt-viewer/CMakeLists.txt
+++ b/tools/opt-viewer/CMakeLists.txt
@@ -8,7 +8,7 @@ set (files
foreach (file ${files})
install(PROGRAMS ${file}
- DESTINATION share/opt-viewer
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/opt-viewer
COMPONENT opt-viewer)
endforeach (file)
diff --git a/tools/remarks-shlib/CMakeLists.txt b/tools/remarks-shlib/CMakeLists.txt
index e948496c603a..1f4df8a98b10 100644
--- a/tools/remarks-shlib/CMakeLists.txt
+++ b/tools/remarks-shlib/CMakeLists.txt
@@ -11,7 +11,7 @@ set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Remarks.exports)
add_llvm_library(Remarks SHARED INSTALL_WITH_TOOLCHAIN ${SOURCES})
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/Remarks.h
- DESTINATION include/llvm-c
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm-c
COMPONENT Remarks)
if (APPLE)

35
llvm/TLI-musl.patch Normal file
View File

@ -0,0 +1,35 @@
From 5c571082fdaf61f6df19d9b7137dc26d71334058 Mon Sep 17 00:00:00 2001
From: Natanael Copa <ncopa@alpinelinux.org>
Date: Thu, 18 Feb 2016 10:33:04 +0100
Subject: [PATCH 2/3] Fix build with musl libc
On musl libc the fopen64 and fopen are the same thing, but for
compatibility they have a `#define fopen64 fopen`. Same applies for
fseek64, fstat64, fstatvfs64, ftello64, lstat64, stat64 and tmpfile64.
---
include/llvm/Analysis/TargetLibraryInfo.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/include/llvm/Analysis/TargetLibraryInfo.h b/include/llvm/Analysis/TargetLibraryInfo.h
index 7becdf0..7f14427 100644
--- a/include/llvm/Analysis/TargetLibraryInfo.h
+++ b/include/llvm/Analysis/TargetLibraryInfo.h
@@ -18,6 +18,15 @@
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
+#undef fopen64
+#undef fseeko64
+#undef fstat64
+#undef fstatvfs64
+#undef ftello64
+#undef lstat64
+#undef stat64
+#undef tmpfile64
+
namespace llvm {
/// VecDesc - Describes a possible vectorization of a function.
/// Function 'VectorFnName' is equivalent to 'ScalarFnName' vectorized
--
2.7.3

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