forked from M-Labs/artiq
drtio: add infrastructure for reporting busy/collision errors
This commit is contained in:
parent
0a687b7902
commit
008678b741
|
@ -20,6 +20,12 @@ use proto::*;
|
|||
pub enum Packet {
|
||||
EchoRequest,
|
||||
EchoReply,
|
||||
|
||||
RtioErrorRequest,
|
||||
RtioNoErrorReply,
|
||||
RtioErrorCollisionReply,
|
||||
RtioErrorBusyReply,
|
||||
|
||||
MonitorRequest { channel: u16, probe: u8 },
|
||||
MonitorReply { value: u32 },
|
||||
InjectionRequest { channel: u16, overrd: u8, value: u8 },
|
||||
|
@ -30,25 +36,31 @@ pub enum Packet {
|
|||
impl Packet {
|
||||
pub fn read_from(reader: &mut Read) -> io::Result<Packet> {
|
||||
Ok(match read_u8(reader)? {
|
||||
0 => Packet::EchoRequest,
|
||||
1 => Packet::EchoReply,
|
||||
2 => Packet::MonitorRequest {
|
||||
0x00 => Packet::EchoRequest,
|
||||
0x01 => Packet::EchoReply,
|
||||
|
||||
0x20 => Packet::RtioErrorRequest,
|
||||
0x21 => Packet::RtioNoErrorReply,
|
||||
0x22 => Packet::RtioErrorCollisionReply,
|
||||
0x23 => Packet::RtioErrorBusyReply,
|
||||
|
||||
0x40 => Packet::MonitorRequest {
|
||||
channel: read_u16(reader)?,
|
||||
probe: read_u8(reader)?
|
||||
},
|
||||
3 => Packet::MonitorReply {
|
||||
0x41 => Packet::MonitorReply {
|
||||
value: read_u32(reader)?
|
||||
},
|
||||
4 => Packet::InjectionRequest {
|
||||
0x50 => Packet::InjectionRequest {
|
||||
channel: read_u16(reader)?,
|
||||
overrd: read_u8(reader)?,
|
||||
value: read_u8(reader)?
|
||||
},
|
||||
5 => Packet::InjectionStatusRequest {
|
||||
0x51 => Packet::InjectionStatusRequest {
|
||||
channel: read_u16(reader)?,
|
||||
overrd: read_u8(reader)?
|
||||
},
|
||||
6 => Packet::InjectionStatusReply {
|
||||
0x52 => Packet::InjectionStatusReply {
|
||||
value: read_u8(reader)?
|
||||
},
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown packet type"))
|
||||
|
@ -57,30 +69,36 @@ impl Packet {
|
|||
|
||||
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
||||
match *self {
|
||||
Packet::EchoRequest => write_u8(writer, 0)?,
|
||||
Packet::EchoReply => write_u8(writer, 1)?,
|
||||
Packet::EchoRequest => write_u8(writer, 0x00)?,
|
||||
Packet::EchoReply => write_u8(writer, 0x01)?,
|
||||
|
||||
Packet::RtioErrorRequest => write_u8(writer, 0x20)?,
|
||||
Packet::RtioNoErrorReply => write_u8(writer, 0x21)?,
|
||||
Packet::RtioErrorCollisionReply => write_u8(writer, 0x22)?,
|
||||
Packet::RtioErrorBusyReply => write_u8(writer, 0x23)?,
|
||||
|
||||
Packet::MonitorRequest { channel, probe } => {
|
||||
write_u8(writer, 2)?;
|
||||
write_u8(writer, 0x40)?;
|
||||
write_u16(writer, channel)?;
|
||||
write_u8(writer, probe)?;
|
||||
},
|
||||
Packet::MonitorReply { value } => {
|
||||
write_u8(writer, 3)?;
|
||||
write_u8(writer, 0x41)?;
|
||||
write_u32(writer, value)?;
|
||||
},
|
||||
Packet::InjectionRequest { channel, overrd, value } => {
|
||||
write_u8(writer, 4)?;
|
||||
write_u8(writer, 0x50)?;
|
||||
write_u16(writer, channel)?;
|
||||
write_u8(writer, overrd)?;
|
||||
write_u8(writer, value)?;
|
||||
},
|
||||
Packet::InjectionStatusRequest { channel, overrd } => {
|
||||
write_u8(writer, 5)?;
|
||||
write_u8(writer, 0x51)?;
|
||||
write_u16(writer, channel)?;
|
||||
write_u8(writer, overrd)?;
|
||||
},
|
||||
Packet::InjectionStatusReply { value } => {
|
||||
write_u8(writer, 6)?;
|
||||
write_u8(writer, 0x52)?;
|
||||
write_u8(writer, value)?;
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +179,18 @@ pub mod hw {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn recv_timeout(timeout_ms: u64) -> io::Result<Packet> {
|
||||
let limit = board::clock::get_ms() + timeout_ms;
|
||||
while board::clock::get_ms() < limit {
|
||||
match recv() {
|
||||
Ok(None) => (),
|
||||
Ok(Some(packet)) => return Ok(packet),
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
}
|
||||
return Err(io::Error::new(io::ErrorKind::TimedOut, "timed out waiting for data"))
|
||||
}
|
||||
|
||||
fn tx_get_buffer() -> &'static mut [u8] {
|
||||
unsafe {
|
||||
while board::csr::drtio::aux_tx_read() != 0 {}
|
||||
|
|
|
@ -35,32 +35,22 @@ fn read_probe_local(channel: u16, probe: u8) -> u32 {
|
|||
}
|
||||
|
||||
#[cfg(has_drtio)]
|
||||
fn read_probe_drtio(io: &Io, channel: u16, probe: u8) -> u32 {
|
||||
fn read_probe_drtio(channel: u16, probe: u8) -> u32 {
|
||||
if rtio_mgt::drtio::link_is_running() {
|
||||
let request = drtioaux::Packet::MonitorRequest { channel: channel, probe: probe };
|
||||
drtioaux::hw::send(&request).unwrap();
|
||||
|
||||
let timeout = clock::get_ms() + 20;
|
||||
while clock::get_ms() < timeout {
|
||||
if !rtio_mgt::drtio::link_is_running() {
|
||||
return 0
|
||||
}
|
||||
match drtioaux::hw::recv() {
|
||||
Ok(None) => (),
|
||||
Ok(Some(drtioaux::Packet::MonitorReply { value })) => return value,
|
||||
Ok(Some(_)) => warn!("received unexpected aux packet"),
|
||||
Err(e) => warn!("aux packet error ({})", e)
|
||||
}
|
||||
io.relinquish().unwrap();
|
||||
match drtioaux::hw::recv_timeout(10) {
|
||||
Ok(drtioaux::Packet::MonitorReply { value }) => return value,
|
||||
Ok(_) => error!("received unexpected aux packet"),
|
||||
Err(e) => error!("aux packet error ({})", e)
|
||||
}
|
||||
warn!("aux packet timeout");
|
||||
0
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn read_probe(_io: &Io, channel: u32, probe: u8) -> u32 {
|
||||
fn read_probe(channel: u32, probe: u8) -> u32 {
|
||||
#[cfg(has_rtio_moninj)]
|
||||
{
|
||||
if channel & 0xff0000 == 0 {
|
||||
|
@ -70,7 +60,7 @@ fn read_probe(_io: &Io, channel: u32, probe: u8) -> u32 {
|
|||
#[cfg(has_drtio)]
|
||||
{
|
||||
if channel & 0xff0000 != 0 {
|
||||
return read_probe_drtio(_io, channel as u16, probe)
|
||||
return read_probe_drtio(channel as u16, probe)
|
||||
}
|
||||
}
|
||||
error!("read_probe: unrecognized channel number {}", channel);
|
||||
|
@ -126,35 +116,25 @@ fn read_injection_status_local(channel: u16, overrd: u8) -> u8 {
|
|||
}
|
||||
|
||||
#[cfg(has_drtio)]
|
||||
fn read_injection_status_drtio(io: &Io, channel: u16, overrd: u8) -> u8 {
|
||||
fn read_injection_status_drtio(channel: u16, overrd: u8) -> u8 {
|
||||
if rtio_mgt::drtio::link_is_running() {
|
||||
let request = drtioaux::Packet::InjectionStatusRequest {
|
||||
channel: channel,
|
||||
overrd: overrd
|
||||
};
|
||||
drtioaux::hw::send(&request).unwrap();
|
||||
|
||||
let timeout = clock::get_ms() + 20;
|
||||
while clock::get_ms() < timeout {
|
||||
if !rtio_mgt::drtio::link_is_running() {
|
||||
return 0
|
||||
}
|
||||
match drtioaux::hw::recv() {
|
||||
Ok(None) => (),
|
||||
Ok(Some(drtioaux::Packet::InjectionStatusReply { value })) => return value,
|
||||
Ok(Some(_)) => warn!("received unexpected aux packet"),
|
||||
Err(e) => warn!("aux packet error ({})", e)
|
||||
}
|
||||
io.relinquish().unwrap();
|
||||
match drtioaux::hw::recv_timeout(10) {
|
||||
Ok(drtioaux::Packet::InjectionStatusReply { value }) => return value,
|
||||
Ok(_) => error!("received unexpected aux packet"),
|
||||
Err(e) => error!("aux packet error ({})", e)
|
||||
}
|
||||
warn!("aux packet timeout");
|
||||
0
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn read_injection_status(_io: &Io, channel: u32, probe: u8) -> u8 {
|
||||
fn read_injection_status(channel: u32, probe: u8) -> u8 {
|
||||
#[cfg(has_rtio_moninj)]
|
||||
{
|
||||
if channel & 0xff0000 == 0 {
|
||||
|
@ -164,7 +144,7 @@ fn read_injection_status(_io: &Io, channel: u32, probe: u8) -> u8 {
|
|||
#[cfg(has_drtio)]
|
||||
{
|
||||
if channel & 0xff0000 != 0 {
|
||||
return read_injection_status_drtio(_io, channel as u16, probe)
|
||||
return read_injection_status_drtio(channel as u16, probe)
|
||||
}
|
||||
}
|
||||
error!("read_injection_status: unrecognized channel number {}", channel);
|
||||
|
@ -191,7 +171,7 @@ fn connection_worker(io: &Io, mut stream: &mut TcpStream) -> io::Result<()> {
|
|||
},
|
||||
HostMessage::Inject { channel, overrd, value } => inject(channel, overrd, value),
|
||||
HostMessage::GetInjectionStatus { channel, overrd } => {
|
||||
let value = read_injection_status(io, channel, overrd);
|
||||
let value = read_injection_status(channel, overrd);
|
||||
let reply = DeviceMessage::InjectionStatus {
|
||||
channel: channel,
|
||||
overrd: overrd,
|
||||
|
@ -206,7 +186,7 @@ fn connection_worker(io: &Io, mut stream: &mut TcpStream) -> io::Result<()> {
|
|||
|
||||
if clock::get_ms() > next_check {
|
||||
for (&(channel, probe), previous) in watch_list.iter_mut() {
|
||||
let current = read_probe(io, channel, probe);
|
||||
let current = read_probe(channel, probe);
|
||||
if previous.is_none() || (previous.unwrap() != current) {
|
||||
let message = DeviceMessage::MonitorStatus {
|
||||
channel: channel,
|
||||
|
|
|
@ -43,7 +43,8 @@ pub mod drtio {
|
|||
|
||||
pub fn startup(io: &Io) {
|
||||
io.spawn(4096, link_thread);
|
||||
io.spawn(4096, error_thread);
|
||||
io.spawn(4096, local_error_thread);
|
||||
io.spawn(4096, aux_error_thread);
|
||||
}
|
||||
|
||||
static mut LINK_RUNNING: bool = false;
|
||||
|
@ -144,7 +145,7 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn error_thread(io: Io) {
|
||||
pub fn local_error_thread(io: Io) {
|
||||
loop {
|
||||
unsafe {
|
||||
io.until(|| csr::drtio::protocol_error_read() != 0).unwrap();
|
||||
|
@ -162,6 +163,22 @@ pub mod drtio {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aux_error_thread(io: Io) {
|
||||
loop {
|
||||
io.sleep(200).unwrap();
|
||||
if link_is_running() {
|
||||
drtioaux::hw::send(&drtioaux::Packet::RtioErrorRequest).unwrap();
|
||||
match drtioaux::hw::recv_timeout(10) {
|
||||
Ok(drtioaux::Packet::RtioNoErrorReply) => (),
|
||||
Ok(drtioaux::Packet::RtioErrorCollisionReply) => error!("RTIO collision (in satellite)"),
|
||||
Ok(drtioaux::Packet::RtioErrorBusyReply) => error!("RTIO busy (in satellite)"),
|
||||
Ok(_) => error!("received unexpected aux packet"),
|
||||
Err(e) => error!("aux packet error ({})", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(has_drtio))]
|
||||
|
|
|
@ -16,6 +16,27 @@ fn process_aux_packet(p: &drtioaux::Packet) {
|
|||
// and u16 otherwise; hence the `as _` conversion.
|
||||
match *p {
|
||||
drtioaux::Packet::EchoRequest => drtioaux::hw::send(&drtioaux::Packet::EchoReply).unwrap(),
|
||||
|
||||
drtioaux::Packet::RtioErrorRequest => {
|
||||
let errors;
|
||||
unsafe {
|
||||
errors = board::csr::drtio::rtio_error_read();
|
||||
}
|
||||
if errors & 1 != 0 {
|
||||
unsafe {
|
||||
board::csr::drtio::rtio_error_write(1);
|
||||
}
|
||||
drtioaux::hw::send(&drtioaux::Packet::RtioErrorCollisionReply).unwrap();
|
||||
} else if errors & 2 != 0 {
|
||||
unsafe {
|
||||
board::csr::drtio::rtio_error_write(2);
|
||||
}
|
||||
drtioaux::hw::send(&drtioaux::Packet::RtioErrorBusyReply).unwrap();
|
||||
} else {
|
||||
drtioaux::hw::send(&drtioaux::Packet::RtioNoErrorReply).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
drtioaux::Packet::MonitorRequest { channel, probe } => {
|
||||
let value;
|
||||
#[cfg(has_rtio_moninj)]
|
||||
|
|
|
@ -8,29 +8,28 @@ from misoc.interconnect.csr import *
|
|||
class RTErrorsSatellite(Module, AutoCSR):
|
||||
def __init__(self, rt_packet, ios):
|
||||
self.protocol_error = CSR(4)
|
||||
self.rtio_error = CSR(2)
|
||||
|
||||
def error_csr(csr, *sources):
|
||||
for n, source in enumerate(sources):
|
||||
pending = Signal(related=source)
|
||||
ps = PulseSynchronizer("rtio", "sys")
|
||||
self.submodules += ps
|
||||
self.comb += ps.i.eq(source)
|
||||
self.sync += [
|
||||
If(csr.re & csr.r[n], pending.eq(0)),
|
||||
If(ps.o, pending.eq(1))
|
||||
]
|
||||
self.comb += csr.w[n].eq(pending)
|
||||
|
||||
# The master is normally responsible for avoiding output overflows and
|
||||
# output underflows.
|
||||
# Error reports here are only for diagnosing internal ARTIQ bugs.
|
||||
|
||||
unknown_packet_type = Signal()
|
||||
packet_truncated = Signal()
|
||||
write_overflow = Signal()
|
||||
write_underflow = Signal()
|
||||
self.comb += self.protocol_error.w.eq(
|
||||
Cat(unknown_packet_type, packet_truncated,
|
||||
write_underflow, write_overflow))
|
||||
|
||||
for n, (target, source) in enumerate([
|
||||
(unknown_packet_type, rt_packet.unknown_packet_type),
|
||||
(packet_truncated, rt_packet.packet_truncated),
|
||||
(write_underflow, ios.write_underflow),
|
||||
(write_overflow, ios.write_overflow)]):
|
||||
ps = PulseSynchronizer("rtio", "sys")
|
||||
self.submodules += ps
|
||||
self.comb += ps.i.eq(source)
|
||||
self.sync += [
|
||||
If(self.protocol_error.re & self.protocol_error.r[n], target.eq(0)),
|
||||
If(ps.o, target.eq(1))
|
||||
]
|
||||
|
||||
error_csr(self.protocol_error,
|
||||
rt_packet.unknown_packet_type,
|
||||
rt_packet.packet_truncated,
|
||||
ios.write_underflow,
|
||||
ios.write_overflow)
|
||||
error_csr(self.rtio_error,
|
||||
ios.collision,
|
||||
ios.busy)
|
||||
|
|
|
@ -11,6 +11,8 @@ class IOS(Module):
|
|||
def __init__(self, rt_packet, channels, max_fine_ts_width, full_ts_width):
|
||||
self.write_underflow = Signal()
|
||||
self.write_overflow = Signal()
|
||||
self.collision = Signal()
|
||||
self.busy = Signal()
|
||||
|
||||
self.rt_packet = rt_packet
|
||||
self.max_fine_ts_width = max_fine_ts_width
|
||||
|
@ -26,7 +28,9 @@ class IOS(Module):
|
|||
|
||||
self.sync.rio += [
|
||||
self.write_underflow.eq(0),
|
||||
self.write_overflow.eq(0)
|
||||
self.write_overflow.eq(0),
|
||||
self.collision.eq(0),
|
||||
self.busy.eq(0)
|
||||
]
|
||||
for n, channel in enumerate(channels):
|
||||
self.add_output(n, channel)
|
||||
|
|
Loading…
Reference in New Issue