diff -r e675450f2174 -r 9440f4ff25c7 master/master.c --- 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)