7
mirror of https://github.com/EEVengers/ThunderScope.git synced 2025-04-22 17:43:44 +00:00

Merge pull request from profezzorn/libthunderscopehw

Libthunderscopehw
This commit is contained in:
Aleksa Bjelogrlic 2022-03-06 09:06:55 -05:00 committed by GitHub
commit f9008ae100
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2260 additions and 0 deletions

3
.gitignore vendored
View File

@ -32,3 +32,6 @@ Software/waveview/CMakeCache.txt
# testing files
Software/waveview/dump.csv
*.c~
*.txt~
*.h~

2
Software/libthunderscope/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
*~

View 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)

View File

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

View File

@ -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));
}
}

View File

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

View File

@ -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));
}
}

View File

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

View File

@ -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);
}
}

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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));
}
}

View 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

View File

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

View File

@ -0,0 +1,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);
}

View 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()

View 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";
}

View File

@ -0,0 +1,53 @@
#include "thunderscopehw_private.h"
enum ThunderScopeHWStatus thunderscopehw_adc_set_reg(struct ThunderScopeHW* ts, enum ThunderScopeHWAdcRegister reg, uint16_t value)
{
uint8_t fifo[4];
fifo[0] = SPI_BYTE_ADC;
fifo[1] = reg;
fifo[2] = value >> 8;
fifo[3] = value & 0xff;
return thunderscopehw_fifo_write(ts, fifo, 4);
}
enum ThunderScopeHWStatus thunderscopehw_adc_power(struct ThunderScopeHW* ts, bool on)
{
return thunderscopehw_adc_set_reg(ts,
THUNDERSCOPEHW_ADC_REG_POWER,
on ? 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);
}

View File

@ -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);
}

View File

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

View File

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

View File

@ -0,0 +1,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

View File

@ -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;
}

View 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