Commit 9b892741 authored by Wesley W. Terpstra's avatar Wesley W. Terpstra

Merge branch 'dev-tlu-software-control' into proposed_master

C++ and command-line interface for the TLU.
parents e0e3a728 090e4c34
......@@ -323,7 +323,7 @@ int main(int argc, char** argv) {
strcasecmp(command, "ahook") == 0 ||
strcasecmp(command, "ohook") == 0) {
if (optind+3 > argc) {
fprintf(stderr, "%s: expecting exactly one argument: %s <address>\n", command, program);
fprintf(stderr, "%s: expecting exactly one argument: %s <address>\n", program, command);
return 1;
}
if (optind+3 < argc) {
......@@ -395,12 +395,12 @@ int main(int argc, char** argv) {
}
/* Select default channel */
if (channel_id == -1 && ecas[eca_id].channels.size() == 1) {
if (channel_id == -1 && eca_id != -1 && ecas[eca_id].channels.size() == 1) {
channel_id = 0;
}
/* Select default stream */
if (stream_id == -1 && ecas[eca_id].streams.size() == 1) {
if (stream_id == -1 && eca_id != -1 && ecas[eca_id].streams.size() == 1) {
stream_id = 0;
}
......
PREFIX ?= /usr/local
STAGING ?=
EB ?= no
ifeq ($(EB),no)
EB_LIB ?= -letherbone
EB_INC ?=
else
EB_LIB ?= -L$(EB) -letherbone
EB_INC ?= -I$(EB)
endif
EXTRA_FLAGS ?=
CXX ?= g++
CXXFLAGS ?= $(EXTRA_FLAGS) -Wall -O2 -I. $(EB_INC)
TARGETS = libtlu.a tlu-ctl
all: $(TARGETS)
install:
mkdir -p $(STAGING)$(PREFIX)/bin $(STAGING)$(PREFIX)/include $(STAGING)$(PREFIX)/lib
cp tlu-ctl $(STAGING)$(PREFIX)/bin
cp tlu.h $(STAGING)$(PREFIX)/include
cp libtlu.a $(STAGING)$(PREFIX)/lib
clean:
rm -f $(TARGETS) *.o lib/*.o
tlu-ctl: tlu-ctl.o libtlu.a
$(CXX) $(CXXFLAGS) -o $@ $^ -Wl,-rpath,$(PREFIX)/lib $(EB_LIB)
libtlu.a: lib/tlu.o
rm -f $@
ar rcs $@ $^
ranlib $@
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
#define GSI_VENDOR_ID 0x651
#define TLU_DEVICE_ID 0x10051981U
#define TLU_READY 0x00 //
#define TLU_CLEAR 0x04
#define TLU_TEST 0x08
#define TLU_ACTIVE_STATUS 0x0C
#define TLU_ACTIVE_SET 0x10
#define TLU_ACTIVE_CLR 0x14
#define TLU_EDGE_STATUS 0x18
#define TLU_EDGE_SET 0x1C
#define TLU_EDGE_CLR 0x20
#define TLU_INT_GLOBAL 0x24
#define TLU_INT_STATUS 0x28
#define TLU_INT_SET 0x2C
#define TLU_INT_CLR 0x30
#define TLU_NUM_CHANNELS 0x34
#define TLU_QUEUE_SIZE 0x38
#define TLU_TIME1 0x50
#define TLU_TIME0 0x54
#define TLU_CH_SELECT 0x58
#define TLU_CH_POP 0x5C
#define TLU_CH_TEST 0x60
#define TLU_CH_FILL_COUNT 0x64
#define TLU_CH_TIME1 0x68
#define TLU_CH_TIME0 0x6C
#define TLU_CH_SUB 0x70
#define TLU_CH_STABLE 0x74
#define TLU_CH_INT_DEST 0x78
#define TLU_CH_INT_MSG 0x7C
/** @file tlu.cpp
* @brief Implement interface to TLU via an Etherbone device.
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2014 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Find all TLU units on device.
* Load contents of all pertinent registers.
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS
#include <stdio.h>
#include <assert.h>
#include "tlu.h"
#include "hw-tlu.h"
namespace GSI_TLU {
struct SearchRecord {
int done;
status_t status;
std::vector<TLU>* tlus;
};
static void trim(std::string& s) {
std::string::size_type x = s.size();
for (x = s.size(); x > 0; --x)
if (s[x-1] != ' ') break;
s.resize(x);
}
void tlu_sdb_search(SearchRecord* record, Device dev, const struct sdb_table* sdb, status_t status) {
if (status != EB_OK) {
record->status = status;
record->done = 1;
return;
}
unsigned devices = sdb->interconnect.sdb_records - 1;
for (unsigned i = 0; i < devices; ++i) {
const union sdb_record* des = &sdb->record[i];
switch (des->empty.record_type) {
case sdb_record_device: {
if (des->device.sdb_component.product.vendor_id == GSI_VENDOR_ID) {
switch (des->device.sdb_component.product.device_id) {
case TLU_DEVICE_ID: {
if (des->device.abi_ver_major != 1) break;
TLU tlu;
tlu.device = dev;
tlu.address = des->device.sdb_component.addr_first;
tlu.sdb_ver_major = des->device.abi_ver_major;
tlu.sdb_ver_minor = des->device.abi_ver_minor;
tlu.sdb_version = des->device.sdb_component.product.version;
tlu.sdb_date = des->device.sdb_component.product.date;
tlu.sdb_name = std::string((const char*)&des->device.sdb_component.product.name[0], 19);
trim(tlu.sdb_name);
record->tlus->push_back(tlu);
break;
}
}
}
break;
}
case sdb_record_bridge: {
dev.sdb_scan_bus(&des->bridge, record, sdb_wrap_function_callback<SearchRecord, tlu_sdb_search>);
record->done = 0;
while (!record->done) dev.socket().run();
break;
}
}
}
record->done = 1;
}
status_t TLU::probe(Device device, std::vector<TLU>& tlus) {
/* Phase 1 -- locate TLU units using SDB */
SearchRecord record;
record.tlus = &tlus;
tlus.clear();
device.sdb_scan_root(&record, sdb_wrap_function_callback<SearchRecord, tlu_sdb_search>);
record.done = 0;
record.status = EB_OK;
while (!record.done) device.socket().run();
if (record.status != EB_OK) return record.status;
/* Phase 2 -- Read TLU parameters */
status_t status;
data_t num_channels;
data_t queue_size;
Cycle cycle;
for (unsigned i = 0; i < tlus.size(); ++i) {
TLU& tlu = tlus[i];
if ((status = cycle.open(tlu.device)) != EB_OK)
return status;
cycle.read(tlu.address + TLU_NUM_CHANNELS, EB_DATA32, &num_channels);
cycle.read(tlu.address + TLU_QUEUE_SIZE, EB_DATA32, &queue_size);
if ((status = cycle.close()) != EB_OK)
return status;
tlu.num_channels = num_channels;
tlu.queue_size = queue_size;
tlu.channels.resize(num_channels);
if ((status = tlu.refresh()) != EB_OK)
return status;
}
return EB_OK;
}
struct Shadow {
data_t fill, stable, dest, msg;
};
status_t TLU::refresh() {
status_t status;
data_t v_active, v_edge, g_int, v_int, time0, time1, time2;
Cycle cycle;
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.read(address + TLU_ACTIVE_STATUS, EB_DATA32, &v_active);
cycle.read(address + TLU_EDGE_STATUS, EB_DATA32, &v_edge);
cycle.read(address + TLU_INT_GLOBAL, EB_DATA32, &g_int);
cycle.read(address + TLU_INT_STATUS, EB_DATA32, &v_int);
cycle.read(address + TLU_TIME1, EB_DATA32, &time1);
cycle.read(address + TLU_TIME0, EB_DATA32, &time0);
cycle.read(address + TLU_TIME1, EB_DATA32, &time2);
std::vector<Shadow> shadow;
shadow.resize(channels.size());
for (unsigned i = 0; i < channels.size(); ++i) {
Shadow& s = shadow[i];
cycle.write(address + TLU_CH_SELECT, EB_DATA32, i);
cycle.read (address + TLU_CH_FILL_COUNT, EB_DATA32, &s.fill);
cycle.read (address + TLU_CH_STABLE, EB_DATA32, &s.stable);
cycle.read (address + TLU_CH_INT_DEST, EB_DATA32, &s.dest);
cycle.read (address + TLU_CH_INT_MSG, EB_DATA32, &s.msg);
}
if ((status = cycle.close()) != EB_OK)
return status;
/* Timestamp overflowed; reread */
if (time1 != time2)
return refresh();
int_enable = (g_int != 0);
current_time = time1;
current_time <<= 32;
current_time |= time0;
current_time <<= 3;
data_t mask = 1;
for (unsigned i = 0; i < channels.size(); ++i) {
Channel& ch = channels[i];
Shadow& s = shadow[i];
ch.int_enable = (v_int & mask) != 0;
ch.active = (v_active & mask) != 0;
ch.pos_edge = (v_edge & mask) != 0;
ch.queued = s.fill;
ch.stable = s.stable;
ch.int_dest = s.dest;
ch.int_msg = s.msg;
mask <<= 1;
}
return EB_OK;
}
status_t TLU::set_enable(bool enable) {
status_t status;
if ((status = device.write(address + TLU_INT_GLOBAL, EB_DATA32, enable?1:0)) != EB_OK)
return status;
int_enable = enable;
return EB_OK;
}
status_t TLU::hook(int channel, bool enable, uint32_t dest, uint32_t msg) {
int first, last;
Cycle cycle;
status_t status;
data_t mask;
if (channel == -1) {
first = 0;
last = channels.size();
mask = (uint32_t)-1;
} else {
first = channel;
last = channel+1;
mask = 1;
mask <<= channel;
}
if (first < 0 || last > (int)channels.size())
return EB_FAIL;
if ((status = cycle.open(device)) != EB_OK)
return status;
if (enable) {
cycle.write(address + TLU_INT_SET, EB_DATA32, mask);
} else {
cycle.write(address + TLU_INT_CLR, EB_DATA32, mask);
}
for (int i = first; i < last; ++i) {
cycle.write(address + TLU_CH_SELECT, EB_DATA32, i);
cycle.write(address + TLU_CH_INT_DEST, EB_DATA32, dest);
cycle.write(address + TLU_CH_INT_MSG, EB_DATA32, msg);
}
if ((status = cycle.close()) != EB_OK)
return status;
for (int i = first; i < last; ++i) {
Channel& c = channels[i];
c.int_enable = enable;
c.int_dest = dest;
c.int_msg = msg;
}
return EB_OK;
}
status_t TLU::listen(int channel, bool enable, bool pos_edge, uint32_t stable) {
int first, last;
Cycle cycle;
status_t status;
data_t mask;
if (channel == -1) {
first = 0;
last = channels.size();
mask = (uint32_t)-1;
} else {
first = channel;
last = channel+1;
mask = 1;
mask <<= channel;
}
if (first < 0 || last > (int)channels.size())
return EB_FAIL;
if ((status = cycle.open(device)) != EB_OK)
return status;
if (enable) {
cycle.write(address + TLU_ACTIVE_SET, EB_DATA32, mask);
} else {
cycle.write(address + TLU_ACTIVE_CLR, EB_DATA32, mask);
}
if (pos_edge) {
cycle.write(address + TLU_EDGE_SET, EB_DATA32, mask);
} else {
cycle.write(address + TLU_EDGE_CLR, EB_DATA32, mask);
}
for (int i = first; i < last; ++i) {
cycle.write(address + TLU_CH_SELECT, EB_DATA32, i);
cycle.write(address + TLU_CH_STABLE, EB_DATA32, stable);
}
if ((status = cycle.close()) != EB_OK)
return status;
for (int i = first; i < last; ++i) {
Channel& c = channels[i];
c.active = enable;
c.pos_edge = pos_edge;
}
return EB_OK;
}
status_t TLU::clear(int channel) {
int first, last;
Cycle cycle;
status_t status;
data_t mask;
if (channel == -1) {
first = 0;
last = channels.size();
mask = (uint32_t)-1;
} else {
first = channel;
last = channel+1;
mask = 1;
mask <<= channel;
}
if (first < 0 || last > (int)channels.size())
return EB_FAIL;
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.write(address + TLU_CLEAR, EB_DATA32, mask);
if ((status = cycle.close()) != EB_OK)
return status;
for (int i = first; i < last; ++i) {
Channel& c = channels[i];
c.queued = 0;
}
return EB_OK;
}
status_t TLU::test(int channel, uint8_t edge) {
status_t status;
Cycle cycle;
if (channel == -1) {
return device.write(address + TLU_TEST, EB_DATA32, edge);
} else if (channel < 0 || channel >= (int)channels.size()) {
return EB_FAIL;
} else {
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.write(address + TLU_CH_SELECT, EB_DATA32, channel);
cycle.write(address + TLU_CH_TEST, EB_DATA32, edge);
return cycle.close();
}
}
status_t TLU::pop(int channel, uint64_t& time) {
status_t status;
data_t time1, time0, sub;
Cycle cycle;
if (channel < 0 || channel >= (int)channels.size())
return EB_FAIL;
Channel& c = channels[channel];
if (c.queued == 0) {
refresh();
if (c.queued == 0) return EB_FAIL;
}
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.write(address + TLU_CH_SELECT, EB_DATA32, channel);
cycle.read (address + TLU_CH_TIME1, EB_DATA32, &time1);
cycle.read (address + TLU_CH_TIME0, EB_DATA32, &time0);
cycle.read (address + TLU_CH_SUB, EB_DATA32, &sub);
cycle.write(address + TLU_CH_POP, EB_DATA32, 1);
if ((status = cycle.close()) != EB_OK)
return status;
time = time1;
time <<= 32;
time |= time0;
time <<= 3;
time |= sub;
--c.queued;
return EB_OK;
}
struct Element {
data_t time1, time0, sub;
};
status_t TLU::pop_all(int channel, std::vector<uint64_t>& queue) {
status_t status;
Cycle cycle;
std::vector<Element> elements;
data_t fill;
uint64_t time;
if (channel < 0 || channel >= (int)channels.size())
return EB_FAIL;
Channel& c = channels[channel];
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.write(address + TLU_CH_SELECT, EB_DATA32, channel);
cycle.read (address + TLU_CH_FILL_COUNT, EB_DATA32, &fill);
if ((status = cycle.close()) != EB_OK)
return status;
elements.resize(fill);
if (fill == 0) return EB_OK;
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.write(address + TLU_CH_SELECT, EB_DATA32, channel);
for (unsigned i = 0; i < elements.size(); ++i) {
Element& e = elements[i];
cycle.read (address + TLU_CH_TIME1, EB_DATA32, &e.time1);
cycle.read (address + TLU_CH_TIME0, EB_DATA32, &e.time0);
cycle.read (address + TLU_CH_SUB, EB_DATA32, &e.sub);
cycle.write(address + TLU_CH_POP, EB_DATA32, 1);
}
if ((status = cycle.close()) != EB_OK)
return status;
for (unsigned i = 0; i < elements.size(); ++i) {
Element& e = elements[i];
time = e.time1;
time <<= 32;
time |= e.time0;
time <<= 3;
time |= e.sub;
queue.push_back(time);
}
c.queued = 0;
return EB_OK;
}
status_t TLU::pop_all(std::vector<std::vector<uint64_t> >& queues) {
status_t status;
Cycle cycle;
std::vector<std::vector<Element> > elements;
std::vector<data_t> fill;
uint64_t time;
queues.resize(channels.size());
fill.resize(channels.size());
if ((status = cycle.open(device)) != EB_OK)
return status;
for (unsigned i = 0; i < channels.size(); ++i) {
cycle.write(address + TLU_CH_SELECT, EB_DATA32, i);
cycle.read (address + TLU_CH_FILL_COUNT, EB_DATA32, &fill[i]);
}
if ((status = cycle.close()) != EB_OK)
return status;
if ((status = cycle.open(device)) != EB_OK)
return status;
elements.resize(channels.size());
for (unsigned c = 0; c < channels.size(); ++c) {
std::vector<Element>& esv = elements[c];
esv.resize(fill[c]);
cycle.write(address + TLU_CH_SELECT, EB_DATA32, c);
for (unsigned e = 0; e < esv.size(); ++e) {
Element& es = esv[e];
cycle.read (address + TLU_CH_TIME1, EB_DATA32, &es.time1);
cycle.read (address + TLU_CH_TIME0, EB_DATA32, &es.time0);
cycle.read (address + TLU_CH_SUB, EB_DATA32, &es.sub);
cycle.write(address + TLU_CH_POP, EB_DATA32, 1);
}
}
if ((status = cycle.close()) != EB_OK)
return status;
for (unsigned c = 0; c < channels.size(); ++c) {
std::vector<uint64_t>& o = queues[c];
std::vector<Element>& esv = elements[c];
for (unsigned e = 0; e < esv.size(); ++e) {
Element& es = esv[e];
time = es.time1;
time <<= 32;
time |= es.time0;
time <<= 3;
time |= es.sub;
o.push_back(time);
}
channels[c].queued = 0;
}
return EB_OK;
}
}
/** @file tlu-ctl.cpp
* @brief Command-line interface for TLU control registers
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2014 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Control registers and queue inspection.
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
#define __STDC_FORMAT_MACROS
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS
#include <unistd.h> /* getopt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tlu.h"
using namespace GSI_TLU;
static const char* program;
static bool quiet;
static bool verbose;
static bool numeric;
static void help(void) {
fprintf(stderr, "Usage: %s [OPTION] <etherbone-device> [command]\n", program);
fprintf(stderr, "\n");
fprintf(stderr, " -a <address> select a TLU unit by Wishbone address\n");
fprintf(stderr, " -t <tlu-id> select a TLU unit by index #\n");
fprintf(stderr, " -c <channel-id> select a trigger channel by index #\n");
fprintf(stderr, " -v verbose operation: print SDB records\n");
fprintf(stderr, " -q quiet: do not display table headers\n");
fprintf(stderr, " -n numeric dates\n");
fprintf(stderr, " -h display this help and exit\n");
fprintf(stderr, "\n");
fprintf(stderr, " status report all top-level TLU information\n");
fprintf(stderr, " enable allow the TLU to send interrupts\n");
fprintf(stderr, " disable drop all interrupts from the TLU\n");
fprintf(stderr, "\n");
fprintf(stderr, " pop pop the next pending timestamp from a channel\n");
fprintf(stderr, " pop_all pop all pending timestamps from a channel\n");
fprintf(stderr, " clear flush the contents of the TLU channel\n");
fprintf(stderr, " test generate a virtual event timestamp\n");
fprintf(stderr, " ignore stop channel from recording edges\n");
fprintf(stderr, " listen <pos|neg> [stable] set channel to record pos/neg edges\n");
fprintf(stderr, " unhook disable arrival interrupts from this channel\n");
fprintf(stderr, " hook <addr> [msg] enable arrival interrupts on this channel\n");
}
static void die(eb_status_t status, const char* what) {
fprintf(stderr, "%s: %s -- %s\n", program, what, eb_status(status));
exit(1);
}
static void render_time(uint64_t time) {
if (numeric) {
printf("time:0x%"PRIx64"", time);
} else {
#define BILLION 1000000000ULL
time_t now = time / BILLION;
double fraction = time % BILLION;
fraction /= BILLION;
struct tm *tm = gmtime(&now);
char buf[40];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
printf("%s.%.9f", buf, fraction);
}
}
static void render_tlu(int i, TLU& tlu) {
printf("TLU #%d (0x%"EB_ADDR_FMT") with %d-deep channels and interrupts %s\n",
i, tlu.address, tlu.queue_size, tlu.int_enable?"enabled":"disabled");
printf(" "); render_time(tlu.current_time); printf("\n");
for (unsigned c = 0; c < tlu.channels.size(); ++c) {
printf(" Channel #%d %s %s: %3d queued, %3d stable time, int:",
c, tlu.channels[c].active?"listen":"ignore",
tlu.channels[c].active?(tlu.channels[c].pos_edge?"pos":"neg"):" ",
tlu.channels[c].queued, tlu.channels[c].stable);
if (tlu.channels[c].int_enable) {
printf("0x%"PRIx32",0x%"PRIx32"\n",
tlu.channels[c].int_dest,
tlu.channels[c].int_msg);
} else {
printf("disabled\n");
}
}
}
int main(int argc, char** argv) {
int opt, error;
char *value_end;
const char *devpath, *command;
int tlu_id = -1, channel_id = -1;
eb_address_t tlu_addr = 0;
bool tlu_addr_set = false;
eb_status_t status;
program = argv[0];
error = 0;
while ((opt = getopt(argc, argv, "a:t:c:vqnh")) != -1) {
switch (opt) {
case 'a':
tlu_addr = strtoull(optarg, &value_end, 0);
if (*value_end != 0) {
fprintf(stderr, "%s: invalid TLU address -- '%s'\n", program, optarg);
error = 1;
} else {
tlu_addr_set = true;
}
break;
case 't':
tlu_id = strtol(optarg, &value_end, 0);
if (*value_end || tlu_id < 0 || tlu_id > 100) {
fprintf(stderr, "%s: invalid TLU id -- '%s'\n", program, optarg);
error = 1;
}
break;
case 'c':
channel_id = strtol(optarg, &value_end, 0);
if (*value_end || channel_id < 0 || channel_id > 32) {
fprintf(stderr, "%s: invalid channel id -- '%s'\n", program, optarg);
error = 1;
}
break;
case 'v':
verbose = true;
break;
case 'q':
quiet = true;
break;
case 'n':
numeric = true;
break;
case 'h':
help();
return 0;
case ':':
case '?':
error = 1;
break;
default:
fprintf(stderr, "%s: bad getopt result\n", program);
return 1;
}
}
if (error) return 1;
if (optind >= argc) {
fprintf(stderr, "%s: expecting one non-optional argument: <etherbone-device>\n", program);
fprintf(stderr, "\n");
help();
return 1;
}
devpath = argv[optind];
if (optind+1 < argc) {
command = argv[optind+1];
} else {
command = "status";
}
if (strcasecmp(command, "hook") == 0) {
if (optind+3 > argc) {
fprintf(stderr, "%s: expecting exactly one-two arguments: hook <addr> [msg]\n", program);
return 1;
}
if (optind+4 < argc) {
fprintf(stderr, "%s: unexpected extra arguments -- '%s'\n", program, argv[optind+4]);
return 1;
}
} else if (strcasecmp(command, "listen") == 0) {
if (optind+3 > argc) {
fprintf(stderr, "%s: expecting exactly one-two arguments: listen <pos|neg> [stable]\n", program);
return 1;
}
if (optind+4 < argc) {
fprintf(stderr, "%s: unexpected extra arguments -- '%s'\n", program, argv[optind+4]);
return 1;
}
if (strcasecmp(argv[optind+2], "pos") != 0 &&
strcasecmp(argv[optind+2], "neg") != 0) {
fprintf(stderr, "%s: listen expects <pos|neg>, not '%s'\n", program, argv[optind+2]);
return 1;
}
} else {
if (optind+2 < argc) {
fprintf(stderr, "%s: unexpected extra arguments -- '%s'\n", program, argv[optind+2]);
return 1;
}
}
if (tlu_addr_set && tlu_id != -1) {
fprintf(stderr, "%s: cannot set both -a and -t at once.\n", program);
return 1;
}
Socket socket;
if ((status = socket.open()) != EB_OK) die(status, "etherbone::socket.open");
Device device;
if ((status = device.open(socket, devpath)) != EB_OK) {
fprintf(stderr, "%s: etherbone::device.open('%s') -- %s\n", program, devpath, eb_status(status));
return 1;
}
std::vector<TLU> tlus;
if ((status = TLU::probe(device, tlus)) != EB_OK) die(status, "TLU::probe");
if (tlus.empty()) {
fprintf(stderr, "%s: no TLU units found\n", program);
return 1;
}
if (tlu_addr_set) {
unsigned i;
for (i = 0; i < tlus.size(); ++i) {
if (tlus[i].address == tlu_addr) break;
}
if (i == tlus.size()) {
fprintf(stderr, "%s: no TLU found at address 0x%"EB_ADDR_FMT"\n", program, tlu_addr);
return 1;
}
tlu_id = (int)i;
}
/* Select default TLU unit */
if (tlu_id == -1 && tlus.size() == 1) {
tlu_id = 0;
}
if (channel_id != -1 && tlu_id == -1) {
fprintf(stderr, "%s: cannot specify a channel id -c '%d' without specifying a TLU unit with -a/-t\n",
program, channel_id);
return 1;
}
if (tlu_id >= (int)tlus.size()) {
fprintf(stderr, "%s: TLU id '%d' is out of range; max=%d\n", program, tlu_id, (int)tlus.size()-1);
return 1;
}
/* Pick a default channel */
if (channel_id == -1 && tlu_id != -1 && tlus[tlu_id].channels.size() == 1) {
channel_id = 0;
}
if (channel_id != -1 && channel_id >= (int)tlus[tlu_id].channels.size()) {
fprintf(stderr, "%s: channel id -c '%d' is out of range; max=%d\n", program, channel_id, (int)tlus[tlu_id].channels.size()-1);
return 1;
}
/* -------------------------------------------------------------------- */
if (!strcasecmp(command, "status")) {
if (tlu_id == -1) {
for (unsigned i = 0; i < tlus.size(); ++i) {
if (i > 0) printf("\n");
render_tlu(i, tlus[i]);
}
} else {
render_tlu(tlu_id, tlus[tlu_id]);
}
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "enable")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU unit to enable with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Enabling TLU #%d (0x%"EB_ADDR_FMT")\n", tlu_id, tlus[tlu_id].address);
}
if ((status = tlus[tlu_id].set_enable(true)) != EB_OK)
die(status, "TLU::set_enable(true)");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "disable")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU unit to disable with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Disabling TLU #%d (0x%"EB_ADDR_FMT")\n", tlu_id, tlus[tlu_id].address);
}
if ((status = tlus[tlu_id].set_enable(false)) != EB_OK)
die(status, "TLU::set_enable(false)");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "pop")) {
if (channel_id == -1) {
fprintf(stderr, "%s: specify a channel to pop with -c\n", program);
return 1;
}
if (tlus[tlu_id].channels[channel_id].queued == 0) {
fprintf(stderr, "%s: channel is empty\n", program);
return 1;
}
if (verbose) {
printf("Popping channel #%d on TLU #%d (0x%"EB_ADDR_FMT")\n",
channel_id, tlu_id, tlus[tlu_id].address);
}
uint64_t time;
if ((status = tlus[tlu_id].pop(channel_id, time)) != EB_OK)
die(status, "TLU::pop");
render_time(time);
printf("\n");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "pop_all")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU to pop_all with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Popping all from channel #%d on TLU #%d (0x%"EB_ADDR_FMT")\n",
channel_id, tlu_id, tlus[tlu_id].address);
}
if (channel_id == -1) {
std::vector<std::vector<uint64_t> > queues;
if ((status = tlus[tlu_id].pop_all(queues)) != EB_OK)
die(status, "TLU::pop_all");
for (unsigned c = 0; c < queues.size(); ++c) {
std::vector<uint64_t>& queue = queues[c];
printf("Channel #%d:\n", c);
for (unsigned e = 0; e < queue.size(); ++e) {
printf(" ");
render_time(queue[e]);
printf("\n");
}
}
} else {
std::vector<uint64_t> queue;
if ((status = tlus[tlu_id].pop_all(channel_id, queue)) != EB_OK)
die(status, "TLU::pop_all");
for (unsigned e = 0; e < queue.size(); ++e) {
render_time(queue[e]);
printf("\n");
}
}
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "clear")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU to clear with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Clearing channel #%d on TLU #%d (0x%"EB_ADDR_FMT")\n",
channel_id, tlu_id, tlus[tlu_id].address);
}
if ((status = tlus[tlu_id].clear(channel_id)) != EB_OK)
die(status, "TLU::clear");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "listen")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU to listen with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Setting channel #%d on TLU #%d (0x%"EB_ADDR_FMT") to watch for %s edges\n",
channel_id, tlu_id, tlus[tlu_id].address, argv[optind+2]);
}
bool pos = strcasecmp(argv[optind+2], "pos") == 0;
int stable = 8;
if (argc>optind+3) {
stable = strtoul(argv[optind+3], &value_end, 0);
if (*value_end != 0 || stable < 8 || stable > 1000000) {
fprintf(stderr, "%s: stable time invalid -- '%s'\n", program, argv[optind+3]);
return 1;
}
}
if ((status = tlus[tlu_id].listen(channel_id, true, pos, stable)) != EB_OK)
die(status, "TLU::listen(true)");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "ignore")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU to ignore with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Setting channel #%d on TLU #%d (0x%"EB_ADDR_FMT") to ignore edges\n",
channel_id, tlu_id, tlus[tlu_id].address);
}
if ((status = tlus[tlu_id].listen(channel_id, false, false)) != EB_OK)
die(status, "TLU::listen(false)");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "unhook")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU unhook with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Unhooking channel #%d on TLU #%d (0x%"EB_ADDR_FMT")\n",
channel_id, tlu_id, tlus[tlu_id].address);
}
if ((status = tlus[tlu_id].hook(channel_id, false)) != EB_OK)
die(status, "TLU::hook(false)");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "hook")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU hook with -t/-a\n", program);
return 1;
}
uint32_t dest, msg;
dest = strtoul(argv[optind+2], &value_end, 0);
if (*value_end != 0) {
fprintf(stderr, "%s: invalid interrupt destination -- '%s'\n", program, argv[optind+2]);
return 1;
}
msg = (argc>optind+3)?strtoul(argv[optind+3], &value_end, 0):0;
if (*value_end != 0) {
fprintf(stderr, "%s: invalid interrupt message -- '%s'\n", program, argv[optind+3]);
return 1;
}
if (verbose) {
printf("Hooking channel #%d on TLU #%d (0x%"EB_ADDR_FMT") to 0x%"PRIx32", 0x%"PRIx32"\n",
channel_id, tlu_id, tlus[tlu_id].address, dest, msg);
}
if ((status = tlus[tlu_id].hook(channel_id, true, dest, msg)) != EB_OK)
die(status, "TLU::hook(true)");
}
/* -------------------------------------------------------------------- */
else if (!strcasecmp(command, "test")) {
if (tlu_id == -1) {
fprintf(stderr, "%s: specify a TLU to test with -t/-a\n", program);
return 1;
}
if (verbose) {
printf("Testing channel #%d on TLU #%d (0x%"EB_ADDR_FMT")\n",
channel_id, tlu_id, tlus[tlu_id].address);
}
if ((status = tlus[tlu_id].test(channel_id, 0xff)) != EB_OK)
die(status, "TLU::test");
}
/* -------------------------------------------------------------------- */
else {
fprintf(stderr, "%s: unknown command -- '%s'\n", program, command);
return 1;
}
return 0;
}
/** @file tlu.h
* @brief C++ Interface to the TLU hardware.
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2014 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Public API to control all aspects of the Timestamp Latch Unit.
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
#ifndef TLU_H
#define TLU_H
#include <etherbone.h>
#include <string>
#include <vector>
namespace GSI_TLU {
using namespace etherbone;
/* ======================================================================= */
/* Software interface to hardware timestamp latch unit */
/* ======================================================================= */
struct TLU {
/* ------------------------------------------------------------------- */
/* Constant hardware values */
/* ------------------------------------------------------------------- */
uint8_t sdb_ver_major;
uint8_t sdb_ver_minor;
uint32_t sdb_version;
uint32_t sdb_date;
std::string sdb_name;
uint32_t num_channels;
uint32_t queue_size;
/* ------------------------------------------------------------------- */
/* Mutable hardware registers; only modify using methods below */
/* ------------------------------------------------------------------- */
bool int_enable; /* Global interrupt enable */
uint64_t current_time; /* # of 1ns ticks since 1970 to last refresh */
struct Channel {
bool active; /* Listening for edges? */
bool pos_edge; /* Listen for positive/negative edge */
bool int_enable; /* Interrupt enable per channel */
uint32_t queued; /* Number of timestamps needing pop */
uint32_t stable; /* Time a signal must remain stable */
uint32_t int_dest; /* Destination of interrupt message */
uint32_t int_msg; /* Content of interrupt message */
};
std::vector<Channel> channels;
/* ------------------------------------------------------------------- */
/* Access/modify the underlying hardware */
/* ------------------------------------------------------------------- */
Device device; /* Device which hosts this ECA */
eb_address_t address; /* Wishbone base address */
/* Reload mutable registers from hardware */
status_t refresh();
/* Global interrupt enable */
status_t set_enable(bool enable);
/* Hook/unhook interrupt handling (channel -1 = all) */
status_t hook(int channel, bool enable, uint32_t dest = 0, uint32_t msg = 0);
/* Listen for positive/negative edge (channel -1 = all) */
status_t listen(int channel, bool enable, bool pos_edge, uint32_t stable = 8);
/* Clear the contents of a channel (channel -1 = all) */
status_t clear(int channel);
/* Inject a virtual timestamp for testing (channel -1 = all) */
status_t test(int channel, uint8_t edge);
/* Pop the next queued timestamp from the channel (units = 1ns step since 1970) */
status_t pop(int channel, uint64_t& time);
/* Pop all timestamps from a specific channel */
status_t pop_all(int channel, std::vector<uint64_t>& queue);
/* Pop all timestamps from all channels */
status_t pop_all(std::vector<std::vector<uint64_t> >& queues);
/* Find all TLUs in the SoC of a target device */
static status_t probe(Device dev, std::vector<TLU>& tlus);
};
}
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment