Compare commits

...

34 Commits

Author SHA1 Message Date
Egor Savkin 9b76896edd Add grabber module
(cherry picked from commit b768d5648c)
Signed-off-by: Egor Savkin <es@m-labs.hk>
2023-10-18 15:01:47 +08:00
morgan 00c5ee01b0 feature: add SFP0..3 LED indication
io_expander.rs: add virtual_led_mapping in IoExpander
                set SFP LED at .service()
kasli_soc.py: add virtual_leds csr device
              couple virtual_leds.get(i) with rx_ready status
runtime main: add async task io_expanders_service()
satman main: add io_expander service after link up/down
2023-09-04 16:08:36 +08:00
morgan b47152f2c2 Fix I2C I/O expander settings for VUSB_PRESENT_N on Kasli-SoC 1.1
Co-authored-by: morgan <mc@m-labs.hk>
Co-committed-by: morgan <mc@m-labs.hk>
2023-09-04 15:50:45 +08:00
morgan 5597927df9 change io_expander not to borrow i2c indefinitely 2023-08-31 14:58:00 +08:00
morgan d019021ff5 change write_rustc_cfg_file to follow artiq repo 2023-08-31 11:54:47 +08:00
morgan 3d43bc8b15 fix compilation error when cfg has has_rtio_moninj 2023-08-31 10:26:55 +08:00
Sebastien Bourdeauducq c4d5ab96cc flake: update dependencies 2023-07-27 11:07:15 +08:00
Sebastien Bourdeauducq a89a7dc495 flake: update dependencies 2023-07-13 16:55:38 +08:00
Sebastien Bourdeauducq 832a861477 flake: update dependencies 2023-06-02 17:22:05 +08:00
Sebastien Bourdeauducq 201953f96b Revert "flake: update dependencies"
This reverts commit 7aa6ddeaf2.
2023-06-01 11:30:27 +08:00
Sebastien Bourdeauducq 7aa6ddeaf2 flake: update dependencies 2023-05-30 12:15:24 +08:00
Sebastien Bourdeauducq b0fde30e8f Revert "flake: update dependencies"
This reverts commit 66d80bf51a.
2023-05-30 12:12:05 +08:00
Sebastien Bourdeauducq 66d80bf51a flake: update dependencies 2023-05-30 12:10:57 +08:00
Sebastien Bourdeauducq 6f867098b0 flake: update dependencies 2023-05-23 11:27:51 +08:00
spaqin d51f86672a fix analyzer target for masters 2023-05-22 15:32:49 +08:00
Sebastien Bourdeauducq 6b9212525a README: update copyright year 2023-05-09 16:28:59 +08:00
Sebastien Bourdeauducq 201470485c README: update use instructions 2023-05-09 16:28:50 +08:00
Sebastien Bourdeauducq b7c49be238 flake: update dependencies 2023-05-01 09:35:58 +08:00
Jonathan Coates da0eedaa76 Fix mismatched signatures for the wide interface 2023-04-17 10:16:33 +08:00
mwojcik a44a21436c qc2: add 4 edge counters to the end of rtio 2023-04-03 12:33:28 +08:00
Sebastien Bourdeauducq e946bcc3ed kasli_soc: add fix_serdes_timing_path 2023-02-20 17:44:15 +08:00
Sebastien Bourdeauducq 4f49c58d3b flake: update dependencies 2023-02-20 17:44:07 +08:00
Egor Savkin 2a71b2fc62 satman: drive SFP TX_DISABLE 2023-02-19 16:47:24 +08:00
David Nadlinger 2ad1970004 rpc: Port over size/alignment fix for structs (tuples) with tail padding
This ports over the following commits from the main ARTIQ repo:
 - 8740ec3dd52d85084237797881ea137492bfe070
 - dbbe8e8ed4f852e623775b7bd3aec818cdd03376
 - b9f13d48aa7e2c0652210152b971b21c3c419347
2023-02-17 17:31:35 +08:00
Sebastien Bourdeauducq d6bcc516cd flake: update dependencies 2023-02-17 16:49:52 +08:00
Egor Savkin 99ebc2fcdb runtime: drive SFP TX_DISABLE
Signed-off-by: Egor Savkin <es@m-labs.hk>
2023-02-17 16:48:51 +08:00
Egor Savkin 709aa8195b Fix idle/startup_kernel typos in config
Signed-off-by: Egor Savkin <es@m-labs.hk>
2023-01-03 11:03:58 +08:00
Sebastien Bourdeauducq 733f270819 flake: update dependencies 2022-10-21 19:00:12 +08:00
Sebastien Bourdeauducq d30fb96674 examples: fix ref_multiplier 2022-10-21 18:59:32 +08:00
mwojcik facc5808ef match ident message with mainline 2022-10-21 12:28:50 +08:00
mwojcik 6f74cff4c5 kasli_soc: ident = variant name 2022-10-21 12:28:50 +08:00
Sebastien Bourdeauducq 3ea2690f15 flake: update dependencies 2022-08-01 10:25:20 +08:00
Sebastien Bourdeauducq e02b8e25fe README: update required versions 2022-07-09 12:28:24 +08:00
Sebastien Bourdeauducq 561efb0466 flake: switch to ARTIQ release 7 2022-07-08 18:20:49 +08:00
15 changed files with 764 additions and 261 deletions

View File

@ -4,14 +4,13 @@ ARTIQ on Zynq
How to use How to use
---------- ----------
1. Install ARTIQ-6 or newer. 1. Install the ARTIQ version that corresponds to the artiq-zynq version you are targeting.
2. Select the latest successful build on Hydra: https://nixbld.m-labs.hk/jobset/artiq/zynq 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. 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. Download the ``boot.bin`` "binary distribution" and place it 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. 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. 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. 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.
8. Create and use an ARTIQ device database as usual, but set ``"target": "cortexa9"`` in the arguments of the core device.
Configuration Configuration
------------- -------------
@ -33,7 +32,7 @@ not implemented as it seems not very useful.
Development instructions Development instructions
------------------------ ------------------------
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``). 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``).
Pure build with Nix and execution on a remote JTAG server: Pure build with Nix and execution on a remote JTAG server:
@ -64,7 +63,7 @@ Notes:
License License
------- -------
Copyright (C) 2019-2022 M-Labs Limited. Copyright (C) 2019-2023 M-Labs Limited.
ARTIQ is free software: you can redistribute it and/or modify ARTIQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by

View File

@ -8,7 +8,7 @@ device_db = {
"arguments": { "arguments": {
"host": "192.168.1.52", "host": "192.168.1.52",
"ref_period": 1e-9, "ref_period": 1e-9,
"ref_multiplier": 1, "ref_multiplier": 8,
"target": "cortexa9" "target": "cortexa9"
} }
}, },
@ -59,6 +59,14 @@ device_db["ad9914dds1"] = {
"arguments": {"sysclk": 3e9, "bus_channel": 50, "channel": 1}, "arguments": {"sysclk": 3e9, "bus_channel": 50, "channel": 1},
} }
for i in range(4):
device_db["ttl"+str(i)+"_counter"] = {
"type": "local",
"module": "artiq.coredevice.edge_counter",
"class": "EdgeCounter",
"arguments": {"channel": 52+i}
}
# for ARTIQ test suite # for ARTIQ test suite
device_db.update( device_db.update(
loop_out="ttl0", loop_out="ttl0",

View File

@ -11,21 +11,23 @@
"src-pythonparser": "src-pythonparser" "src-pythonparser": "src-pythonparser"
}, },
"locked": { "locked": {
"lastModified": 1654087576, "lastModified": 1690427113,
"narHash": "sha256-d+LCvCXikhhEjsf4qBZakx3Sda85p+4vVZVwE1YuS90=", "narHash": "sha256-10XRV7vbdyr/NKDKmBWcVs+9/iYbj6f2bulMRhwy0JY=",
"ref": "master", "ref": "release-7",
"rev": "68ef0073ea66fe9c7f7c178979ecb8c681c47caa", "rev": "21c6f57ce18cdd0c41f467434d58c0b9193da374",
"revCount": 8068, "revCount": 8180,
"type": "git", "type": "git",
"url": "https://github.com/m-labs/artiq.git" "url": "https://github.com/m-labs/artiq.git"
}, },
"original": { "original": {
"ref": "release-7",
"type": "git", "type": "git",
"url": "https://github.com/m-labs/artiq.git" "url": "https://github.com/m-labs/artiq.git"
} }
}, },
"artiq-comtools": { "artiq-comtools": {
"inputs": { "inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [ "nixpkgs": [
"artiq", "artiq",
"nixpkgs" "nixpkgs"
@ -36,11 +38,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1654007592, "lastModified": 1664405593,
"narHash": "sha256-vaDFhE1ItjqtIcinC/6RAJGbj44pxxMUEeQUa3FtgEE=", "narHash": "sha256-yP441NerlLGig7n+9xHsx8yCtZ+Ggd0VqfBSzc20E04=",
"owner": "m-labs", "owner": "m-labs",
"repo": "artiq-comtools", "repo": "artiq-comtools",
"rev": "cb73281154656ee8f74db1866859e31bf42755cd", "rev": "15ddac62813ef623a076ccf982b3bc63d314e651",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -49,14 +51,29 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"mozilla-overlay": { "mozilla-overlay": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650459918, "lastModified": 1687771476,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=", "narHash": "sha256-TSpqz6qYVRoqkEdOCawEQ4/cWt/4pracmvw17HK1tgE=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168", "rev": "3a44b8783514e7d6db4b63df96071b6c2b014b07",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -68,11 +85,11 @@
"mozilla-overlay_2": { "mozilla-overlay_2": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650459918, "lastModified": 1687771476,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=", "narHash": "sha256-TSpqz6qYVRoqkEdOCawEQ4/cWt/4pracmvw17HK1tgE=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168", "rev": "3a44b8783514e7d6db4b63df96071b6c2b014b07",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -84,11 +101,11 @@
"mozilla-overlay_3": { "mozilla-overlay_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650459918, "lastModified": 1684487559,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=", "narHash": "sha256-SZcJEM+NnLr8ctzeQf1BGAqBHzJ3jn+tdSeO7lszIJc=",
"owner": "mozilla", "owner": "mozilla",
"repo": "nixpkgs-mozilla", "repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168", "rev": "e6ca26fe8b9df914d4567604e426fbc185d9ef3e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -99,11 +116,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1653920503, "lastModified": 1685573264,
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=", "narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a", "rev": "380be19fbd2d9079f677978361792cb25e8a3635",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -128,11 +145,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1654006751, "lastModified": 1685540542,
"narHash": "sha256-OWAnoTCutvTQcYdtdtLQuL6uRtG+7Jz7sbRhcScv8bo=", "narHash": "sha256-wQJwL3xc6QVQbiJrt71/Z9tp4Eq1yqdGddcEiv7sPCw=",
"owner": "m-labs", "owner": "m-labs",
"repo": "sipyco", "repo": "sipyco",
"rev": "b3d03a94c751a24769c54a61a0dbe9d6af52dade", "rev": "f5bf2ba875340a31a135aea14ad184575ca800ac",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -144,11 +161,11 @@
"src-migen": { "src-migen": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650337393, "lastModified": 1674045327,
"narHash": "sha256-rm1SlFmF2ASz0vIy2nDEzGlyRw2oYNeJRr8Kh8Mg2Qc=", "narHash": "sha256-oYdeY0MbTReKbAwmSznnqw0wNawdInJoFJVWW3tesFA=",
"owner": "m-labs", "owner": "m-labs",
"repo": "migen", "repo": "migen",
"rev": "d4e3f34177c32f09904397179e6ed9c83175e528", "rev": "ccaee68e14d3636e1d8fb2e0864dd89b1b1f7384",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -160,11 +177,11 @@
"src-misoc": { "src-misoc": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1649324486, "lastModified": 1685415268,
"narHash": "sha256-Mw/fQS3lHFvCm7L1k63joRkz5uyijQfywcOq+X2+o2s=", "narHash": "sha256-g4+yeSV+HtWjcllM5wk4vNBUVCXtDOzUSKhxXPT7Fyc=",
"ref": "master", "ref": "refs/heads/master",
"rev": "f1dc58d2b8c222ba41c25cee4301626625f46e43", "rev": "6d48ce77b6746d3226a682790fbc95b90340986e",
"revCount": 2420, "revCount": 2440,
"submodules": true, "submodules": true,
"type": "git", "type": "git",
"url": "https://github.com/m-labs/misoc.git" "url": "https://github.com/m-labs/misoc.git"
@ -200,11 +217,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1654002148, "lastModified": 1685182853,
"narHash": "sha256-Ztzx7ze8o0gdu+sDpc+PS2M88h/b22rwFq0b12wMqZQ=", "narHash": "sha256-bEgE8Ph7MEHQr7S+xL33CxLuIm3AqvsaxBwUE1vHTdU=",
"ref": "master", "ref": "refs/heads/master",
"rev": "6cd32f6ee0e7bb7ce5a279f8be943903a393a476", "rev": "f20c0082645cfef34575661aeeff8d9c1ed1f127",
"revCount": 617, "revCount": 625,
"type": "git", "type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs" "url": "https://git.m-labs.hk/m-labs/zynq-rs"
}, },

View File

@ -1,7 +1,7 @@
{ {
description = "ARTIQ port to the Zynq-7000 platform"; description = "ARTIQ port to the Zynq-7000 platform";
inputs.artiq.url = git+https://github.com/m-labs/artiq.git; inputs.artiq.url = git+https://github.com/m-labs/artiq.git?ref=release-7;
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; }; 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.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs"; inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";

View File

@ -10,11 +10,13 @@ from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import kasli_soc from migen_axi.platforms import kasli_soc
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from misoc.cores import virtual_leds
from misoc.integration import cpu_interface from misoc.integration import cpu_interface
from artiq.coredevice import jsondesc from artiq.coredevice import jsondesc
from artiq.gateware import rtio, eem_7series from artiq.gateware import rtio, eem_7series
from artiq.gateware.rtio.phy import ttl_simple from artiq.gateware.rtio.phy import ttl_simple
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier
from artiq.gateware.drtio.transceiver import gtx_7series from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.siphaser import SiPhaser7Series
@ -112,24 +114,26 @@ class SMAClkinForward(Module):
class GenericStandalone(SoCCore): class GenericStandalone(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False):
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict() sys_clk_freq = 125e6
platform = kasli_soc.Platform() platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([ platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
]) ])
ident = self.__class__.__name__ ident = description["variant"]
if self.acpki: if self.acpki:
ident = "acpki_" + ident ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident) SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
self.config["hw_rev"] = description["hw_rev"]
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]") platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24") platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.submodules += SMAClkinForward(self.platform) self.submodules += SMAClkinForward(self.platform)
self.rustc_cfg["has_si5324"] = None self.config["HAS_SI5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None self.config["SI5324_SOFT_RESET"] = None
self.crg = self.ps7 # HACK for eem_7series to find the clock self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOCRG(self.platform) self.submodules.rtio_crg = RTIOCRG(self.platform)
@ -138,6 +142,8 @@ class GenericStandalone(SoCCore):
self.platform.add_false_path_constraints( self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk, self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk) self.rtio_crg.cd_rtio.clk)
fix_serdes_timing_path(platform)
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
self.rtio_channels = [] self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"]) has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
@ -158,14 +164,14 @@ class GenericStandalone(SoCCore):
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
if self.acpki: if self.acpki:
self.rustc_cfg["ki_impl"] = "acp" self.config["KI_IMPL"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.rustc_cfg["ki_impl"] = "csr" self.config["KI_IMPL"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -185,7 +191,7 @@ class GenericStandalone(SoCCore):
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
if has_grabber: if has_grabber:
self.rustc_cfg["has_grabber"] = None self.config["HAS_GRABBER"] = None
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
for grabber in self.grabber_csr_group: for grabber in self.grabber_csr_group:
self.platform.add_false_path_constraints( self.platform.add_false_path_constraints(
@ -198,17 +204,18 @@ class GenericMaster(SoCCore):
rtio_clk_freq = description["rtio_frequency"] rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform() platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([ platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
]) ])
ident = self.__class__.__name__ ident = description["variant"]
if self.acpki: if self.acpki:
ident = "acpki_" + ident ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident) SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
self.config["hw_rev"] = description["hw_rev"]
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]") platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24") platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
@ -221,13 +228,15 @@ class GenericMaster(SoCCore):
pads=data_pads, pads=data_pads,
sys_clk_freq=sys_clk_freq) sys_clk_freq=sys_clk_freq)
self.csr_devices.append("drtio_transceiver") self.csr_devices.append("drtio_transceiver")
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
self.crg = self.ps7 # HACK for eem_7series to find the clock self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq) self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg") self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
self.rustc_cfg["has_si5324"] = None self.config["HAS_SI5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None self.config["SI5324_SOFT_RESET"] = None
self.rtio_channels = [] self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"]) has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
@ -272,8 +281,8 @@ class GenericMaster(SoCCore):
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size) memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size)
self.axi2csr.register_port(coreaux.get_rx_port(), size) self.axi2csr.register_port(coreaux.get_rx_port(), size)
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2) self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
self.rustc_cfg["has_drtio"] = None self.config["HAS_DRTIO"] = None
self.rustc_cfg["has_drtio_routing"] = None self.config["HAS_DRTIO_ROUTING"] = None
self.add_csr_group("drtio", drtio_csr_group) self.add_csr_group("drtio", drtio_csr_group)
self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
@ -282,14 +291,14 @@ class GenericMaster(SoCCore):
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
if self.acpki: if self.acpki:
self.rustc_cfg["ki_impl"] = "acp" self.config["KI_IMPL"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.rustc_cfg["ki_impl"] = "csr" self.config["KI_IMPL"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -308,14 +317,19 @@ class GenericMaster(SoCCore):
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
self.csr_devices.append("routing_table") self.csr_devices.append("routing_table")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri, self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.cri_con.switch.slave,
self.ps7.s_axi_hp1) self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
if has_grabber: if has_grabber:
self.rustc_cfg["has_grabber"] = None self.config["HAS_GRABBER"] = None
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
self.submodules.virtual_leds = virtual_leds.VirtualLeds()
self.csr_devices.append("virtual_leds")
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
for i, channel in enumerate(self.drtio_transceiver.channels)]
class GenericSatellite(SoCCore): class GenericSatellite(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False):
@ -323,24 +337,25 @@ class GenericSatellite(SoCCore):
rtio_clk_freq = description["rtio_frequency"] rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki self.acpki = acpki
self.rustc_cfg = dict()
platform = kasli_soc.Platform() platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([ platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
]) ])
ident = self.__class__.__name__ ident = description["variant"]
if self.acpki: if self.acpki:
ident = "acpki_" + ident ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident) SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
self.config["hw_rev"] = description["hw_rev"]
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]") platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24") platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
self.crg = self.ps7 # HACK for eem_7series to find the clock self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq) self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg") self.csr_devices.append("rtio_crg")
self.rustc_cfg["has_rtio_crg"] = None fix_serdes_timing_path(platform)
data_pads = [platform.request("sfp", i) for i in range(4)] data_pads = [platform.request("sfp", i) for i in range(4)]
@ -408,21 +423,21 @@ class GenericSatellite(SoCCore):
# and registered in PS interface # and registered in PS interface
# manually, because software refers to rx/tx by halves of entire memory block, not names # manually, because software refers to rx/tx by halves of entire memory block, not names
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2) self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
self.rustc_cfg["has_drtio"] = None self.config["HAS_DRTIO"] = None
self.rustc_cfg["has_drtio_routing"] = None self.config["HAS_DRTIO_ROUTING"] = None
self.add_csr_group("drtioaux", drtioaux_csr_group) self.add_csr_group("drtioaux", drtioaux_csr_group)
self.add_memory_group("drtioaux_mem", drtioaux_memory_group) self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
self.add_csr_group("drtiorep", drtiorep_csr_group) self.add_csr_group("drtiorep", drtiorep_csr_group)
if self.acpki: if self.acpki:
self.rustc_cfg["ki_impl"] = "acp" self.config["KI_IMPL"] = "acp"
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc, self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
bus=self.ps7.s_axi_acp, bus=self.ps7.s_axi_acp,
user=self.ps7.s_axi_acp_user, user=self.ps7.s_axi_acp_user,
evento=self.ps7.event.o) evento=self.ps7.event.o)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
else: else:
self.rustc_cfg["ki_impl"] = "csr" self.config["KI_IMPL"] = "csr"
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True) self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
self.csr_devices.append("rtio") self.csr_devices.append("rtio")
@ -445,7 +460,8 @@ class GenericSatellite(SoCCore):
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
self.rustc_cfg["rtio_frequency"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk"), si5324_clkin=platform.request("cdr_clk"),
@ -455,9 +471,8 @@ class GenericSatellite(SoCCore):
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
self.csr_devices.append("siphaser") self.csr_devices.append("siphaser")
self.rustc_cfg["has_si5324"] = None self.config["HAS_SI5324"] = None
self.rustc_cfg["has_siphaser"] = None self.config["SI5324_SOFT_RESET"] = None
self.rustc_cfg["si5324_soft_reset"] = None
gtx0 = self.drtio_transceiver.gtxs[0] gtx0 = self.drtio_transceiver.gtxs[0]
platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period) platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period)
@ -471,10 +486,15 @@ class GenericSatellite(SoCCore):
self.crg.cd_sys.clk, gtx.rxoutclk) self.crg.cd_sys.clk, gtx.rxoutclk)
if has_grabber: if has_grabber:
self.rustc_cfg["has_grabber"] = None self.config["HAS_GRABBER"] = None
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
# no RTIO CRG here # no RTIO CRG here
self.submodules.virtual_leds = virtual_leds.VirtualLeds()
self.csr_devices.append("virtual_leds")
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
for i, channel in enumerate(self.drtio_transceiver.channels)]
def write_mem_file(soc, filename): def write_mem_file(soc, filename):
with open(filename, "w") as f: with open(filename, "w") as f:
@ -490,11 +510,14 @@ def write_csr_file(soc, filename):
def write_rustc_cfg_file(soc, filename): def write_rustc_cfg_file(soc, filename):
with open(filename, "w") as f: with open(filename, "w") as f:
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)): for name, origin, busword, obj in soc.get_csr_regions():
if v is None: f.write("has_{}\n".format(name.lower()))
f.write("{}\n".format(k)) for name, value in soc.get_constants():
if name.upper().startswith("CONFIG_"):
if value is None:
f.write("{}\n".format(name.lower()[7:]))
else: else:
f.write("{}=\"{}\"\n".format(k, v)) f.write("{}=\"{}\"\n".format(name.lower()[7:], str(value)))
def main(): def main():

View File

@ -14,7 +14,7 @@ from misoc.integration import cpu_interface
from misoc.cores import gpio from misoc.cores import gpio
from artiq.gateware import rtio, nist_clock, nist_qc2 from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2 from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2, edge_counter
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
from artiq.gateware.drtio.transceiver import gtx_7series from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.siphaser import SiPhaser7Series
@ -320,7 +320,7 @@ class _MasterBase(SoCCore):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri, self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.cri_con.switch.slave,
self.ps7.s_axi_hp1) self.ps7.s_axi_hp1)
self.csr_devices.append("rtio_analyzer") self.csr_devices.append("rtio_analyzer")
@ -562,12 +562,16 @@ class _NIST_QC2_RTIO:
platform.add_extension(pmod1_33) platform.add_extension(pmod1_33)
rtio_channels = [] rtio_channels = []
edge_counter_phy = []
# All TTL channels are In+Out capable # All TTL channels are In+Out capable
for i in range(40): for i in range(40):
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i)) phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512)) rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# first four TTLs will also have edge counters
if i < 4:
edge_counter_phy.append(phy)
# no SMA GPIO, replaced with PMOD1_0 # no SMA GPIO, replaced with PMOD1_0
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0)) phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
@ -607,6 +611,11 @@ class _NIST_QC2_RTIO:
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
for phy in edge_counter_phy:
counter = edge_counter.SimpleEdgeCounter(phy.input_state)
self.submodules += counter
rtio_channels.append(rtio.Channel.from_phy(counter))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel()) rtio_channels.append(rtio.LogChannel())

View File

@ -0,0 +1,163 @@
use log::info;
use crate::pl::csr;
#[derive(PartialEq, Clone, Copy)]
enum State {
Reset,
ExitReset,
Lock,
Align,
Watch,
}
#[derive(Clone, Copy)]
struct Info {
state: State,
frame_size: (u16, u16),
}
static mut INFO: [Info; csr::GRABBER_LEN] = [Info {
state: State::Reset,
frame_size: (0, 0),
}; csr::GRABBER_LEN];
fn get_pll_reset(g: usize) -> bool {
unsafe { (csr::GRABBER[g].pll_reset_read)() != 0 }
}
fn set_pll_reset(g: usize, reset: bool) {
let val = if reset { 1 } else { 0 };
unsafe { (csr::GRABBER[g].pll_reset_write)(val) }
}
fn pll_locked(g: usize) -> bool {
unsafe { (csr::GRABBER[g].pll_locked_read)() != 0 }
}
fn clock_pattern_ok(g: usize) -> bool {
unsafe { (csr::GRABBER[g].clk_sampled_read)() == 0b1100011 }
}
fn clock_pattern_ok_filter(g: usize) -> bool {
for _ in 0..128 {
if !clock_pattern_ok(g) {
return false;
}
}
true
}
fn phase_shift(g: usize, direction: u8) {
unsafe {
(csr::GRABBER[g].phase_shift_write)(direction);
while (csr::GRABBER[g].phase_shift_done_read)() == 0 {}
}
}
fn clock_align(g: usize) -> bool {
while clock_pattern_ok_filter(g) {
phase_shift(g, 1);
}
phase_shift(g, 1);
let mut count = 0;
while !clock_pattern_ok_filter(g) {
phase_shift(g, 1);
count += 1;
if count > 1024 {
return false;
}
}
let mut window = 1;
phase_shift(g, 1);
while clock_pattern_ok_filter(g) {
phase_shift(g, 1);
window += 1;
}
for _ in 0..window / 2 {
phase_shift(g, 0);
}
true
}
fn get_last_pixels(g: usize) -> (u16, u16) {
unsafe { ((csr::GRABBER[g].last_x_read)(), (csr::GRABBER[g].last_y_read)()) }
}
fn get_video_clock(g: usize) -> u32 {
let freq_count = unsafe { (csr::GRABBER[g].freq_count_read)() } as u32;
2 * freq_count * (csr::CONFIG_CLOCK_FREQUENCY / 1000) / (511 * 1000)
}
pub fn tick() {
for g in 0..csr::GRABBER.len() {
let next = match unsafe { INFO[g].state } {
State::Reset => {
set_pll_reset(g, true);
unsafe {
INFO[g].frame_size = (0, 0);
}
State::ExitReset
}
State::ExitReset => {
if get_pll_reset(g) {
set_pll_reset(g, false);
State::Lock
} else {
State::ExitReset
}
}
State::Lock => {
if pll_locked(g) {
info!("grabber{} locked: {}MHz", g, get_video_clock(g));
State::Align
} else {
State::Lock
}
}
State::Align => {
if pll_locked(g) {
if clock_align(g) {
info!("grabber{} alignment success", g);
State::Watch
} else {
info!("grabber{} alignment failure", g);
State::Reset
}
} else {
info!("grabber{} lock lost", g);
State::Reset
}
}
State::Watch => {
if pll_locked(g) {
if clock_pattern_ok(g) {
let last_xy = get_last_pixels(g);
if last_xy != unsafe { INFO[g].frame_size } {
// x capture is on ~LVAL which is after
// the last increment on DVAL
// y capture is on ~FVAL which coincides with the
// last increment on ~LVAL
info!("grabber{} frame size: {}x{}", g, last_xy.0, last_xy.1 + 1);
unsafe { INFO[g].frame_size = last_xy }
}
State::Watch
} else {
info!("grabber{} alignment lost", g);
State::Reset
}
} else {
info!("grabber{} lock lost", g);
State::Reset
}
}
};
unsafe {
INFO[g].state = next;
}
}
}

View File

@ -0,0 +1,177 @@
use libboard_zynq::i2c;
use log::info;
use crate::pl::csr;
// Only the bare minimum registers. Bits/IO connections equivalent between IC types.
struct Registers {
// PCA9539 equivalent register names in comments
iodira: u8, // Configuration Port 0
iodirb: u8, // Configuration Port 1
gpioa: u8, // Output Port 0
gpiob: u8, // Output Port 1
}
//IO expanders pins
const IODIR_OUT_SFP_TX_DISABLE: u8 = 0x02;
const IODIR_OUT_SFP_LED: u8 = 0x40;
#[cfg(hw_rev = "v1.0")]
const IODIR_OUT_SFP0_LED: u8 = 0x40;
#[cfg(hw_rev = "v1.1")]
const IODIR_OUT_SFP0_LED: u8 = 0x80;
//IO expander port direction
const IODIR0: [u8; 2] = [
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP0_LED,
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
];
const IODIR1: [u8; 2] = [
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED,
];
pub struct IoExpander {
address: u8,
virtual_led_mapping: &'static [(u8, u8, u8)],
iodir: [u8; 2],
out_current: [u8; 2],
out_target: [u8; 2],
registers: Registers,
}
impl IoExpander {
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
#[cfg(hw_rev = "v1.0")]
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
#[cfg(hw_rev = "v1.1")]
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)];
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
// Both expanders on SHARED I2C bus
let mut io_expander = match index {
0 => IoExpander {
address: 0x40,
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
iodir: IODIR0,
out_current: [0; 2],
out_target: [0; 2],
registers: Registers {
iodira: 0x00,
iodirb: 0x01,
gpioa: 0x12,
gpiob: 0x13,
},
},
1 => IoExpander {
address: 0x42,
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
iodir: IODIR1,
out_current: [0; 2],
out_target: [0; 2],
registers: Registers {
iodira: 0x00,
iodirb: 0x01,
gpioa: 0x12,
gpiob: 0x13,
},
},
_ => return Err("incorrect I/O expander index"),
};
if !io_expander.check_ack(i2c)? {
info!(
"MCP23017 io expander {} not found. Checking for PCA9539.",
index
);
io_expander.address += 0xa8; // translate to PCA9539 addresses (see schematic)
io_expander.registers = Registers {
iodira: 0x06,
iodirb: 0x07,
gpioa: 0x02,
gpiob: 0x03,
};
if !io_expander.check_ack(i2c)? {
return Err("Neither MCP23017 nor PCA9539 io expander found.");
};
}
Ok(io_expander)
}
fn select(&self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
i2c.pca954x_select(0x70, None)?;
i2c.pca954x_select(0x71, Some(3))?;
Ok(())
}
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(())
}
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)?;
i2c.stop()?;
Ok(ack)
}
fn update_iodir(&self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
self.write(i2c, self.registers.iodira, self.iodir[0])?;
self.write(i2c, self.registers.iodirb, self.iodir[1])?;
Ok(())
}
pub fn init(&mut self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
self.select(i2c)?;
self.update_iodir(i2c)?;
self.out_current[0] = 0x00;
self.write(i2c, self.registers.gpioa, 0x00)?;
self.out_current[1] = 0x00;
self.write(i2c, self.registers.gpiob, 0x00)?;
Ok(())
}
pub fn set_oe(&mut self, i2c: &mut i2c::I2c, port: u8, outputs: u8) -> Result<(), &'static str> {
self.iodir[port as usize] &= !outputs;
self.update_iodir(i2c)?;
Ok(())
}
pub fn set(&mut self, port: u8, bit: u8, high: bool) {
if high {
self.out_target[port as usize] |= 1 << bit;
} else {
self.out_target[port as usize] &= !(1 << bit);
}
}
pub fn service(&mut self, i2c: &mut i2c::I2c) -> Result<(), &'static str> {
#[cfg(has_virtual_leds)]
for (led, port, bit) in self.virtual_led_mapping.iter() {
let level = unsafe { csr::virtual_leds::status_read() >> led & 1 };
self.set(*port, *bit, level != 0);
}
if self.out_target != self.out_current {
self.select(i2c)?;
if self.out_target[0] != self.out_current[0] {
self.write(i2c, self.registers.gpioa, self.out_target[0])?;
self.out_current[0] = self.out_target[0];
}
if self.out_target[1] != self.out_current[1] {
self.write(i2c, self.registers.gpiob, self.out_target[1])?;
self.out_current[1] = self.out_target[1];
}
}
Ok(())
}
}

View File

@ -27,6 +27,10 @@ pub mod drtioaux_async;
#[cfg(has_drtio)] #[cfg(has_drtio)]
#[path = "../../../build/mem.rs"] #[path = "../../../build/mem.rs"]
pub mod mem; pub mod mem;
#[cfg(feature = "target_kasli_soc")]
pub mod io_expander;
#[cfg(has_grabber)]
pub mod grabber;
use core::{cmp, str}; use core::{cmp, str};
use libboard_zynq::slcr; use libboard_zynq::slcr;

View File

@ -427,8 +427,8 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
moninj::start(timer, aux_mutex, drtio_routing_table); moninj::start(timer, aux_mutex, drtio_routing_table);
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start())); let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
let idle_kernel = Rc::new(cfg.read("idle").ok()); let idle_kernel = Rc::new(cfg.read("idle_kernel").ok());
if let Ok(buffer) = cfg.read("startup") { if let Ok(buffer) = cfg.read("startup_kernel") {
info!("Loading startup kernel..."); info!("Loading startup kernel...");
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) { if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
info!("Starting startup kernel..."); info!("Starting startup kernel...");

View File

@ -116,7 +116,7 @@ pub extern fn dma_record_output(target: i32, word: i32) {
} }
} }
pub extern fn dma_record_output_wide(target: i32, words: CSlice<i32>) { pub extern fn dma_record_output_wide(target: i32, words: &CSlice<i32>) {
assert!(words.len() <= 16); // enforce the hardware limit assert!(words.len() <= 16); // enforce the hardware limit
unsafe { unsafe {

View File

@ -11,6 +11,7 @@
extern crate alloc; extern crate alloc;
use core::cell::RefCell;
use log::{info, warn, error}; use log::{info, warn, error};
use libboard_zynq::{timer::GlobalTimer, mpcore, gic}; use libboard_zynq::{timer::GlobalTimer, mpcore, gic};
@ -21,6 +22,8 @@ use void::Void;
use libconfig::Config; use libconfig::Config;
use libcortex_a9::l2c::enable_l2_cache; use libcortex_a9::l2c::enable_l2_cache;
use libboard_artiq::{logger, identifier_read, init_gateware, pl}; use libboard_artiq::{logger, identifier_read, init_gateware, pl};
#[cfg(feature = "target_kasli_soc")]
use libboard_artiq::io_expander;
const ASYNC_ERROR_COLLISION: u8 = 1 << 0; const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
const ASYNC_ERROR_BUSY: u8 = 1 << 1; const ASYNC_ERROR_BUSY: u8 = 1 << 1;
@ -89,6 +92,39 @@ async fn report_async_rtio_errors() {
#[cfg(feature = "target_kasli_soc")]
async fn io_expanders_service(
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>,
io_expander0: RefCell<io_expander::IoExpander>,
io_expander1: RefCell<io_expander::IoExpander>,
) {
loop {
task::r#yield().await;
io_expander0
.borrow_mut()
.service(&mut i2c_bus.borrow_mut())
.expect("I2C I/O expander #0 service failed");
io_expander1
.borrow_mut()
.service(&mut i2c_bus.borrow_mut())
.expect("I2C I/O expander #1 service failed");
}
}
#[cfg(has_grabber)]
mod grabber {
use libasync::delay;
use libboard_artiq::grabber;
use libboard_zynq::time::Milliseconds;
use crate::GlobalTimer;
pub async fn grabber_thread(timer: GlobalTimer) {
let mut countdown = timer.countdown();
loop {
grabber::tick();
delay(&mut countdown, Milliseconds(200)).await;
}
}
}
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17]; static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
#[no_mangle] #[no_mangle]
@ -109,9 +145,31 @@ pub fn main_core0() {
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts(); gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
init_gateware(); init_gateware();
info!("detected gateware: {}", identifier_read(&mut [0; 64])); info!("gateware ident: {}", identifier_read(&mut [0; 64]));
i2c::init(); i2c::init();
let i2c_bus = unsafe { (i2c::I2C_BUS).as_mut().unwrap() };
#[cfg(feature = "target_kasli_soc")]
let (mut io_expander0, mut io_expander1);
#[cfg(feature = "target_kasli_soc")]
{
io_expander0 = io_expander::IoExpander::new(i2c_bus, 0).unwrap();
io_expander1 = io_expander::IoExpander::new(i2c_bus, 1).unwrap();
io_expander0
.init(i2c_bus)
.expect("I2C I/O expander #0 initialization failed");
io_expander1
.init(i2c_bus)
.expect("I2C I/O expander #1 initialization failed");
// Drive TX_DISABLE to false on SFP0..3
io_expander0.set(0, 1, false);
io_expander1.set(0, 1, false);
io_expander0.set(1, 1, false);
io_expander1.set(1, 1, false);
io_expander0.service(i2c_bus).unwrap();
io_expander1.service(i2c_bus).unwrap();
}
let cfg = match Config::new() { let cfg = match Config::new() {
Ok(cfg) => cfg, Ok(cfg) => cfg,
@ -125,5 +183,14 @@ pub fn main_core0() {
task::spawn(report_async_rtio_errors()); task::spawn(report_async_rtio_errors());
#[cfg(has_grabber)]
task::spawn(grabber::grabber_thread(timer));
#[cfg(feature = "target_kasli_soc")]
task::spawn(io_expanders_service(
RefCell::new(i2c_bus),
RefCell::new(io_expander0),
RefCell::new(io_expander1),
));
comms::main(timer, cfg); comms::main(timer, cfg);
} }

View File

@ -15,22 +15,85 @@ use crate::proto_async;
use self::tag::{Tag, TagIterator, split_tag}; use self::tag::{Tag, TagIterator, split_tag};
#[inline] #[inline]
fn alignment_offset(alignment: isize, ptr: isize) -> isize { fn round_up(val: usize, power_of_two: usize) -> usize {
(alignment - ptr % alignment) % alignment assert!(power_of_two.is_power_of_two());
let max_rem = power_of_two - 1;
(val + max_rem) & (!max_rem)
} }
#[inline]
unsafe fn round_up_mut<T>(ptr: *mut T, power_of_two: usize) -> *mut T {
round_up(ptr as usize, power_of_two) as *mut T
}
#[inline]
unsafe fn round_up_const<T>(ptr: *const T, power_of_two: usize) -> *const T {
round_up(ptr as usize, power_of_two) as *const T
}
#[inline]
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T { unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
let alignment = core::mem::align_of::<T>() as isize; round_up_const(ptr, core::mem::align_of::<T>()) as *const T
let fix = alignment_offset(alignment, ptr as isize);
((ptr as isize) + fix) as *const T
} }
#[inline]
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T { unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
let alignment = core::mem::align_of::<T>() as isize; round_up_mut(ptr, core::mem::align_of::<T>()) as *mut T
let fix = alignment_offset(alignment, ptr as isize);
((ptr as isize) + fix) as *mut T
} }
/// Reads (deserializes) `length` array or list elements of type `tag` from `stream`,
/// writing them into the buffer given by `storage`.
///
/// `alloc` is used for nested allocations (if elements themselves contain
/// lists/arrays), see [recv_value].
#[async_recursion(?Send)]
async unsafe fn recv_elements<F>(
stream: &TcpStream,
elt_tag: Tag<'async_recursion>,
length: usize,
storage: *mut (),
alloc: &(impl Fn(usize) -> F + 'async_recursion)
) -> Result<(), smoltcp::Error>
where
F: Future<Output=*mut ()>,
{
// List of simple types are special-cased in the protocol for performance.
match elt_tag {
Tag::Bool => {
let dest = core::slice::from_raw_parts_mut(storage as *mut u8, length);
proto_async::read_chunk(stream, dest).await?;
},
Tag::Int32 => {
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 = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u32(dest);
},
Tag::Int64 | Tag::Float64 => {
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 = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u64(dest);
},
_ => {
let mut data = storage;
for _ in 0..length {
recv_value(stream, elt_tag, &mut data, alloc).await?
}
}
}
Ok(())
}
/// Reads (deserializes) a value of type `tag` from `stream`, writing the results to
/// the kernel-side buffer `data` (the passed pointer to which is incremented to point
/// past the just-received data). For nested allocations (lists/arrays), `alloc` is
/// invoked any number of times with the size of the required allocation as a parameter
/// (which is assumed to be correctly aligned for all payload types).
#[async_recursion(?Send)] #[async_recursion(?Send)]
async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, data: &mut *mut (), async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, data: &mut *mut (),
alloc: &(impl Fn(usize) -> F + 'async_recursion)) alloc: &(impl Fn(usize) -> F + 'async_recursion))
@ -71,120 +134,63 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
}) })
} }
Tag::Tuple(it, arity) => { Tag::Tuple(it, arity) => {
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize)); let alignment = tag.alignment();
*data = round_up_mut(*data, alignment);
let mut it = it.clone(); let mut it = it.clone();
for _ in 0..arity { for _ in 0..arity {
let tag = it.next().expect("truncated tag"); let tag = it.next().expect("truncated tag");
recv_value(stream, tag, data, alloc).await?; recv_value(stream, tag, data, alloc).await?
} }
// Take into account any tail padding (if element(s) with largest alignment
// are not at the end).
*data = round_up_mut(*data, alignment);
Ok(()) Ok(())
} }
Tag::List(it) => { Tag::List(it) => {
#[repr(C)] #[repr(C)]
struct List { elements: *mut (), length: u32 } struct List { elements: *mut (), length: usize }
consume_value!(*mut List, |ptr| { consume_value!(*mut List, |ptr_to_list| {
let length = proto_async::read_i32(stream).await? as usize;
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");
let data_size = tag.size() * length as usize + let length = proto_async::read_i32(stream).await? as usize;
match tag {
Tag::Int64 | Tag::Float64 => 4,
_ => 0
};
let data = alloc(data_size + 8).await as *mut u8;
*ptr = data as *mut List;
let ptr = data as *mut List;
let data = data.offset(8);
let alignment = tag.alignment(); // To avoid multiple kernel CPU roundtrips, use a single allocation for
let mut data = data.offset(alignment_offset(alignment as isize, data as isize)) as *mut (); // both the pointer/length List (slice) and the backing storage for the
(*ptr).length = length as u32; // elements. We can assume that alloc() is aligned suitably, so just
(*ptr).elements = data; // need to take into account any extra padding required.
match tag { // (Note: At the time of writing, there will never actually be any types
Tag::Bool => { // with alignment larger than 8 bytes, so storage_offset == 0 always.)
let ptr = data as *mut u8; let list_size = 4 + 4;
let dest = core::slice::from_raw_parts_mut(ptr, length); let storage_offset = round_up(list_size, tag.alignment());
proto_async::read_chunk(stream, dest).await?; let storage_size = tag.size() * length;
},
Tag::Int32 => { let allocation = alloc(storage_offset + storage_size).await as *mut u8;
let ptr = data as *mut u32; *ptr_to_list = allocation as *mut List;
// reading as raw bytes and do endianness conversion later let storage = allocation.offset(storage_offset as isize) as *mut ();
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
proto_async::read_chunk(stream, dest).await?; (**ptr_to_list).length = length;
drop(dest); (**ptr_to_list).elements = storage;
let dest = core::slice::from_raw_parts_mut(ptr, length); recv_elements(stream, tag, length, storage, alloc).await
NativeEndian::from_slice_u32(dest);
},
Tag::Int64 | Tag::Float64 => {
let ptr = data as *mut u64;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
proto_async::read_chunk(stream, dest).await?;
drop(dest);
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u64(dest);
},
_ => {
for _ in 0..(*ptr).length as usize {
recv_value(stream, tag, &mut data, alloc).await?
}
}
}
Ok(())
}) })
} }
Tag::Array(it, num_dims) => { Tag::Array(it, num_dims) => {
consume_value!(*mut (), |buffer| { consume_value!(*mut (), |buffer| {
let mut total_len: u32 = 1; // Deserialize length along each dimension and compute total number of
// elements.
let mut total_len: usize = 1;
for _ in 0..num_dims { for _ in 0..num_dims {
let len = proto_async::read_i32(stream).await? as u32; let len = proto_async::read_i32(stream).await? as usize;
total_len *= len; total_len *= len;
consume_value!(u32, |ptr| *ptr = len ) consume_value!(usize, |ptr| *ptr = len )
} }
// Allocate backing storage for elements; deserialize them.
let elt_tag = it.clone().next().expect("truncated tag"); let elt_tag = it.clone().next().expect("truncated tag");
let data_size = elt_tag.size() * total_len as usize + *buffer = alloc(elt_tag.size() * total_len).await;
match elt_tag { recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
Tag::Int64 | Tag::Float64 => 4,
_ => 0
};
let mut data = alloc(data_size).await;
let alignment = tag.alignment();
data = data.offset(alignment_offset(alignment as isize, data as isize));
*buffer = data;
let length = total_len as usize;
match elt_tag {
Tag::Bool => {
let ptr = data as *mut u8;
let dest = core::slice::from_raw_parts_mut(ptr, length);
proto_async::read_chunk(stream, dest).await?;
},
Tag::Int32 => {
let ptr = data as *mut u32;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
proto_async::read_chunk(stream, dest).await?;
drop(dest);
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u32(dest);
},
Tag::Int64 | Tag::Float64 => {
let ptr = data as *mut u64;
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
proto_async::read_chunk(stream, dest).await?;
drop(dest);
let dest = core::slice::from_raw_parts_mut(ptr, length);
NativeEndian::from_slice_u64(dest);
},
_ => {
for _ in 0..length {
recv_value(stream, elt_tag, &mut data, alloc).await?
}
}
}
Ok(())
}) })
} }
Tag::Range(it) => { Tag::Range(it) => {
*data = (*data).offset(alignment_offset(tag.alignment() as isize, *data as isize)); *data = round_up_mut(*data, tag.alignment());
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");
recv_value(stream, tag, data, alloc).await?; recv_value(stream, tag, data, alloc).await?;
recv_value(stream, tag, data, alloc).await?; recv_value(stream, tag, data, alloc).await?;
@ -211,6 +217,36 @@ pub async fn recv_return<F>(stream: &TcpStream, tag_bytes: &[u8], data: *mut (),
Ok(()) Ok(())
} }
unsafe fn send_elements<W>(writer: &mut W, elt_tag: Tag, length: usize, data: *const ())
-> Result<(), Error>
where W: Write + ?Sized
{
writer.write_u8(elt_tag.as_u8())?;
match elt_tag {
// we cannot use NativeEndian::from_slice_i32 as the data is not mutable,
// and that is not needed as the data is already in native endian
Tag::Bool => {
let slice = core::slice::from_raw_parts(data as *const u8, length);
writer.write_all(slice)?;
},
Tag::Int32 => {
let slice = core::slice::from_raw_parts(data as *const u8, length * 4);
writer.write_all(slice)?;
},
Tag::Int64 | Tag::Float64 => {
let slice = core::slice::from_raw_parts(data as *const u8, length * 8);
writer.write_all(slice)?;
},
_ => {
let mut data = data;
for _ in 0..length {
send_value(writer, elt_tag, &mut data)?;
}
}
}
Ok(())
}
unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ()) unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
-> Result<(), Error> -> Result<(), Error>
where W: Write + ?Sized where W: Write + ?Sized
@ -244,46 +280,23 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
Tag::Tuple(it, arity) => { Tag::Tuple(it, arity) => {
let mut it = it.clone(); let mut it = it.clone();
writer.write_u8(arity)?; writer.write_u8(arity)?;
let mut max_alignment = 0;
for _ in 0..arity { for _ in 0..arity {
let tag = it.next().expect("truncated tag"); let tag = it.next().expect("truncated tag");
max_alignment = core::cmp::max(max_alignment, tag.alignment());
send_value(writer, tag, data)? send_value(writer, tag, data)?
} }
*data = round_up_const(*data, max_alignment);
Ok(()) Ok(())
} }
Tag::List(it) => { Tag::List(it) => {
#[repr(C)] #[repr(C)]
struct List { elements: *const (), length: u32 } struct List { elements: *const (), length: u32 }
consume_value!(&List, |ptr| { consume_value!(&List, |ptr| {
let length = (**ptr).length as isize; let length = (**ptr).length as usize;
writer.write_u32((*ptr).length)?; writer.write_u32((*ptr).length)?;
let tag = it.clone().next().expect("truncated tag"); let tag = it.clone().next().expect("truncated tag");
let mut data = (**ptr).elements; send_elements(writer, tag, length, (**ptr).elements)
writer.write_u8(tag.as_u8())?;
match tag {
Tag::Bool => {
// we can pretend this is u8...
let ptr1 = align_ptr::<u8>(data);
let slice = core::slice::from_raw_parts(ptr1, length as usize);
writer.write_all(slice)?;
},
Tag::Int32 => {
let ptr1 = align_ptr::<i32>(data);
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 4);
writer.write_all(slice)?;
},
Tag::Int64 | Tag::Float64 => {
let ptr1 = align_ptr::<i64>(data);
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 8);
writer.write_all(slice)?;
},
// non-primitive types, not sure if this would happen but we can handle it...
_ => {
for _ in 0..length {
send_value(writer, tag, &mut data)?;
}
}
};
Ok(())
}) })
} }
Tag::Array(it, num_dims) => { Tag::Array(it, num_dims) => {
@ -298,33 +311,8 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
total_len *= *len; total_len *= *len;
}) })
} }
let mut data = *buffer; let length = total_len as usize;
let length = total_len as isize; send_elements(writer, elt_tag, length, *buffer)
writer.write_u8(elt_tag.as_u8())?;
match elt_tag {
Tag::Bool => {
let ptr1 = align_ptr::<u8>(data);
let slice = core::slice::from_raw_parts(ptr1, length as usize);
writer.write_all(slice)?;
},
Tag::Int32 => {
let ptr1 = align_ptr::<i32>(data);
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 4);
writer.write_all(slice)?;
},
Tag::Int64 | Tag::Float64 => {
let ptr1 = align_ptr::<i64>(data);
let slice = core::slice::from_raw_parts(ptr1 as *const u8, length as usize * 8);
writer.write_all(slice)?;
},
// non-primitive types, not sure if this would happen but we can handle it...
_ => {
for _ in 0..length {
send_value(writer, elt_tag, &mut data)?;
}
}
};
Ok(())
}) })
} }
Tag::Range(it) => { Tag::Range(it) => {
@ -448,18 +436,15 @@ mod tag {
let it = it.clone(); let it = it.clone();
it.take(3).map(|t| t.alignment()).max().unwrap() it.take(3).map(|t| t.alignment()).max().unwrap()
} }
// CSlice basically // the ptr/length(s) pair is basically CSlice
Tag::Bytes | Tag::String | Tag::ByteArray => Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) =>
core::mem::align_of::<CSlice<()>>(), core::mem::align_of::<CSlice<()>>(),
// array buffer is allocated, so no need for alignment first Tag::Keyword(_) => unreachable!("Tag::Keyword should not appear in composite types"),
Tag::List(_) | Tag::Array(_, _) => 1, Tag::Object => core::mem::align_of::<u32>(),
// will not be sent from the host
_ => unreachable!("unexpected tag from host")
} }
} }
pub fn size(self) -> usize { pub fn size(self) -> usize {
use super::alignment_offset;
match self { match self {
Tag::None => 0, Tag::None => 0,
Tag::Bool => 1, Tag::Bool => 1,
@ -471,13 +456,18 @@ mod tag {
Tag::ByteArray => 8, Tag::ByteArray => 8,
Tag::Tuple(it, arity) => { Tag::Tuple(it, arity) => {
let mut size = 0; let mut size = 0;
let mut max_alignment = 0;
let mut it = it.clone(); let mut it = it.clone();
for _ in 0..arity { for _ in 0..arity {
let tag = it.next().expect("truncated tag"); let tag = it.next().expect("truncated tag");
let alignment = tag.alignment();
max_alignment = core::cmp::max(max_alignment, alignment);
size = super::round_up(size, alignment);
size += tag.size(); size += tag.size();
// includes padding
size += alignment_offset(tag.alignment() as isize, size as isize) as usize;
} }
// Take into account any tail padding (if element(s) with largest
// alignment are not at the end).
size = super::round_up(size, max_alignment);
size size
} }
Tag::List(_) => 4, Tag::List(_) => 4,

View File

@ -94,7 +94,7 @@ pub extern fn output(target: i32, data: i32) {
} }
} }
pub extern fn output_wide(target: i32, data: CSlice<i32>) { pub extern fn output_wide(target: i32, data: &CSlice<i32>) {
unsafe { unsafe {
csr::rtio::target_write(target as u32); csr::rtio::target_write(target as u32);
// writing target clears o_data // writing target clears o_data

View File

@ -20,8 +20,12 @@ extern crate alloc;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio}; use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
use libsupport_zynq::ram; use libsupport_zynq::ram;
#[cfg(has_grabber)]
use libboard_artiq::grabber;
#[cfg(has_si5324)] #[cfg(has_si5324)]
use libboard_artiq::si5324; use libboard_artiq::si5324;
#[cfg(feature = "target_kasli_soc")]
use libboard_artiq::io_expander;
use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger, identifier_read, init_gateware}; use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger, identifier_read, init_gateware};
use libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache}; use libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache};
use libregister::{RegisterW, RegisterR}; use libregister::{RegisterW, RegisterR};
@ -204,7 +208,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
drtioaux::send(0, &drtioaux::Packet::RoutingAck) drtioaux::send(0, &drtioaux::Packet::RoutingAck)
} }
drtioaux::Packet::MonitorRequest { destination: _destination, channel: _channel, probe: _probe } => { drtioaux::Packet::MonitorRequest { destination: _destination, channel, probe } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value; let value;
#[cfg(has_rtio_moninj)] #[cfg(has_rtio_moninj)]
@ -221,8 +225,8 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
let reply = drtioaux::Packet::MonitorReply { value: value }; let reply = drtioaux::Packet::MonitorReply { value: value };
drtioaux::send(0, &reply) drtioaux::send(0, &reply)
}, },
drtioaux::Packet::InjectionRequest { destination: _destination, channel: _channel, drtioaux::Packet::InjectionRequest { destination: _destination, channel,
overrd: _overrd, value: _value } => { overrd, value } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
#[cfg(has_rtio_moninj)] #[cfg(has_rtio_moninj)]
unsafe { unsafe {
@ -233,7 +237,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
Ok(()) Ok(())
}, },
drtioaux::Packet::InjectionStatusRequest { destination: _destination, drtioaux::Packet::InjectionStatusRequest { destination: _destination,
channel: _channel, overrd: _overrd } => { channel, overrd } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer); forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let value; let value;
#[cfg(has_rtio_moninj)] #[cfg(has_rtio_moninj)]
@ -415,6 +419,8 @@ fn hardware_tick(ts: &mut u64, timer: &mut GlobalTimer) {
if now > ts_ms { if now > ts_ms {
ts_ms = now + Milliseconds(200); ts_ms = now + Milliseconds(200);
*ts = ts_ms.0; *ts = ts_ms.0;
#[cfg(has_grabber)]
grabber::tick();
} }
} }
@ -469,6 +475,27 @@ pub extern fn main_core0() -> i32 {
let mut i2c = I2c::i2c0(); let mut i2c = I2c::i2c0();
i2c.init().expect("I2C initialization failed"); i2c.init().expect("I2C initialization failed");
#[cfg(feature = "target_kasli_soc")]
let (mut io_expander0, mut io_expander1);
#[cfg(feature = "target_kasli_soc")]
{
io_expander0 = io_expander::IoExpander::new(&mut i2c, 0).unwrap();
io_expander1 = io_expander::IoExpander::new(&mut i2c, 1).unwrap();
io_expander0
.init(&mut i2c)
.expect("I2C I/O expander #0 initialization failed");
io_expander1
.init(&mut i2c)
.expect("I2C I/O expander #1 initialization failed");
// Drive TX_DISABLE to false on SFP0..3
io_expander0.set(0, 1, false);
io_expander1.set(0, 1, false);
io_expander0.set(1, 1, false);
io_expander1.set(1, 1, false);
io_expander0.service(&mut i2c).unwrap();
io_expander1.service(&mut i2c).unwrap();
}
#[cfg(has_si5324)] #[cfg(has_si5324)]
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324"); si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
@ -501,6 +528,16 @@ pub extern fn main_core0() -> i32 {
for mut rep in repeaters.iter_mut() { for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer); rep.service(&routing_table, rank, &mut timer);
} }
#[cfg(feature = "target_kasli_soc")]
{
io_expander0
.service(&mut i2c)
.expect("I2C I/O expander #0 service failed");
io_expander1
.service(&mut i2c)
.expect("I2C I/O expander #1 service failed");
}
hardware_tick(&mut hardware_tick_ts, &mut timer); hardware_tick(&mut hardware_tick_ts, &mut timer);
} }
@ -522,6 +559,15 @@ pub extern fn main_core0() -> i32 {
for mut rep in repeaters.iter_mut() { for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer); rep.service(&routing_table, rank, &mut timer);
} }
#[cfg(feature = "target_kasli_soc")]
{
io_expander0
.service(&mut i2c)
.expect("I2C I/O expander #0 service failed");
io_expander1
.service(&mut i2c)
.expect("I2C I/O expander #1 service failed");
}
hardware_tick(&mut hardware_tick_ts, &mut timer); hardware_tick(&mut hardware_tick_ts, &mut timer);
if drtiosat_tsc_loaded() { if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink"); info!("TSC loaded from uplink");