mirror of
https://github.com/EEVengers/ThunderScope.git
synced 2025-04-22 17:43:44 +00:00
Merge pull request #225 from profezzorn/libthunderscopehw
Libthunderscopehw
This commit is contained in:
commit
f9008ae100
.gitignore
Software/libthunderscope
.gitignoreCMakeLists.txt
examples
thunderscopehwbench
thunderscopehwcalibrate
thunderscopehwcompensate
thunderscopehwdump
thunderscopehwstresstest
include
test
thunderscopehw
3
.gitignore
vendored
3
.gitignore
vendored
@ -32,3 +32,6 @@ Software/waveview/CMakeCache.txt
|
||||
# testing files
|
||||
|
||||
Software/waveview/dump.csv
|
||||
*.c~
|
||||
*.txt~
|
||||
*.h~
|
||||
|
2
Software/libthunderscope/.gitignore
vendored
Normal file
2
Software/libthunderscope/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build
|
||||
*~
|
16
Software/libthunderscope/CMakeLists.txt
Normal file
16
Software/libthunderscope/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
# CMakeLists.txt
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(thunderscopelibrary)
|
||||
set (PROJECT_VERSION "1.0")
|
||||
project(thunderscopelib VERSION ${PROJECT_VERSION})
|
||||
|
||||
add_subdirectory(thunderscopehw)
|
||||
add_subdirectory(examples/thunderscopehwdump)
|
||||
add_subdirectory(examples/thunderscopehwcalibrate)
|
||||
add_subdirectory(examples/thunderscopehwcompensate)
|
||||
add_subdirectory(examples/thunderscopehwbench)
|
||||
add_subdirectory(examples/thunderscopehwstresstest)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
|
@ -0,0 +1,4 @@
|
||||
add_executable(thunderscopehwbench
|
||||
thunderscopehwbench.c)
|
||||
target_link_libraries(thunderscopehwbench
|
||||
thunderscopehwlib)
|
@ -0,0 +1,159 @@
|
||||
#include "thunderscopehw.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
|
||||
static uint64_t time_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (timespec_get(&ts, TIME_UTC) != TIME_UTC)
|
||||
{
|
||||
fprintf(stderr, "timespec_get failed!");
|
||||
exit(1);
|
||||
}
|
||||
return 1000000000 * ts.tv_sec + ts.tv_nsec;
|
||||
}
|
||||
|
||||
struct Option {
|
||||
const char* name;
|
||||
bool needs_argument;
|
||||
int return_value;
|
||||
};
|
||||
|
||||
struct Option options[] = {
|
||||
{"device", true, 1 },
|
||||
{"verbose", false, 2 },
|
||||
{"repeat", true, 3 },
|
||||
{"help", false, 4 },
|
||||
};
|
||||
|
||||
#define TS_RUN(X) do { \
|
||||
enum ThunderScopeHWStatus ret = thunderscopehw_##X; \
|
||||
if (ret != THUNDERSCOPEHW_STATUS_OK) { \
|
||||
fprintf(stderr, "thunderscopehw_%s failed @ line %d, error = %s\n", #X, __LINE__, thunderscopehw_describe_error(ret)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void usage() {
|
||||
printf("thunderscopehwbench [options]\n"
|
||||
" --device=<deviceid>\n"
|
||||
" --verbose\n"
|
||||
" --repeat=<repetitions>\n");
|
||||
}
|
||||
|
||||
char* optarg;
|
||||
int optind = 1;
|
||||
int mygetopt(int argc, char** argv) {
|
||||
if (optind >= argc) return -1;
|
||||
if (!argv[optind][0] == '-' || argv[optind][1] != '-') return -1;
|
||||
char *arg = strchr(argv[optind], '=');
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||
size_t len = strlen(options[i].name);
|
||||
if (strncmp(options[i].name, argv[optind] + 2, len)) continue;
|
||||
if (options[i].needs_argument) {
|
||||
if (!arg) continue;
|
||||
if (argv[optind] + 2 + len != arg) continue;
|
||||
optarg = arg + 1;
|
||||
} else {
|
||||
if (arg) continue;
|
||||
if (argv[optind][2 + len]) continue;
|
||||
optarg = NULL;
|
||||
}
|
||||
optind++;
|
||||
return options[i].return_value;
|
||||
}
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[optind]);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int verbose = 0;
|
||||
int repeat = 1;
|
||||
uint64_t scope_id = 0;
|
||||
uint64_t samples = 0;
|
||||
int samplerate = 0;
|
||||
while (1) {
|
||||
switch (mygetopt(argc, argv)) {
|
||||
case 1:
|
||||
if (!sscanf(optarg, "%" PRIx64, &scope_id)) {
|
||||
fprintf(stderr, "Scope ID must be hexadecimal.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 2:
|
||||
verbose++;
|
||||
continue;
|
||||
case 3:
|
||||
if (!sscanf(optarg, "%d", &repeat)) {
|
||||
fprintf(stderr, "--repeat needs a number.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 4:
|
||||
usage();
|
||||
exit(1);
|
||||
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];
|
||||
}
|
||||
|
||||
// 32Mb
|
||||
#define BUFFER_SIZE (32<<20)
|
||||
uint8_t* buffer;
|
||||
#ifdef _WIN32
|
||||
buffer = _aligned_malloc(BUFFER_SIZE, 4096);
|
||||
#else
|
||||
posix_memalign((void**)&buffer, 4096, BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
struct ThunderScopeHW *ts = thunderscopehw_create();
|
||||
enum ThunderScopeHWStatus ret;
|
||||
TS_RUN(connect(ts, scope_id));
|
||||
|
||||
|
||||
TS_RUN(enable_channel(ts, 0));
|
||||
TS_RUN(start(ts));
|
||||
for (int i = 0; i < repeat; i++) {
|
||||
uint64_t start = time_ns();
|
||||
uint64_t bytes = 0;
|
||||
for (int j = 0; j < (1<<30) / BUFFER_SIZE; j++) {
|
||||
TS_RUN(read(ts, buffer, BUFFER_SIZE));
|
||||
bytes += BUFFER_SIZE;
|
||||
}
|
||||
uint64_t end = time_ns();
|
||||
printf("Rate = %f Mib/s\n", bytes / 1000.0 / 1000.0 * 1000000000.0 / (end - start));
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
add_executable(thunderscopehwcalibrate
|
||||
thunderscopehwcalibrate.c)
|
||||
target_link_libraries(thunderscopehwcalibrate
|
||||
thunderscopehwlib)
|
@ -0,0 +1,184 @@
|
||||
#include "thunderscopehw.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
struct Option {
|
||||
const char* name;
|
||||
bool needs_argument;
|
||||
int return_value;
|
||||
};
|
||||
|
||||
struct Option options[] = {
|
||||
{"device", true, 1 },
|
||||
{"verbose", false, 2 },
|
||||
{"repeat", true, 3 },
|
||||
{"help", false, 4 },
|
||||
};
|
||||
|
||||
#define TS_RUN(X) do { \
|
||||
enum ThunderScopeHWStatus ret = thunderscopehw_##X; \
|
||||
if (ret != THUNDERSCOPEHW_STATUS_OK) { \
|
||||
fprintf(stderr, "thunderscopehw_%s failed @ line %d, error = %s\n", #X, __LINE__, thunderscopehw_describe_error(ret)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void usage() {
|
||||
printf("thunderscopehwcalibrate [options]\n"
|
||||
" --device=<deviceid>\n"
|
||||
" --verbose\n"
|
||||
" --repeat=<repetitions>\n");
|
||||
}
|
||||
|
||||
char* optarg;
|
||||
int optind = 1;
|
||||
int mygetopt(int argc, char** argv) {
|
||||
if (optind >= argc) return -1;
|
||||
if (!argv[optind][0] == '-' || argv[optind][1] != '-') return -1;
|
||||
char *arg = strchr(argv[optind], '=');
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||
size_t len = strlen(options[i].name);
|
||||
if (strncmp(options[i].name, argv[optind] + 2, len)) continue;
|
||||
if (options[i].needs_argument) {
|
||||
if (!arg) continue;
|
||||
if (argv[optind] + 2 + len != arg) continue;
|
||||
optarg = arg + 1;
|
||||
} else {
|
||||
if (arg) continue;
|
||||
if (argv[optind][2 + len]) continue;
|
||||
optarg = NULL;
|
||||
}
|
||||
optind++;
|
||||
return options[i].return_value;
|
||||
}
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[optind]);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int verbose = 0;
|
||||
int repeat = 1;
|
||||
uint64_t scope_id = 0;
|
||||
uint64_t samples = 0;
|
||||
int samplerate = 0;
|
||||
while (1) {
|
||||
switch (mygetopt(argc, argv)) {
|
||||
case 1:
|
||||
if (!sscanf(optarg, "%" PRIx64, &scope_id)) {
|
||||
fprintf(stderr, "Scope ID must be hexadecimal.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 2:
|
||||
verbose++;
|
||||
continue;
|
||||
case 3:
|
||||
if (!sscanf(optarg, "%d", &repeat)) {
|
||||
fprintf(stderr, "--repeat needs a number.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 4:
|
||||
usage();
|
||||
exit(0);
|
||||
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];
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE (1<<20)
|
||||
uint8_t* buffer;
|
||||
#ifdef _WIN32
|
||||
buffer = _aligned_malloc(BUFFER_SIZE, 4096);
|
||||
#else
|
||||
posix_memalign((void**)&buffer, 4096, BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
struct ThunderScopeHW *ts = thunderscopehw_create();
|
||||
enum ThunderScopeHWStatus ret;
|
||||
TS_RUN(connect(ts, scope_id));
|
||||
|
||||
for (int channel = 0; channel < 4; channel++) {
|
||||
TS_RUN(enable_channel(ts, channel));
|
||||
// 1mV / div
|
||||
TS_RUN(voltage_division_set(ts, channel, 1));
|
||||
#ifdef WIN32
|
||||
Sleep(500);
|
||||
#else
|
||||
usleep(500000);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < repeat; i++) {
|
||||
double minoffset = -0.5;
|
||||
double maxoffset = 0.5;
|
||||
for (int j = 0; j < 20; j++) {
|
||||
double midoffset = (minoffset + maxoffset) / 2;
|
||||
TS_RUN(voltage_offset_set(ts, channel, midoffset));
|
||||
TS_RUN(start(ts));
|
||||
enum ThunderScopeHWStatus status = thunderscopehw_read(ts, buffer, BUFFER_SIZE);
|
||||
if (status != THUNDERSCOPEHW_STATUS_OK) {
|
||||
fprintf(stderr, "thunderscopehw_read failed, error = %s\n", thunderscopehw_describe_error(status));
|
||||
j--;
|
||||
TS_RUN(stop(ts));
|
||||
continue;
|
||||
}
|
||||
// Convert signed output to unsigned output
|
||||
for (size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||
buffer[i] += 0x80;
|
||||
}
|
||||
TS_RUN(stop(ts));
|
||||
int high = 0;
|
||||
int low = 0;
|
||||
uint64_t sum = 0;
|
||||
for (size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (buffer[i] > 0x80) high++;
|
||||
if (buffer[i] < 0x80) low++;
|
||||
sum += buffer[i];
|
||||
}
|
||||
if (verbose) {
|
||||
fprintf(stderr," min = %8.4f max = %8.4f mid = %8.4f high=%d low=%d avg=%f\n",
|
||||
minoffset, maxoffset, midoffset,
|
||||
high, low,
|
||||
sum / (double)BUFFER_SIZE);
|
||||
}
|
||||
if (high > low) {
|
||||
minoffset = midoffset;
|
||||
} else {
|
||||
maxoffset = midoffset;
|
||||
}
|
||||
}
|
||||
printf("Channel %d calibration = %f\n", channel + 1, (minoffset + maxoffset) / 2);
|
||||
}
|
||||
TS_RUN(disable_channel(ts, channel));
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
add_executable(thunderscopehwcompensate
|
||||
thunderscopehwcompensate.c)
|
||||
target_link_libraries(thunderscopehwcompensate
|
||||
thunderscopehwlib)
|
@ -0,0 +1,185 @@
|
||||
#include "thunderscopehw.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
struct Option {
|
||||
const char* name;
|
||||
bool needs_argument;
|
||||
int return_value;
|
||||
};
|
||||
|
||||
struct Option options[] = {
|
||||
{"device", true, 1 },
|
||||
{"channel", true, 2 },
|
||||
{"help", false, 4 },
|
||||
};
|
||||
|
||||
#define TS_RUN(X) do { \
|
||||
enum ThunderScopeHWStatus ret = thunderscopehw_##X; \
|
||||
if (ret != THUNDERSCOPEHW_STATUS_OK) { \
|
||||
fprintf(stderr, "thunderscopehw_%s failed @ line %d, error = %s\n", #X, __LINE__, thunderscopehw_describe_error(ret)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void usage() {
|
||||
printf("thunderscopehwcompensate [options]\n"
|
||||
" --device=<deviceid>\n"
|
||||
" --channel=<channel\n");
|
||||
}
|
||||
|
||||
char* optarg;
|
||||
int optind = 1;
|
||||
int mygetopt(int argc, char** argv) {
|
||||
if (optind >= argc) return -1;
|
||||
if (!argv[optind][0] == '-' || argv[optind][1] != '-') return -1;
|
||||
char *arg = strchr(argv[optind], '=');
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||
size_t len = strlen(options[i].name);
|
||||
if (strncmp(options[i].name, argv[optind] + 2, len)) continue;
|
||||
if (options[i].needs_argument) {
|
||||
if (!arg) continue;
|
||||
if (argv[optind] + 2 + len != arg) continue;
|
||||
optarg = arg + 1;
|
||||
} else {
|
||||
if (arg) continue;
|
||||
if (argv[optind][2 + len]) continue;
|
||||
optarg = NULL;
|
||||
}
|
||||
optind++;
|
||||
return options[i].return_value;
|
||||
}
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[optind]);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
uint64_t scope_id = 0;
|
||||
uint64_t samples = 0;
|
||||
int channel = 0;
|
||||
while (1) {
|
||||
switch (mygetopt(argc, argv)) {
|
||||
case 1:
|
||||
if (!sscanf(optarg, "%" PRIx64, &scope_id)) {
|
||||
fprintf(stderr, "Scope ID must be hexadecimal.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 2:
|
||||
if (!sscanf(optarg, "%d", &channel)) {
|
||||
fprintf(stderr, "--channel needs a number.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 4:
|
||||
usage();
|
||||
exit(0);
|
||||
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];
|
||||
}
|
||||
|
||||
if (channel < 1 || channel > 4) {
|
||||
fprintf(stderr, "Must specify a channel between 1 and 4\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
channel--;
|
||||
|
||||
#define BUFFER_SIZE (1<<22)
|
||||
uint8_t* buffer;
|
||||
#ifdef _WIN32
|
||||
buffer = _aligned_malloc(BUFFER_SIZE, 4096);
|
||||
#else
|
||||
posix_memalign((void**)&buffer, 4096, BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
struct ThunderScopeHW *ts = thunderscopehw_create();
|
||||
enum ThunderScopeHWStatus ret;
|
||||
TS_RUN(connect(ts, scope_id));
|
||||
|
||||
TS_RUN(enable_channel(ts, channel));
|
||||
// 100mV / div
|
||||
TS_RUN(voltage_division_set(ts, channel, 100));
|
||||
retry:
|
||||
#ifdef WIN32
|
||||
Sleep(500);
|
||||
#else
|
||||
usleep(500000);
|
||||
#endif
|
||||
TS_RUN(start(ts));
|
||||
while (true)
|
||||
{
|
||||
enum ThunderScopeHWStatus status = thunderscopehw_read(ts, buffer, BUFFER_SIZE);
|
||||
if (status != THUNDERSCOPEHW_STATUS_OK) {
|
||||
fprintf(stderr, "thunderscopehw_read failed, error = %s\n", thunderscopehw_describe_error(status));
|
||||
TS_RUN(stop(ts));
|
||||
goto retry;
|
||||
}
|
||||
if (thunderscopehw_available(ts) > BUFFER_SIZE * 10) continue;
|
||||
fprintf(stderr, "GOT\n");
|
||||
// Convert signed output to unsigned output
|
||||
for (size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||
buffer[i] += 0x80;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int i;
|
||||
for (i = 0; i < BUFFER_SIZE; i += 100) {
|
||||
if (buffer[i] > 196) {
|
||||
count++;
|
||||
if (count > 50) break;
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (count < 50) continue;
|
||||
while (i < BUFFER_SIZE && buffer[i] > 196) i += 100;
|
||||
i -= 100;
|
||||
while (i < BUFFER_SIZE && buffer[i] > 196) i++;
|
||||
i-=10;
|
||||
if (i >= BUFFER_SIZE) continue;
|
||||
char screen[64][256];
|
||||
memset(screen, ' ', sizeof(screen));
|
||||
int div = 1;
|
||||
for (int t = 0; t/div < 256; t++) {
|
||||
screen[buffer[i+t]/4][t/div] = '*';
|
||||
}
|
||||
for (int row = 0; row < 64; row++) {
|
||||
screen[row][255] = '\n';
|
||||
}
|
||||
screen[63][255] = 0;
|
||||
printf("\033[H%s\n", screen[0]);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
add_executable(thunderscopehwdump
|
||||
thunderscopehwdump.c)
|
||||
target_link_libraries(thunderscopehwdump
|
||||
thunderscopehwlib)
|
@ -0,0 +1,326 @@
|
||||
#include "thunderscopehw.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
void write32(uint64_t x, FILE* f) {
|
||||
uint32_t v = 0;
|
||||
if (x < (uint64_t)0xFFFFFFFF) v = (uint32_t)x;
|
||||
if (fwrite(&v, 1, 4, f) != 4) {
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
struct Option {
|
||||
const char* name;
|
||||
bool needs_argument;
|
||||
int return_value;
|
||||
};
|
||||
|
||||
struct Option options[] = {
|
||||
{"device", true, 1 },
|
||||
{"samples", true, 2 },
|
||||
{"output-samplerate", true, 3 },
|
||||
{"help", false, 4 },
|
||||
|
||||
{"bw-all", true, 0x10 },
|
||||
{"bw1", true, 0x11 },
|
||||
{"bw2", true, 0x12 },
|
||||
{"bw3", true, 0x13 },
|
||||
{"bw4", true, 0x14 },
|
||||
|
||||
{"vdiv-all", true, 0x20 },
|
||||
{"vdiv1", true, 0x21 },
|
||||
{"vdiv2", true, 0x22 },
|
||||
{"vdiv3", true, 0x23 },
|
||||
{"vdiv4", true, 0x24 },
|
||||
|
||||
{"voffset-all", true, 0x30 },
|
||||
{"voffset1", true, 0x31 },
|
||||
{"voffset2", true, 0x32 },
|
||||
{"voffset3", true, 0x33 },
|
||||
{"voffset4", true, 0x34 },
|
||||
|
||||
{"ac-all", false, 0x40 },
|
||||
{"ac1", false, 0x41 },
|
||||
{"ac2", false, 0x42 },
|
||||
{"ac3", false, 0x43 },
|
||||
{"ac4", false, 0x44 },
|
||||
|
||||
{"dc-all", false, 0x50 },
|
||||
{"dc1", false, 0x51 },
|
||||
{"dc2", false, 0x52 },
|
||||
{"dc3", false, 0x53 },
|
||||
{"dc4", false, 0x54 },
|
||||
|
||||
{"enable-all", false, 0x60 },
|
||||
{"enable1", false, 0x61 },
|
||||
{"enable2", false, 0x62 },
|
||||
{"enable3", false, 0x63 },
|
||||
{"enable4", false, 0x64 },
|
||||
};
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"thunderscopehwdump [options] [filename.wav]\n"
|
||||
" --device=<deviceid>\n"
|
||||
" --samples=<number of samples> must be divisable by 4096\n"
|
||||
" --output-samplerate=<rate> samplerate used in wav file\n"
|
||||
" --bw[1/2/3/4/-all]=20/100/200/350 (Hz)\n"
|
||||
" --vdiv[1/2/3/4/-all]=1/2/5/10/20/50/100/200/500/1000/2000/5000/10000 (mV)\n"
|
||||
" --voffset[1/2/3/4/-all]=<voltage offset> (volts)\n"
|
||||
" --ac[1/2/3/4/-all]\n"
|
||||
" --dc[1/2/3/4/-all]\n"
|
||||
" --enable[1/2/3/4/-all]\n");
|
||||
}
|
||||
|
||||
char* optarg;
|
||||
int optind = 1;
|
||||
int mygetopt(int argc, char** argv) {
|
||||
if (optind >= argc) return -1;
|
||||
if (!argv[optind][0] == '-' || argv[optind][1] != '-') return -1;
|
||||
char *arg = strchr(argv[optind], '=');
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||
size_t len = strlen(options[i].name);
|
||||
if (strncmp(options[i].name, argv[optind] + 2, len)) continue;
|
||||
if (options[i].needs_argument) {
|
||||
if (!arg) continue;
|
||||
if (argv[optind] + 2 + len != arg) continue;
|
||||
optarg = arg + 1;
|
||||
} else {
|
||||
if (arg) continue;
|
||||
if (argv[optind][2 + len]) continue;
|
||||
optarg = NULL;
|
||||
}
|
||||
optind++;
|
||||
return options[i].return_value;
|
||||
}
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[optind]);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
uint64_t scope_id = 0;
|
||||
uint64_t samples = 0;
|
||||
int samplerate = 0;
|
||||
while (1) {
|
||||
switch (mygetopt(argc, argv)) {
|
||||
case 1:
|
||||
if (!sscanf(optarg, "%" PRIx64, &scope_id)) {
|
||||
fprintf(stderr, "Scope ID must be hexadecimal.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 2:
|
||||
if (!sscanf(optarg, "%" PRId64, &samples)) {
|
||||
fprintf(stderr, "Number of samples must be a number.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 3:
|
||||
if (!sscanf(optarg, "%d", &samplerate)) {
|
||||
fprintf(stderr, "Output samplerate must be a number.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 4:
|
||||
usage();
|
||||
exit(0);
|
||||
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 = mygetopt(argc, argv);
|
||||
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, atof(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;
|
||||
};
|
||||
#ifdef WIN32
|
||||
Sleep(500);
|
||||
#else
|
||||
usleep(500000);
|
||||
#endif
|
||||
|
||||
struct Fmt fmt;
|
||||
fmt.pcm = 1;
|
||||
fmt.channels = num_channels;
|
||||
if (samplerate) {
|
||||
fmt.rate = samplerate;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE samples
|
||||
uint8_t* buffer;
|
||||
#ifdef _WIN32
|
||||
buffer = _aligned_malloc(BUFFER_SIZE, 4096);
|
||||
#else
|
||||
posix_memalign((void**)&buffer, 4096, BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
while (samples) {
|
||||
int64_t to_copy = samples;
|
||||
if (to_copy > BUFFER_SIZE) to_copy = BUFFER_SIZE;
|
||||
ret = thunderscopehw_read(ts, buffer, to_copy);
|
||||
if (ret != THUNDERSCOPEHW_STATUS_OK) {
|
||||
fprintf(stderr, "Thunderscope read error, error = %s\n", thunderscopehw_describe_error(ret));
|
||||
exit(1);
|
||||
}
|
||||
// Convert signed output to unsigned output
|
||||
for (size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||
buffer[i] += 0x80;
|
||||
}
|
||||
if (fwrite(buffer, 1, to_copy, outfile) != to_copy) {
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
samples -= to_copy;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
add_executable(thunderscopehwstresstest
|
||||
thunderscopehwstresstest.c)
|
||||
target_link_libraries(thunderscopehwstresstest
|
||||
thunderscopehwlib)
|
@ -0,0 +1,154 @@
|
||||
#include "thunderscopehw.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define Sleep(X) usleep((X) * 1000);
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
|
||||
static uint64_t time_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (timespec_get(&ts, TIME_UTC) != TIME_UTC)
|
||||
{
|
||||
fprintf(stderr, "timespec_get failed!");
|
||||
exit(1);
|
||||
}
|
||||
return 1000000000 * ts.tv_sec + ts.tv_nsec;
|
||||
}
|
||||
|
||||
void busywait(uint64_t nanos) {
|
||||
uint64_t a = time_ns();
|
||||
while (time_ns() - a < nanos);
|
||||
}
|
||||
|
||||
struct Option {
|
||||
const char* name;
|
||||
bool needs_argument;
|
||||
int return_value;
|
||||
};
|
||||
|
||||
struct Option options[] = {
|
||||
{"device", true, 1 },
|
||||
{"help", false, 4 },
|
||||
};
|
||||
|
||||
#define TS_RUN(X) do { \
|
||||
enum ThunderScopeHWStatus ret = thunderscopehw_##X; \
|
||||
if (ret != THUNDERSCOPEHW_STATUS_OK) { \
|
||||
fprintf(stderr, "thunderscopehw_%s failed @ line %d, error = %s\n", #X, __LINE__, thunderscopehw_describe_error(ret)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void usage() {
|
||||
printf("thunderscopehwcompensate [options]\n"
|
||||
" --device=<deviceid>\n");
|
||||
}
|
||||
|
||||
char* optarg;
|
||||
int optind = 1;
|
||||
int mygetopt(int argc, char** argv) {
|
||||
if (optind >= argc) return -1;
|
||||
if (!argv[optind][0] == '-' || argv[optind][1] != '-') return -1;
|
||||
char *arg = strchr(argv[optind], '=');
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||
size_t len = strlen(options[i].name);
|
||||
if (strncmp(options[i].name, argv[optind] + 2, len)) continue;
|
||||
if (options[i].needs_argument) {
|
||||
if (!arg) continue;
|
||||
if (argv[optind] + 2 + len != arg) continue;
|
||||
optarg = arg + 1;
|
||||
} else {
|
||||
if (arg) continue;
|
||||
if (argv[optind][2 + len]) continue;
|
||||
optarg = NULL;
|
||||
}
|
||||
optind++;
|
||||
return options[i].return_value;
|
||||
}
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[optind]);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
uint64_t scope_id = 0;
|
||||
uint64_t samples = 0;
|
||||
while (1) {
|
||||
switch (mygetopt(argc, argv)) {
|
||||
case 1:
|
||||
if (!sscanf(optarg, "%" PRIx64, &scope_id)) {
|
||||
fprintf(stderr, "Scope ID must be hexadecimal.\n");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
case 4:
|
||||
usage();
|
||||
exit(0);
|
||||
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];
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE (1<<20)
|
||||
uint8_t* buffer;
|
||||
#ifdef _WIN32
|
||||
buffer = _aligned_malloc(BUFFER_SIZE, 4096);
|
||||
#else
|
||||
posix_memalign((void**)&buffer, 4096, BUFFER_SIZE);
|
||||
#endif
|
||||
|
||||
struct ThunderScopeHW *ts = thunderscopehw_create();
|
||||
enum ThunderScopeHWStatus ret;
|
||||
TS_RUN(connect(ts, scope_id));
|
||||
|
||||
TS_RUN(enable_channel(ts, 0));
|
||||
TS_RUN(voltage_division_set(ts, 0, 100));
|
||||
// 100mV / div
|
||||
Sleep(500);
|
||||
// Test pattern 1, there may be more in the future
|
||||
while (true)
|
||||
{
|
||||
TS_RUN(start(ts));
|
||||
fprintf(stderr, ".");
|
||||
fflush(stderr);
|
||||
enum ThunderScopeHWStatus status = thunderscopehw_read(ts, buffer, BUFFER_SIZE);
|
||||
if (status != THUNDERSCOPEHW_STATUS_OK) {
|
||||
fprintf(stderr, "thunderscopehw_read failed, error = %s\n", thunderscopehw_describe_error(status));
|
||||
Sleep(100);
|
||||
}
|
||||
busywait(2983474);
|
||||
TS_RUN(stop(ts));
|
||||
}
|
||||
}
|
||||
|
57
Software/libthunderscope/include/thunderscopehw.h
Normal file
57
Software/libthunderscope/include/thunderscopehw.h
Normal file
@ -0,0 +1,57 @@
|
||||
#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,
|
||||
THUNDERSCOPEHW_STATUS_OFFSET_TOO_LOW,
|
||||
THUNDERSCOPEHW_STATUS_OFFSET_TOO_HIGH,
|
||||
};
|
||||
|
||||
// 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);
|
||||
int64_t thunderscopehw_available(struct ThunderScopeHW* ts);
|
||||
|
||||
|
||||
const char* thunderscopehw_describe_error(enum ThunderScopeHWStatus err);
|
||||
|
||||
#endif // LIBTHUNDERSCOPEHW_THUNDERSCOPEHW_H
|
6
Software/libthunderscope/test/CMakeLists.txt
Normal file
6
Software/libthunderscope/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)
|
21
Software/libthunderscope/test/thunderscopehwtest.c
Normal file
21
Software/libthunderscope/test/thunderscopehwtest.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include "thunderscopehw.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;
|
||||
#ifdef _WIN32
|
||||
buffer = _aligned_malloc(1 << 20, 4096);
|
||||
#else
|
||||
posix_memalign((void**)&buffer, 4096, 1 << 20);
|
||||
#endif
|
||||
thunderscopehw_read(ts, buffer, 1 << 20);
|
||||
}
|
65
Software/libthunderscope/thunderscopehw/CMakeLists.txt
Normal file
65
Software/libthunderscope/thunderscopehw/CMakeLists.txt
Normal file
@ -0,0 +1,65 @@
|
||||
# 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_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
|
||||
)
|
||||
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists(m pow "" LIBM)
|
||||
if(LIBM)
|
||||
target_link_libraries(thunderscopehwlib
|
||||
m)
|
||||
target_link_libraries(thunderscopehwtestlib
|
||||
m)
|
||||
endif()
|
||||
|
||||
|
||||
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()
|
240
Software/libthunderscope/thunderscopehw/thunderscopehw.c
Normal file
240
Software/libthunderscope/thunderscopehw/thunderscopehw.c
Normal file
@ -0,0 +1,240 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "thunderscopehw_private.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#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 = THUNDERSCOPEHW_INVALID_HANDLE_VALUE;
|
||||
ts->c2h0_handle = THUNDERSCOPEHW_INVALID_HANDLE_VALUE;
|
||||
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;
|
||||
ts->buffer_head = 0;
|
||||
ts->buffer_tail = 0;
|
||||
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));
|
||||
#ifdef WIN32
|
||||
Sleep(5);
|
||||
#else
|
||||
usleep(5000);
|
||||
#endif
|
||||
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 & 2)
|
||||
return THUNDERSCOPEHW_STATUS_DATAMOVER_ERROR;
|
||||
|
||||
if (error_code & 1)
|
||||
return THUNDERSCOPEHW_STATUS_FIFO_OVERFLOW;
|
||||
|
||||
uint32_t overflow_cycles = (transfer_counter >> 16) & 0x3FFF;
|
||||
if (overflow_cycles)
|
||||
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;
|
||||
}
|
||||
|
||||
int64_t thunderscopehw_available(struct ThunderScopeHW* ts) {
|
||||
THUNDERSCOPEHW_RUN(update_buffer_head(ts));
|
||||
return (ts->buffer_head - ts->buffer_tail) << 12;
|
||||
}
|
||||
|
||||
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_RUN(read_handle(ts, ts->c2h0_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";
|
||||
case THUNDERSCOPEHW_STATUS_OFFSET_TOO_LOW:
|
||||
return "voffset too low";
|
||||
case THUNDERSCOPEHW_STATUS_OFFSET_TOO_HIGH:
|
||||
return "voffset too high";
|
||||
}
|
||||
return "unkonwn error";
|
||||
}
|
53
Software/libthunderscope/thunderscopehw/thunderscopehw_adc.c
Normal file
53
Software/libthunderscope/thunderscopehw/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 ? 0x0000 : 0x0200);
|
||||
}
|
||||
|
||||
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, 0x007F));
|
||||
|
||||
// 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);
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
#include "thunderscopehw_private.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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_RUN(write32(ts, SERIAL_FIFO_TLR_ADDRESS, (uint32_t)(bytes * 4)));
|
||||
// read ISR for a done value
|
||||
while ((thunderscopehw_read32(ts, SERIAL_FIFO_ISR_ADDRESS) >> 24) != 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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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?
|
||||
int dac_value = (unsigned int)round((ts->channels[channel].voffset + 0.5) * 4095);
|
||||
if (dac_value < 0)
|
||||
return THUNDERSCOPEHW_STATUS_OFFSET_TOO_LOW;
|
||||
if (dac_value > 0xFFF)
|
||||
return THUNDERSCOPEHW_STATUS_OFFSET_TOO_HIGH;
|
||||
|
||||
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:
|
||||
case 1:
|
||||
on_channels[1] = on_channels[2] = on_channels[3] = 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
|
||||
/* No channels, leave data mover off. */
|
||||
if (num_channels_on == 0) return THUNDERSCOPEHW_STATUS_OK;
|
||||
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];
|
||||
if (thunderscopehw_read_handle(ts, ts->user_handle, bytes, addr, 4) != THUNDERSCOPEHW_STATUS_OK) {
|
||||
fprintf(stderr, "Error in thunderscopehw_read32\n");
|
||||
exit(1);
|
||||
}
|
||||
return (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
@ -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/libthunderscope/thunderscopehw/thunderscopehw_pll.c
Normal file
36
Software/libthunderscope/thunderscopehw/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);
|
||||
}
|
||||
|
116
Software/libthunderscope/thunderscopehw/thunderscopehw_private.h
Normal file
116
Software/libthunderscope/thunderscopehw/thunderscopehw_private.h
Normal file
@ -0,0 +1,116 @@
|
||||
#ifndef THUNDERSCOPEHW_PRIVATE_H
|
||||
#define THUNDERSCOPEHW_PRIVATE_H
|
||||
|
||||
#include "thunderscopehw.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define THUNDERSCOPEHW_FILE_HANDLE HANDLE
|
||||
#define THUNDERSCOPEHW_INVALID_HANDLE_VALUE INVALID_HANDLE_VALUE
|
||||
#else
|
||||
#define THUNDERSCOPEHW_FILE_HANDLE int
|
||||
#define THUNDERSCOPEHW_INVALID_HANDLE_VALUE -1
|
||||
#endif
|
||||
|
||||
|
||||
#define USER_DEVICE_PATH "user"
|
||||
#define C2H_0_DEVICE_PATH "c2h_0"
|
||||
|
||||
// TODO: Convert to enum
|
||||
#define DATAMOVER_REG_OUT 0x00000 // bit 0: !halt, bit 1: !reset
|
||||
#define DATAMOVER_TRANSFER_COUNTER 0x00008 // A 32 bit value, low 16 is transfer counter, bit 31 error bit
|
||||
#define BOARD_REG_OUT 0x10000 // A 32 bit value, bit 0:3: attenuation, bit 4:7: dc_cpl, bit 8: acq_en, bit 9: clk_oe, bit 10: fe_en, 21bits unused
|
||||
#define BOARD_REG_IN 0x10008 // unused for now
|
||||
#define SERIAL_FIFO_DATA_WRITE_REG 0x20010
|
||||
#define SERIAL_FIFO_IER_ADDRESS 0x20004
|
||||
#define SERIAL_FIFO_TDR_ADDRESS 0x2002C
|
||||
#define SERIAL_FIFO_TDFV_ADDRESS 0x2000C // read 0x1FC when the transmission is done
|
||||
#define SERIAL_FIFO_TLR_ADDRESS 0x20014 // (in bytes) This byte tells the thing to transfer
|
||||
#define SERIAL_FIFO_ISR_ADDRESS 0x20000
|
||||
|
||||
#define SPI_FRONT_END_CHANNEL_1 0xF8
|
||||
#define SPI_FRONT_END_CHANNEL_2 0xF9
|
||||
#define SPI_FRONT_END_CHANNEL_3 0xFA
|
||||
#define SPI_FRONT_END_CHANNEL_4 0xFB
|
||||
|
||||
#define SPI_BYTE_ADC 0xFD
|
||||
|
||||
#define I2C_BYTE_PLL 0xFF
|
||||
|
||||
#define CLOCK_GEN_I2C_ADDRESS_WRITE 0b10110000 //The address + write bit for the Clock generator
|
||||
#define CLOCK_GEN_I2C_ADDRESS_READ 0b10110001 //IF WE COULD
|
||||
|
||||
#define THUNDERSCOPEHW_CHANNELS 4
|
||||
|
||||
#define THUNDERSCOPEHW_RUN(X) do { \
|
||||
enum ThunderScopeHWStatus ret = (thunderscopehw_##X); \
|
||||
if (ret != THUNDERSCOPEHW_STATUS_OK) return ret; \
|
||||
} while(0)
|
||||
|
||||
enum ThunderScopeHWCouplingType {
|
||||
THUNDERSCOPEHW_COUPLING_AC = 20000,
|
||||
THUNDERSCOPEHW_COUPLING_DC,
|
||||
};
|
||||
|
||||
struct ThunderScopeHWChannel {
|
||||
bool on;
|
||||
int vdiv;
|
||||
int bw;
|
||||
double voffset;
|
||||
enum ThunderScopeHWCouplingType coupling;
|
||||
};
|
||||
|
||||
struct ThunderScopeHW {
|
||||
bool connected;
|
||||
bool board_en; // general front end en
|
||||
bool adc_en; // adc values
|
||||
bool pll_en; // PLL enable
|
||||
bool fe_en; // ??
|
||||
bool datamover_en;
|
||||
bool fpga_adc_en;
|
||||
struct ThunderScopeHWChannel channels[4];
|
||||
|
||||
THUNDERSCOPEHW_FILE_HANDLE user_handle;
|
||||
THUNDERSCOPEHW_FILE_HANDLE c2h0_handle;
|
||||
|
||||
// These are counted in 4k pages
|
||||
uint64_t buffer_head;
|
||||
uint64_t buffer_tail;
|
||||
uint64_t ram_size_pages;
|
||||
};
|
||||
|
||||
|
||||
enum ThunderScopeHWStatus thunderscopehw_initboard(struct ThunderScopeHW* ts);
|
||||
enum ThunderScopeHWStatus thunderscopehw_fifo_write(struct ThunderScopeHW* ts, uint8_t* data, size_t bytes);
|
||||
enum ThunderScopeHWStatus thunderscopehw_set_datamover_reg(struct ThunderScopeHW* ts);
|
||||
enum ThunderScopeHWStatus thunderscopehw_set_pga(struct ThunderScopeHW* ts, int channel);
|
||||
enum ThunderScopeHWStatus thunderscopehw_set_dac(struct ThunderScopeHW* ts, int channel);
|
||||
enum ThunderScopeHWStatus thunderscopehw_configure_channels(struct ThunderScopeHW* ts);
|
||||
enum ThunderScopeHWStatus thunderscopehw_configure_channel(struct ThunderScopeHW* ts, int channel);
|
||||
uint32_t thunderscopehw_read32(struct ThunderScopeHW*ts, size_t addr);
|
||||
enum ThunderScopeHWStatus thunderscopehw_write32(struct ThunderScopeHW*ts, size_t addr, uint32_t value);
|
||||
|
||||
|
||||
// ADC functions (HMHMCAD1)
|
||||
enum ThunderScopeHWAdcRegister {
|
||||
// Datasheet names functions inside these registers, not the registers, so the names are made up.
|
||||
THUNDERSCOPEHW_ADC_REG_RESET = 0x00,
|
||||
THUNDERSCOPEHW_ADC_REG_POWER = 0x0F,
|
||||
THUNDERSCOPEHW_ADC_REG_INVERT = 0x24,
|
||||
THUNDERSCOPEHW_ADC_REG_QUAD_GAIN = 0x2A,
|
||||
THUNDERSCOPEHW_ADC_REG_DUAL_GAIN = 0x2B,
|
||||
THUNDERSCOPEHW_ADC_REG_CHNUM_CLKDIV = 0x31,
|
||||
THUNDERSCOPEHW_ADC_REG_GAIN_CFG = 0x33,
|
||||
THUNDERSCOPEHW_ADC_REG_INSEL12 = 0x3A,
|
||||
THUNDERSCOPEHW_ADC_REG_INSEL34 = 0x3B,
|
||||
THUNDERSCOPEHW_ADC_REG_FS_CNTRL = 0x55,
|
||||
};
|
||||
enum ThunderScopeHWStatus thunderscopehw_adc_set_reg(struct ThunderScopeHW* ts, enum ThunderScopeHWAdcRegister reg, uint16_t value);
|
||||
enum ThunderScopeHWStatus thunderscopehw_adc_power(struct ThunderScopeHW* ts, bool on);
|
||||
enum ThunderScopeHWStatus thunderscopehw_configure_adc(struct ThunderScopeHW* ts);
|
||||
enum ThunderScopeHWStatus thunderscopehw_configure_pll(struct ThunderScopeHW* ts);
|
||||
|
||||
enum ThunderScopeHWStatus thunderscopehw_read_handle(struct ThunderScopeHW* ts, THUNDERSCOPEHW_FILE_HANDLE h, uint8_t* data, uint64_t addr, int64_t bytes);
|
||||
enum ThunderScopeHWStatus thunderscopehw_write_handle(struct ThunderScopeHW* ts, THUNDERSCOPEHW_FILE_HANDLE h, uint8_t* data, uint64_t addr, int64_t bytes);
|
||||
|
||||
#endif
|
@ -0,0 +1,123 @@
|
||||
#include "thunderscopehw_private.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.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 = (THUNDERSCOPEHW_FILE_HANDLE)101;
|
||||
ts->c2h0_handle = (THUNDERSCOPEHW_FILE_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 = THUNDERSCOPEHW_INVALID_HANDLE_VALUE;
|
||||
ts->c2h0_handle = THUNDERSCOPEHW_INVALID_HANDLE_VALUE;
|
||||
ts->connected = false;
|
||||
return THUNDERSCOPEHW_STATUS_OK;
|
||||
}
|
||||
|
||||
uint8_t thunderscopehw_simulator_fifo[256];
|
||||
uint8_t thunderscopehw_simulator_fifo_length = 0;
|
||||
|
||||
static char* thunderscopehw_identify_handle(THUNDERSCOPEHW_FILE_HANDLE h) {
|
||||
if (h == (THUNDERSCOPEHW_FILE_HANDLE)101) return "USER";
|
||||
if (h == (THUNDERSCOPEHW_FILE_HANDLE)102) return "DMA";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static uint16_t blocks = 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,
|
||||
thunderscopehw_identify_handle(h),
|
||||
(long long)addr);
|
||||
memset(data, 0, bytes);
|
||||
switch (addr) {
|
||||
case 8:
|
||||
blocks++;
|
||||
data[3] = blocks & 255;
|
||||
data[2] = blocks >> 8;
|
||||
break;
|
||||
|
||||
case SERIAL_FIFO_ISR_ADDRESS:
|
||||
if (thunderscopehw_simulator_fifo_length) {
|
||||
data[3] = 0;
|
||||
thunderscopehw_simulator_fifo_length--;
|
||||
} else {
|
||||
data[3] = 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (bytes <= 4) {
|
||||
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,
|
||||
thunderscopehw_identify_handle(h),
|
||||
(long long)addr);
|
||||
if (bytes <= 4) {
|
||||
for (int i = 0; i < bytes; i++) {
|
||||
printf(" 0x%02x", data[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
uint32_t tmp = 0;
|
||||
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;
|
||||
}
|
||||
|
178
Software/libthunderscope/thunderscopehw/thunderscopehw_win.c
Normal file
178
Software/libthunderscope/thunderscopehw/thunderscopehw_win.c
Normal file
@ -0,0 +1,178 @@
|
||||
#include "thunderscopehw_private.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <SetupAPI.h>
|
||||
#pragma comment(lib, "setupapi.lib")
|
||||
#include <INITGUID.H>
|
||||
#include <stdio.h>
|
||||
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XDMA,
|
||||
0x74c7e4a9, 0x6d5d, 0x4a70, 0xbc, 0x0d, 0x20, 0x69, 0x1d, 0xff, 0x9e, 0x9d);
|
||||
|
||||
typedef void (*thunderscopehw_iterate_device_callback)(PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail, void* context);
|
||||
void thunderscopehw_iterate_devices(thunderscopehw_iterate_device_callback cb, void* context)
|
||||
{
|
||||
GUID guid = GUID_DEVINTERFACE_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;
|
||||
}
|
||||
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, context);
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(device_info);
|
||||
}
|
||||
|
||||
struct thunderscopehw_scan_callback_context {
|
||||
uint64_t* scope_ids;
|
||||
int max_ids;
|
||||
int count;
|
||||
};
|
||||
|
||||
static void 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 context.count;
|
||||
}
|
||||
|
||||
struct thunderscopehw_connect_callback_context {
|
||||
struct ThunderScopeHW* ts;
|
||||
uint64_t scope_id;
|
||||
int count;
|
||||
};
|
||||
|
||||
static void 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_s(connection_string, sizeof(connection_string), "%s\\%s",dev_detail->DevicePath, USER_DEVICE_PATH);
|
||||
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_s(connection_string, sizeof(connection_string), "%s\\%s",dev_detail->DevicePath, C2H_0_DEVICE_PATH);
|
||||
data->ts->c2h0_handle = CreateFile(connection_string, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (data->ts->c2h0_handle == INVALID_HANDLE_VALUE)
|
||||
fprintf(stderr, "Failed to open c2h_0 handle, win32 error code: %d\n", GetLastError());
|
||||
|
||||
if (data->ts->c2h0_handle != INVALID_HANDLE_VALUE &&
|
||||
data->ts->user_handle != INVALID_HANDLE_VALUE) {
|
||||
data->ts->connected = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
data->count++;
|
||||
}
|
||||
|
||||
enum ThunderScopeHWStatus 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);
|
||||
}
|
||||
|
||||
enum ThunderScopeHWStatus thunderscopehw_disconnect(struct ThunderScopeHW* ts)
|
||||
{
|
||||
// adc power down
|
||||
// datamover reg = 0
|
||||
|
||||
if (ts->user_handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(ts->user_handle);
|
||||
ts->user_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (ts->c2h0_handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(ts->c2h0_handle);
|
||||
ts->c2h0_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
ts->connected = false;
|
||||
|
||||
return THUNDERSCOPEHW_STATUS_OK;
|
||||
}
|
||||
|
||||
enum ThunderScopeHWStatus thunderscopehw_read_handle(struct ThunderScopeHW* ts, HANDLE h, uint8_t* data, uint64_t addr, int64_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(h, offset, NULL, FILE_BEGIN)) {
|
||||
fprintf(stderr, "Error setting file pointer for PCIeLink::_Read(), win32 error code: %d\n", GetLastError());
|
||||
return THUNDERSCOPEHW_STATUS_READ_ERROR;
|
||||
}
|
||||
|
||||
// read from device into buffer
|
||||
DWORD bytesRead;
|
||||
if (!ReadFile(h, data, (DWORD)bytes, &bytesRead, NULL)) {
|
||||
fprintf(stderr, "read handle failed, win32 error code: %d\n", GetLastError());
|
||||
return THUNDERSCOPEHW_STATUS_READ_ERROR;
|
||||
}
|
||||
return THUNDERSCOPEHW_STATUS_OK;
|
||||
}
|
||||
|
||||
enum ThunderScopeHWStatus thunderscopehw_write_handle(struct ThunderScopeHW* ts, HANDLE h, uint8_t* data, uint64_t addr, int64_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(h, offset, NULL, FILE_BEGIN)) {
|
||||
fprintf(stderr, "Error setting file pointer for PCIeLink::_Read(), win32 error code: %d\n", GetLastError());
|
||||
return THUNDERSCOPEHW_STATUS_WRITE_ERROR;
|
||||
}
|
||||
|
||||
// write from buffer to device
|
||||
DWORD bytesWritten;
|
||||
if (!WriteFile(h, data, (DWORD)bytes, &bytesWritten, NULL)) {
|
||||
fprintf(stderr, "write handle failed, win32 error code: %d\n", GetLastError());
|
||||
return THUNDERSCOPEHW_STATUS_WRITE_ERROR;
|
||||
}
|
||||
return THUNDERSCOPEHW_STATUS_OK;
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user