Transition delay calculation. TBC...

This commit is contained in:
Florian Pose 2009-04-30 14:12:47 +00:00
parent 513cd4798c
commit 29eb2d7d06
12 changed files with 312 additions and 88 deletions

1
TODO
View File

@ -11,7 +11,6 @@ vim700: spelllang=en spell
Version 1.5.0:
* Distributed clocks:
- Delay calculation.
- Use common application time offset when setting start times.
- Check 32/64 bit operations.
- Set system time offset only when application time available.

View File

@ -257,21 +257,23 @@ int ec_cdev_ioctl_slave(
data.general_flags = slave->sii.general_flags;
data.current_on_ebus = slave->sii.current_on_ebus;
for (i = 0; i < EC_MAX_PORTS; i++) {
data.port_descs[i] = slave->base_ports[i];
data.ports[i].dl_link = slave->ports[i].dl_link;
data.ports[i].dl_loop = slave->ports[i].dl_loop;
data.ports[i].dl_signal = slave->ports[i].dl_signal;
data.dc_receive_times[i] = slave->dc_receive_times[i];
if (slave->next_slave[i]) {
data.next_slave[i] = slave->next_slave[i]->ring_position;
data.ports[i].desc = slave->ports[i].desc;
data.ports[i].link.link_up = slave->ports[i].link.link_up;
data.ports[i].link.loop_closed = slave->ports[i].link.loop_closed;
data.ports[i].link.signal_detected = slave->ports[i].link.signal_detected;
data.ports[i].receive_time = slave->ports[i].receive_time;
if (slave->ports[i].next_slave) {
data.ports[i].next_slave = slave->ports[i].next_slave->ring_position;
} else {
data.next_slave[i] = 0xffff;
data.ports[i].next_slave = 0xffff;
}
data.ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc;
}
data.fmmu_bit = slave->base_fmmu_bit_operation;
data.dc_supported = slave->base_dc_supported;
data.dc_range = slave->base_dc_range;
data.has_dc_system_time = slave->has_dc_system_time;
data.transition_delay = slave->transition_delay;
data.al_state = slave->current_state;
data.error_flag = slave->error_flag;

View File

@ -865,11 +865,7 @@ void ec_fsm_master_state_scan_slave(
master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue);
// find DC reference clock
ec_master_find_dc_ref_clock(master);
// calculate bus topology
ec_master_calc_topology(master);
ec_master_calc_dc(master);
// Attach slave configurations
ec_master_attach_slave_configs(master);

View File

@ -311,7 +311,7 @@ void ec_fsm_slave_scan_state_base(
octet = EC_READ_U8(datagram->data + 7);
for (i = 0; i < EC_MAX_PORTS; i++) {
slave->base_ports[i] = (octet >> (2 * i)) & 0x03;
slave->ports[i].desc = (octet >> (2 * i)) & 0x03;
}
octet = EC_READ_U8(datagram->data + 8);
@ -417,7 +417,7 @@ void ec_fsm_slave_scan_state_dc_times(
}
for (i = 0; i < EC_MAX_PORTS; i++) {
slave->dc_receive_times[i] = EC_READ_U32(datagram->data + 4 * i);
slave->ports[i].receive_time = EC_READ_U32(datagram->data + 4 * i);
}
ec_fsm_slave_scan_enter_datalink(fsm);
@ -478,9 +478,9 @@ void ec_fsm_slave_scan_state_datalink(ec_fsm_slave_scan_t *fsm /**< slave state
dl_status = EC_READ_U16(datagram->data);
for (i = 0; i < EC_MAX_PORTS; i++) {
slave->ports[i].dl_link = dl_status & (1 << (4 + i)) ? 1 : 0;
slave->ports[i].dl_loop = dl_status & (1 << (8 + i * 2)) ? 1 : 0;
slave->ports[i].dl_signal = dl_status & (1 << (9 + i * 2)) ? 1 : 0;
slave->ports[i].link.link_up = dl_status & (1 << (4 + i)) ? 1 : 0;
slave->ports[i].link.loop_closed = dl_status & (1 << (8 + i * 2)) ? 1 : 0;
slave->ports[i].link.signal_detected = dl_status & (1 << (9 + i * 2)) ? 1 : 0;
}
// Start fetching SII size

View File

@ -169,10 +169,10 @@ typedef enum {
/** EtherCAT slave port information.
*/
typedef struct {
uint8_t dl_link; /**< Link detected. */
uint8_t dl_loop; /**< Loop closed. */
uint8_t dl_signal; /**< Detected signal on RX port. */
} ec_slave_port_t;
uint8_t link_up; /**< Link detected. */
uint8_t loop_closed; /**< Loop closed. */
uint8_t signal_detected; /**< Detected signal on RX port. */
} ec_slave_port_link_t;
/** EtherCAT slave distributed clocks range.
*/

View File

@ -165,14 +165,18 @@ typedef struct {
ec_sii_coe_details_t coe_details;
ec_sii_general_flags_t general_flags;
int16_t current_on_ebus;
ec_slave_port_desc_t port_descs[EC_MAX_PORTS];
ec_slave_port_t ports[EC_MAX_PORTS];
uint16_t next_slave[EC_MAX_PORTS];
struct {
ec_slave_port_desc_t desc;
ec_slave_port_link_t link;
uint32_t receive_time;
uint16_t next_slave;
uint32_t delay_to_next_dc;
} ports[EC_MAX_PORTS];
uint8_t fmmu_bit;
uint8_t dc_supported;
ec_slave_dc_range_t dc_range;
uint8_t has_dc_system_time;
uint32_t dc_receive_times[EC_MAX_PORTS];
uint32_t transition_delay;
uint8_t al_state;
uint8_t error_flag;
uint8_t sync_count;

View File

@ -77,6 +77,7 @@ static int ec_master_operation_thread(void *);
#ifdef EC_EOE
void ec_master_eoe_run(unsigned long);
#endif
void ec_master_find_dc_ref_clock(ec_master_t *);
/*****************************************************************************/
@ -342,6 +343,8 @@ void ec_master_clear_slaves(ec_master_t *master)
{
ec_slave_t *slave;
master->dc_ref_clock = NULL;
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
@ -1357,20 +1360,22 @@ void ec_master_find_dc_ref_clock(
ec_master_t *master /**< EtherCAT master. */
)
{
ec_slave_t *slave;
uint16_t ref_clock_addr = 0xffff;
ec_slave_t *slave, *ref = NULL;
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
if (slave->base_dc_supported && slave->has_dc_system_time) {
ref_clock_addr = slave->station_address;
break;
}
if (slave->base_dc_supported && slave->has_dc_system_time) {
ref = slave;
break;
}
}
// This call always succeeds, because the datagram has been pre-allocated.
ec_datagram_frmw(&master->sync_datagram, ref_clock_addr, 0x0910, 4);
master->dc_ref_clock = ref;
// This call always succeeds, because the datagram has been pre-allocated.
ec_datagram_frmw(&master->sync_datagram,
ref ? ref->station_address : 0xffff, 0x0910, 4);
}
/*****************************************************************************/
@ -1387,13 +1392,13 @@ int ec_master_calc_topology_rec(
unsigned int i;
int ret;
slave->next_slave[0] = port0_slave;
slave->ports[0].next_slave = port0_slave;
for (i = 1; i < EC_MAX_PORTS; i++) {
if (!slave->ports[i].dl_loop) {
if (!slave->ports[i].link.loop_closed) {
*slave_position = *slave_position + 1;
if (*slave_position < master->slave_count) {
slave->next_slave[i] = master->slaves + *slave_position;
slave->ports[i].next_slave = master->slaves + *slave_position;
ret = ec_master_calc_topology_rec(master,
slave, slave_position);
if (ret)
@ -1424,6 +1429,45 @@ void ec_master_calc_topology(
EC_ERR("Failed to calculate bus topology.\n");
}
/*****************************************************************************/
/** Calculates the bus transition delays.
*/
void ec_master_calc_transition_delays(
ec_master_t *master /**< EtherCAT master. */
)
{
ec_slave_t *slave;
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
ec_slave_calc_port_delays(slave);
}
if (master->dc_ref_clock) {
uint32_t delay = 0;
ec_slave_calc_transition_delays_rec(master->dc_ref_clock, &delay);
}
}
/*****************************************************************************/
/** Distributed-clocks calculations.
*/
void ec_master_calc_dc(
ec_master_t *master /**< EtherCAT master. */
)
{
// find DC reference clock
ec_master_find_dc_ref_clock(master);
// calculate bus topology
ec_master_calc_topology(master);
ec_master_calc_transition_delays(master);
}
/******************************************************************************
* Application interface
*****************************************************************************/

View File

@ -122,6 +122,7 @@ struct ec_master {
reference clock to the master clock. */
ec_datagram_t sync_datagram; /**< Datagram used for DC drift
compensation. */
ec_slave_t *dc_ref_clock; /**< DC reference clock slave. */
unsigned int scan_busy; /**< Current scan state. */
unsigned int allow_scan; /**< \a True, if slave scanning is allowed. */
@ -233,8 +234,7 @@ ec_domain_t *ecrt_master_create_domain_err(ec_master_t *);
ec_slave_config_t *ecrt_master_slave_config_err(ec_master_t *, uint16_t,
uint16_t, uint32_t, uint32_t);
void ec_master_find_dc_ref_clock(ec_master_t *);
void ec_master_calc_topology(ec_master_t *);
void ec_master_calc_dc(ec_master_t *);
/*****************************************************************************/

View File

@ -89,22 +89,24 @@ void ec_slave_init(
slave->base_sync_count = 0;
for (i = 0; i < EC_MAX_PORTS; i++) {
slave->base_ports[i] = EC_PORT_NOT_IMPLEMENTED;
slave->ports[i].desc = EC_PORT_NOT_IMPLEMENTED;
slave->ports[i].dl_link = 0;
slave->ports[i].dl_loop = 0;
slave->ports[i].dl_signal = 0;
slave->ports[i].link.link_up = 0;
slave->ports[i].link.loop_closed = 0;
slave->ports[i].link.signal_detected = 0;
slave->sii.physical_layer[i] = 0xFF;
slave->dc_receive_times[i] = 0U;
slave->ports[i].receive_time = 0U;
slave->next_slave[i] = NULL;
slave->ports[i].next_slave = NULL;
slave->ports[i].delay_to_next_dc = 0U;
}
slave->base_fmmu_bit_operation = 0;
slave->base_dc_supported = 0;
slave->base_dc_range = EC_DC_32;
slave->has_dc_system_time = 0;
slave->transition_delay = 0U;
slave->sii_words = NULL;
slave->sii_nwords = 0;
@ -740,3 +742,126 @@ void ec_slave_attach_pdo_names(
}
/*****************************************************************************/
/** Calculates the sum of round-trip-times of connected ports 1-3.
*/
uint32_t ec_slave_calc_rtt_sum(
ec_slave_t *slave /**< EtherCAT slave. */
)
{
uint32_t rtt_sum = 0, rtt;
unsigned int i;
for (i = 1; i < EC_MAX_PORTS; i++) {
if (slave->ports[i].next_slave) {
rtt = slave->ports[i].receive_time - slave->ports[i - 1].receive_time;
rtt_sum += rtt;
}
}
return rtt_sum;
}
/*****************************************************************************/
/** Finds the next slave supporting DC delay measurement.
*/
ec_slave_t *ec_slave_find_next_dc_slave(
ec_slave_t *slave /**< EtherCAT slave. */
)
{
ec_slave_t *dc_slave = NULL;
if (slave->base_dc_supported) {
dc_slave = slave;
} else {
unsigned int i;
for (i = 1; i < EC_MAX_PORTS; i++) {
ec_slave_t *next = slave->ports[i].next_slave;
if (next) {
dc_slave = ec_slave_find_next_dc_slave(next);
if (dc_slave)
break;
}
}
}
return dc_slave;
}
/*****************************************************************************/
/** Calculates the port transition delays.
*/
void ec_slave_calc_port_delays(
ec_slave_t *slave /**< EtherCAT slave. */
)
{
unsigned int i;
ec_slave_t *next, *next_dc;
uint32_t rtt, next_rtt_sum;
if (!slave->base_dc_supported)
return;
for (i = 1; i < EC_MAX_PORTS; i++) {
next = slave->ports[i].next_slave;
if (!next)
continue;
next_dc = ec_slave_find_next_dc_slave(next);
if (!next_dc)
continue;
rtt = slave->ports[i].receive_time - slave->ports[i - 1].receive_time;
next_rtt_sum = ec_slave_calc_rtt_sum(next_dc);
slave->ports[i].delay_to_next_dc = (rtt - next_rtt_sum) / 2; // FIXME
next_dc->ports[0].delay_to_next_dc = (rtt - next_rtt_sum) / 2;
#if 0
EC_DBG("delay %u:%u rtt=%u next_rtt_sum=%u delay=%u\n",
slave->ring_position, i, rtt, next_rtt_sum,
slave->ports[i].delay_to_next_dc);
#endif
}
}
/*****************************************************************************/
/** Calculates the bus topology; recursion function.
*/
void ec_slave_calc_transition_delays_rec(
ec_slave_t *slave, /**< Current slave. */
uint32_t *delay /**< Sum of delays. */
)
{
unsigned int i;
ec_slave_t *next, *next_dc;
#if 0
EC_DBG("%u: %u\n", slave->ring_position, *delay);
#endif
slave->transition_delay = *delay;
for (i = 1; i < EC_MAX_PORTS; i++) {
ec_slave_port_t *port = &slave->ports[i];
next = port->next_slave;
if (!next)
continue;
next_dc = ec_slave_find_next_dc_slave(next);
if (!next_dc)
continue;
*delay = *delay + port->delay_to_next_dc;
#if 0
EC_DBG("%u:%u %u\n", slave->ring_position, i, *delay);
#endif
ec_slave_calc_transition_delays_rec(next_dc, delay);
}
*delay = *delay + slave->ports[0].delay_to_next_dc;
}
/*****************************************************************************/

View File

@ -48,6 +48,20 @@
/*****************************************************************************/
/** Slave port.
*/
typedef struct {
ec_slave_port_desc_t desc; /**< Port descriptors. */
ec_slave_port_link_t link; /**< Port link status. */
ec_slave_t *next_slave; /**< Connected slaves. */
uint32_t receive_time; /**< Port receive times for delay
measurement. */
uint32_t delay_to_next_dc; /**< Delay to next slave with DC support behind
this port [ns]. */
} ec_slave_port_t;
/*****************************************************************************/
/** Slave information interface data.
*/
typedef struct {
@ -102,6 +116,8 @@ struct ec_slave
uint16_t ring_position; /**< Ring position. */
uint16_t station_address; /**< Configured station address. */
ec_slave_port_t ports[EC_MAX_PORTS]; /**< Ports. */
// configuration
ec_slave_config_t *config; /**< Current configuration. */
ec_slave_state_t requested_state; /**< Requested application state. */
@ -119,19 +135,14 @@ struct ec_slave
uint16_t base_build; /**< Build number. */
uint8_t base_fmmu_count; /**< Number of supported FMMUs. */
uint8_t base_sync_count; /**< Number of supported sync managers. */
ec_slave_port_desc_t base_ports[EC_MAX_PORTS]; /**< Port descriptors. */
uint8_t base_fmmu_bit_operation; /**< FMMU bit operation is supported. */
uint8_t base_dc_supported; /**< Distributed clocks are supported. */
ec_slave_dc_range_t base_dc_range; /**< DC range. */
uint8_t has_dc_system_time; /**< The slave supports the DC system time
register. Otherwise it can only be used for
delay measurement. */
uint32_t dc_receive_times[EC_MAX_PORTS]; /**< Port receive times for delay
measurement. */
// data link status
ec_slave_port_t ports[EC_MAX_PORTS]; /**< Port link status. */
ec_slave_t *next_slave[EC_MAX_PORTS]; /**< Connected slaves. */
uint32_t transition_delay; /**< DC transition delay (from reference
clock). */
// SII
uint16_t *sii_words; /**< Complete SII image. */
@ -175,6 +186,9 @@ uint16_t ec_slave_sdo_count(const ec_slave_t *);
const ec_pdo_t *ec_slave_find_pdo(const ec_slave_t *, uint16_t);
void ec_slave_attach_pdo_names(ec_slave_t *);
void ec_slave_calc_port_delays(ec_slave_t *);
void ec_slave_calc_transition_delays_rec(ec_slave_t *, uint32_t *);
/*****************************************************************************/
#endif

View File

@ -72,7 +72,6 @@ void CommandGraph::execute(MasterDevice &m, const StringVector &args)
SlaveVector slaves;
ec_ioctl_slave_t slave;
SlaveVector::const_iterator si;
string font("fontname=\"Helvetica\"");
map<int, string> portMedia;
map<int, string>::const_iterator mi;
map<int, int> mediaWeights;
@ -102,51 +101,84 @@ void CommandGraph::execute(MasterDevice &m, const StringVector &args)
<< endl
<< "strict graph bus {" << endl
<< " rankdir=\"LR\"" << endl
<< " ranksep=0.8" << endl
<< " nodesep=0.8" << endl
<< " node [fontname=\"Helvetica\"]" << endl
<< " edge [fontname=\"Helvetica\",fontsize=\"10\"]" << endl
<< endl
<< " master [" << font << ",label=\"EtherCAT\\nMaster\"]" << endl;
<< " master [label=\"EtherCAT\\nMaster\"]" << endl;
if (slaves.size()) {
cout << " master -- slave0 [" << font;
mi = portMedia.find(slaves.front().port_descs[0]);
cout << " master -- slave0";
mi = portMedia.find(slaves.front().ports[0].desc);
if (mi != portMedia.end())
cout << ",label=\"" << mi->second << "\"";
cout << "[label=\"" << mi->second << "\"]";
cout << "]" << endl;
cout << endl;
}
cout << endl;
for (si = slaves.begin(); si != slaves.end(); si++) {
cout << " slave" << si->position << " [" << font <<
",shape=\"box\",label=\"" << si->position;
cout << " slave" << si->position << " [shape=\"box\""
<< ",label=\"" << si->position;
if (string(si->order).size())
cout << "\\n" << si->order;
if (si->dc_supported) {
cout << "\\nDC: ";
if (si->has_dc_system_time) {
switch (si->dc_range) {
case EC_DC_32:
cout << "32 bit";
break;
case EC_DC_64:
cout << "64 bit";
break;
default:
break;
}
} else {
cout << "Delay meas.";
}
cout << "\\nDelay: " << si->transition_delay << " ns";
}
cout << "\"]" << endl;
for (i = 1; i < EC_MAX_PORTS; i++) {
if (si->next_slave[i] == 0xffff)
uint16_t next_pos = si->ports[i].next_slave;
ec_ioctl_slave_t *next = NULL;
if (next_pos == 0xffff)
continue;
cout << " slave" << si->position << " -- "
<< "slave" << si->next_slave[i] << " [" << font << ","
<< "taillabel=\"" << i << "\"";
if (next_pos < slaves.size()) {
next = &slaves[next_pos];
} else {
cerr << "Invalid next slave pointer." << endl;
}
mi = portMedia.find(si->port_descs[i]);
if (mi == portMedia.end()) {
cout << " slave" << si->position << " -- "
<< "slave" << next_pos << " [taillabel=\"" << i;
if (si->dc_supported) {
cout << " [" << si->ports[i].delay_to_next_dc << "]";
}
cout << "\",headlabel=\"0";
if (next && next->dc_supported) {
cout << " [" << next->ports[0].delay_to_next_dc << "]";
}
cout << "\"";
mi = portMedia.find(si->ports[i].desc);
if (mi == portMedia.end() && next) {
/* Try medium of next-hop slave. */
unsigned int pos = si->next_slave[i];
if (pos < slaves.size()) {
ec_ioctl_slave_t *next = &slaves[pos];
mi = portMedia.find(next->port_descs[0]);
} else {
cerr << "Invalid next slave pointer." << endl;
}
mi = portMedia.find(next->ports[0].desc);
}
if (mi != portMedia.end())
cout << ",label=\"" << mi->second << "\"";
wi = mediaWeights.find(si->port_descs[i]);
wi = mediaWeights.find(si->ports[i].desc);
if (wi != mediaWeights.end())
cout << ",weight=\"" << wi->second << "\"";

View File

@ -262,18 +262,20 @@ void CommandSlaves::showSlaves(
} else {
cout << "yes, delay measurement only" << endl;
}
cout << " DC transition delay: "
<< si->transition_delay << " ns" << endl;
} else {
cout << "no" << endl;
}
cout << "Port Type Link Loop Signal NextSlave";
if (si->dc_supported)
cout << " RxTime Diff";
cout << " RxTime [ns] Diff [ns] NextDc [ns]";
cout << endl;
for (i = 0; i < EC_MAX_PORTS; i++) {
cout << " " << i << " " << setfill(' ') << left << setw(4);
switch (si->port_descs[i]) {
switch (si->ports[i].desc) {
case EC_PORT_NOT_IMPLEMENTED:
cout << "N/A";
break;
@ -291,29 +293,35 @@ void CommandSlaves::showSlaves(
}
cout << " " << setw(4)
<< (si->ports[i].dl_link ? "up" : "down")
<< (si->ports[i].link.link_up ? "up" : "down")
<< " " << setw(6)
<< (si->ports[i].dl_loop ? "closed" : "open")
<< (si->ports[i].link.loop_closed ? "closed" : "open")
<< " " << setw(6)
<< (si->ports[i].dl_signal ? "yes" : "no")
<< (si->ports[i].link.signal_detected ? "yes" : "no")
<< " " << setw(9) << right;
if (si->next_slave[i] != 0xffff) {
cout << dec << si->next_slave[i];
if (si->ports[i].next_slave != 0xffff) {
cout << dec << si->ports[i].next_slave;
} else {
cout << "-";
}
if (si->dc_supported) {
cout << " " << setw(10) << right;
if (si->ports[i].dl_signal) {
cout << dec << si->dc_receive_times[i];
cout << " " << setw(11) << right;
if (!si->ports[i].link.loop_closed) {
cout << dec << si->ports[i].receive_time;
} else {
cout << "-";
}
cout << " " << setw(10);
if (si->ports[i].dl_signal) {
cout << si->dc_receive_times[i] - si->dc_receive_times[0];
if (!si->ports[i].link.loop_closed) {
cout << si->ports[i].receive_time - si->ports[0].receive_time;
} else {
cout << "-";
}
cout << " " << setw(10);
if (!si->ports[i].link.loop_closed) {
cout << si->ports[i].delay_to_next_dc;
} else {
cout << "-";
}