--- a/master/master.c Thu Aug 03 12:59:01 2006 +0000
+++ b/master/master.c Wed Aug 09 14:38:44 2006 +0000
@@ -65,14 +65,12 @@
/** \cond */
-EC_SYSFS_READ_ATTR(slave_count);
-EC_SYSFS_READ_ATTR(mode);
+EC_SYSFS_READ_ATTR(info);
EC_SYSFS_READ_WRITE_ATTR(eeprom_write_enable);
EC_SYSFS_READ_WRITE_ATTR(debug_level);
static struct attribute *ec_def_attrs[] = {
- &attr_slave_count,
- &attr_mode,
+ &attr_info,
&attr_eeprom_write_enable,
&attr_debug_level,
NULL,
@@ -121,6 +119,13 @@
master->eoe_timer.data = (unsigned long) master;
master->internal_lock = SPIN_LOCK_UNLOCKED;
master->eoe_running = 0;
+ master->eoe_checked = 0;
+ for (i = 0; i < HZ; i++) {
+ master->idle_cycle_times[i] = 0;
+ master->eoe_cycle_times[i] = 0;
+ }
+ master->idle_cycle_time_pos = 0;
+ master->eoe_cycle_time_pos = 0;
// create workqueue
if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) {
@@ -239,6 +244,7 @@
master->stats.timeouts = 0;
master->stats.delayed = 0;
master->stats.corrupted = 0;
+ master->stats.skipped = 0;
master->stats.unmatched = 0;
master->stats.t_last = 0;
@@ -286,9 +292,9 @@
// check, if the datagram is already queued
list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
if (queued_datagram == datagram) {
+ master->stats.skipped++;
+ ec_master_output_stats(master);
datagram->state = EC_DATAGRAM_QUEUED;
- if (unlikely(master->debug_level))
- EC_WARN("datagram already queued.\n");
return;
}
}
@@ -405,10 +411,10 @@
\return 0 in case of success, else < 0
*/
-void ec_master_receive(ec_master_t *master, /**< EtherCAT master */
- const uint8_t *frame_data, /**< received data */
- size_t size /**< size of the received data */
- )
+void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
+ const uint8_t *frame_data, /**< frame data */
+ size_t size /**< size of the received data */
+ )
{
size_t frame_size, data_size;
uint8_t datagram_type, datagram_index;
@@ -537,6 +543,10 @@
EC_WARN("%i frame(s) CORRUPTED!\n", master->stats.corrupted);
master->stats.corrupted = 0;
}
+ if (master->stats.skipped) {
+ EC_WARN("%i datagram(s) SKIPPED!\n", master->stats.skipped);
+ master->stats.skipped = 0;
+ }
if (master->stats.unmatched) {
EC_WARN("%i datagram(s) UNMATCHED!\n", master->stats.unmatched);
master->stats.unmatched = 0;
@@ -600,20 +610,28 @@
void ec_master_idle_run(void *data /**< master pointer */)
{
ec_master_t *master = (ec_master_t *) data;
+ cycles_t start, end;
// aquire master lock
spin_lock_bh(&master->internal_lock);
+ start = get_cycles();
ecrt_master_receive(master);
// execute master state machine
ec_fsm_execute(&master->fsm);
ecrt_master_send(master);
+ end = get_cycles();
// release master lock
spin_unlock_bh(&master->internal_lock);
+ master->idle_cycle_times[master->idle_cycle_time_pos]
+ = (u32) (end - start) * 1000 / cpu_khz;
+ master->idle_cycle_time_pos++;
+ master->idle_cycle_time_pos %= HZ;
+
if (master->mode == EC_MASTER_MODE_IDLE)
queue_delayed_work(master->workqueue, &master->idle_work, 1);
}
@@ -680,6 +698,78 @@
/*****************************************************************************/
/**
+ Formats master information for SysFS read access.
+ \return number of bytes written
+*/
+
+ssize_t ec_master_info(ec_master_t *master, /**< EtherCAT master */
+ char *buffer /**< memory to store data */
+ )
+{
+ off_t off = 0;
+ ec_eoe_t *eoe;
+ uint32_t cur, sum, min, max, pos, i;
+
+ off += sprintf(buffer + off, "\nMode: ");
+ switch (master->mode) {
+ case EC_MASTER_MODE_ORPHANED:
+ off += sprintf(buffer + off, "ORPHANED");
+ break;
+ case EC_MASTER_MODE_IDLE:
+ off += sprintf(buffer + off, "IDLE");
+ break;
+ case EC_MASTER_MODE_OPERATION:
+ off += sprintf(buffer + off, "OPERATION");
+ break;
+ }
+
+ off += sprintf(buffer + off, "\nSlaves: %i\n",
+ master->slave_count);
+
+ off += sprintf(buffer + off, "\nTiming (min/avg/max) [us]:\n");
+
+ sum = 0;
+ min = 0xFFFFFFFF;
+ max = 0;
+ pos = master->idle_cycle_time_pos;
+ for (i = 0; i < HZ; i++) {
+ cur = master->idle_cycle_times[(i + pos) % HZ];
+ sum += cur;
+ if (cur < min) min = cur;
+ if (cur > max) max = cur;
+ }
+ off += sprintf(buffer + off, " Idle cycle: %u / %u.%u / %u\n",
+ min, sum / HZ, (sum * 100 / HZ) % 100, max);
+
+ sum = 0;
+ min = 0xFFFFFFFF;
+ max = 0;
+ pos = master->eoe_cycle_time_pos;
+ for (i = 0; i < HZ; i++) {
+ cur = master->eoe_cycle_times[(i + pos) % HZ];
+ sum += cur;
+ if (cur < min) min = cur;
+ if (cur > max) max = cur;
+ }
+ off += sprintf(buffer + off, " EoE cycle: %u / %u.%u / %u\n",
+ min, sum / HZ, (sum * 100 / HZ) % 100, max);
+
+ if (!list_empty(&master->eoe_handlers))
+ off += sprintf(buffer + off, "\nEoE statistics (RX/TX) [bps]:\n");
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ off += sprintf(buffer + off, " %s: %u / %u (%u KB/s)\n",
+ eoe->dev->name, eoe->rx_rate, eoe->tx_rate,
+ ((eoe->rx_rate + eoe->tx_rate) / 8 + 512) / 1024);
+ }
+
+ off += sprintf(buffer + off, "\n");
+
+ return off;
+}
+
+/*****************************************************************************/
+
+/**
Formats attribute data for SysFS read access.
\return number of bytes to read
*/
@@ -691,18 +781,8 @@
{
ec_master_t *master = container_of(kobj, ec_master_t, kobj);
- if (attr == &attr_slave_count) {
- return sprintf(buffer, "%i\n", master->slave_count);
- }
- else if (attr == &attr_mode) {
- switch (master->mode) {
- case EC_MASTER_MODE_ORPHANED:
- return sprintf(buffer, "ORPHANED\n");
- case EC_MASTER_MODE_IDLE:
- return sprintf(buffer, "IDLE\n");
- case EC_MASTER_MODE_OPERATION:
- return sprintf(buffer, "OPERATION\n");
- }
+ if (attr == &attr_info) {
+ return ec_master_info(master, buffer);
}
else if (attr == &attr_debug_level) {
return sprintf(buffer, "%i\n", master->debug_level);
@@ -773,7 +853,7 @@
/*****************************************************************************/
/**
- Starts/Stops Ethernet-over-EtherCAT processing on demand.
+ Starts Ethernet-over-EtherCAT processing on demand.
*/
void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
@@ -782,12 +862,17 @@
ec_slave_t *slave;
unsigned int coupled, found;
- if (master->eoe_running) return;
+ if (master->eoe_running || master->eoe_checked) return;
+
+ master->eoe_checked = 1;
// if the locking callbacks are not set in operation mode,
// the EoE timer my not be started.
if (master->mode == EC_MASTER_MODE_OPERATION
- && (!master->request_cb || !master->release_cb)) return;
+ && (!master->request_cb || !master->release_cb)) {
+ EC_INFO("No EoE processing because of missing locking callbacks.\n");
+ return;
+ }
// decouple all EoE handlers
list_for_each_entry(eoe, &master->eoe_handlers, list)
@@ -843,6 +928,8 @@
{
ec_eoe_t *eoe;
+ master->eoe_checked = 0;
+
if (!master->eoe_running) return;
EC_INFO("Stopping EoE processing.\n");
@@ -871,6 +958,7 @@
ec_master_t *master = (ec_master_t *) data;
ec_eoe_t *eoe;
unsigned int active = 0;
+ cycles_t start, end;
list_for_each_entry(eoe, &master->eoe_handlers, list) {
if (ec_eoe_active(eoe)) active++;
@@ -890,11 +978,15 @@
goto queue_timer;
// actual EoE stuff
+ start = get_cycles();
ecrt_master_receive(master);
+
list_for_each_entry(eoe, &master->eoe_handlers, list) {
ec_eoe_run(eoe);
}
+
ecrt_master_send(master);
+ end = get_cycles();
// release lock...
if (master->mode == EC_MASTER_MODE_OPERATION) {
@@ -904,6 +996,11 @@
spin_unlock(&master->internal_lock);
}
+ master->eoe_cycle_times[master->eoe_cycle_time_pos]
+ = (u32) (end - start) * 1000 / cpu_khz;
+ master->eoe_cycle_time_pos++;
+ master->eoe_cycle_time_pos %= HZ;
+
queue_timer:
master->eoe_timer.expires += HZ / EC_EOE_FREQUENCY;
add_timer(&master->eoe_timer);
@@ -941,6 +1038,69 @@
}
}
+/*****************************************************************************/
+
+/**
+ Measures the time, a frame is on the bus.
+*/
+
+void ec_master_measure_bus_time(ec_master_t *master)
+{
+ ec_datagram_t datagram;
+ cycles_t t_start, t_end, t_timeout;
+ uint32_t times[100], sum, min, max, i;
+
+ ec_datagram_init(&datagram);
+
+ if (ec_datagram_brd(&datagram, 0x130, 2)) {
+ EC_ERR("Failed to allocate datagram for bus time measuring.\n");
+ ec_datagram_clear(&datagram);
+ return;
+ }
+
+ t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
+
+ sum = 0;
+ min = 0xFFFFFFFF;
+ max = 0;
+
+ for (i = 0; i < 100; i++) {
+ ec_master_queue_datagram(master, &datagram);
+ ecrt_master_send(master);
+ t_start = get_cycles();
+
+ while (1) { // active waiting
+ ec_device_call_isr(master->device);
+ t_end = get_cycles(); // take current time
+
+ if (datagram.state == EC_DATAGRAM_RECEIVED) {
+ break;
+ }
+ else if (datagram.state == EC_DATAGRAM_ERROR) {
+ EC_WARN("Failed to measure bus time.\n");
+ goto error;
+ }
+ else if (t_end - t_start >= t_timeout) {
+ EC_WARN("Timeout while measuring bus time.\n");
+ goto error;
+ }
+ }
+
+ times[i] = (unsigned int) (t_end - t_start) * 1000 / cpu_khz;
+ sum += times[i];
+ if (times[i] > max) max = times[i];
+ if (times[i] < min) min = times[i];
+ }
+
+ EC_INFO("Bus time is (min/avg/max) %u/%u.%u/%u us.\n",
+ min, sum / 100, sum % 100, max);
+
+ error:
+ // Dequeue and free datagram
+ list_del(&datagram.queue);
+ ec_datagram_clear(&datagram);
+}
+
/******************************************************************************
* Realtime interface
*****************************************************************************/
@@ -1163,7 +1323,7 @@
ec_device_call_isr(master->device);
t_received = get_cycles();
- t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
+ t_timeout = EC_IO_TIMEOUT * cpu_khz / 1000;
// dequeue all received datagrams
list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue)