Skip to content

Commit

Permalink
Test cpp bindings in Linux
Browse files Browse the repository at this point in the history
- Also get READMEs up to date across repo. Not exhaustive, but improved.
  • Loading branch information
jonnew committed Oct 28, 2020
1 parent 6d5ea6a commit 3d8b2e0
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 80 deletions.
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
`liboni` is the official [Open Ephys](https://open-ephys.org/)
`liboni` is the [Open Ephys](https://open-ephys.org/)
[ONI](https://github.com/jonnew/ONI)-compliant API implementation
for controlling ONI-compliant hardware. It is focused on performance in terms of
for use with ONI-compliant hardware. It is focused on performance in terms of
bandwidth and closed-loop reaction times and includes means for balancing these
characteristics on-the-fly. When used in combination with
characteristics on-the-fly. When used in combination with
[ONIX Hardware](https://github.com/jonnew/ONIX), it can be use to acquire from

- Tetrode headstages
Expand All @@ -16,13 +16,11 @@ main memory, and back again. This repository contains the following folders:
- **[api](api/README.md)** liboni API and language bindings. MIT-licensed.
- **[drivers](drivers/README.md)** device drivers used by the API at runtime.
License depends on driver.

__Citing this work__:

1. Citing the paper
### Documentation
Documentation is provided on the [ONIX
site](https://open-ephys.github.io/onix-docs/API%20Reference/index.html).

- TODO
### Citing
TODO

2. Citing the repository itself

- TODO
2 changes: 1 addition & 1 deletion api/LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2017-2019 JONATHAN P. NEWMAN
Copyright 2020 Jonathan P. Newman & Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
6 changes: 3 additions & 3 deletions api/cpponi/Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
PREFIX := /usr/local/include/

.PHONY: install
install:
install:
@[ -d $(DESTDIR)$(PREFIX) ] || mkdir -p $(DESTDIR)$(PREFIX)oni
cp oni.hpp onidevices.hpp $(DESTDIR)$(PREFIX)
cp oni.hpp onix.hpp $(DESTDIR)$(PREFIX)

.PHONY: uninstall
uninstall:
$(RM) -r $(DESTDIR)$(PREFIX)oni.hpp
$(RM) -r $(DESTDIR)$(PREFIX)onidevices.hpp
$(RM) -r $(DESTDIR)$(PREFIX)onix.hpp
6 changes: 3 additions & 3 deletions api/cpponi/cpponi-test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CFLAGS=-O3 -std=c++2a -stdlib=libc++ -Wall
LDFLAGS=-loni

.PHONY: all
all: host_xilly
all: host

.PHONY: debug
debug: CFLAGS += -DDEBUG -g3
Expand All @@ -19,10 +19,10 @@ debug: all
profile: LDFLAGS += -lprofiler
profile: all

host_xilly: host_xilly.cpp
host: host.cpp
@echo Making $@
$(CC) $(CFLAGS) $? -lm -lpthread -ldl $(LDFLAGS) -o $@

.PHONY: clean
clean:
rm host_xilly
rm host
53 changes: 28 additions & 25 deletions api/cpponi/cpponi-test/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ std::vector<FILE *> dump_files;

volatile int quit = 0;
volatile int display = 0;
volatile int display_clock = 0;
int running = 1;

int parse_reg_cmd(const char *cmd, long *values, int len)
Expand Down Expand Up @@ -69,7 +68,7 @@ void print_dev_table(oni::device_map_t devices)

const char *dev_str = onix::device_str(d.second.id);

printf("%02zd |%05zd: 0x%02x.0x%02x\t|%d\t|%u\t|%u\t|%s\n",
printf("%02d |%05d: 0x%02x.0x%02x\t|%d\t|%u\t|%u\t|%s\n",
k++,
d.second.idx,
(uint8_t)(d.second.idx >> 8),
Expand All @@ -95,18 +94,15 @@ void data_loop(std::shared_ptr<oni::context_t> ctx)

auto frame = ctx->read_frame();

//if (display_clock && counter % 100 == 0)
//std::cout << "\tSample: " << frame.clock() << "\n\n";

auto data = frame.data<uint16_t>();

#ifdef DUMPFILES
#ifdef DUMPFILES
fwrite(data.data(), sizeof(uint16_t), data.size(), dump_files[frame.device_index()]);
#endif

if (display && counter % 1000 == 0) {


std::cout << "\t [" << frame.time() << "] Dev: " << frame.device_index() << " ("
<< onix::device_str(dev_map.at(frame.device_index()).id)
<< ")\n";
Expand All @@ -133,20 +129,28 @@ int main(int argc, char *argv[])
try {
std::cout << oe_logo_med;

int host_idx = 0;
oni_size_t block_read_size = 1024;
oni_size_t block_write_size = 1024;
int host_idx = -1;
std::string driver;

if (argc != 2 && argc != 3) {
std::cout << "usage:\n";
std::cout << "\thost driver [host_index] ...\n";
exit(1);
switch (argc) {

case 5:
block_write_size = atoi(argv[4]);
case 4:
block_read_size = atoi(argv[3]);
case 3:
host_idx = atoi(argv[2]);
case 2:
driver = argv[1];
break;
default:
std::cout << "usage:\n";
std::cout << "\thost driver [host_index] ...\n";
exit(1);
}

driver = argv[1];

if (argc == 3)
host_idx = atoi(argv[2]);

// Create context
auto ctx = std::make_shared<oni::context_t>(driver.c_str(), host_idx);

Expand All @@ -171,18 +175,16 @@ int main(int argc, char *argv[])
<< ctx->get_opt<uint32_t>(ONI_OPT_MAXREADFRAMESIZE)
<< " bytes\n";

oni_size_t block_size = 1024;
std::cout << "Setting block read size to: " << block_size << " bytes\n";
ctx->set_opt(ONI_OPT_BLOCKREADSIZE, block_size);
std::cout << "Setting block read size to: " << block_read_size << " bytes\n";
ctx->set_opt(ONI_OPT_BLOCKREADSIZE, block_read_size);

std::cout << "Block read size: "
<< ctx->get_opt<oni_size_t>(ONI_OPT_BLOCKREADSIZE)
<< " bytes\n";

block_size = 8192;
std::cout << "Setting write pre-allocation buffer to: " << block_size
std::cout << "Setting write pre-allocation buffer to: " << block_write_size
<< " bytes\n";
ctx->set_opt(ONI_OPT_BLOCKWRITESIZE, block_size);
ctx->set_opt(ONI_OPT_BLOCKWRITESIZE, block_write_size);

std::cout << "Write pre-allocation size: "
<< ctx->get_opt<oni_size_t>(ONI_OPT_BLOCKWRITESIZE)
Expand All @@ -204,7 +206,6 @@ int main(int argc, char *argv[])
while (cmd != "q") {

std::cout << "Enter a command and press enter:\n"
<< "\tc - toggle 1/1000 clock display\n"
<< "\td - toggle 1/1000 display\n"
<< "\tt - print device table\n"
<< "\tp - toggle running register\n"
Expand All @@ -224,8 +225,8 @@ int main(int argc, char *argv[])
std::cout << "Running.\n";
else
std::cout << "Paused\n";
} else if (cmd == "c") {
display_clock = (display_clock == 0) ? 1 : 0;
} else if (cmd == "x") {
ctx->set_opt(ONI_OPT_RESET, 1);
} else if (cmd == "d") {
display = (display == 0) ? 1 : 0;
} else if (cmd == "r") {
Expand All @@ -250,6 +251,8 @@ int main(int argc, char *argv[])

auto val = ctx->read_reg(dev_idx, addr);
std::cout << "Reg. value: " << val << "\n";
} else if (cmd == "t") {
print_dev_table(dev_map);
} else if (cmd == "w") {
std::cout << "Write to a device register.\n"
<< "Enter dev_idx reg_addr reg_val\n"
Expand Down
26 changes: 15 additions & 11 deletions api/cpponi/oni.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#pragma once

// TODO
// 1. Write tests for all possible calls to get_opt and set_opt. I don't think
// get_opt currently works e.g. for device map or when a pointer option type is
// provided.

#include <algorithm>
#include <cassert>
#include <cstring>
Expand Down Expand Up @@ -46,12 +51,19 @@ namespace oni {

static_assert(ONI_VERSION >= ONI_MAKE_VERSION(4, 0, 0), "liboni is too old.");

inline std::tuple<int, int, int> version()
{
std::tuple<int, int, int> v;
oni_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v) );
return v;
}

class error_t : public std::exception
{
public:
explicit error_t(int errnum) : errnum_(errnum) {}

virtual const char *what() const noexcept
const char *what() const noexcept override
{
return oni_error_str(errnum_);
}
Expand All @@ -62,18 +74,10 @@ namespace oni {
int errnum_;
};

inline std::tuple<int, int, int> version()
{
std::tuple<int, int, int> v;
oni_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v) );
return v;
}

class context_t;

using device_t = oni_device_t;
using device_map_t = std::unordered_map<oni_dev_idx_t, device_t>;
template<typename T> using driver_arg_t = std::pair<int,T>;

// NB: Data held by frame_t is const. This means data needs to be copied
// in order to be processed. This is good from a thread safety point of
Expand All @@ -84,7 +88,6 @@ namespace oni {
{
friend class context_t; // NB: Fills frame_t::frame_ptr_;

public:
// NB: Copy and move assignment operators are going to be deleted
// since this class has const members. Copy and move ctors will
// implicitly declared.
Expand All @@ -94,6 +97,7 @@ namespace oni {
// Nothing
}

public:
uint64_t time() const { return frame_ptr_->time; }
oni_dev_idx_t device_index() const { return frame_ptr_->dev_idx; }

Expand All @@ -111,7 +115,7 @@ namespace oni {
std::vector<raw_t> data() const
{
// This should be RVOed
return std::vector(
return std::vector<raw_t>(
reinterpret_cast<raw_t *const>(frame_ptr_->data),
reinterpret_cast<raw_t * const>(frame_ptr_->data) + frame_ptr_->data_sz / sizeof(raw_t));
}
Expand Down
2 changes: 1 addition & 1 deletion api/liboni/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SRC := oni.c onix.c
POSIX_SRC := onidriverloader.c
OBJ := $(SRC:.c=.o)
POSIX_OBJ := $(POSIX_SRC:.c=.o)
CFLAGS := -Wall -W -Werror -fPIC -O3 $(DEFS)
CFLAGS := -Wall -W -Werror -fPIC -O3
ISOCFLAGS := -pedantic
LDFLAGS := -L.
PREFIX := /usr/local
Expand Down
29 changes: 29 additions & 0 deletions api/liboni/drivers/riffa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# `RIFFA` ONI Translation Layer
[RIFFA](https://github.com/KastnerRG/riffa) is an open-source kernel driver and
FPGA core that abstracts PCIe communication and allows its easy integration
into C programs. RIFFA is currently the PCIe backed used by ONIX hardware. This
ONI translation layer converts RIFFA's raw API into ONI-compatible API by
implementing all the functions in `onidriver.h`.

**NOTE** Currently, when using RIFFA, we are only supporting 64-bit architectures.

## Building the library
### Linux
```
make # Build without debug symbols
sudo make install # Install in /usr/local and run ldconfig to update library cache
make help # list all make options
```

### Windows
Run the project in Visual Studio. It can be included as a dependency in other
projects.

## Obtaining Host Device Driver
The RIFFA device driver is available in the `drivers` folder at the top level
of this repo. We have modified this slightly compared to the original driver so
you should use this. You can compile it from source, but we have also included
pre-compiled binaries that can be installed in Windows or Linux. This driver
must be installed before using the RIFFA backend.


27 changes: 27 additions & 0 deletions api/liboni/drivers/test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Test ONI Translation Layer
This is a test ONI translation layer that creates a simple device table and
generates data that is useful for testing ONI-compliant APIs. This is a minimal
implementation has the following limitations:

- Fixed size device table
- Fixed size data frame
- Block read size must be set to a multiple of the frame size
- 48 bytes, currently
- Writing frames is implemented without a visible effect: data is just ignored
- Data generation takes place on the read thread.
- Side effect: ONI_OPT_RUNNING does nothing.
- Minimal register R/W facility
- Each device has a single register at address 0 that can be read from and written to
- All else will NACK.

## Building the library
### Linux
```
make # Build without debug symbols
sudo make install # Install in /usr/local and run ldconfig to update library cache
make help # list all make options
```

### Windows
Run the project in Visual Studio. It can be included as a dependency in other
projects.
13 changes: 5 additions & 8 deletions api/liboni/drivers/test/onidriver_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ static int _send_data_signal(oni_test_ctx ctx,
// Generate frames
// Second thread puts frames into FIFO whenever it reaches XX fraction of full
int oni_driver_read_stream(oni_driver_ctx driver_ctx,
oni_read_stream_t stream,
void *data,
size_t size)
oni_read_stream_t stream,
void *data,
size_t size)
{
CTX_CAST;
int rc;
Expand Down Expand Up @@ -355,7 +355,7 @@ int oni_driver_read_config(oni_driver_ctx driver_ctx,
*value = ctx->conf.rw;
break;
case ONI_CONFIG_TRIG:
// NB: if reading register takes time, with more complex
// TODO: if reading register takes time, with more complex
// implementation, this must reflect that
*value = 0;
break;
Expand Down Expand Up @@ -394,9 +394,7 @@ int oni_driver_set_opt_callback(oni_driver_ctx driver_ctx,
if (oni_option == ONI_OPT_BLOCKREADSIZE)
{
// NB: Block size must be a multiple of a full data frame
if (*(oni_size_t *)value
% (ONI_FRAMEHEADERSZ + ctx->dev_table[0].dev.read_size)
!= 0)
if (*(oni_size_t *)value % (ONI_FRAMEHEADERSZ + ctx->dev_table[0].dev.read_size) != 0)
return ONI_EINVALARG;
else
ctx->block_read_size = *(oni_size_t *) value;
Expand Down Expand Up @@ -465,7 +463,6 @@ static void _fill_read_buffer(oni_test_ctx ctx, uint32_t *data, size_t num_words

static int _send_msg_signal(oni_test_ctx ctx, oni_signal_t type)
{
// Src and dst buffers
// COBS data, 1 overhead byte + 0x0 delimiter
uint8_t dst[sizeof(oni_signal_t) + 2] = {0};

Expand Down
Loading

0 comments on commit 3d8b2e0

Please sign in to comment.