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 {
|
pub enum Packet {
|
||||||
EchoRequest,
|
EchoRequest,
|
||||||
EchoReply,
|
EchoReply,
|
||||||
|
|
||||||
|
RtioErrorRequest,
|
||||||
|
RtioNoErrorReply,
|
||||||
|
RtioErrorCollisionReply,
|
||||||
|
RtioErrorBusyReply,
|
||||||
|
|
||||||
MonitorRequest { channel: u16, probe: u8 },
|
MonitorRequest { channel: u16, probe: u8 },
|
||||||
MonitorReply { value: u32 },
|
MonitorReply { value: u32 },
|
||||||
InjectionRequest { channel: u16, overrd: u8, value: u8 },
|
InjectionRequest { channel: u16, overrd: u8, value: u8 },
|
||||||
|
@ -30,25 +36,31 @@ pub enum Packet {
|
||||||
impl Packet {
|
impl Packet {
|
||||||
pub fn read_from(reader: &mut Read) -> io::Result<Packet> {
|
pub fn read_from(reader: &mut Read) -> io::Result<Packet> {
|
||||||
Ok(match read_u8(reader)? {
|
Ok(match read_u8(reader)? {
|
||||||
0 => Packet::EchoRequest,
|
0x00 => Packet::EchoRequest,
|
||||||
1 => Packet::EchoReply,
|
0x01 => Packet::EchoReply,
|
||||||
2 => Packet::MonitorRequest {
|
|
||||||
|
0x20 => Packet::RtioErrorRequest,
|
||||||
|
0x21 => Packet::RtioNoErrorReply,
|
||||||
|
0x22 => Packet::RtioErrorCollisionReply,
|
||||||
|
0x23 => Packet::RtioErrorBusyReply,
|
||||||
|
|
||||||
|
0x40 => Packet::MonitorRequest {
|
||||||
channel: read_u16(reader)?,
|
channel: read_u16(reader)?,
|
||||||
probe: read_u8(reader)?
|
probe: read_u8(reader)?
|
||||||
},
|
},
|
||||||
3 => Packet::MonitorReply {
|
0x41 => Packet::MonitorReply {
|
||||||
value: read_u32(reader)?
|
value: read_u32(reader)?
|
||||||
},
|
},
|
||||||
4 => Packet::InjectionRequest {
|
0x50 => Packet::InjectionRequest {
|
||||||
channel: read_u16(reader)?,
|
channel: read_u16(reader)?,
|
||||||
overrd: read_u8(reader)?,
|
overrd: read_u8(reader)?,
|
||||||
value: read_u8(reader)?
|
value: read_u8(reader)?
|
||||||
},
|
},
|
||||||
5 => Packet::InjectionStatusRequest {
|
0x51 => Packet::InjectionStatusRequest {
|
||||||
channel: read_u16(reader)?,
|
channel: read_u16(reader)?,
|
||||||
overrd: read_u8(reader)?
|
overrd: read_u8(reader)?
|
||||||
},
|
},
|
||||||
6 => Packet::InjectionStatusReply {
|
0x52 => Packet::InjectionStatusReply {
|
||||||
value: read_u8(reader)?
|
value: read_u8(reader)?
|
||||||
},
|
},
|
||||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown packet type"))
|
_ => 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<()> {
|
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
||||||
match *self {
|
match *self {
|
||||||
Packet::EchoRequest => write_u8(writer, 0)?,
|
Packet::EchoRequest => write_u8(writer, 0x00)?,
|
||||||
Packet::EchoReply => write_u8(writer, 1)?,
|
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 } => {
|
Packet::MonitorRequest { channel, probe } => {
|
||||||
write_u8(writer, 2)?;
|
write_u8(writer, 0x40)?;
|
||||||
write_u16(writer, channel)?;
|
write_u16(writer, channel)?;
|
||||||
write_u8(writer, probe)?;
|
write_u8(writer, probe)?;
|
||||||
},
|
},
|
||||||
Packet::MonitorReply { value } => {
|
Packet::MonitorReply { value } => {
|
||||||
write_u8(writer, 3)?;
|
write_u8(writer, 0x41)?;
|
||||||
write_u32(writer, value)?;
|
write_u32(writer, value)?;
|
||||||
},
|
},
|
||||||
Packet::InjectionRequest { channel, overrd, value } => {
|
Packet::InjectionRequest { channel, overrd, value } => {
|
||||||
write_u8(writer, 4)?;
|
write_u8(writer, 0x50)?;
|
||||||
write_u16(writer, channel)?;
|
write_u16(writer, channel)?;
|
||||||
write_u8(writer, overrd)?;
|
write_u8(writer, overrd)?;
|
||||||
write_u8(writer, value)?;
|
write_u8(writer, value)?;
|
||||||
},
|
},
|
||||||
Packet::InjectionStatusRequest { channel, overrd } => {
|
Packet::InjectionStatusRequest { channel, overrd } => {
|
||||||
write_u8(writer, 5)?;
|
write_u8(writer, 0x51)?;
|
||||||
write_u16(writer, channel)?;
|
write_u16(writer, channel)?;
|
||||||
write_u8(writer, overrd)?;
|
write_u8(writer, overrd)?;
|
||||||
},
|
},
|
||||||
Packet::InjectionStatusReply { value } => {
|
Packet::InjectionStatusReply { value } => {
|
||||||
write_u8(writer, 6)?;
|
write_u8(writer, 0x52)?;
|
||||||
write_u8(writer, value)?;
|
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] {
|
fn tx_get_buffer() -> &'static mut [u8] {
|
||||||
unsafe {
|
unsafe {
|
||||||
while board::csr::drtio::aux_tx_read() != 0 {}
|
while board::csr::drtio::aux_tx_read() != 0 {}
|
||||||
|
|
|
@ -35,32 +35,22 @@ fn read_probe_local(channel: u16, probe: u8) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
#[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() {
|
if rtio_mgt::drtio::link_is_running() {
|
||||||
let request = drtioaux::Packet::MonitorRequest { channel: channel, probe: probe };
|
let request = drtioaux::Packet::MonitorRequest { channel: channel, probe: probe };
|
||||||
drtioaux::hw::send(&request).unwrap();
|
drtioaux::hw::send(&request).unwrap();
|
||||||
|
match drtioaux::hw::recv_timeout(10) {
|
||||||
let timeout = clock::get_ms() + 20;
|
Ok(drtioaux::Packet::MonitorReply { value }) => return value,
|
||||||
while clock::get_ms() < timeout {
|
Ok(_) => error!("received unexpected aux packet"),
|
||||||
if !rtio_mgt::drtio::link_is_running() {
|
Err(e) => error!("aux packet error ({})", e)
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
warn!("aux packet timeout");
|
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_probe(_io: &Io, channel: u32, probe: u8) -> u32 {
|
fn read_probe(channel: u32, probe: u8) -> u32 {
|
||||||
#[cfg(has_rtio_moninj)]
|
#[cfg(has_rtio_moninj)]
|
||||||
{
|
{
|
||||||
if channel & 0xff0000 == 0 {
|
if channel & 0xff0000 == 0 {
|
||||||
|
@ -70,7 +60,7 @@ fn read_probe(_io: &Io, channel: u32, probe: u8) -> u32 {
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
{
|
{
|
||||||
if channel & 0xff0000 != 0 {
|
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);
|
error!("read_probe: unrecognized channel number {}", channel);
|
||||||
|
@ -126,35 +116,25 @@ fn read_injection_status_local(channel: u16, overrd: u8) -> u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_drtio)]
|
#[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() {
|
if rtio_mgt::drtio::link_is_running() {
|
||||||
let request = drtioaux::Packet::InjectionStatusRequest {
|
let request = drtioaux::Packet::InjectionStatusRequest {
|
||||||
channel: channel,
|
channel: channel,
|
||||||
overrd: overrd
|
overrd: overrd
|
||||||
};
|
};
|
||||||
drtioaux::hw::send(&request).unwrap();
|
drtioaux::hw::send(&request).unwrap();
|
||||||
|
match drtioaux::hw::recv_timeout(10) {
|
||||||
let timeout = clock::get_ms() + 20;
|
Ok(drtioaux::Packet::InjectionStatusReply { value }) => return value,
|
||||||
while clock::get_ms() < timeout {
|
Ok(_) => error!("received unexpected aux packet"),
|
||||||
if !rtio_mgt::drtio::link_is_running() {
|
Err(e) => error!("aux packet error ({})", e)
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
warn!("aux packet timeout");
|
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
0
|
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)]
|
#[cfg(has_rtio_moninj)]
|
||||||
{
|
{
|
||||||
if channel & 0xff0000 == 0 {
|
if channel & 0xff0000 == 0 {
|
||||||
|
@ -164,7 +144,7 @@ fn read_injection_status(_io: &Io, channel: u32, probe: u8) -> u8 {
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
{
|
{
|
||||||
if channel & 0xff0000 != 0 {
|
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);
|
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::Inject { channel, overrd, value } => inject(channel, overrd, value),
|
||||||
HostMessage::GetInjectionStatus { channel, overrd } => {
|
HostMessage::GetInjectionStatus { channel, overrd } => {
|
||||||
let value = read_injection_status(io, channel, overrd);
|
let value = read_injection_status(channel, overrd);
|
||||||
let reply = DeviceMessage::InjectionStatus {
|
let reply = DeviceMessage::InjectionStatus {
|
||||||
channel: channel,
|
channel: channel,
|
||||||
overrd: overrd,
|
overrd: overrd,
|
||||||
|
@ -206,7 +186,7 @@ fn connection_worker(io: &Io, mut stream: &mut TcpStream) -> io::Result<()> {
|
||||||
|
|
||||||
if clock::get_ms() > next_check {
|
if clock::get_ms() > next_check {
|
||||||
for (&(channel, probe), previous) in watch_list.iter_mut() {
|
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) {
|
if previous.is_none() || (previous.unwrap() != current) {
|
||||||
let message = DeviceMessage::MonitorStatus {
|
let message = DeviceMessage::MonitorStatus {
|
||||||
channel: channel,
|
channel: channel,
|
||||||
|
|
|
@ -43,7 +43,8 @@ pub mod drtio {
|
||||||
|
|
||||||
pub fn startup(io: &Io) {
|
pub fn startup(io: &Io) {
|
||||||
io.spawn(4096, link_thread);
|
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;
|
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 {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
io.until(|| csr::drtio::protocol_error_read() != 0).unwrap();
|
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))]
|
#[cfg(not(has_drtio))]
|
||||||
|
|
|
@ -16,6 +16,27 @@ fn process_aux_packet(p: &drtioaux::Packet) {
|
||||||
// and u16 otherwise; hence the `as _` conversion.
|
// and u16 otherwise; hence the `as _` conversion.
|
||||||
match *p {
|
match *p {
|
||||||
drtioaux::Packet::EchoRequest => drtioaux::hw::send(&drtioaux::Packet::EchoReply).unwrap(),
|
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 } => {
|
drtioaux::Packet::MonitorRequest { channel, probe } => {
|
||||||
let value;
|
let value;
|
||||||
#[cfg(has_rtio_moninj)]
|
#[cfg(has_rtio_moninj)]
|
||||||
|
|
|
@ -8,29 +8,28 @@ from misoc.interconnect.csr import *
|
||||||
class RTErrorsSatellite(Module, AutoCSR):
|
class RTErrorsSatellite(Module, AutoCSR):
|
||||||
def __init__(self, rt_packet, ios):
|
def __init__(self, rt_packet, ios):
|
||||||
self.protocol_error = CSR(4)
|
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
|
# The master is normally responsible for avoiding output overflows and
|
||||||
# output underflows.
|
# output underflows.
|
||||||
# Error reports here are only for diagnosing internal ARTIQ bugs.
|
# Error reports here are only for diagnosing internal ARTIQ bugs.
|
||||||
|
error_csr(self.protocol_error,
|
||||||
unknown_packet_type = Signal()
|
rt_packet.unknown_packet_type,
|
||||||
packet_truncated = Signal()
|
rt_packet.packet_truncated,
|
||||||
write_overflow = Signal()
|
ios.write_underflow,
|
||||||
write_underflow = Signal()
|
ios.write_overflow)
|
||||||
self.comb += self.protocol_error.w.eq(
|
error_csr(self.rtio_error,
|
||||||
Cat(unknown_packet_type, packet_truncated,
|
ios.collision,
|
||||||
write_underflow, write_overflow))
|
ios.busy)
|
||||||
|
|
||||||
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))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ class IOS(Module):
|
||||||
def __init__(self, rt_packet, channels, max_fine_ts_width, full_ts_width):
|
def __init__(self, rt_packet, channels, max_fine_ts_width, full_ts_width):
|
||||||
self.write_underflow = Signal()
|
self.write_underflow = Signal()
|
||||||
self.write_overflow = Signal()
|
self.write_overflow = Signal()
|
||||||
|
self.collision = Signal()
|
||||||
|
self.busy = Signal()
|
||||||
|
|
||||||
self.rt_packet = rt_packet
|
self.rt_packet = rt_packet
|
||||||
self.max_fine_ts_width = max_fine_ts_width
|
self.max_fine_ts_width = max_fine_ts_width
|
||||||
|
@ -26,7 +28,9 @@ class IOS(Module):
|
||||||
|
|
||||||
self.sync.rio += [
|
self.sync.rio += [
|
||||||
self.write_underflow.eq(0),
|
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):
|
for n, channel in enumerate(channels):
|
||||||
self.add_output(n, channel)
|
self.add_output(n, channel)
|
||||||
|
|
Loading…
Reference in New Issue