mirror of https://github.com/m-labs/artiq.git
Merge branch 'master' into nac3
This commit is contained in:
commit
13eb6e89f0
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -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]]
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
|
|
@ -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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
12
flake.nix
12
flake.nix
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in New Issue