Skip to content
Snippets Groups Projects
Commit 4b87fc1b authored by Wesley W. Terpstra's avatar Wesley W. Terpstra
Browse files

Skeleton test bench in C++

parent ad11016b
No related merge requests found
......@@ -9,7 +9,7 @@ CXXFLAGS= $(FLAGS)
CC = gcc
CXX = g++
TARGETS = demo/sizes demo/eb-read demo/eb-write demo/eb-snoop
TARGETS = demo/sizes demo/eb-read demo/eb-write demo/eb-snoop test/loopback
TARGET = etherbone.a
OBJECTS = $(patsubst %.cpp,%.o,$(patsubst %.c,%.o,$(SOURCES)))
SOURCES = memory/static.c \
......@@ -43,6 +43,9 @@ $(TARGET): $(OBJECTS)
demo/%: demo/%.c $(TARGET)
$(CC) $(CFLAGS) -o $@ $^
test/%: test/%.cpp $(TARGET)
$(CXX) $(CXXFLAGS) -o $@ $^
clean:
rm -f $(TARGET) $(OBJECTS) $(TARGETS)
......
......@@ -409,7 +409,12 @@ typedef eb_data_t data_t;
typedef eb_status_t status_t;
typedef eb_width_t width_t;
typedef eb_descriptor_t descriptor_t;
typedef eb_handler_t handler_t;
class Handler {
public:
virtual status_t read (address_t address, width_t width, data_t* data) = 0;
virtual status_t write(address_t address, width_t width, data_t data) = 0;
};
class Socket {
public:
......@@ -419,7 +424,7 @@ class Socket {
status_t close();
/* attach/detach a virtual device */
status_t attach(handler_t handler);
status_t attach(address_t base, address_t mask, Handler* handler);
status_t detach(address_t address);
status_t poll();
......@@ -445,7 +450,8 @@ class Device {
status_t close();
void flush();
Socket socket() const;
const Socket socket() const;
Socket socket();
protected:
Device(eb_device_t device);
......@@ -471,7 +477,8 @@ class Cycle {
Cycle& read_config (address_t address, data_t* data = 0);
Cycle& write_config(address_t address, data_t data);
Device device() const;
const Device device() const;
Device device();
protected:
eb_cycle_t cycle;
......@@ -493,19 +500,20 @@ class Operation {
address_t address() const;
data_t data () const;
Operation next() const;
const Operation next() const;
Operation next();
protected:
Operation(eb_operation_t op);
eb_operation_t operation;
};
/* Convenience templates to convert member functions into callback type */
template <typename T, void (T::*cb)(Operation, status_t)>
void proxy(T* object, eb_operation_t op, eb_status_t status) {
return (object->*cb)(Operation(op), status);
}
/* Convenience templates to convert member functions into callback type */
template <typename T, void (T::*cb)(Operation, status_t)>
friend void proxy(T* object, eb_operation_t op, eb_status_t status) {
return (object->*cb)(Operation(op), status);
}
};
/****************************************************************************/
/* C++ Implementation */
......@@ -529,8 +537,18 @@ inline status_t Socket::close() {
return out;
}
inline status_t Socket::attach(handler_t handler) {
return eb_socket_attach(socket, handler);
/* Proxy */
eb_status_t eb_proxy_read_handler(eb_user_data_t data, eb_address_t address, eb_width_t width, eb_data_t* ptr);
eb_status_t eb_proxy_write_handler(eb_user_data_t data, eb_address_t address, eb_width_t width, eb_data_t value);
inline status_t Socket::attach(address_t base, address_t mask, Handler* handler) {
struct eb_handler h;
h.base = base;
h.mask = mask;
h.data = handler;
h.read = &eb_proxy_read_handler;
h.write = &eb_proxy_write_handler;
return eb_socket_attach(socket, &h);
}
inline status_t Socket::detach(address_t address) {
......@@ -571,10 +589,14 @@ inline status_t Device::close() {
return out;
}
inline Socket Device::socket() const {
inline const Socket Device::socket() const {
return Socket(eb_device_socket(device));
}
inline Socket Device::socket() {
return Socket(eb_device_socket(device));
}
inline void Device::flush() {
return eb_device_flush(device);
}
......@@ -625,7 +647,11 @@ inline Cycle& Cycle::write_config(address_t address, data_t data) {
return *this;
}
inline Device Cycle::device() const {
inline const Device Cycle::device() const {
return Device(eb_cycle_device(cycle));
}
inline Device Cycle::device() {
return Device(eb_cycle_device(cycle));
}
......@@ -657,7 +683,11 @@ inline data_t Operation::data() const {
return eb_operation_data(operation);
}
inline Operation Operation::next() const {
inline Operation Operation::next() {
return Operation(eb_operation_next(operation));
}
inline const Operation Operation::next() const {
return Operation(eb_operation_next(operation));
}
......
......@@ -27,7 +27,7 @@
#include "../etherbone.h"
using namespace etherbone;
namespace etherbone {
static void eb_descriptor_push(eb_user_data_t data, eb_descriptor_t des) {
std::vector<descriptor_t>* out = (std::vector<descriptor_t>*)data;
......@@ -39,3 +39,15 @@ std::vector<descriptor_t> Socket::descriptor() const {
eb_socket_descriptor(socket, &out, &eb_descriptor_push);
return out;
}
eb_status_t eb_proxy_read_handler(eb_user_data_t data, eb_address_t address, eb_width_t width, eb_data_t* ptr) {
Handler* handler = reinterpret_cast<Handler*>(data);
return handler->read(address, width, ptr);
}
eb_status_t eb_proxy_write_handler(eb_user_data_t data, eb_address_t address, eb_width_t width, eb_data_t value) {
Handler* handler = reinterpret_cast<Handler*>(data);
return handler->write(address, width, value);
}
}
/** @file loopback.cpp
* @brief A test program which executes many many EB queries.
*
* Copyright (C) 2011-2012 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* All Etherbone object types are opaque in this interface.
* Only those methods listed in this header comprise the public interface.
*
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
* @bug None!
*
*******************************************************************************
* 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/>.
*******************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include "../etherbone.h"
using namespace etherbone;
using namespace std;
void die(const char* why, status_t error);
void test_query(Device device, int len, int requests);
void test_width(Socket socket, width_t width);
void die(const char* why, status_t error) {
fprintf(stderr, "%s: %s\n", why, eb_status(error));
exit(1);
}
enum RecordType { READ_BUS, READ_CFG, WRITE_BUS, WRITE_CFG };
struct Record {
address_t address;
data_t data;
bool error;
RecordType type;
};
class Echo : public Handler {
public:
status_t read (address_t address, width_t width, data_t* data);
status_t write(address_t address, width_t width, data_t data);
};
status_t Echo::read (address_t address, width_t width, data_t* data) {
return EB_OK;
}
status_t Echo::write(address_t address, width_t width, data_t data) {
return EB_OK;
}
class TestCycle {
public:
std::vector<Record> records;
int* success;
TestCycle(Device device, int length, int* success);
void complete(Operation op, status_t status);
};
void TestCycle::complete(Operation op, status_t status) {
for (unsigned i = 0; i < records.size(); ++i) {
Record& r = records[i];
if (op.is_null()) die("unexpected null op", EB_FAIL);
switch (r.type) {
case READ_BUS: if (!op.is_read() || op.is_config()) die("wrong op", EB_FAIL); break;
case READ_CFG: if (!op.is_read() || !op.is_config()) die("wrong op", EB_FAIL); break;
case WRITE_BUS: if ( op.is_read() || op.is_config()) die("wrong op", EB_FAIL); break;
case WRITE_CFG: if ( op.is_read() || !op.is_config()) die("wrong op", EB_FAIL); break;
}
if (op.address () != r.address) die("wrong addr", EB_FAIL);
if (op.data () != r.data) die("wrong data", EB_FAIL);
if (op.had_error() != r.error) die("wrong flag", EB_FAIL);
}
if (!op.is_null()) die("too many ops", EB_FAIL);
}
TestCycle::TestCycle(Device device, int length, int* success_)
: success(success_) {
Cycle cycle(device, this, &proxy<TestCycle, &TestCycle::complete>);
for (int op = 0; op < length; ++op) {
Record r;
switch (r.type) {
case READ_BUS: cycle.read (r.address, 0); break;
case READ_CFG: cycle.read_config (r.address, 0); break;
case WRITE_BUS: cycle.write (r.address, r.data); break;
case WRITE_CFG: cycle.write_config(r.address, r.data); break;
}
records.push_back(r);
}
}
void test_query(Device device, int len, int requests) {
std::vector<int> cuts;
std::vector<int>::iterator i;
int success, timeout;
cuts.push_back(0);
cuts.push_back(len);
for (int cut = 1; cut < requests; ++cut)
cuts.push_back(random() % len);
sort(cuts.begin(), cuts.end());
/* Prepare each cycle */
for (i = cuts.begin(); i+1 != cuts.end(); ++i) {
int amount = *(i+1) - *i;
TestCycle(device, amount, &success);
}
/* Flush the queries */
device.flush();
/* Wait until all complete successfully */
timeout = 1000000; /* 1 second */
Socket socket = device.socket();
while (success < requests && timeout > 0) {
timeout -= socket.block(timeout);
socket.poll();
}
if (timeout < 0) die("waiting for loopback success", EB_TIMEOUT);
}
void test_width(Socket socket, width_t width) {
Device device;
status_t err;
if ((err = device.open(socket, "udp/localhost/8183", width)) != EB_OK) die("device.open", err);
for (int len = 0; len < 4000; ++len)
for (int requests = 0; requests <= 9; ++requests)
for (int repetitions = 0; repetitions < 100; ++repetitions)
test_query(device, len, requests);
if ((err = device.close()) != EB_OK) die("device.close", err);
}
int main() {
status_t err;
Socket socket;
if ((err = socket.open(8183)) != EB_OK) die("socket.open", err);
Echo echo;
if ((err = socket.attach(0, ~0, &echo)) != EB_OK) die("socket.attach", err);
/* for widths */
test_width(socket, EB_DATAX | EB_ADDRX);
return 0;
}
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