Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2022-04-28 15:01:17 +08:00
commit 13eb6e89f0
20 changed files with 395 additions and 131 deletions

View File

@ -42,6 +42,8 @@ Highlights:
* ``PCA9548`` I2C switch class renamed to ``I2CSwitch``, to accomodate support for PCA9547, and * ``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 possibly other switches in future. Readback has been removed, and now only one channel per
switch is supported. switch is supported.
* 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: Breaking changes:

View File

@ -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()

View File

@ -240,6 +240,12 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.4.1" version = "2.4.1"
@ -321,7 +327,7 @@ dependencies = [
"io", "io",
"log", "log",
"logger_artiq", "logger_artiq",
"managed", "managed 0.7.2",
"proto_artiq", "proto_artiq",
"riscv", "riscv",
"smoltcp", "smoltcp",
@ -365,13 +371,13 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "smoltcp" name = "smoltcp"
version = "0.6.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" checksum = "d2308a1657c8db1f5b4993bab4e620bdbe5623bd81f254cf60326767bb243237"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",
"managed", "managed 0.8.0",
] ]
[[package]] [[package]]

View File

@ -16,5 +16,5 @@ build_misoc = { path = "../libbuild_misoc" }
byteorder = { version = "1.0", default-features = false } byteorder = { version = "1.0", default-features = false }
crc = { version = "1.7", default-features = false } crc = { version = "1.7", default-features = false }
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] } 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"] } riscv = { version = "0.6.0", features = ["inline-asm"] }

View File

@ -18,6 +18,8 @@ use board_misoc::slave_fpga;
use board_misoc::{clock, ethmac, net_settings}; use board_misoc::{clock, ethmac, net_settings};
use board_misoc::uart_console::Console; use board_misoc::uart_console::Console;
use riscv::register::{mcause, mepc, mtval}; use riscv::register::{mcause, mepc, mtval};
use smoltcp::iface::SocketStorage;
use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address};
fn check_integrity() -> bool { fn check_integrity() -> bool {
extern { extern {
@ -396,6 +398,9 @@ fn network_boot() {
println!("Initializing network..."); 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() }; let mut net_device = unsafe { ethmac::EthernetDevice::new() };
net_device.reset_phy_if_any(); net_device.reset_phy_if_any();
@ -405,22 +410,25 @@ fn network_boot() {
let net_addresses = net_settings::get_adresses(); let net_addresses = net_settings::get_adresses();
println!("Network addresses: {}", net_addresses); println!("Network addresses: {}", net_addresses);
let mut ip_addrs = [ 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),
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 { let mut interface = match net_addresses.ipv6_addr {
Some(addr) => { Some(addr) => {
ip_addrs[2] = IpCidr::new(addr, 0); ip_addrs[2] = IpCidr::new(addr, 0);
smoltcp::iface::EthernetInterfaceBuilder::new(net_device) smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..])
.ethernet_addr(net_addresses.hardware_addr) .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
.ip_addrs(&mut ip_addrs[..]) .ip_addrs(&mut ip_addrs[..])
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache)
.finalize() .finalize()
} }
None => None =>
smoltcp::iface::EthernetInterfaceBuilder::new(net_device) smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..])
.ethernet_addr(net_addresses.hardware_addr) .hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
.ip_addrs(&mut ip_addrs[..2]) .ip_addrs(&mut ip_addrs[..2])
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache)
.finalize() .finalize()
@ -429,14 +437,10 @@ fn network_boot() {
let mut rx_storage = [0; 4096]; let mut rx_storage = [0; 4096];
let mut tx_storage = [0; 128]; 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_rx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
let tcp_tx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut tx_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_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 net_conn = NetConn::new();
let mut boot_time = None; let mut boot_time = None;
@ -446,7 +450,7 @@ fn network_boot() {
loop { loop {
let timestamp = clock::get_ms() as i64; let timestamp = clock::get_ms() as i64;
{ {
let socket = &mut *sockets.get::<smoltcp::socket::TcpSocket>(tcp_handle); let socket = &mut *interface.get_socket::<smoltcp::socket::TcpSocket>(tcp_handle);
match boot_time { match boot_time {
None => { None => {
@ -475,7 +479,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(_) => (), Ok(_) => (),
Err(smoltcp::Error::Unrecognized) => (), Err(smoltcp::Error::Unrecognized) => (),
Err(err) => println!("Network error: {}", err) Err(err) => println!("Network error: {}", err)

View File

@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" }
[dependencies] [dependencies]
byteorder = { version = "1.0", default-features = false } byteorder = { version = "1.0", default-features = false }
log = { version = "0.4", default-features = false, optional = true } 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"] } riscv = { version = "0.6.0", features = ["inline-asm"] }
[features] [features]

View File

@ -1,15 +1,43 @@
use core::fmt; use core::fmt;
use core::fmt::{Display, Formatter};
use core::str::FromStr;
use smoltcp::wire::{EthernetAddress, IpAddress}; use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address};
use config; use config;
#[cfg(soc_platform = "kasli")] #[cfg(soc_platform = "kasli")]
use i2c_eeprom; use i2c_eeprom;
pub enum Ipv4AddrConfig {
UseDhcp,
Static(Ipv4Address),
}
impl FromStr for Ipv4AddrConfig {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
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 struct NetAddresses {
pub hardware_addr: EthernetAddress, pub hardware_addr: EthernetAddress,
pub ipv4_addr: IpAddress, pub ipv4_addr: Ipv4AddrConfig,
pub ipv6_ll_addr: IpAddress, pub ipv6_ll_addr: IpAddress,
pub ipv6_addr: Option<IpAddress> pub ipv6_addr: Option<IpAddress>
} }
@ -47,12 +75,7 @@ pub fn get_adresses() -> NetAddresses {
let ipv4_addr; let ipv4_addr;
match config::read_str("ip", |r| r.map(|s| s.parse())) { match config::read_str("ip", |r| r.map(|s| s.parse())) {
Ok(Ok(addr)) => ipv4_addr = addr, Ok(Ok(addr)) => ipv4_addr = addr,
_ => { _ => ipv4_addr = Ipv4AddrConfig::UseDhcp,
#[cfg(soc_platform = "kasli")]
{ ipv4_addr = IpAddress::v4(192, 168, 1, 70); }
#[cfg(soc_platform = "kc705")]
{ ipv4_addr = IpAddress::v4(192, 168, 1, 50); }
}
} }
let ipv6_ll_addr = IpAddress::v6( let ipv6_ll_addr = IpAddress::v6(

View File

@ -27,9 +27,13 @@ board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp
logger_artiq = { path = "../liblogger_artiq" } logger_artiq = { path = "../liblogger_artiq" }
board_artiq = { path = "../libboard_artiq" } board_artiq = { path = "../libboard_artiq" }
proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] } 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"] }
riscv = { version = "0.6.0", features = ["inline-asm"] } 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] [dependencies.fringe]
git = "https://git.m-labs.hk/M-Labs/libfringe.git" git = "https://git.m-labs.hk/M-Labs/libfringe.git"
rev = "3ecbe5" rev = "3ecbe5"

View File

@ -79,5 +79,7 @@ pub fn thread(io: Io) {
Ok(()) => (), Ok(()) => (),
Err(err) => error!("analyzer aborted: {}", err) Err(err) => error!("analyzer aborted: {}", err)
} }
stream.close().expect("analyzer: close socket")
} }
} }

View File

@ -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<Ipv4Cidr> = 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();
}
}

View File

@ -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))
}
}

View File

@ -27,11 +27,12 @@ extern crate riscv;
use core::cell::RefCell; use core::cell::RefCell;
use core::convert::TryFrom; use core::convert::TryFrom;
use smoltcp::wire::IpCidr; use smoltcp::wire::HardwareAddress;
use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot}; use board_misoc::{csr, ident, clock, spiflash, config, net_settings, pmp, boot};
#[cfg(has_ethmac)] #[cfg(has_ethmac)]
use board_misoc::ethmac; use board_misoc::ethmac;
use board_misoc::net_settings::{Ipv4AddrConfig};
#[cfg(has_drtio)] #[cfg(has_drtio)]
use board_artiq::drtioaux; use board_artiq::drtioaux;
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
@ -41,6 +42,7 @@ use proto_artiq::{mgmt_proto, moninj_proto, rpc_proto, session_proto, kernel_pro
use proto_artiq::analyzer_proto; use proto_artiq::analyzer_proto;
use riscv::register::{mcause, mepc, mtval}; use riscv::register::{mcause, mepc, mtval};
use ip_addr_storage::InterfaceBuilderEx;
mod rtio_clocking; mod rtio_clocking;
mod rtio_mgt; mod rtio_mgt;
@ -58,6 +60,8 @@ mod session;
mod moninj; mod moninj;
#[cfg(has_rtio_analyzer)] #[cfg(has_rtio_analyzer)]
mod analyzer; mod analyzer;
mod dhcp;
mod ip_addr_storage;
#[cfg(has_grabber)] #[cfg(has_grabber)]
fn grabber_thread(io: sched::Io) { fn grabber_thread(io: sched::Io) {
@ -123,54 +127,35 @@ fn startup() {
net_device.reset_phy_if_any(); net_device.reset_phy_if_any();
let net_device = { let net_device = {
use smoltcp::time::Instant; use smoltcp::phy::Tracer;
use smoltcp::wire::PrettyPrinter;
use smoltcp::wire::EthernetFrame;
fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter<EthernetFrame<&[u8]>>) { // We can't create the function pointer as a separate variable here because the type of
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n", // the packet argument Packet isn't accessible and rust's type inference isn't sufficient
timestamp.secs(), timestamp.millis(), printer) // to propagate in to a local var.
}
fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
let net_trace_fn: fn(Instant, PrettyPrinter<EthernetFrame<&[u8]>>);
match config::read_str("net_trace", |r| r.map(|s| s == "1")) { match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
Ok(true) => net_trace_fn = net_trace_writer, Ok(true) => Tracer::new(net_device, |timestamp, packet| {
_ => net_trace_fn = net_trace_silent 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 = let neighbor_cache =
smoltcp::iface::NeighborCache::new(alloc::collections::btree_map::BTreeMap::new()); smoltcp::iface::NeighborCache::new(alloc::collections::btree_map::BTreeMap::new());
let net_addresses = net_settings::get_adresses(); let net_addresses = net_settings::get_adresses();
info!("network addresses: {}", net_addresses); info!("network addresses: {}", net_addresses);
let mut interface = match net_addresses.ipv6_addr { let use_dhcp = if matches!(net_addresses.ipv4_addr, Ipv4AddrConfig::UseDhcp) {
Some(addr) => { info!("Will try to acquire an IPv4 address with DHCP");
let ip_addrs = [ true
IpCidr::new(net_addresses.ipv4_addr, 0), } else {
IpCidr::new(net_addresses.ipv6_ll_addr, 0), false
IpCidr::new(addr, 0)
];
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
.ethernet_addr(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::EthernetInterfaceBuilder::new(net_device)
.ethernet_addr(net_addresses.hardware_addr)
.ip_addrs(ip_addrs)
.neighbor_cache(neighbor_cache)
.finalize()
}
}; };
let interface = smoltcp::iface::InterfaceBuilder::new(net_device, vec![])
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
.init_ip_addrs(&net_addresses)
.neighbor_cache(neighbor_cache)
.finalize();
#[cfg(has_drtio)] #[cfg(has_drtio)]
let drtio_routing_table = urc::Urc::new(RefCell::new( let drtio_routing_table = urc::Urc::new(RefCell::new(
@ -184,9 +169,13 @@ fn startup() {
drtio_routing::interconnect_disable_all(); drtio_routing::interconnect_disable_all();
let aux_mutex = sched::Mutex::new(); let aux_mutex = sched::Mutex::new();
let mut scheduler = sched::Scheduler::new(); let mut scheduler = sched::Scheduler::new(interface);
let io = scheduler.io(); let io = scheduler.io();
if use_dhcp {
io.spawn(4096, dhcp::dhcp_thread);
}
rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations); rtio_mgt::startup(&io, &aux_mutex, &drtio_routing_table, &up_destinations);
io.spawn(4096, mgmt::thread); io.spawn(4096, mgmt::thread);
@ -211,19 +200,7 @@ fn startup() {
let mut net_stats = ethmac::EthernetStatistics::new(); let mut net_stats = ethmac::EthernetStatistics::new();
loop { loop {
scheduler.run(); scheduler.run();
scheduler.run_network();
{
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)
}
}
}
if let Some(_net_stats_diff) = net_stats.update() { if let Some(_net_stats_diff) = net_stats.update() {
debug!("ethernet mac:{}", ethmac::EthernetStatistics::new()); debug!("ethernet mac:{}", ethmac::EthernetStatistics::new());

View File

@ -131,6 +131,7 @@ pub fn thread(io: Io) {
Err(Error::Io(IoError::UnexpectedEnd)) => (), Err(Error::Io(IoError::UnexpectedEnd)) => (),
Err(err) => error!("aborted: {}", err) Err(err) => error!("aborted: {}", err)
} }
stream.close().expect("mgmt: close socket");
}); });
} }
} }

View File

@ -214,6 +214,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex, routing_table: &Urc<RefCell<drtio_routi
Ok(()) => {}, Ok(()) => {},
Err(err) => error!("moninj aborted: {}", err) Err(err) => error!("moninj aborted: {}", err)
} }
stream.close().expect("moninj: close socket");
}); });
} }
} }

View File

@ -2,18 +2,21 @@
use core::mem; use core::mem;
use core::result; use core::result;
use core::cell::{Cell, RefCell}; use core::cell::{Cell, RefCell, RefMut};
use alloc::vec::Vec; use alloc::vec::Vec;
use fringe::OwnedStack; use fringe::OwnedStack;
use fringe::generator::{Generator, Yielder, State as GeneratorState}; use fringe::generator::{Generator, Yielder, State as GeneratorState};
use smoltcp::time::Duration; use smoltcp::time::Duration;
use smoltcp::Error as NetworkError; use smoltcp::Error as NetworkError;
use smoltcp::wire::IpEndpoint; use smoltcp::wire::{IpEndpoint, Ipv4Cidr};
use smoltcp::socket::{SocketHandle, SocketRef}; use smoltcp::iface::{Interface, SocketHandle};
use io::{Read, Write}; use io::{Read, Write};
use board_misoc::clock; use board_misoc::clock;
use urc::Urc; use urc::Urc;
use board_misoc::ethmac::EthernetDevice;
use smoltcp::phy::Tracer;
use ip_addr_storage::InterfaceEx;
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum Error { pub enum Error {
@ -31,8 +34,6 @@ impl From<NetworkError> for Error {
} }
} }
type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>;
#[derive(Debug)] #[derive(Debug)]
struct WaitRequest { struct WaitRequest {
event: Option<*mut dyn FnMut() -> bool>, event: Option<*mut dyn FnMut() -> bool>,
@ -59,7 +60,7 @@ impl Thread {
unsafe fn new<F>(io: &Io, stack_size: usize, f: F) -> ThreadHandle unsafe fn new<F>(io: &Io, stack_size: usize, f: F) -> ThreadHandle
where F: 'static + FnOnce(Io) + Send { where F: 'static + FnOnce(Io) + Send {
let spawned = io.spawned.clone(); 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 // Add a 4k stack guard to the stack of any new threads
let stack = OwnedStack::new(stack_size + 4096); let stack = OwnedStack::new(stack_size + 4096);
@ -67,8 +68,8 @@ impl Thread {
generator: Generator::unsafe_new(stack, |yielder, _| { generator: Generator::unsafe_new(stack, |yielder, _| {
f(Io { f(Io {
yielder: Some(yielder), yielder: Some(yielder),
spawned: spawned, spawned,
sockets: sockets network
}) })
}), }),
waiting_for: WaitRequest { waiting_for: WaitRequest {
@ -115,19 +116,21 @@ impl ThreadHandle {
} }
} }
type Network = Interface<'static, Tracer<EthernetDevice>>;
pub struct Scheduler { pub struct Scheduler {
threads: Vec<ThreadHandle>, threads: Vec<ThreadHandle>,
spawned: Urc<RefCell<Vec<ThreadHandle>>>, spawned: Urc<RefCell<Vec<ThreadHandle>>>,
sockets: Urc<RefCell<SocketSet>>, network: Urc<RefCell<Network>>,
run_idx: usize, run_idx: usize,
} }
impl Scheduler { impl Scheduler {
pub fn new() -> Scheduler { pub fn new(network: Network) -> Scheduler {
Scheduler { Scheduler {
threads: Vec::new(), threads: Vec::new(),
spawned: Urc::new(RefCell::new(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, run_idx: 0,
} }
} }
@ -136,13 +139,11 @@ impl Scheduler {
Io { Io {
yielder: None, yielder: None,
spawned: self.spawned.clone(), spawned: self.spawned.clone(),
sockets: self.sockets.clone() network: self.network.clone()
} }
} }
pub fn run(&mut self) { pub fn run(&mut self) {
self.sockets.borrow_mut().prune();
self.threads.append(&mut *self.spawned.borrow_mut()); self.threads.append(&mut *self.spawned.borrow_mut());
if self.threads.len() == 0 { return } if self.threads.len() == 0 { return }
@ -188,8 +189,17 @@ impl Scheduler {
} }
} }
pub fn sockets(&self) -> &RefCell<SocketSet> { pub fn run_network(&mut self) {
&*self.sockets 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 +207,7 @@ impl Scheduler {
pub struct Io<'a> { pub struct Io<'a> {
yielder: Option<&'a Yielder<WaitResult, WaitRequest>>, yielder: Option<&'a Yielder<WaitResult, WaitRequest>>,
spawned: Urc<RefCell<Vec<ThreadHandle>>>, spawned: Urc<RefCell<Vec<ThreadHandle>>>,
sockets: Urc<RefCell<SocketSet>>, network: Urc<RefCell<Network>>,
} }
impl<'a> Io<'a> { impl<'a> Io<'a> {
@ -264,6 +274,10 @@ impl<'a> Io<'a> {
pub fn join(&self, handle: ThreadHandle) -> Result<(), Error> { pub fn join(&self, handle: ThreadHandle) -> Result<(), Error> {
self.until(move || handle.terminated()) self.until(move || handle.terminated())
} }
pub fn set_ipv4_address(&self, addr: &Ipv4Cidr) {
self.network.borrow_mut().update_ipv4_addr(addr)
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -291,10 +305,10 @@ impl<'a> Drop for MutexGuard<'a> {
macro_rules! until { macro_rules! until {
($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({ ($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 || { $socket.io.until(move || {
let mut sockets = sockets.borrow_mut(); let mut network = network.borrow_mut();
let $var = sockets.get::<$ty>(handle); let $var = network.get_socket::<$ty>(handle);
$cond $cond
}) })
}) })
@ -316,9 +330,9 @@ impl<'a> TcpListener<'a> {
fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle { fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle {
let rx_buffer = vec![0; buffer_size]; let rx_buffer = vec![0; buffer_size];
let tx_buffer = vec![0; buffer_size]; let tx_buffer = vec![0; buffer_size];
io.sockets io.network
.borrow_mut() .borrow_mut()
.add(TcpSocketLower::new( .add_socket(TcpSocketLower::new(
TcpSocketBuffer::new(rx_buffer), TcpSocketBuffer::new(rx_buffer),
TcpSocketBuffer::new(tx_buffer))) TcpSocketBuffer::new(tx_buffer)))
} }
@ -333,9 +347,9 @@ impl<'a> TcpListener<'a> {
} }
fn with_lower<F, R>(&self, f: F) -> R fn with_lower<F, R>(&self, f: F) -> R
where F: FnOnce(SocketRef<TcpSocketLower>) -> R { where F: FnOnce(&mut TcpSocketLower) -> R {
let mut sockets = self.io.sockets.borrow_mut(); let mut network = self.io.network.borrow_mut();
let result = f(sockets.get(self.handle.get())); let result = f(network.get_socket(self.handle.get()));
result result
} }
@ -353,7 +367,7 @@ impl<'a> TcpListener<'a> {
pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<(), Error> { pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<(), Error> {
let endpoint = endpoint.into(); let endpoint = endpoint.into();
self.with_lower(|mut s| s.listen(endpoint)) self.with_lower(|s| s.listen(endpoint))
.map(|()| { .map(|()| {
self.endpoint.set(endpoint); self.endpoint.set(endpoint);
() ()
@ -361,14 +375,18 @@ impl<'a> TcpListener<'a> {
.map_err(|err| err.into()) .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<TcpStream<'a>, Error> { pub fn accept(&self) -> Result<TcpStream<'a>, Error> {
// We're waiting until at least one half of the connection becomes open. // 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-- // This handles the case where a remote socket immediately sends a FIN--
// that still counts as accepting even though nothing may be sent. // 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 || { self.io.until(move || {
let mut sockets = sockets.borrow_mut(); let mut network = network.borrow_mut();
let socket = sockets.get::<TcpSocketLower>(handle); let socket = network.get_socket::<TcpSocketLower>(handle);
socket.may_send() || socket.may_recv() socket.may_send() || socket.may_recv()
})?; })?;
@ -385,14 +403,14 @@ impl<'a> TcpListener<'a> {
} }
pub fn close(&self) { pub fn close(&self) {
self.with_lower(|mut s| s.close()) self.with_lower(|s| s.close())
} }
} }
impl<'a> Drop for TcpListener<'a> { impl<'a> Drop for TcpListener<'a> {
fn drop(&mut self) { fn drop(&mut self) {
self.with_lower(|mut s| s.close()); self.with_lower(|s| s.close());
self.io.sockets.borrow_mut().release(self.handle.get()) self.io.network.borrow_mut().remove_socket(self.handle.get());
} }
} }
@ -416,9 +434,9 @@ impl<'a> TcpStream<'a> {
} }
fn with_lower<F, R>(&self, f: F) -> R fn with_lower<F, R>(&self, f: F) -> R
where F: FnOnce(SocketRef<TcpSocketLower>) -> R { where F: FnOnce(&mut TcpSocketLower) -> R {
let mut sockets = self.io.sockets.borrow_mut(); let mut network = self.io.network.borrow_mut();
let result = f(sockets.get(self.handle)); let result = f(network.get_socket(self.handle));
result result
} }
@ -455,7 +473,7 @@ impl<'a> TcpStream<'a> {
} }
pub fn set_timeout(&self, value: Option<u64>) { pub fn set_timeout(&self, value: Option<u64>) {
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<u64> { pub fn keep_alive(&self) -> Option<u64> {
@ -463,11 +481,11 @@ impl<'a> TcpStream<'a> {
} }
pub fn set_keep_alive(&self, value: Option<u64>) { pub fn set_keep_alive(&self, value: Option<u64>) {
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> { 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())?; 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 // 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(); } // a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); }
@ -481,23 +499,33 @@ impl<'a> Read for TcpStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError> { fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError> {
// Only borrow the underlying socket for the span of the next statement. // 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 { match result {
// Slow path: we need to block until buffer is non-empty. // Slow path: we need to block until buffer is non-empty.
Ok(0) => { Ok(0) => {
until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?; 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), Ok(length) => Ok(length),
Err(NetworkError::Finished) |
Err(NetworkError::Illegal) => Ok(0), Err(NetworkError::Illegal) => Ok(0),
_ => unreachable!() Err(e) => {
panic!("Unexpected error from smoltcp: {}", e);
}
} }
} }
// Fast path: we had data in buffer. // Fast path: we had data in buffer.
Ok(length) => Ok(length), Ok(length) => Ok(length),
// We've received a fin.
Err(NetworkError::Finished) |
// Error path: the receive half of the socket is not open. // Error path: the receive half of the socket is not open.
Err(NetworkError::Illegal) => Ok(0), Err(NetworkError::Illegal) => Ok(0),
// No other error may be returned. // 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 +536,12 @@ impl<'a> Write for TcpStream<'a> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> { fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
// Only borrow the underlying socket for the span of the next statement. // 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 { match result {
// Slow path: we need to block until buffer is non-full. // Slow path: we need to block until buffer is non-full.
Ok(0) => { Ok(0) => {
until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?; 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), Ok(length) => Ok(length),
Err(NetworkError::Illegal) => Ok(0), Err(NetworkError::Illegal) => Ok(0),
_ => unreachable!() _ => unreachable!()
@ -540,7 +568,62 @@ impl<'a> Write for TcpStream<'a> {
impl<'a> Drop for TcpStream<'a> { impl<'a> Drop for TcpStream<'a> {
fn drop(&mut self) { fn drop(&mut self) {
self.with_lower(|mut s| s.close()); // There's no point calling the lower close here unless we also defer the removal of the
self.io.sockets.borrow_mut().release(self.handle) // 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())
);
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);
}
}
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<smoltcp::socket::Dhcpv4Socket> {
RefMut::map(
self.io.network.borrow_mut(),
|network| network.get_socket::<smoltcp::socket::Dhcpv4Socket>(self.handle),
)
}
pub fn poll(&mut self) -> Option<smoltcp::socket::Dhcpv4Event> {
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);
} }
} }

View File

@ -650,6 +650,7 @@ pub fn thread(io: Io, aux_mutex: &Mutex,
error!("session aborted: {}", err); error!("session aborted: {}", err);
} }
} }
stream.close().expect("session: close socket");
}); });
} }

View File

@ -225,6 +225,7 @@ _templates = [
"HIST_BIN_BOUNDARIES_DATASET " "HIST_BIN_BOUNDARIES_DATASET "
"HISTS_COUNTS_DATASET"), "HISTS_COUNTS_DATASET"),
("Image", "${artiq_applet}image IMG_DATASET"), ("Image", "${artiq_applet}image IMG_DATASET"),
("Progress bar", "${artiq_applet}progress_bar VALUE"),
] ]

View File

@ -539,3 +539,19 @@ class _Alignment(EnvExperiment):
class AlignmentTest(ExperimentCase): class AlignmentTest(ExperimentCase):
def test_tuple(self): def test_tuple(self):
self.create(_Alignment).run() self.create(_Alignment).run()
class _NumpyQuoting(EnvExperiment):
def build(self):
self.setattr_device("core")
@kernel
def run(self):
a = numpy.array([10, 20])
b = numpy.sqrt(4.0)
class NumpyQuotingTest(ExperimentCase):
def test_issue_1871(self):
"""Ensure numpy.array() does not break NumPy math functions"""
self.create(_NumpyQuoting).run()

View File

@ -231,13 +231,19 @@ 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). 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_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 $ 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. For Kasli devices, flashing a MAC address is not necessary as they can obtain it from their EEPROM.
If DHCP has been used the address can be found in the console output, which can be viewed using: ::
$ 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. 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. 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.

View File

@ -176,10 +176,11 @@
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
name = "artiq-board-${target}-${variant}"; name = "artiq-board-${target}-${variant}";
phases = [ "buildPhase" "checkPhase" "installPhase" ]; phases = [ "buildPhase" "checkPhase" "installPhase" ];
cargoDeps = rustPlatform.fetchCargoTarball { cargoDeps = rustPlatform.importCargoLock {
name = "artiq-firmware-cargo-deps"; lockFile = ./artiq/firmware/Cargo.lock;
src = "${self}/artiq/firmware"; outputHashes = {
sha256 = "sha256-YyycMsDzR+JRcMZJd6A/CRi2J9nKmaWY/KXUnAQaZ00="; "fringe-1.2.1" = "sha256-m4rzttWXRlwx53LWYpaKuU5AZe4GSkbjHS6oINt5d3Y=";
};
}; };
nativeBuildInputs = [ nativeBuildInputs = [
(pkgs.python3.withPackages(ps: [ ps.jsonschema migen misoc artiq])) (pkgs.python3.withPackages(ps: [ ps.jsonschema migen misoc artiq]))
@ -359,6 +360,9 @@
pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme
pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom latex-artiq-manual pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom latex-artiq-manual
]; ];
shellHook = ''
export LIBARTIQ_SUPPORT=`libartiq-support`
'';
}; };
hydraJobs = { hydraJobs = {