Compare commits

...

146 Commits

Author SHA1 Message Date
Sebastien Bourdeauducq ac4887ea33 flake: do not use __impure (breaks hydra) 2022-06-04 13:31:07 +08:00
Sebastien Bourdeauducq edf1999bb2 flake: update dependencies, rebuild with nix 2.8 2022-06-02 19:10:03 +08:00
occheung 9ec6a1feab dyld/rebind: support rela generation with nac3ld 2022-06-01 21:27:38 +08:00
occheung 8e144e41de reloc: impl ARM_PREL31 handling 2022-06-01 21:27:38 +08:00
occheung 512b6bac12 reloc: add PC-relative relocation support 2022-06-01 21:27:38 +08:00
occheung e3ed41ff32 fix index table reference type 2022-06-01 18:35:50 +08:00
occheung 97a63ca8d0 dyld: add EXIDX entry type
The type is just for aesthetic. The interpretation of an index table entry is not our concern.
2022-06-01 18:33:19 +08:00
Sebastien Bourdeauducq d652f01379 flake: update dependencies 2022-05-31 20:59:55 +08:00
Sebastien Bourdeauducq d6ef5fd064 flake: update dependencies 2022-05-31 18:27:59 +08:00
occheung f0febe0ee4 change catch type to single reference 2022-05-31 18:26:30 +08:00
mwojcik dce8c974eb flake: remove unnecessary output 2022-05-26 12:29:51 +08:00
mwojcik 01339c9e78 flake: expose build, allow selection of output 2022-05-26 12:29:51 +08:00
Sebastien Bourdeauducq fa1f300067 flake: support impure derivation for HITL tests 2022-05-26 12:07:35 +08:00
Sebastien Bourdeauducq 191a22f506 flake: update artiq/nixpkgs 2022-05-25 14:21:41 +08:00
mwojcik 3e3fb207a5 flake: update dependencies 2022-05-25 13:49:08 +08:00
mwojcik abeaf5aca7 Revert "flake: update dependencies"
This reverts commit e20c77650d.
2022-05-25 13:48:17 +08:00
mwojcik e20c77650d flake: update dependencies 2022-05-25 12:52:39 +08:00
mwojcik 7a8f96dbd9 rtio_mgt: use mutex's async_lock 2022-05-25 10:39:06 +08:00
mwojcik 596edb480c cargo: update zynq-rs 2022-05-25 10:37:38 +08:00
mwojcik 4f457d9c24 moninj: log link down at debug level 2022-05-25 10:37:38 +08:00
mwojcik 24df52268e moninj: restructure timeout
stop logging errors if satellite is unavailable
drtio: don't even send message if link is down
2022-05-25 10:37:38 +08:00
mwojcik 48c9b43171 moninj: make it use async drtioaux 2022-05-25 10:37:38 +08:00
mwojcik 57d7f01b04 drtio: port 64-bit padding from mainline 2022-05-24 15:43:01 +08:00
mwojcik efc432352e zc706: no syncrtio for master, fixes hangs (#188) 2022-05-03 14:36:10 +08:00
mwojcik 88ffd3b77b flake: fix artiq package in zc706-hitl-tests 2022-04-26 13:06:51 +08:00
mwojcik 07210c7b09 flake: fix szl for hitl tests 2022-04-26 12:43:28 +08:00
mwojcik 1cd704c390 flake: fix szl for hitl tests 2022-04-26 12:01:20 +08:00
mwojcik 715a2dd04c flake: move hitl tests from nix-scripts to flake 2022-04-25 19:58:16 +08:00
mwojcik def4d989cd kasli_soc: fix si5324 pins routed to GTX 2022-04-25 12:33:21 +08:00
Sebastien Bourdeauducq f9a8c76654 flake: update dependencies 2022-04-19 11:05:18 +08:00
mwojcik 1d731a3589 zc706 master: route sma clock to si5324 2022-04-13 16:35:52 +08:00
mwojcik 3cf86a6335 satellites: add rtio_crg cfg 2022-04-12 13:44:53 +08:00
mwojcik 78bc162749 rtio_clocking: remove loop 2022-04-12 13:33:52 +08:00
Sebastien Bourdeauducq b974d7ddee flake: update dependencies 2022-04-09 17:26:22 +08:00
mwojcik 2c5de32a4c flake: update libasync sha256 2022-04-08 15:50:24 +08:00
mwojcik 5a7dbb3f29 flake: update sypico dependency (fix duplicate) 2022-04-08 15:50:11 +08:00
Sebastien Bourdeauducq d27d06f960 flake: update dependencies 2022-04-08 10:48:24 +08:00
mwojcik 14f7778732 update libconfig features 2022-04-08 10:30:21 +08:00
mwojcik dcfb28ce61 fix drtioaux packet corruption
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-04-01 14:15:14 +08:00
mwojcik 433a9cdaf1 runtime: fix warnings on nondrtio systems 2022-03-29 10:05:11 +08:00
Sebastien Bourdeauducq a79bef2243 runtime: provide/fix more libc mem functions 2022-03-28 13:24:01 +08:00
Sebastien Bourdeauducq 7b21889055 README: fix gateware build command 2022-03-28 13:19:59 +08:00
Sebastien Bourdeauducq c6ef9b117c fix previous commit 2022-03-26 20:08:11 +08:00
Sebastien Bourdeauducq dcfaf587ec firmware: add UnwrapNoneError exception 2022-03-26 15:29:40 +08:00
mwojcik a92561b9d3 implement rtio_get_destination_status (#177)
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-03-25 18:20:05 +08:00
Sebastien Bourdeauducq dc54d5f9b6 update artiq/vivado 2022-03-20 16:15:47 +08:00
Sebastien Bourdeauducq 161044e78f drop support for big-endian moninj 2022-03-19 23:01:36 +08:00
Sebastien Bourdeauducq 32f3c636c5 update artiq, work around annoying nix2.5 bug 2022-03-17 21:11:31 +08:00
Sebastien Bourdeauducq 50cafad18b update artiq 2022-03-17 20:28:12 +08:00
Sebastien Bourdeauducq 426500d2f9 firmware: support 64-bit moninj probes 2022-03-17 20:26:44 +08:00
Sebastien Bourdeauducq ebdb08180d drtio: demote default routing table message to info 2022-03-16 21:04:12 +08:00
Sebastien Bourdeauducq 0530e596ba mgmt: remove spurious config write warning 2022-03-16 08:24:52 +08:00
Sebastien Bourdeauducq 7502f3a765 update dependencies 2022-03-10 17:25:40 +08:00
Sebastien Bourdeauducq fa237088a0 hydra/nixUnstable flake.lock annoyance (2) 2022-03-10 16:51:40 +08:00
Sebastien Bourdeauducq ad557edd58 hydra/nixUnstable flakes.lock annoyance 2022-03-10 16:48:58 +08:00
Sebastien Bourdeauducq f5fa5532b6 flake: update artiq 2022-03-10 16:31:23 +08:00
pca006132 ae0d724bf8 runtime: use &CSlice for lists 2022-03-10 16:30:34 +08:00
occheung 6c834899e9 si5324: fix clock source 2022-03-09 13:55:36 +08:00
occheung a22b13cc46 kasli_soc: forward SMA clkin 2022-03-09 12:43:47 +08:00
spaqin 85e5c08d7f kasli_soc: use si5324 in master 2022-03-04 13:17:53 +08:00
spaqin 3c17362fad satman: fix i2cswitch 2022-03-03 17:18:22 +08:00
spaqin 4f2a0986da rtio_clocking: fix wrong descriptions 2022-03-03 10:24:13 +08:00
spaqin 4a2218641f fix BorrowMutError in moninj 2022-03-02 15:45:17 +08:00
mwojcik 9a06cd9d27 expose pca954x_select api (#167)
PR accompanying to ARTIQ's PCA954X support (#1860).
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-03-02 10:52:27 +08:00
Sebastien Bourdeauducq b56b50b147 add comment about EXCEPTION_ID_LOOKUP sync 2022-03-01 09:50:28 +08:00
pca006132 f38117774f runtime/eh_artiq: updated exception IDs
Fixes #166
2022-02-28 21:15:07 +08:00
Sebastien Bourdeauducq 880ba6b206 runtime: add nac3 exception symbols 2022-02-23 11:05:08 +08:00
mwojcik 90ef57f62c flake: update libasync hash 2022-02-11 13:58:04 +08:00
mwojcik accac99f48 updated zynq-rs with pca9547 support (#165)
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2022-02-11 13:53:58 +08:00
mwojcik 412ae98266 flake: add hydraJobs 2022-02-07 09:55:46 +08:00
Sebastien Bourdeauducq 8a89f2b62c flake: sync nixpkgs, update description 2022-02-05 16:33:38 +08:00
mwojcik cc5fdb64c7 flakes support 2022-02-05 16:27:25 +08:00
Sebastien Bourdeauducq 663fdcbabf update copyright year 2022-01-27 18:58:50 +08:00
Sebastien Bourdeauducq f83ab5a662 local_run: fix artiq_netbook invokation 2022-01-27 18:58:26 +08:00
pca006132 6f5ba46e89 runtime/eh_artiq: support exception allocation
The backtrace is now nested, and should be used together with the stack
pointer array to construct the full backtrace for each exception.
We now allocate exception objects in a stack, but their names are still
not allocated. This is fine for exceptions raised in the driver or artiq
code, but we will have to implement allocation for names of exceptions
raised in RPC calls. The compiler should also emit code to store the
exception names once they catch it, to prepare for later reraising.
2022-01-23 21:31:22 +08:00
pca006132 8923feceac runtime/eh_artiq: use forced unwind
This patches ports the LLVM libunwind newly added forced unwinding
function. This enables us to run forced unwinding to obtain correct
backtrace when uncaught exceptions occur.

This patch also changes the exception handling scheme from the standard
two-phase unwinding to single phase using forced unwinding. This brings
some performance improvement and prepared for later nested exception
support. For nested exceptions, we will have to record the backtrace
regardless if the exception is an uncaught exception, as there can be
another exception being thrown while executing the finally block for
caught exceptions, and we will lose the backtrace if we don't store it
earlier before running the cleanup pads.
2022-01-14 13:35:24 +08:00
pca006132 97ca72f7f1 libunwind: enable lto 2022-01-06 14:04:04 +08:00
pca006132 acaf388dbb eh_artiq: handle catch clauses appropriately 2022-01-06 13:41:47 +08:00
pca006132 8788d6458e runtime/rpc: fixes alignment and size problem 2022-01-04 18:25:53 +08:00
pca006132 efe315c21d libdyld: accepts R_ARM_ABS32
Somehow this relocation type is emitted by nac3.
According to table 4-9 of ARM ELF ABI and discussion in ld bugzilla
(https://sourceware.org/bugzilla/show_bug.cgi?id=16163), this behaves
the same as R_ARM_GLOB_DAT and R_ARM_JUMP_SLOT.
2021-12-30 00:05:47 +08:00
stevefan1999 84becfe2c0 report async errors upon kernel termination
Port of 4a6bea479a

Co-authored-by: Steve Fan <sf@m-labs.hk>
Reviewed-on: M-Labs/artiq-zynq#156
Co-authored-by: stevefan1999 <sf@m-labs.hk>
Co-committed-by: stevefan1999 <sf@m-labs.hk>
2021-12-06 17:38:55 +08:00
stevefan1999 a4fbb96296 little fixes for README (#157)
Co-authored-by: Steve Fan <sf@m-labs.hk>
Reviewed-on: M-Labs/artiq-zynq#157
Co-authored-by: stevefan1999 <sf@m-labs.hk>
Co-committed-by: stevefan1999 <sf@m-labs.hk>
2021-12-06 15:20:55 +08:00
mwojcik 64fecf09b7 restore kasli-soc satellite variant check 2021-12-03 19:20:54 +08:00
mwojcik 31fb2b388a Support for DRTIO 100MHz (#155)
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-12-03 17:19:42 +08:00
mwojcik e045837b67 zc706: not actually ultrascale 2021-11-29 12:48:45 +08:00
mwojcik ada3f2e704 drtio: reading still needs work buffer after all 2021-11-29 12:46:08 +08:00
mwojcik 8be5048cd3 upgrade to new clock configuration system (#152)
As mentioned in https://github.com/m-labs/artiq/issues/1735 - this is the Zynq version.

Reviewed-on: M-Labs/artiq-zynq#152
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-11-29 11:17:59 +08:00
mwojcik e8db2a4b49 drtio: crc from mainline, removed byte swap 2021-11-24 12:12:40 +08:00
mwojcik 4218354e65 zc706: updated device_db for tests 2021-10-16 19:01:54 +08:00
mwojcik 2376f9ab5e Merge pull request 'zc706: added dummy spi' (#149) from mwojcik/artiq-zynq:zc706_dummy_spi into master
Reviewed-on: M-Labs/artiq-zynq#149
2021-10-14 16:38:06 +08:00
mwojcik 0b27349ec4 dummy_spi -> pmod_spi 2021-10-14 16:37:13 +08:00
mwojcik 21eb1cab1a zc706: added dummy spi in place of sdio 2021-10-14 15:43:51 +08:00
mwojcik 3096daaaee zc706: removed nist_clock sdcard, put pmod instead 2021-10-14 15:01:38 +08:00
mwojcik 4fbfccf575 zc706: fix nist_qc2 extension, ams101 iostandard 2021-10-14 12:39:09 +08:00
mwojcik 5c40115945 make ZC706 RTIO channels consistent with KC705
Reviewed-on: M-Labs/artiq-zynq#147
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-13 17:20:25 +08:00
Sebastien Bourdeauducq a5e3580d18 Revert "runtime: expose rint from libm"
This reverts commit 3582af564d.
2021-10-11 08:13:26 +08:00
Sebastien Bourdeauducq 3582af564d runtime: expose rint from libm 2021-10-10 20:40:29 +08:00
mwojcik 742ce9fdde fix sd and acpki satellite builds 2021-10-08 15:18:23 +02:00
mwojcik c4de1c261a default.nix: restored proper satellite variants 2021-10-08 15:13:56 +02:00
mwojcik 219c075931 added explicit runtime/satman targets for makefile
Reviewed-on: M-Labs/artiq-zynq#144
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-08 21:06:23 +08:00
mwojcik d04a7decfe removed simple variants from zc706 2021-10-08 11:07:12 +02:00
mwojcik 0efa83e956 update build scripts for DRTIO
Reviewed-on: M-Labs/artiq-zynq#135
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-08 16:25:13 +08:00
Sebastien Bourdeauducq 4fa824f42b kasli-soc: remove irrelevant comment 2021-10-08 16:13:17 +08:00
mwojcik ab0c205dd2 gateware: add DRTIO
Reviewed-on: M-Labs/artiq-zynq#140
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-08 16:12:30 +08:00
mwojcik 8d2bb09149 add satman firmware (#136)
Reviewed-on: M-Labs/artiq-zynq#136
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-08 16:04:50 +08:00
Sebastien Bourdeauducq 41295b0e01 update cargoSha256 2021-10-06 19:43:01 +08:00
mwojcik aaec0abdf6 fix build/warnings before drtio is fully merged 2021-10-06 16:17:19 +08:00
mwojcik e241957419 add libbuild_zynq
Reviewed-on: M-Labs/artiq-zynq#141
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-06 16:16:49 +08:00
mwojcik 50262b3f0c runtime: link_thread -> link_task 2021-10-06 07:59:55 +02:00
mwojcik 827c6c1306 runtime: switch to libio/libboard_artiq, add DRTIO mastering support
Reviewed-on: M-Labs/artiq-zynq#137
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-06 13:05:45 +08:00
mwojcik e6863263b4 add libboard_artiq (to be shared between runtime and satman)
Reviewed-on: M-Labs/artiq-zynq#139
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-06 13:02:28 +08:00
mwojcik d7f45d473e add libio (to be shared between runtime and satman)
Reviewed-on: M-Labs/artiq-zynq#138
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-10-06 13:01:52 +08:00
pca006132 35250b3f56 libdyld: fixed symbol relocation
Note that in libdyld/src/lib.rs #117-118, image pointer is already added
to the symbol offset, so we do not need to add the pointer again
2021-09-25 11:30:45 +08:00
Sebastien Bourdeauducq 2ed2ffe417 update dependencies 2021-08-09 15:16:54 +08:00
Sebastien Bourdeauducq 18e05c91e1 zc706: si5324 is not needed for standalone target 2021-08-04 09:14:19 +08:00
mwojcik e3d3cb2311 si5324: bring on par with mainline ARTIQ (#132)
si5324 driver in runtime should be now equal in function to the one in artiq.

kasli-soc has no way of doing a hard reset on the peripheral, but zc706 does.

Reviewed-on: M-Labs/artiq-zynq#132
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
2021-08-04 09:12:38 +08:00
Sebastien Bourdeauducq f543501012 si5324: remove debug print 2021-08-02 14:14:59 +08:00
Sebastien Bourdeauducq 111ac0c716 runtime: clock Si5324 from its crystal 2021-07-30 17:07:58 +08:00
Sebastien Bourdeauducq 8128dc0b56 Revert "kasli-soc: work around I2C breakage (#130)"
This reverts commit f1fd55dee5.
2021-07-30 16:55:06 +08:00
Sebastien Bourdeauducq cbcda286dc Revert "README: stable ARTIQ channel"
This reverts commit 4f1689f254.
2021-07-07 18:07:49 +08:00
Sebastien Bourdeauducq dcb6129b0e update dependencies 2021-07-05 13:56:40 +08:00
Sebastien Bourdeauducq f5933092c9 nixpkgs 21.05 2021-06-29 15:07:26 +08:00
Sebastien Bourdeauducq f25e261bdd update dependencies 2021-06-25 17:12:47 +08:00
Sebastien Bourdeauducq 44c2c0fe4d support Kasli-SoC in run scripts 2021-06-25 17:03:55 +08:00
Sebastien Bourdeauducq 4c2c23fcdd shell.nix: install pyftdi (for POR GPIO) 2021-06-25 16:03:26 +08:00
Sebastien Bourdeauducq 480a80cab7 shell.nix: use kasli-soc SZL 2021-06-25 16:03:07 +08:00
Sebastien Bourdeauducq 2ba4d8935d fix compilation with nixpkgs 21.05
The environment variable is optional to keep compatibility with other build environments.

Closes #131
2021-06-25 15:57:39 +08:00
Sebastien Bourdeauducq 8c8a5d53b9 update dependencies 2021-06-19 22:51:25 +08:00
Sebastien Bourdeauducq 852123b42a kasli-soc: add RTIO LEDs 2021-05-30 20:40:53 +08:00
Sebastien Bourdeauducq f1fd55dee5 kasli-soc: work around I2C breakage (#130) 2021-05-29 17:13:41 +08:00
Sebastien Bourdeauducq 21d98711c1 use new smoltcp error code 2021-05-29 17:13:22 +08:00
Sebastien Bourdeauducq 0ae2138034 kasli-soc: preliminary si5324 support 2021-05-29 16:15:27 +08:00
Sebastien Bourdeauducq ce9d38827b update cargoSha256 2021-05-29 14:57:54 +08:00
Sebastien Bourdeauducq 1b474d2dd4 update dependencies 2021-05-29 14:20:23 +08:00
Sebastien Bourdeauducq 4f1689f254 README: stable ARTIQ channel 2021-02-17 19:33:13 +08:00
Sebastien Bourdeauducq 506c741238 support absence of gateware RTIO clock selection mux 2021-02-15 21:41:30 +08:00
Sebastien Bourdeauducq 8815f76114 kasli_soc: fix has_grabber 2021-02-15 21:41:02 +08:00
Sebastien Bourdeauducq ef18fa4c6d kasli_soc: add RTIO log channel 2021-02-15 19:56:59 +08:00
Sebastien Bourdeauducq faf9714e10 add demo build for Kasli-SoC 2021-02-15 19:52:13 +08:00
Sebastien Bourdeauducq c90cb7adad add Kasli-SoC demo JSON 2021-02-15 19:51:46 +08:00
Sebastien Bourdeauducq 8d4e42be32 remove redpitaya and coraz7 support 2021-02-15 19:30:13 +08:00
Sebastien Bourdeauducq dcd3cbc488 update copyright year 2021-02-15 19:16:22 +08:00
Astro fcb38fae6c runtime: disable TCP delayed ack 2021-02-08 03:24:18 +01:00
Astro bfd8343876 update zynq-rs and dependencies (smoltcp 0.7.0) 2021-02-08 03:24:18 +01:00
Sebastien Bourdeauducq 4039431533 kasli_soc: fix eem iostandards 2021-02-07 22:34:29 +08:00
Sebastien Bourdeauducq 3f9bd06468 add Kasli-SoC generic gateware builder (WIP) 2021-02-07 14:44:32 +08:00
68 changed files with 6218 additions and 1347 deletions

View File

@ -24,7 +24,7 @@ The following configuration keys are available:
- ``ip``: IPv4 address.
- ``ip6``: IPv6 address.
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
- ``rtioclk``: source of RTIO clock; valid values are ``external`` and ``internal``.
- ``rtio_clock``: source of RTIO clock; valid values are ``ext0_bypass`` and ``int_125``.
- ``boot``: SD card "boot.bin" file, for replacing the boot firmware/gateware. Write only.
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
@ -33,44 +33,38 @@ not implemented as it seems not very useful.
Development instructions
------------------------
Configure Nix channels:
```shell
nix-channel --add https://nixbld.m-labs.hk/channel/custom/artiq/fast-beta/artiq-fast
nix-channel --update
```
Note: if you are using Nix channels the first time, you need to be aware of this bug: https://github.com/NixOS/nix/issues/3831
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
Pure build with Nix and execution on a remote JTAG server:
```shell
nix-build -A zc706-simple-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-jtag
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc.
./remote_run.sh
```
Impure incremental build and execution on a remote JTAG server:
```shell
nix-shell
nix develop
cd src
gateware/zc706.py -g ../build/gateware # build gateware
make # build firmware
gateware/zc706.py -g ../build/gateware -V <variant> # build gateware
make GWARGS="-V <variant>" <runtime/satman> # build firmware
cd ..
./remote_run.sh -i
```
Notes:
- This is developed with Nixpkgs 20.09, and the ``nixbld.m-labs.hk`` binary substituter can also be used here (see the ARTIQ manual for the public key and instructions).
- The impure build process is also compatible with non-Nix systems.
- When calling make, you need to specify both the variant and firmware type.
- Firmware type must be either ``runtime`` for DRTIO-less or DRTIO master variants, or ``satman`` for DRTIO satellite.
- If the board is connected to the local machine, use the ``local_run.sh`` script.
- To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``zynq-rs.nix`` in sync.
- To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``flake.lock`` in sync.
License
-------
Copyright (C) 2019-2020 M-Labs Limited.
Copyright (C) 2019-2022 M-Labs Limited.
ARTIQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,146 +0,0 @@
let
zynq-rs = (import ./zynq-rs.nix);
pkgs = import <nixpkgs> { overlays = [ (import "${zynq-rs}/nix/mozilla-overlay.nix") ]; };
rustPlatform = (import "${zynq-rs}/nix/rust-platform.nix" { inherit pkgs; });
cargo-xbuild = (import zynq-rs).cargo-xbuild;
mkbootimage = import "${zynq-rs}/nix/mkbootimage.nix" { inherit pkgs; };
artiqpkgs = import <artiq-fast/default.nix> { inherit pkgs; };
vivado = import <artiq-fast/vivado.nix> { inherit pkgs; };
# FSBL configuration supplied by Vivado 2020.1 for these boards:
fsblTargets = ["zc702" "zc706" "zed"];
build = { target, variant }: let
szl = (import zynq-rs)."${target}-szl";
fsbl = import "${zynq-rs}/nix/fsbl.nix" {
inherit pkgs;
board = target;
};
firmware = rustPlatform.buildRustPackage rec {
# note: due to fetchCargoTarball, cargoSha256 depends on package name
name = "firmware";
src = ./src;
cargoSha256 = "1d84yknyizbxgsqj478339fxcyvxq9pzdv0ljrwrgmzgfynqmssj";
nativeBuildInputs = [
pkgs.gnumake
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
cargo-xbuild
pkgs.llvmPackages_9.llvm
pkgs.llvmPackages_9.clang-unwrapped
];
buildPhase = ''
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
make TARGET=${target} VARIANT=${variant}
'';
installPhase = ''
mkdir -p $out $out/nix-support
cp ../build/runtime.bin $out/runtime.bin
cp ../build/firmware/armv7-none-eabihf/release/runtime $out/runtime.elf
echo file binary-dist $out/runtime.bin >> $out/nix-support/hydra-build-products
echo file binary-dist $out/runtime.elf >> $out/nix-support/hydra-build-products
'';
doCheck = false;
dontFixup = true;
};
gateware = pkgs.runCommand "${target}-${variant}-gateware"
{
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
vivado
];
}
''
python ${./src/gateware}/${target}.py -g build -V ${variant}
mkdir -p $out $out/nix-support
cp build/top.bit $out
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
'';
# SZL startup
jtag = pkgs.runCommand "${target}-${variant}-jtag" {}
''
mkdir $out
ln -s ${szl}/szl.elf $out
ln -s ${firmware}/runtime.bin $out
ln -s ${gateware}/top.bit $out
'';
sd = pkgs.runCommand "${target}-${variant}-sd"
{
buildInputs = [ mkbootimage ];
}
''
# Do not use "long" paths in boot.bif, because embedded developers
# can't write software (mkbootimage will segfault).
bifdir=`mktemp -d`
cd $bifdir
ln -s ${szl}/szl.elf szl.elf
ln -s ${firmware}/runtime.elf runtime.elf
ln -s ${gateware}/top.bit top.bit
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]szl.elf
top.bit
runtime.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
# FSBL startup
fsbl-sd = pkgs.runCommand "${target}-${variant}-fsbl-sd"
{
buildInputs = [ mkbootimage ];
}
''
bifdir=`mktemp -d`
cd $bifdir
ln -s ${fsbl}/fsbl.elf fsbl.elf
ln -s ${gateware}/top.bit top.bit
ln -s ${firmware}/runtime.elf runtime.elf
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]fsbl.elf
top.bit
runtime.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
in {
"${target}-${variant}-firmware" = firmware;
"${target}-${variant}-gateware" = gateware;
"${target}-${variant}-jtag" = jtag;
"${target}-${variant}-sd" = sd;
} // (
if builtins.elem target fsblTargets
then {
"${target}-${variant}-fsbl-sd" = fsbl-sd;
}
else {}
);
in
(
(build { target = "zc706"; variant = "simple"; }) //
(build { target = "zc706"; variant = "nist_clock"; }) //
(build { target = "zc706"; variant = "nist_qc2"; }) //
(build { target = "zc706"; variant = "acpki_simple"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2"; }) //
(build { target = "coraz7"; variant = "10"; }) //
(build { target = "coraz7"; variant = "07s"; }) //
(build { target = "coraz7"; variant = "acpki_10"; }) //
(build { target = "coraz7"; variant = "acpki_07s"; }) //
(build { target = "redpitaya"; variant = "simple"; }) //
(build { target = "redpitaya"; variant = "acpki_simple"; }) //
{ inherit zynq-rs; }
)

60
demo.json Normal file
View File

@ -0,0 +1,60 @@
{
"target": "kasli_soc",
"variant": "demo",
"hw_rev": "v1.0",
"base": "standalone",
"peripherals": [
{
"type": "grabber",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
},
{
"type": "dio",
"ports": [2],
"bank_direction_low": "output",
"bank_direction_high": "output"
},
{
"type": "urukul",
"dds": "ad9910",
"ports": [3, 4],
"clk_sel": 2
},
{
"type": "zotino",
"ports": [5]
},
{
"type": "sampler",
"ports": [6, 7]
},
{
"type": "mirny",
"ports": [8],
"clk_sel": 1,
"refclk": 125e6
},
{
"type": "fastino",
"ports": [9]
},
{
"type": "dio",
"ports": [10],
"bank_direction_low": "input",
"bank_direction_high": "input"
},
{
"type": "dio",
"ports": [11],
"bank_direction_low": "output",
"bank_direction_high": "input"
}
]
}

View File

@ -29,30 +29,11 @@ device_db = {
"class": "PCA9548"
},
# led? are common to all variants
"led0": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 0},
},
"led1": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 1},
},
"led2": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 2}
},
"led3": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 3}
"arguments": {"channel": 41},
},
}
@ -62,7 +43,7 @@ for i in range(40):
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 4+i}
"arguments": {"channel": i}
}
device_db["ad9914dds0"] = {

219
flake.lock Normal file
View File

@ -0,0 +1,219 @@
{
"nodes": {
"artiq": {
"inputs": {
"artiq-comtools": "artiq-comtools",
"mozilla-overlay": "mozilla-overlay",
"nixpkgs": "nixpkgs",
"sipyco": "sipyco",
"src-migen": "src-migen",
"src-misoc": "src-misoc",
"src-pythonparser": "src-pythonparser"
},
"locked": {
"lastModified": 1654087576,
"narHash": "sha256-d+LCvCXikhhEjsf4qBZakx3Sda85p+4vVZVwE1YuS90=",
"ref": "master",
"rev": "68ef0073ea66fe9c7f7c178979ecb8c681c47caa",
"revCount": 8068,
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
},
"original": {
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
}
},
"artiq-comtools": {
"inputs": {
"nixpkgs": [
"artiq",
"nixpkgs"
],
"sipyco": [
"artiq",
"sipyco"
]
},
"locked": {
"lastModified": 1654007592,
"narHash": "sha256-vaDFhE1ItjqtIcinC/6RAJGbj44pxxMUEeQUa3FtgEE=",
"owner": "m-labs",
"repo": "artiq-comtools",
"rev": "cb73281154656ee8f74db1866859e31bf42755cd",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "artiq-comtools",
"type": "github"
}
},
"mozilla-overlay": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"mozilla-overlay_2": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"mozilla-overlay_3": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1653920503,
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"artiq": "artiq",
"mozilla-overlay": "mozilla-overlay_2",
"zynq-rs": "zynq-rs"
}
},
"sipyco": {
"inputs": {
"nixpkgs": [
"artiq",
"nixpkgs"
]
},
"locked": {
"lastModified": 1654006751,
"narHash": "sha256-OWAnoTCutvTQcYdtdtLQuL6uRtG+7Jz7sbRhcScv8bo=",
"owner": "m-labs",
"repo": "sipyco",
"rev": "b3d03a94c751a24769c54a61a0dbe9d6af52dade",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "sipyco",
"type": "github"
}
},
"src-migen": {
"flake": false,
"locked": {
"lastModified": 1650337393,
"narHash": "sha256-rm1SlFmF2ASz0vIy2nDEzGlyRw2oYNeJRr8Kh8Mg2Qc=",
"owner": "m-labs",
"repo": "migen",
"rev": "d4e3f34177c32f09904397179e6ed9c83175e528",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "migen",
"type": "github"
}
},
"src-misoc": {
"flake": false,
"locked": {
"lastModified": 1649324486,
"narHash": "sha256-Mw/fQS3lHFvCm7L1k63joRkz5uyijQfywcOq+X2+o2s=",
"ref": "master",
"rev": "f1dc58d2b8c222ba41c25cee4301626625f46e43",
"revCount": 2420,
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
},
"original": {
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
}
},
"src-pythonparser": {
"flake": false,
"locked": {
"lastModified": 1628745371,
"narHash": "sha256-p6TgeeaK4NEmbhimEXp31W8hVRo4DgWmcCoqZ+UdN60=",
"owner": "m-labs",
"repo": "pythonparser",
"rev": "5413ee5c9f8760e95c6acd5d6e88dabb831ad201",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "pythonparser",
"type": "github"
}
},
"zynq-rs": {
"inputs": {
"mozilla-overlay": "mozilla-overlay_3",
"nixpkgs": [
"artiq",
"nixpkgs"
]
},
"locked": {
"lastModified": 1654002148,
"narHash": "sha256-Ztzx7ze8o0gdu+sDpc+PS2M88h/b22rwFq0b12wMqZQ=",
"ref": "master",
"rev": "6cd32f6ee0e7bb7ce5a279f8be943903a393a476",
"revCount": 617,
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
},
"original": {
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
}
}
},
"root": "root",
"version": 7
}

381
flake.nix Normal file
View File

@ -0,0 +1,381 @@
{
description = "ARTIQ port to the Zynq-7000 platform";
inputs.artiq.url = git+https://github.com/m-labs/artiq.git;
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
inputs.zynq-rs.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";
outputs = { self, mozilla-overlay, zynq-rs, artiq }:
let
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
zynqpkgs = zynq-rs.packages.x86_64-linux;
artiqpkgs = artiq.packages.x86_64-linux;
rustPlatform = zynq-rs.rustPlatform;
fastnumbers = pkgs.python3Packages.buildPythonPackage rec {
pname = "fastnumbers";
version = "2.2.1";
src = pkgs.python3Packages.fetchPypi {
inherit pname version;
sha256 = "0j15i54p7nri6hkzn1wal9pxri4pgql01wgjccig6ar0v5jjbvsy";
};
};
artiq-netboot = pkgs.python3Packages.buildPythonPackage rec {
pname = "artiq-netboot";
version = "unstable-2020-10-15";
src = pkgs.fetchgit {
url = "https://git.m-labs.hk/m-labs/artiq-netboot.git";
rev = "04f69eb07df73abe4b89fde2c24084f7664f2104";
sha256 = "0ql4fr8m8gpb2yql8aqsdqsssxb8zqd6l65kl1f6s9845zy7shs9";
};
};
ramda = pkgs.python3Packages.buildPythonPackage {
pname = "ramda";
version = "unstable-2019-02-01";
src = pkgs.fetchFromGitHub {
owner = "peteut";
repo = "ramda.py";
rev = "bd58f8e69d0e9a713d9c1f286a1ac5e5603956b1";
sha256 = "0qzd5yp9lbaham8p1wiymdjapzbqsli7lvngv24c3z4ybd9jlq9g";
};
nativeBuildInputs = with pkgs.python3Packages; [ pbr ];
propagatedBuildInputs = with pkgs.python3Packages; [ future fastnumbers ];
checkInputs = with pkgs.python3Packages; [ pytest pytest-flake8 ];
checkPhase = "pytest";
doCheck = false;
preBuild = ''
export PBR_VERSION=0.0.1
'';
};
migen-axi = pkgs.python3Packages.buildPythonPackage {
pname = "migen-axi";
version = "unstable-2021-09-15";
src = pkgs.fetchFromGitHub {
owner = "peteut";
repo = "migen-axi";
rev = "9763505ee96acd7572280a2d1233721342dc7c3f";
sha256 = "15c7g05n183rka66fl1glzp6h7xjlpy1p6k8biry24dangsmxmvg";
};
nativeBuildInputs = with pkgs.python3Packages; [ pbr ];
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
postPatch = ''
substituteInPlace requirements.txt \
--replace "jinja2==2.11.3" "jinja2"
substituteInPlace requirements.txt \
--replace "future==0.18.2" "future"
substituteInPlace requirements.txt \
--replace "ramda==0.5.5" "ramda"
substituteInPlace requirements.txt \
--replace "colorama==0.4.3" "colorama"
substituteInPlace requirements.txt \
--replace "toolz==0.10.0" "toolz"
substituteInPlace requirements.txt \
--replace "pyserial==3.4" "pyserial"
substituteInPlace requirements.txt \
--replace "markupsafe==1.1.1" "markupsafe"
'';
checkInputs = with pkgs.python3Packages; [ pytest pytest-timeout pytest-flake8 ];
checkPhase = "pytest";
preBuild = ''
export PBR_VERSION=0.0.1
'';
};
binutils = { platform, target, zlib }: pkgs.stdenv.mkDerivation rec {
basename = "binutils";
version = "2.30";
name = "${basename}-${platform}-${version}";
src = pkgs.fetchurl {
url = "https://ftp.gnu.org/gnu/binutils/binutils-${version}.tar.bz2";
sha256 = "028cklfqaab24glva1ks2aqa1zxa6w6xmc8q34zs1sb7h22dxspg";
};
configureFlags =
[ "--enable-shared" "--enable-deterministic-archives" "--target=${target}"];
outputs = [ "out" "info" "man" ];
depsBuildBuild = [ pkgs.buildPackages.stdenv.cc ];
buildInputs = [ zlib ];
enableParallelBuilding = true;
};
binutils-arm = pkgs.callPackage binutils { platform = "arm"; target = "armv7-unknown-linux-gnueabihf"; };
# FSBL configuration supplied by Vivado 2020.1 for these boards:
fsblTargets = ["zc702" "zc706" "zed"];
sat_variants = [
# kasli-soc satellite variants
"satellite"
# zc706 satellite variants
"nist_clock_satellite" "nist_qc2_satellite" "acpki_nist_clock_satellite" "acpki_nist_qc2_satellite"
"nist_clock_satellite_100mhz" "nist_qc2_satellite_100mhz" "acpki_nist_clock_satellite_100mhz" "acpki_nist_qc2_satellite_100mhz"
];
build = { target, variant, json ? null }: let
szl = zynqpkgs."${target}-szl";
fsbl = zynqpkgs."${target}-fsbl";
fwtype = if builtins.elem variant sat_variants then "satman" else "runtime";
firmware = rustPlatform.buildRustPackage rec {
name = "firmware";
src = ./src;
cargoLock = {
lockFile = src/Cargo.lock;
outputHashes = {
"libasync-0.0.0" = "sha256-xuwesRrQiccopPTCkwGqQxld74X74q7EVsKIrE0zirc=";
};
};
nativeBuildInputs = [
pkgs.gnumake
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ ps.jsonschema migen migen-axi misoc artiq ])))
artiqpkgs.artiq
zynqpkgs.cargo-xbuild
pkgs.llvmPackages_9.llvm
pkgs.llvmPackages_9.clang-unwrapped
];
buildPhase = ''
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
export CLANG_EXTRA_INCLUDE_DIR="${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
make TARGET=${target} GWARGS="${if json == null then "-V ${variant}" else json}" ${fwtype}
'';
installPhase = ''
mkdir -p $out $out/nix-support
cp ../build/${fwtype}.bin $out/${fwtype}.bin
cp ../build/firmware/armv7-none-eabihf/release/${fwtype} $out/${fwtype}.elf
echo file binary-dist $out/${fwtype}.bin >> $out/nix-support/hydra-build-products
echo file binary-dist $out/${fwtype}.elf >> $out/nix-support/hydra-build-products
'';
doCheck = false;
dontFixup = true;
};
gateware = pkgs.runCommand "${target}-${variant}-gateware"
{
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ ps.jsonschema migen migen-axi misoc artiq ])))
artiqpkgs.artiq
artiqpkgs.vivado
];
}
''
python ${./src/gateware}/${target}.py -g build ${if json == null then "-V ${variant}" else json}
mkdir -p $out $out/nix-support
cp build/top.bit $out
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
'';
# SZL startup
jtag = pkgs.runCommand "${target}-${variant}-jtag" {}
''
mkdir $out
ln -s ${szl}/szl.elf $out
ln -s ${firmware}/${fwtype}.bin $out
ln -s ${gateware}/top.bit $out
'';
sd = pkgs.runCommand "${target}-${variant}-sd"
{
buildInputs = [ zynqpkgs.mkbootimage ];
}
''
# Do not use "long" paths in boot.bif, because embedded developers
# can't write software (mkbootimage will segfault).
bifdir=`mktemp -d`
cd $bifdir
ln -s ${szl}/szl.elf szl.elf
ln -s ${firmware}/${fwtype}.elf ${fwtype}.elf
ln -s ${gateware}/top.bit top.bit
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]szl.elf
top.bit
${fwtype}.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
# FSBL startup
fsbl-sd = pkgs.runCommand "${target}-${variant}-fsbl-sd"
{
buildInputs = [ zynqpkgs.mkbootimage ];
}
''
bifdir=`mktemp -d`
cd $bifdir
ln -s ${fsbl}/fsbl.elf fsbl.elf
ln -s ${gateware}/top.bit top.bit
ln -s ${firmware}/${fwtype}.elf ${fwtype}.elf
cat > boot.bif << EOF
the_ROM_image:
{
[bootloader]fsbl.elf
top.bit
${fwtype}.elf
}
EOF
mkdir $out $out/nix-support
mkbootimage boot.bif $out/boot.bin
echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products
'';
in {
"${target}-${variant}-firmware" = firmware;
"${target}-${variant}-gateware" = gateware;
"${target}-${variant}-jtag" = jtag;
"${target}-${variant}-sd" = sd;
} // (
if builtins.elem target fsblTargets
then {
"${target}-${variant}-fsbl-sd" = fsbl-sd;
}
else {}
);
gateware-sim = pkgs.stdenv.mkDerivation {
name = "gateware-sim";
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi artiq ])))
artiqpkgs.artiq
];
phases = [ "buildPhase" ];
buildPhase =
''
python -m unittest discover ${self}/src/gateware -v
touch $out
'';
};
# for hitl-tests
zc706-nist_qc2 = (build { target = "zc706"; variant = "nist_qc2"; });
zc706-hitl-tests = pkgs.stdenv.mkDerivation {
name = "zc706-hitl-tests";
__networked = true; # compatibility with old patched Nix
# breaks hydra, https://github.com/NixOS/hydra/issues/1216
#__impure = true; # Nix 2.8+
buildInputs = [
pkgs.netcat pkgs.openssh pkgs.rsync artiqpkgs.artiq artiq-netboot zynqpkgs.zc706-szl
];
phases = [ "buildPhase" ];
buildPhase =
''
export NIX_SSHOPTS="-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -i /opt/hydra_id_ed25519"
LOCKCTL=$(mktemp -d)
mkfifo $LOCKCTL/lockctl
cat $LOCKCTL/lockctl | ${pkgs.openssh}/bin/ssh \
$NIX_SSHOPTS \
rpi-4 \
'mkdir -p /tmp/board_lock && flock /tmp/board_lock/zc706-1 -c "echo Ok; cat"' \
| (
# End remote flock via FIFO
atexit_unlock() {
echo > $LOCKCTL/lockctl
}
trap atexit_unlock EXIT
# Read "Ok" line when remote successfully locked
read LOCK_OK
echo Power cycling board...
(echo b; sleep 5; echo B; sleep 5) | nc -N -w6 192.168.1.31 3131
echo Power cycle done.
export USER=hydra
export OPENOCD_ZYNQ=${zynq-rs}/openocd
export SZL=${zynqpkgs.szl}
bash ${self}/remote_run.sh -h rpi-4 -o "$NIX_SSHOPTS" -d ${zc706-nist_qc2.zc706-nist_qc2-jtag}
echo Waiting for the firmware to boot...
sleep 15
echo Running test kernel...
artiq_run --device-db ${self}/examples/device_db.py ${self}/examples/mandelbrot.py
echo Running ARTIQ unit tests...
export ARTIQ_ROOT=${self}/examples
export ARTIQ_LOW_LATENCY=1
python -m unittest discover artiq.test.coredevice -v
touch $out
echo Completed
(echo b; sleep 5) | nc -N -w6 192.168.1.31 3131
echo Board powered off
)
'';
};
in rec {
packages.x86_64-linux = (build { target = "zc706"; variant = "nist_clock"; }) //
(build { target = "zc706"; variant = "nist_clock_master"; }) //
(build { target = "zc706"; variant = "nist_clock_satellite"; }) //
(build { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "nist_qc2"; }) //
(build { target = "zc706"; variant = "nist_qc2_master"; }) //
(build { target = "zc706"; variant = "nist_qc2_satellite"; }) //
(build { target = "zc706"; variant = "nist_qc2_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_master"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_satellite"; }) //
(build { target = "zc706"; variant = "acpki_nist_clock_satellite_100mhz"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_master"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) //
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
(build { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
(build { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
(build { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; });
hydraJobs = packages.x86_64-linux // { inherit zc706-hitl-tests; inherit gateware-sim; };
devShell.x86_64-linux = pkgs.mkShell {
name = "artiq-zynq-dev-shell";
buildInputs = with pkgs; [
rustPlatform.rust.rustc
rustPlatform.rust.cargo
llvmPackages_9.llvm
llvmPackages_9.clang-unwrapped
gnumake
cacert
zynqpkgs.cargo-xbuild
zynqpkgs.mkbootimage
openocd
openssh rsync
(python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ps.jsonschema ps.pyftdi ])))
artiqpkgs.artiq
artiqpkgs.vivado
binutils-arm
];
XARGO_RUST_SRC = "${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library";
CLANG_EXTRA_INCLUDE_DIR = "${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include";
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
SZL = "${zynqpkgs.szl}";
};
makeArtiqZynqPackage = build;
};
}

60
kasli-soc-master.json Normal file
View File

@ -0,0 +1,60 @@
{
"target": "kasli_soc",
"variant": "master",
"hw_rev": "v1.0",
"base": "master",
"peripherals": [
{
"type": "grabber",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
},
{
"type": "dio",
"ports": [2],
"bank_direction_low": "output",
"bank_direction_high": "output"
},
{
"type": "urukul",
"dds": "ad9910",
"ports": [3, 4],
"clk_sel": 2
},
{
"type": "zotino",
"ports": [5]
},
{
"type": "sampler",
"ports": [6, 7]
},
{
"type": "mirny",
"ports": [8],
"clk_sel": 1,
"refclk": 125e6
},
{
"type": "fastino",
"ports": [9]
},
{
"type": "dio",
"ports": [10],
"bank_direction_low": "input",
"bank_direction_high": "input"
},
{
"type": "dio",
"ports": [11],
"bank_direction_low": "output",
"bank_direction_high": "input"
}
]
}

60
kasli-soc-satellite.json Normal file
View File

@ -0,0 +1,60 @@
{
"target": "kasli_soc",
"variant": "satellite",
"hw_rev": "v1.0",
"base": "satellite",
"peripherals": [
{
"type": "grabber",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
},
{
"type": "dio",
"ports": [2],
"bank_direction_low": "output",
"bank_direction_high": "output"
},
{
"type": "urukul",
"dds": "ad9910",
"ports": [3, 4],
"clk_sel": 2
},
{
"type": "zotino",
"ports": [5]
},
{
"type": "sampler",
"ports": [6, 7]
},
{
"type": "mirny",
"ports": [8],
"clk_sel": 1,
"refclk": 125e6
},
{
"type": "fastino",
"ports": [9]
},
{
"type": "dio",
"ports": [10],
"bank_direction_low": "input",
"bank_direction_high": "input"
},
{
"type": "dio",
"ports": [11],
"bank_direction_low": "output",
"bank_direction_high": "input"
}
]
}

View File

@ -13,9 +13,10 @@ fi
impure=0
load_bitstream=1
board_host="192.168.1.52"
board_type="kasli_soc"
fw_type="runtime"
while getopts "ilb:" opt; do
while getopts "ilb:t:f:" opt; do
case "$opt" in
\?) exit 1
;;
@ -25,24 +26,36 @@ while getopts "ilb:" opt; do
;;
b) board_host=$OPTARG
;;
t) board_type=$OPTARG
;;
f) fw_type=$OPTARG
;;
esac
done
if [ -z "$board_host" ]; then
case $board_type in
kasli_soc) board_host="192.168.1.56";;
zc706) board_host="192.168.1.52";;
*) echo "Unknown board type"; exit 1;;
esac
fi
load_bitstream_cmd=""
build_dir=`pwd`/build
result_dir=`pwd`/result
cd $OPENOCD_ZYNQ
openocd -f zc706.cfg -c "load_image $SZL; resume 0; exit"
openocd -f $board_type.cfg -c "load_image $SZL/szl-$board_type.elf; resume 0; exit"
sleep 5
if [ $impure -eq 1 ]; then
if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g $build_dir/gateware/top.bit"
fi
artiq_netboot $load_bitstream_cmd -f $build_dir/runtime.bin -b $board_host
artiq_netboot $load_bitstream_cmd -f $build_dir/$fw_type.bin -b $board_host
else
if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g $result_dir/top.bit"
fi
artiq_netboot $load_bitstream_cmd -f $result_dir/runtime.bin -b $board_host
fi
artiq_netboot $load_bitstream_cmd -f $result_dir/$fw_type.bin -b $board_host
fi

View File

@ -1,5 +1,7 @@
#!/usr/bin/env bash
# Only ZC706 supported for now.
set -e
if [ -z "$OPENOCD_ZYNQ" ]; then
@ -18,8 +20,9 @@ impure_dir="build"
sshopts=""
load_bitstream=1
board_host="192.168.1.52"
fw_type="runtime"
while getopts "h:id:o:l" opt; do
while getopts "h:id:o:lt:" opt; do
case "$opt" in
\?) exit 1
;;
@ -36,6 +39,8 @@ while getopts "h:id:o:l" opt; do
;;
b) board_host=$OPTARG
;;
t) fw_type=$OPTARG
;;
esac
done
@ -46,17 +51,17 @@ echo "Creating $target_folder..."
ssh $sshopts $target_host "mkdir -p $target_folder"
echo "Copying files..."
rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder
rsync -e "ssh $sshopts" -Lc $SZL $target_host:$target_folder
rsync -e "ssh $sshopts" -Lc $SZL/szl-zc706.elf $target_host:$target_folder/szl.elf
if [ $impure -eq 1 ]; then
if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g build/gateware/top.bit"
fi
firmware="build/runtime.bin"
firmware="build/$fw_type.bin"
else
if [ $load_bitstream -eq 1 ]; then
load_bitstream_cmd="-g $pure_dir/top.bit"
fi
firmware="$pure_dir/runtime.bin"
firmware="$pure_dir/$fw_type.bin"
fi
echo "Programming board..."
ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'"

View File

@ -1,35 +0,0 @@
let
zynq-rs = (import ./zynq-rs.nix);
pkgs = import <nixpkgs> { overlays = [ (import "${zynq-rs}/nix/mozilla-overlay.nix") ]; };
rustPlatform = (import "${zynq-rs}/nix/rust-platform.nix" { inherit pkgs; });
cargo-xbuild = (import zynq-rs).cargo-xbuild;
artiq-fast = <artiq-fast>;
artiqpkgs = import "${artiq-fast}/default.nix" { inherit pkgs; };
vivado = import "${artiq-fast}/vivado.nix" { inherit pkgs; };
zc706-szl = (import zynq-rs).zc706-szl;
in
pkgs.stdenv.mkDerivation {
name = "artiq-zynq-env";
buildInputs = [
pkgs.gnumake
rustPlatform.rust.rustc
rustPlatform.rust.cargo
pkgs.llvmPackages_9.llvm
pkgs.llvmPackages_9.clang-unwrapped
pkgs.cacert
cargo-xbuild
pkgs.openocd
pkgs.openssh pkgs.rsync
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ])))
vivado
artiqpkgs.binutils-arm
(import "${zynq-rs}/nix/mkbootimage.nix" { inherit pkgs; })
];
XARGO_RUST_SRC = "${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library";
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
SZL = "${zc706-szl}/szl.elf";
}

261
src/Cargo.lock generated
View File

@ -2,9 +2,9 @@
# It is not intended for manual editing.
[[package]]
name = "async-recursion"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5444eec77a9ec2bfe4524139e09195862e981400c4358d3b760cae634e4c4ee"
checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
dependencies = [
"proc-macro2",
"quote",
@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit_field"
@ -25,21 +25,31 @@ checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.2.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "build_const"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
[[package]]
name = "build_zynq"
version = "0.0.0"
[[package]]
name = "byteorder"
version = "1.3.4"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.66"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
@ -47,6 +57,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "compiler_builtins"
version = "0.1.39"
@ -55,10 +71,20 @@ checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
[[package]]
name = "core_io"
version = "0.1.20200410"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
version = "0.1.20210325"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97f8932064288cc79feb4d343a399d353a6f6f001e586ece47fe518a9e8507df"
dependencies = [
"memchr",
"rustc_version",
]
[[package]]
name = "crc"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
dependencies = [
"build_const",
]
[[package]]
@ -71,8 +97,9 @@ checksum = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
name = "dwarf"
version = "0.0.0"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"compiler_builtins",
"cslice",
"libc",
"unwind",
]
@ -87,9 +114,9 @@ dependencies = [
[[package]]
name = "embedded-hal"
version = "0.2.4"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
@ -97,9 +124,9 @@ dependencies = [
[[package]]
name = "fatfs"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93079df23039e52059e1f03b4c29fb0c72da2c792aad91bb2236c9fb81d3592e"
checksum = "e18f80a87439240dac45d927fd8f8081b6f1e34c03e97271189fa8a8c2e96c8f"
dependencies = [
"bitflags",
"byteorder",
@ -109,9 +136,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
@ -123,9 +150,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
@ -133,23 +160,22 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-io"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-macro"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
@ -157,36 +183,43 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
[[package]]
name = "futures-task"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
version = "0.3.8"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-core",
"futures-macro",
"futures-sink",
"futures-task",
"pin-project",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
]
[[package]]
name = "io"
version = "0.0.0"
dependencies = [
"byteorder",
"core_io",
"libsupport_zynq",
]
[[package]]
name = "libasync"
version = "0.0.0"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
dependencies = [
"embedded-hal",
"libcortex_a9",
@ -195,10 +228,30 @@ dependencies = [
"smoltcp",
]
[[package]]
name = "libboard_artiq"
version = "0.0.0"
dependencies = [
"build_zynq",
"core_io",
"crc",
"embedded-hal",
"io",
"libasync",
"libboard_zynq",
"libconfig",
"libcortex_a9",
"libregister",
"log",
"log_buffer",
"nb 1.0.0",
"void",
]
[[package]]
name = "libboard_zynq"
version = "0.0.0"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
dependencies = [
"bit_field",
"embedded-hal",
@ -223,7 +276,7 @@ dependencies = [
[[package]]
name = "libconfig"
version = "0.1.0"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
dependencies = [
"core_io",
"fatfs",
@ -234,7 +287,7 @@ dependencies = [
[[package]]
name = "libcortex_a9"
version = "0.0.0"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
dependencies = [
"bit_field",
"libregister",
@ -243,14 +296,14 @@ dependencies = [
[[package]]
name = "libm"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
[[package]]
name = "libregister"
version = "0.0.0"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
dependencies = [
"bit_field",
"vcell",
@ -260,7 +313,7 @@ dependencies = [
[[package]]
name = "libsupport_zynq"
version = "0.0.0"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#78d58d17ec7906a6cadd1678576939d20612cf8f"
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#605c8f73a60cf41a6e2d535c5850933d3154e935"
dependencies = [
"cc",
"compiler_builtins",
@ -273,17 +326,17 @@ dependencies = [
[[package]]
name = "linked_list_allocator"
version = "0.8.8"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e6766dff3bf932e0d1c7f1cf27c0a46008f7839f85b015a312c276a4570a399"
checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
[[package]]
name = "log"
version = "0.4.11"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -298,12 +351,6 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "nb"
version = "0.1.3"
@ -340,24 +387,10 @@ dependencies = [
]
[[package]]
name = "pin-project"
version = "1.0.2"
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]]
name = "pin-utils"
@ -365,32 +398,20 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.8"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
@ -406,6 +427,7 @@ name = "runtime"
version = "0.1.0"
dependencies = [
"async-recursion",
"build_zynq",
"byteorder",
"core_io",
"cslice",
@ -413,7 +435,9 @@ dependencies = [
"dyld",
"embedded-hal",
"futures",
"io",
"libasync",
"libboard_artiq",
"libboard_zynq",
"libc",
"libconfig",
@ -432,10 +456,43 @@ dependencies = [
]
[[package]]
name = "smoltcp"
version = "0.6.0"
name = "rustc_version"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
dependencies = [
"semver",
]
[[package]]
name = "satman"
version = "0.0.0"
dependencies = [
"build_zynq",
"embedded-hal",
"libasync",
"libboard_artiq",
"libboard_zynq",
"libc",
"libconfig",
"libcortex_a9",
"libregister",
"libsupport_zynq",
"log",
"unwind",
]
[[package]]
name = "semver"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
[[package]]
name = "smoltcp"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e4a069bef843d170df47e7c0a8bf8d037f217d9f5b325865acc3e466ffe40d3"
dependencies = [
"bitflags",
"byteorder",
@ -444,9 +501,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.55"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
@ -455,25 +512,25 @@ dependencies = [
[[package]]
name = "unicode-xid"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "unwind"
version = "0.0.0"
dependencies = [
"cc",
"cfg-if",
"cfg-if 0.1.10",
"compiler_builtins",
"libc",
]
[[package]]
name = "vcell"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "void"
@ -483,9 +540,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
dependencies = [
"vcell",
]

View File

@ -3,8 +3,10 @@ members = [
"libc",
"libdyld",
"libdwarf",
"libio",
"libunwind",
"runtime",
"satman"
]
[profile.release]
@ -13,6 +15,3 @@ debug = true
codegen-units = 1
opt-level = 2
lto = true
[patch.crates-io]
core_io = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }

View File

@ -1,16 +1,19 @@
TARGET := zc706
VARIANT := simple
GWARGS := -V nist_clock
all: ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
all: runtime
.PHONY: all
runtime: ../build/runtime.bin
satman: ../build/satman.bin
../build/pl.rs ../build/rustc-cfg: gateware/*
.PHONY: all runtime_target satman_target
../build/pl.rs ../build/rustc-cfg ../build/mem.rs: gateware/*
mkdir -p ../build
python gateware/$(TARGET).py -r ../build/pl.rs -c ../build/rustc-cfg -V $(VARIANT)
python gateware/$(TARGET).py -r ../build/pl.rs -c ../build/rustc-cfg -m ../build/mem.rs $(GWARGS)
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg $(shell find . -print)
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
cd runtime && \
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
cargo xbuild --release \
@ -19,3 +22,13 @@ all: ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
cd satman && \
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
cargo xbuild --release \
--target-dir ../../build/firmware \
--no-default-features --features=target_$(TARGET)
../build/satman.bin: ../build/firmware/armv7-none-eabihf/release/satman
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin

View File

@ -1,191 +0,0 @@
#!/usr/bin/env python
import argparse
from operator import itemgetter
from migen import *
from migen.build.generic_platform import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import coraz7
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
import dma
import analyzer
import acpki
class RTIOCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk):
self.clock_sel = CSRStorage()
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
rtio_external_clk = Signal()
# user_sma_clock = platform.request("user_sma_clock")
# platform.add_period_constraint(user_sma_clock.p, 8.0)
# self.specials += Instance("IBUFDS",
# i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
# o_O=rtio_external_clk)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self.clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
class CoraZ7(SoCCore):
def __init__(self, device_variant="10", acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = coraz7.Platform(device_variant=device_variant)
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
self.csr_devices.append("rtio_crg")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
def add_rtio(self, rtio_channels):
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
class Simple(CoraZ7):
def __init__(self, **kwargs):
CoraZ7.__init__(self, **kwargs)
platform = self.platform
rtio_channels = []
for i in range(2):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f:
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)):
if v is None:
f.write("{}\n".format(k))
else:
f.write("{}=\"{}\"\n".format(k, v))
def main():
parser = argparse.ArgumentParser(
description="ARTIQ port to the Cora Z7 Zynq development kit")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("-V", "--variant", default="10",
help="variant: "
"[acpki_]10/07s "
"(default: %(default)s)")
args = parser.parse_args()
variant = args.variant.lower()
acpki = variant.startswith("acpki_")
if acpki:
variant = variant[6:]
try:
soc = Simple(device_variant=variant, acpki=acpki)
except KeyError:
raise SystemExit("Invalid variant (-V/--variant)")
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,85 @@
"""Auxiliary controller, common to satellite and master"""
from artiq.gateware.drtio.aux_controller import Transmitter, Receiver
from migen.fhdl.simplify import FullMemoryWE
from misoc.interconnect.csr import *
from migen_axi.interconnect.sram import SRAM
from migen_axi.interconnect import axi
max_packet = 1024
class _DRTIOAuxControllerBase(Module):
def __init__(self, link_layer):
self.bus = axi.Interface()
self.submodules.transmitter = Transmitter(link_layer, len(self.bus.w.data))
self.submodules.receiver = Receiver(link_layer, len(self.bus.w.data))
def get_csrs(self):
return self.transmitter.get_csrs() + self.receiver.get_csrs()
# TODO: FullMemoryWE should be applied by migen.build
@FullMemoryWE()
class DRTIOAuxControllerAxi(_DRTIOAuxControllerBase):
def __init__(self, link_layer):
_DRTIOAuxControllerBase.__init__(self, link_layer)
tx_sdram_if = SRAM(self.transmitter.mem, read_only=False)
rx_sdram_if = SRAM(self.receiver.mem, read_only=True)
aw_decoder = axi.AddressDecoder(self.bus.aw,
[(lambda a: a[log2_int(max_packet)] == 0, tx_sdram_if.bus.aw),
(lambda a: a[log2_int(max_packet)] == 1, rx_sdram_if.bus.aw)],
register=True)
ar_decoder = axi.AddressDecoder(self.bus.ar,
[(lambda a: a[log2_int(max_packet)] == 0, tx_sdram_if.bus.ar),
(lambda a: a[log2_int(max_packet)] == 1, rx_sdram_if.bus.ar)],
register=True)
# unlike wb, axi address decoder only connects ar/aw lanes,
# the rest must also be connected!
# not quite unlike an address decoder itself.
# connect bus.b with tx.b
self.comb += [tx_sdram_if.bus.b.ready.eq(self.bus.b.ready),
self.bus.b.id.eq(tx_sdram_if.bus.b.id),
self.bus.b.resp.eq(tx_sdram_if.bus.b.resp),
self.bus.b.valid.eq(tx_sdram_if.bus.b.valid)]
# connect bus.w with tx.w
# no worries about w.valid and slave sel here, only tx will be written to
self.comb += [tx_sdram_if.bus.w.id.eq(self.bus.w.id),
tx_sdram_if.bus.w.data.eq(self.bus.w.data),
tx_sdram_if.bus.w.strb.eq(self.bus.w.strb),
tx_sdram_if.bus.w.last.eq(self.bus.w.last),
tx_sdram_if.bus.w.valid.eq(self.bus.w.valid),
self.bus.w.ready.eq(tx_sdram_if.bus.w.ready)]
# connect bus.r with rx.r and tx.r w/o data
self.comb += [self.bus.r.id.eq(rx_sdram_if.bus.r.id | tx_sdram_if.bus.r.id),
#self.bus.r.data.eq(rx_sdram_if.bus.r.data | tx_sdram_if.bus.r.data),
self.bus.r.resp.eq(rx_sdram_if.bus.r.resp | tx_sdram_if.bus.r.resp),
self.bus.r.last.eq(rx_sdram_if.bus.r.last | tx_sdram_if.bus.r.last),
self.bus.r.valid.eq(rx_sdram_if.bus.r.valid | tx_sdram_if.bus.r.valid),
rx_sdram_if.bus.r.ready.eq(self.bus.r.ready),
tx_sdram_if.bus.r.ready.eq(self.bus.r.ready)]
# connect read data after being masked
masked = [Replicate(rx_sdram_if.bus.r.valid,
len(self.bus.r.data)
) & rx_sdram_if.bus.r.data,
Replicate(tx_sdram_if.bus.r.valid,
len(self.bus.r.data)
) & tx_sdram_if.bus.r.data]
self.comb += self.bus.r.data.eq(reduce(or_, masked))
self.submodules += tx_sdram_if, rx_sdram_if, aw_decoder, ar_decoder
@FullMemoryWE()
class DRTIOAuxControllerBare(_DRTIOAuxControllerBase):
# Barebones version of the AuxController. No SRAM, no decoders.
# add memories manually from tx and rx in target code.
def get_tx_port(self):
return self.transmitter.mem.get_port(write_capable=True)
def get_rx_port(self):
return self.receiver.mem.get_port(write_capable=False)
def get_mem_size(self):
return max_packet

544
src/gateware/kasli_soc.py Executable file
View File

@ -0,0 +1,544 @@
#!/usr/bin/env python
import argparse
from operator import itemgetter
from migen import *
from migen.build.generic_platform import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import kasli_soc
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from artiq.coredevice import jsondesc
from artiq.gateware import rtio, eem_7series
from artiq.gateware.rtio.phy import ttl_simple
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
import dma
import analyzer
import acpki
import drtio_aux_controller
class RTIOCRG(Module, AutoCSR):
def __init__(self, platform):
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
clk_synth = platform.request("cdr_clk_clean_fabric")
clk_synth_se = Signal()
platform.add_period_constraint(clk_synth.p, 8.0)
self.specials += [
Instance("IBUFGDS",
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se),
]
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
fb_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_BANDWIDTH="HIGH",
p_REF_JITTER1=0.001,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN2=clk_synth_se,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=0,
# VCO @ 1.5GHz when using 125MHz input
p_CLKFBOUT_MULT=12, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=fb_clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=fb_clk,
p_CLKOUT0_DIVIDE=3, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
p_CLKOUT1_DIVIDE=12, p_CLKOUT1_PHASE=0.0,
o_CLKOUT1=rtio_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
eem_iostandard_dict = {
0: "LVDS_25",
1: "LVDS_25",
2: "LVDS",
3: "LVDS",
4: "LVDS",
5: "LVDS",
6: "LVDS",
7: "LVDS",
8: "LVDS_25",
9: "LVDS_25",
10: "LVDS",
11: "LVDS",
}
def eem_iostandard(eem):
return IOStandard(eem_iostandard_dict[eem])
class SMAClkinForward(Module):
def __init__(self, platform):
sma_clkin = platform.request("sma_clkin")
sma_clkin_se = Signal()
cdr_clk_se = Signal()
cdr_clk = platform.request("cdr_clk")
self.specials += [
Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
Instance("ODDR", i_C=sma_clkin_se, i_CE=1, i_D1=1, i_D2=0, o_Q=cdr_clk_se),
Instance("OBUFDS", i_I=cdr_clk_se, o_O=cdr_clk.p, o_OB=cdr_clk.n)
]
class GenericStandalone(SoCCore):
def __init__(self, description, acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules += SMAClkinForward(self.platform)
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOCRG(self.platform)
self.csr_devices.append("rtio_crg")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
for i in (0, 1):
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
user_led = self.platform.request("user_led", i)
phy = ttl_simple.Output(user_led)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
if has_grabber:
self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
for grabber in self.grabber_csr_group:
self.platform.add_false_path_constraints(
self.rtio_crg.cd_rtio.clk, getattr(self, grabber).deserializer.cd_cl.clk)
class GenericMaster(SoCCore):
def __init__(self, description, acpki=False):
sys_clk_freq = 125e6
rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules += SMAClkinForward(self.platform)
data_pads = [platform.request("sfp", i) for i in range(4)]
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("clk_gtp"),
pads=data_pads,
sys_clk_freq=sys_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
for i in (0, 1):
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
user_led = self.platform.request("user_led", i)
phy = ttl_simple.Output(user_led)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
drtioaux_memory_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
core_name = "drtio" + str(i)
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtio_csr_group.append(core_name)
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, core_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(core_name)
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
size = coreaux.get_mem_size()
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size)
self.axi2csr.register_port(coreaux.get_rx_port(), size)
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtio", drtio_csr_group)
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri] + self.drtio_cri,
enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
if has_grabber:
self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
class GenericSatellite(SoCCore):
def __init__(self, description, acpki=False):
sys_clk_freq = 125e6
rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg"] = None
data_pads = [platform.request("sfp", i) for i in range(4)]
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("clk_gtp"),
pads=data_pads,
sys_clk_freq=sys_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
for i in (0, 1):
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
user_led = self.platform.request("user_led", i)
phy = ttl_simple.Output(user_led)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
drtioaux_csr_group = []
drtioaux_memory_group = []
drtiorep_csr_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite(
self.rtio_tsc, self.drtio_transceiver.channels[i],
self.rx_synchronizer))
self.submodules.drtiosat = core
self.csr_devices.append("drtiosat")
else:
corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name)
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
mem_size = coreaux.get_mem_size()
tx_port = coreaux.get_tx_port()
rx_port = coreaux.get_rx_port()
memory_address = self.axi2csr.register_port(tx_port, mem_size)
# rcv in upper half of the memory, thus added second
self.axi2csr.register_port(rx_port, mem_size)
# and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.add_csr_group("drtiorep", drtiorep_csr_group)
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, self.rtio_channels)
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
self.csr_devices.append("rtio_moninj")
rtio_clk_period = 1e9/rtio_clk_freq
self.rustc_cfg["rtio_frequency"] = str(rtio_clk_freq/1e6)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk"),
rx_synchronizer=self.rx_synchronizer,
ultrascale=False,
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["has_siphaser"] = None
self.rustc_cfg["si5324_soft_reset"] = None
gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, gtx.rxoutclk)
if has_grabber:
self.rustc_cfg["has_grabber"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
# no RTIO CRG here
def write_mem_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_mem_rust(
soc.get_memory_regions(), soc.get_memory_groups(), None))
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f:
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)):
if v is None:
f.write("{}\n".format(k))
else:
f.write("{}=\"{}\"\n".format(k, v))
def main():
parser = argparse.ArgumentParser(
description="ARTIQ device binary builder for generic Kasli-SoC systems")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-m", default=None,
help="build Rust memory interface into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("--acpki", default=False, action="store_true",
help="enable ACPKI")
parser.add_argument("description", metavar="DESCRIPTION",
help="JSON system description file")
args = parser.parse_args()
description = jsondesc.load(args.description)
if description["target"] != "kasli_soc":
raise ValueError("Description is for a different target")
if description["base"] == "standalone":
cls = GenericStandalone
elif description["base"] == "master":
cls = GenericMaster
elif description["base"] == "satellite":
cls = GenericSatellite
else:
raise ValueError("Invalid base")
soc = cls(description, acpki=args.acpki)
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.m is not None:
write_mem_file(soc, args.m)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

View File

@ -1,191 +0,0 @@
#!/usr/bin/env python
import argparse
from operator import itemgetter
from migen import *
from migen.build.generic_platform import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import redpitaya
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
import dma
import analyzer
import acpki
class RTIOCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk):
self.clock_sel = CSRStorage()
self.pll_reset = CSRStorage(reset=1)
self.pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
rtio_external_clk = Signal()
# user_sma_clock = platform.request("user_sma_clock")
# platform.add_period_constraint(user_sma_clock.p, 8.0)
# self.specials += Instance("IBUFDS",
# i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
# o_O=rtio_external_clk)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self.clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self.pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self.pll_locked.status)
]
class Redpitaya(SoCCore):
def __init__(self, acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = redpitaya.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
self.csr_devices.append("rtio_crg")
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
def add_rtio(self, rtio_channels):
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
class Simple(Redpitaya):
def __init__(self, **kwargs):
Redpitaya.__init__(self, **kwargs)
platform = self.platform
rtio_channels = []
for i in range(2):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
VARIANTS = {cls.__name__.lower(): cls for cls in [Simple]}
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f:
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)):
if v is None:
f.write("{}\n".format(k))
else:
f.write("{}=\"{}\"\n".format(k, v))
def main():
parser = argparse.ArgumentParser(
description="ARTIQ port to the Redpitaya Zynq development kit")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("-V", "--variant", default="10",
help="variant: "
"[acpki_]simple "
"(default: %(default)s)")
args = parser.parse_args()
variant = args.variant.lower()
acpki = variant.startswith("acpki_")
if acpki:
variant = variant[6:]
soc = Simple(acpki=acpki)
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

View File

@ -11,13 +11,20 @@ from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import zc706
from misoc.interconnect.csr import *
from misoc.integration import cpu_interface
from misoc.cores import gpio
from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import *
import dma
import analyzer
import acpki
import drtio_aux_controller
class RTIOCRG(Module, AutoCSR):
@ -64,25 +71,90 @@ class RTIOCRG(Module, AutoCSR):
]
class SMAClkinForward(Module):
def __init__(self, platform):
sma_clkin = platform.request("user_sma_clock")
sma_clkin_se = Signal()
si5324_clkin_se = Signal()
si5324_clkin = platform.request("si5324_clkin")
self.specials += [
Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
Instance("ODDR", i_C=sma_clkin_se, i_CE=1, i_D1=1, i_D2=0, o_Q=si5324_clkin_se),
Instance("OBUFDS", i_I=si5324_clkin_se, o_O=si5324_clkin.p, o_OB=si5324_clkin.n)
]
# The NIST backplanes require setting VADJ to 3.3V by reprogramming the power supply.
# This also changes the I/O standard for some on-board LEDs.
leds_fmc33 = [
("user_led_33", 0, Pins("Y21"), IOStandard("LVCMOS33")),
("user_led_33", 1, Pins("G2"), IOStandard("LVCMOS15")),
("user_led_33", 2, Pins("W21"), IOStandard("LVCMOS33")),
("user_led_33", 3, Pins("A17"), IOStandard("LVCMOS15")),
]
# same deal as with LEDs - changed I/O standard.
si5324_fmc33 = [
("si5324_33", 0,
Subsignal("rst_n", Pins("W23"), IOStandard("LVCMOS33")),
Subsignal("int", Pins("AJ25"), IOStandard("LVCMOS33"))
),
]
pmod1_33 = [
("pmod1_33", 0, Pins("AJ21"), IOStandard("LVCMOS33")),
("pmod1_33", 1, Pins("AK21"), IOStandard("LVCMOS33")),
("pmod1_33", 2, Pins("AB21"), IOStandard("LVCMOS33")),
("pmod1_33", 3, Pins("AB16"), IOStandard("LVCMOS33")),
# rest removed for use with dummy spi
]
_ams101_dac = [
("ams101_dac", 0,
Subsignal("ldac", Pins("XADC:GPIO0")),
Subsignal("clk", Pins("XADC:GPIO1")),
Subsignal("mosi", Pins("XADC:GPIO2")),
Subsignal("cs_n", Pins("XADC:GPIO3")),
IOStandard("LVCMOS15")
)
]
_pmod_spi = [
("pmod_spi", 0,
# PMOD_1 4-7 pins, same bank as sfp_tx_disable or user_sma_clock
Subsignal("miso", Pins("Y20"), IOStandard("LVCMOS25")),
Subsignal("clk", Pins("AA20"), IOStandard("LVCMOS25")),
Subsignal("mosi", Pins("AC18"), IOStandard("LVCMOS25")),
Subsignal("cs_n", Pins("AC19"), IOStandard("LVCMOS25")),
IOStandard("LVCMOS25")
)
]
def prepare_zc706_platform(platform):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
class ZC706(SoCCore):
def __init__(self, acpki=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = zc706.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
prepare_zc706_platform(platform)
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg_clock_sel"] = None
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
@ -121,52 +193,303 @@ class ZC706(SoCCore):
self.csr_devices.append("rtio_analyzer")
class Simple(ZC706):
def __init__(self, **kwargs):
ZC706.__init__(self, **kwargs)
class _MasterBase(SoCCore):
def __init__(self, acpki=False, drtio100mhz=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = zc706.Platform()
prepare_zc706_platform(platform)
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_extension(si5324_fmc33)
self.sys_clk_freq = 125e6
rtio_clk_freq = 100e6 if drtio100mhz else self.sys_clk_freq
platform = self.platform
rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.comb += platform.request("sfp_tx_disable_n").eq(1)
data_pads = [
platform.request("sfp"),
platform.request("user_sma_mgt")
]
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.submodules += SMAClkinForward(self.platform)
self.add_rtio(rtio_channels)
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"),
pads=data_pads,
sys_clk_freq=self.sys_clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
drtio_csr_group = []
drtioaux_csr_group = []
drtioaux_memory_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
core_name = "drtio" + str(i)
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtio_csr_group.append(core_name)
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
core = cdr(DRTIOMaster(
self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, core_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(core_name)
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
mem_size = coreaux.get_mem_size()
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), mem_size)
self.axi2csr.register_port(coreaux.get_rx_port(), mem_size)
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtio", drtio_csr_group)
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.rustc_cfg["rtio_frequency"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n)
self.csr_devices.append("si5324_rst_n")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_as_synthesizer"] = None
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk, gtx0.txoutclk, gtx.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(self.sys_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(self.platform)
def add_rtio(self, rtio_channels):
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
self.csr_devices.append("rtio_core")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
self.csr_devices.append("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
# The NIST backplanes require setting VADJ to 3.3V by reprogramming the power supply.
# This also changes the I/O standard for some on-board LEDs.
leds_fmc33 = [
("user_led_33", 0, Pins("Y21"), IOStandard("LVCMOS33")),
("user_led_33", 1, Pins("G2"), IOStandard("LVCMOS15")),
("user_led_33", 2, Pins("W21"), IOStandard("LVCMOS33")),
("user_led_33", 3, Pins("A17"), IOStandard("LVCMOS15")),
]
class _SatelliteBase(SoCCore):
def __init__(self, acpki=False, drtio100mhz=False):
self.acpki = acpki
self.rustc_cfg = dict()
platform = zc706.Platform()
prepare_zc706_platform(platform)
ident = self.__class__.__name__
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
platform.add_extension(si5324_fmc33)
self.sys_clk_freq = 125e6
rtio_clk_freq = 100e6 if drtio100mhz else self.sys_clk_freq
platform = self.platform
# SFP
self.comb += platform.request("sfp_tx_disable_n").eq(0)
data_pads = [
platform.request("sfp"),
platform.request("user_sma_mgt")
]
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock
self.submodules.drtio_transceiver = gtx_7series.GTX(
clock_pads=platform.request("si5324_clkout"),
pads=data_pads,
sys_clk_freq=self.sys_clk_freq,
rtio_clk_freq=rtio_clk_freq)
self.csr_devices.append("drtio_transceiver")
drtioaux_csr_group = []
drtioaux_memory_group = []
drtiorep_csr_group = []
self.drtio_cri = []
for i in range(len(self.drtio_transceiver.channels)):
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
# Satellite
if i == 0:
self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer())
core = cdr(DRTIOSatellite(
self.rtio_tsc, self.drtio_transceiver.channels[0], self.rx_synchronizer))
self.submodules.drtiosat = core
self.csr_devices.append("drtiosat")
# Repeaters
else:
corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.drtio_transceiver.channels[i]))
setattr(self.submodules, corerep_name, core)
self.drtio_cri.append(core.cri)
self.csr_devices.append(corerep_name)
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
setattr(self.submodules, coreaux_name, coreaux)
self.csr_devices.append(coreaux_name)
mem_size = coreaux.get_mem_size()
tx_port = coreaux.get_tx_port()
rx_port = coreaux.get_rx_port()
memory_address = self.axi2csr.register_port(tx_port, mem_size)
# rcv in upper half of the memory, thus added second
self.axi2csr.register_port(rx_port, mem_size)
# and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None
self.rustc_cfg["has_drtio_routing"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_csr_group("drtiorep", drtiorep_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.rustc_cfg["rtio_frequency"] = str(self.drtio_transceiver.rtio_clk_freq/1e6)
# Si5324 Phaser
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"),
rx_synchronizer=self.rx_synchronizer,
ultrascale=False,
rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser")
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324_33").rst_n)
self.csr_devices.append("si5324_rst_n")
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["has_siphaser"] = None
rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq
# Constrain TX & RX timing for the first transceiver channel
# (First channel acts as master for phase alignment for all channels' TX)
gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
gtx0.txoutclk, gtx0.rxoutclk)
# Constrain RX timing for the each transceiver channel
# (Each channel performs single-lane phase alignment for RX)
for gtx in self.drtio_transceiver.gtxs[1:]:
platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints(
self.ps7.cd_sys.clk, gtx.rxoutclk)
self.submodules.rtio_crg = RTIOClockMultiplier(self.sys_clk_freq)
self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg"] = None
fix_serdes_timing_path(self.platform)
def add_rtio(self, rtio_channels):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
if self.acpki:
self.rustc_cfg["ki_impl"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o)
self.csr_devices.append("rtio")
else:
self.rustc_cfg["ki_impl"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri],
[self.local_io.cri] + self.drtio_cri,
mode="sync", enable_routing=True)
self.csr_devices.append("cri_con")
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table")
class NIST_CLOCK(ZC706):
class _NIST_CLOCK_RTIO:
"""
NIST clock hardware, with old backplane and 11 DDS channels
"""
def __init__(self, **kwargs):
ZC706.__init__(self, **kwargs)
def __init__(self):
platform = self.platform
platform.add_extension(nist_clock.fmc_adapter_io)
platform.add_extension(leds_fmc33)
platform.add_extension(pmod1_33)
platform.add_extension(_ams101_dac)
platform.add_extension(_pmod_spi)
rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led_33", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
for i in range(16):
if i % 4 == 3:
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
@ -182,16 +505,40 @@ class NIST_CLOCK(ZC706):
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# no SMA GPIO, replaced with PMOD1_0
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
ams101_dac = self.platform.request("ams101_dac", 0)
phy = ttl_simple.Output(ams101_dac.ldac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = ttl_simple.ClockGen(platform.request("la32_p"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = spi2.SPIMaster(ams101_dac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ififo_depth=4))
for i in range(3):
phy = spi2.SPIMaster(self.platform.request("spi", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ififo_depth=128))
# no SDIO on PL side, dummy SPI placeholder instead
phy = spi2.SPIMaster(platform.request("pmod_spi"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
@ -202,37 +549,51 @@ class NIST_CLOCK(ZC706):
self.add_rtio(rtio_channels)
class NIST_QC2(ZC706):
class _NIST_QC2_RTIO:
"""
NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane
and 24 DDS channels. Two backplanes are used.
"""
def __init__(self, **kwargs):
ZC706.__init__(self, **kwargs)
def __init__(self):
platform = self.platform
platform.add_extension(nist_qc2.fmc_adapter_io)
platform.add_extension(leds_fmc33)
platform.add_extension(_ams101_dac)
platform.add_extension(pmod1_33)
rtio_channels = []
for i in range(4):
phy = ttl_simple.Output(platform.request("user_led_33", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
# All TTL channels are In+Out capable
for i in range(40):
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# no SMA GPIO, replaced with PMOD1_0
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
ams101_dac = self.platform.request("ams101_dac", 0)
phy = ttl_simple.Output(ams101_dac.ldac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
# CLK0, CLK1 are for clock generators, on backplane SMP connectors
for i in range(2):
phy = ttl_simple.ClockGen(
platform.request("clkout", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = spi2.SPIMaster(ams101_dac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ififo_depth=4))
for i in range(4):
phy = spi2.SPIMaster(self.platform.request("spi", i))
@ -252,7 +613,38 @@ class NIST_QC2(ZC706):
self.add_rtio(rtio_channels)
VARIANTS = {cls.__name__.lower(): cls for cls in [Simple, NIST_CLOCK, NIST_QC2]}
class NIST_CLOCK(ZC706, _NIST_CLOCK_RTIO):
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
_NIST_CLOCK_RTIO.__init__(self)
class NIST_CLOCK_Master(_MasterBase, _NIST_CLOCK_RTIO):
def __init__(self, acpki, drtio100mhz):
_MasterBase.__init__(self, acpki, drtio100mhz)
_NIST_CLOCK_RTIO.__init__(self)
class NIST_CLOCK_Satellite(_SatelliteBase, _NIST_CLOCK_RTIO):
def __init__(self, acpki, drtio100mhz):
_SatelliteBase.__init__(self, acpki, drtio100mhz)
_NIST_CLOCK_RTIO.__init__(self)
class NIST_QC2(ZC706, _NIST_QC2_RTIO):
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
_NIST_QC2_RTIO.__init__(self)
class NIST_QC2_Master(_MasterBase, _NIST_QC2_RTIO):
def __init__(self, acpki, drtio100mhz):
_MasterBase.__init__(self, acpki, drtio100mhz)
_NIST_QC2_RTIO.__init__(self)
class NIST_QC2_Satellite(_SatelliteBase, _NIST_QC2_RTIO):
def __init__(self, acpki, drtio100mhz):
_SatelliteBase.__init__(self, acpki, drtio100mhz)
_NIST_QC2_RTIO.__init__(self)
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_CLOCK_Master, NIST_CLOCK_Satellite,
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite]}
def write_csr_file(soc, filename):
@ -260,6 +652,11 @@ def write_csr_file(soc, filename):
f.write(cpu_interface.get_csr_rust(
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
def write_mem_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_mem_rust(
soc.get_memory_regions(), soc.get_memory_groups(), None))
def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f:
@ -275,13 +672,15 @@ def main():
description="ARTIQ port to the ZC706 Zynq development kit")
parser.add_argument("-r", default=None,
help="build Rust interface into the specified file")
parser.add_argument("-m", default=None,
help="build Rust memory interface into the specified file")
parser.add_argument("-c", default=None,
help="build Rust compiler configuration into the specified file")
parser.add_argument("-g", default=None,
help="build gateware into the specified directory")
parser.add_argument("-V", "--variant", default="simple",
parser.add_argument("-V", "--variant", default="nist_clock",
help="variant: "
"[acpki_]simple/nist_clock/nist_qc2 "
"[acpki_]nist_clock/nist_qc2[_master/_satellite][_100mhz]"
"(default: %(default)s)")
args = parser.parse_args()
@ -289,21 +688,25 @@ def main():
acpki = variant.startswith("acpki_")
if acpki:
variant = variant[6:]
drtio100mhz = variant.endswith("_100mhz")
if drtio100mhz:
variant = variant[:-7]
try:
cls = VARIANTS[variant]
except KeyError:
raise SystemExit("Invalid variant (-V/--variant)")
soc = cls(acpki=acpki)
soc = cls(acpki=acpki, drtio100mhz=drtio100mhz)
soc.finalize()
if args.r is not None:
write_csr_file(soc, args.r)
if args.m is not None:
write_mem_file(soc, args.m)
if args.c is not None:
write_rustc_cfg_file(soc, args.c)
if args.g is not None:
soc.build(build_dir=args.g)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,31 @@
[package]
name = "libboard_artiq"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[lib]
name = "libboard_artiq"
[features]
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
[build-dependencies]
build_zynq = { path = "../libbuild_zynq" }
[dependencies]
log = "0.4"
log_buffer = { version = "1.2" }
crc = { version = "1.7", default-features = false }
core_io = { version = "0.1", features = ["collections"] }
embedded-hal = "0.2"
nb = "1.0"
void = { version = "1", default-features = false }
io = { path = "../libio", features = ["byteorder"] }
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"}
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn"] }
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }

View File

@ -0,0 +1,5 @@
extern crate build_zynq;
fn main() {
build_zynq::cfg();
}

View File

@ -0,0 +1,106 @@
use libconfig::Config;
#[cfg(has_drtio_routing)]
use crate::pl::csr;
use core::fmt;
use log::{warn, info};
#[cfg(has_drtio_routing)]
pub const DEST_COUNT: usize = 256;
#[cfg(not(has_drtio_routing))]
pub const DEST_COUNT: usize = 0;
pub const MAX_HOPS: usize = 32;
pub const INVALID_HOP: u8 = 0xff;
pub struct RoutingTable(pub [[u8; MAX_HOPS]; DEST_COUNT]);
impl RoutingTable {
// default routing table is for star topology with no repeaters
pub fn default_master(default_n_links: usize) -> RoutingTable {
let mut ret = RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT]);
let n_entries = default_n_links + 1; // include local RTIO
for i in 0..n_entries {
ret.0[i][0] = i as u8;
}
for i in 1..n_entries {
ret.0[i][1] = 0x00;
}
ret
}
// use this by default on satellite, as they receive
// the routing table from the master
pub fn default_empty() -> RoutingTable {
RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT])
}
}
impl fmt::Display for RoutingTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RoutingTable {{")?;
for i in 0..DEST_COUNT {
if self.0[i][0] != INVALID_HOP {
write!(f, " {}:", i)?;
for j in 0..MAX_HOPS {
if self.0[i][j] == INVALID_HOP {
break;
}
write!(f, " {}", self.0[i][j])?;
}
write!(f, ";")?;
}
}
write!(f, " }}")?;
Ok(())
}
}
pub fn config_routing_table(default_n_links: usize, cfg: &Config) -> RoutingTable {
let mut ret = RoutingTable::default_master(default_n_links);
if let Ok(data) = cfg.read("routing_table") {
if data.len() == DEST_COUNT*MAX_HOPS {
for i in 0..DEST_COUNT {
for j in 0..MAX_HOPS {
ret.0[i][j] = data[i*MAX_HOPS+j];
}
}
} else {
warn!("length of the configured routing table is incorrect, using default");
}
} else {
info!("could not read routing table from configuration, using default");
}
info!("routing table: {}", ret);
ret
}
#[cfg(has_drtio_routing)]
pub fn interconnect_enable(routing_table: &RoutingTable, rank: u8, destination: u8) {
let hop = routing_table.0[destination as usize][rank as usize];
unsafe {
csr::routing_table::destination_write(destination);
csr::routing_table::hop_write(hop);
}
}
#[cfg(has_drtio_routing)]
pub fn interconnect_disable(destination: u8) {
unsafe {
csr::routing_table::destination_write(destination);
csr::routing_table::hop_write(INVALID_HOP);
}
}
#[cfg(has_drtio_routing)]
pub fn interconnect_enable_all(routing_table: &RoutingTable, rank: u8) {
for i in 0..DEST_COUNT {
interconnect_enable(routing_table, rank, i as u8);
}
}
#[cfg(has_drtio_routing)]
pub fn interconnect_disable_all() {
for i in 0..DEST_COUNT {
interconnect_disable(i as u8);
}
}

View File

@ -0,0 +1,167 @@
use crc;
use core_io::{ErrorKind as IoErrorKind, Error as IoError};
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor};
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds};
use libcortex_a9::asm::dmb;
use crate::mem::mem::DRTIOAUX_MEM;
use crate::pl::csr::DRTIOAUX;
use crate::drtioaux_proto::Error as ProtocolError;
pub use crate::drtioaux_proto::Packet;
#[derive(Debug)]
pub enum Error {
GatewareError,
CorruptedPacket,
LinkDown,
TimedOut,
UnexpectedReply,
RoutingError,
Protocol(ProtocolError)
}
impl From<ProtocolError> for Error {
fn from(value: ProtocolError) -> Error {
Error::Protocol(value)
}
}
impl From<IoError> for Error {
fn from(value: IoError) -> Error {
Error::Protocol(ProtocolError::Io(value))
}
}
pub fn reset(linkno: u8) {
let linkno = linkno as usize;
unsafe {
// clear buffer first to limit race window with buffer overflow
// error. We assume the CPU is fast enough so that no two packets
// will be received between the buffer and the error flag are cleared.
(DRTIOAUX[linkno].aux_rx_present_write)(1);
(DRTIOAUX[linkno].aux_rx_error_write)(1);
}
}
pub fn has_rx_error(linkno: u8) -> bool {
let linkno = linkno as usize;
unsafe {
let error = (DRTIOAUX[linkno].aux_rx_error_read)() != 0;
if error {
(DRTIOAUX[linkno].aux_rx_error_write)(1)
}
error
}
}
pub fn copy_work_buffer(src: *mut u32, dst: *mut u32, len: isize) {
// AXI writes must be 4-byte aligned (drtio proto doesn't care for that),
// and AXI burst reads/writes are not implemented yet in gateware
// thus the need for a work buffer for transmitting and copying it over
unsafe {
for i in 0..(len/4) {
*dst.offset(i) = *src.offset(i);
//data memory barrier to prevent bursts
dmb();
}
}
}
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
where F: FnOnce(&[u8]) -> Result<T, Error>
{
let linkidx = linkno as usize;
unsafe {
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u32;
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
// work buffer to accomodate axi burst reads
let mut buf: [u8; 1024] = [0; 1024];
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, len as isize);
let result = f(&buf[0..len]);
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
Ok(Some(result?))
} else {
Ok(None)
}
}
}
pub fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
if has_rx_error(linkno) {
return Err(Error::GatewareError)
}
receive(linkno, |buffer| {
if buffer.len() < 8 {
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into())
}
let mut reader = Cursor::new(buffer);
let checksum_at = buffer.len() - 4;
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
reader.set_position(checksum_at);
if reader.read_u32()? != checksum {
return Err(Error::CorruptedPacket)
}
reader.set_position(0);
Ok(Packet::read_from(&mut reader)?)
})
}
pub fn recv_timeout(linkno: u8, timeout_ms: Option<u64>,
timer: GlobalTimer) -> Result<Packet, Error>
{
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10));
let limit = timer.get_time() + timeout_ms;
while timer.get_time() < limit {
match recv(linkno)? {
None => (),
Some(packet) => return Ok(packet),
}
}
Err(Error::TimedOut)
}
fn transmit<F>(linkno: u8, f: F) -> Result<(), Error>
where F: FnOnce(&mut [u8]) -> Result<usize, Error>
{
let linkno = linkno as usize;
unsafe {
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
let len = DRTIOAUX_MEM[linkno].size / 2;
// work buffer, works with unaligned mem access
let mut buf: [u8; 1024] = [0; 1024];
let len = f(&mut buf[0..len])?;
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
(DRTIOAUX[linkno].aux_tx_write)(1);
Ok(())
}
}
pub fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
transmit(linkno, |buffer| {
let mut writer = Cursor::new(buffer);
packet.write_to(&mut writer)?;
// Pad till offset 4, insert checksum there
let padding = (12 - (writer.position() % 8)) % 8;
for _ in 0..padding {
writer.write_u8(0)?;
}
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
writer.write_u32(checksum)?;
Ok(writer.position())
})
}

View File

@ -0,0 +1,138 @@
use crc;
use core_io::{ErrorKind as IoErrorKind, Error as IoError};
use void::Void;
use nb;
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds};
use libasync::{task, block_async};
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor};
use crate::mem::mem::DRTIOAUX_MEM;
use crate::pl::csr::DRTIOAUX;
use crate::drtioaux::{Error, has_rx_error, copy_work_buffer};
pub use crate::drtioaux_proto::Packet;
pub async fn reset(linkno: u8) {
let linkno = linkno as usize;
unsafe {
// clear buffer first to limit race window with buffer overflow
// error. We assume the CPU is fast enough so that no two packets
// will be received between the buffer and the error flag are cleared.
(DRTIOAUX[linkno].aux_rx_present_write)(1);
(DRTIOAUX[linkno].aux_rx_error_write)(1);
}
}
fn tx_ready(linkno: usize) -> nb::Result<(), Void> {
unsafe {
if (DRTIOAUX[linkno].aux_tx_read)() != 0 {
Err(nb::Error::WouldBlock)
}
else {
Ok(())
}
}
}
async fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
where F: FnOnce(&[u8]) -> Result<T, Error>
{
let linkidx = linkno as usize;
unsafe {
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u32;
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
// work buffer to accomodate axi burst reads
let mut buf: [u8; 1024] = [0; 1024];
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, len as isize);
let result = f(&buf[0..len]);
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
Ok(Some(result?))
} else {
Ok(None)
}
}
}
pub async fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
if has_rx_error(linkno) {
return Err(Error::GatewareError)
}
receive(linkno, |buffer| {
if buffer.len() < 8 {
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into())
}
let mut reader = Cursor::new(buffer);
let checksum_at = buffer.len() - 4;
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
reader.set_position(checksum_at);
if reader.read_u32()? != checksum {
return Err(Error::CorruptedPacket)
}
reader.set_position(0);
Ok(Packet::read_from(&mut reader)?)
}).await
}
pub async fn recv_timeout(linkno: u8, timeout_ms: Option<u64>,
timer: GlobalTimer) -> Result<Packet, Error>
{
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10));
let limit = timer.get_time() + timeout_ms;
let mut would_block = false;
while timer.get_time() < limit {
// to ensure one last time recv would run one last time
// in case async would return after timeout
if would_block {
task::r#yield().await;
}
match recv(linkno).await? {
None => { would_block = true; },
Some(packet) => return Ok(packet),
}
}
Err(Error::TimedOut)
}
async fn transmit<F>(linkno: u8, f: F) -> Result<(), Error>
where F: FnOnce(&mut [u8]) -> Result<usize, Error>
{
let linkno = linkno as usize;
unsafe {
let _ = block_async!(tx_ready(linkno)).await;
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
let len = DRTIOAUX_MEM[linkno].size / 2;
// work buffer, works with unaligned mem access
let mut buf: [u8; 1024] = [0; 1024];
let len = f(&mut buf[0..len])?;
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
(DRTIOAUX[linkno].aux_tx_write)(1);
Ok(())
}
}
pub async fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
transmit(linkno, |buffer| {
let mut writer = Cursor::new(buffer);
packet.write_to(&mut writer)?;
// Pad till offset 4, insert checksum there
let padding = (12 - (writer.position() % 8)) % 8;
for _ in 0..padding {
writer.write_u8(0)?;
}
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
writer.write_u32(checksum)?;
Ok(writer.position())
}).await
}

View File

@ -0,0 +1,353 @@
use core_io::{Write, Read, Error as IoError};
use io::proto::{ProtoWrite, ProtoRead};
#[derive(Debug)]
pub enum Error {
UnknownPacket(u8),
Io(IoError)
}
impl From<IoError> for Error {
fn from(value: IoError) -> Error {
Error::Io(value)
}
}
#[derive(PartialEq, Debug)]
pub enum Packet {
EchoRequest,
EchoReply,
ResetRequest,
ResetAck,
TSCAck,
DestinationStatusRequest { destination: u8 },
DestinationDownReply,
DestinationOkReply,
DestinationSequenceErrorReply { channel: u16 },
DestinationCollisionReply { channel: u16 },
DestinationBusyReply { channel: u16 },
RoutingSetPath { destination: u8, hops: [u8; 32] },
RoutingSetRank { rank: u8 },
RoutingAck,
MonitorRequest { destination: u8, channel: u16, probe: u8 },
MonitorReply { value: u64 },
InjectionRequest { destination: u8, channel: u16, overrd: u8, value: u8 },
InjectionStatusRequest { destination: u8, channel: u16, overrd: u8 },
InjectionStatusReply { value: u8 },
I2cStartRequest { destination: u8, busno: u8 },
I2cRestartRequest { destination: u8, busno: u8 },
I2cStopRequest { destination: u8, busno: u8 },
I2cWriteRequest { destination: u8, busno: u8, data: u8 },
I2cWriteReply { succeeded: bool, ack: bool },
I2cReadRequest { destination: u8, busno: u8, ack: bool },
I2cReadReply { succeeded: bool, data: u8 },
I2cBasicReply { succeeded: bool },
I2cSwitchSelectRequest { destination: u8, busno: u8, address: u8, mask: u8 },
SpiSetConfigRequest { destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8 },
SpiWriteRequest { destination: u8, busno: u8, data: u32 },
SpiReadRequest { destination: u8, busno: u8 },
SpiReadReply { succeeded: bool, data: u32 },
SpiBasicReply { succeeded: bool },
}
impl Packet {
pub fn read_from<R>(reader: &mut R) -> Result<Self, Error>
where R: Read + ?Sized
{
Ok(match reader.read_u8()? {
0x00 => Packet::EchoRequest,
0x01 => Packet::EchoReply,
0x02 => Packet::ResetRequest,
0x03 => Packet::ResetAck,
0x04 => Packet::TSCAck,
0x20 => Packet::DestinationStatusRequest {
destination: reader.read_u8()?
},
0x21 => Packet::DestinationDownReply,
0x22 => Packet::DestinationOkReply,
0x23 => Packet::DestinationSequenceErrorReply {
channel: reader.read_u16()?
},
0x24 => Packet::DestinationCollisionReply {
channel: reader.read_u16()?
},
0x25 => Packet::DestinationBusyReply {
channel: reader.read_u16()?
},
0x30 => {
let destination = reader.read_u8()?;
let mut hops = [0; 32];
reader.read_exact(&mut hops)?;
Packet::RoutingSetPath {
destination: destination,
hops: hops
}
},
0x31 => Packet::RoutingSetRank {
rank: reader.read_u8()?
},
0x32 => Packet::RoutingAck,
0x40 => Packet::MonitorRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
probe: reader.read_u8()?
},
0x41 => Packet::MonitorReply {
value: reader.read_u64()?
},
0x50 => Packet::InjectionRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
overrd: reader.read_u8()?,
value: reader.read_u8()?
},
0x51 => Packet::InjectionStatusRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
overrd: reader.read_u8()?
},
0x52 => Packet::InjectionStatusReply {
value: reader.read_u8()?
},
0x80 => Packet::I2cStartRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x81 => Packet::I2cRestartRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x82 => Packet::I2cStopRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x83 => Packet::I2cWriteRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
data: reader.read_u8()?
},
0x84 => Packet::I2cWriteReply {
succeeded: reader.read_bool()?,
ack: reader.read_bool()?
},
0x85 => Packet::I2cReadRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
ack: reader.read_bool()?
},
0x86 => Packet::I2cReadReply {
succeeded: reader.read_bool()?,
data: reader.read_u8()?
},
0x87 => Packet::I2cBasicReply {
succeeded: reader.read_bool()?
},
0x88 => Packet::I2cSwitchSelectRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
address: reader.read_u8()?,
mask: reader.read_u8()?
},
0x90 => Packet::SpiSetConfigRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
flags: reader.read_u8()?,
length: reader.read_u8()?,
div: reader.read_u8()?,
cs: reader.read_u8()?
},
/* 0x91: was Packet::SpiSetXferRequest */
0x92 => Packet::SpiWriteRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
data: reader.read_u32()?
},
0x93 => Packet::SpiReadRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?
},
0x94 => Packet::SpiReadReply {
succeeded: reader.read_bool()?,
data: reader.read_u32()?
},
0x95 => Packet::SpiBasicReply {
succeeded: reader.read_bool()?
},
ty => return Err(Error::UnknownPacket(ty))
})
}
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError>
where W: Write + ?Sized
{
match *self {
Packet::EchoRequest =>
writer.write_u8(0x00)?,
Packet::EchoReply =>
writer.write_u8(0x01)?,
Packet::ResetRequest =>
writer.write_u8(0x02)?,
Packet::ResetAck =>
writer.write_u8(0x03)?,
Packet::TSCAck =>
writer.write_u8(0x04)?,
Packet::DestinationStatusRequest { destination } => {
writer.write_u8(0x20)?;
writer.write_u8(destination)?;
},
Packet::DestinationDownReply =>
writer.write_u8(0x21)?,
Packet::DestinationOkReply =>
writer.write_u8(0x22)?,
Packet::DestinationSequenceErrorReply { channel } => {
writer.write_u8(0x23)?;
writer.write_u16(channel)?;
},
Packet::DestinationCollisionReply { channel } => {
writer.write_u8(0x24)?;
writer.write_u16(channel)?;
},
Packet::DestinationBusyReply { channel } => {
writer.write_u8(0x25)?;
writer.write_u16(channel)?;
},
Packet::RoutingSetPath { destination, hops } => {
writer.write_u8(0x30)?;
writer.write_u8(destination)?;
writer.write_all(&hops)?;
},
Packet::RoutingSetRank { rank } => {
writer.write_u8(0x31)?;
writer.write_u8(rank)?;
},
Packet::RoutingAck =>
writer.write_u8(0x32)?,
Packet::MonitorRequest { destination, channel, probe } => {
writer.write_u8(0x40)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u8(probe)?;
},
Packet::MonitorReply { value } => {
writer.write_u8(0x41)?;
writer.write_u64(value)?;
},
Packet::InjectionRequest { destination, channel, overrd, value } => {
writer.write_u8(0x50)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u8(overrd)?;
writer.write_u8(value)?;
},
Packet::InjectionStatusRequest { destination, channel, overrd } => {
writer.write_u8(0x51)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u8(overrd)?;
},
Packet::InjectionStatusReply { value } => {
writer.write_u8(0x52)?;
writer.write_u8(value)?;
},
Packet::I2cStartRequest { destination, busno } => {
writer.write_u8(0x80)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::I2cRestartRequest { destination, busno } => {
writer.write_u8(0x81)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::I2cStopRequest { destination, busno } => {
writer.write_u8(0x82)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::I2cWriteRequest { destination, busno, data } => {
writer.write_u8(0x83)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u8(data)?;
},
Packet::I2cWriteReply { succeeded, ack } => {
writer.write_u8(0x84)?;
writer.write_bool(succeeded)?;
writer.write_bool(ack)?;
},
Packet::I2cReadRequest { destination, busno, ack } => {
writer.write_u8(0x85)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_bool(ack)?;
},
Packet::I2cReadReply { succeeded, data } => {
writer.write_u8(0x86)?;
writer.write_bool(succeeded)?;
writer.write_u8(data)?;
},
Packet::I2cBasicReply { succeeded } => {
writer.write_u8(0x87)?;
writer.write_bool(succeeded)?;
},
Packet::I2cSwitchSelectRequest { destination, busno, address, mask } => {
writer.write_u8(0x88)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u8(address)?;
writer.write_u8(mask)?;
},
Packet::SpiSetConfigRequest { destination, busno, flags, length, div, cs } => {
writer.write_u8(0x90)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u8(flags)?;
writer.write_u8(length)?;
writer.write_u8(div)?;
writer.write_u8(cs)?;
},
Packet::SpiWriteRequest { destination, busno, data } => {
writer.write_u8(0x92)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u32(data)?;
},
Packet::SpiReadRequest { destination, busno } => {
writer.write_u8(0x93)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
},
Packet::SpiReadReply { succeeded, data } => {
writer.write_u8(0x94)?;
writer.write_bool(succeeded)?;
writer.write_u32(data)?;
},
Packet::SpiBasicReply { succeeded } => {
writer.write_u8(0x95)?;
writer.write_bool(succeeded)?;
},
}
Ok(())
}
}

View File

@ -0,0 +1,69 @@
#![no_std]
#![feature(never_type)]
extern crate log;
extern crate crc;
extern crate embedded_hal;
extern crate core_io;
extern crate io;
extern crate libboard_zynq;
extern crate libregister;
extern crate libconfig;
extern crate libcortex_a9;
extern crate libasync;
extern crate log_buffer;
#[path = "../../../build/pl.rs"]
pub mod pl;
pub mod drtioaux_proto;
pub mod drtio_routing;
pub mod logger;
#[cfg(has_si5324)]
pub mod si5324;
#[cfg(has_drtio)]
pub mod drtioaux;
#[cfg(has_drtio)]
pub mod drtioaux_async;
#[cfg(has_drtio)]
#[path = "../../../build/mem.rs"]
pub mod mem;
use core::{cmp, str};
use libboard_zynq::slcr;
use libregister::RegisterW;
pub fn identifier_read(buf: &mut [u8]) -> &str {
unsafe {
pl::csr::identifier::address_write(0);
let len = pl::csr::identifier::data_read();
let len = cmp::min(len, buf.len() as u8);
for i in 0..len {
pl::csr::identifier::address_write(1 + i);
buf[i as usize] = pl::csr::identifier::data_read();
}
str::from_utf8_unchecked(&buf[..len as usize])
}
}
pub fn init_gateware() {
// Set up PS->PL clocks
slcr::RegisterBlock::unlocked(|slcr| {
// As we are touching the mux, the clock may glitch, so reset the PL.
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
.fpga0_out_rst(true)
.fpga1_out_rst(true)
.fpga2_out_rst(true)
.fpga3_out_rst(true)
);
slcr.fpga0_clk_ctrl.write(
slcr::Fpga0ClkCtrl::zeroed()
.src_sel(slcr::PllSource::IoPll)
.divisor0(8)
.divisor1(1)
);
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
);
});
}

View File

@ -0,0 +1,353 @@
use core::result;
use log::info;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds};
use embedded_hal::blocking::delay::DelayUs;
#[cfg(not(si5324_soft_reset))]
use crate::pl::csr;
type Result<T> = result::Result<T, &'static str>;
const ADDRESS: u8 = 0x68;
#[cfg(not(si5324_soft_reset))]
fn hard_reset(timer: &mut GlobalTimer) {
unsafe { csr::si5324_rst_n::out_write(0); }
timer.delay_us(1_000);
unsafe { csr::si5324_rst_n::out_write(1); }
timer.delay_us(10_000);
}
// NOTE: the logical parameters DO NOT MAP to physical values written
// into registers. They have to be mapped; see the datasheet.
// DSPLLsim reports the logical parameters in the design summary, not
// the physical register values.
pub struct FrequencySettings {
pub n1_hs: u8,
pub nc1_ls: u32,
pub n2_hs: u8,
pub n2_ls: u32,
pub n31: u32,
pub n32: u32,
pub bwsel: u8,
pub crystal_ref: bool
}
pub enum Input {
Ckin1,
Ckin2,
}
fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings> {
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 {
return Err("NC1_LS must be 0 or even")
}
if settings.nc1_ls > (1 << 20) {
return Err("NC1_LS is too high")
}
if (settings.n2_ls % 2) == 1 {
return Err("N2_LS must be even")
}
if settings.n2_ls > (1 << 20) {
return Err("N2_LS is too high")
}
if settings.n31 > (1 << 19) {
return Err("N31 is too high")
}
if settings.n32 > (1 << 19) {
return Err("N32 is too high")
}
let r = FrequencySettings {
n1_hs: match settings.n1_hs {
4 => 0b000,
5 => 0b001,
6 => 0b010,
7 => 0b011,
8 => 0b100,
9 => 0b101,
10 => 0b110,
11 => 0b111,
_ => return Err("N1_HS has an invalid value")
},
nc1_ls: settings.nc1_ls - 1,
n2_hs: match settings.n2_hs {
4 => 0b000,
5 => 0b001,
6 => 0b010,
7 => 0b011,
8 => 0b100,
9 => 0b101,
10 => 0b110,
11 => 0b111,
_ => return Err("N2_HS has an invalid value")
},
n2_ls: settings.n2_ls - 1,
n31: settings.n31 - 1,
n32: settings.n32 - 1,
bwsel: settings.bwsel,
crystal_ref: settings.crystal_ref
};
Ok(r)
}
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address")
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register")
}
if !i2c.write(val).unwrap() {
return Err("Si5324 failed to ack value")
}
i2c.stop().unwrap();
Ok(())
}
#[allow(dead_code)]
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address")
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register")
}
i2c.write(val).unwrap();
i2c.stop().unwrap();
Ok(())
}
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address")
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register")
}
i2c.restart().unwrap();
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
return Err("Si5324 failed to ack read address")
}
let val = i2c.read(false).unwrap();
i2c.stop().unwrap();
Ok(val)
}
fn rmw<F>(i2c: &mut I2c, reg: u8, f: F) -> Result<()> where
F: Fn(u8) -> u8 {
let value = read(i2c, reg)?;
write(i2c, reg, f(value))?;
Ok(())
}
fn ident(i2c: &mut I2c) -> Result<u16> {
Ok(((read(i2c, 134)? as u16) << 8) | (read(i2c, 135)? as u16))
}
#[cfg(si5324_soft_reset)]
fn soft_reset(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
let val = read(i2c, 136)?;
write_no_ack_value(i2c, 136, val | 0x80)?;
timer.delay_us(10_000);
Ok(())
}
fn has_xtal(i2c: &mut I2c) -> Result<bool> {
Ok((read(i2c, 129)? & 0x01) == 0) // LOSX_INT=0
}
fn has_ckin(i2c: &mut I2c, input: Input) -> Result<bool> {
match input {
Input::Ckin1 => Ok((read(i2c, 129)? & 0x02) == 0), // LOS1_INT=0
Input::Ckin2 => Ok((read(i2c, 129)? & 0x04) == 0), // LOS2_INT=0
}
}
fn locked(i2c: &mut I2c) -> Result<bool> {
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
}
fn monitor_lock(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
info!("waiting for Si5324 lock...");
let timeout = timer.get_time() + Milliseconds(20_000);
while !locked(i2c)? {
// Yes, lock can be really slow.
if timer.get_time() > timeout {
return Err("Si5324 lock timeout");
}
}
info!(" ...locked");
Ok(())
}
fn init(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
#[cfg(not(si5324_soft_reset))]
hard_reset(timer);
#[cfg(feature = "target_kasli_soc")]
{
i2c.pca954x_select(0x70, None)?;
i2c.pca954x_select(0x71, Some(3))?;
}
#[cfg(feature = "target_zc706")]
{
i2c.pca954x_select(0x74, Some(4))?;
}
if ident(i2c)? != 0x0182 {
return Err("Si5324 does not have expected product number");
}
#[cfg(si5324_soft_reset)]
soft_reset(i2c, timer)?;
Ok(())
}
pub fn bypass(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
let cksel_reg = match input {
Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01,
};
init(i2c, timer)?;
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; // CKSEL_REG
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
rmw(i2c, 0, |v| (v & 0xfd) | 0x02)?; // BYPASS_REG=1
Ok(())
}
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: &mut GlobalTimer) -> Result<()> {
let s = map_frequency_settings(settings)?;
let cksel_reg = match input {
Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01,
};
init(i2c, timer)?;
if settings.crystal_ref {
rmw(i2c, 0, |v| v | 0x40)?; // FREE_RUN=1
}
rmw(i2c, 2, |v| (v & 0x0f) | (s.bwsel << 4))?;
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
rmw(i2c, 3, |v| (v & 0x2f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
write(i2c, 25, (s.n1_hs << 5 ) as u8)?;
write(i2c, 31, (s.nc1_ls >> 16) as u8)?;
write(i2c, 32, (s.nc1_ls >> 8 ) as u8)?;
write(i2c, 33, (s.nc1_ls) as u8)?;
write(i2c, 34, (s.nc1_ls >> 16) as u8)?; // write to NC2_LS as well
write(i2c, 35, (s.nc1_ls >> 8 ) as u8)?;
write(i2c, 36, (s.nc1_ls) as u8)?;
write(i2c, 40, (s.n2_hs << 5 ) as u8 | (s.n2_ls >> 16) as u8)?;
write(i2c, 41, (s.n2_ls >> 8 ) as u8)?;
write(i2c, 42, (s.n2_ls) as u8)?;
write(i2c, 43, (s.n31 >> 16) as u8)?;
write(i2c, 44, (s.n31 >> 8) as u8)?;
write(i2c, 45, (s.n31) as u8)?;
write(i2c, 46, (s.n32 >> 16) as u8)?;
write(i2c, 47, (s.n32 >> 8) as u8)?;
write(i2c, 48, (s.n32) as u8)?;
rmw(i2c, 137, |v| v | 0x01)?; // FASTLOCK=1
rmw(i2c, 136, |v| v | 0x40)?; // ICAL=1
if !has_xtal(i2c)? {
return Err("Si5324 misses XA/XB signal");
}
if !has_ckin(i2c, input)? {
return Err("Si5324 misses clock input signal");
}
monitor_lock(i2c, timer)?;
Ok(())
}
pub fn select_input(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
let cksel_reg = match input {
Input::Ckin1 => 0b00,
Input::Ckin2 => 0b01,
};
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?;
if !has_ckin(i2c, input)? {
return Err("Si5324 misses clock input signal");
}
monitor_lock(i2c, timer)?;
Ok(())
}
#[cfg(has_siphaser)]
pub mod siphaser {
use super::*;
use crate::pl::csr;
pub fn select_recovered_clock(i2c: &mut I2c, rc: bool, timer: &mut GlobalTimer) -> Result<()> {
let val = read(i2c, 3)?;
write(i2c, 3, (val & 0xdf) | (1 << 5))?; // DHOLD=1
unsafe {
csr::siphaser::switch_clocks_write(if rc { 1 } else { 0 });
}
let val = read(i2c, 3)?;
write(i2c, 3, (val & 0xdf) | (0 << 5))?; // DHOLD=0
monitor_lock(i2c, timer)?;
Ok(())
}
fn phase_shift(direction: u8, timer: &mut GlobalTimer) {
unsafe {
csr::siphaser::phase_shift_write(direction);
while csr::siphaser::phase_shift_done_read() == 0 {}
}
// wait for the Si5324 loop to stabilize
timer.delay_us(500);
}
fn has_error(timer: &mut GlobalTimer) -> bool {
unsafe {
csr::siphaser::error_write(1);
}
timer.delay_us(5_000);
unsafe {
csr::siphaser::error_read() != 0
}
}
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32> {
let mut nshifts = 0;
let mut previous = has_error(timer);
loop {
phase_shift(1, timer);
nshifts += 1;
let current = has_error(timer);
if previous != target && current == target {
return Ok(nshifts);
}
if nshifts > 5000 {
return Err("failed to find timing error edge");
}
previous = current;
}
}
pub fn calibrate_skew(timer: &mut GlobalTimer) -> Result<()> {
let jitter_margin = 32;
let lead = find_edge(false, timer)?;
for _ in 0..jitter_margin {
phase_shift(1, timer);
}
let width = find_edge(true, timer)? + jitter_margin;
// width is 360 degrees (one full rotation of the phase between s/h limits) minus jitter
info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8));
// Apply reverse phase shift for half the width to get into the
// middle of the working region.
for _ in 0..width/2 {
phase_shift(0, timer);
}
Ok(())
}
}

View File

@ -0,0 +1,8 @@
[package]
authors = ["M-Labs"]
name = "build_zynq"
version = "0.0.0"
[lib]
name = "build_zynq"
path = "lib.rs"

30
src/libbuild_zynq/lib.rs Normal file
View File

@ -0,0 +1,30 @@
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
pub fn add_linker_script() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.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");
}
pub fn cfg() {
// Handle rustc-cfg file
let cfg_path = "../../build/rustc-cfg";
println!("cargo:rerun-if-changed={}", cfg_path);
let f = BufReader::new(File::open(cfg_path).unwrap());
for line in f.lines() {
println!("cargo:rustc-cfg={}", line.unwrap());
}
}

View File

@ -5,6 +5,8 @@ fn main() {
mod libc {
use std::path::Path;
use std::env;
pub fn compile() {
let cfg = &mut cc::Build::new();
cfg.no_default_flags(true);
@ -16,6 +18,9 @@ mod libc {
cfg.flag("-ffreestanding");
cfg.flag("-fno-PIC");
cfg.flag("-isystem../include");
if let Ok(extra_include) = env::var("CLANG_EXTRA_INCLUDE_DIR") {
cfg.flag(&("-isystem".to_owned() + &extra_include));
}
cfg.flag("-fno-stack-protector");
cfg.flag("--target=armv7-none-eabihf");
cfg.flag("-O2");

View File

@ -15,3 +15,4 @@ libc = { path = "../libc" }
unwind = { path = "../libunwind" }
compiler_builtins = "0.1.0"
cfg-if = "0.1.8"
cslice = "0.3"

View File

@ -13,6 +13,7 @@
use crate::DwarfReader;
use core::mem;
use cslice::CSlice;
pub const DW_EH_PE_omit: u8 = 0xFF;
pub const DW_EH_PE_absptr: u8 = 0x00;
@ -51,10 +52,48 @@ pub enum EHAction {
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
fn size_of_encoded_value(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
0
} else {
let encoding = encoding & 0x07;
match encoding {
DW_EH_PE_absptr => core::mem::size_of::<*const ()>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
_ => unreachable!(),
}
}
}
unsafe fn get_ttype_entry(
offset: usize,
encoding: u8,
ttype_base: usize,
ttype: *const u8,
) -> Result<Option<*const u8>, ()> {
let i = (offset * size_of_encoded_value(encoding)) as isize;
read_encoded_pointer_with_base(
&mut DwarfReader::new(ttype.offset(-i)),
// the DW_EH_PE_pcrel is a hack.
// It seems that the default encoding is absolute, but we have to take reallocation into
// account. Unsure if we can fix this in the compiler setting or if this would be affected
// by updating the compiler
encoding | DW_EH_PE_pcrel,
ttype_base,
)
.map(|v| match v {
ttype_base => None,
ttype_entry => Some(ttype_entry as *const u8),
})
}
pub unsafe fn find_eh_action(
lsda: *const u8,
context: &EHContext<'_>,
foreign_exception: bool,
id: u32,
) -> Result<EHAction, ()> {
if lsda.is_null() {
return Ok(EHAction::None);
@ -72,10 +111,17 @@ pub unsafe fn find_eh_action(
};
let ttype_encoding = reader.read::<u8>();
if ttype_encoding != DW_EH_PE_omit {
// Rust doesn't analyze exception types, so we don't care about the type table
reader.read_uleb128();
}
// we do care about the type table
let ttype_offset = if ttype_encoding != DW_EH_PE_omit {
reader.read_uleb128()
} else {
0
};
// for rust functions, it seems that there is no type table, so I just put whatever value here.
// we should not return an error, otherwise we would abort unwinding and cannot unwind through
// rust functions
let ttype_base = get_base(ttype_encoding, context).unwrap_or(1);
let ttype_table = reader.ptr.offset(ttype_offset as isize);
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
@ -94,11 +140,53 @@ pub unsafe fn find_eh_action(
break;
}
if ip < func_start + cs_start + cs_len {
// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/eh_personality.cc#L528
let lpad = lpad_base + cs_lpad;
if cs_lpad == 0 {
// no cleanups/handler
return Ok(EHAction::None);
} else if cs_action == 0 {
return Ok(EHAction::Cleanup(lpad));
} else if foreign_exception {
return Ok(EHAction::None);
} else {
let lpad = lpad_base + cs_lpad;
return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
let mut saw_cleanup = false;
let mut action_record = action_table.offset(cs_action as isize - 1);
loop {
let mut reader = DwarfReader::new(action_record);
let ar_filter = reader.read_sleb128();
action_record = reader.ptr;
let ar_disp = reader.read_sleb128();
if ar_filter == 0 {
saw_cleanup = true;
} else if ar_filter > 0 {
let catch_type = get_ttype_entry(
ar_filter as usize,
ttype_encoding,
ttype_base,
ttype_table,
)?;
match catch_type {
Some(clause_ptr) if *(clause_ptr as *const u32) == id => {
return Ok(EHAction::Catch(lpad))
}
None => return Ok(EHAction::Catch(lpad)),
_ => {}
}
} else if ar_filter < 0 {
// FIXME: how to handle this?
break;
}
if ar_disp == 0 {
break;
}
action_record = action_record.offset((ar_disp as usize) as isize);
}
if saw_cleanup {
return Ok(EHAction::Cleanup(lpad));
} else {
return Ok(EHAction::None);
}
}
}
}
@ -106,7 +194,7 @@ pub unsafe fn find_eh_action(
// So rather than returning EHAction::Terminate, we do this.
Ok(EHAction::None)
} else {
// SjLj version:
// SjLj version: (not yet modified)
// The "IP" is an index into the call-site table, with two exceptions:
// -1 means 'no-action', and 0 means 'terminate'.
match ip as isize {
@ -146,18 +234,41 @@ fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) ->
#[inline]
fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
if align.is_power_of_two() {
Ok((unrounded + align - 1) & !(align - 1))
} else {
Err(())
}
}
fn get_base(encoding: u8, context: &EHContext<'_>) -> Result<usize, ()> {
match encoding & 0x70 {
DW_EH_PE_absptr | DW_EH_PE_pcrel | DW_EH_PE_aligned => Ok(0),
DW_EH_PE_textrel => Ok((*context.get_text_start)()),
DW_EH_PE_datarel => Ok((*context.get_data_start)()),
DW_EH_PE_funcrel if context.func_start != 0 => Ok(context.func_start),
_ => return Err(()),
}
}
unsafe fn read_encoded_pointer(
reader: &mut DwarfReader,
context: &EHContext<'_>,
encoding: u8,
) -> Result<usize, ()> {
read_encoded_pointer_with_base(reader, encoding, get_base(encoding, context)?)
}
unsafe fn read_encoded_pointer_with_base(
reader: &mut DwarfReader,
encoding: u8,
base: usize,
) -> Result<usize, ()> {
if encoding == DW_EH_PE_omit {
return Err(());
}
let original_ptr = reader.ptr;
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
reader.ptr = round_up(reader.ptr as usize, mem::size_of::<usize>())? as *const u8;
@ -177,19 +288,10 @@ unsafe fn read_encoded_pointer(
_ => return Err(()),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => reader.ptr as usize,
DW_EH_PE_funcrel => {
if context.func_start == 0 {
return Err(());
}
context.func_start
}
DW_EH_PE_textrel => (*context.get_text_start)(),
DW_EH_PE_datarel => (*context.get_data_start)(),
_ => return Err(()),
result += if (encoding & 0x70) == DW_EH_PE_pcrel {
original_ptr as usize
} else {
base
};
if encoding & DW_EH_PE_indirect != 0 {

View File

@ -26,6 +26,10 @@ impl DwarfReader {
DwarfReader { ptr }
}
pub unsafe fn offset(&mut self, offset: isize) {
self.ptr = self.ptr.offset(offset);
}
// DWARF streams are packed, so e.g., a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are

View File

@ -2726,6 +2726,9 @@ impl Clone for Elf64_Lib {
fn clone(&self) -> Self { *self }
}
pub type Elf32_Conflict = Elf32_Addr;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EXIDX_Entry(u32, u32);
pub fn ELF32_R_SYM(info: Elf32_Word) -> Elf32_Word { info >> 8 }
pub fn ELF32_R_TYPE(info: Elf32_Word) -> u8 { info as u8 }

View File

@ -138,7 +138,7 @@ impl Library {
reloc::rebind(self.arch, self, name, addr as Elf32_Word)
}
pub fn exidx(&self) -> &[u32] {
pub fn exidx(&self) -> &[EXIDX_Entry] {
self.image.get_ref_slice_unchecked(&self.exidx)
}
}

View File

@ -59,7 +59,8 @@ impl Relocatable for Elf32_Rela {
enum RelType {
None,
Relative,
Lookup,
LookupAbs,
LookupRel,
}
impl RelType {
@ -76,9 +77,11 @@ impl RelType {
Some(RelType::Relative),
R_OR1K_32 | R_OR1K_GLOB_DAT | R_OR1K_JMP_SLOT
if arch == Arch::OpenRisc => Some(RelType::Lookup),
R_ARM_GLOB_DAT | R_ARM_JUMP_SLOT
if arch == Arch::Arm => Some(RelType::Lookup),
if arch == Arch::OpenRisc => Some(RelType::LookupAbs),
R_ARM_GLOB_DAT | R_ARM_JUMP_SLOT | R_ARM_ABS32
if arch == Arch::Arm => Some(RelType::LookupAbs),
R_ARM_PREL31 if arch == Arch::Arm => Some(RelType::LookupRel),
_ =>
None
@ -106,58 +109,86 @@ pub fn relocate<R: Relocatable>(
let rel_type = RelType::new(arch, rel.type_info())
.ok_or("unsupported relocation type")?;
let value;
match rel_type {
let value = match rel_type {
RelType::None =>
return Ok(()),
RelType::Relative => {
let addend = rel.addend(&lib.image);
value = lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word;
lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word
}
RelType::Lookup => {
RelType::LookupAbs | RelType::LookupRel => {
let sym = sym.ok_or("relocation requires an associated symbol")?;
let sym_name = lib.name_starting_at(sym.st_name as usize)?;
if let Some(addr) = lib.lookup(sym_name) {
let sym_addr = if let Some(addr) = lib.lookup(sym_name) {
// First, try to resolve against itself.
trace!("looked up symbol {} in image", format_sym_name(sym_name));
value = lib.image.ptr() as u32 + addr;
addr
} else if let Some(addr) = resolve(sym_name) {
// Second, call the user-provided function.
trace!("resolved symbol {:?}", format_sym_name(sym_name));
value = addr;
addr
} else {
// We couldn't find it anywhere.
return Err(Error::Lookup(format_sym_name(sym_name)))
};
match rel_type {
RelType::LookupAbs => sym_addr,
RelType::LookupRel =>
sym_addr.wrapping_sub(
lib.image.ptr().wrapping_offset(rel.offset() as isize) as Elf32_Addr),
_ => unreachable!()
}
}
}
};
lib.image.write(rel.offset(), value)
match rel.type_info() {
R_ARM_PREL31 => {
let reloc_word = lib.image.get_ref::<Elf32_Word>(rel.offset())
.ok_or("relocation offset cannot be read")?;
lib.image.write(rel.offset(), (reloc_word & 0x80000000) | (value & 0x7FFFFFFF))
},
_ => lib.image.write(rel.offset(), value),
}
}
pub fn rebind(
arch: Arch, lib: &Library, name: &[u8], value: Elf32_Word
) -> Result<(), Error> {
for rela in lib.pltrel() {
let rel_type = RelType::new(arch, rela.type_info())
.ok_or("unsupported relocation type")?;
match rel_type {
RelType::Lookup => {
let sym = lib.symtab().get(ELF32_R_SYM(rela.r_info) as usize)
.ok_or("symbol out of bounds of symbol table")?;
let sym_name = lib.name_starting_at(sym.st_name as usize)?;
fn rebind_symbol_to_value<R: Relocatable>(
arch: Arch, lib: &Library,name: &[u8], value: Elf32_Word, relocs: &[R]
) -> Result<(), Error> {
for reloc in relocs {
let rel_type = RelType::new(arch, reloc.type_info())
.ok_or("unsupported relocation type")?;
match rel_type {
RelType::LookupAbs => {
let sym = lib.symtab().get(reloc.sym_info() as usize)
.ok_or("symbol out of bounds of symbol table")?;
let sym_name = lib.name_starting_at(sym.st_name as usize)?;
if sym_name == name {
lib.image.write(rela.offset(), value)?
if sym_name == name {
lib.image.write(reloc.offset(), value)?
}
}
// No associated symbols for other relocation types.
_ => {}
}
// No associated symbols for other relocation types.
_ => {}
}
Ok(())
}
if lib.pltrel().is_empty() {
rebind_symbol_to_value(arch, lib, name, value, lib.rela())?;
} else {
rebind_symbol_to_value(arch, lib, name, value, lib.pltrel())?;
}
// FIXME: the cache maintainance operations may be more than enough,
// may cause performance degradation.
dcci_slice(lib.image.data);

17
src/libio/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
authors = ["M-Labs"]
name = "io"
version = "0.0.0"
[lib]
name = "io"
path = "lib.rs"
[dependencies]
core_io = { version = "0.1", features = ["collections"] }
byteorder = { version = "1.0", default-features = false, optional = true }
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
[features]
alloc = []

81
src/libio/cursor.rs Normal file
View File

@ -0,0 +1,81 @@
use core_io::{Read, Write, Error as IoError};
#[derive(Debug, Clone)]
pub struct Cursor<T> {
inner: T,
pos: usize
}
impl<T> Cursor<T> {
#[inline]
pub fn new(inner: T) -> Cursor<T> {
Cursor { inner, pos: 0 }
}
#[inline]
pub fn into_inner(self) -> T {
self.inner
}
#[inline]
pub fn get_ref(&self) -> &T {
&self.inner
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
#[inline]
pub fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn set_position(&mut self, pos: usize) {
self.pos = pos
}
}
impl<T: AsRef<[u8]>> Read for Cursor<T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
let data = &self.inner.as_ref()[self.pos..];
let len = buf.len().min(data.len());
buf[..len].copy_from_slice(&data[..len]);
self.pos += len;
Ok(len)
}
}
impl Write for Cursor<&mut [u8]> {
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
let data = &mut self.inner[self.pos..];
let len = buf.len().min(data.len());
data[..len].copy_from_slice(&buf[..len]);
self.pos += len;
Ok(len)
}
#[inline]
fn flush(&mut self) -> Result<(), IoError> {
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Write for Cursor<::alloc::Vec<u8>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
self.inner.extend_from_slice(buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> Result<(), IoError> {
Ok(())
}
}

22
src/libio/lib.rs Normal file
View File

@ -0,0 +1,22 @@
#![no_std]
#![feature(never_type)]
#![cfg_attr(feature = "alloc", feature(alloc))]
extern crate alloc;
extern crate core_io;
#[cfg(feature = "alloc")]
#[macro_use]
use alloc;
#[cfg(feature = "byteorder")]
extern crate byteorder;
pub mod cursor;
#[cfg(feature = "byteorder")]
pub mod proto;
pub use cursor::Cursor;
#[cfg(feature = "byteorder")]
pub use proto::{ProtoRead, ProtoWrite};
#[cfg(all(feature = "byteorder", feature = "alloc"))]
pub use proto::ReadStringError;

View File

@ -6,6 +6,7 @@ fn main() {
mod llvm_libunwind {
use std::path::Path;
use std::env;
fn setup_options(cfg: &mut cc::Build) {
cfg.no_default_flags(true);
@ -16,9 +17,13 @@ mod llvm_libunwind {
cfg.flag("-fno-PIC");
cfg.flag("-Isrc");
cfg.flag("-isystem../include");
if let Ok(extra_include) = env::var("CLANG_EXTRA_INCLUDE_DIR") {
cfg.flag(&("-isystem".to_owned() + &extra_include));
}
cfg.flag("-fno-stack-protector");
cfg.flag("--target=armv7-none-eabihf");
cfg.flag("-O2");
cfg.flag("-flto");
cfg.flag("-std=c99");
cfg.flag("-fstrict-aliasing");

View File

@ -107,9 +107,12 @@ struct _Unwind_Control_Block {
} __attribute__((__aligned__(8)));
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
(_Unwind_State state,
_Unwind_Exception* exceptionObject,
struct _Unwind_Context* context);
(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
_Unwind_Exception* exceptionObject,
struct _Unwind_Context* context,
void* stop_parameter);
typedef _Unwind_Reason_Code (*__personality_routine)
(_Unwind_State state,

View File

@ -95,9 +95,11 @@ _Unwind_Reason_Code ProcessDescriptors(
case Descriptor::LU32:
descriptor = getNextWord(descriptor, &length);
descriptor = getNextWord(descriptor, &offset);
break;
case Descriptor::LU16:
descriptor = getNextNibble(descriptor, &length);
descriptor = getNextNibble(descriptor, &offset);
break;
default:
assert(false);
return _URC_FAILURE;
@ -183,8 +185,14 @@ static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state,
if (result != _URC_CONTINUE_UNWIND)
return result;
if (__unw_step(reinterpret_cast<unw_cursor_t *>(context)) != UNW_STEP_SUCCESS)
switch (__unw_step(reinterpret_cast<unw_cursor_t *>(context))) {
case UNW_STEP_SUCCESS:
return _URC_CONTINUE_UNWIND;
case UNW_STEP_END:
return _URC_END_OF_STACK;
default:
return _URC_FAILURE;
}
return _URC_CONTINUE_UNWIND;
}
@ -677,6 +685,128 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor
return _URC_FATAL_PHASE2_ERROR;
}
static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop,
void *stop_parameter) {
// See comment at the start of unwind_phase1 regarding VRS integrity.
__unw_init_local(cursor, uc);
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p)",
static_cast<void *>(exception_object));
// Walk each frame until we reach where search phase said to stop.
bool end_of_stack = false;
// TODO: why can't libunwind handle end of stack properly?
// We should fix this kind of hack.
unw_word_t forced_phase2_prev_sp = 0x0;
while (!end_of_stack) {
// Get info about this frame.
unw_word_t sp;
unw_proc_info_t frameInfo;
__unw_get_reg(cursor, UNW_REG_SP, &sp);
if (sp == forced_phase2_prev_sp) {
break;
}
forced_phase2_prev_sp = sp;
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info "
"failed => _URC_FATAL_PHASE2_ERROR",
static_cast<void *>(exception_object));
return _URC_FATAL_PHASE2_ERROR;
}
// When tracing, print state information.
if (_LIBUNWIND_TRACING_UNWINDING) {
char functionBuf[512];
const char *functionName = functionBuf;
unw_word_t offset;
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
&offset) != UNW_ESUCCESS) ||
(frameInfo.start_ip + offset > frameInfo.end_ip))
functionName = ".anonymous.";
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", "
"lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "",
static_cast<void *>(exception_object), frameInfo.start_ip,
functionName, sp, frameInfo.lsda,
frameInfo.handler);
}
_Unwind_Action action =
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
_Unwind_Reason_Code stopResult =
(*stop)(1, action, exception_object->exception_class, exception_object,
(_Unwind_Context *)(cursor), stop_parameter);
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): stop function returned %d",
(void *)exception_object, stopResult);
if (stopResult != _URC_NO_REASON) {
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): stopped by stop function",
(void *)exception_object);
return _URC_FATAL_PHASE2_ERROR;
}
// If there is a personality routine, tell it we are unwinding.
if (frameInfo.handler != 0) {
__personality_routine p =
(__personality_routine)(long)(frameInfo.handler);
struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor);
// EHABI #7.2
exception_object->pr_cache.fnstart = frameInfo.start_ip;
exception_object->pr_cache.ehtp =
(_Unwind_EHT_Header *)frameInfo.unwind_info;
exception_object->pr_cache.additional = frameInfo.flags;
_Unwind_Reason_Code personalityResult =
(*p)(_US_FORCE_UNWIND | _US_UNWIND_FRAME_STARTING, exception_object,
context);
switch (personalityResult) {
case _URC_CONTINUE_UNWIND:
// Continue unwinding
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): _URC_CONTINUE_UNWIND",
static_cast<void *>(exception_object));
break;
case _URC_INSTALL_CONTEXT:
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_ojb=%p): _URC_INSTALL_CONTEXT",
static_cast<void *>(exception_object));
{
// EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume
// is called back, to find this same frame.
unw_word_t pc;
__unw_get_reg(cursor, UNW_REG_IP, &pc);
exception_object->unwinder_cache.reserved2 = (uint32_t)pc;
}
// We may get control back if landing pad calls _Unwind_Resume().
__unw_resume(cursor);
break;
case _URC_END_OF_STACK:
end_of_stack = true;
break;
default:
// Personality routine returned an unknown result code.
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
personalityResult);
return _URC_FATAL_PHASE2_ERROR;
}
}
}
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
"function with _UA_END_OF_STACK",
(void *)exception_object);
_Unwind_Action lastAction =
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
(struct _Unwind_Context *)(cursor), stop_parameter);
return _URC_FATAL_PHASE2_ERROR;
}
/// Called by __cxa_throw. Only returns if there is a fatal error.
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
@ -724,15 +854,36 @@ _Unwind_Resume(_Unwind_Exception *exception_object) {
unw_cursor_t cursor;
__unw_getcontext(&uc);
// _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
// which is in the same position as private_1 below.
// TODO(ajwong): Who wronte the above? Why is it true?
unwind_phase2(&uc, &cursor, exception_object, true);
if (exception_object->unwinder_cache.reserved1)
unwind_phase2_forced(
&uc, &cursor, exception_object,
(_Unwind_Stop_Fn)exception_object->unwinder_cache.reserved1,
(void *)exception_object->unwinder_cache.reserved3);
else
unwind_phase2(&uc, &cursor, exception_object, true);
// Clients assume _Unwind_Resume() does not return, so all we can do is abort.
_LIBUNWIND_ABORT("_Unwind_Resume() can't return");
}
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop,
void *stop_parameter) {
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",
(void *)exception_object, (void *)(uintptr_t)stop);
unw_context_t uc;
unw_cursor_t cursor;
__unw_getcontext(&uc);
// Mark that this is a forced unwind, so _Unwind_Resume() can do
// the right thing.
exception_object->unwinder_cache.reserved1 = (uintptr_t)stop;
exception_object->unwinder_cache.reserved3 = (uintptr_t)stop_parameter;
return unwind_phase2_forced(&uc, &cursor, exception_object, stop,
stop_parameter);
}
/// Called by personality handler during phase 2 to get LSDA for current frame.
_LIBUNWIND_EXPORT uintptr_t
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
@ -1002,9 +1153,14 @@ extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
__gnu_unwind_frame(_Unwind_Exception *exception_object,
struct _Unwind_Context *context) {
unw_cursor_t *cursor = (unw_cursor_t *)context;
if (__unw_step(cursor) != UNW_STEP_SUCCESS)
switch (__unw_step(cursor)) {
case UNW_STEP_SUCCESS:
return _URC_OK;
case UNW_STEP_END:
return _URC_END_OF_STACK;
default:
return _URC_FAILURE;
return _URC_OK;
}
}
#endif // defined(_LIBUNWIND_ARM_EHABI)

View File

@ -6,12 +6,13 @@ 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"]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
default = ["target_zc706"]
[build-dependencies]
build_zynq = { path = "../libbuild_zynq" }
[dependencies]
num-traits = { version = "0.2", default-features = false }
num-derive = "0.3"
@ -33,9 +34,11 @@ libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"] }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] }
dyld = { path = "../libdyld" }
dwarf = { path = "../libdwarf" }
unwind = { path = "../libunwind" }
libc = { path = "../libc" }
io = { path = "../libio" }
libboard_artiq = { path = "../libboard_artiq" }

View File

@ -1,28 +1,6 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
extern crate build_zynq;
fn main() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.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");
// Handle rustc-cfg file
let cfg_path = "../../build/rustc-cfg";
println!("cargo:rerun-if-changed={}", cfg_path);
let f = BufReader::new(File::open(cfg_path).unwrap());
for line in f.lines() {
println!("cargo:rustc-cfg={}", line.unwrap());
}
build_zynq::add_linker_script();
build_zynq::cfg();
}

View File

@ -1,8 +1,8 @@
use core::fmt;
use core::cell::RefCell;
use core::str::Utf8Error;
use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc};
use log::{info, warn, error};
use cslice::CSlice;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
@ -21,6 +21,7 @@ use libcortex_a9::{semaphore::Semaphore, mutex::Mutex, sync_channel::{Sender, Re
use futures::{select_biased, future::FutureExt};
use libasync::{smoltcp::{Sockets, TcpStream}, task};
use libconfig::{Config, net_settings};
use libboard_artiq::drtio_routing;
use crate::proto_async::*;
use crate::kernel;
@ -28,7 +29,9 @@ use crate::rpc;
use crate::moninj;
use crate::mgmt;
use crate::analyzer;
use crate::rtio_mgt;
#[cfg(has_drtio)]
use crate::pl;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
@ -36,7 +39,6 @@ pub enum Error {
UnexpectedPattern,
UnrecognizedPacket,
BufferExhausted,
Utf8Error(Utf8Error),
}
pub type Result<T> = core::result::Result<T, Error>;
@ -48,7 +50,6 @@ impl fmt::Display for Error {
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
Error::BufferExhausted => write!(f, "buffer exhausted"),
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
}
}
}
@ -94,13 +95,13 @@ async fn read_request(stream: &TcpStream, allow_close: bool) -> Result<Option<Re
Ok(true) => {}
Ok(false) =>
return Err(Error::UnexpectedPattern),
Err(smoltcp::Error::Illegal) => {
Err(smoltcp::Error::Finished) => {
if allow_close {
info!("peer closed connection");
return Ok(None);
} else {
error!("peer unexpectedly closed connection");
return Err(smoltcp::Error::Illegal)?;
return Err(smoltcp::Error::Finished)?;
}
},
Err(e) =>
@ -119,11 +120,6 @@ async fn read_bytes(stream: &TcpStream, max_length: usize) -> Result<Vec<u8>> {
Ok(buffer)
}
async fn read_string(stream: &TcpStream, max_length: usize) -> Result<String> {
let bytes = read_bytes(stream, max_length).await?;
Ok(String::from_utf8(bytes).map_err(|err| Error::Utf8Error(err.utf8_error()))?)
}
const RETRY_LIMIT: usize = 100;
async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) {
@ -153,7 +149,17 @@ async fn fast_recv(receiver: &mut Receiver<'_, kernel::Message>) -> kernel::Mess
receiver.async_recv().await
}
async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>) -> Result<()> {
async fn write_exception_string(stream: &TcpStream, s: CSlice<'static, u8>) -> Result<()> {
if s.len() == usize::MAX {
write_i32(stream, -1).await?;
write_i32(stream, s.as_ptr() as i32).await?
} else {
write_chunk(stream, s.as_ref()).await?;
};
Ok(())
}
async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>, _up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<()> {
control.borrow_mut().tx.async_send(kernel::Message::StartRequest).await;
loop {
let reply = control.borrow_mut().rx.async_recv().await;
@ -201,17 +207,17 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
kernel::Message::RpcRecvRequest(_) => (),
other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other),
}
let name = read_string(stream, 16384).await?;
let message = read_string(stream, 16384).await?;
let id = read_i32(stream).await? as u32;
let message = read_i32(stream).await? as u32;
let param = [read_i64(stream).await?,
read_i64(stream).await?,
read_i64(stream).await?];
let file = read_string(stream, 16384).await?;
let file = read_i32(stream).await? as u32;
let line = read_i32(stream).await?;
let column = read_i32(stream).await?;
let function = read_string(stream, 16384).await?;
let function = read_i32(stream).await? as u32;
control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
name, message, param, file, line, column, function
id, message, param, file, line, column, function
}))).await;
},
_ => {
@ -221,34 +227,46 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
}
}
},
kernel::Message::KernelFinished => {
kernel::Message::KernelFinished(async_errors) => {
if let Some(stream) = stream {
write_header(stream, Reply::KernelFinished).await?;
write_i8(stream, async_errors as i8).await?;
}
break;
},
kernel::Message::KernelException(exception, backtrace) => {
kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => {
match stream {
Some(stream) => {
// only send the exception data to host if there is host,
// i.e. not idle/startup kernel.
write_header(stream, Reply::KernelException).await?;
write_chunk(stream, exception.name.as_ref()).await?;
write_chunk(stream, exception.message.as_ref()).await?;
write_i64(stream, exception.param[0] as i64).await?;
write_i64(stream, exception.param[1] as i64).await?;
write_i64(stream, exception.param[2] as i64).await?;
write_chunk(stream, exception.file.as_ref()).await?;
write_i32(stream, exception.line as i32).await?;
write_i32(stream, exception.column as i32).await?;
write_chunk(stream, exception.function.as_ref()).await?;
write_i32(stream, backtrace.len() as i32).await?;
for &addr in backtrace {
write_i32(stream, addr as i32).await?;
write_i32(stream, exceptions.len() as i32).await?;
for exception in exceptions.iter() {
let exception = exception.as_ref().unwrap();
write_i32(stream, exception.id as i32).await?;
write_exception_string(stream, exception.message).await?;
write_i64(stream, exception.param[0] as i64).await?;
write_i64(stream, exception.param[1] as i64).await?;
write_i64(stream, exception.param[2] as i64).await?;
write_exception_string(stream, exception.file).await?;
write_i32(stream, exception.line as i32).await?;
write_i32(stream, exception.column as i32).await?;
write_exception_string(stream, exception.function).await?;
}
for sp in stack_pointers.iter() {
write_i32(stream, sp.stack_pointer as i32).await?;
write_i32(stream, sp.initial_backtrace_size as i32).await?;
write_i32(stream, sp.current_backtrace_size as i32).await?;
}
write_i32(stream, backtrace.len() as i32).await?;
for &(addr, sp) in backtrace {
write_i32(stream, addr as i32).await?;
write_i32(stream, sp as i32).await?;
}
write_i8(stream, async_errors as i8).await?;
},
None => {
error!("Uncaught kernel exception: {:?}", exception);
error!("Uncaught kernel exceptions: {:?}", exceptions);
}
}
break;
@ -272,6 +290,11 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
let result = DMA_RECORD_STORE.lock().get(&name).map(|v| v.clone());
control.borrow_mut().tx.async_send(kernel::Message::DmaGetReply(result)).await;
},
#[cfg(has_drtio)]
kernel::Message::UpDestinationsRequest(destination) => {
let result = _up_destinations.borrow()[destination as usize];
control.borrow_mut().tx.async_send(kernel::Message::UpDestinationsReply(result)).await;
}
_ => {
panic!("unexpected message from core1 while kernel was running: {:?}", reply);
}
@ -313,7 +336,9 @@ async fn load_kernel(buffer: &Vec<u8>, control: &Rc<RefCell<kernel::Control>>, s
}
}
async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Control>>) -> Result<()> {
async fn handle_connection(stream: &mut TcpStream, control: Rc<RefCell<kernel::Control>>, up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<()> {
stream.set_ack_delay(None);
if !expect(stream, b"ARTIQ coredev\n").await? {
return Err(Error::UnexpectedPattern);
}
@ -334,7 +359,7 @@ async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Contr
load_kernel(&buffer, &control, Some(stream)).await?;
},
Request::RunKernel => {
handle_run_kernel(Some(stream), &control).await?;
handle_run_kernel(Some(stream), &control, &up_destinations).await?;
},
_ => {
error!("unexpected request from host: {:?}", request);
@ -345,7 +370,7 @@ async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Contr
}
pub fn main(timer: GlobalTimer, cfg: Config) {
let net_addresses = net_settings::get_adresses(&cfg);
let net_addresses = net_settings::get_addresses(&cfg);
info!("network addresses: {}", net_addresses);
let eth = zynq::eth::Eth::eth0(net_addresses.hardware_addr.0.clone());
@ -385,8 +410,21 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
Sockets::init(32);
// before, mutex was on io, but now that io isn't used...?
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
#[cfg(has_drtio)]
let drtio_routing_table = Rc::new(RefCell::new(
drtio_routing::config_routing_table(pl::csr::DRTIO.len(), &cfg)));
#[cfg(not(has_drtio))]
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::RoutingTable::default_empty()));
let up_destinations = Rc::new(RefCell::new([false; drtio_routing::DEST_COUNT]));
#[cfg(has_drtio_routing)]
drtio_routing::interconnect_disable_all();
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
analyzer::start();
moninj::start(timer);
moninj::start(timer, aux_mutex, drtio_routing_table);
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
let idle_kernel = Rc::new(cfg.read("idle").ok());
@ -394,7 +432,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
info!("Loading startup kernel...");
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
info!("Starting startup kernel...");
let _ = task::block_on(handle_run_kernel(None, &control));
let _ = task::block_on(handle_run_kernel(None, &control, &up_destinations));
info!("Startup kernel finished!");
} else {
error!("Error loading startup kernel!");
@ -407,7 +445,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let connection = Rc::new(Semaphore::new(1, 1));
let terminate = Rc::new(Semaphore::new(0, 1));
loop {
let stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
if connection.try_wait().is_none() {
// there is an existing connection
@ -419,13 +457,14 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let idle_kernel = idle_kernel.clone();
let connection = connection.clone();
let terminate = terminate.clone();
let up_destinations = up_destinations.clone();
// we make sure the value of terminate is 0 before we start
let _ = terminate.try_wait();
task::spawn(async move {
select_biased! {
_ = (async {
let _ = handle_connection(&stream, control.clone())
let _ = handle_connection(&mut stream, control.clone(), &up_destinations)
.await
.map_err(|e| warn!("connection terminated: {}", e));
if let Some(buffer) = &*idle_kernel {
@ -433,7 +472,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
let _ = load_kernel(&buffer, &control, None)
.await.map_err(|_| warn!("error loading idle kernel"));
info!("Running idle kernel");
let _ = handle_run_kernel(None, &control)
let _ = handle_run_kernel(None, &control, &up_destinations)
.await.map_err(|_| warn!("error running idle kernel"));
info!("Idle kernel terminated");
}

View File

@ -15,8 +15,9 @@
use core::mem;
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, uintptr_t};
use log::trace;
use libc::{c_int, c_void, uintptr_t};
use log::{trace, error};
use crate::kernel::KERNEL_IMAGE;
use dwarf::eh::{self, EHAction, EHContext};
@ -26,10 +27,12 @@ const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51;
#[cfg(target_arch = "arm")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
// Note: CSlice within an exception may not be actual cslice, they may be strings that exist only
// in the host. If the length == usize:MAX, the pointer is actually a string key in the host.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Exception<'a> {
pub name: CSlice<'a, u8>,
pub id: u32,
pub file: CSlice<'a, u8>,
pub line: u32,
pub column: u32,
@ -42,31 +45,100 @@ fn str_err(_: core::str::Utf8Error) -> core::fmt::Error {
core::fmt::Error
}
impl<'a> core::fmt::Debug for Exception<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
core::str::from_utf8(self.name.as_ref()).map_err(str_err)?,
core::str::from_utf8(self.function.as_ref()).map_err(str_err)?,
core::str::from_utf8(self.file.as_ref()).map_err(str_err)?,
self.line, self.column,
core::str::from_utf8(self.message.as_ref()).map_err(str_err)?)
fn exception_str<'a>(s: &'a CSlice<'a, u8>) -> Result<&'a str, core::str::Utf8Error> {
if s.len() == usize::MAX {
Ok("<host string>")
} else {
core::str::from_utf8(s.as_ref())
}
}
impl<'a> core::fmt::Debug for Exception<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
self.id,
exception_str(&self.function).map_err(str_err)?,
exception_str(&self.file).map_err(str_err)?,
self.line, self.column,
exception_str(&self.message).map_err(str_err)?)
}
}
const MAX_INFLIGHT_EXCEPTIONS: usize = 10;
const MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
#[derive(Debug, Default)]
pub struct StackPointerBacktrace {
pub stack_pointer: usize,
pub initial_backtrace_size: usize,
pub current_backtrace_size: usize,
}
struct ExceptionBuffer {
// we need n _Unwind_Exception, because each will have their own private data
uw_exceptions: [uw::_Unwind_Exception; MAX_INFLIGHT_EXCEPTIONS],
exceptions: [Option<Exception<'static>>; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_stack: [isize; MAX_INFLIGHT_EXCEPTIONS + 1],
// nested exceptions will share the backtrace buffer, treated as a tree
// backtrace contains a tuple of IP and SP
backtrace: [(usize, usize); MAX_BACKTRACE_SIZE],
backtrace_size: usize,
// stack pointers are stored to reconstruct backtrace for each exception
stack_pointers: [StackPointerBacktrace; MAX_INFLIGHT_EXCEPTIONS + 1],
// current allocated nested exceptions
exception_count: usize,
}
static mut EXCEPTION_BUFFER: ExceptionBuffer = ExceptionBuffer {
uw_exceptions: [uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
}; MAX_INFLIGHT_EXCEPTIONS],
exceptions: [None; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_stack: [-1; MAX_INFLIGHT_EXCEPTIONS + 1],
backtrace: [(0, 0); MAX_BACKTRACE_SIZE],
backtrace_size: 0,
stack_pointers: [StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: 0,
current_backtrace_size: 0
}; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_count: 0
};
pub unsafe extern fn reset_exception_buffer() {
trace!("reset exception buffer");
EXCEPTION_BUFFER.uw_exceptions = [uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
}; MAX_INFLIGHT_EXCEPTIONS];
EXCEPTION_BUFFER.exceptions = [None; MAX_INFLIGHT_EXCEPTIONS + 1];
EXCEPTION_BUFFER.exception_stack = [-1; MAX_INFLIGHT_EXCEPTIONS + 1];
EXCEPTION_BUFFER.backtrace_size = 0;
EXCEPTION_BUFFER.exception_count = 0;
}
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: i32,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
// not defined in EHABI, but LLVM added it and is useful to us
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
unsafe fn find_eh_action(
context: *mut uw::_Unwind_Context,
foreign_exception: bool,
id: u32,
) -> Result<EHAction, ()> {
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let mut ip_before_instr: c_int = 0;
@ -79,32 +151,14 @@ unsafe fn find_eh_action(
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
};
eh::find_eh_action(lsda, &eh_context, foreign_exception)
eh::find_eh_action(lsda, &eh_context, foreign_exception, id)
}
pub unsafe fn artiq_personality(state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
let state = state as c_int;
let action = state & uw::_US_ACTION_MASK as c_int;
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
// Backtraces on ARM will call the personality routine with
// state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
// we want to continue unwinding the stack, otherwise all our backtraces
// would end at __rust_try
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
return continue_unwind(exception_object, context);
}
true
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
false
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
return continue_unwind(exception_object, context);
} else {
return uw::_URC_FAILURE;
};
pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
// we will only do phase 2 forced unwinding now
// The DWARF unwinder assumes that _Unwind_Context holds things like the function
// and LSDA pointers, however ARM EHABI places them into the exception object.
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
@ -120,45 +174,27 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State,
let exception_class = (*exception_object).exception_class;
let foreign_exception = exception_class != EXCEPTION_CLASS;
let eh_action = match find_eh_action(context, foreign_exception) {
assert!(!foreign_exception, "we do not expect foreign exceptions");
let index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(index != -1);
let exception = EXCEPTION_BUFFER.exceptions[index as usize].as_ref().unwrap();
let id = exception.id;
let eh_action = match find_eh_action(context, foreign_exception, id) {
Ok(action) => action,
Err(_) => return uw::_URC_FAILURE,
};
let exception_info = &mut *(exception_object as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
if search_phase {
match eh_action {
EHAction::None => return continue_unwind(exception_object, context),
// Actually, cleanup should not return handler found, this is to workaround
// the issue of terminating directly when no catch cause is found while
// having some cleanup routines defined by finally.
// The best way to handle this is to force unwind the stack in the raise
// function when end of stack is reached, and call terminate at the end of
// the unwind. Unfortunately, there is no forced unwind function defined
// for EHABI, and I have no idea how to implement that, so this is a hack.
EHAction::Cleanup(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Catch(_) => {
// EHABI requires the personality routine to update the
// SP value in the barrier cache of the exception object.
(*exception_object).private[5] =
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
return uw::_URC_HANDLER_FOUND;
}
EHAction::Terminate => return uw::_URC_FAILURE,
}
} else {
match eh_action {
EHAction::None => return continue_unwind(exception_object, context),
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
exception_object as uintptr_t);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FAILURE,
match eh_action {
EHAction::None => return continue_unwind(exception_object, context),
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
exception_object as uintptr_t);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FAILURE,
}
// On ARM EHABI the personality routine is responsible for actually
@ -166,10 +202,11 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State,
unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
let reason = __gnu_unwind_frame(exception_object, context);
if reason == uw::_URC_NO_REASON {
uw::_URC_CONTINUE_UNWIND
} else {
uw::_URC_FAILURE
reason
}
}
// defined in libgcc
@ -180,74 +217,225 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State,
}
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
exception_info.exception = None;
}
}
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: true,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};
pub unsafe extern fn raise(exception: *const Exception) -> ! {
trace!("Trying to raise exception");
// FIXME: unsound transmute
// This would cause stack memory corruption.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
// read backtrace
let _ = uw::backtrace(|ip| {
if INFLIGHT.backtrace_size < MAX_BACKTRACE_SIZE {
INFLIGHT.backtrace[INFLIGHT.backtrace_size] = ip;
INFLIGHT.backtrace_size += 1;
}
});
crate::kernel::core1::terminate(INFLIGHT.exception.as_ref().unwrap(), INFLIGHT.backtrace[..INFLIGHT.backtrace_size].as_mut());
}
pub unsafe extern fn reraise() -> ! {
use cslice::AsCSlice;
// Reraise is basically cxa_rethrow, which calls _Unwind_Resume_or_Rethrow,
// which for EHABI would always call _Unwind_RaiseException.
match INFLIGHT.exception {
Some(ref exception) => raise(exception),
None => raise(&Exception {
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
file: file!().as_c_slice(),
line: line!(),
column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719
function: "__artiq_reraise".as_c_slice(),
message: "No active exception to reraise".as_c_slice(),
param: [0, 0, 0]
})
let count = EXCEPTION_BUFFER.exception_count;
let stack = &mut EXCEPTION_BUFFER.exception_stack;
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
if 0 <= diff && diff <= (mem::size_of::<Option<Exception>>() * MAX_INFLIGHT_EXCEPTIONS) as isize {
let index = diff / (mem::size_of::<Option<Exception>>() as isize);
trace!("reraise at {}", index);
let mut found = false;
for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 {
if found {
if stack[i] == -1 {
stack[i - 1] = index;
assert!(i == count);
break;
} else {
stack[i - 1] = stack[i];
}
} else {
if stack[i] == index {
found = true;
}
}
}
assert!(found);
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[stack[count - 1] as usize],
stop_fn, core::ptr::null_mut());
} else {
if count < MAX_INFLIGHT_EXCEPTIONS {
trace!("raising exception at level {}", count);
let exception = &*exception;
for (i, slot) in EXCEPTION_BUFFER.exceptions.iter_mut().enumerate() {
// we should always be able to find a slot
if slot.is_none() {
*slot = Some(
*mem::transmute::<*const Exception, *const Exception<'static>>
(exception));
EXCEPTION_BUFFER.exception_stack[count] = i as isize;
EXCEPTION_BUFFER.uw_exceptions[i].private =
[0; uw::unwinder_private_data_size];
EXCEPTION_BUFFER.stack_pointers[i] = StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: EXCEPTION_BUFFER.backtrace_size,
current_backtrace_size: 0,
};
EXCEPTION_BUFFER.exception_count += 1;
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i],
stop_fn, core::ptr::null_mut());
}
}
} else {
error!("too many nested exceptions");
// TODO: better reporting?
let exception = Exception {
id: get_exception_id("RuntimeError"),
file: file!().as_c_slice(),
line: line!(),
column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719
function: "__artiq_raise".as_c_slice(),
message: "too many nested exceptions".as_c_slice(),
param: [0, 0, 0]
};
EXCEPTION_BUFFER.exceptions[MAX_INFLIGHT_EXCEPTIONS] = Some(mem::transmute(exception));
EXCEPTION_BUFFER.stack_pointers[MAX_INFLIGHT_EXCEPTIONS] = Default::default();
EXCEPTION_BUFFER.exception_count += 1;
uncaught_exception()
}
}
unreachable!();
}
pub unsafe extern fn resume() -> ! {
trace!("resume");
assert!(EXCEPTION_BUFFER.exception_count != 0);
let i = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(i != -1);
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i as usize],
stop_fn, core::ptr::null_mut());
unreachable!()
}
pub unsafe extern fn end_catch() {
let mut count = EXCEPTION_BUFFER.exception_count;
assert!(count != 0);
// we remove all exceptions with SP <= current exception SP
// i.e. the outer exception escapes the finally block
let index = EXCEPTION_BUFFER.exception_stack[count - 1] as usize;
EXCEPTION_BUFFER.exception_stack[count - 1] = -1;
EXCEPTION_BUFFER.exceptions[index] = None;
let outer_sp = EXCEPTION_BUFFER.stack_pointers
[index].stack_pointer;
count -= 1;
for i in (0..count).rev() {
let index = EXCEPTION_BUFFER.exception_stack[i];
assert!(index != -1);
let index = index as usize;
let sp = EXCEPTION_BUFFER.stack_pointers[index].stack_pointer;
if sp >= outer_sp {
break;
}
EXCEPTION_BUFFER.exceptions[index] = None;
EXCEPTION_BUFFER.exception_stack[i] = -1;
count -= 1;
}
EXCEPTION_BUFFER.exception_count = count;
EXCEPTION_BUFFER.backtrace_size = if count > 0 {
let index = EXCEPTION_BUFFER.exception_stack[count - 1];
assert!(index != -1);
EXCEPTION_BUFFER.stack_pointers[index as usize].current_backtrace_size
} else {
0
};
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
_uw_exception: *mut uw::_Unwind_Exception) {
unimplemented!()
}
fn uncaught_exception() -> ! {
unsafe {
// dump way to reorder the stack
for i in 0..EXCEPTION_BUFFER.exception_count {
if EXCEPTION_BUFFER.exception_stack[i] != i as isize {
// find the correct index
let index = EXCEPTION_BUFFER.exception_stack
.iter()
.position(|v| *v == i as isize).unwrap();
let a = EXCEPTION_BUFFER.exception_stack[index];
let b = EXCEPTION_BUFFER.exception_stack[i];
assert!(a != -1 && b != -1);
core::mem::swap(&mut EXCEPTION_BUFFER.exception_stack[index],
&mut EXCEPTION_BUFFER.exception_stack[i]);
core::mem::swap(&mut EXCEPTION_BUFFER.exceptions[a as usize],
&mut EXCEPTION_BUFFER.exceptions[b as usize]);
core::mem::swap(&mut EXCEPTION_BUFFER.stack_pointers[a as usize],
&mut EXCEPTION_BUFFER.stack_pointers[b as usize]);
}
}
}
unsafe {
crate::kernel::core1::terminate(
EXCEPTION_BUFFER.exceptions[..EXCEPTION_BUFFER.exception_count].as_ref(),
EXCEPTION_BUFFER.stack_pointers[..EXCEPTION_BUFFER.exception_count].as_ref(),
EXCEPTION_BUFFER.backtrace[..EXCEPTION_BUFFER.backtrace_size].as_mut())
}
}
// stop function which would be executed when we unwind each frame
extern fn stop_fn(_version: c_int,
actions: i32,
_uw_exception_class: uw::_Unwind_Exception_Class,
_uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code {
unsafe {
let load_addr = KERNEL_IMAGE.as_ref().unwrap().get_load_addr();
let backtrace_size = EXCEPTION_BUFFER.backtrace_size;
// we try to remove unrelated backtrace here to save some buffer size
if backtrace_size < MAX_BACKTRACE_SIZE {
let ip = uw::_Unwind_GetIP(context);
if ip >= load_addr {
let ip = ip - load_addr;
let sp = uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
trace!("SP: {:X}, backtrace_size: {}", sp, backtrace_size);
EXCEPTION_BUFFER.backtrace[backtrace_size] = (ip, sp);
EXCEPTION_BUFFER.backtrace_size += 1;
let last_index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(last_index != -1);
let sp_info = &mut EXCEPTION_BUFFER.stack_pointers[last_index as usize];
sp_info.stack_pointer = sp;
sp_info.current_backtrace_size = backtrace_size + 1;
}
} else {
trace!("backtrace size exceeded");
}
if actions as u32 & uw::_US_END_OF_STACK as u32 != 0 {
uncaught_exception()
} else {
uw::_URC_NO_REASON
}
}
}
// Must be kept in sync with preallocate_runtime_exception_names() in artiq/language/embedding_map.py
static EXCEPTION_ID_LOOKUP: [(&str, u32); 11] = [
("RuntimeError", 0),
("RTIOUnderflow", 1),
("RTIOOverflow", 2),
("RTIODestinationUnreachable", 3),
("DMAError", 4),
("I2CError", 5),
("CacheError", 6),
("SPIError", 7),
("ZeroDivisionError", 8),
("IndexError", 9),
("UnwrapNoneError", 10),
];
pub fn get_exception_id(name: &str) -> u32 {
for (n, id) in EXCEPTION_ID_LOOKUP.iter() {
if *n == name {
return *id
}
}
unimplemented!("unallocated internal exception id")
}
#[macro_export]
macro_rules! artiq_raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice;
let name_id = $crate::eh_artiq::get_exception_id($name);
let exn = $crate::eh_artiq::Exception {
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
id: name_id,
file: file!().as_c_slice(),
line: line!(),
column: column!(),
@ -257,7 +445,9 @@ macro_rules! artiq_raise {
param: [$param0, $param1, $param2]
};
#[allow(unused_unsafe)]
unsafe { $crate::eh_artiq::raise(&exn) }
unsafe {
$crate::eh_artiq::raise(&exn)
}
});
($name:expr, $message:expr) => ({
artiq_raise!($name, $message, 0, 0, 0)

View File

@ -1,100 +1,90 @@
#[cfg(feature = "target_zc706")]
mod i2c {
use libboard_zynq;
use crate::artiq_raise;
use libboard_zynq;
use crate::artiq_raise;
static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
pub extern fn start(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
artiq_raise!("I2CError", "I2C start failed");
}
}
pub extern fn start(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
pub extern fn restart(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
artiq_raise!("I2CError", "I2C start failed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
artiq_raise!("I2CError", "I2C restart failed");
}
}
}
pub extern fn stop(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() {
artiq_raise!("I2CError", "I2C stop failed");
}
}
}
pub extern fn write(busno: i32, data: i32) -> bool {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) {
Ok(r) => r,
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
}
}
}
pub extern fn read(busno: i32, ack: bool) -> i32 {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().read(ack) {
Ok(r) => r as i32,
Err(_) => artiq_raise!("I2CError", "I2C read failed"),
}
}
}
pub fn init() {
let mut i2c = libboard_zynq::i2c::I2c::i2c0();
i2c.init().expect("I2C bus initialization failed");
unsafe { I2C_BUS = Some(i2c) };
}
}
#[cfg(not(feature = "target_zc706"))]
mod i2c {
use crate::artiq_raise;
pub extern fn start(_busno: i32) {
artiq_raise!("I2CError", "No I2C bus");
pub extern fn restart(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
pub extern fn restart(_busno: i32) {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn stop(_busno: i32) {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn write(_busno: i32, _data: i32) -> bool {
artiq_raise!("I2CError", "No I2C bus");
}
pub extern fn read(_busno: i32, _ack: bool) -> i32 {
artiq_raise!("I2CError", "No I2C bus");
}
pub fn init() {
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
artiq_raise!("I2CError", "I2C restart failed");
}
}
}
pub use i2c::*;
pub extern fn stop(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() {
artiq_raise!("I2CError", "I2C stop failed");
}
}
}
pub extern fn write(busno: i32, data: i32) -> bool {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) {
Ok(r) => r,
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
}
}
}
pub extern fn read(busno: i32, ack: bool) -> i32 {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().read(ack) {
Ok(r) => r as i32,
Err(_) => artiq_raise!("I2CError", "I2C read failed"),
}
}
}
pub extern fn switch_select(busno: i32, address: i32, mask: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
let ch = match mask { //decode from mainline, PCA9548-centric API
0x00 => None,
0x01 => Some(0),
0x02 => Some(1),
0x04 => Some(2),
0x08 => Some(3),
0x10 => Some(4),
0x20 => Some(5),
0x40 => Some(6),
0x80 => Some(7),
_ => artiq_raise!("I2CError", "switch select supports only one channel")
};
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().pca954x_select(address as u8, ch).is_err() {
artiq_raise!("I2CError", "switch select failed");
}
}
}
pub fn init() {
let mut i2c = libboard_zynq::i2c::I2c::i2c0();
i2c.init().expect("I2C bus initialization failed");
unsafe { I2C_BUS = Some(i2c) };
}

View File

@ -13,7 +13,7 @@ use crate::i2c;
use super::rpc::{rpc_send, rpc_send_async, rpc_recv};
use super::dma;
use super::cache;
use super::core1::rtio_get_destination_status;
extern "C" {
fn vsnprintf_(buffer: *mut c_char, count: size_t, format: *const c_char, va: VaList) -> c_int;
@ -87,7 +87,7 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
// rtio
api!(rtio_init = rtio::init),
api!(rtio_get_destination_status = rtio::get_destination_status),
api!(rtio_get_destination_status = rtio_get_destination_status),
api!(rtio_get_counter = rtio::get_counter),
api!(rtio_output = rtio::output),
api!(rtio_output_wide = rtio::output_wide),
@ -116,6 +116,7 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
api!(i2c_stop = i2c::stop),
api!(i2c_write = i2c::write),
api!(i2c_read = i2c::read),
api!(i2c_switch_select = i2c::switch_select),
// Double-precision floating-point arithmetic helper functions
// RTABI chapter 4.1.2, Table 2
@ -198,13 +199,22 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
api!(__aeabi_memclr),
// libc
api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }),
api!(memcpy, extern { fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }),
api!(memmove, extern { fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }),
api!(memset, extern { fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8; }),
api!(memcmp, extern { fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; }),
// exceptions
api!(_Unwind_Resume = unwind::_Unwind_Resume),
api!(__nac3_personality = eh_artiq::artiq_personality),
api!(__nac3_raise = eh_artiq::raise),
api!(__nac3_resume = eh_artiq::resume),
api!(__nac3_end_catch = eh_artiq::end_catch),
// legacy exception symbols
api!(__artiq_personality = eh_artiq::artiq_personality),
api!(__artiq_raise = eh_artiq::raise),
api!(__artiq_reraise = eh_artiq::reraise),
api!(__artiq_resume = eh_artiq::resume),
api!(__artiq_end_catch = eh_artiq::end_catch),
// Implementations for LLVM math intrinsics
api!(__powidf2),

View File

@ -1,26 +1,26 @@
use alloc::string::String;
use alloc::{string::String, boxed::Box};
use cslice::{CSlice, AsCSlice};
use core::mem::{transmute, forget};
use super::{KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message};
pub extern fn get(key: CSlice<u8>) -> CSlice<'static, i32> {
pub extern fn get(key: CSlice<u8>) -> &CSlice<'static, i32> {
let key = String::from_utf8(key.as_ref().to_vec()).unwrap();
unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CacheGetRequest(key));
let msg = KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv();
if let Message::CacheGetReply(v) = msg {
let slice = transmute(v.as_c_slice());
// we intentionally leak the memory here,
// which does not matter as core1 would restart
let leaked = Box::new(v.as_c_slice());
let reference = transmute(leaked.as_ref());
forget(leaked);
forget(v);
slice
reference
} else {
panic!("Expected CacheGetReply for CacheGetRequest");
}
}
}
pub extern fn put(key: CSlice<u8>, list: CSlice<i32>) {
pub extern fn put(key: CSlice<u8>, list: &CSlice<i32>) {
let key = String::from_utf8(key.as_ref().to_vec()).unwrap();
let value = list.as_ref().to_vec();
unsafe {

View File

@ -13,8 +13,8 @@ use libcortex_a9::{
};
use libboard_zynq::{mpcore, gic};
use libsupport_zynq::ram;
use dyld::{self, Library};
use crate::{eh_artiq, rtio};
use dyld::{self, Library, elf::EXIDX_Entry};
use crate::{eh_artiq, get_async_errors, rtio};
use super::{
api::resolve,
rpc::rpc_send_async,
@ -31,8 +31,8 @@ use super::{
extern "C" {
static __text_start: u32;
static __text_end: u32;
static __exidx_start: u32;
static __exidx_end: u32;
static __exidx_start: EXIDX_Entry;
static __exidx_end: EXIDX_Entry;
}
unsafe fn attribute_writeback(typeinfo: *const ()) {
@ -182,6 +182,7 @@ pub extern "C" fn main_core1() {
info!("kernel starting");
if let Some(kernel) = loaded_kernel.take() {
unsafe {
eh_artiq::reset_exception_buffer();
KERNEL_CHANNEL_0TO1 = Some(core1_rx);
KERNEL_CHANNEL_1TO0 = Some(core1_tx);
KERNEL_IMAGE = &kernel as *const KernelImage;
@ -192,7 +193,8 @@ pub extern "C" fn main_core1() {
}
}
info!("kernel finished");
core1_tx.send(Message::KernelFinished);
let async_errors = unsafe { get_async_errors() };
core1_tx.send(Message::KernelFinished(async_errors));
}
_ => error!("Core1 received unexpected message: {:?}", message),
}
@ -200,23 +202,13 @@ pub extern "C" fn main_core1() {
}
/// Called by eh_artiq
pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'static mut [usize]) -> ! {
let load_addr = unsafe {
KERNEL_IMAGE.as_ref().unwrap().get_load_addr()
};
let mut cursor = 0;
// The address in the backtrace is relocated, so we have to convert it back to the address in
// the original python script, and remove those Rust function backtrace.
for i in 0..backtrace.len() {
if backtrace[i] >= load_addr {
backtrace[cursor] = backtrace[i] - load_addr;
cursor += 1;
}
}
pub fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
backtrace: &'static mut [(usize, usize)]) -> ! {
{
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
core1_tx.send(Message::KernelException(exception, &backtrace[..cursor]));
let errors = unsafe { get_async_errors() };
core1_tx.send(Message::KernelException(exceptions, stack_pointers, backtrace, errors));
}
loop {}
}
@ -225,10 +217,10 @@ pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'
#[no_mangle]
extern fn dl_unwind_find_exidx(pc: *const u32, len_ptr: *mut u32) -> *const u32 {
let length;
let start: *const u32;
let start: *const EXIDX_Entry;
unsafe {
if &__text_start as *const u32 <= pc && pc < &__text_end as *const u32 {
length = (&__exidx_end as *const u32).offset_from(&__exidx_start) as u32;
length = (&__exidx_end as *const EXIDX_Entry).offset_from(&__exidx_start) as u32;
start = &__exidx_start;
} else {
let exidx = KERNEL_IMAGE.as_ref()
@ -239,5 +231,23 @@ extern fn dl_unwind_find_exidx(pc: *const u32, len_ptr: *mut u32) -> *const u32
}
*len_ptr = length;
}
start
start as *const u32
}
pub extern fn rtio_get_destination_status(destination: i32) -> bool {
#[cfg(has_drtio)]
if destination > 0 && destination < 255 {
let reply = unsafe {
let core1_rx = KERNEL_CHANNEL_0TO1.as_mut().unwrap();
let core1_tx = KERNEL_CHANNEL_1TO0.as_mut().unwrap();
core1_tx.send(Message::UpDestinationsRequest(destination));
core1_rx.recv()
};
return match reply {
Message::UpDestinationsReply(x) => x,
_ => panic!("received unexpected reply to UpDestinationsRequest: {:?}", reply)
};
}
destination == 0
}

View File

@ -15,13 +15,13 @@ mod cache;
#[derive(Debug, Clone)]
pub struct RPCException {
pub name: String,
pub message: String,
pub id: u32,
pub message: u32,
pub param: [i64; 3],
pub file: String,
pub file: u32,
pub line: i32,
pub column: i32,
pub function: String
pub function: u32
}
#[derive(Debug, Clone)]
@ -30,8 +30,11 @@ pub enum Message {
LoadCompleted,
LoadFailed,
StartRequest,
KernelFinished,
KernelException(&'static eh_artiq::Exception<'static>, &'static [usize]),
KernelFinished(u8),
KernelException(&'static [Option<eh_artiq::Exception<'static>>],
&'static [eh_artiq::StackPointerBacktrace],
&'static [(usize, usize)],
u8),
RpcSend { is_async: bool, data: Vec<u8> },
RpcRecvRequest(*mut ()),
RpcRecvReply(Result<usize, RPCException>),
@ -44,6 +47,11 @@ pub enum Message {
DmaEraseRequest(String),
DmaGetRequest(String),
DmaGetReply(Option<(Vec<u8>, i64)>),
#[cfg(has_drtio)]
UpDestinationsRequest(i32),
#[cfg(has_drtio)]
UpDestinationsReply(bool),
}
static CHANNEL_0TO1: Mutex<Option<sync_channel::Sender<'static, Message>>> = Mutex::new(None);
@ -53,7 +61,7 @@ static CHANNEL_SEM: Semaphore = Semaphore::new(0, 1);
static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None;
static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None;
static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
pub static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
static INIT_LOCK: Mutex<()> = Mutex::new(());

View File

@ -1,7 +1,7 @@
//! Kernel-side RPC API
use alloc::vec::Vec;
use cslice::{CSlice, AsCSlice};
use cslice::CSlice;
use crate::eh_artiq;
use crate::rpc::send_args;
@ -36,12 +36,12 @@ pub extern fn rpc_recv(slot: *mut ()) -> usize {
Message::RpcRecvReply(Ok(alloc_size)) => alloc_size,
Message::RpcRecvReply(Err(exception)) => unsafe {
eh_artiq::raise(&eh_artiq::Exception {
name: exception.name.as_bytes().as_c_slice(),
file: exception.file.as_bytes().as_c_slice(),
id: exception.id,
file: CSlice::new(exception.file as *const u8, usize::MAX),
line: exception.line as u32,
column: exception.column as u32,
function: exception.function.as_bytes().as_c_slice(),
message: exception.message.as_bytes().as_c_slice(),
function: CSlice::new(exception.function as *const u8, usize::MAX),
message: CSlice::new(exception.message as *const u8, usize::MAX),
param: exception.param
})
},

View File

@ -11,120 +11,43 @@
extern crate alloc;
use core::{cmp, str};
use log::{info, warn, error};
use libboard_zynq::{timer::GlobalTimer, mpcore, gic, slcr};
use libboard_zynq::{timer::GlobalTimer, mpcore, gic};
use libasync::{task, block_async};
use libsupport_zynq::ram;
use nb;
use void::Void;
use embedded_hal::blocking::delay::DelayMs;
use libconfig::Config;
use libregister::RegisterW;
use libcortex_a9::l2c::enable_l2_cache;
use libboard_artiq::{logger, identifier_read, init_gateware, pl};
mod proto_core_io;
mod proto_async;
mod comms;
mod rpc;
#[path = "../../../build/pl.rs"]
mod pl;
#[cfg(ki_impl = "csr")]
#[path = "rtio_csr.rs"]
mod rtio;
#[cfg(ki_impl = "acp")]
#[path = "rtio_acp.rs"]
mod rtio;
mod rtio_mgt;
mod rtio_clocking;
mod kernel;
mod moninj;
mod eh_artiq;
mod panic;
mod logger;
mod mgmt;
mod analyzer;
mod irq;
mod i2c;
fn init_gateware() {
// Set up PS->PL clocks
slcr::RegisterBlock::unlocked(|slcr| {
// As we are touching the mux, the clock may glitch, so reset the PL.
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
.fpga0_out_rst(true)
.fpga1_out_rst(true)
.fpga2_out_rst(true)
.fpga3_out_rst(true)
);
slcr.fpga0_clk_ctrl.write(
slcr::Fpga0ClkCtrl::zeroed()
.src_sel(slcr::PllSource::IoPll)
.divisor0(8)
.divisor1(1)
);
slcr.fpga_rst_ctrl.write(
slcr::FpgaRstCtrl::zeroed()
);
});
}
static mut SEEN_ASYNC_ERRORS: u8 = 0;
fn identifier_read(buf: &mut [u8]) -> &str {
unsafe {
pl::csr::identifier::address_write(0);
let len = pl::csr::identifier::data_read();
let len = cmp::min(len, buf.len() as u8);
for i in 0..len {
pl::csr::identifier::address_write(1 + i);
buf[i as usize] = pl::csr::identifier::data_read();
}
str::from_utf8_unchecked(&buf[..len as usize])
}
}
fn init_rtio(timer: &mut GlobalTimer, cfg: &Config) {
let clock_sel =
if let Ok(rtioclk) = cfg.read_str("rtioclk") {
match rtioclk.as_ref() {
"internal" => {
info!("using internal RTIO clock");
0
},
"external" => {
info!("using external RTIO clock");
1
},
other => {
warn!("RTIO clock specification '{}' not recognized", other);
info!("using internal RTIO clock");
0
},
}
} else {
info!("using internal RTIO clock (default)");
0
};
loop {
unsafe {
pl::csr::rtio_crg::pll_reset_write(1);
pl::csr::rtio_crg::clock_sel_write(clock_sel);
pl::csr::rtio_crg::pll_reset_write(0);
}
timer.delay_ms(1);
let locked = unsafe { pl::csr::rtio_crg::pll_locked_read() != 0 };
if locked {
info!("RTIO PLL locked");
break;
} else {
warn!("RTIO PLL failed to lock, retrying...");
timer.delay_ms(500);
}
}
unsafe {
pl::csr::rtio_core::reset_phy_write(1);
}
pub unsafe fn get_async_errors() -> u8 {
let errors = SEEN_ASYNC_ERRORS;
SEEN_ASYNC_ERRORS = 0;
errors
}
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
@ -154,11 +77,14 @@ async fn report_async_rtio_errors() {
error!("RTIO sequence error involving channel {}",
pl::csr::rtio_core::sequence_error_channel_read());
}
SEEN_ASYNC_ERRORS = errors;
pl::csr::rtio_core::async_error_write(errors);
}
}
}
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
#[no_mangle]
@ -191,7 +117,8 @@ pub fn main_core0() {
}
};
init_rtio(&mut timer, &cfg);
rtio_clocking::init(&mut timer, &cfg);
task::spawn(report_async_rtio_errors());
comms::main(timer, cfg);

View File

@ -6,7 +6,7 @@ use core::cell::RefCell;
use alloc::{rc::Rc, vec::Vec, string::String};
use log::{self, info, debug, warn, error, LevelFilter};
use crate::logger::{BufferLogger, LogBufferRef};
use libboard_artiq::logger::{BufferLogger, LogBufferRef};
use crate::proto_async::*;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
@ -121,7 +121,7 @@ async fn handle_connection(
loop {
let msg = read_i8(stream).await;
if let Err(smoltcp::Error::Illegal) = msg {
if let Err(smoltcp::Error::Finished) = msg {
return Ok(());
}
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;
@ -194,7 +194,7 @@ async fn handle_connection(
},
Request::ConfigWrite => {
let key = read_key(stream).await?;
warn!("write key: {}", key);
debug!("write key: {}", key);
let len = read_i32(stream).await?;
let len = if len <= 0 {
0

View File

@ -1,25 +1,26 @@
use core::fmt;
use alloc::collections::BTreeMap;
use core::{fmt, cell::RefCell};
use alloc::{collections::BTreeMap, rc::Rc};
use log::{debug, info, warn};
use void::Void;
use libboard_artiq::drtio_routing;
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
use libasync::{task, smoltcp::TcpStream, block_async, nb};
use libcortex_a9::mutex::Mutex;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use futures::{pin_mut, select_biased, FutureExt};
use crate::proto_async::*;
use crate::pl::csr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
NetworkError(smoltcp::Error),
UnexpectedPattern,
UnrecognizedPacket,
UnrecognizedPacket
}
pub type Result<T> = core::result::Result<T, Error>;
@ -54,53 +55,131 @@ enum DeviceMessage {
InjectionStatus = 1
}
fn read_probe(channel: i32, probe: i8) -> i32 {
unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe as _);
csr::rtio_moninj::mon_value_update_write(1);
csr::rtio_moninj::mon_value_read() as i32
#[cfg(has_drtio)]
mod remote_moninj {
use super::*;
use libboard_artiq::drtioaux_async;
use crate::rtio_mgt::drtio;
use log::error;
pub async fn read_probe(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, probe: i8) -> i64 {
let reply = drtio::aux_transact(aux_mutex, linkno, &drtioaux_async::Packet::MonitorRequest {
destination: destination,
channel: channel as _,
probe: probe as _},
timer).await;
match reply {
Ok(drtioaux_async::Packet::MonitorReply { value }) => return value as i64,
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
Err("link went down") => { debug!("link is down"); },
Err(e) => error!("aux packet error ({})", e)
}
0
}
pub async fn inject(aux_mutex: &Rc<Mutex<bool>>, _timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8, value: i8) {
let _lock = aux_mutex.lock();
drtioaux_async::send(linkno, &drtioaux_async::Packet::InjectionRequest {
destination: destination,
channel: channel as _,
overrd: overrd as _,
value: value as _
}).await.unwrap();
}
pub async fn read_injection_status(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8) -> i8 {
let reply = drtio::aux_transact(aux_mutex,
linkno,
&drtioaux_async::Packet::InjectionStatusRequest {
destination: destination,
channel: channel as _,
overrd: overrd as _},
timer).await;
match reply {
Ok(drtioaux_async::Packet::InjectionStatusReply { value }) => return value as i8,
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
Err("link went down") => { debug!("link is down"); },
Err(e) => error!("aux packet error ({})", e)
}
0
}
}
fn inject(channel: i32, overrd: i8, value: i8) {
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd as _);
csr::rtio_moninj::inj_value_write(value as _);
mod local_moninj {
use libboard_artiq::pl::csr;
pub fn read_probe(channel: i32, probe: i8) -> i64 {
unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe as _);
csr::rtio_moninj::mon_value_update_write(1);
csr::rtio_moninj::mon_value_read() as i64
}
}
pub fn inject(channel: i32, overrd: i8, value: i8) {
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd as _);
csr::rtio_moninj::inj_value_write(value as _);
}
}
pub fn read_injection_status(channel: i32, overrd: i8) -> i8 {
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd as _);
csr::rtio_moninj::inj_value_read() as i8
}
}
}
fn read_injection_status(channel: i32, overrd: i8) -> i8 {
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd as _);
csr::rtio_moninj::inj_value_read() as i8
}
#[cfg(has_drtio)]
macro_rules! dispatch {
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
let destination = ($channel >> 16) as u8;
let channel = $channel;
let hop = $routing_table.0[destination as usize][0];
if hop == 0 {
local_moninj::$func(channel.into(), $($param, )*)
} else {
let linkno = hop - 1 as u8;
remote_moninj::$func($aux_mutex, $timer, linkno, destination, channel, $($param, )*).await
}
}}
}
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()> {
#[cfg(not(has_drtio))]
macro_rules! dispatch {
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
let channel = $channel as u16;
local_moninj::$func(channel.into(), $($param, )*)
}}
}
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &drtio_routing::RoutingTable) -> Result<()> {
if !expect(&stream, b"ARTIQ moninj\n").await? {
return Err(Error::UnexpectedPattern);
}
stream.send_slice("e".as_bytes()).await?;
let mut probe_watch_list: BTreeMap<(i32, i8), Option<i32>> = BTreeMap::new();
let mut probe_watch_list: BTreeMap<(i32, i8), Option<i64>> = BTreeMap::new();
let mut inject_watch_list: BTreeMap<(i32, i8), Option<i8>> = BTreeMap::new();
let mut next_check = Milliseconds(0);
let mut next_check = timer.get_time();
let timeout = |next_check: Milliseconds| -> nb::Result<(), Void> {
if timer.get_time() < next_check {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
};
loop {
// TODO: we don't need fuse() here.
// remove after https://github.com/rust-lang/futures-rs/issues/1989 lands
let read_message_f = read_i8(&stream).fuse();
let next_check_c = next_check.clone();
let timeout = || -> nb::Result<(), Void> {
if timer.get_time() < next_check_c {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
};
let timeout_f = block_async!(timeout()).fuse();
let timeout_f = block_async!(timeout(next_check_c)).fuse();
pin_mut!(read_message_f, timeout_f);
select_biased! {
message = read_message_f => {
@ -135,13 +214,13 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
let channel = read_i32(&stream).await?;
let overrd = read_i8(&stream).await?;
let value = read_i8(&stream).await?;
inject(channel, overrd, value);
dispatch!(timer, _aux_mutex, _routing_table, channel, inject, overrd, value);
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
},
HostMessage::GetInjectionStatus => {
let channel = read_i32(&stream).await?;
let overrd = read_i8(&stream).await?;
let value = read_injection_status(channel, overrd);
let value = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
write_i32(&stream, channel).await?;
write_i8(&stream, overrd).await?;
@ -151,17 +230,17 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
},
_ = timeout_f => {
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
let current = read_probe(channel, probe);
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_probe, probe);
if previous.is_none() || previous.unwrap() != current {
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
write_i32(&stream, channel).await?;
write_i8(&stream, probe).await?;
write_i32(&stream, current).await?;
write_i64(&stream, current).await?;
*previous = Some(current);
}
}
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
let current = read_injection_status(channel, overrd);
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
if previous.is_none() || previous.unwrap() != current {
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
write_i32(&stream, channel).await?;
@ -170,21 +249,24 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
*previous = Some(current);
}
}
next_check = next_check + Milliseconds(200);
next_check = timer.get_time() + Milliseconds(200);
}
}
}
}
pub fn start(timer: GlobalTimer) {
pub fn start(timer: GlobalTimer, aux_mutex: Rc<Mutex<bool>>, routing_table: Rc<RefCell<drtio_routing::RoutingTable>>) {
task::spawn(async move {
loop {
let aux_mutex = aux_mutex.clone();
let routing_table = routing_table.clone();
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
task::spawn(async move {
info!("received connection");
let result = handle_connection(&stream, timer).await;
let routing_table = routing_table.borrow();
let result = handle_connection(&stream, timer, &aux_mutex, &routing_table).await;
match result {
Err(Error::NetworkError(smoltcp::Error::Illegal)) => info!("peer closed connection"),
Err(Error::NetworkError(smoltcp::Error::Finished)) => info!("peer closed connection"),
Err(error) => warn!("connection terminated: {}", error),
_ => (),
}

View File

@ -10,19 +10,24 @@ use libasync::smoltcp::TcpStream;
use alloc::boxed::Box;
use async_recursion::async_recursion;
use crate::proto_core_io::ProtoWrite;
use io::proto::ProtoWrite;
use crate::proto_async;
use self::tag::{Tag, TagIterator, split_tag};
#[inline]
fn alignment_offset(alignment: isize, ptr: isize) -> isize {
(alignment - ptr % alignment) % alignment
}
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
let alignment = core::mem::align_of::<T>() as isize;
let fix = (alignment - (ptr as isize) % alignment) % alignment;
let fix = alignment_offset(alignment, ptr as isize);
((ptr as isize) + fix) as *const T
}
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
let alignment = core::mem::align_of::<T>() as isize;
let fix = (alignment - (ptr as isize) % alignment) % alignment;
let fix = alignment_offset(alignment, ptr as isize);
((ptr as isize) + fix) as *mut T
}
@ -66,6 +71,7 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
})
}
Tag::Tuple(it, arity) => {
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize));
let mut it = it.clone();
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
@ -76,21 +82,31 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
Tag::List(it) => {
#[repr(C)]
struct List { elements: *mut (), length: u32 }
consume_value!(List, |ptr| {
consume_value!(*mut List, |ptr| {
let length = proto_async::read_i32(stream).await? as usize;
(*ptr).length = length as u32;
let tag = it.clone().next().expect("truncated tag");
let mut data = alloc(tag.size() * length as usize).await;
let data_size = tag.size() * length as usize +
match tag {
Tag::Int64 | Tag::Float64 => 4,
_ => 0
};
let data = alloc(data_size + 8).await as *mut u8;
*ptr = data as *mut List;
let ptr = data as *mut List;
let data = data.offset(8);
let alignment = tag.alignment();
let mut data = data.offset(alignment_offset(alignment as isize, data as isize)) as *mut ();
(*ptr).length = length as u32;
(*ptr).elements = data;
match tag {
Tag::Bool => {
let ptr = align_ptr_mut::<u8>(data);
let ptr = data as *mut u8;
let dest = core::slice::from_raw_parts_mut(ptr, length);
proto_async::read_chunk(stream, dest).await?;
},
Tag::Int32 => {
let ptr = align_ptr_mut::<u32>(data);
let ptr = data as *mut u32;
// reading as raw bytes and do endianness conversion later
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
proto_async::read_chunk(stream, dest).await?;
@ -99,7 +115,7 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
NativeEndian::from_slice_u32(dest);
},
Tag::Int64 | Tag::Float64 => {
let ptr = align_ptr_mut::<u64>(data);
let ptr = data as *mut u64;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
proto_async::read_chunk(stream, dest).await?;
drop(dest);
@ -125,18 +141,25 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
}
let elt_tag = it.clone().next().expect("truncated tag");
*buffer = alloc(elt_tag.size() * total_len as usize).await;
let data_size = elt_tag.size() * total_len as usize +
match elt_tag {
Tag::Int64 | Tag::Float64 => 4,
_ => 0
};
let mut data = alloc(data_size).await;
let alignment = tag.alignment();
data = data.offset(alignment_offset(alignment as isize, data as isize));
*buffer = data;
let length = total_len as usize;
let mut data = *buffer;
match elt_tag {
Tag::Bool => {
let ptr = align_ptr_mut::<u8>(data);
let ptr = data as *mut u8;
let dest = core::slice::from_raw_parts_mut(ptr, length);
proto_async::read_chunk(stream, dest).await?;
},
Tag::Int32 => {
let ptr = align_ptr_mut::<u32>(data);
let ptr = data as *mut u32;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
proto_async::read_chunk(stream, dest).await?;
drop(dest);
@ -144,7 +167,7 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
NativeEndian::from_slice_u32(dest);
},
Tag::Int64 | Tag::Float64 => {
let ptr = align_ptr_mut::<u64>(data);
let ptr = data as *mut u64;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
proto_async::read_chunk(stream, dest).await?;
drop(dest);
@ -161,6 +184,7 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
})
}
Tag::Range(it) => {
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize));
let tag = it.clone().next().expect("truncated tag");
recv_value(stream, tag, data, alloc).await?;
recv_value(stream, tag, data, alloc).await?;
@ -229,11 +253,11 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
Tag::List(it) => {
#[repr(C)]
struct List { elements: *const (), length: u32 }
consume_value!(List, |ptr| {
let length = (*ptr).length as isize;
consume_value!(&List, |ptr| {
let length = (**ptr).length as isize;
writer.write_u32((*ptr).length)?;
let tag = it.clone().next().expect("truncated tag");
let mut data = (*ptr).elements;
let mut data = (**ptr).elements;
writer.write_u8(tag.as_u8())?;
match tag {
Tag::Bool => {
@ -407,7 +431,35 @@ mod tag {
}
}
pub fn alignment(self) -> usize {
use cslice::CSlice;
match self {
Tag::None => 1,
Tag::Bool => core::mem::align_of::<u8>(),
Tag::Int32 => core::mem::align_of::<i32>(),
Tag::Int64 => core::mem::align_of::<i64>(),
Tag::Float64 => core::mem::align_of::<f64>(),
// struct type: align to largest element
Tag::Tuple(it, arity) => {
let it = it.clone();
it.take(arity.into()).map(|t| t.alignment()).max().unwrap()
},
Tag::Range(it) => {
let it = it.clone();
it.take(3).map(|t| t.alignment()).max().unwrap()
}
// CSlice basically
Tag::Bytes | Tag::String | Tag::ByteArray =>
core::mem::align_of::<CSlice<()>>(),
// array buffer is allocated, so no need for alignment first
Tag::List(_) | Tag::Array(_, _) => 1,
// will not be sent from the host
_ => unreachable!("unexpected tag from host")
}
}
pub fn size(self) -> usize {
use super::alignment_offset;
match self {
Tag::None => 0,
Tag::Bool => 1,
@ -423,10 +475,12 @@ mod tag {
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
size += tag.size();
// includes padding
size += alignment_offset(tag.alignment() as isize, size as isize) as usize;
}
size
}
Tag::List(_) => 8,
Tag::List(_) => 4,
Tag::Array(_, num_dims) => 4 * (1 + num_dims as usize),
Tag::Range(it) => {
let tag = it.clone().next().expect("truncated tag");
@ -445,10 +499,23 @@ mod tag {
impl<'a> TagIterator<'a> {
pub fn new(data: &'a [u8]) -> TagIterator<'a> {
TagIterator { data: data }
TagIterator { data }
}
pub fn next(&mut self) -> Option<Tag<'a>> {
fn sub(&mut self, count: u8) -> TagIterator<'a> {
let data = self.data;
for _ in 0..count {
self.next().expect("truncated tag");
}
TagIterator { data: &data[..(data.len() - self.data.len())] }
}
}
impl<'a> core::iter::Iterator for TagIterator<'a> {
type Item = Tag<'a>;
fn next(&mut self) -> Option<Tag<'a>> {
if self.data.len() == 0 {
return None
}
@ -481,14 +548,6 @@ mod tag {
_ => unreachable!()
})
}
fn sub(&mut self, count: u8) -> TagIterator<'a> {
let data = self.data;
for _ in 0..count {
self.next().expect("truncated tag");
}
TagIterator { data: &data[..(data.len() - self.data.len())] }
}
}
impl<'a> fmt::Display for TagIterator<'a> {

View File

@ -57,11 +57,6 @@ pub extern fn init() {
}
}
pub extern fn get_destination_status(destination: i32) -> bool {
// TODO
destination == 0
}
pub extern fn get_counter() -> i64 {
unsafe {
csr::rtio::counter_update_write(1);

View File

@ -0,0 +1,254 @@
use log::{info, warn, error};
use libboard_zynq::timer::GlobalTimer;
use embedded_hal::blocking::delay::DelayMs;
use libconfig::Config;
use libboard_artiq::pl;
#[cfg(has_si5324)]
use libboard_zynq::i2c::I2c;
#[cfg(has_si5324)]
use crate::i2c;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
#[derive(Debug, PartialEq, Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum RtioClock {
Default,
Int_125,
Int_100,
Int_150,
Ext0_Bypass,
Ext0_Synth0_10to125,
Ext0_Synth0_100to125,
Ext0_Synth0_125to125,
}
#[allow(unreachable_code)]
fn get_rtio_clock_cfg(cfg: &Config) -> RtioClock {
let mut res = RtioClock::Default;
if let Ok(clk) = cfg.read_str("rtio_clock") {
res = match clk.as_ref() {
"int_125" => RtioClock::Int_125,
"int_100" => RtioClock::Int_100,
"int_150" => RtioClock::Int_150,
"ext0_bypass" => RtioClock::Ext0_Bypass,
"ext0_bypass_125" => RtioClock::Ext0_Bypass,
"ext0_bypass_100" => RtioClock::Ext0_Bypass,
"ext0_synth0_10to125" => RtioClock::Ext0_Synth0_10to125,
"ext0_synth0_100to125" => RtioClock::Ext0_Synth0_100to125,
"ext0_synth0_125to125" => RtioClock::Ext0_Synth0_125to125,
_ => {
warn!("Unrecognised rtio_clock setting. Falling back to default.");
RtioClock::Default
}
};
}
else {
warn!("error reading configuration. Falling back to default.");
}
if res == RtioClock::Default {
#[cfg(rtio_frequency="100.0")]
{
warn!("Using default configuration - internal 100MHz RTIO clock.");
return RtioClock::Int_100;
}
#[cfg(rtio_frequency="125.0")]
{
warn!("Using default configuration - internal 125MHz RTIO clock.");
return RtioClock::Int_125;
}
// anything else
{
warn!("Using default configuration - internal 125MHz RTIO clock.");
return RtioClock::Int_125;
}
}
res
}
fn init_rtio(timer: &mut GlobalTimer, _clk: RtioClock) {
#[cfg(has_rtio_crg_clock_sel)]
let clock_sel = match _clk {
RtioClock::Ext0_Bypass => {
info!("Using bypassed external clock");
1
},
RtioClock::Int_125 => {
info!("Using internal RTIO clock");
0
},
_ => {
warn!("rtio_clock setting '{:?}' is not supported. Using default internal RTIO clock instead", _clk);
0
}
};
unsafe {
pl::csr::rtio_crg::pll_reset_write(1);
#[cfg(has_rtio_crg_clock_sel)]
pl::csr::rtio_crg::clock_sel_write(clock_sel);
pl::csr::rtio_crg::pll_reset_write(0);
}
timer.delay_ms(1);
let locked = unsafe { pl::csr::rtio_crg::pll_locked_read() != 0 };
if locked {
info!("RTIO PLL locked");
} else {
error!("RTIO PLL failed to lock");
}
unsafe {
pl::csr::rtio_core::reset_phy_write(1);
}
}
#[cfg(has_drtio)]
fn init_drtio(timer: &mut GlobalTimer)
{
unsafe {
pl::csr::drtio_transceiver::stable_clkin_write(1);
}
timer.delay_ms(2); // wait for CPLL/QPLL lock
unsafe {
pl::csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
}
#[cfg(has_si5324)]
fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) {
let (si5324_settings, si5324_ref_input) = match clk {
RtioClock::Ext0_Synth0_10to125 => { // 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW
info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 300,
n31 : 6,
n32 : 6,
bwsel : 4,
crystal_ref: false
},
si5324::Input::Ckin1
)
},
RtioClock::Ext0_Synth0_100to125 => { // 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth
info!("using 100MHz reference to make 125MHz RTIO clock with PLL");
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 260,
n31 : 52,
n32 : 52,
bwsel : 4,
crystal_ref: false
},
si5324::Input::Ckin1
)
},
RtioClock::Ext0_Synth0_125to125 => { // 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth
info!("using 125MHz reference to make 125MHz RTIO clock with PLL");
(
si5324::FrequencySettings {
n1_hs : 5,
nc1_ls : 8,
n2_hs : 7,
n2_ls : 360,
n31 : 63,
n32 : 63,
bwsel : 4,
crystal_ref: false
},
si5324::Input::Ckin1
)
},
RtioClock::Int_150 => { // 150MHz output, from crystal
info!("using internal 150MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 9,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 33732,
n31 : 7139,
n32 : 7139,
bwsel : 3,
crystal_ref: true
},
si5324::Input::Ckin2
)
},
RtioClock::Int_100 => { // 100MHz output, from crystal.
info!("using internal 100MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 9,
nc1_ls : 6,
n2_hs : 10,
n2_ls : 33732,
n31 : 7139,
n32 : 7139,
bwsel : 3,
crystal_ref: true
},
si5324::Input::Ckin2
)
},
RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz
info!("using internal 125MHz RTIO clock");
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
},
si5324::Input::Ckin2
)
},
_ => { // same setting as Int_125, but fallback to default
warn!("rtio_clock setting '{:?}' is unsupported. Falling back to default internal 125MHz RTIO clock.", clk);
(
si5324::FrequencySettings {
n1_hs : 10,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 19972,
n31 : 4565,
n32 : 4565,
bwsel : 4,
crystal_ref: true
},
si5324::Input::Ckin2
)
}
};
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
}
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
let clk = get_rtio_clock_cfg(cfg);
#[cfg(has_si5324)]
{
let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
let si5324_ext_input = si5324::Input::Ckin1;
match clk {
RtioClock::Ext0_Bypass => si5324::bypass(i2c, si5324_ext_input, timer).expect("cannot bypass Si5324"),
_ => setup_si5324(i2c, timer, clk),
}
}
#[cfg(has_drtio)]
init_drtio(timer);
init_rtio(timer, clk);
}

View File

@ -25,11 +25,6 @@ pub extern fn init() {
}
}
pub extern fn get_destination_status(destination: i32) -> bool {
// TODO
destination == 0
}
pub extern fn get_counter() -> i64 {
unsafe {
csr::rtio::counter_update_write(1);

355
src/runtime/src/rtio_mgt.rs Normal file
View File

@ -0,0 +1,355 @@
use core::cell::RefCell;
use alloc::rc::Rc;
use libboard_zynq::timer::GlobalTimer;
use libboard_artiq::{pl::csr, drtio_routing};
use libcortex_a9::mutex::Mutex;
#[cfg(has_drtio)]
pub mod drtio {
use super::*;
use libboard_artiq::drtioaux_async;
use libboard_artiq::drtioaux_async::Packet;
use libboard_artiq::drtioaux::Error;
use log::{warn, error, info};
use embedded_hal::blocking::delay::DelayMs;
use libasync::{task, delay};
use libboard_zynq::time::Milliseconds;
pub fn startup(aux_mutex: &Rc<Mutex<bool>>,
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
let aux_mutex = aux_mutex.clone();
let routing_table = routing_table.clone();
let up_destinations = up_destinations.clone();
task::spawn(async move {
let routing_table = routing_table.borrow();
link_task(&aux_mutex, &routing_table, &up_destinations, timer).await;
});
}
async fn link_rx_up(linkno: u8) -> bool {
let linkno = linkno as usize;
unsafe {
(csr::DRTIO[linkno].rx_up_read)() == 1
}
}
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, &'static str> {
if !link_rx_up(linkno).await {
return Err("link went down");
}
match drtioaux_async::recv_timeout(linkno, Some(timeout), timer).await {
Ok(packet) => return Ok(packet),
Err(Error::TimedOut) => return Err("timed out"),
Err(_) => return Err("aux packet error"),
}
}
pub async fn aux_transact(aux_mutex: &Mutex<bool>, linkno: u8, request: &Packet,
timer: GlobalTimer) -> Result<Packet, &'static str> {
if !link_rx_up(linkno).await {
return Err("link went down");
}
let _lock = aux_mutex.async_lock().await;
drtioaux_async::send(linkno, request).await.unwrap();
recv_aux_timeout(linkno, 200, timer).await
}
async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) {
let max_time = timer.get_time() + draining_time;
loop {
if timer.get_time() > max_time {
return;
} //could this be cut short?
let _ = drtioaux_async::recv(linkno).await;
}
}
async fn ping_remote(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, timer: GlobalTimer) -> u32 {
let mut count = 0;
loop {
if !link_rx_up(linkno).await {
return 0
}
count += 1;
if count > 100 {
return 0;
}
let reply = aux_transact(aux_mutex, linkno, &Packet::EchoRequest, timer).await;
match reply {
Ok(Packet::EchoReply) => {
// make sure receive buffer is drained
let draining_time = Milliseconds(200);
drain_buffer(linkno, draining_time, timer).await;
return count;
}
_ => {}
}
}
}
async fn sync_tsc(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, timer: GlobalTimer) -> Result<(), &'static str> {
let _lock = aux_mutex.async_lock().await;
unsafe {
(csr::DRTIO[linkno as usize].set_time_write)(1);
while (csr::DRTIO[linkno as usize].set_time_read)() == 1 {}
}
// TSCAck is the only aux packet that is sent spontaneously
// by the satellite, in response to a TSC set on the RT link.
let reply = recv_aux_timeout(linkno, 10000, timer).await?;
if reply == Packet::TSCAck {
return Ok(());
} else {
return Err("unexpected reply");
}
}
async fn load_routing_table(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, routing_table: &drtio_routing::RoutingTable,
timer: GlobalTimer) -> Result<(), &'static str> {
for i in 0..drtio_routing::DEST_COUNT {
let reply = aux_transact(aux_mutex, linkno, &Packet::RoutingSetPath {
destination: i as u8,
hops: routing_table.0[i]
}, timer).await?;
if reply != Packet::RoutingAck {
return Err("unexpected reply");
}
}
Ok(())
}
async fn set_rank(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, rank: u8, timer: GlobalTimer) -> Result<(), &'static str> {
let reply = aux_transact(aux_mutex, linkno, &Packet::RoutingSetRank {
rank: rank
}, timer).await?;
if reply != Packet::RoutingAck {
return Err("unexpected reply");
}
Ok(())
}
async fn init_buffer_space(destination: u8, linkno: u8) {
let linkno = linkno as usize;
unsafe {
(csr::DRTIO[linkno].destination_write)(destination);
(csr::DRTIO[linkno].force_destination_write)(1);
(csr::DRTIO[linkno].o_get_buffer_space_write)(1);
while (csr::DRTIO[linkno].o_wait_read)() == 1 {}
info!("[DEST#{}] buffer space is {}",
destination, (csr::DRTIO[linkno].o_dbg_buffer_space_read)());
(csr::DRTIO[linkno].force_destination_write)(0);
}
}
async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) {
let _lock = aux_mutex.async_lock().await;
match drtioaux_async::recv(linkno).await {
Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet),
Ok(None) => (),
Err(_) => warn!("[LINK#{}] aux packet error", linkno)
}
}
async fn process_local_errors(linkno: u8) {
let errors;
let linkidx = linkno as usize;
unsafe {
errors = (csr::DRTIO[linkidx].protocol_error_read)();
(csr::DRTIO[linkidx].protocol_error_write)(errors);
}
if errors != 0 {
error!("[LINK#{}] error(s) found (0x{:02x}):", linkno, errors);
if errors & 1 != 0 {
error!("[LINK#{}] received packet of an unknown type", linkno);
}
if errors & 2 != 0 {
error!("[LINK#{}] received truncated packet", linkno);
}
if errors & 4 != 0 {
error!("[LINK#{}] timeout attempting to get remote buffer space", linkno);
}
}
}
async fn destination_set_up(routing_table: &drtio_routing::RoutingTable,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
destination: u8, up: bool) {
let mut up_destinations = up_destinations.borrow_mut();
up_destinations[destination as usize] = up;
if up {
drtio_routing::interconnect_enable(routing_table, 0, destination);
info!("[DEST#{}] destination is up", destination);
} else {
drtio_routing::interconnect_disable(destination);
info!("[DEST#{}] destination is down", destination);
}
}
async fn destination_up(up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, destination: u8) -> bool {
let up_destinations = up_destinations.borrow();
up_destinations[destination as usize]
}
async fn destination_survey(aux_mutex: &Rc<Mutex<bool>>, routing_table: &drtio_routing::RoutingTable,
up_links: &[bool],
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
for destination in 0..drtio_routing::DEST_COUNT {
let hop = routing_table.0[destination][0];
let destination = destination as u8;
if hop == 0 {
/* local RTIO */
if !destination_up(up_destinations, destination).await {
destination_set_up(routing_table, up_destinations, destination, true).await;
}
} else if hop as usize <= csr::DRTIO.len() {
let linkno = hop - 1;
if destination_up(up_destinations, destination).await {
if up_links[linkno as usize] {
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest {
destination: destination
}, timer).await;
match reply {
Ok(Packet::DestinationDownReply) =>
destination_set_up(routing_table, up_destinations, destination, false).await,
Ok(Packet::DestinationOkReply) => (),
Ok(Packet::DestinationSequenceErrorReply { channel }) =>
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}", destination, channel),
Ok(Packet::DestinationCollisionReply { channel }) =>
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel),
Ok(Packet::DestinationBusyReply { channel }) =>
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel),
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
}
} else {
destination_set_up(routing_table, up_destinations, destination, false).await;
}
} else {
if up_links[linkno as usize] {
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest {
destination: destination
}, timer).await;
match reply {
Ok(Packet::DestinationDownReply) => (),
Ok(Packet::DestinationOkReply) => {
destination_set_up(routing_table, up_destinations, destination, true).await;
init_buffer_space(destination as u8, linkno).await;
},
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
}
}
}
}
}
}
pub async fn link_task(aux_mutex: &Rc<Mutex<bool>>,
routing_table: &drtio_routing::RoutingTable,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
let mut up_links = [false; csr::DRTIO.len()];
loop {
for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8;
if up_links[linkno as usize] {
/* link was previously up */
if link_rx_up(linkno).await {
process_unsolicited_aux(aux_mutex, linkno).await;
process_local_errors(linkno).await;
} else {
info!("[LINK#{}] link is down", linkno);
up_links[linkno as usize] = false;
}
} else {
/* link was previously down */
if link_rx_up(linkno).await {
info!("[LINK#{}] link RX became up, pinging", linkno);
let ping_count = ping_remote(aux_mutex, linkno, timer).await;
if ping_count > 0 {
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count);
up_links[linkno as usize] = true;
if let Err(e) = sync_tsc(aux_mutex, linkno, timer).await {
error!("[LINK#{}] failed to sync TSC ({})", linkno, e);
}
if let Err(e) = load_routing_table(aux_mutex, linkno, routing_table, timer).await {
error!("[LINK#{}] failed to load routing table ({})", linkno, e);
}
if let Err(e) = set_rank(aux_mutex, linkno, 1 as u8, timer).await {
error!("[LINK#{}] failed to set rank ({})", linkno, e);
}
info!("[LINK#{}] link initialization completed", linkno);
} else {
error!("[LINK#{}] ping failed", linkno);
}
}
}
}
destination_survey(aux_mutex, routing_table, &up_links, up_destinations, timer).await;
let mut countdown = timer.countdown();
delay(&mut countdown, Milliseconds(200)).await;
}
}
#[allow(dead_code)]
pub fn reset(aux_mutex: Rc<Mutex<bool>>, mut timer: GlobalTimer) {
for linkno in 0..csr::DRTIO.len() {
unsafe {
(csr::DRTIO[linkno].reset_write)(1);
}
}
timer.delay_ms(1);
for linkno in 0..csr::DRTIO.len() {
unsafe {
(csr::DRTIO[linkno].reset_write)(0);
}
}
for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8;
if task::block_on(link_rx_up(linkno)) {
let reply = task::block_on(aux_transact(&aux_mutex, linkno,
&Packet::ResetRequest, timer));
match reply {
Ok(Packet::ResetAck) => (),
Ok(_) => error!("[LINK#{}] reset failed, received unexpected aux packet", linkno),
Err(e) => error!("[LINK#{}] reset failed, aux packet error ({})", linkno, e)
}
}
}
}
}
#[cfg(not(has_drtio))]
pub mod drtio {
use super::*;
pub fn startup(_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
_up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _timer: GlobalTimer) {}
#[allow(dead_code)]
pub fn reset(_aux_mutex: Rc<Mutex<bool>>, mut _timer: GlobalTimer) {}
}
pub fn startup(aux_mutex: &Rc<Mutex<bool>>,
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer) {
drtio::startup(aux_mutex, routing_table, up_destinations, timer);
unsafe {
csr::rtio_core::reset_phy_write(1);
}
}
#[allow(dead_code)]
pub fn reset(aux_mutex: Rc<Mutex<bool>>, timer: GlobalTimer) {
unsafe {
csr::rtio_core::reset_write(1);
}
drtio::reset(aux_mutex, timer)
}

28
src/satman/Cargo.toml Normal file
View File

@ -0,0 +1,28 @@
[package]
authors = ["M-Labs"]
name = "satman"
version = "0.0.0"
build = "build.rs"
[features]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
default = ["target_zc706", ]
[build-dependencies]
build_zynq = { path = "../libbuild_zynq" }
[dependencies]
log = { version = "0.4", default-features = false }
embedded-hal = "0.2"
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["ipv6"]}
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["fat_lfn", "ipv6"] }
libboard_artiq = { path = "../libboard_artiq" }
unwind = { path = "../libunwind" }
libc = { path = "../libc" }

6
src/satman/build.rs Normal file
View File

@ -0,0 +1,6 @@
extern crate build_zynq;
fn main() {
build_zynq::add_linker_script();
build_zynq::cfg();
}

659
src/satman/src/main.rs Normal file
View File

@ -0,0 +1,659 @@
#![no_std]
#![no_main]
#![feature(never_type, panic_info_message, asm, naked_functions)]
#![feature(alloc_error_handler)]
#[macro_use]
extern crate log;
extern crate embedded_hal;
extern crate libboard_zynq;
extern crate libboard_artiq;
extern crate libsupport_zynq;
extern crate libcortex_a9;
extern crate libregister;
extern crate unwind;
extern crate alloc;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
use libsupport_zynq::ram;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger, identifier_read, init_gateware};
use libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache};
use libregister::{RegisterW, RegisterR};
use embedded_hal::blocking::delay::DelayUs;
use core::sync::atomic::{AtomicBool, Ordering};
mod repeater;
fn drtiosat_reset(reset: bool) {
unsafe {
csr::drtiosat::reset_write(if reset { 1 } else { 0 });
}
}
fn drtiosat_reset_phy(reset: bool) {
unsafe {
csr::drtiosat::reset_phy_write(if reset { 1 } else { 0 });
}
}
fn drtiosat_link_rx_up() -> bool {
unsafe {
csr::drtiosat::rx_up_read() == 1
}
}
fn drtiosat_tsc_loaded() -> bool {
unsafe {
let tsc_loaded = csr::drtiosat::tsc_loaded_read() == 1;
if tsc_loaded {
csr::drtiosat::tsc_loaded_write(1);
}
tsc_loaded
}
}
#[cfg(has_drtio_routing)]
macro_rules! forward {
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr, $timer:expr) => {{
let hop = $routing_table.0[$destination as usize][$rank as usize];
if hop != 0 {
let repno = (hop - 1) as usize;
if repno < $repeaters.len() {
return $repeaters[repno].aux_forward($packet, $timer);
} else {
return Err(drtioaux::Error::RoutingError);
}
}
}}
}
#[cfg(not(has_drtio_routing))]
macro_rules! forward {
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr, $timer:expr) => {}
}
fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
packet: drtioaux::Packet, timer: &mut GlobalTimer, i2c: &mut I2c) -> Result<(), drtioaux::Error> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
// and u16 otherwise; hence the `as _` conversion.
match packet {
drtioaux::Packet::EchoRequest =>
drtioaux::send(0, &drtioaux::Packet::EchoReply),
drtioaux::Packet::ResetRequest => {
info!("resetting RTIO");
drtiosat_reset(true);
timer.delay_us(100);
drtiosat_reset(false);
for rep in _repeaters.iter() {
if let Err(e) = rep.rtio_reset(timer) {
error!("failed to issue RTIO reset ({:?})", e);
}
}
drtioaux::send(0, &drtioaux::Packet::ResetAck)
},
drtioaux::Packet::DestinationStatusRequest { destination: _destination } => {
#[cfg(has_drtio_routing)]
let hop = _routing_table.0[_destination as usize][*_rank as usize];
#[cfg(not(has_drtio_routing))]
let hop = 0;
if hop == 0 {
let errors;
unsafe {
errors = csr::drtiosat::rtio_error_read();
}
if errors & 1 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::sequence_error_channel_read();
csr::drtiosat::rtio_error_write(1);
}
drtioaux::send(0,
&drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
} else if errors & 2 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::collision_channel_read();
csr::drtiosat::rtio_error_write(2);
}
drtioaux::send(0,
&drtioaux::Packet::DestinationCollisionReply { channel })?;
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::busy_channel_read();
csr::drtiosat::rtio_error_write(4);
}
drtioaux::send(0,
&drtioaux::Packet::DestinationBusyReply { channel })?;
}
else {
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
}
}
#[cfg(has_drtio_routing)]
{
if hop != 0 {
let hop = hop as usize;
if hop <= csr::DRTIOREP.len() {
let repno = hop - 1;
match _repeaters[repno].aux_forward(&drtioaux::Packet::DestinationStatusRequest {
destination: _destination
}, timer) {
Ok(()) => (),
Err(drtioaux::Error::LinkDown) => drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?,
Err(e) => {
drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?;
error!("aux error when handling destination status request: {:?}", e);
},
}
} else {
drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?;
}
}
}
Ok(())
}
#[cfg(has_drtio_routing)]
drtioaux::Packet::RoutingSetPath { destination, hops } => {
_routing_table.0[destination as usize] = hops;
for rep in _repeaters.iter() {
if let Err(e) = rep.set_path(destination, &hops, timer) {
error!("failed to set path ({:?})", e);
}
}
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
#[cfg(has_drtio_routing)]
drtioaux::Packet::RoutingSetRank { rank } => {
*_rank = rank;
drtio_routing::interconnect_enable_all(_routing_table, rank);
let rep_rank = rank + 1;
for rep in _repeaters.iter() {
if let Err(e) = rep.set_rank(rep_rank, timer) {
error!("failed to set rank ({:?})", e);
}
}
info!("rank: {}", rank);
info!("routing table: {}", _routing_table);
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
#[cfg(not(has_drtio_routing))]
drtioaux::Packet::RoutingSetPath { destination: _, hops: _ } => {
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
#[cfg(not(has_drtio_routing))]
drtioaux::Packet::RoutingSetRank { rank: _ } => {
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
}
drtioaux::Packet::MonitorRequest { destination: _destination, channel: _channel, probe: _probe } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe);
csr::rtio_moninj::mon_value_update_write(1);
value = csr::rtio_moninj::mon_value_read() as u64;
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
let reply = drtioaux::Packet::MonitorReply { value: value };
drtioaux::send(0, &reply)
},
drtioaux::Packet::InjectionRequest { destination: _destination, channel: _channel,
overrd: _overrd, value: _value } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
csr::rtio_moninj::inj_value_write(value);
}
Ok(())
},
drtioaux::Packet::InjectionStatusRequest { destination: _destination,
channel: _channel, overrd: _overrd } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
value = csr::rtio_moninj::inj_value_read();
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
drtioaux::send(0, &drtioaux::Packet::InjectionStatusReply { value: value })
},
drtioaux::Packet::I2cStartRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.start().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cRestartRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.restart().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cStopRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.stop().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cWriteRequest { destination: _destination, busno: _busno, data } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
match i2c.write(data) {
Ok(ack) => drtioaux::send(0,
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
Err(_) => drtioaux::send(0,
&drtioaux::Packet::I2cWriteReply { succeeded: false, ack: false })
}
}
drtioaux::Packet::I2cReadRequest { destination: _destination, busno: _busno, ack } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
match i2c.read(ack) {
Ok(data) => drtioaux::send(0,
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
Err(_) => drtioaux::send(0,
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
}
}
drtioaux::Packet::I2cSwitchSelectRequest { destination: _destination, busno: _busno, address, mask } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let ch = match mask { //decode from mainline, PCA9548-centric API
0x00 => None,
0x01 => Some(0),
0x02 => Some(1),
0x04 => Some(2),
0x08 => Some(3),
0x10 => Some(4),
0x20 => Some(5),
0x40 => Some(6),
0x80 => Some(7),
_ => { return drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: false }); }
};
let succeeded = i2c.pca954x_select(address, ch).is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::SpiSetConfigRequest { destination: _destination, busno: _busno,
flags: _flags, length: _length, div: _div, cs: _cs } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
// todo: reimplement when/if SPI is available
//let succeeded = spi::set_config(busno, flags, length, div, cs).is_ok();
drtioaux::send(0,
&drtioaux::Packet::SpiBasicReply { succeeded: false })
},
drtioaux::Packet::SpiWriteRequest { destination: _destination, busno: _busno, data: _data } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
// todo: reimplement when/if SPI is available
//let succeeded = spi::write(busno, data).is_ok();
drtioaux::send(0,
&drtioaux::Packet::SpiBasicReply { succeeded: false })
}
drtioaux::Packet::SpiReadRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
// todo: reimplement when/if SPI is available
// match spi::read(busno) {
// Ok(data) => drtioaux::send(0,
// &drtioaux::Packet::SpiReadReply { succeeded: true, data: data }),
// Err(_) => drtioaux::send(0,
// &drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
// }
drtioaux::send(0,
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
}
_ => {
warn!("received unexpected aux packet");
Ok(())
}
}
}
fn process_aux_packets(repeaters: &mut [repeater::Repeater],
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8,
timer: &mut GlobalTimer, i2c: &mut I2c) {
let result =
drtioaux::recv(0).and_then(|packet| {
if let Some(packet) = packet {
process_aux_packet(repeaters, routing_table, rank, packet, timer, i2c)
} else {
Ok(())
}
});
match result {
Ok(()) => (),
Err(e) => warn!("aux packet error ({:?})", e)
}
}
fn drtiosat_process_errors() {
let errors;
unsafe {
errors = csr::drtiosat::protocol_error_read();
}
if errors & 1 != 0 {
error!("received packet of an unknown type");
}
if errors & 2 != 0 {
error!("received truncated packet");
}
if errors & 4 != 0 {
let destination;
unsafe {
destination = csr::drtiosat::buffer_space_timeout_dest_read();
}
error!("timeout attempting to get buffer space from CRI, destination=0x{:02x}", destination)
}
if errors & 8 != 0 {
let channel;
let timestamp_event;
let timestamp_counter;
unsafe {
channel = csr::drtiosat::underflow_channel_read();
timestamp_event = csr::drtiosat::underflow_timestamp_event_read() as i64;
timestamp_counter = csr::drtiosat::underflow_timestamp_counter_read() as i64;
}
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
}
if errors & 16 != 0 {
error!("write overflow");
}
unsafe {
csr::drtiosat::protocol_error_write(errors);
}
}
#[cfg(has_rtio_crg)]
fn init_rtio_crg(timer: &mut GlobalTimer) {
unsafe {
csr::rtio_crg::pll_reset_write(0);
}
timer.delay_us(150);
let locked = unsafe { csr::rtio_crg::pll_locked_read() != 0 };
if !locked {
error!("RTIO clock failed");
}
else {
info!("RTIO PLL locked");
}
}
#[cfg(not(has_rtio_crg))]
fn init_rtio_crg(_timer: &mut GlobalTimer) { }
fn hardware_tick(ts: &mut u64, timer: &mut GlobalTimer) {
let now = timer.get_time();
let mut ts_ms = Milliseconds(*ts);
if now > ts_ms {
ts_ms = now + Milliseconds(200);
*ts = ts_ms.0;
}
}
#[cfg(all(has_si5324, rtio_frequency = "125.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 5,
nc1_ls : 8,
n2_hs : 7,
n2_ls : 360,
n31 : 63,
n32 : 63,
bwsel : 4,
crystal_ref: true
};
#[cfg(all(has_si5324, rtio_frequency = "100.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 5,
nc1_ls : 10,
n2_hs : 10,
n2_ls : 250,
n31 : 50,
n32 : 50,
bwsel : 4,
crystal_ref: true
};
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
#[no_mangle]
pub extern fn main_core0() -> i32 {
enable_l2_cache(0x8);
let mut timer = GlobalTimer::start();
let buffer_logger = unsafe {
logger::BufferLogger::new(&mut LOG_BUFFER[..])
};
buffer_logger.set_uart_log_level(log::LevelFilter::Info);
buffer_logger.register();
log::set_max_level(log::LevelFilter::Info);
init_gateware();
info!("ARTIQ satellite manager starting...");
info!("gateware ident {}", identifier_read(&mut [0; 64]));
ram::init_alloc_core0();
let mut i2c = I2c::i2c0();
i2c.init().expect("I2C initialization failed");
#[cfg(has_si5324)]
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
timer.delay_us(1500); // wait for CPLL/QPLL lock
unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
}
init_rtio_crg(&mut timer);
#[cfg(has_drtio_routing)]
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
#[cfg(not(has_drtio_routing))]
let mut repeaters = [repeater::Repeater::default(); 0];
for i in 0..repeaters.len() {
repeaters[i] = repeater::Repeater::new(i as u8);
}
let mut routing_table = drtio_routing::RoutingTable::default_empty();
let mut rank = 1;
let mut hardware_tick_ts = 0;
loop {
while !drtiosat_link_rx_up() {
drtiosat_process_errors();
#[allow(unused_mut)]
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer);
}
hardware_tick(&mut hardware_tick_ts, &mut timer);
}
info!("uplink is up, switching to recovered clock");
#[cfg(has_siphaser)]
{
si5324::siphaser::select_recovered_clock(&mut i2c, true, &mut timer).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew");
}
drtioaux::reset(0);
drtiosat_reset(false);
drtiosat_reset_phy(false);
while drtiosat_link_rx_up() {
drtiosat_process_errors();
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank, &mut timer, &mut i2c);
#[allow(unused_mut)]
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer);
}
hardware_tick(&mut hardware_tick_ts, &mut timer);
if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink");
for rep in repeaters.iter() {
if let Err(e) = rep.sync_tsc(&mut timer) {
error!("failed to sync TSC ({:?})", e);
}
}
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::TSCAck) {
error!("aux packet error: {:?}", e);
}
}
}
drtiosat_reset_phy(true);
drtiosat_reset(true);
drtiosat_tsc_loaded();
info!("uplink is down, switching to local oscillator clock");
#[cfg(has_siphaser)]
si5324::siphaser::select_recovered_clock(&mut i2c, false, &mut timer).expect("failed to switch clocks");
}
}
extern "C" {
static mut __stack1_start: u32;
}
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
if MPIDR.read().cpu_id() == 1{
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);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
stdio::drop_uart();
}
loop {}
});
static mut PANICKED: [bool; 2] = [false; 2];
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
pub fn restart_core1() {
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
CORE1_RESTART.store(true, Ordering::Relaxed);
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
while CORE1_RESTART.load(Ordering::Relaxed) {
spin_lock_yield();
}
}
#[no_mangle]
pub fn main_core1() {
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
loop {}
}
#[no_mangle]
pub extern fn exception(_vect: u32, _regs: *const u32, pc: u32, ea: u32) {
fn hexdump(addr: u32) {
let addr = (addr - addr % 4) as *const u32;
let mut ptr = addr;
println!("@ {:08p}", ptr);
for _ in 0..4 {
print!("+{:04x}: ", ptr as usize - addr as usize);
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
print!("{:08x}\n", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
}
}
hexdump(pc);
hexdump(ea);
panic!("exception at PC 0x{:x}, EA 0x{:x}", pc, ea)
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_handler]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
let id = MPIDR.read().cpu_id() as usize;
print!("Core {} ", id);
unsafe {
if PANICKED[id] {
println!("nested panic!");
loop {}
}
PANICKED[id] = true;
}
print!("panic at ");
if let Some(location) = info.location() {
print!("{}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}
// linker symbols
extern "C" {
static __text_start: u32;
static __text_end: u32;
static __exidx_start: u32;
static __exidx_end: u32;
}
#[no_mangle]
extern fn dl_unwind_find_exidx(_pc: *const u32, len_ptr: *mut u32) -> *const u32 {
let length;
let start: *const u32;
unsafe {
length = (&__exidx_end as *const u32).offset_from(&__exidx_start) as u32;
start = &__exidx_start;
*len_ptr = length;
}
start
}

290
src/satman/src/repeater.rs Normal file
View File

@ -0,0 +1,290 @@
use libboard_artiq::{drtioaux, drtio_routing};
use libboard_zynq::timer::GlobalTimer;
#[cfg(has_drtio_routing)]
use libboard_artiq::{pl::csr};
#[cfg(has_drtio_routing)]
use libboard_zynq::time::Milliseconds;
#[cfg(has_drtio_routing)]
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
#[cfg(has_drtio_routing)]
fn rep_link_rx_up(repno: u8) -> bool {
let repno = repno as usize;
unsafe {
(csr::DRTIOREP[repno].rx_up_read)() == 1
}
}
#[cfg(has_drtio_routing)]
#[derive(Clone, Copy, PartialEq)]
enum RepeaterState {
Down,
SendPing { ping_count: u16 },
WaitPingReply { ping_count: u16, timeout: Milliseconds },
Up,
Failed
}
#[cfg(has_drtio_routing)]
impl Default for RepeaterState {
fn default() -> RepeaterState { RepeaterState::Down }
}
#[cfg(has_drtio_routing)]
#[derive(Clone, Copy, Default)]
pub struct Repeater {
repno: u8,
auxno: u8,
state: RepeaterState
}
#[cfg(has_drtio_routing)]
impl Repeater {
pub fn new(repno: u8) -> Repeater {
Repeater {
repno: repno,
auxno: repno + 1,
state: RepeaterState::Down
}
}
#[allow(dead_code)]
pub fn is_up(&self) -> bool {
self.state == RepeaterState::Up
}
pub fn service(&mut self, routing_table: &drtio_routing::RoutingTable, rank: u8,
timer: &mut GlobalTimer) {
self.process_local_errors();
match self.state {
RepeaterState::Down => {
if rep_link_rx_up(self.repno) {
info!("[REP#{}] link RX became up, pinging", self.repno);
self.state = RepeaterState::SendPing { ping_count: 0 };
}
}
RepeaterState::SendPing { ping_count } => {
if rep_link_rx_up(self.repno) {
drtioaux::send(self.auxno, &drtioaux::Packet::EchoRequest).unwrap();
self.state = RepeaterState::WaitPingReply {
ping_count: ping_count + 1,
timeout: timer.get_time() + Milliseconds(100)
}
} else {
error!("[REP#{}] link RX went down during ping", self.repno);
self.state = RepeaterState::Down;
}
}
RepeaterState::WaitPingReply { ping_count, timeout } => {
if rep_link_rx_up(self.repno) {
if let Ok(Some(drtioaux::Packet::EchoReply)) = drtioaux::recv(self.auxno) {
info!("[REP#{}] remote replied after {} packets", self.repno, ping_count);
self.state = RepeaterState::Up;
if let Err(e) = self.sync_tsc(timer) {
error!("[REP#{}] failed to sync TSC ({:?})", self.repno, e);
self.state = RepeaterState::Failed;
return;
}
if let Err(e) = self.load_routing_table(routing_table, timer) {
error!("[REP#{}] failed to load routing table ({:?})", self.repno, e);
self.state = RepeaterState::Failed;
return;
}
if let Err(e) = self.set_rank(rank + 1, timer) {
error!("[REP#{}] failed to set rank ({:?})", self.repno, e);
self.state = RepeaterState::Failed;
return;
}
} else {
if timer.get_time() > timeout {
if ping_count > 200 {
error!("[REP#{}] ping failed", self.repno);
self.state = RepeaterState::Failed;
} else {
self.state = RepeaterState::SendPing { ping_count: ping_count };
}
}
}
} else {
error!("[REP#{}] link RX went down during ping", self.repno);
self.state = RepeaterState::Down;
}
}
RepeaterState::Up => {
self.process_unsolicited_aux();
if !rep_link_rx_up(self.repno) {
info!("[REP#{}] link is down", self.repno);
self.state = RepeaterState::Down;
}
}
RepeaterState::Failed => {
if !rep_link_rx_up(self.repno) {
info!("[REP#{}] link is down", self.repno);
self.state = RepeaterState::Down;
}
}
}
}
fn process_unsolicited_aux(&self) {
match drtioaux::recv(self.auxno) {
Ok(Some(packet)) => warn!("[REP#{}] unsolicited aux packet: {:?}", self.repno, packet),
Ok(None) => (),
Err(_) => warn!("[REP#{}] aux packet error", self.repno)
}
}
fn process_local_errors(&self) {
let repno = self.repno as usize;
let errors;
unsafe {
errors = (csr::DRTIOREP[repno].protocol_error_read)();
}
if errors & 1 != 0 {
error!("[REP#{}] received packet of an unknown type", repno);
}
if errors & 2 != 0 {
error!("[REP#{}] received truncated packet", repno);
}
if errors & 4 != 0 {
let cmd;
let chan_sel;
unsafe {
cmd = (csr::DRTIOREP[repno].command_missed_cmd_read)();
chan_sel = (csr::DRTIOREP[repno].command_missed_chan_sel_read)();
}
error!("[REP#{}] CRI command missed, cmd={}, chan_sel=0x{:06x}", repno, cmd, chan_sel)
}
if errors & 8 != 0 {
let destination;
unsafe {
destination = (csr::DRTIOREP[repno].buffer_space_timeout_dest_read)();
}
error!("[REP#{}] timeout attempting to get remote buffer space, destination=0x{:02x}", repno, destination);
}
unsafe {
(csr::DRTIOREP[repno].protocol_error_write)(errors);
}
}
fn recv_aux_timeout(&self, timeout: u32, timer: &mut GlobalTimer) -> Result<drtioaux::Packet, drtioaux::Error> {
let max_time = timer.get_time() + Milliseconds(timeout.into());
loop {
if !rep_link_rx_up(self.repno) {
return Err(drtioaux::Error::LinkDown);
}
if timer.get_time() > max_time {
return Err(drtioaux::Error::TimedOut);
}
match drtioaux::recv(self.auxno) {
Ok(Some(packet)) => return Ok(packet),
Ok(None) => (),
Err(e) => return Err(e)
}
}
}
pub fn aux_forward(&self, request: &drtioaux::Packet, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Err(drtioaux::Error::LinkDown);
}
drtioaux::send(self.auxno, request).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
drtioaux::send(0, &reply).unwrap();
Ok(())
}
pub fn sync_tsc(&self, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Ok(());
}
let repno = self.repno as usize;
unsafe {
(csr::DRTIOREP[repno].set_time_write)(1);
while (csr::DRTIOREP[repno].set_time_read)() == 1 {}
}
// TSCAck is the only aux packet that is sent spontaneously
// by the satellite, in response to a TSC set on the RT link.
let reply = self.recv_aux_timeout(10000, timer)?;
if reply == drtioaux::Packet::TSCAck {
return Ok(());
} else {
return Err(drtioaux::Error::UnexpectedReply);
}
}
pub fn set_path(&self, destination: u8, hops: &[u8; drtio_routing::MAX_HOPS], timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Ok(());
}
drtioaux::send(self.auxno, &drtioaux::Packet::RoutingSetPath {
destination: destination,
hops: *hops
}).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
if reply != drtioaux::Packet::RoutingAck {
return Err(drtioaux::Error::UnexpectedReply);
}
Ok(())
}
pub fn load_routing_table(&self, routing_table: &drtio_routing::RoutingTable, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
for i in 0..drtio_routing::DEST_COUNT {
self.set_path(i as u8, &routing_table.0[i], timer)?;
}
Ok(())
}
pub fn set_rank(&self, rank: u8, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
if self.state != RepeaterState::Up {
return Ok(());
}
drtioaux::send(self.auxno, &drtioaux::Packet::RoutingSetRank {
rank: rank
}).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
if reply != drtioaux::Packet::RoutingAck {
return Err(drtioaux::Error::UnexpectedReply);
}
Ok(())
}
pub fn rtio_reset(&self, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
let repno = self.repno as usize;
unsafe { (csr::DRTIOREP[repno].reset_write)(1); }
timer.delay_us(100);
unsafe { (csr::DRTIOREP[repno].reset_write)(0); }
if self.state != RepeaterState::Up {
return Ok(());
}
drtioaux::send(self.auxno, &drtioaux::Packet::ResetRequest).unwrap();
let reply = self.recv_aux_timeout(200, timer)?;
if reply != drtioaux::Packet::ResetAck {
return Err(drtioaux::Error::UnexpectedReply);
}
Ok(())
}
}
#[cfg(not(has_drtio_routing))]
#[derive(Clone, Copy, Default)]
pub struct Repeater {
}
#[cfg(not(has_drtio_routing))]
impl Repeater {
pub fn new(_repno: u8) -> Repeater { Repeater::default() }
pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8, _timer: &mut GlobalTimer) { }
pub fn sync_tsc(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> { Ok(()) }
pub fn rtio_reset(&self, _timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> { Ok(()) }
}

View File

@ -1,8 +0,0 @@
let
pkgs = import <nixpkgs> {};
in
pkgs.fetchgit {
url = "https://git.m-labs.hk/M-Labs/zynq-rs.git";
rev = "78d58d17ec7906a6cadd1678576939d20612cf8f";
sha256 = "1y74i7j9kawhlq22zyicjsxldx9f7h4i22yabw1z4qga19zv6qjd";
}