Compare commits

...

165 Commits

Author SHA1 Message Date
afd0389bf3 rtio: reset satellites on rtio_init as well 2025-04-10 15:01:46 +08:00
f5139ee140 i2c io expander: fix the write changes 2025-04-01 14:37:37 +08:00
761b7c99cd i2c io expander: stop the bus even after an error 2025-03-29 16:37:24 +08:00
5982937ee6 flake: update dependencies 2025-03-29 16:37:06 +08:00
32889c11f1 i2c: dispatch remote i2c requests from kernel 2025-03-24 11:32:00 +08:00
bc7925989b cxp syscall: use usual order of ROI coordinates 2025-03-20 15:47:18 +08:00
b2256800fe cxp syscall: error if roiviewer over height limit 2025-03-20 15:47:18 +08:00
637163bbca flake: update dependencies 2025-03-19 17:25:37 +08:00
50ead76c09 i2c: use error enum, nacks cannot be ignored 2025-03-19 16:36:56 +08:00
267a1222ed cxp_grabber: add ROIViewer syscalls
api: add start roi viewer to flush the fifo and set the ROI coordinate
api: add roi viewer frame download
2025-03-14 12:12:58 +08:00
fe09e8615e flake: update dependencies 2025-03-14 12:11:57 +08:00
cb8fa20beb remove/allow dead code
CxpRead/CxpWrite functions are unused
and unplanned, removing. DrtioContext emits warn
because Clone is ignored in dead code analysis.
2025-03-14 11:30:31 +08:00
0efa450537 update cargo lockfile 2025-03-14 11:29:43 +08:00
4c59ab933b cargo fmt 2025-03-12 17:36:34 +08:00
504d7a8d5b flake: update dependencies 2025-03-12 17:28:35 +08:00
a577238bae bump rustfmt 2025-03-12 13:56:26 +08:00
1c90228f84 silence static mut ref warn for RECORDER 2025-03-12 13:56:26 +08:00
8129b8163c update futures, num-derive, async-recursion
bunched together to avoid depending on two separate `syn`
versions
2025-03-12 13:56:26 +08:00
95175b7168 silence dead code warns 2025-03-12 13:56:26 +08:00
3172625ba6 allow internal features
Signed-off-by: Simon Renblad <srenblad@m-labs.hk>
2025-03-12 13:56:26 +08:00
9e51599195 remove unnecessary imports 2025-03-12 13:56:26 +08:00
5445a1268c remove unnecessary mut 2025-03-12 13:56:26 +08:00
48ed2f188e fix dropping references 2025-03-12 13:56:26 +08:00
64d79de6c5 bump compiler_builtins to 0.1.108 2025-03-12 13:56:26 +08:00
be8f618d95 renamed const_btree_new feature 2025-03-12 13:56:26 +08:00
dca808b2e4 remove stabilized nll, static_nobundle flags 2025-03-12 13:56:26 +08:00
e4b85bf51a bump llvm to 18 2025-03-12 13:56:26 +08:00
c2c5367572 kasli-soc: support shuttler as a peripheral of kasli-soc satellite 2025-03-11 11:45:45 +08:00
290134e07e libio: add endianness as generic type param
Based on the API of ReadBytesExt, WriteBytesExt
from byteorder without the std dependency.
2025-03-10 15:44:33 +08:00
e07dad71d5 libksupport: add cxp syscall support
cxp: add read/write 32 bit value
cxp: add xml file download
lib: gate cxp import
api: add cxp syscalls
2025-03-04 14:46:39 +08:00
d0c34671d7 artiq error: add cxp error for syscall 2025-03-04 14:46:39 +08:00
82a1b38a19 runtime main: add cxp grabber support
main: init cxp phys
main: start cxp grabber task
2025-03-04 14:46:39 +08:00
db76dfc209 cxp_grabber fw: add cxp grabber handler
cxp_grabber: add cxp grabber tick task
cxp_grabber: add camera_connected and with_tag helper fns for syscall
libboard_artiq: add cxp_grabber.rs
2025-03-04 14:46:39 +08:00
b0ceac0f3a cxp_camera_setup fw: initalize camera
camera_setup: add setup error and error message
camera_setup: add camera discovery
camera_setup: add camera setup sequence
camera_setup: add placeholder HOST_CONNECTION_ID
libboard_artiq: add cxp_camera_setup.rs
2025-03-04 14:46:39 +08:00
a0673f13a1 cxp_packet fw: add cxp packet handler
packet: add receiving/sending control packet (w/ or w/o tags) handling
packet: add sending test packet
packet: add read/write register interface
packet: add read bytes for xml file download
libboard_artiq: add cxp_packet.rs
2025-03-04 14:46:39 +08:00
6086b867c8 cxp_ctrl fw: add cxp control packet parser
ctrl: add control packet error and error message
ctrl: add CXPCRC32 calculation
ctrl: use byteoder crate to handle endianness
ctrl: add control packer reader and writer
ctrl: add error correction for reading 4x char
libboard_artiq: add cxp_ctrl.rs
2025-03-04 14:46:39 +08:00
17f59e2353 cxp_phys fw: add CXP TRX phys support
phys: add tx & rx setup
tx: add csr to change linerate between 20.83/41.6Mpbs
rx: add GTX and QPLL DRP to change linerate 1.25-12.5Gbps
libboard artiq: add cxp_phys.rs
2025-03-04 14:46:39 +08:00
f080bee029 libboard_artiq cargo: add byteorder 2025-03-04 14:46:39 +08:00
599442dc0d flake: add zc706 cxp_4r_fmc variant build options 2025-03-04 14:46:39 +08:00
622bca24d1 zc706: add CXP_4R_FMC variant
zc706 base: expose cdr_clk (gt_refclk) for CXPGrabber
grabber: add cxp_4r_fmc pads
grabber: add CXPGrabber module
grabber: add csr and memory group for CXPGrabber
grabber: add CXPGrabber and user led to rtio_channel
grabber: add constraint for cd_cxp_gt_rx
2025-03-04 14:46:39 +08:00
bcab64f1ff gateware: add cxp_4r_fmc pinout 2025-03-04 14:46:39 +08:00
dfc731a4c1 flake: update dependencies 2025-03-04 14:46:39 +08:00
58ecf62921 libksupport: inline _resolve_channel_name helper
Signed-off-by: Simon Renblad <srenblad@m-labs.hk>
2025-03-04 14:19:50 +08:00
11134cdfd6 use MaybeUninit for LOGGER 2025-02-27 13:18:23 +08:00
2e99b9cf0d use MaybeUninit for I2C_BUS, create get_bus helper 2025-02-27 13:18:23 +08:00
4f79ee962c fix cargo-xbuild dependency 2025-02-17 15:15:33 +08:00
d4105b79e7 flake: update dependencies 2025-02-17 15:15:22 +08:00
1beb6fd944 cargo fmt 2025-02-13 17:46:15 +08:00
ce1c430fdc upgrade rustfmt required version 2025-02-13 17:45:29 +08:00
cf99700299 update cargo lockfile 2025-02-13 17:31:43 +08:00
9f1f349b29 export rust_eh_personality manually 2025-02-13 17:31:43 +08:00
98255ec25a prevent vectorizing copy_work_buffer 2025-02-13 17:31:43 +08:00
421033ef98 remove unused asm feature flag 2025-02-13 17:31:43 +08:00
c603a4ba12 remove allow incomplete features 2025-02-13 17:31:43 +08:00
529d7819a9 fix missing asm macro 2025-02-13 17:31:43 +08:00
1a1a7112ca silence target-feature warns 2025-02-13 17:31:43 +08:00
e524317eb9 bump to llvm14 2025-02-13 17:31:43 +08:00
d545feddbd flake: update dependencies 2025-02-13 17:31:28 +08:00
0e6da19406 ksupport: remove redundant enable_fpu 2025-02-11 20:16:24 +08:00
d0e2404311 flake: update zynq-rs 2025-02-11 20:15:55 +08:00
cd9f8e6d7c flake: update dependencies 2025-02-11 20:11:53 +08:00
4a2b28dcc3 flake: update dependencies 2025-02-06 20:16:34 +08:00
21a4a0b5dd core1: use C repr in attribute writeback 2025-02-06 14:34:18 +08:00
a82d356f52 flake: update dependencies 2025-02-05 16:58:48 +08:00
8b9bb38331 Do not apply offsets to null pointers 2025-02-03 12:04:02 +00:00
63157588bb flake: update dependencies 2025-01-15 21:17:34 +08:00
11f8675ad6 drtio: fix RTIO channel name resolution for remote channels 2025-01-15 13:12:14 +08:00
a0281e4989 cargo fmt 2024-12-23 12:51:10 +08:00
850e783139 bump rustfmt 2024-12-23 12:50:54 +08:00
8fd8cae9d5 update dependencies 2024-12-20 16:11:26 +08:00
7acf8af7f7 flake add outputHashes 2024-12-20 16:11:26 +08:00
13264d9992 update cargo lockfile 2024-12-20 16:11:26 +08:00
8d07f006f2 silence inline_const warns 2024-12-20 16:11:26 +08:00
97e15d51f2 remove unused abi-blacklist, force-unwind-tables 2024-12-20 16:11:26 +08:00
0021a01bdf use forked core_io, up nalgebra 2024-12-20 16:11:04 +08:00
16801a35f4 llvm11 -> llvm13 2024-12-18 17:06:39 +08:00
81eba30a29 prevent cursor r/w optimization 2024-12-18 17:06:39 +08:00
7d6d40a785 replace deprecated NoneError use 2024-12-18 17:06:39 +08:00
ffe3020788 replace const_in_array_expression with inline 2024-12-18 17:06:39 +08:00
8f510b5ca6 change to C-unwind interface 2024-12-18 17:06:38 +08:00
5582ca74d2 runtime: clean up unused imports 2024-12-17 15:52:17 +08:00
7c741d9c18 flake: update dependencies 2024-12-11 13:34:54 +08:00
922a03b807 drtio: restore copy_work_buffer for transmission 2024-12-11 12:09:21 +08:00
716a5924d1 kasli_soc: fix acpki import 2024-12-10 12:54:22 +08:00
4856cddb65 gateware: add extra ident info, source version 2024-12-10 12:54:22 +08:00
e1f493f3ca drtio: add InjectionRequest to expects_response 2024-11-26 14:58:24 +08:00
1f5ea41934 flake: update dependencies 2024-11-20 19:58:55 +08:00
7f83d56ef5 cargo fmt 2024-11-20 09:42:49 +08:00
1d431456f4 Fix DWARF parser treating catch blocks as unconditional
Signed-off-by: Jonathan Coates <jonathan.coates@oxionics.com>
2024-11-20 09:32:38 +08:00
b03e380c1e flake: update dependencies 2024-11-20 09:07:00 +08:00
47fc53c4bf drtio_tuple -> drtio_context 2024-11-18 13:13:10 +08:00
42eaecf9e1 remove debug message 2024-11-18 12:19:37 +08:00
beb7e6f994 cargo fmt 2024-11-18 12:19:37 +08:00
4502a47aa6 drtio_proto: add allocate step for flashing
This avoids reallocation while transfering binaries.
2024-11-18 12:19:37 +08:00
ac6b7d5cf0 satman: fix checksum error message 2024-11-18 12:19:37 +08:00
3019bc6123 runtime: check crc when flashing 2024-11-18 12:19:37 +08:00
95b8562812 cargo fmt 2024-11-18 12:19:37 +08:00
a13f5d02fa mgmt: supplementary tuple -> tuple struct 2024-11-18 12:19:37 +08:00
e52aa77068 cargo fmt 2024-11-18 12:19:37 +08:00
8e28d12ad0 runtime mgmt: avoid pull_log resource hog 2024-11-18 12:19:37 +08:00
47cddae04f runtime mgmt: avoid passing incomplete log to core_log 2024-11-18 12:19:37 +08:00
27a65df40e satman mgmt: fix uart log level change message 2024-11-18 12:19:37 +08:00
759cca3bfd satman mgmt: allow sliceable to consume log source 2024-11-18 12:19:37 +08:00
aadb6fc22d satman mgmt: get logger unconditionally 2024-11-18 12:19:37 +08:00
ae4d5a4228 mgmt: minor fix 2024-11-18 12:19:37 +08:00
6f1d727ca2 drtio-proto: avoid expecting response to drop link ack 2024-11-18 12:19:37 +08:00
7da5061f7e coremgmt: fix import/uses 2024-11-18 12:19:37 +08:00
47d418c69e coremgmt: remove unnecsaary cursors 2024-11-18 12:19:37 +08:00
d2979e8894 runtime coremgmt: implement firmware rewrite 2024-11-18 12:19:37 +08:00
3884c14a19 satman coremgmt: code after reboot is unreachable 2024-11-18 12:19:37 +08:00
c5b00d8e4e cargo fmt 2024-11-18 12:19:37 +08:00
2985875f9a satman: implement boot file rewrite sequence 2024-11-18 12:19:37 +08:00
5cb565a7e0 coremgr: current_payload -> config_payload 2024-11-18 12:19:37 +08:00
59954829a2 drtio-proto: (N)ACK -> Reply { succeeded } 2024-11-18 12:19:37 +08:00
960864c847 drtio-proto: add coremgmt-over-drtio messages 2024-11-18 12:19:37 +08:00
bdc29e5709 runtime: support coremgmt on satellites 2024-11-18 12:19:37 +08:00
332732dc44 satman: implement cfg/mgmt operations 2024-11-18 12:19:37 +08:00
244c7396d9 runtime: handle drtio-eem satellite disconnection 2024-11-18 12:08:44 +08:00
2c633409b8 Set FCLK0 for EBAZ4205
EBAZ4205 uses FCLK0 as the RTIO clock.

If the user modifies the gateware to use an external clock, FCLK0 is not used.
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-11-17 10:08:43 +08:00
9774b39fd8 flake: update zynq-rs 2024-11-16 17:32:05 +08:00
9054e4a7cb flake: update zynq-rs, switch to oxalica rust overlay 2024-11-16 17:22:01 +08:00
d79bf8d54a gateware: Add default TTLs to EBAZ4205 (#335)
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-11-16 10:40:45 +08:00
75e7fc55a3 flake: update dependencies 2024-11-16 10:39:39 +08:00
24a4d79f0f README: general update 2024-11-07 19:07:38 +01:00
9ce3aadb15 cargo fmt 2024-10-18 17:43:39 +08:00
3390abd5a1 subkernels: pass now_mu when calling subkernels 2024-10-18 13:51:48 +08:00
a410c40b50 ADD SPI to EBAZ4205 for AD9834 (#331)
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-10-17 15:06:11 +08:00
030247be18 add pre-commit hooks for code formatting
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-10-08 15:19:07 +08:00
61df939c87 ebaz4205: add variant and hydra job
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-10-08 11:35:31 +08:00
aba97175c6 Fix formatting 2024-10-05 16:30:45 -07:00
81790257a5 Add ebaz4205 support (#327)
Co-authored-by: newell <newell.jensen@gmail.com>
Co-committed-by: newell <newell.jensen@gmail.com>
2024-10-05 15:05:49 +08:00
1f81d038e0 update dependencies 2024-10-05 14:50:13 +08:00
1e42228aac flake: remove deprecated pytest-runner 2024-09-30 16:19:56 +08:00
c84653b500 flake: update dependencies 2024-09-30 16:01:11 +08:00
6585b9b441 flake: update dependencies 2024-09-30 14:17:25 +08:00
873dd86b4d runtime: cargo fmt (NFC) 2024-09-19 10:23:31 +08:00
e7614d2e8e rerun idle kernel on finish 2024-09-13 09:35:38 +08:00
491e426222 run idle kernel on flash 2024-09-12 16:12:57 +08:00
ccd3bf3003 runtime: fix drtio inject lock 2024-09-02 17:19:20 +08:00
3fdb7e80a8 flake: update dependencies 2024-08-23 19:14:08 +08:00
bd1de933fb cargo fmt 2024-08-23 17:49:14 +08:00
e8d77fca3e firmware: add UnwrapNoneError exception 2024-08-23 16:50:47 +08:00
85e8a3fc44 firmware: add LinAlgError exception 2024-08-22 10:42:28 +08:00
04078b3d89 flake: update dependencies 2024-08-21 18:51:19 +08:00
d508c5c6f8 firmware: add unit tests for exception sync 2024-08-21 16:35:03 +08:00
bae41253e4 firmware: sync exception names and ids 2024-08-21 16:34:25 +08:00
20181e9915 fix nalgebra url 2024-08-07 13:49:03 +08:00
a835149619 kernel/linalg: remove redundant unsafe blocks 2024-08-07 13:48:21 +08:00
78d6b7ddcf cargo fmt 2024-08-05 19:37:55 +08:00
fad1db9796 comms: remove idle kernel DRTIO error case 2024-08-05 19:28:09 +08:00
fee30033ec comms: run idle kernel on start-up 2024-08-05 19:28:09 +08:00
fe6f259d48 kernel: add linalg functions 2024-08-01 18:20:32 +08:00
e4d7ce114f flake: update fastnumbers 2024-08-01 07:41:32 +08:00
63f4783687 subkernels: support exceptions from subkernels 2024-07-31 17:22:29 +08:00
69a0b1bfb7 subkernels: raise exceptions to kernel 2024-07-31 17:22:29 +08:00
f6bff80105 flake: update dependencies 2024-07-31 17:20:54 +08:00
57fd327ecb rustfmt 2024-07-22 18:55:17 +08:00
69d5b11ebf kernel/api: add nalgebra::linalg methods 2024-07-22 11:57:58 +08:00
bab938c563 add nalgebra dependency
Co-authored-by: abdul124 <ar@m-labs.hk>
Co-committed-by: abdul124 <ar@m-labs.hk>
2024-07-22 11:13:45 +08:00
d51e5e60c3 repeater: handle async messages 2024-07-09 23:04:34 +08:00
23857eef63 allow toggling SED spread with flash config key 2024-07-09 18:11:20 +08:00
d0615bf965 flake: update dependencies 2024-07-09 10:37:37 +02:00
3a789889cf kernel/api: add rint api 2024-07-05 14:53:09 +08:00
72b814f7fd repeater: clear buffer after ping 2024-07-04 17:30:04 +08:00
ead20a66a5 flake: update dependencies 2024-06-06 10:05:55 +08:00
70 changed files with 6114 additions and 1037 deletions

View File

@ -4,60 +4,102 @@ ARTIQ on Zynq
How to use
----------
1. Install the ARTIQ version that corresponds to the artiq-zynq version you are targeting.
2. To obtain firmware binaries, select the latest successful build on [Hydra](https://nixbld.m-labs.hk/) for the targeted artiq-zynq version, or use AFWS. If using Hydra, search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
3. Place the ``boot.bin`` file, obtained from Hydra's "binary distribution" download link or from AFWS, at the root of a FAT-formatted SD card.
4. Optionally, create a ``config.txt`` configuration file at the root of the SD card containing ``key=value`` pairs on each line. Use the ``ip``, ``ip6`` and ``mac`` keys to respectively set the IPv4, IPv6 and MAC address of the board. Configuring an IPv6 address is entirely optional. If these keys are not found, the firmware will use default values that may or may not be compatible with your network.
5. Insert the SD card into the board and set up the board to boot from the SD card. For the ZC706, this is achieved by placing the large DIP switch SW11 in the 00110 position.
6. Power up the board. After the firmware starts successfully, it should respond to ping at its IP addresses, and boot messages can be observed from its UART at 115200bps.
7. Create and use an ARTIQ device database as usual, but set ``"target": "cortexa9"`` in the arguments of the core device.
1. [Install ARTIQ](https://m-labs.hk/artiq/manual/installing.html). Get the corresponding version to the ``artiq-zynq`` version you are targeting.
2. To obtain firmware binaries, use AFWS or build your own; see [the ARTIQ manual](https://m-labs.hk/artiq/manual/building_developing.html) for detailed instructions or skip to "Development" below. ZC706 variants only can also be downloaded from latest successful build on [Hydra](https://nixbld.m-labs.hk/).
3. Place ``boot.bin`` file at the root ``/`` of a FAT-formatted SD card.
4. Optionally, create a ``config.txt`` configuration file containing ``key=value`` pairs on each line and place it at the root of the SD card. See below for valid keys. The ``ip``, ``ip6`` and ``mac`` keys can be used to set networking information. If these keys are not found, the firmware will use default values which may or may not be compatible with your network.
5. Insert the SD card into the board and set the board to boot from the SD card. For ZC706, this is achieved by placing the large DIP switch SW11 into the 00110 position. On Kasli-SoC, place the BOOT MODE switches to SD.
6. Power up the board. After successful boot the firmware should respond to ping at its IP addresses. Boot output can be observed from UART at 115200bps 8-N-1.
7. Create and use an ARTIQ device database as usual.
Configuration
-------------
Configuring the device is done using the ``config.txt`` text file at the root of the SD card, plus the contents of the ``config`` folder. When searching for a configuration key, the firmware first looks for a file named ``/config/[key].bin`` and, if it exists, returns the contents of that file. If not, it looks into ``/config.txt``, which contains a list of ``key=value`` pairs, one per line. The ``config`` folder allows configuration values that consist in binary data, such as the startup kernel.
Configuring the device is done using the ``config.txt`` text file at the root of the SD card plus optionally a ``config`` folder. When searching for a configuration key, the firmware first looks for a file named ``/config/[key].bin`` and, if it exists, returns the contents of that file. If not, it looks into ``/config.txt``, which should contain a list of ``key=value`` pairs, one per line. ``config.txt`` should be used for most keys but the ``config`` folder allows for setting configuration values which consist of binary data, such as the startup kernel.
The following configuration keys are available:
The following configuration keys are available among others:
- ``mac``: Ethernet MAC address.
- ``ip``: IPv4 address.
- ``ip6``: IPv6 address.
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
- ``idle_kernel``: idle kernel in ELF format (as produced by ``artiq_compile``).
- ``startup_kernel``: startup kernel in ELF format (as produced by ``artiq_compile``).
- ``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
not implemented as it seems not very useful.
See [ARTIQ manual](https://m-labs.hk/artiq/manual-beta/core_device.html#configuration-storage) for full list. Configurations can be read/written/removed with ``artiq_coremgmt``. Config erase is not implemented, as it isn't particularly useful.
For convenience, the ``boot`` key can be used with ``artiq_coremgmt`` and a ``boot.bin`` file to replace firmware/gateware in a running system. This key is read-only. When loading ``boot.bin`` onto the SD card directly, place it at the root and not in the ``config`` folder.
Development instructions
------------------------
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.8+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
ARTIQ on Zynq is packaged using [Nix](https://nixos.org) Flakes. Install Nix 2.8+ 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:
**Pure build with Nix:**
```shell
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc.
./remote_run.sh
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-sd or etc
```
Impure incremental build and execution on a remote JTAG server:
Run ``nix flake show`` to see all valid build targets. Targets suffixed with ``-jtag`` produce separate firmware and gateware files, intended for use in booting via JTAG server/Ethernet, e.g. ``./remote_run.sh -i`` with a remote JTAG server. Targets suffixed with ``-sd`` will produce ``boot.bin`` file suitable for SD card boot. ``-firmware`` and ``-gateware`` respectively build firmware and gateware only.
The Kasli-SoC target requires a system description file as input. See ARTIQ manual for exact instructions or use incremental build.
**Impure incremental build:**
For boards with fixed variants, i.e. ZC706, etc. :
```shell
nix develop
cd src
gateware/zc706.py -g ../build/gateware -V <variant> # build gateware
make GWARGS="-V <variant>" <runtime/satman> # build firmware
cd ..
./remote_run.sh -i
gateware/<board>.py -g ../build/gateware -V <variant> # gateware
make GWARGS="-V <variant>" <runtime/satman> # firmware
```
For boards with system descriptions, i.e. Kasli-SoC, etc. :
```shell
nix develop
cd src
gateware/<board>.py -g ../build/gateware <description.json> # gateware
make TARGET=<board> GWARGS="path/to/description.json" <runtime/satman> # firmware
```
``szl.elf`` can be obtained with:
```shell
nix build git+https://git.m-labs.hk/m-labs/zynq-rs#<board>-szl
```
To generate ``boot.bin`` use ``mkbootimage``, e.g.:
```shell
echo "the_ROM_image:
{
[bootloader]result/szl.elf
gateware/top.bit
firmware/armv7-none-eabihf/release/<runtime/satman>
}
EOF" >> boot.bif
mkbootimage boot.bif boot.bin
```
Notes:
- 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.
- If the board is connected to the local machine by JTAG, use the ``local_run.sh`` script.
- A known Xilinx hardware bug prevents repeatedly loading the bootloader over JTAG without a POR reset. If booting over JTAG, install a jumper on ``PS_POR_B`` and use the POR reset script [here](https://git.m-labs.hk/M-Labs/zynq-rs/src/branch/master/kasli_soc_por.py).
Pre-Commit Hooks
----------------
You are strongly recommended to use the provided pre-commit hooks to automatically reformat files and check for non-optimal Rust/C/C++ practices. Run `pre-commit install` to install the hook and `pre-commit` will automatically run `cargo fmt`, `cargo clippy`, and `clang-format` for you.
Several things to note:
- If `cargo fmt`, `cargo clippy`, or `clang-format` returns an error, the pre-commit hook will fail. You should fix all errors before trying to commit again.
- If `cargo fmt` or `clang-format` reformats some files, the pre-commit hook will also fail. You should review the changes and, if satisfied, try to commit again.
License
-------

View File

@ -0,0 +1,78 @@
core_addr = "192.168.1.57"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {
"host": core_addr,
"ref_period": 1e-9,
"target": "cortexa9",
},
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr,
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} "
+ core_addr,
},
"core_analyzer": {
"type": "controller",
"host": "::1",
"port_proxy": 1385,
"port": 1386,
"command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} "
+ core_addr,
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache",
},
"core_dma": {"type": "local", "module": "artiq.coredevice.dma", "class": "CoreDMA"},
"led0": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 0},
},
"led1": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 1},
},
}
# TTLs starting at RTIO channel 2, ending at RTIO channel 15
for i in range(2, 16):
device_db["ttl" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": i},
}
device_db.update(
spi0={
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 16},
},
dds0={
"type": "local",
"module": "artiq.coredevice.ad9834",
"class": "AD9834",
"arguments": {"spi_device": "spi0"},
},
)

155
flake.lock generated
View File

@ -3,19 +3,19 @@
"artiq": {
"inputs": {
"artiq-comtools": "artiq-comtools",
"mozilla-overlay": "mozilla-overlay",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay",
"sipyco": "sipyco",
"src-migen": "src-migen",
"src-misoc": "src-misoc",
"src-pythonparser": "src-pythonparser"
},
"locked": {
"lastModified": 1716972728,
"narHash": "sha256-88J+eckZamtwhcCQkPpKLu6R1hmgj5+C9n2U5i+sHUE=",
"lastModified": 1743144117,
"narHash": "sha256-LOh3KwjcEKcLA2vva6awAnMLUEyq4d+LPLCu7FwMAhw=",
"ref": "refs/heads/master",
"rev": "49e402780bebba437c6098047ab1dc68eaf5a17c",
"revCount": 8808,
"rev": "4ddad5fd17bb07fc754d764d3ff157300d27e109",
"revCount": 9204,
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
},
@ -37,11 +37,11 @@
]
},
"locked": {
"lastModified": 1707216368,
"narHash": "sha256-ZXoqzG2QsVsybALLYXs473avXcyKSZNh2kIgcPo60XQ=",
"lastModified": 1734270714,
"narHash": "sha256-7bzGn/hXLIsLQHGQsvo+uoIFUrw9DjXSlMC449BY4ME=",
"owner": "m-labs",
"repo": "artiq-comtools",
"rev": "e5d0204490bccc07ef9141b0d7c405ab01cb8273",
"rev": "7e3152314af8f5987370e33b347b2ec2697567ed",
"type": "github"
},
"original": {
@ -55,11 +55,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@ -68,66 +68,18 @@
"type": "github"
}
},
"mozilla-overlay": {
"flake": false,
"locked": {
"lastModified": 1704373101,
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"mozilla-overlay_2": {
"flake": false,
"locked": {
"lastModified": 1704373101,
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"mozilla-overlay_3": {
"flake": false,
"locked": {
"lastModified": 1704373101,
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
"type": "github"
},
"original": {
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1716542732,
"narHash": "sha256-0Y9fRr0CUqWT4KgBITmaGwlnNIGMYuydu2L8iLTfHU4=",
"lastModified": 1741851582,
"narHash": "sha256-cPfs8qMccim2RBgtKGF+x9IBCduRvd/N5F4nYpU0TVE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d12251ef6e8e6a46e05689eeccd595bdbd3c9e60",
"rev": "6607cf789e541e7873d40d3a8f7815ea92204f32",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
@ -135,10 +87,53 @@
"root": {
"inputs": {
"artiq": "artiq",
"mozilla-overlay": "mozilla-overlay_2",
"zynq-rs": "zynq-rs"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"artiq",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719454714,
"narHash": "sha256-MojqG0lyUINkEk0b3kM2drsU5vyaF8DFZe/FAlZVOGs=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "d1c527659cf076ecc4b96a91c702d080b213801e",
"type": "github"
},
"original": {
"owner": "oxalica",
"ref": "snapshot/2024-08-01",
"repo": "rust-overlay",
"type": "github"
}
},
"rust-overlay_2": {
"inputs": {
"nixpkgs": [
"zynq-rs",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719454714,
"narHash": "sha256-MojqG0lyUINkEk0b3kM2drsU5vyaF8DFZe/FAlZVOGs=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "d1c527659cf076ecc4b96a91c702d080b213801e",
"type": "github"
},
"original": {
"owner": "oxalica",
"ref": "snapshot/2024-08-01",
"repo": "rust-overlay",
"type": "github"
}
},
"sipyco": {
"inputs": {
"nixpkgs": [
@ -147,11 +142,11 @@
]
},
"locked": {
"lastModified": 1708937641,
"narHash": "sha256-Hkb9VYFzFgkYxfbh4kYcDSn7DbMUYehoQDeTALrxo2Q=",
"lastModified": 1734267097,
"narHash": "sha256-aWg7XDiOlWnkXfDbKrBn9ITR46/JXfndvYHxFJ1vN78=",
"owner": "m-labs",
"repo": "sipyco",
"rev": "4a28b311ce0069454b4e8fe1e6049db11b9f1296",
"rev": "430978ada3fefe32de01f1b884b3031e48aaef96",
"type": "github"
},
"original": {
@ -163,11 +158,11 @@
"src-migen": {
"flake": false,
"locked": {
"lastModified": 1715484909,
"narHash": "sha256-4DCHBUBfc/VA+7NW2Hr0+JP4NnKPru2uVJyZjCCk0Ws=",
"lastModified": 1738906518,
"narHash": "sha256-GproDJowtcgbccsT+I0mObzFhE483shcS8MSszKXwlc=",
"owner": "m-labs",
"repo": "migen",
"rev": "4790bb577681a8c3a8d226bc196a4e5deb39e4df",
"rev": "2828df54594673653a641ab551caf6c6b1bfeee5",
"type": "github"
},
"original": {
@ -179,11 +174,11 @@
"src-misoc": {
"flake": false,
"locked": {
"lastModified": 1715647536,
"narHash": "sha256-q+USDcaKHABwW56Jzq8u94iGPWlyLXMyVt0j/Gyg+IE=",
"lastModified": 1741001607,
"narHash": "sha256-05BGqWV4Zc9ArwaW0uuBYWjg4oTeP4vznPQQjEpQPEM=",
"ref": "refs/heads/master",
"rev": "fea9de558c730bc394a5936094ae95bb9d6fa726",
"revCount": 2455,
"rev": "fa73f42f3c163833f17fc99399bb41005970c503",
"revCount": 2495,
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
@ -227,18 +222,18 @@
},
"zynq-rs": {
"inputs": {
"mozilla-overlay": "mozilla-overlay_3",
"nixpkgs": [
"artiq",
"nixpkgs"
]
],
"rust-overlay": "rust-overlay_2"
},
"locked": {
"lastModified": 1716519432,
"narHash": "sha256-vgKBJCQRPCutJ4n+FtJNczMZULWW7J3B8icf/PUothw=",
"lastModified": 1743144882,
"narHash": "sha256-B1J8Ou/T8EDNcPVAu+c5bRiRUjoCiWCh6WNo60sRyDc=",
"ref": "refs/heads/master",
"rev": "46dc25b89e46b9043129d77e3c9348916748e325",
"revCount": 645,
"rev": "c98c30c6565f0aa35a2f4ab31e5174f167714841",
"revCount": 696,
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
},

View File

@ -2,27 +2,26 @@
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 }:
outputs = { self, zynq-rs, artiq }:
let
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
pkgs = import artiq.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import zynq-rs.inputs.rust-overlay) ]; };
zynqpkgs = zynq-rs.packages.x86_64-linux;
artiqpkgs = artiq.packages.x86_64-linux;
llvmPackages_11 = zynq-rs.llvmPackages_11;
zynqRev = self.sourceInfo.rev or "unknown";
rust = zynq-rs.rust;
rustPlatform = zynq-rs.rustPlatform;
fastnumbers = pkgs.python3Packages.buildPythonPackage rec {
pname = "fastnumbers";
version = "2.2.1";
version = "5.1.0";
src = pkgs.python3Packages.fetchPypi {
inherit pname version;
sha256 = "0j15i54p7nri6hkzn1wal9pxri4pgql01wgjccig6ar0v5jjbvsy";
sha256 = "sha256-4JLTP4uVwxcaL7NOV57+DFSwKQ3X+W/6onYkN2AdkKc=";
};
};
@ -75,7 +74,7 @@
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
checkInputs = with pkgs.python3Packages; [ pytest-runner pytestCheckHook pytest-timeout ];
checkInputs = with pkgs.python3Packages; [ pytestCheckHook pytest-timeout ];
# migen/misoc version checks are broken with pyproject for some reason
postPatch = ''
@ -126,19 +125,23 @@
lockFile = src/Cargo.lock;
outputHashes = {
"tar-no-std-0.1.8" = "sha256-xm17108v4smXOqxdLvHl9CxTCJslmeogjm4Y87IXFuM=";
"nalgebra-0.32.6" = "sha256-ZbQQZbM3A5cJ4QbujtUxkrI0/qGlI4UzfahtyQnvMZA=";
"core_io-0.1.0" = "sha256-0HINFWRiJx8pjMgUOL/CS336ih7SENSRh3Kah9LPRrw=";
"fatfs-0.3.6" = "sha256-Nz9hCq/1YgSXF8ltJ5ZawV0Hc8WV44KNK0tJdVnNb4U=";
};
};
nativeBuildInputs = [
pkgs.gnumake
(pkgs.python3.withPackages(ps: [ ps.jsonschema artiqpkgs.migen migen-axi artiqpkgs.misoc artiqpkgs.artiq ]))
zynqpkgs.cargo-xbuild
llvmPackages_11.llvm
llvmPackages_11.clang-unwrapped
pkgs.cargo-xbuild
pkgs.llvmPackages_18.llvm
pkgs.llvmPackages_18.clang-unwrapped
];
buildPhase = ''
export ZYNQ_REV=${zynqRev}
export XARGO_RUST_SRC="${rust}/lib/rustlib/src/rust/library"
export CLANG_EXTRA_INCLUDE_DIR="${llvmPackages_11.clang-unwrapped.lib}/lib/clang/11.1.0/include"
export CLANG_EXTRA_INCLUDE_DIR="${pkgs.llvmPackages_18.clang-unwrapped.lib}/lib/clang/18/include"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
export ZYNQ_RS=${zynq-rs}
make TARGET=${target} GWARGS="${if json == null then "-V ${variant}" else json}" ${fwtype}
@ -164,6 +167,7 @@
];
}
''
export ZYNQ_REV=${zynqRev}
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
@ -341,6 +345,7 @@
{
inherit fastnumbers artiq-netboot ramda migen-axi binutils-arm;
} //
(board-package-set { target = "zc706"; variant = "cxp_4r_fmc"; }) //
(board-package-set { target = "zc706"; variant = "nist_clock"; }) //
(board-package-set { target = "zc706"; variant = "nist_clock_master"; }) //
(board-package-set { target = "zc706"; variant = "nist_clock_master_100mhz"; }) //
@ -363,7 +368,8 @@
(board-package-set { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
(board-package-set { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
(board-package-set { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
(board-package-set { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; });
(board-package-set { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; }) //
(board-package-set { target = "ebaz4205"; variant = "base"; });
hydraJobs = packages.x86_64-linux // { inherit zc706-hitl-tests; inherit gateware-sim; inherit fmt-check; };
@ -371,11 +377,11 @@
name = "artiq-zynq-dev-shell";
buildInputs = with pkgs; [
rust
llvmPackages_11.llvm
llvmPackages_11.clang-unwrapped
llvmPackages_18.llvm
llvmPackages_18.clang-unwrapped
gnumake
cacert
zynqpkgs.cargo-xbuild
pkgs.cargo-xbuild
zynqpkgs.mkbootimage
openocd
openssh rsync
@ -383,9 +389,11 @@
artiqpkgs.artiq
artiqpkgs.vivado
binutils-arm
pre-commit
];
ZYNQ_REV="${zynqRev}";
XARGO_RUST_SRC = "${rust}/lib/rustlib/src/rust/library";
CLANG_EXTRA_INCLUDE_DIR = "${llvmPackages_11.clang-unwrapped.lib}/lib/clang/11.1.0/include";
CLANG_EXTRA_INCLUDE_DIR = "${pkgs.llvmPackages_18.clang-unwrapped.lib}/lib/clang/18/include";
ZYNQ_RS = "${zynq-rs}";
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
SZL = "${zynqpkgs.szl}";

View File

@ -1,7 +1,6 @@
[target.armv7-none-eabihf]
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "target-feature=a9,armv7-a,neon",
"-C", "target-cpu=cortex-a9",
]

32
src/.clang-format Normal file
View File

@ -0,0 +1,32 @@
BasedOnStyle: LLVM
Language: Cpp
Standard: Cpp11
AccessModifierOffset: -1
AlignEscapedNewlines: Left
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: Inline
BinPackParameters: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ContinuationIndentWidth: 4
DerivePointerAlignment: false
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
MaxEmptyLinesToKeep: 1
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterTemplateKeyword: false
SpacesBeforeTrailingComments: 2
TabWidth: 4
UseTab: Never

1
src/.clippy.toml Normal file
View File

@ -0,0 +1 @@
doc-valid-idents = ["CPython", "NumPy", ".."]

View File

@ -0,0 +1,32 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
default_stages: [commit]
repos:
- repo: local
hooks:
- id: cargo-fmt
name: artiq-zynq cargo format
entry: nix
language: system
types: [file, rust]
pass_filenames: false
description: Runs cargo fmt on the codebase.
args: [develop, -c, cargo, fmt, --manifest-path, src/Cargo.toml, --all]
- id: cargo-clippy
name: artiq-zynq cargo clippy
entry: nix
language: system
types: [file, rust]
pass_filenames: false
description: Runs cargo clippy on the codebase.
args: [develop, -c, cargo, clippy, --manifest-path, src/Cargo.toml, --tests]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v19.1.0
hooks:
- id: clang-format
name: artiq-zynq clang-format
description: Runs clang-format on the codebase.
files: \.(cpp|h|hpp|c)$
args: [-style=file, -fallback-style=none, -assume-filename=src/.clang-format]

178
src/Cargo.lock generated
View File

@ -1,6 +1,15 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "arrayvec"
@ -10,9 +19,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "async-recursion"
version = "0.3.2"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
@ -49,9 +58,9 @@ version = "0.0.0"
[[package]]
name = "byteorder"
version = "1.4.3"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
checksum = "60f0b0d4c0a382d2734228fd12b5a6b5dac185c60e938026fd31b265b94f9bd2"
[[package]]
name = "cc"
@ -73,18 +82,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "compiler_builtins"
version = "0.1.39"
version = "0.1.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
checksum = "d68bc55329711cd719c2687bb147bc06211b0521f97ef398280108ccb23227e9"
[[package]]
name = "core_io"
version = "0.1.20210325"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97f8932064288cc79feb4d343a399d353a6f6f001e586ece47fe518a9e8507df"
dependencies = [
"rustc_version",
]
version = "0.1.0"
source = "git+https://git.m-labs.hk/M-Labs/rs-core_io.git?rev=e9d3edf027#e9d3edf0272502b0dd6c26e8a4869c2912657615"
[[package]]
name = "crc"
@ -132,9 +137,8 @@ dependencies = [
[[package]]
name = "fatfs"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e18f80a87439240dac45d927fd8f8081b6f1e34c03e97271189fa8a8c2e96c8f"
version = "0.3.6"
source = "git+https://git.m-labs.hk/M-Labs/rust-fatfs.git?rev=4b5e420084#4b5e420084fd1c4a9c105680b687523909b6469c"
dependencies = [
"bitflags",
"byteorder",
@ -144,9 +148,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
@ -158,9 +162,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
@ -168,21 +172,21 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-io"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
@ -191,21 +195,21 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.25"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-macro",
@ -246,6 +250,7 @@ dependencies = [
"libsupport_zynq",
"log",
"log_buffer",
"nalgebra",
"nb 0.1.3",
"unwind",
"vcell",
@ -259,7 +264,6 @@ dependencies = [
"embedded-hal",
"libcortex_a9",
"nb 1.0.0",
"pin-utils",
"smoltcp",
]
@ -268,6 +272,7 @@ name = "libboard_artiq"
version = "0.0.0"
dependencies = [
"build_zynq",
"byteorder",
"core_io",
"crc",
"embedded-hal",
@ -363,9 +368,9 @@ checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
[[package]]
name = "log"
version = "0.4.17"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 1.0.0",
]
@ -382,6 +387,19 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]]
name = "nalgebra"
version = "0.32.6"
source = "git+https://git.m-labs.hk/M-Labs/nalgebra.git?rev=ad42410ab0#ad42410ab0abb014229e3ff6bc6ccd39ca92d5d1"
dependencies = [
"approx",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nb"
version = "0.1.3"
@ -398,10 +416,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
[[package]]
name = "num-derive"
version = "0.3.3"
name = "num-complex"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
dependencies = [
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
@ -409,14 +436,41 @@ dependencies = [
]
[[package]]
name = "num-traits"
version = "0.2.15"
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -431,18 +485,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.43"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@ -461,6 +515,7 @@ dependencies = [
"build_zynq",
"byteorder",
"core_io",
"crc",
"cslice",
"dwarf",
"dyld",
@ -486,21 +541,14 @@ dependencies = [
"void",
]
[[package]]
name = "rustc_version"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
dependencies = [
"semver",
]
[[package]]
name = "satman"
version = "0.0.0"
dependencies = [
"build_zynq",
"byteorder",
"core_io",
"crc",
"cslice",
"embedded-hal",
"io",
@ -518,10 +566,16 @@ dependencies = [
]
[[package]]
name = "semver"
version = "0.1.20"
name = "simba"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
]
[[package]]
name = "smoltcp"
@ -536,9 +590,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.101"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
@ -555,6 +609,12 @@ dependencies = [
"log",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.5"

View File

@ -1,18 +1,10 @@
{
"abi-blacklist": [
"stdcall",
"fastcall",
"vectorcall",
"thiscall",
"win64",
"sysv64"
],
"arch": "arm",
"data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
"emit-debug-gdb-scripts": false,
"env": "",
"executables": true,
"features": "+v7,+vfp3,-d32,+thumb2,-neon",
"features": "+v7,+vfp3,-d32,+thumb2,+neon,+a9,+armv7-a",
"is-builtin": false,
"linker": "rust-lld",
"linker-flavor": "ld.lld",
@ -21,7 +13,6 @@
"os": "none",
"panic-strategy": "abort",
"requires-uwtable": true,
"force-unwind-tables": "yes",
"relocation-model": "static",
"target-c-int-width": "32",
"target-endian": "little",

View File

@ -1,5 +1,15 @@
import os
from artiq._version import get_version
from misoc.integration import cpu_interface
def generate_ident(variant):
return "{}+{};{}".format(
get_version().split(".")[0],
os.getenv("ZYNQ_REV", default="unknown")[:8],
variant,
)
def write_csr_file(soc, filename):
with open(filename, "w") as f:
f.write(cpu_interface.get_csr_rust(

View File

@ -0,0 +1,74 @@
from migen.build.generic_platform import *
fmc_adapter_io = [
# CoaXPress high speed link
("CXP_HS", 0,
Subsignal("rxp", Pins("HPC:DP0_M2C_P")),
Subsignal("rxn", Pins("HPC:DP0_M2C_N")),
),
("CXP_HS", 1,
Subsignal("rxp", Pins("HPC:DP1_M2C_P")),
Subsignal("rxn", Pins("HPC:DP1_M2C_N")),
),
("CXP_HS", 2,
Subsignal("rxp", Pins("HPC:DP2_M2C_P")),
Subsignal("rxn", Pins("HPC:DP2_M2C_N")),
),
("CXP_HS", 3,
Subsignal("rxp", Pins("HPC:DP3_M2C_P")),
Subsignal("rxn", Pins("HPC:DP3_M2C_N")),
),
# CoaXPress low speed link
("CXP_LS", 0, Pins("HPC:LA00_CC_P"), IOStandard("LVCMOS33")),
("CXP_LS", 1, Pins("HPC:LA01_CC_N"), IOStandard("LVCMOS33")),
("CXP_LS", 2, Pins("HPC:LA01_CC_P"), IOStandard("LVCMOS33")),
("CXP_LS", 3, Pins("HPC:LA02_N"), IOStandard("LVCMOS33")),
# CoaXPress green and red LED
("CXP_LED", 0,
Subsignal("green", Pins("HPC:LA11_P"), IOStandard("LVCMOS33")),
Subsignal("red", Pins("HPC:LA11_N"), IOStandard("LVCMOS33")),
),
("CXP_LED", 1,
Subsignal("green", Pins("HPC:LA12_P"), IOStandard("LVCMOS33")),
Subsignal("red", Pins("HPC:LA12_N"), IOStandard("LVCMOS33")),
),
("CXP_LED", 2,
Subsignal("green", Pins("HPC:LA13_P"), IOStandard("LVCMOS33")),
Subsignal("red", Pins("HPC:LA13_N"), IOStandard("LVCMOS33")),
),
("CXP_LED", 3,
Subsignal("green", Pins("HPC:LA14_P"), IOStandard("LVCMOS33")),
Subsignal("red", Pins("HPC:LA14_N"), IOStandard("LVCMOS33")),
),
# Power over CoaXPress
("PoCXP", 0,
Subsignal("enable", Pins("HPC:LA21_N"), IOStandard("LVCMOS33")),
Subsignal("alert", Pins("HPC:LA18_CC_P"), IOStandard("LVCMOS33")),
),
("PoCXP", 1,
Subsignal("enable", Pins("HPC:LA21_P"), IOStandard("LVCMOS33")),
Subsignal("alert", Pins("HPC:LA19_N"), IOStandard("LVCMOS33")),
),
("PoCXP", 2,
Subsignal("enable", Pins("HPC:LA22_N"), IOStandard("LVCMOS33")),
Subsignal("alert", Pins("HPC:LA19_P"), IOStandard("LVCMOS33")),
),
("PoCXP", 3,
Subsignal("enable", Pins("HPC:LA22_P"), IOStandard("LVCMOS33")),
Subsignal("alert", Pins("HPC:LA20_N"), IOStandard("LVCMOS33")),
),
("i2c", 0,
Subsignal("scl", Pins("HPC:IIC_SCL")),
Subsignal("sda", Pins("HPC:IIC_SDA")),
IOStandard("LVCMOS33")
),
# On board 125MHz reference
("clk125", 0,
Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")),
Subsignal("n", Pins("HPC:GBTCLK0_M2C_N")),
),
]

307
src/gateware/ebaz4205.py Normal file
View File

@ -0,0 +1,307 @@
#!/usr/bin/env python
import argparse
import analyzer
import dma
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import spi2, ttl_simple
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
from config import generate_ident, write_csr_file, write_mem_file, write_rustc_cfg_file
from migen import *
from migen.build.generic_platform import IOStandard, Misc, Pins, Subsignal
from migen.build.platforms import ebaz4205
from migen_axi.integration.soc_core import SoCCore
from misoc.interconnect.csr import *
_ps = [
(
"ps",
0,
Subsignal("clk", Pins("E7"), IOStandard("LVCMOS33"), Misc("SLEW=FAST")),
Subsignal("por_b", Pins("C7"), IOStandard("LVCMOS33"), Misc("SLEW=FAST")),
Subsignal("srst_b", Pins("B10"), IOStandard("LVCMOS18"), Misc("SLEW=FAST")),
)
]
_ddr = [
(
"ddr",
0,
Subsignal(
"a",
Pins("N2 K2 M3 K3 M4 L1 L4 K4 K1 J4 F5 G4 E4 D4 F4"),
IOStandard("SSTL15"),
),
Subsignal("ba", Pins("L5 R4 J5"), IOStandard("SSTL15")),
Subsignal("cas_n", Pins("P5"), IOStandard("SSTL15")),
Subsignal("cke", Pins("N3"), IOStandard("SSTL15")),
Subsignal("cs_n", Pins("N1"), IOStandard("SSTL15")),
Subsignal("ck_n", Pins("M2"), IOStandard("DIFF_SSTL15"), Misc("SLEW=FAST")),
Subsignal("ck_p", Pins("L2"), IOStandard("DIFF_SSTL15"), Misc("SLEW=FAST")),
# Pins "T1 Y1" not connected
Subsignal("dm", Pins("A1 F1"), IOStandard("SSTL15_T_DCI"), Misc("SLEW=FAST")),
Subsignal(
"dq",
Pins("C3 B3 A2 A4 D3 D1 C1 E1 E2 E3 G3 H3 J3 H2 H1 J1"),
# Pins "P1 P3 R3 R1 T4 U4 U2 U3 V1 Y3 W1 Y4 Y2 W3 V2 V3" not connected
IOStandard("SSTL15_T_DCI"),
Misc("SLEW=FAST"),
),
Subsignal(
"dqs_n",
Pins("B2 F2"), # Pins "T2 W4" not connected
IOStandard("DIFF_SSTL15_T_DCI"),
Misc("SLEW=FAST"),
),
Subsignal(
"dqs_p",
Pins("C2 G2"), # Pins "R2 W5" not connected
IOStandard("DIFF_SSTL15_T_DCI"),
Misc("SLEW=FAST"),
),
Subsignal("vrn", Pins("G5"), IOStandard("SSTL15_T_DCI"), Misc("SLEW=FAST")),
Subsignal("vrp", Pins("H5"), IOStandard("SSTL15_T_DCI"), Misc("SLEW=FAST")),
Subsignal("drst_n", Pins("B4"), IOStandard("SSTL15"), Misc("SLEW=FAST")),
Subsignal("odt", Pins("N5"), IOStandard("SSTL15")),
Subsignal("ras_n", Pins("P4"), IOStandard("SSTL15")),
Subsignal("we_n", Pins("M5"), IOStandard("SSTL15")),
)
]
# Connector J3
_i2c = [
(
"i2c",
0,
Subsignal("scl", Pins("U12"), IOStandard("LVCMOS33")),
Subsignal("sda", Pins("V13"), IOStandard("LVCMOS33")),
)
]
_spi = [
(
"spi",
0,
Subsignal("clk", Pins("V20")),
Subsignal("mosi", Pins("U20")),
Subsignal("cs_n", Pins("P19")),
IOStandard("LVCMOS33"),
)
]
# Connector DATA1
def _create_ttl():
_ttl = []
for idx, elem in enumerate([x for x in range(5, 21) if x not in (10, 12)]):
_ttl.append(
("ttl", idx, Pins("DATA1:DATA1-{}".format(elem)), IOStandard("LVCMOS33")),
)
return _ttl
class EBAZ4205(SoCCore):
def __init__(self, rtio_clk=125e6, acpki=False):
self.acpki = acpki
platform = ebaz4205.Platform()
platform.toolchain.bitstream_commands.extend(
[
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
]
)
platform.add_extension(_ps)
platform.add_extension(_ddr)
platform.add_extension(_i2c)
platform.add_extension(_spi)
platform.add_extension(_create_ttl())
gmii = platform.request("gmii")
platform.add_period_constraint(gmii.rx_clk, 10)
platform.add_period_constraint(gmii.tx_clk, 10)
platform.add_platform_command(
"set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets gmii_tx_clk_IBUF]"
)
ident = generate_ident(self.__class__.__name__)
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
fix_serdes_timing_path(platform)
self.config["RTIO_FREQUENCY"] = str(rtio_clk / 1e6)
platform.add_period_constraint(self.ps7.cd_sys.clk, 10)
self.comb += [
self.ps7.enet0.enet.gmii.tx_clk.eq(gmii.tx_clk),
self.ps7.enet0.enet.gmii.rx_clk.eq(gmii.rx_clk),
]
self.clock_domains.cd_eth_rx = ClockDomain(reset_less=False)
self.clock_domains.cd_eth_tx = ClockDomain(reset_less=False)
self.comb += [
ClockSignal("eth_rx").eq(gmii.rx_clk),
ClockSignal("eth_tx").eq(gmii.tx_clk),
]
self.sync.eth_tx += [
gmii.txd.eq(self.ps7.enet0.enet.gmii.txd),
gmii.tx_en.eq(self.ps7.enet0.enet.gmii.tx_en),
]
self.sync.eth_rx += [
self.ps7.enet0.enet.gmii.rxd.eq(gmii.rxd),
self.ps7.enet0.enet.gmii.rx_dv.eq(gmii.rx_dv),
]
# MDIO
mdio = platform.request("mdio")
self.comb += mdio.mdc.eq(self.ps7.enet0.enet.mdio.mdc)
self.specials += Instance(
"IOBUF",
i_I=self.ps7.enet0.enet.mdio.o,
io_IO=mdio.mdio,
o_O=self.ps7.enet0.enet.mdio.i,
i_T=~self.ps7.enet0.enet.mdio.t_n,
)
# I2C
i2c = self.platform.request("i2c")
self.specials += [
# SCL
Instance(
"IOBUF",
i_I=self.ps7.i2c0.scl.o,
io_IO=i2c.scl,
o_O=self.ps7.i2c0.scl.i,
i_T=~self.ps7.i2c0.scl.t_n,
),
# SDA
Instance(
"IOBUF",
i_I=self.ps7.i2c0.sda.o,
io_IO=i2c.sda,
o_O=self.ps7.i2c0.sda.i,
i_T=~self.ps7.i2c0.sda.t_n,
),
]
self.rtio_channels = []
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))
for i in range(14):
print("TTL at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
ttl = self.platform.request("ttl", i)
phy = ttl_simple.InOut(ttl)
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
print("SPI at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
spi_phy = spi2.SPIMaster(platform.request("spi"))
self.submodules += spi_phy
self.rtio_channels.append(rtio.Channel.from_phy(spi_phy, ififo_depth=4))
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
self.rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_tsc = rtio.TSC(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:
import acpki
self.config["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.config["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],
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.rtio_analyzer = analyzer.Analyzer(
self.rtio_tsc, self.rtio_core.cri, self.ps7.s_axi_hp1
)
self.csr_devices.append("rtio_analyzer")
class BASE(EBAZ4205):
def __init__(self, rtio_clk, acpki):
EBAZ4205.__init__(self, rtio_clk, acpki)
VARIANTS = {cls.__name__.lower(): cls for cls in [BASE]}
def main():
parser = argparse.ArgumentParser(
description="ARTIQ port to the EBAZ4205 control card of Ebit E9+ BTC miner"
)
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("--rtio-clk", default=125e6, help="RTIO Clock Frequency (Hz)")
parser.add_argument(
"-V",
"--variant",
default="base",
help="variant: " "[acpki_]base" "(default: %(default)s)",
)
args = parser.parse_args()
rtio_clk = int(args.rtio_clk)
variant = args.variant.lower()
acpki = variant.startswith("acpki_")
if acpki:
variant = variant[6:]
try:
cls = VARIANTS[variant]
except KeyError:
raise SystemExit("Invalid variant (-V/--variant)")
soc = cls(rtio_clk=rtio_clk, acpki=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

@ -24,10 +24,10 @@ from artiq.gateware.wrpll import wrpll
import dma
import analyzer
import acpki
import acpki as acpki_lib
import drtio_aux_controller
import zynq_clocking
from config import write_csr_file, write_mem_file, write_rustc_cfg_file
from config import generate_ident, write_csr_file, write_mem_file, write_rustc_cfg_file
eem_iostandard_dict = {
0: "LVDS_25",
@ -115,7 +115,7 @@ class GenericStandalone(SoCCore):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = description["variant"]
ident = generate_ident(description["variant"])
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
@ -184,10 +184,10 @@ class GenericStandalone(SoCCore):
if self.acpki:
self.config["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.submodules.rtio = acpki_lib.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.config["KI_IMPL"] = "csr"
@ -229,7 +229,7 @@ class GenericMaster(SoCCore):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = description["variant"]
ident = generate_ident(description["variant"])
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
@ -349,10 +349,10 @@ class GenericMaster(SoCCore):
if self.acpki:
self.config["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.submodules.rtio = acpki_lib.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.config["KI_IMPL"] = "csr"
@ -432,13 +432,14 @@ class GenericSatellite(SoCCore):
clk_freq = description["rtio_frequency"]
with_wrpll = description["enable_wrpll"]
has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
self.acpki = acpki
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = description["variant"]
ident = generate_ident(description["variant"])
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
@ -480,6 +481,8 @@ class GenericSatellite(SoCCore):
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
if has_drtio_over_eem:
self.eem_drtio_channels = []
if has_grabber:
self.grabber_csr_group = []
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
@ -494,15 +497,15 @@ class GenericSatellite(SoCCore):
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
drtioaux_csr_group = []
drtioaux_memory_group = []
drtiorep_csr_group = []
self.drtioaux_csr_group = []
self.drtioaux_memory_group = []
self.drtiorep_csr_group = []
self.drtio_cri = []
for i in range(len(self.gt_drtio.channels)):
coreaux_name = "drtioaux" + str(i)
memory_name = "drtioaux" + str(i) + "_mem"
drtioaux_csr_group.append(coreaux_name)
drtioaux_memory_group.append(memory_name)
self.drtioaux_csr_group.append(coreaux_name)
self.drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
@ -515,7 +518,7 @@ class GenericSatellite(SoCCore):
self.csr_devices.append("drtiosat")
else:
corerep_name = "drtiorep" + str(i-1)
drtiorep_csr_group.append(corerep_name)
self.drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.gt_drtio.channels[i]))
@ -538,16 +541,16 @@ class GenericSatellite(SoCCore):
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.config["HAS_DRTIO"] = None
self.config["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 has_drtio_over_eem:
self.add_eem_drtio(self.eem_drtio_channels)
self.add_drtio_cpuif_groups()
if self.acpki:
self.config["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.submodules.rtio = acpki_lib.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.config["KI_IMPL"] = "csr"
@ -560,7 +563,10 @@ class GenericSatellite(SoCCore):
self.submodules.local_io = SyncRTIO(
self.rtio_tsc, self.rtio_channels, lane_count=description["sed_lanes"]
)
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
self.comb += [
self.drtiosat.async_errors.eq(self.local_io.async_errors),
self.local_io.sed_spread_enable.eq(self.drtiosat.sed_spread_enable.storage)
]
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri, self.rtio_dma.cri, self.rtio.cri],
@ -622,6 +628,50 @@ class GenericSatellite(SoCCore):
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
for i, channel in enumerate(self.gt_drtio.channels)]
def add_eem_drtio(self, eem_drtio_channels):
# Must be called before constructing CRIInterconnectShared
self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, eem_drtio_channels)
self.csr_devices.append("eem_transceiver")
self.config["HAS_DRTIO_EEM"] = None
self.config["EEM_DRTIO_COUNT"] = len(eem_drtio_channels)
for i in range(len(self.eem_transceiver.channels)):
channel = i + len(self.gt_drtio.channels)
coreaux_name = "drtioaux" + str(channel)
memory_name = "drtioaux" + str(channel) + "_mem"
self.drtioaux_csr_group.append(coreaux_name)
self.drtioaux_memory_group.append(memory_name)
cdr = ClockDomainsRenamer({"rtio_rx": "sys"})
corerep_name = "drtiorep" + str(channel-1)
self.drtiorep_csr_group.append(corerep_name)
core = cdr(DRTIORepeater(
self.rtio_tsc, self.eem_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)
def add_drtio_cpuif_groups(self):
self.add_csr_group("drtiorep", self.drtiorep_csr_group)
self.add_csr_group("drtioaux", self.drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", self.drtioaux_memory_group)
def main():
parser = argparse.ArgumentParser(
description="ARTIQ device binary builder for generic Kasli-SoC systems")

View File

@ -13,7 +13,7 @@ from misoc.interconnect.csr import *
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, edge_counter
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2, edge_counter, cxp_grabber
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
@ -25,7 +25,8 @@ import analyzer
import acpki
import drtio_aux_controller
import zynq_clocking
from config import write_csr_file, write_mem_file, write_rustc_cfg_file
import cxp_4r_fmc
from config import generate_ident, write_csr_file, write_mem_file, write_rustc_cfg_file
class SMAClkinForward(Module):
def __init__(self, platform):
@ -130,7 +131,7 @@ class ZC706(SoCCore):
platform = zc706.Platform()
prepare_zc706_platform(platform)
ident = self.__class__.__name__
ident = generate_ident(self.__class__.__name__)
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
@ -138,7 +139,7 @@ class ZC706(SoCCore):
platform.add_extension(si5324_fmc33)
self.comb += platform.request("si5324_33").rst_n.eq(1)
cdr_clk = Signal()
self.cdr_clk = Signal()
cdr_clk_buf = Signal()
si5324_out = platform.request("si5324_clkout")
platform.add_period_constraint(si5324_out.p, 8.0)
@ -146,11 +147,11 @@ class ZC706(SoCCore):
Instance("IBUFDS_GTE2",
i_CEB=0,
i_I=si5324_out.p, i_IB=si5324_out.n,
o_O=cdr_clk,
o_O=self.cdr_clk,
p_CLKCM_CFG="TRUE",
p_CLKRCV_TRST="TRUE",
p_CLKSWING_CFG=3),
Instance("BUFG", i_I=cdr_clk, o_O=cdr_clk_buf)
Instance("BUFG", i_I=self.cdr_clk, o_O=cdr_clk_buf)
]
self.config["HAS_SI5324"] = None
self.config["SI5324_AS_SYNTHESIZER"] = None
@ -203,7 +204,7 @@ class _MasterBase(SoCCore):
platform = zc706.Platform()
prepare_zc706_platform(platform)
ident = self.__class__.__name__
ident = generate_ident(self.__class__.__name__)
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
@ -344,7 +345,7 @@ class _SatelliteBase(SoCCore):
platform = zc706.Platform()
prepare_zc706_platform(platform)
ident = self.__class__.__name__
ident = generate_ident(self.__class__.__name__)
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident, ps_cd_sys=False)
@ -487,6 +488,10 @@ class _SatelliteBase(SoCCore):
self.csr_devices.append("rtio_dma")
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
self.comb += [
self.drtiosat.async_errors.eq(self.local_io.async_errors),
self.local_io.sed_spread_enable.eq(self.drtiosat.sed_spread_enable.storage)
]
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.drtiosat.cri, self.rtio_dma.cri, self.rtio.cri],
[self.local_io.cri] + self.drtio_cri,
@ -648,6 +653,56 @@ class _NIST_QC2_RTIO:
self.add_rtio(rtio_channels)
class _CXP_4R_FMC_RTIO():
"""
CoaXpress host FMC card
"""
def __init__(self):
platform = self.platform
platform.add_extension(cxp_4r_fmc.fmc_adapter_io)
platform.add_extension(leds_fmc33)
rtio_channels = []
clk_freq = 125e6
self.submodules.cxp_grabber = cxp_grabber.CXPGrabber(
refclk=self.cdr_clk,
tx_pads=[platform.request("CXP_LS", 0)],
rx_pads=[platform.request("CXP_HS", 0)],
sys_clk_freq=clk_freq,
)
mem_size = self.cxp_grabber.core.get_mem_size()
# upper half is tx while lower half is rx
memory_address = self.axi2csr.register_port(self.cxp_grabber.core.get_tx_port(), mem_size)
self.axi2csr.register_port(self.cxp_grabber.core.get_rx_port(), mem_size)
self.add_memory_region("cxp_mem", self.mem_map["csr"] + memory_address, mem_size * 2)
self.csr_devices.append("cxp_grabber")
print("CoaXPress at RTIO channel 0x{:06x}".format(len(rtio_channels)))
rtio_channels += [
rtio.Channel(self.cxp_grabber.trigger),
rtio.Channel(self.cxp_grabber.config),
rtio.Channel(self.cxp_grabber.gate_data),
]
# max freq of cxp_gt_rx = linerate/internal_datawidth = 12.5Gbps/40 = 312.5MHz
# zc706 use speed grade 2 which only support up to 10.3125Gbps (~4ns)
# pushing to 12.5Gbps (3.2ns) will result in Pulse width violation but setup/hold times will still meet
rx = self.cxp_grabber.phy_rx.phys[0]
platform.add_period_constraint(rx.gtx.cd_cxp_gt_rx.clk, 3.2)
# constraint the clk path
platform.add_false_path_constraints(self.sys_crg.cd_sys.clk, rx.gtx.cd_cxp_gt_rx.clk)
# Add a user LED for rtio moninj
print("USER LED at RTIO channel 0x{:06x}".format(len(rtio_channels)))
phy = ttl_simple.Output(self.platform.request("user_led_33", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["HAS_RTIO_LOG"] = None
rtio_channels.append(rtio.LogChannel())
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
self.add_rtio(rtio_channels)
class NIST_CLOCK(ZC706, _NIST_CLOCK_RTIO):
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
@ -680,8 +735,13 @@ class NIST_QC2_Satellite(_SatelliteBase, _NIST_QC2_RTIO):
_SatelliteBase.__init__(self, acpki, drtio100mhz)
_NIST_QC2_RTIO.__init__(self)
class CXP_4R_FMC(ZC706, _CXP_4R_FMC_RTIO):
def __init__(self, acpki, drtio100mhz):
ZC706.__init__(self, acpki)
_CXP_4R_FMC_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]}
NIST_QC2, NIST_QC2_Master, NIST_QC2_Satellite, CXP_4R_FMC]}
def main():
parser = argparse.ArgumentParser(

View File

@ -10,6 +10,7 @@ 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"]
target_ebaz4205 = ["libboard_zynq/target_ebaz4205", "libconfig/target_ebaz4205"]
calibrate_wrpll_skew = []
[build-dependencies]
@ -19,10 +20,11 @@ build_zynq = { path = "../libbuild_zynq" }
log = "0.4"
log_buffer = { version = "1.2" }
crc = { version = "1.7", default-features = false }
core_io = { version = "0.1", features = ["collections"] }
core_io = { git = "https://git.m-labs.hk/M-Labs/rs-core_io.git", rev = "e9d3edf027", features = ["collections"] }
embedded-hal = "0.2"
nb = "1.0"
void = { version = "1", default-features = false }
byteorder = { version = "1.3", default-features = false }
io = { path = "../libio", features = ["byteorder"] }
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }

View File

@ -0,0 +1,305 @@
use core::fmt;
use embedded_hal::blocking::delay::DelayMs;
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use log::debug;
use crate::{cxp_ctrl::Error as CtrlErr,
cxp_packet::{read_u32, read_u64, reset_tag, send_test_packet, write_bytes_no_ack, write_u32, write_u64},
cxp_phys::{rx, tx, CXPSpeed},
pl::csr};
// Bootstrap registers address
const REVISION: u32 = 0x0004;
const CONNECTION_RESET: u32 = 0x4000;
const DEVICE_CONNECTION_ID: u32 = 0x4004;
const MASTER_HOST_CONNECTION_ID: u32 = 0x4008;
const STREAM_PACKET_SIZE_MAX: u32 = 0x4010;
const CONNECTION_CFG: u32 = 0x4014;
const CONNECTION_CFG_DEFAULT: u32 = 0x4018;
const TESTMODE: u32 = 0x401C;
const TEST_ERROR_COUNT_SELECTOR: u32 = 0x4020;
const TEST_ERROR_COUNT: u32 = 0x4024;
const TEST_PACKET_COUNT_TX: u32 = 0x4028;
const TEST_PACKET_COUNT_RX: u32 = 0x4030;
const VERSION_SUPPORTED: u32 = 0x4044;
const VERSION_USED: u32 = 0x4048;
// Setup const
const CHANNEL_LEN: u8 = 1;
const HOST_CONNECTION_ID: u32 = 0x00006303; // TODO: rename to CXP grabber sinara number when it comes out
// The MAX_STREAM_PAK_SIZE should be set as large as possible - Section 9.5.2 (CXP-001-2021)
// Since the ROI pipeline just consume all pixel data without buffering, any big number will do.
const MAX_STREAM_PAK_SIZE: u32 = 16384; // 16 KiB
const TX_TEST_CNT: u8 = 10;
// From DS191 (v1.18.1), max CDR time lock is 37*10^6 UI,
// 37*10^6 UI at lowest CXP linerate of 1.25Gbps = 29.6 ms, double it to account for overhead
const MONITOR_TIMEOUT_MS: u64 = 60;
pub enum Error {
CameraNotDetected,
ConnectionLost,
UnstableRX,
UnstableTX,
UnsupportedSpeed(u32),
UnsupportedTopology,
UnsupportedVersion,
CtrlPacketError(CtrlErr),
}
impl From<CtrlErr> for Error {
fn from(value: CtrlErr) -> Error {
Error::CtrlPacketError(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::CameraNotDetected => write!(f, "CameraNotDetected"),
&Error::ConnectionLost => write!(f, "ConnectionLost - Channel #0 cannot be detected"),
&Error::UnstableRX => write!(f, "UnstableRX - RX connection test failed"),
&Error::UnstableTX => write!(f, "UnstableTX - TX connection test failed"),
&Error::UnsupportedSpeed(linerate_code) => write!(
f,
"UnsupportedSpeed - {:#X} linerate code is not supported",
linerate_code
),
&Error::UnsupportedTopology => {
write!(
f,
"UnsupportedTopology - Channel #0 should be connected to the master channel"
)
}
&Error::UnsupportedVersion => write!(
f,
"UnsupportedVersion - Cannot find a compatible protocol version between the cxp grabber & camera"
),
&Error::CtrlPacketError(ref err) => write!(f, "{}", err),
}
}
}
pub fn master_channel_ready() -> bool {
unsafe { csr::cxp_grabber::core_rx_ready_read() == 1 }
}
fn monitor_channel_status_timeout(timer: GlobalTimer) -> Result<(), Error> {
let limit = timer.get_time() + Milliseconds(MONITOR_TIMEOUT_MS);
while timer.get_time() < limit {
if master_channel_ready() {
return Ok(());
}
}
Err(Error::ConnectionLost)
}
pub fn discover_camera(mut timer: GlobalTimer) -> Result<(), Error> {
// Section 7.6 (CXP-001-2021)
// 1.25Gbps (CXP_1) and 3.125Gbps (CXP_3) are the discovery rate
// both linerate need to be checked as camera only support ONE of discovery rates
for speed in [CXPSpeed::CXP1, CXPSpeed::CXP3].iter() {
// Section 12.1.2 (CXP-001-2021)
// set tx linerate -> send ConnectionReset -> wait 200ms -> set rx linerate -> monitor connection status with a timeout
tx::change_linerate(*speed);
write_bytes_no_ack(CONNECTION_RESET, &1_u32.to_be_bytes(), false)?;
timer.delay_ms(200);
rx::change_linerate(*speed);
if monitor_channel_status_timeout(timer).is_ok() {
debug!("camera detected at linerate {:}", speed);
return Ok(());
}
}
Err(Error::CameraNotDetected)
}
fn check_master_channel() -> Result<(), Error> {
if read_u32(DEVICE_CONNECTION_ID, false)? == 0 {
Ok(())
} else {
Err(Error::UnsupportedTopology)
}
}
fn disable_excess_channels(timer: GlobalTimer) -> Result<(), Error> {
let current_cfg = read_u32(CONNECTION_CFG, false)?;
let active_camera_chs = current_cfg >> 16;
// After camera receive ConnectionReset, only the master connection should be active while
// the extension connections shall not be active - Section 12.3.33 (CXP-001-2021)
// In case some camera didn't follow the spec properly (e.g. Basler boA2448-250cm),
// the grabber need to manually disable the excess channels
if active_camera_chs > CHANNEL_LEN as u32 {
debug!(
"only {} channel(s) is available on cxp grabber, disabling excess channels on camera",
CHANNEL_LEN
);
// disable excess channels and preserve the discovery linerate
write_u32(CONNECTION_CFG, current_cfg & 0xFFFF | (CHANNEL_LEN as u32) << 16, false)?;
// check if the master channel is down after the cfg change
monitor_channel_status_timeout(timer)
} else {
Ok(())
}
}
fn set_host_connection_id() -> Result<(), Error> {
debug!("set host connection id to = {:#X}", HOST_CONNECTION_ID);
write_u32(MASTER_HOST_CONNECTION_ID, HOST_CONNECTION_ID, false)?;
Ok(())
}
fn negotiate_cxp_version() -> Result<bool, Error> {
let rev = read_u32(REVISION, false)?;
let mut major_rev: u32 = rev >> 16;
let mut minor_rev: u32 = rev & 0xFF;
debug!("camera's CoaXPress revision is {}.{}", major_rev, minor_rev);
// Section 12.1.4 (CXP-001-2021)
// For CXP 2.0 and onward, Host need to check the VersionSupported register to determine
// the highest common version that supported by both device & host
if major_rev >= 2 {
let reg = read_u32(VERSION_SUPPORTED, false)?;
// grabber support CXP 2.1, 2.0 and 1.1 only
if ((reg >> 3) & 1) == 1 {
major_rev = 2;
minor_rev = 1;
} else if ((reg >> 2) & 1) == 1 {
major_rev = 2;
minor_rev = 0;
} else if ((reg >> 1) & 1) == 1 {
major_rev = 1;
minor_rev = 1;
} else {
return Err(Error::UnsupportedVersion);
}
write_u32(VERSION_USED, major_rev << 16 | minor_rev, false)?;
}
debug!(
"both camera and cxp grabber support CoaXPress {}.{}, switch to CoaXPress {}.{} protocol now",
major_rev, minor_rev, major_rev, minor_rev
);
Ok(major_rev >= 2)
}
fn negotiate_pak_max_size(with_tag: bool) -> Result<(), Error> {
write_u32(STREAM_PACKET_SIZE_MAX, MAX_STREAM_PAK_SIZE, with_tag)?;
Ok(())
}
fn decode_cxp_speed(linerate_code: u32) -> Option<CXPSpeed> {
match linerate_code {
0x28 => Some(CXPSpeed::CXP1),
0x30 => Some(CXPSpeed::CXP2),
0x38 => Some(CXPSpeed::CXP3),
0x40 => Some(CXPSpeed::CXP5),
0x48 => Some(CXPSpeed::CXP6),
0x50 => Some(CXPSpeed::CXP10),
0x58 => Some(CXPSpeed::CXP12),
_ => None,
}
}
fn set_operation_linerate(with_tag: bool, timer: GlobalTimer) -> Result<(), Error> {
let recommended_linerate_code = read_u32(CONNECTION_CFG_DEFAULT, with_tag)? & 0xFFFF;
if let Some(speed) = decode_cxp_speed(recommended_linerate_code) {
debug!("changing linerate to {}", speed);
// preserve the number of active channels
let current_cfg = read_u32(CONNECTION_CFG, with_tag)?;
write_u32(
CONNECTION_CFG,
current_cfg & 0xFFFF0000 | recommended_linerate_code,
with_tag,
)?;
tx::change_linerate(speed);
rx::change_linerate(speed);
monitor_channel_status_timeout(timer)
} else {
Err(Error::UnsupportedSpeed(recommended_linerate_code))
}
}
fn test_counter_reset(with_tag: bool) -> Result<(), Error> {
unsafe { csr::cxp_grabber::core_rx_test_counts_reset_write(1) };
write_u32(TEST_ERROR_COUNT_SELECTOR, 0, with_tag)?;
write_u32(TEST_ERROR_COUNT, 0, with_tag)?;
write_u64(TEST_PACKET_COUNT_TX, 0, with_tag)?;
write_u64(TEST_PACKET_COUNT_RX, 0, with_tag)?;
Ok(())
}
fn verify_test_result(with_tag: bool) -> Result<(), Error> {
write_u32(TEST_ERROR_COUNT_SELECTOR, 0, with_tag)?;
// Section 9.9.3 (CXP-001-2021)
// verify grabber -> camera connection test result
if read_u64(TEST_PACKET_COUNT_RX, with_tag)? != TX_TEST_CNT as u64 {
return Err(Error::UnstableTX);
};
if read_u32(TEST_ERROR_COUNT, with_tag)? > 0 {
return Err(Error::UnstableTX);
};
// Section 9.9.4 (CXP-001-2021)
// verify camera -> grabber connection test result
let camera_test_pak_cnt = read_u64(TEST_PACKET_COUNT_TX, true)?;
unsafe {
if csr::cxp_grabber::core_rx_test_packet_counter_read() != camera_test_pak_cnt as u16 {
return Err(Error::UnstableRX);
};
if csr::cxp_grabber::core_rx_test_error_counter_read() > 0 {
return Err(Error::UnstableRX);
};
};
debug!("channel #0 passed connection test");
Ok(())
}
fn test_channel_stability(with_tag: bool, mut timer: GlobalTimer) -> Result<(), Error> {
test_counter_reset(with_tag)?;
// cxp grabber -> camera connection test
for _ in 0..TX_TEST_CNT {
send_test_packet()?;
// sending the whole test sequence @ 20.833Mbps will take a minimum of 1.972ms
// and leave some room to send IDLE word
timer.delay_ms(2);
}
// camera -> grabber connection test
// enabling the TESTMODE on master channel will send test packets on all channels
// and ctrl packet write overhead is used as a delay
write_u32(TESTMODE, 1, with_tag)?;
write_u32(TESTMODE, 0, with_tag)?;
verify_test_result(with_tag)?;
Ok(())
}
pub fn camera_setup(timer: GlobalTimer) -> Result<bool, Error> {
reset_tag();
check_master_channel()?;
disable_excess_channels(timer)?;
set_host_connection_id()?;
let with_tag = negotiate_cxp_version()?;
negotiate_pak_max_size(with_tag)?;
set_operation_linerate(with_tag, timer)?;
test_channel_stability(with_tag, timer)?;
Ok(with_tag)
}

View File

@ -0,0 +1,250 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core_io::{Error as IoError, Read, Write};
use crc::crc32::checksum_ieee;
use io::{Cursor, ProtoRead, ProtoWrite};
pub const CTRL_PACKET_MAXSIZE: usize = 128; // for compatibility with version1.x compliant Devices - Section 12.1.6 (CXP-001-2021)
pub const DATA_MAXSIZE: usize =
CTRL_PACKET_MAXSIZE - /*packet start KCodes, data packet types, CMD, Tag, Addr, CRC, packet end KCode*/4*7;
pub enum Error {
CorruptedPacket,
CtrlAckError(u8),
Io(IoError),
LengthOutOfRange(u32),
TagMismatch,
TimedOut,
UnexpectedReply,
UnknownPacket(u8),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::CorruptedPacket => write!(f, "CorruptedPacket - Received packet fail CRC test"),
&Error::CtrlAckError(ref ack_code) => match ack_code {
0x40 => write!(f, "CtrlAckError - Invalid Address"),
0x41 => write!(f, "CtrlAckError - Invalid data for the address"),
0x42 => write!(f, "CtrlAckError - Invalid operation code"),
0x43 => write!(f, "CtrlAckError - Write attempted to a read-only address"),
0x44 => write!(f, "CtrlAckError - Read attempted from a write-only address"),
0x45 => write!(f, "CtrlAckError - Size field too large, exceed packet size limit"),
0x46 => write!(f, "CtrlAckError - Message size is inconsistent with size field"),
0x47 => write!(f, "CtrlAckError - Malformed packet"),
0x80 => write!(f, "CtrlAckError - Failed CRC test in last received command"),
_ => write!(f, "CtrlAckError - Unknown ack code {:#X}", ack_code),
},
&Error::Io(ref err) => write!(f, "IoError - {:?}", err),
&Error::LengthOutOfRange(length) => write!(
f,
"LengthOutOfRange - Message length {} is not between 1 and {}",
length, DATA_MAXSIZE
),
&Error::TagMismatch => write!(f, "TagMismatch - Received tag is different from the transmitted tag"),
&Error::TimedOut => write!(f, "MessageTimedOut"),
&Error::UnexpectedReply => write!(f, "UnexpectedReply"),
&Error::UnknownPacket(packet_type) => {
write!(f, "UnknownPacket - Unknown packet type id {:#X} ", packet_type)
}
}
}
}
impl From<IoError> for Error {
fn from(value: IoError) -> Error {
Error::Io(value)
}
}
fn get_cxp_crc(bytes: &[u8]) -> u32 {
// Section 9.2.2.2 (CXP-001-2021)
// Only Control packet need CRC32 appended in the end of the packet
// CoaXpress use the polynomial of IEEE-802.3 (Ethernet) CRC but the checksum calculation is different
(!checksum_ieee(bytes)).swap_bytes()
}
trait CxpRead: Read {
fn read_exact_4x(&mut self, buf: &mut [u8]) -> Result<(), Error> {
let mut temp = [0u8; 4];
for byte in buf {
// Section 9.2.2.1 (CXP-001-2021)
// decoder should immune to single bit errors when handling 4x duplicated characters
self.read_exact(&mut temp)?;
let [a, b, c, d] = temp;
// vote and return majority
*byte = a & b & c | a & b & d | a & c & d | b & c & d;
}
Ok(())
}
fn read_4x_u8(&mut self) -> Result<u8, Error> {
let mut bytes = [0; 1];
self.read_exact_4x(&mut bytes)?;
Ok(bytes[0])
}
}
impl<T: Read> CxpRead for T {}
impl<T: Write> CxpWrite for T {}
#[derive(Debug)]
pub enum RXCTRLPacket {
CtrlReply {
tag: Option<u8>,
length: u32,
data: [u8; DATA_MAXSIZE],
},
CtrlDelay {
tag: Option<u8>,
time: u32,
},
CtrlAck {
tag: Option<u8>,
},
}
impl RXCTRLPacket {
pub fn read_from(reader: &mut Cursor<&[u8]>) -> Result<Self, Error> {
match reader.read_4x_u8()? {
0x03 => RXCTRLPacket::get_ctrl_packet(reader, false),
0x06 => RXCTRLPacket::get_ctrl_packet(reader, true),
ty => Err(Error::UnknownPacket(ty)),
}
}
fn get_ctrl_packet(reader: &mut Cursor<&[u8]>, with_tag: bool) -> Result<Self, Error> {
let mut tag: Option<u8> = None;
if with_tag {
tag = Some(reader.read_4x_u8()?);
}
let ackcode = reader.read_4x_u8()?;
match ackcode {
0x00 | 0x04 => {
let length = reader.read_u32::<NetworkEndian>()?;
let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
reader.read(&mut data[0..length as usize])?;
// Section 9.6.3 (CXP-001-2021)
// when length is not multiple of 4, dummy bits are padded to align to the word boundary
// set position to next word boundary for CRC calculation
let padding = (4 - (reader.position() % 4)) % 4;
reader.set_position(reader.position() + padding);
// Section 9.6.3 (CXP-001-2021)
// only bytes after the first 4 are used in calculating the checksum
let checksum = get_cxp_crc(&reader.get_ref()[4..reader.position()]);
if reader.read_u32::<NetworkEndian>()? != checksum {
return Err(Error::CorruptedPacket);
}
if ackcode == 0x00 {
return Ok(RXCTRLPacket::CtrlReply { tag, length, data });
} else {
return Ok(RXCTRLPacket::CtrlDelay {
tag,
time: NetworkEndian::read_u32(&data[..4]),
});
}
}
0x01 => return Ok(RXCTRLPacket::CtrlAck { tag }),
_ => return Err(Error::CtrlAckError(ackcode)),
}
}
}
trait CxpWrite: Write {
fn write_all_4x(&mut self, buf: &[u8]) -> Result<(), Error> {
for byte in buf {
self.write_all(&[*byte; 4])?;
}
Ok(())
}
fn write_4x_u8(&mut self, value: u8) -> Result<(), Error> {
self.write_all_4x(&[value])
}
}
#[derive(Debug)]
pub enum TXCTRLPacket {
CtrlRead {
tag: Option<u8>,
addr: u32,
length: u32,
},
CtrlWrite {
tag: Option<u8>,
addr: u32,
length: u32,
data: [u8; DATA_MAXSIZE],
},
}
impl TXCTRLPacket {
pub fn write_to(&self, writer: &mut Cursor<&mut [u8]>) -> Result<(), Error> {
match *self {
TXCTRLPacket::CtrlRead { tag, addr, length } => {
match tag {
Some(t) => {
writer.write_4x_u8(0x05)?;
writer.write_4x_u8(t)?;
}
None => {
writer.write_4x_u8(0x02)?;
}
}
let mut bytes = [0; 3];
NetworkEndian::write_u24(&mut bytes, length);
writer.write_all(&[0x00, bytes[0], bytes[1], bytes[2]])?;
writer.write_u32::<NetworkEndian>(addr)?;
// Section 9.6.2 (CXP-001-2021)
// only bytes after the first 4 are used in calculating the checksum
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
writer.write_u32::<NetworkEndian>(checksum)?;
}
TXCTRLPacket::CtrlWrite {
tag,
addr,
length,
data,
} => {
match tag {
Some(t) => {
writer.write_4x_u8(0x05)?;
writer.write_4x_u8(t)?;
}
None => {
writer.write_4x_u8(0x02)?;
}
}
let mut bytes = [0; 3];
NetworkEndian::write_u24(&mut bytes, length);
writer.write_all(&[0x01, bytes[0], bytes[1], bytes[2]])?;
writer.write_u32::<NetworkEndian>(addr)?;
writer.write_all(&data[0..length as usize])?;
// Section 9.6.2 (CXP-001-2021)
// when length is not multiple of 4, dummy bites are padded to align to the word boundary
let padding = (4 - (writer.position() % 4)) % 4;
for _ in 0..padding {
writer.write_u8(0)?;
}
// Section 9.6.2 (CXP-001-2021)
// only bytes after the first 4 are used in calculating the checksum
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
writer.write_u32::<NetworkEndian>(checksum)?;
}
}
Ok(())
}
}

View File

@ -0,0 +1,90 @@
use libboard_zynq::timer::GlobalTimer;
use libcortex_a9::mutex::Mutex;
use log::{error, info};
use crate::{cxp_camera_setup::{camera_setup, discover_camera, master_channel_ready},
pl::csr};
#[derive(Clone, Copy, Debug, PartialEq)]
enum State {
Connected,
Detected,
Disconnected,
}
// Mutex as they are needed by core1 cxp api calls
static mut STATE: Mutex<State> = Mutex::new(State::Disconnected);
static mut WITH_TAG: Mutex<bool> = Mutex::new(false);
pub fn camera_connected() -> bool {
unsafe { *STATE.lock() == State::Connected }
}
pub fn with_tag() -> bool {
unsafe { *WITH_TAG.lock() }
}
pub fn tick(timer: GlobalTimer) {
let mut state_guard = unsafe { STATE.lock() };
let mut with_tag_guard = unsafe { WITH_TAG.lock() };
*state_guard = match *state_guard {
State::Disconnected => match discover_camera(timer) {
Ok(_) => {
info!("camera detected, setting up camera...");
State::Detected
}
Err(_) => State::Disconnected,
},
State::Detected => match camera_setup(timer) {
Ok(with_tag) => {
info!("camera setup complete");
*with_tag_guard = with_tag;
State::Connected
}
Err(e) => {
error!("camera setup failure: {}", e);
*with_tag_guard = false;
State::Disconnected
}
},
State::Connected => {
if master_channel_ready() {
unsafe {
if csr::cxp_grabber::stream_decoder_crc_error_read() == 1 {
error!("frame packet has CRC error");
csr::cxp_grabber::stream_decoder_crc_error_write(1);
};
if csr::cxp_grabber::stream_decoder_stream_type_error_read() == 1 {
error!("Non CoaXPress stream type detected, the CXP grabber doesn't support GenDC stream type");
csr::cxp_grabber::stream_decoder_stream_type_error_write(1);
};
if csr::cxp_grabber::core_rx_trigger_ack_read() == 1 {
info!("received CXP linktrigger ack");
csr::cxp_grabber::core_rx_trigger_ack_write(1);
};
if csr::cxp_grabber::stream_decoder_new_frame_read() == 1 {
let width = csr::cxp_grabber::stream_decoder_x_size_read();
let height = csr::cxp_grabber::stream_decoder_y_size_read();
match csr::cxp_grabber::stream_decoder_pixel_format_code_read() {
0x0101 => info!("received frame: {}x{} with MONO8 format", width, height),
0x0102 => info!("received frame: {}x{} with MONO10 format", width, height),
0x0103 => info!("received frame: {}x{} with MONO12 format", width, height),
0x0104 => info!("received frame: {}x{} with MONO14 format", width, height),
0x0105 => info!("received frame: {}x{} with MONO16 format", width, height),
_ => info!("received frame: {}x{} with Unsupported pixel format", width, height),
};
csr::cxp_grabber::stream_decoder_new_frame_write(1);
};
}
State::Connected
} else {
*with_tag_guard = false;
info!("camera disconnected");
State::Disconnected
}
}
};
}

View File

@ -0,0 +1,192 @@
use core::slice;
use byteorder::{ByteOrder, NetworkEndian};
use io::Cursor;
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use crate::{cxp_ctrl::{Error, RXCTRLPacket, TXCTRLPacket, CTRL_PACKET_MAXSIZE, DATA_MAXSIZE},
mem::mem,
pl::csr};
const TRANSMISSION_TIMEOUT: u64 = 200;
// Section 9.6.1.2 (CXP-001-2021)
// CTRL packet need to be tagged for CXP 2.0 or greater
static mut TAG: u8 = 0;
pub fn reset_tag() {
unsafe { TAG = 0 };
}
fn increment_tag() {
unsafe { TAG = TAG.wrapping_add(1) };
}
fn check_tag(tag: Option<u8>) -> Result<(), Error> {
unsafe {
if tag.is_some() && tag != Some(TAG) {
Err(Error::TagMismatch)
} else {
Ok(())
}
}
}
fn receive_ctrl_packet() -> Result<Option<RXCTRLPacket>, Error> {
if unsafe { csr::cxp_grabber::core_rx_pending_packet_read() == 1 } {
unsafe {
let read_buffer_ptr = csr::cxp_grabber::core_rx_read_ptr_read() as usize;
let ptr = (mem::CXP_MEM_BASE + mem::CXP_MEM_SIZE / 2 + read_buffer_ptr * CTRL_PACKET_MAXSIZE) as *mut u32;
let mut reader = Cursor::new(slice::from_raw_parts(ptr as *const u8, CTRL_PACKET_MAXSIZE));
let packet = RXCTRLPacket::read_from(&mut reader);
csr::cxp_grabber::core_rx_pending_packet_write(1);
Ok(Some(packet?))
}
} else {
Ok(None)
}
}
fn receive_ctrl_packet_timeout(timeout_ms: u64) -> Result<RXCTRLPacket, Error> {
// assume timer was initialized successfully
let timer = unsafe { GlobalTimer::get() };
let limit = timer.get_time() + Milliseconds(timeout_ms);
while timer.get_time() < limit {
match receive_ctrl_packet()? {
None => (),
Some(packet) => return Ok(packet),
}
}
Err(Error::TimedOut)
}
fn send_ctrl_packet(packet: &TXCTRLPacket) -> Result<(), Error> {
unsafe {
while csr::cxp_grabber::core_tx_writer_busy_read() == 1 {}
let ptr = mem::CXP_MEM_BASE as *mut u32;
let mut writer = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, CTRL_PACKET_MAXSIZE));
packet.write_to(&mut writer)?;
csr::cxp_grabber::core_tx_writer_word_len_write((writer.position() / 4) as u8);
csr::cxp_grabber::core_tx_writer_stb_write(1);
}
Ok(())
}
pub fn send_test_packet() -> Result<(), Error> {
unsafe {
while csr::cxp_grabber::core_tx_writer_busy_read() == 1 {}
csr::cxp_grabber::core_tx_writer_stb_testseq_write(1);
}
Ok(())
}
fn get_ctrl_ack(timeout: u64) -> Result<(), Error> {
match receive_ctrl_packet_timeout(timeout) {
Ok(RXCTRLPacket::CtrlAck { tag }) => {
check_tag(tag)?;
Ok(())
}
Ok(RXCTRLPacket::CtrlDelay { tag, time }) => {
check_tag(tag)?;
get_ctrl_ack(time as u64)
}
Ok(_) => Err(Error::UnexpectedReply),
Err(e) => Err(e),
}
}
fn get_ctrl_reply(timeout: u64, expected_length: u32) -> Result<[u8; DATA_MAXSIZE], Error> {
match receive_ctrl_packet_timeout(timeout) {
Ok(RXCTRLPacket::CtrlReply { tag, length, data }) => {
check_tag(tag)?;
if length != expected_length {
return Err(Error::UnexpectedReply);
};
Ok(data)
}
Ok(RXCTRLPacket::CtrlDelay { tag, time }) => {
check_tag(tag)?;
get_ctrl_reply(time as u64, expected_length)
}
Ok(_) => Err(Error::UnexpectedReply),
Err(e) => Err(e),
}
}
fn check_length(length: u32) -> Result<(), Error> {
if length > DATA_MAXSIZE as u32 || length == 0 {
Err(Error::LengthOutOfRange(length))
} else {
Ok(())
}
}
pub fn write_bytes_no_ack(addr: u32, val: &[u8], with_tag: bool) -> Result<(), Error> {
let length = val.len() as u32;
check_length(length)?;
let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
data[..length as usize].clone_from_slice(val);
let tag: Option<u8> = if with_tag { Some(unsafe { TAG }) } else { None };
send_ctrl_packet(&TXCTRLPacket::CtrlWrite {
tag,
addr,
length,
data,
})
}
pub fn write_bytes(addr: u32, val: &[u8], with_tag: bool) -> Result<(), Error> {
write_bytes_no_ack(addr, val, with_tag)?;
get_ctrl_ack(TRANSMISSION_TIMEOUT)?;
if with_tag {
increment_tag();
};
Ok(())
}
pub fn write_u32(addr: u32, val: u32, with_tag: bool) -> Result<(), Error> {
write_bytes(addr, &val.to_be_bytes(), with_tag)
}
pub fn write_u64(addr: u32, val: u64, with_tag: bool) -> Result<(), Error> {
write_bytes(addr, &val.to_be_bytes(), with_tag)
}
pub fn read_bytes(addr: u32, bytes: &mut [u8], with_tag: bool) -> Result<(), Error> {
let length = bytes.len() as u32;
check_length(length)?;
let tag: Option<u8> = if with_tag { Some(unsafe { TAG }) } else { None };
send_ctrl_packet(&TXCTRLPacket::CtrlRead { tag, addr, length })?;
let data = get_ctrl_reply(TRANSMISSION_TIMEOUT, length)?;
bytes.clone_from_slice(&data[..length as usize]);
if with_tag {
increment_tag();
};
Ok(())
}
pub fn read_u32(addr: u32, with_tag: bool) -> Result<u32, Error> {
let mut bytes: [u8; 4] = [0; 4];
read_bytes(addr, &mut bytes, with_tag)?;
let val = NetworkEndian::read_u32(&bytes);
Ok(val)
}
pub fn read_u64(addr: u32, with_tag: bool) -> Result<u64, Error> {
let mut bytes: [u8; 8] = [0; 8];
read_bytes(addr, &mut bytes, with_tag)?;
let val = NetworkEndian::read_u64(&bytes);
Ok(val)
}

View File

@ -0,0 +1,193 @@
use core::fmt;
use crate::pl::csr;
#[derive(Clone, Copy)]
pub enum CXPSpeed {
CXP1,
CXP2,
CXP3,
CXP5,
CXP6,
CXP10,
CXP12,
}
impl fmt::Display for CXPSpeed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&CXPSpeed::CXP1 => write!(f, "1.25 Gbps"),
&CXPSpeed::CXP2 => write!(f, "2.5 Gbps"),
&CXPSpeed::CXP3 => write!(f, "3.125 Gbps"),
&CXPSpeed::CXP5 => write!(f, "5 Gbps"),
&CXPSpeed::CXP6 => write!(f, "6.25 Gbps"),
&CXPSpeed::CXP10 => write!(f, "10 Gbps"),
&CXPSpeed::CXP12 => write!(f, "12.5 Gbps"),
}
}
}
pub fn setup() {
let init_speed = CXPSpeed::CXP1;
tx::setup();
tx::change_linerate(init_speed);
rx::setup();
rx::change_linerate(init_speed);
}
pub mod tx {
use super::*;
pub fn setup() {
unsafe {
csr::cxp_grabber::phy_tx_enable_write(1);
}
}
pub fn change_linerate(speed: CXPSpeed) {
unsafe {
match speed {
CXPSpeed::CXP1 | CXPSpeed::CXP2 | CXPSpeed::CXP3 | CXPSpeed::CXP5 | CXPSpeed::CXP6 => {
csr::cxp_grabber::phy_tx_bitrate2x_enable_write(0);
}
CXPSpeed::CXP10 | CXPSpeed::CXP12 => {
csr::cxp_grabber::phy_tx_bitrate2x_enable_write(1);
}
};
csr::cxp_grabber::phy_tx_clk_reset_write(1);
}
}
}
pub mod rx {
use super::*;
pub fn setup() {
unsafe {
csr::cxp_grabber::phy_rx_gtx_refclk_stable_write(1);
}
}
pub fn change_linerate(speed: CXPSpeed) {
change_qpll_fb_divider(speed);
change_gtx_divider(speed);
change_cdr_cfg(speed);
unsafe {
csr::cxp_grabber::phy_rx_qpll_reset_write(1);
while csr::cxp_grabber::phy_rx_qpll_locked_read() != 1 {}
// Changing RXOUT_DIV via DRP requires a manual reset
// https://adaptivesupport.amd.com/s/question/0D52E00006hplwnSAA/re-gtx-line-rate-change
csr::cxp_grabber::phy_rx_gtx_restart_write(1);
}
}
fn change_qpll_fb_divider(speed: CXPSpeed) {
let qpll_div_reg = match speed {
CXPSpeed::CXP1 | CXPSpeed::CXP2 | CXPSpeed::CXP5 | CXPSpeed::CXP10 => 0x0120, // FB_Divider = 80, QPLL VCO @ 10GHz
CXPSpeed::CXP3 | CXPSpeed::CXP6 | CXPSpeed::CXP12 => 0x0170, // FB_Divider = 100, QPLL VCO @ 12.5GHz
};
qpll_write(0x36, qpll_div_reg);
}
fn change_gtx_divider(speed: CXPSpeed) {
let div_reg = match speed {
CXPSpeed::CXP1 => 0x33, // RXOUT_DIV = 8
CXPSpeed::CXP2 | CXPSpeed::CXP3 => 0x22, // RXOUT_DIV = 4
CXPSpeed::CXP5 | CXPSpeed::CXP6 => 0x11, // RXOUT_DIV = 2
CXPSpeed::CXP10 | CXPSpeed::CXP12 => 0x00, // RXOUT_DIV = 1
};
gtx_write(0x88, div_reg);
}
fn change_cdr_cfg(speed: CXPSpeed) {
struct CdrConfig {
pub cfg_reg0: u16, // addr = 0xA8
pub cfg_reg1: u16, // addr = 0xA9
pub cfg_reg2: u16, // addr = 0xAA
pub cfg_reg3: u16, // addr = 0xAB
pub cfg_reg4: u16, // addr = 0xAC
}
let cdr_cfg = match speed {
// when RXOUT_DIV = 8
CXPSpeed::CXP1 => CdrConfig {
cfg_reg0: 0x0020,
cfg_reg1: 0x1008,
cfg_reg2: 0x23FF,
cfg_reg3: 0x0000,
cfg_reg4: 0x0003,
},
// when RXOUT_DIV = 4
CXPSpeed::CXP2 | CXPSpeed::CXP5 => CdrConfig {
cfg_reg0: 0x0020,
cfg_reg1: 0x1010,
cfg_reg2: 0x23FF,
cfg_reg3: 0x0000,
cfg_reg4: 0x0003,
},
// when RXOUT_DIV= 2
CXPSpeed::CXP3 | CXPSpeed::CXP6 => CdrConfig {
cfg_reg0: 0x0020,
cfg_reg1: 0x1020,
cfg_reg2: 0x23FF,
cfg_reg3: 0x0000,
cfg_reg4: 0x0003,
},
// when RXOUT_DIV= 1
CXPSpeed::CXP10 | CXPSpeed::CXP12 => CdrConfig {
cfg_reg0: 0x0020,
cfg_reg1: 0x1040,
cfg_reg2: 0x23FF,
cfg_reg3: 0x0000,
cfg_reg4: 0x000B,
},
};
gtx_write(0x0A8, cdr_cfg.cfg_reg0);
gtx_write(0x0A9, cdr_cfg.cfg_reg1);
gtx_write(0x0AA, cdr_cfg.cfg_reg2);
gtx_write(0x0AB, cdr_cfg.cfg_reg3);
gtx_write(0x0AC, cdr_cfg.cfg_reg4);
}
#[allow(dead_code)]
fn gtx_read(address: u16) -> u16 {
unsafe {
csr::cxp_grabber::phy_rx_gtx_daddr_write(address);
csr::cxp_grabber::phy_rx_gtx_dread_write(1);
while csr::cxp_grabber::phy_rx_gtx_dready_read() != 1 {}
csr::cxp_grabber::phy_rx_gtx_dout_read()
}
}
fn gtx_write(address: u16, value: u16) {
unsafe {
csr::cxp_grabber::phy_rx_gtx_daddr_write(address);
csr::cxp_grabber::phy_rx_gtx_din_write(value);
csr::cxp_grabber::phy_rx_gtx_din_stb_write(1);
while csr::cxp_grabber::phy_rx_gtx_dready_read() != 1 {}
}
}
#[allow(dead_code)]
fn qpll_read(address: u8) -> u16 {
unsafe {
csr::cxp_grabber::phy_rx_qpll_daddr_write(address);
csr::cxp_grabber::phy_rx_qpll_dread_write(1);
while csr::cxp_grabber::phy_rx_qpll_dready_read() != 1 {}
csr::cxp_grabber::phy_rx_qpll_dout_read()
}
}
fn qpll_write(address: u8, value: u16) {
unsafe {
csr::cxp_grabber::phy_rx_qpll_daddr_write(address);
csr::cxp_grabber::phy_rx_qpll_din_write(value);
csr::cxp_grabber::phy_rx_qpll_din_stb_write(1);
while csr::cxp_grabber::phy_rx_qpll_dready_read() != 1 {}
}
}
}

View File

@ -185,6 +185,24 @@ unsafe fn align_comma(timer: &mut GlobalTimer) {
}
}
pub unsafe fn align_wordslip(timer: &mut GlobalTimer, trx_no: u8) -> bool {
pl::csr::eem_transceiver::transceiver_sel_write(trx_no);
for slip in 0..=1 {
pl::csr::eem_transceiver::wordslip_write(slip as u8);
timer.delay_us(1);
pl::csr::eem_transceiver::comma_align_reset_write(1);
timer.delay_us(100);
if pl::csr::eem_transceiver::comma_read() == 1 {
debug!("comma alignment completed with {} wordslip", slip);
return true;
}
}
false
}
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
for trx_no in 0..pl::csr::CONFIG_EEM_DRTIO_COUNT {
unsafe {
@ -222,7 +240,6 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
unsafe {
align_comma(timer);
pl::csr::eem_transceiver::rx_ready_write(1);
}
}
}

View File

@ -1,12 +1,12 @@
use core::slice;
use core::{arch::asm, slice};
use byteorder::NativeEndian;
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
use crc;
use io::{proto::{ProtoRead, ProtoWrite},
Cursor};
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
pub use crate::drtioaux_proto::Packet;
pub use crate::drtioaux_proto::{Packet, MAX_PACKET};
use crate::{drtioaux_proto::Error as ProtocolError, mem::mem::DRTIOAUX_MEM, pl::csr::DRTIOAUX};
#[derive(Debug)]
@ -35,6 +35,16 @@ impl From<IoError> for Error {
}
}
pub fn copy_work_buffer(src: *mut u32, dst: *mut u32, len: isize) {
// fix for artiq-zynq#344
unsafe {
for i in 0..(len / 4) {
asm!("", options(preserves_flags, nostack, readonly));
*dst.offset(i) = *src.offset(i);
}
}
}
pub fn reset(linkno: u8) {
let linkno = linkno as usize;
unsafe {
@ -90,7 +100,7 @@ pub fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
let checksum_at = reader.position() + padding;
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
reader.set_position(checksum_at);
if reader.read_u32()? != checksum {
if reader.read_u32::<NativeEndian>()? != checksum {
return Err(Error::CorruptedPacket);
}
Ok(packet)
@ -115,7 +125,9 @@ where F: FnOnce(&mut [u8]) -> Result<usize, Error> {
unsafe {
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
let len = f(slice::from_raw_parts_mut(ptr as *mut u8, 0x400 as usize))?;
let mut buf: [u8; MAX_PACKET] = [0; MAX_PACKET];
let len = f(&mut buf)?;
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(())
@ -135,7 +147,7 @@ pub fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
}
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
writer.write_u32(checksum)?;
writer.write_u32::<NativeEndian>(checksum)?;
Ok(writer.position())
})

View File

@ -1,16 +1,15 @@
use core::slice;
use byteorder::NativeEndian;
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
use crc;
use io::{proto::{ProtoRead, ProtoWrite},
Cursor};
use libasync::{block_async, task};
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use nb;
use void::Void;
pub use crate::drtioaux_proto::Packet;
use crate::{drtioaux::{has_rx_error, Error},
pub use crate::drtioaux_proto::{Packet, MAX_PACKET};
use crate::{drtioaux::{copy_work_buffer, has_rx_error, Error},
mem::mem::DRTIOAUX_MEM,
pl::csr::DRTIOAUX};
@ -68,7 +67,7 @@ pub async fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
let checksum_at = reader.position() + padding;
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
reader.set_position(checksum_at);
if reader.read_u32()? != checksum {
if reader.read_u32::<NativeEndian>()? != checksum {
return Err(Error::CorruptedPacket);
}
Ok(packet)
@ -102,7 +101,9 @@ where F: FnOnce(&mut [u8]) -> Result<usize, Error> {
unsafe {
let _ = block_async!(tx_ready(linkno)).await;
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
let len = f(slice::from_raw_parts_mut(ptr as *mut u8, 0x400 as usize))?;
let mut buf: [u8; MAX_PACKET] = [0; MAX_PACKET];
let len = f(&mut buf)?;
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(())
@ -122,7 +123,7 @@ pub async fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
}
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
writer.write_u32(checksum)?;
writer.write_u32::<NativeEndian>(checksum)?;
Ok(writer.position())
})

View File

@ -1,11 +1,13 @@
use core_io::{Error as IoError, Read, Write};
use byteorder::NativeEndian;
use core_io::Error as IoError;
use io::proto::{ProtoRead, ProtoWrite};
const MAX_PACKET: usize = 1024;
pub const MAX_PACKET: usize = 1024;
// maximum size of arbitrary payloads
// used by satellite -> master analyzer, subkernel exceptions
pub const SAT_PAYLOAD_MAX_SIZE: usize = /*max size*/MAX_PACKET - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
pub const SAT_PAYLOAD_MAX_SIZE: usize = /*max size*/
MAX_PACKET - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
// used by DDMA, subkernel program data (need to provide extra ID and destination)
pub const MASTER_PAYLOAD_MAX_SIZE: usize = SAT_PAYLOAD_MAX_SIZE - /*source*/1 - /*destination*/1 - /*ID*/4;
@ -255,6 +257,7 @@ pub enum Packet {
destination: u8,
id: u32,
run: bool,
timestamp: u64,
},
SubkernelLoadRunReply {
destination: u8,
@ -267,12 +270,14 @@ pub enum Packet {
exception_src: u8,
},
SubkernelExceptionRequest {
source: u8,
destination: u8,
},
SubkernelException {
destination: u8,
last: bool,
length: u16,
data: [u8; SAT_PAYLOAD_MAX_SIZE],
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
SubkernelMessage {
source: u8,
@ -285,11 +290,81 @@ pub enum Packet {
SubkernelMessageAck {
destination: u8,
},
CoreMgmtGetLogRequest {
destination: u8,
clear: bool,
},
CoreMgmtClearLogRequest {
destination: u8,
},
CoreMgmtSetLogLevelRequest {
destination: u8,
log_level: u8,
},
CoreMgmtSetUartLogLevelRequest {
destination: u8,
log_level: u8,
},
CoreMgmtConfigReadRequest {
destination: u8,
length: u16,
key: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
CoreMgmtConfigReadContinue {
destination: u8,
},
CoreMgmtConfigWriteRequest {
destination: u8,
last: bool,
length: u16,
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
CoreMgmtConfigRemoveRequest {
destination: u8,
length: u16,
key: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
CoreMgmtConfigEraseRequest {
destination: u8,
},
CoreMgmtRebootRequest {
destination: u8,
},
CoreMgmtAllocatorDebugRequest {
destination: u8,
},
CoreMgmtFlashRequest {
destination: u8,
payload_length: u32,
},
CoreMgmtFlashAddDataRequest {
destination: u8,
last: bool,
length: u16,
data: [u8; MASTER_PAYLOAD_MAX_SIZE],
},
CoreMgmtDropLinkAck {
destination: u8,
},
CoreMgmtDropLink,
CoreMgmtGetLogReply {
last: bool,
length: u16,
data: [u8; SAT_PAYLOAD_MAX_SIZE],
},
CoreMgmtConfigReadReply {
last: bool,
length: u16,
value: [u8; SAT_PAYLOAD_MAX_SIZE],
},
CoreMgmtReply {
succeeded: bool,
},
}
impl Packet {
pub fn read_from<R>(reader: &mut R) -> Result<Self, Error>
where R: Read + ?Sized {
pub fn read_from<R: ProtoRead>(reader: &mut R) -> Result<Self, Error> {
Ok(match reader.read_u8()? {
0x00 => Packet::EchoRequest,
0x01 => Packet::EchoReply,
@ -303,13 +378,13 @@ impl Packet {
0x21 => Packet::DestinationDownReply,
0x22 => Packet::DestinationOkReply,
0x23 => Packet::DestinationSequenceErrorReply {
channel: reader.read_u16()?,
channel: reader.read_u16::<NativeEndian>()?,
},
0x24 => Packet::DestinationCollisionReply {
channel: reader.read_u16()?,
channel: reader.read_u16::<NativeEndian>()?,
},
0x25 => Packet::DestinationBusyReply {
channel: reader.read_u16()?,
channel: reader.read_u16::<NativeEndian>()?,
},
0x30 => {
@ -328,21 +403,21 @@ impl Packet {
0x40 => Packet::MonitorRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
channel: reader.read_u16::<NativeEndian>()?,
probe: reader.read_u8()?,
},
0x41 => Packet::MonitorReply {
value: reader.read_u64()?,
value: reader.read_u64::<NativeEndian>()?,
},
0x50 => Packet::InjectionRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
channel: reader.read_u16::<NativeEndian>()?,
overrd: reader.read_u8()?,
value: reader.read_u8()?,
},
0x51 => Packet::InjectionStatusRequest {
destination: reader.read_u8()?,
channel: reader.read_u16()?,
channel: reader.read_u16::<NativeEndian>()?,
overrd: reader.read_u8()?,
},
0x52 => Packet::InjectionStatusReply {
@ -401,7 +476,7 @@ impl Packet {
0x92 => Packet::SpiWriteRequest {
destination: reader.read_u8()?,
busno: reader.read_u8()?,
data: reader.read_u32()?,
data: reader.read_u32::<NativeEndian>()?,
},
0x93 => Packet::SpiReadRequest {
destination: reader.read_u8()?,
@ -409,7 +484,7 @@ impl Packet {
},
0x94 => Packet::SpiReadReply {
succeeded: reader.read_bool()?,
data: reader.read_u32()?,
data: reader.read_u32::<NativeEndian>()?,
},
0x95 => Packet::SpiBasicReply {
succeeded: reader.read_bool()?,
@ -419,8 +494,8 @@ impl Packet {
destination: reader.read_u8()?,
},
0xa1 => Packet::AnalyzerHeader {
sent_bytes: reader.read_u32()?,
total_byte_count: reader.read_u64()?,
sent_bytes: reader.read_u32::<NativeEndian>()?,
total_byte_count: reader.read_u64::<NativeEndian>()?,
overflow_occurred: reader.read_bool()?,
},
0xa2 => Packet::AnalyzerDataRequest {
@ -428,7 +503,7 @@ impl Packet {
},
0xa3 => {
let last = reader.read_bool()?;
let length = reader.read_u16()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::AnalyzerData {
@ -441,9 +516,9 @@ impl Packet {
0xb0 => {
let source = reader.read_u8()?;
let destination = reader.read_u8()?;
let id = reader.read_u32()?;
let id = reader.read_u32::<NativeEndian>()?;
let status = reader.read_u8()?;
let length = reader.read_u16()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut trace: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut trace[0..length as usize])?;
Packet::DmaAddTraceRequest {
@ -458,13 +533,13 @@ impl Packet {
0xb1 => Packet::DmaAddTraceReply {
source: reader.read_u8()?,
destination: reader.read_u8()?,
id: reader.read_u32()?,
id: reader.read_u32::<NativeEndian>()?,
succeeded: reader.read_bool()?,
},
0xb2 => Packet::DmaRemoveTraceRequest {
source: reader.read_u8()?,
destination: reader.read_u8()?,
id: reader.read_u32()?,
id: reader.read_u32::<NativeEndian>()?,
},
0xb3 => Packet::DmaRemoveTraceReply {
destination: reader.read_u8()?,
@ -473,8 +548,8 @@ impl Packet {
0xb4 => Packet::DmaPlaybackRequest {
source: reader.read_u8()?,
destination: reader.read_u8()?,
id: reader.read_u32()?,
timestamp: reader.read_u64()?,
id: reader.read_u32::<NativeEndian>()?,
timestamp: reader.read_u64::<NativeEndian>()?,
},
0xb5 => Packet::DmaPlaybackReply {
destination: reader.read_u8()?,
@ -483,17 +558,17 @@ impl Packet {
0xb6 => Packet::DmaPlaybackStatus {
source: reader.read_u8()?,
destination: reader.read_u8()?,
id: reader.read_u32()?,
id: reader.read_u32::<NativeEndian>()?,
error: reader.read_u8()?,
channel: reader.read_u32()?,
timestamp: reader.read_u64()?,
channel: reader.read_u32::<NativeEndian>()?,
timestamp: reader.read_u64::<NativeEndian>()?,
},
0xc0 => {
let destination = reader.read_u8()?;
let id = reader.read_u32()?;
let id = reader.read_u32::<NativeEndian>()?;
let status = PayloadStatus::from(reader.read_u8()?);
let length = reader.read_u16()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::SubkernelAddDataRequest {
@ -510,8 +585,9 @@ impl Packet {
0xc4 => Packet::SubkernelLoadRunRequest {
source: reader.read_u8()?,
destination: reader.read_u8()?,
id: reader.read_u32()?,
id: reader.read_u32::<NativeEndian>()?,
run: reader.read_bool()?,
timestamp: reader.read_u64::<NativeEndian>()?,
},
0xc5 => Packet::SubkernelLoadRunReply {
destination: reader.read_u8()?,
@ -519,19 +595,22 @@ impl Packet {
},
0xc8 => Packet::SubkernelFinished {
destination: reader.read_u8()?,
id: reader.read_u32()?,
id: reader.read_u32::<NativeEndian>()?,
with_exception: reader.read_bool()?,
exception_src: reader.read_u8()?,
},
0xc9 => Packet::SubkernelExceptionRequest {
source: reader.read_u8()?,
destination: reader.read_u8()?,
},
0xca => {
let destination = reader.read_u8()?;
let last = reader.read_bool()?;
let length = reader.read_u16()?;
let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::SubkernelException {
destination: destination,
last: last,
length: length,
data: data,
@ -540,9 +619,9 @@ impl Packet {
0xcb => {
let source = reader.read_u8()?;
let destination = reader.read_u8()?;
let id = reader.read_u32()?;
let id = reader.read_u32::<NativeEndian>()?;
let status = reader.read_u8()?;
let length = reader.read_u16()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::SubkernelMessage {
@ -558,12 +637,120 @@ impl Packet {
destination: reader.read_u8()?,
},
0xd0 => Packet::CoreMgmtGetLogRequest {
destination: reader.read_u8()?,
clear: reader.read_bool()?,
},
0xd1 => Packet::CoreMgmtClearLogRequest {
destination: reader.read_u8()?,
},
0xd2 => Packet::CoreMgmtSetLogLevelRequest {
destination: reader.read_u8()?,
log_level: reader.read_u8()?,
},
0xd3 => Packet::CoreMgmtSetUartLogLevelRequest {
destination: reader.read_u8()?,
log_level: reader.read_u8()?,
},
0xd4 => {
let destination = reader.read_u8()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut key[0..length as usize])?;
Packet::CoreMgmtConfigReadRequest {
destination: destination,
length: length,
key: key,
}
}
0xd5 => Packet::CoreMgmtConfigReadContinue {
destination: reader.read_u8()?,
},
0xd6 => {
let destination = reader.read_u8()?;
let last = reader.read_bool()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::CoreMgmtConfigWriteRequest {
destination: destination,
last: last,
length: length,
data: data,
}
}
0xd7 => {
let destination = reader.read_u8()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut key[0..length as usize])?;
Packet::CoreMgmtConfigRemoveRequest {
destination: destination,
length: length,
key: key,
}
}
0xd8 => Packet::CoreMgmtConfigEraseRequest {
destination: reader.read_u8()?,
},
0xd9 => Packet::CoreMgmtRebootRequest {
destination: reader.read_u8()?,
},
0xda => Packet::CoreMgmtAllocatorDebugRequest {
destination: reader.read_u8()?,
},
0xdb => Packet::CoreMgmtFlashRequest {
destination: reader.read_u8()?,
payload_length: reader.read_u32::<NativeEndian>()?,
},
0xdc => {
let destination = reader.read_u8()?;
let last = reader.read_bool()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::CoreMgmtFlashAddDataRequest {
destination: destination,
last: last,
length: length,
data: data,
}
}
0xdd => Packet::CoreMgmtDropLinkAck {
destination: reader.read_u8()?,
},
0xde => Packet::CoreMgmtDropLink,
0xdf => {
let last = reader.read_bool()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut data[0..length as usize])?;
Packet::CoreMgmtGetLogReply {
last: last,
length: length,
data: data,
}
}
0xe0 => {
let last = reader.read_bool()?;
let length = reader.read_u16::<NativeEndian>()?;
let mut value: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
reader.read_exact(&mut value[0..length as usize])?;
Packet::CoreMgmtConfigReadReply {
last: last,
length: length,
value: value,
}
}
0xe1 => Packet::CoreMgmtReply {
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 {
pub fn write_to<W: ProtoWrite>(&self, writer: &mut W) -> Result<(), IoError> {
match *self {
Packet::EchoRequest => writer.write_u8(0x00)?,
Packet::EchoReply => writer.write_u8(0x01)?,
@ -579,15 +766,15 @@ impl Packet {
Packet::DestinationOkReply => writer.write_u8(0x22)?,
Packet::DestinationSequenceErrorReply { channel } => {
writer.write_u8(0x23)?;
writer.write_u16(channel)?;
writer.write_u16::<NativeEndian>(channel)?;
}
Packet::DestinationCollisionReply { channel } => {
writer.write_u8(0x24)?;
writer.write_u16(channel)?;
writer.write_u16::<NativeEndian>(channel)?;
}
Packet::DestinationBusyReply { channel } => {
writer.write_u8(0x25)?;
writer.write_u16(channel)?;
writer.write_u16::<NativeEndian>(channel)?;
}
Packet::RoutingSetPath { destination, hops } => {
@ -608,12 +795,12 @@ impl Packet {
} => {
writer.write_u8(0x40)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u16::<NativeEndian>(channel)?;
writer.write_u8(probe)?;
}
Packet::MonitorReply { value } => {
writer.write_u8(0x41)?;
writer.write_u64(value)?;
writer.write_u64::<NativeEndian>(value)?;
}
Packet::InjectionRequest {
destination,
@ -623,7 +810,7 @@ impl Packet {
} => {
writer.write_u8(0x50)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u16::<NativeEndian>(channel)?;
writer.write_u8(overrd)?;
writer.write_u8(value)?;
}
@ -634,7 +821,7 @@ impl Packet {
} => {
writer.write_u8(0x51)?;
writer.write_u8(destination)?;
writer.write_u16(channel)?;
writer.write_u16::<NativeEndian>(channel)?;
writer.write_u8(overrd)?;
}
Packet::InjectionStatusReply { value } => {
@ -728,7 +915,7 @@ impl Packet {
writer.write_u8(0x92)?;
writer.write_u8(destination)?;
writer.write_u8(busno)?;
writer.write_u32(data)?;
writer.write_u32::<NativeEndian>(data)?;
}
Packet::SpiReadRequest { destination, busno } => {
writer.write_u8(0x93)?;
@ -738,7 +925,7 @@ impl Packet {
Packet::SpiReadReply { succeeded, data } => {
writer.write_u8(0x94)?;
writer.write_bool(succeeded)?;
writer.write_u32(data)?;
writer.write_u32::<NativeEndian>(data)?;
}
Packet::SpiBasicReply { succeeded } => {
writer.write_u8(0x95)?;
@ -755,8 +942,8 @@ impl Packet {
overflow_occurred,
} => {
writer.write_u8(0xa1)?;
writer.write_u32(sent_bytes)?;
writer.write_u64(total_byte_count)?;
writer.write_u32::<NativeEndian>(sent_bytes)?;
writer.write_u64::<NativeEndian>(total_byte_count)?;
writer.write_bool(overflow_occurred)?;
}
Packet::AnalyzerDataRequest { destination } => {
@ -766,7 +953,7 @@ impl Packet {
Packet::AnalyzerData { last, length, data } => {
writer.write_u8(0xa3)?;
writer.write_bool(last)?;
writer.write_u16(length)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[0..length as usize])?;
}
@ -781,11 +968,11 @@ impl Packet {
writer.write_u8(0xb0)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_u8(status as u8)?;
// trace may be broken down to fit within drtio aux memory limit
// will be reconstructed by satellite
writer.write_u16(length)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&trace[0..length as usize])?;
}
Packet::DmaAddTraceReply {
@ -797,7 +984,7 @@ impl Packet {
writer.write_u8(0xb1)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_bool(succeeded)?;
}
Packet::DmaRemoveTraceRequest {
@ -808,7 +995,7 @@ impl Packet {
writer.write_u8(0xb2)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
}
Packet::DmaRemoveTraceReply { destination, succeeded } => {
writer.write_u8(0xb3)?;
@ -824,8 +1011,8 @@ impl Packet {
writer.write_u8(0xb4)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u64(timestamp)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_u64::<NativeEndian>(timestamp)?;
}
Packet::DmaPlaybackReply { destination, succeeded } => {
writer.write_u8(0xb5)?;
@ -843,10 +1030,10 @@ impl Packet {
writer.write_u8(0xb6)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_u8(error)?;
writer.write_u32(channel)?;
writer.write_u64(timestamp)?;
writer.write_u32::<NativeEndian>(channel)?;
writer.write_u64::<NativeEndian>(timestamp)?;
}
Packet::SubkernelAddDataRequest {
@ -858,9 +1045,9 @@ impl Packet {
} => {
writer.write_u8(0xc0)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_u8(status as u8)?;
writer.write_u16(length)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::SubkernelAddDataReply { succeeded } => {
@ -872,12 +1059,14 @@ impl Packet {
destination,
id,
run,
timestamp,
} => {
writer.write_u8(0xc4)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_bool(run)?;
writer.write_u64::<NativeEndian>(timestamp)?;
}
Packet::SubkernelLoadRunReply { destination, succeeded } => {
writer.write_u8(0xc5)?;
@ -892,18 +1081,25 @@ impl Packet {
} => {
writer.write_u8(0xc8)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_bool(with_exception)?;
writer.write_u8(exception_src)?;
}
Packet::SubkernelExceptionRequest { destination } => {
Packet::SubkernelExceptionRequest { source, destination } => {
writer.write_u8(0xc9)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
}
Packet::SubkernelException { last, length, data } => {
Packet::SubkernelException {
destination,
last,
length,
data,
} => {
writer.write_u8(0xca)?;
writer.write_u8(destination)?;
writer.write_bool(last)?;
writer.write_u16(length)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::SubkernelMessage {
@ -917,15 +1113,124 @@ impl Packet {
writer.write_u8(0xcb)?;
writer.write_u8(source)?;
writer.write_u8(destination)?;
writer.write_u32(id)?;
writer.write_u32::<NativeEndian>(id)?;
writer.write_u8(status as u8)?;
writer.write_u16(length)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::SubkernelMessageAck { destination } => {
writer.write_u8(0xcc)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtGetLogRequest { destination, clear } => {
writer.write_u8(0xd0)?;
writer.write_u8(destination)?;
writer.write_bool(clear)?;
}
Packet::CoreMgmtClearLogRequest { destination } => {
writer.write_u8(0xd1)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtSetLogLevelRequest { destination, log_level } => {
writer.write_u8(0xd2)?;
writer.write_u8(destination)?;
writer.write_u8(log_level)?;
}
Packet::CoreMgmtSetUartLogLevelRequest { destination, log_level } => {
writer.write_u8(0xd3)?;
writer.write_u8(destination)?;
writer.write_u8(log_level)?;
}
Packet::CoreMgmtConfigReadRequest {
destination,
length,
key,
} => {
writer.write_u8(0xd4)?;
writer.write_u8(destination)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&key[0..length as usize])?;
}
Packet::CoreMgmtConfigReadContinue { destination } => {
writer.write_u8(0xd5)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtConfigWriteRequest {
destination,
last,
length,
data,
} => {
writer.write_u8(0xd6)?;
writer.write_u8(destination)?;
writer.write_bool(last)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::CoreMgmtConfigRemoveRequest {
destination,
length,
key,
} => {
writer.write_u8(0xd7)?;
writer.write_u8(destination)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&key[0..length as usize])?;
}
Packet::CoreMgmtConfigEraseRequest { destination } => {
writer.write_u8(0xd8)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtRebootRequest { destination } => {
writer.write_u8(0xd9)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtAllocatorDebugRequest { destination } => {
writer.write_u8(0xda)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtFlashRequest {
destination,
payload_length,
} => {
writer.write_u8(0xdb)?;
writer.write_u8(destination)?;
writer.write_u32::<NativeEndian>(payload_length)?;
}
Packet::CoreMgmtFlashAddDataRequest {
destination,
last,
length,
data,
} => {
writer.write_u8(0xdc)?;
writer.write_u8(destination)?;
writer.write_bool(last)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[..length as usize])?;
}
Packet::CoreMgmtDropLinkAck { destination } => {
writer.write_u8(0xdd)?;
writer.write_u8(destination)?;
}
Packet::CoreMgmtDropLink => writer.write_u8(0xde)?,
Packet::CoreMgmtGetLogReply { last, length, data } => {
writer.write_u8(0xdf)?;
writer.write_bool(last)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&data[0..length as usize])?;
}
Packet::CoreMgmtConfigReadReply { last, length, value } => {
writer.write_u8(0xe0)?;
writer.write_bool(last)?;
writer.write_u16::<NativeEndian>(length)?;
writer.write_all(&value[0..length as usize])?;
}
Packet::CoreMgmtReply { succeeded } => {
writer.write_u8(0xe1)?;
writer.write_bool(succeeded)?;
}
}
Ok(())
}
@ -943,6 +1248,8 @@ impl Packet {
Packet::SubkernelLoadRunReply { destination, .. } => Some(*destination),
Packet::SubkernelMessage { destination, .. } => Some(*destination),
Packet::SubkernelMessageAck { destination } => Some(*destination),
Packet::SubkernelExceptionRequest { destination, .. } => Some(*destination),
Packet::SubkernelException { destination, .. } => Some(*destination),
Packet::DmaPlaybackStatus { destination, .. } => Some(*destination),
Packet::SubkernelFinished { destination, .. } => Some(*destination),
_ => None,
@ -960,7 +1267,9 @@ impl Packet {
| Packet::SubkernelLoadRunReply { .. }
| Packet::SubkernelMessageAck { .. }
| Packet::DmaPlaybackStatus { .. }
| Packet::SubkernelFinished { .. } => false,
| Packet::SubkernelFinished { .. }
| Packet::CoreMgmtDropLinkAck { .. }
| Packet::InjectionRequest { .. } => false,
_ => true,
}
}

View File

@ -1,3 +1,5 @@
use core::arch::asm;
use libboard_zynq::{println, stdio};
use libcortex_a9::{interrupt_handler, regs::MPIDR};
use libregister::RegisterR;

View File

@ -111,18 +111,23 @@ impl IoExpander {
fn write(&self, i2c: &mut i2c::I2c, addr: u8, value: u8) -> Result<(), &'static str> {
i2c.start()?;
i2c.write(self.address)?;
i2c.write(addr)?;
i2c.write(value)?;
i2c.stop()?;
Ok(())
let write_res = i2c
.write(self.address)
.and_then(|_| i2c.write(addr))
.and_then(|_| i2c.write(value));
let stop_res = i2c.stop();
write_res.and(stop_res).map_err(|e| e.into())
}
fn check_ack(&self, i2c: &mut i2c::I2c) -> Result<bool, &'static str> {
// Check for ack from io expander
self.select(i2c)?;
i2c.start()?;
let ack = i2c.write(self.address)?;
let ack = match i2c.write(self.address) {
Ok(()) => true,
Err(i2c::Error::Nack) => false,
Err(e) => return Err(e.into()),
};
i2c.stop()?;
Ok(ack)
}

View File

@ -1,7 +1,6 @@
#![no_std]
#![feature(never_type)]
#![feature(naked_functions)]
#![feature(asm)]
extern crate core_io;
extern crate crc;
@ -25,7 +24,7 @@ pub mod fiq;
#[cfg(feature = "target_kasli_soc")]
pub mod io_expander;
pub mod logger;
#[cfg(has_drtio)]
#[cfg(any(has_drtio, has_cxp_grabber))]
#[rustfmt::skip]
#[path = "../../../build/mem.rs"]
pub mod mem;
@ -42,6 +41,17 @@ pub mod si5324;
pub mod si549;
use core::{cmp, str};
#[cfg(has_cxp_grabber)]
pub mod cxp_camera_setup;
#[cfg(has_cxp_grabber)]
pub mod cxp_ctrl;
#[cfg(has_cxp_grabber)]
pub mod cxp_grabber;
#[cfg(has_cxp_grabber)]
pub mod cxp_packet;
#[cfg(has_cxp_grabber)]
pub mod cxp_phys;
pub fn identifier_read(buf: &mut [u8]) -> &str {
unsafe {
pl::csr::identifier::address_write(0);

View File

@ -1,4 +1,4 @@
use core::{cell::Cell, fmt::Write};
use core::{cell::Cell, fmt::Write, mem::MaybeUninit};
use libboard_zynq::{println, timer::GlobalTimer};
use libcortex_a9::mutex::{Mutex, MutexGuard};
@ -42,7 +42,7 @@ pub struct BufferLogger {
buffer_filter: Cell<LevelFilter>,
}
static mut LOGGER: Option<BufferLogger> = None;
static mut LOGGER: MaybeUninit<BufferLogger> = MaybeUninit::uninit();
impl BufferLogger {
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
@ -55,13 +55,13 @@ impl BufferLogger {
pub fn register(self) {
unsafe {
LOGGER = Some(self);
log::set_logger(LOGGER.as_ref().unwrap()).expect("global logger can only be initialized once");
LOGGER.write(self);
log::set_logger(LOGGER.assume_init_ref()).expect("global logger can only be initialized once");
}
}
pub unsafe fn get_logger() -> &'static mut Option<BufferLogger> {
&mut LOGGER
pub fn get_logger() -> &'static mut BufferLogger {
unsafe { LOGGER.assume_init_mut() }
}
pub fn buffer<'a>(&'a self) -> Option<LogBufferRef<'a>> {

View File

@ -1,7 +1,9 @@
use core::result;
use embedded_hal::blocking::delay::DelayUs;
use libboard_zynq::{i2c::I2c, time::Milliseconds, timer::GlobalTimer};
use libboard_zynq::{i2c::{Error as I2cError, I2c},
time::Milliseconds,
timer::GlobalTimer};
use log::info;
#[cfg(not(si5324_soft_reset))]
@ -97,15 +99,18 @@ fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySetti
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.write(ADDRESS << 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack write address",
err => err.into(),
})?;
i2c.write(reg).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack register",
err => err.into(),
})?;
i2c.write(val).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack value",
err => err.into(),
})?;
i2c.stop().unwrap();
Ok(())
}
@ -113,29 +118,37 @@ fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
#[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");
i2c.write(ADDRESS << 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack write address",
err => err.into(),
})?;
i2c.write(reg).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack register",
err => err.into(),
})?;
match i2c.write(val) {
Ok(()) | Err(I2cError::Nack) => (),
Err(e) => return Err(e.into()),
}
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.write(ADDRESS << 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack write address",
err => err.into(),
})?;
i2c.write(reg).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack register",
err => err.into(),
})?;
i2c.restart().unwrap();
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
return Err("Si5324 failed to ack read address");
}
i2c.write((ADDRESS << 1) | 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack read address",
err => err.into(),
})?;
let val = i2c.read(false).unwrap();
i2c.stop().unwrap();
Ok(val)

View File

@ -85,10 +85,7 @@ unsafe fn get_ttype_entry(
encoding | DW_EH_PE_pcrel,
ttype_base,
)
.map(|v| match v {
ttype_base => None,
ttype_entry => Some(ttype_entry as *const u8),
})
.map(|v| (v != 0).then(|| v as *const u8))
}
pub unsafe fn find_eh_action(
@ -278,6 +275,11 @@ unsafe fn read_encoded_pointer_with_base(reader: &mut DwarfReader, encoding: u8,
_ => return Err(()),
};
if result == 0 {
// null is just encoded as 0, even if a relative encoding is used for the table.
return Ok(0);
}
result += if (encoding & 0x70) == DW_EH_PE_pcrel {
original_ptr as usize
} else {

View File

@ -8,7 +8,7 @@ name = "io"
path = "lib.rs"
[dependencies]
core_io = { version = "0.1", features = ["collections"] }
core_io = { git = "https://git.m-labs.hk/M-Labs/rs-core_io.git", rev = "e9d3edf027", features = ["collections"] }
byteorder = { version = "1.0", default-features = false, optional = true }
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }

View File

@ -1,5 +1,6 @@
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::arch::asm;
use core_io::{Error as IoError, Read, Write};
@ -47,6 +48,9 @@ impl<T: AsRef<[u8]>> Read for Cursor<T> {
let len = buf.len().min(data.len());
// ``copy_from_slice`` generates AXI bursts, use a regular loop instead
for i in 0..len {
unsafe {
asm!("", options(preserves_flags, nostack, readonly));
}
buf[i] = data[i];
}
self.pos += len;
@ -59,6 +63,9 @@ impl Write for Cursor<&mut [u8]> {
let data = &mut self.inner[self.pos..];
let len = buf.len().min(data.len());
for i in 0..len {
unsafe {
asm!("", options(preserves_flags, nostack, readonly));
}
data[i] = buf[i];
}
self.pos += len;

View File

@ -2,58 +2,54 @@
use alloc::{string::String, vec};
use core::str::Utf8Error;
use byteorder::{ByteOrder, NativeEndian};
use core_io::{Error as IoError, Read, Write};
use byteorder::ByteOrder;
use core_io::{Error, Read, Write};
#[allow(dead_code)]
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq)]
pub enum ReadStringError<T> {
Utf8(Utf8Error),
Other(T),
}
pub trait ProtoRead {
type ReadError;
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError>;
pub trait ProtoRead: Read {
#[inline]
fn read_u8(&mut self) -> Result<u8, Self::ReadError> {
fn read_u8(&mut self) -> Result<u8, Error> {
let mut bytes = [0; 1];
self.read_exact(&mut bytes)?;
Ok(bytes[0])
}
#[inline]
fn read_u16(&mut self) -> Result<u16, Self::ReadError> {
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16, Error> {
let mut bytes = [0; 2];
self.read_exact(&mut bytes)?;
Ok(NativeEndian::read_u16(&bytes))
Ok(T::read_u16(&bytes))
}
#[inline]
fn read_u32(&mut self) -> Result<u32, Self::ReadError> {
fn read_u32<T: ByteOrder>(&mut self) -> Result<u32, Error> {
let mut bytes = [0; 4];
self.read_exact(&mut bytes)?;
Ok(NativeEndian::read_u32(&bytes))
Ok(T::read_u32(&bytes))
}
#[inline]
fn read_u64(&mut self) -> Result<u64, Self::ReadError> {
fn read_u64<T: ByteOrder>(&mut self) -> Result<u64, Error> {
let mut bytes = [0; 8];
self.read_exact(&mut bytes)?;
Ok(NativeEndian::read_u64(&bytes))
Ok(T::read_u64(&bytes))
}
#[inline]
fn read_bool(&mut self) -> Result<bool, Self::ReadError> {
fn read_bool(&mut self) -> Result<bool, Error> {
Ok(self.read_u8()? != 0)
}
#[inline]
#[cfg(feature = "alloc")]
fn read_bytes(&mut self) -> Result<vec::Vec<u8>, Self::ReadError> {
let length = self.read_u32()?;
fn read_bytes<T: ByteOrder>(&mut self) -> Result<vec::Vec<u8>, Error> {
let length = self.read_u32::<T>()?;
let mut value = vec![0; length as usize];
self.read_exact(&mut value)?;
Ok(value)
@ -61,105 +57,85 @@ pub trait ProtoRead {
#[inline]
#[cfg(feature = "alloc")]
fn read_string(&mut self) -> Result<String, ReadStringError<Self::ReadError>> {
let bytes = self.read_bytes().map_err(ReadStringError::Other)?;
fn read_string<T: ByteOrder>(&mut self) -> Result<String, ReadStringError<Error>> {
let bytes = self.read_bytes::<T>().map_err(ReadStringError::Other)?;
String::from_utf8(bytes).map_err(|err| ReadStringError::Utf8(err.utf8_error()))
}
}
pub trait ProtoWrite {
type WriteError;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError>;
pub trait ProtoWrite: Write {
#[inline]
fn write_u8(&mut self, value: u8) -> Result<(), Self::WriteError> {
fn write_u8(&mut self, value: u8) -> Result<(), Error> {
let bytes = [value; 1];
self.write_all(&bytes)
}
#[inline]
fn write_i8(&mut self, value: i8) -> Result<(), Self::WriteError> {
fn write_i8(&mut self, value: i8) -> Result<(), Error> {
let bytes = [value as u8; 1];
self.write_all(&bytes)
}
#[inline]
fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> {
fn write_u16<T: ByteOrder>(&mut self, value: u16) -> Result<(), Error> {
let mut bytes = [0; 2];
NativeEndian::write_u16(&mut bytes, value);
T::write_u16(&mut bytes, value);
self.write_all(&bytes)
}
#[inline]
fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> {
fn write_i16<T: ByteOrder>(&mut self, value: i16) -> Result<(), Error> {
let mut bytes = [0; 2];
NativeEndian::write_i16(&mut bytes, value);
T::write_i16(&mut bytes, value);
self.write_all(&bytes)
}
#[inline]
fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> {
fn write_u32<T: ByteOrder>(&mut self, value: u32) -> Result<(), Error> {
let mut bytes = [0; 4];
NativeEndian::write_u32(&mut bytes, value);
T::write_u32(&mut bytes, value);
self.write_all(&bytes)
}
#[inline]
fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> {
fn write_i32<T: ByteOrder>(&mut self, value: i32) -> Result<(), Error> {
let mut bytes = [0; 4];
NativeEndian::write_i32(&mut bytes, value);
T::write_i32(&mut bytes, value);
self.write_all(&bytes)
}
#[inline]
fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> {
fn write_u64<T: ByteOrder>(&mut self, value: u64) -> Result<(), Error> {
let mut bytes = [0; 8];
NativeEndian::write_u64(&mut bytes, value);
T::write_u64(&mut bytes, value);
self.write_all(&bytes)
}
#[inline]
fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> {
fn write_i64<T: ByteOrder>(&mut self, value: i64) -> Result<(), Error> {
let mut bytes = [0; 8];
NativeEndian::write_i64(&mut bytes, value);
T::write_i64(&mut bytes, value);
self.write_all(&bytes)
}
#[inline]
fn write_bool(&mut self, value: bool) -> Result<(), Self::WriteError> {
fn write_bool(&mut self, value: bool) -> Result<(), Error> {
self.write_u8(value as u8)
}
#[inline]
fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::WriteError> {
self.write_u32(value.len() as u32)?;
fn write_bytes<T: ByteOrder>(&mut self, value: &[u8]) -> Result<(), Error> {
self.write_u32::<T>(value.len() as u32)?;
self.write_all(value)
}
#[inline]
#[cfg(feature = "alloc")]
fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> {
self.write_bytes(value.as_bytes())
fn write_string<T: ByteOrder>(&mut self, value: &str) -> Result<(), Error> {
self.write_bytes::<T>(value.as_bytes())
}
}
impl<T> ProtoRead for T
where T: Read + ?Sized
{
type ReadError = IoError;
impl<T: Read> ProtoRead for T {}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError> {
T::read_exact(self, buf)
}
}
impl<T> ProtoWrite for T
where T: Write + ?Sized
{
type WriteError = IoError;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> {
T::write_all(self, buf)
}
}
impl<T: Write> ProtoWrite for T {}

View File

@ -12,7 +12,7 @@ build_zynq = { path = "../libbuild_zynq" }
cslice = "0.3"
log = "0.4"
nb = "0.1"
core_io = { version = "0.1", features = ["collections"] }
core_io = { git = "https://git.m-labs.hk/M-Labs/rs-core_io.git", rev = "e9d3edf027", features = ["collections"] }
byteorder = { version = "1.3", default-features = false }
void = { version = "1", default-features = false }
log_buffer = { version = "1.2" }
@ -32,3 +32,9 @@ unwind = { path = "../libunwind" }
libc = { path = "../libc" }
io = { path = "../libio" }
libboard_artiq = { path = "../libboard_artiq" }
[dependencies.nalgebra]
git = "https://git.m-labs.hk/M-Labs/nalgebra.git"
rev = "ad42410ab0"
default-features = false
features = ["libm", "alloc"]

228
src/libksupport/src/cxp.rs Normal file
View File

@ -0,0 +1,228 @@
use alloc::{string::{String, ToString},
vec::Vec};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use cslice::CMutSlice;
use libboard_artiq::{cxp_ctrl::{Error as CtrlErr, DATA_MAXSIZE},
cxp_grabber::{camera_connected, with_tag},
cxp_packet::{read_bytes, read_u32, write_u32}};
use log::info;
use crate::{artiq_raise, pl::csr::cxp_grabber};
const ROI_MAX_SIZE: usize = 4096;
#[repr(C)]
pub struct ROIViewerFrame {
width: i32,
height: i32,
pixel_width: i32,
}
enum Error {
BufferSizeTooSmall(usize, usize),
ROISizeTooBig(usize, usize),
InvalidLocalUrl(String),
CtrlPacketError(CtrlErr),
}
impl From<CtrlErr> for Error {
fn from(value: CtrlErr) -> Error {
Error::CtrlPacketError(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::BufferSizeTooSmall(required_size, buffer_size) => {
write!(
f,
"BufferSizeTooSmall - The required size is {} bytes but the buffer size is {} bytes",
required_size, buffer_size
)
}
&Error::ROISizeTooBig(width, height) => {
write!(
f,
"ROISizeTooBig - The maximum ROIViewer height and total size are {} and {} pixels respectively \
but the ROI is set to {} ({}x{}) pixels",
ROI_MAX_SIZE / 4,
ROI_MAX_SIZE,
width * height,
width,
height
)
}
&Error::InvalidLocalUrl(ref s) => {
write!(f, "InvalidLocalUrl - Cannot download xml file locally from {}", s)
}
&Error::CtrlPacketError(ref err) => write!(f, "{}", err),
}
}
}
fn read_xml_url(with_tag: bool) -> Result<String, Error> {
let mut addr = read_u32(0x0018, with_tag)?;
let mut buffer = Vec::new();
// Strings stored in the bootstrap and manufacturer-specific registers space shall be NULL-terminated, encoded ASCII - Section 12.3.1 (CXP-001-2021)
// String length is not known during runtime, grabber must read 4 bytes at a time until NULL-terminated
loop {
let mut bytes: [u8; 4] = [0; 4];
read_bytes(addr, &mut bytes, with_tag)?;
addr += 4;
for b in bytes {
if b == 0 {
// UTF-8 is compatible with ASCII encoding
// use U+FFFD REPLACEMENT_CHARACTER to represent decoding error
return Ok(String::from_utf8_lossy(&buffer).to_string());
} else {
buffer.push(b);
}
}
}
}
fn read_xml_location(with_tag: bool) -> Result<(String, u32, u32), Error> {
let url = read_xml_url(with_tag)?;
// url example - Section 13.2.3 (CXP-001-2021)
// Available on camera - "Local:MyFilename.zip;B8000;33A?SchemaVersion=1.0.0"
// => ZIP file starting at address 0xB8000 in the Device with a length of 0x33A bytes
//
// Available online - "Web:http://www.example.com/xml/MyFilename.xml"
// => xml is available at http://www.example.com/xml/MyFilename.xml
let mut splitter = url.split(|c| c == ':' || c == ';' || c == '?');
let scheme = splitter.next().unwrap();
if scheme.eq_ignore_ascii_case("local") {
if let (Some(file_name), Some(addr_str), Some(size_str)) = (splitter.next(), splitter.next(), splitter.next()) {
let addr = u32::from_str_radix(addr_str, 16).map_err(|_| Error::InvalidLocalUrl(url.to_string()))?;
let size = u32::from_str_radix(size_str, 16).map_err(|_| Error::InvalidLocalUrl(url.to_string()))?;
return Ok((file_name.to_string(), addr, size));
}
}
Err(Error::InvalidLocalUrl(url.to_string()))
}
fn read_xml_file(buffer: &mut [i32], with_tag: bool) -> Result<u32, Error> {
let (file_name, base_addr, size) = read_xml_location(with_tag)?;
if buffer.len() * 4 < size as usize {
return Err(Error::BufferSizeTooSmall(size as usize, buffer.len() * 4));
};
info!("downloading xml file {} with {} bytes...", file_name, size);
let mut v: Vec<u8> = Vec::new();
let mut addr = base_addr;
let mut bytesleft = size;
let mut bytes: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
while bytesleft > 0 {
let read_len = DATA_MAXSIZE.min(bytesleft as usize);
read_bytes(addr, &mut bytes[..read_len], with_tag)?;
v.extend(&bytes[..read_len]);
addr += read_len as u32;
bytesleft -= read_len as u32;
}
info!("download successful");
// pad to 32 bit boundary
let padding = (4 - (size % 4)) % 4;
for _ in 0..padding {
v.push(0);
}
NetworkEndian::read_i32_into(&v, &mut buffer[..((size + padding) / 4) as usize]);
Ok((size + padding) / 4)
}
pub extern "C" fn download_xml_file(buffer: &mut CMutSlice<i32>) -> i32 {
if camera_connected() {
match read_xml_file(buffer.as_mut_slice(), with_tag()) {
Ok(size_read) => size_read as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
} else {
artiq_raise!("CXPError", "Camera is not connected");
}
}
pub extern "C" fn read32(addr: i32) -> i32 {
if camera_connected() {
match read_u32(addr as u32, with_tag()) {
Ok(result) => result as i32,
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
} else {
artiq_raise!("CXPError", "Camera is not connected");
}
}
pub extern "C" fn write32(addr: i32, val: i32) {
if camera_connected() {
match write_u32(addr as u32, val as u32, with_tag()) {
Ok(_) => {}
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
}
} else {
artiq_raise!("CXPError", "Camera is not connected");
}
}
pub extern "C" fn start_roi_viewer(x0: i32, y0: i32, x1: i32, y1: i32) {
let (width, height) = ((x1 - x0) as usize, (y1 - y0) as usize);
if width * height > ROI_MAX_SIZE || height > ROI_MAX_SIZE / 4 {
artiq_raise!("CXPError", format!("{}", Error::ROISizeTooBig(width, height)));
} else {
unsafe {
// flush the fifo before arming
while cxp_grabber::roi_viewer_fifo_stb_read() == 1 {
cxp_grabber::roi_viewer_fifo_ack_write(1);
}
cxp_grabber::roi_viewer_x0_write(x0 as u16);
cxp_grabber::roi_viewer_x1_write(x1 as u16);
cxp_grabber::roi_viewer_y0_write(y0 as u16);
cxp_grabber::roi_viewer_y1_write(y1 as u16);
cxp_grabber::roi_viewer_arm_write(1);
}
}
}
pub extern "C" fn download_roi_viewer_frame(buffer: &mut CMutSlice<i64>) -> ROIViewerFrame {
if buffer.len() * 4 < ROI_MAX_SIZE {
// each pixel is 16 bits
artiq_raise!(
"CXPError",
format!("{}", Error::BufferSizeTooSmall(ROI_MAX_SIZE * 2, buffer.len() * 8))
);
};
let buf = buffer.as_mut_slice();
unsafe {
while cxp_grabber::roi_viewer_ready_read() == 0 {}
let mut i = 0;
while cxp_grabber::roi_viewer_fifo_stb_read() == 1 {
buf[i] = cxp_grabber::roi_viewer_fifo_data_read() as i64;
i += 1;
cxp_grabber::roi_viewer_fifo_ack_write(1);
}
let width = (cxp_grabber::roi_viewer_x1_read() - cxp_grabber::roi_viewer_x0_read()) as i32;
let height = (cxp_grabber::roi_viewer_y1_read() - cxp_grabber::roi_viewer_y0_read()) as i32;
let pixel_width = match cxp_grabber::stream_decoder_pixel_format_code_read() {
0x0101 => 8,
0x0102 => 10,
0x0103 => 12,
0x0104 => 14,
0x0105 => 16,
_ => artiq_raise!("CXPError", "UnsupportedPixelFormat"),
};
ROIViewerFrame {
width,
height,
pixel_width,
}
}
}

View File

@ -14,8 +14,11 @@
use core::mem;
use cslice::CSlice;
use byteorder::NativeEndian;
use core_io::Error as ReadError;
use cslice::{AsCSlice, CSlice};
use dwarf::eh::{self, EHAction, EHContext};
use io::{Cursor, ProtoRead};
use libc::{c_int, c_void, uintptr_t};
use log::{error, trace};
use unwind as uw;
@ -94,29 +97,35 @@ struct ExceptionBuffer {
}
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],
uw_exceptions: [const {
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,
stack_pointers: [const {
StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: 0,
current_backtrace_size: 0,
}
}; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_count: 0,
};
pub unsafe extern "C" 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],
EXCEPTION_BUFFER.uw_exceptions = [const {
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];
@ -220,8 +229,6 @@ pub unsafe fn artiq_personality(
}
pub unsafe extern "C" fn raise(exception: *const Exception) -> ! {
use cslice::AsCSlice;
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;
@ -295,6 +302,60 @@ pub unsafe extern "C" fn raise(exception: *const Exception) -> ! {
unreachable!();
}
fn read_exception_string<'a>(reader: &mut Cursor<&[u8]>) -> Result<CSlice<'a, u8>, ReadError> {
let len = reader.read_u32::<NativeEndian>()? as usize;
if len == usize::MAX {
let data = reader.read_u32::<NativeEndian>()?;
Ok(unsafe { CSlice::new(data as *const u8, len) })
} else {
let pos = reader.position();
let slice = unsafe {
let ptr = reader.get_ref().as_ptr().offset(pos as isize);
CSlice::new(ptr, len)
};
reader.set_position(pos + len);
Ok(slice)
}
}
fn read_exception(raw_exception: &[u8]) -> Result<Exception, ReadError> {
let mut reader = Cursor::new(raw_exception);
let mut byte = reader.read_u8()?;
// to sync
while byte != 0x5a {
byte = reader.read_u8()?;
}
// skip sync bytes, 0x09 indicates exception
while byte != 0x09 {
byte = reader.read_u8()?;
}
let _len = reader.read_u32::<NativeEndian>()?;
// ignore the remaining exceptions, stack traces etc. - unwinding from another device would be unwise anyway
Ok(Exception {
id: reader.read_u32::<NativeEndian>()?,
message: read_exception_string(&mut reader)?,
param: [
reader.read_u64::<NativeEndian>()? as i64,
reader.read_u64::<NativeEndian>()? as i64,
reader.read_u64::<NativeEndian>()? as i64,
],
file: read_exception_string(&mut reader)?,
line: reader.read_u32::<NativeEndian>()?,
column: reader.read_u32::<NativeEndian>()?,
function: read_exception_string(&mut reader)?,
})
}
pub fn raise_raw(raw_exception: &[u8]) -> ! {
use crate::artiq_raise;
if let Ok(exception) = read_exception(raw_exception) {
unsafe { raise(&exception) };
} else {
artiq_raise!("SubkernelError", "Error passing exception");
}
}
pub unsafe extern "C" fn resume() -> ! {
trace!("resume");
assert!(EXCEPTION_BUFFER.exception_count != 0);
@ -421,20 +482,31 @@ extern "C" fn stop_fn(
}
}
// Must be kept in sync with preallocate_runtime_exception_names() in artiq/language/embedding_map.py
static EXCEPTION_ID_LOOKUP: [(&str, u32); 12] = [
("RuntimeError", 0),
("RTIOUnderflow", 1),
("RTIOOverflow", 2),
("RTIODestinationUnreachable", 3),
("DMAError", 4),
("I2CError", 5),
("CacheError", 6),
("SPIError", 7),
("ZeroDivisionError", 8),
("IndexError", 9),
("UnwrapNoneError", 10),
("SubkernelError", 11),
// Must be kept in sync with preallocate_runtime_exception_names() in `artiq.compiler.embedding`
static EXCEPTION_ID_LOOKUP: [(&str, u32); 23] = [
("RTIOUnderflow", 0),
("RTIOOverflow", 1),
("RTIODestinationUnreachable", 2),
("DMAError", 3),
("I2CError", 4),
("CacheError", 5),
("SPIError", 6),
("SubkernelError", 7),
("AssertionError", 8),
("AttributeError", 9),
("IndexError", 10),
("IOError", 11),
("KeyError", 12),
("NotImplementedError", 13),
("OverflowError", 14),
("RuntimeError", 15),
("TimeoutError", 16),
("TypeError", 17),
("ValueError", 18),
("ZeroDivisionError", 19),
("LinAlgError", 20),
("UnwrapNoneError", 21),
("CXPError", 22),
];
pub fn get_exception_id(name: &str) -> u32 {
@ -469,3 +541,29 @@ macro_rules! artiq_raise {
}};
($name:expr, $message:expr) => {{ artiq_raise!($name, $message, 0, 0, 0) }};
}
/// Takes as input exception id from host
/// Generates a new exception with:
/// * `id` set to `exn_id`
/// * `message` set to corresponding exception name from `EXCEPTION_ID_LOOKUP`
///
/// The message is matched on host to ensure correct exception is being referred
/// This test checks the synchronization of exception ids for runtime errors
#[no_mangle]
pub extern "C" fn test_exception_id_sync(exn_id: u32) {
let message = EXCEPTION_ID_LOOKUP
.iter()
.find_map(|&(name, id)| if id == exn_id { Some(name) } else { None })
.unwrap_or("unallocated internal exception id");
let exn = Exception {
id: exn_id,
file: file!().as_c_slice(),
line: 0,
column: 0,
function: "test_exception_id_sync".as_c_slice(),
message: message.as_c_slice(),
param: [0, 0, 0],
};
unsafe { raise(&exn) };
}

View File

@ -1,97 +0,0 @@
use libboard_zynq;
use crate::artiq_raise;
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
pub extern "C" 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 "C" fn restart(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
artiq_raise!("I2CError", "I2C restart failed");
}
}
}
pub extern "C" 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 "C" 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 "C" 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 "C" 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

@ -1,4 +1,5 @@
use core::sync::atomic::{AtomicBool, Ordering};
use core::{arch::asm,
sync::atomic::{AtomicBool, Ordering}};
use libboard_zynq::{gic, mpcore, println, stdio};
use libcortex_a9::{asm, interrupt_handler, notify_spin_lock, regs::MPIDR, spin_lock_yield};

View File

@ -2,16 +2,18 @@ use alloc::vec;
use core::{ffi::VaList, ptr, str};
use libc::{c_char, c_int, size_t};
use libm;
use log::{info, warn};
#[cfg(has_drtio)]
use super::subkernel;
use super::{cache,
core1::rtio_get_destination_status,
dma,
rpc::{rpc_recv, rpc_send, rpc_send_async}};
use crate::{eh_artiq, i2c, rtio};
dma, i2c, linalg,
rpc::{rpc_recv, rpc_send, rpc_send_async},
rtio};
#[cfg(has_cxp_grabber)]
use crate::cxp;
use crate::eh_artiq;
extern "C" {
fn vsnprintf_(buffer: *mut c_char, count: size_t, format: *const c_char, va: VaList) -> c_int;
@ -126,6 +128,18 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
#[cfg(has_drtio)]
api!(subkernel_await_message = subkernel::await_message),
// cxp grabber
#[cfg(has_cxp_grabber)]
api!(cxp_download_xml_file = cxp::download_xml_file),
#[cfg(has_cxp_grabber)]
api!(cxp_read32 = cxp::read32),
#[cfg(has_cxp_grabber)]
api!(cxp_write32 = cxp::write32),
#[cfg(has_cxp_grabber)]
api!(cxp_start_roi_viewer = cxp::start_roi_viewer),
#[cfg(has_cxp_grabber)]
api!(cxp_download_roi_viewer_frame = cxp::download_roi_viewer_frame),
// Double-precision floating-point arithmetic helper functions
// RTABI chapter 4.1.2, Table 2
api!(__aeabi_dadd),
@ -303,6 +317,7 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
api_libm_f64f64f64!(nextafter),
api_libm_f64f64f64!(pow),
api_libm_f64f64!(round),
api_libm_f64f64!(rint),
api_libm_f64f64!(sin),
api_libm_f64f64!(sinh),
api_libm_f64f64!(sqrt),
@ -318,6 +333,26 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
}
api!(yn = yn)
},
// linalg
api!(np_linalg_cholesky = linalg::np_linalg_cholesky),
api!(np_linalg_qr = linalg::np_linalg_qr),
api!(np_linalg_svd = linalg::np_linalg_svd),
api!(np_linalg_inv = linalg::np_linalg_inv),
api!(np_linalg_pinv = linalg::np_linalg_pinv),
api!(np_linalg_matrix_power = linalg::np_linalg_matrix_power),
api!(np_linalg_det = linalg::np_linalg_det),
api!(sp_linalg_lu = linalg::sp_linalg_lu),
api!(sp_linalg_schur = linalg::sp_linalg_schur),
api!(sp_linalg_hessenberg = linalg::sp_linalg_hessenberg),
/*
* syscall for unit tests
* Used in `artiq.tests.coredevice.test_exceptions.ExceptionTest.test_raise_exceptions_kernel`
* This syscall checks that the exception IDs used in the Python `EmbeddingMap` (in `artiq.language.embedding`)
* match the `EXCEPTION_ID_LOOKUP` defined in the firmware (`libksupport::src::eh_artiq`)
*/
api!(test_exception_id_sync = eh_artiq::test_exception_id_sync)
];
api.iter()
.find(|&&(exported, _)| exported.as_bytes() == required)

View File

@ -4,17 +4,17 @@ use alloc::borrow::ToOwned;
use core::{cell::UnsafeCell, mem, ptr};
use cslice::CSlice;
use dyld::{self, elf::EXIDX_Entry, Library};
use dyld::{elf::EXIDX_Entry, Library};
use libboard_zynq::{gic, mpcore};
use libcortex_a9::{asm::{dsb, isb},
cache::{bpiall, dcci_slice, iciallu},
enable_fpu, sync_channel};
sync_channel};
use libsupport_zynq::ram;
use log::{debug, error, info};
use super::{api::resolve, dma, rpc::rpc_send_async, Message, CHANNEL_0TO1, CHANNEL_1TO0, CHANNEL_SEM, INIT_LOCK,
use super::{api::resolve, dma, rpc::rpc_send_async, rtio, Message, CHANNEL_0TO1, CHANNEL_1TO0, CHANNEL_SEM, INIT_LOCK,
KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, KERNEL_IMAGE};
use crate::{eh_artiq, get_async_errors, rtio};
use crate::{eh_artiq, get_async_errors};
// linker symbols
extern "C" {
@ -25,12 +25,14 @@ extern "C" {
}
unsafe fn attribute_writeback(typeinfo: *const ()) {
#[repr(C)]
struct Attr {
offset: usize,
tag: CSlice<'static, u8>,
name: CSlice<'static, u8>,
}
#[repr(C)]
struct Type {
attributes: *const *const Attr,
objects: *const *const (),
@ -126,7 +128,6 @@ impl KernelImage {
#[no_mangle]
pub extern "C" fn main_core1() {
enable_fpu();
debug!("Core1 started");
ram::init_alloc_core1();

View File

@ -1,10 +1,10 @@
use alloc::{string::String, vec::Vec};
use core::mem;
use core::{mem, ptr};
use cslice::CSlice;
use super::{Message, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, KERNEL_IMAGE};
use crate::{artiq_raise, pl::csr, rtio};
use super::{rtio, Message, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, KERNEL_IMAGE};
use crate::{artiq_raise, pl::csr};
#[repr(C)]
pub struct DmaTrace {
@ -26,7 +26,7 @@ static mut RECORDER: Option<DmaRecorder> = None;
pub unsafe fn init_dma_recorder() {
// as static would remain after restart, we have to reset it,
// without running its destructor.
mem::forget(mem::replace(&mut RECORDER, None));
mem::forget(ptr::replace(&raw mut RECORDER, None));
}
pub extern "C" fn dma_record_start(name: CSlice<u8>) {
@ -82,7 +82,7 @@ pub extern "C" fn dma_record_stop(duration: i64, enable_ddma: bool) {
#[inline(always)]
unsafe fn dma_record_output_prepare(timestamp: i64, target: i32, words: usize) {
// See gateware/rtio/dma.py.
const HEADER_LENGTH: usize = /*length*/1 + /*channel*/3 + /*timestamp*/8 + /*address*/1;
const HEADER_LENGTH: usize = /*length*/ 1 + /*channel*/3 + /*timestamp*/8 + /*address*/1;
let length = HEADER_LENGTH + /*data*/words * 4;
let buffer = &mut RECORDER.as_mut().unwrap().buffer;

View File

@ -0,0 +1,192 @@
use core::mem::MaybeUninit;
use libboard_zynq::i2c::{Error, I2c};
#[cfg(has_drtio)]
use super::{Message, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0};
use crate::artiq_raise;
static mut I2C_BUS: MaybeUninit<I2c> = MaybeUninit::uninit();
pub extern "C" fn start(busno: i32) {
let _destination = (busno >> 16) as u8;
#[cfg(has_drtio)]
if _destination != 0 {
let reply = unsafe {
KERNEL_CHANNEL_1TO0
.as_mut()
.unwrap()
.send(Message::I2cStartRequest(busno as u32));
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
};
match reply {
Message::I2cBasicReply(true) => return,
Message::I2cBasicReply(false) => artiq_raise!("I2CError", "I2C remote start fail"),
msg => panic!("Expected I2cBasicReply for I2cStartRequest, got: {:?}", msg),
}
}
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
if get_bus().start().is_err() {
artiq_raise!("I2CError", "I2C start failed");
}
}
pub extern "C" fn restart(busno: i32) {
let _destination = (busno >> 16) as u8;
#[cfg(has_drtio)]
if _destination != 0 {
let reply = unsafe {
KERNEL_CHANNEL_1TO0
.as_mut()
.unwrap()
.send(Message::I2cRestartRequest(busno as u32));
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
};
match reply {
Message::I2cBasicReply(true) => return,
Message::I2cBasicReply(false) => artiq_raise!("I2CError", "I2C remote restart fail"),
msg => panic!("Expected I2cBasicReply for I2cRetartRequest, got: {:?}", msg),
}
}
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
if get_bus().restart().is_err() {
artiq_raise!("I2CError", "I2C restart failed");
}
}
pub extern "C" fn stop(busno: i32) {
let _destination = (busno >> 16) as u8;
#[cfg(has_drtio)]
if _destination != 0 {
// remote
let reply = unsafe {
KERNEL_CHANNEL_1TO0
.as_mut()
.unwrap()
.send(Message::I2cStopRequest(busno as u32));
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
};
match reply {
Message::I2cBasicReply(true) => return,
Message::I2cBasicReply(false) => artiq_raise!("I2CError", "I2C remote stop fail"),
msg => panic!("Expected I2cBasicReply for I2cStopRequest, got: {:?}", msg),
}
}
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
if get_bus().stop().is_err() {
artiq_raise!("I2CError", "I2C stop failed");
}
}
pub extern "C" fn write(busno: i32, data: i32) -> bool {
let _destination = (busno >> 16) as u8;
#[cfg(has_drtio)]
if _destination != 0 {
// remote
let reply = unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::I2cWriteRequest {
busno: busno as u32,
data: data as u8,
});
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
};
match reply {
Message::I2cWriteReply { succeeded: true, ack } => return ack,
Message::I2cWriteReply { succeeded: false, .. } => artiq_raise!("I2CError", "I2C remote write fail"),
msg => panic!("Expected I2cWriteReply for I2cWriteRequest, got: {:?}", msg),
}
}
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
match get_bus().write(data as u8) {
Ok(()) => true,
Err(Error::Nack) => false,
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
}
}
pub extern "C" fn read(busno: i32, ack: bool) -> i32 {
let _destination = (busno >> 16) as u8;
#[cfg(has_drtio)]
if _destination != 0 {
let reply = unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::I2cReadRequest {
busno: busno as u32,
ack,
});
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
};
match reply {
Message::I2cReadReply { succeeded: true, data } => return data as i32,
Message::I2cReadReply { succeeded: false, .. } => artiq_raise!("I2CError", "I2C remote read fail"),
msg => panic!("Expected I2cReadReply for I2cReadRequest, got: {:?}", msg),
}
}
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
match get_bus().read(ack) {
Ok(r) => r as i32,
Err(_) => artiq_raise!("I2CError", "I2C read failed"),
}
}
pub extern "C" fn switch_select(busno: i32, address: i32, mask: i32) {
let _destination = (busno >> 16) as u8;
#[cfg(has_drtio)]
if _destination != 0 {
// remote
let reply = unsafe {
KERNEL_CHANNEL_1TO0
.as_mut()
.unwrap()
.send(Message::I2cSwitchSelectRequest {
busno: busno as u32,
address: address as u8,
mask: mask as u8,
});
KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv()
};
match reply {
Message::I2cBasicReply(true) => return,
Message::I2cBasicReply(false) => artiq_raise!("I2CError", "I2C remote start fail"),
msg => panic!("Expected I2cBasicReply for I2cSwitchSelectRequest, got: {:?}", msg),
}
}
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"),
};
if get_bus().pca954x_select(address as u8, ch).is_err() {
artiq_raise!("I2CError", "switch select failed");
}
}
pub fn init() {
let mut i2c = I2c::i2c0();
i2c.init().expect("I2C bus initialization failed");
unsafe { I2C_BUS.write(i2c) };
}
pub fn get_bus() -> &'static mut I2c {
unsafe { I2C_BUS.assume_init_mut() }
}

View File

@ -0,0 +1,440 @@
// Uses `nalgebra` crate to invoke `np_linalg` and `sp_linalg` functions
// When converting between `nalgebra::Matrix` and `NDArray` following considerations are necessary
//
// * Both `nalgebra::Matrix` and `NDArray` require their content to be stored in row-major order
// * `NDArray` data pointer can be directly read and converted to `nalgebra::Matrix` (row and column number must be known)
// * `nalgebra::Matrix::as_slice` returns the content of matrix in column-major order and initial data needs to be transposed before storing it in `NDArray` data pointer
use alloc::vec::Vec;
use core::slice;
use nalgebra::DMatrix;
use crate::artiq_raise;
pub struct InputMatrix {
pub ndims: usize,
pub dims: *const usize,
pub data: *mut f64,
}
impl InputMatrix {
fn get_dims(&mut self) -> Vec<usize> {
let dims = unsafe { slice::from_raw_parts(self.dims, self.ndims) };
dims.to_vec()
}
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_cholesky(mat1: *mut InputMatrix, out: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out = out.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
if dim1[0] != dim1[1] {
artiq_raise!(
"ValueError",
"last 2 dimensions of the array must be square: {1} != {2}",
0,
dim1[0] as i64,
dim1[1] as i64
);
}
let outdim = out.get_dims();
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let result = matrix1.cholesky();
match result {
Some(res) => {
out_slice.copy_from_slice(res.unpack().transpose().as_slice());
}
None => {
artiq_raise!("LinAlgError", "Matrix is not positive definite");
}
};
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_qr(mat1: *mut InputMatrix, out_q: *mut InputMatrix, out_r: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out_q = out_q.as_mut().unwrap();
let out_r = out_r.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
let outq_dim = (*out_q).get_dims();
let outr_dim = (*out_r).get_dims();
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let out_q_slice = slice::from_raw_parts_mut(out_q.data, outq_dim[0] * outq_dim[1]);
let out_r_slice = slice::from_raw_parts_mut(out_r.data, outr_dim[0] * outr_dim[1]);
// Refer to https://github.com/dimforge/nalgebra/issues/735
let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let res = matrix1.qr();
let (q, r) = res.unpack();
// Uses different algo need to match numpy
out_q_slice.copy_from_slice(q.transpose().as_slice());
out_r_slice.copy_from_slice(r.transpose().as_slice());
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_svd(
mat1: *mut InputMatrix,
outu: *mut InputMatrix,
outs: *mut InputMatrix,
outvh: *mut InputMatrix,
) {
let mat1 = mat1.as_mut().unwrap();
let outu = outu.as_mut().unwrap();
let outs = outs.as_mut().unwrap();
let outvh = outvh.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
let outu_dim = (*outu).get_dims();
let outs_dim = (*outs).get_dims();
let outvh_dim = (*outvh).get_dims();
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let out_u_slice = slice::from_raw_parts_mut(outu.data, outu_dim[0] * outu_dim[1]);
let out_s_slice = slice::from_raw_parts_mut(outs.data, outs_dim[0]);
let out_vh_slice = slice::from_raw_parts_mut(outvh.data, outvh_dim[0] * outvh_dim[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let result = matrix.svd(true, true);
out_u_slice.copy_from_slice(result.u.unwrap().transpose().as_slice());
out_s_slice.copy_from_slice(result.singular_values.as_slice());
out_vh_slice.copy_from_slice(result.v_t.unwrap().transpose().as_slice());
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_inv(mat1: *mut InputMatrix, out: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out = out.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
if dim1[0] != dim1[1] {
artiq_raise!(
"ValueError",
"last 2 dimensions of the array must be square: {1} != {2}",
0,
dim1[0] as i64,
dim1[1] as i64
);
}
let outdim = out.get_dims();
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
if !matrix.is_invertible() {
artiq_raise!("LinAlgError", "no inverse for Singular Matrix");
}
let inv = matrix.try_inverse().unwrap();
out_slice.copy_from_slice(inv.transpose().as_slice());
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_pinv(mat1: *mut InputMatrix, out: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out = out.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
let outdim = out.get_dims();
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let svd = matrix.svd(true, true);
let inv = svd.pseudo_inverse(1e-15);
match inv {
Ok(m) => {
out_slice.copy_from_slice(m.transpose().as_slice());
}
Err(_) => {
artiq_raise!("LinAlgError", "SVD computation does not converge");
}
}
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_matrix_power(mat1: *mut InputMatrix, mat2: *mut InputMatrix, out: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let mat2 = mat2.as_mut().unwrap();
let out = out.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
let power = slice::from_raw_parts_mut(mat2.data, 1);
let power = power[0];
let outdim = out.get_dims();
let out_slice = slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]);
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let mut abs_power = power;
if abs_power < 0.0 {
abs_power = abs_power * -1.0;
}
let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
if !matrix1.is_square() {
artiq_raise!(
"ValueError",
"last 2 dimensions of the array must be square: {1} != {2}",
0,
dim1[0] as i64,
dim1[1] as i64
);
}
let mut result = matrix1.pow(abs_power as u32);
if power < 0.0 {
if !matrix1.is_invertible() {
artiq_raise!("LinAlgError", "no inverse for Singular Matrix");
}
result = result.try_inverse().unwrap();
}
out_slice.copy_from_slice(result.transpose().as_slice());
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn np_linalg_det(mat1: *mut InputMatrix, out: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out = out.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
let out_slice = slice::from_raw_parts_mut(out.data, 1);
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
if !matrix.is_square() {
artiq_raise!(
"ValueError",
"last 2 dimensions of the array must be square: {1} != {2}",
0,
dim1[0] as i64,
dim1[1] as i64
);
}
out_slice[0] = matrix.determinant();
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn sp_linalg_lu(mat1: *mut InputMatrix, out_l: *mut InputMatrix, out_u: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out_l = out_l.as_mut().unwrap();
let out_u = out_u.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
let outl_dim = (*out_l).get_dims();
let outu_dim = (*out_u).get_dims();
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let out_l_slice = slice::from_raw_parts_mut(out_l.data, outl_dim[0] * outl_dim[1]);
let out_u_slice = slice::from_raw_parts_mut(out_u.data, outu_dim[0] * outu_dim[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let (_, l, u) = matrix.lu().unpack();
out_l_slice.copy_from_slice(l.transpose().as_slice());
out_u_slice.copy_from_slice(u.transpose().as_slice());
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn sp_linalg_schur(mat1: *mut InputMatrix, out_t: *mut InputMatrix, out_z: *mut InputMatrix) {
let mat1 = mat1.as_mut().unwrap();
let out_t = out_t.as_mut().unwrap();
let out_z = out_z.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
if dim1[0] != dim1[1] {
artiq_raise!(
"ValueError",
"last 2 dimensions of the array must be square: {1} != {2}",
0,
dim1[0] as i64,
dim1[1] as i64
);
}
let out_t_dim = (*out_t).get_dims();
let out_z_dim = (*out_z).get_dims();
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let out_t_slice = slice::from_raw_parts_mut(out_t.data, out_t_dim[0] * out_t_dim[1]);
let out_z_slice = slice::from_raw_parts_mut(out_z.data, out_z_dim[0] * out_z_dim[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let (z, t) = matrix.schur().unpack();
out_t_slice.copy_from_slice(t.transpose().as_slice());
out_z_slice.copy_from_slice(z.transpose().as_slice());
}
/// # Safety
///
/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order
#[no_mangle]
pub unsafe extern "C" fn sp_linalg_hessenberg(
mat1: *mut InputMatrix,
out_h: *mut InputMatrix,
out_q: *mut InputMatrix,
) {
let mat1 = mat1.as_mut().unwrap();
let out_h = out_h.as_mut().unwrap();
let out_q = out_q.as_mut().unwrap();
if mat1.ndims != 2 {
artiq_raise!(
"ValueError",
"expected 2D Vector Input, but received {1}D input)",
0,
mat1.ndims as i64,
0
);
}
let dim1 = (*mat1).get_dims();
if dim1[0] != dim1[1] {
artiq_raise!(
"ValueError",
"last 2 dimensions of the array must be square: {1} != {2}",
0,
dim1[0] as i64,
dim1[1] as i64
);
}
let out_h_dim = (*out_h).get_dims();
let out_q_dim = (*out_q).get_dims();
let data_slice1 = slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]);
let out_h_slice = slice::from_raw_parts_mut(out_h.data, out_h_dim[0] * out_h_dim[1]);
let out_q_slice = slice::from_raw_parts_mut(out_q.data, out_q_dim[0] * out_q_dim[1]);
let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1);
let (q, h) = matrix.hessenberg().unpack();
out_h_slice.copy_from_slice(h.transpose().as_slice());
out_q_slice.copy_from_slice(q.transpose().as_slice());
}

View File

@ -10,9 +10,17 @@ pub use control::Control;
mod api;
pub mod core1;
mod dma;
pub mod i2c;
mod rpc;
#[cfg(ki_impl = "csr")]
#[path = "rtio_csr.rs"]
pub mod rtio;
#[cfg(ki_impl = "acp")]
#[path = "rtio_acp.rs"]
pub mod rtio;
pub use dma::DmaRecorder;
mod cache;
mod linalg;
#[cfg(has_drtio)]
mod subkernel;
@ -23,6 +31,7 @@ pub enum SubkernelStatus {
Timeout,
IncorrectState,
CommLost,
Exception(Vec<u8>),
OtherError,
}
@ -39,6 +48,10 @@ pub enum Message {
&'static [(usize, usize)],
u8,
),
#[cfg(has_drtio)]
RtioInitRequest,
RpcSend {
is_async: bool,
data: Vec<u8>,
@ -74,11 +87,47 @@ pub enum Message {
#[cfg(has_drtio)]
UpDestinationsReply(bool),
#[cfg(has_drtio)]
I2cStartRequest(u32),
#[cfg(has_drtio)]
I2cRestartRequest(u32),
#[cfg(has_drtio)]
I2cStopRequest(u32),
#[cfg(has_drtio)]
I2cWriteRequest {
busno: u32,
data: u8,
},
#[cfg(has_drtio)]
I2cWriteReply {
succeeded: bool,
ack: bool,
},
#[cfg(has_drtio)]
I2cReadRequest {
busno: u32,
ack: bool,
},
#[cfg(has_drtio)]
I2cReadReply {
succeeded: bool,
data: u8,
},
#[cfg(has_drtio)]
I2cBasicReply(bool),
#[cfg(has_drtio)]
I2cSwitchSelectRequest {
busno: u32,
address: u8,
mask: u8,
},
#[cfg(has_drtio)]
SubkernelLoadRunRequest {
id: u32,
destination: u8,
run: bool,
timestamp: u64,
},
#[cfg(has_drtio)]
SubkernelLoadRunReply {
@ -90,9 +139,7 @@ pub enum Message {
timeout: i64,
},
#[cfg(has_drtio)]
SubkernelAwaitFinishReply {
status: SubkernelStatus,
},
SubkernelAwaitFinishReply,
#[cfg(has_drtio)]
SubkernelMsgSend {
id: u32,
@ -109,9 +156,10 @@ pub enum Message {
},
#[cfg(has_drtio)]
SubkernelMsgRecvReply {
status: SubkernelStatus,
count: u8,
},
#[cfg(has_drtio)]
SubkernelError(SubkernelStatus),
}
static CHANNEL_0TO1: Mutex<Option<sync_channel::Sender<'static, Message>>> = Mutex::new(None);

View File

@ -4,6 +4,8 @@ use cslice::CSlice;
use libcortex_a9::asm;
use vcell::VolatileCell;
#[cfg(has_drtio)]
use super::{Message, KERNEL_CHANNEL_1TO0};
use crate::{artiq_raise, pl::csr, resolve_channel_name, rtio_core};
pub const RTIO_O_STATUS_WAIT: i32 = 1;
@ -56,6 +58,10 @@ pub extern "C" fn init() {
csr::rtio::engine_addr_base_write(&TRANSACTION_BUFFER as *const Transaction as u32);
csr::rtio::enable_write(1);
}
#[cfg(has_drtio)]
unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::RtioInitRequest);
}
}
pub extern "C" fn get_counter() -> i64 {

View File

@ -2,6 +2,8 @@ use core::ptr::{read_volatile, write_volatile};
use cslice::CSlice;
#[cfg(has_drtio)]
use super::{Message, KERNEL_CHANNEL_1TO0};
use crate::{artiq_raise, pl::csr, resolve_channel_name, rtio_core};
pub const RTIO_O_STATUS_WAIT: u8 = 1;
@ -22,6 +24,10 @@ pub extern "C" fn init() {
unsafe {
rtio_core::reset_write(1);
}
#[cfg(has_drtio)]
unsafe {
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::RtioInitRequest);
}
}
pub extern "C" fn get_counter() -> i64 {

View File

@ -2,8 +2,8 @@ use alloc::vec::Vec;
use cslice::CSlice;
use super::{Message, SubkernelStatus, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0};
use crate::{artiq_raise, rpc::send_args};
use super::{rtio::now_mu, Message, SubkernelStatus, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0};
use crate::{artiq_raise, eh_artiq, rpc::send_args};
pub extern "C" fn load_run(id: u32, destination: u8, run: bool) {
unsafe {
@ -14,6 +14,7 @@ pub extern "C" fn load_run(id: u32, destination: u8, run: bool) {
id: id,
destination: destination,
run: run,
timestamp: now_mu() as u64,
});
}
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
@ -36,21 +37,18 @@ pub extern "C" fn await_finish(id: u32, timeout: i64) {
});
}
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::NoError,
} => (),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::IncorrectState,
} => artiq_raise!("SubkernelError", "Subkernel not running"),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::Timeout,
} => artiq_raise!("SubkernelError", "Subkernel timed out"),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::CommLost,
} => artiq_raise!("SubkernelError", "Lost communication with satellite"),
Message::SubkernelAwaitFinishReply {
status: SubkernelStatus::OtherError,
} => artiq_raise!("SubkernelError", "An error occurred during subkernel operation"),
Message::SubkernelAwaitFinishReply => (),
Message::SubkernelError(SubkernelStatus::IncorrectState) => {
artiq_raise!("SubkernelError", "Subkernel not running")
}
Message::SubkernelError(SubkernelStatus::Timeout) => artiq_raise!("SubkernelError", "Subkernel timed out"),
Message::SubkernelError(SubkernelStatus::CommLost) => {
artiq_raise!("SubkernelError", "Lost communication with satellite")
}
Message::SubkernelError(SubkernelStatus::OtherError) => {
artiq_raise!("SubkernelError", "An error occurred during subkernel operation")
}
Message::SubkernelError(SubkernelStatus::Exception(raw_exception)) => eh_artiq::raise_raw(&raw_exception),
_ => panic!("expected SubkernelAwaitFinishReply after SubkernelAwaitFinishRequest"),
}
}
@ -92,30 +90,22 @@ pub extern "C" fn await_message(id: i32, timeout: i64, tags: &CSlice<u8>, min: u
});
}
match unsafe { KERNEL_CHANNEL_0TO1.as_mut().unwrap() }.recv() {
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::NoError,
count,
} => {
Message::SubkernelMsgRecvReply { count } => {
if min > count || count > max {
artiq_raise!("SubkernelError", "Received more or less arguments than required")
}
}
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::IncorrectState,
..
} => artiq_raise!("SubkernelError", "Subkernel not running"),
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::Timeout,
..
} => artiq_raise!("SubkernelError", "Subkernel timed out"),
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::CommLost,
..
} => artiq_raise!("SubkernelError", "Lost communication with satellite"),
Message::SubkernelMsgRecvReply {
status: SubkernelStatus::OtherError,
..
} => artiq_raise!("SubkernelError", "An error occurred during subkernel operation"),
Message::SubkernelError(SubkernelStatus::IncorrectState) => {
artiq_raise!("SubkernelError", "Subkernel not running")
}
Message::SubkernelError(SubkernelStatus::Timeout) => artiq_raise!("SubkernelError", "Subkernel timed out"),
Message::SubkernelError(SubkernelStatus::CommLost) => {
artiq_raise!("SubkernelError", "Lost communication with satellite")
}
Message::SubkernelError(SubkernelStatus::OtherError) => {
artiq_raise!("SubkernelError", "An error occurred during subkernel operation")
}
Message::SubkernelError(SubkernelStatus::Exception(raw_exception)) => eh_artiq::raise_raw(&raw_exception),
_ => panic!("expected SubkernelMsgRecvReply after SubkernelMsgRecvRequest"),
}
// RpcRecvRequest should be called after this to receive message data

View File

@ -1,15 +1,16 @@
#![no_std]
#![feature(c_variadic)]
#![feature(const_btree_new)]
#![feature(const_in_array_repeat_expressions)]
#![feature(const_btree_len)]
#![feature(inline_const)]
#![feature(naked_functions)]
#![feature(asm)]
#![feature(raw_ref_op)]
#[macro_use]
extern crate alloc;
use alloc::{collections::BTreeMap, string::String};
use byteorder::NativeEndian;
use io::{Cursor, ProtoRead};
use libasync::block_async;
use libconfig::Config;
@ -21,20 +22,15 @@ pub use pl::csr::rtio_core;
use void::Void;
pub mod eh_artiq;
pub mod i2c;
pub mod irq;
pub mod kernel;
pub mod rpc;
#[cfg(ki_impl = "csr")]
#[path = "rtio_csr.rs"]
pub mod rtio;
#[cfg(ki_impl = "acp")]
#[path = "rtio_acp.rs"]
pub mod rtio;
#[rustfmt::skip]
#[path = "../../../build/pl.rs"]
pub mod pl;
#[cfg(has_cxp_grabber)]
pub mod cxp;
#[derive(Debug, Clone)]
pub struct RPCException {
@ -122,10 +118,10 @@ fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> {
.read("device_map")
.and_then(|raw_bytes| {
let mut bytes_cr = Cursor::new(raw_bytes);
let size = bytes_cr.read_u32().unwrap();
let size = bytes_cr.read_u32::<NativeEndian>().unwrap();
for _ in 0..size {
let channel = bytes_cr.read_u32().unwrap();
let device_name = bytes_cr.read_string().unwrap();
let channel = bytes_cr.read_u32::<NativeEndian>().unwrap();
let device_name = bytes_cr.read_string::<NativeEndian>().unwrap();
if let Some(old_entry) = device_map.insert(channel, device_name.clone()) {
warn!(
"conflicting device map entries for RTIO channel {}: '{}' and '{}'",
@ -145,15 +141,13 @@ fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> {
device_map
}
fn _resolve_channel_name(channel: u32, device_map: &BTreeMap<u32, String>) -> String {
match device_map.get(&channel) {
Some(val) => val.clone(),
None => String::from("unknown"),
}
}
pub fn resolve_channel_name(channel: u32) -> String {
_resolve_channel_name(channel, unsafe { &RTIO_DEVICE_MAP })
unsafe {
match RTIO_DEVICE_MAP.get(&channel) {
Some(val) => val.clone(),
None => String::from("unknown"),
}
}
}
pub fn setup_device_map(cfg: &Config) {

View File

@ -1,7 +1,7 @@
use core::str;
use byteorder::{ByteOrder, NativeEndian};
use core_io::{Error, Read, Write};
use core_io::Error;
use cslice::{CMutSlice, CSlice};
use io::{ProtoRead, ProtoWrite};
use log::trace;
@ -37,7 +37,7 @@ pub unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
// versions for reader rather than TcpStream
// they will be made into sync for satellite subkernels later
unsafe fn recv_elements<F, R>(
unsafe fn recv_elements<F, R: ProtoRead>(
reader: &mut R,
elt_tag: Tag,
length: usize,
@ -46,7 +46,6 @@ unsafe fn recv_elements<F, R>(
) -> Result<(), Error>
where
F: FnMut(usize) -> *mut (),
R: Read + ?Sized,
{
match elt_tag {
Tag::Bool => {
@ -57,7 +56,7 @@ where
let ptr = storage as *mut u32;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
reader.read_exact(dest)?;
drop(dest);
let _ = dest;
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u32(dest);
}
@ -65,7 +64,7 @@ where
let ptr = storage as *mut u64;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
reader.read_exact(dest)?;
drop(dest);
let _ = dest;
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u64(dest);
}
@ -82,7 +81,7 @@ where
unsafe fn recv_value<F, R>(reader: &mut R, tag: Tag, data: &mut *mut (), alloc: &mut F) -> Result<(), Error>
where
F: FnMut(usize) -> *mut (),
R: Read + ?Sized,
R: ProtoRead,
{
macro_rules! consume_value {
($ty:ty, | $ptr:ident | $map:expr) => {{
@ -99,16 +98,16 @@ where
Ok(())
}),
Tag::Int32 => consume_value!(i32, |ptr| {
*ptr = reader.read_u32()? as i32;
*ptr = reader.read_u32::<NativeEndian>()? as i32;
Ok(())
}),
Tag::Int64 | Tag::Float64 => consume_value!(i64, |ptr| {
*ptr = reader.read_u64()? as i64;
*ptr = reader.read_u64::<NativeEndian>()? as i64;
Ok(())
}),
Tag::String | Tag::Bytes | Tag::ByteArray => {
consume_value!(CMutSlice<u8>, |ptr| {
let length = reader.read_u32()? as usize;
let length = reader.read_u32::<NativeEndian>()? as usize;
*ptr = CMutSlice::new(alloc(length) as *mut u8, length);
reader.read_exact((*ptr).as_mut())?;
Ok(())
@ -133,7 +132,7 @@ where
}
consume_value!(*mut List, |ptr_to_list| {
let tag = it.clone().next().expect("truncated tag");
let length = reader.read_u32()? as usize;
let length = reader.read_u32::<NativeEndian>()? as usize;
let list_size = 4 + 4;
let storage_offset = round_up(list_size, tag.alignment());
@ -152,7 +151,7 @@ where
consume_value!(*mut (), |buffer| {
let mut total_len: usize = 1;
for _ in 0..num_dims {
let len = reader.read_u32()? as usize;
let len = reader.read_u32::<NativeEndian>()? as usize;
total_len *= len;
consume_value!(usize, |ptr| *ptr = len)
}
@ -183,7 +182,7 @@ pub fn recv_return<'a, F, R>(
) -> Result<&'a [u8], Error>
where
F: FnMut(usize) -> *mut (),
R: Read + ?Sized,
R: ProtoRead,
{
let mut it = TagIterator::new(tag_bytes);
trace!("recv ...->{}", it);
@ -195,16 +194,13 @@ where
Ok(it.data)
}
unsafe fn send_elements<W>(
unsafe fn send_elements<W: ProtoWrite>(
writer: &mut W,
elt_tag: Tag,
length: usize,
data: *const (),
write_tags: bool,
) -> Result<(), Error>
where
W: Write + ?Sized,
{
) -> Result<(), Error> {
if write_tags {
writer.write_u8(elt_tag.as_u8())?;
}
@ -233,8 +229,12 @@ where
Ok(())
}
unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const (), write_tags: bool) -> Result<(), Error>
where W: Write + ?Sized {
unsafe fn send_value<W: ProtoWrite>(
writer: &mut W,
tag: Tag,
data: &mut *const (),
write_tags: bool,
) -> Result<(), Error> {
macro_rules! consume_value {
($ty:ty, | $ptr:ident | $map:expr) => {{
let $ptr = align_ptr::<$ty>(*data);
@ -249,12 +249,14 @@ where W: Write + ?Sized {
match tag {
Tag::None => Ok(()),
Tag::Bool => consume_value!(u8, |ptr| writer.write_u8(*ptr)),
Tag::Int32 => consume_value!(u32, |ptr| writer.write_u32(*ptr)),
Tag::Int64 | Tag::Float64 => consume_value!(u64, |ptr| writer.write_u64(*ptr)),
Tag::Int32 => consume_value!(u32, |ptr| writer.write_u32::<NativeEndian>(*ptr)),
Tag::Int64 | Tag::Float64 => consume_value!(u64, |ptr| writer.write_u64::<NativeEndian>(*ptr)),
Tag::String => consume_value!(CSlice<u8>, |ptr| {
writer.write_string(str::from_utf8((*ptr).as_ref()).unwrap())
writer.write_string::<NativeEndian>(str::from_utf8((*ptr).as_ref()).unwrap())
}),
Tag::Bytes | Tag::ByteArray => consume_value!(CSlice<u8>, |ptr| writer.write_bytes((*ptr).as_ref())),
Tag::Bytes | Tag::ByteArray => {
consume_value!(CSlice<u8>, |ptr| writer.write_bytes::<NativeEndian>((*ptr).as_ref()))
}
Tag::Tuple(it, arity) => {
let mut it = it.clone();
if write_tags {
@ -277,7 +279,7 @@ where W: Write + ?Sized {
}
consume_value!(&List, |ptr| {
let length = (**ptr).length as usize;
writer.write_u32((*ptr).length)?;
writer.write_u32::<NativeEndian>((*ptr).length)?;
let tag = it.clone().next().expect("truncated tag");
send_elements(writer, tag, length, (**ptr).elements, write_tags)
})
@ -292,7 +294,7 @@ where W: Write + ?Sized {
let mut total_len = 1;
for _ in 0..num_dims {
consume_value!(u32, |len| {
writer.write_u32(*len)?;
writer.write_u32::<NativeEndian>(*len)?;
total_len *= *len;
})
}
@ -313,7 +315,7 @@ where W: Write + ?Sized {
name: CSlice<'a, u8>,
}
consume_value!(Keyword, |ptr| {
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
writer.write_string::<NativeEndian>(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
let tag = it.clone().next().expect("truncated tag");
let mut data = ptr.offset(1) as *const ();
send_value(writer, tag, &mut data, write_tags)
@ -326,28 +328,25 @@ where W: Write + ?Sized {
struct Object {
id: u32,
}
consume_value!(*const Object, |ptr| writer.write_u32((**ptr).id))
consume_value!(*const Object, |ptr| writer.write_u32::<NativeEndian>((**ptr).id))
}
}
}
pub fn send_args<W>(
pub fn send_args<W: ProtoWrite>(
writer: &mut W,
service: u32,
tag_bytes: &[u8],
data: *const *const (),
write_tags: bool,
) -> Result<(), Error>
where
W: Write + ?Sized,
{
) -> Result<(), Error> {
let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes);
let mut args_it = TagIterator::new(arg_tags_bytes);
let return_it = TagIterator::new(return_tag_bytes);
trace!("send<{}>({})->{}", service, args_it, return_it);
writer.write_u32(service)?;
writer.write_u32::<NativeEndian>(service)?;
for index in 0.. {
if let Some(arg_tag) = args_it.next() {
let mut data = unsafe { *data.offset(index) };
@ -357,7 +356,7 @@ where
}
}
writer.write_u8(0)?;
writer.write_bytes(return_tag_bytes)?;
writer.write_bytes::<NativeEndian>(return_tag_bytes)?;
Ok(())
}

View File

@ -1,8 +1,6 @@
#![no_std]
#![feature(link_cfg)]
#![feature(nll)]
#![feature(unwind_attributes)]
#![feature(static_nobundle)]
#![feature(c_unwind)]
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
cfg_if::cfg_if! {
@ -24,7 +22,12 @@ cfg_if::cfg_if! {
extern "C" {}
#[cfg(target_os = "redox")]
#[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))]
#[link(
name = "gcc_eh",
kind = "static",
modifiers = "-bundle",
cfg(target_feature = "crt-static")
)]
#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))]
extern "C" {}

View File

@ -77,8 +77,7 @@ pub type _Unwind_Exception_Cleanup_Fn =
all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")),
link(name = "unwind", kind = "static")
)]
extern "C" {
#[unwind(allowed)]
extern "C-unwind" {
pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
@ -226,8 +225,7 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
#[cfg_attr(all(feature = "llvm-libunwind",
any(target_os = "fuchsia", target_os = "linux")),
link(name = "unwind", kind = "static"))]
extern "C" {
#[unwind(allowed)]
extern "C-unwind" {
pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
trace_argument: *mut c_void)
@ -238,8 +236,7 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
#[cfg_attr(all(feature = "llvm-libunwind",
any(target_os = "fuchsia", target_os = "linux")),
link(name = "unwind", kind = "static"))]
extern "C" {
#[unwind(allowed)]
extern "C-unwind" {
pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
}

View File

@ -8,6 +8,7 @@ edition = "2018"
[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"]
target_ebaz4205 = ["libboard_zynq/target_ebaz4205", "libsupport_zynq/target_ebaz4205", "libconfig/target_ebaz4205", "libboard_artiq/target_ebaz4205"]
default = ["target_zc706"]
[build-dependencies]
@ -15,15 +16,16 @@ build_zynq = { path = "../libbuild_zynq" }
[dependencies]
num-traits = { version = "0.2", default-features = false }
num-derive = "0.3"
num-derive = "0.4"
cslice = "0.3"
log = "0.4"
embedded-hal = "0.2"
core_io = { version = "0.1", features = ["collections"] }
core_io = { git = "https://git.m-labs.hk/M-Labs/rs-core_io.git", rev = "e9d3edf027", features = ["collections"] }
crc = { version = "1.7", default-features = false }
byteorder = { version = "1.3", default-features = false }
void = { version = "1", default-features = false }
futures = { version = "0.3", default-features = false, features = ["async-await"] }
async-recursion = "0.3"
async-recursion = "1.1"
log_buffer = { version = "1.2" }
vcell = "0.1"
@ -44,4 +46,4 @@ libboard_artiq = { path = "../libboard_artiq" }
[dependencies.tar-no-std]
git = "https://git.m-labs.hk/M-Labs/tar-no-std"
rev = "2ab6dc5"
rev = "2ab6dc5"

View File

@ -10,15 +10,11 @@ use io::Cursor;
#[cfg(has_drtio)]
use ksupport::rpc;
use ksupport::{kernel, resolve_channel_name};
#[cfg(has_drtio)]
use libasync::delay;
use libasync::{smoltcp::{Sockets, TcpStream},
task};
use libboard_artiq::drtio_routing;
#[cfg(feature = "target_kasli_soc")]
use libboard_zynq::error_led::ErrorLED;
#[cfg(has_drtio)]
use libboard_zynq::time::Milliseconds;
use libboard_zynq::{self as zynq,
smoltcp::{self,
iface::{EthernetInterfaceBuilder, NeighborCache},
@ -401,12 +397,49 @@ async fn handle_run_kernel(
control.borrow_mut().tx.async_send(reply).await;
}
#[cfg(has_drtio)]
kernel::Message::I2cStartRequest(busno)
| kernel::Message::I2cRestartRequest(busno)
| kernel::Message::I2cStopRequest(busno)
| kernel::Message::I2cSwitchSelectRequest { busno, .. } => {
let result = rtio_mgt::drtio::i2c_send_basic(aux_mutex, routing_table, timer, &reply, busno).await;
let reply = match result {
Ok(succeeded) => kernel::Message::I2cBasicReply(succeeded),
Err(_) => kernel::Message::I2cBasicReply(false),
};
control.borrow_mut().tx.async_send(reply).await;
}
#[cfg(has_drtio)]
kernel::Message::I2cWriteRequest { busno, data } => {
let result = rtio_mgt::drtio::i2c_send_write(aux_mutex, routing_table, timer, busno, data).await;
let reply = match result {
Ok((succeeded, ack)) => kernel::Message::I2cWriteReply { succeeded, ack },
Err(_) => kernel::Message::I2cWriteReply {
succeeded: false,
ack: false,
},
};
control.borrow_mut().tx.async_send(reply).await;
}
#[cfg(has_drtio)]
kernel::Message::I2cReadRequest { busno, ack } => {
let result = rtio_mgt::drtio::i2c_send_read(aux_mutex, routing_table, timer, busno, ack).await;
let reply = match result {
Ok((succeeded, data)) => kernel::Message::I2cReadReply { succeeded, data },
Err(_) => kernel::Message::I2cReadReply {
succeeded: false,
data: 0xFF,
},
};
control.borrow_mut().tx.async_send(reply).await;
}
#[cfg(has_drtio)]
kernel::Message::SubkernelLoadRunRequest {
id,
destination: _,
run,
timestamp,
} => {
let succeeded = match subkernel::load(aux_mutex, routing_table, timer, id, run).await {
let succeeded = match subkernel::load(aux_mutex, routing_table, timer, id, run, timestamp).await {
Ok(()) => true,
Err(e) => {
error!("Error loading subkernel: {:?}", e);
@ -422,33 +455,23 @@ async fn handle_run_kernel(
#[cfg(has_drtio)]
kernel::Message::SubkernelAwaitFinishRequest { id, timeout } => {
let res = subkernel::await_finish(aux_mutex, routing_table, timer, id, timeout).await;
let status = match res {
Ok(ref res) => {
let response = match res {
Ok(res) => {
if res.status == subkernel::FinishStatus::CommLost {
kernel::SubkernelStatus::CommLost
} else if let Some(exception) = &res.exception {
error!("Exception in subkernel");
match stream {
None => (),
Some(stream) => {
write_chunk(stream, exception).await?;
}
}
// will not be called after exception is served
kernel::SubkernelStatus::OtherError
kernel::Message::SubkernelError(kernel::SubkernelStatus::CommLost)
} else if let Some(exception) = res.exception {
kernel::Message::SubkernelError(kernel::SubkernelStatus::Exception(exception))
} else {
kernel::SubkernelStatus::NoError
kernel::Message::SubkernelAwaitFinishReply
}
}
Err(SubkernelError::Timeout) => kernel::SubkernelStatus::Timeout,
Err(SubkernelError::IncorrectState) => kernel::SubkernelStatus::IncorrectState,
Err(_) => kernel::SubkernelStatus::OtherError,
Err(SubkernelError::Timeout) => kernel::Message::SubkernelError(kernel::SubkernelStatus::Timeout),
Err(SubkernelError::IncorrectState) => {
kernel::Message::SubkernelError(kernel::SubkernelStatus::IncorrectState)
}
Err(_) => kernel::Message::SubkernelError(kernel::SubkernelStatus::OtherError),
};
control
.borrow_mut()
.tx
.async_send(kernel::Message::SubkernelAwaitFinishReply { status: status })
.await;
control.borrow_mut().tx.async_send(response).await;
}
#[cfg(has_drtio)]
kernel::Message::SubkernelMsgSend { id, destination, data } => {
@ -469,35 +492,23 @@ async fn handle_run_kernel(
#[cfg(has_drtio)]
kernel::Message::SubkernelMsgRecvRequest { id, timeout, tags } => {
let message_received = subkernel::message_await(id as u32, timeout, timer).await;
let (status, count) = match message_received {
Ok(ref message) => (kernel::SubkernelStatus::NoError, message.count),
Err(SubkernelError::Timeout) => (kernel::SubkernelStatus::Timeout, 0),
Err(SubkernelError::IncorrectState) => (kernel::SubkernelStatus::IncorrectState, 0),
Err(SubkernelError::CommLost) => (kernel::SubkernelStatus::CommLost, 0),
let response = match message_received {
Ok(ref message) => kernel::Message::SubkernelMsgRecvReply { count: message.count },
Err(SubkernelError::Timeout) => kernel::Message::SubkernelError(kernel::SubkernelStatus::Timeout),
Err(SubkernelError::IncorrectState) => {
kernel::Message::SubkernelError(kernel::SubkernelStatus::IncorrectState)
}
Err(SubkernelError::CommLost) => kernel::Message::SubkernelError(kernel::SubkernelStatus::CommLost),
Err(SubkernelError::SubkernelException) => {
error!("Exception in subkernel");
// just retrieve the exception
let status = subkernel::await_finish(aux_mutex, routing_table, timer, id as u32, timeout)
.await
.unwrap();
match stream {
None => (),
Some(stream) => {
write_chunk(stream, &status.exception.unwrap()).await?;
}
}
(kernel::SubkernelStatus::OtherError, 0)
kernel::Message::SubkernelError(kernel::SubkernelStatus::Exception(status.exception.unwrap()))
}
Err(_) => (kernel::SubkernelStatus::OtherError, 0),
Err(_) => kernel::Message::SubkernelError(kernel::SubkernelStatus::OtherError),
};
control
.borrow_mut()
.tx
.async_send(kernel::Message::SubkernelMsgRecvReply {
status: status,
count: count,
})
.await;
control.borrow_mut().tx.async_send(response).await;
if let Ok(message) = message_received {
// receive code almost identical to RPC recv, except we are not reading from a stream
let mut reader = Cursor::new(message.data);
@ -529,7 +540,7 @@ async fn handle_run_kernel(
.async_send(kernel::Message::RpcRecvReply(Ok(0)))
.await;
i += 1;
if i < count {
if i < message.count {
current_tags = remaining_tags;
} else {
break;
@ -546,6 +557,10 @@ async fn handle_run_kernel(
.async_send(kernel::Message::UpDestinationsReply(result))
.await;
}
#[cfg(has_drtio)]
kernel::Message::RtioInitRequest => {
rtio_mgt::drtio::reset(aux_mutex, routing_table, timer).await;
}
_ => {
panic!("unexpected message from core1 while kernel was running: {:?}", reply);
}
@ -771,14 +786,13 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
#[cfg(has_drtio_routing)]
drtio_routing::interconnect_disable_all();
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, &cfg, timer);
ksupport::setup_device_map(&cfg);
analyzer::start(&aux_mutex, &drtio_routing_table, &up_destinations, 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_kernel").ok());
if let Ok(buffer) = cfg.read("startup_kernel") {
info!("Loading startup kernel...");
let routing_table = drtio_routing_table.borrow();
@ -805,13 +819,34 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
}
}
mgmt::start(cfg);
let cfg = Rc::new(cfg);
let restart_idle = Rc::new(Semaphore::new(1, 1));
mgmt::start(
cfg.clone(),
restart_idle.clone(),
Some(mgmt::DrtioContext(
aux_mutex.clone(),
drtio_routing_table.clone(),
timer,
)),
);
task::spawn(async move {
let connection = Rc::new(Semaphore::new(1, 1));
let terminate = Rc::new(Semaphore::new(0, 1));
let can_restart_idle = Rc::new(Semaphore::new(1, 1));
let restart_idle = restart_idle.clone();
loop {
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
let control = control.clone();
let mut maybe_stream = select_biased! {
s = (async {
TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap()
}).fuse() => Some(s),
_ = (async {
restart_idle.async_wait().await;
can_restart_idle.async_wait().await;
}).fuse() => None
};
if connection.try_wait().is_none() {
// there is an existing connection
@ -819,47 +854,58 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
connection.async_wait().await;
}
let maybe_idle_kernel = cfg.read("idle_kernel").ok();
if maybe_idle_kernel.is_none() && maybe_stream.is_none() {
control.borrow_mut().restart(); // terminate idle kernel if running
}
let control = control.clone();
let idle_kernel = idle_kernel.clone();
let connection = connection.clone();
let terminate = terminate.clone();
let can_restart_idle = can_restart_idle.clone();
let up_destinations = up_destinations.clone();
let aux_mutex = aux_mutex.clone();
let routing_table = drtio_routing_table.clone();
// we make sure the value of terminate is 0 before we start
let _ = terminate.try_wait();
let _ = can_restart_idle.try_wait();
task::spawn(async move {
let routing_table = routing_table.borrow();
select_biased! {
_ = (async {
let _ = handle_connection(&mut stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
.await
.map_err(|e| warn!("connection terminated: {}", e));
if let Some(buffer) = &*idle_kernel {
info!("Loading idle kernel");
let res = handle_flash_kernel(&buffer, &control, &up_destinations, &aux_mutex, &routing_table, timer)
.await;
match res {
#[cfg(has_drtio)]
Err(Error::DestinationDown) => {
let mut countdown = timer.countdown();
delay(&mut countdown, Milliseconds(500)).await;
if let Some(stream) = &mut maybe_stream {
let _ = handle_connection(stream, control.clone(), &up_destinations, &aux_mutex, &routing_table, timer)
.await
.map_err(|e| warn!("connection terminated: {}", e));
}
can_restart_idle.signal();
match maybe_idle_kernel {
Some(buffer) => {
loop {
info!("loading idle kernel");
match handle_flash_kernel(&buffer, &control, &up_destinations, &aux_mutex, &routing_table, timer).await {
Ok(_) => {
info!("running idle kernel");
match handle_run_kernel(None, &control, &up_destinations, &aux_mutex, &routing_table, timer).await {
Ok(_) => info!("idle kernel finished"),
Err(_) => warn!("idle kernel running error")
}
},
Err(_) => warn!("idle kernel loading error")
}
}
Err(_) => warn!("error loading idle kernel"),
_ => (),
}
info!("Running idle kernel");
let _ = handle_run_kernel(None, &control, &up_destinations, &aux_mutex, &routing_table, timer)
.await.map_err(|_| warn!("error running idle kernel"));
info!("Idle kernel terminated");
},
None => info!("no idle kernel found")
}
}).fuse() => (),
_ = terminate.async_wait().fuse() => ()
}
connection.signal();
let _ = stream.flush().await;
let _ = stream.abort().await;
if let Some(stream) = maybe_stream {
let _ = stream.flush().await;
let _ = stream.abort().await;
}
});
}
});
@ -908,7 +954,8 @@ pub fn soft_panic_main(timer: GlobalTimer, cfg: Config) -> ! {
Sockets::init(32);
mgmt::start(cfg);
let dummy = Rc::new(Semaphore::new(0, 1));
mgmt::start(Rc::new(cfg), dummy, None);
// getting eth settings disables the LED as it resets GPIO
// need to re-enable it here

View File

@ -2,8 +2,10 @@
#![no_main]
#![recursion_limit = "1024"] // for futures_util::select!
#![feature(alloc_error_handler)]
#![feature(const_btree_new)]
#![feature(const_btree_len)]
#![feature(panic_info_message)]
#![allow(internal_features)]
#![feature(lang_items)]
#[macro_use]
extern crate alloc;
@ -11,8 +13,9 @@ extern crate alloc;
#[cfg(all(feature = "target_kasli_soc", has_virtual_leds))]
use core::cell::RefCell;
use ksupport;
use libasync::task;
#[cfg(has_cxp_grabber)]
use libboard_artiq::cxp_phys;
#[cfg(has_drtio_eem)]
use libboard_artiq::drtio_eem;
#[cfg(feature = "target_kasli_soc")]
@ -78,6 +81,23 @@ mod grabber {
}
}
#[cfg(has_cxp_grabber)]
mod cxp {
use libasync::delay;
use libboard_artiq::cxp_grabber;
use libboard_zynq::time::Milliseconds;
use crate::GlobalTimer;
pub async fn grabber_thread(timer: GlobalTimer) {
let mut countdown = timer.countdown();
loop {
cxp_grabber::tick(timer);
delay(&mut countdown, Milliseconds(200)).await;
}
}
}
static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17];
#[no_mangle]
@ -100,10 +120,10 @@ pub fn main_core0() {
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
ksupport::i2c::init();
ksupport::kernel::i2c::init();
#[cfg(feature = "target_kasli_soc")]
{
let i2c_bus = unsafe { (ksupport::i2c::I2C_BUS).as_mut().unwrap() };
let i2c_bus = ksupport::kernel::i2c::get_bus();
let mut io_expander0 = io_expander::IoExpander::new(i2c_bus, 0).unwrap();
let mut io_expander1 = io_expander::IoExpander::new(i2c_bus, 1).unwrap();
io_expander0
@ -150,5 +170,11 @@ pub fn main_core0() {
task::spawn(ksupport::report_async_rtio_errors());
#[cfg(has_cxp_grabber)]
{
cxp_phys::setup();
task::spawn(cxp::grabber_thread(timer));
}
comms::main(timer, cfg);
}

File diff suppressed because it is too large Load Diff

View File

@ -102,7 +102,7 @@ mod remote_moninj {
overrd: i8,
value: i8,
) {
let _lock = aux_mutex.lock();
let _lock = aux_mutex.async_lock().await;
drtioaux_async::send(
linkno,
&drtioaux_async::Packet::InjectionRequest {

View File

@ -71,3 +71,7 @@ fn soft_panic(info: &core::panic::PanicInfo) -> ! {
};
soft_panic_main(timer, cfg);
}
#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

View File

@ -38,7 +38,7 @@ where
let ptr = storage 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);
let _ = dest;
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u32(dest);
}
@ -46,7 +46,7 @@ where
let ptr = storage 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);
let _ = dest;
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u64(dest);
}

View File

@ -1,6 +1,8 @@
#[cfg(not(feature = "target_ebaz4205"))]
use embedded_hal::blocking::delay::DelayMs;
#[cfg(has_si5324)]
use ksupport::i2c;
use ksupport::kernel::i2c;
#[cfg(not(feature = "target_ebaz4205"))]
use libboard_artiq::pl;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
@ -11,6 +13,8 @@ use libboard_zynq::i2c::I2c;
use libboard_zynq::timer::GlobalTimer;
use libconfig::Config;
use log::{info, warn};
#[cfg(feature = "target_ebaz4205")]
use {libboard_zynq::slcr, libregister::RegisterRW};
#[derive(Debug, PartialEq, Copy, Clone)]
#[allow(non_camel_case_types)]
@ -69,7 +73,7 @@ fn get_rtio_clock_cfg(cfg: &Config) -> RtioClock {
res
}
#[cfg(not(has_drtio))]
#[cfg(not(any(has_drtio, feature = "target_ebaz4205")))]
fn init_rtio(timer: &mut GlobalTimer) {
info!("Switching SYS clocks...");
unsafe {
@ -406,11 +410,43 @@ fn get_si549_setting(clk: RtioClock) -> si549::FrequencySetting {
}
}
#[cfg(feature = "target_ebaz4205")]
fn set_fclk0_freq(clk: RtioClock, cfg: &Config) {
let io_pll_freq: u32 = 1_000_000_000; // Hardcoded in zynq-rs
let mut target_freq = 0;
let mut divisor0 = 1u8;
match clk {
RtioClock::Int_100 => {
target_freq = 100_000_000;
divisor0 = 10;
}
RtioClock::Int_125 => {
target_freq = 125_000_000;
divisor0 = 8;
}
_ => {
warn!("Unsupported RTIO Clock: '{:?}'", clk);
return;
}
}
slcr::RegisterBlock::unlocked(|slcr| {
slcr.fpga0_clk_ctrl.modify(|_, w| w.divisor0(divisor0));
});
info!(
"Set FCLK0 to {:.2} MHz (target: {} MHz).",
io_pll_freq as f64 / divisor0 as f64,
target_freq / 1_000_000
);
}
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 i2c = i2c::get_bus();
match clk {
RtioClock::Ext0_Bypass => {
info!("bypassing the PLL for RTIO clock");
@ -429,9 +465,19 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
#[cfg(has_drtio)]
init_drtio(timer);
#[cfg(not(has_drtio))]
#[cfg(not(any(has_drtio, feature = "target_ebaz4205")))]
init_rtio(timer);
#[cfg(feature = "target_ebaz4205")]
{
match clk {
RtioClock::Int_100 | RtioClock::Int_125 => {
set_fclk0_freq(clk, cfg);
}
_ => {} // Not set for external clocks
}
}
#[cfg(all(has_si549, has_wrpll))]
{
// SYS CLK switch will reset CSRs that are used by WRPLL

View File

@ -144,7 +144,7 @@ pub mod remote_dma {
pub async fn playback_done(&mut self, source: u8, error: u8, channel: u32, timestamp: u64) {
let mut traces_locked = self.traces.async_lock().await;
let mut trace = traces_locked.get_mut(&source).unwrap();
let trace = traces_locked.get_mut(&source).unwrap();
trace.state = RemoteState::PlaybackEnded {
error: error,
channel: channel,

View File

@ -3,7 +3,9 @@ use core::cell::RefCell;
use libboard_artiq::{drtio_routing, drtio_routing::RoutingTable, pl::csr};
use libboard_zynq::timer::GlobalTimer;
use libconfig::Config;
use libcortex_a9::mutex::Mutex;
use log::{info, warn};
#[cfg(has_drtio)]
pub mod drtio {
@ -11,9 +13,13 @@ pub mod drtio {
use core::fmt;
use embedded_hal::blocking::delay::DelayMs;
use ksupport::{resolve_channel_name, ASYNC_ERROR_BUSY, ASYNC_ERROR_COLLISION, ASYNC_ERROR_SEQUENCE_ERROR,
SEEN_ASYNC_ERRORS};
#[cfg(has_drtio_eem)]
use embedded_hal::blocking::delay::DelayUs;
use ksupport::{kernel::Message as KernelMessage, resolve_channel_name, ASYNC_ERROR_BUSY, ASYNC_ERROR_COLLISION,
ASYNC_ERROR_SEQUENCE_ERROR, SEEN_ASYNC_ERRORS};
use libasync::{delay, task};
#[cfg(has_drtio_eem)]
use libboard_artiq::drtio_eem;
use libboard_artiq::{drtioaux::Error as DrtioError,
drtioaux_async,
drtioaux_async::Packet,
@ -24,6 +30,10 @@ pub mod drtio {
use super::*;
use crate::{analyzer::remote_analyzer::RemoteBuffer, rtio_dma::remote_dma, subkernel};
#[cfg(has_drtio_eem)]
const DRTIO_EEM_LINKNOS: core::ops::Range<usize> =
(csr::DRTIO.len() - csr::CONFIG_EEM_DRTIO_COUNT as usize)..csr::DRTIO.len();
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Error {
Timeout,
@ -74,8 +84,18 @@ pub mod drtio {
});
}
async fn link_rx_up(linkno: u8) -> bool {
async fn link_rx_up(linkno: u8, _timer: &mut GlobalTimer) -> bool {
let linkno = linkno as usize;
#[cfg(has_drtio_eem)]
if DRTIO_EEM_LINKNOS.contains(&linkno) {
let eem_trx_no = linkno - DRTIO_EEM_LINKNOS.start;
unsafe {
csr::eem_transceiver::transceiver_sel_write(eem_trx_no as u8);
csr::eem_transceiver::comma_align_reset_write(1);
}
_timer.delay_us(100);
return unsafe { csr::eem_transceiver::comma_read() == 1 };
}
unsafe { (csr::DRTIO[linkno].rx_up_read)() == 1 }
}
@ -127,6 +147,8 @@ pub mod drtio {
| Packet::SubkernelLoadRunReply { destination, .. }
| Packet::SubkernelMessage { destination, .. }
| Packet::SubkernelMessageAck { destination, .. }
| Packet::SubkernelException { destination, .. }
| Packet::SubkernelExceptionRequest { destination, .. }
| Packet::DmaPlaybackStatus { destination, .. }
| Packet::SubkernelFinished { destination, .. } => {
if destination == 0 {
@ -148,8 +170,8 @@ pub mod drtio {
}
}
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, Error> {
if !link_rx_up(linkno).await {
async fn recv_aux_timeout(linkno: u8, timeout: u64, mut timer: GlobalTimer) -> Result<Packet, Error> {
if !link_rx_up(linkno, &mut timer).await {
return Err(Error::LinkDown);
}
match drtioaux_async::recv_timeout(linkno, Some(timeout), timer).await {
@ -164,9 +186,9 @@ pub mod drtio {
linkno: u8,
routing_table: &RoutingTable,
request: &Packet,
timer: GlobalTimer,
mut timer: GlobalTimer,
) -> Result<Packet, Error> {
if !link_rx_up(linkno).await {
if !link_rx_up(linkno, &mut timer).await {
return Err(Error::LinkDown);
}
let _lock = aux_mutex.async_lock().await;
@ -181,10 +203,7 @@ pub mod drtio {
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;
}
while timer.get_time() < max_time {
let _ = drtioaux_async::recv(linkno).await;
}
}
@ -193,11 +212,11 @@ pub mod drtio {
aux_mutex: &Rc<Mutex<bool>>,
linkno: u8,
routing_table: &RoutingTable,
timer: GlobalTimer,
mut timer: GlobalTimer,
) -> u32 {
let mut count = 0;
loop {
if !link_rx_up(linkno).await {
if !link_rx_up(linkno, &mut timer).await {
return 0;
}
count += 1;
@ -392,29 +411,32 @@ pub mod drtio {
}
Ok(Packet::DestinationOkReply) => (),
Ok(Packet::DestinationSequenceErrorReply { channel }) => {
let global_ch = ((destination as u32) << 16) | channel as u32;
error!(
"[DEST#{}] RTIO sequence error involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
resolve_channel_name(global_ch)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
}
Ok(Packet::DestinationCollisionReply { channel }) => {
let global_ch = ((destination as u32) << 16) | channel as u32;
error!(
"[DEST#{}] RTIO collision involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
resolve_channel_name(global_ch)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
}
Ok(Packet::DestinationBusyReply { channel }) => {
let global_ch = ((destination as u32) << 16) | channel as u32;
error!(
"[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}",
destination,
channel,
resolve_channel_name(channel as u32)
resolve_channel_name(global_ch)
);
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
}
@ -461,7 +483,7 @@ pub mod drtio {
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
timer: GlobalTimer,
mut timer: GlobalTimer,
) {
let mut up_links = [false; csr::DRTIO.len()];
loop {
@ -469,16 +491,35 @@ pub mod drtio {
let linkno = linkno as u8;
if up_links[linkno as usize] {
/* link was previously up */
if link_rx_up(linkno).await {
if link_rx_up(linkno, &mut timer).await {
process_unsolicited_aux(aux_mutex, linkno, routing_table).await;
process_local_errors(linkno).await;
} else {
info!("[LINK#{}] link is down", linkno);
up_links[linkno as usize] = false;
#[cfg(has_drtio_eem)]
if DRTIO_EEM_LINKNOS.contains(&(linkno as usize)) {
unsafe {
csr::eem_transceiver::rx_ready_write(0);
}
while !matches!(drtioaux_async::recv(linkno).await, Ok(None)) {}
}
}
} else {
/* link was previously down */
if link_rx_up(linkno).await {
#[cfg(has_drtio_eem)]
if DRTIO_EEM_LINKNOS.contains(&(linkno as usize)) {
let eem_trx_no = linkno - DRTIO_EEM_LINKNOS.start as u8;
if !unsafe { drtio_eem::align_wordslip(&mut timer, eem_trx_no) } {
continue;
}
unsafe {
csr::eem_transceiver::rx_ready_write(1);
}
}
if link_rx_up(linkno, &mut timer).await {
info!("[LINK#{}] link RX became up, pinging", linkno);
let ping_count = ping_remote(aux_mutex, linkno, routing_table, timer).await;
if ping_count > 0 {
@ -506,8 +547,7 @@ pub mod drtio {
}
}
#[allow(dead_code)]
pub fn reset(aux_mutex: Rc<Mutex<bool>>, routing_table: &RoutingTable, mut timer: GlobalTimer) {
pub async fn reset(aux_mutex: &Rc<Mutex<bool>>, routing_table: &RoutingTable, mut timer: GlobalTimer) {
for linkno in 0..csr::DRTIO.len() {
unsafe {
(csr::DRTIO[linkno].reset_write)(1);
@ -522,14 +562,8 @@ pub mod drtio {
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,
routing_table,
&Packet::ResetRequest,
timer,
));
if link_rx_up(linkno, &mut timer).await {
let reply = aux_transact(&aux_mutex, linkno, routing_table, &Packet::ResetRequest, timer).await;
match reply {
Ok(Packet::ResetAck) => (),
Ok(_) => error!("[LINK#{}] reset failed, received unexpected aux packet", linkno),
@ -539,7 +573,7 @@ pub mod drtio {
}
}
async fn partition_data<PacketF, HandlerF>(
pub async fn partition_data<PacketF, HandlerF>(
linkno: u8,
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
@ -791,6 +825,7 @@ pub mod drtio {
id: u32,
destination: u8,
run: bool,
timestamp: u64,
) -> Result<(), Error> {
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(
@ -802,6 +837,7 @@ pub mod drtio {
source: 0,
destination: destination,
run: run,
timestamp,
},
timer,
)
@ -833,13 +869,19 @@ pub mod drtio {
linkno,
routing_table,
&Packet::SubkernelExceptionRequest {
source: 0,
destination: destination,
},
timer,
)
.await?;
match reply {
Packet::SubkernelException { last, length, data } => {
Packet::SubkernelException {
destination: 0,
last,
length,
data,
} => {
remote_data.extend(&data[0..length as usize]);
if last {
return Ok(remote_data);
@ -880,6 +922,91 @@ pub mod drtio {
)
.await
}
pub async fn i2c_send_basic(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
request: &KernelMessage,
busno: u32,
) -> Result<bool, Error> {
let destination = (busno >> 16) as u8;
let busno = busno as u8;
let packet = match request {
KernelMessage::I2cStartRequest(_) => Packet::I2cStartRequest { destination, busno },
KernelMessage::I2cRestartRequest(_) => Packet::I2cRestartRequest { destination, busno },
KernelMessage::I2cStopRequest(_) => Packet::I2cStopRequest { destination, busno },
KernelMessage::I2cSwitchSelectRequest { address, mask, .. } => Packet::I2cSwitchSelectRequest {
destination,
busno,
address: *address,
mask: *mask,
},
_ => unreachable!(),
};
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(aux_mutex, linkno, routing_table, &packet, timer).await?;
match reply {
Packet::I2cBasicReply { succeeded } => Ok(succeeded),
_ => Err(Error::UnexpectedReply),
}
}
pub async fn i2c_send_write(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
busno: u32,
data: u8,
) -> Result<(bool, bool), Error> {
let destination = (busno >> 16) as u8;
let busno = busno as u8;
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(
aux_mutex,
linkno,
routing_table,
&Packet::I2cWriteRequest {
destination,
busno,
data,
},
timer,
)
.await?;
match reply {
Packet::I2cWriteReply { succeeded, ack } => Ok((succeeded, ack)),
_ => Err(Error::UnexpectedReply),
}
}
pub async fn i2c_send_read(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &RoutingTable,
timer: GlobalTimer,
busno: u32,
ack: bool,
) -> Result<(bool, u8), Error> {
let destination = (busno >> 16) as u8;
let busno = busno as u8;
let linkno = routing_table.0[destination as usize][0] - 1;
let reply = aux_transact(
aux_mutex,
linkno,
routing_table,
&Packet::I2cReadRequest {
destination,
busno,
ack,
},
timer,
)
.await?;
match reply {
Packet::I2cReadReply { succeeded, data } => Ok((succeeded, data)),
_ => Err(Error::UnexpectedReply),
}
}
}
#[cfg(not(has_drtio))]
@ -898,22 +1025,38 @@ pub mod drtio {
pub fn reset(_aux_mutex: Rc<Mutex<bool>>, _routing_table: &RoutingTable, mut _timer: GlobalTimer) {}
}
fn toggle_sed_spread(val: u8) {
unsafe {
csr::rtio_core::sed_spread_enable_write(val);
}
}
fn setup_sed_spread(cfg: &Config) {
if let Ok(spread_enable) = cfg.read_str("sed_spread_enable") {
match spread_enable.as_ref() {
"1" => toggle_sed_spread(1),
"0" => toggle_sed_spread(0),
_ => {
warn!("sed_spread_enable value not supported (only 1, 0 allowed), disabling by default");
toggle_sed_spread(0)
}
};
} else {
info!("SED spreading disabled by default");
toggle_sed_spread(0);
}
}
pub fn startup(
aux_mutex: &Rc<Mutex<bool>>,
routing_table: &Rc<RefCell<RoutingTable>>,
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
cfg: &Config,
timer: GlobalTimer,
) {
setup_sed_spread(cfg);
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>>, routing_table: &RoutingTable, timer: GlobalTimer) {
unsafe {
csr::rtio_core::reset_write(1);
}
drtio::reset(aux_mutex, routing_table, timer)
}

View File

@ -40,6 +40,7 @@ impl From<DrtioError> for Error {
}
}
#[allow(dead_code)]
pub struct SubkernelFinished {
pub id: u32,
pub status: FinishStatus,
@ -100,12 +101,22 @@ pub async fn load(
timer: GlobalTimer,
id: u32,
run: bool,
timestamp: u64,
) -> Result<(), Error> {
if let Some(subkernel) = SUBKERNELS.async_lock().await.get_mut(&id) {
if subkernel.state != SubkernelState::Uploaded {
return Err(Error::IncorrectState);
}
drtio::subkernel_load(aux_mutex, routing_table, timer, id, subkernel.destination, run).await?;
drtio::subkernel_load(
aux_mutex,
routing_table,
timer,
id,
subkernel.destination,
run,
timestamp,
)
.await?;
if run {
subkernel.state = SubkernelState::Running;
}

View File

@ -9,7 +9,6 @@ format_code_in_doc_comments = false
comment_width = 100
normalize_comments = false
normalize_doc_attributes = false
license_template_path = ""
format_strings = true
format_macro_matchers = true
format_macro_bodies = true
@ -19,7 +18,7 @@ fn_single_line = false
where_single_line = true
imports_indent = "Visual"
imports_layout = "Mixed"
merge_imports = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
reorder_imports = true
reorder_modules = true
@ -37,7 +36,7 @@ enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
fn_args_layout = "Tall"
fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
@ -54,15 +53,13 @@ use_field_init_shorthand = false
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
required_version = "1.4.32"
required_version = "1.7.0"
unstable_features = false
disable_all_formatting = false
skip_children = false
hide_parse_errors = false
error_on_line_overflow = false
error_on_unformatted = false
report_todo = "Never"
report_fixme = "Never"
ignore = []
emit_mode = "Files"
make_backup = false
make_backup = false

View File

@ -15,7 +15,9 @@ build_zynq = { path = "../libbuild_zynq" }
[dependencies]
log = { version = "0.4", default-features = false }
core_io = { version = "0.1", features = ["collections"] }
byteorder = { version = "1.3", default-features = false }
core_io = { git = "https://git.m-labs.hk/M-Labs/rs-core_io.git", rev = "e9d3edf027", features = ["collections"] }
crc = { version = "1.7", default-features = false }
cslice = "0.3"
embedded-hal = "0.2"

File diff suppressed because it is too large Load Diff

152
src/satman/src/mgmt.rs Normal file
View File

@ -0,0 +1,152 @@
use alloc::vec::Vec;
use byteorder::{ByteOrder, NativeEndian};
use core_io::Write;
use crc::crc32;
use io::ProtoRead;
use libboard_artiq::{drtioaux_proto::SAT_PAYLOAD_MAX_SIZE,
logger::{BufferLogger, LogBufferRef}};
use libconfig::Config;
use log::{debug, error, info, warn, LevelFilter};
use crate::routing::{SliceMeta, Sliceable};
type Result<T> = core::result::Result<T, ()>;
pub fn byte_to_level_filter(level_byte: u8) -> Result<LevelFilter> {
Ok(match level_byte {
0 => LevelFilter::Off,
1 => LevelFilter::Error,
2 => LevelFilter::Warn,
3 => LevelFilter::Info,
4 => LevelFilter::Debug,
5 => LevelFilter::Trace,
lv => {
error!("unknown log level: {}", lv);
return Err(());
}
})
}
fn get_logger_buffer() -> LogBufferRef<'static> {
let logger = BufferLogger::get_logger();
loop {
if let Some(buffer_ref) = logger.buffer() {
return buffer_ref;
}
}
}
pub fn clear_log() {
let mut buffer = get_logger_buffer();
buffer.clear();
}
pub struct Manager<'a> {
cfg: &'a mut Config,
last_log: Sliceable,
config_payload: Vec<u8>,
last_value: Sliceable,
image_payload: Vec<u8>,
}
impl<'a> Manager<'_> {
pub fn new(cfg: &mut Config) -> Manager {
Manager {
cfg: cfg,
last_log: Sliceable::new(0, Vec::new()),
config_payload: Vec::new(),
last_value: Sliceable::new(0, Vec::new()),
image_payload: Vec::new(),
}
}
pub fn log_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE], consume: bool) -> SliceMeta {
// Populate buffer if depleted
if self.last_log.at_end() {
let mut buffer = get_logger_buffer();
self.last_log.extend(buffer.extract().as_bytes());
if consume {
buffer.clear();
}
}
self.last_log.get_slice_satellite(data_slice)
}
pub fn fetch_config_value(&mut self, key: &str) -> Result<()> {
self.cfg
.read(&key)
.map(|value| {
debug!("got value");
self.last_value = Sliceable::new(0, value)
})
.map_err(|_| warn!("read error: no such key"))
}
pub fn get_config_value_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
self.last_value.get_slice_satellite(data_slice)
}
pub fn add_config_data(&mut self, data: &[u8], data_len: usize) {
self.config_payload.write_all(&data[..data_len]).unwrap();
}
pub fn clear_config_data(&mut self) {
self.config_payload.clear();
}
pub fn write_config(&mut self) -> Result<()> {
let mut payload = &self.config_payload[..];
let key = payload
.read_string::<NativeEndian>()
.map_err(|_err| error!("error on reading key"))?;
debug!("write key: {}", key);
let value = payload.read_bytes::<NativeEndian>().unwrap();
self.cfg
.write(&key, value)
.map(|()| debug!("write success"))
.map_err(|err| error!("failed to write: {:?}", err))
}
pub fn remove_config(&mut self, key: &str) -> Result<()> {
debug!("erase key: {}", key);
self.cfg
.remove(&key)
.map(|()| debug!("erase success"))
.map_err(|err| warn!("failed to erase: {:?}", err))
}
pub fn allocate_image_buffer(&mut self, image_size: usize) {
self.image_payload = Vec::with_capacity(image_size);
}
pub fn add_image_data(&mut self, data: &[u8], data_len: usize) {
self.image_payload.extend(&data[..data_len]);
}
pub fn write_image(&self) {
let mut image = self.image_payload.clone();
let image_ref = &image[..];
let bin_len = image.len() - 4;
let (image_ref, expected_crc) = {
let (image_ref, crc_slice) = image_ref.split_at(bin_len);
(image_ref, NativeEndian::read_u32(crc_slice))
};
let actual_crc = crc32::checksum_ieee(image_ref);
if actual_crc == expected_crc {
info!("CRC passed. Writing boot image to SD card...");
image.truncate(bin_len);
self.cfg.write("boot", image).expect("failed to write boot image");
} else {
panic!(
"CRC failed, images have not been written to flash.\n(actual {:08x}, expected {:08x})",
actual_crc, expected_crc
);
}
}
}

View File

@ -87,6 +87,10 @@ impl Repeater {
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);
let max_time = timer.get_time() + Milliseconds(200);
while timer.get_time() < max_time {
let _ = drtioaux::recv(self.auxno);
}
self.state = RepeaterState::Up;
if let Err(e) = self.sync_tsc(timer) {
error!("[REP#{}] failed to sync TSC ({:?})", self.repno, e);
@ -204,10 +208,37 @@ impl Repeater {
}
}
pub fn aux_forward(&self, request: &drtioaux::Packet, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
pub fn aux_forward(
&self,
request: &drtioaux::Packet,
router: &mut Router,
routing_table: &drtio_routing::RoutingTable,
rank: u8,
self_destination: u8,
timer: &mut GlobalTimer,
) -> Result<(), drtioaux::Error> {
self.aux_send(request)?;
let reply = self.recv_aux_timeout(200, timer)?;
drtioaux::send(0, &reply).unwrap();
loop {
let reply = self.recv_aux_timeout(200, timer)?;
match reply {
// async/locally requested packets to be consumed or routed
// these may come while a packet would be forwarded
drtioaux::Packet::DmaPlaybackStatus { .. }
| drtioaux::Packet::SubkernelFinished { .. }
| drtioaux::Packet::SubkernelMessage { .. }
| drtioaux::Packet::SubkernelMessageAck { .. }
| drtioaux::Packet::SubkernelLoadRunReply { .. }
| drtioaux::Packet::SubkernelException { .. }
| drtioaux::Packet::DmaAddTraceReply { .. }
| drtioaux::Packet::DmaPlaybackReply { .. } => {
router.route(reply, routing_table, rank, self_destination);
}
_ => {
drtioaux::send(0, &reply).unwrap();
break;
}
}
}
Ok(())
}

View File

@ -57,8 +57,8 @@ impl Sliceable {
self.data.extend(data);
}
get_slice_fn!(get_slice_sat, SAT_PAYLOAD_MAX_SIZE);
get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE);
get_slice_fn!(get_slice_satellite, SAT_PAYLOAD_MAX_SIZE);
}
// Packets from downstream (further satellites) are received and routed appropriately.

View File

@ -2,16 +2,17 @@ use alloc::{collections::BTreeMap,
format,
string::{String, ToString},
vec::Vec};
use core::{option::NoneError, slice, str};
use core::{slice, str};
use core_io::{Error as IoError, Write};
use byteorder::NativeEndian;
use core_io::Error as IoError;
use cslice::AsCSlice;
use dma::{Error as DmaError, Manager as DmaManager};
use io::{Cursor, ProtoWrite};
use ksupport::{eh_artiq, kernel, rpc};
use ksupport::{eh_artiq, kernel, kernel::rtio, rpc};
use libboard_artiq::{drtio_routing::RoutingTable,
drtioaux,
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE},
drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE},
pl::csr};
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
use libcortex_a9::sync_channel::Receiver;
@ -47,8 +48,12 @@ enum KernelState {
DmaAwait {
max_time: Milliseconds,
},
SubkernelRetrievingException {
destination: u8,
},
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error {
Load(String),
@ -62,12 +67,6 @@ pub enum Error {
DmaError(DmaError),
}
impl From<NoneError> for Error {
fn from(_: NoneError) -> Error {
Error::KernelNotFound
}
}
impl From<IoError> for Error {
fn from(_value: IoError) -> Error {
Error::SubkernelIoError
@ -123,10 +122,11 @@ struct MessageManager {
struct Session {
id: u32,
kernel_state: KernelState,
last_exception: Option<Sliceable>,
last_exception: Option<Sliceable>, // exceptions raised locally
external_exception: Option<Vec<u8>>, // exceptions from sub-subkernels
messages: MessageManager,
source: u8, // which destination requested running the kernel
subkernels_finished: Vec<u32>,
subkernels_finished: Vec<(u32, Option<u8>)>,
}
impl Session {
@ -135,6 +135,7 @@ impl Session {
id: id,
kernel_state: KernelState::Absent,
last_exception: None,
external_exception: None,
messages: MessageManager::new(),
source: 0,
subkernels_finished: Vec::new(),
@ -311,7 +312,7 @@ impl<'a> Manager<'_> {
complete: false,
},
);
self.kernels.get_mut(&id)?
self.kernels.get_mut(&id).ok_or_else(|| Error::KernelNotFound)?
} else {
kernel
}
@ -324,7 +325,7 @@ impl<'a> Manager<'_> {
complete: false,
},
);
self.kernels.get_mut(&id)?
self.kernels.get_mut(&id).ok_or_else(|| Error::KernelNotFound)?
}
};
kernel.library.extend(&data[0..data_len]);
@ -344,7 +345,7 @@ impl<'a> Manager<'_> {
}
}
pub fn run(&mut self, source: u8, id: u32) -> Result<(), Error> {
pub fn run(&mut self, source: u8, id: u32, timestamp: u64) -> Result<(), Error> {
if self.session.kernel_state != KernelState::Loaded || self.session.id != id {
self.load(id)?;
}
@ -354,6 +355,7 @@ impl<'a> Manager<'_> {
csr::cri_con::selected_write(2);
}
rtio::at_mu(timestamp as i64);
self.control.tx.send(kernel::Message::StartRequest);
Ok(())
}
@ -390,15 +392,19 @@ impl<'a> Manager<'_> {
if self.session.id == id && self.session.kernel_state == KernelState::Loaded {
return Ok(());
}
if !self.kernels.get(&id)?.complete {
if !self.kernels.get(&id).ok_or_else(|| Error::KernelNotFound)?.complete {
return Err(Error::KernelNotFound);
}
self.session = Session::new(id);
self.control.restart();
self.control
.tx
.send(kernel::Message::LoadRequest(self.kernels.get(&id)?.library.clone()));
self.control.tx.send(kernel::Message::LoadRequest(
self.kernels
.get(&id)
.ok_or_else(|| Error::KernelNotFound)?
.library
.clone(),
));
let reply = self.control.rx.recv();
match reply {
kernel::Message::LoadCompleted => Ok(()),
@ -410,9 +416,9 @@ impl<'a> Manager<'_> {
}
}
pub fn exception_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
pub fn exception_get_slice(&mut self, data_slice: &mut [u8; MASTER_PAYLOAD_MAX_SIZE]) -> SliceMeta {
match self.session.last_exception.as_mut() {
Some(exception) => exception.get_slice_sat(data_slice),
Some(exception) => exception.get_slice_master(data_slice),
None => SliceMeta {
destination: 0,
len: 0,
@ -540,7 +546,7 @@ impl<'a> Manager<'_> {
return;
}
match self.process_external_messages(timer) {
match self.process_external_messages(router, routing_table, rank, destination, timer) {
Ok(()) => (),
Err(Error::AwaitingMessage) => return, // kernel still waiting, do not process kernel messages
Err(Error::KernelException(exception)) => {
@ -596,6 +602,41 @@ impl<'a> Manager<'_> {
}
}
fn check_finished_kernels(
&mut self,
id: u32,
router: &mut Router,
routing_table: &RoutingTable,
rank: u8,
self_destination: u8,
) {
for (i, (status, exception_source)) in self.session.subkernels_finished.iter().enumerate() {
if *status == id {
if exception_source.is_none() {
self.control.tx.send(kernel::Message::SubkernelAwaitFinishReply);
self.session.kernel_state = KernelState::Running;
self.session.subkernels_finished.swap_remove(i);
} else {
let destination = exception_source.unwrap();
self.session.external_exception = Some(Vec::new());
self.session.kernel_state = KernelState::SubkernelRetrievingException {
destination: destination,
};
router.route(
drtioaux::Packet::SubkernelExceptionRequest {
source: self_destination,
destination: destination,
},
&routing_table,
rank,
self_destination,
);
}
break;
}
}
}
pub fn subkernel_load_run_reply(&mut self, succeeded: bool) {
if self.session.kernel_state == KernelState::SubkernelAwaitLoad {
self.control
@ -608,16 +649,46 @@ impl<'a> Manager<'_> {
}
pub fn remote_subkernel_finished(&mut self, id: u32, with_exception: bool, exception_source: u8) {
if with_exception {
self.kernel_stop();
self.last_finished = Some(SubkernelFinished {
source: self.session.source,
id: self.session.id,
with_exception: true,
exception_source: exception_source,
})
let exception_src = if with_exception { Some(exception_source) } else { None };
self.session.subkernels_finished.push((id, exception_src));
}
pub fn received_exception(
&mut self,
exception_data: &[u8],
last: bool,
router: &mut Router,
routing_table: &RoutingTable,
rank: u8,
self_destination: u8,
) {
if let KernelState::SubkernelRetrievingException { destination } = self.session.kernel_state {
self.session
.external_exception
.as_mut()
.unwrap()
.extend_from_slice(exception_data);
if last {
self.control
.tx
.send(kernel::Message::SubkernelError(kernel::SubkernelStatus::Exception(
self.session.external_exception.take().unwrap(),
)));
self.session.kernel_state = KernelState::Running;
} else {
/* fetch another slice */
router.route(
drtioaux::Packet::SubkernelExceptionRequest {
source: self_destination,
destination: destination,
},
routing_table,
rank,
self_destination,
);
}
} else {
self.session.subkernels_finished.push(id);
warn!("Received unsolicited exception data");
}
}
@ -742,6 +813,7 @@ impl<'a> Manager<'_> {
id,
destination: sk_destination,
run,
timestamp,
} => {
self.session.kernel_state = KernelState::SubkernelAwaitLoad;
router.route(
@ -750,6 +822,7 @@ impl<'a> Manager<'_> {
destination: sk_destination,
id: id,
run: run,
timestamp,
},
routing_table,
rank,
@ -773,6 +846,8 @@ impl<'a> Manager<'_> {
destination == (self_destination as i32),
));
}
/* core.reset() on satellites only affects the satellite, ignore the request */
kernel::Message::RtioInitRequest => {}
_ => {
unexpected!("unexpected message from core1 while kernel was running: {:?}", reply);
}
@ -780,28 +855,35 @@ impl<'a> Manager<'_> {
Ok(false)
}
fn process_external_messages(&mut self, timer: &GlobalTimer) -> Result<(), Error> {
fn process_external_messages(
&mut self,
router: &mut Router,
routing_table: &RoutingTable,
rank: u8,
self_destination: u8,
timer: &GlobalTimer,
) -> Result<(), Error> {
match &self.session.kernel_state {
KernelState::MsgAwait { max_time, id, tags } => {
if let Some(max_time) = *max_time {
if timer.get_time() > max_time {
self.control.tx.send(kernel::Message::SubkernelMsgRecvReply {
status: kernel::SubkernelStatus::Timeout,
count: 0,
});
self.control
.tx
.send(kernel::Message::SubkernelError(kernel::SubkernelStatus::Timeout));
self.session.kernel_state = KernelState::Running;
return Ok(());
}
}
if let Some(message) = self.session.messages.get_incoming(*id) {
self.control.tx.send(kernel::Message::SubkernelMsgRecvReply {
status: kernel::SubkernelStatus::NoError,
count: message.count,
});
self.control
.tx
.send(kernel::Message::SubkernelMsgRecvReply { count: message.count });
let tags = tags.clone();
self.session.kernel_state = KernelState::Running;
self.pass_message_to_kernel(&message, tags, timer)
} else {
let id = *id;
self.check_finished_kernels(id, router, routing_table, rank, self_destination);
Err(Error::AwaitingMessage)
}
}
@ -817,27 +899,18 @@ impl<'a> Manager<'_> {
KernelState::SubkernelAwaitFinish { max_time, id } => {
if let Some(max_time) = *max_time {
if timer.get_time() > max_time {
self.control.tx.send(kernel::Message::SubkernelAwaitFinishReply {
status: kernel::SubkernelStatus::Timeout,
});
self.control
.tx
.send(kernel::Message::SubkernelError(kernel::SubkernelStatus::Timeout));
self.session.kernel_state = KernelState::Running;
return Ok(());
}
}
let mut i = 0;
for status in &self.session.subkernels_finished {
if *status == *id {
self.control.tx.send(kernel::Message::SubkernelAwaitFinishReply {
status: kernel::SubkernelStatus::NoError,
});
self.session.kernel_state = KernelState::Running;
self.session.subkernels_finished.swap_remove(i);
break;
}
i += 1;
}
let id = *id;
self.check_finished_kernels(id, router, routing_table, rank, self_destination);
Ok(())
}
KernelState::SubkernelRetrievingException { .. } => Err(Error::AwaitingMessage),
KernelState::DmaAwait { max_time } | KernelState::DmaPendingAwait { max_time, .. } => {
if timer.get_time() > *max_time {
self.control.tx.send(kernel::Message::DmaAwaitRemoteReply {
@ -911,27 +984,24 @@ impl<'a> Manager<'_> {
}
}
fn write_exception<W>(
fn write_exception<W: ProtoWrite>(
writer: &mut W,
exceptions: &[Option<eh_artiq::Exception>],
stack_pointers: &[eh_artiq::StackPointerBacktrace],
backtrace: &[(usize, usize)],
async_errors: u8,
) -> Result<(), Error>
where
W: Write + ?Sized,
{
) -> Result<(), Error> {
/* header */
writer.write_bytes(&[0x5a, 0x5a, 0x5a, 0x5a, /*Reply::KernelException*/ 9])?;
writer.write_u32(exceptions.len() as u32)?;
writer.write_bytes::<NativeEndian>(&[0x5a, 0x5a, 0x5a, 0x5a, /*Reply::KernelException*/ 9])?;
writer.write_u32::<NativeEndian>(exceptions.len() as u32)?;
for exception in exceptions.iter() {
let exception = exception.as_ref().unwrap();
writer.write_u32(exception.id)?;
writer.write_u32::<NativeEndian>(exception.id)?;
if exception.message.len() == usize::MAX {
// exception with host string
writer.write_u32(u32::MAX)?;
writer.write_u32(exception.message.as_ptr() as u32)?;
writer.write_u32::<NativeEndian>(u32::MAX)?;
writer.write_u32::<NativeEndian>(exception.message.as_ptr() as u32)?;
} else {
let msg =
str::from_utf8(unsafe { slice::from_raw_parts(exception.message.as_ptr(), exception.message.len()) })
@ -944,26 +1014,26 @@ where
ksupport::resolve_channel_name(exception.param[0] as u32)
),
);
writer.write_string(&msg)?;
writer.write_string::<NativeEndian>(&msg)?;
}
writer.write_u64(exception.param[0] as u64)?;
writer.write_u64(exception.param[1] as u64)?;
writer.write_u64(exception.param[2] as u64)?;
writer.write_bytes(exception.file.as_ref())?;
writer.write_u32(exception.line)?;
writer.write_u32(exception.column)?;
writer.write_bytes(exception.function.as_ref())?;
writer.write_u64::<NativeEndian>(exception.param[0] as u64)?;
writer.write_u64::<NativeEndian>(exception.param[1] as u64)?;
writer.write_u64::<NativeEndian>(exception.param[2] as u64)?;
writer.write_bytes::<NativeEndian>(exception.file.as_ref())?;
writer.write_u32::<NativeEndian>(exception.line)?;
writer.write_u32::<NativeEndian>(exception.column)?;
writer.write_bytes::<NativeEndian>(exception.function.as_ref())?;
}
for sp in stack_pointers.iter() {
writer.write_u32(sp.stack_pointer as u32)?;
writer.write_u32(sp.initial_backtrace_size as u32)?;
writer.write_u32(sp.current_backtrace_size as u32)?;
writer.write_u32::<NativeEndian>(sp.stack_pointer as u32)?;
writer.write_u32::<NativeEndian>(sp.initial_backtrace_size as u32)?;
writer.write_u32::<NativeEndian>(sp.current_backtrace_size as u32)?;
}
writer.write_u32(backtrace.len() as u32)?;
writer.write_u32::<NativeEndian>(backtrace.len() as u32)?;
for &(addr, sp) in backtrace {
writer.write_u32(addr as u32)?;
writer.write_u32(sp as u32)?;
writer.write_u32::<NativeEndian>(addr as u32)?;
writer.write_u32::<NativeEndian>(sp as u32)?;
}
writer.write_u8(async_errors as u8)?;
Ok(())