ThunderScope/Software/libthunderscope/thunderscopehw/thunderscopehw.c
2022-03-05 23:43:44 -08:00

241 lines
7.0 KiB
C

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