forked from M-Labs/artiq
Merge branch 'master' into nac3
This commit is contained in:
commit
a43baa4cc4
|
@ -9,13 +9,13 @@ ARTIQ-8 (Unreleased)
|
||||||
Highlights:
|
Highlights:
|
||||||
|
|
||||||
* Hardware support:
|
* Hardware support:
|
||||||
- Implemented Phaser-servo. This requires recent gateware on Phaser.
|
- Implemented Phaser-servo. This requires recent gateware on Phaser.
|
||||||
- Implemented Phaser-MIQRO support. This requires the Phaser MIQRO gateware
|
- Implemented Phaser-MIQRO support. This requires the Phaser MIQRO gateware
|
||||||
variant.
|
variant.
|
||||||
- Sampler: fixed ADC MU to Volt conversion factor for Sampler v2.2+.
|
- Sampler: fixed ADC MU to Volt conversion factor for Sampler v2.2+.
|
||||||
For earlier hardware versions, specify the hardware version in the device
|
For earlier hardware versions, specify the hardware version in the device
|
||||||
database file (e.g. ``"hw_rev": "v2.1"``) to use the correct conversion factor.
|
database file (e.g. ``"hw_rev": "v2.1"``) to use the correct conversion factor.
|
||||||
- Metlino and Sayma support has been dropped due to complications with synchronous RTIO clocking.
|
- Metlino and Sayma support has been dropped due to complications with synchronous RTIO clocking.
|
||||||
* CPU (on softcore platforms) and AXI bus (on Zynq) are now clocked synchronously with the RTIO
|
* CPU (on softcore platforms) and AXI bus (on Zynq) are now clocked synchronously with the RTIO
|
||||||
clock, to facilitate implementation of local processing on DRTIO satellites, and to slightly
|
clock, to facilitate implementation of local processing on DRTIO satellites, and to slightly
|
||||||
reduce RTIO latency.
|
reduce RTIO latency.
|
||||||
|
|
|
@ -302,6 +302,12 @@
|
||||||
"pll_n": {
|
"pll_n": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"pll_en": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 1,
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
"pll_vco": {
|
"pll_vco": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
@ -364,6 +370,12 @@
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default": 32
|
"default": 32
|
||||||
},
|
},
|
||||||
|
"pll_en": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 1,
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
"pll_vco": {
|
"pll_vco": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
|
|
|
@ -347,6 +347,7 @@ dependencies = [
|
||||||
name = "satman"
|
name = "satman"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"alloc_list",
|
||||||
"board_artiq",
|
"board_artiq",
|
||||||
"board_misoc",
|
"board_misoc",
|
||||||
"build_misoc",
|
"build_misoc",
|
||||||
|
|
|
@ -18,8 +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::iface::{Routes, SocketStorage};
|
||||||
use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address};
|
use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address, Ipv6Address};
|
||||||
|
|
||||||
fn check_integrity() -> bool {
|
fn check_integrity() -> bool {
|
||||||
extern {
|
extern {
|
||||||
|
@ -411,28 +411,29 @@ fn network_boot() {
|
||||||
println!("Network addresses: {}", net_addresses);
|
println!("Network addresses: {}", net_addresses);
|
||||||
let mut ip_addrs = [
|
let mut ip_addrs = [
|
||||||
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0),
|
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0),
|
||||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0),
|
net_addresses.ipv6_ll_addr,
|
||||||
IpCidr::new(net_addresses.ipv6_ll_addr, 0)
|
IpCidr::new(IpAddress::Ipv6(Ipv6Address::UNSPECIFIED), 0)
|
||||||
];
|
];
|
||||||
if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
||||||
ip_addrs[0] = IpCidr::new(IpAddress::Ipv4(ipv4), 0);
|
ip_addrs[0] = IpCidr::Ipv4(ipv4);
|
||||||
}
|
}
|
||||||
let mut interface = match net_addresses.ipv6_addr {
|
if let Some(ipv6) = net_addresses.ipv6_addr {
|
||||||
Some(addr) => {
|
ip_addrs[2] = IpCidr::Ipv6(ipv6);
|
||||||
ip_addrs[2] = IpCidr::new(addr, 0);
|
|
||||||
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::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()
|
|
||||||
};
|
};
|
||||||
|
let mut routes = [None; 2];
|
||||||
|
let mut interface = 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)
|
||||||
|
.routes(Routes::new(&mut routes[..]))
|
||||||
|
.finalize();
|
||||||
|
|
||||||
|
if let Some(default_route) = net_addresses.ipv4_default_route {
|
||||||
|
interface.routes_mut().add_default_ipv4_route(default_route).unwrap();
|
||||||
|
}
|
||||||
|
if let Some(default_route) = net_addresses.ipv6_default_route {
|
||||||
|
interface.routes_mut().add_default_ipv6_route(default_route).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
let mut rx_storage = [0; 4096];
|
let mut rx_storage = [0; 4096];
|
||||||
let mut tx_storage = [0; 128];
|
let mut tx_storage = [0; 128];
|
||||||
|
|
|
@ -163,7 +163,7 @@ mod imp {
|
||||||
while let Some(result) = iter.next() {
|
while let Some(result) = iter.next() {
|
||||||
let (record_key, record_value) = result?;
|
let (record_key, record_value) = result?;
|
||||||
if key.as_bytes() == record_key {
|
if key.as_bytes() == record_key {
|
||||||
found = true;
|
found = !record_value.is_empty();
|
||||||
// last write wins
|
// last write wins
|
||||||
value = record_value
|
value = record_value
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use core::fmt;
|
||||||
use core::fmt::{Display, Formatter};
|
use core::fmt::{Display, Formatter};
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress, Ipv4Address};
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr, Ipv6Address, Ipv6Cidr};
|
||||||
|
|
||||||
use config;
|
use config;
|
||||||
#[cfg(soc_platform = "kasli")]
|
#[cfg(soc_platform = "kasli")]
|
||||||
|
@ -10,18 +10,22 @@ use i2c_eeprom;
|
||||||
|
|
||||||
pub enum Ipv4AddrConfig {
|
pub enum Ipv4AddrConfig {
|
||||||
UseDhcp,
|
UseDhcp,
|
||||||
Static(Ipv4Address),
|
Static(Ipv4Cidr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Ipv4AddrConfig {
|
impl FromStr for Ipv4AddrConfig {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(if s == "use_dhcp" {
|
if s == "use_dhcp" {
|
||||||
Ipv4AddrConfig::UseDhcp
|
Ok(Ipv4AddrConfig::UseDhcp)
|
||||||
|
} else if let Ok(cidr) = Ipv4Cidr::from_str(s) {
|
||||||
|
Ok(Ipv4AddrConfig::Static(cidr))
|
||||||
|
} else if let Ok(addr) = Ipv4Address::from_str(s) {
|
||||||
|
Ok(Ipv4AddrConfig::Static(Ipv4Cidr::new(addr, 0)))
|
||||||
} else {
|
} else {
|
||||||
Ipv4AddrConfig::Static(Ipv4Address::from_str(s)?)
|
Err(())
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +42,10 @@ impl Display for Ipv4AddrConfig {
|
||||||
pub struct NetAddresses {
|
pub struct NetAddresses {
|
||||||
pub hardware_addr: EthernetAddress,
|
pub hardware_addr: EthernetAddress,
|
||||||
pub ipv4_addr: Ipv4AddrConfig,
|
pub ipv4_addr: Ipv4AddrConfig,
|
||||||
pub ipv6_ll_addr: IpAddress,
|
pub ipv6_ll_addr: IpCidr,
|
||||||
pub ipv6_addr: Option<IpAddress>
|
pub ipv6_addr: Option<Ipv6Cidr>,
|
||||||
|
pub ipv4_default_route: Option<Ipv4Address>,
|
||||||
|
pub ipv6_default_route: Option<Ipv6Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for NetAddresses {
|
impl fmt::Display for NetAddresses {
|
||||||
|
@ -72,28 +78,39 @@ 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)) => addr,
|
||||||
Ok(Ok(addr)) => ipv4_addr = addr,
|
_ => Ipv4AddrConfig::UseDhcp,
|
||||||
_ => ipv4_addr = Ipv4AddrConfig::UseDhcp,
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let ipv6_ll_addr = IpAddress::v6(
|
let ipv4_default_route = match config::read_str("ipv4_default_route", |r| r.map(|s| s.parse())) {
|
||||||
|
Ok(Ok(addr)) => Some(addr),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ipv6_ll_addr = IpCidr::new(IpAddress::v6(
|
||||||
0xfe80, 0x0000, 0x0000, 0x0000,
|
0xfe80, 0x0000, 0x0000, 0x0000,
|
||||||
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16),
|
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16),
|
||||||
((hardware_addr.0[2] as u16) << 8) | 0x00ff,
|
((hardware_addr.0[2] as u16) << 8) | 0x00ff,
|
||||||
0xfe00 | (hardware_addr.0[3] as u16),
|
0xfe00 | (hardware_addr.0[3] as u16),
|
||||||
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16));
|
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16)), 10);
|
||||||
|
|
||||||
let ipv6_addr = match config::read_str("ip6", |r| r.map(|s| s.parse())) {
|
let ipv6_addr = match config::read_str("ip6", |r| r.map(|s| s.parse())) {
|
||||||
Ok(Ok(addr)) => Some(addr),
|
Ok(Ok(addr)) => Some(addr),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ipv6_default_route = match config::read_str("ipv6_default_route", |r| r.map(|s| s.parse())) {
|
||||||
|
Ok(Ok(addr)) => Some(addr),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
NetAddresses {
|
NetAddresses {
|
||||||
hardware_addr: hardware_addr,
|
hardware_addr,
|
||||||
ipv4_addr: ipv4_addr,
|
ipv4_addr,
|
||||||
ipv6_ll_addr: ipv6_ll_addr,
|
ipv6_ll_addr,
|
||||||
ipv6_addr: ipv6_addr
|
ipv6_addr,
|
||||||
|
ipv4_default_route,
|
||||||
|
ipv6_default_route,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ impl<T> From<IoError<T>> for Error<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 512 (max size) - 4 (CRC) - 1 (packet ID) - 4 (trace ID) - 1 (last) - 2 (length) */
|
||||||
|
const DMA_TRACE_MAX_SIZE: usize = 500;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Packet {
|
pub enum Packet {
|
||||||
EchoRequest,
|
EchoRequest,
|
||||||
|
@ -55,8 +58,14 @@ pub enum Packet {
|
||||||
SpiReadReply { succeeded: bool, data: u32 },
|
SpiReadReply { succeeded: bool, data: u32 },
|
||||||
SpiBasicReply { succeeded: bool },
|
SpiBasicReply { succeeded: bool },
|
||||||
|
|
||||||
JdacBasicRequest { destination: u8, dacno: u8, reqno: u8, param: u8 },
|
DmaAddTraceRequest { id: u32, last: bool, length: u16, trace: [u8; DMA_TRACE_MAX_SIZE] },
|
||||||
JdacBasicReply { succeeded: bool, retval: u8 },
|
DmaAddTraceReply { succeeded: bool },
|
||||||
|
DmaRemoveTraceRequest { id: u32 },
|
||||||
|
DmaRemoveTraceReply { succeeded: bool },
|
||||||
|
DmaPlaybackRequest { id: u32, timestamp: u64 },
|
||||||
|
DmaPlaybackReply { succeeded: bool },
|
||||||
|
DmaPlaybackStatus { id: u32, error: u8, channel: u32, timestamp: u64 }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Packet {
|
impl Packet {
|
||||||
|
@ -188,15 +197,40 @@ impl Packet {
|
||||||
succeeded: reader.read_bool()?
|
succeeded: reader.read_bool()?
|
||||||
},
|
},
|
||||||
|
|
||||||
0xa0 => Packet::JdacBasicRequest {
|
0xb0 => {
|
||||||
destination: reader.read_u8()?,
|
let id = reader.read_u32()?;
|
||||||
dacno: reader.read_u8()?,
|
let last = reader.read_bool()?;
|
||||||
reqno: reader.read_u8()?,
|
let length = reader.read_u16()?;
|
||||||
param: reader.read_u8()?,
|
let mut trace: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
|
||||||
|
reader.read_exact(&mut trace[0..length as usize])?;
|
||||||
|
Packet::DmaAddTraceRequest {
|
||||||
|
id: id,
|
||||||
|
last: last,
|
||||||
|
length: length as u16,
|
||||||
|
trace: trace,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0xa1 => Packet::JdacBasicReply {
|
0xb1 => Packet::DmaAddTraceReply {
|
||||||
succeeded: reader.read_bool()?,
|
succeeded: reader.read_bool()?
|
||||||
retval: reader.read_u8()?
|
},
|
||||||
|
0xb2 => Packet::DmaRemoveTraceRequest {
|
||||||
|
id: reader.read_u32()?
|
||||||
|
},
|
||||||
|
0xb3 => Packet::DmaRemoveTraceReply {
|
||||||
|
succeeded: reader.read_bool()?
|
||||||
|
},
|
||||||
|
0xb4 => Packet::DmaPlaybackRequest {
|
||||||
|
id: reader.read_u32()?,
|
||||||
|
timestamp: reader.read_u64()?
|
||||||
|
},
|
||||||
|
0xb5 => Packet::DmaPlaybackReply {
|
||||||
|
succeeded: reader.read_bool()?
|
||||||
|
},
|
||||||
|
0xb6 => Packet::DmaPlaybackStatus {
|
||||||
|
id: reader.read_u32()?,
|
||||||
|
error: reader.read_u8()?,
|
||||||
|
channel: reader.read_u32()?,
|
||||||
|
timestamp: reader.read_u64()?
|
||||||
},
|
},
|
||||||
|
|
||||||
ty => return Err(Error::UnknownPacket(ty))
|
ty => return Err(Error::UnknownPacket(ty))
|
||||||
|
@ -358,18 +392,43 @@ impl Packet {
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
},
|
},
|
||||||
|
|
||||||
Packet::JdacBasicRequest { destination, dacno, reqno, param } => {
|
Packet::DmaAddTraceRequest { id, last, trace, length } => {
|
||||||
writer.write_u8(0xa0)?;
|
writer.write_u8(0xb0)?;
|
||||||
writer.write_u8(destination)?;
|
writer.write_u32(id)?;
|
||||||
writer.write_u8(dacno)?;
|
writer.write_bool(last)?;
|
||||||
writer.write_u8(reqno)?;
|
// trace may be broken down to fit within drtio aux memory limit
|
||||||
writer.write_u8(param)?;
|
// will be reconstructed by satellite
|
||||||
}
|
writer.write_u16(length)?;
|
||||||
Packet::JdacBasicReply { succeeded, retval } => {
|
writer.write_all(&trace[0..length as usize])?;
|
||||||
writer.write_u8(0xa1)?;
|
|
||||||
writer.write_bool(succeeded)?;
|
|
||||||
writer.write_u8(retval)?;
|
|
||||||
},
|
},
|
||||||
|
Packet::DmaAddTraceReply { succeeded } => {
|
||||||
|
writer.write_u8(0xb1)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
},
|
||||||
|
Packet::DmaRemoveTraceRequest { id } => {
|
||||||
|
writer.write_u8(0xb2)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
|
},
|
||||||
|
Packet::DmaRemoveTraceReply { succeeded } => {
|
||||||
|
writer.write_u8(0xb3)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
},
|
||||||
|
Packet::DmaPlaybackRequest { id, timestamp } => {
|
||||||
|
writer.write_u8(0xb4)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
|
writer.write_u64(timestamp)?;
|
||||||
|
},
|
||||||
|
Packet::DmaPlaybackReply { succeeded } => {
|
||||||
|
writer.write_u8(0xb5)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
},
|
||||||
|
Packet::DmaPlaybackStatus { id, error, channel, timestamp } => {
|
||||||
|
writer.write_u8(0xb6)?;
|
||||||
|
writer.write_u32(id)?;
|
||||||
|
writer.write_u8(error)?;
|
||||||
|
writer.write_u32(channel)?;
|
||||||
|
writer.write_u64(timestamp)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,25 @@
|
||||||
use board_misoc::clock;
|
use board_misoc::clock;
|
||||||
use sched;
|
use sched;
|
||||||
use sched::Dhcpv4Socket;
|
use sched::Dhcpv4Socket;
|
||||||
use smoltcp::socket::Dhcpv4Event;
|
use core::fmt::{Display, Formatter};
|
||||||
|
use smoltcp::socket::{Dhcpv4Config, Dhcpv4Event};
|
||||||
use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
||||||
|
|
||||||
|
struct OptionalIpAddressDisplay<'a> (&'a Option<Ipv4Address>);
|
||||||
|
|
||||||
|
impl<'a> Display for OptionalIpAddressDisplay<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self.0 {
|
||||||
|
Some(ip) => write!(f, "{}", ip),
|
||||||
|
None => write!(f, "<not set>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn dhcp_thread(io: sched::Io) {
|
pub fn dhcp_thread(io: sched::Io) {
|
||||||
let mut socket = Dhcpv4Socket::new(&io);
|
let mut socket = Dhcpv4Socket::new(&io);
|
||||||
let mut last_ip: Option<Ipv4Cidr> = None;
|
let mut last_config: Option<Dhcpv4Config> = None;
|
||||||
let mut done_reset = false;
|
let mut done_reset = false;
|
||||||
let start_time = clock::get_ms();
|
let start_time = clock::get_ms();
|
||||||
|
|
||||||
|
@ -15,8 +28,8 @@ pub fn dhcp_thread(io: sched::Io) {
|
||||||
// by the server. This is likely to be because the ethernet device isn't quite
|
// 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
|
// ready at the point that it is sent. The following makes recovery from
|
||||||
// that faster.
|
// that faster.
|
||||||
if !done_reset && last_ip.is_none() && start_time + 1000 < clock::get_ms() {
|
if !done_reset && last_config.is_none() && start_time + 1000 < clock::get_ms() {
|
||||||
info!("Didn't get initial IP in first second. Assuming packet loss, trying a reset.");
|
info!("Didn't get initial config in first second. Assuming packet loss, trying a reset.");
|
||||||
socket.reset();
|
socket.reset();
|
||||||
done_reset = true;
|
done_reset = true;
|
||||||
}
|
}
|
||||||
|
@ -26,27 +39,45 @@ pub fn dhcp_thread(io: sched::Io) {
|
||||||
// Only compare the ip address in the config with previous config because we
|
// 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
|
// ignore the rest of the config i.e. we don't do any DNS or require a default
|
||||||
// gateway.
|
// gateway.
|
||||||
let changed = if let Some(last_ip) = last_ip {
|
let changed = if let Some(last_config) = last_config {
|
||||||
if config.address != last_ip {
|
let mut changed = false;
|
||||||
info!("IP address changed {} -> {}", last_ip, config.address);
|
if config.address != last_config.address {
|
||||||
true
|
info!("IP address changed {} -> {}", last_config.address, config.address);
|
||||||
} else {
|
changed = true;
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
if config.router != last_config.router {
|
||||||
|
info!("Default route changed {} -> {}",
|
||||||
|
OptionalIpAddressDisplay(&last_config.router),
|
||||||
|
OptionalIpAddressDisplay(&config.router),
|
||||||
|
);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
changed
|
||||||
} else {
|
} else {
|
||||||
info!("Acquired IP address: None -> {}", config.address);
|
info!("Acquired DHCP config IP address: None -> {} default route: None -> {}",
|
||||||
|
config.address,
|
||||||
|
OptionalIpAddressDisplay(&config.router),
|
||||||
|
);
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
if changed {
|
if changed {
|
||||||
last_ip = Some(config.address);
|
last_config = Some(config);
|
||||||
io.set_ipv4_address(&config.address);
|
io.set_ipv4_address(&config.address);
|
||||||
|
match config.router {
|
||||||
|
Some(route) => { io.set_ipv4_default_route(route).unwrap(); }
|
||||||
|
None => { io.remove_ipv4_default_route(); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dhcpv4Event::Deconfigured => {
|
Dhcpv4Event::Deconfigured => {
|
||||||
if let Some(ip) = last_ip {
|
if let Some(config) = last_config {
|
||||||
info!("Lost IP address {} -> None", ip);
|
info!("Lost DHCP config IP address {} -> None; default route {} -> None",
|
||||||
|
config.address,
|
||||||
|
OptionalIpAddressDisplay(&config.router),
|
||||||
|
);
|
||||||
io.set_ipv4_address(&Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
|
io.set_ipv4_address(&Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
|
||||||
last_ip = None;
|
io.remove_ipv4_default_route();
|
||||||
|
last_config = None;
|
||||||
}
|
}
|
||||||
// We always get one of these events at the start, ignore that one
|
// We always get one of these events at the start, ignore that one
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use smoltcp::iface::{Interface, InterfaceBuilder};
|
use smoltcp::iface::{Interface, InterfaceBuilder};
|
||||||
use smoltcp::phy::Device;
|
use smoltcp::phy::Device;
|
||||||
use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
|
use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr, Ipv6Cidr};
|
||||||
use board_misoc::net_settings::{Ipv4AddrConfig, NetAddresses};
|
use board_misoc::net_settings::{Ipv4AddrConfig, NetAddresses};
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,14 +16,14 @@ pub trait InterfaceBuilderEx {
|
||||||
impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a, DeviceT> {
|
impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a, DeviceT> {
|
||||||
fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self {
|
fn init_ip_addrs(self, net_addresses: &NetAddresses) -> Self {
|
||||||
let mut storage = [
|
let mut storage = [
|
||||||
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); IP_ADDRESS_STORAGE_SIZE
|
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 32); IP_ADDRESS_STORAGE_SIZE
|
||||||
];
|
];
|
||||||
if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
if let Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
|
||||||
storage[IPV4_INDEX] = IpCidr::new(IpAddress::Ipv4(ipv4), 0);
|
storage[IPV4_INDEX] = IpCidr::Ipv4(ipv4);
|
||||||
}
|
}
|
||||||
storage[IPV6_LL_INDEX] = IpCidr::new(net_addresses.ipv6_ll_addr, 0);
|
storage[IPV6_LL_INDEX] = net_addresses.ipv6_ll_addr;
|
||||||
if let Some(ipv6) = net_addresses.ipv6_addr {
|
if let Some(ipv6) = net_addresses.ipv6_addr {
|
||||||
storage[IPV6_INDEX] = IpCidr::new(ipv6, 0);
|
storage[IPV6_INDEX] = IpCidr::Ipv6(ipv6);
|
||||||
}
|
}
|
||||||
self.ip_addrs(storage)
|
self.ip_addrs(storage)
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,14 @@ impl<'a, DeviceT: for<'d> Device<'d>> InterfaceBuilderEx for InterfaceBuilder<'a
|
||||||
|
|
||||||
pub trait InterfaceEx {
|
pub trait InterfaceEx {
|
||||||
fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr);
|
fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr);
|
||||||
|
fn update_ipv6_addr(&mut self, addr: &Ipv6Cidr);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, DeviceT: for<'d> Device<'d>> InterfaceEx for Interface<'a, DeviceT> {
|
impl<'a, DeviceT: for<'d> Device<'d>> InterfaceEx for Interface<'a, DeviceT> {
|
||||||
fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr) {
|
fn update_ipv4_addr(&mut self, addr: &Ipv4Cidr) {
|
||||||
self.update_ip_addrs(|storage| storage[IPV4_INDEX] = IpCidr::Ipv4(*addr))
|
self.update_ip_addrs(|storage| storage[IPV4_INDEX] = IpCidr::Ipv4(*addr))
|
||||||
}
|
}
|
||||||
|
fn update_ipv6_addr(&mut self, addr: &Ipv6Cidr) {
|
||||||
|
self.update_ip_addrs(|storage| storage[IPV6_INDEX] = IpCidr::Ipv6(*addr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ extern crate logger_artiq;
|
||||||
extern crate proto_artiq;
|
extern crate proto_artiq;
|
||||||
extern crate riscv;
|
extern crate riscv;
|
||||||
|
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use smoltcp::wire::HardwareAddress;
|
use smoltcp::wire::HardwareAddress;
|
||||||
|
@ -42,6 +43,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 smoltcp::iface::Routes;
|
||||||
use ip_addr_storage::InterfaceBuilderEx;
|
use ip_addr_storage::InterfaceBuilderEx;
|
||||||
|
|
||||||
mod rtio_clocking;
|
mod rtio_clocking;
|
||||||
|
@ -151,12 +153,23 @@ fn startup() {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
let interface = smoltcp::iface::InterfaceBuilder::new(net_device, vec![])
|
let mut interface = smoltcp::iface::InterfaceBuilder::new(net_device, vec![])
|
||||||
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
|
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
|
||||||
.init_ip_addrs(&net_addresses)
|
.init_ip_addrs(&net_addresses)
|
||||||
.neighbor_cache(neighbor_cache)
|
.neighbor_cache(neighbor_cache)
|
||||||
|
.routes(Routes::new(BTreeMap::new()))
|
||||||
.finalize();
|
.finalize();
|
||||||
|
|
||||||
|
if !use_dhcp {
|
||||||
|
if let Some(ipv4_default_route) = net_addresses.ipv4_default_route {
|
||||||
|
interface.routes_mut().add_default_ipv4_route(ipv4_default_route).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ipv6_default_route) = net_addresses.ipv6_default_route {
|
||||||
|
interface.routes_mut().add_default_ipv6_route(ipv6_default_route).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
let drtio_routing_table = urc::Urc::new(RefCell::new(
|
let drtio_routing_table = urc::Urc::new(RefCell::new(
|
||||||
drtio_routing::config_routing_table(csr::DRTIO.len())));
|
drtio_routing::config_routing_table(csr::DRTIO.len())));
|
||||||
|
|
|
@ -8,8 +8,8 @@ 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, Ipv4Cidr};
|
use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv4Cidr};
|
||||||
use smoltcp::iface::{Interface, SocketHandle};
|
use smoltcp::iface::{Interface, Route, SocketHandle};
|
||||||
|
|
||||||
use io::{Read, Write};
|
use io::{Read, Write};
|
||||||
use board_misoc::clock;
|
use board_misoc::clock;
|
||||||
|
@ -278,6 +278,14 @@ impl<'a> Io<'a> {
|
||||||
pub fn set_ipv4_address(&self, addr: &Ipv4Cidr) {
|
pub fn set_ipv4_address(&self, addr: &Ipv4Cidr) {
|
||||||
self.network.borrow_mut().update_ipv4_addr(addr)
|
self.network.borrow_mut().update_ipv4_addr(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_ipv4_default_route(&self, addr: Ipv4Address) -> Result<Option<Route>, Error> {
|
||||||
|
Ok(self.network.borrow_mut().routes_mut().add_default_ipv4_route(addr)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_ipv4_default_route(&self) -> Option<Route> {
|
||||||
|
self.network.borrow_mut().routes_mut().remove_default_ipv4_route()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -607,25 +607,18 @@ pub fn thread(io: Io, aux_mutex: &Mutex,
|
||||||
|
|
||||||
let mut kernel_thread = None;
|
let mut kernel_thread = None;
|
||||||
{
|
{
|
||||||
let aux_mutex = aux_mutex.clone();
|
let mut congress = congress.borrow_mut();
|
||||||
let routing_table = routing_table.clone();
|
info!("running startup kernel");
|
||||||
let up_destinations = up_destinations.clone();
|
match flash_kernel_worker(&io, &aux_mutex, &routing_table.borrow(), &up_destinations, &mut congress, "startup_kernel") {
|
||||||
let congress = congress.clone();
|
Ok(()) =>
|
||||||
respawn(&io, &mut kernel_thread, move |io| {
|
info!("startup kernel finished"),
|
||||||
let routing_table = routing_table.borrow();
|
Err(Error::KernelNotFound) =>
|
||||||
let mut congress = congress.borrow_mut();
|
info!("no startup kernel found"),
|
||||||
info!("running startup kernel");
|
Err(err) => {
|
||||||
match flash_kernel_worker(&io, &aux_mutex, &routing_table, &up_destinations, &mut congress, "startup_kernel") {
|
congress.finished_cleanly.set(false);
|
||||||
Ok(()) =>
|
error!("startup kernel aborted: {}", err);
|
||||||
info!("startup kernel finished"),
|
|
||||||
Err(Error::KernelNotFound) =>
|
|
||||||
info!("no startup kernel found"),
|
|
||||||
Err(err) => {
|
|
||||||
congress.finished_cleanly.set(false);
|
|
||||||
error!("startup kernel aborted: {}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -16,4 +16,5 @@ build_misoc = { path = "../libbuild_misoc" }
|
||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
||||||
board_artiq = { path = "../libboard_artiq" }
|
board_artiq = { path = "../libboard_artiq" }
|
||||||
|
alloc_list = { path = "../liballoc_list" }
|
||||||
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
riscv = { version = "0.6.0", features = ["inline-asm"] }
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
use board_misoc::csr;
|
||||||
|
use alloc::{vec::Vec, collections::btree_map::BTreeMap};
|
||||||
|
|
||||||
|
const ALIGNMENT: usize = 64;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum ManagerState {
|
||||||
|
Idle,
|
||||||
|
Playback
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RtioStatus {
|
||||||
|
pub id: u32,
|
||||||
|
pub error: u8,
|
||||||
|
pub channel: u32,
|
||||||
|
pub timestamp: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
IdNotFound,
|
||||||
|
PlaybackInProgress,
|
||||||
|
EntryNotComplete
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Entry {
|
||||||
|
trace: Vec<u8>,
|
||||||
|
padding_len: usize,
|
||||||
|
complete: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Manager {
|
||||||
|
entries: BTreeMap<u32, Entry>,
|
||||||
|
state: ManagerState,
|
||||||
|
currentid: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Manager {
|
||||||
|
pub fn new() -> Manager {
|
||||||
|
// in case Manager is created during a DMA in progress
|
||||||
|
// wait for it to end
|
||||||
|
unsafe {
|
||||||
|
while csr::rtio_dma::enable_read() != 0 {}
|
||||||
|
}
|
||||||
|
Manager {
|
||||||
|
entries: BTreeMap::new(),
|
||||||
|
currentid: 0,
|
||||||
|
state: ManagerState::Idle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, id: u32, last: bool, trace: &[u8], trace_len: usize) -> Result<(), Error> {
|
||||||
|
let entry = match self.entries.get_mut(&id) {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => {
|
||||||
|
self.entries.insert(id, Entry {
|
||||||
|
trace: Vec::new(),
|
||||||
|
padding_len: 0,
|
||||||
|
complete: false });
|
||||||
|
self.entries.get_mut(&id).unwrap()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
entry.trace.extend(&trace[0..trace_len]);
|
||||||
|
|
||||||
|
if last {
|
||||||
|
entry.trace.push(0);
|
||||||
|
let data_len = entry.trace.len();
|
||||||
|
|
||||||
|
// Realign.
|
||||||
|
entry.trace.reserve(ALIGNMENT - 1);
|
||||||
|
let padding = ALIGNMENT - entry.trace.as_ptr() as usize % ALIGNMENT;
|
||||||
|
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
||||||
|
for _ in 0..padding {
|
||||||
|
// Vec guarantees that this will not reallocate
|
||||||
|
entry.trace.push(0)
|
||||||
|
}
|
||||||
|
for i in 1..data_len + 1 {
|
||||||
|
entry.trace[data_len + padding - i] = entry.trace[data_len - i]
|
||||||
|
}
|
||||||
|
entry.complete = true;
|
||||||
|
entry.padding_len = padding;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn erase(&mut self, id: u32) -> Result<(), Error> {
|
||||||
|
match self.entries.remove(&id) {
|
||||||
|
Some(_) => Ok(()),
|
||||||
|
None => Err(Error::IdNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn playback(&mut self, id: u32, timestamp: u64) -> Result<(), Error> {
|
||||||
|
if self.state != ManagerState::Idle {
|
||||||
|
return Err(Error::PlaybackInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry = match self.entries.get(&id){
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => { return Err(Error::IdNotFound); }
|
||||||
|
};
|
||||||
|
if !entry.complete {
|
||||||
|
return Err(Error::EntryNotComplete);
|
||||||
|
}
|
||||||
|
let ptr = entry.trace[entry.padding_len..].as_ptr();
|
||||||
|
assert!(ptr as u32 % 64 == 0);
|
||||||
|
|
||||||
|
self.state = ManagerState::Playback;
|
||||||
|
self.currentid = id;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_dma::base_address_write(ptr as u64);
|
||||||
|
csr::rtio_dma::time_offset_write(timestamp as u64);
|
||||||
|
|
||||||
|
csr::cri_con::selected_write(1);
|
||||||
|
csr::rtio_dma::enable_write(1);
|
||||||
|
// playback has begun here, for status call check_state
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_state(&mut self) -> Option<RtioStatus> {
|
||||||
|
if self.state != ManagerState::Playback {
|
||||||
|
// nothing to report
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let dma_enable = unsafe { csr::rtio_dma::enable_read() };
|
||||||
|
if dma_enable != 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.state = ManagerState::Idle;
|
||||||
|
unsafe {
|
||||||
|
csr::cri_con::selected_write(0);
|
||||||
|
let error = csr::rtio_dma::error_read();
|
||||||
|
let channel = csr::rtio_dma::error_channel_read();
|
||||||
|
let timestamp = csr::rtio_dma::error_timestamp_read();
|
||||||
|
if error != 0 {
|
||||||
|
csr::rtio_dma::error_write(1);
|
||||||
|
}
|
||||||
|
return Some(RtioStatus {
|
||||||
|
id: self.currentid,
|
||||||
|
error: error,
|
||||||
|
channel: channel,
|
||||||
|
timestamp: timestamp });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#![feature(never_type, panic_info_message, llvm_asm)]
|
#![feature(never_type, panic_info_message, llvm_asm, default_alloc_error_handler)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -7,6 +7,7 @@ extern crate log;
|
||||||
extern crate board_misoc;
|
extern crate board_misoc;
|
||||||
extern crate board_artiq;
|
extern crate board_artiq;
|
||||||
extern crate riscv;
|
extern crate riscv;
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
|
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
|
||||||
|
@ -15,8 +16,13 @@ use board_artiq::si5324;
|
||||||
use board_artiq::{spi, drtioaux};
|
use board_artiq::{spi, drtioaux};
|
||||||
use board_artiq::drtio_routing;
|
use board_artiq::drtio_routing;
|
||||||
use riscv::register::{mcause, mepc, mtval};
|
use riscv::register::{mcause, mepc, mtval};
|
||||||
|
use dma::Manager as DmaManager;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static mut ALLOC: alloc_list::ListAlloc = alloc_list::EMPTY;
|
||||||
|
|
||||||
mod repeater;
|
mod repeater;
|
||||||
|
mod dma;
|
||||||
|
|
||||||
fn drtiosat_reset(reset: bool) {
|
fn drtiosat_reset(reset: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -67,7 +73,7 @@ macro_rules! forward {
|
||||||
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {}
|
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
fn process_aux_packet(_manager: &mut DmaManager, _repeaters: &mut [repeater::Repeater],
|
||||||
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
|
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
|
||||||
packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
||||||
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
||||||
|
@ -294,6 +300,24 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||||
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
|
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(has_rtio_dma)]
|
||||||
|
drtioaux::Packet::DmaAddTraceRequest { id, last, length, trace } => {
|
||||||
|
let succeeded = _manager.add(id, last, &trace, length as usize).is_ok();
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::DmaAddTraceReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
#[cfg(has_rtio_dma)]
|
||||||
|
drtioaux::Packet::DmaRemoveTraceRequest { id } => {
|
||||||
|
let succeeded = _manager.erase(id).is_ok();
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::DmaRemoveTraceReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
#[cfg(has_rtio_dma)]
|
||||||
|
drtioaux::Packet::DmaPlaybackRequest { id, timestamp } => {
|
||||||
|
let succeeded = _manager.playback(id, timestamp).is_ok();
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
warn!("received unexpected aux packet");
|
warn!("received unexpected aux packet");
|
||||||
|
@ -302,12 +326,12 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_aux_packets(repeaters: &mut [repeater::Repeater],
|
fn process_aux_packets(dma_manager: &mut DmaManager, repeaters: &mut [repeater::Repeater],
|
||||||
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) {
|
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) {
|
||||||
let result =
|
let result =
|
||||||
drtioaux::recv(0).and_then(|packet| {
|
drtioaux::recv(0).and_then(|packet| {
|
||||||
if let Some(packet) = packet {
|
if let Some(packet) = packet {
|
||||||
process_aux_packet(repeaters, routing_table, rank, packet)
|
process_aux_packet(dma_manager, repeaters, routing_table, rank, packet)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -432,10 +456,13 @@ fn sysclk_setup() {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern fn main() -> i32 {
|
pub extern fn main() -> i32 {
|
||||||
extern {
|
extern {
|
||||||
|
static mut _fheap: u8;
|
||||||
|
static mut _eheap: u8;
|
||||||
static mut _sstack_guard: u8;
|
static mut _sstack_guard: u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
ALLOC.add_range(&mut _fheap, &mut _eheap);
|
||||||
pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
|
pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,13 +538,18 @@ pub extern fn main() -> i32 {
|
||||||
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
|
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA manager created here, so when link is dropped, all DMA traces
|
||||||
|
// are cleared out for a clean slate on subsequent connections,
|
||||||
|
// without a manual intervention.
|
||||||
|
let mut dma_manager = DmaManager::new();
|
||||||
|
|
||||||
drtioaux::reset(0);
|
drtioaux::reset(0);
|
||||||
drtiosat_reset(false);
|
drtiosat_reset(false);
|
||||||
drtiosat_reset_phy(false);
|
drtiosat_reset_phy(false);
|
||||||
|
|
||||||
while drtiosat_link_rx_up() {
|
while drtiosat_link_rx_up() {
|
||||||
drtiosat_process_errors();
|
drtiosat_process_errors();
|
||||||
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank);
|
process_aux_packets(&mut dma_manager, &mut repeaters, &mut routing_table, &mut rank);
|
||||||
for rep in repeaters.iter_mut() {
|
for rep in repeaters.iter_mut() {
|
||||||
rep.service(&routing_table, rank);
|
rep.service(&routing_table, rank);
|
||||||
}
|
}
|
||||||
|
@ -538,6 +570,12 @@ pub extern fn main() -> i32 {
|
||||||
error!("aux packet error: {}", e);
|
error!("aux packet error: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(status) = dma_manager.check_state() {
|
||||||
|
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
|
||||||
|
id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp }) {
|
||||||
|
error!("error sending DMA playback status: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drtiosat_reset_phy(true);
|
drtiosat_reset_phy(true);
|
||||||
|
|
|
@ -66,4 +66,12 @@ SECTIONS
|
||||||
. += 0x10000;
|
. += 0x10000;
|
||||||
_fstack = . - 16;
|
_fstack = . - 16;
|
||||||
} > main_ram
|
} > main_ram
|
||||||
|
|
||||||
|
/* 64MB heap for alloc use */
|
||||||
|
.heap (NOLOAD) : ALIGN(16)
|
||||||
|
{
|
||||||
|
_fheap = .;
|
||||||
|
. = . + 0x4000000;
|
||||||
|
_eheap = .;
|
||||||
|
} > main_ram
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,6 +250,7 @@ class PeripheralManager:
|
||||||
"class": "AD9910",
|
"class": "AD9910",
|
||||||
"arguments": {{
|
"arguments": {{
|
||||||
"pll_n": {pll_n},
|
"pll_n": {pll_n},
|
||||||
|
"pll_en": {pll_en},
|
||||||
"chip_select": {chip_select},
|
"chip_select": {chip_select},
|
||||||
"cpld_device": "{name}_cpld"{sw}{pll_vco}{sync_delay_seed}{io_update_delay}
|
"cpld_device": "{name}_cpld"{sw}{pll_vco}{sync_delay_seed}{io_update_delay}
|
||||||
}}
|
}}
|
||||||
|
@ -259,7 +260,7 @@ class PeripheralManager:
|
||||||
uchn=i,
|
uchn=i,
|
||||||
sw=",\n \"sw_device\": \"ttl_{name}_sw{uchn}\"".format(name=urukul_name, uchn=i) if len(peripheral["ports"]) > 1 else "",
|
sw=",\n \"sw_device\": \"ttl_{name}_sw{uchn}\"".format(name=urukul_name, uchn=i) if len(peripheral["ports"]) > 1 else "",
|
||||||
pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "",
|
pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "",
|
||||||
pll_n=peripheral.get("pll_n", 32),
|
pll_n=peripheral.get("pll_n", 32), pll_en=peripheral.get("pll_en", 1),
|
||||||
sync_delay_seed=",\n \"sync_delay_seed\": \"eeprom_{}:{}\"".format(urukul_name, 64 + 4*i) if synchronization else "",
|
sync_delay_seed=",\n \"sync_delay_seed\": \"eeprom_{}:{}\"".format(urukul_name, 64 + 4*i) if synchronization else "",
|
||||||
io_update_delay=",\n \"io_update_delay\": \"eeprom_{}:{}\"".format(urukul_name, 64 + 4*i) if synchronization else "")
|
io_update_delay=",\n \"io_update_delay\": \"eeprom_{}:{}\"".format(urukul_name, 64 + 4*i) if synchronization else "")
|
||||||
elif dds == "ad9912":
|
elif dds == "ad9912":
|
||||||
|
@ -270,6 +271,7 @@ class PeripheralManager:
|
||||||
"class": "AD9912",
|
"class": "AD9912",
|
||||||
"arguments": {{
|
"arguments": {{
|
||||||
"pll_n": {pll_n},
|
"pll_n": {pll_n},
|
||||||
|
"pll_en": {pll_en},
|
||||||
"chip_select": {chip_select},
|
"chip_select": {chip_select},
|
||||||
"cpld_device": "{name}_cpld"{sw}{pll_vco}
|
"cpld_device": "{name}_cpld"{sw}{pll_vco}
|
||||||
}}
|
}}
|
||||||
|
@ -279,7 +281,7 @@ class PeripheralManager:
|
||||||
uchn=i,
|
uchn=i,
|
||||||
sw=",\n \"sw_device\": \"ttl_{name}_sw{uchn}\"".format(name=urukul_name, uchn=i) if len(peripheral["ports"]) > 1 else "",
|
sw=",\n \"sw_device\": \"ttl_{name}_sw{uchn}\"".format(name=urukul_name, uchn=i) if len(peripheral["ports"]) > 1 else "",
|
||||||
pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "",
|
pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "",
|
||||||
pll_n=peripheral.get("pll_n", 8))
|
pll_n=peripheral.get("pll_n", 8), pll_en=peripheral.get("pll_en", 1))
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return next(channel)
|
return next(channel)
|
||||||
|
@ -463,6 +465,7 @@ class PeripheralManager:
|
||||||
"class": "AD9910",
|
"class": "AD9910",
|
||||||
"arguments": {{
|
"arguments": {{
|
||||||
"pll_n": {pll_n},
|
"pll_n": {pll_n},
|
||||||
|
"pll_en": {pll_en},
|
||||||
"chip_select": 3,
|
"chip_select": 3,
|
||||||
"cpld_device": "{urukul_name}_cpld"{pll_vco}
|
"cpld_device": "{urukul_name}_cpld"{pll_vco}
|
||||||
}}
|
}}
|
||||||
|
@ -472,7 +475,7 @@ class PeripheralManager:
|
||||||
refclk=peripheral.get("refclk", self.master_description["rtio_frequency"]),
|
refclk=peripheral.get("refclk", self.master_description["rtio_frequency"]),
|
||||||
clk_sel=peripheral["clk_sel"],
|
clk_sel=peripheral["clk_sel"],
|
||||||
pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "",
|
pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "",
|
||||||
pll_n=peripheral["pll_n"])
|
pll_n=peripheral["pll_n"], pll_en=peripheral.get("pll_en", 1))
|
||||||
return next(channel)
|
return next(channel)
|
||||||
|
|
||||||
def process_zotino(self, rtio_offset, peripheral):
|
def process_zotino(self, rtio_offset, peripheral):
|
||||||
|
|
|
@ -66,7 +66,7 @@ class Core(Module, AutoCSR):
|
||||||
interface=self.cri)
|
interface=self.cri)
|
||||||
self.submodules += outputs
|
self.submodules += outputs
|
||||||
self.comb += outputs.coarse_timestamp.eq(tsc.coarse_ts)
|
self.comb += outputs.coarse_timestamp.eq(tsc.coarse_ts)
|
||||||
self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts + 16)
|
self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts + 12)
|
||||||
|
|
||||||
inputs = InputCollector(tsc, channels,
|
inputs = InputCollector(tsc, channels,
|
||||||
quash_channels=quash_channels,
|
quash_channels=quash_channels,
|
||||||
|
|
|
@ -567,8 +567,10 @@ class SatelliteBase(BaseSoC):
|
||||||
|
|
||||||
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels, lane_count=sed_lanes)
|
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels, lane_count=sed_lanes)
|
||||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
||||||
|
self.submodules.rtio_dma = rtio.DMA(self.get_native_sdram_if(), self.cpu_dw)
|
||||||
|
self.csr_devices.append("rtio_dma")
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
[self.drtiosat.cri],
|
[self.drtiosat.cri, self.rtio_dma.cri],
|
||||||
[self.local_io.cri] + self.drtio_cri,
|
[self.local_io.cri] + self.drtio_cri,
|
||||||
enable_routing=True)
|
enable_routing=True)
|
||||||
self.csr_devices.append("cri_con")
|
self.csr_devices.append("cri_con")
|
||||||
|
|
|
@ -451,8 +451,10 @@ class _SatelliteBase(BaseSoC):
|
||||||
|
|
||||||
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
||||||
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors)
|
||||||
|
self.submodules.rtio_dma = rtio.DMA(self.get_native_sdram_if(), self.cpu_dw)
|
||||||
|
self.csr_devices.append("rtio_dma")
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
[self.drtiosat.cri],
|
[self.drtiosat.cri, self.rtio_dma.cri],
|
||||||
[self.local_io.cri] + self.drtio_cri,
|
[self.local_io.cri] + self.drtio_cri,
|
||||||
enable_routing=True)
|
enable_routing=True)
|
||||||
self.csr_devices.append("cri_con")
|
self.csr_devices.append("cri_con")
|
||||||
|
|
|
@ -132,7 +132,7 @@ We suggest that you define a function ``get_argparser`` that returns the argumen
|
||||||
Logging
|
Logging
|
||||||
-------
|
-------
|
||||||
|
|
||||||
For the debug, information and warning messages, use the ``logging`` Python module and print the log on the standard error output (the default setting). The logging level is by default "WARNING", meaning that only warning messages and more critical messages will get printed (and no debug nor information messages). By calling ``sipyco.common_args.verbosity_args`` with the parser as argument, you add support for the ``--verbose`` (``-v``) and ``--quiet`` (``-q``) arguments in the parser. Each occurence of ``-v`` (resp. ``-q``) in the arguments will increase (resp. decrease) the log level of the logging module. For instance, if only one ``-v`` is present in the arguments, then more messages (info, warning and above) will get printed. If only one ``-q`` is present in the arguments, then only errors and critical messages will get printed. If ``-qq`` is present in the arguments, then only critical messages will get printed, but no debug/info/warning/error.
|
For the debug, information and warning messages, use the ``logging`` Python module and print the log on the standard error output (the default setting). The logging level is by default "WARNING", meaning that only warning messages and more critical messages will get printed (and no debug nor information messages). By calling ``sipyco.common_args.verbosity_args`` with the parser as argument, you add support for the ``--verbose`` (``-v``) and ``--quiet`` (``-q``) arguments in the parser. Each occurrence of ``-v`` (resp. ``-q``) in the arguments will increase (resp. decrease) the log level of the logging module. For instance, if only one ``-v`` is present in the arguments, then more messages (info, warning and above) will get printed. If only one ``-q`` is present in the arguments, then only errors and critical messages will get printed. If ``-qq`` is present in the arguments, then only critical messages will get printed, but no debug/info/warning/error.
|
||||||
|
|
||||||
The program below exemplifies how to use logging: ::
|
The program below exemplifies how to use logging: ::
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,11 @@ Installing multiple packages and making them visible to the ARTIQ commands requi
|
||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
inputs.artiq.url = "git+https://github.com/m-labs/artiq.git";
|
|
||||||
inputs.extrapkg.url = "git+https://git.m-labs.hk/M-Labs/artiq-extrapkg.git";
|
inputs.extrapkg.url = "git+https://git.m-labs.hk/M-Labs/artiq-extrapkg.git";
|
||||||
inputs.extrapkg.inputs.artiq.follows = "artiq";
|
outputs = { self, extrapkg }:
|
||||||
outputs = { self, artiq, extrapkg }:
|
|
||||||
let
|
let
|
||||||
pkgs = artiq.inputs.nixpkgs.legacyPackages.x86_64-linux;
|
pkgs = extrapkg.pkgs;
|
||||||
aqmain = artiq.packages.x86_64-linux;
|
artiq = extrapkg.packages.x86_64-linux;
|
||||||
aqextra = extrapkg.packages.x86_64-linux;
|
|
||||||
in {
|
in {
|
||||||
defaultPackage.x86_64-linux = pkgs.buildEnv {
|
defaultPackage.x86_64-linux = pkgs.buildEnv {
|
||||||
name = "artiq-env";
|
name = "artiq-env";
|
||||||
|
@ -41,9 +38,11 @@ Installing multiple packages and making them visible to the ARTIQ commands requi
|
||||||
# ========================================
|
# ========================================
|
||||||
(pkgs.python3.withPackages(ps: [
|
(pkgs.python3.withPackages(ps: [
|
||||||
# List desired Python packages here.
|
# List desired Python packages here.
|
||||||
aqmain.artiq
|
artiq.artiq
|
||||||
#ps.paramiko # needed if and only if flashing boards remotely (artiq_flash -H)
|
#ps.paramiko # needed if and only if flashing boards remotely (artiq_flash -H)
|
||||||
#aqextra.flake8-artiq
|
#artiq.flake8-artiq
|
||||||
|
#artiq.dax
|
||||||
|
#artiq.dax-applets
|
||||||
|
|
||||||
# The NixOS package collection contains many other packages that you may find
|
# The NixOS package collection contains many other packages that you may find
|
||||||
# interesting. Here are some examples:
|
# interesting. Here are some examples:
|
||||||
|
@ -52,17 +51,18 @@ Installing multiple packages and making them visible to the ARTIQ commands requi
|
||||||
#ps.scipy
|
#ps.scipy
|
||||||
#ps.numba
|
#ps.numba
|
||||||
#ps.matplotlib
|
#ps.matplotlib
|
||||||
|
#ps.jsonschema # required by artiq_ddb_template
|
||||||
# or if you need Qt (will recompile):
|
# or if you need Qt (will recompile):
|
||||||
#(ps.matplotlib.override { enableQt = true; })
|
#(ps.matplotlib.override { enableQt = true; })
|
||||||
#ps.bokeh
|
#ps.bokeh
|
||||||
#ps.cirq
|
#ps.cirq
|
||||||
#ps.qiskit
|
#ps.qiskit
|
||||||
]))
|
]))
|
||||||
#aqextra.korad_ka3005p
|
#artiq.korad_ka3005p
|
||||||
#aqextra.novatech409b
|
#artiq.novatech409b
|
||||||
# List desired non-Python packages here
|
# List desired non-Python packages here
|
||||||
#aqmain.openocd-bscanspi # needed if and only if flashing boards
|
#artiq.openocd-bscanspi # needed if and only if flashing boards
|
||||||
# Other potentially interesting packages from the NixOS package collection:
|
# Other potentially interesting non-Python packages from the NixOS package collection:
|
||||||
#pkgs.gtkwave
|
#pkgs.gtkwave
|
||||||
#pkgs.spyder
|
#pkgs.spyder
|
||||||
#pkgs.R
|
#pkgs.R
|
||||||
|
@ -231,20 +231,26 @@ 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).
|
||||||
|
|
||||||
If the ip config field is not set, or set to "use_dhcp" then the device will attempt to obtain an IP address using
|
If the ``ip`` config field is not set, or set to ``use_dhcp`` then the device will
|
||||||
DHCP. If a static IP address is wanted, install OpenOCD as before, and flash the IP (and, if necessary, MAC) addresses
|
attempt to obtain an IP address and default gateway using DHCP. If a static IP
|
||||||
directly: ::
|
address is wanted, install OpenOCD as before, and flash the IP, default gateway
|
||||||
|
(and, if necessary, MAC and IPv6) 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/xx -s ipv4_default_route xx.xx.xx.xx -s ip6 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/xx -s ipv6_default_route xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
|
||||||
$ 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 you only want to access the core device from the same subnet you may
|
||||||
|
omit the default gateway and IPv4 prefix length: ::
|
||||||
|
|
||||||
|
$ artiq_mkfs flash_storage.img -s mac xx:xx:xx:xx:xx:xx -s ip xx.xx.xx.xx
|
||||||
|
|
||||||
If DHCP has been used the address can be found in the console output, which can be viewed using: ::
|
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
|
$ 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. Note that on Windows you might need to install the `FTDI drivers <https://ftdichip.com/drivers/>`_ first.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue