Compare commits

...

132 Commits

Author SHA1 Message Date
88bda0cb7a i2c: fix comments 2021-07-13 15:41:55 +08:00
91445620ee i2c: fix SCL/SDA race conditions & handle slave holding SCL after ack 2021-06-23 17:09:07 +08:00
040d41fd76 update cargosha256 2021-06-19 22:47:21 +08:00
9180918619 update dependencies 2021-06-19 22:40:09 +08:00
8ec7216e9a update cargo-xbuild 2021-06-19 22:25:07 +08:00
0e9d3f146a remove use of deprecated stdenv.lib 2021-06-19 22:04:14 +08:00
411eebd96c i2c: configure I2C_SW_RESET MIO on Kasli-SoC 2021-06-19 18:49:51 +08:00
e31adb722d README: add Kasli-SoC 2021-05-30 07:37:27 +08:00
759bf95a76 add Kasli-SoC POR control program 2021-05-29 17:43:21 +08:00
f83ef218de update cargosha256 2021-05-29 14:13:51 +08:00
062b894a4b use updated upstream core_io 2021-05-29 14:09:11 +08:00
2f9019ca0f szl: update copyright year 2021-05-29 14:01:29 +08:00
7b29ab2628 update dependencies
Prevent cargo from messing up core_io this time.
2021-05-29 13:10:54 +08:00
a9697ec8d8 Revert "update dependencies"
This reverts commit 0220cf19eb.
2021-05-29 13:05:35 +08:00
0220cf19eb update dependencies 2021-05-29 13:03:35 +08:00
a11cb852a8 libboard_zynq: work around Kasli-SoC MDIO breakage (#78) 2021-05-29 12:50:28 +08:00
42cdedae91 openocd: fix Kasli-SoC nSRST 2021-05-29 12:17:59 +08:00
823ec374ba kasli-soc: internal JTAG 2021-05-28 18:44:07 +08:00
666c077cef openocd: add Kasli-SoC (with digilent external JTAG) 2021-05-28 18:22:17 +08:00
def17829d9 LGPLv3 2021-04-06 16:46:38 +08:00
ba252e72da libasync: provide get/set ack_delay (new in smoltcp 0.7.0) 2021-02-08 03:06:51 +01:00
a2416f26a0 default.nix: update cargoSha256* 2021-02-04 18:29:00 +01:00
bf9f1e33c8 update smoltcp 0.6.0 -> 0.7.0 2021-02-04 18:29:00 +01:00
481a0eb25f Revert "default.nix: update cargoSha256*"
This reverts commit 89e4d61dc0.
2021-02-04 18:27:44 +01:00
05c30dd52e Revert "update smoltcp 0.6.0 -> 0.7.0"
This reverts commit ccf758cc85.
2021-02-04 18:27:42 +01:00
f816a04cef Revert "Revert "update smoltcp 0.6.0 -> 0.7.0""
This reverts commit faa64c2ce8.
2021-02-04 16:22:39 +01:00
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
89e4d61dc0 default.nix: update cargoSha256* 2021-02-04 00:32:57 +01:00
0b07e9fefc update Rust
the old nightly crashes with smoltcp 0.7.0
2021-02-03 22:42:00 +01:00
ccf758cc85 update smoltcp 0.6.0 -> 0.7.0 2021-02-03 22:41:32 +01:00
78d58d17ec libsupport_zynq: fix stack pointer problems 2021-01-28 12:33:06 +08:00
06c646e61f libcortex_a9: added interrupt_handler macro
closes #73
2021-01-28 11:41:34 +08:00
7681745282 experiments: fix broken build 2021-01-26 12:53:26 +08:00
6e6612bc3e szl: move enable l2c to artiq-zynq 2021-01-26 11:19:33 +08:00
b50540915d libcortex_a9: memcpy throughput optimization 2021-01-26 11:16:37 +08:00
4aa252546f experiments: updated experiment 2021-01-15 17:08:20 +08:00
b42869e655 libsupport_zynq: removed naked annotation 2021-01-15 17:08:20 +08:00
d62c77e6e0 libcortex_a9: fixed usage of deprecated APIs 2021-01-15 17:08:20 +08:00
4555afa624 libsupport_zynq: custom memcpy 2021-01-15 17:08:14 +08:00
5923a66512 libsupport_zynq/boot: enable FPU on boot 2021-01-15 16:45:27 +08:00
2c6cc58c07 updated rust and compiler_builtins 2021-01-15 16:45:11 +08:00
b4d91e7904 nix/rust-platform: remove rustcSrc which had to be kept in sync 2021-01-15 00:52:22 +01:00
4c79c797d5 default.nix: filterSource 2021-01-15 00:51:45 +01:00
966e43e14e update dependency: linked_list_allocator 2020-12-24 00:52:14 +01:00
8432ff3e30 default.nix: update cargoSha256* 2020-11-20 17:32:52 +01:00
1cd4056370 libboard_zynq: remove unused eth phy name information 2020-11-20 17:21:38 +01:00
ddff295ae1 libboard_zynq: implement eth hot-plugging 2020-11-20 17:12:22 +01:00
379b6b973a libboard_zynq: add support for target_kasli_soc's Marvell88E1512 eth phy 2020-11-19 20:38:10 +01:00
178ab38e35 experiments: delint 2020-11-19 20:27:05 +01:00
3172aba1a8 libboard_zynq: improve i2c doc 2020-11-19 20:26:48 +01:00
a62ca507d0 libregister: allow rustdoc for register! macro 2020-11-19 20:26:18 +01:00
975202a653 libboard_zynq: enable i2c+eeprom for target_kasli_soc 2020-11-19 20:17:36 +01:00
d76a77b443 libregister: s/libregister::/$crate::/ 2020-11-19 20:16:36 +01:00
500472b2a8 add feature target_kasli_soc to libsupport_zynq, libconfig, experiments, szl, default.nix 2020-11-19 19:41:38 +01:00
02217f27d1 libcortex_a9: remove useless target_* features 2020-11-19 19:38:57 +01:00
a3eabf1947 libboard_zynq: prepare target_kasli_soc 2020-11-19 19:28:17 +01:00
a32d7abb9a libboard_zynq: rename ddr DCI_FREQ to DCI_MAX_FREQ 2020-11-19 19:21:38 +01:00
0e8354faa1 remote_run.sh: clean up getopts 2020-11-18 17:45:43 +08:00
07f161d9d8 libconfig: fix ipv6 address configuration key 2020-11-18 10:47:48 +08:00
ef160aa841 README: update board support info 2020-11-18 10:36:55 +08:00
cb50c8d61b update deps, pin compiler_builtins to 0.1.35 2020-11-13 19:40:38 +01:00
7bbd16f143 default.nix: build crates for all targets 2020-11-13 18:07:22 +01:00
0714162113 rename target_cora_z7_10 to target_coraz7 globally 2020-11-13 17:56:47 +01:00
5b2c779cba libboard_zynq: delint ps7_init 2020-11-13 00:23:56 +01:00
0a40d4f36d libboard_zynq: fix zc706 build 2020-11-13 00:23:38 +01:00
55f8d02da8 libboard_zynq: remove ddr-only ps7_init for redpitaya 2020-11-13 00:12:43 +01:00
990fa56d6a libboard_zynq: complete ddr without ps7_init for redpitaya 2020-11-13 00:10:34 +01:00
8fd317d580 libboard_zynq: remove ps7_init for cora_z7_10 2020-11-11 14:21:48 +01:00
07fedddad9 libboard_zynq: doc ddr size limitation, correct target_redpitaya to 512MB 2020-11-11 13:25:55 +01:00
0fde82a982 szl: add target_cora_z7_10 2020-11-10 20:57:31 +01:00
dffe3cb251 libboard_zynq: rm superfluous ddr settings for cora_z7_10 2020-11-10 20:53:46 +01:00
b9323653bb libboard_zynq: complete ddr without ps7_init for cora_z7_10 2020-11-10 14:33:31 +01:00
515d3bb381 libboard_zynq: configure ddr while keeping rstb low 2020-11-08 22:47:59 +01:00
7e22010d7d libboard_zynq: fix pll_cp/pll_res swap in ClockSource::setup() 2020-11-08 22:46:43 +01:00
9ee77d8f44 libboard_zynq: indent ps7_init/cora_z7_10 2020-11-08 19:32:31 +01:00
e508b78b3e libboard_zynq: add ps7_init for cora_z7_10 2020-11-08 19:28:59 +01:00
aef010cb14 openocd: remove xilinx-tcl on Cora Z7 2020-11-08 18:44:41 +01:00
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
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
22833ef0c6 libconfig/sd_reader: added FAT16_LBA ID 2020-10-17 13:37:18 +08:00
80d12d5780 libconfig: make default IP/MAC depend on board 2020-10-14 12:53:32 +08:00
4dd8c93729 add SZL for Red Pitaya 2020-10-14 12:35:23 +08:00
6266d28095 expose patched cargo-xbuild 2020-10-13 18:43:37 +08:00
1796d4e236 update some dependencies
Older versions do not compile with newer Rust.
2020-10-13 18:28:42 +08:00
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
34a63d7732 use new location for libraries in Rust source 2020-10-13 18:27:01 +08:00
27d310a937 update Rust
Needed for compatibility with recent cargo-xbuild.
2020-10-13 18:25:39 +08:00
f60d0589cc fix ps7_init compilation error and warnings 2020-10-01 00:17:47 +08:00
7c9edfdbd5 README: add introduction 2020-09-29 16:27:44 +08:00
c336e450b1 libboard_zynq/eth/phy: add PEF7071 2020-09-29 16:01:54 +08:00
6af453494b libboard_zynq/ddr: use ps7_init for redpitaya ddr 2020-09-26 17:01:37 +08:00
ac3e6983b0 experiments: fix zc706 build 2020-09-09 21:30:56 +08:00
338f918531 experiments: update banner 2020-09-09 21:29:25 +08:00
4751fd6011 default.nix: build redpitaya-experiments 2020-09-09 21:28:32 +08:00
e601ac9c45 remove flash support
PITA to get to work and most boards have SD.
2020-09-09 20:13:13 +08:00
a6955edf14 add Red Pitaya support (WIP) 2020-09-09 20:10:05 +08:00
c634313d5e update authors in cargo.toml 2020-09-09 19:36:25 +08:00
7360984efb add libconfig, libcoreio, szl from artiq-zynq a277e89b3ad; update dependencies 2020-09-09 17:56:50 +08:00
82794d3abd default.nix: make naming consistent with artiq-zynq 2020-09-09 17:12:08 +08:00
450ccef18e sync nix files with artiq-zynq c3f9a76f2a; add fsbl 2020-09-09 16:51:50 +08:00
4e18368aaf remove obsolete build.sh 2020-09-09 15:03:17 +08:00
75494421c5 cargo: remove unmaintained runner 2020-09-09 15:01:39 +08:00
c4fb7b4c41 cargo: remove unmaintained dev profile 2020-09-09 15:01:03 +08:00
a51f8f2eea openocd: remove ps7_init on Cora Z7 2020-09-09 15:00:09 +08:00
7680de26f0 openocd: sync with artiq-zynq 8bb1727e64 2020-09-09 14:58:18 +08:00
7edd192c3b remove outdated/unmaintained files 2020-09-09 14:57:03 +08:00
4fef8a7192 libasync/executor: reduced reallocation for vector 2020-09-07 16:13:51 +08:00
ae244082ed more cpu options 2020-09-07 16:13:51 +08:00
66c66447dd fix some compilation warnings 2020-09-06 00:17:59 +08:00
02c67051e8 CPU options for better performance
L2 cache options and prefetch options
2020-09-04 16:38:48 +08:00
08fd1391c5 libcortex_a9/mmu: enabled program flow prediction 2020-09-04 13:18:39 +08:00
a116142f63 libsupport_zynq/ram: check ptr range for deallocation 2020-09-03 12:56:10 +08:00
157439bc88 libcortex_a9/semaphore: mark new as const fn 2020-09-02 09:51:52 +08:00
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
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
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
273f9ea72b libboard_zynq/eth: fix comment 2020-08-24 21:47:10 +08:00
671968bac3 libboard_zynq/eth: fixed tx lost packet 2020-08-24 15:51:01 +08:00
39f672dde8 libasync/smoltcp/mod: prevent duplicated wakers 2020-08-24 15:25:03 +08:00
c13ca614ef libcortex_a9/mutex: use AcqRel for CAS operations 2020-08-24 15:24:20 +08:00
bb09d25378 libboard_zynq/ethernet: ethernet fix and config 2020-08-21 13:34:02 +08:00
a1f859637a experiments: enabled L2 cache
...and removed some trailing spaces
2020-08-20 13:02:28 +08:00
7cb2669c3b Updated cargo dependencies 2020-08-20 13:01:49 +08:00
511c906d4d libcortex_a9/uncached: fixed mmu setting 2020-08-20 13:01:49 +08:00
1ba0aa450f libsupport_zynq/boot: fix cache mainteinance opertaions 2020-08-20 13:01:49 +08:00
283bc9b810 libcortex_a9: added L2 cache 2020-08-20 13:01:17 +08:00
b268fe015a stdio::drop_uart(): add delay 2020-08-17 19:38:41 +02:00
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
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
1a96a7550a libboard_zynq: make RegisterBlock constructors more consistent 2020-08-13 14:49:26 +08:00
36947104e3 libboard_zynq: make constructor names more consistent 2020-08-13 13:31:53 +08:00
11089d8a64 i2c: delete dead code 2020-08-12 16:51:25 +08:00
97 changed files with 13245 additions and 4616 deletions

View File

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

212
Cargo.lock generated
View File

@ -2,181 +2,257 @@
# It is not intended for manual editing.
[[package]]
name = "bit_field"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.3.4"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
[[package]]
name = "cfg-if"
version = "0.1.10"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "compiler_builtins"
version = "0.1.27"
version = "0.1.39"
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]]
name = "embedded-hal"
version = "0.2.3"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db184d3fa27bc7a2344250394c0264144dfe0bc81a4401801dcb964b8dd172ad"
dependencies = [
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"nb 0.1.3",
"void",
]
[[package]]
name = "experiments"
version = "0.0.0"
dependencies = [
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libasync 0.0.0",
"libboard_zynq 0.0.0",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"libsupport_zynq 0.0.0",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"embedded-hal",
"libasync",
"libboard_zynq",
"libcortex_a9",
"libregister",
"libsupport_zynq",
"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]]
name = "libasync"
version = "0.0.0"
dependencies = [
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libcortex_a9 0.0.0",
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"embedded-hal",
"libcortex_a9",
"nb 1.0.0",
"pin-utils",
"smoltcp",
]
[[package]]
name = "libboard_zynq"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bit_field",
"embedded-hal",
"libasync",
"libcortex_a9",
"libregister",
"log",
"nb 0.1.3",
"smoltcp",
"void",
"volatile-register",
]
[[package]]
name = "libconfig"
version = "0.1.0"
dependencies = [
"core_io",
"fatfs",
"libboard_zynq",
"log",
]
[[package]]
name = "libcortex_a9"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libregister 0.0.0",
"bit_field",
"libregister",
"volatile-register",
]
[[package]]
name = "libregister"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bit_field",
"vcell",
"volatile-register",
]
[[package]]
name = "libsupport_zynq"
version = "0.0.0"
dependencies = [
"compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"libboard_zynq 0.0.0",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc",
"compiler_builtins",
"libboard_zynq",
"libcortex_a9",
"libregister",
"linked_list_allocator",
"r0",
]
[[package]]
name = "linked_list_allocator"
version = "0.8.4"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
[[package]]
name = "log"
version = "0.4.8"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if",
]
[[package]]
name = "managed"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]]
name = "nb"
version = "0.1.2"
version = "0.1.3"
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]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "r0"
version = "1.0.0"
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]]
name = "smoltcp"
version = "0.6.0"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ec49aa038736b0bc852ccd9a0eadc8f7832fe8f9c8eec4e8a740b36d665614"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags",
"byteorder",
"managed",
]
[[package]]
name = "szl"
version = "0.1.0"
dependencies = [
"byteorder",
"core_io",
"libboard_zynq",
"libconfig",
"libcortex_a9",
"libregister",
"libsupport_zynq",
"log",
]
[[package]]
name = "vcell"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
"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]
members = [
"libregister", "libcortex_a9",
"libboard_zynq", "libsupport_zynq",
"libregister",
"libcortex_a9",
"libboard_zynq",
"libsupport_zynq",
"libasync",
"libconfig",
"experiments",
"szl",
]
[profile.dev]
panic = "abort"
lto = false
[profile.release]
panic = "abort"
debug = true
# Link-Time Optimization:
# turn off if you get unusable debug symbols.
codegen-units = 1
opt-level = 'z'
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.

View File

@ -1,4 +1,24 @@
# 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
```shell
nix-shell --command "cargo xbuild --release -p experiments"
@ -6,37 +26,7 @@ nix-shell --command "cargo xbuild --release -p experiments"
Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments`
# Debug
## Using the Xilinx toolchain
Tested with the ZC706 board.
Run the Xilinx Microprocessor Debugger:
```shell
/opt/Xilinx/14.7/ISE_DS/EDK/bin/lin64/xmd
```
Connect to target (given it is connected and you have permissions):
```tcl
connect arm hw
```
Leave xmd running.
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
## Debug
### Running on the ZC706
@ -49,7 +39,7 @@ openocd -f zc706.cfg
### Running on the Cora Z7-10
```shell
nix-shell --command "cd experiments && cargo xbuild --release --no-default-features --features=target_cora_z7_10"
nix-shell --command "cd experiments && cargo xbuild --release --no-default-features --features=target_coraz7"
cd openocd
openocd -f cora-z7-10.cfg
```
@ -60,43 +50,7 @@ openocd -f cora-z7-10.cfg
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
```
### Development Process
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 ~/zynq-rs
./build.sh $your_user_or_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 zynq-rs
# For ZC706, run:
./tmux.sh 0
# For Cora Z7, run:
./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`.
## License
Copyright (C) 2019-2021 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 -p experiments" && scp -C target/armv7-none-eabihf/release/experiments $1@rpi-4.m-labs.hk:/home/$1/zc706/zc706.elf

View File

@ -1,63 +1,48 @@
{ # 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 ];
pkgs = import <nixpkgs> { overlays = [ (import ./nix/mozilla-overlay.nix) ]; };
rustPlatform = (import ./nix/rust-platform.nix { inherit pkgs; });
cargo-xbuild = pkgs.callPackage ./nix/cargo-xbuild.nix {};
cargoSha256Experiments = "154sdcfs1wazzn1pvm1nqbpary6kq5f3cx5sccz0ss4w8rkj9hr2";
cargoSha256SZL = "18rbf6vvvz65nc8m0l9y9km97pydhidw4f6yx6yb9vv58yzklsvy";
build-crate = name: crate: features: cargoSha256:
rustPlatform.buildRustPackage rec {
name = "${crate}";
src = builtins.filterSource (path: type:
baseNameOf path != "target"
) ./.;
inherit cargoSha256;
nativeBuildInputs = [ cargo-xbuild pkgs.llvmPackages_9.clang-unwrapped ];
buildPhase = ''
pushd ${crateSubdir}
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
pushd ${crate}
cargo xbuild --release --frozen \
--no-default-features \
--features=${cargoFeatures}
--features=${features}
popd
'';
XARGO_RUST_SRC = "${rustcSrc}/src";
installPhase = ''
mkdir $out
ls -la target/armv7-none-eabihf/release/
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
'';
});
xbuildCrate = name: crate: features: xbuildRustPackage rec {
name = "${crate}";
src = ./.;
crateSubdir = crate;
cargoSha256 = "1c1qpg9by8bg93yhgllb5xs155g27qmh99pbrb681wazm8k7nwim";
cargoFeatures = features;
doCheck = false;
dontFixup = true;
doCheck = false;
dontFixup = true;
};
targetCrates = target: {
"${target}-experiments" = build-crate "${target}-experiments" "experiments" "target_${target}" cargoSha256Experiments;
"${target}-szl" = build-crate "${target}-szl" "szl" "target_${target}" cargoSha256SZL;
};
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";
};
}
targets = ["zc706" "coraz7" "redpitaya" "kasli_soc"];
in
{
inherit cargo-xbuild;
zc706-fsbl = import ./nix/fsbl.nix { inherit pkgs; };
} // (builtins.foldl' (results: target:
results // targetCrates target
) {} targets)

View File

@ -2,12 +2,14 @@
name = "experiments"
description = "Developing bare-metal Rust on Zynq"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
authors = ["M-Labs"]
edition = "2018"
[features]
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_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"]
[dependencies]

View File

@ -34,6 +34,20 @@ SECTIONS
__bss_end = .;
} > 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_end = .;
. += 0x200;

View File

@ -2,6 +2,7 @@
#![no_main]
#![feature(const_in_array_repeat_expressions)]
#![feature(naked_functions)]
#![feature(asm)]
extern crate alloc;
@ -15,7 +16,7 @@ use libboard_zynq::{
self as zynq,
clocks::source::{ArmPll, ClockSource, IoPll},
clocks::Clocks,
print, println, stdio,
println, stdio,
mpcore,
gic,
smoltcp::{
@ -25,13 +26,16 @@ use libboard_zynq::{
},
time::Milliseconds,
};
#[cfg(feature = "target_zc706")]
use libboard_zynq::print;
use libcortex_a9::{
mutex::Mutex,
l2c::enable_l2_cache,
sync_channel::{Sender, Receiver},
sync_channel,
regs::{MPIDR, SP},
spin_lock_yield, notify_spin_lock,
asm
asm, interrupt_handler
};
use libregister::{RegisterR, RegisterW};
use libsupport_zynq::{
@ -51,13 +55,10 @@ extern "C" {
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn IRQ() {
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
if MPIDR.read().cpu_id() == 1{
let mpcore = mpcore::RegisterBlock::new();
let mut gic = gic::InterruptController::new(mpcore);
let mpcore = mpcore::RegisterBlock::mpcore();
let mut gic = gic::InterruptController::gic(mpcore);
let id = gic.get_interrupt_id();
if id.0 == 0 {
gic.end_interrupt(id);
@ -72,10 +73,10 @@ pub unsafe extern "C" fn IRQ() {
stdio::drop_uart();
println!("IRQ");
loop {}
}
});
pub fn restart_core1() {
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
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) {
@ -86,41 +87,36 @@ pub fn restart_core1() {
#[no_mangle]
pub fn main_core0() {
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
println!("\nzc706 main");
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
enable_l2_cache(0x8);
println!("\nZynq experiments");
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
// ps7_init::apply();
libboard_zynq::stdio::drop_uart();
libboard_zynq::logger::init().unwrap();
log::set_max_level(log::LevelFilter::Trace);
info!(
"Boot mode: {:?}",
zynq::slcr::RegisterBlock::new()
zynq::slcr::RegisterBlock::slcr()
.boot_mode
.read()
.boot_mode_pins()
);
#[cfg(feature = "target_zc706")]
#[cfg(any(
feature = "target_zc706",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
const CPU_FREQ: u32 = 800_000_000;
#[cfg(feature = "target_cora_z7_10")]
#[cfg(feature = "target_coraz7")]
const CPU_FREQ: u32 = 650_000_000;
info!("Setup clock sources...");
ArmPll::setup(2 * CPU_FREQ);
Clocks::set_cpu_freq(CPU_FREQ);
#[cfg(feature = "target_zc706")]
{
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
}
#[cfg(feature = "target_cora_z7_10")]
{
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
}
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
info!("PLLs set up");
let clocks = zynq::clocks::Clocks::get();
info!(
@ -131,63 +127,13 @@ pub fn main_core0() {
clocks.cpu_1x()
);
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 _flash = flash.stop();
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"))]
ddr.memtest();
ram::init_alloc_ddr(&mut ddr);
#[cfg(dev)]
for i in 0..=1 {
let mut flash_io = flash.manual_mode(i);
// 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);
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| {
flash_io.erase(0);
});
flash_io.write_enabled(|flash_io| {
flash_io.program(0, [0x23054223; 0x100 >> 2].iter().cloned());
});
flash = flash_io.stop();
}
boot::Core1::start(false);
let core1_req = unsafe { &mut CORE1_REQ.0 };
@ -203,33 +149,33 @@ pub fn main_core0() {
unsafe {
core1_req.drop_elements();
}
// Test I2C
#[cfg(feature = "target_zc706")]
{
let mut i2c = zynq::i2c::I2C::i2c();
i2c.init();
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,
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]);
eeprom.write(0x08, &eeprom_buffer[6..22]);
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);
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);
eeprom.read(0x08, &mut eeprom_buffer).unwrap();
print!("Data read from EEPROM @ 0x08: (hex) ");
for i in 0..16 {
print!("{:02x} ", eeprom_buffer[i]);
@ -237,7 +183,7 @@ pub fn main_core0() {
println!("");
}
let eth = zynq::eth::Eth::default(HWADDR.clone());
let eth = zynq::eth::Eth::eth0(HWADDR.clone());
println!("Eth on");
const RX_LEN: usize = 4096;
@ -270,12 +216,12 @@ pub fn main_core0() {
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
let stats_tx = stats_tx.clone();
task::spawn(async move {
let tx_data = (0..=255).take(4096).collect::<alloc::vec::Vec<u8>>();
let tx_data = (0..=255).cycle().take(4096).collect::<alloc::vec::Vec<u8>>();
loop {
// const CHUNK_SIZE: usize = 65536;
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
match stream.send_slice(&tx_data[..]).await {
Ok(len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
Ok(_len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
Err(e) => {
warn!("tx: {:?}", e);
break
@ -331,7 +277,7 @@ static DONE: Mutex<bool> = Mutex::new(false);
#[no_mangle]
pub fn main_core1() {
println!("Hello from core1!");
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
let req = unsafe { &mut CORE1_REQ.1 };
let res = unsafe { &mut CORE1_RES.0 };

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

View File

@ -6,7 +6,7 @@ use core::{
sync::atomic::{AtomicBool, Ordering},
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 pin_utils::pin_mut;
@ -31,7 +31,7 @@ static VTABLE: RawWakerVTable = {
/// ready should not move as long as this waker references it. That is
/// the reason for keeping Tasks in a pinned box.
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
@ -44,7 +44,7 @@ pub struct Executor {
/// Tasks reside on the heap, so that we just queue pointers. They
/// must also be pinned in memory because our RawWaker is a pointer
/// to their `ready` field.
tasks: RefCell<Deque<Pin<Box<Task>>>>,
tasks: RefCell<Vec<Pin<Box<Task>>>>,
}
impl Executor {
@ -52,7 +52,7 @@ impl Executor {
pub fn new() -> Self {
Self {
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);
let ready = AtomicBool::new(true);
let waker = wrap_waker(&ready);
let mut backup = Vec::new();
let val = loop {
// advance the main task
if ready.load(Ordering::Relaxed) {
@ -85,10 +86,9 @@ impl Executor {
// println!("ran block_on");
}
// println!("tasks: {}", self.tasks.borrow().len());
// advance other tasks
let next_task = self.tasks.borrow_mut().pop_front();
if let Some(mut task) = next_task {
// advance all tasks
core::mem::swap(&mut *self.tasks.borrow_mut(), &mut backup);
for mut task in backup.drain(..) {
// 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
// "oneshot": they'll issue a `wake` and then disable themselves to not run again
@ -106,7 +106,7 @@ impl Executor {
}
}
// 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
@ -119,7 +119,7 @@ impl Executor {
pub fn spawn(&self, f: impl Future + 'static) {
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,
phy::Device,
socket::SocketSet,
time::Instant,
time::{Duration, Instant},
};
use crate::task;
mod tcp_stream;
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;
pub struct Sockets {
sockets: RefCell<SocketSet<'static, 'static, 'static>>,
sockets: RefCell<SocketSet<'static>>,
wakers: RefCell<Vec<Waker>>,
}
@ -30,7 +36,7 @@ impl Sockets {
let sockets = RefCell::new(SocketSet::new(sockets_storage));
let wakers = RefCell::new(Vec::new());
let instance = Sockets {
sockets,
wakers,
@ -41,14 +47,24 @@ impl Sockets {
/// Block and run executor indefinitely while polling the smoltcp
/// iface
pub fn run<'b, 'c, 'e, D: for<'d> Device<'d>>(
iface: &mut EthernetInterface<'b, 'c, 'e, D>,
pub fn run<'b, D: for<'d> Device<'d> + LinkCheck>(
iface: &mut EthernetInterface<'b, D>,
mut get_time: impl FnMut() -> Instant,
) -> ! {
task::block_on(async {
let mut last_link_check = Instant::from_millis(0);
const LINK_CHECK_INTERVAL: u64 = 500;
loop {
let instant = get_time();
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;
}
})
@ -57,10 +73,10 @@ impl Sockets {
pub(crate) fn instance() -> &'static Self {
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,
iface: &mut EthernetInterface<'b, 'c, 'e, D>,
iface: &mut EthernetInterface<'b, D>,
instant: Instant
) {
let processed = {
@ -81,7 +97,14 @@ impl Sockets {
/// TODO: this was called through eg. TcpStream, another poll()
/// might want to send packets before sleeping for an interrupt.
pub(crate) fn register_waker(waker: Waker) {
Self::instance().wakers.borrow_mut()
.push(waker);
let mut wakers = Self::instance().wakers.borrow_mut();
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

@ -262,6 +262,14 @@ impl TcpStream {
pub fn set_timeout(&mut self, duration: Option<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 {

View File

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

View File

@ -14,7 +14,7 @@ enum CpuClockMode {
impl CpuClockMode {
pub fn get() -> Self {
let regs = slcr::RegisterBlock::new();
let regs = slcr::RegisterBlock::slcr();
if regs.clk_621_true.read().clk_621_true() {
CpuClockMode::C621
} else {
@ -59,7 +59,7 @@ impl Clocks {
}
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 pll = match arm_clk_ctrl.srcsel() {
ArmPllSource::ArmPll => self.arm,
@ -92,7 +92,7 @@ impl Clocks {
}
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 pll = match uart_clk_ctrl.srcsel() {
slcr::PllSource::ArmPll =>
@ -106,7 +106,7 @@ impl Clocks {
}
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 pll = match sdio_clk_ctrl.srcsel() {
slcr::PllSource::ArmPll =>

View File

@ -4,8 +4,12 @@ use super::slcr;
#[cfg(feature = "target_zc706")]
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;
#[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))
const PLL_FDIV_LOCK_PARAM: &[(u16, (u8, u8, u16))] = &[
@ -44,7 +48,7 @@ pub trait ClockSource {
/// get configured frequency
fn freq() -> u32 {
let mut slcr = slcr::RegisterBlock::new();
let mut slcr = slcr::RegisterBlock::slcr();
let (pll_ctrl, _, _) = Self::pll_regs(&mut slcr);
u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK
}
@ -55,7 +59,7 @@ pub trait ClockSource {
/// 25.10.4 PLLs
fn setup(target_freq: u32) {
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)
.nth(0)
.expect("PLL_FDIV_LOCK_PARAM")

View File

@ -1,7 +1,9 @@
use libregister::{RegisterR, RegisterW, RegisterRW};
use log::{debug, info, error};
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}};
mod regs;
@ -10,27 +12,32 @@ mod regs;
/// Micron MT41J256M8HX-15E: 667 MHz DDR3
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
const DDR_FREQ: u32 = 525_000_000;
/// MT41K256M16HA-125
const DCI_FREQ: u32 = 10_000_000;
#[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 {
regs: &'static mut regs::RegisterBlock,
}
impl DdrRam {
pub fn new() -> Self {
pub fn ddrram() -> Self {
let clocks = Self::clock_setup();
Self::calibrate_iob_impedance(&clocks);
Self::configure_iob();
let regs = unsafe { regs::RegisterBlock::new() };
Self::calibrate_iob_impedance(&clocks);
let regs = regs::RegisterBlock::ddrc();
let mut ddr = DdrRam { regs };
ddr.configure();
ddr.reset_ddrc();
ddr.reset_ddrc(|ddr| ddr.configure());
ddr
}
@ -57,7 +64,7 @@ impl DdrRam {
}
fn calculate_dci_divisors(clocks: &Clocks) -> (u8, u8) {
let target = (DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ;
let target = (DCI_MAX_FREQ - 1 + clocks.ddr) / DCI_MAX_FREQ;
let mut best = None;
let mut best_error = 0;
@ -140,13 +147,22 @@ impl DdrRam {
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_zc706")]
let data1_config = data0_config.clone();
#[cfg(feature = "target_cora_z7_10")]
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
let data0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::VrefDifferential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_cora_z7_10")]
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
let data1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
#[cfg(feature = "target_redpitaya")]
let data0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::VrefDifferential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_redpitaya")]
let data1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
slcr.ddriob_data0.write(data0_config);
@ -160,16 +176,24 @@ impl DdrRam {
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_zc706")]
let diff1_config = diff0_config.clone();
#[cfg(feature = "target_cora_z7_10")]
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
let diff0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::Differential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_cora_z7_10")]
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
let diff1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
#[cfg(feature = "target_redpitaya")]
let diff0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::Differential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_redpitaya")]
let diff1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
slcr.ddriob_diff0.write(diff0_config);
slcr.ddriob_diff1.write(diff1_config);
@ -186,12 +210,12 @@ impl DdrRam {
slcr.ddriob_drive_slew_clock.write(0x00F9861C);
}
// Enable external V[REF]
#[cfg(feature = "target_cora_z7_10")]
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
slcr.ddriob_ddr_ctrl.modify(|_, w| w
.vref_int_en(false)
.vref_ext_en_lower(true)
.vref_ext_en_upper(false)
.refio_en(true)
);
#[cfg(feature = "target_zc706")]
slcr.ddriob_ddr_ctrl.modify(|_, w| w
@ -200,16 +224,48 @@ impl DdrRam {
.vref_ext_en_lower(false)
.vref_ext_en_upper(false)
);
#[cfg(feature = "target_redpitaya")]
slcr.ddriob_ddr_ctrl.modify(|_, w| w
.vref_int_en(false)
.vref_ext_en_lower(true)
.vref_ext_en_upper(false)
.refio_en(true)
);
});
}
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_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_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()
@ -221,6 +277,20 @@ impl DdrRam {
.rd2pre(0x4)
.t_rcd(0x7)
);
#[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()
@ -228,6 +298,23 @@ impl DdrRam {
.emr(0x4)
);
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
self.regs.phy_configs[2].modify(
|_, w| w.data_slice_in_use(false)
);
#[cfg(any(feature = "target_coraz7", feature = "target_kasli_soc"))]
self.regs.phy_configs[3].modify(
|_, w| w.data_slice_in_use(false)
);
#[cfg(feature = "target_redpitaya")]
self.regs.phy_configs[2].modify(
|_, w| w.data_slice_in_use(false)
);
#[cfg(feature = "target_redpitaya")]
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)
@ -260,12 +347,32 @@ impl DdrRam {
.ctrlup_max(0x40)
);
self.regs.phy_init_ratio3.write(
#[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_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)
@ -280,7 +387,20 @@ impl DdrRam {
}
/// 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_kasli_soc"))]
let width = regs::DataBusWidth::Width16bit;
#[cfg(feature = "target_redpitaya")]
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")]
unsafe {
// row/column address bits
@ -288,16 +408,18 @@ impl DdrRam {
self.regs.dram_addr_map_col.write(0xFFF00000);
self.regs.dram_addr_map_row.write(0x0F666666);
}
#[cfg(any(
feature = "target_coraz7",
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
.soft_rstb(true)
.powerdown_en(false)
@ -318,10 +440,16 @@ impl DdrRam {
/// actually there's 1 MB more but starting at 0x0000_0000
/// overlaps with OCM.
pub fn size(&self) -> usize {
// DDR range ends at 0x3FFF_FFFF in the default SCU address
// filtering address map
#[cfg(feature = "target_zc706")]
let megabytes = 1023;
#[cfg(feature = "target_cora_z7_10")]
let megabytes = 511;
#[cfg(any(
feature = "target_coraz7",
feature = "target_redpitaya",
feature = "target_kasli_soc",
))]
let megabytes = 512;
megabytes * 1024 * 1024
}

View File

@ -1,6 +1,6 @@
use volatile_register::{RO, RW};
use libregister::{register, register_bit, register_bits, register_bits_typed};
use libregister::{register, register_at, register_bit, register_bits, register_bits_typed};
#[allow(unused)]
#[derive(Clone, Copy)]
@ -33,14 +33,14 @@ pub struct RegisterBlock {
pub lpr: RW<u32>,
pub wr: RW<u32>,
pub dram_param0: DramParam0,
pub dram_param1: RW<u32>,
pub dram_param1: DramParam1,
pub dram_param2: DramParam2,
pub dram_param3: RW<u32>,
pub dram_param3: DramParam3,
pub dram_param4: RW<u32>,
pub dram_init_param: RW<u32>,
pub dram_emr: 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_addr_map_bank: RW<u32>,
pub dram_addr_map_col: RW<u32>,
@ -60,7 +60,7 @@ pub struct RegisterBlock {
pub ctrl6: RW<u32>,
_unused1: [RO<u32>; 8],
pub che_refresh_timer01: RW<u32>,
pub che_t_zq: RW<u32>,
pub che_t_zq: CheTZq,
pub che_t_zq_short_interval: RW<u32>,
pub deep_pwrdwn: RW<u32>,
pub reg_2c: Reg2C,
@ -84,15 +84,9 @@ pub struct RegisterBlock {
pub che_ecc_corr_bit_mask_63_32_offset: RW<u32>,
_unused3: [RO<u32>; 5],
pub phy_rcvr_enable: RW<u32>,
pub phy_config0: RW<u32>,
pub phy_config1: RW<u32>,
pub phy_config2: RW<u32>,
pub phy_config3: RW<u32>,
pub phy_configs: [PhyConfig; 4],
_unused4: RO<u32>,
pub phy_init_ratio0: PhyInitRatio,
pub phy_init_ratio1: PhyInitRatio,
pub phy_init_ratio2: PhyInitRatio,
pub phy_init_ratio3: PhyInitRatio,
pub phy_init_ratios: [PhyInitRatio; 4],
_unused5: RO<u32>,
pub phy_rd_dqs_cfg0: RW<u32>,
pub phy_rd_dqs_cfg1: RW<u32>,
@ -114,7 +108,7 @@ pub struct RegisterBlock {
pub wr_data_slv2: RW<u32>,
pub wr_data_slv3: RW<u32>,
_unused9: RO<u32>,
pub reg_64: RW<u32>,
pub reg_64: Reg64,
pub reg_65: Reg65,
_unused10: [RO<u32>; 3],
pub reg69_6a0: RW<u32>,
@ -138,14 +132,8 @@ pub struct RegisterBlock {
_unused14: [RO<u32>; 5],
pub axi_id: RW<u32>,
pub page_mask: RW<u32>,
pub axi_priority_wr_port0: RW<u32>,
pub axi_priority_wr_port1: RW<u32>,
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>,
pub axi_priority_wr_ports: [RW<u32>; 4],
pub axi_priority_rd_ports: [AxiPriorityRd; 4],
_unused15: [RO<u32>; 27],
pub excl_access_cfg0: RW<u32>,
pub excl_access_cfg1: RW<u32>,
@ -158,11 +146,7 @@ pub struct RegisterBlock {
pub lpddr_ctrl3: RW<u32>,
}
impl RegisterBlock {
pub unsafe fn new() -> &'static mut Self {
&mut *(0xF8006000 as *mut _)
}
}
register_at!(RegisterBlock, 0xF8006000, ddrc);
register!(ddrc_ctrl, DdrcCtrl, RW, u32);
register_bit!(ddrc_ctrl,
@ -177,6 +161,14 @@ 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);
@ -186,10 +178,29 @@ 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);
@ -202,6 +213,13 @@ 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);
@ -216,10 +234,27 @@ 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);
@ -235,3 +270,10 @@ register!(mode_sts_reg,
ModeStsReg, RO, u32);
register_bits_typed!(mode_sts_reg, operating_mode, u8, ControllerStatus, 0, 2);
// (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

@ -27,7 +27,7 @@ const TX_1000: u32 = 125_000_000;
pub struct Buffer(pub [u8; MTU]);
impl Buffer {
pub fn new() -> Self {
pub const fn new() -> Self {
Buffer([0; MTU])
}
}
@ -145,10 +145,12 @@ pub struct Eth<GEM: Gem, RX, TX> {
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 default(macaddr: [u8; 6]) -> Self {
pub fn eth0(macaddr: [u8; 6]) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Manual example: 0x0000_1280
// MDIO
@ -224,48 +226,48 @@ impl Eth<Gem0, (), ()> {
// RX_CLK
slcr.mio_pin_22.write(
slcr::MioPin22::zeroed()
.tri_enable(true)
.l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl)
.pullup(true)
);
// RX_CTRL
slcr.mio_pin_27.write(
slcr::MioPin27::zeroed()
.tri_enable(true)
.l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl)
.pullup(true)
);
// RXD3
slcr.mio_pin_26.write(
slcr::MioPin26::zeroed()
.tri_enable(true)
.l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl)
.pullup(true)
);
// RXD2
slcr.mio_pin_25.write(
slcr::MioPin25::zeroed()
.tri_enable(true)
.l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl)
.pullup(true)
);
// RXD1
slcr.mio_pin_24.write(
slcr::MioPin24::zeroed()
.tri_enable(true)
.l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl)
.pullup(true)
);
// RXD0
slcr.mio_pin_23.write(
slcr::MioPin23::zeroed()
.tri_enable(true)
.l0_sel(true)
.speed(true)
.io_type(slcr::IoBufferType::Hstl)
.pullup(true)
);
@ -280,19 +282,22 @@ impl Eth<Gem0, (), ()> {
}
pub fn gem0(macaddr: [u8; 6]) -> Self {
Self::new(macaddr)
Self::gem_common(macaddr)
}
}
impl Eth<Gem1, (), ()> {
// TODO: Add a `eth1()`
pub fn gem1(macaddr: [u8; 6]) -> Self {
Self::new(macaddr)
Self::gem_common(macaddr)
}
}
impl<GEM: Gem> Eth<GEM, (), ()> {
fn new(macaddr: [u8; 6]) -> Self {
fn gem_common(macaddr: [u8; 6]) -> Self {
GEM::setup_clock(TX_1000);
let mut inner = EthInner {
@ -311,6 +316,7 @@ impl<GEM: Gem> Eth<GEM, (), ()> {
tx: (),
inner,
phy,
idle: true,
}
}
}
@ -322,6 +328,7 @@ impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
tx: self.tx,
inner: self.inner,
phy: self.phy,
idle: self.idle,
};
let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0);
@ -341,6 +348,7 @@ impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
tx: tx::DescList::new(tx_size),
inner: self.inner,
phy: self.phy,
idle: self.idle,
};
let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0);
@ -392,17 +400,31 @@ impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
regs::RxStatus::zeroed()
.frame_recd(true)
);
self.idle = true;
}
_ => {}
_ =>
self.idle = false,
}
result
} else {
self.inner.check_link_change(&self.phy);
self.idle = true;
Ok(None)
}
}
}
impl<GEM: Gem, TX> libasync::smoltcp::LinkCheck for &mut Eth<GEM, rx::DescList, TX> {
type Link = Option<phy::Link>;
fn check_link_change(&mut self) -> Option<Self::Link> {
self.inner.check_link_change(&self.phy)
}
fn is_idle(&self) -> bool {
self.idle
}
}
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)
@ -436,10 +458,11 @@ impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::
regs: GEM::regs(),
desc_list: &mut self.tx,
};
self.idle = false;
Some((pktref, tx_token))
}
Ok(None) => {
self.inner.check_link_change(&self.phy);
self.idle = true;
None
}
Err(e) => {
@ -530,7 +553,10 @@ impl<GEM: Gem> EthInner<GEM> {
fn configure(&mut self, macaddr: [u8; 6]) {
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;
}
GEM::regs().net_cfg.write(
regs::NetCfg::zeroed()
@ -539,10 +565,10 @@ impl<GEM: Gem> EthInner<GEM> {
.speed(true)
.no_broadcast(false)
.multi_hash_en(true)
// Promiscuous mode (TODO?)
.copy_all(true)
.rx_1536_byte_frames(true)
// Remove 4-byte Frame CheckSum
.fcs_remove(true)
.dis_cp_pause_frame(true)
// RX checksum offload
.rx_chksum_offld_en(true)
// One of the slower speeds
@ -550,22 +576,23 @@ impl<GEM: Gem> EthInner<GEM> {
);
let macaddr_msbs =
(u16::from(macaddr[0]) << 8) |
u16::from(macaddr[1]);
(u16::from(macaddr[5]) << 8) |
u16::from(macaddr[4]);
let macaddr_lsbs =
(u32::from(macaddr[2]) << 24) |
(u32::from(macaddr[3]) << 16) |
(u32::from(macaddr[4]) << 8) |
u32::from(macaddr[5]);
GEM::regs().spec_addr1_top.write(
regs::SpecAddrTop::zeroed()
.addr_msbs(macaddr_msbs)
);
(u32::from(macaddr[3]) << 24) |
(u32::from(macaddr[2]) << 16) |
(u32::from(macaddr[1]) << 8) |
u32::from(macaddr[0]);
// writing to bot would disable the specific address
GEM::regs().spec_addr1_bot.write(
regs::SpecAddrBot::zeroed()
.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(
regs::DmaCfg::zeroed()
@ -595,13 +622,7 @@ impl<GEM: Gem> EthInner<GEM> {
}
fn check_link_change(&mut self, phy: &Phy) {
// 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
}
fn check_link_change(&mut self, phy: &Phy) -> Option<Option<phy::Link>> {
let link = phy.get_link(self);
// Check link state transition
@ -633,6 +654,9 @@ impl<GEM: Gem> EthInner<GEM> {
}
self.link = link;
Some(link)
} else {
None
}
}
}

View File

@ -1,5 +1,4 @@
pub mod id;
use id::{identify_phy, PhyIdentifier};
mod status;
pub use status::Status;
mod control;
@ -7,7 +6,7 @@ pub use control::Control;
mod pssr;
pub use pssr::PSSR;
#[derive(Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Link {
pub speed: LinkSpeed,
pub duplex: LinkDuplex,
@ -31,110 +30,154 @@ pub trait PhyAccess {
fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
}
#[derive(Clone)]
pub struct Phy {
pub addr: u8,
device: PhyDevice,
}
#[derive(Clone, Copy)]
pub enum PhyDevice {
Marvell88E1116R,
Rtl8211E,
}
const OUI_MARVELL: u32 = 0x005043;
const OUI_REALTEK: u32 = 0x000732;
impl Phy {
/// Probe all addresses on MDIO for a known PHY
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
(1..32).filter_map(|addr| {
match identify_phy(pa, addr) {
Some(PhyIdentifier {
oui: OUI_MARVELL,
model: 36,
..
}) => Some(PhyDevice::Marvell88E1116R),
Some(PhyIdentifier {
oui: OUI_REALTEK,
model: 0b010001,
rev: 0b0101,
}) => Some(PhyDevice::Rtl8211E),
_ => None,
}.map(|device| Phy { addr, device })
}).next()
}
pub fn name(&self) -> &'static str {
match self.device {
PhyDevice::Marvell88E1116R => &"Marvell 88E1116R",
PhyDevice::Rtl8211E => &"RTL8211E",
}
}
pub fn read_reg<PA, PR>(&self, pa: &mut PA) -> PR
where
PA: PhyAccess,
PR: PhyRegister + From<u16>,
{
pa.read_phy(self.addr, PR::addr()).into()
}
pub fn modify_reg<PA, PR, F>(&self, pa: &mut PA, mut f: F)
where
PA: PhyAccess,
PR: PhyRegister + From<u16> + Into<u16>,
F: FnMut(PR) -> PR,
{
let reg = pa.read_phy(self.addr, PR::addr()).into();
let reg = f(reg);
pa.write_phy(self.addr, PR::addr(), reg.into())
}
pub fn modify_control<PA, F>(&self, pa: &mut PA, f: F)
where
PA: PhyAccess,
F: FnMut(Control) -> Control,
{
self.modify_reg(pa, f)
}
pub fn get_control<PA: PhyAccess>(&self, pa: &mut PA) -> Control {
self.read_reg(pa)
}
pub fn get_status<PA: PhyAccess>(&self, pa: &mut PA) -> Status {
self.read_reg(pa)
}
pub fn get_link<PA: PhyAccess>(&self, pa: &mut PA) -> Option<Link> {
let status = self.get_status(pa);
if !status.link_status() {
None
} else if status.cap_1000base_t_extended_status() {
let phy_status: PSSR = self.read_reg(pa);
phy_status.get_link()
} else {
status.get_link()
}
}
pub fn reset<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control|
control.set_reset(true)
);
while self.get_control(pa).reset() {}
}
pub fn restart_autoneg<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control|
control.set_autoneg_enable(true)
.set_restart_autoneg(true)
);
}
}
pub trait PhyRegister {
fn addr() -> u8;
}
#[cfg(not(feature = "target_kasli_soc"))]
mod phy_impl {
use super::*;
use id::{identify_phy, PhyIdentifier};
#[derive(Clone)]
pub struct Phy {
pub addr: u8,
}
const OUI_MARVELL: u32 = 0x005043;
const OUI_REALTEK: u32 = 0x000732;
const OUI_LANTIQ : u32 = 0x355969;
impl Phy {
/// Probe all addresses on MDIO for a known PHY
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
(1..32).find(|addr| {
match identify_phy(pa, *addr) {
Some(PhyIdentifier {
oui: OUI_MARVELL,
// Marvell 88E1116R
model: 36,
..
}) => true,
Some(PhyIdentifier {
oui: OUI_MARVELL,
// Marvell 88E1512
model: 29,
..
}) => true,
Some(PhyIdentifier {
oui: OUI_REALTEK,
// RTL 8211E
model: 0b010001,
rev: 0b0101,
}) => true,
Some(PhyIdentifier {
oui: OUI_LANTIQ,
// Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6
model: 0,
..
}) => true,
_ => false,
}
}).map(|addr| Phy { addr })
}
pub fn read_reg<PA, PR>(&self, pa: &mut PA) -> PR
where
PA: PhyAccess,
PR: PhyRegister + From<u16>,
{
pa.read_phy(self.addr, PR::addr()).into()
}
pub fn modify_reg<PA, PR, F>(&self, pa: &mut PA, mut f: F)
where
PA: PhyAccess,
PR: PhyRegister + From<u16> + Into<u16>,
F: FnMut(PR) -> PR,
{
let reg = pa.read_phy(self.addr, PR::addr()).into();
let reg = f(reg);
pa.write_phy(self.addr, PR::addr(), reg.into())
}
pub fn modify_control<PA, F>(&self, pa: &mut PA, f: F)
where
PA: PhyAccess,
F: FnMut(Control) -> Control,
{
self.modify_reg(pa, f)
}
pub fn get_control<PA: PhyAccess>(&self, pa: &mut PA) -> Control {
self.read_reg(pa)
}
pub fn get_status<PA: PhyAccess>(&self, pa: &mut PA) -> Status {
self.read_reg(pa)
}
pub fn get_link<PA: PhyAccess>(&self, pa: &mut PA) -> Option<Link> {
let status = self.get_status(pa);
if !status.link_status() {
None
} else if status.cap_1000base_t_extended_status() {
let phy_status: PSSR = self.read_reg(pa);
phy_status.get_link()
} else {
status.get_link()
}
}
pub fn reset<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control|
control.set_reset(true)
);
while self.get_control(pa).reset() {}
}
pub fn restart_autoneg<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control|
control.set_autoneg_enable(true)
.set_restart_autoneg(true)
);
}
}
}
#[cfg(feature = "target_kasli_soc")]
mod phy_impl {
use super::*;
#[derive(Clone)]
pub struct Phy {
}
impl Phy {
pub fn find<PA: PhyAccess>(_pa: &mut PA) -> Option<Phy> {
Some(Phy {})
}
pub fn get_link<PA: PhyAccess>(&self, _pa: &mut PA) -> Option<Link> {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Full,
})
}
pub fn modify_control<PA, F>(&self, _pa: &mut PA, _f: F)
where
PA: PhyAccess,
F: FnMut(Control) -> Control,
{
}
pub fn reset<PA: PhyAccess>(&self, _pa: &mut PA) {
}
pub fn restart_autoneg<PA: PhyAccess>(&self, _pa: &mut PA) {
}
}
}
pub use phy_impl::*;

View File

@ -1,6 +1,6 @@
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)]
pub struct RegisterBlock {
@ -110,18 +110,8 @@ pub struct RegisterBlock {
pub design_cfg5: RO<u32>,
}
impl RegisterBlock {
const GEM0: *mut Self = 0xE000B000 as *mut _;
const GEM1: *mut Self = 0xE000C000 as *mut _;
pub fn gem0() -> &'static mut Self {
unsafe { &mut *Self::GEM0 }
}
pub fn gem1() -> &'static mut Self {
unsafe { &mut *Self::GEM1 }
}
}
register_at!(RegisterBlock, 0xE000B000, gem0);
register_at!(RegisterBlock, 0xE000C000, gem1);
register!(net_ctrl, NetCtrl, RW, u32);
register_bit!(net_ctrl, loopback_local, 1);

View File

@ -108,6 +108,20 @@ impl DescList {
if entry.word0.read().used() {
let word1 = entry.word1.read();
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];
self.next += 1;
@ -135,10 +149,6 @@ pub struct PktRef<'a> {
impl<'a> Drop for PktRef<'a> {
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));
dmb();
}

View File

@ -132,10 +132,8 @@ impl<'a> Drop for PktRef<'a> {
dcc_slice(self.buffer);
self.entry.word1.modify(|_, w| w.used(false));
if ! self.regs.tx_status.read().tx_go() {
// Start TX if not already running
self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
}
// Start the TX engine
self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
}
}
@ -166,10 +164,7 @@ impl<'a> smoltcp::phy::TxToken for Token<'a> {
None =>
Err(smoltcp::Error::Exhausted),
Some(mut pktref) => {
let result = f(pktref.deref_mut());
// TODO: on result.is_err() don;t send
drop(pktref);
result
f(pktref.deref_mut())
}
}
}

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())
}
}

View File

@ -62,7 +62,7 @@ pub struct InterruptController {
}
impl InterruptController {
pub fn new(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
pub fn gic(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
InterruptController { mpcore }
}

View File

@ -1,9 +1,9 @@
use super::I2C;
use super::I2c;
use crate::time::Milliseconds;
use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> {
i2c: &'a mut I2C,
i2c: &'a mut I2c,
port: u8,
address: u8,
page_size: u8,
@ -12,7 +12,7 @@ pub struct EEPROM<'a> {
impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
pub fn new(i2c: &'a mut I2c, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 2,
@ -22,6 +22,17 @@ impl<'a> EEPROM<'a> {
}
}
#[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> {
let mask: u16 = 1 << self.port;
@ -29,6 +40,14 @@ impl<'a> EEPROM<'a> {
Ok(())
}
#[cfg(feature = "target_kasli_soc")]
fn select(&mut self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
// tca9548 is compatible with pca9548
self.i2c.pca9548_select(0b1110001, mask as u8)?;
Ok(())
}
/// Random read
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
self.select()?;

View File

@ -2,22 +2,19 @@
mod regs;
pub mod eeprom;
use super::clocks::Clocks;
use super::slcr;
use super::time::Microseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW, RegisterW};
const INVALID_BUS: &'static str = "Invalid I2C bus";
pub struct I2C {
regs: regs::RegisterWrapper,
pub struct I2c {
regs: regs::RegisterBlock,
count_down: super::timer::global::CountDown<Microseconds>
}
impl I2C {
#[cfg(feature = "target_zc706")]
pub fn i2c() -> Self {
impl I2c {
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
pub fn i2c0() -> Self {
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
slcr::RegisterBlock::unlocked(|slcr| {
// SCL
@ -36,18 +33,28 @@ impl I2C {
.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)
);
// Reset
slcr.gpio_rst_ctrl.reset_gpio();
});
Self::ctor_common(0xFFFF - 0x000C)
Self::i2c_common(0xFFFF - 0x000C)
}
fn ctor_common(gpio_output_mask: u16) -> Self {
fn i2c_common(gpio_output_mask: u16) -> Self {
// Setup register block
let clocks = Clocks::get();
let self_ = Self {
regs: regs::RegisterWrapper::new(),
regs: regs::RegisterBlock::i2c(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
};
@ -143,16 +150,18 @@ impl I2C {
self.sda_oe(true);
self.half_period();
self.scl_oe(true);
while self.scl_i() {} // ensure SCL is low before start() returns
// postcondition: SCL and SDA low
Ok(())
}
pub fn restart(&mut self) -> Result<(), &'static str> {
// precondition SCL and SDA low
// precondition: any
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
// enter the precondition for start(): SCL and SDA high
self.start()?;
// postcondition: SCL and SDA low
Ok(())
@ -181,6 +190,7 @@ impl I2C {
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
while self.scl_i() {} // ensure SCL is low before toggling SDA
}
self.sda_oe(false);
self.half_period();
@ -189,6 +199,7 @@ impl I2C {
// Read ack/nack
let ack = !self.sda_i();
self.scl_oe(true);
while self.scl_i() {} // ensure SCL is low before toggling SDA
self.sda_oe(true);
// postcondition: SCL and SDA low
@ -208,6 +219,7 @@ impl I2C {
self.half_period();
if self.sda_i() { data |= 1 << bit }
self.scl_oe(true);
while self.scl_i() {} // ensure SCL is low so that slave can toggle SDA
}
// Send ack/nack
self.sda_oe(ack);
@ -215,6 +227,7 @@ impl I2C {
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
while self.scl_i() {} // ensure SCL is low before toggling SDA
self.sda_oe(true);
// postcondition: SCL and SDA low

View File

@ -1,5 +1,3 @@
use volatile_register::{RO, WO, RW};
use libregister::{
register, register_at,
register_bit, register_bits
@ -22,16 +20,17 @@ use libregister::{
//
// Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA
// kasli_soc: GPIO 50, 51 == SCL, SDA
pub struct RegisterWrapper {
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,
}
impl RegisterWrapper {
pub fn new() -> Self {
impl RegisterBlock {
pub fn i2c() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(),
@ -41,53 +40,66 @@ impl RegisterWrapper {
}
}
// MASK_DATA_1_MSW:
// Maskable output data for MIO[53:48]
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
#[cfg(feature = "target_zc706")]
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"))]
register_at!(GPIOOutputMask, 0xE000A00C, new);
// Output for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, scl_o, 2);
// Output for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, sda_o, 3);
// Mask for keeping bits except SCL and SDA unchanged
#[cfg(feature = "target_zc706")]
register_bits!(gpio_output_mask, mask, u16, 16, 31);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_output_mask,
/// Output for SCL
scl_o, 2);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_output_mask,
/// Output for SDA
sda_o, 3);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bits!(gpio_output_mask,
/// Mask for keeping bits except SCL and SDA unchanged
mask, u16, 16, 31);
// DATA_1_RO:
// Input data for MIO[53:32]
register!(gpio_input, GPIOInput, RO, u32);
#[cfg(feature = "target_zc706")]
register!(gpio_input,
/// DATA_1_RO:
/// Input data for MIO[53:32]
GPIOInput, RO, u32);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_at!(GPIOInput, 0xE000A064, new);
// Input for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, scl, 18);
// Input for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, sda, 19);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_input,
/// Input for SCL
scl, 18);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_input,
/// Input for SDA
sda, 19);
// DIRM_1:
// Direction mode for MIO[53:32]; 0/1 = in/out
register!(gpio_direction, GPIODirection, RW, u32);
#[cfg(feature = "target_zc706")]
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"))]
register_at!(GPIODirection, 0xE000A244, new);
// Direction for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, scl, 18);
// Direction for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, sda, 19);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_direction,
/// Direction for SCL
scl, 18);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_direction,
/// Direction for SDA
sda, 19);
// OEN_1:
// Output enable for MIO[53:32]
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_zc706")]
register!(gpio_output_enable,
/// OEN_1:
/// Output enable for MIO[53:32]
GPIOOutputEnable, RW, u32);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_at!(GPIOOutputEnable, 0xE000A248, new);
// Output enable for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, scl, 18);
// Output enable for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, sda, 19);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_output_enable,
/// Output enable for SCL
scl, 18);
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
register_bit!(gpio_output_enable,
/// Output enable for SDA
sda, 19);

View File

@ -16,11 +16,10 @@ pub mod axi_gp;
pub mod ddr;
pub mod mpcore;
pub mod gic;
pub mod flash;
pub mod time;
pub mod timer;
pub mod sdio;
#[cfg(feature = "target_zc706")]
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
pub mod i2c;
pub mod logger;
pub mod ps7_init;

View File

@ -138,7 +138,7 @@ pub struct RegisterBlock {
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);
@ -154,7 +154,7 @@ register_bit!(scu_control, enable, 0);
impl ScuControl {
pub fn start(&mut self) {
self.modify(|_, w| w.enable(true));
self.modify(|_, w| w.enable(true).scu_speculative_linefill_enable(true));
}
}

View File

@ -1,14 +1,14 @@
#![cfg(feature = "target_zc706")]
use crate::println;
#[cfg(feature = "target_zc706")]
mod zc706;
// mod cora_z7_10;
#[cfg(not(feature = "target_zc706"))]
mod none;
#[cfg(feature = "target_zc706")]
use zc706 as target;
// #[cfg(feature = "target_cora_z7_10")]
// use cora_z7_10 as target;
#[cfg(not(feature = "target_zc706"))]
use none as target;
pub fn report_differences() {
for (i, op) in target::INIT_DATA.iter().enumerate() {

View File

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

View File

@ -1,6 +1,6 @@
/// ADMA library
use core::mem::MaybeUninit;
use super::SDIO;
use super::Sdio;
use libcortex_a9::cache;
use libregister::{
register, register_bit,
@ -32,7 +32,7 @@ impl Adma2DescTable {
}
/// 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 blk_size = sdio
.regs

View File

@ -12,7 +12,7 @@ use log::{trace, debug};
use nb;
/// Basic SDIO Struct with common low-level functions.
pub struct SDIO {
pub struct Sdio {
regs: &'static mut regs::RegisterBlock,
count_down: super::timer::global::CountDown<Milliseconds>,
input_clk_hz: u32,
@ -48,7 +48,7 @@ pub enum CardType {
CardMmc,
}
impl SDIO {
impl Sdio {
/// Initialize SDIO0
/// card_detect means if we would use the card detect pin,
/// false to disable card detection (assume there is card inserted)
@ -105,7 +105,7 @@ impl SDIO {
);
}
// cora card detect pin
#[cfg(feature = "target_cora_z7_10")]
#[cfg(feature = "target_coraz7")]
{
unsafe {
slcr.sd0_wp_cd_sel.write(47 << 16);
@ -116,12 +116,24 @@ impl SDIO {
.speed(true),
);
}
// redpitaya card detect pin
#[cfg(any(feature = "target_redpitaya", feature = "target_kasli_soc"))]
{
unsafe {
slcr.sd0_wp_cd_sel.write(46 << 16);
}
slcr.mio_pin_46.write(
slcr::MioPin46::zeroed()
.io_type(slcr::IoBufferType::Lvcmos25)
.speed(true),
);
}
slcr.sdio_rst_ctrl.reset_sdio0();
slcr.aper_clk_ctrl.enable_sdio0();
slcr.sdio_clk_ctrl.enable_sdio0();
});
let clocks = Clocks::get();
let mut self_ = SDIO {
let mut self_ = Sdio {
regs: regs::RegisterBlock::sdio0(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
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 libregister::{RegisterR, RegisterRW, RegisterW};
use log::{trace, debug};
@ -37,7 +37,7 @@ enum CardVersion {
}
pub struct SdCard {
sdio: SDIO,
sdio: Sdio,
adma2_desc_table: Adma2DescTable,
card_version: CardVersion,
hcs: bool,
@ -171,8 +171,8 @@ impl SdCard {
Ok(())
}
/// 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> {
/// 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> {
match sdio.identify_card()? {
CardType::CardSd => (),
_ => return Err(CardInitializationError::NoCardInserted),
@ -192,8 +192,8 @@ impl SdCard {
Ok(_self)
}
/// Convert SdCard struct back to SDIO struct.
pub fn to_sdio(self) -> SDIO {
/// Convert SdCard struct back to Sdio struct.
pub fn to_sdio(self) -> Sdio {
self.sdio
}

View File

@ -253,12 +253,12 @@ pub struct RegisterBlock {
pub ddriob_dci_ctrl: DdriobDciCtrl,
pub ddriob_dci_status: DdriobDciStatus,
}
register_at!(RegisterBlock, 0xF8000000, new);
register_at!(RegisterBlock, 0xF8000000, slcr);
impl RegisterBlock {
/// Required to modify any sclr register
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();
let r = f(&mut self_);
self_.slcr_lock.lock();
@ -590,16 +590,19 @@ register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0);
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
pub enum BootModePins {
// CAUTION!
// The BOOT_MODE bits table 6-4 in UG585 are *out of order*.
Jtag = 0b000,
Nor = 0b001,
Nand = 0b010,
QuadSpi = 0b100,
SdCard = 0b110,
Nor = 0b010,
Nand = 0b100,
QuadSpi = 0b001,
SdCard = 0b101,
}
register!(boot_mode, BootMode, RO, u32);
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_bit!(pss_rst_ctrl, soft_rst, 1);

View File

@ -1,5 +1,5 @@
use core::ops::{Deref, DerefMut};
use libcortex_a9::mutex::{Mutex, MutexGuard};
use libcortex_a9::{asm, mutex::{Mutex, MutexGuard}};
use crate::uart::Uart;
const UART_RATE: u32 = 115_200;
@ -10,7 +10,15 @@ pub fn get_uart<'a>() -> MutexGuard<'a, LazyUart> {
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() {
for _ in 0..1_000_000 {
asm::nop();
}
unsafe { UART = Mutex::new(LazyUart::Uninitialized); }
}
@ -37,7 +45,10 @@ impl DerefMut for LazyUart {
fn deref_mut(&mut self) -> &mut Uart {
match self {
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_kasli_soc"))]
let uart = Uart::uart1(UART_RATE);
*self = LazyUart::Initialized(uart);
self
}

View File

@ -16,13 +16,13 @@ pub struct GlobalTimer {
impl GlobalTimer {
/// Get the potentially uninitialized timer
pub unsafe fn get() -> GlobalTimer {
let regs = mpcore::RegisterBlock::new();
let regs = mpcore::RegisterBlock::mpcore();
GlobalTimer { regs }
}
/// Get the timer with a reset
pub fn start() -> GlobalTimer {
let mut regs = mpcore::RegisterBlock::new();
let mut regs = mpcore::RegisterBlock::mpcore();
Self::reset(&mut regs);
GlobalTimer { regs }
}

View File

@ -13,31 +13,8 @@ pub struct Uart {
}
impl Uart {
#[cfg(feature = "target_zc706")]
pub fn serial(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 {
#[cfg(any(feature = "target_coraz7", feature = "target_redpitaya"))]
pub fn uart0(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Route UART 0 RxD/TxD Signals to MIO Pins
// TX pin
@ -56,10 +33,7 @@ impl Uart {
.pullup(true)
);
});
Self::uart0(baudrate)
}
pub fn uart0(baudrate: u32) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
slcr.uart_rst_ctrl.reset_uart0();
slcr.aper_clk_ctrl.enable_uart0();
@ -72,7 +46,27 @@ impl Uart {
self_
}
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
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();

18
libconfig/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[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_redpitaya = []
target_kasli_soc = []
ipv6 = []

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)
}

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

@ -0,0 +1,173 @@
#![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 mut f = root_dir.create_file(&["/CONFIG/", key, ".BIN"].concat())?;
f.write_all(&value)?;
}
}
Ok(())
}
}

View File

@ -0,0 +1,76 @@
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(())
}
}
pub fn get_adresses(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 = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x56]);
#[cfg(feature = "target_kasli_soc")]
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 56);
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,15 +1,14 @@
[package]
name = "libcortex_a9"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = []
target_cora_z7_10 = []
power_saving = []
default = ["target_zc706"]
default = []
[dependencies]
bit_field = "0.10"
volatile-register = "0.2"
libregister = { path = "../libregister" }

View File

@ -1,4 +1,5 @@
use super::asm::{dmb, dsb};
use super::l2c::*;
/// Invalidate TLBs
#[inline(always)]
@ -52,10 +53,9 @@ pub fn dccisw(setway: u32) {
}
}
/// A made-up "instruction": invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dciall() {
pub fn dciall_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;
@ -80,9 +80,17 @@ 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() {
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;
@ -107,6 +115,15 @@ pub fn dcciall() {
}
}
#[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_MASK: usize = CACHE_LINE - 1;
@ -145,7 +162,16 @@ pub fn dccimvac(addr: usize) {
/// Data cache clean and invalidate for an object.
pub fn dcci<T>(object: &T) {
// ref: L2C310 TRM 3.3.10
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) {
dccimvac(addr);
}
@ -154,6 +180,14 @@ pub fn dcci<T>(object: &T) {
pub fn dcci_slice<T>(slice: &[T]) {
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) {
dccimvac(addr);
}
@ -175,17 +209,28 @@ pub fn dcc<T>(object: &T) {
dccmvac(addr);
}
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
/// aligned and properly sized to be contained in an exact number of
/// cache lines.
pub fn dcc_slice<T>(slice: &[T]) {
if slice.len() == 0 {
return;
}
dmb();
for addr in slice_cache_line_addrs(slice) {
dccmvac(addr);
}
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
@ -205,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");
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) {
dcimvac(addr);
}
@ -219,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");
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) {
dcimvac(addr);
}

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

@ -6,16 +6,17 @@
extern crate alloc;
pub mod asm;
pub mod regs;
pub mod cache;
mod fpu;
pub mod l2c;
pub mod mmu;
pub mod mutex;
pub mod sync_channel;
pub mod regs;
pub mod semaphore;
pub mod sync_channel;
mod uncached;
mod fpu;
pub use uncached::UncachedSlice;
pub use fpu::enable_fpu;
pub use uncached::UncachedSlice;
global_asm!(include_str!("exceptions.s"));
@ -34,3 +35,38 @@ pub fn notify_spin_lock() {
}
}
#[macro_export]
/// Interrupt handler, which setup the stack and jump to actual interrupt 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, so
/// interrupt handlers should not return to normal program or it may corrupt the stack.
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
"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 sp, r1",
concat!("bl ", stringify!($name2)),
options(noreturn)
);
}
#[no_mangle]
pub unsafe extern "C" fn $name2() -> ! $body
};
}

View File

@ -158,7 +158,7 @@ impl L1Table {
global: true,
shareable: true,
access: AccessPermissions::FullAccess,
tex: 0b101,
tex: 0b0,
domain: 0b1111,
exec: true,
cacheable: true,
@ -213,7 +213,7 @@ impl L1Table {
access: AccessPermissions::FullAccess,
tex: 0,
domain: 0,
exec: true,
exec: false,
cacheable: false,
bufferable: true,
});
@ -410,6 +410,7 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
.a(false)
.c(true)
.i(true)
.z(true)
.unaligned(true)
);

View File

@ -3,7 +3,7 @@ use core::sync::atomic::{AtomicU32, Ordering};
use core::cell::UnsafeCell;
use super::{
spin_lock_yield, notify_spin_lock,
asm::{dmb, enter_critical, exit_critical}
asm::{enter_critical, exit_critical}
};
const LOCKED: u32 = 1;
@ -32,30 +32,27 @@ impl<T> Mutex<T> {
/// Lock the Mutex, blocks when already locked
pub fn lock(&self) -> MutexGuard<T> {
let mut irq = unsafe { enter_critical() };
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
while self.locked.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::AcqRel, Ordering::Relaxed).is_err() {
unsafe {
exit_critical(irq);
spin_lock_yield();
irq = enter_critical();
}
}
dmb();
MutexGuard { mutex: self, irq }
}
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
let irq = unsafe { enter_critical() };
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
if self.locked.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::AcqRel, Ordering::Relaxed).is_err() {
unsafe { exit_critical(irq) };
None
} else {
dmb();
Some(MutexGuard { mutex: self, irq })
}
}
fn unlock(&self) {
dmb();
self.locked.store(UNLOCKED, Ordering::Release);
notify_spin_lock();

View File

@ -156,6 +156,8 @@ register_bit!(actlr, excl, 7);
register_bit!(actlr, smp, 6);
register_bit!(actlr, write_full_line_of_zeros, 3);
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
register_bit!(actlr, fw, 0);
@ -171,7 +173,11 @@ impl RegisterRW for ACTLR {
impl ACTLR {
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

@ -12,7 +12,7 @@ pub struct Semaphore {
}
impl Semaphore {
pub fn new(value: i32, max: i32) -> Self {
pub const fn new(value: i32, max: i32) -> Self {
Semaphore { value: AtomicI32::new(value), max}
}
@ -20,7 +20,7 @@ impl Semaphore {
loop {
let value = self.value.load(Ordering::Relaxed);
if value > 0 {
if self.value.compare_and_swap(value, value - 1, Ordering::SeqCst) == value {
if self.value.compare_exchange_weak(value, value - 1, Ordering::SeqCst, Ordering::Relaxed).is_ok() {
return Some(());
}
} else {
@ -58,7 +58,7 @@ impl Semaphore {
loop {
let value = self.value.load(Ordering::Relaxed);
if value < self.max {
if self.value.compare_and_swap(value, value + 1, Ordering::SeqCst) == value {
if self.value.compare_exchange_weak(value, value + 1, Ordering::SeqCst, Ordering::Relaxed).is_ok() {
notify_spin_lock();
return;
}

View File

@ -1,7 +1,6 @@
use core::{
pin::Pin,
future::Future,
ptr::drop_in_place,
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
task::{Context, Poll},
};
@ -38,7 +37,7 @@ impl<'a, T> Sender<'a, T> where T: Clone {
notify_spin_lock();
if !prev.is_null() {
unsafe {
drop_in_place(prev);
Box::from_raw(prev);
}
}
Ok(())
@ -92,7 +91,7 @@ impl<'a, T> Sender<'a, T> where T: Clone {
for v in self.list.iter() {
let original = v.swap(core::ptr::null_mut(), Ordering::Relaxed);
if !original.is_null() {
drop_in_place(original);
Box::from_raw(original);
}
}
}

View File

@ -2,7 +2,7 @@ use core::{
ops::{Deref, DerefMut},
mem::{align_of, size_of},
};
use alloc::alloc::{dealloc, Layout, LayoutErr};
use alloc::alloc::{dealloc, Layout, LayoutError};
use crate::mmu::{L1_PAGE_SIZE, L1Table};
pub struct UncachedSlice<T: 'static> {
@ -12,7 +12,7 @@ pub struct UncachedSlice<T: 'static> {
impl<T> UncachedSlice<T> {
/// 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
let size = ((len * size_of::<T>() - 1) | (L1_PAGE_SIZE - 1)) + 1;
let align = align_of::<T>()
@ -23,10 +23,11 @@ impl<T> UncachedSlice<T> {
assert_eq!(start & (L1_PAGE_SIZE - 1), 0);
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
// non-shareable device
L1Table::get()
.update(page_start as *const (), |l1_section| {
l1_section.tex = 0b100;
l1_section.cacheable = false;
l1_section.tex = 0b10;
l1_section.cacheable = true;
l1_section.bufferable = false;
});
}

View File

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

View File

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

View File

@ -2,12 +2,14 @@
name = "libsupport_zynq"
description = "Software support for running in the Zynq PS"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = ["libboard_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"]
target_coraz7 = ["libboard_zynq/target_coraz7"]
target_redpitaya = ["libboard_zynq/target_redpitaya"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc"]
panic_handler = []
dummy_irq_handler = []
alloc_core = []
@ -16,8 +18,11 @@ default = ["panic_handler", "dummy_irq_handler"]
[dependencies]
r0 = "1"
compiler_builtins = "0.1"
linked_list_allocator = { version = "0.8", default-features = false }
compiler_builtins = "=0.1.39"
linked_list_allocator = { version = "0.8", default-features = false, features = ["const_mut_refs"] }
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }
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,70 +1,49 @@
use libregister::RegisterR;
use libcortex_a9::regs::{DFSR, MPIDR};
use libcortex_a9::{regs::{DFSR, MPIDR}, interrupt_handler};
use libboard_zynq::{println, stdio};
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn UndefinedInstruction() {
interrupt_handler!(UndefinedInstruction, undefined_instruction, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("UndefinedInstruction");
loop {}
}
});
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn SoftwareInterrupt() {
interrupt_handler!(SoftwareInterrupt, software_interrupt, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("SoftwareInterrupt");
loop {}
}
});
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn PrefetchAbort() {
interrupt_handler!(PrefetchAbort, prefetch_abort, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("PrefetchAbort");
loop {}
}
});
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn DataAbort() {
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 {}
}
});
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn ReservedException() {
interrupt_handler!(ReservedException, reserved_exception, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("ReservedException");
loop {}
}
});
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
#[cfg(feature = "dummy_irq_handler")]
pub unsafe extern "C" fn IRQ() {
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("IRQ");
loop {}
}
});
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn FIQ() {
interrupt_handler!(FIQ, fiq, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("FIQ");
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 libregister::{
VolatileCell,
RegisterR, RegisterW, RegisterRW,
RegisterR, RegisterRW,
};
use libcortex_a9::{asm, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock};
use libcortex_a9::{asm, l2c, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock, enable_fpu, interrupt_handler};
use libboard_zynq::{slcr, mpcore};
extern "C" {
@ -18,32 +18,28 @@ extern "C" {
static mut CORE1_ENABLED: VolatileCell<bool> = VolatileCell::new(false);
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn Reset() -> ! {
interrupt_handler!(Reset, reset_irq, __stack0_start, __stack1_start, {
// no need to setup stack here, as we already did when entering the handler
match MPIDR.read().cpu_id() {
0 => {
SP.write(&mut __stack0_start as *mut _ as u32);
boot_core0();
}
1 => {
while !CORE1_ENABLED.get() {
spin_lock_yield();
}
SP.write(&mut __stack1_start as *mut _ as u32);
boot_core1();
}
_ => unreachable!(),
}
}
});
#[naked]
#[inline(never)]
unsafe fn boot_core0() -> ! {
unsafe extern "C" fn boot_core0() -> ! {
l1_cache_init();
let mpcore = mpcore::RegisterBlock::new();
enable_fpu();
let mpcore = mpcore::RegisterBlock::mpcore();
mpcore.scu_invalidate.invalidate_all_cores();
zero_bss(&mut __bss_start, &mut __bss_end);
@ -53,6 +49,7 @@ unsafe fn boot_core0() -> ! {
mmu::with_mmu(mmu_table, || {
mpcore.scu_control.start();
ACTLR.enable_smp();
ACTLR.enable_prefetch();
// TODO: Barriers reqd when core1 is not yet starting?
asm::dmb();
asm::dsb();
@ -63,17 +60,17 @@ unsafe fn boot_core0() -> ! {
});
}
#[naked]
#[inline(never)]
unsafe fn boot_core1() -> ! {
unsafe extern "C" fn boot_core1() -> ! {
l1_cache_init();
let mpcore = mpcore::RegisterBlock::new();
let mpcore = mpcore::RegisterBlock::mpcore();
mpcore.scu_invalidate.invalidate_core1();
let mmu_table = mmu::L1Table::get();
mmu::with_mmu(mmu_table, || {
ACTLR.enable_smp();
ACTLR.enable_prefetch();
// TODO: Barriers reqd when core1 is not yet starting?
asm::dmb();
asm::dsb();
@ -101,7 +98,7 @@ fn l1_cache_init() {
// for all of the L1 data cache rather than a (previously
// unspecified) combination of one cache set and one cache
// way.
dciall();
dciall_l1();
}
pub struct Core1 {
@ -131,12 +128,13 @@ impl Core1 {
unsafe {
CORE1_ENABLED.set(true);
}
// Ensure values have been written to cache
asm::dmb();
// Flush cache-line
cache::dccmvac(unsafe { &CORE1_ENABLED } as *const _ as usize);
cache::dcc(unsafe { &CORE1_ENABLED });
if sdram {
cache::dccmvac(0);
asm::dsb();
l2c::l2_cache_clean(0);
l2c::l2_cache_sync();
}
// wake up core1

View File

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

View File

@ -34,7 +34,10 @@ unsafe impl GlobalAlloc for CortexA9Alloc {
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
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

22
nix/cargo-xbuild.nix Normal file
View File

@ -0,0 +1,22 @@
{ lib, fetchFromGitHub, rustPlatform }:
rustPlatform.buildRustPackage rec {
pname = "cargo-xbuild";
version = "0.6.5";
src = fetchFromGitHub {
owner = "rust-osdev";
repo = pname;
rev = "v${version}";
sha256 = "18djvygq9v8rmfchvi2hfj0i6fhn36m716vqndqnj56fiqviwxvf";
};
cargoSha256 = "13sj9j9kl6js75h9xq0yidxy63vixxm9q3f8jil6ymarml5wkhx8";
meta = with lib; {
description = "Automatically cross-compiles the sysroot crates core, compiler_builtins, and alloc";
homepage = "https://github.com/rust-osdev/cargo-xbuild";
license = with licenses; [ mit asl20 ];
maintainers = with maintainers; [ johntitor xrelkd ];
};
}

File diff suppressed because it is too large Load Diff

37
nix/fsbl.nix Normal file
View File

@ -0,0 +1,37 @@
{ pkgs, board ? "zc706" }:
let
gnutoolchain = import ./gnutoolchain.nix { inherit pkgs; };
in
pkgs.stdenv.mkDerivation {
name = "${board}-fsbl";
src = pkgs.fetchFromGitHub {
owner = "Xilinx";
repo = "embeddedsw";
rev = "65c849ed46c88c67457e1fc742744f96db968ff1";
sha256 = "1rvl06ha40dzd6s9aa4sylmksh4xb9dqaxq462lffv1fdk342pda";
};
patches = [ ./fsbl.patch ];
nativeBuildInputs = [
pkgs.gnumake
gnutoolchain.binutils
gnutoolchain.gcc
];
patchPhase =
''
patch -p1 -i ${./fsbl.patch}
patchShebangs lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh
echo 'SEARCH_DIR("${gnutoolchain.newlib}/arm-none-eabi/lib");' >> lib/sw_apps/zynq_fsbl/src/lscript.ld
'';
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;
}

31
nix/fsbl.patch Normal file
View File

@ -0,0 +1,31 @@
diff --git a/lib/sw_apps/zynq_fsbl/src/Makefile b/lib/sw_apps/zynq_fsbl/src/Makefile
index 0e3ccdf1c5..a5b02f386e 100644
--- a/lib/sw_apps/zynq_fsbl/src/Makefile
+++ b/lib/sw_apps/zynq_fsbl/src/Makefile
@@ -71,11 +71,14 @@ endif
all: $(EXEC)
$(EXEC): $(LIBS) $(OBJS) $(INCLUDES)
- cp $(BSP_DIR)/$(BOARD)/ps7_init.* .
$(LINKER) $(LD1FLAGS) -o $@ $(OBJS) $(LDFLAGS)
rm -rf $(OBJS)
-
-
+
+.PHONY: ps7_init
+
+ps7_init:
+ cp $(BSP_DIR)/$(BOARD)/ps7_init.* .
+
$(LIBS):
echo "Copying BSP files"
$(BSP_DIR)/copy_bsp.sh $(BOARD) $(CC)
@@ -86,7 +89,7 @@ $(LIBS):
make -C $(BSP_DIR) -k all "CC=armcc" "AR=armar" "C_FLAGS= -O2 -c" "EC_FLAGS=--debug --wchar32"; \
fi;
-%.o:%.c
+%.o:%.c ps7_init
$(CC) $(CC_FLAGS) $(CFLAGS) $(ECFLAGS) -c $< -o $@ $(INCLUDEPATH)
%.o:%.S

134
nix/gnutoolchain.nix Normal file
View File

@ -0,0 +1,134 @@
{ pkgs ? import <nixpkgs> }:
let
platform = "arm-none-eabi";
binutils-pkg = { stdenv, lib, buildPackages
, fetchurl, zlib
, extraConfigureFlags ? []
}:
stdenv.mkDerivation rec {
basename = "binutils";
version = "2.30";
name = "${basename}-${platform}-${version}";
src = fetchurl {
url = "https://ftp.gnu.org/gnu/binutils/binutils-${version}.tar.bz2";
sha256 = "028cklfqaab24glva1ks2aqa1zxa6w6xmc8q34zs1sb7h22dxspg";
};
configureFlags = [
"--enable-deterministic-archives"
"--target=${platform}"
"--with-cpu=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--with-mode=thumb"
] ++ extraConfigureFlags;
outputs = [ "out" "info" "man" ];
depsBuildBuild = [ buildPackages.stdenv.cc ];
buildInputs = [ zlib ];
enableParallelBuilding = true;
meta = {
description = "Tools for manipulating binaries (linker, assembler, etc.)";
longDescription = ''
The GNU Binutils are a collection of binary tools. The main
ones are `ld' (the GNU linker) and `as' (the GNU assembler).
They also include the BFD (Binary File Descriptor) library,
`gprof', `nm', `strip', etc.
'';
homepage = http://www.gnu.org/software/binutils/;
license = lib.licenses.gpl3Plus;
/* Give binutils a lower priority than gcc-wrapper to prevent a
collision due to the ld/as wrappers/symlinks in the latter. */
priority = "10";
};
};
gcc-pkg = { stdenv, buildPackages
, fetchurl, gmp, mpfr, libmpc, platform-binutils
, extraConfigureFlags ? []
}:
stdenv.mkDerivation rec {
basename = "gcc";
version = "9.1.0";
name = "${basename}-${platform}-${version}";
src = fetchurl {
url = "https://ftp.gnu.org/gnu/gcc/gcc-${version}/gcc-${version}.tar.xz";
sha256 = "1817nc2bqdc251k0lpc51cimna7v68xjrnvqzvc50q3ax4s6i9kr";
};
preConfigure =
''
mkdir build
cd build
'';
configureScript = "../configure";
configureFlags =
[ "--target=${platform}"
"--with-arch=armv7-a"
"--with-tune=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--disable-libssp"
"--enable-languages=c"
"--with-as=${platform-binutils}/bin/${platform}-as"
"--with-ld=${platform-binutils}/bin/${platform}-ld" ] ++ extraConfigureFlags;
outputs = [ "out" "info" "man" ];
hardeningDisable = [ "format" "pie" ];
propagatedBuildInputs = [ gmp mpfr libmpc platform-binutils ];
enableParallelBuilding = true;
dontFixup = true;
};
newlib-pkg = { stdenv, fetchurl, buildPackages, platform-binutils, platform-gcc }:
stdenv.mkDerivation rec {
pname = "newlib";
version = "3.1.0";
src = fetchurl {
url = "ftp://sourceware.org/pub/newlib/newlib-${version}.tar.gz";
sha256 = "0ahh3n079zjp7d9wynggwrnrs27440aac04340chf1p9476a2kzv";
};
nativeBuildInputs = [ platform-binutils platform-gcc ];
configureFlags = [
"--target=${platform}"
"--with-cpu=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--with-mode=thumb"
"--enable-interwork"
"--disable-multilib"
"--disable-newlib-supplied-syscalls"
"--with-gnu-ld"
"--with-gnu-as"
"--disable-newlib-io-float"
"--disable-werror"
];
dontFixup = true;
};
in rec {
binutils-bootstrap = pkgs.callPackage binutils-pkg { };
gcc-bootstrap = pkgs.callPackage gcc-pkg {
platform-binutils = binutils-bootstrap;
extraConfigureFlags = [ "--disable-libgcc" ];
};
newlib = pkgs.callPackage newlib-pkg {
platform-binutils = binutils-bootstrap;
platform-gcc = gcc-bootstrap;
};
binutils = pkgs.callPackage binutils-pkg {
extraConfigureFlags = [ "--with-lib-path=${newlib}/arm-none-eabi/lib" ];
};
gcc = pkgs.callPackage gcc-pkg {
platform-binutils = binutils;
extraConfigureFlags = [ "--enable-newlib" "--with-headers=${newlib}/arm-none-eabi/include" ];
};
}

24
nix/mkbootimage.nix Normal file
View File

@ -0,0 +1,24 @@
{ pkgs }:
pkgs.stdenv.mkDerivation {
pname = "mkbootimage";
version = "2.2";
src = pkgs.fetchFromGitHub {
owner = "antmicro";
repo = "zynq-mkbootimage";
rev = "4ee42d782a9ba65725ed165a4916853224a8edf7";
sha256 = "1k1mbsngqadqihzjgvwvsrkvryxy5ladpxd9yh9iqn2s7fxqwqa9";
};
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
'';
}

10
nix/mozilla-overlay.nix Normal file
View File

@ -0,0 +1,10 @@
let
pkgs = import <nixpkgs> {};
overlay = pkgs.fetchFromGitHub {
owner = "mozilla";
repo = "nixpkgs-mozilla";
rev = "efda5b357451dbb0431f983cca679ae3cd9b9829";
sha256 = "11wqrg86g3qva67vnk81ynvqyfj0zxk83cbrf0p9hsvxiwxs8469";
};
in
import overlay

20
nix/rust-platform.nix Normal file
View File

@ -0,0 +1,20 @@
{ pkgs }:
let
rustManifest = ./channel-rust-nightly.toml;
targets = [];
rustChannelOfTargets = _channel: _date: targets:
(pkgs.lib.rustLib.fromManifestFile rustManifest {
inherit (pkgs) stdenv fetchurl patchelf;
}).rust.override {
inherit targets;
extensions = ["rust-src"];
};
rust =
rustChannelOfTargets "nightly" null targets;
in
pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustc = rust;
cargo = rust;
})

View File

@ -5,8 +5,6 @@ set PL_TAPID 0x13722093
set SMP 1
source ./zynq-7000.cfg
source ./xilinx-tcl.cfg
source ./ps7_init.tcl
reset_config srst_only srst_push_pull
@ -31,11 +29,10 @@ pld device virtex2 zynq.tap 1
init
xc7_program zynq.tap
xilinx_ps7_init
reset halt
# Disable MMU
targets $_TARGETNAME_1
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
targets $_TARGETNAME_0
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]

12
openocd/digilent-hs2.cfg Normal file
View File

@ -0,0 +1,12 @@
# this is the original file from OpenOCD, but with ftdi_device_desc
# removed because some cables don't have it programmed.
# this supports JTAG-HS2 (and apparently Nexys4 as well)
interface ftdi
ftdi_vid_pid 0x0403 0x6014
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
reset_config none

View File

@ -1,18 +0,0 @@
#
# Digilent JTAG-SMT2-NC
#
# http://store.digilentinc.com/jtag-smt2-nc-surface-mount-programming-module/
# https://reference.digilentinc.com/_media/jtag_smt2nc/jtag-smt2-nc_rm.pdf
#
# Based on reference sheet (above) and Xilinx KCU105 schematics
# https://www.xilinx.com/products/boards-and-kits/kcu105.html#documentation
#
# Note that the digilent_jtag_smt2 layout does not work and hangs while
# the ftdi_device_desc from digilent_hs2 is wrong.
interface ftdi
ftdi_device_desc "Digilent USB Device"
ftdi_vid_pid 0x0403 0x6014
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
ftdi_layout_signal nSRST -data 0x2000

View File

@ -1,28 +0,0 @@
def zynq-connect
target remote :3333
end
def zynq-fsbl-restart
mon xilinx_ps7_init
end
def zynq-restart
mon xilinx_ps7_init
load
end
# easily typed shortcuts
# device connect
def dc
zynq-connect
end
# device restart
def dr
zynq-restart
end
def dfr
zynq-fsbl-restart
end

47
openocd/kasli-soc.cfg Normal file
View File

@ -0,0 +1,47 @@
interface ftdi
ftdi_device_desc "Quad RS232-HS"
ftdi_vid_pid 0x0403 0x6011
ftdi_channel 0
# some GPIOs need to be set, otherwise the FTDI chip craps out for some reason.
ftdi_layout_init 0x0098 0x008b
transport select jtag
adapter_khz 1000
set PL_TAPID 0x1372c093
set SMP 1
source ./zynq-7000.cfg
ftdi_layout_signal nSRST -oe 0x0004
reset_config srst_only srst_open_drain
adapter_nsrst_assert_width 250
adapter_nsrst_delay 400
set XC7_JSHUTDOWN 0x0d
set XC7_JPROGRAM 0x0b
set XC7_JSTART 0x0c
set XC7_BYPASS 0x3f
proc xc7_program {tap} {
global XC7_JSHUTDOWN XC7_JPROGRAM XC7_JSTART XC7_BYPASS
irscan $tap $XC7_JSHUTDOWN
irscan $tap $XC7_JPROGRAM
runtest 60000
#JSTART prevents this from working...
#irscan $tap $XC7_JSTART
runtest 2000
irscan $tap $XC7_BYPASS
runtest 2000
}
pld device virtex2 zynq.tap 1
init
xc7_program zynq.tap
reset halt
# Disable MMU
targets $_TARGETNAME_1
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
targets $_TARGETNAME_0
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]

View File

@ -1,771 +0,0 @@
proc ps7_pll_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000110 0x003FFFF0 0x000FA220
mask_write 0XF8000100 0x0007F000 0x00028000
mask_write 0XF8000100 0x00000010 0x00000010
mask_write 0XF8000100 0x00000001 0x00000001
mask_write 0XF8000100 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000001
mask_write 0XF8000100 0x00000010 0x00000000
mask_write 0XF8000120 0x1F003F30 0x1F000200
mask_write 0XF8000114 0x003FFFF0 0x0012C220
mask_write 0XF8000104 0x0007F000 0x00020000
mask_write 0XF8000104 0x00000010 0x00000010
mask_write 0XF8000104 0x00000001 0x00000001
mask_write 0XF8000104 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000002
mask_write 0XF8000104 0x00000010 0x00000000
mask_write 0XF8000124 0xFFF00003 0x0C200003
mask_write 0XF8000118 0x003FFFF0 0x001452C0
mask_write 0XF8000108 0x0007F000 0x0001E000
mask_write 0XF8000108 0x00000010 0x00000010
mask_write 0XF8000108 0x00000001 0x00000001
mask_write 0XF8000108 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000004
mask_write 0XF8000108 0x00000010 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_clock_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000128 0x03F03F01 0x00700F01
mask_write 0XF8000138 0x00000011 0x00000001
mask_write 0XF8000140 0x03F03F71 0x00100801
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00001402
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01EC044D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_3_0 {} {
mask_write 0XF8006000 0x0001FFFF 0x00000080
mask_write 0XF8006004 0x0007FFFF 0x00001081
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
mask_write 0XF800600C 0x03FFFFFF 0x02001001
mask_write 0XF8006010 0x03FFFFFF 0x00014001
mask_write 0XF8006014 0x001FFFFF 0x0004159B
mask_write 0XF8006018 0xF7FFFFFF 0x452460D2
mask_write 0XF800601C 0xFFFFFFFF 0x720238E5
mask_write 0XF8006020 0x7FDFFFFC 0x270872D0
mask_write 0XF8006024 0x0FFFFFC3 0x00000000
mask_write 0XF8006028 0x00003FFF 0x00002007
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
mask_write 0XF8006030 0xFFFFFFFF 0x00040930
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
mask_write 0XF8006038 0x00000003 0x00000000
mask_write 0XF800603C 0x000FFFFF 0x00000777
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
mask_write 0XF8006048 0x0003F03F 0x0003C008
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
mask_write 0XF8006058 0x00010000 0x00000000
mask_write 0XF800605C 0x0000FFFF 0x00005003
mask_write 0XF8006060 0x000017FF 0x0000003E
mask_write 0XF8006064 0x00021FE0 0x00020000
mask_write 0XF8006068 0x03FFFFFF 0x00284141
mask_write 0XF800606C 0x0000FFFF 0x00001610
mask_write 0XF8006078 0x03FFFFFF 0x00466111
mask_write 0XF800607C 0x000FFFFF 0x00032222
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
mask_write 0XF80060AC 0x000001FF 0x000001FE
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
mask_write 0XF80060B4 0x00000200 0x00000200
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
mask_write 0XF80060C4 0x00000003 0x00000000
mask_write 0XF80060C8 0x000000FF 0x00000000
mask_write 0XF80060DC 0x00000001 0x00000000
mask_write 0XF80060F0 0x0000FFFF 0x00000000
mask_write 0XF80060F4 0x0000000F 0x00000008
mask_write 0XF8006114 0x000000FF 0x00000000
mask_write 0XF8006118 0x7FFFFFCF 0x40000001
mask_write 0XF800611C 0x7FFFFFCF 0x40000001
mask_write 0XF8006120 0x7FFFFFCF 0x40000001
mask_write 0XF8006124 0x7FFFFFCF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x00000080
mask_write 0XF8006160 0x000FFFFF 0x00000080
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000C0
mask_write 0XF8006188 0x000FFFFF 0x000000C0
mask_write 0XF8006190 0x6FFFFEFE 0x00040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
mask_write 0XF8006208 0x000703FF 0x000003FF
mask_write 0XF800620C 0x000703FF 0x000003FF
mask_write 0XF8006210 0x000703FF 0x000003FF
mask_write 0XF8006214 0x000703FF 0x000003FF
mask_write 0XF8006218 0x000F03FF 0x000003FF
mask_write 0XF800621C 0x000F03FF 0x000003FF
mask_write 0XF8006220 0x000F03FF 0x000003FF
mask_write 0XF8006224 0x000F03FF 0x000003FF
mask_write 0XF80062A8 0x00000FF5 0x00000000
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
mask_write 0XF80062B0 0x003FFFFF 0x00005125
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
mask_poll 0XF8000B74 0x00002000
mask_write 0XF8006000 0x0001FFFF 0x00000081
mask_poll 0XF8006054 0x00000007
}
proc ps7_mio_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B40 0x00000FFF 0x00000600
mask_write 0XF8000B44 0x00000FFF 0x00000600
mask_write 0XF8000B48 0x00000FFF 0x00000672
mask_write 0XF8000B4C 0x00000FFF 0x00000672
mask_write 0XF8000B50 0x00000FFF 0x00000674
mask_write 0XF8000B54 0x00000FFF 0x00000674
mask_write 0XF8000B58 0x00000FFF 0x00000600
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B6C 0x00007FFF 0x00000209
mask_write 0XF8000B70 0x00000001 0x00000001
mask_write 0XF8000B70 0x00000021 0x00000020
mask_write 0XF8000B70 0x07FEFFFF 0x00000823
mask_write 0XF8000700 0x00003FFF 0x00000600
mask_write 0XF8000704 0x00003FFF 0x00000702
mask_write 0XF8000708 0x00003FFF 0x00000702
mask_write 0XF800070C 0x00003FFF 0x00000702
mask_write 0XF8000710 0x00003FFF 0x00000702
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00000600
mask_write 0XF8000728 0x00003FFF 0x00000600
mask_write 0XF800072C 0x00003FFF 0x00000600
mask_write 0XF8000730 0x00003FFF 0x00000600
mask_write 0XF8000734 0x00003FFF 0x00000600
mask_write 0XF8000738 0x00003FFF 0x00000600
mask_write 0XF800073C 0x00003FFF 0x00000600
mask_write 0XF8000740 0x00003FFF 0x00000302
mask_write 0XF8000744 0x00003FFF 0x00000302
mask_write 0XF8000748 0x00003FFF 0x00000302
mask_write 0XF800074C 0x00003FFF 0x00000302
mask_write 0XF8000750 0x00003FFF 0x00000302
mask_write 0XF8000754 0x00003FFF 0x00000302
mask_write 0XF8000758 0x00003FFF 0x00000303
mask_write 0XF800075C 0x00003FFF 0x00000303
mask_write 0XF8000760 0x00003FFF 0x00000303
mask_write 0XF8000764 0x00003FFF 0x00000303
mask_write 0XF8000768 0x00003FFF 0x00000303
mask_write 0XF800076C 0x00003FFF 0x00000303
mask_write 0XF8000770 0x00003FFF 0x00000304
mask_write 0XF8000774 0x00003FFF 0x00000305
mask_write 0XF8000778 0x00003FFF 0x00000304
mask_write 0XF800077C 0x00003FFF 0x00000305
mask_write 0XF8000780 0x00003FFF 0x00000304
mask_write 0XF8000784 0x00003FFF 0x00000304
mask_write 0XF8000788 0x00003FFF 0x00000304
mask_write 0XF800078C 0x00003FFF 0x00000304
mask_write 0XF8000790 0x00003FFF 0x00000305
mask_write 0XF8000794 0x00003FFF 0x00000304
mask_write 0XF8000798 0x00003FFF 0x00000304
mask_write 0XF800079C 0x00003FFF 0x00000304
mask_write 0XF80007A0 0x00003FFF 0x00000380
mask_write 0XF80007A4 0x00003FFF 0x00000380
mask_write 0XF80007A8 0x00003FFF 0x00000380
mask_write 0XF80007AC 0x00003FFF 0x00000380
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x002F002E
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B48 0x00000180 0x00000180
mask_write 0XF8000B4C 0x00000180 0x00000180
mask_write 0XF8000B50 0x00000180 0x00000180
mask_write 0XF8000B54 0x00000180 0x00000180
mwr -force 0XF8000004 0x0000767B
mask_write 0XE0001034 0x000000FF 0x00000006
mask_write 0XE0001018 0x0000FFFF 0x0000003E
mask_write 0XE0001000 0x000001FF 0x00000017
mask_write 0XE0001004 0x000003FF 0x00000020
mask_write 0XE000D000 0x00080000 0x00080000
mask_write 0XF8007000 0x20000000 0x00000000
}
proc ps7_post_config_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000900 0x0000000F 0x0000000F
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_debug_3_0 {} {
mwr -force 0XF8898FB0 0xC5ACCE55
mwr -force 0XF8899FB0 0xC5ACCE55
mwr -force 0XF8809FB0 0xC5ACCE55
}
proc ps7_pll_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000110 0x003FFFF0 0x000FA220
mask_write 0XF8000100 0x0007F000 0x00028000
mask_write 0XF8000100 0x00000010 0x00000010
mask_write 0XF8000100 0x00000001 0x00000001
mask_write 0XF8000100 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000001
mask_write 0XF8000100 0x00000010 0x00000000
mask_write 0XF8000120 0x1F003F30 0x1F000200
mask_write 0XF8000114 0x003FFFF0 0x0012C220
mask_write 0XF8000104 0x0007F000 0x00020000
mask_write 0XF8000104 0x00000010 0x00000010
mask_write 0XF8000104 0x00000001 0x00000001
mask_write 0XF8000104 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000002
mask_write 0XF8000104 0x00000010 0x00000000
mask_write 0XF8000124 0xFFF00003 0x0C200003
mask_write 0XF8000118 0x003FFFF0 0x001452C0
mask_write 0XF8000108 0x0007F000 0x0001E000
mask_write 0XF8000108 0x00000010 0x00000010
mask_write 0XF8000108 0x00000001 0x00000001
mask_write 0XF8000108 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000004
mask_write 0XF8000108 0x00000010 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_clock_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000128 0x03F03F01 0x00700F01
mask_write 0XF8000138 0x00000011 0x00000001
mask_write 0XF8000140 0x03F03F71 0x00100801
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00001402
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01EC044D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_2_0 {} {
mask_write 0XF8006000 0x0001FFFF 0x00000080
mask_write 0XF8006004 0x1FFFFFFF 0x00081081
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
mask_write 0XF800600C 0x03FFFFFF 0x02001001
mask_write 0XF8006010 0x03FFFFFF 0x00014001
mask_write 0XF8006014 0x001FFFFF 0x0004159B
mask_write 0XF8006018 0xF7FFFFFF 0x452460D2
mask_write 0XF800601C 0xFFFFFFFF 0x720238E5
mask_write 0XF8006020 0xFFFFFFFC 0x272872D0
mask_write 0XF8006024 0x0FFFFFFF 0x0000003C
mask_write 0XF8006028 0x00003FFF 0x00002007
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
mask_write 0XF8006030 0xFFFFFFFF 0x00040930
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
mask_write 0XF8006038 0x00001FC3 0x00000000
mask_write 0XF800603C 0x000FFFFF 0x00000777
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
mask_write 0XF8006048 0x3FFFFFFF 0x0003C248
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
mask_write 0XF8006058 0x0001FFFF 0x00000101
mask_write 0XF800605C 0x0000FFFF 0x00005003
mask_write 0XF8006060 0x000017FF 0x0000003E
mask_write 0XF8006064 0x00021FE0 0x00020000
mask_write 0XF8006068 0x03FFFFFF 0x00284141
mask_write 0XF800606C 0x0000FFFF 0x00001610
mask_write 0XF8006078 0x03FFFFFF 0x00466111
mask_write 0XF800607C 0x000FFFFF 0x00032222
mask_write 0XF80060A0 0x00FFFFFF 0x00008000
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
mask_write 0XF80060AC 0x000001FF 0x000001FE
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
mask_write 0XF80060B4 0x000007FF 0x00000200
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
mask_write 0XF80060C4 0x00000003 0x00000000
mask_write 0XF80060C8 0x000000FF 0x00000000
mask_write 0XF80060DC 0x00000001 0x00000000
mask_write 0XF80060F0 0x0000FFFF 0x00000000
mask_write 0XF80060F4 0x0000000F 0x00000008
mask_write 0XF8006114 0x000000FF 0x00000000
mask_write 0XF8006118 0x7FFFFFFF 0x40000001
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x00000080
mask_write 0XF8006160 0x000FFFFF 0x00000080
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000C0
mask_write 0XF8006188 0x000FFFFF 0x000000C0
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
mask_write 0XF8006208 0x000F03FF 0x000803FF
mask_write 0XF800620C 0x000F03FF 0x000803FF
mask_write 0XF8006210 0x000F03FF 0x000803FF
mask_write 0XF8006214 0x000F03FF 0x000803FF
mask_write 0XF8006218 0x000F03FF 0x000003FF
mask_write 0XF800621C 0x000F03FF 0x000003FF
mask_write 0XF8006220 0x000F03FF 0x000003FF
mask_write 0XF8006224 0x000F03FF 0x000003FF
mask_write 0XF80062A8 0x00000FF7 0x00000000
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
mask_write 0XF80062B0 0x003FFFFF 0x00005125
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
mask_poll 0XF8000B74 0x00002000
mask_write 0XF8006000 0x0001FFFF 0x00000081
mask_poll 0XF8006054 0x00000007
}
proc ps7_mio_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B40 0x00000FFF 0x00000600
mask_write 0XF8000B44 0x00000FFF 0x00000600
mask_write 0XF8000B48 0x00000FFF 0x00000672
mask_write 0XF8000B4C 0x00000FFF 0x00000672
mask_write 0XF8000B50 0x00000FFF 0x00000674
mask_write 0XF8000B54 0x00000FFF 0x00000674
mask_write 0XF8000B58 0x00000FFF 0x00000600
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B6C 0x00007FFF 0x00000209
mask_write 0XF8000B70 0x00000021 0x00000021
mask_write 0XF8000B70 0x00000021 0x00000020
mask_write 0XF8000B70 0x07FFFFFF 0x00000823
mask_write 0XF8000700 0x00003FFF 0x00000600
mask_write 0XF8000704 0x00003FFF 0x00000702
mask_write 0XF8000708 0x00003FFF 0x00000702
mask_write 0XF800070C 0x00003FFF 0x00000702
mask_write 0XF8000710 0x00003FFF 0x00000702
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00000600
mask_write 0XF8000728 0x00003FFF 0x00000600
mask_write 0XF800072C 0x00003FFF 0x00000600
mask_write 0XF8000730 0x00003FFF 0x00000600
mask_write 0XF8000734 0x00003FFF 0x00000600
mask_write 0XF8000738 0x00003FFF 0x00000600
mask_write 0XF800073C 0x00003FFF 0x00000600
mask_write 0XF8000740 0x00003FFF 0x00000302
mask_write 0XF8000744 0x00003FFF 0x00000302
mask_write 0XF8000748 0x00003FFF 0x00000302
mask_write 0XF800074C 0x00003FFF 0x00000302
mask_write 0XF8000750 0x00003FFF 0x00000302
mask_write 0XF8000754 0x00003FFF 0x00000302
mask_write 0XF8000758 0x00003FFF 0x00000303
mask_write 0XF800075C 0x00003FFF 0x00000303
mask_write 0XF8000760 0x00003FFF 0x00000303
mask_write 0XF8000764 0x00003FFF 0x00000303
mask_write 0XF8000768 0x00003FFF 0x00000303
mask_write 0XF800076C 0x00003FFF 0x00000303
mask_write 0XF8000770 0x00003FFF 0x00000304
mask_write 0XF8000774 0x00003FFF 0x00000305
mask_write 0XF8000778 0x00003FFF 0x00000304
mask_write 0XF800077C 0x00003FFF 0x00000305
mask_write 0XF8000780 0x00003FFF 0x00000304
mask_write 0XF8000784 0x00003FFF 0x00000304
mask_write 0XF8000788 0x00003FFF 0x00000304
mask_write 0XF800078C 0x00003FFF 0x00000304
mask_write 0XF8000790 0x00003FFF 0x00000305
mask_write 0XF8000794 0x00003FFF 0x00000304
mask_write 0XF8000798 0x00003FFF 0x00000304
mask_write 0XF800079C 0x00003FFF 0x00000304
mask_write 0XF80007A0 0x00003FFF 0x00000380
mask_write 0XF80007A4 0x00003FFF 0x00000380
mask_write 0XF80007A8 0x00003FFF 0x00000380
mask_write 0XF80007AC 0x00003FFF 0x00000380
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x002F002E
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B48 0x00000180 0x00000180
mask_write 0XF8000B4C 0x00000180 0x00000180
mask_write 0XF8000B50 0x00000180 0x00000180
mask_write 0XF8000B54 0x00000180 0x00000180
mwr -force 0XF8000004 0x0000767B
mask_write 0XE0001034 0x000000FF 0x00000006
mask_write 0XE0001018 0x0000FFFF 0x0000003E
mask_write 0XE0001000 0x000001FF 0x00000017
mask_write 0XE0001004 0x00000FFF 0x00000020
mask_write 0XE000D000 0x00080000 0x00080000
mask_write 0XF8007000 0x20000000 0x00000000
}
proc ps7_post_config_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000900 0x0000000F 0x0000000F
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_debug_2_0 {} {
mwr -force 0XF8898FB0 0xC5ACCE55
mwr -force 0XF8899FB0 0xC5ACCE55
mwr -force 0XF8809FB0 0xC5ACCE55
}
proc ps7_pll_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000110 0x003FFFF0 0x000FA220
mask_write 0XF8000100 0x0007F000 0x00028000
mask_write 0XF8000100 0x00000010 0x00000010
mask_write 0XF8000100 0x00000001 0x00000001
mask_write 0XF8000100 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000001
mask_write 0XF8000100 0x00000010 0x00000000
mask_write 0XF8000120 0x1F003F30 0x1F000200
mask_write 0XF8000114 0x003FFFF0 0x0012C220
mask_write 0XF8000104 0x0007F000 0x00020000
mask_write 0XF8000104 0x00000010 0x00000010
mask_write 0XF8000104 0x00000001 0x00000001
mask_write 0XF8000104 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000002
mask_write 0XF8000104 0x00000010 0x00000000
mask_write 0XF8000124 0xFFF00003 0x0C200003
mask_write 0XF8000118 0x003FFFF0 0x001452C0
mask_write 0XF8000108 0x0007F000 0x0001E000
mask_write 0XF8000108 0x00000010 0x00000010
mask_write 0XF8000108 0x00000001 0x00000001
mask_write 0XF8000108 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000004
mask_write 0XF8000108 0x00000010 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_clock_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000128 0x03F03F01 0x00700F01
mask_write 0XF8000138 0x00000011 0x00000001
mask_write 0XF8000140 0x03F03F71 0x00100801
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00001402
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200400
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01EC044D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_1_0 {} {
mask_write 0XF8006000 0x0001FFFF 0x00000080
mask_write 0XF8006004 0x1FFFFFFF 0x00081081
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
mask_write 0XF800600C 0x03FFFFFF 0x02001001
mask_write 0XF8006010 0x03FFFFFF 0x00014001
mask_write 0XF8006014 0x001FFFFF 0x0004159B
mask_write 0XF8006018 0xF7FFFFFF 0x452460D2
mask_write 0XF800601C 0xFFFFFFFF 0x720238E5
mask_write 0XF8006020 0xFFFFFFFC 0x272872D0
mask_write 0XF8006024 0x0FFFFFFF 0x0000003C
mask_write 0XF8006028 0x00003FFF 0x00002007
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
mask_write 0XF8006030 0xFFFFFFFF 0x00040930
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
mask_write 0XF8006038 0x00001FC3 0x00000000
mask_write 0XF800603C 0x000FFFFF 0x00000777
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
mask_write 0XF8006048 0x3FFFFFFF 0x0003C248
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
mask_write 0XF8006058 0x0001FFFF 0x00000101
mask_write 0XF800605C 0x0000FFFF 0x00005003
mask_write 0XF8006060 0x000017FF 0x0000003E
mask_write 0XF8006064 0x00021FE0 0x00020000
mask_write 0XF8006068 0x03FFFFFF 0x00284141
mask_write 0XF800606C 0x0000FFFF 0x00001610
mask_write 0XF80060A0 0x00FFFFFF 0x00008000
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
mask_write 0XF80060AC 0x000001FF 0x000001FE
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
mask_write 0XF80060B4 0x000007FF 0x00000200
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
mask_write 0XF80060C4 0x00000003 0x00000000
mask_write 0XF80060C8 0x000000FF 0x00000000
mask_write 0XF80060DC 0x00000001 0x00000000
mask_write 0XF80060F0 0x0000FFFF 0x00000000
mask_write 0XF80060F4 0x0000000F 0x00000008
mask_write 0XF8006114 0x000000FF 0x00000000
mask_write 0XF8006118 0x7FFFFFFF 0x40000001
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x00000080
mask_write 0XF8006160 0x000FFFFF 0x00000080
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000C0
mask_write 0XF8006188 0x000FFFFF 0x000000C0
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
mask_write 0XF8006208 0x000F03FF 0x000803FF
mask_write 0XF800620C 0x000F03FF 0x000803FF
mask_write 0XF8006210 0x000F03FF 0x000803FF
mask_write 0XF8006214 0x000F03FF 0x000803FF
mask_write 0XF8006218 0x000F03FF 0x000003FF
mask_write 0XF800621C 0x000F03FF 0x000003FF
mask_write 0XF8006220 0x000F03FF 0x000003FF
mask_write 0XF8006224 0x000F03FF 0x000003FF
mask_write 0XF80062A8 0x00000FF7 0x00000000
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
mask_write 0XF80062B0 0x003FFFFF 0x00005125
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
mask_poll 0XF8000B74 0x00002000
mask_write 0XF8006000 0x0001FFFF 0x00000081
mask_poll 0XF8006054 0x00000007
}
proc ps7_mio_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B40 0x00000FFF 0x00000600
mask_write 0XF8000B44 0x00000FFF 0x00000600
mask_write 0XF8000B48 0x00000FFF 0x00000672
mask_write 0XF8000B4C 0x00000FFF 0x00000672
mask_write 0XF8000B50 0x00000FFF 0x00000674
mask_write 0XF8000B54 0x00000FFF 0x00000674
mask_write 0XF8000B58 0x00000FFF 0x00000600
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B6C 0x000073FF 0x00000209
mask_write 0XF8000B70 0x00000021 0x00000021
mask_write 0XF8000B70 0x00000021 0x00000020
mask_write 0XF8000B70 0x07FFFFFF 0x00000823
mask_write 0XF8000700 0x00003FFF 0x00000600
mask_write 0XF8000704 0x00003FFF 0x00000702
mask_write 0XF8000708 0x00003FFF 0x00000702
mask_write 0XF800070C 0x00003FFF 0x00000702
mask_write 0XF8000710 0x00003FFF 0x00000702
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00000600
mask_write 0XF8000728 0x00003FFF 0x00000600
mask_write 0XF800072C 0x00003FFF 0x00000600
mask_write 0XF8000730 0x00003FFF 0x00000600
mask_write 0XF8000734 0x00003FFF 0x00000600
mask_write 0XF8000738 0x00003FFF 0x00000600
mask_write 0XF800073C 0x00003FFF 0x00000600
mask_write 0XF8000740 0x00003FFF 0x00000302
mask_write 0XF8000744 0x00003FFF 0x00000302
mask_write 0XF8000748 0x00003FFF 0x00000302
mask_write 0XF800074C 0x00003FFF 0x00000302
mask_write 0XF8000750 0x00003FFF 0x00000302
mask_write 0XF8000754 0x00003FFF 0x00000302
mask_write 0XF8000758 0x00003FFF 0x00000303
mask_write 0XF800075C 0x00003FFF 0x00000303
mask_write 0XF8000760 0x00003FFF 0x00000303
mask_write 0XF8000764 0x00003FFF 0x00000303
mask_write 0XF8000768 0x00003FFF 0x00000303
mask_write 0XF800076C 0x00003FFF 0x00000303
mask_write 0XF8000770 0x00003FFF 0x00000304
mask_write 0XF8000774 0x00003FFF 0x00000305
mask_write 0XF8000778 0x00003FFF 0x00000304
mask_write 0XF800077C 0x00003FFF 0x00000305
mask_write 0XF8000780 0x00003FFF 0x00000304
mask_write 0XF8000784 0x00003FFF 0x00000304
mask_write 0XF8000788 0x00003FFF 0x00000304
mask_write 0XF800078C 0x00003FFF 0x00000304
mask_write 0XF8000790 0x00003FFF 0x00000305
mask_write 0XF8000794 0x00003FFF 0x00000304
mask_write 0XF8000798 0x00003FFF 0x00000304
mask_write 0XF800079C 0x00003FFF 0x00000304
mask_write 0XF80007A0 0x00003FFF 0x00000380
mask_write 0XF80007A4 0x00003FFF 0x00000380
mask_write 0XF80007A8 0x00003FFF 0x00000380
mask_write 0XF80007AC 0x00003FFF 0x00000380
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x002F002E
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B48 0x00000180 0x00000180
mask_write 0XF8000B4C 0x00000180 0x00000180
mask_write 0XF8000B50 0x00000180 0x00000180
mask_write 0XF8000B54 0x00000180 0x00000180
mwr -force 0XF8000004 0x0000767B
mask_write 0XE0001034 0x000000FF 0x00000006
mask_write 0XE0001018 0x0000FFFF 0x0000003E
mask_write 0XE0001000 0x000001FF 0x00000017
mask_write 0XE0001004 0x00000FFF 0x00000020
mask_write 0XE000D000 0x00080000 0x00080000
mask_write 0XF8007000 0x20000000 0x00000000
}
proc ps7_post_config_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000900 0x0000000F 0x0000000F
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_debug_1_0 {} {
mwr -force 0XF8898FB0 0xC5ACCE55
mwr -force 0XF8899FB0 0xC5ACCE55
mwr -force 0XF8809FB0 0xC5ACCE55
}
set PCW_SILICON_VER_1_0 "0x0"
set PCW_SILICON_VER_2_0 "0x1"
set PCW_SILICON_VER_3_0 "0x2"
set APU_FREQ 666666667
proc mask_poll { addr mask } {
set count 1
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval & $mask}]
while { $maskedval == 0 } {
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval & $mask}]
set count [ expr { $count + 1 } ]
if { $count == 100000000 } {
puts "Timeout Reached. Mask poll failed at ADDRESS: $addr MASK: $mask"
break
}
}
}
proc mask_delay { addr val } {
set delay [ get_number_of_cycles_for_delay $val ]
perf_reset_and_start_timer
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval < $delay}]
while { $maskedval == 1 } {
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval < $delay}]
}
perf_reset_clock
}
proc ps_version { } {
set si_ver "0x[string range [mrd 0xF8007080] end-8 end]"
set mask_sil_ver "0x[expr {$si_ver >> 28}]"
return $mask_sil_ver;
}
proc ps7_post_config {} {
ps7_post_config_1_0
}
proc ps7_debug {} {
ps7_debug_1_0
}
proc ps7_init {} {
ps7_mio_init_data_1_0
ps7_pll_init_data_1_0
ps7_clock_init_data_1_0
ps7_ddr_init_data_1_0
ps7_peripherals_init_data_1_0
}
# For delay calculation using global timer
# start timer
proc perf_start_clock { } {
#writing SCU_GLOBAL_TIMER_CONTROL register
mask_write 0xF8F00208 0x00000109 0x00000009
}
# stop timer and reset timer count regs
proc perf_reset_clock { } {
perf_disable_clock
mask_write 0xF8F00200 0xFFFFFFFF 0x00000000
mask_write 0xF8F00204 0xFFFFFFFF 0x00000000
}
# Compute mask for given delay in miliseconds
proc get_number_of_cycles_for_delay { delay } {
# GTC is always clocked at 1/2 of the CPU frequency (CPU_3x2x)
variable APU_FREQ
return [ expr ($delay * $APU_FREQ /(2 * 1000))]
}
# stop timer
proc perf_disable_clock {} {
mask_write 0xF8F00208 0xFFFFFFFF 0x00000000
}
proc perf_reset_and_start_timer {} {
perf_reset_clock
perf_start_clock
}

38
openocd/redpitaya.cfg Normal file
View File

@ -0,0 +1,38 @@
source ./digilent-hs2.cfg
adapter_khz 1000
set PL_TAPID 0x13722093
set SMP 1
source ./zynq-7000.cfg
reset_config none
set XC7_JSHUTDOWN 0x0d
set XC7_JPROGRAM 0x0b
set XC7_JSTART 0x0c
set XC7_BYPASS 0x3f
proc xc7_program {tap} {
global XC7_JSHUTDOWN XC7_JPROGRAM XC7_JSTART XC7_BYPASS
irscan $tap $XC7_JSHUTDOWN
irscan $tap $XC7_JPROGRAM
runtest 60000
#JSTART prevents this from working...
#irscan $tap $XC7_JSTART
runtest 2000
irscan $tap $XC7_BYPASS
runtest 2000
}
pld device virtex2 zynq.tap 1
init
xc7_program zynq.tap
halt
# Disable MMU
targets $_TARGETNAME_1
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
targets $_TARGETNAME_0
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]

View File

@ -1,61 +0,0 @@
#
# TCL to allow the Xilinx PS7 Init TCL code to run in OpenOCD.
#
proc mrd { args } {
if {[llength $args] == 0} {
echo "mrd address \[count \[w|h|b\]\]"
echo " Read <count> memory locations starting at <address>. Defaults to one word."
return
}
set addr [lindex $args 0]
set count 1
set bits 32
if {[llength $args] > 1} {
set count [lindex $args 1]
if {[llength $args] > 2} {
switch [lindex $args 2] {
w { set bits 32 }
h { set bits 16 }
b { set bits 8 }
default { set bits 32 }
}
}
}
mem2array x $bits $addr $count
set nibbles [expr {$bits / 4}]
set bytes [expr {$bits / 8}]
set result {}
foreach {idx elmt} $x {
append result [format "%08x: %0*x\n" [expr {$addr + $idx * $bytes}] $nibbles $elmt]
}
return $result
}
proc mwr { args } {
set addr [lindex $args 1]
set data [lindex $args 2]
mww $addr $data
}
proc mask_write { addr mask value } {
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval & ~$mask}]
#echo "curval = [format 0x%08x $curval] maskedval = [format 0x%08x $maskedval]"
set writeval(0) [expr {$maskedval | $value}]
#echo " $addr <= [format 0x%08x $writeval(0)] ([format 0x%08x $curval]: [format 0x%08x $mask]/[format 0x%08x $value])"
array2mem writeval 32 $addr 1
}
proc xilinx_ps7_init { } {
poll off
reset init
reset halt
targets zynq.cpu.0
sleep 100
halt
ps7_debug
ps7_init
ps7_post_config
poll on
}

View File

@ -1,13 +1,10 @@
source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
source [find xilinx-tcl.cfg]
adapter_khz 1000
set PL_TAPID 0x23731093
set SMP 1
source ./zynq-7000.cfg
source ./xilinx-tcl.cfg
source ./ps7_init.tcl
reset_config srst_only srst_open_drain
adapter_nsrst_assert_width 250
@ -34,7 +31,7 @@ pld device virtex2 zynq.tap 1
init
xc7_program zynq.tap
xilinx_ps7_init
reset halt
# Disable MMU
targets $_TARGETNAME_1

View File

@ -93,119 +93,3 @@ if { $_SMP } {
-dbgbase 0x80092000
target smp $_TARGETNAME_0 $_TARGETNAME_1
}
#
# Hack to get the registers into a stable state when first booting a zynq in
# JTAG mode. If r11 is pointing to an invalid address and you use gdb to set a
# register the write will fail because gdb attempts to scan or unwind the
# current frame and the bad address seems to lock the bus up. This code puts
# the registers into the OCM and hopefull safe.
#
proc zynq_clear_registers { target } {
echo "Zynq-7000 Series setup: $target"
set _OCM_END 0x0003FFF0
mww phys 0xF8007000 0x4E00E07F
reg r0 0
reg r1 0
reg r2 0
reg r3 0
reg r4 0
reg r5 0
reg r6 0
reg r7 0
reg r8 0
reg r9 0
reg r10 0
reg r11 $_OCM_END
reg sp_svc $_OCM_END
reg lr_svc $_OCM_END
reg sp_abt $_OCM_END
reg lr_abt $_OCM_END
reg sp_und $_OCM_END
reg lr_und $_OCM_END
}
proc zynq_disable_mmu_and_caches { target } {
# arm mcr pX op1 CRn CRm op2 value
echo "Disable MMU and caches"
# Invalidate caches
catch {
$target arm mcr 15 0 7 5 0 0
$target arm mcr 15 0 7 7 0 0
# Invalidate all TLBs
$target arm mcr 15 0 8 5 0 0
$target arm mcr 15 0 8 6 0 0
$target arm mcr 15 0 8 7 0 0
$target arm mcr 15 4 8 3 0 0
$target arm mcr 15 4 8 7 0 0
set cp [$target arm mrc 15 0 1 0 0]
echo "SCTRL => [format 0x%x $cp]"
set mask [expr 1 << 29 | 1 << 12 | 1 << 11 | 1 << 2 | 1 << 1 | 1 << 0]
set cp [expr ($cp & ~$mask)]
$target arm mcr 15 0 1 0 0 $cp
echo "SCTRL <= [format 0x%x $cp]"
}
}
proc zynq_boot_ocm_setup { } {
#
# Enable the OCM
#
echo "Zynq Boot OCM setup"
catch {
mww phys 0xF8000008 0xDF0D
mww phys 0xF8000238 0
mww phys 0xF8000910 0xC
}
}
proc zynq_rtems_setup { } {
cache_config l2x 0xF8F02000 8
cortex_a maskisr on
}
proc zynq_restart { wait } {
global _SMP
global _TARGETNAME_0
global _TARGETNAME_1
set target0 $_TARGETNAME_0
set target1 $_TARGETNAME_1
echo "Zynq reset, resetting the board ... "
poll off
#
# Issue the reset via the SLCR
#
catch {
mww phys 0xF8000008 0xDF0D
mww phys 0xF8000200 1
}
echo "Zynq reset waiting for $wait msecs ... "
sleep $wait
#
# Reconnect the DAP etc due to the reset.
#
$target0 cortex_a dbginit
$target0 arm core_state arm
if { $_SMP } {
$target1 arm core_state arm
$target1 cortex_a dbginit
cortex_a smp_off
}
poll on
#
# We can now halt the core.
#
if { $_SMP } {
targets $target1
halt
}
targets $target0
halt
zynq_rtems_setup
}
proc zynq_gdb_attach { target } {
catch {
halt
}
}

View File

@ -1,6 +0,0 @@
target remote :1234
# print demangled symbols by default
set print asm-demangle on
load

View File

@ -4,7 +4,7 @@ set -e
target_host="rpi-4.m-labs.hk"
while getopts "h:i" opt; do
while getopts "h:" opt; do
case "$opt" in
\?) exit 0
;;

View File

@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -e -m
ELF=$1
IMAGE=$ELF.bin
arm-none-eabihf-objcopy -O binary $ELF $IMAGE
qemu-system-arm -M xilinx-zynq-a9 -s -kernel $IMAGE -chardev file,id=uart0,path=/tmp/qemu.serial &
sleep 1
gdb -x qemu.gdb $ELF
kill -KILL %1

View File

@ -1,24 +1,26 @@
let
mozillaOverlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz);
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
pkgs = import <nixpkgs> { overlays = [ (import ./nix/mozilla-overlay.nix) ]; };
rustPlatform = (import ./nix/rust-platform.nix { inherit pkgs; });
cargo-xbuild = (import ./default.nix).cargo-xbuild;
in
with pkgs;
let
project = callPackage ./default.nix {};
in
with project;
stdenv.mkDerivation {
name = "zynq-env";
buildInputs = (with rustPlatform.rust; [
rustc cargo
cargo-xbuild rustcSrc
]) ++ (with pkgs; [ openocd gdb ]);
pkgs.stdenv.mkDerivation {
name = "zynq-env";
buildInputs = [
rustPlatform.rust.rustc
rustPlatform.rust.cargo
pkgs.cacert
cargo-xbuild
# Set Environment Variables
RUST_BACKTRACE = 1;
XARGO_RUST_SRC = "${rustcSrc}/src";
pkgs.openocd pkgs.gdb
pkgs.openssh pkgs.rsync
pkgs.llvmPackages_9.clang-unwrapped
shellHook = ''
echo "Run 'cargo xbuild --release -p experiments' to build."
'';
}
(import ./nix/mkbootimage.nix { inherit pkgs; })
];
XARGO_RUST_SRC = "${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library";
shellHook = ''
echo "Run 'cargo xbuild --release -p ...' to build."
'';
}

24
szl/Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "szl"
description = "Simple Zynq Loader"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706"]
target_coraz7 = ["libboard_zynq/target_coraz7", "libsupport_zynq/target_coraz7", "libconfig/target_coraz7"]
target_redpitaya = ["libboard_zynq/target_redpitaya", "libsupport_zynq/target_redpitaya", "libconfig/target_redpitaya"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
default = ["target_zc706"]
[dependencies]
log = "0.4"
byteorder = { version = "1.3", default-features = false }
core_io = { version = "0.1", features = ["collections"] }
libboard_zynq = { path = "../libboard_zynq" }
libsupport_zynq = { path = "../libsupport_zynq" }
libcortex_a9 = { path = "../libcortex_a9" }
libregister = { path = "../libregister" }
libconfig = { path = "../libconfig" }

22
szl/build.rs Normal file
View File

@ -0,0 +1,22 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let out = env::var("OUT_DIR").unwrap();
let out_dir = &PathBuf::from(&out);
// Put the linker script somewhere the linker can find it
File::create(out_dir.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out_dir.display());
// Only re-run the build script when link.x is changed,
// instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=link.x");
}

84
szl/link.x Normal file
View File

@ -0,0 +1,84 @@
ENTRY(Reset);
MEMORY
{
/* 256 kB On-Chip Memory */
OCM : ORIGIN = 0, LENGTH = 0x30000
SDRAM : ORIGIN = 0x00100000, LENGTH = 0x1FF00000
OCM3 : ORIGIN = 0xFFFF0000, LENGTH = 0x10000
}
SECTIONS
{
.text :
{
KEEP(*(.text.exceptions));
*(.text.boot);
*(.text .text.*);
} > OCM
.rodata : ALIGN(4)
{
*(.rodata .rodata.*);
} > OCM
.data : ALIGN(4)
{
*(.data .data.*);
} > OCM
.heap (NOLOAD) : ALIGN(8)
{
__runtime_start = .;
. += 0x8000000;
__runtime_end = .;
__heap0_start = .;
. += 0x8000000;
__heap0_end = .;
} > SDRAM
.bss (NOLOAD) : ALIGN(4)
{
__bss_start = .;
*(.bss .bss.*);
. = ALIGN(4);
__bss_end = .;
} > OCM3
.stack1 (NOLOAD) : ALIGN(8)
{
__stack1_end = .;
. += 0x100;
__stack1_start = .;
} > OCM3
.stack0 (NOLOAD) : ALIGN(8)
{
__stack0_end = .;
. += 0x4000;
__stack0_start = .;
} > 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
/DISCARD/ :
{
/* Unused exception related info that only wastes space */
*(.ARM.exidx);
*(.ARM.exidx.*);
*(.ARM.extab.*);
}
}

165
szl/src/main.rs Normal file
View File

@ -0,0 +1,165 @@
#![no_std]
#![no_main]
extern crate alloc;
extern crate log;
mod netboot;
use alloc::rc::Rc;
use core::mem;
use core_io::{Read, Seek};
use libboard_zynq::{
self as zynq,
clocks::source::{ArmPll, ClockSource, IoPll},
clocks::Clocks,
logger, println, sdio, slcr,
timer::GlobalTimer,
};
use libconfig::{bootgen, sd_reader, Config};
use libcortex_a9::{
asm::{dsb, isb},
cache::{bpiall, dcciall, iciallu},
};
use libregister::RegisterR;
use libsupport_zynq::ram;
use log::info;
extern "C" {
static mut __runtime_start: usize;
static mut __runtime_end: usize;
}
fn boot_sd<File: Read + Seek>(
file: &mut Option<File>,
runtime_start: *mut u8,
runtime_max: usize,
) -> Result<(), ()> {
if file.is_none() {
log::error!("No bootgen file");
return Err(());
}
let mut file = file.as_mut().unwrap();
info!("Loading gateware");
bootgen::load_bitstream(&mut file).map_err(|e| log::error!("Cannot load gateware: {:?}", e))?;
info!("Loading runtime");
let runtime =
bootgen::get_runtime(&mut file).map_err(|e| log::error!("Cannot load runtime: {:?}", e))?;
if runtime.len() > runtime_max {
log::error!(
"Runtime binary too large, max {} but got {}",
runtime_max,
runtime.len()
);
}
unsafe {
let target = core::slice::from_raw_parts_mut(runtime_start, runtime.len());
target.copy_from_slice(&runtime);
}
Ok(())
}
#[no_mangle]
pub fn main_core0() {
GlobalTimer::start();
logger::init().unwrap();
log::set_max_level(log::LevelFilter::Debug);
println!(
r#"
__________ __
/ ___/__ / / /
\__ \ / / / /
___/ / / /__/ /___
/____/ /____/_____/
(C) 2020-2021 M-Labs
"#
);
info!("Simple Zynq Loader starting...");
const CPU_FREQ: u32 = 800_000_000;
ArmPll::setup(2 * CPU_FREQ);
Clocks::set_cpu_freq(CPU_FREQ);
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart(); // reinitialize UART after clocking change
let mut ddr = zynq::ddr::DdrRam::ddrram();
ram::init_alloc_core0();
let sdio0 = sdio::Sdio::sdio0(true);
let fs = if sdio0.is_card_inserted() {
info!("Card inserted. Mounting file system.");
let sd = sdio::sd_card::SdCard::from_sdio(sdio0).unwrap();
let reader = sd_reader::SdReader::new(sd);
reader
.mount_fatfs(sd_reader::PartitionEntry::Entry1)
.map(|v| Rc::new(v))
.ok()
} else {
info!("No SD card inserted.");
None
};
let fs_ref = fs.as_ref();
let root_dir = fs_ref.map(|fs| fs.root_dir());
let mut bootgen_file = root_dir.and_then(|root_dir| root_dir.open_file("/BOOT.BIN").ok());
let config = Config::from_fs(fs.clone());
unsafe {
let max_len =
&__runtime_end as *const usize as usize - &__runtime_start as *const usize as usize;
match slcr::RegisterBlock::unlocked(|slcr| slcr.boot_mode.read().boot_mode_pins()) {
slcr::BootModePins::Jtag => netboot::netboot(
&mut bootgen_file,
config,
&mut __runtime_start as *mut usize as *mut u8,
max_len,
),
slcr::BootModePins::SdCard => {
if boot_sd(
&mut bootgen_file,
&mut __runtime_start as *mut usize as *mut u8,
max_len,
)
.is_err()
{
log::error!("Error booting from SD card");
log::info!("Fall back on netboot");
netboot::netboot(
&mut bootgen_file,
config,
&mut __runtime_start as *mut usize as *mut u8,
max_len,
)
}
}
v => {
panic!("Boot mode {:?} not supported", v);
}
};
}
info!("Preparing for runtime execution");
// Flush data cache entries for all of L1 cache, including
// Memory/Instruction Synchronization Barriers
dcciall();
iciallu();
bpiall();
dsb();
isb();
// Start core0 only, for compatibility with FSBL.
info!("executing payload");
unsafe {
(mem::transmute::<*mut u8, fn()>(ddr.ptr::<u8>()))();
}
loop {}
}
#[no_mangle]
pub fn main_core1() {
panic!("core1 started but should not have");
}

399
szl/src/netboot.rs Normal file
View File

@ -0,0 +1,399 @@
use alloc::vec;
use alloc::vec::Vec;
use byteorder::{ByteOrder, NetworkEndian};
use core_io::{Read, Seek};
use libboard_zynq::{
devc,
eth::Eth,
smoltcp::{
self,
iface::{EthernetInterfaceBuilder, NeighborCache},
time::Instant,
wire::IpCidr,
},
timer::GlobalTimer,
};
use libconfig::{bootgen, net_settings, Config};
enum NetConnState {
WaitCommand,
FirmwareLength(usize, u8),
FirmwareDownload(usize, usize),
FirmwareWaitO,
FirmwareWaitK,
GatewareLength(usize, u8),
GatewareDownload(usize, usize),
GatewareWaitO,
GatewareWaitK,
}
struct NetConn {
state: NetConnState,
firmware_downloaded: bool,
gateware_downloaded: bool,
}
impl NetConn {
pub fn new() -> NetConn {
NetConn {
state: NetConnState::WaitCommand,
firmware_downloaded: false,
gateware_downloaded: false,
}
}
pub fn reset(&mut self) {
self.state = NetConnState::WaitCommand;
self.firmware_downloaded = false;
self.gateware_downloaded = false;
}
fn input_partial<File: Read + Seek>(
&mut self,
bootgen_file: &mut Option<File>,
runtime_start: *mut u8,
runtime_max_len: usize,
buf: &[u8],
storage: &mut Vec<u8>,
mut boot_callback: impl FnMut(),
) -> Result<usize, ()> {
match self.state {
NetConnState::WaitCommand => match buf[0] {
b'F' => {
log::info!("Received firmware load command");
self.state = NetConnState::FirmwareLength(0, 0);
Ok(1)
}
b'G' => {
log::info!("Received gateware load command");
self.state = NetConnState::GatewareLength(0, 0);
storage.clear();
Ok(1)
}
b'B' => {
if !self.gateware_downloaded {
log::info!("Gateware not loaded via netboot");
if bootgen_file.is_none() {
log::error!("No bootgen file to load gateware");
return Err(());
}
log::info!("Attempting to load from SD card");
if let Err(e) = bootgen::load_bitstream(bootgen_file.as_mut().unwrap()) {
log::error!("Gateware load failed: {:?}", e);
return Err(());
}
}
if self.firmware_downloaded {
log::info!("Received boot command");
boot_callback();
self.state = NetConnState::WaitCommand;
Ok(1)
} else {
log::error!("Received boot command, but no firmware downloaded");
Err(())
}
}
_ => {
log::error!("Received unknown netboot command: 0x{:02x}", buf[0]);
Err(())
}
},
NetConnState::FirmwareLength(firmware_length, recv_bytes) => {
let firmware_length = (firmware_length << 8) | (buf[0] as usize);
let recv_bytes = recv_bytes + 1;
if recv_bytes == 4 {
if firmware_length > runtime_max_len {
log::error!(
"Runtime too large, maximum {} but requested {}",
runtime_max_len,
firmware_length
);
return Err(());
}
self.state = NetConnState::FirmwareDownload(firmware_length, 0);
storage.clear();
storage.reserve(firmware_length);
} else {
self.state = NetConnState::FirmwareLength(firmware_length, recv_bytes);
}
Ok(1)
}
NetConnState::FirmwareDownload(firmware_length, recv_bytes) => {
let max_length = firmware_length - recv_bytes;
let buf = if buf.len() > max_length {
&buf[..max_length]
} else {
&buf[..]
};
let length = buf.len();
storage.extend_from_slice(buf);
let recv_bytes = recv_bytes + length;
if recv_bytes == firmware_length {
self.state = NetConnState::FirmwareWaitO;
Ok(length)
} else {
self.state = NetConnState::FirmwareDownload(firmware_length, recv_bytes);
Ok(length)
}
}
NetConnState::FirmwareWaitO => {
if buf[0] == b'O' {
self.state = NetConnState::FirmwareWaitK;
Ok(1)
} else {
log::error!("End-of-firmware confirmation failed");
Err(())
}
}
NetConnState::FirmwareWaitK => {
if buf[0] == b'K' {
log::info!("Firmware successfully downloaded");
self.state = NetConnState::WaitCommand;
self.firmware_downloaded = true;
{
let dest = unsafe {
core::slice::from_raw_parts_mut(runtime_start, storage.len())
};
dest.copy_from_slice(storage);
}
Ok(1)
} else {
log::error!("End-of-firmware confirmation failed");
Err(())
}
}
NetConnState::GatewareLength(gateware_length, recv_bytes) => {
let gateware_length = (gateware_length << 8) | (buf[0] as usize);
let recv_bytes = recv_bytes + 1;
if recv_bytes == 4 {
self.state = NetConnState::GatewareDownload(gateware_length, 0);
storage.clear();
storage.reserve_exact(gateware_length);
} else {
self.state = NetConnState::GatewareLength(gateware_length, recv_bytes);
}
Ok(1)
}
NetConnState::GatewareDownload(gateware_length, recv_bytes) => {
let max_length = gateware_length - recv_bytes;
let buf = if buf.len() > max_length {
&buf[..max_length]
} else {
&buf[..]
};
let length = buf.len();
storage.extend_from_slice(buf);
let recv_bytes = recv_bytes + length;
if recv_bytes == gateware_length {
self.state = NetConnState::GatewareWaitO;
Ok(length)
} else {
self.state = NetConnState::GatewareDownload(gateware_length, recv_bytes);
Ok(length)
}
}
NetConnState::GatewareWaitO => {
if buf[0] == b'O' {
self.state = NetConnState::GatewareWaitK;
Ok(1)
} else {
log::error!("End-of-gateware confirmation failed");
Err(())
}
}
NetConnState::GatewareWaitK => {
if buf[0] == b'K' {
log::info!("Preprocessing bitstream...");
// find sync word 0xFFFFFFFF AA995566
let sync_word: [u8; 8] = [0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x99, 0x55, 0x66];
let mut i = 0;
let mut state = 0;
while i < storage.len() {
if storage[i] == sync_word[state] {
state += 1;
if state == sync_word.len() {
break;
}
} else {
// backtrack
// not very efficient but we only have 8 elements
'outer: while state > 0 {
state -= 1;
for j in 0..state {
if storage[i - j] != sync_word[state - j] {
continue 'outer;
}
}
break;
}
}
i += 1;
}
if state != sync_word.len() {
log::error!("Sync word not found in bitstream (corrupted?)");
return Err(());
}
// we need the sync word
// i was pointing to the last element in the sync sequence
i -= sync_word.len() - 1;
// // append no-op
// storage.extend_from_slice(&[0x20, 0, 0, 0]);
let bitstream = &mut storage[i..];
{
// swap endian
let swap = unsafe {
core::slice::from_raw_parts_mut(
bitstream.as_mut_ptr() as usize as *mut u32,
bitstream.len() / 4,
)
};
NetworkEndian::from_slice_u32(swap);
}
unsafe {
// align to 64 bytes
let ptr = alloc::alloc::alloc(
alloc::alloc::Layout::from_size_align(bitstream.len(), 64).unwrap(),
);
let buffer = core::slice::from_raw_parts_mut(ptr, bitstream.len());
buffer.copy_from_slice(bitstream);
let mut devcfg = devc::DevC::new();
devcfg.enable();
let result = devcfg.program(&buffer);
core::ptr::drop_in_place(ptr);
if let Err(e) = result {
log::error!("Error during FPGA startup: {}", e);
return Err(());
}
}
log::info!("Gateware successfully downloaded");
self.state = NetConnState::WaitCommand;
self.gateware_downloaded = true;
Ok(1)
} else {
log::info!("End-of-gateware confirmation failed");
Err(())
}
}
}
}
fn input<File: Read + Seek>(
&mut self,
bootgen_file: &mut Option<File>,
runtime_start: *mut u8,
runtime_max_len: usize,
buf: &[u8],
storage: &mut Vec<u8>,
mut boot_callback: impl FnMut(),
) -> Result<(), ()> {
let mut remaining = &buf[..];
while !remaining.is_empty() {
let read_cnt = self.input_partial(
bootgen_file,
runtime_start,
runtime_max_len,
remaining,
storage,
&mut boot_callback,
)?;
remaining = &remaining[read_cnt..];
}
Ok(())
}
}
pub fn netboot<File: Read + Seek>(
bootgen_file: &mut Option<File>,
cfg: Config,
runtime_start: *mut u8,
runtime_max_len: usize,
) {
log::info!("Preparing network for netboot");
let net_addresses = net_settings::get_adresses(&cfg);
log::info!("Network addresses: {}", net_addresses);
let eth = Eth::eth0(net_addresses.hardware_addr.0.clone());
let eth = eth.start_rx(8);
let mut eth = eth.start_tx(8);
let mut neighbor_map = [None; 2];
let neighbor_cache = NeighborCache::new(&mut neighbor_map[..]);
let mut ip_addrs = [IpCidr::new(net_addresses.ipv4_addr, 0)];
let mut interface = EthernetInterfaceBuilder::new(&mut eth)
.ethernet_addr(net_addresses.hardware_addr)
.ip_addrs(&mut ip_addrs[..])
.neighbor_cache(neighbor_cache)
.finalize();
let mut rx_storage = vec![0; 4096];
let mut tx_storage = vec![0; 128];
let mut socket_set_entries: [_; 1] = Default::default();
let mut sockets = smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]);
let tcp_rx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
let tcp_tx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]);
let tcp_socket = smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
let tcp_handle = sockets.add(tcp_socket);
let mut net_conn = NetConn::new();
let mut storage = Vec::new();
let mut boot_flag = false;
let timer = unsafe { GlobalTimer::get() };
log::info!("Waiting for connections...");
loop {
let timestamp = Instant::from_millis(timer.get_time().0 as i64);
{
let socket = &mut *sockets.get::<smoltcp::socket::TcpSocket>(tcp_handle);
if boot_flag {
return;
}
if !socket.is_open() {
socket.listen(4269).unwrap() // 0x10ad
}
if socket.may_recv() {
if socket
.recv(|data| {
(
data.len(),
net_conn
.input(
bootgen_file,
runtime_start,
runtime_max_len,
data,
&mut storage,
|| {
boot_flag = true;
},
)
.is_err(),
)
})
.unwrap()
{
net_conn.reset();
socket.close();
}
} else if socket.may_send() {
net_conn.reset();
socket.close();
}
}
match interface.poll(&mut sockets, timestamp) {
Ok(_) => (),
Err(smoltcp::Error::Unrecognized) => (),
Err(err) => log::error!("Network error: {}", err),
}
}
}

36
tmux.sh
View File

@ -1,36 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p gdb openocd cgdb tmux
SESSION=$USER
if [ $1 -eq 0 ]
then
tmux -2 new-session -d -s $SESSION
tmux new-window -t $SESSION:1 -n 'ZC706'
tmux split-window -h
tmux select-pane -t 0
tmux send-keys "stty 115200 < /dev/ttyUSB1 && cat /dev/ttyUSB1" C-m
tmux select-pane -t 1
tmux send-keys "sleep 10 && cgdb zc706.elf -x openocd/gdb-zynq-commands" C-m
tmux split-window -v
tmux resize-pane -D 20
tmux send-keys "cd openocd && openocd -f zc706.cfg -c reset init" C-m
else
tmux -2 new-session -d -s $SESSION
tmux new-window -t $SESSION:1 -n 'CORA Z7'
tmux split-window -h
tmux select-pane -t 0
tmux send-keys "stty 115200 < /dev/ttyUSB1 && cat /dev/ttyUSB1" C-m
tmux select-pane -t 1
tmux send-keys "sleep 10 && cgdb target/armv7-none-eabihf/release/zc706-experiments -x openocd/gdb-zynq-commands" C-m
tmux split-window -v
tmux resize-pane -D 20
tmux send-keys "cd openocd && openocd -f cora-z7-10.cfg -c reset init" C-m
fi
# Set default window
tmux select-window -t $SESSION:1
# Set focus on gdb
tmux select-pane -t $SESSION:.-
# Attach to session
tmux -2 attach-session -t $SESSION