7
mirror of https://github.com/EEVengers/ThunderScope.git synced 2025-04-08 06:25:30 +00:00

add libthunderscopehw

This commit is contained in:
profezzorn 2022-01-27 17:17:59 -08:00
parent 5a84c4d60b
commit 594ed0ead1
14 changed files with 1291 additions and 0 deletions

View File

@ -0,0 +1,12 @@
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(thunderscopehwlibrary)
set (PROJECT_VERSION "1.0")
project(thunderscopehwlib VERSION ${PROJECT_VERSION})
add_subdirectory(library)
add_subdirectory(examples/thunderscopehwdump)
enable_testing()
add_subdirectory(test)

View File

@ -0,0 +1,4 @@
add_executable(thunderscopehwdump
thunderscopehwdump.c)
target_link_libraries(thunderscopehwdump
thunderscopehwlib)

View File

@ -0,0 +1,239 @@
#include "thunderscopehw.h"
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
void write32(uint64_t x, FILE* f) {
uint32_t v = 0;
if (x < 0xFFFFFFFF) v = x;
if (fwrite(&v, 1, 4, f) != 4) {
perror("fwrite");
exit(1);
}
}
int main(int argc, char** argv) {
static struct option long_options[] = {
{"device", required_argument, 0, 1 },
{"samples", required_argument, 0, 2 },
{"bw-all", required_argument, 0, 0x10 },
{"bw1", required_argument, 0, 0x11 },
{"bw2", required_argument, 0, 0x12 },
{"bw3", required_argument, 0, 0x13 },
{"bw4", required_argument, 0, 0x14 },
{"vdiv-all", required_argument, 0, 0x20 },
{"vdiv1", required_argument, 0, 0x21 },
{"vdiv2", required_argument, 0, 0x22 },
{"vdiv3", required_argument, 0, 0x23 },
{"vdiv4", required_argument, 0, 0x24 },
{"voffset-all", required_argument, 0, 0x30 },
{"voffset1", required_argument, 0, 0x31 },
{"voffset2", required_argument, 0, 0x32 },
{"voffset3", required_argument, 0, 0x33 },
{"voffset4", required_argument, 0, 0x34 },
{"ac-all", no_argument, 0, 0x40 },
{"ac1", no_argument, 0, 0x41 },
{"ac2", no_argument, 0, 0x42 },
{"ac3", no_argument, 0, 0x43 },
{"ac4", no_argument, 0, 0x44 },
{"dc-all", no_argument, 0, 0x50 },
{"ac1", no_argument, 0, 0x51 },
{"ac2", no_argument, 0, 0x52 },
{"ac3", no_argument, 0, 0x53 },
{"ac4", no_argument, 0, 0x54 },
{"enable-all", no_argument, 0, 0x60 },
{"enable1", no_argument, 0, 0x61 },
{"enable2", no_argument, 0, 0x62 },
{"enable3", no_argument, 0, 0x63 },
{"enable4", no_argument, 0, 0x64 },
{0, 0, 0, 0 }
};
uint64_t scope_id = 0;
uint64_t samples = 0;
while (1) {
switch (getopt_long(argc, argv, "", long_options, NULL)) {
case 1:
sscanf(optarg, "%" PRIx64, &scope_id);
continue;
case 2:
sscanf(optarg, "%" PRId64, &samples);
default:
continue;
case -1:
break;
}
break;
}
if (scope_id == 0) {
uint64_t scope_ids[32];
int scopes = thunderscopehw_scan(scope_ids, 32);
if (scopes == 0) {
fprintf(stderr, "No thunderscopehw hardware found.\n");
exit(1);
}
if (scopes > 1) {
fprintf(stderr, "Multiple scopes found, please select one with --device.\n");
for (int i = 0; i < scopes; i++) {
fprintf(stderr, " %0" PRIx64 "\n", scope_ids[i]);
}
exit(1);
}
scope_id = scope_ids[0];
}
struct ThunderScopeHW *ts = thunderscopehw_create();
enum ThunderScopeHWStatus ret;
ret = thunderscopehw_connect(ts, scope_id);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to connecto to thunderscope, error = %s\n", thunderscopehw_describe_error(ret));
exit(1);
}
int enabled_channels = 0;
int num_channels = 0;
optind = 1;
while (1) {
int c = getopt_long(argc, argv, "", long_options, NULL);
if (c == -1) break;
if (!(c >> 4)) continue;
for (int channel = 0; channel < 4; channel++) {
if ((c & 0xf) != channel+1 && (c & 0xf) != 0) continue;
switch (c >> 4) {
case 1: // bw
ret = thunderscopehw_bandwidth_set(ts, channel, atoi(optarg));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set bandwidth. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 2: // vdiv
ret = thunderscopehw_voltage_division_set(ts, channel, atoi(optarg));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set vdiv. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 3: // voffset
ret = thunderscopehw_voltage_offset_set(ts, channel, atoi(optarg));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set voffset. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 4: // ac
ret = thunderscopehw_ac_couple(ts, channel);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set AC coupling. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 5: // dc
ret = thunderscopehw_dc_couple(ts, channel);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set DC coupling. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
}
if ((c >> 4) == 6 || (c & 0xf)) {
ret = thunderscopehw_enable_channel(ts, channel);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to enable channel. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
if (!(enabled_channels & (1 << channel))) {
num_channels++;
enabled_channels |= 1 << channel;
}
}
}
}
if (!num_channels) {
fprintf(stderr, "No channels selected.\n");
exit(1);
}
if (samples <= 0) {
fprintf(stderr, "Must select number of samples.\n");
exit(1);
}
if (samples & 4095) {
fprintf(stderr, "Number of samples must be divisible by 4096\n");
exit(1);
}
FILE* outfile = stdout;
if (optind < argc) {
outfile = fopen(argv[optind], "wb");
if (!outfile) {
perror("open output file");
exit(1);
}
}
struct Fmt {
uint16_t pcm;
uint16_t channels;
uint32_t rate;
uint32_t byterate;
uint16_t block_align;
uint16_t bits_per_sample;
};
struct Fmt fmt;
fmt.pcm = 1;
fmt.channels = num_channels;
fmt.rate = 1000000000 / fmt.channels;
fmt.byterate = 1000000000;
fmt.block_align = 0;
fmt.bits_per_sample = 8;
fwrite("RIFF", 4, 1, outfile);
write32(4ull +
16 + 8 + /* fmt */
samples * fmt.channels + 8 /* data */, outfile);
fwrite("WAVEfmt ", 8, 1, outfile);
write32(16, outfile);
fwrite(&fmt, 16, 1, outfile);
fwrite("data", 4, 1, outfile);
write32(samples * fmt.channels, outfile);
ret = thunderscopehw_start(ts);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to start thunderscope, error=%d\n", ret);
exit(1);
}
uint8_t* buffer;
posix_memalign((void**)&buffer, 4096, 1 << 20);
while (samples) {
int64_t to_copy = samples;
if (to_copy > sizeof(buffer)) to_copy = sizeof(buffer);
ret = thunderscopehw_read(ts, buffer, sizeof(buffer));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Thunderscope read error, error = %s\n", thunderscopehw_describe_error(ret));
exit(1);
}
if (fwrite(buffer, 1, sizeof(buffer), outfile) != sizeof(buffer)) {
perror("fwrite");
exit(1);
}
samples -= to_copy;
}
}

View File

@ -0,0 +1,53 @@
#ifndef LIBTHUNDERSCOPEHW_THUNDERSCOPEHW_H
#define LIBTHUNDERSCOPEHW_THUNDERSCOPEHW_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
struct ThunderScopeHW;
enum ThunderScopeHWStatus {
THUNDERSCOPEHW_STATUS_OK = 10000,
THUNDERSCOPEHW_STATUS_BUFFER_NOT_ALIGNED,
THUNDERSCOPEHW_STATUS_MEMORY_FULL,
THUNDERSCOPEHW_STATUS_PIPELINE_OVERFLOW,
THUNDERSCOPEHW_STATUS_FIFO_OVERFLOW,
THUNDERSCOPEHW_STATUS_DATAMOVER_ERROR,
THUNDERSCOPEHW_STATUS_NO_CHANNELS,
THUNDERSCOPEHW_STATUS_INVALID_VDIV,
THUNDERSCOPEHW_STATUS_INVALID_BANDWIDTH,
THUNDERSCOPEHW_STATUS_ALREADY_CONNECTED,
THUNDERSCOPEHW_STATUS_NOT_CONNECTED,
THUNDERSCOPEHW_STATUS_OPEN_FAILED,
THUNDERSCOPEHW_STATUS_READ_ERROR,
THUNDERSCOPEHW_STATUS_WRITE_ERROR,
THUNDERSCOPEHW_STATUS_ALREADY_STARTED,
THUNDERSCOPEHW_STATUS_NOT_STARTED,
THUNDERSCOPEHW_STATUS_ALREADY_STOPPED,
};
// Return's number of scopes.
int thunderscopehw_scan(uint64_t* scope_ids, int max_ids);
struct ThunderScopeHW* thunderscopehw_create();
enum ThunderScopeHWStatus thunderscopehw_destroy(struct ThunderScopeHW* ts);
enum ThunderScopeHWStatus thunderscopehw_connect(struct ThunderScopeHW* ts, uint64_t scope_id);
enum ThunderScopeHWStatus thunderscopehw_disconnect(struct ThunderScopeHW* ts);
enum ThunderScopeHWStatus thunderscopehw_enable_channel(struct ThunderScopeHW *ts, int channel);
enum ThunderScopeHWStatus thunderscopehw_disable_channel(struct ThunderScopeHW *ts, int channel);
enum ThunderScopeHWStatus thunderscopehw_ac_couple(struct ThunderScopeHW *ts, int channel);
enum ThunderScopeHWStatus thunderscopehw_dc_couple(struct ThunderScopeHW *ts, int channel);
enum ThunderScopeHWStatus thunderscopehw_voltage_division_set(struct ThunderScopeHW *ts, int channel, int voltage_div);
enum ThunderScopeHWStatus thunderscopehw_voltage_offset_set(struct ThunderScopeHW *ts, int channel, double voltage);
enum ThunderScopeHWStatus thunderscopehw_bandwidth_set(struct ThunderScopeHW *ts, int channel, int bandwidth);
enum ThunderScopeHWStatus thunderscopehw_start(struct ThunderScopeHW* ts);
enum ThunderScopeHWStatus thunderscopehw_stop(struct ThunderScopeHW* ts);
enum ThunderScopeHWStatus thunderscopehw_read(struct ThunderScopeHW* ts, uint8_t* data, int64_t length);
const char* thunderscopehw_describe_error(enum ThunderScopeHWStatus err);
#endif // LIBTHUNDERSCOPEHW_THUNDERSCOPEHW_H

View File

@ -0,0 +1,60 @@
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
set (PROJECT_VERSION "1.0")
project(thunderscopehwlib VERSION ${PROJECT_VERSION})
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
add_library(thunderscopehwlib STATIC)
target_sources(thunderscopehwlib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw.c
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_internals.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_linux.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_win.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_adc.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_pll.c
)
target_link_libraries(thunderscopehwlib
m)
target_include_directories(thunderscopehwlib
PUBLIC ../include)
# This will name your output .so files "libsomething.1.0" which is pretty useful
set_target_properties(thunderscopehwlib
PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION}
)
add_library(thunderscopehwtestlib STATIC)
target_sources(thunderscopehwtestlib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw.c
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_internals.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_adc.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_pll.c
${CMAKE_CURRENT_SOURCE_DIR}/thunderscopehw_simulator.c
)
target_link_libraries(thunderscopehwtestlib
m)
target_include_directories(thunderscopehwtestlib
PUBLIC ../include)
# Let's set compiler-specific flags
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
# G++
target_compile_options(thunderscopehwlib PRIVATE -Wall -Wextra)
elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
# MSVC
target_compile_options(thunderscopehwlib PRIVATE /EHsc /MTd /W2 /c)
# Set the DLLEXPORT variable to export symbols
target_compile_definitions(thunderscopehwlib PRIVATE WIN_EXPORT)
endif()

View File

@ -0,0 +1,226 @@
#include <stdlib.h>
#include "thunderscopehw_private.h"
#ifdef WIN32
#else
#include <unistd.h>
#endif
struct ThunderScopeHW* thunderscopehw_create()
{
struct ThunderScopeHW* ts;
ts = (struct ThunderScopeHW*)malloc(sizeof(struct ThunderScopeHW));
if (!ts) return ts;
ts->connected = false;
ts->board_en = false;
ts->adc_en = false;
ts->pll_en = false;
ts->datamover_en = false;
ts->fpga_adc_en = false;
for (int i = 0; i < THUNDERSCOPEHW_CHANNELS; i++) {
ts->channels[i].on = false;
ts->channels[i].vdiv = 1000;
ts->channels[i].bw = 350;
ts->channels[i].voffset = 0.0;
ts->channels[i].coupling = THUNDERSCOPEHW_COUPLING_DC;
}
ts->user_handle = -1;
ts->c2h0_handle = -1;
ts->buffer_head = 0;
ts->buffer_tail = 0;
ts->ram_size_pages = 0x10000;
return ts;
}
enum ThunderScopeHWStatus thunderscopehw_destroy(struct ThunderScopeHW* ts)
{
THUNDERSCOPEHW_RUN(stop(ts));
return thunderscopehw_disconnect(ts);
}
enum ThunderScopeHWStatus thunderscopehw_enable_channel(struct ThunderScopeHW *ts, int channel)
{
ts->channels[channel].on = true;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_disable_channel(struct ThunderScopeHW *ts, int channel)
{
ts->channels[channel].on = false;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_ac_couple(struct ThunderScopeHW *ts, int channel)
{
ts->channels[channel].coupling = THUNDERSCOPEHW_COUPLING_AC;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_dc_couple(struct ThunderScopeHW *ts, int channel)
{
ts->channels[channel].coupling = THUNDERSCOPEHW_COUPLING_DC;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_voltage_division_set(struct ThunderScopeHW *ts, int channel, int voltage_div)
{
ts->channels[channel].vdiv = voltage_div;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_voltage_offset_set(struct ThunderScopeHW *ts, int channel, double voltage)
{
ts->channels[channel].voffset = voltage;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_bandwidth_set(struct ThunderScopeHW *ts, int channel, int bandwidth)
{
ts->channels[channel].bw = bandwidth;
return thunderscopehw_configure_channel(ts, channel);
}
enum ThunderScopeHWStatus thunderscopehw_start(struct ThunderScopeHW* ts) {
if (!ts->connected)
return THUNDERSCOPEHW_STATUS_NOT_CONNECTED;
if (ts->datamover_en)
return THUNDERSCOPEHW_STATUS_ALREADY_STARTED;
ts->datamover_en = true;
ts->fpga_adc_en = true;
return thunderscopehw_set_datamover_reg(ts);
}
enum ThunderScopeHWStatus thunderscopehw_stop(struct ThunderScopeHW* ts) {
if (!ts->datamover_en)
return THUNDERSCOPEHW_STATUS_ALREADY_STOPPED;
ts->datamover_en = false;
THUNDERSCOPEHW_RUN(set_datamover_reg(ts));
ts->fpga_adc_en = false;
return thunderscopehw_set_datamover_reg(ts);
}
#include <stdio.h>
static enum ThunderScopeHWStatus thunderscopehw_update_buffer_head(struct ThunderScopeHW* ts)
{
// 1 page = 4k
uint32_t transfer_counter = thunderscopehw_read32(ts, DATAMOVER_TRANSFER_COUNTER);
uint32_t error_code = transfer_counter >> 30;
if (error_code & 1)
return THUNDERSCOPEHW_STATUS_DATAMOVER_ERROR;
if (error_code & 2)
return THUNDERSCOPEHW_STATUS_FIFO_OVERFLOW;
uint32_t overflow_cycles = (transfer_counter >> 16) & 0x3FFF;
if (overflow_cycles) {
fprintf(stderr, "TRANSFER COUNTER = %llx\n", (long long)transfer_counter);
return THUNDERSCOPEHW_STATUS_PIPELINE_OVERFLOW;
}
uint32_t pages_moved = transfer_counter & 0xFFFF;
uint64_t buffer_head = (ts->buffer_head & ~0xFFFFULL) | pages_moved;
if (buffer_head < ts->buffer_head)
buffer_head += 0x10000ULL;
ts->buffer_head = buffer_head;
uint64_t pages_available = ts->buffer_head - ts->buffer_tail;
if (pages_available >= ts->ram_size_pages)
return THUNDERSCOPEHW_STATUS_MEMORY_FULL;
return THUNDERSCOPEHW_STATUS_OK;
}
enum ThunderScopeHWStatus thunderscopehw_read(struct ThunderScopeHW* ts, uint8_t* data, int64_t length)
{
if (!ts->datamover_en)
return THUNDERSCOPEHW_STATUS_NOT_STARTED;
// Buffer data must be aligned to 4096
if (0xFFF & (ptrdiff_t)data) {
return THUNDERSCOPEHW_STATUS_BUFFER_NOT_ALIGNED;
}
// Align length to 4096.
length &=~ 0xFFFULL;
THUNDERSCOPEHW_RUN(update_buffer_head(ts));
while (length) {
uint64_t pages_available = ts->buffer_head - ts->buffer_tail;
if (pages_available == 0) {
#ifdef WIN32
Sleep(1);
#else
usleep(1500);
#endif
THUNDERSCOPEHW_RUN(update_buffer_head(ts));
continue;
}
uint64_t pages_to_read = length >> 12;
if (pages_to_read > pages_available) pages_to_read = pages_available;
uint64_t buffer_read_pos = ts->buffer_tail % ts->ram_size_pages;
if (pages_to_read > ts->ram_size_pages - buffer_read_pos) pages_to_read = ts->ram_size_pages - buffer_read_pos;
if (pages_to_read > ts->ram_size_pages / 4) pages_to_read = ts->ram_size_pages / 4;
thunderscopehw_read_handle(ts, ts->user_handle, data, buffer_read_pos << 12, pages_to_read << 12);
data += pages_to_read << 12;
length -= pages_to_read << 12;
// Update buffer head and calculate overflow BEFORE
// updating buffer tail as it is possible
// that a buffer overflow occured while we were reading.
THUNDERSCOPEHW_RUN(update_buffer_head(ts));
ts->buffer_tail += pages_to_read;
}
return THUNDERSCOPEHW_STATUS_OK;
}
const char* thunderscopehw_describe_error(enum ThunderScopeHWStatus err) {
switch (err) {
case THUNDERSCOPEHW_STATUS_OK:
return "ok";
case THUNDERSCOPEHW_STATUS_BUFFER_NOT_ALIGNED:
return "buffer not aligned";
case THUNDERSCOPEHW_STATUS_MEMORY_FULL:
return "memory full";
case THUNDERSCOPEHW_STATUS_PIPELINE_OVERFLOW:
return "pipeline overflow";
case THUNDERSCOPEHW_STATUS_FIFO_OVERFLOW:
return "fifo overflow";
case THUNDERSCOPEHW_STATUS_DATAMOVER_ERROR:
return "datamover error";
case THUNDERSCOPEHW_STATUS_NO_CHANNELS:
return "no channels";
case THUNDERSCOPEHW_STATUS_INVALID_VDIV:
return "invalid vdiv";
case THUNDERSCOPEHW_STATUS_INVALID_BANDWIDTH:
return "invalid bandwidth";
case THUNDERSCOPEHW_STATUS_ALREADY_CONNECTED:
return "already connected";
case THUNDERSCOPEHW_STATUS_NOT_CONNECTED:
return "not connect";
case THUNDERSCOPEHW_STATUS_OPEN_FAILED:
return "open failed";
case THUNDERSCOPEHW_STATUS_READ_ERROR:
return "read error";
case THUNDERSCOPEHW_STATUS_WRITE_ERROR:
return "write error";
case THUNDERSCOPEHW_STATUS_ALREADY_STARTED:
return "already started";
case THUNDERSCOPEHW_STATUS_NOT_STARTED:
return "not started";
case THUNDERSCOPEHW_STATUS_ALREADY_STOPPED:
return "already stopped";
}
return "unkonwn error";
}

View File

@ -0,0 +1,53 @@
#include "thunderscopehw_private.h"
enum ThunderScopeHWStatus thunderscopehw_adc_set_reg(struct ThunderScopeHW* ts, enum ThunderScopeHWAdcRegister reg, uint16_t value)
{
uint8_t fifo[4];
fifo[0] = SPI_BYTE_ADC;
fifo[1] = reg;
fifo[2] = value >> 8;
fifo[3] = value & 0xff;
return thunderscopehw_fifo_write(ts, fifo, 4);
}
enum ThunderScopeHWStatus thunderscopehw_adc_power(struct ThunderScopeHW* ts, bool on)
{
return thunderscopehw_adc_set_reg(ts,
THUNDERSCOPEHW_ADC_REG_POWER,
on ? 0x0200 : 0x0000);
}
enum ThunderScopeHWStatus thunderscopehw_configure_adc(struct ThunderScopeHW* ts)
{
//Reset ADC
THUNDERSCOPEHW_RUN(adc_set_reg(ts, THUNDERSCOPEHW_ADC_REG_RESET, 0x0001));
// Power Down ADC
THUNDERSCOPEHW_RUN(adc_power(ts, false));
// invert channels
THUNDERSCOPEHW_RUN(adc_set_reg(ts, THUNDERSCOPEHW_ADC_REG_INVERT, 0x0074));
// Adjust full scale value
THUNDERSCOPEHW_RUN(adc_set_reg(ts, THUNDERSCOPEHW_ADC_REG_FS_CNTRL, 0x0010));
// Course Gain On
THUNDERSCOPEHW_RUN(adc_set_reg(ts, THUNDERSCOPEHW_ADC_REG_GAIN_CFG, 0x000));
// Course Gain 4-CH set
THUNDERSCOPEHW_RUN(adc_set_reg(ts, THUNDERSCOPEHW_ADC_REG_QUAD_GAIN, 0x9999));
// Course Gain 1-CH & 2-CH set
THUNDERSCOPEHW_RUN(adc_set_reg(ts, THUNDERSCOPEHW_ADC_REG_DUAL_GAIN, 0x0A99));
//Set adc into active mode
//currentBoardState.num_ch_on++;
//currentBoardState.ch_is_on[0] = true;
//_FIFO_WRITE(user_handle,currentBoardState.adc_chnum_clkdiv,sizeof(currentBoardState.adc_chnum_clkdiv));
THUNDERSCOPEHW_RUN(adc_power(ts, true));
//_FIFO_WRITE(user_handle,currentBoardState.adc_in_sel_12,sizeof(currentBoardState.adc_in_sel_12));
//_FIFO_WRITE(user_handle,currentBoardState.adc_in_sel_34,sizeof(currentBoardState.adc_in_sel_34));
ts->fe_en = true;
return thunderscopehw_set_datamover_reg(ts);
}

View File

@ -0,0 +1,224 @@
#include "thunderscopehw_private.h"
#include <math.h>
#ifdef WIN32
#else
#include <unistd.h>
#endif
enum ThunderScopeHWStatus thunderscopehw_initboard(struct ThunderScopeHW* ts)
{
THUNDERSCOPEHW_RUN(write32(ts, DATAMOVER_REG_OUT, 0));
ts->board_en = true;
THUNDERSCOPEHW_RUN(set_datamover_reg(ts));
THUNDERSCOPEHW_RUN(configure_pll(ts));
return thunderscopehw_configure_adc(ts);
}
enum ThunderScopeHWStatus thunderscopehw_fifo_write(struct ThunderScopeHW* ts, uint8_t* data, size_t bytes)
{
// reset ISR
THUNDERSCOPEHW_RUN(write32(ts, SERIAL_FIFO_ISR_ADDRESS, 0xFFFFFFFFU));
// read ISR and IER
thunderscopehw_read32(ts, SERIAL_FIFO_ISR_ADDRESS);
thunderscopehw_read32(ts, SERIAL_FIFO_IER_ADDRESS);
// enable IER
THUNDERSCOPEHW_RUN(write32(ts, SERIAL_FIFO_IER_ADDRESS, 0x0C000000U));
// Set false TDR
THUNDERSCOPEHW_RUN(write32(ts, SERIAL_FIFO_TDR_ADDRESS, 0x2U));
// Put data into queue
for(size_t i = 0; i < bytes; i++) {
// TODO: Replace with write32
THUNDERSCOPEHW_RUN(write_handle(ts, ts->user_handle, data + i, SERIAL_FIFO_DATA_WRITE_REG, 1));
}
// read TDFV (vacancy byte)
thunderscopehw_read32(ts, SERIAL_FIFO_TDFV_ADDRESS);
// write to TLR (the size of the packet)
thunderscopehw_write32(ts, SERIAL_FIFO_TLR_ADDRESS, bytes * 4);
// read ISR for a done value
while ((thunderscopehw_read32(ts, SERIAL_FIFO_ISR_ADDRESS) & 0xff) != 8) {
#ifdef WIN32
Sleep(1);
#else
usleep(1500);
#endif
}
// reset ISR
THUNDERSCOPEHW_RUN(write32(ts, SERIAL_FIFO_ISR_ADDRESS, 0xFFFFFFFFU));
// read TDFV
thunderscopehw_read32(ts, SERIAL_FIFO_TDFV_ADDRESS);
return THUNDERSCOPEHW_STATUS_OK;
}
enum ThunderScopeHWStatus thunderscopehw_set_datamover_reg(struct ThunderScopeHW* ts)
{
uint32_t datamover_reg = 0;
int num_channels_on = 0;
if (ts->board_en) datamover_reg |= 0x01000000;
if (ts->pll_en) datamover_reg |= 0x02000000;
if (ts->fe_en) datamover_reg |= 0x04000000;
if (ts->datamover_en) datamover_reg |= 0x1;
if (ts->fpga_adc_en) datamover_reg |= 0x2;
#if 1
for (int channel = 0; channel < 4; channel++) {
if (ts->channels[channel].on == true) {
num_channels_on++;
}
if (ts->channels[channel].vdiv > 100) {
datamover_reg |= 1 << (16 + channel);
}
if (ts->channels[channel].coupling == THUNDERSCOPEHW_COUPLING_DC) {
datamover_reg |= 1 << (20 + channel);
}
}
#endif
switch (num_channels_on) {
case 0:
case 1: break; // do nothing
case 2: datamover_reg |= 0x10; break;
default: datamover_reg |= 0x30; break;
}
// write to datamover reg
return thunderscopehw_write32(ts, DATAMOVER_REG_OUT, datamover_reg);
}
// PGA LMH6518
enum ThunderScopeHWStatus thunderscopehw_set_pga(struct ThunderScopeHW* ts, int channel)
{
uint8_t fifo[4];
fifo[0] = 0xFB - channel; // SPI chip enable
fifo[1] = 0;
fifo[2] = 0x04; // ??
int vdiv = ts->channels[channel].vdiv;
if (vdiv > 100) {
// Attenuator relay on, handled by
// thunderscopehw_set_datamover_reg.
vdiv /= 100;
}
switch (vdiv) {
case 100: fifo[3] = 0x0A; break;
case 50: fifo[3] = 0x07; break;
case 20: fifo[3] = 0x03; break;
case 10: fifo[3] = 0x1A; break;
case 5: fifo[3] = 0x17; break;
case 2: fifo[3] = 0x13; break;
case 1: fifo[3] = 0x10; break;
default: return THUNDERSCOPEHW_STATUS_INVALID_VDIV;
}
switch (ts->channels[channel].bw) {
case 20: fifo[3] |= 0x40; break;
case 100: fifo[3] |= 0x80; break;
case 200: fifo[3] |= 0xC0; break;
case 350: /* 0 */ break;
default: return THUNDERSCOPEHW_STATUS_INVALID_BANDWIDTH;
}
return thunderscopehw_fifo_write(ts, fifo, 4);
}
enum ThunderScopeHWStatus thunderscopehw_set_dac(struct ThunderScopeHW* ts, int channel)
{
// value is 12-bit
// Is this right?? Or is it rounding wrong?
unsigned int dac_value = (unsigned int)round((ts->channels[channel].voffset + 0.5) * 4095);
uint8_t fifo[5];
fifo[0] = 0xFF; // I2C
fifo[1] = 0xC2; // DAC?
fifo[2] = 0x40 + (channel << 1);
fifo[3] = ((dac_value >> 8) & 0xF);
fifo[4] = dac_value & 0xFF;
return thunderscopehw_fifo_write(ts, fifo, 5);
}
enum ThunderScopeHWStatus thunderscopehw_configure_channels(struct ThunderScopeHW* ts)
{
uint8_t on_channels[4];
int i;
int num_channels_on = 0;
uint8_t clkdiv = 0;
for (i = 0; i < 4; i++) {
if (ts->channels[i].on) {
on_channels[num_channels_on++] = i;
}
}
switch (num_channels_on) {
case 0:
return false;
case 1:
on_channels[2] = on_channels[3] = on_channels[4] = on_channels[0];
clkdiv = 0;
break;
case 2:
on_channels[2] = on_channels[3] = on_channels[1];
on_channels[1] = on_channels[0];
clkdiv = 1;
break;
default:
on_channels[0] = 0;
on_channels[1] = 1;
on_channels[2] = 2;
on_channels[3] = 3;
num_channels_on = 4;
clkdiv = 2;
break;
}
THUNDERSCOPEHW_RUN(adc_power(ts, false));
THUNDERSCOPEHW_RUN(adc_set_reg(ts,
THUNDERSCOPEHW_ADC_REG_CHNUM_CLKDIV,
(clkdiv << 8) | num_channels_on));
THUNDERSCOPEHW_RUN(adc_power(ts, true));
THUNDERSCOPEHW_RUN(adc_set_reg(ts,
THUNDERSCOPEHW_ADC_REG_INSEL12,
(2 << on_channels[0]) | (512 << on_channels[1])));
THUNDERSCOPEHW_RUN(adc_set_reg(ts,
THUNDERSCOPEHW_ADC_REG_INSEL34,
(2 << on_channels[2]) | (512 << on_channels[3])));
struct ThunderScopeHW tmp = *ts;
tmp.datamover_en = false;
tmp.fpga_adc_en = false;
THUNDERSCOPEHW_RUN(set_datamover_reg(&tmp));
#ifdef WIN32
Sleep(5);
#else
usleep(5000);
#endif
return thunderscopehw_set_datamover_reg(ts);
}
enum ThunderScopeHWStatus thunderscopehw_configure_channel(struct ThunderScopeHW* ts, int channel)
{
THUNDERSCOPEHW_RUN(configure_channels(ts));
THUNDERSCOPEHW_RUN(set_dac(ts, channel));
THUNDERSCOPEHW_RUN(set_datamover_reg(ts));
return thunderscopehw_set_pga(ts, channel);
}
uint32_t thunderscopehw_read32(struct ThunderScopeHW* ts, size_t addr)
{
uint8_t bytes[4];
thunderscopehw_read_handle(ts, ts->user_handle, bytes, addr, 4);
return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
}
enum ThunderScopeHWStatus thunderscopehw_write32(struct ThunderScopeHW* ts, size_t addr, uint32_t value)
{
uint8_t bytes[4];
bytes[3] = value >> 24;
bytes[2] = value >> 16;
bytes[1] = value >> 8;
bytes[0] = value;
return thunderscopehw_write_handle(ts, ts->user_handle, bytes, addr, 4);
}

View File

@ -0,0 +1,84 @@
#include "thunderscopehw_private.h"
#ifdef __linux__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int thunderscopehw_scan(uint64_t* scope_ids, int max_ids)
{
struct dirent* entry;
int num_scopes = 0;
DIR* dir = opendir("/dev");
if (!dir) return 0;
while ((entry = readdir(dir))) {
if (strncmp(entry->d_name, "xdma", 4)) continue;
if (strcmp(entry->d_name + strlen(entry->d_name) - 4, "user")) continue;
int n = atoi(entry->d_name + 4);
// TODO: find scope serial number and return that.
if (num_scopes < max_ids) scope_ids[num_scopes] = n;
num_scopes++;
}
return num_scopes;
}
static int thunderscopehw_open_helper(int n, const char* postfix)
{
char device_path[128];
snprintf(device_path, sizeof(device_path), "/dev/xdma%d_%s", n, postfix);
int fd = open(device_path, O_RDWR);
if (fd < 0) {
perror("open");
fprintf(stderr, "failed to open: %s, errno=%d\n", device_path, errno);
}
return fd;
}
enum ThunderScopeHWStatus thunderscopehw_connect(struct ThunderScopeHW* ts, uint64_t scope_id)
{
if (ts->connected) return THUNDERSCOPEHW_STATUS_ALREADY_CONNECTED;
ts->user_handle = thunderscopehw_open_helper(scope_id, USER_DEVICE_PATH);
if (ts->user_handle <= 0) return THUNDERSCOPEHW_STATUS_OPEN_FAILED;
ts->c2h0_handle = thunderscopehw_open_helper(scope_id, C2H_0_DEVICE_PATH);
if (ts->c2h0_handle <= 0) return THUNDERSCOPEHW_STATUS_OPEN_FAILED;
ts->connected = true;
return thunderscopehw_initboard(ts);
}
enum ThunderScopeHWStatus thunderscopehw_disconnect(struct ThunderScopeHW* ts)
{
if (!ts->connected) return THUNDERSCOPEHW_STATUS_NOT_CONNECTED;
close(ts->user_handle);
close(ts->c2h0_handle);
ts->connected = false;
return THUNDERSCOPEHW_STATUS_OK;
}
enum ThunderScopeHWStatus thunderscopehw_read_handle(struct ThunderScopeHW* ts, THUNDERSCOPEHW_FILE_HANDLE h, uint8_t* data, uint64_t addr, int64_t bytes)
{
(void)ts;
ssize_t ret = pread(h, data, bytes, addr);
if (ret != bytes) {
perror("pread");
return THUNDERSCOPEHW_STATUS_READ_ERROR;
}
return THUNDERSCOPEHW_STATUS_OK;
}
enum ThunderScopeHWStatus thunderscopehw_write_handle(struct ThunderScopeHW* ts, THUNDERSCOPEHW_FILE_HANDLE h, uint8_t* data, uint64_t addr, int64_t bytes)
{
(void)ts;
ssize_t ret = pwrite(h, data, bytes, addr);
if (ret != bytes) {
perror("pwrite");
return THUNDERSCOPEHW_STATUS_WRITE_ERROR;
}
return THUNDERSCOPEHW_STATUS_OK;
}
#endif /* __linux__ */

View File

@ -0,0 +1,36 @@
#include "thunderscopehw_private.h"
enum ThunderScopeHWStatus thunderscopehw_set_pll_reg(struct ThunderScopeHW* ts, uint8_t reg, uint8_t value)
{
uint8_t fifo[4];
fifo[0] = I2C_BYTE_PLL;
fifo[1] = CLOCK_GEN_I2C_ADDRESS_WRITE;
fifo[2] = reg;
fifo[3] = value;
return thunderscopehw_fifo_write(ts, fifo, 4);
}
enum ThunderScopeHWStatus thunderscopehw_configure_pll(struct ThunderScopeHW* ts)
{
// These were providedd by the chip configuration tool.
const uint16_t config_clk_gen[] = {
0x0010, 0x010B, 0x0233, 0x08B0,
0x0901, 0x1000, 0x1180, 0x1501,
0x1600, 0x1705, 0x1900, 0x1A32,
0x1B00, 0x1C00, 0x1D00, 0x1E00,
0x1F00, 0x2001, 0x210C, 0x2228,
0x2303, 0x2408, 0x2500, 0x2600,
0x2700, 0x2F00, 0x3000, 0x3110,
0x3200, 0x3300, 0x3400, 0x3500,
0x3800, 0x4802 };
// write to the clock generator
for (size_t i = 0; i < sizeof(config_clk_gen) / sizeof(config_clk_gen[0]); i++) {
THUNDERSCOPEHW_RUN(set_pll_reg(ts,
config_clk_gen[i] >> 8,
config_clk_gen[i] & 0xff));
}
ts->pll_en = true;
return thunderscopehw_set_datamover_reg(ts);
}

View File

@ -0,0 +1,108 @@
#include "thunderscopehw_private.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int thunderscopehw_scan(uint64_t* scope_ids, int max_ids)
{
if (max_ids > 0)
*scope_ids = 0;
return 1;
}
enum ThunderScopeHWStatus thunderscopehw_connect(struct ThunderScopeHW* ts, uint64_t scope_id)
{
if (ts->connected) return THUNDERSCOPEHW_STATUS_ALREADY_CONNECTED;
ts->user_handle = 101;
ts->c2h0_handle = 102;
ts->connected = true;
return thunderscopehw_initboard(ts);
}
enum ThunderScopeHWStatus thunderscopehw_disconnect(struct ThunderScopeHW* ts)
{
if (!ts->connected) return THUNDERSCOPEHW_STATUS_NOT_CONNECTED;
ts->user_handle = -1;
ts->c2h0_handle = -1;
ts->connected = false;
return THUNDERSCOPEHW_STATUS_OK;
}
uint8_t thunderscopehw_simulator_fifo[256];
uint8_t thunderscopehw_simulator_fifo_length = 0;
enum ThunderScopeHWStatus thunderscopehw_read_handle(struct ThunderScopeHW* ts, THUNDERSCOPEHW_FILE_HANDLE h, uint8_t* data, uint64_t addr, int64_t bytes)
{
(void)ts;
printf("READ %lld from %s at 0x%06llx :",
(long long)bytes,
h == 101 ? "USER" : h == 102 ? "DMA" : "UNKNOWN",
(long long)addr);
memset(data, 0, bytes);
switch (addr) {
case SERIAL_FIFO_ISR_ADDRESS:
if (thunderscopehw_simulator_fifo_length) {
data[3] = 0;
thunderscopehw_simulator_fifo_length--;
} else {
data[3] = 8;
}
break;
}
for (int i = 0; i < bytes; i++) {
printf(" 0x%02x", data[i]);
}
printf("\n");
return THUNDERSCOPEHW_STATUS_OK;
}
enum ThunderScopeHWStatus thunderscopehw_write_handle(struct ThunderScopeHW* ts, THUNDERSCOPEHW_FILE_HANDLE h, uint8_t* data, uint64_t addr, int64_t bytes)
{
(void)ts;
printf("WRITE %lld to %s at 0x%06llx :",
(long long)bytes,
h == 101 ? "USER" : h == 102 ? "DMA" : "UNKNOWN",
(long long)addr);
for (int i = 0; i < bytes; i++) {
printf(" 0x%02x", data[i]);
}
printf("\n");
uint32_t tmp;
if (bytes == 4) {
tmp = (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0];
}
switch (addr) {
case 0x20010: // FIFO write
if (bytes == 1) {
thunderscopehw_simulator_fifo[thunderscopehw_simulator_fifo_length++] = *data;
return THUNDERSCOPEHW_STATUS_OK;
}
case SERIAL_FIFO_TLR_ADDRESS:
if (bytes == 4) {
#if 0
printf("FIFO SEND:");
for (int i = 0; i < thunderscopehw_simulator_fifo_length; i++) {
printf(" 0x%02x", thunderscopehw_simulator_fifo[i]);
}
printf("\n");
#endif
if (thunderscopehw_simulator_fifo_length != tmp / 4) {
printf("Wrong fifo length: %u / 4 != %d\n",
tmp,
thunderscopehw_simulator_fifo_length);
exit(1);
}
return THUNDERSCOPEHW_STATUS_OK;
}
}
return THUNDERSCOPEHW_STATUS_OK;
}

View File

@ -0,0 +1,167 @@
#include "thunderscopehw_private.h"
#ifdef WIN32
typedef void (*thunderscopehw_iterate_device_callback)(PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail, void* context);
int thunderscopehw_iterate_devices(thunderscopehw_iterate_device_calback cb, void* context)
{
GUID guid = GUID_DEVINTERACE_XDMA;
HDEVINFO device_info = SetupDiGetClassDevs((LPGUID)&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (device_info == INVALID_HANDLE_VALUE) {
fprintf(stderr, "GetDevices INVALID_HANDLE_VALUE\n");
return 0;
}
SP_DEVICE_INTERFACE_DATA device_interface;
device_interface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// enumerate through devices
DWORD index;
for (index = 0; SetupDiEnumDeviceInterfaces(device_info, NULL, &guid, index, &device_interface); ++index) {
// get required buffer size
ULONG detailLength = 0;
if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_interface, NULL, 0, &detailLength, NULL) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
fprintf(stderr, "SetupDiGetDeviceInterfaceDetail - get length failed\n");
break;
}
// allocate space for device interface detail
PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, detailLength);
if (!dev_detail) {
fprintf(stderr, "HeapAlloc failed\n");
break;
}
dev_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// get device interface detail
if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_interface, dev_detail, detailLength, NULL, NULL)) {
fprintf(stderr, "SetupDiGetDeviceInterfaceDetail - get detail failed\n");
HeapFree(GetProcessHeap(), 0, dev_detail);
break;
}
(*cb)(dev_detail);
HeapFree(GetProcessHeap(), 0, dev_detail);
}
SetupDiDestroyDeviceInfoList(device_info);
}
struct thunderscopehw_scan_callback_context {
uint64_t* scope_ids;
int max_ids;
int count;
};
static int thunderscopehw_scan_callback(PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail, void* context) {
struct thunderscopehw_scan_callback_context* data = context;
if (data->count < data->max_ids) {
*(data->scope_ids) = data->count;
}
data->count++;
}
int thunderscopehw_scan(uint64_t* scope_ids, int max_ids)
{
struct thunderscopehw_scan_callback_context context;
context.scope_ids = scope_ids;
context.max_ids = max_ids;
context.count = 0;
thunderscopehw_iterate_devices(&thunderscopehw_scan_callback, (void*)&context);
return data->count;
}
struct thunderscopehw_connect_callback_context {
uint64_t scope_id;
ThunderScopeHW* ts;
int count;
};
static int thunderscopehw_connect_callback(PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail, void* context) {
struct thunderscopehw_connect_callback_context* data = context;
if (data->count == data->scope_id) {
char connection_string[256];
sprintf(connection_string,"%s\\%s",dev_detail->DevicePath, user_device);
data->ts->user_handle = CreateFile(connection_string, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (data->ts->user_handle == INVALID_HANDLE_VALUE)
fprintf(stderr, "Failed to open user handle, win32 error code: %d\n", GetLastError());
sprintf(connection_string,"%s\\%s",dev_detail->DevicePath, c2h_0_device);
data->ts->c2h_0_handle = CreateFile(connection_string, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (data->ts->c2h_0_handle == INVALID_HANDLE_VALUE)
fprintf(stderr, "Failed to open c2h_0 handle, win32 error code: %d\n", GetLastError());
if (data->ts->c2h_0_handle != INVALID_HANDLE_VALUE &&
data->ts->user_handle != INVALID_HANDLE_VALUE) {
data->ts->connected = true;
}
}
data->count++;
}
int thunderscopehw_connect(struct ThunderScopeHW* ts, uint64_t scope_id)
{
struct thunderscopehw_connect_callback_context context;
context.ts = ts;
context.scope_id = scope_id;
context.count = 0;
thunderscopehw_iterate_devices(&thunderscopehw_connect_callback, (void*)&context);
if (!ts->connected)
return THUNDERSCOPEHW_STATUS_OPEN_FAILED;
return thunderscopehw_initboard(ts);
}
int thunderscopehw_disconnect(struct ThunderScopeHW* ts)
{
// adc power down
// datamover reg = 0
if (ts->user_handle != INVALID_USER_HANDLE) {
CloseHandle(ts->user_handle);
ts->user_handle = INVALID_USER_HANDLE;
}
if (ts->c2h_0_handle != INVALID_USER_HANDLE) {
CloseHandle(ts->c2h_0_handle);
ts->c2h_0_handle = INVALID_USER_HANDLE;
}
ts->connected = false;
}
int thunderscopehw_read_handle(struct ThunderScopeHW* ts, HANDLE h, uint8_t* data, uint64_t addr, uint64_t bytes)
{
LARGE_INTEGER offset;
offset.QuadPart = addr;
// set file pointer to offset of target address within PCIe BAR
if (INVALID_SET_FILE_POINTER == SetFilePointerEx(hPCIE, offset, NULL, FILE_BEGIN)) {
fprintf(stderr, "Error setting file pointer for PCIeLink::_Read(), win32 error code: %d\n", GetLastError());
return false;
}
// read from device into buffer
DWORD bytesRead;
if (!ReadFile(hPCIE, buff, bytes, &bytesRead, NULL)) {
fprintf(stderr, "read handle failed, win32 error code: %d\n", GetLastError());
return false;
}
return true;
}
int thunderscopehw_write_handle(struct ThunderScopeHW* ts, HANDLE h, uint8_t* data, uint64_t addr, uint64_t bytes)
{
LARGE_INTEGER offset;
offset.QuadPart = address;
// set file pointer to offset of target address within PCIe BAR
if (INVALID_SET_FILE_POINTER == SetFilePointerEx(hPCIE, offset, NULL, FILE_BEGIN)) {
fprintf(stderr, "Error setting file pointer for PCIeLink::_Read(), win32 error code: %d\n", GetLastError());
return false;
}
// write from buffer to device
DWORD bytesWritten;
if (!WriteFile(hPCIE, buff, bytesToWrite, &bytesWritten, NULL)) {
fprintf(stderr, "write handle failed, win32 error code: %d\n", GetLastError());
return false;
}
return true;
}
#endif

View File

@ -0,0 +1,6 @@
add_executable(thunderscopehwtest thunderscopehwtest.c)
target_link_libraries(thunderscopehwtest
thunderscopehwtestlib)
add_test(NAME TSHWT COMMAND thunderscopehwtest)

View File

@ -0,0 +1,19 @@
#include "thunderscopehw.h"
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main(int argc, char** argv) {
uint64_t ids[1];
thunderscopehw_scan(ids, 1);
struct ThunderScopeHW *ts = thunderscopehw_create();
thunderscopehw_connect(ts, ids[0]);
thunderscopehw_enable_channel(ts, 0);
thunderscopehw_start(ts);
uint8_t* buffer;
posix_memalign((void**)&buffer, 4096, 1 << 20);
thunderscopehw_read(ts, buffer, 1 << 20);
}