From 58b59b99ff665860a5101075d4e2e2a47e35db43 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 19 Apr 2022 11:04:49 +0800 Subject: [PATCH 01/14] flake: update dependencies --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index cd9228e88..7c7414949 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1649313840, - "narHash": "sha256-d23rAnhL08BCc88PxWAhEs/D+Kz3fMW2OTGDXWZSFMI=", + "lastModified": 1650244918, + "narHash": "sha256-DsS5nxjTpnoUC4pNXJI1rit7TnDTij8vQDa5PtcDCD0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e387bb3a4d0d63446086abbfbe2117e80fe3522a", + "rev": "7b38b03d76ab71bdc8dc325e3f6338d984cc35ca", "type": "github" }, "original": { @@ -89,11 +89,11 @@ "src-migen": { "flake": false, "locked": { - "lastModified": 1639659493, - "narHash": "sha256-qpVj/yJf4hDDc99XXpVPH4EbLC8aCmEtACn5qNc3DGI=", + "lastModified": 1650337393, + "narHash": "sha256-rm1SlFmF2ASz0vIy2nDEzGlyRw2oYNeJRr8Kh8Mg2Qc=", "owner": "m-labs", "repo": "migen", - "rev": "ac703010eaa06ac9b6e32f97c6fa98b15de22b31", + "rev": "d4e3f34177c32f09904397179e6ed9c83175e528", "type": "github" }, "original": { From 2cb08814e8fac7da26d7d49b729ab38ead2078a0 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Thu, 21 Apr 2022 23:47:23 +0100 Subject: [PATCH 02/14] flake: Add compiler test prerequisites to devShell Useful while working on the legacy compiler. --- flake.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/flake.nix b/flake.nix index 36e223d53..a455c5a94 100644 --- a/flake.nix +++ b/flake.nix @@ -426,6 +426,10 @@ pkgs.llvmPackages_11.clang-unwrapped pkgs.llvm_11 pkgs.lld_11 + # To manually run compiler tests: + pkgs.lit + outputcheck + libartiq-support # use the vivado-env command to enter a FHS shell that lets you run the Vivado installer packages.x86_64-linux.vivadoEnv packages.x86_64-linux.vivado @@ -433,6 +437,9 @@ pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom latex-artiq-manual ]; + shellHook = '' + export LIBARTIQ_SUPPORT=`libartiq-support` + ''; }; hydraJobs = { From 6b5c390d48c0238e19eb8d019a0dd33bd921e4d8 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Thu, 21 Apr 2022 23:43:20 +0100 Subject: [PATCH 03/14] compiler: Fix #1871 (array() breaks math functions) GitHub: Fixes #1871. --- artiq/compiler/builtins.py | 4 +++- artiq/test/coredevice/test_embedding.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 4524d87dd..fdd5286e1 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -174,7 +174,9 @@ def fn_list(): return types.TConstructor(TList()) def fn_array(): - return types.TConstructor(TArray()) + # numpy.array() is actually a "magic" macro that is expanded in-place, but + # just as for builtin functions, we do not want to quote it, etc. + return types.TBuiltinFunction("array") def fn_Exception(): return types.TExceptionConstructor(TException("Exception")) diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 4e86e9aa5..3f2ca96c9 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -538,3 +538,19 @@ class _Alignment(EnvExperiment): class AlignmentTest(ExperimentCase): def test_tuple(self): self.create(_Alignment).run() + + +class _NumpyQuoting(EnvExperiment): + def build(self): + self.setattr_device("core") + + @kernel + def run(self): + a = np.array([10, 20]) + b = np.sqrt(4.0) + + +class NumpyQuotingTest(ExperimentCase): + def test_issue_1871(self): + """Ensure numpy.array() does not break NumPy math functions""" + self.create(_NumpyQuoting).run() From b2b84b1fd6a12d0927974ea64f7f104724c2ab16 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Fri, 22 Apr 2022 00:28:36 +0100 Subject: [PATCH 04/14] test: Fixup 6b5c390d4 typo --- artiq/test/coredevice/test_embedding.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 3f2ca96c9..3a6498933 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -546,8 +546,8 @@ class _NumpyQuoting(EnvExperiment): @kernel def run(self): - a = np.array([10, 20]) - b = np.sqrt(4.0) + a = numpy.array([10, 20]) + b = numpy.sqrt(4.0) class NumpyQuotingTest(ExperimentCase): From 06ad76b6ab86abdf3070fae8133c9d2d1661df98 Mon Sep 17 00:00:00 2001 From: Suthep Pomjaksilp Date: Fri, 22 Apr 2022 03:27:28 +0200 Subject: [PATCH 05/14] applets: add progress bar applet Signed-off-by: Suthep Pomjaksilp --- artiq/applets/progress_bar.py | 34 ++++++++++++++++++++++++++++++++++ artiq/gui/applets.py | 1 + 2 files changed, 35 insertions(+) create mode 100644 artiq/applets/progress_bar.py diff --git a/artiq/applets/progress_bar.py b/artiq/applets/progress_bar.py new file mode 100644 index 000000000..bbded954c --- /dev/null +++ b/artiq/applets/progress_bar.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +from PyQt5 import QtWidgets + +from artiq.applets.simple import SimpleApplet + + +class ProgressWidget(QtWidgets.QProgressBar): + def __init__(self, args): + QtWidgets.QProgressBar.__init__(self) + self.setMinimum(args.min) + self.setMaximum(args.max) + self.dataset_value = args.value + + def data_changed(self, data, mods): + try: + value = round(data[self.dataset_value][1]) + except (KeyError, ValueError, TypeError): + value = 0 + self.setValue(value) + + + +def main(): + applet = SimpleApplet(ProgressWidget) + applet.add_dataset("value", "counter") + applet.argparser.add_argument("--min", type=int, default=0, + help="minimum (left) value of the bar") + applet.argparser.add_argument("--max", type=int, default=100, + help="maximum (right) value of the bar") + applet.run() + +if __name__ == "__main__": + main() diff --git a/artiq/gui/applets.py b/artiq/gui/applets.py index 178593a07..940149631 100644 --- a/artiq/gui/applets.py +++ b/artiq/gui/applets.py @@ -225,6 +225,7 @@ _templates = [ "HIST_BIN_BOUNDARIES_DATASET " "HISTS_COUNTS_DATASET"), ("Image", "${artiq_applet}image IMG_DATASET"), + ("Progress bar", "${artiq_applet}progress_bar VALUE"), ] From c60de48a300a80c7ee9c3f09322510727f14d741 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Fri, 14 Jan 2022 16:51:43 +0000 Subject: [PATCH 06/14] Upgrade smoltcp 0.6.0 -> 0.8.0 Main changes: Deal with interfaces now being generic over mediums, update interface name and initialisation. Interfaces now own their sockets. So we store a reference to the Interface instead of the SocketSet in Scheduler and IO. Sockets are no longer reference counted. We never called the function to increase the socket's reference count, so now we just remove it where it was previously released. This will result in the socket being dropped at a different time, but I think that should be fine. Tested firmware upload to the bootloader and spamming artiq_coremgmt log calls to download the log from the firmware. Signed-off-by: Michael Birtwell --- artiq/firmware/Cargo.lock | 14 ++- artiq/firmware/bootloader/Cargo.toml | 2 +- artiq/firmware/bootloader/main.rs | 23 ++--- artiq/firmware/libboard_misoc/Cargo.toml | 2 +- artiq/firmware/runtime/Cargo.toml | 2 +- artiq/firmware/runtime/main.rs | 51 ++++------- artiq/firmware/runtime/sched.rs | 107 +++++++++++++---------- 7 files changed, 105 insertions(+), 96 deletions(-) diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index e5d303a00..8e0c7f95a 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -240,6 +240,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "memchr" version = "2.4.1" @@ -321,7 +327,7 @@ dependencies = [ "io", "log", "logger_artiq", - "managed", + "managed 0.7.2", "proto_artiq", "riscv", "smoltcp", @@ -365,13 +371,13 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "smoltcp" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" +checksum = "d2308a1657c8db1f5b4993bab4e620bdbe5623bd81f254cf60326767bb243237" dependencies = [ "bitflags", "byteorder", - "managed", + "managed 0.8.0", ] [[package]] diff --git a/artiq/firmware/bootloader/Cargo.toml b/artiq/firmware/bootloader/Cargo.toml index 5c2a54ba5..9e5ba820a 100644 --- a/artiq/firmware/bootloader/Cargo.toml +++ b/artiq/firmware/bootloader/Cargo.toml @@ -16,5 +16,5 @@ build_misoc = { path = "../libbuild_misoc" } byteorder = { version = "1.0", default-features = false } crc = { version = "1.7", default-features = false } board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] } -smoltcp = { version = "0.6.0", default-features = false, features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } +smoltcp = { version = "0.8.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } riscv = { version = "0.6.0", features = ["inline-asm"] } diff --git a/artiq/firmware/bootloader/main.rs b/artiq/firmware/bootloader/main.rs index 555f8b0b6..535c6234d 100644 --- a/artiq/firmware/bootloader/main.rs +++ b/artiq/firmware/bootloader/main.rs @@ -18,6 +18,8 @@ use board_misoc::slave_fpga; use board_misoc::{clock, ethmac, net_settings}; use board_misoc::uart_console::Console; use riscv::register::{mcause, mepc, mtval}; +use smoltcp::iface::SocketStorage; +use smoltcp::wire::HardwareAddress; fn check_integrity() -> bool { extern { @@ -396,6 +398,9 @@ fn network_boot() { println!("Initializing network..."); + // Assuming only one socket is ever needed by the bootloader. + // The smoltcp reuses the listening socket when the connection is established. + let mut sockets = [SocketStorage::EMPTY]; let mut net_device = unsafe { ethmac::EthernetDevice::new() }; net_device.reset_phy_if_any(); @@ -412,15 +417,15 @@ fn network_boot() { let mut interface = match net_addresses.ipv6_addr { Some(addr) => { ip_addrs[2] = IpCidr::new(addr, 0); - smoltcp::iface::EthernetInterfaceBuilder::new(net_device) - .ethernet_addr(net_addresses.hardware_addr) + smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..]) + .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) .ip_addrs(&mut ip_addrs[..]) .neighbor_cache(neighbor_cache) .finalize() } None => - smoltcp::iface::EthernetInterfaceBuilder::new(net_device) - .ethernet_addr(net_addresses.hardware_addr) + smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..]) + .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) .ip_addrs(&mut ip_addrs[..2]) .neighbor_cache(neighbor_cache) .finalize() @@ -429,14 +434,10 @@ fn network_boot() { let mut rx_storage = [0; 4096]; let mut tx_storage = [0; 128]; - let mut socket_set_entries: [_; 1] = Default::default(); - let mut sockets = - smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]); - let tcp_rx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]); let tcp_tx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]); let tcp_socket = smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - let tcp_handle = sockets.add(tcp_socket); + let tcp_handle = interface.add_socket(tcp_socket); let mut net_conn = NetConn::new(); let mut boot_time = None; @@ -446,7 +447,7 @@ fn network_boot() { loop { let timestamp = clock::get_ms() as i64; { - let socket = &mut *sockets.get::(tcp_handle); + let socket = &mut *interface.get_socket::(tcp_handle); match boot_time { None => { @@ -475,7 +476,7 @@ fn network_boot() { } } - match interface.poll(&mut sockets, smoltcp::time::Instant::from_millis(timestamp)) { + match interface.poll(smoltcp::time::Instant::from_millis(timestamp)) { Ok(_) => (), Err(smoltcp::Error::Unrecognized) => (), Err(err) => println!("Network error: {}", err) diff --git a/artiq/firmware/libboard_misoc/Cargo.toml b/artiq/firmware/libboard_misoc/Cargo.toml index 0d8d705cc..69c1d46d3 100644 --- a/artiq/firmware/libboard_misoc/Cargo.toml +++ b/artiq/firmware/libboard_misoc/Cargo.toml @@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" } [dependencies] byteorder = { version = "1.0", default-features = false } log = { version = "0.4", default-features = false, optional = true } -smoltcp = { version = "0.6.0", default-features = false, optional = true } +smoltcp = { version = "0.8.0", default-features = false, optional = true } riscv = { version = "0.6.0", features = ["inline-asm"] } [features] diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index ee987ad2d..241246821 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -27,7 +27,7 @@ board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp logger_artiq = { path = "../liblogger_artiq" } board_artiq = { path = "../libboard_artiq" } proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] } -smoltcp = { version = "0.6.0", default-features = false, features = ["alloc", "ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } +smoltcp = { version = "0.8.0", default-features = false, features = ["alloc", "medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } riscv = { version = "0.6.0", features = ["inline-asm"] } [dependencies.fringe] diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index 45fb76f91..f8843352d 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -27,7 +27,7 @@ extern crate riscv; use core::cell::RefCell; use core::convert::TryFrom; -use smoltcp::wire::IpCidr; +use smoltcp::wire::{IpCidr, HardwareAddress}; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot}; #[cfg(has_ethmac)] @@ -123,38 +123,33 @@ fn startup() { net_device.reset_phy_if_any(); let net_device = { - use smoltcp::time::Instant; - use smoltcp::wire::PrettyPrinter; - use smoltcp::wire::EthernetFrame; + use smoltcp::phy::Tracer; - fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter>) { - print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n", - timestamp.secs(), timestamp.millis(), printer) - } - - fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter>) {} - - let net_trace_fn: fn(Instant, PrettyPrinter>); + // We can't create the function pointer as a separate variable here because the type of + // the packet argument Packet isn't accessible and rust's type inference isn't sufficient + // to propagate in to a local var. match config::read_str("net_trace", |r| r.map(|s| s == "1")) { - Ok(true) => net_trace_fn = net_trace_writer, - _ => net_trace_fn = net_trace_silent + Ok(true) => Tracer::new(net_device, |timestamp, packet| { + print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n", + timestamp.secs(), timestamp.millis(), packet) + }), + _ => Tracer::new(net_device, |_, _| {}), } - smoltcp::phy::EthernetTracer::new(net_device, net_trace_fn) }; let neighbor_cache = smoltcp::iface::NeighborCache::new(alloc::collections::btree_map::BTreeMap::new()); let net_addresses = net_settings::get_adresses(); info!("network addresses: {}", net_addresses); - let mut interface = match net_addresses.ipv6_addr { + let interface = match net_addresses.ipv6_addr { Some(addr) => { let ip_addrs = [ IpCidr::new(net_addresses.ipv4_addr, 0), IpCidr::new(net_addresses.ipv6_ll_addr, 0), IpCidr::new(addr, 0) ]; - smoltcp::iface::EthernetInterfaceBuilder::new(net_device) - .ethernet_addr(net_addresses.hardware_addr) + smoltcp::iface::InterfaceBuilder::new(net_device, vec![]) + .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) .ip_addrs(ip_addrs) .neighbor_cache(neighbor_cache) .finalize() @@ -164,8 +159,8 @@ fn startup() { IpCidr::new(net_addresses.ipv4_addr, 0), IpCidr::new(net_addresses.ipv6_ll_addr, 0) ]; - smoltcp::iface::EthernetInterfaceBuilder::new(net_device) - .ethernet_addr(net_addresses.hardware_addr) + smoltcp::iface::InterfaceBuilder::new(net_device, vec![]) + .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) .ip_addrs(ip_addrs) .neighbor_cache(neighbor_cache) .finalize() @@ -184,7 +179,7 @@ fn startup() { drtio_routing::interconnect_disable_all(); let aux_mutex = sched::Mutex::new(); - let mut scheduler = sched::Scheduler::new(); + let mut scheduler = sched::Scheduler::new(interface); let io = scheduler.io(); rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations); @@ -211,19 +206,7 @@ fn startup() { let mut net_stats = ethmac::EthernetStatistics::new(); loop { scheduler.run(); - - { - let sockets = &mut *scheduler.sockets().borrow_mut(); - loop { - let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64); - match interface.poll(sockets, timestamp) { - Ok(true) => (), - Ok(false) => break, - Err(smoltcp::Error::Unrecognized) => (), - Err(err) => debug!("network error: {}", err) - } - } - } + scheduler.run_network(); if let Some(_net_stats_diff) = net_stats.update() { debug!("ethernet mac:{}", ethmac::EthernetStatistics::new()); diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index d6a347269..991569cd1 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -9,11 +9,13 @@ use fringe::generator::{Generator, Yielder, State as GeneratorState}; use smoltcp::time::Duration; use smoltcp::Error as NetworkError; use smoltcp::wire::IpEndpoint; -use smoltcp::socket::{SocketHandle, SocketRef}; +use smoltcp::iface::{Interface, SocketHandle}; use io::{Read, Write}; use board_misoc::clock; use urc::Urc; +use board_misoc::ethmac::EthernetDevice; +use smoltcp::phy::Tracer; #[derive(Fail, Debug)] pub enum Error { @@ -31,8 +33,6 @@ impl From for Error { } } -type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>; - #[derive(Debug)] struct WaitRequest { event: Option<*mut dyn FnMut() -> bool>, @@ -59,7 +59,7 @@ impl Thread { unsafe fn new(io: &Io, stack_size: usize, f: F) -> ThreadHandle where F: 'static + FnOnce(Io) + Send { let spawned = io.spawned.clone(); - let sockets = io.sockets.clone(); + let network = io.network.clone(); // Add a 4k stack guard to the stack of any new threads let stack = OwnedStack::new(stack_size + 4096); @@ -67,8 +67,8 @@ impl Thread { generator: Generator::unsafe_new(stack, |yielder, _| { f(Io { yielder: Some(yielder), - spawned: spawned, - sockets: sockets + spawned, + network }) }), waiting_for: WaitRequest { @@ -115,19 +115,21 @@ impl ThreadHandle { } } +type Network = Interface<'static, Tracer>; + pub struct Scheduler { threads: Vec, spawned: Urc>>, - sockets: Urc>, + network: Urc>, run_idx: usize, } impl Scheduler { - pub fn new() -> Scheduler { + pub fn new(network: Network) -> Scheduler { Scheduler { threads: Vec::new(), spawned: Urc::new(RefCell::new(Vec::new())), - sockets: Urc::new(RefCell::new(SocketSet::new(Vec::new()))), + network: Urc::new(RefCell::new(network)), run_idx: 0, } } @@ -136,13 +138,11 @@ impl Scheduler { Io { yielder: None, spawned: self.spawned.clone(), - sockets: self.sockets.clone() + network: self.network.clone() } } pub fn run(&mut self) { - self.sockets.borrow_mut().prune(); - self.threads.append(&mut *self.spawned.borrow_mut()); if self.threads.len() == 0 { return } @@ -188,8 +188,17 @@ impl Scheduler { } } - pub fn sockets(&self) -> &RefCell { - &*self.sockets + pub fn run_network(&mut self) { + let mut interface = self.network.borrow_mut(); + loop { + let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64); + match interface.poll(timestamp) { + Ok(true) => (), + Ok(false) => break, + Err(smoltcp::Error::Unrecognized) => (), + Err(err) => debug!("network error: {}", err) + } + } } } @@ -197,7 +206,7 @@ impl Scheduler { pub struct Io<'a> { yielder: Option<&'a Yielder>, spawned: Urc>>, - sockets: Urc>, + network: Urc>, } impl<'a> Io<'a> { @@ -291,10 +300,10 @@ impl<'a> Drop for MutexGuard<'a> { macro_rules! until { ($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({ - let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle); + let (network, handle) = ($socket.io.network.clone(), $socket.handle); $socket.io.until(move || { - let mut sockets = sockets.borrow_mut(); - let $var = sockets.get::<$ty>(handle); + let mut network = network.borrow_mut(); + let $var = network.get_socket::<$ty>(handle); $cond }) }) @@ -316,9 +325,9 @@ impl<'a> TcpListener<'a> { fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle { let rx_buffer = vec![0; buffer_size]; let tx_buffer = vec![0; buffer_size]; - io.sockets + io.network .borrow_mut() - .add(TcpSocketLower::new( + .add_socket(TcpSocketLower::new( TcpSocketBuffer::new(rx_buffer), TcpSocketBuffer::new(tx_buffer))) } @@ -333,9 +342,9 @@ impl<'a> TcpListener<'a> { } fn with_lower(&self, f: F) -> R - where F: FnOnce(SocketRef) -> R { - let mut sockets = self.io.sockets.borrow_mut(); - let result = f(sockets.get(self.handle.get())); + where F: FnOnce(&mut TcpSocketLower) -> R { + let mut network = self.io.network.borrow_mut(); + let result = f(network.get_socket(self.handle.get())); result } @@ -353,7 +362,7 @@ impl<'a> TcpListener<'a> { pub fn listen>(&self, endpoint: T) -> Result<(), Error> { let endpoint = endpoint.into(); - self.with_lower(|mut s| s.listen(endpoint)) + self.with_lower(|s| s.listen(endpoint)) .map(|()| { self.endpoint.set(endpoint); () @@ -365,10 +374,10 @@ impl<'a> TcpListener<'a> { // We're waiting until at least one half of the connection becomes open. // This handles the case where a remote socket immediately sends a FIN-- // that still counts as accepting even though nothing may be sent. - let (sockets, handle) = (self.io.sockets.clone(), self.handle.get()); + let (network, handle) = (self.io.network.clone(), self.handle.get()); self.io.until(move || { - let mut sockets = sockets.borrow_mut(); - let socket = sockets.get::(handle); + let mut network = network.borrow_mut(); + let socket = network.get_socket::(handle); socket.may_send() || socket.may_recv() })?; @@ -385,14 +394,14 @@ impl<'a> TcpListener<'a> { } pub fn close(&self) { - self.with_lower(|mut s| s.close()) + self.with_lower(|s| s.close()) } } impl<'a> Drop for TcpListener<'a> { fn drop(&mut self) { - self.with_lower(|mut s| s.close()); - self.io.sockets.borrow_mut().release(self.handle.get()) + self.with_lower(|s| s.close()); + self.io.network.borrow_mut().remove_socket(self.handle.get()); } } @@ -416,9 +425,9 @@ impl<'a> TcpStream<'a> { } fn with_lower(&self, f: F) -> R - where F: FnOnce(SocketRef) -> R { - let mut sockets = self.io.sockets.borrow_mut(); - let result = f(sockets.get(self.handle)); + where F: FnOnce(&mut TcpSocketLower) -> R { + let mut network = self.io.network.borrow_mut(); + let result = f(network.get_socket(self.handle)); result } @@ -455,7 +464,7 @@ impl<'a> TcpStream<'a> { } pub fn set_timeout(&self, value: Option) { - self.with_lower(|mut s| s.set_timeout(value.map(Duration::from_millis))) + self.with_lower(|s| s.set_timeout(value.map(Duration::from_millis))) } pub fn keep_alive(&self) -> Option { @@ -463,11 +472,11 @@ impl<'a> TcpStream<'a> { } pub fn set_keep_alive(&self, value: Option) { - self.with_lower(|mut s| s.set_keep_alive(value.map(Duration::from_millis))) + self.with_lower(|s| s.set_keep_alive(value.map(Duration::from_millis))) } pub fn close(&self) -> Result<(), Error> { - self.with_lower(|mut s| s.close()); + self.with_lower(|s| s.close()); until!(self, TcpSocketLower, |s| !s.is_open())?; // right now the socket may be in TIME-WAIT state. if we don't give it a chance to send // a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); } @@ -481,23 +490,33 @@ impl<'a> Read for TcpStream<'a> { fn read(&mut self, buf: &mut [u8]) -> Result { // Only borrow the underlying socket for the span of the next statement. - let result = self.with_lower(|mut s| s.recv_slice(buf)); + let result = self.with_lower(|s| s.recv_slice(buf)); match result { // Slow path: we need to block until buffer is non-empty. Ok(0) => { until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?; - match self.with_lower(|mut s| s.recv_slice(buf)) { + match self.with_lower(|s| s.recv_slice(buf)) { Ok(length) => Ok(length), + Err(NetworkError::Finished) | Err(NetworkError::Illegal) => Ok(0), - _ => unreachable!() + Err(e) => { + panic!("Unexpected error from smoltcp: {}", e); + } } } // Fast path: we had data in buffer. Ok(length) => Ok(length), + // We've received a fin. + Err(NetworkError::Finished) | // Error path: the receive half of the socket is not open. Err(NetworkError::Illegal) => Ok(0), // No other error may be returned. - Err(_) => unreachable!() + Err(e) => { + // This could return Err(Error::Network(e)) rather than panic, + // but I expect that'll just cause a panic later perhaps with + // less interesting context. + panic!("Unexpected error from smoltcp: {}", e); + } } } } @@ -508,12 +527,12 @@ impl<'a> Write for TcpStream<'a> { fn write(&mut self, buf: &[u8]) -> Result { // Only borrow the underlying socket for the span of the next statement. - let result = self.with_lower(|mut s| s.send_slice(buf)); + let result = self.with_lower(|s| s.send_slice(buf)); match result { // Slow path: we need to block until buffer is non-full. Ok(0) => { until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?; - match self.with_lower(|mut s| s.send_slice(buf)) { + match self.with_lower(|s| s.send_slice(buf)) { Ok(length) => Ok(length), Err(NetworkError::Illegal) => Ok(0), _ => unreachable!() @@ -540,7 +559,7 @@ impl<'a> Write for TcpStream<'a> { impl<'a> Drop for TcpStream<'a> { fn drop(&mut self) { - self.with_lower(|mut s| s.close()); - self.io.sockets.borrow_mut().release(self.handle) + self.with_lower(|s| s.close()); + self.io.network.borrow_mut().remove_socket(self.handle); } } From 6ffb1f83eecfb05e774f60715451cdb2a396e9b9 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Fri, 14 Jan 2022 16:52:12 +0000 Subject: [PATCH 07/14] DHCP support for core device firmware DHCP is enabled by setting the `ip` config entry to "use_dhcp". Reusing this config field rather than creating a new one means that there is no ambiguity over which config field takes precedence. Adds a thread to configure the interface based on DHCP events Adds a `Dhcpv4Socket` as a wrapper around smoltcp's version Formalises the storage of the IP addresses so that we can update one in another module. There's also a workaround for the first DHCP discover packet frequently going missing. Signed-off-by: Michael Birtwell --- RELEASE_NOTES.rst | 1 + artiq/firmware/libboard_misoc/net_settings.rs | 13 +++- artiq/firmware/runtime/Cargo.toml | 6 +- artiq/firmware/runtime/dhcp.rs | 59 ++++++++++++++++++ artiq/firmware/runtime/main.rs | 61 +++++++++++-------- artiq/firmware/runtime/sched.rs | 53 +++++++++++++++- doc/manual/installing.rst | 6 ++ 7 files changed, 169 insertions(+), 30 deletions(-) create mode 100644 artiq/firmware/runtime/dhcp.rs diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index a14fafd11..d592a2743 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -42,6 +42,7 @@ Highlights: * ``PCA9548`` I2C switch class renamed to ``I2CSwitch``, to accomodate support for PCA9547, and possibly other switches in future. Readback has been removed, and now only one channel per switch is supported. +* The "ip" config option can now be set to "use_dhcp" to use DHCP to obtain an IP address. Breaking changes: diff --git a/artiq/firmware/libboard_misoc/net_settings.rs b/artiq/firmware/libboard_misoc/net_settings.rs index 2663be5db..754f326fa 100644 --- a/artiq/firmware/libboard_misoc/net_settings.rs +++ b/artiq/firmware/libboard_misoc/net_settings.rs @@ -1,12 +1,15 @@ use core::fmt; -use smoltcp::wire::{EthernetAddress, IpAddress}; +use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address}; use config; #[cfg(soc_platform = "kasli")] use i2c_eeprom; +pub const USE_DHCP: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED); + + pub struct NetAddresses { pub hardware_addr: EthernetAddress, pub ipv4_addr: IpAddress, @@ -49,7 +52,13 @@ pub fn get_adresses() -> NetAddresses { } let ipv4_addr; - match config::read_str("ip", |r| r.map(|s| s.parse())) { + match config::read_str("ip", |r| r.map(|s| { + if s == "use_dhcp" { + Ok(USE_DHCP) + } else { + s.parse() + } + })) { Ok(Ok(addr)) => ipv4_addr = addr, _ => { #[cfg(soc_platform = "kasli")] diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 241246821..85d05768c 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -27,9 +27,13 @@ board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp logger_artiq = { path = "../liblogger_artiq" } board_artiq = { path = "../libboard_artiq" } proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] } -smoltcp = { version = "0.8.0", default-features = false, features = ["alloc", "medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] } riscv = { version = "0.6.0", features = ["inline-asm"] } +[dependencies.smoltcp] +version = "0.8.0" +default-features = false +features = ["alloc", "medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp", "socket-dhcpv4"] + [dependencies.fringe] git = "https://git.m-labs.hk/M-Labs/libfringe.git" rev = "3ecbe5" diff --git a/artiq/firmware/runtime/dhcp.rs b/artiq/firmware/runtime/dhcp.rs new file mode 100644 index 000000000..0e7d3e438 --- /dev/null +++ b/artiq/firmware/runtime/dhcp.rs @@ -0,0 +1,59 @@ +use board_misoc::clock; +use sched; +use sched::Dhcpv4Socket; +use smoltcp::socket::Dhcpv4Event; +use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; + +pub fn dhcp_thread(io: sched::Io) { + let mut socket = Dhcpv4Socket::new(&io); + let mut last_ip: Option = None; + let mut done_reset = false; + let start_time = clock::get_ms(); + + loop { + // A significant amount of the time our first discover isn't received + // by the server. This is likely to be because the ethernet device isn't quite + // ready at the point that it is sent. The following makes recovery from + // that faster. + if !done_reset && last_ip.is_none() && start_time + 1000 < clock::get_ms() { + info!("Didn't get initial IP in first second. Assuming packet loss, trying a reset."); + socket.reset(); + done_reset = true; + } + if let Some(event) = socket.poll() { + match event { + Dhcpv4Event::Configured(config) => { + // Only compare the ip address in the config with previous config because we + // ignore the rest of the config i.e. we don't do any DNS or require a default + // gateway. + let changed = if let Some(last_ip) = last_ip { + if config.address != last_ip { + info!("IP address changed {} -> {}", last_ip, config.address); + true + } else { + false + } + } else { + info!("Acquired IP address: None -> {}", config.address); + true + }; + if changed { + last_ip = Some(config.address); + io.set_ipv4_address(&config.address); + } + } + Dhcpv4Event::Deconfigured => { + if let Some(ip) = last_ip { + info!("Lost IP address {} -> None", ip); + io.set_ipv4_address(&Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + last_ip = None; + } + // We always get one of these events at the start, ignore that one + } + } + } + // We want to poll after every poll of the interface. So we need to + // do a minimal yield here. + io.relinquish().unwrap(); + } +} diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index f8843352d..ab731a6b9 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -27,11 +27,12 @@ extern crate riscv; use core::cell::RefCell; use core::convert::TryFrom; -use smoltcp::wire::{IpCidr, HardwareAddress}; +use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr, Ipv4Address}; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot}; #[cfg(has_ethmac)] use board_misoc::ethmac; +use board_misoc::net_settings::{NetAddresses, USE_DHCP}; #[cfg(has_drtio)] use board_artiq::drtioaux; use board_artiq::drtio_routing; @@ -58,6 +59,14 @@ mod session; mod moninj; #[cfg(has_rtio_analyzer)] mod analyzer; +mod dhcp; + +// Fixed indexes for the IP addresses so that they can be modified with some +// semblance of confidence +pub const IPV4_INDEX: usize = 0; +pub const IPV6_LL_INDEX: usize = 1; +pub const IPV6_INDEX: usize = 2; +const IP_ADDRESS_STORAGE_SIZE: usize = 3; #[cfg(has_grabber)] fn grabber_thread(io: sched::Io) { @@ -87,6 +96,18 @@ fn setup_log_levels() { } } +pub fn get_ip_addrs(net_addresses: &NetAddresses) -> [IpCidr; IP_ADDRESS_STORAGE_SIZE] { + let mut storage = [ + IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE + ]; + storage[IPV4_INDEX] = IpCidr::new(net_addresses.ipv4_addr, 0); + storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0); + if let Some(ipv6) = net_addresses.ipv6_addr { + storage[IPV6_INDEX] = IpCidr::new(ipv6, 0); + } + storage +} + fn startup() { clock::init(); info!("ARTIQ runtime starting..."); @@ -141,31 +162,17 @@ fn startup() { smoltcp::iface::NeighborCache::new(alloc::collections::btree_map::BTreeMap::new()); let net_addresses = net_settings::get_adresses(); info!("network addresses: {}", net_addresses); - let interface = match net_addresses.ipv6_addr { - Some(addr) => { - let ip_addrs = [ - IpCidr::new(net_addresses.ipv4_addr, 0), - IpCidr::new(net_addresses.ipv6_ll_addr, 0), - IpCidr::new(addr, 0) - ]; - smoltcp::iface::InterfaceBuilder::new(net_device, vec![]) - .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) - .ip_addrs(ip_addrs) - .neighbor_cache(neighbor_cache) - .finalize() - } - None => { - let ip_addrs = [ - IpCidr::new(net_addresses.ipv4_addr, 0), - IpCidr::new(net_addresses.ipv6_ll_addr, 0) - ]; - smoltcp::iface::InterfaceBuilder::new(net_device, vec![]) - .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) - .ip_addrs(ip_addrs) - .neighbor_cache(neighbor_cache) - .finalize() - } + let use_dhcp = if net_addresses.ipv4_addr == USE_DHCP { + info!("Will try to acquire an IPv4 address with DHCP"); + true + } else { + false }; + let interface = smoltcp::iface::InterfaceBuilder::new(net_device, vec![]) + .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) + .ip_addrs(get_ip_addrs(&net_addresses)) + .neighbor_cache(neighbor_cache) + .finalize(); #[cfg(has_drtio)] let drtio_routing_table = urc::Urc::new(RefCell::new( @@ -182,6 +189,10 @@ fn startup() { let mut scheduler = sched::Scheduler::new(interface); let io = scheduler.io(); + if use_dhcp { + io.spawn(4096, dhcp::dhcp_thread); + } + rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations); io.spawn(4096, mgmt::thread); diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index 991569cd1..a5903c2d1 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -2,13 +2,13 @@ use core::mem; use core::result; -use core::cell::{Cell, RefCell}; +use core::cell::{Cell, RefCell, RefMut}; use alloc::vec::Vec; use fringe::OwnedStack; use fringe::generator::{Generator, Yielder, State as GeneratorState}; use smoltcp::time::Duration; use smoltcp::Error as NetworkError; -use smoltcp::wire::IpEndpoint; +use smoltcp::wire::{IpEndpoint, Ipv4Cidr, IpCidr}; use smoltcp::iface::{Interface, SocketHandle}; use io::{Read, Write}; @@ -16,6 +16,7 @@ use board_misoc::clock; use urc::Urc; use board_misoc::ethmac::EthernetDevice; use smoltcp::phy::Tracer; +use IPV4_INDEX; #[derive(Fail, Debug)] pub enum Error { @@ -273,6 +274,12 @@ impl<'a> Io<'a> { pub fn join(&self, handle: ThreadHandle) -> Result<(), Error> { self.until(move || handle.terminated()) } + + pub fn set_ipv4_address(&self, addr: &Ipv4Cidr) { + self.network.borrow_mut().update_ip_addrs(|addrs| { + addrs.as_mut()[IPV4_INDEX] = IpCidr::Ipv4(*addr); + }) + } } #[derive(Clone)] @@ -563,3 +570,45 @@ impl<'a> Drop for TcpStream<'a> { self.io.network.borrow_mut().remove_socket(self.handle); } } + +pub struct Dhcpv4Socket<'a> { + io: &'a Io<'a>, + handle: SocketHandle, +} + +impl<'a> Dhcpv4Socket<'a> { + + fn new_lower(io: &'a Io<'a>) -> SocketHandle { + let socket = smoltcp::socket::Dhcpv4Socket::new(); + io.network.borrow_mut().add_socket(socket) + } + + pub fn new(io: &'a Io<'a>) -> Self { + Self { + io, + handle: Self::new_lower(io) + } + } + + fn lower(&mut self) -> RefMut { + RefMut::map( + self.io.network.borrow_mut(), + |network| network.get_socket::(self.handle), + ) + } + + pub fn poll(&mut self) -> Option { + self.lower().poll() + } + + pub fn reset(&mut self) { + self.lower().reset() + } +} + +impl<'a> Drop for Dhcpv4Socket<'a> { + fn drop(&mut self) { + let mut network = self.io.network.borrow_mut(); + network.remove_socket(self.handle); + } +} diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 3bc38cd41..6ee48c75c 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -268,6 +268,12 @@ In other cases, install OpenOCD as before, and flash the IP (and, if necessary, For Kasli devices, flashing a MAC address is not necessary as they can obtain it from their EEPROM. +Alternatively you can set the "ip" config field to "use_dhcp" to have the device use DHCP to obtain an IP address on +boot. e.g. :: + + $ artiq_mkfs flash_storage.img -s ip use_dhcp + $ artiq_flash -t [board] -V [variant] -f flash_storage.img storage start + Check that you can ping the device. If ping fails, check that the Ethernet link LED is ON - on Kasli, it is the LED next to the SFP0 connector. As a next step, look at the messages emitted on the UART during boot. Use a program such as flterm or PuTTY to connect to the device's serial port at 115200bps 8-N-1 and reboot the device. On Kasli, the serial port is on FTDI channel 2 with v1.1 hardware (with channel 0 being JTAG) and on FTDI channel 1 with v1.0 hardware. If you want to use IPv6, the device also has a link-local address that corresponds to its EUI-64, and an additional arbitrary IPv6 address can be defined by using the ``ip6`` configuration key. All IPv4 and IPv6 addresses can be used at the same time. From 596b9a265c5b52b3e30f90c898784aca86dd0aa4 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Fri, 14 Jan 2022 16:52:23 +0000 Subject: [PATCH 08/14] Prefer DHCP to the built-in static IPs Signed-off-by: Michael Birtwell --- RELEASE_NOTES.rst | 3 ++- artiq/firmware/libboard_misoc/net_settings.rs | 11 +---------- doc/manual/installing.rst | 10 +++++----- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index d592a2743..0bd400186 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -42,7 +42,8 @@ Highlights: * ``PCA9548`` I2C switch class renamed to ``I2CSwitch``, to accomodate support for PCA9547, and possibly other switches in future. Readback has been removed, and now only one channel per switch is supported. -* The "ip" config option can now be set to "use_dhcp" to use DHCP to obtain an IP address. +* The "ip" config option can now be set to "use_dhcp" in order to use DHCP to obtain an IP address. + DHCP will also be used if no "ip" config option is set. Breaking changes: diff --git a/artiq/firmware/libboard_misoc/net_settings.rs b/artiq/firmware/libboard_misoc/net_settings.rs index 754f326fa..4c70dece6 100644 --- a/artiq/firmware/libboard_misoc/net_settings.rs +++ b/artiq/firmware/libboard_misoc/net_settings.rs @@ -60,16 +60,7 @@ pub fn get_adresses() -> NetAddresses { } })) { Ok(Ok(addr)) => ipv4_addr = addr, - _ => { - #[cfg(soc_platform = "kasli")] - { ipv4_addr = IpAddress::v4(192, 168, 1, 70); } - #[cfg(soc_platform = "sayma_amc")] - { ipv4_addr = IpAddress::v4(192, 168, 1, 60); } - #[cfg(soc_platform = "metlino")] - { ipv4_addr = IpAddress::v4(192, 168, 1, 65); } - #[cfg(soc_platform = "kc705")] - { ipv4_addr = IpAddress::v4(192, 168, 1, 50); } - } + _ => ipv4_addr = USE_DHCP, } let ipv6_ll_addr = IpAddress::v6( diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 6ee48c75c..1695aeac6 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -261,18 +261,18 @@ If you purchased a Kasli device from M-Labs, it usually comes with the IP addres and then reboot the device (with ``artiq_flash start`` or a power cycle). -In other cases, install OpenOCD as before, and flash the IP (and, if necessary, MAC) addresses directly: :: +If the ip config field is not set, or set to "use_dhcp" then the device will attempt to obtain an IP address using +DHCP. If a static IP address is wanted, install OpenOCD as before, and flash the IP (and, if necessary, MAC) addresses +directly: :: $ artiq_mkfs flash_storage.img -s mac xx:xx:xx:xx:xx:xx -s ip xx.xx.xx.xx $ artiq_flash -t [board] -V [variant] -f flash_storage.img storage start For Kasli devices, flashing a MAC address is not necessary as they can obtain it from their EEPROM. -Alternatively you can set the "ip" config field to "use_dhcp" to have the device use DHCP to obtain an IP address on -boot. e.g. :: +If DHCP has been used the address can be found in the console output, which can be viewed using: :: - $ artiq_mkfs flash_storage.img -s ip use_dhcp - $ artiq_flash -t [board] -V [variant] -f flash_storage.img storage start + $ python -m misoc.tools.flterm /dev/ttyUSB2 Check that you can ping the device. If ping fails, check that the Ethernet link LED is ON - on Kasli, it is the LED next to the SFP0 connector. As a next step, look at the messages emitted on the UART during boot. Use a program such as flterm or PuTTY to connect to the device's serial port at 115200bps 8-N-1 and reboot the device. On Kasli, the serial port is on FTDI channel 2 with v1.1 hardware (with channel 0 being JTAG) and on FTDI channel 1 with v1.0 hardware. From 73082d116f639e5985451d157a5f23e72531f2b4 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Fri, 25 Mar 2022 15:25:24 +0000 Subject: [PATCH 09/14] Ensure that pending data is sent when closing sockets This is only necessary if close hasn't been called on the socket but that's not always done. e.g. by the core analyzer server. --- artiq/firmware/runtime/sched.rs | 49 ++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index a5903c2d1..fc3c7ac04 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -250,14 +250,26 @@ impl<'a> Io<'a> { }) } - pub fn until bool>(&self, mut f: F) -> Result<(), Error> { + pub fn inner_until bool>( + &self, + timeout: Option, + mut f: F + ) -> Result<(), Error> { let f = unsafe { mem::transmute::<&mut dyn FnMut() -> bool, *mut dyn FnMut() -> bool>(&mut f) }; self.suspend(WaitRequest { - timeout: None, - event: Some(f) + timeout, + event: Some(f), }) } + pub fn until bool>(&self, f: F) -> Result<(), Error> { + self.inner_until(None, f) + } + + pub fn until_with_timeout bool>(&self, timeout: u64, f: F) -> Result<(), Error> { + self.inner_until(Some(timeout), f) + } + pub fn until_ok(&self, mut f: F) -> Result where F: FnMut() -> result::Result { @@ -313,7 +325,15 @@ macro_rules! until { let $var = network.get_socket::<$ty>(handle); $cond }) - }) + }); + ($socket:expr, $ty:ty, timeout=$timeout:expr, |$var:ident| $cond:expr) => ({ + let (network, handle) = ($socket.io.network.clone(), $socket.handle); + $socket.io.until_with_timeout($timeout, move || { + let mut network = network.borrow_mut(); + let $var = network.get_socket::<$ty>(handle); + $cond + }) + }); } type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>; @@ -567,6 +587,27 @@ impl<'a> Write for TcpStream<'a> { impl<'a> Drop for TcpStream<'a> { fn drop(&mut self) { self.with_lower(|s| s.close()); + let result = until!( + self, TcpSocketLower, timeout=clock::get_ms() + 1000, |s| !s.is_active() + ); + let unsent_bytes = self.with_lower(|s| s.send_queue()); + match result{ + Ok(()) => { + if unsent_bytes != 0 { + // This is normal if we received a reset whilst sending + debug!("Dropping socket with {} bytes unsent", unsent_bytes) + } + } + Err(Error::TimedOut) => { + warn!( + "Timed out whilst waiting for socket to close during drop, with {} unsent bytes", + unsent_bytes + ); + } + Err(e) => { + error!("Unexpected error whilst waiting for socket to close during drop: {:?}", e) + } + } self.io.network.borrow_mut().remove_socket(self.handle); } } From 1fe59d27dc1fa4c55ba2e89ca5a52c2523079141 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Tue, 12 Apr 2022 11:39:47 +0100 Subject: [PATCH 10/14] Use an Ipv4AddrConfig enum instead of the USE_DHCP constant --- artiq/firmware/bootloader/main.rs | 7 +++- artiq/firmware/libboard_misoc/net_settings.rs | 40 ++++++++++++++----- artiq/firmware/runtime/main.rs | 8 ++-- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/artiq/firmware/bootloader/main.rs b/artiq/firmware/bootloader/main.rs index 535c6234d..c496a79b7 100644 --- a/artiq/firmware/bootloader/main.rs +++ b/artiq/firmware/bootloader/main.rs @@ -19,7 +19,7 @@ use board_misoc::{clock, ethmac, net_settings}; use board_misoc::uart_console::Console; use riscv::register::{mcause, mepc, mtval}; use smoltcp::iface::SocketStorage; -use smoltcp::wire::HardwareAddress; +use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address}; fn check_integrity() -> bool { extern { @@ -410,10 +410,13 @@ fn network_boot() { let net_addresses = net_settings::get_adresses(); println!("Network addresses: {}", net_addresses); let mut ip_addrs = [ - IpCidr::new(net_addresses.ipv4_addr, 0), + IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0), IpCidr::new(net_addresses.ipv6_ll_addr, 0), IpCidr::new(net_addresses.ipv6_ll_addr, 0) ]; + if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { + ip_addrs[0] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); + } let mut interface = match net_addresses.ipv6_addr { Some(addr) => { ip_addrs[2] = IpCidr::new(addr, 0); diff --git a/artiq/firmware/libboard_misoc/net_settings.rs b/artiq/firmware/libboard_misoc/net_settings.rs index 4c70dece6..466e1a893 100644 --- a/artiq/firmware/libboard_misoc/net_settings.rs +++ b/artiq/firmware/libboard_misoc/net_settings.rs @@ -1,4 +1,6 @@ use core::fmt; +use core::fmt::{Display, Formatter}; +use core::str::FromStr; use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address}; @@ -7,12 +9,36 @@ use config; use i2c_eeprom; -pub const USE_DHCP: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED); +pub enum Ipv4AddrConfig { + UseDhcp, + Static(Ipv4Address), +} + +impl FromStr for Ipv4AddrConfig { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(if s == "use_dhcp" { + Ipv4AddrConfig::UseDhcp + } else { + Ipv4AddrConfig::Static(Ipv4Address::from_str(s)?) + }) + } +} + +impl Display for Ipv4AddrConfig { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Ipv4AddrConfig::UseDhcp => write!(f, "use_dhcp"), + Ipv4AddrConfig::Static(ipv4) => write!(f, "{}", ipv4) + } + } +} pub struct NetAddresses { pub hardware_addr: EthernetAddress, - pub ipv4_addr: IpAddress, + pub ipv4_addr: Ipv4AddrConfig, pub ipv6_ll_addr: IpAddress, pub ipv6_addr: Option } @@ -52,15 +78,9 @@ pub fn get_adresses() -> NetAddresses { } let ipv4_addr; - match config::read_str("ip", |r| r.map(|s| { - if s == "use_dhcp" { - Ok(USE_DHCP) - } else { - s.parse() - } - })) { + match config::read_str("ip", |r| r.map(|s| s.parse())) { Ok(Ok(addr)) => ipv4_addr = addr, - _ => ipv4_addr = USE_DHCP, + _ => ipv4_addr = Ipv4AddrConfig::UseDhcp, } let ipv6_ll_addr = IpAddress::v6( diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index ab731a6b9..01cf89f5c 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -32,7 +32,7 @@ use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr, Ipv4Address}; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot}; #[cfg(has_ethmac)] use board_misoc::ethmac; -use board_misoc::net_settings::{NetAddresses, USE_DHCP}; +use board_misoc::net_settings::{NetAddresses, Ipv4AddrConfig}; #[cfg(has_drtio)] use board_artiq::drtioaux; use board_artiq::drtio_routing; @@ -100,7 +100,9 @@ pub fn get_ip_addrs(net_addresses: &NetAddresses) -> [IpCidr; IP_ADDRESS_STORAGE let mut storage = [ IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE ]; - storage[IPV4_INDEX] = IpCidr::new(net_addresses.ipv4_addr, 0); + if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { + storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); + } storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0); if let Some(ipv6) = net_addresses.ipv6_addr { storage[IPV6_INDEX] = IpCidr::new(ipv6, 0); @@ -162,7 +164,7 @@ fn startup() { smoltcp::iface::NeighborCache::new(alloc::collections::btree_map::BTreeMap::new()); let net_addresses = net_settings::get_adresses(); info!("network addresses: {}", net_addresses); - let use_dhcp = if net_addresses.ipv4_addr == USE_DHCP { + let use_dhcp = if matches!(net_addresses.ipv4_addr, Ipv4AddrConfig::UseDhcp) { info!("Will try to acquire an IPv4 address with DHCP"); true } else { From 671453938b43824f6f13d19ac4639a18bdf1a6e6 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Tue, 12 Apr 2022 12:08:01 +0100 Subject: [PATCH 11/14] Require explicitly closing TcpStreams Instead of automatically closing and draining the TcpStream in the Drop implementation instead expect the user to call TcpStream::close. Add close called to all users of TcpStream. Document the requirement to call close on TcpListener::accept, this seems to be the only way to get a new TcpStream at the moment. --- artiq/firmware/runtime/analyzer.rs | 2 + artiq/firmware/runtime/mgmt.rs | 1 + artiq/firmware/runtime/moninj.rs | 1 + artiq/firmware/runtime/sched.rs | 64 ++++++++++-------------------- artiq/firmware/runtime/session.rs | 1 + 5 files changed, 25 insertions(+), 44 deletions(-) diff --git a/artiq/firmware/runtime/analyzer.rs b/artiq/firmware/runtime/analyzer.rs index 2e9d74c14..5b090f732 100644 --- a/artiq/firmware/runtime/analyzer.rs +++ b/artiq/firmware/runtime/analyzer.rs @@ -79,5 +79,7 @@ pub fn thread(io: Io) { Ok(()) => (), Err(err) => error!("analyzer aborted: {}", err) } + + stream.close().expect("analyzer: close socket") } } diff --git a/artiq/firmware/runtime/mgmt.rs b/artiq/firmware/runtime/mgmt.rs index c100df05f..449ed91be 100644 --- a/artiq/firmware/runtime/mgmt.rs +++ b/artiq/firmware/runtime/mgmt.rs @@ -131,6 +131,7 @@ pub fn thread(io: Io) { Err(Error::Io(IoError::UnexpectedEnd)) => (), Err(err) => error!("aborted: {}", err) } + stream.close().expect("mgmt: close socket"); }); } } diff --git a/artiq/firmware/runtime/moninj.rs b/artiq/firmware/runtime/moninj.rs index 6feb7ec55..36d4bbb47 100644 --- a/artiq/firmware/runtime/moninj.rs +++ b/artiq/firmware/runtime/moninj.rs @@ -214,6 +214,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex, routing_table: &Urc {}, Err(err) => error!("moninj aborted: {}", err) } + stream.close().expect("moninj: close socket"); }); } } diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index fc3c7ac04..860ec5347 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -250,26 +250,14 @@ impl<'a> Io<'a> { }) } - pub fn inner_until bool>( - &self, - timeout: Option, - mut f: F - ) -> Result<(), Error> { + pub fn until bool>(&self, mut f: F) -> Result<(), Error> { let f = unsafe { mem::transmute::<&mut dyn FnMut() -> bool, *mut dyn FnMut() -> bool>(&mut f) }; self.suspend(WaitRequest { - timeout, - event: Some(f), + timeout: None, + event: Some(f) }) } - pub fn until bool>(&self, f: F) -> Result<(), Error> { - self.inner_until(None, f) - } - - pub fn until_with_timeout bool>(&self, timeout: u64, f: F) -> Result<(), Error> { - self.inner_until(Some(timeout), f) - } - pub fn until_ok(&self, mut f: F) -> Result where F: FnMut() -> result::Result { @@ -325,15 +313,7 @@ macro_rules! until { let $var = network.get_socket::<$ty>(handle); $cond }) - }); - ($socket:expr, $ty:ty, timeout=$timeout:expr, |$var:ident| $cond:expr) => ({ - let (network, handle) = ($socket.io.network.clone(), $socket.handle); - $socket.io.until_with_timeout($timeout, move || { - let mut network = network.borrow_mut(); - let $var = network.get_socket::<$ty>(handle); - $cond - }) - }); + }) } type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>; @@ -397,6 +377,10 @@ impl<'a> TcpListener<'a> { .map_err(|err| err.into()) } + /// Accept a TCP connection + /// + /// When the returned TcpStream is dropped it is immediately forgotten about. In order to + /// ensure that pending data is sent and the far end is notified, `close` must be called. pub fn accept(&self) -> Result, Error> { // We're waiting until at least one half of the connection becomes open. // This handles the case where a remote socket immediately sends a FIN-- @@ -586,28 +570,20 @@ impl<'a> Write for TcpStream<'a> { impl<'a> Drop for TcpStream<'a> { fn drop(&mut self) { - self.with_lower(|s| s.close()); - let result = until!( - self, TcpSocketLower, timeout=clock::get_ms() + 1000, |s| !s.is_active() + // There's no point calling the lower close here unless we also defer the removal of the + // socket from smoltcp until it's had a chance to process the event + let (unsent_bytes, is_open) = self.with_lower( + |s| (s.send_queue(), s.is_open()) ); - let unsent_bytes = self.with_lower(|s| s.send_queue()); - match result{ - Ok(()) => { - if unsent_bytes != 0 { - // This is normal if we received a reset whilst sending - debug!("Dropping socket with {} bytes unsent", unsent_bytes) - } - } - Err(Error::TimedOut) => { - warn!( - "Timed out whilst waiting for socket to close during drop, with {} unsent bytes", - unsent_bytes - ); - } - Err(e) => { - error!("Unexpected error whilst waiting for socket to close during drop: {:?}", e) - } + if is_open { + warn!( + "Dropping open TcpStream in state {}, with {} unsent bytes", + self.with_lower(|s| s.state()), unsent_bytes + ) + } else if unsent_bytes != 0 { + debug!("Dropping socket with {} bytes unsent", unsent_bytes) } + self.io.network.borrow_mut().remove_socket(self.handle); } } diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index aec47afe1..bd1562269 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -650,6 +650,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex, error!("session aborted: {}", err); } } + stream.close().expect("session: close socket"); }); } From 95378cf9c9996412690ace20905981b7296c1585 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Fri, 22 Apr 2022 10:40:54 +0100 Subject: [PATCH 12/14] Centralise all uses of the IPv4 index in net_settings.rs --- artiq/firmware/libboard_misoc/net_settings.rs | 39 ++++++++++++++++++- artiq/firmware/runtime/main.rs | 27 ++----------- artiq/firmware/runtime/sched.rs | 8 ++-- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/artiq/firmware/libboard_misoc/net_settings.rs b/artiq/firmware/libboard_misoc/net_settings.rs index 466e1a893..20b204c80 100644 --- a/artiq/firmware/libboard_misoc/net_settings.rs +++ b/artiq/firmware/libboard_misoc/net_settings.rs @@ -1,14 +1,21 @@ use core::fmt; use core::fmt::{Display, Formatter}; use core::str::FromStr; +use smoltcp::iface::{Interface, InterfaceBuilder}; +use smoltcp::phy::Device; -use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; use config; #[cfg(soc_platform = "kasli")] use i2c_eeprom; +const IPV4_INDEX: usize = 0; +const IPV6_LL_INDEX: usize = 1; +const IPV6_INDEX: usize = 2; +const IP_ADDRESS_STORAGE_SIZE: usize = 3; + pub enum Ipv4AddrConfig { UseDhcp, Static(Ipv4Address), @@ -102,3 +109,33 @@ pub fn get_adresses() -> NetAddresses { ipv6_addr: ipv6_addr } } + +pub trait InterfaceBuilderEx { + fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self; +} + +impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a, DeviceT> { + fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self { + let mut storage = [ + IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE + ]; + if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { + storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); + } + storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0); + if let Some(ipv6) = net_addresses.ipv6_addr { + storage[IPV6_INDEX] = IpCidr::new(ipv6, 0); + } + self.ip_addrs(storage) + } +} + +pub trait InterfaceEx { + fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr); +} + +impl<'a, DeviceT: for<'d> Device<'d>> InterfaceEx for Interface<'a, DeviceT> { + fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr) { + self.update_ip_addrs(|storage| storage[IPV4_INDEX] = IpCidr::Ipv4(*addr)) + } +} diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index 01cf89f5c..f1e7e7108 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -27,12 +27,12 @@ extern crate riscv; use core::cell::RefCell; use core::convert::TryFrom; -use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::HardwareAddress; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot}; #[cfg(has_ethmac)] use board_misoc::ethmac; -use board_misoc::net_settings::{NetAddresses, Ipv4AddrConfig}; +use board_misoc::net_settings::{Ipv4AddrConfig, InterfaceBuilderEx}; #[cfg(has_drtio)] use board_artiq::drtioaux; use board_artiq::drtio_routing; @@ -61,13 +61,6 @@ mod moninj; mod analyzer; mod dhcp; -// Fixed indexes for the IP addresses so that they can be modified with some -// semblance of confidence -pub const IPV4_INDEX: usize = 0; -pub const IPV6_LL_INDEX: usize = 1; -pub const IPV6_INDEX: usize = 2; -const IP_ADDRESS_STORAGE_SIZE: usize = 3; - #[cfg(has_grabber)] fn grabber_thread(io: sched::Io) { loop { @@ -96,20 +89,6 @@ fn setup_log_levels() { } } -pub fn get_ip_addrs(net_addresses: &NetAddresses) -> [IpCidr; IP_ADDRESS_STORAGE_SIZE] { - let mut storage = [ - IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE - ]; - if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { - storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); - } - storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0); - if let Some(ipv6) = net_addresses.ipv6_addr { - storage[IPV6_INDEX] = IpCidr::new(ipv6, 0); - } - storage -} - fn startup() { clock::init(); info!("ARTIQ runtime starting..."); @@ -172,7 +151,7 @@ fn startup() { }; let interface = smoltcp::iface::InterfaceBuilder::new(net_device, vec![]) .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr)) - .ip_addrs(get_ip_addrs(&net_addresses)) + .init_ip_addrs(&net_addresses) .neighbor_cache(neighbor_cache) .finalize(); diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index 860ec5347..a31c6ec43 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -8,7 +8,7 @@ use fringe::OwnedStack; use fringe::generator::{Generator, Yielder, State as GeneratorState}; use smoltcp::time::Duration; use smoltcp::Error as NetworkError; -use smoltcp::wire::{IpEndpoint, Ipv4Cidr, IpCidr}; +use smoltcp::wire::{IpEndpoint, Ipv4Cidr}; use smoltcp::iface::{Interface, SocketHandle}; use io::{Read, Write}; @@ -16,7 +16,7 @@ use board_misoc::clock; use urc::Urc; use board_misoc::ethmac::EthernetDevice; use smoltcp::phy::Tracer; -use IPV4_INDEX; +use board_misoc::net_settings::InterfaceEx; #[derive(Fail, Debug)] pub enum Error { @@ -276,9 +276,7 @@ impl<'a> Io<'a> { } pub fn set_ipv4_address(&self, addr: &Ipv4Cidr) { - self.network.borrow_mut().update_ip_addrs(|addrs| { - addrs.as_mut()[IPV4_INDEX] = IpCidr::Ipv4(*addr); - }) + self.network.borrow_mut().update_ipv4_addr(addr) } } From 50dbda4f43d80725d1ccca9af3fcdfffbad121e1 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Fri, 22 Apr 2022 12:14:51 +0100 Subject: [PATCH 13/14] Use new ip_addr_storage module instead of net_settings Necessary to avoid needing the alloc only trait impls in net_settings when compiling the bootloader. --- artiq/firmware/libboard_misoc/net_settings.rs | 40 +------------------ artiq/firmware/runtime/ip_addr_storage.rs | 40 +++++++++++++++++++ artiq/firmware/runtime/main.rs | 4 +- artiq/firmware/runtime/sched.rs | 2 +- 4 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 artiq/firmware/runtime/ip_addr_storage.rs diff --git a/artiq/firmware/libboard_misoc/net_settings.rs b/artiq/firmware/libboard_misoc/net_settings.rs index 20b204c80..2b69d9262 100644 --- a/artiq/firmware/libboard_misoc/net_settings.rs +++ b/artiq/firmware/libboard_misoc/net_settings.rs @@ -1,21 +1,13 @@ use core::fmt; use core::fmt::{Display, Formatter}; use core::str::FromStr; -use smoltcp::iface::{Interface, InterfaceBuilder}; -use smoltcp::phy::Device; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; +use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address}; use config; #[cfg(soc_platform = "kasli")] use i2c_eeprom; - -const IPV4_INDEX: usize = 0; -const IPV6_LL_INDEX: usize = 1; -const IPV6_INDEX: usize = 2; -const IP_ADDRESS_STORAGE_SIZE: usize = 3; - pub enum Ipv4AddrConfig { UseDhcp, Static(Ipv4Address), @@ -109,33 +101,3 @@ pub fn get_adresses() -> NetAddresses { ipv6_addr: ipv6_addr } } - -pub trait InterfaceBuilderEx { - fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self; -} - -impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a, DeviceT> { - fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self { - let mut storage = [ - IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE - ]; - if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { - storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); - } - storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0); - if let Some(ipv6) = net_addresses.ipv6_addr { - storage[IPV6_INDEX] = IpCidr::new(ipv6, 0); - } - self.ip_addrs(storage) - } -} - -pub trait InterfaceEx { - fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr); -} - -impl<'a, DeviceT: for<'d> Device<'d>> InterfaceEx for Interface<'a, DeviceT> { - fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr) { - self.update_ip_addrs(|storage| storage[IPV4_INDEX] = IpCidr::Ipv4(*addr)) - } -} diff --git a/artiq/firmware/runtime/ip_addr_storage.rs b/artiq/firmware/runtime/ip_addr_storage.rs new file mode 100644 index 000000000..310c21941 --- /dev/null +++ b/artiq/firmware/runtime/ip_addr_storage.rs @@ -0,0 +1,40 @@ +use smoltcp::iface::{Interface, InterfaceBuilder}; +use smoltcp::phy::Device; +use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; +use board_misoc::net_settings::{Ipv4AddrConfig, NetAddresses}; + + +const IPV4_INDEX: usize = 0; +const IPV6_LL_INDEX: usize = 1; +const IPV6_INDEX: usize = 2; +const IP_ADDRESS_STORAGE_SIZE: usize = 3; + +pub trait InterfaceBuilderEx { + fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self; +} + +impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a, DeviceT> { + fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self { + let mut storage = [ + IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE + ]; + if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr { + storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0); + } + storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0); + if let Some(ipv6) = net_addresses.ipv6_addr { + storage[IPV6_INDEX] = IpCidr::new(ipv6, 0); + } + self.ip_addrs(storage) + } +} + +pub trait InterfaceEx { + fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr); +} + +impl<'a, DeviceT: for<'d> Device<'d>> InterfaceEx for Interface<'a, DeviceT> { + fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr) { + self.update_ip_addrs(|storage| storage[IPV4_INDEX] = IpCidr::Ipv4(*addr)) + } +} diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index f1e7e7108..b36fd9251 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -32,7 +32,7 @@ use smoltcp::wire::HardwareAddress; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot}; #[cfg(has_ethmac)] use board_misoc::ethmac; -use board_misoc::net_settings::{Ipv4AddrConfig, InterfaceBuilderEx}; +use board_misoc::net_settings::{Ipv4AddrConfig}; #[cfg(has_drtio)] use board_artiq::drtioaux; use board_artiq::drtio_routing; @@ -42,6 +42,7 @@ use proto_artiq::{mgmt_proto, moninj_proto, rpc_proto, session_proto, kernel_pro use proto_artiq::analyzer_proto; use riscv::register::{mcause, mepc, mtval}; +use ip_addr_storage::InterfaceBuilderEx; mod rtio_clocking; mod rtio_mgt; @@ -60,6 +61,7 @@ mod moninj; #[cfg(has_rtio_analyzer)] mod analyzer; mod dhcp; +mod ip_addr_storage; #[cfg(has_grabber)] fn grabber_thread(io: sched::Io) { diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index a31c6ec43..1f9c79bc2 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -16,7 +16,7 @@ use board_misoc::clock; use urc::Urc; use board_misoc::ethmac::EthernetDevice; use smoltcp::phy::Tracer; -use board_misoc::net_settings::InterfaceEx; +use ip_addr_storage::InterfaceEx; #[derive(Fail, Debug)] pub enum Error { From 69b642680098bae7b0e363432478dbc42f10bef6 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 24 Apr 2022 14:02:59 +0800 Subject: [PATCH 14/14] flake: use importCargoLock --- flake.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index a455c5a94..ed0cad534 100644 --- a/flake.nix +++ b/flake.nix @@ -253,10 +253,11 @@ pkgs.stdenv.mkDerivation { name = "artiq-board-${target}-${variant}"; phases = [ "buildPhase" "checkPhase" "installPhase" ]; - cargoDeps = rustPlatform.fetchCargoTarball { - name = "artiq-firmware-cargo-deps"; - src = "${self}/artiq/firmware"; - sha256 = "sha256-YyycMsDzR+JRcMZJd6A/CRi2J9nKmaWY/KXUnAQaZ00="; + cargoDeps = rustPlatform.importCargoLock { + lockFile = ./artiq/firmware/Cargo.lock; + outputHashes = { + "fringe-1.2.1" = "sha256-m4rzttWXRlwx53LWYpaKuU5AZe4GSkbjHS6oINt5d3Y="; + }; }; nativeBuildInputs = [ (pkgs.python3.withPackages(ps: [ ps.jsonschema migen misoc artiq]))