mirror of
https://github.com/EEVengers/ThunderScope.git
synced 2025-04-08 06:25:30 +00:00
add libthunderscopehw
This commit is contained in:
parent
5a84c4d60b
commit
594ed0ead1
Software/libthunderscopehw
CMakeLists.txt
examples/thunderscopehwdump
include
library
CMakeLists.txtthunderscopehw.cthunderscopehw_adc.cthunderscopehw_internals.cthunderscopehw_linux.cthunderscopehw_pll.cthunderscopehw_simulator.cthunderscopehw_win.c
test
12
Software/libthunderscopehw/CMakeLists.txt
Normal file
12
Software/libthunderscopehw/CMakeLists.txt
Normal 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)
|
||||
|
@ -0,0 +1,4 @@
|
||||
add_executable(thunderscopehwdump
|
||||
thunderscopehwdump.c)
|
||||
target_link_libraries(thunderscopehwdump
|
||||
thunderscopehwlib)
|
@ -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;
|
||||
}
|
||||
}
|
53
Software/libthunderscopehw/include/thunderscopehw.h
Normal file
53
Software/libthunderscopehw/include/thunderscopehw.h
Normal 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
|
60
Software/libthunderscopehw/library/CMakeLists.txt
Normal file
60
Software/libthunderscopehw/library/CMakeLists.txt
Normal 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()
|
226
Software/libthunderscopehw/library/thunderscopehw.c
Normal file
226
Software/libthunderscopehw/library/thunderscopehw.c
Normal 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";
|
||||
}
|
53
Software/libthunderscopehw/library/thunderscopehw_adc.c
Normal file
53
Software/libthunderscopehw/library/thunderscopehw_adc.c
Normal 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);
|
||||
}
|
224
Software/libthunderscopehw/library/thunderscopehw_internals.c
Normal file
224
Software/libthunderscopehw/library/thunderscopehw_internals.c
Normal 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);
|
||||
}
|
84
Software/libthunderscopehw/library/thunderscopehw_linux.c
Normal file
84
Software/libthunderscopehw/library/thunderscopehw_linux.c
Normal 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__ */
|
36
Software/libthunderscopehw/library/thunderscopehw_pll.c
Normal file
36
Software/libthunderscopehw/library/thunderscopehw_pll.c
Normal 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);
|
||||
}
|
||||
|
108
Software/libthunderscopehw/library/thunderscopehw_simulator.c
Normal file
108
Software/libthunderscopehw/library/thunderscopehw_simulator.c
Normal 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;
|
||||
}
|
||||
|
167
Software/libthunderscopehw/library/thunderscopehw_win.c
Normal file
167
Software/libthunderscopehw/library/thunderscopehw_win.c
Normal 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
|
6
Software/libthunderscopehw/test/CMakeLists.txt
Normal file
6
Software/libthunderscopehw/test/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
add_executable(thunderscopehwtest thunderscopehwtest.c)
|
||||
|
||||
target_link_libraries(thunderscopehwtest
|
||||
thunderscopehwtestlib)
|
||||
|
||||
add_test(NAME TSHWT COMMAND thunderscopehwtest)
|
19
Software/libthunderscopehw/test/thunderscopehwtest.c
Normal file
19
Software/libthunderscopehw/test/thunderscopehwtest.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user