Commit 25e16479 authored by Wesley W. Terpstra's avatar Wesley W. Terpstra

eca: update to new etherbone API and ECA hardware

parent 007b38c9
PREFIX ?= /usr/local
STAGING ?=
EB ?= ../../ip_cores/etherbone-core/api
EB ?= no
ifeq ($(EB),no))
EB_LIB ?= -letherbone
EB_INC ?=
else
EB_LIB ?= -L$(EB) -letherbone
EB_INC ?= -I$(EB)
endif
CXX = g++
CXXFLAGS = -Wall -O2 -I. -I $(EB)
CXXFLAGS = -Wall -O2 -I. $(EB_INC)
TARGETS = lib/version.h libeca.a eca-ctl eca-table
all: $(TARGETS)
......@@ -18,14 +26,14 @@ clean:
rm -f $(TARGETS) *.o lib/*.o git.*
eca-ctl: eca-ctl.o libeca.a
$(CXX) $(CXXFLAGS) -o $@ $^ -L $(EB) -Wl,-rpath,$(PREFIX)/lib -letherbone
$(CXX) $(CXXFLAGS) -o $@ $^ -Wl,-rpath,$(PREFIX)/lib $(EB_LIB)
eca-table: eca-table.o libeca.a
$(CXX) $(CXXFLAGS) -o $@ $^ -L $(EB) -Wl,-rpath,$(PREFIX)/lib -letherbone
$(CXX) $(CXXFLAGS) -o $@ $^ -Wl,-rpath,$(PREFIX)/lib $(EB_LIB)
libeca.a: lib/hw-eca.o lib/hw-stream.o lib/hw-channel.o \
lib/load-search.o lib/load-walk.o lib/load-queue.o lib/load-eca.o \
lib/program-search.o lib/program-walk.o lib/program-table.o
lib/load-table.o lib/store-table.o \
lib/table.o lib/probe-eca.o
rm -f $@
ar rcs $@ $^
ranlib $@
......
This diff is collapsed.
......@@ -67,8 +67,10 @@ static void die(eb_status_t status, const char* what) {
exit(1);
}
static void dump(const ECA& eca, const ReverseTable& rt) {
std::vector<TableEntry> t = rt.reverse();
static void dump(const ECA& eca, const Table& table) {
std::vector<TableEntry> t;
table.get(t);
if (!quiet) {
if (numeric) {
......@@ -94,6 +96,7 @@ static void dump(const ECA& eca, const ReverseTable& rt) {
}
}
#if 0
if (verbose) {
if (numeric) {
printf("--------------------------------------------------------------\n");
......@@ -108,6 +111,7 @@ static void dump(const ECA& eca, const ReverseTable& rt) {
printf("Table usage: %d/%d search and %d/%d walk\n",
(int)st.size(), (int)eca.table_size*2, (int)wt.size(), (int)eca.table_size);
}
#endif
}
int main(int argc, char** argv) {
......@@ -220,7 +224,7 @@ int main(int argc, char** argv) {
}
std::vector<ECA> ecas;
if ((status = ECA::load(device, ecas)) != EB_OK) die(status, "ECA::load");
if ((status = ECA::probe(device, ecas)) != EB_OK) die(status, "ECA::load");
if (ecas.empty()) {
fprintf(stderr, "%s: no ECA units found\n", program);
......@@ -289,9 +293,7 @@ int main(int argc, char** argv) {
te.channel = channel;
}
std::vector<SearchEntry> st;
std::vector<WalkEntry> wt;
ReverseTable rt;
Table table;
if (!strcasecmp(command, "dump")) {
if (verbose) {
......@@ -299,13 +301,10 @@ int main(int argc, char** argv) {
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.loadSearch(device, false, st)) != EB_OK)
die(status, "ECA::loadSearch(inactive)");
if ((status = eca.loadWalk(device, false, wt)) != EB_OK)
die(status, "ECA::loadWalk(inactive)");
if ((status = eca.load(false, table)) != EB_OK)
die(status, "ECA::load(inactive)");
rt.load(st, wt);
dump(eca, rt);
dump(eca, table);
}
if (!strcasecmp(command, "dump-active")) {
......@@ -314,13 +313,10 @@ int main(int argc, char** argv) {
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.loadSearch(device, true, st)) != EB_OK)
die(status, "ECA::loadSearch(active)");
if ((status = eca.loadWalk(device, true, wt)) != EB_OK)
die(status, "ECA::loadWalk(active)");
if ((status = eca.load(true, table)) != EB_OK)
die(status, "ECA::load(active)");
rt.load(st, wt);
dump(eca, rt);
dump(eca, table);
}
if (!strcasecmp(command, "flip-active")) {
......@@ -329,24 +325,19 @@ int main(int argc, char** argv) {
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.flipTables(device)) != EB_OK)
if ((status = eca.flipTables()) != EB_OK)
die(status, "ECA::flipTables");
}
if (!strcasecmp(command, "flush")) {
rt.compile(st, wt); /* rt is empty */
if (verbose) {
printf("Table usage now %d/%d search and %d/%d walk\n",
(int)st.size(), (int)eca.table_size*2, (int)wt.size(), (int)eca.table_size);
printf("Flushing inactive table on ECA #%d \"%s\" (0x%"EB_ADDR_FMT"):\n",
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.programSearch(device, st)) != EB_OK)
die(status, "ECA::programSearch");
if ((status = eca.programWalk(device, wt)) != EB_OK)
die(status, "ECA::programWalk");
/* table is empty */
if ((status = eca.store(table)) != EB_OK)
die(status, "ECA::store");
}
if (!strcasecmp(command, "add")) {
......@@ -361,30 +352,23 @@ int main(int argc, char** argv) {
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.loadSearch(device, false, st)) != EB_OK)
die(status, "ECA::loadSearch(inactive)");
if ((status = eca.loadWalk(device, false, wt)) != EB_OK)
die(status, "ECA::loadWalk(inactive)");
if ((status = eca.load(false, table)) != EB_OK)
die(status, "ECA::load(inactive)");
rt.load(st, wt);
if ((error = rt.add(te)) > 0) {
if (table.add(te) > 0) {
fprintf(stderr, "%s: new rule has a tag conflict with existing overlapping rules\n", program);
return 1;
}
rt.compile(st, wt); /* Do the hard work */
if (verbose) {
printf("Table usage now %d/%d search and %d/%d walk\n",
(int)st.size(), (int)eca.table_size*2, (int)wt.size(), (int)eca.table_size);
// printf("Table usage now %d/%d search and %d/%d walk\n",
// searchs, (int)eca.table_size*2, walks, (int)eca.table_size);
printf("Programming inactive table on ECA #%d \"%s\" (0x%"EB_ADDR_FMT"):\n",
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.programSearch(device, st)) != EB_OK)
die(status, "ECA::programSearch");
if ((status = eca.programWalk(device, wt)) != EB_OK)
die(status, "ECA::programWalk");
if ((status = eca.store(table)) != EB_OK)
die(status, "ECA::program");
}
if (!strcasecmp(command, "del")) {
......@@ -393,30 +377,23 @@ int main(int argc, char** argv) {
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.loadSearch(device, false, st)) != EB_OK)
die(status, "ECA::loadSearch(inactive)");
if ((status = eca.loadWalk(device, false, wt)) != EB_OK)
die(status, "ECA::loadWalk(inactive)");
if ((status = eca.load(false, table)) != EB_OK)
die(status, "ECA::load(inactive)");
rt.load(st, wt);
if ((error = rt.remove(te)) == 0) {
if ((error = table.del(te)) == 0) {
fprintf(stderr, "%s: no rules were removed\n", program);
return 1;
}
rt.compile(st, wt); /* Do the hard work */
if (verbose) {
printf("Table usage now %d/%d search and %d/%d walk\n",
(int)st.size(), (int)eca.table_size*2, (int)wt.size(), (int)eca.table_size);
// printf("Table usage now %d/%d search and %d/%d walk\n",
// (int)st.size(), (int)eca.table_size*2, (int)wt.size(), (int)eca.table_size);
printf("Programming inactive table on ECA #%d \"%s\" (0x%"EB_ADDR_FMT"):\n",
eca_id, eca.name.c_str(), eca.address);
}
if ((status = eca.programSearch(device, st)) != EB_OK)
die(status, "ECA::programSearch");
if ((status = eca.programWalk(device, wt)) != EB_OK)
die(status, "ECA::programWalk");
if ((status = eca.store(table)) != EB_OK)
die(status, "ECA::program");
}
device.close();
......
......@@ -28,25 +28,42 @@
#include <etherbone.h>
#include <string>
#include <vector>
#include <map>
namespace GSI_ECA {
using namespace etherbone;
typedef uint64_t Time;
typedef uint64_t Event;
typedef uint32_t Tag;
typedef uint32_t Param;
typedef uint8_t Channel;
typedef uint64_t Event;
typedef int16_t Index;
typedef uint64_t Param;
typedef uint32_t Tag;
typedef uint32_t Tef;
typedef uint64_t Time;
struct ECA;
/* An Event sent to the ECA has these fields */
struct EventEntry {
Event event;
Param param;
Tef tef;
Time time;
EventEntry(Event e = 0, Param p = 0, Tef t = 0, Time i = 0)
: event(e), param(p), tef(t), time(i) { }
};
/* An action queued to be executed has these fields */
struct ActionEntry {
Event event;
Time time;
Tag tag;
Param param;
Tag tag;
Tef tef;
Time time;
ActionEntry(Event e = 0, Param p = 0, Tag a = 0, Tef t = 0, Time i = 0)
: event(e), param(p), tag(a), tef(t), time(i) { }
};
/* Software condition table entry fields */
......@@ -56,52 +73,33 @@ struct TableEntry {
Tag tag;
Channel channel;
uint8_t event_bits;
TableEntry(Event e = 0, Time o = 0, Tag t = 0, Channel c = 0, uint8_t b = 0)
: event(e), offset(o), tag(t), channel(c), event_bits(b) { }
};
/* Hardware condition search table fields */
struct SearchEntry {
Event event;
Index first; /* -1 if end-of-list */
};
/* Hardware condition walk table fields */
struct WalkEntry {
Time offset;
Tag tag;
Index next; /* -1 if end-of-list */
Channel channel;
};
/* A useful intermediate format for the condition table */
struct ReverseTable {
/* Condition table */
class Table {
private:
struct Impl;
Impl *impl;
public:
/* Returns the number of conflicting records overwritten by this new record */
int add (const TableEntry& te);
int add (Event begin, Event end, Time, Channel, Tag);
/* Returns the number of records removed/modified */
int remove(const TableEntry& te); /* ignores tag */
int remove(Event begin, Event end, Time, Channel);
Table();
Table(const Table& table);
~Table();
/* Convert it to user-friendly form */
std::vector<TableEntry> reverse() const;
/* Compile it for loading to hardware */
void compile(std::vector<SearchEntry>& s, std::vector<WalkEntry>& w) const;
void swap(Table& table);
Table& operator = (Table x);
/* Bulk load it from user-friendly table; returns count of conflicting records */
int load(const std::vector<TableEntry>& t);
/* Bulk load it from hardware tables; returns count of conflicting records */
int load(const std::vector<SearchEntry>& s, const std::vector<WalkEntry>& w);
protected:
struct EventRange {
Event end; /* [key, end] */
Tag tag;
};
typedef std::map<Event, EventRange> EventFilter;
typedef std::map<Time, EventFilter> TableActions;
typedef std::vector<TableActions> ChannelMap;
int add(const TableEntry& te); /* Returns # records overwritten (conflict) */
int del(const TableEntry& te); /* Returns # records removed/modified */
ChannelMap data;
/* Bulk import/export of entries */
int set(const std::vector<TableEntry>& vect);
void get(std::vector<TableEntry>& vect) const;
friend struct ECA;
};
/* ======================================================================= */
......@@ -111,7 +109,6 @@ struct ActionChannel {
/* ------------------------------------------------------------------- */
/* Constant hardware values */
/* ------------------------------------------------------------------- */
eb_address_t address; /* Wishbone base address of channel */
std::string name; /* Channel instance name */
/* ------------------------------------------------------------------- */
......@@ -121,20 +118,27 @@ struct ActionChannel {
bool frozen; /* Queue is frozen; nothing enters/exits */
uint16_t fill; /* Current number of entries in the queue */
uint16_t max_fill; /* Maximum entries in the queue since reset */
uint32_t valid; /* How many valid actions have been sent */
uint32_t late; /* How many late actions have been sent */
/* ------------------------------------------------------------------- */
/* Access/modify the underlying hardware */
/* ------------------------------------------------------------------- */
ECA* eca;; /* ECA which contains this channel */
Channel index; /* Index of the channel */
/* Reload drain, freeze, fill, max_fill from hardware. */
status_t refresh(Device dev);
status_t refresh();
/* Clear all counters (max_fill, valid, late) */
status_t reset();
/* Toggle queue states */
status_t freeze(Device dev, bool freeze);
status_t drain (Device dev, bool drain);
status_t freeze(bool freeze);
status_t drain (bool drain);
/* Clear the queue max_fill counter back to fill */
status_t reset (Device dev);
/* Grab the contents from a frozen channel */
status_t load(std::vector<ActionEntry>& queue);
};
/* ======================================================================= */
......@@ -144,8 +148,6 @@ struct EventStream {
/* ------------------------------------------------------------------- */
/* Constant hardware values */
/* ------------------------------------------------------------------- */
eb_address_t address;
uint8_t sdb_ver_major;
uint8_t sdb_ver_minor;
uint32_t sdb_version;
......@@ -156,8 +158,11 @@ struct EventStream {
/* Access/modify the underlying hardware */
/* ------------------------------------------------------------------- */
ECA* eca; /* Device with this event stream */
eb_address_t address; /* Base address of the event stream */
/* Send an event to the stream */
status_t send(Device dev, Event event, Time time, Param param);
status_t send(EventEntry e);
};
/* ======================================================================= */
......@@ -168,7 +173,6 @@ struct ECA {
/* Constant hardware values */
/* ------------------------------------------------------------------- */
eb_address_t address; /* Wishbone base address */
std::string name; /* ECA instance name */
uint8_t sdb_ver_major; /* API version; major.minor */
......@@ -195,7 +199,7 @@ struct ECA {
/* Mutable hardware registers; only modify using methods below */
/* ------------------------------------------------------------------- */
Time time; /* Time as of the last refresh */
Time time; /* Time as of the last refresh */
bool disabled; /* When disabled, incoming events are dropped */
/* ------------------------------------------------------------------- */
......@@ -222,24 +226,39 @@ struct ECA {
/* Access/modify the underlying hardware */
/* ------------------------------------------------------------------- */
status_t refresh (Device dev); /* refresh time+disabled */
Device device; /* Device which hosts this ECA */
eb_address_t address; /* Wishbone base address */
unsigned index; /* Index of the ECA */
status_t disable (Device dev, bool disabled); /* Enable/disable the ECA unit */
status_t flipTables(Device dev); /* Atomicly flip inactive and active tables */
status_t refresh(); /* refresh time+disabled */
/* Load the H/W representation of the tables */
status_t loadQueue (Device dev, unsigned channel, std::vector<ActionEntry>& queue);
status_t loadSearch (Device dev, bool active, std::vector<SearchEntry>& table);
status_t loadWalk (Device dev, bool active, std::vector<WalkEntry>& table);
status_t disable(bool disabled); /* Enable/disable the ECA unit */
status_t flipTables(); /* Atomicly flip inactive and active tables */
/* Program the INACTIVE table */
status_t programSearch(Device dev, const std::vector<SearchEntry>& table);
status_t programWalk (Device dev, const std::vector<WalkEntry>& table);
/* Load/store the condition table */
status_t load(bool active, Table& table);
status_t store(const Table& table);
/* Locate all the ECA units on the bus */
static status_t load(Device dev, std::vector<ECA>& ecas);
static status_t probe(Device dev, std::vector<ECA>& ecas);
};
/* ======================================================================= */
/* Inline functions that are not part of the ABI */
/* ======================================================================= */
inline void Table::swap(Table& table) {
Impl* tmp = impl;
impl = table.impl;
table.impl = tmp;
}
inline Table& Table::operator = (Table x) {
swap(x);
return *this;
}
}
#endif
......@@ -11,7 +11,11 @@
--! 0x00 RW: ECA Control
--! 0x00 : 0x01 = disable, 0x2 = flip, 0x80=inspect_table, 0x40=inspect_queue
--! 0x01 : ASCII ECA Name
--! 0x04 RW: Table size, Q depth, channels, IDX
--! 0x04 RW: ECA params
--! 0x04 : log(table size)
--! 0x05 : log(queue depth)
--! 0x06 : number of channels
--! 0x07 : index of ECA
--! 0x08 R : Time1
--! 0x0C R : Time0
--! 0x10 RW: Search index
......@@ -35,7 +39,7 @@
--! 0x48 -- reserved --
--!
--! 0x4C RW: Select
--! 0x01 : Channel #
--! 0x00-01 : Channel #
--! 0x02-03 : Buffer Index
--! 0x50 RW: Channel Control
--! 0x00 : 0x01 = disable, 0x02 = freeze, 0x40 = late, 0x80 = valid
......@@ -43,7 +47,7 @@
--! 0x54 RW: Fill
--! 0x00-01 : Current Queue fill
--! 0x02-03 : Max fill (can be cleared to 0)
--! 0x58 RW: Total actions counter
--! 0x58 RW: Valid actions counter
--! 0x5C RW: Late actions counter
--!
--! 0x60 R : Event1 ... do NOT synchronize; hold index long enough
......@@ -52,8 +56,8 @@
--! 0x6C R : Param0
--! 0x70 R : Tag
--! 0x74 R : Tef
--! 0x78 R : Time0
--! 0x7C R : Time1
--! 0x78 R : Time1
--! 0x7C R : Time0
--!
--------------------------------------------------------------------------------
--! This library is free software; you can redistribute it and/or
......
......@@ -28,97 +28,159 @@
#include <stdio.h>
#include <assert.h>
#include <algorithm>
#include "eca.h"
#include "hw-eca.h"
namespace GSI_ECA {
status_t ActionChannel::refresh(Device dev) {
status_t ActionChannel::refresh() {
Cycle cycle;
eb_status_t status;
eb_address_t address;
eb_data_t ctl;
eb_data_t fill;
int done;
eb_data_t d_fill;
eb_data_t d_valid;
eb_data_t d_late;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
cycle.read(address + ECAQ_CTL, EB_DATA32, &ctl);
cycle.read(address + ECAQ_FILL, EB_DATA32, &fill);
cycle.close();
address = eca->address;
cycle.write(address + ECAQ_SELECT, EB_DATA32, index << 16);
cycle.read(address + ECAQ_CTL, EB_DATA32, &ctl);
cycle.read(address + ECAQ_FILL, EB_DATA32, &d_fill);
cycle.read(address + ECAQ_VALID, EB_DATA32, &d_valid);
cycle.read(address + ECAQ_LATE, EB_DATA32, &d_late);
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
draining = ((ctl >> 24) & 0x01) != 0;
frozen = ((ctl >> 24) & 0x02) != 0;
fill = (fill >> 16) & 0xFFFF;
max_fill = (fill >> 0) & 0xFFFF;
fill = (d_fill >> 16) & 0xFFFF;
max_fill = (d_fill >> 0) & 0xFFFF;
valid = d_valid & 0xFFFFFFFF;
late = d_late & 0xFFFFFFFF;
return EB_OK;
}
status_t ActionChannel::freeze(Device dev, bool freeze) {
status_t ActionChannel::freeze(bool freeze) {
Cycle cycle;
eb_status_t status;
eb_address_t address;
eb_data_t ctl;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
address = eca->address;
ctl = (draining?0x1:0) | (freeze?0x2:0);
cycle.write(address + ECAQ_SELECT, EB_DATA16|EB_BIG_ENDIAN, index);
cycle.write(address + ECAQ_CTL, EB_DATA8|EB_BIG_ENDIAN, ctl);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
frozen = freeze;
return EB_OK;
}
status_t ActionChannel::drain(Device dev, bool drain) {
status_t ActionChannel::drain(bool drain) {
Cycle cycle;
eb_address_t address;
eb_status_t status;
eb_data_t ctl;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
address = eca->address;
ctl = (drain?0x1:0) | (frozen?0x2:0);
cycle.write(address + ECAQ_SELECT, EB_DATA16|EB_BIG_ENDIAN, index);
cycle.write(address + ECAQ_CTL, EB_DATA8|EB_BIG_ENDIAN, ctl);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
draining = drain;
return EB_OK;
}
status_t ActionChannel::reset(Device dev) {
status_t ActionChannel::reset() {
Cycle cycle;
eb_address_t address;
eb_status_t status;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
cycle.write(address + ECAQ_MAX_FILL, EB_DATA16|EB_BIG_ENDIAN, 0);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
address = eca->address;
cycle.write(address + ECAQ_SELECT, EB_DATA16|EB_BIG_ENDIAN, index);
cycle.write(address + ECAQ_FILL, EB_DATA32|EB_BIG_ENDIAN, 0);
cycle.write(address + ECAQ_VALID, EB_DATA32|EB_BIG_ENDIAN, 0);
cycle.write(address + ECAQ_LATE, EB_DATA32|EB_BIG_ENDIAN, 0);
return cycle.close();
}
static bool sort_time(ActionEntry a, ActionEntry b) {
return (a.time < b.time);
}
status_t ActionChannel::load(std::vector<ActionEntry>& table) {
Cycle cycle;
eb_address_t address;
eb_status_t status;
eb_data_t ctl;
eb_data_t event1, event0;
eb_data_t param1, param0;
eb_data_t tag, tef;
eb_data_t time1, time0;
table.clear();
/* Can only probe if inspect_queue is true */
if (!eca->inspect_queue) return EB_OK;
/* If the queue is not frozen, it won't work */
if (!frozen) return EB_FAIL;
address = eca->address;
for (unsigned i = 0; i < eca->queue_size; ++i) {
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
cycle.write(address + ECAQ_SELECT, EB_DATA32|EB_BIG_ENDIAN, (index << 16) | i);
cycle.read (address + ECAQ_CTL, EB_DATA32, &ctl);
cycle.read (address + ECAQ_EVENT1, EB_DATA32, &event1);
cycle.read (address + ECAQ_EVENT0, EB_DATA32, &event0);
cycle.read (address + ECAQ_PARAM1, EB_DATA32, &param1);
cycle.read (address + ECAQ_PARAM0, EB_DATA32, &param0);
cycle.read (address + ECAQ_TAG, EB_DATA32, &tag);
cycle.read (address + ECAQ_TEF, EB_DATA32, &tef);
cycle.read (address + ECAQ_TIME1, EB_DATA32, &time1);
cycle.read (address + ECAQ_TIME0, EB_DATA32, &time0);
if ((status = cycle.close()) != EB_OK)
return status;
/* Is the record invalid? */
if (((ctl >> 24) & 0x80) == 0) continue;
ActionEntry ae;
ae.event = event1; ae.event <<= 32; ae.event += event0;
ae.param = param1; ae.param <<= 32; ae.param += param0;
ae.tag = tag;
ae.tef = tef;
ae.time = time1; ae.time <<= 32; ae.time += time0;
table.push_back(ae);
}
std::sort(table.begin(), table.end(), sort_time);
return EB_OK;
}
......
......@@ -166,25 +166,20 @@ double ECA::delay(uint64_t seconds) const {
return out;
}
status_t ECA::refresh(Device dev) {
status_t ECA::refresh() {
Cycle cycle;
eb_status_t status;
eb_data_t control;
eb_data_t time1, time0;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.read(address + ECA_CTL, EB_DATA32, &control);
cycle.read(address + ECA_TIME1, EB_DATA32, &time1);
cycle.read(address + ECA_TIME0, EB_DATA32, &time0);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK) return status;
time = time1;
time <<= 32;
......@@ -194,47 +189,19 @@ status_t ECA::refresh(Device dev) {
return EB_OK;
}
status_t ECA::disable(Device dev, bool d) {
Cycle cycle;
eb_data_t data;
status_t ECA::disable(bool d) {
eb_status_t status;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
return status;
data = d?1:0;
cycle.write(address + ECA_CTL, EB_DATA8|EB_BIG_ENDIAN, data);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
status = device.write(address + ECA_CTL, EB_DATA8|EB_BIG_ENDIAN, d?1:0);
if (status != EB_OK) return status;
disabled = d;
return EB_OK;
}
status_t ECA::flipTables(Device dev) {
Cycle cycle;
eb_data_t data;
eb_status_t status;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
return status;
data = 2;
cycle.write(address + ECA_CTL, EB_DATA8|EB_BIG_ENDIAN, data);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
return EB_OK;
status_t ECA::flipTables() {
return device.write(address + ECA_CTL, EB_DATA8|EB_BIG_ENDIAN, 2);
}
}
......@@ -26,6 +26,7 @@
#define ECA_HW_H
#include <etherbone.h>
#include <map>
#define GSI_VENDOR_ID 0x651
#define ECA_DEVICE_ID 0x8752bf44U
......@@ -54,23 +55,74 @@
#define ECA_FREQ_5S 0x3C
#define ECA_FREQ_2S 0x3D
#define ECA_FREQ_DIV 0x3E
#define ECA_END 0x40
#define ECAQ_CTL 0x00
#define ECAQ_NAME 0x01
#define ECAQ_INDEX 0x02
#define ECAQ_FILL 0x04
#define ECAQ_MAX_FILL 0x06
#define ECAQ_TIME1 0x08
#define ECAQ_TIME0 0x0C
#define ECAQ_EVENT1 0x10
#define ECAQ_EVENT0 0x14
#define ECAQ_TAG 0x18
#define ECAQ_PARAM 0x1C
#define ECAQ_END 0x20
#define ECAQ_SELECT 0x4C
#define ECAQ_CHANNEL 0x4C
#define ECAQ_INDEX 0x4E
#define ECAQ_CTL 0x50
#define ECAQ_NAME 0x51
#define ECAQ_FILL 0x54
#define ECAQ_MAX_FILL 0x56
#define ECAQ_VALID 0x58
#define ECAQ_LATE 0x5C
#define ECAQ_EVENT1 0x60
#define ECAQ_EVENT0 0x64
#define ECAQ_PARAM1 0x68
#define ECAQ_PARAM0 0x6C
#define ECAQ_TAG 0x70
#define ECAQ_TEF 0x74
#define ECAQ_TIME1 0x78
#define ECAQ_TIME0 0x7C
namespace GSI_ECA {
extern void eca_cycle_done(int* done, etherbone::Device dev, etherbone::Operation op, eb_status_t status);
/* Hardware condition search table fields */
struct SearchEntry {
Event event;
Index first; /* -1 if end-of-list */
};
/* Hardware condition walk table fields */
struct WalkEntry {
Time offset;
Tag tag;
Index next; /* -1 if end-of-list */
Channel channel;
};
/* A useful intermediate format for the condition table */
struct Table::Impl {
public:
/* Returns the number of conflicting records overwritten by this new record */
int add (const TableEntry& te);
int add (Event begin, Event end, Time, Channel, Tag);
/* Returns the number of records removed/modified */
int remove(const TableEntry& te); /* ignores tag */
int remove(Event begin, Event end, Time, Channel);
/* Convert it to user-friendly form */
void get(std::vector<TableEntry>& te) const;
/* Bulk load it from user-friendly table; returns count of conflicting records */
int set(const std::vector<TableEntry>& t);
/* Bulk load it from hardware tables; returns count of conflicting records */
int decompile(const std::vector<SearchEntry>& s, const std::vector<WalkEntry>& w);
/* Compile it for loading to hardware */
void compile(std::vector<SearchEntry>& s, std::vector<WalkEntry>& w) const;
protected:
struct EventRange {
Event end; /* [key, end] */
Tag tag;
};
typedef std::map<Event, EventRange> EventFilter;
typedef std::map<Time, EventFilter> TableActions;
typedef std::vector<TableActions> ChannelMap;
ChannelMap data;
};
}
#endif
......@@ -33,27 +33,23 @@
namespace GSI_ECA {
status_t EventStream::send(Device dev, Event event, Time time, Param param) {
status_t EventStream::send(EventEntry e) {
Cycle cycle;
eb_status_t status;
int done;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
cycle.write(address, EB_DATA32, event >> 32);
cycle.write(address, EB_DATA32, event & UINT32_C(0xFFFFFFFF));
cycle.write(address, EB_DATA32, time >> 32);
cycle.write(address, EB_DATA32, time & UINT32_C(0xFFFFFFFF));
cycle.write(address, EB_DATA32, param);
cycle.close();
cycle.write(address, EB_DATA32, e.event >> 32);
cycle.write(address, EB_DATA32, e.event & UINT32_C(0xFFFFFFFF));
cycle.write(address, EB_DATA32, e.param >> 32);
cycle.write(address, EB_DATA32, e.param & UINT32_C(0xFFFFFFFF));
cycle.write(address, EB_DATA32, 0); // reserved
cycle.write(address, EB_DATA32, e.tef & UINT32_C(0xFFFFFFFF));
cycle.write(address, EB_DATA32, e.time >> 32);
cycle.write(address, EB_DATA32, e.time & UINT32_C(0xFFFFFFFF));
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
return EB_OK;
return cycle.close();
}
}
/** @file load-queue.cpp
* @brief Load the contents of a queue
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Retrieve the actions queued in a channel.
*
*******************************************************************************
* 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 <algorithm>
#include "eca.h"
#include "hw-eca.h"
namespace GSI_ECA {
static bool sort_time(ActionEntry a, ActionEntry b) {
return (a.time < b.time);
}
status_t ECA::loadQueue(Device dev, unsigned channel, std::vector<ActionEntry>& table) {
Cycle cycle;
eb_status_t status;
eb_address_t base;
eb_data_t ctl;
eb_data_t time1, time0;
eb_data_t event1, event0;
eb_data_t tag, param;
int done;
table.clear();
/* Can only probe if inspect_queue is true */
if (!inspect_queue) return EB_OK;
/* If the queue is not frozen, it won't work */
if (channel >= channels.size() || !channels[channel].frozen) return EB_FAIL;
base = channels[channel].address;
for (unsigned i = 0; i < queue_size; ++i) {
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
return status;
cycle.write(base + ECAQ_INDEX, EB_DATA16|EB_BIG_ENDIAN, i);
cycle.read (base + ECAQ_CTL, EB_DATA32, &ctl);
cycle.read (base + ECAQ_TIME1, EB_DATA32, &time1);
cycle.read (base + ECAQ_TIME0, EB_DATA32, &time0);
cycle.read (base + ECAQ_EVENT1, EB_DATA32, &event1);
cycle.read (base + ECAQ_EVENT0, EB_DATA32, &event0);
cycle.read (base + ECAQ_TAG, EB_DATA32, &tag);
cycle.read (base + ECAQ_PARAM, EB_DATA32, &param);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
/* Is the record invalid? */
if (((ctl >> 24) & 0x80) == 0) continue;
ActionEntry ae;
ae.time = time1; ae.time <<= 32; ae.time += time0;
ae.event = event1; ae.event <<= 32; ae.event += event0;
ae.tag = tag;
ae.param = param;
table.push_back(ae);
}
std::sort(table.begin(), table.end(), sort_time);
return EB_OK;
}
}
/** @file load-search.cpp
* @brief Load the search table
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Grab the contents of the search component of the condition table.
*
*******************************************************************************
* 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 "eca.h"
#include "hw-eca.h"
namespace GSI_ECA {
status_t ECA::loadSearch(Device dev, bool active, std::vector<SearchEntry>& table) {
Cycle cycle;
eb_status_t status;
eb_data_t index, first;
eb_data_t event1, event0;
int done;
if (!inspect_table) {
table.clear();
return EB_OK;
}
table.resize(table_size*2); /* Two entries for every table entry */
for (unsigned i = 0; i < table.size(); ++i) {
SearchEntry& se = table[i];
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
return status;
index = (active?0x80000000UL:0) + i;
cycle.write(address + ECA_SEARCH, EB_DATA32, index);
cycle.read (address + ECA_FIRST, EB_DATA32, &first);
cycle.read (address + ECA_EVENT1, EB_DATA32, &event1);
cycle.read (address + ECA_EVENT0, EB_DATA32, &event0);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
se.event = event1;
se.event <<= 32;
se.event |= event0;
if ((first >> 31) != 0) {
se.first = first & 0x7FFF;
} else {
se.first = -1;
}
}
return EB_OK;
}
}
\ No newline at end of file
/** @file load-walk.cpp
* @brief Load the walk table
/** @file load-table.cpp
* @brief Load the condition table
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Grab the contents of the walker component of the condition table.
* Grab the contents of the condition table from the ECA.
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
......@@ -33,25 +33,69 @@
namespace GSI_ECA {
status_t ECA::loadWalk(Device dev, bool active, std::vector<WalkEntry>& table) {
static status_t loadSearch(ECA* eca, bool active, std::vector<SearchEntry>& table) {
Cycle cycle;
eb_address_t address;
eb_status_t status;
eb_data_t index, first;
eb_data_t event1, event0;
if (!eca->inspect_table) {
table.clear();
return EB_OK;
}
table.resize(eca->table_size*2); /* Two entries for every table entry */
address = eca->address;
for (unsigned i = 0; i < table.size(); ++i) {
SearchEntry& se = table[i];
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
index = (active?0x80000000UL:0) + i;
cycle.write(address + ECA_SEARCH, EB_DATA32, index);
cycle.read (address + ECA_FIRST, EB_DATA32, &first);
cycle.read (address + ECA_EVENT1, EB_DATA32, &event1);
cycle.read (address + ECA_EVENT0, EB_DATA32, &event0);
if ((status = cycle.close()) != EB_OK)
return status;
se.event = event1;
se.event <<= 32;
se.event |= event0;
if ((first >> 31) != 0) {
se.first = first & 0x7FFF;
} else {
se.first = -1;
}
}
return EB_OK;
}
static status_t loadWalk(ECA* eca, bool active, std::vector<WalkEntry>& table) {
Cycle cycle;
eb_address_t address;
eb_status_t status;
eb_data_t index, next;
eb_data_t delay1, delay0;
eb_data_t tag, channel;
int done;
if (!inspect_table) {
if (!eca->inspect_table) {
table.clear();
return EB_OK;
}
table.resize(table_size);
table.resize(eca->table_size);
address = eca->address;
for (unsigned i = 0; i < table.size(); ++i) {
WalkEntry& we = table[i];
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
index = (active?0x80000000UL:0) + i;
......@@ -61,12 +105,9 @@ status_t ECA::loadWalk(Device dev, bool active, std::vector<WalkEntry>& table) {
cycle.read (address + ECA_DELAY0, EB_DATA32, &delay0);
cycle.read (address + ECA_TAG, EB_DATA32, &tag);
cycle.read (address + ECA_CHANNEL,EB_DATA32, &channel);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
we.offset = delay1;
we.offset <<= 32;
......@@ -84,4 +125,21 @@ status_t ECA::loadWalk(Device dev, bool active, std::vector<WalkEntry>& table) {
return EB_OK;
}
status_t ECA::load(bool active, Table& table) {
status_t status;
std::vector<SearchEntry> se;
std::vector<WalkEntry> we;
if ((status = loadSearch(this, active, se)) != EB_OK)
return status;
if ((status = loadWalk(this, active, we)) != EB_OK)
return status;
if (table.impl->decompile(se, we) > 0)
return EB_FAIL;
return EB_OK;
}
}
......@@ -104,22 +104,6 @@ void eca_sdb_search(SearchRecord* record, Device dev, const struct sdb_table* sd
record->done = 1;
}
void eca_cycle_done(int* done, Device dev, Operation op, eb_status_t status) {
if (status == EB_OK) {
*done = 1;
for (; !op.is_null(); op = op.next()) {
if (op.had_error()) {
*done = 2;
fprintf(stderr, "Wishbone segfault %s address %"EB_ADDR_FMT"\n",
op.is_read()?"reading":"writing", op.address());
}
}
} else {
fprintf(stderr, "Etherbone error: %s\n", eb_status(status));
*done = status;
}
}
std::string eca_extract_name(eb_data_t* data) {
char name8[64];
......@@ -143,17 +127,17 @@ std::string eca_extract_name(eb_data_t* data) {
}
status_t ECA::load(Device dev, std::vector<ECA>& ecas) {
status_t ECA::probe(Device device, std::vector<ECA>& ecas) {
/* Phase 1 -- locate ECA units using SDB */
SearchRecord record;
record.ecas = &ecas;
dev.sdb_scan_root(&record, sdb_wrap_function_callback<SearchRecord, eca_sdb_search>);
device.sdb_scan_root(&record, sdb_wrap_function_callback<SearchRecord, eca_sdb_search>);
record.done = 0;
record.status = EB_OK;
while (!record.done) dev.socket().run();
while (!record.done) device.socket().run();
if (record.status != EB_OK) return record.status;
/* Phase 2a -- Read ECA parameters */
......@@ -163,16 +147,19 @@ status_t ECA::load(Device dev, std::vector<ECA>& ecas) {
eb_data_t time1, time0;
eb_data_t freq1, freq0;
eb_data_t fill;
eb_data_t valid;
eb_data_t late;
eb_data_t id;
Cycle cycle;
int done;
for (unsigned i = 0; i < ecas.size(); ++i) {
ECA& eca = ecas[i];
unsigned num_channels;
eca.index = i;
eca.device = device;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(device)) != EB_OK)
return status;
for (unsigned j = 0; j < 64; ++j)
......@@ -184,12 +171,9 @@ status_t ECA::load(Device dev, std::vector<ECA>& ecas) {
cycle.read(eca.address + ECA_FREQ_MUL, EB_DATA32, &freq1);
cycle.read(eca.address + ECA_FREQ_5S, EB_DATA32, &freq0);
cycle.write(eca.address + ECA_INDEX, EB_BIG_ENDIAN|EB_DATA8, i); /* set index for matching streams */
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
eca.name = eca_extract_name(name);
......@@ -213,26 +197,29 @@ status_t ECA::load(Device dev, std::vector<ECA>& ecas) {
/* Phase 2b -- Read Channel parameters + names */
for (unsigned c = 0; c < num_channels; ++c) {
ActionChannel ac;
ac.address = eca.address + ECA_END + c*ECAQ_END;
ac.eca = &eca;
ac.index = c;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(device)) != EB_OK)
return status;
cycle.write(eca.address + ECAQ_SELECT, EB_DATA32, c << 16);
for (unsigned j = 0; j < 64; ++j)
cycle.read(ac.address + ECAQ_CTL, EB_DATA32, &name[j]);
cycle.read(ac.address + ECAQ_FILL, EB_DATA32, &fill);
cycle.close();
cycle.read(eca.address + ECAQ_CTL, EB_DATA32, &name[j]);
cycle.read(eca.address + ECAQ_FILL, EB_DATA32, &fill);
cycle.read(eca.address + ECAQ_VALID, EB_DATA32, &valid);
cycle.read(eca.address + ECAQ_LATE, EB_DATA32, &late);
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
ac.name = eca_extract_name(name);
ac.draining = ((name[0] >> 24) & 0x01) != 0;
ac.frozen = ((name[0] >> 24) & 0x02) != 0;
ac.fill = (fill >> 16) & 0xFFFF;
ac.max_fill = (fill >> 0) & 0xFFFF;
ac.valid = valid & 0xFFFFFFFF;
ac.late = late & 0xFFFFFFFF;
eca.channels.push_back(ac);
}
......@@ -245,17 +232,9 @@ status_t ECA::load(Device dev, std::vector<ECA>& ecas) {
for (unsigned s = 0; s < record.streams.size(); ++s) {
EventStream& es = record.streams[s];
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
return status;
/* These have to be separate cycles so the crossbar doesn't get stuffed up */
cycle.read(es.address, EB_DATA32, &id);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
status = device.read(es.address, EB_DATA32, &id);
if (status != EB_OK) return status;
ids[s] = id;
}
......@@ -267,6 +246,7 @@ status_t ECA::load(Device dev, std::vector<ECA>& ecas) {
/* fprintf(stderr, "Unmatched ECA Event stream; id: %d\n", mid); */
continue;
}
es.eca = &ecas[mid];
ecas[mid].streams.push_back(es);
}
......
/** @file program-walk.cpp
* @brief Program the walk table
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Write the contents of the walker component of the condition table.
*
*******************************************************************************
* 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 "eca.h"
#include "hw-eca.h"
namespace GSI_ECA {
status_t ECA::programWalk(Device dev, const std::vector<WalkEntry>& table) {
Cycle cycle;
eb_status_t status;
eb_data_t next;
int done;
/* Must fit inside this hardware */
if (table.size() > table_size) return EB_OOM;
for (unsigned i = 0; i < table.size(); ++i) {
const WalkEntry& we = table[i];
/* Ensure we don't program bullshit */
assert (we.channel < channels.size());
assert (we.next < (int)i);
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
return status;
next = (we.next==-1)?0:(UINT32_C(0x80000000)|we.next);
cycle.write(address + ECA_WALK, EB_DATA32, i);
cycle.write(address + ECA_NEXT, EB_DATA32, next);
cycle.write(address + ECA_DELAY1, EB_DATA32, we.offset >> 32);
cycle.write(address + ECA_DELAY0, EB_DATA32, we.offset & UINT32_C(0xFFFFFFFF));
cycle.write(address + ECA_TAG, EB_DATA32, we.tag);
cycle.write(address + ECA_CHANNEL, EB_DATA32, we.channel);
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
}
return EB_OK;
}
}
/** @file program-search.cpp
* @brief Program the search table
/** @file store-table.cpp
* @brief Program the walk table
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* Write the contents of the search component of the condition table.
* Write the contents of the condition table to the ECA.
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
......@@ -33,13 +33,15 @@
namespace GSI_ECA {
status_t ECA::programSearch(Device dev, const std::vector<SearchEntry>& table) {
static status_t storeSearch(ECA* eca, const std::vector<SearchEntry>& table) {
Cycle cycle;
unsigned table_size;
eb_address_t address;
eb_status_t status;
eb_data_t first;
int done;
/* Must fit inside this hardware */
table_size = eca->table_size;
if (table.size() > 2*table_size) return EB_OOM;
/* Must have a 0 as first entry */
......@@ -47,6 +49,7 @@ status_t ECA::programSearch(Device dev, const std::vector<SearchEntry>& table) {
Event last_event = 0;
address = eca->address;
for (unsigned i = 0; i < 2*table_size; ++i) {
/* Duplicate the last entry to fill out the table */
const SearchEntry& se = (i<table.size())?table[i]:(*--table.end());
......@@ -55,7 +58,7 @@ status_t ECA::programSearch(Device dev, const std::vector<SearchEntry>& table) {
assert (last_event <= se.event);
last_event = se.event;
if ((status = cycle.open(dev, &done, wrap_function_callback<int, eca_cycle_done>)) != EB_OK)
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
first = (se.first==-1)?0:(UINT32_C(0x80000000)|se.first);
......@@ -64,15 +67,65 @@ status_t ECA::programSearch(Device dev, const std::vector<SearchEntry>& table) {
cycle.write(address + ECA_FIRST, EB_DATA32, first);
cycle.write(address + ECA_EVENT1, EB_DATA32, se.event >> 32);
cycle.write(address + ECA_EVENT0, EB_DATA32, se.event & UINT32_C(0xFFFFFFFF));
cycle.close();
done = 0;
while (!done) dev.socket().run();
if (done < 0) return done;
if (done == 2) return EB_FAIL;
if ((status = cycle.close()) != EB_OK)
return status;
}
return EB_OK;
}
static status_t storeWalk(ECA* eca, const std::vector<WalkEntry>& table) {
Cycle cycle;
eb_address_t address;
eb_status_t status;
eb_data_t next;
unsigned channels;
/* Must fit inside this hardware */
channels = eca->channels.size();
if (table.size() > eca->table_size) return EB_OOM;
address = eca->address;
for (unsigned i = 0; i < table.size(); ++i) {
const WalkEntry& we = table[i];
/* Ensure we don't program bullshit */
if (we.channel >= channels) return EB_FAIL;
assert (we.next < (int)i);
if ((status = cycle.open(eca->device)) != EB_OK)
return status;
next = (we.next==-1)?0:(UINT32_C(0x80000000)|we.next);
cycle.write(address + ECA_WALK, EB_DATA32, i);
cycle.write(address + ECA_NEXT, EB_DATA32, next);
cycle.write(address + ECA_DELAY1, EB_DATA32, we.offset >> 32);
cycle.write(address + ECA_DELAY0, EB_DATA32, we.offset & UINT32_C(0xFFFFFFFF));
cycle.write(address + ECA_TAG, EB_DATA32, we.tag);
cycle.write(address + ECA_CHANNEL, EB_DATA32, we.channel);
if ((status = cycle.close()) != EB_OK)
return status;
}
return EB_OK;
}
status_t ECA::store(const Table& table) {
status_t status;
std::vector<SearchEntry> se;
std::vector<WalkEntry> we;
table.impl->compile(se, we);
if ((status = storeSearch(this, se)) != EB_OK)
return status;
if ((status = storeWalk(this, we)) != EB_OK)
return status;
return EB_OK;
}
}
/** @file program-table.cpp
/** @file table.cpp
* @brief Convert to/from user-facing condition table
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
......@@ -39,7 +39,35 @@
namespace GSI_ECA {
int ReverseTable::add(Event begin, Event end, Time offset, Channel channel, Tag tag) {
Table::Table() :
impl(new Impl) {
}
Table::~Table() {
delete impl;
}
Table::Table(const Table& table)
: impl(new Impl(*table.impl)) {
}
int Table::add(const TableEntry& te) {
return impl->add(te);
}
int Table::del(const TableEntry& te) {
return impl->remove(te);
}
int Table::set(const std::vector<TableEntry>& vect) {
return impl->set(vect);
}
void Table::get(std::vector<TableEntry>& vect) const {
return impl->get(vect);
}
int Table::Impl::add(Event begin, Event end, Time offset, Channel channel, Tag tag) {
int out = 0;
if (end < begin) return out;
......@@ -96,7 +124,7 @@ int ReverseTable::add(Event begin, Event end, Time offset, Channel channel, Tag
return out;
}
int ReverseTable::remove(Event begin, Event end, Time offset, Channel channel) {
int Table::Impl::remove(Event begin, Event end, Time offset, Channel channel) {
int out = 0;
if (end < begin) return out;
......@@ -165,7 +193,7 @@ static Event eca_encode_mask(uint8_t event_bits) {
}
int ReverseTable::add(const TableEntry& te) {
int Table::Impl::add(const TableEntry& te) {
Event mask = eca_encode_mask(te.event_bits);
Event begin = te.event & ~mask;
Event end = begin | mask;
......@@ -173,7 +201,7 @@ int ReverseTable::add(const TableEntry& te) {
return add(begin, end, te.offset, te.channel, te.tag);
}
int ReverseTable::remove(const TableEntry& te) {
int Table::Impl::remove(const TableEntry& te) {
Event mask = eca_encode_mask(te.event_bits);
Event begin = te.event & ~mask;
Event end = begin | mask;
......@@ -181,7 +209,7 @@ int ReverseTable::remove(const TableEntry& te) {
return remove(begin, end, te.offset, te.channel);
}
int ReverseTable::load(const std::vector<TableEntry>& t) {
int Table::Impl::set(const std::vector<TableEntry>& t) {
int count = 0;
for (unsigned ti = 0; ti < t.size(); ++ti) {
......@@ -191,7 +219,7 @@ int ReverseTable::load(const std::vector<TableEntry>& t) {
return count;
}
int ReverseTable::load(const std::vector<SearchEntry>& s, const std::vector<WalkEntry>& w) {
int Table::Impl::decompile(const std::vector<SearchEntry>& s, const std::vector<WalkEntry>& w) {
int count = 0;
Event last = ((Event)-1);
......@@ -233,8 +261,8 @@ static unsigned eca_decode_mask(Event mask) {
return count;
}
std::vector<TableEntry> ReverseTable::reverse() const {
std::vector<TableEntry> result;
void Table::Impl::get(std::vector<TableEntry>& result) const {
result.clear();
for (unsigned c = 0; c < data.size(); ++c) {
const TableActions& ta = data[c];
......@@ -295,11 +323,11 @@ std::vector<TableEntry> ReverseTable::reverse() const {
}
sort(result.begin(), result.end(), sort_event_bits_offset_channel);
return result;
}
void ReverseTable::compile(std::vector<SearchEntry>& s, std::vector<WalkEntry>& w) const {
std::vector<TableEntry> t(reverse());
void Table::Impl::compile(std::vector<SearchEntry>& s, std::vector<WalkEntry>& w) const {
std::vector<TableEntry> t;
get(t);
s.clear();
w.clear();
......
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