From 4b87fc1baebe2d97afbedbef7a2b8c363b781f68 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" <w.terpstra@gsi.de> Date: Thu, 9 Feb 2012 09:44:26 +0000 Subject: [PATCH] Skeleton test bench in C++ --- api/v2/Makefile | 5 +- api/v2/etherbone.h | 64 ++++++++++---- api/v2/glue/cplusplus.cpp | 14 ++- api/v2/test/loopback.cpp | 173 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 api/v2/test/loopback.cpp diff --git a/api/v2/Makefile b/api/v2/Makefile index a5ed088..40c4b64 100644 --- a/api/v2/Makefile +++ b/api/v2/Makefile @@ -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) diff --git a/api/v2/etherbone.h b/api/v2/etherbone.h index 67af32b..6819220 100644 --- a/api/v2/etherbone.h +++ b/api/v2/etherbone.h @@ -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)); } diff --git a/api/v2/glue/cplusplus.cpp b/api/v2/glue/cplusplus.cpp index cfc2424..373617f 100644 --- a/api/v2/glue/cplusplus.cpp +++ b/api/v2/glue/cplusplus.cpp @@ -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); +} + +} diff --git a/api/v2/test/loopback.cpp b/api/v2/test/loopback.cpp new file mode 100644 index 0000000..be0a3db --- /dev/null +++ b/api/v2/test/loopback.cpp @@ -0,0 +1,173 @@ +/** @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; +} -- GitLab