Compare commits

...

15 Commits

Author SHA1 Message Date
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
12 changed files with 417 additions and 210 deletions

View File

@ -4,7 +4,7 @@ ARTIQ on Zynq
How to use
----------
1. Install ARTIQ-6 or newer.
1. Install ARTIQ-7 or newer.
2. Select the latest successful build on Hydra: https://nixbld.m-labs.hk/jobset/artiq/zynq
3. Search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
4. Download the ``boot.bin`` "binary distribution" and place it at the root of a FAT-formatted SD card.
@ -33,7 +33,7 @@ not implemented as it seems not very useful.
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:

View File

@ -8,7 +8,7 @@ device_db = {
"arguments": {
"host": "192.168.1.52",
"ref_period": 1e-9,
"ref_multiplier": 1,
"ref_multiplier": 8,
"target": "cortexa9"
}
},
@ -59,6 +59,14 @@ device_db["ad9914dds1"] = {
"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
device_db.update(
loop_out="ttl0",

View File

@ -11,21 +11,23 @@
"src-pythonparser": "src-pythonparser"
},
"locked": {
"lastModified": 1654087576,
"narHash": "sha256-d+LCvCXikhhEjsf4qBZakx3Sda85p+4vVZVwE1YuS90=",
"ref": "master",
"rev": "68ef0073ea66fe9c7f7c178979ecb8c681c47caa",
"revCount": 8068,
"lastModified": 1676885665,
"narHash": "sha256-DCi2fyz/vOM82YGBTgkmruw2tOm+EepTWeF7+oNs1+s=",
"ref": "release-7",
"rev": "8a2ea578b81dd4d8576c8e2b939474f418e03ad7",
"revCount": 8161,
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
},
"original": {
"ref": "release-7",
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
}
},
"artiq-comtools": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"artiq",
"nixpkgs"
@ -36,11 +38,11 @@
]
},
"locked": {
"lastModified": 1654007592,
"narHash": "sha256-vaDFhE1ItjqtIcinC/6RAJGbj44pxxMUEeQUa3FtgEE=",
"lastModified": 1664405593,
"narHash": "sha256-yP441NerlLGig7n+9xHsx8yCtZ+Ggd0VqfBSzc20E04=",
"owner": "m-labs",
"repo": "artiq-comtools",
"rev": "cb73281154656ee8f74db1866859e31bf42755cd",
"rev": "15ddac62813ef623a076ccf982b3bc63d314e651",
"type": "github"
},
"original": {
@ -49,14 +51,29 @@
"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": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"lastModified": 1664789696,
"narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"rev": "80627b282705101e7b38e19ca6e8df105031b072",
"type": "github"
},
"original": {
@ -68,11 +85,11 @@
"mozilla-overlay_2": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"lastModified": 1675354105,
"narHash": "sha256-ZAJGIZ7TjOCU7302lSUabNDz+rxM4If0l8/ZbE/7R5U=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"rev": "85eb0ba7d8e5d6d4b79e5b0180aadbdd25d76404",
"type": "github"
},
"original": {
@ -84,11 +101,11 @@
"mozilla-overlay_3": {
"flake": false,
"locked": {
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"lastModified": 1664789696,
"narHash": "sha256-UGWJHQShiwLCr4/DysMVFrYdYYHcOqAOVsWNUu+l6YU=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"rev": "80627b282705101e7b38e19ca6e8df105031b072",
"type": "github"
},
"original": {
@ -99,11 +116,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1653920503,
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=",
"lastModified": 1666056570,
"narHash": "sha256-e7EkIY68Tp7NKyp9JSHh6CgPPdsKYYWxiL4wZQN8Cwg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a",
"rev": "47edaa313fc3767ce3026037a5b62352f22f3602",
"type": "github"
},
"original": {
@ -128,11 +145,11 @@
]
},
"locked": {
"lastModified": 1654006751,
"narHash": "sha256-OWAnoTCutvTQcYdtdtLQuL6uRtG+7Jz7sbRhcScv8bo=",
"lastModified": 1664319253,
"narHash": "sha256-hycJAgy+NFF9f5I6++7yo8KdhMSyKCPKJazRPxeedI4=",
"owner": "m-labs",
"repo": "sipyco",
"rev": "b3d03a94c751a24769c54a61a0dbe9d6af52dade",
"rev": "d58ded7280e0f020be2446d4fee70f4393e6045f",
"type": "github"
},
"original": {
@ -144,11 +161,11 @@
"src-migen": {
"flake": false,
"locked": {
"lastModified": 1650337393,
"narHash": "sha256-rm1SlFmF2ASz0vIy2nDEzGlyRw2oYNeJRr8Kh8Mg2Qc=",
"lastModified": 1662111470,
"narHash": "sha256-IPyhoFZLhY8d3jHB8jyvGdbey7V+X5eCzBZYSrJ18ec=",
"owner": "m-labs",
"repo": "migen",
"rev": "d4e3f34177c32f09904397179e6ed9c83175e528",
"rev": "639e66f4f453438e83d86dc13491b9403bbd8ec6",
"type": "github"
},
"original": {
@ -160,11 +177,11 @@
"src-misoc": {
"flake": false,
"locked": {
"lastModified": 1649324486,
"narHash": "sha256-Mw/fQS3lHFvCm7L1k63joRkz5uyijQfywcOq+X2+o2s=",
"lastModified": 1665395741,
"narHash": "sha256-7ULMGBPPn5NxZX6rdxU5GheoSNBiJklHQEVf04jU9tI=",
"ref": "master",
"rev": "f1dc58d2b8c222ba41c25cee4301626625f46e43",
"revCount": 2420,
"rev": "4fb0730db4c5de7e86f82fa3bd204e6c4608af85",
"revCount": 2427,
"submodules": true,
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
@ -200,11 +217,11 @@
]
},
"locked": {
"lastModified": 1654002148,
"narHash": "sha256-Ztzx7ze8o0gdu+sDpc+PS2M88h/b22rwFq0b12wMqZQ=",
"ref": "master",
"rev": "6cd32f6ee0e7bb7ce5a279f8be943903a393a476",
"revCount": 617,
"lastModified": 1669819016,
"narHash": "sha256-WvNMUekL4Elc55RdqX8XP43QPnBrK8Rbd0bsoI61E5U=",
"ref": "refs/heads/master",
"rev": "67dbb5932fa8ff5f143983476f741f945871d286",
"revCount": 624,
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
},

View File

@ -1,7 +1,7 @@
{
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.zynq-rs.url = git+https://git.m-labs.hk/m-labs/zynq-rs;
inputs.zynq-rs.inputs.nixpkgs.follows = "artiq/nixpkgs";

View File

@ -15,6 +15,7 @@ from misoc.integration import cpu_interface
from artiq.coredevice import jsondesc
from artiq.gateware import rtio, eem_7series
from artiq.gateware.rtio.phy import ttl_simple
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier
from artiq.gateware.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
@ -118,7 +119,7 @@ class GenericStandalone(SoCCore):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
ident = description["variant"]
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
@ -138,6 +139,7 @@ class GenericStandalone(SoCCore):
self.platform.add_false_path_constraints(
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
fix_serdes_timing_path(platform)
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
@ -204,7 +206,7 @@ class GenericMaster(SoCCore):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
ident = description["variant"]
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
@ -225,6 +227,7 @@ class GenericMaster(SoCCore):
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
self.rustc_cfg["has_si5324"] = None
self.rustc_cfg["si5324_soft_reset"] = None
@ -329,7 +332,7 @@ class GenericSatellite(SoCCore):
platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
ident = self.__class__.__name__
ident = description["variant"]
if self.acpki:
ident = "acpki_" + ident
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
@ -341,6 +344,7 @@ class GenericSatellite(SoCCore):
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
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)]

View File

@ -14,7 +14,7 @@ from misoc.integration import cpu_interface
from misoc.cores import gpio
from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
from artiq.gateware.rtio.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.drtio.transceiver import gtx_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series
@ -562,12 +562,16 @@ class _NIST_QC2_RTIO:
platform.add_extension(pmod1_33)
rtio_channels = []
edge_counter_phy = []
# All TTL channels are In+Out capable
for i in range(40):
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# first four TTLs will also have edge counters
if i < 4:
edge_counter_phy.append(phy)
# no SMA GPIO, replaced with PMOD1_0
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
@ -607,6 +611,11 @@ class _NIST_QC2_RTIO:
self.submodules += phy
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)
rtio_channels.append(rtio.LogChannel())

View File

@ -0,0 +1,145 @@
use libboard_zynq::i2c;
use log::info;
// 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
}
pub struct IoExpander<'a> {
i2c: &'a mut i2c::I2c,
address: u8,
iodir: [u8; 2],
out_current: [u8; 2],
out_target: [u8; 2],
registers: Registers,
}
impl<'a> IoExpander<'a> {
pub fn new(i2c: &'a mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
// Both expanders on SHARED I2C bus
let mut io_expander = match index {
0 => IoExpander {
i2c,
address: 0x40,
iodir: [0xff; 2],
out_current: [0; 2],
out_target: [0; 2],
registers: Registers {
iodira: 0x00,
iodirb: 0x01,
gpioa: 0x12,
gpiob: 0x13,
},
},
1 => IoExpander {
i2c,
address: 0x42,
iodir: [0xff; 2],
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()? {
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()? {
return Err("Neither MCP23017 nor PCA9539 io expander found.");
};
}
Ok(io_expander)
}
fn select(&mut self) -> Result<(), &'static str> {
self.i2c.pca954x_select(0x70, None)?;
self.i2c.pca954x_select(0x71, Some(3))?;
Ok(())
}
fn write(&mut self, addr: u8, value: u8) -> Result<(), &'static str> {
self.i2c.start()?;
self.i2c.write(self.address)?;
self.i2c.write(addr)?;
self.i2c.write(value)?;
self.i2c.stop()?;
Ok(())
}
fn check_ack(&mut self) -> Result<bool, &'static str> {
// Check for ack from io expander
self.select()?;
self.i2c.start()?;
let ack = self.i2c.write(self.address)?;
self.i2c.stop()?;
Ok(ack)
}
fn update_iodir(&mut self) -> Result<(), &'static str> {
self.write(self.registers.iodira, self.iodir[0])?;
self.write(self.registers.iodirb, self.iodir[1])?;
Ok(())
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.select()?;
self.update_iodir()?;
self.out_current[0] = 0x00;
self.write(self.registers.gpioa, 0x00)?;
self.out_current[1] = 0x00;
self.write(self.registers.gpiob, 0x00)?;
Ok(())
}
pub fn set_oe(&mut self, port: u8, outputs: u8) -> Result<(), &'static str> {
self.iodir[port as usize] &= !outputs;
self.update_iodir()?;
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) -> Result<(), &'static str> {
if self.out_target != self.out_current {
self.select()?;
if self.out_target[0] != self.out_current[0] {
self.write(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(self.registers.gpiob, self.out_target[1])?;
self.out_current[1] = self.out_target[1];
}
}
Ok(())
}
}

View File

@ -27,6 +27,8 @@ pub mod drtioaux_async;
#[cfg(has_drtio)]
#[path = "../../../build/mem.rs"]
pub mod mem;
#[cfg(feature = "target_kasli_soc")]
pub mod io_expander;
use core::{cmp, str};
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);
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
let idle_kernel = Rc::new(cfg.read("idle").ok());
if let Ok(buffer) = cfg.read("startup") {
let idle_kernel = Rc::new(cfg.read("idle_kernel").ok());
if let Ok(buffer) = cfg.read("startup_kernel") {
info!("Loading startup kernel...");
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
info!("Starting startup kernel...");

View File

@ -21,6 +21,8 @@ use void::Void;
use libconfig::Config;
use libcortex_a9::l2c::enable_l2_cache;
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_BUSY: u8 = 1 << 1;
@ -109,10 +111,25 @@ pub fn main_core0() {
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
init_gateware();
info!("detected gateware: {}", identifier_read(&mut [0; 64]));
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
i2c::init();
#[cfg(feature = "target_kasli_soc")]
{
let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
for expander_i in 0..=1 {
let mut io_expander = io_expander::IoExpander::new(i2c, expander_i).unwrap();
io_expander.init().expect("I2C I/O expander #0 initialization failed");
// Actively drive TX_DISABLE to false on SFP0..3
io_expander.set_oe(0, 1 << 1).unwrap();
io_expander.set_oe(1, 1 << 1).unwrap();
io_expander.set(0, 1, false);
io_expander.set(1, 1, false);
io_expander.service().unwrap();
}
}
let cfg = match Config::new() {
Ok(cfg) => cfg,
Err(err) => {

View File

@ -15,22 +15,85 @@ use crate::proto_async;
use self::tag::{Tag, TagIterator, split_tag};
#[inline]
fn alignment_offset(alignment: isize, ptr: isize) -> isize {
(alignment - ptr % alignment) % alignment
fn round_up(val: usize, power_of_two: usize) -> usize {
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 {
let alignment = core::mem::align_of::<T>() as isize;
let fix = alignment_offset(alignment, ptr as isize);
((ptr as isize) + fix) as *const T
round_up_const(ptr, core::mem::align_of::<T>()) as *const T
}
#[inline]
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
let alignment = core::mem::align_of::<T>() as isize;
let fix = alignment_offset(alignment, ptr as isize);
((ptr as isize) + fix) as *mut T
round_up_mut(ptr, core::mem::align_of::<T>()) 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 unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, data: &mut *mut (),
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) => {
*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();
for _ in 0..arity {
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(())
}
Tag::List(it) => {
#[repr(C)]
struct List { elements: *mut (), length: u32 }
consume_value!(*mut List, |ptr| {
let length = proto_async::read_i32(stream).await? as usize;
struct List { elements: *mut (), length: usize }
consume_value!(*mut List, |ptr_to_list| {
let tag = it.clone().next().expect("truncated tag");
let data_size = tag.size() * length as usize +
match tag {
Tag::Int64 | Tag::Float64 => 4,
_ => 0
};
let data = alloc(data_size + 8).await as *mut u8;
*ptr = data as *mut List;
let ptr = data as *mut List;
let data = data.offset(8);
let length = proto_async::read_i32(stream).await? as usize;
let alignment = tag.alignment();
let mut data = data.offset(alignment_offset(alignment as isize, data as isize)) as *mut ();
(*ptr).length = length as u32;
(*ptr).elements = data;
match tag {
Tag::Bool => {
let ptr = 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;
// reading as raw bytes and do endianness conversion later
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
proto_async::read_chunk(stream, dest).await?;
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..(*ptr).length as usize {
recv_value(stream, tag, &mut data, alloc).await?
}
}
}
Ok(())
// To avoid multiple kernel CPU roundtrips, use a single allocation for
// both the pointer/length List (slice) and the backing storage for the
// elements. We can assume that alloc() is aligned suitably, so just
// need to take into account any extra padding required.
// (Note: At the time of writing, there will never actually be any types
// with alignment larger than 8 bytes, so storage_offset == 0 always.)
let list_size = 4 + 4;
let storage_offset = round_up(list_size, tag.alignment());
let storage_size = tag.size() * length;
let allocation = alloc(storage_offset + storage_size).await as *mut u8;
*ptr_to_list = allocation as *mut List;
let storage = allocation.offset(storage_offset as isize) as *mut ();
(**ptr_to_list).length = length;
(**ptr_to_list).elements = storage;
recv_elements(stream, tag, length, storage, alloc).await
})
}
Tag::Array(it, num_dims) => {
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 {
let len = proto_async::read_i32(stream).await? as u32;
let len = proto_async::read_i32(stream).await? as usize;
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 data_size = elt_tag.size() * total_len as usize +
match elt_tag {
Tag::Int64 | Tag::Float64 => 4,
_ => 0
};
let mut data = alloc(data_size).await;
let alignment = tag.alignment();
data = data.offset(alignment_offset(alignment as isize, data as isize));
*buffer = data;
let length = total_len as usize;
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(())
*buffer = alloc(elt_tag.size() * total_len).await;
recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
})
}
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");
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(())
}
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 ())
-> Result<(), Error>
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) => {
let mut it = it.clone();
writer.write_u8(arity)?;
let mut max_alignment = 0;
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
max_alignment = core::cmp::max(max_alignment, tag.alignment());
send_value(writer, tag, data)?
}
*data = round_up_const(*data, max_alignment);
Ok(())
}
Tag::List(it) => {
#[repr(C)]
struct List { elements: *const (), length: u32 }
consume_value!(&List, |ptr| {
let length = (**ptr).length as isize;
let length = (**ptr).length as usize;
writer.write_u32((*ptr).length)?;
let tag = it.clone().next().expect("truncated tag");
let mut data = (**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(())
send_elements(writer, tag, length, (**ptr).elements)
})
}
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;
})
}
let mut data = *buffer;
let length = total_len as isize;
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(())
let length = total_len as usize;
send_elements(writer, elt_tag, length, *buffer)
})
}
Tag::Range(it) => {
@ -448,18 +436,15 @@ mod tag {
let it = it.clone();
it.take(3).map(|t| t.alignment()).max().unwrap()
}
// CSlice basically
Tag::Bytes | Tag::String | Tag::ByteArray =>
// the ptr/length(s) pair is basically CSlice
Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) =>
core::mem::align_of::<CSlice<()>>(),
// array buffer is allocated, so no need for alignment first
Tag::List(_) | Tag::Array(_, _) => 1,
// will not be sent from the host
_ => unreachable!("unexpected tag from host")
Tag::Keyword(_) => unreachable!("Tag::Keyword should not appear in composite types"),
Tag::Object => core::mem::align_of::<u32>(),
}
}
pub fn size(self) -> usize {
use super::alignment_offset;
match self {
Tag::None => 0,
Tag::Bool => 1,
@ -471,13 +456,18 @@ mod tag {
Tag::ByteArray => 8,
Tag::Tuple(it, arity) => {
let mut size = 0;
let mut max_alignment = 0;
let mut it = it.clone();
for _ in 0..arity {
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();
// 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
}
Tag::List(_) => 4,

View File

@ -22,6 +22,8 @@ use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, pri
use libsupport_zynq::ram;
#[cfg(has_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 libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache};
use libregister::{RegisterW, RegisterR};
@ -468,6 +470,19 @@ pub extern fn main_core0() -> i32 {
let mut i2c = I2c::i2c0();
i2c.init().expect("I2C initialization failed");
#[cfg(feature = "target_kasli_soc")]
{
for expander_i in 0..=1 {
let mut io_expander = io_expander::IoExpander::new(&mut i2c, expander_i).unwrap();
io_expander.init().expect("I2C I/O expander #0 initialization failed");
// Actively drive TX_DISABLE to false on SFP0..3
io_expander.set_oe(0, 1 << 1).unwrap();
io_expander.set_oe(1, 1 << 1).unwrap();
io_expander.set(0, 1, false);
io_expander.set(1, 1, false);
io_expander.service().unwrap();
}
}
#[cfg(has_si5324)]
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");